this repo has no description
at master 365 lines 10 kB view raw
1/* 2This file is part of Darling. 3 4Copyright (C) 2020 Lubos Dolezel 5 6Darling is free software: you can redistribute it and/or modify 7it under the terms of the GNU General Public License as published by 8the Free Software Foundation, either version 3 of the License, or 9(at your option) any later version. 10 11Darling is distributed in the hope that it will be useful, 12but WITHOUT ANY WARRANTY; without even the implied warranty of 13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14GNU General Public License for more details. 15 16You should have received a copy of the GNU General Public License 17along with Darling. If not, see <http://www.gnu.org/licenses/>. 18*/ 19 20#include "AUHAL.h" 21#include <iostream> 22 23#pragma GCC visibility push(default) 24AUDIOCOMPONENT_ENTRY(AUOutputBaseFactory, AUHAL); 25#pragma GCC visibility pop 26 27enum { 28 kOutputBus = 0, 29 kInputBus 30}; 31 32AUHAL::AUHAL(AudioComponentInstance inInstance, bool supportRecording) 33: AUBase(inInstance, 1, supportRecording ? 1 : 0) 34{ 35 UInt32 propSize = sizeof(AudioDeviceID); 36 AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propSize, &m_outputDevice); 37 AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &propSize, &m_inputDevice); 38} 39 40AUHAL::~AUHAL() 41{ 42 Stop(); 43} 44 45bool AUHAL::CanScheduleParameters() const 46{ 47 return true; 48} 49 50bool AUHAL::StreamFormatWritable(AudioUnitScope scope, AudioUnitElement element) 51{ 52 return !m_running && IsInitialized(); 53} 54 55OSStatus AUHAL::Version() 56{ 57 return 1; 58} 59 60// Provide data from the microphone 61OSStatus AUHAL::Render(AudioUnitRenderActionFlags& ioActionFlags, const AudioTimeStamp& inTimeStamp, UInt32 inNumberFrames) 62{ 63 std::unique_lock<std::mutex> lk(m_dataAvailableMutex); 64 65 m_dataAvailableCV.wait(lk, [=]{ return m_dataAvailable; }); 66 67 AudioBufferList& abl = GetOutput(kInputBus)->GetBufferList(); 68 69 // TODO: Prepare for non-interleaved audio 70 UInt32 howMuch = std::min<UInt32>(abl.mBuffers[0].mDataByteSize, m_bufferUsed); 71 memcpy(abl.mBuffers[0].mData, m_buffer.get(), howMuch); 72 abl.mBuffers[0].mDataByteSize = howMuch; 73 74 return noErr; 75} 76 77OSStatus AUHAL::Start() 78{ 79 if (m_running) 80 return noErr; 81 82 if (m_enableOutput) 83 { 84 // std::cout << "Output is enabled, starting playback\n"; 85 AudioDeviceCreateIOProcID(m_outputDevice, playbackCallback, this, &m_outputProcID); 86 87 // m_auhalData.open("/tmp/auhal.raw", std::ios_base::binary | std::ios_base::out); 88 89 const CAStreamBasicDescription& desc = GetStreamFormat(kAudioUnitScope_Input, kOutputBus); 90 AudioDeviceSetProperty(m_outputDevice, nullptr, 0, false, kAudioDevicePropertyStreamFormat, sizeof(AudioStreamBasicDescription), &desc); 91 AudioDeviceStart(m_outputDevice, m_outputProcID); 92 } 93 if (m_enableInput) 94 { 95 AudioDeviceCreateIOProcID(m_inputDevice, recordCallback, this, &m_inputProcID); 96 const CAStreamBasicDescription& desc = GetStreamFormat(kAudioUnitScope_Output, kInputBus); 97 AudioDeviceSetProperty(m_inputDevice, nullptr, 0, true, kAudioDevicePropertyStreamFormat, sizeof(AudioStreamBasicDescription), &desc); 98 AudioDeviceStart(m_inputDevice, m_inputProcID); 99 } 100 101 m_running = m_enableOutput || m_enableInput; 102 return noErr; 103} 104 105OSStatus AUHAL::Stop() 106{ 107 if (!m_running) 108 return noErr; 109 110 if (m_outputProcID) 111 { 112 AudioDeviceStop(m_outputDevice, m_outputProcID); 113 AudioDeviceDestroyIOProcID(m_outputDevice, m_outputProcID); 114 } 115 if (m_inputProcID) 116 { 117 AudioDeviceStop(m_inputDevice, m_inputProcID); 118 AudioDeviceDestroyIOProcID(m_inputDevice, m_inputProcID); 119 } 120 121 return noErr; 122} 123 124OSStatus AUHAL::SetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize) 125{ 126 switch (inID) 127 { 128 case kAudioOutputUnitProperty_SetInputCallback: 129 { 130 ca_require(inDataSize == sizeof(AURenderCallbackStruct), InvalidPropertyValue); 131 const AURenderCallbackStruct* cb = static_cast<const AURenderCallbackStruct*>(inData); 132 133 m_outputAvailableCb = *cb; 134 135 PropertyChanged(inID, inScope, inElement); 136 return noErr; 137 } 138 case kAudioOutputUnitProperty_EnableIO: 139 { 140 ca_require(inDataSize == sizeof(UInt32), InvalidPropertyValue); 141 142 const bool enable = *((const UInt32*) inData); 143 144 if (inElement == kOutputBus) 145 { 146 m_enableOutput = enable; 147 PropertyChanged(inID, inScope, inElement); 148 } 149 else if (inElement == kInputBus) 150 { 151 m_enableInput = enable; 152 PropertyChanged(inID, inScope, inElement); 153 } 154 else 155 return kAudioUnitErr_InvalidElement; 156 157 return noErr; 158 } 159 case kAudioOutputUnitProperty_CurrentDevice: 160 { 161 ca_require(inDataSize == sizeof(AudioDeviceID), InvalidPropertyValue); 162 const AudioDeviceID* dev = static_cast<const AudioDeviceID*>(inData); 163 164 if (inElement == kOutputBus) 165 { 166 m_outputDevice = *dev; 167 PropertyChanged(inID, inScope, inElement); 168 } 169 else if (inElement == kInputBus) 170 { 171 m_inputDevice = *dev; 172 PropertyChanged(inID, inScope, inElement); 173 } 174 else 175 return kAudioUnitErr_InvalidElement; 176 return noErr; 177 } 178 } 179 return AUBase::SetProperty(inID, inScope, inElement, inData, inDataSize); 180InvalidPropertyValue: 181 return kAudioUnitErr_InvalidPropertyValue; 182} 183 184OSStatus AUHAL::GetPropertyInfo(AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32& outDataSize, Boolean& outWritable) 185{ 186 switch (inID) 187 { 188 case kAudioOutputUnitProperty_SetInputCallback: 189 { 190 outDataSize = sizeof(AURenderCallbackStruct); 191 outWritable = true; 192 break; 193 } 194 case kAudioOutputUnitProperty_EnableIO: 195 { 196 outDataSize = sizeof(UInt32); 197 outWritable = true; 198 return noErr; 199 } 200 case kAudioOutputUnitProperty_HasIO: 201 { 202 outDataSize = sizeof(UInt32); 203 outWritable = false; 204 return noErr; 205 } 206 case kAudioOutputUnitProperty_CurrentDevice: 207 { 208 outDataSize = sizeof(AudioDeviceID); 209 outWritable = true; 210 return noErr; 211 } 212 } 213 return AUBase::GetPropertyInfo(inID, inScope, inElement, outDataSize, outWritable); 214} 215 216OSStatus AUHAL::GetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData) 217{ 218 switch (inID) 219 { 220 case kAudioOutputUnitProperty_SetInputCallback: 221 { 222 memcpy(outData, &m_outputAvailableCb, sizeof(m_outputAvailableCb)); 223 return noErr; 224 } 225 case kAudioOutputUnitProperty_EnableIO: 226 case kAudioOutputUnitProperty_HasIO: 227 { 228 if (inElement == kOutputBus) 229 { 230 UInt32 value = m_enableOutput; 231 memcpy(&outData, &value, sizeof(value)); 232 } 233 else if (inElement == kInputBus) 234 { 235 UInt32 value = m_enableInput; 236 memcpy(&outData, &value, sizeof(value)); 237 } 238 else 239 return kAudioUnitErr_InvalidElement; 240 return noErr; 241 } 242 case kAudioOutputUnitProperty_CurrentDevice: 243 { 244 if (inElement == kOutputBus) 245 { 246 memcpy(outData, &m_outputDevice, sizeof(m_outputDevice)); 247 } 248 else if (inElement == kInputBus) 249 { 250 memcpy(outData, &m_inputDevice, sizeof(m_inputDevice)); 251 } 252 else 253 return kAudioUnitErr_InvalidElement; 254 return noErr; 255 } 256 } 257 return AUBase::GetProperty(inID, inScope, inElement, outData); 258} 259 260OSStatus AUHAL::playbackCallback(AudioObjectID inObjectID, 261 const AudioTimeStamp* inNow, const AudioBufferList* inInputData, 262 const AudioTimeStamp* inInputTime, 263 AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, 264 void* inClientData) 265{ 266 AUHAL* This = static_cast<AUHAL*>(inClientData); 267 return This->doPlayback(inNow, outOutputData, inOutputTime); 268} 269 270OSStatus AUHAL::recordCallback(AudioObjectID inObjectID, 271 const AudioTimeStamp* inNow, const AudioBufferList* inInputData, 272 const AudioTimeStamp* inInputTime, 273 AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, 274 void* inClientData) 275{ 276 AUHAL* This = static_cast<AUHAL*>(inClientData); 277 return This->doRecord(inNow, inInputData, inInputTime); 278} 279 280OSStatus AUHAL::doPlayback(const AudioTimeStamp* inNow, AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime) 281{ 282 // std::cout << "AUHAL::DoPlayback()\n"; 283 if (!HasInput(0)) 284 { 285 // std::cerr << "No connection\n"; 286 return kAudioUnitErr_NoConnection; 287 } 288 289 OSStatus result = noErr; 290 AudioUnitRenderActionFlags flags = kAudioUnitRenderAction_PreRender; 291 const CAStreamBasicDescription& desc = GetStreamFormat(kAudioUnitScope_Input, kOutputBus); 292 293 UInt32 nFrames = outOutputData->mBuffers[0].mDataByteSize / (desc.mBytesPerFrame / outOutputData->mBuffers[0].mNumberChannels); 294 result = GetInput(kOutputBus)->PullInputWithBufferList(flags, *inNow, kOutputBus, nFrames, outOutputData); 295 296 // std::cout << "Pull result: " << result << std::endl; 297 // std::cout << "Bytes: " << outOutputData->mBuffers[0].mDataByteSize << std::endl; 298 // m_auhalData.write((char*) outOutputData->mBuffers[0].mData, outOutputData->mBuffers[0].mDataByteSize); 299 // m_auhalData.flush(); 300 301 return result; 302} 303 304OSStatus AUHAL::doRecord(const AudioTimeStamp* inNow, const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime) 305{ 306 // TODO: Prepare for non-interleaved audio 307 std::unique_lock<std::mutex> lk(m_dataAvailableMutex); 308 309 if (m_bufferSize < inInputData->mBuffers[0].mDataByteSize) 310 { 311 m_buffer.reset(new uint8_t[inInputData->mBuffers[0].mDataByteSize]); 312 m_bufferSize = inInputData->mBuffers[0].mDataByteSize; 313 } 314 315 m_bufferUsed = inInputData->mBuffers[0].mDataByteSize; 316 memcpy(m_buffer.get(), inInputData->mBuffers[0].mData, m_bufferUsed); 317 318 m_dataAvailable = true; 319 320 lk.unlock(); 321 m_dataAvailableCV.notify_one(); 322 323 if (m_outputAvailableCb.inputProc) 324 { 325 AudioUnitRenderActionFlags flags = kAudioUnitRenderAction_PostRender; 326 const CAStreamBasicDescription& desc = GetStreamFormat(kAudioUnitScope_Output, kInputBus); 327 UInt32 numFrames = m_bufferUsed / desc.mBytesPerFrame; 328 329 m_outputAvailableCb.inputProc(m_outputAvailableCb.inputProcRefCon, &flags, inInputTime, kInputBus, numFrames, nullptr); 330 } 331 332 return noErr; 333} 334 335// AUDispatch.cpp doesn't implement dispatch code for AudioOutputUnits 336OSStatus AUHAL::ComponentEntryDispatch(ComponentParameters *params, AUHAL *This) 337{ 338 if (This == NULL) return kAudio_ParamError; 339 340 OSStatus result = noErr; 341 342 switch (params->what) 343 { 344 case kComponentCanDoSelect: 345 switch (GetSelectorForCanDo(params)) 346 { 347 case kAudioOutputUnitStartSelect: 348 case kAudioOutputUnitStopSelect: 349 return 1; 350 } 351 break; 352 case kAudioOutputUnitStartSelect: 353 { 354 CAMutex::Locker lock(This->GetMutex()); 355 return This->Start(); 356 } 357 case kAudioOutputUnitStopSelect: 358 { 359 CAMutex::Locker lock(This->GetMutex()); 360 return This->Stop(); 361 } 362 } 363 364 return AUBase::ComponentEntryDispatch(params, This); 365}