1 /* Copyright 2016, Ableton AG, Berlin. All rights reserved.
2  *
3  *  This program is free software: you can redistribute it and/or modify
4  *  it under the terms of the GNU General Public License as published by
5  *  the Free Software Foundation, either version 2 of the License, or
6  *  (at your option) any later version.
7  *
8  *  This program is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *  GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License
14  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
15  *
16  *  If you would like to incorporate Link into a proprietary software application,
17  *  please contact <link-devs@ableton.com>.
18  */
19 
20 #include "AudioPlatform_CoreAudio.hpp"
21 #include <chrono>
22 #include <iostream>
23 #include <mach/mach_time.h>
24 
25 namespace ableton
26 {
27 namespace linkaudio
28 {
29 
AudioPlatform(Link & link)30 AudioPlatform::AudioPlatform(Link& link)
31   : mEngine(link)
32 {
33   initialize();
34   start();
35 }
36 
~AudioPlatform()37 AudioPlatform::~AudioPlatform()
38 {
39   stop();
40   uninitialize();
41 }
42 
audioCallback(void * inRefCon,AudioUnitRenderActionFlags *,const AudioTimeStamp * inTimeStamp,UInt32,UInt32 inNumberFrames,AudioBufferList * ioData)43 OSStatus AudioPlatform::audioCallback(void* inRefCon,
44   AudioUnitRenderActionFlags*,
45   const AudioTimeStamp* inTimeStamp,
46   UInt32,
47   UInt32 inNumberFrames,
48   AudioBufferList* ioData)
49 {
50   AudioEngine* engine = static_cast<AudioEngine*>(inRefCon);
51 
52   const auto bufferBeginAtOutput =
53     engine->mLink.clock().ticksToMicros(inTimeStamp->mHostTime) + engine->mOutputLatency;
54 
55   engine->audioCallback(bufferBeginAtOutput, inNumberFrames);
56 
57   for (std::size_t i = 0; i < inNumberFrames; ++i)
58   {
59     for (UInt32 j = 0; j < ioData->mNumberBuffers; ++j)
60     {
61       SInt16* bufData = static_cast<SInt16*>(ioData->mBuffers[j].mData);
62       bufData[i] = static_cast<SInt16>(32761. * engine->mBuffer[i]);
63     }
64   }
65 
66   return noErr;
67 }
68 
initialize()69 void AudioPlatform::initialize()
70 {
71   AudioComponentDescription cd = {};
72   cd.componentManufacturer = kAudioUnitManufacturer_Apple;
73   cd.componentFlags = 0;
74   cd.componentFlagsMask = 0;
75   cd.componentType = kAudioUnitType_Output;
76   cd.componentSubType = kAudioUnitSubType_DefaultOutput;
77 
78   AudioComponent component = AudioComponentFindNext(nullptr, &cd);
79   OSStatus result = AudioComponentInstanceNew(component, &mIoUnit);
80   if (result)
81   {
82     std::cerr << "Could not get Audio Unit. " << result << std::endl;
83     std::terminate();
84   }
85 
86   UInt32 size = sizeof(mEngine.mSampleRate);
87   result = AudioUnitGetProperty(mIoUnit, kAudioUnitProperty_SampleRate,
88     kAudioUnitScope_Output, 0, &mEngine.mSampleRate, &size);
89   if (result)
90   {
91     std::cerr << "Could not get sample rate. " << result << std::endl;
92     std::terminate();
93   }
94   std::clog << "SAMPLE RATE: " << mEngine.mSampleRate << std::endl;
95 
96   AudioStreamBasicDescription asbd = {};
97   asbd.mFormatID = kAudioFormatLinearPCM;
98   asbd.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked
99                       | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsNonInterleaved;
100   asbd.mChannelsPerFrame = 2;
101   asbd.mBytesPerPacket = sizeof(SInt16);
102   asbd.mFramesPerPacket = 1;
103   asbd.mBytesPerFrame = sizeof(SInt16);
104   asbd.mBitsPerChannel = 8 * sizeof(SInt16);
105   asbd.mSampleRate = mEngine.mSampleRate;
106 
107   result = AudioUnitSetProperty(mIoUnit, kAudioUnitProperty_StreamFormat,
108     kAudioUnitScope_Input, 0, &asbd, sizeof(asbd));
109   if (result)
110   {
111     std::cerr << "Could not set stream format. " << result << std::endl;
112   }
113 
114   char deviceName[512];
115   size = sizeof(deviceName);
116   result = AudioUnitGetProperty(mIoUnit, kAudioDevicePropertyDeviceName,
117     kAudioUnitScope_Global, 0, &deviceName, &size);
118   if (result)
119   {
120     std::cerr << "Could not get device name. " << result << std::endl;
121     std::terminate();
122   }
123   std::clog << "DEVICE NAME: " << deviceName << std::endl;
124 
125   UInt32 bufferSize = 512;
126   size = sizeof(bufferSize);
127   result = AudioUnitSetProperty(mIoUnit, kAudioDevicePropertyBufferFrameSize,
128     kAudioUnitScope_Global, 0, &bufferSize, size);
129   if (result)
130   {
131     std::cerr << "Could not set buffer size. " << result << std::endl;
132     std::terminate();
133   }
134   mEngine.setBufferSize(bufferSize);
135 
136   UInt32 propertyResult = 0;
137   size = sizeof(propertyResult);
138   result = AudioUnitGetProperty(mIoUnit, kAudioDevicePropertyBufferFrameSize,
139     kAudioUnitScope_Global, 0, &propertyResult, &size);
140   if (result)
141   {
142     std::cerr << "Could not get buffer size. " << result << std::endl;
143     std::terminate();
144   }
145   std::clog << "BUFFER SIZE: " << propertyResult << " samples, "
146             << propertyResult / mEngine.mSampleRate * 1e3 << " ms." << std::endl;
147 
148   // the buffer, stream and safety-offset latencies are part of inTimeStamp->mHostTime
149   // within the audio callback.
150   UInt32 deviceLatency = 0;
151   size = sizeof(deviceLatency);
152   result = AudioUnitGetProperty(mIoUnit, kAudioDevicePropertyLatency,
153     kAudioUnitScope_Output, 0, &deviceLatency, &size);
154   if (result)
155   {
156     std::cerr << "Could not get output device latency. " << result << std::endl;
157     std::terminate();
158   }
159   std::clog << "OUTPUT DEVICE LATENCY: " << deviceLatency << " samples, "
160             << deviceLatency / mEngine.mSampleRate * 1e3 << " ms." << std::endl;
161 
162   using namespace std::chrono;
163   const double latency = static_cast<double>(deviceLatency) / mEngine.mSampleRate;
164   mEngine.mOutputLatency = duration_cast<microseconds>(duration<double>{latency});
165 
166   AURenderCallbackStruct ioRemoteInput;
167   ioRemoteInput.inputProc = audioCallback;
168   ioRemoteInput.inputProcRefCon = &mEngine;
169 
170   result = AudioUnitSetProperty(mIoUnit, kAudioUnitProperty_SetRenderCallback,
171     kAudioUnitScope_Input, 0, &ioRemoteInput, sizeof(ioRemoteInput));
172   if (result)
173   {
174     std::cerr << "Could not set render callback. " << result << std::endl;
175   }
176 
177   result = AudioUnitInitialize(mIoUnit);
178   if (result)
179   {
180     std::cerr << "Could not initialize audio unit. " << result << std::endl;
181   }
182 }
183 
uninitialize()184 void AudioPlatform::uninitialize()
185 {
186   OSStatus result = AudioUnitUninitialize(mIoUnit);
187   if (result)
188   {
189     std::cerr << "Could not uninitialize Audio Unit. " << result << std::endl;
190   }
191 }
192 
start()193 void AudioPlatform::start()
194 {
195   OSStatus result = AudioOutputUnitStart(mIoUnit);
196   if (result)
197   {
198     std::cerr << "Could not start Audio Unit. " << result << std::endl;
199     std::terminate();
200   }
201 }
202 
stop()203 void AudioPlatform::stop()
204 {
205   OSStatus result = AudioOutputUnitStop(mIoUnit);
206   if (result)
207   {
208     std::cerr << "Could not stop Audio Unit. " << result << std::endl;
209   }
210 }
211 
212 } // namespace linkaudio
213 } // namespace ableton
214