diff --git a/src/CoreAudio/AudioToolbox/AUComponent.cpp b/src/CoreAudio/AudioToolbox/AUComponent.cpp index 9381bbcb8..10f63b774 100644 --- a/src/CoreAudio/AudioToolbox/AUComponent.cpp +++ b/src/CoreAudio/AudioToolbox/AUComponent.cpp @@ -1,27 +1,14 @@ #include "darling-config.h" #include "AUComponent.h" -#include "AudioUnitALSA.h" -#include "AudioUnitPA.h" +#include "AudioUnitCA.h" #include #include #include "stub.h" AudioComponent AudioComponentFindNext(AudioComponent inAComponent, AudioComponentDescription *inDesc) { - int index = -1; - - if (inDesc->componentType != kAudioUnitType_Output && inDesc->componentType != kAudioUnitType_Mixer) - return nullptr; - - if (inAComponent != nullptr) - index = GetComponentIndex(inAComponent); - -#if defined(ENABLE_ALSA) - if (snd_card_next(&index) || index == -1) - return nullptr; -#endif - - return CreateComponent(kComponentTypeAudioUnit, index); + // TODO + return nullptr; } OSStatus AudioComponentGetDescription(AudioComponent inComponent, AudioComponentDescription *outDesc) @@ -54,39 +41,21 @@ OSStatus AudioComponentInstanceDispose(AudioComponentInstance inInstance) AudioComponent AudioComponentInstanceGetComponent(AudioComponentInstance inInstance) { - return CreateComponent(kComponentTypeAudioUnit, static_cast(inInstance)->cardIndex()); + // return CreateComponent(kComponentTypeAudioUnit, static_cast(inInstance)->cardIndex()); + return nullptr; } OSStatus AudioComponentInstanceNew(AudioComponent inComponent, AudioComponentInstance *outInstance) { -#if defined(ENABLE_PULSEAUDIO) - *outInstance = new AudioUnitPA; -#elif defined(ENABLE_ALSA) - *outInstance = AudioUnitALSA::create(GetComponentIndex(inComponent)); -#else - *outInstance = nullptr; - return unimpErr; -#endif - return *outInstance ? noErr : paramErr; + // TODO: pass inComponent to specify which audio device to use? + //*outInstance = new AudioUnitCA; + //return *outInstance ? noErr : paramErr; + return noErr; } OSStatus AudioComponentCopyName(AudioComponent inComponent, CFStringRef *outName) { - int index = GetComponentIndex(inComponent); - char* name; - - if (!outName) - return paramErr; - -#if defined(ENABLE_ALSA) - if (snd_card_get_longname(index, &name)) - return paramErr; -#elif defined(ENABLE_PULSEAUDIO) - name = strdup("PulseAudio"); -#endif - - *outName = CFStringCreateWithCString(nullptr, name, kCFStringEncodingUTF8); - free(name); + // TODO return noErr; } @@ -95,20 +64,7 @@ UInt32 AudioComponentCount(AudioComponentDescription *inDesc) { UInt32 count = 0; -#if defined(ENABLE_ALSA) - int index = -1; - - // Is there a better way? - do - { - snd_card_next(&index); - if (index != -1) - count++; - } - while (index != -1); -#elif defined(ENABLE_PULSEAUDIO) count = 1; -#endif return count; } diff --git a/src/CoreAudio/AudioToolbox/AudioUnit.cpp b/src/CoreAudio/AudioToolbox/AudioUnit.cpp index b88423c1e..7f757ef7c 100644 --- a/src/CoreAudio/AudioToolbox/AudioUnit.cpp +++ b/src/CoreAudio/AudioToolbox/AudioUnit.cpp @@ -1,7 +1,6 @@ #include "darling-config.h" #include "AudioUnit.h" #include "AudioOutputUnitComponent.h" -#include "AudioUnitALSA.h" #include "stub.h" #include diff --git a/src/CoreAudio/AudioToolbox/AudioUnitALSA.cpp b/src/CoreAudio/AudioToolbox/AudioUnitALSA.cpp deleted file mode 100644 index becf3659d..000000000 --- a/src/CoreAudio/AudioToolbox/AudioUnitALSA.cpp +++ /dev/null @@ -1,770 +0,0 @@ -#include -#include "AudioUnitALSA.h" -#include "AudioUnitProperties.h" -#include -#include "stub.h" -#include -#include -#include -#include -#include - -#define SAMPLE_PERIOD 4096 - -static dispatch_queue_t g_audioQueue; - -AudioUnitALSA::AudioUnitALSA(int cardIndex, char* cardName) -: m_cardIndex(cardIndex), m_cardName(cardName), m_pcmOutput(nullptr), m_pcmInput(nullptr) -{ - static dispatch_once_t pred; - dispatch_once(&pred, ^{ - g_audioQueue = dispatch_queue_create("org.darlinghq.audiounit", nullptr); - }); -} - -static void throwAlsaError(const std::string& msg, int errCode) -{ - std::stringstream ss; - ss << msg << ": " << snd_strerror(errCode); - throw std::runtime_error(ss.str()); -} - -static snd_pcm_format_t alsaFormatForASBD(const AudioStreamBasicDescription& asbd) -{ - bool isFloat = asbd.mFormatFlags & kAudioFormatFlagIsFloat; - bool isSigned = asbd.mFormatFlags & kAudioFormatFlagIsSignedInteger; - bool isBE = asbd.mFormatFlags & kAudioFormatFlagIsBigEndian; - - if (asbd.mFormatID == kAudioFormatLinearPCM) - { - if (isFloat) - return isBE ? SND_PCM_FORMAT_FLOAT_BE : SND_PCM_FORMAT_FLOAT_LE; - - switch (asbd.mBitsPerChannel) - { - case 8: - if (isSigned) - return SND_PCM_FORMAT_S8; - else - return SND_PCM_FORMAT_U8; - case 16: - if (isSigned) - return isBE ? SND_PCM_FORMAT_S16_BE : SND_PCM_FORMAT_S16_LE; - else - return isBE ? SND_PCM_FORMAT_U16_BE : SND_PCM_FORMAT_U16_LE; - case 24: - if (isSigned) - return isBE ? SND_PCM_FORMAT_S24_BE : SND_PCM_FORMAT_S24_LE; - else - return isBE ? SND_PCM_FORMAT_U24_BE : SND_PCM_FORMAT_U24_LE; - case 32: - if (isSigned) - return isBE ? SND_PCM_FORMAT_S32_BE : SND_PCM_FORMAT_S32_LE; - else - return isBE ? SND_PCM_FORMAT_U32_BE : SND_PCM_FORMAT_U32_LE; - default: - throw std::runtime_error("Invalid mBitsPerChannel value"); - } - } - else - throw std::runtime_error("Unsupported mFormatID value"); -} - -AudioUnitComponent* AudioUnitALSA::create(int cardIndex) -{ - char* name; - - if (cardIndex > 0) - { - if (snd_card_get_name(cardIndex, &name)) - return nullptr; - } - else - { - name = strdup("default" /*"plughw:0"*/); - } - - return new AudioUnitALSA(cardIndex, name); -} - -AudioUnitALSA::~AudioUnitALSA() -{ - deinit(); - free(m_cardName); -} - -OSStatus AudioUnitALSA::reset(AudioUnitScope inScope, AudioUnitElement inElement) -{ - STUB(); - return unimpErr; -} - -void AudioUnitALSA::initOutput() -{ - snd_pcm_hw_params_t* hw_params = nullptr; - snd_pcm_sw_params_t* sw_params = nullptr; - - try - { - // Configure ALSA based on m_configOutputPlayback - int err; - const AudioStreamBasicDescription& alsaConfig = m_config[kOutputBus].second; - unsigned int rate = alsaConfig.mSampleRate; - - //if (memcmp(&m_config[kOutputBus].second, &m_config[kOutputBus].first, sizeof(AudioStreamBasicDescription)) != 0) - // throw std::runtime_error("Different input and HW config not implemented yet"); - - // Let ALSA do the conversion on its own - m_config[kOutputBus].second = m_config[kOutputBus].first; - - err = snd_pcm_open(&m_pcmOutput, m_cardName, SND_PCM_STREAM_PLAYBACK, 0); - if (err < 0) - throwAlsaError("Failed to initialize playback PCM", err); - - err = snd_pcm_hw_params_malloc(&hw_params); - if (err < 0) - throwAlsaError("Failed to alloc hw params", err); - - err = snd_pcm_hw_params_any(m_pcmOutput, hw_params); - if (err < 0) - throwAlsaError("Failed to init hw params", err); - - if (isOutputPlanar()) - err = snd_pcm_hw_params_set_access(m_pcmOutput, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED); - else - err = snd_pcm_hw_params_set_access(m_pcmOutput, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); - - if (err < 0) - throwAlsaError("Failed to set interleaved access", err); - - err = snd_pcm_hw_params_set_format(m_pcmOutput, hw_params, alsaFormatForASBD(alsaConfig)); - if (err < 0) - throwAlsaError("Failed to set format", err); - - err = snd_pcm_hw_params_set_rate_near(m_pcmOutput, hw_params, &rate, 0); - if (err < 0) - throwAlsaError("Failed to set sample rate", err); - - // LOG << "Channel count: " << int(alsaConfig.mChannelsPerFrame) << std::endl; - err = snd_pcm_hw_params_set_channels(m_pcmOutput, hw_params, alsaConfig.mChannelsPerFrame); - if (err < 0) - throwAlsaError("Failed to set channel count", err); - - err = snd_pcm_hw_params(m_pcmOutput, hw_params); - if (err < 0) - throwAlsaError("Failed to set HW parameters", err); - - snd_pcm_hw_params_free(hw_params); - hw_params = nullptr; - - err = snd_pcm_sw_params_malloc(&sw_params); - if (err < 0) - throwAlsaError("Failed to alloc sw params", err); - - err = snd_pcm_sw_params_current(m_pcmOutput, sw_params); - if (err < 0) - throwAlsaError("Failed to init sw params", err); - - err = snd_pcm_sw_params_set_avail_min(m_pcmOutput, sw_params, SAMPLE_PERIOD); - if (err < 0) - throwAlsaError("snd_pcm_sw_params_set_avail_min() failed", err); - - err = snd_pcm_sw_params_set_start_threshold(m_pcmOutput, sw_params, 0U); - if (err < 0) - throwAlsaError("snd_pcm_sw_params_set_start_threshold() failed", err); - - err = snd_pcm_sw_params_set_tstamp_mode(m_pcmOutput, sw_params, SND_PCM_TSTAMP_ENABLE); - if (err < 0) - throwAlsaError("snd_pcm_sw_params_set_tstamp_mode() failed", err); - - err = snd_pcm_sw_params(m_pcmOutput, sw_params); - if (err < 0) - throwAlsaError("Failed to set SW parameters", err); - - snd_pcm_sw_params_free(sw_params); - sw_params = nullptr; - } - catch (...) - { - if (hw_params != nullptr) - snd_pcm_hw_params_free(hw_params); - if (sw_params != nullptr) - snd_pcm_sw_params_free(sw_params); - - throw; - } -} - -void AudioUnitALSA::initInput() -{ - STUB(); - - try - { - //if (memcmp(&m_config[kInputBus].second, &m_config[kInputBus].first, sizeof(AudioStreamBasicDescription)) != 0) - // throw std::runtime_error("Different input and HW config not implemented yet"); - - // Let ALSA do the conversion on its own - m_config[kInputBus].first = m_config[kInputBus].second; - - // TODO: support recording - } - catch (...) - { - throw; - } -} - -OSStatus AudioUnitALSA::init() -{ - try - { - if (m_pcmOutput || m_pcmInput) - return kAudioUnitErr_Initialized; - - /*if (m_enableInput && snd_pcm_open(&m_pcmInput, m_cardName, SND_PCM_STREAM_CAPTURE, 0)) - { - deinit(); - - LOG << "Failed to initialize capture PCM " << m_cardName << std::endl; - return kAudioUnitErr_FailedInitialization; - }*/ - - if (m_enableOutput) - initOutput(); - if (m_enableInput) - initInput(); - } - catch (const std::exception& e) - { - // ERROR() << e.what(); - - deinit(); - return kAudioUnitErr_FailedInitialization; - } - - return noErr; -} - -OSStatus AudioUnitALSA::deinit() -{ - if (m_pcmOutput) - { - snd_pcm_close(m_pcmOutput); - m_pcmOutput = nullptr; - } - if (m_pcmInput) - { - snd_pcm_close(m_pcmInput); - m_pcmInput = nullptr; - } - return noErr; -} - -void AudioUnitALSA::processAudioEvent(struct pollfd origPoll, int event) -{ - struct pollfd pfd = origPoll; - unsigned short revents; - int err; - - TRACE1(event); - - pfd.revents = event; - - err = snd_pcm_poll_descriptors_revents(m_pcmOutput, &pfd, 1, &revents); - // if (err < 0) - // ERROR() << "snd_pcm_poll_descriptors_revents() failed: " << snd_strerror(err); - - if (revents & POLLIN) - pushDataFromInput(); - - if (revents & POLLOUT) - requestDataForPlayback(); -} - -void AudioUnitALSA::requestDataForPlayback() -{ - AudioUnitRenderActionFlags flags; - AudioTimeStamp ts; - AudioBufferList* bufs; - OSStatus err; - std::unique_ptr data; - snd_pcm_uframes_t availFrames; - snd_htimestamp_t alsaTimestamp; - const AudioStreamBasicDescription& config = m_config[kOutputBus].first; - UInt32 cc = config.mChannelsPerFrame; - - // TRACE(); - - memset(&ts, 0, sizeof(ts)); - - if (isOutputPlanar()) - { - bufs = (AudioBufferList*) operator new(sizeof(AudioBufferList) + (cc-1)*sizeof(AudioBuffer)); - bufs->mNumberBuffers = cc; - } - else - { - bufs = (AudioBufferList*) operator new(sizeof(AudioBufferList)); - bufs->mNumberBuffers = 1; - } - - if (m_shouldAllocateBuffer) - { - if (isOutputPlanar()) - { - UInt32 bytesPerChannel = config.mBytesPerFrame * SAMPLE_PERIOD; - - data.reset(new uint8_t[cc*bytesPerChannel]); - - for (UInt32 i = 0; i < cc; i++) - { - bufs->mBuffers[i].mNumberChannels = 1; - bufs->mBuffers[i].mDataByteSize = bytesPerChannel; - bufs->mBuffers[i].mData = data.get() + i*bytesPerChannel; - } - } - else - { - bufs->mBuffers[0].mNumberChannels = cc; - bufs->mBuffers[0].mDataByteSize = config.mBytesPerFrame * SAMPLE_PERIOD; - - data.reset(new uint8_t[bufs->mBuffers[0].mDataByteSize]); - bufs->mBuffers[0].mData = data.get(); - } - } - else - { - if (isOutputPlanar()) - { - for (UInt32 i = 0; i < cc; i++) - { - bufs->mBuffers[i].mDataByteSize = 0; - bufs->mBuffers[i].mData = nullptr; - } - } - else - { - bufs->mBuffers[0].mDataByteSize = 0; - bufs->mBuffers[0].mData = nullptr; - } - } - - snd_pcm_htimestamp(m_pcmOutput, &availFrames, &alsaTimestamp); - - // TODO: fill in AudioTimeStamp based on alsaTimestamp - - err = AudioUnitRender(m_inputUnit.sourceAudioUnit, &flags, &ts, kOutputBus, SAMPLE_PERIOD, bufs); - - if (err != noErr) - { - // ERROR() << "Render callback failed with error " << err; - - // Fill with silence, the error may be temporary - UInt32 bytes = config.mBytesPerFrame * SAMPLE_PERIOD; - - if (!m_shouldAllocateBuffer) - { - if (isOutputPlanar()) - { - data.reset(new uint8_t[bytes*cc]); - memset(data.get(), 0, bytes*cc); - } - else - { - data.reset(new uint8_t[bytes]); - memset(data.get(), 0, bytes); - } - } - - if (isOutputPlanar()) - { - for (UInt32 i = 0; i < cc; i++) - { - bufs->mBuffers[i].mData = data.get() + bytes*i; - bufs->mBuffers[i].mDataByteSize = bytes; - } - } - else - { - bufs->mBuffers[0].mData = data.get(); - bufs->mBuffers[0].mDataByteSize = bytes; - } - } - - m_lastRenderError = AudioUnitRender(this, &flags, &ts, kOutputBus, SAMPLE_PERIOD, bufs); - - operator delete(bufs); -} - -void AudioUnitALSA::pushDataFromInput() -{ - AudioUnitRenderActionFlags flags; - AudioTimeStamp ts; - AudioBufferList* bufs; - OSStatus err; - std::unique_ptr data; - snd_pcm_uframes_t availFrames; - snd_htimestamp_t alsaTimestamp; - const AudioStreamBasicDescription& config = m_config[kInputBus].second; - - TRACE(); - - memset(&ts, 0, sizeof(ts)); - - if (m_shouldAllocateBuffer) - { - if (isInputPlanar()) - { - UInt32 cc = config.mChannelsPerFrame; - bufs = (AudioBufferList*) operator new(sizeof(AudioBufferList) + (cc-1)*sizeof(AudioBuffer)); - bufs->mNumberBuffers = cc; - } - else - { - bufs = (AudioBufferList*) operator new(sizeof(AudioBufferList)); - bufs->mNumberBuffers = 1; - } - - if (isInputPlanar()) - { - UInt32 cc = config.mChannelsPerFrame; - UInt32 bytesPerChannel = config.mBytesPerFrame * SAMPLE_PERIOD; - - data.reset(new uint8_t[cc*bytesPerChannel]); - - for (UInt32 i = 0; i < cc; i++) - { - bufs->mBuffers[i].mNumberChannels = 1; - bufs->mBuffers[i].mDataByteSize = bytesPerChannel; - bufs->mBuffers[i].mData = data.get() + i*bytesPerChannel; - } - } - else - { - bufs->mBuffers[0].mNumberChannels = config.mChannelsPerFrame; - bufs->mBuffers[0].mDataByteSize = config.mBytesPerFrame * SAMPLE_PERIOD; - - data.reset(new uint8_t[bufs->mBuffers[0].mDataByteSize]); - bufs->mBuffers[0].mData = data.get(); - } - - //render(&flags, &ts, kInputBus, SAMPLE_PERIOD, bufs); - } - else - bufs = nullptr; - - // TODO: fill in AudioTimeStamp based on alsaTimestamp - snd_pcm_htimestamp(m_pcmOutput, &availFrames, &alsaTimestamp); - - m_outputCallback.inputProc(m_outputCallback.inputProcRefCon, &flags, &ts, kInputBus, SAMPLE_PERIOD, bufs); - - operator delete(bufs); -} - -OSStatus AudioUnitALSA::render(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) -{ - if (inBusNumber == kOutputBus) - return renderOutput(ioActionFlags, inTimeStamp, inNumberFrames, ioData); - else if (inBusNumber == kInputBus) - return renderInput(ioActionFlags, inTimeStamp, inNumberFrames, ioData); - else - return paramErr; -} - -OSStatus AudioUnitALSA::renderOutput(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData) -{ - if (!isOutputPlanar()) - return renderInterleavedOutput(ioActionFlags, inTimeStamp, inNumberFrames, ioData); - else - return renderPlanarOutput(ioActionFlags, inTimeStamp, inNumberFrames, ioData); -} - -OSStatus AudioUnitALSA::renderInterleavedOutput(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData) -{ - int wr, sampleCount; - const AudioStreamBasicDescription& config = m_config[kOutputBus].first; - UInt32 framesSoFar = 0; - - for (UInt32 i = 0; i < ioData->mNumberBuffers; i++) - { - // LOG << "Writing " << ioData->mBuffers[i].mDataByteSize << " bytes into sound card\n"; - - sampleCount = std::min(ioData->mBuffers[i].mDataByteSize / config.mBytesPerFrame, inNumberFrames - framesSoFar); - framesSoFar += sampleCount; - - if (!sampleCount) - break; - -do_write: - wr = snd_pcm_writei(m_pcmOutput, ioData->mBuffers[i].mData, sampleCount); - if (wr < 0) - { - if (wr == -EINTR || wr == -EPIPE) - { - // LOG << "Recovering PCM\n"; - snd_pcm_recover(m_pcmOutput, wr, false); - goto do_write; - } - else - { - // ERROR() << "snd_pcm_writei() failed: " << snd_strerror(wr); - return kAudioUnitErr_NoConnection; - } - } - else if (wr < sampleCount) - ;// ERROR() << "snd_pcm_writei(): not all data written?"; - } - - return noErr; -} - -OSStatus AudioUnitALSA::renderPlanarOutput(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData) -{ - std::unique_ptr bufferPointers (new void*[ioData->mNumberBuffers]); - int size, sampleCount, wr; - const AudioStreamBasicDescription& config = m_config[kOutputBus].first; - - if (ioData->mNumberBuffers != config.mChannelsPerFrame) - { - // ERROR() << "Incorrect buffer count for planar audio, only " << ioData->mNumberBuffers; - return paramErr; - } - - size = ioData->mBuffers[0].mDataByteSize; - for (UInt32 i = 1; i < ioData->mNumberBuffers; i++) - { - if (size != ioData->mBuffers[i].mDataByteSize) - { - // ERROR() << "Bad buffer size in buffer " << i; - return paramErr; - } - } - - for (UInt32 i = 0; i < ioData->mNumberBuffers; i++) - bufferPointers[i] = ioData->mBuffers[i].mData; - - sampleCount = size / config.mBytesPerFrame; - -do_write: - wr = snd_pcm_writen(m_pcmOutput, bufferPointers.get(), sampleCount); - if (wr < 0) - { - if (wr == -EINTR || wr == -EPIPE) - { - // LOG << "Recovering PCM\n"; - snd_pcm_recover(m_pcmOutput, wr, false); - goto do_write; - } - else - { - // ERROR() << "snd_pcm_writen() failed: " << snd_strerror(wr); - return kAudioUnitErr_NoConnection; - } - } - else if (wr < sampleCount) - ;// ERROR() << "snd_pcm_writen(): not all data written?"; - - return noErr; -} - -OSStatus AudioUnitALSA::renderInput(AudioUnitRenderActionFlags *ioActionFlags,const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData) -{ - if (!m_outputCallback.inputProc) - return noErr; // We don't push, we should be polled - - if (!isInputPlanar()) - return renderInterleavedInput(ioActionFlags, inTimeStamp, inNumberFrames, ioData); - else - return renderPlanarInput(ioActionFlags, inTimeStamp, inNumberFrames, ioData); -} - -OSStatus AudioUnitALSA::renderInterleavedInput(AudioUnitRenderActionFlags *ioActionFlags,const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData) -{ - int wr, sampleCount; - const AudioStreamBasicDescription& config = m_config[kInputBus].second; - - for (UInt32 i = 0; i < ioData->mNumberBuffers; i++) - { - // LOG << "Reading up to " << ioData->mBuffers[i].mDataByteSize << " bytes from sound card\n"; - - sampleCount = ioData->mBuffers[i].mDataByteSize / config.mBytesPerFrame; - -do_write: - wr = snd_pcm_writei(m_pcmOutput, ioData->mBuffers[i].mData, sampleCount); - if (wr < 0) - { - if (wr == -EINTR || wr == -EPIPE) - { - snd_pcm_recover(m_pcmOutput, wr, false); - goto do_write; - } - else - { - // ERROR() << "snd_pcm_writei() failed: " << snd_strerror(wr); - return kAudioUnitErr_NoConnection; - } - } - - ioData->mBuffers[i].mDataByteSize = wr * config.mBytesPerFrame; - } - - return noErr; -} - -OSStatus AudioUnitALSA::renderPlanarInput(AudioUnitRenderActionFlags *ioActionFlags,const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData) -{ - std::unique_ptr bufferPointers (new void*[ioData->mNumberBuffers]); - int size, sampleCount, wr; - const AudioStreamBasicDescription& config = m_config[kInputBus].second; - - if (ioData->mNumberBuffers != config.mChannelsPerFrame) - { - // ERROR() << "Incorrect buffer count for planar audio, only " << ioData->mNumberBuffers; - return paramErr; - } - - size = ioData->mBuffers[0].mDataByteSize; - for (UInt32 i = 1; i < ioData->mNumberBuffers; i++) - { - if (size != ioData->mBuffers[i].mDataByteSize) - { - // ERROR() << "Bad buffer size in buffer " << i; - return paramErr; - } - } - - for (UInt32 i = 0; i < ioData->mNumberBuffers; i++) - bufferPointers[i] = ioData->mBuffers[i].mData; - - sampleCount = size / config.mBytesPerFrame; - -do_write: - wr = snd_pcm_readn(m_pcmInput, bufferPointers.get(), sampleCount); - if (wr < 0) - { - if (wr == -EINTR || wr == -EPIPE) - { - snd_pcm_recover(m_pcmInput, wr, false); - goto do_write; - } - else - { - // ERROR() << "snd_pcm_writen() failed: " << snd_strerror(wr); - return kAudioUnitErr_NoConnection; - } - } - - for (UInt32 i = 0; i < ioData->mNumberBuffers; i++) - ioData->mBuffers[i].mDataByteSize = sampleCount * config.mBytesPerFrame; - - return noErr; -} - -void AudioUnitALSA::startOutput() -{ - std::unique_ptr pollfds; - int count, err; - - if (!m_inputUnit.sourceAudioUnit) - throw std::runtime_error("No input unit set"); - - err = snd_pcm_prepare(m_pcmOutput); - if (err != 0) - throwAlsaError("snd_pcm_prepare() failed", err); - - count = snd_pcm_poll_descriptors_count(m_pcmOutput); - pollfds.reset(new struct pollfd[count]); - - if (snd_pcm_poll_descriptors(m_pcmOutput, pollfds.get(), count) != count) - throw std::runtime_error("snd_pcm_poll_descriptors() failed"); - - // LOG << "ALSA descriptor count: " << count << std::endl; - startDescriptors(pollfds.get(), count); -} - -void AudioUnitALSA::startDescriptors(const struct pollfd* pollfds, int count) -{ - for (int i = 0; i < count; i++) - { - dispatch_source_t source; - const struct pollfd& cur = pollfds[i]; - - if (cur.events & POLLIN) - { - source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, cur.fd, 0, g_audioQueue); - dispatch_source_set_event_handler(source, ^{ - processAudioEvent(cur, POLLIN); - }); - dispatch_resume(source); - m_sources.push_back(source); - } - - if (cur.events & POLLOUT) - { - source = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, cur.fd, 0, g_audioQueue); - dispatch_source_set_event_handler(source, ^{ - processAudioEvent(cur, POLLOUT); - }); - dispatch_resume(source); - m_sources.push_back(source); - } - - } -} - -void AudioUnitALSA::startInput() -{ - int err; - - STUB(); - - err = snd_pcm_prepare(m_pcmInput); - if (err != 0) - throwAlsaError("snd_pcm_prepare() failed", err); -} - -OSStatus AudioUnitALSA::start() -{ - int err; - - // TRACE(); - - try - { - if (m_pcmInput) - startInput(); - - if (m_pcmOutput) - startOutput(); - - } - catch (const std::exception& e) - { - if (m_pcmInput) - snd_pcm_drop(m_pcmInput); - if (m_pcmOutput) - snd_pcm_drop(m_pcmOutput); - - // ERROR() << e.what(); - return kAudioUnitErr_FailedInitialization; - } - - return noErr; -} - -OSStatus AudioUnitALSA::stop() -{ - TRACE(); - - for (dispatch_source_t src : m_sources) - dispatch_source_cancel(src); - m_sources.clear(); - - if (m_pcmInput) - snd_pcm_drop(m_pcmInput); - if (m_pcmOutput) - snd_pcm_drop(m_pcmOutput); - return noErr; -} - diff --git a/src/CoreAudio/AudioToolbox/AudioUnitALSA.h b/src/CoreAudio/AudioToolbox/AudioUnitALSA.h deleted file mode 100644 index 61d7dace6..000000000 --- a/src/CoreAudio/AudioToolbox/AudioUnitALSA.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef AUCOMPONENTINTERNAL_H -#define AUCOMPONENTINTERNAL_H -#include "AudioUnit.h" -#include -#include -#include -#include -#include "AudioOutputUnitComponent.h" - -#ifndef kOutputBus -# define kOutputBus 0 // playback -# define kInputBus 1 // recording -#endif - -class AudioUnitALSA : public AudioOutputUnitComponent -{ -private: - AudioUnitALSA(int cardIndex, char* cardName); -public: - static AudioUnitComponent* create(int cardIndex); - virtual ~AudioUnitALSA(); - - int cardIndex() const override { return m_cardIndex; } - - OSStatus init() override; - OSStatus deinit() override; - - OSStatus start() override; - OSStatus stop() override; - - OSStatus reset(AudioUnitScope inScope, AudioUnitElement inElement) override; - - OSStatus render(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) override; -private: - void initOutput(); - void initInput(); - - void startOutput(); - void startInput(); - void startDescriptors(const struct pollfd* fds, int count); - - void processAudioEvent(struct pollfd origPoll, int event); - void requestDataForPlayback(); - void pushDataFromInput(); - - OSStatus renderOutput(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData); - OSStatus renderInterleavedOutput(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData); - OSStatus renderPlanarOutput(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData); - - OSStatus renderInput(AudioUnitRenderActionFlags *ioActionFlags,const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData); - OSStatus renderInterleavedInput(AudioUnitRenderActionFlags *ioActionFlags,const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData); - OSStatus renderPlanarInput(AudioUnitRenderActionFlags *ioActionFlags,const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData); - - inline bool isOutputPlanar() const { return m_config[kOutputBus].second.mFormatFlags & kAudioFormatFlagIsNonInterleaved; } - inline bool isInputPlanar() const { return m_config[kInputBus].first.mFormatFlags & kAudioFormatFlagIsNonInterleaved; } -private: - int m_cardIndex; - char* m_cardName; - snd_pcm_t *m_pcmOutput, *m_pcmInput; - std::vector m_sources; -}; - -#endif - diff --git a/src/CoreAudio/AudioToolbox/AudioUnitBase.cpp b/src/CoreAudio/AudioToolbox/AudioUnitBase.cpp index ef192ba55..058251afe 100644 --- a/src/CoreAudio/AudioToolbox/AudioUnitBase.cpp +++ b/src/CoreAudio/AudioToolbox/AudioUnitBase.cpp @@ -7,6 +7,7 @@ AudioUnitComponent::AudioUnitComponent(std::initializer_list elements) { + m_inputRenderer.reset(new AudioUnitRenderer); m_elementNames.insert(m_elementNames.end(), elements.begin(), elements.end()); m_config.resize(m_elementNames.size()); @@ -96,9 +97,9 @@ OSStatus AudioUnitComponent::setProperty(AudioUnitPropertyID prop, AudioUnitScop // TODO: perform validation - if (scope == kAudioUnitScope_Output) + if (scope == kAudioUnitScope_Output) // playback m_config[elem].second = *newConfig; - else if (scope == kAudioUnitScope_Input) + else if (scope == kAudioUnitScope_Input) // recording m_config[elem].first = *newConfig; else if (scope == kAudioUnitScope_Global) m_config[0].second = *newConfig; @@ -107,25 +108,37 @@ OSStatus AudioUnitComponent::setProperty(AudioUnitPropertyID prop, AudioUnitScop return noErr; } + case 7: // kAudioUnitProperty_SetInputCallback (do not mistake with kAudioOutputUnitProperty_SetInputCallback) case kAudioUnitProperty_SetRenderCallback: { if (dataSize != sizeof(AURenderCallbackStruct)) return kAudioUnitErr_InvalidParameter; //if (scope == kAudioUnitScope_Input) //{ - if (elem != 0) + if (elem != kOutputBus) return kAudioUnitErr_InvalidElement; - CloseComponent(m_inputUnit.sourceAudioUnit); // TODO: wrong, we may not own the unit! + *m_inputRenderer = *(AURenderCallbackStruct*) data; m_inputUnit.sourceOutputNumber = 0; m_inputUnit.destInputNumber = 0; - m_inputUnit.sourceAudioUnit = new AudioUnitRenderer(*(AURenderCallbackStruct*) data); + m_inputUnit.sourceAudioUnit = m_inputRenderer.get(); //} //else // return kAudioUnitErr_InvalidScope; return noErr; } + case kAudioOutputUnitProperty_SetInputCallback: + { + // For audio recording + + if (elem != kInputBus) + return kAudioUnitErr_InvalidElement; + + // Notification callback - the recipient needs to call AudioUnitRender to obtain microphone data + m_outputCallback = *(AURenderCallbackStruct*) data; + return noErr; + } case kAudioUnitProperty_MakeConnection: { if (dataSize != sizeof(AudioUnitConnection)) @@ -136,7 +149,6 @@ OSStatus AudioUnitComponent::setProperty(AudioUnitPropertyID prop, AudioUnitScop // TODO: support multiple units! // TODO: reconfigure input format based on output format - CloseComponent(m_inputUnit.sourceAudioUnit); // TODO: wrong, we may not own the unit! memcpy(&m_inputUnit, data, sizeof(AudioUnitConnection)); return noErr; diff --git a/src/CoreAudio/AudioToolbox/AudioUnitBase.h b/src/CoreAudio/AudioToolbox/AudioUnitBase.h index f91034122..147dd8159 100644 --- a/src/CoreAudio/AudioToolbox/AudioUnitBase.h +++ b/src/CoreAudio/AudioToolbox/AudioUnitBase.h @@ -6,10 +6,17 @@ #include #include #include +#include #include -#include -class AudioUnitComponent : public CarbonComponent +#ifndef kOutputBus +# define kOutputBus 0 // playback +# define kInputBus 1 // recording +#endif + +class AudioUnitRenderer; + +class AudioUnitComponent { protected: AudioUnitComponent(std::initializer_list elements); @@ -34,7 +41,16 @@ protected: std::vector m_elementNames; std::vector> m_config; //AudioStreamBasicDescription m_configOutputPlayback, m_configInputPlayback, m_configInputCapture, m_configOutputCapture; + + // We're simplifying things here - instead of having generic sets of input/output busses + // and associated connections/callbacks, we have just this. + // For kOutputBus only AudioUnitConnection m_inputUnit; + std::unique_ptr m_inputRenderer; + + // For kInputBus only + AURenderCallbackStruct m_outputCallback; + bool m_shouldAllocateBuffer = true; OSStatus m_lastRenderError = 0; CFStringRef m_contextName = nullptr; diff --git a/src/CoreAudio/AudioToolbox/AudioUnitPA.cpp b/src/CoreAudio/AudioToolbox/AudioUnitPA.cpp deleted file mode 100644 index ad5c59d28..000000000 --- a/src/CoreAudio/AudioToolbox/AudioUnitPA.cpp +++ /dev/null @@ -1,544 +0,0 @@ -#include "AudioUnitPA.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include "stub.h" -#include - -extern "C" char*** _NSGetArgv(void); - -pa_mainloop* AudioUnitPA::m_mainloop = nullptr; -pa_context* AudioUnitPA::m_context = nullptr; -std::thread* AudioUnitPA::m_mainloopThread; - -AudioUnitPA::AudioUnitPA() -{ -} - -AudioUnitPA::~AudioUnitPA() -{ - deinit(); -} - -static std::string CFStringToStdString(CFStringRef str) -{ - CFIndex length, maxSize; - std::unique_ptr buffer; - - length = CFStringGetLength(str); - maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1; - buffer.reset(new char[maxSize]); - - if (CFStringGetCString(str, buffer.get(), maxSize, kCFStringEncodingUTF8)) - return std::string(buffer.get()); - else - return std::string(); -} - -OSStatus AudioUnitPA::init() -{ - static std::once_flag once; - OSStatus ret; - - std::call_once(once, initializePA); - if (m_context == nullptr) - return kAudioUnitErr_FailedInitialization; - - if (m_enableOutput) - ret = initOutput(); - - // TODO: support recording - return ret; -} - -OSStatus AudioUnitPA::initOutput() -{ - std::string streamName; - pa_sample_spec spec; - - if (m_contextName != nullptr) - streamName = CFStringToStdString(m_contextName); - else - streamName = "AudioUnit"; - - spec = paSampleSpecForASBD(m_config[kOutputBus].second); - m_stream = pa_stream_new(m_context, streamName.c_str(), &spec, nullptr); - - if (m_stream == nullptr) - { - // ERROR() << "pa_stream_new() failed"; - return kAudioUnitErr_FailedInitialization; - } - - // TODO: support recording - pa_stream_set_state_callback(m_stream, paStreamStateCB, this); - pa_stream_set_write_callback(m_stream, paStreamWriteCB, this); - - pa_stream_connect_playback(m_stream, nullptr, nullptr, PA_STREAM_START_CORKED /*(pa_stream_flags_t) 0*/, - nullptr, nullptr); - - return noErr; -} - -int AudioUnitPA::cardIndex() const -{ - return 0; -} - -void AudioUnitPA::paStreamStateCB(pa_stream* s, void*) -{ - int state = pa_stream_get_state(s); - //TRACE() << "state=" << state; - - switch (state) - { - case PA_STREAM_FAILED: - //ERROR() << "PA stream error: " << pa_strerror(pa_context_errno(pa_stream_get_context(s))); - break; - case PA_STREAM_READY: - //LOG << "PA stream is ready\n"; - break; - } -} - -void AudioUnitPA::paStreamWriteCB(pa_stream* s, size_t length, void* self) -{ - AudioUnitPA* This = static_cast(self); - This->requestDataForPlayback(length); -} - -void AudioUnitPA::requestDataForPlayback(size_t length) -{ - AudioUnitRenderActionFlags flags; - AudioTimeStamp ts; - AudioBufferList* bufs; - OSStatus err; - std::unique_ptr data; - const AudioStreamBasicDescription& config = m_config[kOutputBus].first; - const UInt32 cc = config.mChannelsPerFrame; - - if (!m_stream) - { - // std::cerr << "No stream?!\n"; - return; - } - - //TRACE() << "m_started=" << m_started; - - if (!m_started) - { - std::unique_ptr empty; - size_t len; - int ret; - - len = config.mBytesPerFrame * (config.mSampleRate / 10); - empty.reset(new char[len]); - - memset(empty.get(), 0, len); - - ret = pa_stream_write(m_stream, empty.get(), len, - nullptr, 0, PA_SEEK_RELATIVE); - - // pa_stream_cork(m_stream, true, [](pa_stream*, int, void*) {}, nullptr); - return; - } - - memset(&ts, 0, sizeof(ts)); - - if (isOutputPlanar()) - { - bufs = (AudioBufferList*) operator new(sizeof(AudioBufferList) + (cc-1)*sizeof(AudioBuffer)); - bufs->mNumberBuffers = cc; - } - else - { - bufs = (AudioBufferList*) operator new(sizeof(AudioBufferList)); - bufs->mNumberBuffers = 1; - } - - if (m_shouldAllocateBuffer) - { - if (isOutputPlanar()) - { - UInt32 bytesPerChannel = length / cc; - - data.reset(new uint8_t[cc*bytesPerChannel]); - - for (UInt32 i = 0; i < cc; i++) - { - bufs->mBuffers[i].mNumberChannels = 1; - bufs->mBuffers[i].mDataByteSize = bytesPerChannel; - bufs->mBuffers[i].mData = data.get() + i*bytesPerChannel; - } - } - else - { - bufs->mBuffers[0].mNumberChannels = cc; - bufs->mBuffers[0].mDataByteSize = length; - - data.reset(new uint8_t[bufs->mBuffers[0].mDataByteSize]); - bufs->mBuffers[0].mData = data.get(); - } - } - else - { - if (isOutputPlanar()) - { - for (UInt32 i = 0; i < cc; i++) - { - bufs->mBuffers[i].mDataByteSize = 0; - bufs->mBuffers[i].mData = nullptr; - } - } - else - { - bufs->mBuffers[0].mDataByteSize = 0; - bufs->mBuffers[0].mData = nullptr; - } - } - - // snd_pcm_htimestamp(m_pcmOutput, &availFrames, &alsaTimestamp); - - // TODO: fill in AudioTimeStamp based on some PA timestamp - - err = AudioUnitRender(m_inputUnit.sourceAudioUnit, &flags, &ts, kOutputBus, length / config.mBytesPerFrame, bufs); - - if (err != noErr || bufs->mBuffers[0].mDataByteSize == 0) - { - // ERROR() << "Render callback failed with error " << err; - - // Fill with silence, the error may be temporary - UInt32 bytes = length; - - if (!m_shouldAllocateBuffer) - { - if (isOutputPlanar()) - { - data.reset(new uint8_t[bytes*cc]); - memset(data.get(), 0, bytes*cc); - } - else - { - data.reset(new uint8_t[bytes]); - memset(data.get(), 0, bytes); - } - } - - if (isOutputPlanar()) - { - for (UInt32 i = 0; i < cc; i++) - { - bufs->mBuffers[i].mData = data.get() + bytes*i; - bufs->mBuffers[i].mDataByteSize = bytes; - } - } - else - { - bufs->mBuffers[0].mData = data.get(); - bufs->mBuffers[0].mDataByteSize = bytes; - } - } - - //LOG << "Rendering...\n"; - m_lastRenderError = AudioUnitRender(this, &flags, &ts, kOutputBus, length / config.mBytesPerFrame, bufs); - - operator delete(bufs); -} - -OSStatus AudioUnitPA::render(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) -{ - if (inBusNumber == kOutputBus) - return renderOutput(ioActionFlags, inTimeStamp, inNumberFrames, ioData); - else if (inBusNumber == kInputBus) - return renderInput(ioActionFlags, inTimeStamp, inNumberFrames, ioData); - else - return paramErr; -} - -OSStatus AudioUnitPA::renderOutput(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData) -{ - if (!isOutputPlanar()) - return renderInterleavedOutput(ioActionFlags, inTimeStamp, inNumberFrames, ioData); - else - return renderPlanarOutput(ioActionFlags, inTimeStamp, inNumberFrames, ioData); -} - -OSStatus AudioUnitPA::renderInterleavedOutput(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData) -{ - const AudioStreamBasicDescription& config = m_config[kOutputBus].first; - UInt32 framesSoFar = 0; - - for (UInt32 i = 0; i < ioData->mNumberBuffers; i++) - { - size_t bytes = std::min((inNumberFrames - framesSoFar) * config.mBytesPerFrame, ioData->mBuffers[i].mDataByteSize); - - if (!bytes) - break; - - //LOG << "AudioUnitPA::renderInterleavedOutput(): data=" << ioData->mBuffers[i].mData << ", bytes=" << bytes << std::endl; - pa_stream_write(m_stream, ((char*) ioData->mBuffers[i].mData) + framesSoFar * config.mBytesPerFrame, bytes, - nullptr, 0, PA_SEEK_RELATIVE); - - framesSoFar += ioData->mBuffers[i].mDataByteSize / config.mBytesPerFrame; - } - - return noErr; -} - -OSStatus AudioUnitPA::renderPlanarOutput(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData) -{ - // PulseAudio doesn't support planar audio, we have to perform conversion - return unimpErr; -} - -OSStatus AudioUnitPA::renderInput(AudioUnitRenderActionFlags *ioActionFlags,const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData) -{ - if (!m_outputCallback.inputProc) - return noErr; // We don't push, we should be polled - - if (!isInputPlanar()) - return renderInterleavedInput(ioActionFlags, inTimeStamp, inNumberFrames, ioData); - else - return renderPlanarInput(ioActionFlags, inTimeStamp, inNumberFrames, ioData); -} - -OSStatus AudioUnitPA::renderInterleavedInput(AudioUnitRenderActionFlags *ioActionFlags,const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData) -{ - STUB(); - return unimpErr; -} - -OSStatus AudioUnitPA::renderPlanarInput(AudioUnitRenderActionFlags *ioActionFlags,const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData) -{ - STUB(); - return unimpErr; -} - -pa_sample_spec AudioUnitPA::paSampleSpecForASBD(const AudioStreamBasicDescription& asbd) -{ - const bool isFloat = asbd.mFormatFlags & kAudioFormatFlagIsFloat; - const bool isSigned = asbd.mFormatFlags & kAudioFormatFlagIsSignedInteger; - const bool isBE = asbd.mFormatFlags & kAudioFormatFlagIsBigEndian; - - pa_sample_spec spec; - - spec.rate = asbd.mSampleRate; - spec.channels = asbd.mChannelsPerFrame; - - if (asbd.mFormatID == kAudioFormatLinearPCM) - { - if (isFloat) - spec.format = isBE ? PA_SAMPLE_FLOAT32BE : PA_SAMPLE_FLOAT32LE; - else - { - switch (asbd.mBitsPerChannel) - { - case 8: - m_convertUnsignedSigned = isSigned; - spec.format = PA_SAMPLE_U8; - break; - case 16: - m_convertUnsignedSigned = !isSigned; - - spec.format = isBE ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE; - break; - case 24: - m_convertUnsignedSigned = !isSigned; - - spec.format = isBE ? PA_SAMPLE_S24BE : PA_SAMPLE_S24LE; - break; - case 32: - m_convertUnsignedSigned = !isSigned; - - spec.format = isBE ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE; - break; - } - } - } - else - throw std::runtime_error("Unsupported mFormatID value"); - - if (!pa_sample_spec_valid(&spec)) - throw std::logic_error("Invalid pa_sample_spec constructed"); - - return spec; -} - -OSStatus AudioUnitPA::reset(AudioUnitScope inScope, AudioUnitElement inElement) -{ - STUB(); - return unimpErr; -} - -OSStatus AudioUnitPA::deinit() -{ - TRACE(); - if (!m_stream) - return kAudioUnitErr_Uninitialized; - - pa_stream_disconnect(m_stream); - pa_stream_unref(m_stream); - m_stream = nullptr; - - return noErr; -} - -OSStatus AudioUnitPA::start() -{ - TRACE(); - if (!m_stream) - return kAudioUnitErr_Uninitialized; - - if (pa_stream_is_corked(m_stream)) - { - m_started = true; - - // const AudioStreamBasicDescription& config = m_config[kOutputBus].first; - pa_stream_cork(m_stream, 0, [](pa_stream*, int, void*) {}, nullptr); - // requestDataForPlayback(config.mSampleRate / 10 * config.mBytesPerFrame); - } - - return noErr; -} - -OSStatus AudioUnitPA::stop() -{ - TRACE(); - if (!m_stream) - return kAudioUnitErr_Uninitialized; - - if (pa_stream_is_corked(m_stream)) - pa_stream_cork(m_stream, 1, [](pa_stream*, int, void*) {}, nullptr); - - m_started = false; - return noErr; -} - -struct Completion -{ - std::condition_variable cond; - std::mutex mutex; - bool complete = false; -}; - -void AudioUnitPA::initializePA() -{ - try - { - pa_mainloop_api* mainloopApi; - const char* appName; - Completion comp; - static int retval; - - if ((m_mainloop = pa_mainloop_new()) == nullptr) - throw std::runtime_error("Failed to create PA mainloop"); - - mainloopApi = pa_mainloop_get_api(m_mainloop); - - // TODO: Detect if this is an app in a bundle and get a nice app name - appName = (*_NSGetArgv())[0]; - - if ((m_context = pa_context_new(mainloopApi, appName)) == nullptr) - throw std::runtime_error("Failed to create PA context"); - - pa_context_set_state_callback(m_context, paContextStateCB, &comp); - - if (pa_context_connect(m_context, nullptr, pa_context_flags_t(0), nullptr) < 0) - { - std::stringstream ss; - ss << "pa_context_connect() failed: " << pa_strerror(pa_context_errno(m_context)); - throw std::runtime_error(ss.str()); - } - - m_mainloopThread = new std::thread(pa_mainloop_run, m_mainloop, &retval); - - std::unique_lock lk(comp.mutex); - comp.cond.wait(lk, [&]{ return comp.complete; }); - - if (m_context == nullptr) - throw std::runtime_error("Failed to connect context"); - } - catch (const std::exception& e) - { - pa_mainloop_free(m_mainloop); - m_mainloop = nullptr; - - // ERROR() << e.what(); - } -} - -std::string AudioUnitPA::getAppName() -{ - Class classBundle; - std::string name; - size_t pos; - - classBundle = (Class) objc_getClass("NSBundle"); - - if (classBundle != nullptr) - { - // TODO: get a nice app name - } - - name = (*_NSGetArgv())[0]; - pos = name.rfind('/'); - - if (pos != std::string::npos) - name = name.substr(pos+1); - - return name; -} - -void AudioUnitPA::deinitializePA() -{ - if (m_context != nullptr) - { - pa_context_unref(m_context); - m_context = nullptr; - } - if (m_mainloop != nullptr) - { - pa_mainloop_api* mainloopApi; - - mainloopApi = pa_mainloop_get_api(m_mainloop); - mainloopApi->quit(mainloopApi, 0); - - m_mainloopThread->join(); - delete m_mainloopThread; - - pa_mainloop_free(m_mainloop); - } -} - -void AudioUnitPA::paContextStateCB(pa_context* c, void* priv) -{ - Completion* comp = static_cast(priv); - - // TRACE() << pa_context_get_state(c); - switch (pa_context_get_state(c)) - { - case PA_CONTEXT_READY: - { - std::unique_lock lk(comp->mutex); - comp->complete = true; - comp->cond.notify_one(); - break; - } - - case PA_CONTEXT_FAILED: - // Indicate failure - pa_context_unref(m_context); - m_context = nullptr; - - comp->complete = true; - comp->cond.notify_one(); - break; - default: - break; - } -} diff --git a/src/CoreAudio/AudioToolbox/AudioUnitPA.h b/src/CoreAudio/AudioToolbox/AudioUnitPA.h deleted file mode 100644 index 11f0b254c..000000000 --- a/src/CoreAudio/AudioToolbox/AudioUnitPA.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef AUDIOUNITPA_H -#define AUDIOUNITPA_H -#include "AudioOutputUnitComponent.h" -#include -#include - -#ifndef kOutputBus -# define kOutputBus 0 // playback -# define kInputBus 1 // recording -#endif - -class AudioUnitPA : public AudioOutputUnitComponent -{ -public: - AudioUnitPA(); - virtual ~AudioUnitPA(); - - OSStatus init() override; - OSStatus deinit() override; - - OSStatus start() override; - OSStatus stop() override; - - OSStatus reset(AudioUnitScope inScope, AudioUnitElement inElement) override; - - OSStatus render(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) override; - - int cardIndex() const override; -private: - OSStatus initOutput(); -private: - static void initializePA(); - static void deinitializePA(); - static void paContextStateCB(pa_context* c, void*); - static void paStreamStateCB(pa_stream* s, void*); - static void paStreamWriteCB(pa_stream* s, size_t length, void*); - static std::string getAppName(); - pa_sample_spec paSampleSpecForASBD(const AudioStreamBasicDescription& asbd); - - void requestDataForPlayback(size_t length); - - inline bool isOutputPlanar() const { return m_config[kOutputBus].second.mFormatFlags & kAudioFormatFlagIsNonInterleaved; } - inline bool isInputPlanar() const { return m_config[kInputBus].first.mFormatFlags & kAudioFormatFlagIsNonInterleaved; } - - OSStatus renderOutput(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData); - OSStatus renderInterleavedOutput(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData); - OSStatus renderPlanarOutput(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData); - - OSStatus renderInput(AudioUnitRenderActionFlags *ioActionFlags,const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData); - OSStatus renderInterleavedInput(AudioUnitRenderActionFlags *ioActionFlags,const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData); - OSStatus renderPlanarInput(AudioUnitRenderActionFlags *ioActionFlags,const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData); -private: - static pa_mainloop* m_mainloop; - static pa_context* m_context; - static std::thread* m_mainloopThread; -private: - pa_stream* m_stream; - - // PulseAudio supports only unsigned 8-bit PCM and only signed >8-bit PCM. - // We need to perform conversion of samples if input/output differs. - bool m_convertUnsignedSigned = false; - bool m_started = false; -}; - -#endif /* AUDIOUNITPA_H */ - diff --git a/src/CoreAudio/AudioToolbox/AudioUnitRenderer.cpp b/src/CoreAudio/AudioToolbox/AudioUnitRenderer.cpp index e129ceb61..a098e5a8d 100644 --- a/src/CoreAudio/AudioToolbox/AudioUnitRenderer.cpp +++ b/src/CoreAudio/AudioToolbox/AudioUnitRenderer.cpp @@ -6,6 +6,11 @@ AudioUnitRenderer::AudioUnitRenderer(const AURenderCallbackStruct& cb) { } +AudioUnitRenderer::AudioUnitRenderer() +: AudioUnitComponent({ CFSTR("Fake") }), m_renderCallback({0}) +{ +} + OSStatus AudioUnitRenderer::init() { return noErr; diff --git a/src/CoreAudio/AudioToolbox/AudioUnitRenderer.h b/src/CoreAudio/AudioToolbox/AudioUnitRenderer.h index 952a0b3f1..391941519 100644 --- a/src/CoreAudio/AudioToolbox/AudioUnitRenderer.h +++ b/src/CoreAudio/AudioToolbox/AudioUnitRenderer.h @@ -7,6 +7,9 @@ class AudioUnitRenderer : public AudioUnitComponent { public: AudioUnitRenderer(const AURenderCallbackStruct& cb); + AudioUnitRenderer(); + + AudioUnitRenderer& operator=(const AURenderCallbackStruct& cb) { m_renderCallback = cb; return *this; } OSStatus init() override; OSStatus deinit() override; diff --git a/src/CoreAudio/AudioToolbox/CMakeLists.txt b/src/CoreAudio/AudioToolbox/CMakeLists.txt index 61a9dd104..62dccbdb7 100644 --- a/src/CoreAudio/AudioToolbox/CMakeLists.txt +++ b/src/CoreAudio/AudioToolbox/CMakeLists.txt @@ -6,8 +6,6 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../include/AudioToolbox ) -add_definitions(-DENABLE_PULSEAUDIO=1) - set(audiotoolbox_sources AUComponent.cpp AudioConverter.cpp @@ -17,10 +15,9 @@ set(audiotoolbox_sources AudioQueueBase.cpp AudioQueue.cpp AudioQueueOutput.cpp - # AudioUnitALSA.cpp - AudioUnitBase.cpp - AudioUnit.cpp - AudioUnitPA.cpp + #AudioUnitBase.cpp + #AudioUnit.cpp + #AudioUnitCA.cpp AudioUnitRenderer.cpp AUGraph.cpp ) @@ -44,7 +41,6 @@ add_framework(AudioToolbox avresample avcodec avutil - pulse # This should go away in favor of abstraction inside CoreAudio ) # TODO: Add AudioUnit.framework diff --git a/src/CoreAudio/CMakeLists.txt b/src/CoreAudio/CMakeLists.txt index 604f1c964..b0196ab8d 100644 --- a/src/CoreAudio/CMakeLists.txt +++ b/src/CoreAudio/CMakeLists.txt @@ -20,4 +20,4 @@ wrap_elf(avutil libavutil.so) wrap_elf(pulse libpulse.so) add_subdirectory(CoreAudio) -add_subdirectory(AudioToolbox) +#add_subdirectory(AudioToolbox) diff --git a/src/CoreAudio/CoreAudio/AudioHardwareImpl.cpp b/src/CoreAudio/CoreAudio/AudioHardwareImpl.cpp index 63cb9f764..c45ed7961 100644 --- a/src/CoreAudio/CoreAudio/AudioHardwareImpl.cpp +++ b/src/CoreAudio/CoreAudio/AudioHardwareImpl.cpp @@ -26,6 +26,14 @@ along with Darling. If not, see . AudioHardwareImpl::AudioHardwareImpl(AudioObjectID myId) : m_myId(myId) { + // These are the standard values macOS applications have learned to expect + m_asbd.mChannelsPerFrame = 2; + m_asbd.mSampleRate = 44100; + m_asbd.mBitsPerChannel = 32; + m_asbd.mFormatID = kAudioFormatLinearPCM; + m_asbd.mFormatFlags = kLinearPCMFormatFlagIsFloat | kAudioFormatFlagsNativeEndian; + m_asbd.mFramesPerPacket = 1; + m_asbd.mBytesPerFrame = UInt32(m_asbd.mBytesPerPacket = m_asbd.mChannelsPerFrame * m_asbd.mSampleRate * m_asbd.mBitsPerChannel / 8); } @@ -147,14 +155,7 @@ OSStatus AudioHardwareImpl::getPropertyData(const AudioObjectPropertyAddress* in { if (AudioStreamBasicDescription* asbd = static_cast(outData); asbd && *ioDataSize >= sizeof(AudioStreamBasicDescription)) { - // These are standard values macOS applications have learned to expect - asbd->mChannelsPerFrame = 2; - asbd->mSampleRate = 44100; - asbd->mBitsPerChannel = 32; - asbd->mFormatID = kAudioFormatLinearPCM; - asbd->mFormatFlags = kLinearPCMFormatFlagIsFloat; - asbd->mFramesPerPacket = 1; - asbd->mBytesPerFrame = UInt32(asbd->mBytesPerPacket = asbd->mChannelsPerFrame * asbd->mSampleRate * asbd->mBitsPerChannel / 8); + memcpy(asbd, &m_asbd, sizeof(m_asbd)); } *ioDataSize = sizeof(AudioStreamBasicDescription); return kAudioHardwareNoError; @@ -268,6 +269,24 @@ OSStatus AudioHardwareImpl::setPropertyData(const AudioObjectPropertyAddress* in { return kAudioHardwareNoError; } + case kAudioDevicePropertyStreamFormat: + { + if (inDataSize != sizeof(m_asbd)) + return kAudioHardwareBadPropertySizeError; + + const AudioStreamBasicDescription* setting = static_cast(inData); + if (setting->mFormatID != kAudioFormatLinearPCM) + return kAudioDeviceUnsupportedFormatError; + if (!validateFormat(setting)) + return kAudioDeviceUnsupportedFormatError; + + memcpy(&m_asbd, setting, sizeof(m_asbd)); + + m_asbd.mFramesPerPacket = 1; + m_asbd.mBytesPerFrame = UInt32(m_asbd.mBytesPerPacket = m_asbd.mChannelsPerFrame * m_asbd.mSampleRate * m_asbd.mBitsPerChannel / 8); + + return kAudioHardwareNoError; + } // These make sense only for ALSA, but I find it ridiculous that these properties can be set... case kAudioHardwarePropertyDefaultOutputDevice: return kAudioHardwareNoError; diff --git a/src/CoreAudio/CoreAudio/AudioHardwareImpl.h b/src/CoreAudio/CoreAudio/AudioHardwareImpl.h index e5fdd5102..f74d9b0c3 100644 --- a/src/CoreAudio/CoreAudio/AudioHardwareImpl.h +++ b/src/CoreAudio/CoreAudio/AudioHardwareImpl.h @@ -69,11 +69,13 @@ public: uint32_t bufferSize() const { return m_bufferSize; } AudioObjectID id() const { return m_myId; } + const AudioStreamBasicDescription& asbd() const { return m_asbd; } private: static OSStatus getPropertyCFString(CFStringRef str, UInt32* ioDataSize, void* outData); static OSStatus getPropertyString(CFStringRef str, UInt32* ioDataSize, void* outData); protected: virtual AudioHardwareStream* createStream(AudioDeviceIOProc callback, void* clientData) = 0; + virtual bool validateFormat(const AudioStreamBasicDescription* absd) const { return true; } protected: AudioObjectID m_myId; std::mutex m_procMutex; @@ -84,6 +86,7 @@ protected: uint32_t m_bufferSize = 8192; CFStringRef m_name = CFSTR("unknown"), m_uid = CFSTR("unknown"), m_manufacturer = CFSTR("unknown"), m_modelUid = CFSTR("unknown"); + AudioStreamBasicDescription m_asbd; }; #endif /* AUDIOHARDWAREIMPL_H */ diff --git a/src/CoreAudio/CoreAudio/pulse/AudioHardwareImplPA.cpp b/src/CoreAudio/CoreAudio/pulse/AudioHardwareImplPA.cpp index d1451256a..58fe3b8b0 100644 --- a/src/CoreAudio/CoreAudio/pulse/AudioHardwareImplPA.cpp +++ b/src/CoreAudio/CoreAudio/pulse/AudioHardwareImplPA.cpp @@ -210,3 +210,65 @@ void AudioHardwareImplPA::getPAContext(void (^cb)(pa_context*)) else cb(m_context); } + +pa_sample_spec AudioHardwareImplPA::paSampleSpecForASBD(const AudioStreamBasicDescription& asbd, bool* convertSignedUnsigned) +{ + const bool isFloat = asbd.mFormatFlags & kAudioFormatFlagIsFloat; + const bool isSigned = asbd.mFormatFlags & kAudioFormatFlagIsSignedInteger; + const bool isBE = asbd.mFormatFlags & kAudioFormatFlagIsBigEndian; + + pa_sample_spec spec; + + spec.rate = asbd.mSampleRate; + spec.channels = asbd.mChannelsPerFrame; + spec.format = PA_SAMPLE_INVALID; + + if (convertSignedUnsigned) + *convertSignedUnsigned = false; + + if (asbd.mFormatID == kAudioFormatLinearPCM) + { + if (isFloat) + spec.format = isBE ? PA_SAMPLE_FLOAT32BE : PA_SAMPLE_FLOAT32LE; + else + { + switch (asbd.mBitsPerChannel) + { + case 8: + if (convertSignedUnsigned) + *convertSignedUnsigned = isSigned; + spec.format = PA_SAMPLE_U8; + break; + case 16: + if (convertSignedUnsigned) + *convertSignedUnsigned = !isSigned; + + spec.format = isBE ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE; + break; + case 24: + if (convertSignedUnsigned) + *convertSignedUnsigned = !isSigned; + + if (asbd.mFormatFlags & kAudioFormatFlagIsPacked) + spec.format = isBE ? PA_SAMPLE_S24BE : PA_SAMPLE_S24LE; + else + spec.format = isBE ? PA_SAMPLE_S24_32BE : PA_SAMPLE_S24_32LE; + break; + case 32: + if (convertSignedUnsigned) + *convertSignedUnsigned = !isSigned; + + spec.format = isBE ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE; + break; + } + } + } + + return spec; +} + +bool AudioHardwareImplPA::validateFormat(const AudioStreamBasicDescription* asbd) const +{ + pa_sample_spec spec = paSampleSpecForASBD(*asbd); + return pa_sample_spec_valid(&spec); +} diff --git a/src/CoreAudio/CoreAudio/pulse/AudioHardwareImplPA.h b/src/CoreAudio/CoreAudio/pulse/AudioHardwareImplPA.h index bcdd16180..abf9a9f58 100644 --- a/src/CoreAudio/CoreAudio/pulse/AudioHardwareImplPA.h +++ b/src/CoreAudio/CoreAudio/pulse/AudioHardwareImplPA.h @@ -37,8 +37,10 @@ public: const void* inQualifierData, UInt32 inDataSize, const void* inData) override; static void getPAContext(void (^cb)(pa_context*)); + static pa_sample_spec paSampleSpecForASBD(const AudioStreamBasicDescription& asbd, bool* convertSignedUnsigned = nullptr); protected: AudioHardwareStream* createStream(AudioDeviceIOProc callback, void* clientData) override; + bool validateFormat(const AudioStreamBasicDescription* asbd) const override; private: static pa_context* m_context; static std::unique_ptr m_loop; diff --git a/src/CoreAudio/CoreAudio/pulse/AudioHardwareStreamPA.cpp b/src/CoreAudio/CoreAudio/pulse/AudioHardwareStreamPA.cpp index 7e1a76230..a87cd0acf 100644 --- a/src/CoreAudio/CoreAudio/pulse/AudioHardwareStreamPA.cpp +++ b/src/CoreAudio/CoreAudio/pulse/AudioHardwareStreamPA.cpp @@ -20,6 +20,8 @@ along with Darling. If not, see . #include "AudioHardwareStreamPA.h" #include "AudioHardwareImplPA.h" #include +#include +#include AudioHardwareStreamPA::AudioHardwareStreamPA(AudioHardwareImpl* hw, AudioDeviceIOProc callback, void* clientData) : AudioHardwareStream(hw, false), m_callback(callback), m_clientData(clientData) @@ -30,18 +32,9 @@ AudioHardwareStreamPA::AudioHardwareStreamPA(AudioHardwareImpl* hw, AudioDeviceI std::cerr << "Failed to get PulseAudio context\n"; return; } - - pa_sample_spec spec; - - spec.rate = 44100; - spec.channels = 2; -#if defined(__BIG_ENDIAN__) - spec.format = PA_SAMPLE_FLOAT32BE; -#elif defined(__LITTLE_ENDIAN__) - spec.format = PA_SAMPLE_FLOAT32LE; -#else -# error Unknown endianess! -#endif + + pa_sample_spec spec = AudioHardwareImplPA::paSampleSpecForASBD(hw->asbd(), &m_convertSignedUnsigned); + if (!pa_sample_spec_valid(&spec)) { std::cerr << "Failed to create a valid pa_sample_spec\n"; @@ -85,3 +78,83 @@ void AudioHardwareStreamPA::stop(void(^cbDone)()) }, this); } +// This function seems to only convert unsigned to signed, but it works both ways in practice +template +void transform(typename std::make_unsigned::type* data) +{ + typedef typename std::make_signed::type signed_type; + signed_type* s = reinterpret_cast(data); + + *s = *data + std::numeric_limits::min(); +} + +void AudioHardwareStreamPA::transformSignedUnsigned(AudioBufferList* abl) const +{ + const AudioStreamBasicDescription& asbd = m_hw->asbd(); + const bool revEndian = (asbd.mFormatFlags & kAudioFormatFlagIsBigEndian) != (asbd.mFormatFlags & kAudioFormatFlagsNativeEndian); + + for (int i = 0; i < abl->mNumberBuffers; i++) + { + AudioBuffer* buf = &abl->mBuffers[i]; + + switch (asbd.mBitsPerChannel) + { + case 8: + for (int j = 0; j < buf->mDataByteSize; j++) + transform(reinterpret_cast(buf->mData) + j); + break; + case 16: + if (!revEndian) + { + for (int j = 0; j < buf->mDataByteSize / sizeof(uint16_t); j++) + transform(reinterpret_cast(buf->mData) + j); + } + else + { + for (int j = 0; j < buf->mDataByteSize / sizeof(uint16_t); j++) + { + uint16_t v = __builtin_bswap16(*(reinterpret_cast(buf->mData) + j)); + transform(&v); + *(reinterpret_cast(buf->mData) + j) = __builtin_bswap16(v); + } + } + break; + case 24: + for (int j = 0; j < buf->mDataByteSize / sizeof(uint32_t); j++) + { + uint32_t v = *(reinterpret_cast(buf->mData) + j); + if (revEndian) + v = __builtin_bswap32(v); + + // sign extend + if (v & 0x800000) + v |= 0xff000000; + + transform(&v); + + v &= 0xffffff; + if (revEndian) + v = __builtin_bswap32(v); + + *(reinterpret_cast(buf->mData) + j) = v; + } + break; + case 32: + if (!revEndian) + { + for (int j = 0; j < buf->mDataByteSize / sizeof(uint32_t); j++) + transform(reinterpret_cast(buf->mData) + j); + } + else + { + for (int j = 0; j < buf->mDataByteSize / sizeof(uint32_t); j++) + { + uint32_t v = __builtin_bswap32(*(reinterpret_cast(buf->mData) + j)); + transform(&v); + *(reinterpret_cast(buf->mData) + j) = __builtin_bswap32(v); + } + } + break; + } + } +} diff --git a/src/CoreAudio/CoreAudio/pulse/AudioHardwareStreamPA.h b/src/CoreAudio/CoreAudio/pulse/AudioHardwareStreamPA.h index cc0b11201..7e539e139 100644 --- a/src/CoreAudio/CoreAudio/pulse/AudioHardwareStreamPA.h +++ b/src/CoreAudio/CoreAudio/pulse/AudioHardwareStreamPA.h @@ -33,11 +33,13 @@ public: void stop(void(^cbDone)()) override; protected: virtual void start() = 0; + void transformSignedUnsigned(AudioBufferList* abl) const; protected: AudioDeviceIOProc m_callback; void* m_clientData; pa_stream* m_stream; void(^m_cbDone)(); + bool m_convertSignedUnsigned = false; }; #endif /* AUDIOHARDWARESTREAMPA_H */ diff --git a/src/CoreAudio/CoreAudio/pulse/AudioHardwareStreamPAInput.cpp b/src/CoreAudio/CoreAudio/pulse/AudioHardwareStreamPAInput.cpp index 710e6a02c..4ca676552 100644 --- a/src/CoreAudio/CoreAudio/pulse/AudioHardwareStreamPAInput.cpp +++ b/src/CoreAudio/CoreAudio/pulse/AudioHardwareStreamPAInput.cpp @@ -63,6 +63,8 @@ void AudioHardwareStreamPAInput::paStreamReadCB(pa_stream* s, size_t length, voi // This->m_dump.write((char*) abl->mBuffers[0].mData, nbytes); // This->m_dump.flush(); + if (This->m_convertSignedUnsigned) + This->transformSignedUnsigned(abl); // std::cout << "AudioHardwareStreamPAInput::paStreamReadCB(): bytes=" << nbytes << std::endl; OSStatus status = This->m_callback(This->m_hw->id(), &fake, abl, &fake, nullptr, nullptr, This->m_clientData); diff --git a/src/CoreAudio/CoreAudio/pulse/AudioHardwareStreamPAOutput.cpp b/src/CoreAudio/CoreAudio/pulse/AudioHardwareStreamPAOutput.cpp index 32b0fd071..ce9ac42ec 100644 --- a/src/CoreAudio/CoreAudio/pulse/AudioHardwareStreamPAOutput.cpp +++ b/src/CoreAudio/CoreAudio/pulse/AudioHardwareStreamPAOutput.cpp @@ -61,6 +61,9 @@ void AudioHardwareStreamPAOutput::paStreamWriteCB(pa_stream* s, size_t length, v } else { + if (This->m_convertSignedUnsigned) + This->transformSignedUnsigned(abl); + // std::cout << "AudioHardwareStreamPAOutput::paStreamWriteCB(): got " << abl->mBuffers[0].mDataByteSize << " bytes\n"; int rv = pa_stream_write(This->m_stream, abl->mBuffers[0].mData, abl->mBuffers[0].mDataByteSize, nullptr, 0, PA_SEEK_RELATIVE); if (rv != 0) diff --git a/src/CoreAudio/include/CoreAudio/CoreAudioTypes.h b/src/CoreAudio/include/CoreAudio/CoreAudioTypes.h index b5e9235e7..991561279 100644 --- a/src/CoreAudio/include/CoreAudio/CoreAudioTypes.h +++ b/src/CoreAudio/include/CoreAudio/CoreAudioTypes.h @@ -111,7 +111,6 @@ enum enum { - kAudioFormatFlagsNativeEndian = 0, kAudioFormatFlagIsFloat = (1 << 0), // 0x1 kAudioFormatFlagIsBigEndian = (1 << 1), // 0x2 kAudioFormatFlagIsSignedInteger = (1 << 2), // 0x4 @@ -120,6 +119,11 @@ enum kAudioFormatFlagIsNonInterleaved = (1 << 5), // 0x20 kAudioFormatFlagIsNonMixable = (1 << 6), // 0x40 kAudioFormatFlagsAreAllClear = (1 << 31), +#if defined(__BIG_ENDIAN__) + kAudioFormatFlagsNativeEndian = kAudioFormatFlagIsBigEndian, +#else + kAudioFormatFlagsNativeEndian = 0, +#endif kAudioFormatFlagsCanonical = kAudioFormatFlagIsFloat | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked, kAudioFormatFlagsNativeFloatPacked = kAudioFormatFlagIsFloat | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked, diff --git a/src/external/libkqueue b/src/external/libkqueue index cf2248b1e..d534499fd 160000 --- a/src/external/libkqueue +++ b/src/external/libkqueue @@ -1 +1 @@ -Subproject commit cf2248b1ed7a8793c1541bcbb9f6f7f073888125 +Subproject commit d534499fdbf493ee6ec29e887869f2541ae8f8fc diff --git a/src/external/lkm b/src/external/lkm index 84d38d9a1..e8e296730 160000 --- a/src/external/lkm +++ b/src/external/lkm @@ -1 +1 @@ -Subproject commit 84d38d9a15617e80c1b1335f7dc5b87879045363 +Subproject commit e8e2967304f224b2036912ad77d8b79bf5c50715 diff --git a/src/frameworks/CoreServices/MacMemory.cpp b/src/frameworks/CoreServices/MacMemory.cpp index 3a8cde040..b51d99dfa 100644 --- a/src/frameworks/CoreServices/MacMemory.cpp +++ b/src/frameworks/CoreServices/MacMemory.cpp @@ -21,12 +21,7 @@ along with Darling. If not, see . #include #include #include - -struct PtrInternal -{ - uintptr_t size; - uint8_t data[]; -}; +#include void BlockMove(const void* src, void* dst, size_t count) { @@ -60,21 +55,17 @@ void BlockZeroUncached(void* dst, size_t count) Ptr NewPtr(long len) { - PtrInternal* ptr = (PtrInternal*) malloc(len + sizeof(uintptr_t)); - ptr->size = len; - return (Ptr) ptr->data; + return (Ptr) malloc(len); } Ptr NewPtrClear(long len) { - PtrInternal* ptr = (PtrInternal*) calloc(1, len + sizeof(uintptr_t)); - ptr->size = len; - return (Ptr) ptr->data; + return (Ptr) calloc(1, len); } void DisposePtr(Ptr p) { - free(((uint8_t*)p) - sizeof(uintptr_t)); + free(p); } void DisposeHandle(Handle handle) @@ -136,15 +127,13 @@ Handle NewEmptyHandle(void) long GetPtrSize(Ptr ptr) { - PtrInternal* internal = (PtrInternal*)(((char*) ptr) - sizeof(uintptr_t)); - return internal->size; + return malloc_size(ptr); } void SetPtrSize(Ptr ptr, long newSize) { - PtrInternal* internal = (PtrInternal*)(((char*) ptr) - sizeof(uintptr_t)); - if (newSize < internal->size) - internal->size = newSize; + if (newSize < malloc_size(ptr)) + realloc(ptr, newSize); // Let's hope this never changes the ptr, but the whole SetPtrSize() call is idiotic else { // This is not possible with a non-relocatable block @@ -162,12 +151,7 @@ void SetHandleSize(Handle h, long newSize) } else { - PtrInternal* internal = (PtrInternal*)(((char*) *h) - sizeof(uintptr_t)); - - internal = (PtrInternal*) realloc(internal, newSize + sizeof(uintptr_t)); - internal->size = newSize; - - *h = (Ptr) internal->data; + *h = (Ptr) realloc(*h, newSize); } } diff --git a/src/kernel/emulation/linux/signal/sigaction.h b/src/kernel/emulation/linux/signal/sigaction.h index e2e7a51d3..6fe97d4fc 100644 --- a/src/kernel/emulation/linux/signal/sigaction.h +++ b/src/kernel/emulation/linux/signal/sigaction.h @@ -47,8 +47,15 @@ struct linux_siginfo int si_signo; int si_errno; int si_code; - int si_pid; - int si_uid; + union + { + struct + { + int si_pid; + int si_uid; + }; + void* si_addr; + }; union { diff --git a/src/kernel/emulation/linux/signal/sigexc.c b/src/kernel/emulation/linux/signal/sigexc.c index ee220f3af..3572be11b 100644 --- a/src/kernel/emulation/linux/signal/sigexc.c +++ b/src/kernel/emulation/linux/signal/sigexc.c @@ -30,6 +30,7 @@ void darling_sigexc_uninstall(void); void sigrt_handler(int signum, struct linux_siginfo* info, void* ctxt); #define SIGEXC_TSD_KEY 102 +#define SIGEXC_CONTEXT_TSD_KEY 103 static char sigexc_altstack[8192]; static struct bsd_stack orig_stack; @@ -45,6 +46,8 @@ static void thread_state_to_mcontext(const x86_thread_state32_t* s, struct linux static void float_state_to_mcontext(const x86_float_state32_t* s, linux_fpregset_t fx); #endif +static void state_to_kernel(struct linux_ucontext* ctxt, thread_t thread); + #define DEBUG_SIGEXC #ifdef DEBUG_SIGEXC #define kern_printf(...) { char buf[128]; __simple_sprintf(buf, __VA_ARGS__); lkm_call(0x1028, buf); } @@ -173,12 +176,25 @@ void sigrt_handler(int signum, struct linux_siginfo* info, void* ctxt) int sig = info->si_value; if (sig == SIGNAL_THREAD_SUSPEND) { - kern_printf("sigexc: SIGNAL_THREAD_SUSPEND"); - sigexc_fake_suspend(ctxt); + kern_printf("sigexc: SIGNAL_THREAD_SUSPEND\n"); + if (_pthread_getspecific_direct(SIGEXC_TSD_KEY) == 0) + sigexc_fake_suspend(ctxt); + else + { + kern_printf("sigexc: already suspended\n"); + void* ctxt = _pthread_getspecific_direct(SIGEXC_CONTEXT_TSD_KEY); + + if (ctxt) + { + thread_t thread = mach_thread_self(); + state_to_kernel(ctxt, thread); + kern_printf("sigexc: state_to_kernel complete\n"); + } + } } else if (sig == SIGNAL_THREAD_RESUME) { - kern_printf("sigexc: SIGNAL_THREAD_RESUME"); + kern_printf("sigexc: SIGNAL_THREAD_RESUME\n"); } else if (sig < 0) { @@ -310,6 +326,8 @@ static void state_to_kernel(struct linux_ucontext* ctxt, thread_t thread) memset(&fstate, 0, sizeof(fstate)); } + __simple_kprintf("sigexc: Telling kernel our RIP is %p\n", tstate.__rip); + thread_set_state(thread, x86_THREAD_STATE64, (thread_state_t) &tstate, x86_THREAD_STATE64_COUNT); thread_set_state(thread, x86_FLOAT_STATE64, (thread_state_t) &fstate, x86_FLOAT_STATE64_COUNT); #elif defined(__i386__) @@ -420,6 +438,11 @@ void sigexc_handler(int linux_signum, struct linux_siginfo* info, struct linux_u return; } + if (linux_signum == 11) + { + kern_printf("sigexc: faulting address: %p, code: %d\n", info->si_addr, info->si_code); + } + // SIGSEGV + SIGBUS -> EXC_BAD_ACCESS // SIGTRAP -> EXC_BREAKPOINT // SIGILL -> EXC_BAD_INSTRUCTION @@ -485,6 +508,7 @@ void sigexc_handler(int linux_signum, struct linux_siginfo* info, struct linux_u if (port != 0) { _pthread_setspecific_direct(SIGEXC_TSD_KEY, bsd_signum); + _pthread_setspecific_direct(SIGEXC_CONTEXT_TSD_KEY, ctxt); kern_return_t ret; @@ -552,6 +576,8 @@ void sigexc_handler(int linux_signum, struct linux_siginfo* info, struct linux_u handler_linux_to_bsd(linux_signum, info, ctxt); } } + + kern_printf("sigexc: handler (%d) returning\n", linux_signum); } #if defined(__x86_64__) @@ -578,6 +604,8 @@ void mcontext_to_thread_state(const struct linux_gregset* regs, x86_thread_state s->__cs = regs->cs; s->__fs = regs->fs; s->__gs = regs->gs; + + kern_printf("sigexc: saving to kernel: RIP %p, eflags 0x%x\n", regs->rip, regs->efl); } void mcontext_to_float_state(const linux_fpregset_t fx, x86_float_state64_t* s) @@ -622,6 +650,8 @@ void thread_state_to_mcontext(const x86_thread_state64_t* s, struct linux_gregse regs->cs = s->__cs; regs->fs = s->__fs; regs->gs = s->__gs; + + kern_printf("sigexc: Next RIP will be %p, eflags: 0x%x\n", regs->rip, regs->efl); // single step trace bit is 0x100 } void float_state_to_mcontext(const x86_float_state64_t* s, linux_fpregset_t fx)