1 /* libSoX file format: Grandstream ring tone (c) 2009 robs@users.sourceforge.net
2  *
3  * See https://web.archive.org/web/20101128121923/http://grandstream.com/ringtone.html
4  *
5  * This library is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation; either version 2.1 of the License, or (at
8  * your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this library; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 
20 #include "sox_i.h"
21 #include <time.h>
22 
23 #define VERSION_      0x1000000
24 #define MAX_FILE_SIZE 0x10000
25 #define HEADER_SIZE   (size_t)512
26 #define PADDING_SIZE  (size_t)478
27 
28 static char const id[16] = "ring.bin";
29 
30 typedef struct {
31   char const *    string;
32   int             ft_encoding;
33   unsigned        bits_per_sample;
34   sox_encoding_t  sox_encoding;
35 } table_t;
36 
37 static table_t const table[] = {
38   {NULL,    0,    8, SOX_ENCODING_ULAW},
39   {"G726",  2,    0, SOX_ENCODING_UNKNOWN},
40   {NULL,    3,    0, SOX_ENCODING_GSM},
41   {NULL,    4,    0, SOX_ENCODING_G723},
42   {NULL,    8,    8, SOX_ENCODING_ALAW},
43   {"G722",  9,    0, SOX_ENCODING_UNKNOWN},
44   {"G728", 15,    2, SOX_ENCODING_UNKNOWN},
45   {"iLBC", 98,    0, SOX_ENCODING_UNKNOWN},
46 };
47 
ft_enc(unsigned bits_per_sample,sox_encoding_t encoding)48 static int ft_enc(unsigned bits_per_sample, sox_encoding_t encoding)
49 {
50   size_t i;
51   for (i = 0; i < array_length(table); ++i) {
52     table_t const * t = &table[i];
53     if (t->sox_encoding == encoding && t->bits_per_sample == bits_per_sample)
54       return t->ft_encoding;
55   }
56   return -1; /* Should never get here. */
57 }
58 
sox_enc(int ft_encoding,unsigned * bits_per_sample)59 static sox_encoding_t sox_enc(int ft_encoding, unsigned * bits_per_sample)
60 {
61   size_t i;
62   for (i = 0; i < array_length(table); ++i) {
63     table_t const * t = &table[i];
64     if (t->ft_encoding == ft_encoding) {
65       *bits_per_sample = t->bits_per_sample;
66       if (t->sox_encoding == SOX_ENCODING_UNKNOWN)
67         lsx_report("unsupported encoding: %s", t->string);
68       return t->sox_encoding;
69     }
70   }
71   *bits_per_sample = 0;
72   return SOX_ENCODING_UNKNOWN;
73 }
74 
start_read(sox_format_t * ft)75 static int start_read(sox_format_t * ft)
76 {
77   off_t num_samples;
78   char read_id[array_length(id)];
79   uint32_t file_size;
80   int16_t ft_encoding;
81   sox_encoding_t encoding;
82   unsigned bits_per_sample;
83 
84   lsx_readdw(ft, &file_size);
85   num_samples = file_size? file_size * 2 - HEADER_SIZE : SOX_UNSPEC;
86 
87   if (file_size >= 2 && ft->seekable) {
88     int i, checksum = (file_size >> 16) + file_size;
89     for (i = file_size - 2; i; --i) {
90       int16_t int16;
91       lsx_readsw(ft, &int16);
92       checksum += int16;
93     }
94     if (lsx_seeki(ft, (off_t)sizeof(file_size), SEEK_SET) != 0)
95       return SOX_EOF;
96     if (checksum & 0xffff)
97       lsx_warn("invalid checksum in input file %s", ft->filename);
98   }
99 
100   lsx_skipbytes(ft, (size_t)(2 + 4 + 6)); /* Checksum, version, time stamp. */
101 
102   lsx_readchars(ft, read_id, sizeof(read_id));
103   if (memcmp(read_id, id, strlen(id))) {
104     lsx_fail_errno(ft, SOX_EHDR, "gsrt: invalid file name in header");
105     return SOX_EOF;
106   }
107 
108   lsx_readsw(ft, &ft_encoding);
109   encoding = sox_enc(ft_encoding, &bits_per_sample);
110   if (encoding != SOX_ENCODING_ALAW &&
111       encoding != SOX_ENCODING_ULAW)
112     ft->handler.read = NULL;
113 
114   lsx_skipbytes(ft, PADDING_SIZE);
115 
116   return lsx_check_read_params(ft, 1, 8000., encoding,
117       bits_per_sample, (uint64_t)num_samples, sox_true);
118 }
119 
start_write(sox_format_t * ft)120 static int start_write(sox_format_t * ft)
121 {
122   int i, encoding = ft_enc(ft->encoding.bits_per_sample, ft->encoding.encoding);
123   time_t now = sox_globals.repeatable? 0 : time(NULL);
124   struct tm const * t = sox_globals.repeatable? gmtime(&now) : localtime(&now);
125 
126   int checksum = (VERSION_ >> 16) + VERSION_;
127   checksum += t->tm_year + 1900;
128   checksum += ((t->tm_mon + 1) << 8) + t->tm_mday;
129   checksum += (t->tm_hour << 8) + t->tm_min;
130   for (i = sizeof(id) - 2; i >= 0; i -= 2)
131     checksum += (id[i] << 8) + id[i + 1];
132   checksum += encoding;
133 
134   return lsx_writedw(ft, 0)
135       || lsx_writesw(ft, -checksum)
136       || lsx_writedw(ft, VERSION_)
137       || lsx_writesw(ft, t->tm_year + 1900)
138       || lsx_writesb(ft, t->tm_mon + 1)
139       || lsx_writesb(ft, t->tm_mday)
140       || lsx_writesb(ft, t->tm_hour)
141       || lsx_writesb(ft, t->tm_min)
142       || lsx_writechars(ft, id, sizeof(id))
143       || lsx_writesw(ft, encoding)
144       || lsx_padbytes(ft, PADDING_SIZE) ? SOX_EOF : SOX_SUCCESS;
145 }
146 
write_samples(sox_format_t * ft,sox_sample_t const * buf,size_t nsamp)147 static size_t write_samples(
148     sox_format_t * ft, sox_sample_t const * buf, size_t nsamp)
149 {
150   size_t n = min(nsamp, MAX_FILE_SIZE - (size_t)ft->tell_off);
151   if (n != nsamp)
152     lsx_warn("audio truncated");
153   return lsx_rawwrite(ft, buf, n);
154 }
155 
stop_write(sox_format_t * ft)156 static int stop_write(sox_format_t * ft)
157 {
158   long num_samples = ft->tell_off - HEADER_SIZE;
159 
160   if (num_samples & 1) {
161     sox_sample_t pad = 0;
162     lsx_rawwrite(ft, &pad, 1);
163   }
164 
165   if (ft->seekable) {
166     unsigned i, file_size = ft->tell_off >> 1;
167     int16_t int16;
168     int checksum;
169     if (!lsx_seeki(ft, (off_t)sizeof(uint32_t), SEEK_SET)) {
170       lsx_readsw(ft, &int16);
171       checksum = (file_size >> 16) + file_size - int16;
172       if (!lsx_seeki(ft, (off_t)HEADER_SIZE, SEEK_SET)) {
173         for (i = (num_samples + 1) >> 1; i; --i) {
174           lsx_readsw(ft, &int16);
175           checksum += int16;
176         }
177         if (!lsx_seeki(ft, (off_t)0, SEEK_SET)) {
178           lsx_writedw(ft, file_size);
179           lsx_writesw(ft, -checksum);
180           return SOX_SUCCESS;
181         }
182       }
183     }
184   }
185   lsx_warn("can't seek in output file `%s'; "
186       "length in file header will be unspecified", ft->filename);
187   return SOX_SUCCESS;
188 }
189 
LSX_FORMAT_HANDLER(gsrt)190 LSX_FORMAT_HANDLER(gsrt)
191 {
192   static char const *const names[] = { "gsrt", NULL };
193   static sox_rate_t const write_rates[] = { 8000, 0 };
194   static unsigned const write_encodings[] = {
195     SOX_ENCODING_ALAW, 8, 0,
196     SOX_ENCODING_ULAW, 8, 0,
197     0
198   };
199   static sox_format_handler_t const handler = {
200     SOX_LIB_VERSION_CODE, "Grandstream ring tone",
201     names, SOX_FILE_BIG_END | SOX_FILE_MONO,
202     start_read, lsx_rawread, NULL,
203     start_write, write_samples, stop_write,
204     lsx_rawseek, write_encodings, write_rates, 0
205   };
206   return &handler;
207 }
208