/*************************************************************************** * * * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2009 Grigor Iliev * * * * 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., 59 Temple Place, Suite 330, Boston, * * MA 02111-1307 USA * ***************************************************************************/ #include "AudioOutputDeviceCoreAudio.h" #include "../../common/global_private.h" namespace LinuxSampler { void AudioOutputDeviceCoreAudio::AudioQueueListener ( void *inUserData, AudioQueueRef inAQ, AudioQueuePropertyID inID ) { switch(inID) { case kAudioQueueProperty_IsRunning: break; case kAudioQueueProperty_CurrentDevice: break; } } void AudioOutputDeviceCoreAudio::HandleOutputBuffer ( void *aqData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer ) { AQPlayerState* pAqData = (AQPlayerState*) aqData; if (atomic_read(&(pAqData->mIsRunning)) == 0) { AudioQueueFlush(pAqData->mQueue); AudioQueueStop (pAqData->mQueue, true); return; } if(atomic_read(&(pAqData->pDevice->restartQueue))) return; uint bufferSize = pAqData->pDevice->uiBufferSize; // let all connected engines render 'fragmentSize' sample points pAqData->pDevice->RenderAudio(bufferSize); int16_t* pDataBuf = (int16_t*)(inBuffer->mAudioData); uint uiCoreAudioChannels = pAqData->pDevice->uiCoreAudioChannels; // convert from DSP value range (-1.0..+1.0) to 16 bit integer value // range (-32768..+32767), check clipping and copy to Alsa output buffer for (int c = 0; c < uiCoreAudioChannels; c++) { float* in = pAqData->pDevice->Channels[c]->Buffer(); for (int i = 0, o = c; i < bufferSize; i++ , o += uiCoreAudioChannels) { float sample_point = in[i] * 32768.0f; if (sample_point < -32768.0) sample_point = -32768.0; if (sample_point > 32767.0) sample_point = 32767.0; pDataBuf[o] = (int16_t) sample_point; } } inBuffer->mAudioDataByteSize = (uiCoreAudioChannels * 2) * bufferSize; OSStatus res = AudioQueueEnqueueBuffer(pAqData->mQueue, inBuffer, 0, NULL); if(res) std::cerr << "AudioQueueEnqueueBuffer: Error " << res << std::endl; } void AudioOutputDeviceCoreAudio::DeviceChanged() { dmsg(1,("Restarting audio queue...")); atomic_set(&restartQueue, 1); } AudioOutputDeviceCoreAudio::AudioOutputDeviceCoreAudio ( std::map Parameters ) : AudioOutputDevice(Parameters), Thread(true, true, 1, 0), CurrentDevice(0) { dmsg(2,("AudioOutputDeviceCoreAudio::AudioOutputDeviceCoreAudio()\n")); if(CAAudioDeviceListModel::GetModel()->GetOutputDeviceCount() < 1) { throw Exception("No audio output device found"); } atomic_set(&pausedNew, 0); pausedOld = 0; atomic_set(&restartQueue, 0); uiCoreAudioChannels = ((DeviceCreationParameterInt*)Parameters["CHANNELS"])->ValueAsInt(); uint samplerate = ((DeviceCreationParameterInt*)Parameters["SAMPLERATE"])->ValueAsInt(); uiBufferNumber = ((DeviceCreationParameterInt*)Parameters["BUFFERS"])->ValueAsInt(); uiBufferSize = ((DeviceCreationParameterInt*)Parameters["BUFFERSIZE"])->ValueAsInt(); int device = 0; try { device = ((ParameterDevice*)Parameters["DEVICE"])->GetDeviceIndex(); } catch(Exception x) { } CurrentDevice = CAAudioDeviceListModel::GetModel()->GetOutputDevice(device); CurrentDevice.AddListener(this); aqPlayerState.mDataFormat.mSampleRate = samplerate; aqPlayerState.mDataFormat.mFormatID = kAudioFormatLinearPCM; aqPlayerState.mDataFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; aqPlayerState.mDataFormat.mBytesPerPacket = uiCoreAudioChannels * 2; aqPlayerState.mDataFormat.mFramesPerPacket = 1; aqPlayerState.mDataFormat.mBytesPerFrame = uiCoreAudioChannels * 2; aqPlayerState.mDataFormat.mChannelsPerFrame = uiCoreAudioChannels; aqPlayerState.mDataFormat.mBitsPerChannel = 16; aqPlayerState.mBuffers = new AudioQueueBufferRef[uiBufferNumber]; aqPlayerState.bufferByteSize = MaxSamplesPerCycle() * aqPlayerState.mDataFormat.mBytesPerFrame; aqPlayerState.mNumPacketsToRead = MaxSamplesPerCycle(); aqPlayerState.pDevice = this; aqPlayerState.mQueue = NULL; uint fragmentSize = MaxSamplesPerCycle(); // create audio channels for this audio device to which the sampler engines can write to for (int i = 0; i < uiCoreAudioChannels; i++) { this->Channels.push_back(new AudioChannel(i, fragmentSize)); } StartThread(); if (!((DeviceCreationParameterBool*)Parameters["ACTIVE"])->ValueAsBool()) { Stop(); } } AudioOutputDeviceCoreAudio::~AudioOutputDevice() { atomic_set(&(aqPlayerState.mIsRunning), 0); destroyMutex.Lock(); AudioQueueDispose(aqPlayerState.mQueue, true); destroyMutex.Unlock(); delete [] aqPlayerState.mBuffers; CurrentDevice.RemoveListener(this); } void AudioOutputDeviceCoreAudio::SetAudioDataFormat(AudioStreamBasicDescription* pDataFormat) { pDataFormat->mSampleRate = 44100; pDataFormat->mFormatID = kAudioFormatLinearPCM; pDataFormat->mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; pDataFormat->mBytesPerPacket = 4; pDataFormat->mFramesPerPacket = 1; pDataFormat->mBytesPerFrame = 4; pDataFormat->mChannelsPerFrame = 2; pDataFormat->mBitsPerChannel = 16; } String AudioOutputDeviceCoreAudio::Name() { return "COREAUDIO"; } String AudioOutputDeviceCoreAudio::Description() { return "Apple CoreAudio"; } String AudioOutputDeviceCoreAudio::Version() { String s = "$Revision: 1.4 $"; return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword } void AudioOutputDeviceCoreAudio::Play() { atomic_set(&pausedNew, 0); } bool AudioOutputDeviceCoreAudio::IsPlaying() { return !atomic_read(&pausedNew); } void AudioOutputDeviceCoreAudio::Stop() { atomic_set(&pausedNew, 1); } uint AudioOutputDeviceCoreAudio::MaxSamplesPerCycle() { return uiBufferSize; } uint AudioOutputDeviceCoreAudio::SampleRate() { return aqPlayerState.mDataFormat.mSampleRate; } String AudioOutputDeviceCoreAudio::Driver() { return Name(); } AudioChannel* AudioOutputDeviceCoreAudio::CreateChannel(uint ChannelNr) { // just create a mix channel return new AudioChannel(ChannelNr, Channel(ChannelNr % uiCoreAudioChannels)); } void AudioOutputDeviceCoreAudio::FillBuffers() { for (int i = 0; i < uiBufferNumber; ++i) { HandleOutputBuffer ( &aqPlayerState, aqPlayerState.mQueue, aqPlayerState.mBuffers[i] ); } } void AudioOutputDeviceCoreAudio::PrimeAudioQueue() { OSStatus res = AudioQueuePrime(aqPlayerState.mQueue, 0, NULL); if(res) { String s = String("AudioQueuePrime: Error ") + ToString(res); throw Exception(s); } } void AudioOutputDeviceCoreAudio::CreateAndStartAudioQueue() { OSStatus res = AudioQueueNewOutput ( &aqPlayerState.mDataFormat, HandleOutputBuffer, &aqPlayerState, CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &aqPlayerState.mQueue ); if(res) { String s = String("AudioQueueNewOutput: Error ") + ToString(res); throw Exception(s); } CFStringRef devUID = CFStringCreateWithCString ( NULL, CurrentDevice.GetUID().c_str(), kCFStringEncodingASCII ); res = AudioQueueSetProperty ( aqPlayerState.mQueue, kAudioQueueProperty_CurrentDevice, &devUID, sizeof(CFStringRef) ); CFRelease(devUID); if(res) { String s = String("Failed to set audio device: ") + ToString(res); throw Exception(s); } for (int i = 0; i < uiBufferNumber; ++i) { res = AudioQueueAllocateBuffer ( aqPlayerState.mQueue, aqPlayerState.bufferByteSize, &aqPlayerState.mBuffers[i] ); if(res) { String s = String("AudioQueueAllocateBuffer: Error "); throw Exception(s + ToString(res)); } } res = AudioQueueAddPropertyListener ( aqPlayerState.mQueue, kAudioQueueProperty_CurrentDevice, AudioQueueListener, NULL ); if(res) std::cerr << "Failed to register device change listener: " << res << std::endl; res = AudioQueueAddPropertyListener ( aqPlayerState.mQueue, kAudioQueueProperty_IsRunning, AudioQueueListener, NULL ); if(res) std::cerr << "Failed to register running listener: " << res << std::endl; Float32 gain = 1.0; res = AudioQueueSetParameter ( aqPlayerState.mQueue, kAudioQueueParam_Volume, gain ); if(res) std::cerr << "AudioQueueSetParameter: Error " << res << std::endl; atomic_set(&(aqPlayerState.mIsRunning), 1); FillBuffers(); PrimeAudioQueue(); res = AudioQueueStart(aqPlayerState.mQueue, NULL); if(res) { String s = String("AudioQueueStart: Error ") + ToString(res); throw Exception(s); } } void AudioOutputDeviceCoreAudio::DestroyAudioQueue() { AudioQueueFlush(aqPlayerState.mQueue); AudioQueueStop (aqPlayerState.mQueue, true); AudioQueueDispose(aqPlayerState.mQueue, true); aqPlayerState.mQueue = NULL; } /** * Entry point for the thread. */ int AudioOutputDeviceCoreAudio::Main() { dmsg(1,("CoreAudio thread started\n")); OSStatus res; if(aqPlayerState.mQueue == NULL) { /* * Need to be run from this thread because of CFRunLoopGetCurrent() * which returns the CFRunLoop object for the current thread. */ CreateAndStartAudioQueue(); } destroyMutex.Lock(); do { if(atomic_read(&pausedNew) != pausedOld) { pausedOld = atomic_read(&pausedNew); if(pausedOld) { res = AudioQueuePause(aqPlayerState.mQueue); if(res) std::cerr << "AudioQueuePause: Error " << res << std::endl; } else { res = AudioQueuePrime(aqPlayerState.mQueue, 0, NULL); if(res) std::cerr << "AudioQueuePrime: Error " << res << std::endl; res = AudioQueueStart(aqPlayerState.mQueue, NULL); if(res) std::cerr << "AudioQueueStart: Error " << res << std::endl; } } if(atomic_read(&restartQueue)) { DestroyAudioQueue(); try { CreateAndStartAudioQueue(); } catch(Exception e) { destroyMutex.Unlock(); throw e; } atomic_set(&restartQueue, 0); dmsg(1,("Audio queue restarted")); } CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.2, false); } while (atomic_read(&(aqPlayerState.mIsRunning))); destroyMutex.Unlock(); dmsg(2,("CoreAudio thread stopped\n")); pthread_exit(NULL); return 0; } // *************** ParameterDevice *************** // * AudioOutputDeviceCoreAudio::ParameterDevice::ParameterDevice() : DeviceCreationParameterString() { InitWithDefault(); // use default device } AudioOutputDeviceCoreAudio::ParameterDevice::ParameterDevice(String s) throw (Exception) : DeviceCreationParameterString(s) { } String AudioOutputDeviceCoreAudio::ParameterDevice::Description() { return "Output device to be used"; } bool AudioOutputDeviceCoreAudio::ParameterDevice::Fix() { return true; } bool AudioOutputDeviceCoreAudio::ParameterDevice::Mandatory() { return false; } std::map AudioOutputDeviceCoreAudio::ParameterDevice::DependsAsParameters() { return std::map(); // no dependencies } optional AudioOutputDeviceCoreAudio::ParameterDevice::DefaultAsString(std::map Parameters) { CAAudioDeviceListModel* devs = CAAudioDeviceListModel::GetModel(); if (devs->GetOutputDeviceCount() < 1) { throw Exception("AudioOutputDeviceCoreAudio: Can't find any output device"); } UInt32 idx = devs->GetOutputDeviceIndex(devs->GetDefaultOutputDeviceID()); return CreateDeviceName(idx); } std::vector AudioOutputDeviceCoreAudio::ParameterDevice::PossibilitiesAsString(std::map Parameters) { std::vector deviceNames; CAAudioDeviceListModel* devs = CAAudioDeviceListModel::GetModel(); for(int i = 0; i < devs->GetOutputDeviceCount(); i++) { if(devs->GetOutputDevice(i).GetChannelNumber() < 1) continue; deviceNames.push_back(CreateDeviceName(i)); } return deviceNames; } String AudioOutputDeviceCoreAudio::ParameterDevice::CreateDeviceName(int devIndex) { CAAudioDeviceListModel* devs = CAAudioDeviceListModel::GetModel(); // Note that the space " " is used as delimiter to obtain the // device index. See GetDeviceIndex() return ToString(devIndex + 1) + " " + devs->GetOutputDevice(devIndex).GetName(); } void AudioOutputDeviceCoreAudio::ParameterDevice::OnSetValue(String s) throw (Exception) { // not posssible, as parameter is fix } String AudioOutputDeviceCoreAudio::ParameterDevice::Name() { return "DEVICE"; } int AudioOutputDeviceCoreAudio::ParameterDevice::GetDeviceIndex() { String s = ValueAsString(); if(s.empty()) return -1; int n = s.find(' '); s = s.substr(0, n); return ToInt(s) - 1; } // *************** ParameterSampleRate *************** // * AudioOutputDeviceCoreAudio::ParameterSampleRate::ParameterSampleRate() : AudioOutputDevice::ParameterSampleRate::ParameterSampleRate() { } AudioOutputDeviceCoreAudio::ParameterSampleRate::ParameterSampleRate(String s) : AudioOutputDevice::ParameterSampleRate::ParameterSampleRate(s) { } std::map AudioOutputDeviceCoreAudio::ParameterSampleRate::DependsAsParameters() { static ParameterDevice device; std::map dependencies; dependencies[device.Name()] = &device; return dependencies; } optional AudioOutputDeviceCoreAudio::ParameterSampleRate::DefaultAsInt(std::map Parameters) { dmsg(2,("AudioOutputDeviceCoreAudio::ParameterSampleRate::DefaultAsInt()\n")); ParameterDevice dev(Parameters["DEVICE"]); int samplerate = 44100; try { int idx = dev.GetDeviceIndex(); CAAudioDeviceListModel* devs = CAAudioDeviceListModel::GetModel(); samplerate = devs->GetOutputDevice(idx).GetDefaultSamplerate(); } catch(Exception e) { } return samplerate; } std::vector AudioOutputDeviceCoreAudio::ParameterSampleRate::PossibilitiesAsInt(std::map Parameters) { ParameterDevice dev(Parameters["DEVICE"]); std::vector srates; try { int idx = dev.GetDeviceIndex(); CAAudioDeviceListModel* devs = CAAudioDeviceListModel::GetModel(); srates = devs->GetOutputDevice(idx).GetNominalSamplerates(); } catch(Exception x) { } return srates; } // *************** ParameterChannels *************** // * AudioOutputDeviceCoreAudio::ParameterChannels::ParameterChannels() : AudioOutputDevice::ParameterChannels::ParameterChannels() { } AudioOutputDeviceCoreAudio::ParameterChannels::ParameterChannels(String s) : AudioOutputDevice::ParameterChannels::ParameterChannels(s) { } optional AudioOutputDeviceCoreAudio::ParameterChannels::DefaultAsInt(std::map Parameters) { dmsg(2,("AudioOutputDeviceCoreAudio::ParameterChannels::DefaultAsInt()\n")); ParameterDevice dev(Parameters["DEVICE"]); int chns = 2; try { int idx = dev.GetDeviceIndex(); CAAudioDeviceListModel* devs = CAAudioDeviceListModel::GetModel(); chns = devs->GetOutputDevice(idx).GetChannelNumber(); } catch(Exception e) { } return chns; } std::vector AudioOutputDeviceCoreAudio::ParameterChannels::PossibilitiesAsInt(std::map Parameters) { ParameterDevice dev(Parameters["DEVICE"]); std::vector chns; try { int idx = dev.GetDeviceIndex(); CAAudioDeviceListModel* devs = CAAudioDeviceListModel::GetModel(); for(int i = 1; i <= devs->GetOutputDevice(idx).GetChannelNumber(); i++) { chns.push_back(i); } } catch(Exception x) { } return chns; } // *************** ParameterBuffers *************** // * AudioOutputDeviceCoreAudio::ParameterBuffers::ParameterBuffers() : DeviceCreationParameterInt() { InitWithDefault(); } AudioOutputDeviceCoreAudio::ParameterBuffers::ParameterBuffers(String s) throw (Exception) : DeviceCreationParameterInt(s) { } String AudioOutputDeviceCoreAudio::ParameterBuffers::Description() { return "Number of audio buffer"; } bool AudioOutputDeviceCoreAudio::ParameterBuffers::Fix() { return true; } bool AudioOutputDeviceCoreAudio::ParameterBuffers::Mandatory() { return false; } std::map AudioOutputDeviceCoreAudio::ParameterBuffers::DependsAsParameters() { return std::map(); } optional AudioOutputDeviceCoreAudio::ParameterBuffers::DefaultAsInt(std::map Parameters) { return 3; } optional AudioOutputDeviceCoreAudio::ParameterBuffers::RangeMinAsInt(std::map Parameters) { return 2; } optional AudioOutputDeviceCoreAudio::ParameterBuffers::RangeMaxAsInt(std::map Parameters) { return 16; } std::vector AudioOutputDeviceCoreAudio::ParameterBuffers::PossibilitiesAsInt(std::map Parameters) { return std::vector(); } void AudioOutputDeviceCoreAudio::ParameterBuffers::OnSetValue(int i) throw (Exception) { // not posssible, as parameter is fix } String AudioOutputDeviceCoreAudio::ParameterBuffers::Name() { return "BUFFERS"; } // *************** ParameterBufferSize *************** // * AudioOutputDeviceCoreAudio::ParameterBufferSize::ParameterBufferSize() : DeviceCreationParameterInt() { InitWithDefault(); } AudioOutputDeviceCoreAudio::ParameterBufferSize::ParameterBufferSize(String s) throw (Exception) : DeviceCreationParameterInt(s) { } String AudioOutputDeviceCoreAudio::ParameterBufferSize::Description() { return "Size of the audio buffer in sample frames"; } bool AudioOutputDeviceCoreAudio::ParameterBufferSize::Fix() { return true; } bool AudioOutputDeviceCoreAudio::ParameterBufferSize::Mandatory() { return false; } std::map AudioOutputDeviceCoreAudio::ParameterBufferSize::DependsAsParameters() { return std::map(); } optional AudioOutputDeviceCoreAudio::ParameterBufferSize::DefaultAsInt(std::map Parameters) { return 128; } optional AudioOutputDeviceCoreAudio::ParameterBufferSize::RangeMinAsInt(std::map Parameters) { return 32; } optional AudioOutputDeviceCoreAudio::ParameterBufferSize::RangeMaxAsInt(std::map Parameters) { return 4096; } std::vector AudioOutputDeviceCoreAudio::ParameterBufferSize::PossibilitiesAsInt(std::map Parameters) { return std::vector(); } void AudioOutputDeviceCoreAudio::ParameterBufferSize::OnSetValue(int i) throw (Exception) { // not posssible, as parameter is fix } String AudioOutputDeviceCoreAudio::ParameterBufferSize::Name() { return "BUFFERSIZE"; } } // namespace LinuxSampler