1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the copyright holder nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27#include <stdlib.h>
28#include <string.h>
29
30#include "containers/core/containers_private.h"
31#include "containers/core/containers_io_helpers.h"
32#include "containers/core/containers_utils.h"
33#include "containers/core/containers_logging.h"
34
35/******************************************************************************
36Defines.
37******************************************************************************/
38
39#define BI32(b) (((b)[0]<<24)|((b)[1]<<16)|((b)[2]<<8)|((b)[3]))
40#define BI16(b) (((b)[0]<<8)|((b)[1]))
41
42#define HEADER_LENGTH 14
43#define MAX_TRACKS 128
44
45/******************************************************************************
46Type definitions
47******************************************************************************/
48struct _QSYNTH_SEGMENT_T {
49 struct _QSYNTH_SEGMENT_T *next;
50 uint32_t len;
51 uint8_t *data;
52};
53typedef struct _QSYNTH_SEGMENT_T QSYNTH_SEGMENT_T;
54
55typedef struct VC_CONTAINER_MODULE_T
56{
57 VC_CONTAINER_TRACK_T *track;
58 uint32_t filesize;
59 QSYNTH_SEGMENT_T *seg;
60 QSYNTH_SEGMENT_T *pass;
61 uint32_t sent;
62 int64_t timestamp;
63 uint32_t seek;
64} VC_CONTAINER_MODULE_T;
65
66/******************************************************************************
67Function prototypes
68******************************************************************************/
69VC_CONTAINER_STATUS_T qsynth_reader_open( VC_CONTAINER_T * );
70
71/******************************************************************************
72Local Functions
73******************************************************************************/
74
75static VC_CONTAINER_STATUS_T qsynth_read_header(uint8_t *data, uint32_t *tracks,
76 uint32_t *division, uint8_t *fps, uint8_t *dpf)
77{
78 if(data[0] != 'M' || data[1] != 'T' || data[2] != 'h' || data[3] != 'd' ||
79 data[4] != 0 || data[5] != 0 || data[6] != 0 || data[7] != 6)
80 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
81
82 if(data[12] < 0x80)
83 {
84 if(division) *division = BI16(data+12);
85 }
86 else
87 {
88 if(fps) *fps = 256-data[12];
89 if(dpf) *dpf = data[13];
90 }
91
92 if(tracks) *tracks = BI16(data+10);
93
94 return VC_CONTAINER_SUCCESS;
95}
96
97static int qsynth_read_variable(uint8_t *data, uint32_t *val)
98{
99 int i = 0;
100 *val = 0;
101 do {
102 *val = (*val << 7) + (data[i] & 0x7f);
103 } while(data[i++] & 0x80);
104
105 return i;
106}
107
108static VC_CONTAINER_STATUS_T qsynth_read_event(uint8_t *data, uint32_t *used, uint8_t *last,
109 uint32_t *time, uint32_t *tempo, uint32_t *end)
110{
111 int read;
112
113 // need at least 4 bytes here
114 read = qsynth_read_variable(data, time);
115
116 if(data[read] == 0xff) // meta event
117 {
118 uint32_t len;
119 uint8_t type = data[read+1];
120
121 read += 2;
122 read += qsynth_read_variable(data+read, &len);
123
124 if(type == 0x2f) // end of track
125 {
126 if(len != 0)
127 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
128
129 *end = 1;
130 }
131 else if(type == 0x51) // tempo event
132 {
133 if(len != 3)
134 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
135
136 *tempo = (data[read]<<16) | (data[read+1]<<8) | data[read+2];
137 }
138
139 read += len;
140 }
141 else if(data[read] == 0xf0 || data[read] == 0xf7) // sysex events
142 {
143 uint32_t len;
144 read += 1;
145 read += qsynth_read_variable(data+read, &len) + len;
146 }
147 else // midi event
148 {
149 uint8_t type;
150
151 if(data[read] < 128)
152 type = *last;
153 else
154 {
155 type = data[read] >> 4;
156 *last = type;
157 read++;
158 }
159
160 switch(type) {
161 case 8: case 9: case 0xa: case 0xb: case 0xe:
162 read += 2;
163 break;
164 case 0xc: case 0xd:
165 read += 1;
166 break;
167 default:
168 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
169 }
170 }
171
172 *used = read;
173 return VC_CONTAINER_SUCCESS;
174}
175
176static VC_CONTAINER_STATUS_T qsynth_read_track(QSYNTH_SEGMENT_T *seg,
177 uint32_t *ticks, int64_t *time,
178 uint32_t *us_perclock, uint32_t *tempo_ticks)
179{
180 uint32_t total_ticks = 0;
181 uint32_t used = 8;
182 uint8_t last = 0;
183
184 *time = 0LL;
185 *tempo_ticks = 0;
186
187 while(used < seg->len)
188 {
189 VC_CONTAINER_STATUS_T status;
190 uint32_t event_ticks, new_tempo = 0, end = 0, event_used;
191 if((status = qsynth_read_event(seg->data+used, &event_used, &last, &event_ticks, &new_tempo, &end)) != VC_CONTAINER_SUCCESS)
192 return status;
193
194 used += event_used;
195 total_ticks += event_ticks;
196
197 if(new_tempo != 0)
198 {
199 *time += ((int64_t) (total_ticks - *tempo_ticks)) * (*us_perclock);
200 *us_perclock = new_tempo;
201 *tempo_ticks = total_ticks;
202 }
203
204 if(end)
205 break;
206 }
207
208 *ticks = total_ticks;
209 return VC_CONTAINER_SUCCESS;
210}
211
212static VC_CONTAINER_STATUS_T qsynth_get_duration(VC_CONTAINER_T *p_ctx)
213{
214 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
215 VC_CONTAINER_STATUS_T status;
216 QSYNTH_SEGMENT_T **seg = &(module->seg);
217 uint32_t i, tracks, division = 0, max_ticks = 0, us_perclock = 500000;
218 uint32_t end_uspc = 0, end_ticks = 0;
219 int64_t end_time = 0;
220 uint8_t fps = 1, dpf = 1;
221
222 if((*seg = malloc(sizeof(QSYNTH_SEGMENT_T) + HEADER_LENGTH)) == NULL)
223 return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
224
225 (*seg)->next = NULL;
226 (*seg)->len = HEADER_LENGTH;
227 (*seg)->data = (uint8_t *) ((*seg) + 1);
228
229 if(PEEK_BYTES(p_ctx, (*seg)->data, HEADER_LENGTH) != HEADER_LENGTH)
230 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
231
232 if((status = qsynth_read_header((*seg)->data, &tracks, &division, &fps, &dpf)) != VC_CONTAINER_SUCCESS)
233 return status;
234
235 // if we have a suspiciously large number of tracks, this is probably a bad file
236 if(tracks > MAX_TRACKS)
237 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
238
239 SKIP_BYTES(p_ctx, HEADER_LENGTH);
240
241 seg = &((*seg)->next);
242 module->filesize = HEADER_LENGTH;
243
244 if(division == 0)
245 {
246 us_perclock = 1000000 / (fps * dpf);
247 division = 1;
248 }
249
250 for(i=0; i<tracks; i++)
251 {
252 uint32_t len, ticks, tempo_ticks;
253 int64_t time;
254 uint8_t dummy[8];
255
256 if(READ_BYTES(p_ctx, dummy, sizeof(dummy)) != sizeof(dummy) ||
257 dummy[0] != 'M' || dummy[1] != 'T' || dummy[2] != 'r' || dummy[3] != 'k')
258 return VC_CONTAINER_ERROR_FORMAT_INVALID;
259
260 len = BI32(dummy+4);
261
262 // impose a 1mb limit on track size
263 if(len > (1<<20) || (*seg = malloc(sizeof(QSYNTH_SEGMENT_T) + 8 + len)) == NULL)
264 return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
265
266 module->filesize += len+8;
267 (*seg)->next = NULL;
268 (*seg)->len = len + 8;
269 (*seg)->data = (uint8_t *) ((*seg) + 1);
270
271 memcpy((*seg)->data, dummy, 8);
272 if(READ_BYTES(p_ctx, (*seg)->data+8, len) != len)
273 return VC_CONTAINER_ERROR_FORMAT_INVALID;
274
275 if((status = qsynth_read_track(*seg, &ticks, &time, &us_perclock, &tempo_ticks)) != VC_CONTAINER_SUCCESS)
276 return status;
277
278 if(end_uspc == 0)
279 {
280 end_uspc = us_perclock;
281 end_ticks = tempo_ticks;
282 end_time = time;
283 }
284
285 if(ticks > max_ticks)
286 max_ticks = ticks;
287
288 seg = &((*seg)->next);
289 }
290
291 if(end_uspc == 0)
292 return VC_CONTAINER_ERROR_FORMAT_INVALID;
293
294 module->pass = module->seg;
295 module->sent = 0;
296 p_ctx->duration = (end_time + (((int64_t) (max_ticks - end_ticks)) * end_uspc)) / division;
297 module->track->format->extradata = (uint8_t *) &module->filesize;
298 module->track->format->extradata_size = 4;
299 return VC_CONTAINER_SUCCESS;
300}
301
302
303/*****************************************************************************
304Functions exported as part of the Container Module API
305*****************************************************************************/
306static VC_CONTAINER_STATUS_T qsynth_reader_read( VC_CONTAINER_T *p_ctx,
307 VC_CONTAINER_PACKET_T *packet,
308 uint32_t flags )
309{
310 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
311
312 if(module->pass)
313 {
314 packet->size = module->pass->len - module->sent;
315 packet->dts = packet->pts = 0;
316 packet->track = 0;
317 packet->flags = module->sent ? 0 : VC_CONTAINER_PACKET_FLAG_FRAME_START;
318 }
319 else
320 {
321 if(module->timestamp > p_ctx->duration)
322 return VC_CONTAINER_ERROR_EOS;
323
324 packet->size = 5;
325 packet->dts = packet->pts = module->timestamp;
326 packet->track = 0;
327 packet->flags = VC_CONTAINER_PACKET_FLAG_FRAME;
328 }
329
330 if(flags & VC_CONTAINER_READ_FLAG_SKIP)
331 {
332 if(module->pass)
333 {
334 module->pass = module->pass->next;
335 module->sent = 0;
336 }
337 else
338 {
339 // if we're playing then we can't really skip, but have to simulate a seek instead
340 module->seek = 1;
341 module->timestamp += 40;
342 }
343
344 return VC_CONTAINER_SUCCESS;
345 }
346
347 if(flags & VC_CONTAINER_READ_FLAG_INFO)
348 return VC_CONTAINER_SUCCESS;
349
350 // read frame into packet->data
351 if(module->pass)
352 {
353 uint32_t copy = MIN(packet->size, packet->buffer_size);
354 memcpy(packet->data, module->pass->data + module->sent, copy);
355 packet->size = copy;
356
357 if((module->sent += copy) == module->pass->len)
358 {
359 module->pass = module->pass->next;
360 module->sent = 0;
361 packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END;
362 }
363 }
364 else
365 {
366 if(packet->buffer_size < packet->size)
367 return VC_CONTAINER_ERROR_BUFFER_TOO_SMALL;
368
369 if(module->seek)
370 {
371 uint32_t current_time = module->timestamp / 1000;
372
373 packet->data[0] = 'S';
374 packet->data[1] = (uint8_t)((current_time >> 24) & 0xFF);
375 packet->data[2] = (uint8_t)((current_time >> 16) & 0xFF);
376 packet->data[3] = (uint8_t)((current_time >> 8) & 0xFF);
377 packet->data[4] = (uint8_t)((current_time ) & 0xFF);
378 module->seek = 0;
379 }
380 else
381 {
382 packet->data[0] = 'P';
383 packet->data[1] = 0;
384 packet->data[2] = 0;
385 packet->data[3] = 0;
386 packet->data[4] = 40;
387 module->timestamp += 40 * 1000;
388 }
389 }
390
391 return VC_CONTAINER_SUCCESS;
392}
393
394/*****************************************************************************/
395static VC_CONTAINER_STATUS_T qsynth_reader_seek( VC_CONTAINER_T *p_ctx,
396 int64_t *offset,
397 VC_CONTAINER_SEEK_MODE_T mode,
398 VC_CONTAINER_SEEK_FLAGS_T flags)
399{
400 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
401 VC_CONTAINER_PARAM_UNUSED(flags);
402
403 if (mode != VC_CONTAINER_SEEK_MODE_TIME)
404 return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
405
406 if(*offset < 0)
407 *offset = 0;
408 else if(*offset > p_ctx->duration)
409 *offset = p_ctx->duration;
410
411 module->timestamp = *offset;
412 module->seek = 1;
413
414 return VC_CONTAINER_SUCCESS;
415}
416
417/*****************************************************************************/
418static VC_CONTAINER_STATUS_T qsynth_reader_close( VC_CONTAINER_T *p_ctx )
419{
420 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
421 QSYNTH_SEGMENT_T *seg = module->seg;
422 for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--)
423 vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]);
424 while(seg != NULL)
425 {
426 QSYNTH_SEGMENT_T *next = seg->next;
427 free(seg);
428 seg = next;
429 }
430 free(module);
431 return VC_CONTAINER_SUCCESS;
432}
433
434/*****************************************************************************/
435VC_CONTAINER_STATUS_T qsynth_reader_open( VC_CONTAINER_T *p_ctx )
436{
437 VC_CONTAINER_MODULE_T *module = 0;
438 VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
439 uint8_t header[HEADER_LENGTH];
440
441 /* Check the file header */
442 if((PEEK_BYTES(p_ctx, header, HEADER_LENGTH) != HEADER_LENGTH) ||
443 qsynth_read_header(header, 0, 0, 0, 0) != VC_CONTAINER_SUCCESS)
444 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
445
446 /* Allocate our context */
447 module = malloc(sizeof(*module));
448 if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
449 memset(module, 0, sizeof(*module));
450 p_ctx->priv->module = module;
451 p_ctx->tracks_num = 1;
452 p_ctx->tracks = &module->track;
453 p_ctx->tracks[0] = vc_container_allocate_track(p_ctx, 0);
454 if(!p_ctx->tracks[0]) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
455 p_ctx->tracks[0]->format->es_type = VC_CONTAINER_ES_TYPE_AUDIO;
456 p_ctx->tracks[0]->format->codec = VC_CONTAINER_CODEC_MIDI;
457 p_ctx->tracks[0]->is_enabled = true;
458
459 if((status = qsynth_get_duration(p_ctx)) != VC_CONTAINER_SUCCESS) goto error;
460
461 LOG_DEBUG(p_ctx, "using qsynth reader");
462
463 p_ctx->capabilities = VC_CONTAINER_CAPS_CAN_SEEK;
464
465 p_ctx->priv->pf_close = qsynth_reader_close;
466 p_ctx->priv->pf_read = qsynth_reader_read;
467 p_ctx->priv->pf_seek = qsynth_reader_seek;
468 return VC_CONTAINER_SUCCESS;
469
470 error:
471 LOG_DEBUG(p_ctx, "qsynth: error opening stream (%i)", status);
472 if(module) qsynth_reader_close(p_ctx);
473 return status;
474}
475
476/********************************************************************************
477 Entrypoint function
478********************************************************************************/
479
480#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
481# pragma weak reader_open qsynth_reader_open
482#endif
483