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; }
PyObject* encoders_encode_shn(PyObject *dummy, PyObject *args, PyObject *keywds) { static char *kwlist[] = {"filename", "pcmreader", "is_big_endian", "signed_samples", "header_data", "footer_data", "block_size", NULL}; char *filename; FILE *output_file; BitstreamWriter* writer; pcmreader* pcmreader; int is_big_endian = 0; int signed_samples = 0; char* header_data; #ifdef PY_SSIZE_T_CLEAN Py_ssize_t header_size; #else int header_size; #endif char* footer_data = NULL; #ifdef PY_SSIZE_T_CLEAN Py_ssize_t footer_size = 0; #else int footer_size = 0; #endif unsigned block_size = 256; unsigned bytes_written = 0; unsigned i; /*fetch arguments*/ if (!PyArg_ParseTupleAndKeywords(args, keywds, "sO&iis#|s#I", kwlist, &filename, pcmreader_converter, &pcmreader, &is_big_endian, &signed_samples, &header_data, &header_size, &footer_data, &footer_size, &block_size)) return NULL; /*ensure PCMReader is compatible with Shorten*/ if ((pcmreader->bits_per_sample != 8) && (pcmreader->bits_per_sample != 16)) { pcmreader->del(pcmreader); PyErr_SetString(PyExc_ValueError, "unsupported bits per sample"); return NULL; } /*open given filename for writing*/ if ((output_file = fopen(filename, "wb")) == NULL) { PyErr_SetFromErrnoWithFilename(PyExc_IOError, filename); pcmreader->del(pcmreader); return NULL; } else { writer = bw_open(output_file, BS_BIG_ENDIAN); } /*write magic number and version*/ writer->build(writer, "4b 8u", "ajkg", 2); bw_add_callback(writer, byte_counter, &bytes_written); /*write Shorten header*/ write_header(writer, pcmreader->bits_per_sample, is_big_endian, signed_samples, pcmreader->channels, block_size); /*issue initial VERBATIM command with header data*/ write_unsigned(writer, COMMAND_SIZE, FN_VERBATIM); write_unsigned(writer, VERBATIM_SIZE, header_size); for (i = 0; i < header_size; i++) write_unsigned(writer, VERBATIM_BYTE_SIZE, (uint8_t)header_data[i]); /*process PCM frames*/ if (encode_audio(writer, pcmreader, signed_samples, block_size)) goto error; /*if there's footer data, issue a VERBATIM command for it*/ if ((footer_data != NULL) && (footer_size > 0)) { write_unsigned(writer, COMMAND_SIZE, FN_VERBATIM); write_unsigned(writer, VERBATIM_SIZE, footer_size); for (i = 0; i < footer_size; i++) write_unsigned(writer, VERBATIM_BYTE_SIZE, (uint8_t)footer_data[i]); } /*issue QUIT command*/ write_unsigned(writer, COMMAND_SIZE, FN_QUIT); /*pad output (not including header) to a multiple of 4 bytes*/ writer->byte_align(writer); while ((bytes_written % 4) != 0) { writer->write(writer, 8, 0); } /*deallocate temporary buffers and close files*/ pcmreader->del(pcmreader); writer->close(writer); Py_INCREF(Py_None); return Py_None; error: pcmreader->del(pcmreader); writer->close(writer); return NULL; }