1 /*
  2  * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 #import "AVFAudioProcessor.h"
 27 #import "AVFMediaPlayer.h"
 28 
 29 #import <AVFoundation/AVFoundation.h>
 30 #import <MediaToolbox/MediaToolbox.h>
 31 
 32 #import <CoreFoundation/CoreFoundation.h>
 33 
 34 #import <pthread.h>
 35 #import <objc/message.h>
 36 
 37 static void InitAudioTap(MTAudioProcessingTapRef tapRef, void *clientInfo, void **tapStorageOut);
 38 static void FinalizeAudioTap(MTAudioProcessingTapRef tapRef);
 39 static void PrepareAudioTap(MTAudioProcessingTapRef tapRef,
 40         CMItemCount maxFrames,
 41         const AudioStreamBasicDescription *processingFormat);
 42 static void UnprepareAudioTap(MTAudioProcessingTapRef tapRef);
 43 static void ProcessAudioTap(MTAudioProcessingTapRef tapRef, CMItemCount numberFrames,
 44         MTAudioProcessingTapFlags flags,
 45         AudioBufferList *bufferListInOut,
 46         CMItemCount *numberFramesOut,
 47         MTAudioProcessingTapFlags *flagsOut);
 48 static OSStatus AVFTapRenderCallback(void *inRefCon,
 49                                      AudioUnitRenderActionFlags *ioActionFlags,
 50                                      const AudioTimeStamp *inTimeStamp,
 51                                      UInt32 inBusNumber,
 52                                      UInt32 inNumberFrames,
 53                                      AudioBufferList *ioData);
 54 
 55 @implementation AVFAudioProcessor
 56 
 57 - (id) init {
 58     if ((self = [super init]) != nil) {
 59         _soundLevelUnit = AVFSoundLevelUnitPtr(new AVFSoundLevelUnit());
 60         _audioSpectrum = AVFAudioSpectrumUnitPtr(new AVFAudioSpectrumUnit());
 61         _audioEqualizer = AVFAudioEqualizerPtr(new AVFAudioEqualizer());
 62 
 63         _volume = 1.0f;
 64         _balance = 0.0f;
 65         _audioDelay = 0LL;
 66     }
 67     return self;
 68 }
 69 
 70 -(void) dealloc {
 71     _soundLevelUnit = nullptr;
 72     _audioSpectrum = nullptr;
 73     _audioEqualizer = nullptr;
 74 }
 75 
 76 -(void) setAudioTrack : (AVAssetTrack *) track {
 77     if (track != _audioTrack) {
 78         // reset the audio mixer if it's already been created
 79         // this theoretically should never happen...
 80         _mixer = nil;
 81     }
 82     _audioTrack = track;
 83 }
 84 
 85 -(AVAudioMix*) mixer {
 86     if (!self.audioTrack) {
 87         return nil;
 88     }
 89 
 90     if (!_mixer) {
 91         AVMutableAudioMix *mixer = [AVMutableAudioMix audioMix];
 92         if (mixer) {
 93             AVMutableAudioMixInputParameters *audioMixInputParameters =
 94                     [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack : self.audioTrack];
 95             if (audioMixInputParameters &&
 96                     [audioMixInputParameters respondsToSelector : @selector(setAudioTapProcessor :)]) {
 97                 MTAudioProcessingTapCallbacks callbacks;
 98 
 99                 callbacks.version = kMTAudioProcessingTapCallbacksVersion_0;
100                 callbacks.clientInfo = (__bridge void *) self;
101                 callbacks.init = InitAudioTap;
102                 callbacks.finalize = FinalizeAudioTap;
103                 callbacks.prepare = PrepareAudioTap;
104                 callbacks.unprepare = UnprepareAudioTap;
105                 callbacks.process = ProcessAudioTap;
106 
107                 MTAudioProcessingTapRef audioProcessingTap;
108                 if (noErr == MTAudioProcessingTapCreate(kCFAllocatorDefault, &callbacks,
109                         kMTAudioProcessingTapCreationFlag_PreEffects,
110                         &audioProcessingTap)) {
111                     [audioMixInputParameters setAudioTapProcessor:audioProcessingTap];
112 
113                     CFRelease(audioProcessingTap); // owned by the mixer now
114                     mixer.inputParameters = @[audioMixInputParameters];
115 
116                     _mixer = mixer;
117                 }
118             }
119         }
120     }
121     return _mixer;
122 }
123 
124 -(void) setVolume : (float) volume {
125     _volume = volume;
126     if (_soundLevelUnit != nullptr) {
127         _soundLevelUnit->setVolume(volume);
128     }
129 }
130 
131 -(void) setBalance : (float) balance {
132     _balance = balance;
133     if (_soundLevelUnit != nullptr) {
134         _soundLevelUnit->setBalance(balance);
135     }
136 }
137 
138 @end
139 
140 AVFTapContext::AVFTapContext(AVFSoundLevelUnitPtr slu, AVFAudioSpectrumUnitPtr spectrum,
141                              AVFAudioEqualizerPtr eq) : audioSLU(slu),
142                                                         audioSpectrum(spectrum),
143                                                         audioEQ(eq),
144                                                         // Some reasonable defaults
145                                                         mSampleRate(48000),
146                                                         mChannels(2) {
147 }
148 
149 AVFTapContext::~AVFTapContext() {
150     // AudioUnits have already been deallocated by now
151     // shared_ptrs get freed automatically
152 }
153 
154 void InitAudioTap(MTAudioProcessingTapRef tapRef, void *clientInfo, void **tapStorageOut) {
155     // retain the AU kernels so they don't get freed while we're running
156     AVFAudioProcessor *processor = (__bridge AVFAudioProcessor *) clientInfo;
157     if (processor) {
158         AVFTapContext *context = new AVFTapContext(processor.soundLevelUnit,
159                 processor.audioSpectrum,
160                 processor.audioEqualizer);
161         *tapStorageOut = context;
162     }
163 }
164 
165 void FinalizeAudioTap(MTAudioProcessingTapRef tapRef) {
166     AVFTapContext *context = (AVFTapContext*) MTAudioProcessingTapGetStorage(tapRef);
167     if (context) {
168         delete context;
169     }
170 }
171 
172 static OSStatus SetupAudioUnit(AudioUnit unit,
173                                const AudioStreamBasicDescription *processingFormat,
174                                UInt32 maxFrames) {
175     OSStatus status = noErr;
176     if (noErr == status) {
177         status = AudioUnitSetProperty(unit,
178                                       kAudioUnitProperty_StreamFormat,
179                                       kAudioUnitScope_Input, 0,
180                                       processingFormat, sizeof(AudioStreamBasicDescription));
181     }
182     if (noErr == status) {
183         status = AudioUnitSetProperty(unit,
184                                       kAudioUnitProperty_StreamFormat,
185                                       kAudioUnitScope_Output, 0,
186                                       processingFormat, sizeof(AudioStreamBasicDescription));
187     }
188     if (noErr == status) {
189         status = AudioUnitSetProperty(unit,
190                                       kAudioUnitProperty_MaximumFramesPerSlice,
191                                       kAudioUnitScope_Global, 0,
192                                       &maxFrames, sizeof(UInt32));
193     }
194     if (noErr == status) {
195         status = AudioUnitInitialize(unit);
196     }
197     return status;
198 }
199 
200 void PrepareAudioTap(MTAudioProcessingTapRef tapRef,
201         CMItemCount maxFrames,
202         const AudioStreamBasicDescription *processingFormat) {
203     AVFTapContext *context = (AVFTapContext*) MTAudioProcessingTapGetStorage(tapRef);
204 
205     // Validate the audio format before we enable the processor
206     // Failures here should rarely, if ever, happen so leave the NSLogs in for
207     // easier diagnosis in the field
208     if (processingFormat->mFormatID != kAudioFormatLinearPCM) {
209         NSLog(@"AVFAudioProcessor needs linear PCM");
210         return;
211     }
212 
213     // Use the convenient kAudioFormatFlagsNativeFloatPacked to check if we can
214     // process the incoming audio
215     if ((processingFormat->mFormatFlags & kAudioFormatFlagsNativeFloatPacked)
216             != kAudioFormatFlagsNativeFloatPacked) {
217         NSLog(@"AVFAudioProcessor needs native endian packed float samples!!");
218         return;
219     }
220 
221     context->mSampleRate = processingFormat->mSampleRate;
222     context->mChannels = processingFormat->mChannelsPerFrame;
223     context->mMaxFrames = maxFrames;
224 
225     // Configure audio equalizer
226     if (context->audioEQ != nullptr) {
227         context->audioEQ.get()->SetSampleRate(context->mSampleRate);
228         context->audioEQ.get()->SetChannels(context->mChannels);
229         context->audioEQ.get()->ResetBandParameters();
230     }
231 
232     // Configure spectrum
233     if (context->audioSpectrum != nullptr) {
234         context->audioSpectrum.get()->SetSampleRate(context->mSampleRate);
235         context->audioSpectrum.get()->SetChannels(context->mChannels);
236         context->audioSpectrum.get()->SetMaxFrames(context->mMaxFrames);
237     }
238 
239     if (context->audioSLU != nullptr) {
240         context->audioSLU.get()->SetChannels(context->mChannels);
241     }
242 }
243 
244 void UnprepareAudioTap(MTAudioProcessingTapRef tapRef) {
245     // We do not need it anymore
246 }
247 
248 void ProcessAudioTap(MTAudioProcessingTapRef tapRef,
249         CMItemCount numberFrames,
250         uint32_t flags,
251         AudioBufferList *bufferListInOut,
252         CMItemCount *numberFramesOut,
253         uint32_t *flagsOut) {
254     AVFTapContext *context = (AVFTapContext*) MTAudioProcessingTapGetStorage(tapRef);
255     OSStatus status = MTAudioProcessingTapGetSourceAudio(tapRef, numberFrames, bufferListInOut,
256             flagsOut, NULL, numberFramesOut);
257     if (status != noErr) {
258         NSLog(@"MTAudioProcessingTapGetSourceAudio failed: %d", status);
259         return;
260     }
261 
262     if (context->audioEQ != nullptr) {
263         if (!context->audioEQ.get()->ProcessBufferLists(*bufferListInOut, numberFrames)) {
264             NSLog(@"audioEQ ProcessBufferLists() failed");
265             return;
266         }
267     }
268 
269     if (context->audioSpectrum != nullptr) {
270         if (!context->audioSpectrum.get()->ProcessBufferLists(*bufferListInOut, numberFrames)) {
271             NSLog(@"audioSpectrum ProcessBufferLists() failed");
272             return;
273         }
274     }
275 
276     if (context->audioSLU != nullptr) {
277         if (!context->audioSLU.get()->ProcessBufferLists(*bufferListInOut, numberFrames)) {
278             NSLog(@"audioSLU ProcessBufferLists() failed");
279             return;
280         }
281     }
282 
283     if (context->audioSpectrum != nullptr) {
284         context->audioSpectrum.get()->SetFirstBufferDelivered(true);
285     }
286 }