48 static const char config[] =
"indications.conf";
50 static const int midi_tohz[128] = {
51 8, 8, 9, 9, 10, 10, 11, 12, 12, 13,
52 14, 15, 16, 17, 18, 19, 20, 21, 23, 24,
53 25, 27, 29, 30, 32, 34, 36, 38, 41, 43,
54 46, 48, 51, 55, 58, 61, 65, 69, 73, 77,
55 82, 87, 92, 97, 103, 110, 116, 123, 130, 138,
56 146, 155, 164, 174, 184, 195, 207, 220, 233, 246,
57 261, 277, 293, 311, 329, 349, 369, 391, 415, 440,
58 466, 493, 523, 554, 587, 622, 659, 698, 739, 783,
59 830, 880, 932, 987, 1046, 1108, 1174, 1244, 1318, 1396,
60 1479, 1567, 1661, 1760, 1864, 1975, 2093, 2217, 2349, 2489,
61 2637, 2793, 2959, 3135, 3322, 3520, 3729, 3951, 4186, 4434,
62 4698, 4978, 5274, 5587, 5919, 6271, 6644, 7040, 7458, 7902,
63 8372, 8869, 9397, 9956, 10548, 11175, 11839, 12543
68 #define NUM_TONE_ZONE_BUCKETS 53
114 static void playtones_release(
struct ast_channel *chan,
void *params)
122 ao2_cleanup(ps->origwfmt);
128 static void *playtones_alloc(
struct ast_channel *chan,
void *params)
137 ps->origwfmt =
ao2_bump(ast_channel_writeformat(chan));
140 ast_log(LOG_WARNING,
"Unable to set '%s' to signed linear format (write)\n", ast_channel_name(chan));
141 playtones_release(NULL, ps);
145 ps->reppos = pd->reppos;
146 ps->nitems = pd->nitems;
147 ps->items = pd->items;
152 if (pd->interruptible) {
161 static int playtones_generator(
struct ast_channel *chan,
void *data,
int len,
int samples)
171 if (len >
sizeof(ps->data) / 2 - 1) {
172 ast_log(LOG_WARNING,
"Can't generate that much data!\n");
176 memset(&ps->f, 0,
sizeof(ps->f));
178 pi = &ps->items[ps->npos];
180 if (ps->oldnpos != ps->npos) {
183 ps->v2_1 = pi->init_v2_1;
184 ps->v3_1 = pi->init_v3_1;
186 ps->v2_2 = pi->init_v2_2;
187 ps->v3_2 = pi->init_v3_2;
188 ps->oldnpos = ps->npos;
191 for (x = 0; x < samples; x++) {
194 ps->v3_1 = (pi->fac1 * ps->v2_1 >> 15) - ps->v1_1;
198 ps->v3_2 = (pi->fac2 * ps->v2_2 >> 15) - ps->v1_2;
201 p = ps->v3_2 - 32768;
205 p = ((p * 9) / 10) + 1;
206 ps->data[x] = (ps->v3_1 * p) >> 15;
208 ps->data[x] = ps->v3_1 + ps->v3_2;
217 ps->f.
data.ptr = ps->data;
225 if (pi->duration && ps->pos >= pi->duration * 8) {
228 if (ps->npos >= ps->nitems) {
229 if (ps->reppos == -1) {
232 ps->npos = ps->reppos;
240 .alloc = playtones_alloc,
241 .release = playtones_release,
242 .generate = playtones_generator,
247 if (sscanf(s,
"%30u+%30u/%30u", &tone_data->freq1, &tone_data->freq2,
248 &tone_data->time) == 3) {
250 }
else if (sscanf(s,
"%30u+%30u", &tone_data->freq1, &tone_data->freq2) == 2) {
253 }
else if (sscanf(s,
"%30u*%30u/%30u", &tone_data->freq1, &tone_data->freq2,
254 &tone_data->time) == 3) {
256 tone_data->modulate = 1;
257 }
else if (sscanf(s,
"%30u*%30u", &tone_data->freq1, &tone_data->freq2) == 2) {
260 tone_data->modulate = 1;
261 }
else if (sscanf(s,
"%30u/%30u", &tone_data->freq1, &tone_data->time) == 2) {
263 tone_data->freq2 = 0;
264 }
else if (sscanf(s,
"%30u", &tone_data->freq1) == 1) {
266 tone_data->freq2 = 0;
268 }
else if (sscanf(s,
"M%30u+M%30u/%30u", &tone_data->freq1, &tone_data->freq2,
269 &tone_data->time) == 3) {
271 tone_data->midinote = 1;
272 }
else if (sscanf(s,
"M%30u+M%30u", &tone_data->freq1, &tone_data->freq2) == 2) {
275 tone_data->midinote = 1;
276 }
else if (sscanf(s,
"M%30u*M%30u/%30u", &tone_data->freq1, &tone_data->freq2,
277 &tone_data->time) == 3) {
279 tone_data->modulate = 1;
280 tone_data->midinote = 1;
281 }
else if (sscanf(s,
"M%30u*M%30u", &tone_data->freq1, &tone_data->freq2) == 2) {
284 tone_data->modulate = 1;
285 tone_data->midinote = 1;
286 }
else if (sscanf(s,
"M%30u/%30u", &tone_data->freq1, &tone_data->time) == 2) {
288 tone_data->freq2 = -1;
289 tone_data->midinote = 1;
290 }
else if (sscanf(s,
"M%30u", &tone_data->freq1) == 1) {
292 tone_data->freq2 = -1;
294 tone_data->midinote = 1;
308 static const float sample_rate = 8000.0;
309 static const float max_sample_val = 32768.0;
315 d.interruptible = interruptible;
320 if (strchr(stringp,
'|')) {
326 while ((s = strsep(&stringp, separator)) && !ast_strlen_zero(s)) {
335 }
else if (d.reppos == -1) {
340 ast_log(LOG_ERROR,
"Failed to parse tone part '%s'\n", s);
344 if (tone_data.midinote) {
346 if (tone_data.freq1 <= 127) {
347 tone_data.freq1 = midi_tohz[tone_data.freq1];
352 if (tone_data.freq2 <= 127) {
353 tone_data.freq2 = midi_tohz[tone_data.freq2];
359 new_items =
ast_realloc(d.items, (d.nitems + 1) *
sizeof(*d.items));
366 d.items[d.nitems].fac1 = 2.0 * cos(2.0 * M_PI * (tone_data.freq1 / sample_rate)) * max_sample_val;
367 d.items[d.nitems].init_v2_1 = sin(-4.0 * M_PI * (tone_data.freq1 / sample_rate)) * d.vol;
368 d.items[d.nitems].init_v3_1 = sin(-2.0 * M_PI * (tone_data.freq1 / sample_rate)) * d.vol;
370 d.items[d.nitems].fac2 = 2.0 * cos(2.0 * M_PI * (tone_data.freq2 / sample_rate)) * max_sample_val;
371 d.items[d.nitems].init_v2_2 = sin(-4.0 * M_PI * (tone_data.freq2 / sample_rate)) * d.vol;
372 d.items[d.nitems].init_v3_2 = sin(-2.0 * M_PI * (tone_data.freq2 / sample_rate)) * d.vol;
374 d.items[d.nitems].duration = tone_data.time;
375 d.items[d.nitems].modulate = tone_data.modulate;
381 ast_log(LOG_ERROR,
"No valid tone parts\n");
414 if (ast_strlen_zero(country)) {
415 ast_log(LOG_WARNING,
"Failed to get indication zone, country not set\n");
420 ast_log(LOG_WARNING,
"Failed to get indication zone: %s\n", country);
424 ast_log(LOG_NOTICE,
"Setting default indication country to '%s'\n", country);
426 ao2_lock(ast_tone_zones);
427 if (default_tone_zone) {
431 ao2_unlock(ast_tone_zones);
446 if (ast_strlen_zero(country)) {
447 ao2_lock(ast_tone_zones);
448 if (default_tone_zone) {
451 ao2_unlock(ast_tone_zones);
458 return ao2_find(ast_tone_zones, &zone_arg,
OBJ_POINTER);
469 ao2_lock(ast_tone_zones);
470 if (default_tone_zone) {
473 ao2_unlock(ast_tone_zones);
484 if (!strcasecmp(ts->
name, indication)) {
499 static void ast_tone_zone_sound_destructor(
void *obj)
505 ast_free((
char *) ts->
name);
510 ast_free((
char *) ts->
data);
534 ao2_lock(ast_tone_zones);
535 if (!default_tone_zone) {
538 ao2_unlock(ast_tone_zones);
542 ast_verb(5,
"Registered indication country '%s'\n", zone->
country);
557 ao2_lock(ast_tone_zones);
560 ao2_unlock(ast_tone_zones);
564 if (default_tone_zone == tz) {
567 default_tone_zone =
ao2_callback(ast_tone_zones, 0, NULL, NULL);
569 ao2_unlock(ast_tone_zones);
580 const char *tonelist)
584 if (ast_strlen_zero(indication) || ast_strlen_zero(tonelist)) {
589 if (!strcasecmp(indication, ts->
name)) {
597 ts = ao2_alloc_options(
sizeof(*ts), ast_tone_zone_sound_destructor,
622 if (!strcasecmp(indication, ts->
name)) {
647 wordlen = strlen(a->word);
650 while ((tz = ao2_iterator_next(&i))) {
651 if (!strncasecmp(a->word, tz->
country, wordlen)) {
667 int created_country = 0;
668 char *res = CLI_SUCCESS;
674 "Usage: indication add <country> <indication> \"<tonelist>\"\n"
675 " Add the given indication to the country.\n";
679 return complete_country(a);
686 return CLI_SHOWUSAGE;
691 ast_log(LOG_NOTICE,
"Country '%s' does not exist, creating it.\n", a->argv[2]);
693 if (!(tz = ast_tone_zone_alloc())) {
700 ast_log(LOG_WARNING,
"Unable to register new country\n");
711 if (ast_strlen_zero(a->argv[3])) {
712 ast_log(LOG_WARNING,
"Unable to register indication %s\n", a->argv[2]);
714 ast_log(LOG_WARNING,
"Unable to register indication %s/%s\n", a->argv[2], a->argv[3]);
716 if (created_country) {
729 static char *complete_indications(
struct ast_cli_args *a)
740 tz = ao2_find(ast_tone_zones, &tmp_tz,
OBJ_POINTER);
745 wordlen = strlen(a->word);
749 if (!strncasecmp(a->word, ts->
name, wordlen)) {
765 char *res = CLI_SUCCESS;
769 e->
command =
"indication remove";
771 "Usage: indication remove <country> [indication]\n"
772 " Remove the given indication from the country.\n";
776 return complete_country(a);
779 return complete_indications(a);
784 if (a->argc != 3 && a->argc != 4) {
785 return CLI_SHOWUSAGE;
791 ast_log(LOG_WARNING,
"Unable to unregister indication country %s\n", a->argv[2]);
799 ast_log(LOG_WARNING,
"Unable to unregister indication %s/%s, country does not exists\n", a->argv[2], a->argv[3]);
804 ast_log(LOG_WARNING,
"Unable to unregister indication %s/%s\n", a->argv[2], a->argv[3]);
817 int found_country = 0;
822 e->
command =
"indication show";
824 "Usage: indication show [<country> ...]\n"
825 " Display either a condensed summary of all countries and indications, or a\n"
826 " more verbose list of indications for the specified countries.\n";
829 return complete_country(a);
835 ast_cli(a->fd,
"Country Description\n");
836 ast_cli(a->fd,
"===========================\n");
838 while ((tz = ao2_iterator_next(&iter))) {
846 ao2_lock(ast_tone_zones);
847 if (default_tone_zone) {
848 ast_cli(a->fd,
"===========================\n");
850 ast_cli(a->fd,
"Default tone zone: %s\n", tz->
country);
853 ao2_unlock(ast_tone_zones);
857 buf = ast_str_alloca(256);
859 for (i = 2; i < a->argc; i++) {
868 if (!(tz = ao2_find(ast_tone_zones, &zone_arg,
OBJ_POINTER))) {
872 if (!found_country) {
874 ast_cli(a->fd,
"Country Indication PlayList\n");
875 ast_cli(a->fd,
"=====================================\n");
889 ast_cli(a->fd,
"%-7.7s %-15.15s %s\n", tz->
country, ts->
name, ts->
data);
896 if (!found_country) {
897 ast_cli(a->fd,
"No countries matched your criteria.\n");
921 char *ring, *c = buf;
925 while ((ring = strsep(&c,
","))) {
930 if (!isdigit(ring[0]) || (value = atoi(ring)) == -1) {
931 ast_log(LOG_WARNING,
"Invalid ringcadence given '%s'.\n", ring);
945 static void store_config_tone_zone(
struct ast_tone_zone *zone,
const char *var,
973 static int parse_tone_zone(
struct ast_config *cfg,
const char *country)
984 if ((zone = ao2_find(ast_tone_zones, &tmp_zone,
OBJ_POINTER))) {
985 reset_tone_zone(zone);
986 }
else if ((zone = ast_tone_zone_alloc())) {
994 for (v = ast_variable_browse(cfg, country); v; v = v->
next) {
995 store_config_tone_zone(zone, v->
name, v->
value);
1000 if (!is_valid_tone_zone(zone)) {
1001 ast_log(LOG_WARNING,
"Indication country '%s' is invalid\n", country);
1003 ast_log(LOG_WARNING,
"Unable to register indication country '%s'.\n",
1063 const char *cxt = NULL;
1064 const char *country = NULL;
1070 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
1071 ast_log(LOG_WARNING,
"Can't find indications config file %s.\n", config);
1073 }
else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
1078 ao2_lock(ast_tone_zones);
1085 if (!strcasecmp(cxt,
"general")) {
1089 if (parse_tone_zone(cfg, cxt)) {
1090 goto return_cleanup;
1098 country = ast_variable_retrieve(cfg,
"general",
"country");
1099 if (!ast_strlen_zero(country)) {
1100 ast_log(LOG_NOTICE,
"Default country for indication tones: %s\n", country);
1102 ast_log(LOG_WARNING,
"Unable to set the default country (for indication tones)\n");
1105 ast_log(LOG_WARNING,
"Missing default country (for indication tones)\n");
1111 ao2_unlock(ast_tone_zones);
1119 AST_CLI_DEFINE(handle_cli_indication_add,
"Add the given indication to the country"),
1120 AST_CLI_DEFINE(handle_cli_indication_remove,
"Remove the given indication from the country"),
1121 AST_CLI_DEFINE(handle_cli_indication_show,
"Display a list of all countries/indications")
1124 static int ast_tone_zone_hash(
const void *obj,
const int flags)
1131 static int ast_tone_zone_cmp(
void *obj,
void *arg,
int flags)
1144 static int unload_module(
void)
1147 if (default_tone_zone) {
1149 default_tone_zone = NULL;
1151 if (ast_tone_zones) {
1153 ast_tone_zones = NULL;
1163 NUM_TONE_ZONE_BUCKETS, ast_tone_zone_hash, NULL, ast_tone_zone_cmp);
1164 if (!ast_tone_zones) {
1183 AST_MODULE_INFO(
ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER,
"Indication Tone Handling",
1184 .support_level = AST_MODULE_SUPPORT_CORE,
1186 .unload = unload_module,
1189 .requires =
"extconfig",
struct ast_variable * next
Main Channel structure associated with a channel.
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
#define ast_realloc(p, len)
A wrapper for realloc()
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
char description[40]
Text description of the given country.
static struct ast_tone_zone * ast_tone_zone_unref(struct ast_tone_zone *tz)
Release a reference to an ast_tone_zone.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
char country[MAX_TONEZONE_COUNTRY]
Country code that this set of tones is for.
static struct ast_tone_zone * default_tone_zone
int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen, void *params)
descriptor for a cli entry.
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
#define ast_tone_zone_unlock(tz)
Unlock an ast_tone_zone.
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container, as described below.
A description of a part of a tone.
int ast_tone_zone_part_parse(const char *s, struct ast_tone_zone_part *tone_data)
Parse a tone part.
static int reload_module(void)
Reload indications module.
struct ast_config * ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
Load a config file.
Structure for variables, used for configurations and for channel variables.
static int load_module(void)
Load indications module.
static int prune_tone_zone(void *obj, void *arg, int flags)
Prune tones no longer in the configuration, and have the tone zone unlinked if it is no longer in the...
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
#define ast_cli_register_multiple(e, len)
Register multiple commands.
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
static int ast_unregister_indication(struct ast_tone_zone *zone, const char *indication)
remove an existing country's indication. Both country and indication must exist
#define ast_strdup(str)
A wrapper for strdup()
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
#define CV_END
close a variable parsing block
static int ast_register_indication(struct ast_tone_zone *zone, const char *indication, const char *tonelist)
#define CV_START(__in_var, __in_val)
the macro to open a block for variable parsing
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
struct ast_frame_subclass subclass
static int tone_zone_mark(void *obj, void *arg, int flags)
Mark the zone and its tones before parsing configuration. We will use this to know what to remove aft...
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
#define CV_F(__pattern, __body)
call a generic function if the name matches.
static int ast_register_indication_country(struct ast_tone_zone *zone)
add a new country, if country exists, it will be replaced.
General Asterisk PBX channel definitions.
#define AST_FRIENDLY_OFFSET
Offset into a frame's data buffer.
A set of tones for a given locale.
#define ast_strdupa(s)
duplicate a string in memory from the stack
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
static void ast_tone_zone_destructor(void *obj)
deallocate the passed tone zone
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
static struct ast_cli_entry cli_indications[]
CLI entries for commands provided by this module.
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Asterisk internal frame definitions.
static struct ast_tone_zone_sound * ast_tone_zone_sound_unref(struct ast_tone_zone_sound *ts)
Release a reference to an ast_tone_zone_sound.
A set of macros to manage forward-linked lists.
int ast_set_write_format(struct ast_channel *chan, struct ast_format *format)
Sets write format on channel chan.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
void ast_playtones_stop(struct ast_channel *chan)
Stop playing tones on a channel.
int ast_playtones_start(struct ast_channel *chan, int vol, const char *playlst, int interruptible)
Start playing a list of tones on a channel.
const char * name
Name of the tone. For example, "busy".
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Support for dynamic strings.
static struct ast_tone_zone_sound * ast_tone_zone_sound_ref(struct ast_tone_zone_sound *ts)
Increase the reference count on an ast_tone_zone_sound.
static struct ast_tone_zone * ast_tone_zone_ref(struct ast_tone_zone *tz)
Increase the reference count on an ast_tone_zone.
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
union ast_frame::@224 data
struct ao2_iterator ast_tone_zone_iterator_init(void)
Get an iterator for the available tone zones.
#define ast_calloc(num, len)
A wrapper for calloc()
int ast_tone_zone_count(void)
Get the number of registered tone zones.
Module could not be loaded properly.
int ast_write(struct ast_channel *chan, struct ast_frame *frame)
Write a frame to a channel This function writes the given frame to the indicated channel.
Prototypes for public functions only of internal interest,.
Structure used to handle boolean flags.
int * ringcadence
Array of ring cadence parts.
struct ast_tone_zone * ast_get_indication_zone(const char *country)
locate ast_tone_zone, given the country. if country == NULL, use the default country ...
static int load_indications(int reload)
load indications module
void ast_deactivate_generator(struct ast_channel *chan)
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
static int ast_set_indication_country(const char *country)
Set global indication country If no country is specified or we are unable to find the zone...
Standard Command Line Interface.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
unsigned int nrringcadence
Number of ring cadence elements in the ringcadence array.
Data structure associated with a single frame of data.
const char * data
Description of a tone.
#define ast_tone_zone_lock(tz)
Lock an ast_tone_zone.
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
enum ast_frame_type frametype
struct ast_format * format
struct ast_tone_zone_sound * ast_get_indication_tone(const struct ast_tone_zone *_zone, const char *indication)
Locate a tone zone sound.
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
int ast_cli_completion_add(char *value)
Add a result to a request for completion options.
static void store_tone_zone_ring_cadence(struct ast_tone_zone *zone, const char *val)
#define ASTERISK_GPL_KEY
The text the key() function should return.
Asterisk module definitions.
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
struct ast_tone_zone::@232 tones
A list of tones for this locale.
static int ast_unregister_indication_country(const char *country)
remove an existing country and all its indications, country must exist.
static force_inline int attribute_pure ast_str_case_hash(const char *str)
Compute a hash value on a case-insensitive string.
#define ao2_link(container, obj)
Add an object to a container.