static status_t decode_frameset(decoders_ALACDecoder *self, unsigned *pcm_frames_read, int *samples) { BitstreamReader *br = self->bitstream; int channel_0[self->params.block_size]; int channel_1[self->params.block_size]; unsigned c = 0; unsigned block_size = self->params.block_size; unsigned channels = br->read(br, 3) + 1; while (channels != 8) { status_t status; unsigned frame_block_size; if ((channels != 1) && (channels != 2)) { /*only handle 1 or 2 channel frames*/ return INVALID_FRAME_CHANNEL_COUNT; } else if ((c + channels) > self->channels) { /*ensure one doesn't decode too many channels*/ return EXCESSIVE_FRAME_CHANNEL_COUNT; } if ((status = decode_frame(br, &(self->params), self->bits_per_sample, c == 0 ? &block_size : &frame_block_size, channels, channel_0, channel_1)) != OK) { return status; } else if ((c != 0) && (block_size != frame_block_size)) { return FRAME_BLOCK_SIZE_MISMATCH; } put_channel_data(samples, c++, self->channels, block_size, channel_0); if (channels == 2) { put_channel_data(samples, c++, self->channels, block_size, channel_1); } channels = br->read(br, 3) + 1; } br->byte_align(br); *pcm_frames_read = block_size; return OK; }
static PyObject* ALACDecoder_read(decoders_ALACDecoder* self, PyObject *args) { unsigned channel_count; BitstreamReader* mdat = self->bitstream; array_ia* frameset_channels = self->frameset_channels; PyThreadState *thread_state; /*return an empty framelist if total samples are exhausted*/ if (self->remaining_frames == 0) { return empty_FrameList(self->audiotools_pcm, self->channels, self->bits_per_sample); } thread_state = PyEval_SaveThread(); if (!setjmp(*br_try(mdat))) { frameset_channels->reset(frameset_channels); /*get initial frame's channel count*/ channel_count = mdat->read(mdat, 3) + 1; while (channel_count != 8) { /*read a frame from the frameset into "channels"*/ if (read_frame(self, mdat, frameset_channels, channel_count) != OK) { br_etry(mdat); PyEval_RestoreThread(thread_state); PyErr_SetString(PyExc_ValueError, self->error_message); return NULL; } else { /*ensure all frames have the same sample count*/ /*FIXME*/ /*read the channel count of the next frame in the frameset, if any*/ channel_count = mdat->read(mdat, 3) + 1; } } /*once all the frames in the frameset are read, byte-align the output stream*/ mdat->byte_align(mdat); br_etry(mdat); PyEval_RestoreThread(thread_state); /*decrement the remaining sample count*/ self->remaining_frames -= MIN(self->remaining_frames, frameset_channels->_[0]->len); /*convert ALAC channel assignment to standard audiotools assignment*/ alac_order_to_wave_order(frameset_channels); /*finally, build and return framelist object from the sample data*/ return array_ia_to_FrameList(self->audiotools_pcm, frameset_channels, self->bits_per_sample); } else { br_etry(mdat); PyEval_RestoreThread(thread_state); PyErr_SetString(PyExc_IOError, "EOF during frame reading"); return NULL; } }
int main(int argc, char* argv[]) { FILE* ogg_file; OggReader* ogg_stream = NULL; BitstreamReader* packet = NULL; struct flac_STREAMINFO streaminfo; uint16_t header_packets; a_int* residuals = NULL; a_int* qlp_coeffs = NULL; aa_int* subframe_data = NULL; a_int* framelist_data = NULL; ogg_status result; uint16_t crc16 = 0; FrameList_int_to_char_converter converter; unsigned pcm_size; unsigned output_data_size = 1; uint8_t* output_data = NULL; audiotools__MD5Context md5; unsigned char stream_md5sum[16]; const static unsigned char blank_md5sum[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; if (argc < 2) { fprintf(stderr, "*** Usage: %s <file.oga>\n", argv[0]); return 1; } /*open input file for reading*/ if ((ogg_file = fopen(argv[1], "rb")) == NULL) { fprintf(stderr, "*** %s: %s\n", argv[1], strerror(errno)); return 1; } else { /*open bitstream and setup temporary arrays/buffers*/ ogg_stream = oggreader_open(ogg_file); packet = br_substream_new(BS_BIG_ENDIAN); subframe_data = aa_int_new(); residuals = a_int_new(); qlp_coeffs = a_int_new(); framelist_data = a_int_new(); output_data = malloc(output_data_size); } /*the first packet should be the FLAC's STREAMINFO*/ if ((result = oggreader_next_packet(ogg_stream, packet)) == OGG_OK) { if (!oggflac_read_streaminfo(packet, &streaminfo, &header_packets)) { goto error; } else { converter = FrameList_get_int_to_char_converter( streaminfo.bits_per_sample, 0, 1); } } else { fprintf(stderr, "*** Error: %s\n", ogg_strerror(result)); goto error; } /*skip subsequent header packets*/ for (; header_packets > 0; header_packets--) { if ((result = oggreader_next_packet(ogg_stream, packet)) != OGG_OK) { fprintf(stderr, "*** Error: %s\n", ogg_strerror(result)); goto error; } } /*initialize the output MD5 sum*/ audiotools__MD5Init(&md5); /*add callback for CRC16 calculation*/ br_add_callback(packet, (bs_callback_f)flac_crc16, &crc16); /*decode the next FrameList from the stream*/ result = oggreader_next_packet(ogg_stream, packet); while (result != OGG_STREAM_FINISHED) { if (result == OGG_OK) { flac_status flac_status; struct flac_frame_header frame_header; unsigned channel; subframe_data->reset(subframe_data); if (!setjmp(*br_try(packet))) { /*read frame header*/ if ((flac_status = flacdec_read_frame_header(packet, &streaminfo, &frame_header)) != OK) { fprintf(stderr, "*** Error: %s\n", FlacDecoder_strerror(flac_status)); br_etry(packet); goto error; } /*read 1 subframe per channel*/ for (channel = 0; channel < frame_header.channel_count; channel++) if ((flac_status = flacdec_read_subframe( packet, qlp_coeffs, residuals, frame_header.block_size, flacdec_subframe_bits_per_sample(&frame_header, channel), subframe_data->append(subframe_data))) != OK) { fprintf(stderr, "*** Error: %s\n", FlacDecoder_strerror(flac_status)); br_etry(packet); goto error; } br_etry(packet); } else { br_etry(packet); fprintf(stderr, "*** I/O Error reading FLAC frame\n"); goto error; } /*handle difference channels, if any*/ flacdec_decorrelate_channels(frame_header.channel_assignment, subframe_data, framelist_data); /*check CRC-16*/ packet->byte_align(packet); packet->read(packet, 16); if (crc16 != 0) { fprintf(stderr, "*** Error: invalid checksum in frame\n"); goto error; } /*turn FrameList into string of output*/ pcm_size = (streaminfo.bits_per_sample / 8) * framelist_data->len; if (pcm_size > output_data_size) { output_data_size = pcm_size; output_data = realloc(output_data, output_data_size); } FrameList_samples_to_char(output_data, framelist_data->_, converter, framelist_data->len, streaminfo.bits_per_sample); /*update MD5 sum*/ audiotools__MD5Update(&md5, output_data, pcm_size); /*output string to stdout*/ fwrite(output_data, sizeof(unsigned char), pcm_size, stdout); result = oggreader_next_packet(ogg_stream, packet); } else { /*some error reading Ogg stream*/ fprintf(stderr, "*** Error: %s\n", ogg_strerror(result)); goto error; } } /*Ogg stream is finished so verify stream's MD5 sum*/ audiotools__MD5Final(stream_md5sum, &md5); if (!((memcmp(streaminfo.md5sum, blank_md5sum, 16) == 0) || (memcmp(stream_md5sum, streaminfo.md5sum, 16) == 0))) { fprintf(stderr, "*** MD5 mismatch at end of stream\n"); goto error; } /*close streams, temporary buffers*/ oggreader_close(ogg_stream); packet->close(packet); subframe_data->del(subframe_data); residuals->del(residuals); qlp_coeffs->del(qlp_coeffs); framelist_data->del(framelist_data); free(output_data); return 0; error: oggreader_close(ogg_stream); packet->close(packet); subframe_data->del(subframe_data); residuals->del(residuals); qlp_coeffs->del(qlp_coeffs); framelist_data->del(framelist_data); free(output_data); return 1; }