| @@ -0,0 +1,310 @@ | |||
| /* | |||
| Copyright (C) 2006 Jesse Chappell <jesse@essej.net> (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 <unistd.h> | |||
| #include <string.h> | |||
| #include <stdio.h> | |||
| #define max(x,y) (((x)>(y)) ? (x) : (y)) | |||
| #define min(x,y) (((x)<(y)) ? (x) : (y)) | |||
| namespace Jack | |||
| { | |||
| 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; | |||
| 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 | |||
| int res = aften_encode_frame(&fAftenContext, fAC3Buffer + SPDIF_HEADER_SIZE, fSampleBuffer); | |||
| 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(char* name, 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 | |||
| */ | |||
| switch (channel) { | |||
| case 0: | |||
| strcpy(name, "AC3_1_Left"); | |||
| break; | |||
| case 1: | |||
| if (fAftenContext.channels == 2 || fAftenContext.channels == 4) { | |||
| strcpy(name, "AC3_2_Right"); | |||
| } else { | |||
| strcpy(name, "AC3_2_Center"); | |||
| } | |||
| break; | |||
| case 2: | |||
| if (fAftenContext.channels == 4) { | |||
| strcpy(name, "AC3_3_LeftSurround"); | |||
| } else { | |||
| strcpy(name, "AC3_3_Right"); | |||
| } | |||
| break; | |||
| case 3: | |||
| if (fAftenContext.channels == 4) { | |||
| strcpy(name, "AC3_4_RightSurround"); | |||
| } else { | |||
| strcpy(name, "AC3_4_LeftSurround"); | |||
| } | |||
| break; | |||
| case 4: | |||
| if (fAftenContext.channels > 4) { | |||
| strcpy(name, "AC3_5_RightSurround"); | |||
| } | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| // Last channel | |||
| if (fAftenContext.lfe && (channel == fAftenContext.channels - 1)) { | |||
| sprintf(name, "AC3_%d_LFE", fAftenContext.channels + 1); | |||
| } | |||
| } | |||
| } // end of namespace | |||
| @@ -0,0 +1,90 @@ | |||
| /* | |||
| Copyright (C) 2006 Jesse Chappell <jesse@essej.net> (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. | |||
| */ | |||
| #ifndef __JackAC3Encoder__ | |||
| #define __JackAC3Encoder__ | |||
| #include <aften/aften.h> | |||
| #include <aften/aften-types.h> | |||
| #include "ringbuffer.h" | |||
| #include "types.h" | |||
| #define MAX_AC3_CHANNELS 6 | |||
| #define SPDIF_HEADER_SIZE 8 | |||
| #define SPDIF_FRAME_SIZE 6144 | |||
| #define SAMPLE_MAX_16BIT 32768.0f | |||
| #define SAMPLE_MAX_24BIT 8388608.0f | |||
| namespace Jack | |||
| { | |||
| struct JackAC3EncoderParams | |||
| { | |||
| int64_t duration; | |||
| unsigned int channels; | |||
| int bitdepth; | |||
| int bitrate; | |||
| unsigned int sample_rate; | |||
| bool lfe; | |||
| }; | |||
| class JackAC3Encoder | |||
| { | |||
| private: | |||
| AftenContext fAftenContext; | |||
| jack_ringbuffer_t* fRingBuffer; | |||
| float* fSampleBuffer; | |||
| unsigned char* fAC3Buffer; | |||
| unsigned char* fZeroBuffer; | |||
| int fOutSizeByte; | |||
| jack_nframes_t fFramePos; | |||
| jack_nframes_t fSampleRate; | |||
| jack_nframes_t fByteRate; | |||
| void FillSpdifHeader(unsigned char* buf, int outsize); | |||
| int Output2Driver(float** outputs, jack_nframes_t nframes); | |||
| void sample_move_dS_s16(jack_default_audio_sample_t* dst, char *src, jack_nframes_t nsamples, unsigned long src_skip); | |||
| void sample_move_dS_s16_24ph(jack_default_audio_sample_t* dst, char *src, jack_nframes_t nsamples, unsigned long src_skip); | |||
| public: | |||
| JackAC3Encoder(const JackAC3EncoderParams& params); | |||
| virtual ~JackAC3Encoder(); | |||
| bool Init(jack_nframes_t sample_rate); | |||
| void Process(float** inputs, float** outputs, int nframes); | |||
| void GetChannelName(char* name, int channel); | |||
| }; | |||
| typedef JackAC3Encoder * JackAC3EncoderPtr; | |||
| } // end of namespace | |||
| #endif | |||
| @@ -878,6 +878,10 @@ | |||
| 4BA7BE240DC2350D00AA3457 /* Jackservermp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B35C4FC0D4731D1000DE7AE /* Jackservermp.framework */; }; | |||
| 4BA7BE270DC2352A00AA3457 /* Jackservermp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B35C4FC0D4731D1000DE7AE /* Jackservermp.framework */; }; | |||
| 4BA7FECA0D8E76650017FF73 /* control.c in Sources */ = {isa = PBXBuildFile; fileRef = 4BA7FEC80D8E76650017FF73 /* control.c */; }; | |||
| 4BAA150314F04FB600402512 /* JackAC3Encoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAA150114F04FB600402512 /* JackAC3Encoder.cpp */; }; | |||
| 4BAA150414F04FB600402512 /* JackAC3Encoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BAA150214F04FB600402512 /* JackAC3Encoder.h */; }; | |||
| 4BAA150514F04FB600402512 /* JackAC3Encoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAA150114F04FB600402512 /* JackAC3Encoder.cpp */; }; | |||
| 4BAA150614F04FB600402512 /* JackAC3Encoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BAA150214F04FB600402512 /* JackAC3Encoder.h */; }; | |||
| 4BAB95B80B9E20B800A0C723 /* JackPortType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB95B60B9E20B800A0C723 /* JackPortType.cpp */; }; | |||
| 4BAB95B90B9E20B800A0C723 /* JackPortType.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BAB95B70B9E20B800A0C723 /* JackPortType.h */; }; | |||
| 4BAB95BA0B9E20B800A0C723 /* JackPortType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB95B60B9E20B800A0C723 /* JackPortType.cpp */; }; | |||
| @@ -1887,6 +1891,8 @@ | |||
| 4BA692D60CBE4CC600EAD520 /* ipunload.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = ipunload.c; path = "../example-clients/ipunload.c"; sourceTree = SOURCE_ROOT; }; | |||
| 4BA7FEC30D8E76270017FF73 /* jack_server_control */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = jack_server_control; sourceTree = BUILT_PRODUCTS_DIR; }; | |||
| 4BA7FEC80D8E76650017FF73 /* control.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = control.c; path = "../example-clients/control.c"; sourceTree = SOURCE_ROOT; }; | |||
| 4BAA150114F04FB600402512 /* JackAC3Encoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JackAC3Encoder.cpp; path = ../common/JackAC3Encoder.cpp; sourceTree = SOURCE_ROOT; }; | |||
| 4BAA150214F04FB600402512 /* JackAC3Encoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JackAC3Encoder.h; path = ../common/JackAC3Encoder.h; sourceTree = SOURCE_ROOT; }; | |||
| 4BAB95B60B9E20B800A0C723 /* JackPortType.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = JackPortType.cpp; path = ../common/JackPortType.cpp; sourceTree = SOURCE_ROOT; }; | |||
| 4BAB95B70B9E20B800A0C723 /* JackPortType.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = JackPortType.h; path = ../common/JackPortType.h; sourceTree = SOURCE_ROOT; }; | |||
| 4BAB95EC0B9E21A500A0C723 /* JackAudioPort.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = JackAudioPort.cpp; path = ../common/JackAudioPort.cpp; sourceTree = SOURCE_ROOT; }; | |||
| @@ -3238,8 +3244,8 @@ | |||
| 4B21794D13E2EEA60095B3E5 /* JackTimedDriver.cpp */, | |||
| 4BF3390D0F8B86AF0080FB5B /* MIDI */, | |||
| 4B19B3010E23629800DD4A82 /* Adapter */, | |||
| BA222AEA0DC88379001A17F4 /* Net */, | |||
| 4BD56D8707968982006D44F9 /* Threaded */, | |||
| BA222AEA0DC88379001A17F4 /* Net */, | |||
| 4BD56D8607968979006D44F9 /* Audio */, | |||
| ); | |||
| name = Driver; | |||
| @@ -3353,6 +3359,8 @@ | |||
| 4BD56D8607968979006D44F9 /* Audio */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| 4BAA150114F04FB600402512 /* JackAC3Encoder.cpp */, | |||
| 4BAA150214F04FB600402512 /* JackAC3Encoder.h */, | |||
| 4BBB00CF0E72614F0018AB1B /* JackPortAudioDevices.cpp */, | |||
| 4BBB00D00E72614F0018AB1B /* JackPortAudioDevices.h */, | |||
| 4BBB00D10E72614F0018AB1B /* JackPortAudioDriver.cpp */, | |||
| @@ -3843,6 +3851,7 @@ | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| 4BA4ADB50E87AB2600F26C85 /* JackCoreAudioDriver.h in Headers */, | |||
| 4BAA150614F04FB600402512 /* JackAC3Encoder.h in Headers */, | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| @@ -4277,6 +4286,7 @@ | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| 4BE5FECE0E725C090020B576 /* JackCoreAudioDriver.h in Headers */, | |||
| 4BAA150414F04FB600402512 /* JackAC3Encoder.h in Headers */, | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| @@ -7381,6 +7391,7 @@ | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| 4BA4ADB40E87AB2500F26C85 /* JackCoreAudioDriver.cpp in Sources */, | |||
| 4BAA150514F04FB600402512 /* JackAC3Encoder.cpp in Sources */, | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| @@ -7791,6 +7802,7 @@ | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| 4BE5FECD0E725C090020B576 /* JackCoreAudioDriver.cpp in Sources */, | |||
| 4BAA150314F04FB600402512 /* JackAC3Encoder.cpp in Sources */, | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| @@ -11851,6 +11863,8 @@ | |||
| "-DJACK_32_64", | |||
| ); | |||
| OTHER_LDFLAGS = ( | |||
| libaften_pcm.a, | |||
| libaften_static.a, | |||
| "-framework", | |||
| Jackservermp, | |||
| "-framework", | |||
| @@ -27,6 +27,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||
| #include "JackGlobals.h" | |||
| #include "JackTools.h" | |||
| #include "JackLockedEngine.h" | |||
| #include "JackAC3Encoder.h" | |||
| #include <sstream> | |||
| #include <iostream> | |||
| @@ -323,17 +324,37 @@ int JackCoreAudioDriver::Read() | |||
| int JackCoreAudioDriver::Write() | |||
| { | |||
| for (int i = 0; i < fPlaybackChannels; i++) { | |||
| if (fGraphManager->GetConnectionsNum(fPlaybackPortList[i]) > 0) { | |||
| jack_default_audio_sample_t* buffer = GetOutputBuffer(i); | |||
| int size = sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize; | |||
| memcpy((jack_default_audio_sample_t*)fDriverOutputData->mBuffers[i].mData, buffer, size); | |||
| // Monitor ports | |||
| if (fWithMonitorPorts && fGraphManager->GetConnectionsNum(fMonitorPortList[i]) > 0) { | |||
| memcpy(GetMonitorBuffer(i), buffer, size); | |||
| if (fAC3Encoder) { | |||
| // AC3 encoding and SPDIF write | |||
| jack_default_audio_sample_t* AC3_inputs[MAX_AC3_CHANNELS]; | |||
| jack_default_audio_sample_t* AC3_outputs[2]; | |||
| for (int i = 0; i < fPlaybackChannels; i++) { | |||
| AC3_inputs[i] = GetOutputBuffer(i); | |||
| // If not connected, clear the buffer | |||
| if (fGraphManager->GetConnectionsNum(fPlaybackPortList[i]) == 0) { | |||
| memset(AC3_inputs[i], 0, sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize); | |||
| } | |||
| } | |||
| AC3_outputs[0] = (jack_default_audio_sample_t*)fDriverOutputData->mBuffers[0].mData; | |||
| AC3_outputs[1] = (jack_default_audio_sample_t*)fDriverOutputData->mBuffers[1].mData; | |||
| fAC3Encoder->Process(AC3_inputs, AC3_outputs, fEngineControl->fBufferSize); | |||
| } else { | |||
| // Standard write | |||
| for (int i = 0; i < fPlaybackChannels; i++) { | |||
| if (fGraphManager->GetConnectionsNum(fPlaybackPortList[i]) > 0) { | |||
| jack_default_audio_sample_t* buffer = GetOutputBuffer(i); | |||
| int size = sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize; | |||
| memcpy((jack_default_audio_sample_t*)fDriverOutputData->mBuffers[i].mData, buffer, size); | |||
| // Monitor ports | |||
| if (fWithMonitorPorts && fGraphManager->GetConnectionsNum(fMonitorPortList[i]) > 0) { | |||
| memcpy(GetMonitorBuffer(i), buffer, size); | |||
| } | |||
| } else { | |||
| memset((jack_default_audio_sample_t*)fDriverOutputData->mBuffers[i].mData, 0, sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize); | |||
| } | |||
| } else { | |||
| memset((jack_default_audio_sample_t*)fDriverOutputData->mBuffers[i].mData, 0, sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize); | |||
| } | |||
| } | |||
| return 0; | |||
| @@ -461,7 +482,7 @@ OSStatus JackCoreAudioDriver::DeviceNotificationCallback(AudioDeviceID inDevice, | |||
| } | |||
| case kAudioDeviceProcessorOverload: { | |||
| jack_error("JackCoreAudioDriver::DeviceNotificationCallback kAudioDeviceProcessorOverload"); | |||
| jack_error("DeviceNotificationCallback kAudioDeviceProcessorOverload"); | |||
| jack_time_t cur_time = GetMicroSeconds(); | |||
| driver->NotifyXRun(cur_time, float(cur_time - driver->fBeginDateUst)); // Better this value than nothing... | |||
| break; | |||
| @@ -688,6 +709,7 @@ OSStatus JackCoreAudioDriver::GetStreamLatencies(AudioDeviceID device, bool isIn | |||
| JackCoreAudioDriver::JackCoreAudioDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) | |||
| : JackAudioDriver(name, alias, engine, table), | |||
| fAC3Encoder(NULL), | |||
| fJackInputData(NULL), | |||
| fDriverOutputData(NULL), | |||
| fPluginID(0), | |||
| @@ -699,7 +721,9 @@ JackCoreAudioDriver::JackCoreAudioDriver(const char* name, const char* alias, Ja | |||
| {} | |||
| JackCoreAudioDriver::~JackCoreAudioDriver() | |||
| {} | |||
| { | |||
| delete fAC3Encoder; | |||
| } | |||
| OSStatus JackCoreAudioDriver::DestroyAggregateDevice() | |||
| { | |||
| @@ -714,14 +738,14 @@ OSStatus JackCoreAudioDriver::DestroyAggregateDevice() | |||
| osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize); | |||
| if (osErr != noErr) { | |||
| jack_error("JackCoreAudioDriver::DestroyAggregateDevice : AudioObjectGetPropertyDataSize error"); | |||
| jack_error("DestroyAggregateDevice : AudioObjectGetPropertyDataSize error"); | |||
| printError(osErr); | |||
| return osErr; | |||
| } | |||
| osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, 0, NULL, &outDataSize, &fDeviceID); | |||
| if (osErr != noErr) { | |||
| jack_error("JackCoreAudioDriver::DestroyAggregateDevice : AudioObjectGetPropertyData error"); | |||
| jack_error("DestroyAggregateDevice : AudioObjectGetPropertyData error"); | |||
| printError(osErr); | |||
| return osErr; | |||
| } | |||
| @@ -793,18 +817,18 @@ OSStatus JackCoreAudioDriver::CreateAggregateDeviceAux(vector<AudioDeviceID> cap | |||
| for (UInt32 i = 0; i < captureDeviceID.size(); i++) { | |||
| if (SetupSampleRateAux(captureDeviceID[i], samplerate) < 0) { | |||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice : cannot set SR of input device"); | |||
| jack_error("CreateAggregateDevice : cannot set SR of input device"); | |||
| } else { | |||
| // Check clock domain | |||
| osErr = AudioDeviceGetProperty(captureDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain); | |||
| if (osErr != 0) { | |||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice : kAudioDevicePropertyClockDomain error"); | |||
| jack_error("CreateAggregateDevice : kAudioDevicePropertyClockDomain error"); | |||
| printError(osErr); | |||
| } else { | |||
| keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain; | |||
| jack_log("JackCoreAudioDriver::CreateAggregateDevice : input clockdomain = %d", clockdomain); | |||
| if (clockdomain != 0 && clockdomain != keptclockdomain) { | |||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice : devices do not share the same clock!! clock drift compensation would be needed..."); | |||
| jack_error("CreateAggregateDevice : devices do not share the same clock!! clock drift compensation would be needed..."); | |||
| need_clock_drift_compensation = true; | |||
| } | |||
| } | |||
| @@ -813,18 +837,18 @@ OSStatus JackCoreAudioDriver::CreateAggregateDeviceAux(vector<AudioDeviceID> cap | |||
| for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { | |||
| if (SetupSampleRateAux(playbackDeviceID[i], samplerate) < 0) { | |||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice : cannot set SR of output device"); | |||
| jack_error("CreateAggregateDevice : cannot set SR of output device"); | |||
| } else { | |||
| // Check clock domain | |||
| osErr = AudioDeviceGetProperty(playbackDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain); | |||
| if (osErr != 0) { | |||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice : kAudioDevicePropertyClockDomain error"); | |||
| jack_error("CreateAggregateDevice : kAudioDevicePropertyClockDomain error"); | |||
| printError(osErr); | |||
| } else { | |||
| keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain; | |||
| jack_log("JackCoreAudioDriver::CreateAggregateDevice : output clockdomain = %d", clockdomain); | |||
| if (clockdomain != 0 && clockdomain != keptclockdomain) { | |||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice : devices do not share the same clock!! clock drift compensation would be needed..."); | |||
| jack_error("CreateAggregateDevice : devices do not share the same clock!! clock drift compensation would be needed..."); | |||
| need_clock_drift_compensation = true; | |||
| } | |||
| } | |||
| @@ -853,7 +877,7 @@ OSStatus JackCoreAudioDriver::CreateAggregateDeviceAux(vector<AudioDeviceID> cap | |||
| osErr = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyPlugInForBundleID, &outSize, &outWritable); | |||
| if (osErr != noErr) { | |||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioHardwareGetPropertyInfo kAudioHardwarePropertyPlugInForBundleID error"); | |||
| jack_error("CreateAggregateDevice : AudioHardwareGetPropertyInfo kAudioHardwarePropertyPlugInForBundleID error"); | |||
| printError(osErr); | |||
| return osErr; | |||
| } | |||
| @@ -869,7 +893,7 @@ OSStatus JackCoreAudioDriver::CreateAggregateDeviceAux(vector<AudioDeviceID> cap | |||
| osErr = AudioHardwareGetProperty(kAudioHardwarePropertyPlugInForBundleID, &outSize, &pluginAVT); | |||
| if (osErr != noErr) { | |||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioHardwareGetProperty kAudioHardwarePropertyPlugInForBundleID error"); | |||
| jack_error("CreateAggregateDevice : AudioHardwareGetProperty kAudioHardwarePropertyPlugInForBundleID error"); | |||
| printError(osErr); | |||
| return osErr; | |||
| } | |||
| @@ -986,14 +1010,14 @@ OSStatus JackCoreAudioDriver::CreateAggregateDeviceAux(vector<AudioDeviceID> cap | |||
| osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize); | |||
| if (osErr != noErr) { | |||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectGetPropertyDataSize error"); | |||
| jack_error("CreateAggregateDevice : AudioObjectGetPropertyDataSize error"); | |||
| printError(osErr); | |||
| goto error; | |||
| } | |||
| osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, sizeof(aggDeviceDict), &aggDeviceDict, &outDataSize, outAggregateDevice); | |||
| if (osErr != noErr) { | |||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectGetPropertyData error"); | |||
| jack_error("CreateAggregateDevice : AudioObjectGetPropertyData error"); | |||
| printError(osErr); | |||
| goto error; | |||
| } | |||
| @@ -1012,7 +1036,7 @@ OSStatus JackCoreAudioDriver::CreateAggregateDeviceAux(vector<AudioDeviceID> cap | |||
| outDataSize = sizeof(CFMutableArrayRef); | |||
| osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &subDevicesArray); | |||
| if (osErr != noErr) { | |||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectSetPropertyData for sub-device list error"); | |||
| jack_error("CreateAggregateDevice : AudioObjectSetPropertyData for sub-device list error"); | |||
| printError(osErr); | |||
| goto error; | |||
| } | |||
| @@ -1032,7 +1056,7 @@ OSStatus JackCoreAudioDriver::CreateAggregateDeviceAux(vector<AudioDeviceID> cap | |||
| outDataSize = sizeof(CFStringRef); | |||
| osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &captureDeviceUID[0]); // First apture is master... | |||
| if (osErr != noErr) { | |||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice : AudioObjectSetPropertyData for master device error"); | |||
| jack_error("CreateAggregateDevice : AudioObjectSetPropertyData for master device error"); | |||
| printError(osErr); | |||
| goto error; | |||
| } | |||
| @@ -1050,7 +1074,7 @@ OSStatus JackCoreAudioDriver::CreateAggregateDeviceAux(vector<AudioDeviceID> cap | |||
| // Get the property data size | |||
| osErr = AudioObjectGetPropertyDataSize(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize); | |||
| if (osErr != noErr) { | |||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error"); | |||
| jack_error("CreateAggregateDevice kAudioObjectPropertyOwnedObjects error"); | |||
| printError(osErr); | |||
| } | |||
| @@ -1062,7 +1086,7 @@ OSStatus JackCoreAudioDriver::CreateAggregateDeviceAux(vector<AudioDeviceID> cap | |||
| osErr = AudioObjectGetPropertyData(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize, subDevices); | |||
| if (osErr != noErr) { | |||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error"); | |||
| jack_error("CreateAggregateDevice kAudioObjectPropertyOwnedObjects error"); | |||
| printError(osErr); | |||
| } | |||
| @@ -1071,7 +1095,7 @@ OSStatus JackCoreAudioDriver::CreateAggregateDeviceAux(vector<AudioDeviceID> cap | |||
| UInt32 theDriftCompensationValue = 1; | |||
| osErr = AudioObjectSetPropertyData(subDevices[index], &theAddressDrift, 0, NULL, sizeof(UInt32), &theDriftCompensationValue); | |||
| if (osErr != noErr) { | |||
| jack_error("JackCoreAudioDriver::CreateAggregateDevice kAudioSubDevicePropertyDriftCompensation error"); | |||
| jack_error("CreateAggregateDevice kAudioSubDevicePropertyDriftCompensation error"); | |||
| printError(osErr); | |||
| } | |||
| } | |||
| @@ -1255,7 +1279,7 @@ int JackCoreAudioDriver::SetupChannels(bool capturing, bool playing, int& inchan | |||
| if (capturing) { | |||
| err = GetTotalChannels(fDeviceID, in_nChannels, true); | |||
| if (err != noErr) { | |||
| jack_error("JackCoreAudioDriver::SetupChannels : cannot get input channel number"); | |||
| jack_error("SetupChannels : cannot get input channel number"); | |||
| printError(err); | |||
| return -1; | |||
| } else { | |||
| @@ -1335,7 +1359,7 @@ int JackCoreAudioDriver::SetupBufferSize(jack_nframes_t buffer_size) | |||
| err = AudioDeviceSetProperty(fDeviceID, NULL, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyBufferFrameSize, outSize, &tmp_buffer_size); | |||
| if (err != noErr) { | |||
| jack_error("JackCoreAudioDriver::SetupBufferSize : cannot set buffer size = %ld", tmp_buffer_size); | |||
| jack_error("SetupBufferSize : cannot set buffer size = %ld", tmp_buffer_size); | |||
| printError(err); | |||
| goto error; | |||
| } | |||
| @@ -1850,7 +1874,10 @@ int JackCoreAudioDriver::Open(jack_nframes_t buffer_size, | |||
| int async_output_latency, | |||
| int computation_grain, | |||
| bool hogged, | |||
| bool clock_drift) | |||
| bool clock_drift, | |||
| bool ac3_encoding, | |||
| int ac3_bitrate, | |||
| bool ac3_lfe) | |||
| { | |||
| int in_nChannels = 0; | |||
| int out_nChannels = 0; | |||
| @@ -1890,7 +1917,7 @@ int JackCoreAudioDriver::Open(jack_nframes_t buffer_size, | |||
| AudioObjectPropertyAddress theAddress = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; | |||
| OSStatus osErr = AudioObjectSetPropertyData (kAudioObjectSystemObject, &theAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop); | |||
| if (osErr != noErr) { | |||
| jack_error("JackCoreAudioDriver::Open kAudioHardwarePropertyRunLoop error"); | |||
| jack_error("Open kAudioHardwarePropertyRunLoop error"); | |||
| printError(osErr); | |||
| } | |||
| } | |||
| @@ -1922,6 +1949,20 @@ int JackCoreAudioDriver::Open(jack_nframes_t buffer_size, | |||
| if (SetupSampleRate(sample_rate) < 0) { | |||
| goto error; | |||
| } | |||
| if (ac3_encoding) { | |||
| JackAC3EncoderParams params; | |||
| memset(¶ms, 0, sizeof(JackAC3EncoderParams)); | |||
| params.bitrate = ac3_bitrate; | |||
| params.channels = outchannels; | |||
| params.sample_rate = sample_rate; | |||
| params.lfe = ac3_lfe; | |||
| fAC3Encoder = new JackAC3Encoder(params); | |||
| if (!fAC3Encoder || !fAC3Encoder->Init(sample_rate)) { | |||
| jack_error("Cannot allocate of init AC3 encoder"); | |||
| goto error; | |||
| } | |||
| } | |||
| if (OpenAUHAL(capturing, playing, inchannels, outchannels, in_nChannels, out_nChannels, parsed_chan_in_list, parsed_chan_out_list, buffer_size, sample_rate) < 0) { | |||
| goto error; | |||
| @@ -2112,6 +2153,15 @@ int JackCoreAudioDriver::Attach() | |||
| } | |||
| } | |||
| } | |||
| if (fAC3Encoder) { | |||
| // Setup specific AC3 channels names | |||
| for (int i = 0; i < fPlaybackChannels; i++) { | |||
| fAC3Encoder->GetChannelName(alias, i); | |||
| port = fGraphManager->GetPort(port_index); | |||
| port->SetAlias(alias); | |||
| } | |||
| } | |||
| UpdateLatencies(); | |||
| @@ -2274,6 +2324,15 @@ extern "C" | |||
| value.i = 0; | |||
| jack_driver_descriptor_add_parameter(desc, &filler, "monitor", 'm', JackDriverParamBool, &value, NULL, "Provide monitor ports for the output", NULL); | |||
| value.i = 0; | |||
| jack_driver_descriptor_add_parameter(desc, &filler, "AC3-encoding", 'a', JackDriverParamBool, &value, NULL, "AC3 multi-channels encoding", NULL); | |||
| value.i = 448; | |||
| jack_driver_descriptor_add_parameter(desc, &filler, "AC3-bitrate", 'b', JackDriverParamUInt, &value, NULL, "AC3 bitrate", NULL); | |||
| value.i = 0; | |||
| jack_driver_descriptor_add_parameter(desc, &filler, "AC3-LFE", 'f', JackDriverParamBool, &value, NULL, "AC3 LFE channel", NULL); | |||
| value.i = TRUE; | |||
| jack_driver_descriptor_add_parameter(desc, &filler, "duplex", 'D', JackDriverParamBool, &value, NULL, "Provide both capture and playback ports", NULL); | |||
| @@ -2330,6 +2389,9 @@ extern "C" | |||
| int computation_grain = -1; | |||
| bool hogged = false; | |||
| bool clock_drift = false; | |||
| bool ac3_encoding = false; | |||
| int ac3_bitrate = 448; | |||
| bool ac3_lfe = false; | |||
| for (node = params; node; node = jack_slist_next(node)) { | |||
| param = (const jack_driver_param_t *) node->data; | |||
| @@ -2383,6 +2445,18 @@ extern "C" | |||
| case 'm': | |||
| monitor = param->value.i; | |||
| break; | |||
| case 'a': | |||
| ac3_encoding = param->value.i; | |||
| break; | |||
| case 'b': | |||
| ac3_bitrate = param->value.i; | |||
| break; | |||
| case 'f': | |||
| ac3_lfe = param->value.i; | |||
| break; | |||
| case 'r': | |||
| srate = param->value.ui; | |||
| @@ -2449,7 +2523,8 @@ extern "C" | |||
| systemic_output_latency, | |||
| async_output_latency, | |||
| computation_grain, | |||
| hogged, clock_drift) == 0) { | |||
| hogged, clock_drift, | |||
| ac3_encoding, ac3_bitrate, ac3_lfe) == 0) { | |||
| return driver; | |||
| } else { | |||
| delete driver; | |||
| @@ -50,10 +50,14 @@ typedef UInt8 CAAudioHardwareDeviceSectionID; | |||
| \todo hardware monitoring | |||
| */ | |||
| class JackAC3Encoder; | |||
| class JackCoreAudioDriver : public JackAudioDriver | |||
| { | |||
| private: | |||
| JackAC3Encoder* fAC3Encoder; | |||
| AudioUnit fAUHAL; | |||
| @@ -180,7 +184,10 @@ class JackCoreAudioDriver : public JackAudioDriver | |||
| int async_output_latency, | |||
| int computation_grain, | |||
| bool hogged, | |||
| bool clock_drift); | |||
| bool clock_drift, | |||
| bool ac3_encoding, | |||
| int ac3_bitrate, | |||
| bool ac3_lfe); | |||
| int Close(); | |||
| int Attach(); | |||