1 /* libSoX NIST Sphere file format handler.
2  *
3  * August 7, 2000
4  *
5  * Copyright (C) 2000 Chris Bagwell (cbagwell@sprynet.com)
6  *
7  * This library is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as published by
9  * the Free Software Foundation; either version 2.1 of the License, or (at
10  * your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this library; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21 
22 #include "sox_i.h"
23 #include <string.h>
24 
start_read(sox_format_t * ft)25 static int start_read(sox_format_t * ft)
26 {
27   unsigned long header_size_ul = 0, num_samples_ul = 0;
28   sox_encoding_t encoding = SOX_ENCODING_SIGN2;
29   size_t     header_size, bytes_read;
30   size_t     num_samples = 0;
31   unsigned       bytes_per_sample = 0;
32   unsigned       channels = 1;
33   unsigned       rate = 16000;
34   char           fldname[64], fldtype[16], fldsval[128];
35   char           * buf;
36 
37   /* Magic header */
38   if (lsx_reads(ft, fldname, (size_t)8) || strncmp(fldname, "NIST_1A", (size_t)7) != 0) {
39     lsx_fail_errno(ft, SOX_EHDR, "Sphere header does not begin with magic word `NIST_1A'");
40     return (SOX_EOF);
41   }
42 
43   if (lsx_reads(ft, fldsval, (size_t)8)) {
44     lsx_fail_errno(ft, SOX_EHDR, "Error reading Sphere header");
45     return (SOX_EOF);
46   }
47 
48   /* Determine header size, and allocate a buffer large enough to hold it. */
49   sscanf(fldsval, "%lu", &header_size_ul);
50   if (header_size_ul < 16) {
51     lsx_fail_errno(ft, SOX_EHDR, "Error reading Sphere header");
52     return (SOX_EOF);
53   }
54 
55   buf = lsx_malloc(header_size = header_size_ul);
56 
57   /* Skip what we have read so far */
58   header_size -= 16;
59 
60   if (lsx_reads(ft, buf, header_size) == SOX_EOF) {
61     lsx_fail_errno(ft, SOX_EHDR, "Error reading Sphere header");
62     free(buf);
63     return (SOX_EOF);
64   }
65 
66   header_size -= (strlen(buf) + 1);
67 
68   while (strncmp(buf, "end_head", (size_t)8) != 0) {
69     if (strncmp(buf, "sample_n_bytes", (size_t)14) == 0)
70       sscanf(buf, "%63s %15s %u", fldname, fldtype, &bytes_per_sample);
71     else if (strncmp(buf, "channel_count", (size_t)13) == 0)
72       sscanf(buf, "%63s %15s %u", fldname, fldtype, &channels);
73     else if (strncmp(buf, "sample_count ", (size_t)13) == 0)
74       sscanf(buf, "%53s %15s %lu", fldname, fldtype, &num_samples_ul);
75     else if (strncmp(buf, "sample_rate ", (size_t)12) == 0)
76       sscanf(buf, "%53s %15s %u", fldname, fldtype, &rate);
77     else if (strncmp(buf, "sample_coding", (size_t)13) == 0) {
78       sscanf(buf, "%63s %15s %127s", fldname, fldtype, fldsval);
79       if (!strcasecmp(fldsval, "ulaw") || !strcasecmp(fldsval, "mu-law"))
80         encoding = SOX_ENCODING_ULAW;
81       else if (!strcasecmp(fldsval, "pcm"))
82         encoding = SOX_ENCODING_SIGN2;
83       else {
84         lsx_fail_errno(ft, SOX_EFMT, "sph: unsupported coding `%s'", fldsval);
85         free(buf);
86         return SOX_EOF;
87       }
88     }
89     else if (strncmp(buf, "sample_byte_format", (size_t)18) == 0) {
90       sscanf(buf, "%53s %15s %127s", fldname, fldtype, fldsval);
91       if (strcmp(fldsval, "01") == 0)         /* Data is little endian. */
92         ft->encoding.reverse_bytes = MACHINE_IS_BIGENDIAN;
93       else if (strcmp(fldsval, "10") == 0)    /* Data is big endian. */
94         ft->encoding.reverse_bytes = MACHINE_IS_LITTLEENDIAN;
95       else if (strcmp(fldsval, "1")) {
96         lsx_fail_errno(ft, SOX_EFMT, "sph: unsupported coding `%s'", fldsval);
97         free(buf);
98         return SOX_EOF;
99       }
100     }
101 
102     if (lsx_reads(ft, buf, header_size) == SOX_EOF) {
103       lsx_fail_errno(ft, SOX_EHDR, "Error reading Sphere header");
104       free(buf);
105       return (SOX_EOF);
106     }
107 
108     header_size -= (strlen(buf) + 1);
109   }
110 
111   if (!bytes_per_sample)
112     bytes_per_sample = encoding == SOX_ENCODING_ULAW? 1 : 2;
113 
114   while (header_size) {
115     bytes_read = lsx_readbuf(ft, buf, header_size);
116     if (bytes_read == 0) {
117       free(buf);
118       return (SOX_EOF);
119     }
120     header_size -= bytes_read;
121   }
122   free(buf);
123 
124   if (ft->seekable) {
125     /* Check first four bytes of data to see if it's shorten compressed. */
126     char           shorten_check[4];
127 
128     if (lsx_readchars(ft, shorten_check, sizeof(shorten_check)))
129       return SOX_EOF;
130     lsx_seeki(ft, -(off_t)sizeof(shorten_check), SEEK_CUR);
131 
132     if (!memcmp(shorten_check, "ajkg", sizeof(shorten_check))) {
133       lsx_fail_errno(ft, SOX_EFMT,
134                      "File uses shorten compression, cannot handle this.");
135       return (SOX_EOF);
136     }
137   }
138 
139   num_samples = num_samples_ul;
140   return lsx_check_read_params(ft, channels, (sox_rate_t)rate, encoding,
141       bytes_per_sample << 3, (uint64_t)num_samples * channels, sox_true);
142 }
143 
write_header(sox_format_t * ft)144 static int write_header(sox_format_t * ft)
145 {
146   char buf[128];
147   uint64_t samples = (ft->olength ? ft->olength : ft->signal.length) / ft->signal.channels;
148 
149   lsx_writes(ft, "NIST_1A\n");
150   lsx_writes(ft, "   1024\n");
151 
152   if (samples) {
153     sprintf(buf, "sample_count -i %" PRIu64 "\n", samples);
154     lsx_writes(ft, buf);
155   }
156 
157   sprintf(buf, "sample_n_bytes -i %d\n", ft->encoding.bits_per_sample >> 3);
158   lsx_writes(ft, buf);
159 
160   sprintf(buf, "channel_count -i %d\n", ft->signal.channels);
161   lsx_writes(ft, buf);
162 
163   if (ft->encoding.bits_per_sample == 8)
164     sprintf(buf, "sample_byte_format -s1 1\n");
165   else
166     sprintf(buf, "sample_byte_format -s2 %s\n",
167             ft->encoding.reverse_bytes != MACHINE_IS_BIGENDIAN ? "10" : "01");
168   lsx_writes(ft, buf);
169 
170   sprintf(buf, "sample_rate -i %u\n", (unsigned) (ft->signal.rate + .5));
171   lsx_writes(ft, buf);
172 
173   if (ft->encoding.encoding == SOX_ENCODING_ULAW)
174     lsx_writes(ft, "sample_coding -s4 ulaw\n");
175   else
176     lsx_writes(ft, "sample_coding -s3 pcm\n");
177 
178   lsx_writes(ft, "end_head\n");
179 
180   lsx_padbytes(ft, 1024 - (size_t)lsx_tell(ft));
181   return SOX_SUCCESS;
182 }
183 
LSX_FORMAT_HANDLER(sphere)184 LSX_FORMAT_HANDLER(sphere)
185 {
186   static char const *const names[] = {"sph", "nist", NULL};
187   static unsigned const write_encodings[] = {
188     SOX_ENCODING_SIGN2, 8, 16, 24, 32, 0,
189     SOX_ENCODING_ULAW, 8, 0,
190     0
191   };
192   static sox_format_handler_t const handler = {SOX_LIB_VERSION_CODE,
193     "SPeech HEader Resources; defined by NIST", names, SOX_FILE_REWIND,
194     start_read, lsx_rawread, NULL,
195     write_header, lsx_rawwrite, NULL,
196     lsx_rawseek, write_encodings, NULL, 0
197   };
198   return &handler;
199 }
200