/* Copyright (C) 2006 Jesse Chappell (AC3Jack) Copyright (C) 2012 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "JackAC3Encoder.h" #include "JackError.h" #include #include #include #define max(x,y) (((x)>(y)) ? (x) : (y)) #define min(x,y) (((x)<(y)) ? (x) : (y)) namespace Jack { #ifndef __ppc__ JackAC3Encoder::JackAC3Encoder(const JackAC3EncoderParams& params) { aften_set_defaults(&fAftenContext); fAftenContext.channels = params.channels; fAftenContext.samplerate = params.sample_rate; fAftenContext.params.bitrate = params.bitrate; int acmod = A52_ACMOD_MONO; int lfe = params.lfe; switch (params.channels) { case 1: acmod = A52_ACMOD_MONO; break; case 2: acmod = A52_ACMOD_STEREO; break; case 3: acmod = A52_ACMOD_3_0; break; case 4: acmod = A52_ACMOD_2_2; break; case 5: acmod = A52_ACMOD_3_2; break; default: break; } if (lfe) { fAftenContext.channels += 1; } fAftenContext.acmod = acmod; fAftenContext.lfe = lfe; fAftenContext.sample_format = A52_SAMPLE_FMT_FLT; fAftenContext.verbose = 1; fAftenContext.system.n_threads = 1; // create interleaved framebuffer for MAX_AC3_CHANNELS fSampleBuffer = new float[MAX_AC3_CHANNELS * A52_SAMPLES_PER_FRAME]; // create AC3 buffer fAC3Buffer = new unsigned char[A52_MAX_CODED_FRAME_SIZE]; memset(fAC3Buffer, 0, A52_MAX_CODED_FRAME_SIZE); fZeroBuffer = new unsigned char[SPDIF_FRAME_SIZE]; memset(fZeroBuffer, 0, SPDIF_FRAME_SIZE); fRingBuffer = jack_ringbuffer_create(32768); fOutSizeByte = 0; fFramePos = 0; fSampleRate = 0; fByteRate = 0; } bool JackAC3Encoder::Init(jack_nframes_t sample_rate) { fSampleRate = sample_rate; fByteRate = fSampleRate * sizeof(short) * 2; return (aften_encode_init(&fAftenContext) == 0); } JackAC3Encoder::~JackAC3Encoder() { aften_encode_close(&fAftenContext); delete [] fSampleBuffer; delete [] fAC3Buffer; delete [] fZeroBuffer; if (fRingBuffer) { jack_ringbuffer_free(fRingBuffer); } } void JackAC3Encoder::Process(float** inputs_buffer, float** outputs_buffer, int nframes) { // fill and process frame buffers as appropriate jack_nframes_t frames_left = A52_SAMPLES_PER_FRAME - fFramePos; jack_nframes_t offset = 0; while (offset < nframes) { if ((nframes - offset) >= frames_left) { // copy only frames_left more data jack_nframes_t pos = fFramePos * fAftenContext.channels; for (jack_nframes_t spos = offset; spos < offset + frames_left; ++spos) { for (size_t i = 0; i < fAftenContext.channels; ++i) { fSampleBuffer[pos + i] = inputs_buffer[i][spos]; } pos += fAftenContext.channels; } // use interleaved version #ifdef HAVE_AFTEN_NEW_API // note additional parameter 'nframes' // added in commit e1cbb66628de8aa496a75092d8d694234c67aa95 git://aften.git.sourceforge.net/gitroot/aften/aften int res = aften_encode_frame(&fAftenContext, fAC3Buffer + SPDIF_HEADER_SIZE, fSampleBuffer, nframes); #else // released version 0.0.8 hasn't the 'count' parameter int res = aften_encode_frame(&fAftenContext, fAC3Buffer + SPDIF_HEADER_SIZE, fSampleBuffer); #endif if (res < 0) { jack_error("aften_encode_frame error !!"); return; } fOutSizeByte = res; FillSpdifHeader(fAC3Buffer, fOutSizeByte + SPDIF_HEADER_SIZE); // push AC3 output to SPDIF ring buffer float calc_ac3byterate = (fOutSizeByte * fSampleRate / (float) A52_SAMPLES_PER_FRAME); jack_nframes_t silencebytes = (jack_nframes_t) (fOutSizeByte * (fByteRate / calc_ac3byterate)) - fOutSizeByte - SPDIF_HEADER_SIZE; jack_ringbuffer_write(fRingBuffer, (const char *)fAC3Buffer, fOutSizeByte + SPDIF_HEADER_SIZE); // write the proper remainder of zero padding (inefficient, should be memsetting) jack_ringbuffer_write(fRingBuffer, (const char *)fZeroBuffer, silencebytes); offset += frames_left; frames_left = A52_SAMPLES_PER_FRAME; fFramePos = 0; } else { // copy incoming data into frame buffers without processing jack_nframes_t pos = fFramePos * fAftenContext.channels; for (jack_nframes_t spos = offset; spos < nframes; ++spos) { for (size_t i = 0; i < fAftenContext.channels; ++i) { fSampleBuffer[pos + i] = inputs_buffer[i][spos]; } pos += fAftenContext.channels; } fFramePos += (nframes - offset); offset += (nframes-offset); } } Output2Driver(outputs_buffer, nframes); } void JackAC3Encoder::FillSpdifHeader(unsigned char* buf, int outsize) { // todo, use outsize and not assume the fixed frame size? int ac3outsize = outsize - SPDIF_HEADER_SIZE; buf[0] = 0x72; buf[1] = 0xf8; /* spdif syncword */ buf[2] = 0x1f; buf[3] = 0x4e; /* .............. */ buf[4] = 0x01; /* AC3 data */ buf[5] = buf[13] & 7; /* bsmod, stream = 0 */ buf[6] = (ac3outsize << 3) & 0xff; buf[7] = (ac3outsize >> 5) & 0xff; #if !IS_BIGENDIAN swab(buf+SPDIF_HEADER_SIZE, buf + SPDIF_HEADER_SIZE, ac3outsize); #endif } int JackAC3Encoder::Output2Driver(float** outputs, jack_nframes_t nframes) { int wrotebytes = 0; jack_nframes_t nframes_left = nframes; if (jack_ringbuffer_read_space(fRingBuffer) == 0) { // just write silence memset(outputs[0], 0, nframes * sizeof(jack_default_audio_sample_t)); memset(outputs[1], 0, nframes * sizeof(jack_default_audio_sample_t)); } else { jack_ringbuffer_data_t rb_data[2]; jack_ringbuffer_get_read_vector(fRingBuffer, rb_data); while (nframes_left > 0 && rb_data[0].len > 4) { jack_nframes_t towrite_frames = (rb_data[0].len) / (sizeof(short) * 2); towrite_frames = min(towrite_frames, nframes_left); // write and deinterleave into the two channels #if 1 sample_move_dS_s16(outputs[0] + (nframes - nframes_left), (char *) rb_data[0].buf, towrite_frames, sizeof(short) * 2); sample_move_dS_s16(outputs[1] + (nframes - nframes_left), (char *) rb_data[0].buf + sizeof(short), towrite_frames, sizeof(short) * 2); #else sample_move_dS_s16_24ph(outputs[0] + (nframes - nframes_left), (char *) rb_data[0].buf, towrite_frames, sizeof(short) * 2); sample_move_dS_s16_24ph(outputs[1] + (nframes - nframes_left), (char *) rb_data[0].buf + sizeof(short), towrite_frames, sizeof(short) * 2); #endif wrotebytes = towrite_frames * sizeof(short) * 2; nframes_left -= towrite_frames; jack_ringbuffer_read_advance(fRingBuffer, wrotebytes); jack_ringbuffer_get_read_vector(fRingBuffer, rb_data); } if (nframes_left > 0) { // write silence memset(outputs[0] + (nframes - nframes_left), 0, (nframes_left) * sizeof(jack_default_audio_sample_t)); memset(outputs[1] + (nframes - nframes_left), 0, (nframes_left) * sizeof(jack_default_audio_sample_t)); } } return wrotebytes; } void JackAC3Encoder::sample_move_dS_s16(jack_default_audio_sample_t* dst, char *src, jack_nframes_t nsamples, unsigned long src_skip) { /* ALERT: signed sign-extension portability !!! */ while (nsamples--) { *dst = (*((short *) src)) / SAMPLE_MAX_16BIT; dst++; src += src_skip; } } void JackAC3Encoder::sample_move_dS_s16_24ph(jack_default_audio_sample_t* dst, char *src, jack_nframes_t nsamples, unsigned long src_skip) { /* ALERT: signed sign-extension portability !!! */ while (nsamples--) { *dst = (((int)(*((short *) src))) << 8) / SAMPLE_MAX_24BIT; dst++; src += src_skip; } } void JackAC3Encoder::GetChannelName(const char* name, const char* alias, char* portname, int channel) { /* * 2 channels = L, R * 3 channels = L, C, R * 4 channels = L, R, LS, RS * 5 ch = L, C, R, LS, RS * 6 ch = L, C, R, LS, RS, LFE */ const char* AC3_name = ""; switch (channel) { case 0: AC3_name = "AC3_1_Left"; break; case 1: if (fAftenContext.channels == 2 || fAftenContext.channels == 4) { AC3_name = "AC3_2_Right"; } else { AC3_name = "AC3_2_Center"; } break; case 2: if (fAftenContext.channels == 4) { AC3_name = "AC3_3_LeftSurround"; } else { AC3_name = "AC3_3_Right"; } break; case 3: if (fAftenContext.channels == 4) { AC3_name = "AC3_4_RightSurround"; } else { AC3_name = "AC3_4_LeftSurround"; } break; case 4: if (fAftenContext.channels > 4) { AC3_name = "AC3_5_RightSurround"; } break; default: break; } // Last channel if (fAftenContext.lfe && (channel == fAftenContext.channels - 1)) { sprintf(portname, "%s:%s:AC3_%d_LFE", name, alias, fAftenContext.channels); } else { sprintf(portname, "%s:%s:%s", name, alias, AC3_name); } } #endif } // end of namespace