--- linuxsampler/trunk/src/drivers/audio/AudioOutputDeviceCoreAudio.cpp 2009/01/30 19:22:36 1829 +++ linuxsampler/trunk/src/drivers/audio/AudioOutputDeviceCoreAudio.cpp 2009/02/05 17:48:54 1832 @@ -26,6 +26,19 @@ 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, @@ -37,6 +50,9 @@ 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 @@ -63,68 +79,33 @@ if(res) std::cerr << "AudioQueueEnqueueBuffer: Error " << res << std::endl; } - AudioOutputDeviceCoreAudio::DeviceInfo* AudioOutputDeviceCoreAudio::pDeviceInfo = NULL; - - AudioOutputDeviceCoreAudio::DeviceInfo* AudioOutputDeviceCoreAudio::GetDeviceInfo() { - if(pDeviceInfo == NULL) { - pDeviceInfo = new DeviceInfo; - - pDeviceInfo->uiSamplerate = 44100; - pDeviceInfo->uiChannelNumber = 2; - - AudioStreamBasicDescription dataFormat; - SetAudioDataFormat(&dataFormat); - AudioQueueRef pQueue = NULL; - - OSStatus res = AudioQueueNewOutput ( - &dataFormat, NULL, NULL, NULL, NULL, 0, &pQueue - ); - - if(res) { - std::cerr << "Failed to retrieve device info: " << res << std::endl; - return pDeviceInfo; - } - - UInt32 chns = pDeviceInfo->uiChannelNumber; - UInt32 size = sizeof(chns); - res = AudioQueueGetProperty ( - pQueue, - kAudioQueueDeviceProperty_NumberChannels, - &chns, &size - ); - - if(res) std::cerr << "Failed to retrieve channel number: " << res << std::endl; - else pDeviceInfo->uiChannelNumber = chns; - - Float64 sRate = pDeviceInfo->uiSamplerate; - size = sizeof(sRate); - res = AudioQueueGetProperty ( - pQueue, - kAudioQueueDeviceProperty_SampleRate, - &sRate, &size - ); - - if(res) std::cerr << "Failed to retrieve samplerate: " << res << std::endl; - else pDeviceInfo->uiSamplerate = (uint)sRate; - - AudioQueueDispose(pQueue, true); - } - - return pDeviceInfo; + void AudioOutputDeviceCoreAudio::DeviceChanged() { + dmsg(1,("Restarting audio queue...")); + atomic_set(&restartQueue, 1); } AudioOutputDeviceCoreAudio::AudioOutputDeviceCoreAudio ( std::map Parameters - ) : AudioOutputDevice(Parameters), Thread(true, true, 1, 0) { + ) : 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(); + 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; @@ -167,6 +148,8 @@ AudioQueueDispose(aqPlayerState.mQueue, true); destroyMutex.Unlock(); delete [] aqPlayerState.mBuffers; + + CurrentDevice.RemoveListener(this); } void AudioOutputDeviceCoreAudio::SetAudioDataFormat(AudioStreamBasicDescription* pDataFormat) { @@ -192,7 +175,7 @@ } String AudioOutputDeviceCoreAudio::Version() { - String s = "$Revision: 1.1 $"; + String s = "$Revision: 1.2 $"; return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword } @@ -225,59 +208,87 @@ return new AudioChannel(ChannelNr, Channel(ChannelNr % uiCoreAudioChannels)); } - /** - * Entry point for the thread. - */ - int AudioOutputDeviceCoreAudio::Main() { - dmsg(2,("CoreAudio thread started\n")); - - bool initialized = aqPlayerState.mQueue != NULL; - if(!initialized) { - /* - * Initializing the audio queue - * Need to be run from this thread because of CFRunLoopGetCurrent() - * which returns the CFRunLoop object for the current thread. - */ - OSStatus res = AudioQueueNewOutput ( - &aqPlayerState.mDataFormat, - HandleOutputBuffer, + void AudioOutputDeviceCoreAudio::FillBuffers() { + for (int i = 0; i < uiBufferNumber; ++i) { + HandleOutputBuffer ( &aqPlayerState, - CFRunLoopGetCurrent(), - kCFRunLoopCommonModes, - 0, - &aqPlayerState.mQueue + aqPlayerState.mQueue, + aqPlayerState.mBuffers[i] ); + } + } - if(res) { - String s = String("AudioQueueNewOutput: Error ") + ToString(res); - throw Exception(s); - } + void AudioOutputDeviceCoreAudio::PrimeAudioQueue() { + OSStatus res = AudioQueuePrime(aqPlayerState.mQueue, 0, NULL); + if(res) { + String s = String("AudioQueuePrime: Error ") + ToString(res); + throw Exception(s); + } + } - for (int i = 0; i < uiBufferNumber; ++i) { - OSStatus res = AudioQueueAllocateBuffer ( - aqPlayerState.mQueue, - aqPlayerState.bufferByteSize, - &aqPlayerState.mBuffers[i] - ); - - if(res) { - String s = String("AudioQueueAllocateBuffer: Error "); - throw Exception(s + ToString(res)); - } - } + 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) { - HandleOutputBuffer ( - &aqPlayerState, + res = AudioQueueAllocateBuffer ( aqPlayerState.mQueue, - aqPlayerState.mBuffers[i] + 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; - OSStatus res = AudioQueueSetParameter ( + res = AudioQueueSetParameter ( aqPlayerState.mQueue, kAudioQueueParam_Volume, gain @@ -286,20 +297,37 @@ if(res) std::cerr << "AudioQueueSetParameter: Error " << res << std::endl; atomic_set(&(aqPlayerState.mIsRunning), 1); - - if(!initialized) { - res = AudioQueuePrime(aqPlayerState.mQueue, 0, NULL); - if(res) { - String s = String("AudioQueuePrime: Error ") + ToString(res); - throw Exception(s); - } - } + 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(2,("CoreAudio thread started\n")); + OSStatus res; + std::cout<<"thread started ca\n"; + 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 { @@ -316,6 +344,17 @@ 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))); @@ -327,6 +366,81 @@ } +// *************** 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 *************** // * @@ -340,10 +454,42 @@ } + 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")); - return GetDeviceInfo()->uiSamplerate; + 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; } @@ -363,7 +509,32 @@ optional AudioOutputDeviceCoreAudio::ParameterChannels::DefaultAsInt(std::map Parameters) { dmsg(2,("AudioOutputDeviceCoreAudio::ParameterChannels::DefaultAsInt()\n")); - return GetDeviceInfo()->uiChannelNumber; + 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 ***************