/* * Tone generation * * Copyright Andreas Eversberg (jolly@eversberg.eu) * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * */ #include #include #include #include #include #include #include #include #include #include "../liblogging/logging.h" #include #include #include "tones.h" #define DATA_S sample_silence #define SIZE_S (&sizeof_silence) #define DATA_GA sample_german_all #define SIZE_GA (&sizeof_german_all) #define DATA_GO sample_german_old #define SIZE_GO (&sizeof_german_old) #define DATA_DT sample_american_dialtone #define SIZE_DT (&sizeof_american_dialtone) #define DATA_RI sample_american_ringing #define SIZE_RI (&sizeof_american_ringing) #define DATA_BU sample_american_busy #define SIZE_BU (&sizeof_american_busy) #define DATA_S1 sample_special1 #define SIZE_S1 (&sizeof_special1) #define DATA_S2 sample_special2 #define SIZE_S2 (&sizeof_special2) #define DATA_S3 sample_special3 #define SIZE_S3 (&sizeof_special3) /***************/ /* tones loops */ /***************/ /* all tones are alaw encoded */ /* the last sample+1 is in phase with the first sample. the error is low */ static uint8_t sample_german_all[] = { 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, 0xdc, 0xfc, 0x6c, 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, 0xdc, 0xfc, 0x6c, 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, 0xdc, 0xfc, 0x6c, 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, 0xdc, 0xfc, 0x6c, }; static int sizeof_german_all = sizeof(sample_german_all); static uint8_t sample_german_old[] = { 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, 0x8c, 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, 0x8c, 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, 0x8c, 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, 0x8c, }; static int sizeof_german_old = sizeof(sample_german_old); static uint8_t sample_american_dialtone[] = { 0x2a, 0x18, 0x90, 0x6c, 0x4c, 0xbc, 0x4c, 0x6c, 0x10, 0x58, 0x32, 0xb9, 0x31, 0x2d, 0x8d, 0x0d, 0x8d, 0x2d, 0x31, 0x99, 0x0f, 0x28, 0x60, 0xf0, 0xd0, 0x50, 0xd0, 0x30, 0x60, 0x08, 0x8e, 0x67, 0x09, 0x19, 0x21, 0xe1, 0xd9, 0xb9, 0x29, 0x67, 0x83, 0x02, 0xce, 0xbe, 0xee, 0x1a, 0x1b, 0xef, 0xbf, 0xcf, 0x03, 0x82, 0x66, 0x28, 0xb8, 0xd8, 0xe0, 0x20, 0x18, 0x08, 0x66, 0x8f, 0x09, 0x61, 0x31, 0xd1, 0x51, 0xd1, 0xf1, 0x61, 0x29, 0x0e, 0x98, 0x30, 0x2c, 0x8c, 0x0c, 0x8c, 0x2c, 0x30, 0xb8, 0x33, 0x59, 0x11, 0x6d, 0x4d, 0xbd, 0x4d, 0x6d, 0x91, 0x19, }; static int sizeof_american_dialtone = sizeof(sample_american_dialtone); static uint8_t sample_american_ringing[] = { 0x2a, 0xe0, 0xac, 0x0c, 0xbc, 0x4c, 0x8c, 0x90, 0x48, 0xc7, 0xc1, 0xed, 0xcd, 0x4d, 0xcd, 0xed, 0xc1, 0xb7, 0x08, 0x30, 0xec, 0xcc, 0xcc, 0x8c, 0x10, 0x58, 0x1a, 0x99, 0x71, 0xed, 0x8d, 0x8d, 0x2d, 0x41, 0x89, 0x9e, 0x20, 0x70, 0x2c, 0xec, 0x2c, 0x70, 0x20, 0x86, 0x77, 0xe1, 0x31, 0x11, 0xd1, 0xf1, 0x81, 0x09, 0xa3, 0x56, 0x58, 0x00, 0x40, 0xc0, 0x60, 0x38, 0x46, 0x43, 0x57, 0x39, 0xd9, 0x59, 0x99, 0xc9, 0x77, 0x2f, 0x2e, 0xc6, 0xd6, 0x28, 0xd6, 0x36, 0x26, 0x2e, 0x8a, 0xa3, 0x43, 0x63, 0x4b, 0x4a, 0x62, 0x42, 0xa2, 0x8b, 0x2f, 0x27, 0x37, 0xd7, 0x29, 0xd7, 0xc7, 0x2f, 0x2e, 0x76, 0xc8, 0x98, 0x58, 0xd8, 0x38, 0x56, 0x42, 0x47, 0x39, 0x61, 0xc1, 0x41, 0x01, 0x59, 0x57, 0xa2, 0x08, 0x80, 0xf0, 0xd0, 0x10, 0x30, 0xe0, 0x76, 0x87, 0x21, 0x71, 0x2d, 0xed, 0x2d, 0x71, 0x21, 0x9f, 0x88, 0x40, 0x2c, 0x8c, 0x8c, 0xec, 0x70, 0x98, 0x1b, 0x59, 0x11, 0x8d, 0xcd, 0xcd, 0xed, 0x31, 0x09, 0xb6, 0xc0, 0xec, 0xcc, 0x4c, 0xcc, 0xec, 0xc0, 0xc6, 0x49, 0x91, 0x8d, 0x4d, 0xbd, 0x0d, 0xad, 0xe1, }; static int sizeof_american_ringing = sizeof(sample_american_ringing); static uint8_t sample_american_busy[] = { 0x2a, 0x00, 0x6c, 0x4c, 0x4c, 0x6c, 0xb0, 0x66, 0x99, 0x11, 0x6d, 0x8d, 0x2d, 0x41, 0xd7, 0x96, 0x60, 0xf0, 0x70, 0x40, 0x58, 0xf6, 0x53, 0x57, 0x09, 0x89, 0xd7, 0x5f, 0xe3, 0x2a, 0xe3, 0x5f, 0xd7, 0x89, 0x09, 0x57, 0x53, 0xf6, 0x58, 0x40, 0x70, 0xf0, 0x60, 0x96, 0xd7, 0x41, 0x2d, 0x8d, 0x6d, 0x11, 0x99, 0x66, 0xb0, 0x6c, 0x4c, 0x4c, 0x6c, 0x00, 0x2a, 0x01, 0x6d, 0x4d, 0x4d, 0x6d, 0xb1, 0x67, 0x98, 0x10, 0x6c, 0x8c, 0x2c, 0x40, 0xd6, 0x97, 0x61, 0xf1, 0x71, 0x41, 0x59, 0xf7, 0x52, 0x56, 0x08, 0x88, 0xd6, 0x5e, 0xe2, 0x2a, 0xe2, 0x5e, 0xd6, 0x88, 0x08, 0x56, 0x52, 0xf7, 0x59, 0x41, 0x71, 0xf1, 0x61, 0x97, 0xd6, 0x40, 0x2c, 0x8c, 0x6c, 0x10, 0x98, 0x67, 0xb1, 0x6d, 0x4d, 0x4d, 0x6d, 0x01, }; static int sizeof_american_busy = sizeof(sample_american_busy); static uint8_t sample_special1[] = { 0x2a, 0x2c, 0xbc, 0x6c, 0xd6, 0x71, 0xbd, 0x0d, 0xd9, 0x80, 0xcc, 0x4c, 0x40, 0x39, 0x0d, 0xbd, 0x11, 0x86, 0xec, 0xbc, 0xec, 0x0e, 0x51, 0xbd, 0x8d, 0x89, 0x30, 0x4c, 0xcc, 0xe0, 0xe1, 0xcd, 0x4d, 0x31, 0x88, 0x8c, 0xbc, 0x50, 0x0f, 0xed, 0xbd, 0xed, 0x87, 0x10, 0xbc, 0x0c, 0x38, 0x41, 0x4d, 0xcd, 0x81, 0xd8, 0x0c, 0xbc, 0x70, 0xd7, 0x6d, 0xbd, 0x2d, }; static int sizeof_special1 = sizeof(sample_special1); static uint8_t sample_special2[] = { 0x2a, 0xcc, 0x8c, 0xd7, 0x4d, 0x2d, 0x18, 0xbc, 0x10, 0xc1, 0xbd, 0xc1, 0x10, 0xbc, 0x18, 0x2d, 0x4d, 0xd7, 0x8c, 0xcc, 0x2a, 0xcd, 0x8d, 0xd6, 0x4c, 0x2c, 0x19, 0xbd, 0x11, 0xc0, 0xbc, 0xc0, 0x11, 0xbd, 0x19, 0x2c, 0x4c, 0xd6, 0x8d, 0xcd, 0x2a, 0xcc, 0x8c, 0xd7, 0x4d, 0x2d, 0x18, 0xbc, 0x10, 0xc1, 0xbd, 0xc1, 0x10, 0xbc, 0x18, 0x2d, 0x4d, 0xd7, 0x8c, 0xcc, 0x2a, 0xcd, 0x8d, 0xd6, 0x4c, 0x2c, 0x19, 0xbd, 0x11, 0xc0, 0xbc, 0xc0, 0x11, 0xbd, 0x19, 0x2c, 0x4c, 0xd6, 0x8d, 0xcd, }; static int sizeof_special2 = sizeof(sample_special2); static uint8_t sample_special3[] = { 0x2a, 0xbc, 0x18, 0xcd, 0x11, 0x2c, 0x8c, 0xc1, 0x4d, 0xd6, 0xbc, 0xd6, 0x4d, 0xc1, 0x8c, 0x2c, 0x11, 0xcd, 0x18, 0xbc, 0x2a, 0xbd, 0x19, 0xcc, 0x10, 0x2d, 0x8d, 0xc0, 0x4c, 0xd7, 0xbd, 0xd7, 0x4c, 0xc0, 0x8d, 0x2d, 0x10, 0xcc, 0x19, 0xbd, 0x2a, 0xbc, 0x18, 0xcd, 0x11, 0x2c, 0x8c, 0xc1, 0x4d, 0xd6, 0xbc, 0xd6, 0x4d, 0xc1, 0x8c, 0x2c, 0x11, 0xcd, 0x18, 0xbc, 0x2a, 0xbd, 0x19, 0xcc, 0x10, 0x2d, 0x8d, 0xc0, 0x4c, 0xd7, 0xbd, 0xd7, 0x4c, 0xc0, 0x8d, 0x2d, 0x10, 0xcc, 0x19, 0xbd, }; static int sizeof_special3 = sizeof(sample_special3); static uint8_t sample_silence[] = { 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, }; static int sizeof_silence = sizeof(sample_silence); struct tones_samples { int *len; uint8_t *data; }; static struct tones_samples samples[] = { {&sizeof_german_all, sample_german_all}, {&sizeof_german_old, sample_german_old}, {&sizeof_american_dialtone, sample_american_dialtone}, {&sizeof_american_ringing, sample_american_ringing}, {&sizeof_american_busy, sample_american_busy}, {&sizeof_special1, sample_special1}, {&sizeof_special2, sample_special2}, {&sizeof_special3, sample_special3}, {NULL, NULL}, }; /*********************************** * generate ulaw from alaw samples * ***********************************/ void isdn_tone_generate_ulaw_samples(void) { int i; int16_t *audio; int audio_len; uint8_t *data; int len; i = 0; while (samples[i].len) { g711_decode_alaw_flipped(samples[i].data, *samples[i].len, (uint8_t **)&audio, &audio_len, NULL); g711_encode_ulaw_flipped((uint8_t *)audio, audio_len, &data, &len, NULL); memcpy(samples[i].data, data, len); free(audio); free(data); i++; } } /**************************** * tone sequence definition * ****************************/ static struct pattern { int tone; uint8_t *data[10]; int *siz[10]; int seq[10]; } pattern[] = { {TONE_GERMAN_DIALTONE, {DATA_GA, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {SIZE_GA, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {1900, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, {TONE_GERMAN_OLDDIALTONE, {DATA_GO, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {SIZE_GO, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {7956, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, {TONE_AMERICAN_DIALTONE, {DATA_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {SIZE_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {8008, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, {TONE_GERMAN_DIALPBX, {DATA_GA, DATA_S, DATA_GA, DATA_S, DATA_GA, DATA_S, NULL, NULL, NULL, NULL}, {SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL}, {2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} }, {TONE_GERMAN_OLDDIALPBX, {DATA_GO, DATA_S, DATA_GO, DATA_S, DATA_GO, DATA_S, NULL, NULL, NULL, NULL}, {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL}, {2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} }, {TONE_AMERICAN_DIALPBX, {DATA_DT, DATA_S, DATA_DT, DATA_S, DATA_DT, DATA_S, NULL, NULL, NULL, NULL}, {SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, NULL, NULL, NULL, NULL}, {2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} }, {TONE_GERMAN_RINGING, {DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} }, {TONE_GERMAN_OLDRINGING, {DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {8000, 40000, 0, 0, 0, 0, 0, 0, 0, 0} }, {TONE_AMERICAN_RINGING, {DATA_RI, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {SIZE_RI, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} }, {TONE_GERMAN_RINGPBX, {DATA_GA, DATA_S, DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL}, {SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL}, {4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} }, {TONE_GERMAN_OLDRINGPBX, {DATA_GO, DATA_S, DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL}, {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL}, {4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} }, {TONE_AMERICAN_RINGPBX, {DATA_RI, DATA_S, DATA_RI, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL}, {SIZE_RI, SIZE_S, SIZE_RI, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL}, {4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} }, {TONE_GERMAN_BUSY, {DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} }, {TONE_GERMAN_OLDBUSY, {DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} }, {TONE_AMERICAN_BUSY, {DATA_BU, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {SIZE_BU, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} }, {TONE_GERMAN_HANGUP, {DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} }, {TONE_GERMAN_OLDHANGUP, {DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} }, {TONE_AMERICAN_HANGUP, {DATA_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {SIZE_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, {TONE_SPECIAL_INFO, {DATA_S1, DATA_S2, DATA_S3, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL}, {SIZE_S1, SIZE_S2, SIZE_S3, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL}, {2666, 2666, 2666, 8002, 0, 0, 0, 0, 0, 0} }, {TONE_GERMAN_GASSENBESETZT, {DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {2000, 2000, 0, 0, 0, 0, 0, 0, 0, 0} }, {TONE_GERMAN_AUFSCHALTTON, {DATA_GO, DATA_S, DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL}, {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL}, {1000, 5000, 1000, 17000, 0, 0, 0, 0, 0, 0} }, {0, {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, }; /****************** * copy tone data * ******************/ /* the count will be changed and may begin from 0 each pattern period. * the clue is to precalculate the pointers and legths to use only one * memcpy per function call, or two memcpy if the tone sequence changes. * * pattern - the type of the pattern * count - the sample from the beginning of the pattern (phase) * len - the number of bytes */ void isdn_tone_copy(struct isdn_tone *t, uint8_t *data, int len) { int index, count, start, num; struct pattern *pat; /* if we have no tone, we do not overwrite data */ if (!t->tone) return; /* process pattern */ pat = (struct pattern *)t->pattern; /* points to the current pattern */ index = t->index; /* gives current sequence index */ count = t->count; /* gives current sample */ /* copy sample */ while (len) { /* find sample to start with */ while (42) { /* wrap around */ if (!pat->seq[index]) { count = 0; index = 0; } /* check if we are currently playing this tone */ if (count < pat->seq[index]) break; count -= pat->seq[index]; index++; } /* calculate start and number of samples */ start = count % (*(pat->siz[index])); num = len; if (num + count > pat->seq[index]) num = pat->seq[index] - count; if (num + start > (*(pat->siz[index]))) num = (*(pat->siz[index])) - start; /* copy memory */ memcpy(data, pat->data[index] + start, num); /* reduce length */ data += num; count += num; len -= num; } t->index = index; t->count = count; } /******************** * set/release tone * ********************/ int isdn_tone_set(struct isdn_tone *t, int tone) { struct pattern *pat; int i; /* we turn off the tone */ if (!tone) { t->tone = 0; return 0; } pat = NULL; i = 0; while (pattern[i].tone) { if (pattern[i].tone == tone) { pat = &pattern[i]; break; } i++; } if (!pat) { LOGP(DISDN, LOGL_ERROR, "given tone 0x%x is invalid\n", tone); return -EINVAL; } LOGP(DISDN, LOGL_DEBUG, "playing given tone 0x%x\n", tone); t->tone = tone; t->pattern = pat; t->index = 0; t->count = 0; return 0; }