summaryrefslogtreecommitdiff
path: root/tonezone.c
diff options
context:
space:
mode:
authormarkster <markster@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2001-06-08 16:13:48 +0000
committermarkster <markster@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2001-06-08 16:13:48 +0000
commita2bbaa084edb6a669880368fc63c1cf62b5c4f42 (patch)
treedf9f944cf945fb546be284d54c00ac8c583d64d7 /tonezone.c
parentd0f5c4c6d5c4ac688a88b4369e8c8501eab78127 (diff)
Version 0.1.0 from FTP
git-svn-id: http://svn.digium.com/svn/zaptel/trunk@6 5390a7c7-147a-4af0-8ec9-7488f05a26cb
Diffstat (limited to 'tonezone.c')
-rwxr-xr-xtonezone.c412
1 files changed, 412 insertions, 0 deletions
diff --git a/tonezone.c b/tonezone.c
new file mode 100755
index 0000000..e601de9
--- /dev/null
+++ b/tonezone.c
@@ -0,0 +1,412 @@
+/*
+ * BSD Telephony Of Mexico "Tormenta" Tone Zone Support 2/22/01
+ *
+ * Working with the "Tormenta ISA" Card
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under thet erms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Primary Author: Mark Spencer <markster@linux-support.net>
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include "tonezone.h"
+
+#define DEFAULT_ZT_DEV "/dev/zap/ctl"
+
+#define MAX_SIZE 16384
+#define CLIP 32635
+#define BIAS 0x84
+
+static float loudness=8192.0;
+
+unsigned char
+linear2ulaw(sample)
+short sample; {
+ static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
+ int sign, exponent, mantissa;
+ unsigned char ulawbyte;
+
+ /* Get the sample into sign-magnitude. */
+ sign = (sample >> 8) & 0x80; /* set aside the sign */
+ if (sign != 0) sample = -sample; /* get magnitude */
+ if (sample > CLIP) sample = CLIP; /* clip the magnitude */
+
+ /* Convert from 16 bit linear to ulaw. */
+ sample = sample + BIAS;
+ exponent = exp_lut[(sample >> 7) & 0xFF];
+ mantissa = (sample >> (exponent + 3)) & 0x0F;
+ ulawbyte = ~(sign | (exponent << 4) | mantissa);
+#ifdef ZEROTRAP
+ if (ulawbyte == 0) ulawbyte = 0x02; /* optional CCITT trap */
+#endif
+
+ return(ulawbyte);
+}
+
+
+
+static int calc_samples(int freq)
+{
+ int x, samples;
+ /* Calculate the number of samples at 8000hz sampling
+ we need to have this wave form */
+ samples = 8000;
+ /* Take out common 2's up to six times */
+ for (x=0;x<6;x++)
+ if (!(freq % 2)) {
+ freq /= 2;
+ samples /= 2;
+ }
+ /* Take out common 5's (up to three times */
+ for (x=0;x<3;x++)
+ if (!(freq % 5)) {
+ freq /= 5;
+ samples /=5;
+ }
+ /* No more common factors. */
+ return samples;
+}
+
+
+
+struct tone_zone *tone_zone_find(char *country)
+{
+ struct tone_zone *z;
+ z = builtin_zones;
+ while(z->zone > -1) {
+ if (!strcasecmp(country, z->country))
+ return z;
+ z++;
+ }
+ return NULL;
+}
+
+struct tone_zone *tone_zone_find_by_num(int id)
+{
+ struct tone_zone *z;
+ z = builtin_zones;
+ while(z->zone > -1) {
+ if (z->zone == id)
+ return z;
+ z++;
+ }
+ return NULL;
+}
+
+static int build_tone(char *data, int size, struct tone_zone_sound *t, int *count)
+{
+ char *dup, *s;
+ struct zt_tone_def *td=NULL;
+ int firstnobang = -1;
+ int freq1, freq2, time;
+ int samples, samples1, samples2;
+ int x;
+ int used = 0;
+ float val;
+ dup = strdup(t->data);
+ s = strtok(dup, ",");
+ while(s && strlen(s)) {
+ /* Handle optional ! which signifies don't start here*/
+ if (s[0] == '!')
+ s++;
+ else if (firstnobang < 0) {
+#if 0
+ printf("First no bang: %s\n", s);
+#endif
+ firstnobang = *count;
+ }
+ if (sscanf(s, "%d+%d/%d", &freq1, &freq2, &time) == 3) {
+ /* f1+f2/time format */
+#if 0
+ printf("f1+f2/time format: %d, %d, %d\n", freq1, freq2, time);
+#endif
+ } else if (sscanf(s, "%d+%d", &freq1, &freq2) == 2) {
+#if 0
+ printf("f1+f2 format: %d, %d\n", freq1, freq2);
+#endif
+ time = 0;
+ } else if (sscanf(s, "%d/%d", &freq1, &time) == 2) {
+#if 0
+ printf("f1/time format: %d, %d\n", freq1, time);
+#endif
+ freq2 = 0;
+ } else if (sscanf(s, "%d", &freq1) == 1) {
+#if 0
+ printf("f1 format: %d\n", freq1);
+#endif
+ firstnobang = *count;
+ freq2 = 0;
+ time = 0;
+ } else {
+ fprintf(stderr, "tone component '%s' of '%s' is a syntax error\n", s,t->data);
+ return -1;
+ }
+ if (freq1)
+ samples1 = calc_samples(freq1);
+ else
+ samples1 = 40;
+ if (freq2)
+ samples2 = calc_samples(freq2);
+ else
+ samples2 = 0;
+ samples = samples1;
+ if (freq2) {
+ while(samples % samples2)
+ samples += samples1;
+ }
+#if 0
+ printf("Using %d samples for %d and %d\n", samples, freq1, freq2);
+#endif
+ if (size < samples + sizeof(struct zt_tone_def)) {
+ fprintf(stderr, "Not enough space for samples\n");
+ return -1;
+ }
+ td = (struct zt_tone_def *)data;
+ data += (sizeof(struct zt_tone_def) + samples);
+ used += (sizeof(struct zt_tone_def) + samples);
+ size -= (sizeof(struct zt_tone_def) + samples);
+ td->size = samples;
+ td->tone = t->toneid;
+ if (time) {
+ /* We should move to the next tone */
+ td->next = *count + 1;
+ td->samples = time * 8;
+ } else {
+ /* Stay with us */
+ td->next = *count;
+ td->samples = samples;
+ }
+ for (x=0;x<samples;x++) {
+ val = 0.0;
+ if (freq1)
+ val = loudness * sin((freq1 * 2.0 * M_PI * x)/8000.0);
+ if (freq2)
+ val += loudness * sin((freq2 * 2.0 * M_PI * x)/8000.0);
+ td->data[x] = linear2ulaw((int)val);
+ }
+ (*count)++;
+ s = strtok(NULL, ",");
+ }
+ if (td && time) {
+ /* If we don't end on a solid tone, return */
+ td->next = firstnobang;
+ }
+ return used;
+}
+
+char *tone_zone_tone_name(int id)
+{
+ static char tmp[80];
+ switch(id) {
+ case ZT_TONE_DIALTONE:
+ return "Dialtone";
+ case ZT_TONE_BUSY:
+ return "Busy";
+ case ZT_TONE_RINGTONE:
+ return "Ringtone";
+ case ZT_TONE_CONGESTION:
+ return "Congestion";
+ case ZT_TONE_CALLWAIT:
+ return "Call Waiting";
+ case ZT_TONE_DIALRECALL:
+ return "Dial Recall";
+ case ZT_TONE_RECORDTONE:
+ return "Record Tone";
+ case ZT_TONE_CUST1:
+ return "Custom 1";
+ case ZT_TONE_CUST2:
+ return "Custom 2";
+ case ZT_TONE_INFO:
+ return "Special Information";
+ default:
+ snprintf(tmp, sizeof(tmp), "Unknown tone %d", id);
+ return tmp;
+ }
+}
+
+#ifdef TONEZONE_DRIVER
+static void dump_tone_zone(void *data)
+{
+ struct zt_tone_def_header *z;
+ struct zt_tone_def *td;
+ int x;
+ int len=0;
+ z = data;
+ data += sizeof(*z);
+ printf("Header: %d tones, %d bytes of data, zone %d (%s)\n",
+ z->count, z->size, z->zone, z->name);
+ for (x=0;x < z->count; x++) {
+ td = data;
+ printf("Tone Fragment %d: %d bytes, %s tone, next is %d, %d samples total\n",
+ x, td->size, tone_name(td->tone), td->next, td->samples);
+ data += sizeof(*td);
+ data += td->size;
+ len += td->size;
+ }
+ printf("Total measured bytes of data: %d\n", len);
+}
+#endif
+
+int tone_zone_register_zone(int fd, struct tone_zone *z)
+{
+ char buf[MAX_SIZE];
+ int res;
+ int count=0;
+ int x;
+ int used = 0;
+ int iopenedit = 0;
+ int space = MAX_SIZE;
+ char *ptr = buf;
+ struct zt_tone_def_header *h;
+ if (fd < 0) {
+ fd = open(DEFAULT_ZT_DEV, O_RDWR);
+ iopenedit=1;
+ if (fd < 0) {
+ fprintf(stderr, "Unable to open %s and fd not provided\n", DEFAULT_ZT_DEV);
+ return -1;
+ }
+ }
+ h = (struct zt_tone_def_header *)ptr;
+ ptr += sizeof(struct zt_tone_def_header);
+ space -= sizeof(struct zt_tone_def_header);
+ used += sizeof(struct zt_tone_def_header);
+ /*
+ * Fill in ring cadence
+ */
+ for (x=0;x<ZT_MAX_CADENCE;x++)
+ h->ringcadence[x] = z->ringcadence[x];
+ /* Put in an appropriate method for a kernel ioctl */
+ for (x=0;x<ZT_TONE_MAX;x++) {
+ if (strlen(z->tones[x].data)) {
+ /* It's a real tone */
+#if 0
+ printf("Tone: %d, string: %s\n", z->tones[x].toneid, z->tones[x].data);
+#endif
+ res = build_tone(ptr, space, &z->tones[x], &count);
+ if (res < 0) {
+ fprintf(stderr, "Tone not built.\n");
+ if (iopenedit)
+ close(fd);
+ return -1;
+ }
+ ptr += res;
+ used += res;
+ space -= res;
+ }
+ }
+ h->count = count;
+ h->size = used - sizeof(struct zt_tone_def_header) - count * sizeof(struct zt_tone_def);
+ h->zone = z->zone;
+ strncpy(h->name, z->description, sizeof(h->name));
+ x = z->zone;
+ ioctl(fd, ZT_FREEZONE, &x);
+ res = ioctl(fd, ZT_LOADZONE, h);
+ if (res)
+ fprintf(stderr, "ioctl(ZT_LOADZONE) failed: %s\n", strerror(errno));
+ if (iopenedit)
+ close(fd);
+ return res;
+}
+
+int tone_zone_register(int fd, char *country)
+{
+ struct tone_zone *z;
+ z = tone_zone_find(country);
+ if (z) {
+ return tone_zone_register_zone(-1, z);
+ } else {
+ return -1;
+ }
+}
+
+
+
+int tone_zone_set_zone(int fd, char *country)
+{
+ int res=-1;
+ struct tone_zone *z;
+ if (fd > -1) {
+ z = tone_zone_find(country);
+ if (z)
+ res = ioctl(fd, ZT_SETTONEZONE, &z->zone);
+ if ((res < 0) && (errno == ENODATA)) {
+ tone_zone_register_zone(fd, z);
+ res = ioctl(fd, ZT_SETTONEZONE, &z->zone);
+ }
+ }
+ return res;
+}
+
+int tone_zone_get_zone(int fd)
+{
+ int x=-1;
+ if (fd > -1) {
+ ioctl(fd, ZT_GETTONEZONE, &x);
+ return x;
+ }
+ return -1;
+}
+
+int tone_zone_play_tone(int fd, int tone)
+{
+ struct tone_zone *z;
+ int res = -1;
+ int zone;
+#if 0
+ fprintf(stderr, "Playing tone %d (%s) on %d\n", tone, tone_zone_tone_name(tone), fd);
+#endif
+ if (fd > -1) {
+ res = ioctl(fd, ZT_SENDTONE, &tone);
+ if ((res < 0) && (errno == ENODATA)) {
+ ioctl(fd, ZT_GETTONEZONE, &zone);
+ z = tone_zone_find_by_num(zone);
+ if (z) {
+ res = tone_zone_register_zone(fd, z);
+ /* Recall the zone */
+ ioctl(fd, ZT_SETTONEZONE, &zone);
+ if (res < 0) {
+ fprintf(stderr, "Failed to register zone '%s': %s\n", z->description, strerror(errno));
+ } else {
+ res = ioctl(fd, ZT_SENDTONE, &tone);
+ }
+ } else
+ fprintf(stderr, "Don't know anything about zone %d\n", zone);
+ }
+ }
+ return res;
+}