summaryrefslogtreecommitdiff
path: root/apps/codecs/smaf.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/codecs/smaf.c')
-rw-r--r--apps/codecs/smaf.c348
1 files changed, 191 insertions, 157 deletions
diff --git a/apps/codecs/smaf.c b/apps/codecs/smaf.c
index 8349d394ac..33a2a4b403 100644
--- a/apps/codecs/smaf.c
+++ b/apps/codecs/smaf.c
@@ -32,8 +32,8 @@ CODEC_HEADER
32 */ 32 */
33 33
34enum { 34enum {
35 SMAF_TRACK_CHUNK_SCORE = 0, /* Score Track */ 35 SMAF_AUDIO_TRACK_CHUNK = 0, /* PCM Audio Track */
36 SMAF_TRACK_CHUNK_AUDIO, /* PCM Audio Track */ 36 SMAF_SCORE_TRACK_CHUNK, /* Score Track */
37}; 37};
38 38
39/* SMAF supported codec formats */ 39/* SMAF supported codec formats */
@@ -44,9 +44,9 @@ enum {
44 SMAF_FORMAT_ADPCM, /* YAMAHA ADPCM */ 44 SMAF_FORMAT_ADPCM, /* YAMAHA ADPCM */
45}; 45};
46 46
47static int support_formats[2][3] = { 47static const int support_formats[2][3] = {
48 {SMAF_FORMAT_SIGNED_PCM, SMAF_FORMAT_UNSIGNED_PCM, SMAF_FORMAT_ADPCM },
49 {SMAF_FORMAT_SIGNED_PCM, SMAF_FORMAT_ADPCM, SMAF_FORMAT_UNSUPPORT }, 48 {SMAF_FORMAT_SIGNED_PCM, SMAF_FORMAT_ADPCM, SMAF_FORMAT_UNSUPPORT },
49 {SMAF_FORMAT_SIGNED_PCM, SMAF_FORMAT_UNSIGNED_PCM, SMAF_FORMAT_ADPCM },
50}; 50};
51 51
52static const struct pcm_entry pcm_codecs[] = { 52static const struct pcm_entry pcm_codecs[] = {
@@ -57,7 +57,7 @@ static const struct pcm_entry pcm_codecs[] = {
57 57
58#define NUM_FORMATS 3 58#define NUM_FORMATS 3
59 59
60static int basebits[4] = { 4, 8, 12, 16 }; 60static const int basebits[4] = { 4, 8, 12, 16 };
61 61
62#define PCM_SAMPLE_SIZE (2048*2) 62#define PCM_SAMPLE_SIZE (2048*2)
63 63
@@ -79,183 +79,243 @@ static const struct pcm_codec *get_codec(uint32_t formattag)
79 return 0; 79 return 0;
80} 80}
81 81
82static unsigned int get_be32(uint8_t *buf) 82static unsigned int get_be32(const uint8_t *buf)
83{ 83{
84 return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; 84 return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
85} 85}
86 86
87static int convert_smaf_audio_format(int track_chunk, unsigned int audio_format) 87static int convert_smaf_channels(unsigned int ch)
88{
89 return (ch >> 7) + 1;
90}
91
92static int convert_smaf_audio_format(unsigned int chunk, unsigned int audio_format)
88{ 93{
89 if (audio_format > 3) 94 int idx = (audio_format & 0x70) >> 4;
90 return SMAF_FORMAT_UNSUPPORT; 95
96 if (idx < 3)
97 return support_formats[chunk][idx];
91 98
92 return support_formats[track_chunk][audio_format]; 99 DEBUGF("CODEC_ERROR: unsupport audio format: %d\n", audio_format);
100 return SMAF_FORMAT_UNSUPPORT;
93} 101}
94 102
95static int convert_smaf_audio_basebit(unsigned int basebit) 103static int convert_smaf_audio_basebit(unsigned int basebit)
96{ 104{
97 if (basebit > 4) 105 if (basebit < 4)
98 return 0; 106 return basebits[basebit];
99 return basebits[basebit]; 107
108 DEBUGF("CODEC_ERROR: illegal basebit: %d\n", basebit);
109 return 0;
110}
111
112static unsigned int search_chunk(const unsigned char *name, int nlen, off_t *pos)
113{
114 const unsigned char *buf;
115 unsigned int chunksize;
116 size_t size;
117
118 while (true)
119 {
120 buf = ci->request_buffer(&size, 8);
121 if (size < 8)
122 break;
123
124 chunksize = get_be32(buf + 4);
125 ci->advance_buffer(8);
126 *pos += 8;
127 if (memcmp(buf, name, nlen) == 0)
128 return chunksize;
129
130 ci->advance_buffer(chunksize);
131 *pos += chunksize;
132 }
133 DEBUGF("CODEC_ERROR: missing '%s' chunk\n", name);
134 return 0;
100} 135}
101 136
102static bool parse_audio_track(struct pcm_format *fmt, 137static bool parse_audio_track(struct pcm_format *fmt, unsigned int chunksize, off_t *pos)
103 unsigned char **stbuf, unsigned char *endbuf)
104{ 138{
105 unsigned char *buf = *stbuf; 139 const unsigned char *buf;
106 int chunksize; 140 size_t size;
107 141
108 buf += 8; 142 /* search PCM Audio Track Chunk */
109 fmt->channels = ((buf[2] & 0x80) >> 7) + 1; 143 ci->advance_buffer(chunksize);
110 fmt->formattag = convert_smaf_audio_format(SMAF_TRACK_CHUNK_AUDIO, 144 *pos += chunksize;
111 (buf[2] >> 4) & 0x07); 145 if (search_chunk("ATR", 3, pos) == 0)
112 if (fmt->formattag == SMAF_FORMAT_UNSUPPORT)
113 { 146 {
114 DEBUGF("CODEC_ERROR: unsupport pcm data format : %d\n", (buf[2] >> 4) & 0x07); 147 DEBUGF("CODEC_ERROR: missing PCM Audio Track Chunk\n");
115 return false; 148 return false;
116 } 149 }
117 fmt->bitspersample = convert_smaf_audio_basebit(buf[3] >> 4); 150
118 if (fmt->bitspersample == 0) 151 /*
152 * get format
153 * buf
154 * +0: Format Type
155 * +1: Sequence Type
156 * +2: bit 7 0:mono/1:stereo, bit 4-6 format, bit 0-3: frequency
157 * +3: bit 4-7: base bit
158 * +4: TimeBase_D
159 * +5: TimeBase_G
160 *
161 * Note: If PCM Audio Track does not include Sequence Data Chunk,
162 * tmp+6 is the start position of Wave Data Chunk.
163 */
164 buf = ci->request_buffer(&size, 6);
165 if (size < 6)
119 { 166 {
120 DEBUGF("CODEC_ERROR: unsupport pcm data basebit : %d\n", buf[3] >> 4); 167 DEBUGF("CODEC_ERROR: smaf is too small\n");
121 return false; 168 return false;
122 } 169 }
123 buf += 6; 170
124 while (buf < endbuf) 171 fmt->formattag = convert_smaf_audio_format(SMAF_AUDIO_TRACK_CHUNK, buf[2]);
172 fmt->channels = convert_smaf_channels(buf[2]);
173 fmt->bitspersample = convert_smaf_audio_basebit(buf[3] >> 4);
174
175 /* search Wave Data Chunk */
176 ci->advance_buffer(6);
177 *pos += 6;
178 fmt->numbytes = search_chunk("Awa", 3, pos);
179 if (fmt->numbytes == 0)
125 { 180 {
126 chunksize = get_be32(buf + 4) + 8; 181 DEBUGF("CODEC_ERROR: missing Wave Data Chunk\n");
127 if (memcmp(buf, "Awa", 3) == 0) 182 return false;
128 {
129 fmt->numbytes = get_be32(buf + 4);
130 buf += 8;
131 return true;
132 }
133 buf += chunksize;
134 } 183 }
135 DEBUGF("CODEC_ERROR: smaf does not include stream pcm data\n"); 184
136 return false; 185 return true;
137} 186}
138 187
139static bool parse_score_track(struct pcm_format *fmt, 188static bool parse_score_track(struct pcm_format *fmt, off_t *pos)
140 unsigned char **stbuf, unsigned char *endbuf)
141{ 189{
142 unsigned char *buf = *stbuf; 190 const unsigned char *buf;
143 int chunksize; 191 unsigned int chunksize;
192 size_t size;
193
194 /* parse Optional Data Chunk */
195 buf = ci->request_buffer(&size, 13);
196 if (size < 13)
197 {
198 DEBUGF("CODEC_ERROR: smaf is too small\n");
199 return false;
200 }
201
202 if (memcmp(buf + 5, "OPDA", 4) != 0)
203 {
204 DEBUGF("CODEC_ERROR: missing Optional Data Chunk\n");
205 return false;
206 }
144 207
145 if (buf[9] != 0x00) 208 /* Optional Data Chunk size */
209 chunksize = get_be32(buf + 9);
210
211 /* search Score Track Chunk */
212 ci->advance_buffer(13 + chunksize);
213 *pos += (13 + chunksize);
214 if (search_chunk("MTR", 3, pos) == 0)
146 { 215 {
147 DEBUGF("CODEC_ERROR: score track chunk unsupport sequence type %d\n", buf[9]); 216 DEBUGF("CODEC_ERROR: missing Score Track Chunk\n");
148 return false; 217 return false;
149 } 218 }
150 219
151 /* 220 /*
152 * skip to the next chunk. 221 * search next chunk
153 * MA-2/MA-3/MA-5: padding 16 bytes 222 * usually, next chunk ('M***') found within 40 bytes.
154 * MA-7: padding 32 bytes
155 */ 223 */
156 if (buf[3] < 7) 224 buf = ci->request_buffer(&size, 40);
157 buf += 28; 225 if (size < 40)
158 else
159 buf += 44;
160
161 while (buf < endbuf)
162 { 226 {
163 chunksize = get_be32(buf + 4) + 8; 227 DEBUGF("CODEC_ERROR: smaf is too small\n");
164 if (memcmp(buf, "Mtsp", 4) == 0) 228 return false;
165 {
166 buf += 8;
167 if (memcmp(buf, "Mwa", 3) != 0)
168 {
169 DEBUGF("CODEC_ERROR: smaf does not include stream pcm data\n");
170 return false;
171 }
172 fmt->numbytes = get_be32(buf + 4) - 3;
173 fmt->channels = ((buf[8] & 0x80) >> 7) + 1;
174 fmt->formattag = convert_smaf_audio_format(SMAF_TRACK_CHUNK_SCORE,
175 (buf[8] >> 4) & 0x07);
176 if (fmt->formattag == SMAF_FORMAT_UNSUPPORT)
177 {
178 DEBUGF("CODEC_ERROR: unsupport pcm data format : %d\n",
179 (buf[8] >> 4) & 0x07);
180 return false;
181 }
182 fmt->bitspersample = convert_smaf_audio_basebit(buf[8] & 0x0f);
183 if (fmt->bitspersample == 0)
184 {
185 DEBUGF("CODEC_ERROR: unsupport pcm data basebit : %d\n",
186 buf[8] & 0x0f);
187 return false;
188 }
189 buf += 11;
190 return true;
191 }
192 buf += chunksize;
193 } 229 }
194 230
195 DEBUGF("CODEC_ERROR: smaf does not include stream pcm data\n"); 231 size = 0;
196 return false; 232 while (size < 40 && buf[size] != 'M')
197} 233 size++;
198 234
199static bool parse_header(struct pcm_format *fmt, size_t *pos) 235 if (size >= 40)
200{ 236 {
201 unsigned char *buf, *stbuf, *endbuf; 237 DEBUGF("CODEC_ERROR: missing Score Track Stream PCM Data Chunk");
202 size_t chunksize; 238 return false;
239 }
203 240
204 ci->memset(fmt, 0, sizeof(struct pcm_format)); 241 /* search Score Track Stream PCM Data Chunk */
242 ci->advance_buffer(size);
243 *pos += size;
244 if (search_chunk("Mtsp", 4, pos) == 0)
245 {
246 DEBUGF("CODEC_ERROR: missing Score Track Stream PCM Data Chunk\n");
247 return false;
248 }
205 249
206 /* assume the SMAF pcm data position is less than 1024 bytes */ 250 /*
207 stbuf = ci->request_buffer(&chunksize, 1024); 251 * parse Score Track Stream Wave Data Chunk
208 if (chunksize < 1024) 252 * buf
253 * +4-7: chunk size (WaveType(3bytes) + wave data count)
254 * +8: bit 7 0:mono/1:stereo, bit 4-6 format, bit 0-3: base bit
255 * +9: frequency (MSB)
256 * +10: frequency (LSB)
257 */
258 buf = ci->request_buffer(&size, 9);
259 if (size < 9)
260 {
261 DEBUGF("CODEC_ERROR: smaf is too small\n");
209 return false; 262 return false;
263 }
210 264
211 buf = stbuf; 265 if (memcmp(buf, "Mwa", 3) != 0)
212 endbuf = stbuf + chunksize;
213
214 if (memcmp(buf, "MMMD", 4) != 0)
215 { 266 {
216 DEBUGF("CODEC_ERROR: does not smaf format %c%c%c%c\n", 267 DEBUGF("CODEC_ERROR: missing Score Track Stream Wave Data Chunk\n");
217 buf[0], buf[1], buf[2], buf[3]);
218 return false; 268 return false;
219 } 269 }
220 buf += 8;
221 270
222 while (buf < endbuf) 271 fmt->formattag = convert_smaf_audio_format(SMAF_SCORE_TRACK_CHUNK, buf[8]);
272 fmt->channels = convert_smaf_channels(buf[8]);
273 fmt->bitspersample = convert_smaf_audio_basebit(buf[8] & 0xf);
274 fmt->numbytes = get_be32(buf + 4) - 3;
275
276 *pos += 11;
277 return true;
278}
279
280static bool parse_header(struct pcm_format *fmt, off_t *pos)
281{
282 const unsigned char *buf;
283 unsigned int chunksize;
284 size_t size;
285
286 ci->memset(fmt, 0, sizeof(struct pcm_format));
287
288 /* check File Chunk and Contents Info Chunk */
289 buf = ci->request_buffer(&size, 16);
290 if (size < 16)
223 { 291 {
224 chunksize = get_be32(buf + 4) + 8; 292 DEBUGF("CODEC_ERROR: smaf is too small\n");
225 if (memcmp(buf, "ATR", 3) == 0) 293 return false;
226 {
227 if (!parse_audio_track(fmt, &buf, endbuf))
228 return false;
229 break;
230 }
231 if (memcmp(buf, "MTR", 3) == 0)
232 {
233 if (!parse_score_track(fmt, &buf, endbuf))
234 return false;
235 break;
236 }
237 buf += chunksize;
238 } 294 }
239 295
240 if (buf >= endbuf) 296 if ((memcmp(buf, "MMMD", 4) != 0) || (memcmp(buf + 8, "CNTI", 4) != 0))
241 { 297 {
242 DEBUGF("CODEC_ERROR: unsupported smaf format\n"); 298 DEBUGF("CODEC_ERROR: does not smaf format\n");
243 return false; 299 return false;
244 } 300 }
245 301
246 /* blockalign */ 302 chunksize = get_be32(buf + 12);
247 if (fmt->formattag == SMAF_FORMAT_SIGNED_PCM || 303 ci->advance_buffer(16);
248 fmt->formattag == SMAF_FORMAT_UNSIGNED_PCM) 304 *pos = 16;
249 fmt->blockalign = fmt->channels * fmt->bitspersample >> 3; 305 if (chunksize > 5)
306 {
307 if (!parse_audio_track(fmt, chunksize, pos))
308 return false;
309 }
310 else if (!parse_score_track(fmt, pos))
311 return false;
250 312
251 /* data signess (default signed) */ 313 /* data signess (default signed) */
252 fmt->is_signed = (fmt->formattag != SMAF_FORMAT_UNSIGNED_PCM); 314 fmt->is_signed = (fmt->formattag != SMAF_FORMAT_UNSIGNED_PCM);
253 315
316 /* data is always big endian */
254 fmt->is_little_endian = false; 317 fmt->is_little_endian = false;
255 318
256 /* sets pcm data position */
257 *pos = buf - stbuf;
258
259 return true; 319 return true;
260} 320}
261 321
@@ -297,14 +357,13 @@ next_track:
297 357
298 codec_set_replaygain(ci->id3); 358 codec_set_replaygain(ci->id3);
299 359
300 ci->memset(&format, 0, sizeof(struct pcm_format)); 360 /* Need to save offset for later use (cleared indirectly by advance_buffer) */
301 format.is_signed = true; 361 bytesdone = ci->id3->offset;
302 format.is_little_endian = false;
303 362
304 decodedsamples = 0; 363 decodedsamples = 0;
305 codec = 0; 364 codec = 0;
306 365
307 if (!parse_header(&format, &n)) 366 if (!parse_header(&format, &firstblockposn))
308 { 367 {
309 status = CODEC_ERROR; 368 status = CODEC_ERROR;
310 goto done; 369 goto done;
@@ -324,29 +383,6 @@ next_track:
324 goto done; 383 goto done;
325 } 384 }
326 385
327 /* common format check */
328 if (format.channels == 0) {
329 DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-channels file\n");
330 status = CODEC_ERROR;
331 goto done;
332 }
333 if (format.samplesperblock == 0) {
334 DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-wSamplesPerBlock file\n");
335 status = CODEC_ERROR;
336 goto done;
337 }
338 if (format.blockalign == 0)
339 {
340 DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-blockalign file\n");
341 status = CODEC_ERROR;
342 goto done;
343 }
344 if (format.numbytes == 0) {
345 DEBUGF("CODEC_ERROR: 'data' chunk not found or has zero-length\n");
346 status = CODEC_ERROR;
347 goto done;
348 }
349
350 /* check chunksize */ 386 /* check chunksize */
351 if ((format.chunksize / format.blockalign) * format.samplesperblock * format.channels 387 if ((format.chunksize / format.blockalign) * format.samplesperblock * format.channels
352 > PCM_SAMPLE_SIZE) 388 > PCM_SAMPLE_SIZE)
@@ -370,12 +406,10 @@ next_track:
370 goto done; 406 goto done;
371 } 407 }
372 408
373 firstblockposn = 1024 - n; 409 ci->seek_buffer(firstblockposn);
374 ci->advance_buffer(firstblockposn); 410 ci->seek_complete();
375 411
376 /* The main decoder loop */ 412 /* The main decoder loop */
377 bytesdone = 0;
378 ci->set_elapsed(0);
379 endofstream = 0; 413 endofstream = 0;
380 414
381 while (!endofstream) { 415 while (!endofstream) {