Esempio n. 1
0
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;
}
Esempio n. 2
0
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;
}