diff --git a/common/JackAC3Encoder.cpp b/common/JackAC3Encoder.cpp new file mode 100644 index 00000000..71a9deb1 --- /dev/null +++ b/common/JackAC3Encoder.cpp @@ -0,0 +1,313 @@ +/* +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 +{ + +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(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); + } +} + +} // end of namespace \ No newline at end of file diff --git a/common/JackAC3Encoder.h b/common/JackAC3Encoder.h new file mode 100644 index 00000000..963f63bd --- /dev/null +++ b/common/JackAC3Encoder.h @@ -0,0 +1,90 @@ +/* +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. + +*/ + +#ifndef __JackAC3Encoder__ +#define __JackAC3Encoder__ + +#include +#include + +#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(const char* name, const char* alias, char* portname, int channel); +}; + +typedef JackAC3Encoder * JackAC3EncoderPtr; + +} // end of namespace + +#endif diff --git a/macosx/Jackdmp.xcodeproj/project.pbxproj b/macosx/Jackdmp.xcodeproj/project.pbxproj index 06cd89cc..5b845a7c 100644 --- a/macosx/Jackdmp.xcodeproj/project.pbxproj +++ b/macosx/Jackdmp.xcodeproj/project.pbxproj @@ -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", diff --git a/macosx/coreaudio/JackCoreAudioDriver.cpp b/macosx/coreaudio/JackCoreAudioDriver.cpp index f94939f3..62bc77a9 100644 --- a/macosx/coreaudio/JackCoreAudioDriver.cpp +++ b/macosx/coreaudio/JackCoreAudioDriver.cpp @@ -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 #include @@ -49,7 +50,7 @@ static void PrintStreamDesc(AudioStreamBasicDescription *inDesc) { jack_log("- - - - - - - - - - - - - - - - - - - -"); jack_log(" Sample Rate:%f", inDesc->mSampleRate); - jack_log(" Format ID:%.*s", (int) sizeof(inDesc->mFormatID), (char*)&inDesc->mFormatID); + jack_log(" Format ID:%.*s", (int)sizeof(inDesc->mFormatID), (char*)&inDesc->mFormatID); jack_log(" Format Flags:%lX", inDesc->mFormatFlags); jack_log(" Bytes per Packet:%ld", inDesc->mBytesPerPacket); jack_log(" Frames per Packet:%ld", inDesc->mFramesPerPacket); @@ -123,7 +124,7 @@ static void printError(OSStatus err) jack_log("error code : kAudioHardwareUnsupportedOperationError"); break; default: - Print4CharCode("error code : unknown", err); + Print4CharCode("error code : unknown ", err); break; } } @@ -191,7 +192,6 @@ static bool CheckAvailableDevice(AudioDeviceID device_id) if (device_id == devices[i]) { return true; } - } return false; @@ -323,17 +323,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; @@ -353,7 +373,7 @@ OSStatus JackCoreAudioDriver::SRNotificationCallback(AudioDeviceID inDevice, jack_log("JackCoreAudioDriver::SRNotificationCallback kAudioDevicePropertyNominalSampleRate"); // Check new sample rate Float64 tmp_sample_rate; - UInt32 outSize = sizeof(Float64); + UInt32 outSize = sizeof(Float64); OSStatus err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outSize, &tmp_sample_rate); if (err != noErr) { jack_error("Cannot get current sample rate"); @@ -371,7 +391,7 @@ OSStatus JackCoreAudioDriver::SRNotificationCallback(AudioDeviceID inDevice, OSStatus JackCoreAudioDriver::BSNotificationCallback(AudioDeviceID inDevice, UInt32 inChannel, - Boolean isInput, + Boolean isInput, AudioDevicePropertyID inPropertyID, void* inClientData) { @@ -383,7 +403,7 @@ OSStatus JackCoreAudioDriver::BSNotificationCallback(AudioDeviceID inDevice, jack_log("JackCoreAudioDriver::BSNotificationCallback kAudioDevicePropertyBufferFrameSize"); // Check new buffer size UInt32 tmp_buffer_size; - UInt32 outSize = sizeof(UInt32); + UInt32 outSize = sizeof(UInt32); OSStatus err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyBufferFrameSize, &outSize, &tmp_buffer_size); if (err != noErr) { jack_error("Cannot get current buffer size"); @@ -406,18 +426,17 @@ OSStatus JackCoreAudioDriver::AudioHardwareNotificationCallback(AudioHardwarePro switch (inPropertyID) { - case kAudioHardwarePropertyDevices: { - jack_log("JackCoreAudioDriver::AudioHardwareNotificationCallback kAudioHardwarePropertyDevices"); - DisplayDeviceNames(); - AudioDeviceID captureID, playbackID; - if (CheckAvailableDevice(driver->fDeviceID) || - (CheckAvailableDeviceName(driver->fCaptureUID, &captureID) - && CheckAvailableDeviceName(driver->fPlaybackUID, &playbackID))) { + case kAudioHardwarePropertyDevices: { + jack_log("JackCoreAudioDriver::AudioHardwareNotificationCallback kAudioHardwarePropertyDevices"); + DisplayDeviceNames(); + AudioDeviceID captureID, playbackID; + if (CheckAvailableDevice(driver->fDeviceID) || + (CheckAvailableDeviceName(driver->fCaptureUID, &captureID) + && CheckAvailableDeviceName(driver->fPlaybackUID, &playbackID))) { - } - break; } - + break; + } } return noErr; @@ -461,7 +480,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; @@ -628,12 +647,13 @@ OSStatus JackCoreAudioDriver::GetTotalChannels(AudioDeviceID device, int& channe err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize, &outWritable); if (err == noErr) { int stream_count = outSize / sizeof(AudioBufferList); + jack_log(" JackCoreAudioDriver::GetTotalChannels stream_count = %d", stream_count); AudioBufferList bufferList[stream_count]; err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize, bufferList); if (err == noErr) { for (uint i = 0; i < bufferList->mNumberBuffers; i++) { channelCount += bufferList->mBuffers[i].mNumberChannels; - //jack_info("GetTotalChannels stream = %d channels = %d", i, bufferList->mBuffers[i].mNumberChannels); + jack_log(" JackCoreAudioDriver::GetTotalChannels stream = %d channels = %d", i, bufferList->mBuffers[i].mNumberChannels); } } } @@ -686,8 +706,77 @@ OSStatus JackCoreAudioDriver::GetStreamLatencies(AudioDeviceID device, bool isIn return err; } +bool JackCoreAudioDriver::IsDigitalDevice(AudioDeviceID device) +{ + OSStatus err = noErr; + UInt32 outSize1; + bool is_digital = false; + + /* Get a list of all the streams on this device */ + AudioObjectPropertyAddress streamsAddress = { kAudioDevicePropertyStreams, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster }; + err = AudioObjectGetPropertyDataSize(device, &streamsAddress, 0, NULL, &outSize1); + if (err != noErr) { + jack_error("IsDigitalDevice kAudioDevicePropertyStreams err = %d", err); + return false; + } + + int stream_count = outSize1 / sizeof(AudioStreamID); + AudioStreamID streamIDs[stream_count]; + + err = AudioObjectGetPropertyData(device, &streamsAddress, 0, NULL, &outSize1, streamIDs); + + if (err != noErr) { + jack_error("IsDigitalDevice kAudioDevicePropertyStreams list err = %d", err); + return false; + } + + AudioObjectPropertyAddress physicalFormatsAddress = { kAudioStreamPropertyAvailablePhysicalFormats, kAudioObjectPropertyScopeGlobal, 0 }; + + for (int i = 0; i < stream_count ; i++) { + + /* Find a stream with a cac3 stream */ + int format_num = 0; + + /* Retrieve all the stream formats supported by each output stream */ + err = AudioObjectGetPropertyDataSize(streamIDs[i], &physicalFormatsAddress, 0, NULL, &outSize1); + + if (err != noErr) { + jack_error("IsDigitalDevice kAudioStreamPropertyAvailablePhysicalFormats err = %d", err); + return false; + } + + format_num = outSize1 / sizeof(AudioStreamRangedDescription); + AudioStreamRangedDescription format_list[format_num]; + + err = AudioObjectGetPropertyData(streamIDs[i], &physicalFormatsAddress, 0, NULL, &outSize1, format_list); + + if (err != noErr) { + jack_error("IsDigitalDevice could not get the list of streamformats err = %d", err); + return false; + } + + /* Check if one of the supported formats is a digital format */ + for (int j = 0; j < format_num; j++) { + + PrintStreamDesc(&format_list[j].mFormat); + + if (format_list[j].mFormat.mFormatID == 'IAC3' || + format_list[j].mFormat.mFormatID == 'iac3' || + format_list[j].mFormat.mFormatID == kAudioFormat60958AC3 || + format_list[j].mFormat.mFormatID == kAudioFormatAC3) + { + is_digital = true; + break; + } + } + } + + return is_digital; +} + 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), @@ -695,11 +784,14 @@ JackCoreAudioDriver::JackCoreAudioDriver(const char* name, const char* alias, Ja fHogged(false), fIOUsage(1.f), fComputationGrain(-1.f), - fClockDriftCompensate(false) + fClockDriftCompensate(false), + fDigitalPlayback(false) {} JackCoreAudioDriver::~JackCoreAudioDriver() -{} +{ + delete fAC3Encoder; +} OSStatus JackCoreAudioDriver::DestroyAggregateDevice() { @@ -714,14 +806,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 +885,18 @@ OSStatus JackCoreAudioDriver::CreateAggregateDeviceAux(vector 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 +905,18 @@ OSStatus JackCoreAudioDriver::CreateAggregateDeviceAux(vector 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 +945,7 @@ OSStatus JackCoreAudioDriver::CreateAggregateDeviceAux(vector 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 +961,7 @@ OSStatus JackCoreAudioDriver::CreateAggregateDeviceAux(vector 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 +1078,14 @@ OSStatus JackCoreAudioDriver::CreateAggregateDeviceAux(vector 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 +1104,7 @@ OSStatus JackCoreAudioDriver::CreateAggregateDeviceAux(vector 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 +1124,7 @@ OSStatus JackCoreAudioDriver::CreateAggregateDeviceAux(vector 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 +1142,7 @@ OSStatus JackCoreAudioDriver::CreateAggregateDeviceAux(vector 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 +1154,7 @@ OSStatus JackCoreAudioDriver::CreateAggregateDeviceAux(vector 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 +1163,7 @@ OSStatus JackCoreAudioDriver::CreateAggregateDeviceAux(vector 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); } } @@ -1119,7 +1211,8 @@ int JackCoreAudioDriver::SetupDevices(const char* capture_driver_uid, const char* playback_driver_uid, char* capture_driver_name, char* playback_driver_name, - jack_nframes_t samplerate) + jack_nframes_t samplerate, + bool ac3_encoding) { capture_driver_name[0] = 0; playback_driver_name[0] = 0; @@ -1142,12 +1235,22 @@ int JackCoreAudioDriver::SetupDevices(const char* capture_driver_uid, jack_error("Cannot get device name from device ID"); return -1; } + + if (fHogged) { + if (!TakeHogAux(fDeviceID, false)) { + jack_error("Cannot take hog mode"); + } + if (ac3_encoding) { + fDigitalPlayback = IsDigitalDevice(fDeviceID); + } + } } else { // Creates aggregate device - AudioDeviceID captureID, playbackID; - + AudioDeviceID captureID = -1; + AudioDeviceID playbackID = -1; + if (GetDeviceIDFromUID(capture_driver_uid, &captureID) != noErr) { jack_log("JackCoreAudioDriver::SetupDevices : will take default input"); if (GetDefaultInputDevice(&captureID) != noErr) { @@ -1170,7 +1273,20 @@ int JackCoreAudioDriver::SetupDevices(const char* capture_driver_uid, GetDeviceNameFromID(captureID, fCaptureUID); GetDeviceNameFromID(playbackID, fPlaybackUID); - } + + if (fHogged) { + if (!TakeHogAux(captureID, true)) { + jack_error("Cannot take hog mode for capture device"); + } + if (!TakeHogAux(playbackID, false)) { + jack_error("Cannot take hog mode for playback device"); + } + if (ac3_encoding) { + fDigitalPlayback = IsDigitalDevice(playbackID); + } + } + + } // Capture only } else if (strcmp(capture_driver_uid, "") != 0) { @@ -1186,6 +1302,12 @@ int JackCoreAudioDriver::SetupDevices(const char* capture_driver_uid, jack_error("Cannot get device name from device ID"); return -1; } + + if (fHogged) { + if (!TakeHogAux(fDeviceID, true)) { + jack_error("Cannot take hog mode for capture device"); + } + } // Playback only } else if (strcmp(playback_driver_uid, "") != 0) { @@ -1201,6 +1323,15 @@ int JackCoreAudioDriver::SetupDevices(const char* capture_driver_uid, jack_error("Cannot get device name from device ID"); return -1; } + + if (fHogged) { + if (!TakeHogAux(fDeviceID, false)) { + jack_error("Cannot take hog mode for playback device"); + } + if (ac3_encoding) { + fDigitalPlayback = IsDigitalDevice(fDeviceID); + } + } // Use default driver in duplex mode } else { @@ -1209,8 +1340,9 @@ int JackCoreAudioDriver::SetupDevices(const char* capture_driver_uid, jack_error("Cannot open default device in duplex mode, so aggregate default input and default output"); // Creates aggregate device - AudioDeviceID captureID, playbackID; - + AudioDeviceID captureID = -1; + AudioDeviceID playbackID = -1; + if (GetDeviceIDFromUID(capture_driver_uid, &captureID) != noErr) { jack_log("JackCoreAudioDriver::SetupDevices : will take default input"); if (GetDefaultInputDevice(&captureID) != noErr) { @@ -1233,15 +1365,21 @@ int JackCoreAudioDriver::SetupDevices(const char* capture_driver_uid, GetDeviceNameFromID(captureID, fCaptureUID); GetDeviceNameFromID(playbackID, fPlaybackUID); + + if (fHogged) { + if (!TakeHogAux(captureID, true)) { + jack_error("Cannot take hog mode for capture device"); + } + if (!TakeHogAux(playbackID, false)) { + jack_error("Cannot take hog mode for playback device"); + } + if (ac3_encoding) { + fDigitalPlayback = IsDigitalDevice(playbackID); + } + } } } - - if (fHogged) { - if (TakeHog()) { - jack_info("Device = %ld has been hogged", fDeviceID); - } - } - + return 0; } @@ -1255,7 +1393,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 +1473,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; } @@ -1351,7 +1489,7 @@ int JackCoreAudioDriver::SetupBufferSize(jack_nframes_t buffer_size) } // Check new buffer size - outSize = sizeof(UInt32); + outSize = sizeof(UInt32); err = AudioDeviceGetProperty(fDeviceID, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyBufferFrameSize, &outSize, &tmp_buffer_size); if (err != noErr) { jack_error("Cannot get current buffer size"); @@ -1368,7 +1506,7 @@ int JackCoreAudioDriver::SetupBufferSize(jack_nframes_t buffer_size) error: - // Remove SR change notification + // Remove BS change notification AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyBufferFrameSize, BSNotificationCallback); return -1; @@ -1430,7 +1568,7 @@ int JackCoreAudioDriver::SetupSampleRateAux(AudioDeviceID inDevice, jack_nframes } // Check new sample rate - outSize = sizeof(Float64); + outSize = sizeof(Float64); err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outSize, &tmp_sample_rate); if (err != noErr) { jack_error("Cannot get current sample rate"); @@ -1494,7 +1632,7 @@ int JackCoreAudioDriver::OpenAUHAL(bool capturing, printError(err1); goto error; } - + // Start I/O if (capturing && inchannels > 0) { enableIO = 1; @@ -1850,7 +1988,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,12 +2031,12 @@ 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); } } - - if (SetupDevices(capture_driver_uid, playback_driver_uid, capture_driver_name, playback_driver_name, sample_rate) < 0) { + + if (SetupDevices(capture_driver_uid, playback_driver_uid, capture_driver_name, playback_driver_name, sample_rate, ac3_encoding) < 0) { goto error; } @@ -1911,7 +2052,7 @@ int JackCoreAudioDriver::Open(jack_nframes_t buffer_size, goto error; } - if (SetupChannels(capturing, playing, inchannels, outchannels, in_nChannels, out_nChannels, true) < 0) { + if (SetupChannels(capturing, playing, inchannels, outchannels, in_nChannels, out_nChannels, !ac3_encoding) < 0) { goto error; } @@ -1922,6 +2063,47 @@ int JackCoreAudioDriver::Open(jack_nframes_t buffer_size, if (SetupSampleRate(sample_rate) < 0) { goto error; } + + if (ac3_encoding) { + + if (!fDigitalPlayback) { + jack_error("AC3 encoding can only be used with a digital device"); + goto error; + } + + 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 or init AC3 encoder"); + goto error; + } + + // Setup AC3 channel number + fPlaybackChannels = outchannels; + if (ac3_lfe) { + fPlaybackChannels++; + } + + if (fPlaybackChannels < 2 || fPlaybackChannels > 6) { + jack_error("AC3 encoder channels must be between 2 and 6"); + goto error; + } + + // Force real output channel number to 2 + outchannels = out_nChannels = 2; + + } else { + fPlaybackChannels = outchannels; + } + + // Core driver may have changed the in/out values + fCaptureChannels = inchannels; 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; @@ -1936,10 +2118,7 @@ int JackCoreAudioDriver::Open(jack_nframes_t buffer_size, if (AddListeners() < 0) { goto error; } - - // Core driver may have changed the in/out values - fCaptureChannels = inchannels; - fPlaybackChannels = outchannels; + return noErr; error: @@ -2112,6 +2291,15 @@ int JackCoreAudioDriver::Attach() } } } + + if (fAC3Encoder) { + // Setup specific AC3 channels names + for (int i = 0; i < fPlaybackChannels; i++) { + fAC3Encoder->GetChannelName("coreaudio", "", alias, i); + port = fGraphManager->GetPort(fPlaybackPortList[i]); + port->SetAlias(alias); + } + } UpdateLatencies(); @@ -2194,6 +2382,8 @@ bool JackCoreAudioDriver::TakeHogAux(AudioDeviceID deviceID, bool isInput) jack_error("Cannot read hog state..."); printError(err); } + + jack_log("JackCoreAudioDriver::TakeHogAux : deviceID = %d", deviceID); if (hog_pid != getpid()) { hog_pid = getpid(); @@ -2274,6 +2464,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 +2529,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 +2585,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 +2663,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; diff --git a/macosx/coreaudio/JackCoreAudioDriver.h b/macosx/coreaudio/JackCoreAudioDriver.h index bc5ddb11..5e70efb8 100644 --- a/macosx/coreaudio/JackCoreAudioDriver.h +++ b/macosx/coreaudio/JackCoreAudioDriver.h @@ -50,10 +50,14 @@ typedef UInt8 CAAudioHardwareDeviceSectionID; \todo hardware monitoring */ +class JackAC3Encoder; + class JackCoreAudioDriver : public JackAudioDriver { private: + + JackAC3Encoder* fAC3Encoder; AudioUnit fAUHAL; @@ -75,8 +79,8 @@ class JackCoreAudioDriver : public JackAudioDriver float fIOUsage; float fComputationGrain; bool fClockDriftCompensate; + bool fDigitalPlayback; - static OSStatus Render(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, @@ -122,7 +126,8 @@ class JackCoreAudioDriver : public JackAudioDriver const char* playback_driver_uid, char* capture_driver_name, char* playback_driver_name, - jack_nframes_t samplerate); + jack_nframes_t samplerate, + bool ac3_encoding); int SetupChannels(bool capturing, bool playing, @@ -158,6 +163,8 @@ class JackCoreAudioDriver : public JackAudioDriver bool TakeHog(); void UpdateLatencies(); + + bool IsDigitalDevice(AudioDeviceID device); public: @@ -180,7 +187,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(); diff --git a/macosx/libaften_pcm.a b/macosx/libaften_pcm.a new file mode 100644 index 00000000..9fb3da30 Binary files /dev/null and b/macosx/libaften_pcm.a differ diff --git a/macosx/libaften_static.a b/macosx/libaften_static.a new file mode 100644 index 00000000..b5769919 Binary files /dev/null and b/macosx/libaften_static.a differ