PyObject* encoders_encode_alac(PyObject *dummy, PyObject *args, PyObject *keywds) { static char *kwlist[] = {"file", "pcmreader", "block_size", "initial_history", "history_multiplier", "maximum_k", "minimum_interlacing_leftweight", "maximum_interlacing_leftweight", NULL}; PyObject *file_obj; FILE *output_file; BitstreamWriter *output = NULL; PyObject *pcmreader_obj; pcmreader* pcmreader; struct alac_context encoder; array_ia* channels = array_ia_new(); unsigned frame_file_offset; PyObject *log_output; alacenc_init_encoder(&encoder); encoder.options.minimum_interlacing_leftweight = 0; encoder.options.maximum_interlacing_leftweight = 4; /*extract a file object, PCMReader-compatible object and encoding options*/ if (!PyArg_ParseTupleAndKeywords( args, keywds, "OOiiii|ii", kwlist, &file_obj, &pcmreader_obj, &(encoder.options.block_size), &(encoder.options.initial_history), &(encoder.options.history_multiplier), &(encoder.options.maximum_k), &(encoder.options.minimum_interlacing_leftweight), &(encoder.options.maximum_interlacing_leftweight))) return NULL; /*transform the Python PCMReader-compatible object to a pcm_reader struct*/ if ((pcmreader = open_pcmreader(pcmreader_obj)) == NULL) { return NULL; } encoder.bits_per_sample = pcmreader->bits_per_sample; /*determine if the PCMReader is compatible with ALAC*/ if ((pcmreader->bits_per_sample != 16) && (pcmreader->bits_per_sample != 24)) { PyErr_SetString(PyExc_ValueError, "bits per sample must be 16 or 24"); goto error; } /*convert file object to bitstream writer*/ if ((output_file = PyFile_AsFile(file_obj)) == NULL) { PyErr_SetString(PyExc_TypeError, "file must by a concrete file object"); goto error; } else { output = bw_open(output_file, BS_BIG_ENDIAN); bw_add_callback(output, alac_byte_counter, &(encoder.mdat_byte_size)); } #else int ALACEncoder_encode_alac(char *filename, FILE *input, int block_size, int initial_history, int history_multiplier, int maximum_k) { FILE *output_file; BitstreamWriter *output = NULL; pcmreader *pcmreader; struct alac_context encoder; array_ia* channels = array_ia_new(); unsigned frame_file_offset; alacenc_init_encoder(&encoder); encoder.options.block_size = block_size; encoder.options.initial_history = initial_history; encoder.options.history_multiplier = history_multiplier; encoder.options.maximum_k = maximum_k; encoder.options.minimum_interlacing_leftweight = 0; encoder.options.maximum_interlacing_leftweight = 4; output_file = fopen(filename, "wb"); /*assume CD quality for now*/ pcmreader = open_pcmreader(input, 44100, 2, 0x3, 16, 0, 1); encoder.bits_per_sample = pcmreader->bits_per_sample; /*convert file object to bitstream writer*/ output = bw_open(output_file, BS_BIG_ENDIAN); bw_add_callback(output, alac_byte_counter, &(encoder.mdat_byte_size)); #endif /*write placeholder mdat header*/ output->write(output, 32, 0); output->write_bytes(output, (uint8_t*)"mdat", 4); /*write frames from pcm_reader until empty*/ if (pcmreader->read(pcmreader, encoder.options.block_size, channels)) goto error; while (channels->_[0]->size > 0) { #ifndef STANDALONE Py_BEGIN_ALLOW_THREADS #endif /*log the number of PCM frames in each ALAC frameset*/ encoder.frame_log->_[LOG_SAMPLE_SIZE]->append( encoder.frame_log->_[LOG_SAMPLE_SIZE], channels->_[0]->size); frame_file_offset = encoder.mdat_byte_size; /*log each frameset's starting offset in the mdat atom*/ encoder.frame_log->_[LOG_FILE_OFFSET]->append( encoder.frame_log->_[LOG_FILE_OFFSET], frame_file_offset); alac_write_frameset(output, &encoder, channels); /*log each frame's total size in bytes*/ encoder.frame_log->_[LOG_BYTE_SIZE]->append( encoder.frame_log->_[LOG_BYTE_SIZE], encoder.mdat_byte_size - frame_file_offset); #ifndef STANDALONE Py_END_ALLOW_THREADS #endif if (pcmreader->read(pcmreader, encoder.options.block_size, channels)) goto error; } /*return to header and rewrite it with the actual value*/ bw_pop_callback(output, NULL); fseek(output_file, 0, 0); output->write(output, 32, encoder.mdat_byte_size); /*close and free allocated files/buffers, which varies depending on whether we're running standlone or not*/ #ifndef STANDALONE log_output = alac_log_output(&encoder); pcmreader->del(pcmreader); output->free(output); alacenc_free_encoder(&encoder); channels->del(channels); return log_output; error: pcmreader->del(pcmreader); output->free(output); alacenc_free_encoder(&encoder); channels->del(channels); return NULL; }
static result_t encode_opus_file(char *filename, pcmreader *pcmreader, int quality, unsigned original_sample_rate) { const int multichannel = (pcmreader->channels > 2); const unsigned channel_mapping = (pcmreader->channels > 8 ? 255 : pcmreader->channels > 2); int stream_count; int coupled_stream_count; unsigned char stream_map[255]; result_t result = ENCODE_OK; FILE *output_file = NULL; ogg_stream_state ogg_stream; ogg_page ogg_page; OpusEncoder *opus_encoder = NULL; OpusMSEncoder *opus_ms_encoder = NULL; int error; aa_int *samples = NULL; opus_int16 *opus_samples = NULL; unsigned char opus_frame[OPUS_FRAME_LEN]; ogg_int64_t granulepos = 0; ogg_int64_t packetno = 0; opus_int32 preskip; /*open output file for writing*/ if ((output_file = fopen(filename, "w+b")) == NULL) { return ERR_IOERROR; } if (!multichannel) { if ((opus_encoder = opus_encoder_create(48000, pcmreader->channels, OPUS_APPLICATION_AUDIO, &error)) == NULL) { fclose(output_file); return ERR_ENCODER_INIT; } opus_encoder_ctl(opus_encoder, OPUS_SET_COMPLEXITY(quality)); opus_encoder_ctl(opus_encoder, OPUS_GET_LOOKAHEAD(&preskip)); } else { if ((opus_ms_encoder = opus_multistream_surround_encoder_create( 48000, pcmreader->channels, channel_mapping, &stream_count, &coupled_stream_count, stream_map, OPUS_APPLICATION_AUDIO, &error)) == NULL) { fclose(output_file); return ERR_ENCODER_INIT; } opus_multistream_encoder_ctl(opus_ms_encoder, OPUS_SET_COMPLEXITY(quality)); opus_multistream_encoder_ctl(opus_ms_encoder, OPUS_GET_LOOKAHEAD(&preskip)); } srand((unsigned)time(NULL)); ogg_stream_init(&ogg_stream, rand()); /*write header and comment packets to Ogg stream*/ { BitstreamWriter *header = bw_open_recorder(BS_LITTLE_ENDIAN); BitstreamWriter *comment = bw_open_recorder(BS_LITTLE_ENDIAN); int i; /*write header packet to Ogg stream*/ const char opushead[] = "OpusHead"; const char opuscomment[] = "OpusTags"; const char *vendor_string = opus_get_version_string(); const size_t vendor_string_len = strlen(vendor_string); ogg_packet packet_head; ogg_packet packet_tags; header->write_bytes(header, (uint8_t*)opushead, (unsigned)strlen(opushead)); header->write(header, 8, 1); /*version*/ header->write(header, 8, pcmreader->channels); header->write(header, 16, preskip); header->write(header, 32, original_sample_rate); header->write(header, 16, 0); /*output gain*/ header->write(header, 8, channel_mapping); if (channel_mapping != 0) { header->write(header, 8, stream_count); header->write(header, 8, coupled_stream_count); for (i = 0; i < pcmreader->channels; i++) { header->write(header, 8, stream_map[i]); } } packet_head.packet = BUF_WINDOW_START(header->output.buffer); packet_head.bytes = BUF_WINDOW_SIZE(header->output.buffer); packet_head.b_o_s = 1; packet_head.e_o_s = 0; packet_head.granulepos = 0; packet_head.packetno = packetno++; ogg_stream_packetin(&ogg_stream, &packet_head); for (i = ogg_stream_flush(&ogg_stream, &ogg_page); i != 0; i = ogg_stream_flush(&ogg_stream, &ogg_page)) { fwrite(ogg_page.header, 1, ogg_page.header_len, output_file); fwrite(ogg_page.body, 1, ogg_page.body_len, output_file); } /*write comment packet to Ogg stream*/ comment->write_bytes(comment, (uint8_t*)opuscomment, (unsigned)strlen(opuscomment)); comment->write(comment, 32, (unsigned)vendor_string_len); comment->write_bytes(comment, (uint8_t*)vendor_string, (unsigned)vendor_string_len); comment->write(comment, 32, 0); packet_tags.packet = BUF_WINDOW_START(comment->output.buffer); packet_tags.bytes = BUF_WINDOW_SIZE(comment->output.buffer); packet_tags.b_o_s = 0; packet_tags.e_o_s = 0; packet_tags.granulepos = 0; packet_tags.packetno = packetno++; ogg_stream_packetin(&ogg_stream, &packet_tags); for (i = ogg_stream_flush(&ogg_stream, &ogg_page); i != 0; i = ogg_stream_flush(&ogg_stream, &ogg_page)) { fwrite(ogg_page.header, 1, ogg_page.header_len, output_file); fwrite(ogg_page.body, 1, ogg_page.body_len, output_file); } header->close(header); comment->close(comment); } samples = aa_int_new(); opus_samples = malloc(sizeof(opus_int16) * pcmreader->channels * BLOCK_SIZE); /*for each non-empty FrameList from PCMReader, encode Opus frame*/ if (pcmreader->read(pcmreader, BLOCK_SIZE, samples)) { result = ERR_PCMREADER; goto cleanup; } else if (samples->_[0]->len > BLOCK_SIZE) { result = ERR_BLOCK_SIZE; goto cleanup; } while (samples->_[0]->len > 0) { const int short_framelist = (samples->_[0]->len < BLOCK_SIZE); unsigned c; opus_int32 encoded_size; ogg_packet packet; granulepos += samples->_[0]->len; /*pad FrameList with additional null samples if necessary*/ for (c = 0; c < samples->len; c++) { a_int *channel = samples->_[c]; channel->mappend(channel, BLOCK_SIZE - samples->_[0]->len, 0); } /*rearrange channels to Vorbis order if necessary*/ reorder_channels(pcmreader->channel_mask, samples); /*place samples in interleaved buffer*/ for (c = 0; c < samples->len; c++) { unsigned i; a_int *channel = samples->_[c]; for (i = 0; i < channel->len; i++) { opus_samples[c + (i * samples->len)] = (opus_int16)channel->_[i]; } } /*call opus_encode on interleaved buffer to get next packet*/ if (!multichannel) { encoded_size = opus_encode(opus_encoder, opus_samples, samples->_[0]->len, opus_frame, OPUS_FRAME_LEN); } else { encoded_size = opus_multistream_encode(opus_ms_encoder, opus_samples, samples->_[0]->len, opus_frame, OPUS_FRAME_LEN); } /*get next FrameList to encode*/ if (pcmreader->read(pcmreader, BLOCK_SIZE, samples)) { result = ERR_PCMREADER; goto cleanup; } else if (samples->_[0]->len > BLOCK_SIZE) { result = ERR_BLOCK_SIZE; goto cleanup; } /*dump Opus packet to Ogg stream*/ /*do this *after* reading the next FrameList in order to detect the end of stream properly based on whether the FrameList has no frames*/ packet.packet = (unsigned char *)opus_frame; packet.bytes = encoded_size; packet.b_o_s = 0; packet.e_o_s = (short_framelist || (samples->_[0]->len == 0)); packet.granulepos = granulepos; packet.packetno = packetno; ogg_stream_packetin(&ogg_stream, &packet); while (ogg_stream_pageout(&ogg_stream, &ogg_page)) { fwrite(ogg_page.header, 1, ogg_page.header_len, output_file); fwrite(ogg_page.body, 1, ogg_page.body_len, output_file); } } /*flush any remaining Ogg pages to disk*/ while (ogg_stream_flush(&ogg_stream, &ogg_page)) { fwrite(ogg_page.header, 1, ogg_page.header_len, output_file); fwrite(ogg_page.body, 1, ogg_page.body_len, output_file); } cleanup: fclose(output_file); ogg_stream_clear(&ogg_stream); if (!multichannel) { opus_encoder_destroy(opus_encoder); } else { opus_multistream_encoder_destroy(opus_ms_encoder); } samples->del(samples); free(opus_samples); return result; }