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