1 /* libSoX file format: Grandstream ring tone (c) 2009 robs@users.sourceforge.net
3 * See https://web.archive.org/web/20101128121923/http://grandstream.com/ringtone.html
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.
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.
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
23 #define VERSION_ 0x1000000
24 #define MAX_FILE_SIZE 0x10000
25 #define HEADER_SIZE (size_t)512
26 #define PADDING_SIZE (size_t)478
28 static char const id
[16] = "ring.bin";
33 unsigned bits_per_sample
;
34 sox_encoding_t sox_encoding
;
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
},
48 static int ft_enc(unsigned bits_per_sample
, sox_encoding_t encoding
)
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
;
56 return -1; /* Should never get here. */
59 static sox_encoding_t
sox_enc(int ft_encoding
, unsigned * bits_per_sample
)
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
;
72 return SOX_ENCODING_UNKNOWN
;
75 static int start_read(sox_format_t
* ft
)
78 char read_id
[array_length(id
)];
81 sox_encoding_t encoding
;
82 unsigned bits_per_sample
;
84 lsx_readdw(ft
, &file_size
);
85 num_samples
= file_size
? file_size
* 2 - HEADER_SIZE
: SOX_UNSPEC
;
87 if (file_size
>= 2 && ft
->seekable
) {
88 int i
, checksum
= (file_size
>> 16) + file_size
;
89 for (i
= file_size
- 2; i
; --i
) {
91 lsx_readsw(ft
, &int16
);
94 if (lsx_seeki(ft
, (off_t
)sizeof(file_size
), SEEK_SET
) != 0)
96 if (checksum
& 0xffff)
97 lsx_warn("invalid checksum in input file %s", ft
->filename
);
100 lsx_skipbytes(ft
, (size_t)(2 + 4 + 6)); /* Checksum, version, time stamp. */
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");
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
;
114 lsx_skipbytes(ft
, PADDING_SIZE
);
116 return lsx_check_read_params(ft
, 1, 8000., encoding
,
117 bits_per_sample
, (uint64_t)num_samples
, sox_true
);
120 static int start_write(sox_format_t
* ft
)
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
);
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
;
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
;
147 static size_t write_samples(
148 sox_format_t
* ft
, sox_sample_t
const * buf
, size_t nsamp
)
150 size_t n
= min(nsamp
, MAX_FILE_SIZE
- (size_t)ft
->tell_off
);
152 lsx_warn("audio truncated");
153 return lsx_rawwrite(ft
, buf
, n
);
156 static int stop_write(sox_format_t
* ft
)
158 long num_samples
= ft
->tell_off
- HEADER_SIZE
;
160 if (num_samples
& 1) {
161 sox_sample_t pad
= 0;
162 lsx_rawwrite(ft
, &pad
, 1);
166 unsigned i
, file_size
= ft
->tell_off
>> 1;
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
);
177 if (!lsx_seeki(ft
, (off_t
)0, SEEK_SET
)) {
178 lsx_writedw(ft
, file_size
);
179 lsx_writesw(ft
, -checksum
);
185 lsx_warn("can't seek in output file `%s'; "
186 "length in file header will be unspecified", ft
->filename
);
190 LSX_FORMAT_HANDLER(gsrt
)
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,
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