iPlug2 - C++ Audio Plug-in Framework
Loading...
Searching...
No Matches
IPlugAUPlayer.mm
1 /*
2 ==============================================================================
3
4 This file is part of the iPlug 2 library. Copyright (C) the iPlug 2 developers.
5
6 See LICENSE.txt for more info.
7
8 ==============================================================================
9*/
10
11#import "IPlugAUPlayer.h"
12#include "IPlugConstants.h"
13#include "config.h"
14
15#if !__has_feature(objc_arc)
16#error This file must be compiled with Arc. Use -fobjc-arc flag
17#endif
18
19bool isInstrument()
20{
21#if PLUG_TYPE == 1
22 return YES;
23#else
24 return NO;
25#endif
26}
27
28@implementation IPlugAUPlayer
29{
30 AVAudioEngine* engine;
31 AVAudioUnit* avAudioUnit;
32 UInt32 componentType;
33}
34
35- (instancetype) initWithComponentType: (UInt32) unitComponentType
36{
37 self = [super init];
38
39 if (self)
40 {
41 engine = [[AVAudioEngine alloc] init];
42 componentType = unitComponentType;
43 }
44
45 return self;
46}
47
48- (void) loadAudioUnitWithComponentDescription:(AudioComponentDescription)desc
49 completion:(void (^) (void))completionBlock
50{
51 [AVAudioUnit instantiateWithComponentDescription:desc options:0
52 completionHandler:^(AVAudioUnit* __nullable audioUnit, NSError* __nullable error) {
53 [self onAudioUnitInstantiated:audioUnit error:error completion:completionBlock];
54 }];
55}
56
57- (void) onAudioUnitInstantiated:(AVAudioUnit* __nullable) audioUnit error:(NSError* __nullable) error completion:(void (^) (void))completionBlock
58{
59 if (audioUnit == nil)
60 return;
61
62 avAudioUnit = audioUnit;
63
64 [engine attachNode:avAudioUnit];
65
66 self.currentAudioUnit = avAudioUnit.AUAudioUnit;
67
68 [self setupSession];
69
70#ifdef _DEBUG
71 [self printEngineInfo];
72 [self printSessionInfo];
73#endif
74
75 [self makeEngineConnections];
76 [self addNotifications];
77
78 AVAudioSession* session = [AVAudioSession sharedInstance];
79
80 if (![session setActive:TRUE error: &error])
81 {
82 NSLog(@"Error setting session active: %@", [error localizedDescription]);
83 }
84
85 if (![engine startAndReturnError: &error])
86 {
87 NSLog(@"engine failed to start: %@", error);
88 }
89
90 completionBlock();
91}
92
93- (void) dealloc
94{
95 [[NSNotificationCenter defaultCenter] removeObserver: self];
96}
97
98- (void) restartAudioEngine
99{
100 [engine stop];
101
102 NSError *error = nil;
103
104 if (![engine startAndReturnError:&error])
105 {
106 NSLog(@"Error re-starting audio engine: %@", error);
107 }
108 else
109 {
110 [self printSessionInfo];
111 }
112}
113
114- (void) setupSession
115{
116 AVAudioSession* session = [AVAudioSession sharedInstance];
117 NSError* error = nil;
118
119 AVAudioSessionCategoryOptions options = AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionAllowBluetooth;
120 [session setCategory: isInstrument() ? AVAudioSessionCategoryPlayback
121 : AVAudioSessionCategoryPlayAndRecord
122 withOptions:options error: &error];
123
124 if (error)
125 {
126 NSLog(@"Error setting category: %@", error);
127 }
128
129 [session setPreferredSampleRate:iplug::DEFAULT_SAMPLE_RATE error: &error];
130
131 if (error)
132 {
133 NSLog(@"Error setting samplerate: %@", error);
134 }
135
136 [session setPreferredIOBufferDuration:128.0/iplug::DEFAULT_SAMPLE_RATE error: &error];
137
138 if (error)
139 {
140 NSLog(@"Error setting io buffer duration: %@", error);
141 }
142}
143
144- (void) makeEngineConnections
145{
146 if (!isInstrument())
147 {
148 AVAudioNode* inputNode = [engine inputNode];
149 AVAudioFormat* inputNodeFormat = [inputNode inputFormatForBus:0];
150
151 @autoreleasepool {
152 @try {
153 [engine connect:inputNode to:avAudioUnit format: inputNodeFormat];
154 }
155 @catch (NSException *exception) {
156 NSLog(@"NSException when trying to connect input node: %@, Reason: %@", exception.name, exception.reason);
157 }
158 }
159 }
160
161 auto numOutputBuses = [avAudioUnit numberOfOutputs];
162 AVAudioMixerNode* mainMixer = [engine mainMixerNode];
163 AVAudioFormat* pluginOutputFormat = [avAudioUnit outputFormatForBus:0];
164 AVAudioNode* outputNode = [engine outputNode];
165
166 if (numOutputBuses > 1)
167 {
168 // Assume all output buses are the same format
169 for (int busIdx=0; busIdx<numOutputBuses; busIdx++)
170 {
171 [engine connect:avAudioUnit to:mainMixer fromBus: busIdx toBus:[mainMixer nextAvailableInputBus] format: pluginOutputFormat];
172 }
173 }
174 else
175 {
176 [engine connect:avAudioUnit to:outputNode format: pluginOutputFormat];
177 }
178}
179
180- (void) printEngineInfo
181{
182 if (!isInstrument())
183 {
184 AVAudioFormat* inputNodeFormat = [[engine inputNode] inputFormatForBus:0];
185 AVAudioFormat* pluginInputFormat = [avAudioUnit inputFormatForBus:0];
186 NSLog(@"Input Node SR: %i", int(inputNodeFormat.sampleRate));
187 NSLog(@"Input Node Chans: %i", inputNodeFormat.channelCount);
188 NSLog(@"Plugin Input SR: %i", int(pluginInputFormat.sampleRate));
189 NSLog(@"Plugin Input Chans: %i", pluginInputFormat.channelCount);
190 }
191
192 AVAudioFormat* pluginOutputFormat = [avAudioUnit outputFormatForBus:0];
193 AVAudioFormat* outputNodeFormat = [[engine outputNode] outputFormatForBus:0];
194
195 NSLog(@"Plugin Output SR: %i", int(pluginOutputFormat.sampleRate));
196 NSLog(@"Plugin Output Chans: %i", pluginOutputFormat.channelCount);
197 NSLog(@"Output Node SR: %i", int(outputNodeFormat.sampleRate));
198 NSLog(@"Output Node Chans: %i", outputNodeFormat.channelCount);
199}
200
201- (void) printSessionInfo
202{
203 AVAudioSession* session = [AVAudioSession sharedInstance];
204 NSLog(@"Session SR: %i", int(session.sampleRate));
205 NSLog(@"Session IO Buffer: %i", int((session.IOBufferDuration * session.sampleRate)+0.5));
206 if (!isInstrument()) NSLog(@"Session Input Chans: %i", int(session.inputNumberOfChannels));
207 NSLog(@"Session Output Chans: %i", int(session.outputNumberOfChannels));
208 if (!isInstrument()) NSLog(@"Session Input Latency: %f ms", session.inputLatency * 1000.0f);
209 NSLog(@"Session Output Latency: %f ms", session.outputLatency * 1000.0f);
210 AVAudioSessionRouteDescription *currentRoute = [session currentRoute];
211 for (AVAudioSessionPortDescription* input in currentRoute.inputs)
212 {
213 NSLog(@"Input Port Name: %@", input.portName);
214 }
215
216 for (AVAudioSessionPortDescription* output in currentRoute.outputs)
217 {
218 NSLog(@"Output Port Name: %@", output.portName);
219 }
220}
221
222- (void) addNotifications
223{
224 NSNotificationCenter* notifCtr = [NSNotificationCenter defaultCenter];
225
226 [notifCtr addObserver: self selector: @selector (onEngineConfigurationChange:) name:AVAudioEngineConfigurationChangeNotification object: engine];
227}
228
229#pragma mark Notifications
230- (void) onEngineConfigurationChange: (NSNotification*) notification
231{
232 [self restartAudioEngine];
233}
234
235@end
IPlug Constant definitions, Types, magic numbers.