From 8ec959b22013c260e420f641b73980e5dcf2786e Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Fri, 28 Aug 2020 09:08:04 -0500 Subject: [PATCH 01/33] This branch is an example for taking the FFT of input audio samples and plotting them with the time samples. --- AudioLabSwift/AudioModel.swift | 122 +++-------------------------- AudioLabSwift/ViewController.swift | 9 +-- 2 files changed, 15 insertions(+), 116 deletions(-) diff --git a/AudioLabSwift/AudioModel.swift b/AudioLabSwift/AudioModel.swift index b176d20..acec869 100644 --- a/AudioLabSwift/AudioModel.swift +++ b/AudioLabSwift/AudioModel.swift @@ -13,6 +13,8 @@ class AudioModel { // MARK: Properties private var BUFFER_SIZE:Int + // thse properties are for interfaceing with the API + // the user can access these arrays at any time and plot them if they like var timeData:[Float] var fftData:[Float] @@ -26,54 +28,23 @@ class AudioModel { // public function for starting processing of microphone data func startMicrophoneProcessing(withFps:Double){ + // setup the microphone to copy to circualr buffer self.audioManager?.inputBlock = self.handleMicrophone // repeat this fps times per second using the timer class + // every time this is called, we update the arrays "timeData" and "fftData" Timer.scheduledTimer(timeInterval: 1.0/withFps, target: self, selector: #selector(self.runEveryInterval), userInfo: nil, repeats: true) } - // public function for playing from a file reader file - func startProcesingAudioFileForPlayback(){ - self.audioManager?.outputBlock = self.handleSpeakerQueryWithAudioFile - self.fileReader?.play() - } - - func startProcessingSinewaveForPlayback(withFreq:Float=330.0){ - sineFrequency = withFreq - // Two examples are given that use either objective c or that use swift - // the swift code for loop is slightly slower thatn doing this in c, - // but the implementations are very similar - //self.audioManager?.outputBlock = self.handleSpeakerQueryWithSinusoid // swift for loop - self.audioManager?.setOutputBlockToPlaySineWave(sineFrequency) // c for loop - } // You must call this when you want the audio to start being handled by our model func play(){ self.audioManager?.play() } - // Here is an example function for getting the maximum frequency - func getMaxFrequencyMagnitude() -> (Float,Float){ - // this is the slow way of getting the maximum... - // you might look into the Accelerate framework to make things more efficient - var max:Float = -1000.0 - var maxi:Int = 0 - - if inputBuffer != nil { - for i in 0..max){ - max = fftData[i] - maxi = i - } - } - } - let frequency = Float(maxi) / Float(BUFFER_SIZE) * Float(self.audioManager!.samplingRate) - return (max,frequency) - } - // for sliding max windows, you might be interested in the following: vDSP_vswmax //========================================== // MARK: Private Properties @@ -85,10 +56,6 @@ class AudioModel { return FFTHelper.init(fftSize: Int32(BUFFER_SIZE)) }() - private lazy var outputBuffer:CircularBuffer? = { - return CircularBuffer.init(numChannels: Int64(self.audioManager!.numOutputChannels), - andBufferSize: Int64(BUFFER_SIZE)) - }() private lazy var inputBuffer:CircularBuffer? = { return CircularBuffer.init(numChannels: Int64(self.audioManager!.numInputChannels), @@ -98,102 +65,35 @@ class AudioModel { //========================================== // MARK: Private Methods - private lazy var fileReader:AudioFileReader? = { - - if let url = Bundle.main.url(forResource: "satisfaction", withExtension: "mp3"){ - var tmpFileReader:AudioFileReader? = AudioFileReader.init(audioFileURL: url, - samplingRate: Float(audioManager!.samplingRate), - numChannels: audioManager!.numOutputChannels) - - tmpFileReader!.currentTime = 0.0 - print("Audio file succesfully loaded for \(url)") - return tmpFileReader - }else{ - print("Could not initialize audio input file") - return nil - } - }() + // NONE for this model //========================================== // MARK: Model Callback Methods @objc private func runEveryInterval(){ if inputBuffer != nil { - // copy data to swift array + // copy time data to swift array self.inputBuffer!.fetchFreshData(&timeData, withNumSamples: Int64(BUFFER_SIZE)) - // now take FFT and display it + // now take FFT fftHelper!.performForwardFFT(withData: &timeData, andCopydBMagnitudeToBuffer: &fftData) - + // at this point, we have saved the data to the arrays: + // timeData: the raw audio samples + // fftData: the FFT of those same samples + // the user can now use these variables however they like } } - - //========================================== // MARK: Audiocard Callbacks // in obj-C it was (^InputBlock)(float *data, UInt32 numFrames, UInt32 numChannels) // and in swift this translates to: private func handleMicrophone (data:Optional>, numFrames:UInt32, numChannels: UInt32) { -// var max:Float = 0.0 -// if let arrayData = data{ -// for i in 0..max){ -// max = abs(arrayData[i]) -// } -// } -// } -// // can this max operation be made faster?? -// print(max) - // copy samples from the microphone into circular buffer self.inputBuffer?.addNewFloatData(data, withNumSamples: Int64(numFrames)) } - private func handleSpeakerQueryWithAudioFile(data:Optional>, numFrames:UInt32, numChannels: UInt32){ - if let file = self.fileReader{ - - // read from file, loaidng into data (a float pointer) - file.retrieveFreshAudio(data, - numFrames: numFrames, - numChannels: numChannels) - - // set samples to output speaker buffer - self.outputBuffer?.addNewFloatData(data, - withNumSamples: Int64(numFrames)) - } - } - // _ _ _ _ _ _ _ _ _ _ - // / \ / \ / \ / \ / \ / \ / \ / \ / \ / - // / \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ - var sineFrequency:Float = 0.0 { // frequency in Hz (changeable by user) - didSet{ - // if using swift for generating the sine wave: when changed, we need to update our increment - //phaseIncrement = Float(2*Double.pi*sineFrequency/audioManager!.samplingRate) - - // if using objective c: this changes the frequency in the novocain block - self.audioManager?.sineFrequency = sineFrequency - } - } - private var phase:Float = 0.0 - private var phaseIncrement:Float = 0.0 - private var sineWaveRepeatMax:Float = Float(2*Double.pi) - - private func handleSpeakerQueryWithSinusoid(data:Optional>, numFrames:UInt32, numChannels: UInt32){ - // while pretty fast, this loop is still not quite as fast as - // writing the code in c, so I placed a function in Novocaine to do it for you - // use setOutputBlockToPlaySineWave() in Novocaine - if let arrayData = data{ - var i = 0 - while i= sineWaveRepeatMax) { phase -= sineWaveRepeatMax } - i+=1 - } - } - } } diff --git a/AudioLabSwift/ViewController.swift b/AudioLabSwift/ViewController.swift index 01e10aa..7085998 100644 --- a/AudioLabSwift/ViewController.swift +++ b/AudioLabSwift/ViewController.swift @@ -15,7 +15,7 @@ let AUDIO_BUFFER_SIZE = 1024*4 class ViewController: UIViewController { - + // setup audio model let audio = AudioModel(buffer_size: AUDIO_BUFFER_SIZE) lazy var graph:MetalGraph? = { return MetalGraph(mainView: self.view) @@ -35,10 +35,9 @@ class ViewController: UIViewController { shouldNormalize: false, numPointsInGraph: AUDIO_BUFFER_SIZE) - // just start up the audio model here + // start up the audio model here, querying microphone audio.startMicrophoneProcessing(withFps: 10) - //audio.startProcesingAudioFileForPlayback() - audio.startProcessingSinewaveForPlayback(withFreq: 630.0) + audio.play() // run the loop for updating the graph peridocially @@ -49,7 +48,7 @@ class ViewController: UIViewController { } - + // periodically, update the graph with refreshed FFT Data @objc func updateGraph(){ self.graph?.updateGraph( From 164993131ebdcb3884644a15ce6c41e0e1ad8da3 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Fri, 28 Aug 2020 13:09:07 -0500 Subject: [PATCH 02/33] cleaned up spacing in file --- AudioLabSwift/AudioModel.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AudioLabSwift/AudioModel.swift b/AudioLabSwift/AudioModel.swift index acec869..729f204 100644 --- a/AudioLabSwift/AudioModel.swift +++ b/AudioLabSwift/AudioModel.swift @@ -73,7 +73,8 @@ class AudioModel { private func runEveryInterval(){ if inputBuffer != nil { // copy time data to swift array - self.inputBuffer!.fetchFreshData(&timeData, withNumSamples: Int64(BUFFER_SIZE)) + self.inputBuffer!.fetchFreshData(&timeData, + withNumSamples: Int64(BUFFER_SIZE)) // now take FFT fftHelper!.performForwardFFT(withData: &timeData, From 9039ca73c8bc661f633a04e26e6c99a8c16f7b40 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Fri, 28 Aug 2020 14:21:15 -0500 Subject: [PATCH 03/33] Updated code from the flipped module. --- AudioLabSwift/ViewController.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/AudioLabSwift/ViewController.swift b/AudioLabSwift/ViewController.swift index 7085998..93f3f38 100644 --- a/AudioLabSwift/ViewController.swift +++ b/AudioLabSwift/ViewController.swift @@ -35,6 +35,8 @@ class ViewController: UIViewController { shouldNormalize: false, numPointsInGraph: AUDIO_BUFFER_SIZE) + + // start up the audio model here, querying microphone audio.startMicrophoneProcessing(withFps: 10) @@ -61,6 +63,8 @@ class ViewController: UIViewController { forKey: "time" ) + + } From 9f39b6817d14fdad5d1b8936cb146b9e57e0d610 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Tue, 31 Aug 2021 16:30:31 -0500 Subject: [PATCH 04/33] changing to use latest license --- AudioLabSwift.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AudioLabSwift.xcodeproj/project.pbxproj b/AudioLabSwift.xcodeproj/project.pbxproj index 2ddc72d..e2ec38e 100644 --- a/AudioLabSwift.xcodeproj/project.pbxproj +++ b/AudioLabSwift.xcodeproj/project.pbxproj @@ -329,7 +329,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = APD62CDC25; + DEVELOPMENT_TEAM = F69WJ27LN4; INFOPLIST_FILE = AudioLabSwift/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.6; LD_RUNPATH_SEARCH_PATHS = ( @@ -351,7 +351,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = APD62CDC25; + DEVELOPMENT_TEAM = F69WJ27LN4; INFOPLIST_FILE = AudioLabSwift/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.6; LD_RUNPATH_SEARCH_PATHS = ( From 8b5be82e66bf0a0c0e395123c71d174f18ca6047 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Mon, 13 Sep 2021 14:52:09 -0500 Subject: [PATCH 05/33] new remote branch and output --- AudioLabSwift/AudioModel.swift | 23 ++++++++++++++--------- AudioLabSwift/ViewController.swift | 12 ++++++++---- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/AudioLabSwift/AudioModel.swift b/AudioLabSwift/AudioModel.swift index 729f204..a6e592a 100644 --- a/AudioLabSwift/AudioModel.swift +++ b/AudioLabSwift/AudioModel.swift @@ -29,20 +29,24 @@ class AudioModel { // public function for starting processing of microphone data func startMicrophoneProcessing(withFps:Double){ // setup the microphone to copy to circualr buffer - self.audioManager?.inputBlock = self.handleMicrophone - - // repeat this fps times per second using the timer class - // every time this is called, we update the arrays "timeData" and "fftData" - Timer.scheduledTimer(timeInterval: 1.0/withFps, target: self, - selector: #selector(self.runEveryInterval), - userInfo: nil, - repeats: true) + if let manager = self.audioManager{ + manager.inputBlock = self.handleMicrophone + + // repeat this fps times per second using the timer class + // every time this is called, we update the arrays "timeData" and "fftData" + Timer.scheduledTimer(timeInterval: 1.0/withFps, target: self, + selector: #selector(self.runEveryInterval), + userInfo: nil, + repeats: true) + } } // You must call this when you want the audio to start being handled by our model func play(){ - self.audioManager?.play() + if let manager = self.audioManager{ + manager.play() + } } @@ -79,6 +83,7 @@ class AudioModel { // now take FFT fftHelper!.performForwardFFT(withData: &timeData, andCopydBMagnitudeToBuffer: &fftData) + // at this point, we have saved the data to the arrays: // timeData: the raw audio samples // fftData: the FFT of those same samples diff --git a/AudioLabSwift/ViewController.swift b/AudioLabSwift/ViewController.swift index 93f3f38..d7dff38 100644 --- a/AudioLabSwift/ViewController.swift +++ b/AudioLabSwift/ViewController.swift @@ -10,13 +10,17 @@ import UIKit import Metal -let AUDIO_BUFFER_SIZE = 1024*4 + class ViewController: UIViewController { + struct AudioConstants{ + static let AUDIO_BUFFER_SIZE = 1024*4 + } + // setup audio model - let audio = AudioModel(buffer_size: AUDIO_BUFFER_SIZE) + let audio = AudioModel(buffer_size: AudioConstants.AUDIO_BUFFER_SIZE) lazy var graph:MetalGraph? = { return MetalGraph(mainView: self.view) }() @@ -29,11 +33,11 @@ class ViewController: UIViewController { // add in graphs for display graph?.addGraph(withName: "fft", shouldNormalize: true, - numPointsInGraph: AUDIO_BUFFER_SIZE/2) + numPointsInGraph: AudioConstants.AUDIO_BUFFER_SIZE/2) graph?.addGraph(withName: "time", shouldNormalize: false, - numPointsInGraph: AUDIO_BUFFER_SIZE) + numPointsInGraph: AudioConstants.AUDIO_BUFFER_SIZE) From d2a597e57c604cd4d7b2bdbc2100466aad70b44e Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 22 Sep 2021 12:31:23 -0500 Subject: [PATCH 06/33] fixing novocaine issue with sampling rate --- AudioDSPUtils/Novocaine.h | 4 +- AudioDSPUtils/Novocaine.m | 220 ++++++++++++++++++++------------------ 2 files changed, 117 insertions(+), 107 deletions(-) diff --git a/AudioDSPUtils/Novocaine.h b/AudioDSPUtils/Novocaine.h index 12cb253..ab18709 100644 --- a/AudioDSPUtils/Novocaine.h +++ b/AudioDSPUtils/Novocaine.h @@ -71,7 +71,7 @@ typedef void (^InputBlock)(float *data, UInt32 numFrames, UInt32 numChannels); @interface Novocaine : NSObject -@property AudioUnit inputUnit; +@property AudioUnit audioUnit; @property AudioBufferList *inputBuffer; @property (nonatomic, copy) OutputBlock outputBlock; @property (nonatomic, copy) InputBlock inputBlock; @@ -79,7 +79,7 @@ typedef void (^InputBlock)(float *data, UInt32 numFrames, UInt32 numChannels); @property (nonatomic, retain) NSString *inputRoute; @property UInt32 numInputChannels; @property UInt32 numOutputChannels; -@property Float64 samplingRate; +@property (readonly) Float64 samplingRate; @property BOOL isInterleaved; @property BOOL isSetUp; @property UInt32 numBytesPerSample; diff --git a/AudioDSPUtils/Novocaine.m b/AudioDSPUtils/Novocaine.m index 12b3f42..9b40a66 100644 --- a/AudioDSPUtils/Novocaine.m +++ b/AudioDSPUtils/Novocaine.m @@ -24,6 +24,7 @@ // // TODO: // Switching mic and speaker on/off +// Updating audio route from user // // HOUSEKEEPING AND NICE FEATURES: // Disambiguate outputFormat (the AUHAL's stream format) @@ -62,6 +63,7 @@ @interface Novocaine() @property (nonatomic, strong) NSTimer *audioFileTimer; @property (nonatomic) float *outputBuffer; @property float phaseIncrement; +@property Float64 samplingRate; - (void)setupAudio; @@ -92,16 +94,6 @@ + (Novocaine *) audioManager return _sharedInstance; } -//+ (id)allocWithZone:(NSZone *)zone { -// @synchronized(self) { -// if (audioManager == nil) { -// audioManager = [super allocWithZone:zone]; -// return audioManager; // assignment and return on first allocation -// } -// } -// return nil; // on subsequent allocation attempts return nil -//} - - (id)init { @@ -112,11 +104,14 @@ - (id)init _outputBlock = nil; _inputBlock = nil; + AVAudioSession *session = [AVAudioSession sharedInstance]; + self.samplingRate = session.sampleRate; // need this setup right away + // Initialize a float buffer to hold audio _inData = (float *)calloc(8192, sizeof(float)); // probably more than we'll need _outData = (float *)calloc(8192, sizeof(float)); - _outputBuffer = (float *)calloc(2*44100.0, sizeof(float)); + _outputBuffer = (float *)calloc(2*self.samplingRate, sizeof(float)); pthread_mutex_init(&outputAudioFileLock, NULL); _playing = NO; @@ -210,6 +205,68 @@ -(void)setOutputBlockToPlaySineWave:(float)frequency{ }]; } +-(void)setAudioRoute{ + // NSArray* inputs = [session availableInputs]; + // + // // Locate the Port corresponding to the built-in microphone. + // AVAudioSessionPortDescription* builtInMicPort = nil; + // for (AVAudioSessionPortDescription* port in inputs) + // { + // if ([port.portType isEqualToString:AVAudioSessionPortBuiltInMic]) + // { + // builtInMicPort = port; + // break; + // } + // } + // + // // Print out a description of the data sources for the built-in microphone + // NSLog(@"There are %u data sources for port :\"%@\"", (unsigned)[builtInMicPort.dataSources count], builtInMicPort); + // NSLog(@"%@", builtInMicPort.dataSources); + // + // // loop over the built-in mic's data sources and attempt to locate the front microphone + // AVAudioSessionDataSourceDescription* frontDataSource = nil; + // for (AVAudioSessionDataSourceDescription* source in builtInMicPort.dataSources) + // { + // // other options: + // // AVAudioSessionOrientation( Top | {Front} | Back | Bottom ) + // if ([source.orientation isEqual:AVAudioSessionOrientationBottom]) + // { + // frontDataSource = source; + // break; + // } + // } // end data source iteration + // + // if (frontDataSource) + // { + // NSLog(@"Currently selected source is \"%@\" for port \"%@\"", builtInMicPort.selectedDataSource.dataSourceName, builtInMicPort.portName); + // NSLog(@"Attempting to select source \"%@\" on port \"%@\"", frontDataSource, builtInMicPort.portName); + // + // // Set a preference for the front data source. + // error = nil; + // if (![builtInMicPort setPreferredDataSource:frontDataSource error:&error]) + // { + // // an error occurred. + // NSLog(@"setPreferredDataSource failed"); + // } + // } + // else{ + // NSLog(@"Front Data Source is nil, cannot change source."); + // } + // + // // Make sure the built-in mic is selected for input. This will be a no-op if the built-in mic is + // // already the current input Port. + // error = nil; + // if(![session setPreferredInput:builtInMicPort error:&error]){ + // NSLog(@"%@ Couldn't set mic as preferred port %@", + // NSStringFromSelector(_cmd), [error localizedDescription]); + // @throw error; + // } + + // Add a property listener, to listen to changes to the Route of Audio Input + // NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + // [nc addObserver:self selector:@selector(audioRouteChangedListener:) name:AVAudioSessionRouteChangeNotification object:nil]; +} + #pragma mark - Audio Methods @@ -243,7 +300,7 @@ - (void) teardownAudio { // Set the audio session to not active if(![[AVAudioSession sharedInstance] setActive:NO error:&error]){ - NSLog(@"%@ Couldn't activate audio session %@", + NSLog(@"%@ Couldn't set activate audio session %@", NSStringFromSelector(_cmd), [error localizedDescription]); @throw error; } @@ -256,9 +313,9 @@ - (void) teardownAudio { // CheckError( AudioSessionRemovePropertyListenerWithUserData(kAudioSessionProperty_AudioRouteChange, sessionPropertyListener, self), "Couldn't remove audio session property listener"); // Uninitialize and dispose the audio input unit - CheckError( AudioUnitUninitialize(self.inputUnit), "Couldn't uninitialize audio input unit"); - CheckError( AudioComponentInstanceDispose(self.inputUnit), "Couldn't dispose of audio input unit"); - self.inputUnit = nil; + CheckError( AudioUnitUninitialize(self.audioUnit), "Couldn't uninitialize audio input unit"); + CheckError( AudioComponentInstanceDispose(self.audioUnit), "Couldn't dispose of audio input unit"); + self.audioUnit = nil; _isSetUp = NO; @@ -300,78 +357,25 @@ - (void)setupAudio // Code inserted by Eric Larson for setting audio route // Get the set of available inputs. If there are no audio accessories attached, there will be // only one available input -- the built in microphone. -// NSArray* inputs = [session availableInputs]; -// -// // Locate the Port corresponding to the built-in microphone. -// AVAudioSessionPortDescription* builtInMicPort = nil; -// for (AVAudioSessionPortDescription* port in inputs) -// { -// if ([port.portType isEqualToString:AVAudioSessionPortBuiltInMic]) -// { -// builtInMicPort = port; -// break; -// } -// } -// -// // Print out a description of the data sources for the built-in microphone -// NSLog(@"There are %u data sources for port :\"%@\"", (unsigned)[builtInMicPort.dataSources count], builtInMicPort); -// NSLog(@"%@", builtInMicPort.dataSources); -// -// // loop over the built-in mic's data sources and attempt to locate the front microphone -// AVAudioSessionDataSourceDescription* frontDataSource = nil; -// for (AVAudioSessionDataSourceDescription* source in builtInMicPort.dataSources) -// { -// // other options: -// // AVAudioSessionOrientation( Top | {Front} | Back | Bottom ) -// if ([source.orientation isEqual:AVAudioSessionOrientationBottom]) -// { -// frontDataSource = source; -// break; -// } -// } // end data source iteration -// -// if (frontDataSource) -// { -// NSLog(@"Currently selected source is \"%@\" for port \"%@\"", builtInMicPort.selectedDataSource.dataSourceName, builtInMicPort.portName); -// NSLog(@"Attempting to select source \"%@\" on port \"%@\"", frontDataSource, builtInMicPort.portName); -// -// // Set a preference for the front data source. -// error = nil; -// if (![builtInMicPort setPreferredDataSource:frontDataSource error:&error]) -// { -// // an error occurred. -// NSLog(@"setPreferredDataSource failed"); -// } -// } -// else{ -// NSLog(@"Front Data Source is nil, cannot change source."); -// } -// -// // Make sure the built-in mic is selected for input. This will be a no-op if the built-in mic is -// // already the current input Port. -// error = nil; -// if(![session setPreferredInput:builtInMicPort error:&error]){ -// NSLog(@"%@ Couldn't set mic as preferred port %@", -// NSStringFromSelector(_cmd), [error localizedDescription]); -// @throw error; -// } - - // Add a property listener, to listen to changes to the Route of Audio Input -// NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; -// [nc addObserver:self selector:@selector(audioRouteChangedListener:) name:AVAudioSessionRouteChangeNotification object:nil]; + [self setAudioRoute]; // currently does nothing + // Set the buffer size, this will affect the number of samples that get rendered every time the audio callback is fired // A small number will get you lower latency audio, but will make your processor work harder -#if !TARGET_IPHONE_SIMULATOR - Float32 preferredBufferSize = 0.0232; - [session setPreferredIOBufferDuration:preferredBufferSize error:&error]; - //CheckError( AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration, sizeof(preferredBufferSize), &preferredBufferSize), "Couldn't set the preferred buffer duration"); -#endif + Float32 preferredBufferSize = 1024.0/self.samplingRate; // 1024/44100 = 0.0232 + [session setPreferredIOBufferDuration:preferredBufferSize error:&error]; + if(error!=nil){ + NSLog(@"Could not set preferred buffer duration, Error: %@",error.localizedDescription); + } + + [session setPreferredSampleRate: self.samplingRate error:&error]; + if(error!=nil){ + NSLog(@"Could not set preferred sample rate, Error: %@",error.localizedDescription); + } - [self checkSessionProperties]; - + [self checkSessionProperties]; // ----- Audio Unit Setup ----- // ---------------------------- @@ -385,12 +389,12 @@ - (void)setupAudio // Get component AudioComponent inputComponent = AudioComponentFindNext(NULL, &inputDescription); - CheckError( AudioComponentInstanceNew(inputComponent, &_inputUnit), "Couldn't create the output audio unit"); + CheckError( AudioComponentInstanceNew(inputComponent, &_audioUnit), "Couldn't create the output audio unit"); // Enable input UInt32 one = 1; - CheckError( AudioUnitSetProperty(_inputUnit, + CheckError( AudioUnitSetProperty(_audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, @@ -402,7 +406,7 @@ - (void)setupAudio UInt32 size; size = sizeof( AudioStreamBasicDescription ); - CheckError( AudioUnitGetProperty( _inputUnit, + CheckError( AudioUnitGetProperty( _audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, @@ -412,7 +416,7 @@ - (void)setupAudio // Check the output stream format size = sizeof( AudioStreamBasicDescription ); - CheckError( AudioUnitGetProperty( _inputUnit, + CheckError( AudioUnitGetProperty( _audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, @@ -420,13 +424,13 @@ - (void)setupAudio &size ), "Couldn't get the hardware output stream format"); - _inputFormat.mSampleRate = 44100.0; - _outputFormat.mSampleRate = 44100.0; - self.samplingRate = _inputFormat.mSampleRate; + _inputFormat.mSampleRate = self.samplingRate; + _outputFormat.mSampleRate = self.samplingRate; + //self.samplingRate = _inputFormat.mSampleRate; self.numBytesPerSample = _inputFormat.mBitsPerChannel / 8; size = sizeof(AudioStreamBasicDescription); - CheckError(AudioUnitSetProperty(_inputUnit, + CheckError(AudioUnitSetProperty(_audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, @@ -440,7 +444,7 @@ - (void)setupAudio UInt32 numFramesPerBuffer; size = sizeof(UInt32); - CheckError(AudioUnitGetProperty(_inputUnit, + CheckError(AudioUnitGetProperty(_audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, kOutputBus, @@ -498,7 +502,7 @@ - (void)setupAudio callbackStruct.inputProc = inputCallback; callbackStruct.inputProcRefCon = (__bridge void *)(self); - CheckError( AudioUnitSetProperty(_inputUnit, + CheckError( AudioUnitSetProperty(_audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, @@ -509,7 +513,7 @@ - (void)setupAudio callbackStruct.inputProc = renderCallback; callbackStruct.inputProcRefCon = (__bridge void *)(self); - CheckError( AudioUnitSetProperty(_inputUnit, + CheckError( AudioUnitSetProperty(_audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, @@ -520,7 +524,7 @@ - (void)setupAudio - CheckError(AudioUnitInitialize(_inputUnit), "Couldn't initialize the output unit"); + CheckError(AudioUnitInitialize(_audioUnit), "Couldn't initialize the output unit"); _isSetUp = YES; @@ -537,7 +541,7 @@ - (void)pause { if(self.shouldSaveContinuouslySampledMicrophoneAudioDataToNewFile) [self closeAudioFileForWritingFromMicrophone]; - CheckError( AudioOutputUnitStop(_inputUnit), "Couldn't stop the output unit"); + CheckError( AudioOutputUnitStop(_audioUnit), "Couldn't stop the output unit"); self.playing = NO; } @@ -548,7 +552,7 @@ - (void)play { if(self.shouldUseAudioFromFile){ //Play from file - CheckError( AudioOutputUnitStop(_inputUnit), "Couldn't stop the output unit"); + CheckError( AudioOutputUnitStop(_audioUnit), "Couldn't stop the output unit"); // setup audio file for continuous reading float preferredTimeInterval = [self initAudioFileForReadingWithName:self.audioFileName]; @@ -581,7 +585,7 @@ - (void)play { if(self.shouldSaveContinuouslySampledMicrophoneAudioDataToNewFile) [self setupAudioFileForWritingFromMicrophone]; - CheckError( AudioOutputUnitStart(self.inputUnit), "Couldn't start the output unit"); + CheckError( AudioOutputUnitStart(self.audioUnit), "Couldn't start the output unit"); self.playing = YES; } @@ -605,6 +609,7 @@ OSStatus inputCallback (void *inRefCon, Novocaine *sm = (__bridge Novocaine *)inRefCon; + // setup rendering callback if (!sm.playing) return noErr; if (sm.inputBlock == nil) @@ -619,7 +624,8 @@ OSStatus inputCallback (void *inRefCon, if( inNumberFrames == 471 ) inNumberFrames = 470; #endif - CheckError( AudioUnitRender(sm.inputUnit, ioActionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, sm.inputBuffer), "Couldn't render the output unit"); + // NSLog(@"Frames: %d",inNumberFrames); // had some weird stuff going on + CheckError( AudioUnitRender(sm.audioUnit, ioActionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, sm.inputBuffer), "Couldn't render the audio unit"); // Convert the audio in something manageable @@ -816,7 +822,7 @@ - (void)checkAudioSource { AVAudioSession *session = [AVAudioSession sharedInstance]; - + //CheckError( AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &propertySize, &route), "Couldn't check the audio route"); //self.inputRoute = (NSString *)route; //CFRelease(route); @@ -833,13 +839,14 @@ - (void)checkAudioSource { // To be run ONCE per session property change and once on initialization. - (void)checkSessionProperties -{ +{ + AVAudioSession *session = [AVAudioSession sharedInstance]; + NSLog(@"Checking session properties"); // Check if there is input, and from where [self checkAudioSource]; - AVAudioSession *session = [AVAudioSession sharedInstance]; // Check the number of input channels. // Find the number of channels @@ -856,9 +863,12 @@ - (void)checkSessionProperties // Get the hardware sampling rate. This is settable, but here we're only reading. - - self.samplingRate = session.sampleRate; - NSLog(@"Current sampling rate: %f", self.samplingRate); + NSLog(@"Current sampling rate: Preferred:%.1f, Actual:%.1f", self.samplingRate, session.sampleRate); + + + NSLog(@"Actual Buffer Duration: %.4f, length %d", session.IOBufferDuration, + (UInt32)(session.IOBufferDuration*session.sampleRate+1)); + } @@ -950,7 +960,7 @@ - (float) initAudioFileForReadingWithName: (NSString*)name { AudioStreamBasicDescription audioFormat; - audioFormat.mSampleRate = 44100; + audioFormat.mSampleRate = self.samplingRate; audioFormat.mFormatID = kAudioFormatLinearPCM; audioFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat; audioFormat.mBitsPerChannel = sizeof(Float32) * 8; @@ -1085,7 +1095,7 @@ -(void)setupAudioFileForWritingFromMicrophone{ AudioStreamBasicDescription audioFormat; - AudioStreamBasicDescription outputFileDesc = {44100.0, kAudioFormatMPEG4AAC, 0, 0, 1024, 0, self.numInputChannels, 0, 0}; + AudioStreamBasicDescription outputFileDesc = {self.samplingRate, kAudioFormatMPEG4AAC, 0, 0, 1024, 0, self.numInputChannels, 0, 0}; CheckError(ExtAudioFileCreateWithURL(outputFileURL, kAudioFileM4AType, &outputFileDesc, NULL, kAudioFileFlags_EraseFile, &_audioFileRefOutput), "Creating file"); From 054ee2291b88d22b009729a41bbb75744015e0f2 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 3 Nov 2021 21:24:35 -0500 Subject: [PATCH 07/33] commit of better graphing library --- AudioLabSwift/MetalGraph.swift | 304 ++++++++++++++++++++--------- AudioLabSwift/ViewController.swift | 5 +- 2 files changed, 211 insertions(+), 98 deletions(-) diff --git a/AudioLabSwift/MetalGraph.swift b/AudioLabSwift/MetalGraph.swift index b20b8a0..636c817 100644 --- a/AudioLabSwift/MetalGraph.swift +++ b/AudioLabSwift/MetalGraph.swift @@ -2,10 +2,18 @@ // MetalGraph.swift // AudioLabSwift // -// Created by Eric Larson +// Created by Eric Larson // Copyright © 2020 Eric Larson. All rights reserved. // + +//TODO: +// 0. make private things +// 1. Limit values hi/lo to fit in view +// 2. grid in the graph +// 3. values for grid? + + import Foundation import UIKit import Metal @@ -13,31 +21,47 @@ import Accelerate class MetalGraph { - var device: MTLDevice! - var metalLayer: CAMetalLayer! - var pipelineState: MTLRenderPipelineState! - var commandQueue: MTLCommandQueue! - var timer: CADisplayLink! - + //MARK: MTL Properties + private var device: MTLDevice! + private var metalLayer: CAMetalLayer! + private var pipelineState: MTLRenderPipelineState! + private var commandQueue: MTLCommandQueue! + private var timer: CADisplayLink! - var vertexData: [String:[Float]] = [String: [Float]]() - var vertexBuffer: [String:MTLBuffer] = [String:MTLBuffer]() - var vertexColorBuffer: [String:MTLBuffer] = [String:MTLBuffer]() - var vertexPointer: [String:UnsafeMutablePointer] = [String:UnsafeMutablePointer]() - var vertexNormalize: [String:Bool] = [String:Bool]() - var vertexNum: [String:Int] = [String:Int]() - var dsFactor: [String:Int] = [String:Int]() + private var backgroundColor = MTLClearColor( + red: 50.0/255.0, + green: 50.0/255.0, + blue: 50.0/255.0, + alpha: 1.0) + + //MARK: Dictionary Properties for saving state/data from user + private var vertexData: [String:[Float]] = [String: [Float]]() + private var vertexBuffer: [String:MTLBuffer] = [String:MTLBuffer]() + private var vertexColorBuffer: [String:MTLBuffer] = [String:MTLBuffer]() + private var vertexPointer: [String:UnsafeMutablePointer] = [String:UnsafeMutablePointer]() + private var vertexNum: [String:Int] = [String:Int]() + private var dsFactor: [String:Int] = [String:Int]() + private var vertexGain: [String:Float] = [String:Float]() + private var vertexBias: [String:Float] = [String:Float]() + private var needsRender = false - let maxPointsPerGraph = 512 // you can increase this or decrease for different GPU speeds - var needsRender = false - let numShaderFloats = 4 + //MARK: iOS color palette with gradients + private let R = [0xFF,0xFF, 0x52,0x5A, 0xFF,0xFF, 0x1A,0x1D, 0xEF,0xC6, 0xDB,0x89, 0x87,0x0B, 0xFF,0xFF] + private let G = [0x5E,0x2A, 0xED,0xC8, 0xDB,0xCD, 0xD6,0x62, 0x4D,0x43, 0xDD,0x8C, 0xFC,0xD3, 0x95,0x5E] + private let B = [0x3A,0x68, 0xC7,0xFB, 0x4C,0x02, 0xFD,0xF0, 0xB6,0xFC, 0xDE,0x90, 0x70,0x18, 0x00,0x3A] + - //iOS color palette with gradients - let R = [0xFF,0xFF, 0x52,0x5A, 0xFF,0xFF, 0x1A,0x1D, 0xEF,0xC6, 0xDB,0x89, 0x87,0x0B, 0xFF,0xFF] - let G = [0x5E,0x2A, 0xED,0xC8, 0xDB,0xCD, 0xD6,0x62, 0x4D,0x43, 0xDD,0x8C, 0xFC,0xD3, 0x95,0x5E] - let B = [0x3A,0x68, 0xC7,0xFB, 0x4C,0x02, 0xFD,0xF0, 0xB6,0xFC, 0xDE,0x90, 0x70,0x18, 0x00,0x3A] + //MARK: Constants + private struct GraphConstants{ + static let fftNormalizer:Float = 64.0 + static let fftAddition:Float = 40.0 + static let maxPointsPerGraph = 512 // you can increase this or decrease for different GPU speeds + static let numShaderFloats = 4 + } - init(mainView:UIView) + //MARK: Initialization and Rendering Functions + // Initialize the class, setup where this view will be drawing to + init(userView:UIView) { // get device guard let device = MTLCreateSystemDefaultDevice() else { fatalError("GPU not available") } @@ -48,17 +72,20 @@ class MetalGraph { metalLayer.device = self.device metalLayer.pixelFormat = .bgra8Unorm metalLayer.framebufferOnly = true - metalLayer.frame = mainView.layer.frame - mainView.layer.insertSublayer(metalLayer, at:0) - + metalLayer.frame = userView.bounds + metalLayer.contentsScale = 2.0 + userView.layer.insertSublayer(metalLayer, at:0) + commandQueue = self.device.makeCommandQueue() + // setup a repeating render function timer = CADisplayLink(target: self, selector: #selector(gameloop)) timer.add(to: RunLoop.main, forMode: .default) + // add in shaders to the program guard let defaultLibrary = device.makeDefaultLibrary(), let fragmentProgram = defaultLibrary.makeFunction(name: "passThroughFragment"), - let vertexProgram = defaultLibrary.makeFunction(name: "passThroughVertex") else { fatalError() } + let vertexProgram = defaultLibrary.makeFunction(name: "passThroughVertex") else { fatalError("Could not find Shaders.metal file.") } let pipelineStateDescriptor = MTLRenderPipelineDescriptor() pipelineStateDescriptor.vertexFunction = vertexProgram @@ -67,64 +94,36 @@ class MetalGraph { pipelineStateDescriptor.colorAttachments[0].isBlendingEnabled = false pipelineState = try! device.makeRenderPipelineState(descriptor: pipelineStateDescriptor) + + //createBox() } - func addGraph(withName:String, - shouldNormalize:Bool, - numPointsInGraph:Int){ - - //setup graph - let key = withName - let numGraphs = Int(vertexData.count) - - dsFactor[key] = Int(numPointsInGraph/maxPointsPerGraph) // downsample factor for each graph - if dsFactor[key]!<1 { dsFactor[key] = 1 } - - vertexData[key] = Array.init(repeating: 0.0, count: (numPointsInGraph/dsFactor[key]!)*numShaderFloats) - vertexNormalize[key] = shouldNormalize - vertexNum[key] = numGraphs + private var boxBuffer:MTLBuffer? = nil + private var boxColorBuffer:MTLBuffer? = nil + + private func createBox(){ + let box:[Float] = [-0.75, 0.0, 0.0, 1.0, + -0.75, 0.25, 0.0, 1.0, + 0.75, 0.25, 0.0, 1.0, + 0.75, 0.0, 0.0, 1.0, + -0.75, 0.0, 0.0, 1.0] - // we use a 4D location, so copy over the right things - let maxIdx = Int(vertexData[key]!.count/numShaderFloats) - for j in 0.. Date: Fri, 5 Nov 2021 09:02:28 -0500 Subject: [PATCH 08/33] adding graphing support for grids and examples to set background color --- AudioLabSwift/MetalGraph.swift | 146 +++++++++++++++++++++-------- AudioLabSwift/ViewController.swift | 4 +- 2 files changed, 109 insertions(+), 41 deletions(-) diff --git a/AudioLabSwift/MetalGraph.swift b/AudioLabSwift/MetalGraph.swift index 636c817..c15bfc3 100644 --- a/AudioLabSwift/MetalGraph.swift +++ b/AudioLabSwift/MetalGraph.swift @@ -35,14 +35,17 @@ class MetalGraph { alpha: 1.0) //MARK: Dictionary Properties for saving state/data from user - private var vertexData: [String:[Float]] = [String: [Float]]() + private var vertexData: [String:[Float32]] = [String: [Float32]]() private var vertexBuffer: [String:MTLBuffer] = [String:MTLBuffer]() private var vertexColorBuffer: [String:MTLBuffer] = [String:MTLBuffer]() - private var vertexPointer: [String:UnsafeMutablePointer] = [String:UnsafeMutablePointer]() + private var vertexPointer: [String:UnsafeMutablePointer] = [String:UnsafeMutablePointer]() private var vertexNum: [String:Int] = [String:Int]() + private var vertexShowGrid: [String:Bool] = [String:Bool]() private var dsFactor: [String:Int] = [String:Int]() - private var vertexGain: [String:Float] = [String:Float]() - private var vertexBias: [String:Float] = [String:Float]() + private var vertexGain: [String:Float32] = [String:Float32]() + private var vertexBias: [String:Float32] = [String:Float32]() + private var boxBuffer:[String:MTLBuffer] = [String:MTLBuffer]() + private var boxColorBuffer:[String:MTLBuffer] = [String:MTLBuffer]() private var needsRender = false //MARK: iOS color palette with gradients @@ -72,8 +75,9 @@ class MetalGraph { metalLayer.device = self.device metalLayer.pixelFormat = .bgra8Unorm metalLayer.framebufferOnly = true - metalLayer.frame = userView.bounds metalLayer.contentsScale = 2.0 + metalLayer.frame = userView.bounds + userView.layer.insertSublayer(metalLayer, at:0) commandQueue = self.device.makeCommandQueue() @@ -90,35 +94,58 @@ class MetalGraph { let pipelineStateDescriptor = MTLRenderPipelineDescriptor() pipelineStateDescriptor.vertexFunction = vertexProgram pipelineStateDescriptor.fragmentFunction = fragmentProgram - pipelineStateDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm + pipelineStateDescriptor.colorAttachments[0].pixelFormat = metalLayer.pixelFormat pipelineStateDescriptor.colorAttachments[0].isBlendingEnabled = false pipelineState = try! device.makeRenderPipelineState(descriptor: pipelineStateDescriptor) - //createBox() } - private var boxBuffer:MTLBuffer? = nil - private var boxColorBuffer:MTLBuffer? = nil - - private func createBox(){ - let box:[Float] = [-0.75, 0.0, 0.0, 1.0, - -0.75, 0.25, 0.0, 1.0, - 0.75, 0.25, 0.0, 1.0, - 0.75, 0.0, 0.0, 1.0, - -0.75, 0.0, 0.0, 1.0] + + private var gridLength:Int = 0 + private func createGraphGrid(name:String,min:Float,max:Float){ + let mid = (max-min)/2.0+min + let box:[Float32] = [-0.99, min, 0.0, 0.0, // primitve draw protect + -0.99, min, 0.0, 1.0, + -0.99, max, 0.0, 1.0, + 0.99, max, 0.0, 1.0, + 0.99, min, 0.0, 1.0, + -0.99, min, 0.0, 1.0, // outer box + -0.75, min, 0.0, 1.0, + -0.75, max, 0.0, 1.0, + 0.75, max, 0.0, 1.0, + 0.75, min, 0.0, 1.0, + -0.75, min, 0.0, 1.0, // outer quartile box + -0.25, min, 0.0, 1.0, + -0.25, max, 0.0, 1.0, + 0.25, max, 0.0, 1.0, + 0.25, min, 0.0, 1.0, + -0.25, min, 0.0, 1.0, // inner quartile box + -0.5, min, 0.0, 1.0, + -0.5, max, 0.0, 1.0, + 0.5, max, 0.0, 1.0, + 0.5, min, 0.0, 1.0, + -0.5, min, 0.0, 1.0, // mid quartile box + 0.0, min, 0.0, 1.0, + 0.0, max, 0.0, 1.0, // mid line + -0.99, max, 0.0, 1.0, // center line + -0.99, mid, 0.0, 1.0, + 0.99, mid, 0.0, 1.0, + 0.99, mid, 0.0, 0.0 // primitve draw protect + ] - let boxColor:[Float] = Array.init(repeating: 0.5, count:box.count) + let boxColor:[Float32] = [Float32].init(repeating: 0.5, count:box.count) + gridLength = box.count var dataSize = box.count * MemoryLayout.size(ofValue: box[0]) - boxBuffer = device.makeBuffer(bytes: box, + boxBuffer[name] = device.makeBuffer(bytes: box, length: dataSize, - options: .cpuCacheModeWriteCombined) + options: []) //cpuCacheModeWriteCombined dataSize = boxColor.count * MemoryLayout.size(ofValue: boxColor[0]) - boxColorBuffer = device.makeBuffer(bytes: boxColor, + boxColorBuffer[name] = device.makeBuffer(bytes: boxColor, length: dataSize, - options: .cpuCacheModeWriteCombined) + options: []) } @@ -143,11 +170,13 @@ class MetalGraph { // show graph boxes -// renderEncoder.setVertexBuffer(boxBuffer, offset: 0, index: 0) -// renderEncoder.setVertexBuffer(boxColorBuffer, offset: 0, index: 1) -// renderEncoder.drawPrimitives(type: .lineStrip, -// vertexStart: 0, -// vertexCount: 20) + for (key,_) in boxBuffer{ + renderEncoder.setVertexBuffer(boxBuffer[key], offset: 0, index: 0) + renderEncoder.setVertexBuffer(boxColorBuffer[key], offset: 0, index: 1) + renderEncoder.drawPrimitives(type: .lineStrip, + vertexStart: 0, + vertexCount: gridLength-2) + } // for each graph, update the values for the line @@ -208,6 +237,20 @@ class MetalGraph { withGain:Float, withBias:Float, numPointsInGraph:Int){ + + self.addGraph(withName: withName, + withGain: withGain, + withBias: withBias, + numPointsInGraph: numPointsInGraph, + showGrid: true + ) + } + + func addGraph(withName:String, + withGain:Float, + withBias:Float, + numPointsInGraph:Int, + showGrid:Bool){ //setup graph let key = withName let numGraphs = Int(vertexData.count) @@ -245,21 +288,46 @@ class MetalGraph { // when we want to update the data, we can simply use this pointer and vDSP // now make a color buffer, that we setup once and then forget about - var vertexColorData:[Float] = Array.init(repeating: 1.0, count: (numPointsInGraph/dsFactor[key]!)*GraphConstants.numShaderFloats) - //setup colors in a gradient within one spectra of color pallette - var gradOne:Float = 0.0 - var gradTwo:Float = 0.0 + var vertexColorData:[Float32] = Array.init(repeating: 1.0, count: (numPointsInGraph/dsFactor[key]!)*GraphConstants.numShaderFloats) + + + let b:Float32 = Float32(B[(2*numGraphs)%16])/255.0 + let g:Float32 = Float32(G[(2*numGraphs)%16])/255.0 + let r:Float32 = Float32(R[(2*numGraphs)%16])/255.0 + let a:Float32 = Float32(0.9) for j in 0..= maxIdx-2{ + vertexColorData[j*GraphConstants.numShaderFloats] = 0.0 + vertexColorData[j*GraphConstants.numShaderFloats+1] = 0.0 + vertexColorData[j*GraphConstants.numShaderFloats+2] = 0.0 + vertexColorData[j*GraphConstants.numShaderFloats+3] = 0.0 + } } vertexColorBuffer[key] = device.makeBuffer(bytes: vertexColorData, length: dataSize, options: []) + + // now save if we should have a grid for this graph + vertexShowGrid[key] = showGrid + + } + + func makeGrids(){ + for (forKey,_) in vertexBuffer{ + if vertexShowGrid[forKey]!{ + let numGraphs = Float(vertexData.count) + let addToPlot = -1.0 + 2*(Float(vertexNum[forKey]!) / numGraphs) + 1.0/numGraphs + // get to midpoint of plot on screen + let minVal:Float = addToPlot - (0.9 / numGraphs) + let maxVal:Float = addToPlot + (0.9 / numGraphs) + createGraphGrid(name:forKey, min: minVal, max: maxVal) + } + } } func updateGraph(data:[Float], forKey:String){ @@ -272,8 +340,8 @@ class MetalGraph { var multiplier:Float = 1.0 // get to midpoint of plot on screen - var minVal:Float = addToPlot - (0.99 / numGraphs) - var maxVal:Float = addToPlot + (0.99 / numGraphs) + var minVal:Float = addToPlot - (0.89 / numGraphs) + var maxVal:Float = addToPlot + (0.89 / numGraphs) // now add custom normalizations addToPlot += vertexBias[forKey]!/(vertexGain[forKey]! * numGraphs) diff --git a/AudioLabSwift/ViewController.swift b/AudioLabSwift/ViewController.swift index 29aa9b1..6cda6f9 100644 --- a/AudioLabSwift/ViewController.swift +++ b/AudioLabSwift/ViewController.swift @@ -29,7 +29,7 @@ class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - + graph?.setBackgroundColor(r: 0, g: 0, b: 0, a: 1) // add in graphs for display graph?.addGraph(withName: "fft", shouldNormalizeForFFT: true, @@ -38,7 +38,7 @@ class ViewController: UIViewController { graph?.addGraph(withName: "time", numPointsInGraph: AudioConstants.AUDIO_BUFFER_SIZE) - + graph?.makeGrids() // add grids to graph // start up the audio model here, querying microphone audio.startMicrophoneProcessing(withFps: 10) From 7eda5aa20bcde9d26369288ba747afe505d1b623 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 14 Sep 2022 12:37:50 -0500 Subject: [PATCH 09/33] added better error checking --- AudioLabSwift/ViewController.swift | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/AudioLabSwift/ViewController.swift b/AudioLabSwift/ViewController.swift index 6cda6f9..d5158c5 100644 --- a/AudioLabSwift/ViewController.swift +++ b/AudioLabSwift/ViewController.swift @@ -29,16 +29,18 @@ class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - graph?.setBackgroundColor(r: 0, g: 0, b: 0, a: 1) - // add in graphs for display - graph?.addGraph(withName: "fft", - shouldNormalizeForFFT: true, - numPointsInGraph: AudioConstants.AUDIO_BUFFER_SIZE/2) - - graph?.addGraph(withName: "time", - numPointsInGraph: AudioConstants.AUDIO_BUFFER_SIZE) - - graph?.makeGrids() // add grids to graph + if let graph = self.graph{ + graph.setBackgroundColor(r: 0, g: 0, b: 0, a: 1) + // add in graphs for display + graph.addGraph(withName: "fft", + shouldNormalizeForFFT: true, + numPointsInGraph: AudioConstants.AUDIO_BUFFER_SIZE/2) + + graph.addGraph(withName: "time", + numPointsInGraph: AudioConstants.AUDIO_BUFFER_SIZE) + + graph.makeGrids() // add grids to graph + } // start up the audio model here, querying microphone audio.startMicrophoneProcessing(withFps: 10) From 40ddafa883eb11639972b541151533fe339eae49 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Fri, 15 Sep 2023 12:35:53 -0500 Subject: [PATCH 10/33] Added example for placing the metal graph inside a custom view. --- AudioLabSwift/AudioModel.swift | 9 ++--- AudioLabSwift/Base.lproj/Main.storyboard | 48 ++++++++++++++++++++++-- AudioLabSwift/Info.plist | 4 +- AudioLabSwift/ViewController.swift | 37 +++++++++--------- 4 files changed, 70 insertions(+), 28 deletions(-) diff --git a/AudioLabSwift/AudioModel.swift b/AudioLabSwift/AudioModel.swift index a6e592a..58ace6b 100644 --- a/AudioLabSwift/AudioModel.swift +++ b/AudioLabSwift/AudioModel.swift @@ -34,10 +34,10 @@ class AudioModel { // repeat this fps times per second using the timer class // every time this is called, we update the arrays "timeData" and "fftData" - Timer.scheduledTimer(timeInterval: 1.0/withFps, target: self, - selector: #selector(self.runEveryInterval), - userInfo: nil, - repeats: true) + Timer.scheduledTimer(withTimeInterval: 1.0/withFps, repeats: true) { _ in + self.runEveryInterval() + } + } } @@ -73,7 +73,6 @@ class AudioModel { //========================================== // MARK: Model Callback Methods - @objc private func runEveryInterval(){ if inputBuffer != nil { // copy time data to swift array diff --git a/AudioLabSwift/Base.lproj/Main.storyboard b/AudioLabSwift/Base.lproj/Main.storyboard index 31ce0c9..04b0f53 100644 --- a/AudioLabSwift/Base.lproj/Main.storyboard +++ b/AudioLabSwift/Base.lproj/Main.storyboard @@ -1,9 +1,11 @@ - + - + + + @@ -14,13 +16,51 @@ - + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + diff --git a/AudioLabSwift/Info.plist b/AudioLabSwift/Info.plist index b8c43bf..5e02b96 100644 --- a/AudioLabSwift/Info.plist +++ b/AudioLabSwift/Info.plist @@ -57,10 +57,10 @@ UISupportedInterfaceOrientations~ipad - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown diff --git a/AudioLabSwift/ViewController.swift b/AudioLabSwift/ViewController.swift index d5158c5..2600b55 100644 --- a/AudioLabSwift/ViewController.swift +++ b/AudioLabSwift/ViewController.swift @@ -15,6 +15,7 @@ import Metal class ViewController: UIViewController { + @IBOutlet weak var userView: UIView! struct AudioConstants{ static let AUDIO_BUFFER_SIZE = 1024*4 } @@ -22,7 +23,7 @@ class ViewController: UIViewController { // setup audio model let audio = AudioModel(buffer_size: AudioConstants.AUDIO_BUFFER_SIZE) lazy var graph:MetalGraph? = { - return MetalGraph(userView: self.view) + return MetalGraph(userView: self.userView) }() @@ -31,7 +32,10 @@ class ViewController: UIViewController { if let graph = self.graph{ graph.setBackgroundColor(r: 0, g: 0, b: 0, a: 1) + // add in graphs for display + // note that we need to normalize the scale of this graph + // becasue the fft is returned in dB which has very large negative values and some large positive values graph.addGraph(withName: "fft", shouldNormalizeForFFT: true, numPointsInGraph: AudioConstants.AUDIO_BUFFER_SIZE/2) @@ -43,32 +47,31 @@ class ViewController: UIViewController { } // start up the audio model here, querying microphone - audio.startMicrophoneProcessing(withFps: 10) + audio.startMicrophoneProcessing(withFps: 20) // preferred number of FFT calculations per second audio.play() // run the loop for updating the graph peridocially - Timer.scheduledTimer(timeInterval: 0.05, target: self, - selector: #selector(self.updateGraph), - userInfo: nil, - repeats: true) + Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true) { _ in + self.updateGraph() + } } // periodically, update the graph with refreshed FFT Data - @objc func updateGraph(){ - self.graph?.updateGraph( - data: self.audio.fftData, - forKey: "fft" - ) - - self.graph?.updateGraph( - data: self.audio.timeData, - forKey: "time" - ) - + if let graph = self.graph{ + graph.updateGraph( + data: self.audio.fftData, + forKey: "fft" + ) + + graph.updateGraph( + data: self.audio.timeData, + forKey: "time" + ) + } } From b874bebb83e231fa7750bea082c62086384b4539 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 27 Sep 2023 10:05:04 -0500 Subject: [PATCH 11/33] added a "zoomed fit" graph for some extra clarity --- AudioLabSwift/AudioModel.swift | 3 +++ AudioLabSwift/Base.lproj/Main.storyboard | 22 ++++++++++------------ AudioLabSwift/ViewController.swift | 22 +++++++++++++++++++++- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/AudioLabSwift/AudioModel.swift b/AudioLabSwift/AudioModel.swift index 58ace6b..d16dfa5 100644 --- a/AudioLabSwift/AudioModel.swift +++ b/AudioLabSwift/AudioModel.swift @@ -17,6 +17,9 @@ class AudioModel { // the user can access these arrays at any time and plot them if they like var timeData:[Float] var fftData:[Float] + lazy var samplingRate = { + return self.audioManager?.samplingRate + }() // MARK: Public Methods init(buffer_size:Int) { diff --git a/AudioLabSwift/Base.lproj/Main.storyboard b/AudioLabSwift/Base.lproj/Main.storyboard index 04b0f53..16a4367 100644 --- a/AudioLabSwift/Base.lproj/Main.storyboard +++ b/AudioLabSwift/Base.lproj/Main.storyboard @@ -18,16 +18,13 @@ - + - - - + + - - - + + + + - + diff --git a/AudioLabSwift/ViewController.swift b/AudioLabSwift/ViewController.swift index 0e4ff4c..671c5b1 100644 --- a/AudioLabSwift/ViewController.swift +++ b/AudioLabSwift/ViewController.swift @@ -143,7 +143,8 @@ class ViewController: UIViewController { // BONUS: show the zoomed FFT // we can start at about 150Hz and show the next 300 points // actual Hz = f_0 * N/F_s - let startIdx:Int = 150 * AudioConstants.AUDIO_BUFFER_SIZE/audio.samplingRate + let minfreq = min(min(frequency1,frequency2),frequency3) + let startIdx:Int = (Int(minfreq)-50) * AudioConstants.AUDIO_BUFFER_SIZE/audio.samplingRate let subArray:[Float] = Array(self.audio.fftData[startIdx...startIdx+300]) graph.updateGraph( data: subArray, From fcbe0b036978e43a3c8a0181354cefd8d16a481e Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Thu, 28 Sep 2023 21:45:06 -0500 Subject: [PATCH 18/33] app store updates --- .../AppIcon.appiconset/Contents.json | 18 +++++++++++++++++ .../AppIcon.appiconset/peruna1024.png | Bin 0 -> 66680 bytes .../AppIcon.appiconset/peruna120 1.png | Bin 0 -> 10202 bytes .../AppIcon.appiconset/peruna120.png | Bin 0 -> 10202 bytes .../AppIcon.appiconset/peruna152.png | Bin 0 -> 14564 bytes .../AppIcon.appiconset/peruna167.png | Bin 0 -> 16829 bytes .../AppIcon.appiconset/peruna180.png | Bin 0 -> 14996 bytes .../AppIcon.appiconset/peruna20.png | Bin 0 -> 1876 bytes .../AppIcon.appiconset/peruna29.png | Bin 0 -> 2579 bytes .../AppIcon.appiconset/peruna40 1.png | Bin 0 -> 2778 bytes .../AppIcon.appiconset/peruna40 2.png | Bin 0 -> 2778 bytes .../AppIcon.appiconset/peruna40.png | Bin 0 -> 2778 bytes .../AppIcon.appiconset/peruna58 1.png | Bin 0 -> 4426 bytes .../AppIcon.appiconset/peruna58.png | Bin 0 -> 4426 bytes .../AppIcon.appiconset/peruna60.png | Bin 0 -> 4517 bytes .../AppIcon.appiconset/peruna76.png | Bin 0 -> 7112 bytes .../AppIcon.appiconset/peruna80 1.png | Bin 0 -> 6009 bytes .../AppIcon.appiconset/peruna80.png | Bin 0 -> 6009 bytes .../AppIcon.appiconset/peruna87.png | Bin 0 -> 6707 bytes AudioLabSwift/ViewController.swift | 19 ++++++++---------- 20 files changed, 26 insertions(+), 11 deletions(-) create mode 100644 AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna1024.png create mode 100644 AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna120 1.png create mode 100644 AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna120.png create mode 100644 AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna152.png create mode 100644 AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna167.png create mode 100644 AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna180.png create mode 100644 AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna20.png create mode 100644 AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna29.png create mode 100644 AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna40 1.png create mode 100644 AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna40 2.png create mode 100644 AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna40.png create mode 100644 AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna58 1.png create mode 100644 AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna58.png create mode 100644 AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna60.png create mode 100644 AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna76.png create mode 100644 AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna80 1.png create mode 100644 AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna80.png create mode 100644 AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna87.png diff --git a/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/Contents.json b/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/Contents.json index 9221b9b..0384086 100644 --- a/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,91 +1,109 @@ { "images" : [ { + "filename" : "peruna40 2.png", "idiom" : "iphone", "scale" : "2x", "size" : "20x20" }, { + "filename" : "peruna60.png", "idiom" : "iphone", "scale" : "3x", "size" : "20x20" }, { + "filename" : "peruna58 1.png", "idiom" : "iphone", "scale" : "2x", "size" : "29x29" }, { + "filename" : "peruna87.png", "idiom" : "iphone", "scale" : "3x", "size" : "29x29" }, { + "filename" : "peruna80 1.png", "idiom" : "iphone", "scale" : "2x", "size" : "40x40" }, { + "filename" : "peruna120.png", "idiom" : "iphone", "scale" : "3x", "size" : "40x40" }, { + "filename" : "peruna120 1.png", "idiom" : "iphone", "scale" : "2x", "size" : "60x60" }, { + "filename" : "peruna180.png", "idiom" : "iphone", "scale" : "3x", "size" : "60x60" }, { + "filename" : "peruna20.png", "idiom" : "ipad", "scale" : "1x", "size" : "20x20" }, { + "filename" : "peruna40.png", "idiom" : "ipad", "scale" : "2x", "size" : "20x20" }, { + "filename" : "peruna29.png", "idiom" : "ipad", "scale" : "1x", "size" : "29x29" }, { + "filename" : "peruna58.png", "idiom" : "ipad", "scale" : "2x", "size" : "29x29" }, { + "filename" : "peruna40 1.png", "idiom" : "ipad", "scale" : "1x", "size" : "40x40" }, { + "filename" : "peruna80.png", "idiom" : "ipad", "scale" : "2x", "size" : "40x40" }, { + "filename" : "peruna76.png", "idiom" : "ipad", "scale" : "1x", "size" : "76x76" }, { + "filename" : "peruna152.png", "idiom" : "ipad", "scale" : "2x", "size" : "76x76" }, { + "filename" : "peruna167.png", "idiom" : "ipad", "scale" : "2x", "size" : "83.5x83.5" }, { + "filename" : "peruna1024.png", "idiom" : "ios-marketing", "scale" : "1x", "size" : "1024x1024" diff --git a/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna1024.png b/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna1024.png new file mode 100644 index 0000000000000000000000000000000000000000..3aa7026817aae4d02cb98e8d0f03400bb69bdd95 GIT binary patch literal 66680 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 LACHQIfmi+mZV<7o literal 0 HcmV?d00001 diff --git a/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna120 1.png b/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna120 1.png new file mode 100644 index 0000000000000000000000000000000000000000..582eac83eecbfdbad465a242f3782b95ffdf88ba GIT binary patch literal 10202 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 literal 0 HcmV?d00001 diff --git a/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna120.png b/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna120.png new file mode 100644 index 0000000000000000000000000000000000000000..582eac83eecbfdbad465a242f3782b95ffdf88ba GIT binary patch literal 10202 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 literal 0 HcmV?d00001 diff --git a/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna152.png b/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna152.png new file mode 100644 index 0000000000000000000000000000000000000000..57da765f4e94eeb37c586040ce6a9886bcbf1b2d GIT binary patch literal 14564 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 literal 0 HcmV?d00001 diff --git a/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna180.png b/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna180.png new file mode 100644 index 0000000000000000000000000000000000000000..52575264574c4c641da1b9eae80bb61e1a06ac5b GIT binary patch literal 14996 zcmb_@b9g4t(&+of#>U1byRpqTwz08o+uYdPXyc7-+qP|N?BwQmzH`sH-~YGg>8YBU zLRa@ZJw4M^Ve+zKh;X=Y0000{LR?t!6W0AxVW2+GyyH+zp8)8nC?*J~n7}{&ya+N@ zlQ5By0Z@J7FaSv47XbJ_ET0Dehzo%H7Y+bO0`dNXD*`G0%>xDigqi~&{>`KD3I8eL zpY>Dv-w-?p_+O1VVE^H+%K`r{{*PF}<%`EBfVC4>cLV_7(f+AGKzauDr#5wSWi=-? z8EGyPd|6*~n zltE(%WD>I#~ zgDE{DCnqO80~0+H6YVDlt)sh*lfE0RjpNtas0PipAORhbBCUhj)DF^x<5sE{-JWoIhY%NTK-2rA0yAd zIR6*z-*$ND|1tjmH0FOi{TKDqRX#W#`u}tr9~_R(HZ%ahAuJ&*pzH=b_l9-T>QAzn zZ*Qzu+qfbfj&IWw4De5d(d_XLCB`$65d6YugcCw`=^aCsuxIQ(OqPb#HyVT`c#rt) zhBsb1gr4kMFT50tzCBu3gB1ja9z&m7oRP#vSA-Sq`oqG@M~P=a^4$7KvW82k^QevU zrl%$E-C}c*Y$g}EFt#9E5IPJciXw47NZcPb7nFdq0AWmEf~UB(rwD>2Q77#v-+CA> z7dR+*lVw>cGd-Z}NmYsmvsS?xL&VLx%t1QgRM^}$OQRT(wd;Ueix#OofkzkPNiOAQ1 zoxnUX@k%ei>qL=_Eq4}*k?oBa$()`&*- z93oFBFQLE7?$uwp)KIQFzaSdV8f5@`V_sV9OVsE0(b`z^UJk*Qu$w@gbL@3bTdet$ zCS{O#-4eJNZ)=cg(a%J$zCPec4{_^O?0PZ-h9%0DB)Opt0Rj&Igw|>O))JG1=Koky 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 literal 0 HcmV?d00001 diff --git a/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna20.png b/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna20.png new file mode 100644 index 0000000000000000000000000000000000000000..1435f184bf6187f8d0da99484394a714770869ed GIT binary patch literal 1876 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>>x%Bs2U~)hW1Pb7O0viAPoW@Kn&6Yp_vyjB1~Ms z1XnGxfEmFCX>)u&_aBhrEbxddW?Q&?xfOIj~R9FF-xv3?I3Kh9IdBs*0wn|_XRzNmLSYJs2tfVB{Rw=?aK*2e`C{@8s z&p^*W$&O1wLBXadCCw_x#SN+*$g@?-C@Cqh($_C9FV`zK*2^zS*Eh7ZwA42+(l;{F z1**_3uFNY*tkBIXR)!b?Gsh*hIJqdZpd>RtPXT0ZVp4u-iLH_n$Rap^xU(cP4PjGW zG1OZ?59)(t^bPe4^s#A6t;oco4I~562KE=kIvbE-R*^xe#rZj)0j_Z5dyK)8w<}^OB;0zR9c0(J-{PE`I-U&P=@}*;)s=jjgNC{oMC??tDoF23B^9vpcHt|3$vi-(srR zy?6Pf(E26&f8KXw@D2zGE^vLmV6K4ILW|@kCiT}U7FMeh)6{0Xw|SUdq_>83cYF9I z<%O&o!5;V7IFfmG>PS34KX0;s^3osD(l_Mgfm(zf1?9|URGzoA<+b#t$6_nq3x-@! zsM2Dngc`N|Kh6zUdt{JMO4eNAh?7HR)^GB@&8e80JT#^3a5cjOvs zr#?Q{mSQRBc5>1izJ@Z}wQa%OPvbXSEB>`q@5*)Qo`jPH*I5qFyVTjX^}5xRcN{lY zHAo*;S84J!5<12v6qFJ&At*0t_8IrNr?=}_)-A1BJnv+`@*|7uUs;;-O-`tpF4~>f z+@{fUk;f}8pN@w)6 z<`-(;6PT!3vTn_cgtB$F%50QWBvKy)&MPo{<*hq=ewhPr?qt!R&WT2?Hd@yiO{Bi; zT=(k9_nhWc^XvV!|45vi*Kx{mMnaq-y%NA@INctUKD%nxt9~0@9Fo4 zn9jefyDf6?_N^DI`31Tzy{};3t!;lt@ld&Zx~a^Djay{)Y)Cs(S@&-%v+T+P;)j^x z@9@vFeX#J=+3dQ#dp#)D-^X2_H6?4@q_T2y8b}JxXt7Yk~+?T;}S z8r@=9x$o%7r)g*6{`}*zT*T6vvGU|*`3+i+f-j$2Je^(4%bH(V_))viyvOT(C;m+- zGz*yV+_kpdQ(tdMu_WWa^~$PmS=Xici--RW*6-4-+x@MI|A)?jMH;z^_6mRYALeuV Z#Xmna=XR>-wEdvE!qe5yWt~$(697Mru`vJu literal 0 HcmV?d00001 diff --git a/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna29.png b/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna29.png new file mode 100644 index 0000000000000000000000000000000000000000..cf501765079f73564563002c355b7b2420c69e15 GIT binary patch literal 2579 zcmZ`*c|4SB8-9l@5iQ74jxiVxznNhW#>{+{v1N+}GeVXbV=`N2G=!2Y)hUvMMCizp zN*iCuQj%n=1|cn+tlt0Ho|} zajyJTSun*#`EU0W%2xieiDK?(4ghzOB$xa|_X0FV+Oh{m^oy`Wt`Y*&!h z8b=5K5~#qYHI65L6%1Sc6lhjg;T+I<#2ld?Y~>u`bz2~VnMVkk+sv{hvH?I+MleCZ z_phhf2>vW^4xy6yA9AHGy|uD7VjK8^?oj)v zVPqNsN2QY)Y(X!3bkT@^bo?x|WYU-+`&oVgWGp|=iev-yGkYZs@!!&HaI6W@2*Fuz zIQ!L$wSw|*7FGg{_;E|I5_9nrTmTT&u)~?-ZKWQ#d(-e%vRKOCyFQrgi6fHZj5^U> zF2(hwzQ?266*M4qn;;80SjB4%Vo`cinsqyl`_EXNLp0RO^Td?w+BXH%o)V?HTvB-e zhMalD7%%h9ZJ(HU#BG>-+h=GG16ib#xPz@%zC;aiCp0SJ{Z12jNnQ8f{W-KGcdsC3 z;aZV?S^1qu#nTSYEr}1OKkV()%tT#-40#aCQKgef(4I?MI?D~SZZW;@C+|6A!k(51 z9#UK^EwMkTOsw^i^w$ISA8zys$K<=fVDP!|)@8RET82bWVh1!uvnNk(2=hY!z5Nx` z%dFgvc)C#S7oE~ZlS{TwB<@qENIF;nF4HWYMuicEi>!GFR>?Sx0Ple|e~rQzxov^ZWp`^|Ze9g^>H! zR@f%>1DU(~0~&h1ijO@p6plEkEfbhR-+EVt_SFzk0@bgNcdl=+VdIjmhYn${R*zV1@V&mEj+@Z@5BaDX)GeZ~q6wsxi+XC1J25RSO zq&0htD(f?Sa|xeKo1NrHve946lr#ra)!(SvkhHC%AMosS&!x%`7Uqo{xkqN^9BPg8 za&f(Vg}*0B5$ms}+WJSv5ZmyrmfbnN(U}RXEsO2tAeorP*ye4n(FyxBmQwOh-+i?m zFKodupgl%p{uahHSkCw*mX+o|id4K9adsZGO)NXtW-RN?xdE1ceVB+=&}Q$4uPp5C z)#XTcg?n$t$+hm^n>0*b@m}pHe^YHTQ zVt97KW0M`uhCJ)HD&SqFDuq_5(m+fsZ{Jb#^y+BIk;B2qG80EFk4YGpxq>_PR)VcZ ze--oR>CPKU99HegP~N@l!Wt%JX~uR}w2oS-jhH96AzFi5r(%~?iW<2uRIDK>-z3xD zOMr{ZKOos=W$)GWw{<1{#%ch-RUyHCbqSt@&S#$tOP_2vau}u%fAa7vG_NhpXx(8tG2hzRDq*E-Ab2; z-0N%~lbau!@lH7Co+c3a3rxeYr!=b3*@$#>5>Z-%PhJ*X)T3^eZHl>>UoPqL{P+`v6IdBt^dL@JQpattSRK};#qZ*|UeoC(#fmE5wthDu z)=Ts{E4;z&+sOH=H6IAo&-J0(3qKGYlMN;+LPdfPioG^Hb?V;9 z%P%!>l_UMU&5Flz&C_P^vgF@iKUJK(am<*vs|&v1jyYFuw-Zbm%POXZ-NYrg9dEZQ zhto^45K38i`RfIRQB&W^cE=0+7kSy;TDj7R3y0CiLT< z1xi5|MFQAGWaxl*2m4{6belIiDkqnonN{A@pNKtDdX|ze^I+Qp1@o S2A2iDI(Al0xLX#!G5-Vj@D=U= literal 0 HcmV?d00001 diff --git a/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna40 1.png b/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna40 1.png new file mode 100644 index 0000000000000000000000000000000000000000..bde2084949044b63fcf97d40fc6039ef1757d67d GIT binary patch literal 2778 zcmY*bc|2768$M%+?E6;2P()_NzJ$qc$~F@<)KQRED1s(Z~ zQ6Pn193}ur_61meaU2=>U|BGNq5hSbNuYlnNld@l3MA%#@qsLLlCy@94hLE|-~fPA z@E}0IoqREdn}e?%8jrTJ)b+pyK-{ii-8~`U0f7f9KrdXE0RueoZqnfa{urEYxIXwN zLzjUM%22TMPYT{oAB?uLl{Ur(drE6UU=SGCfJ0hZS}*vDmoCc0?3bLe(+B(D@qxNf zXjoVnBuov04fckr=;-J`VX9D7Rb>W485e=UyM-%baI*iH{MX0C6Xy}^8;JMCVx$jz z-Q2MuczrPVpwaL3&pGkFUjKH&;C_w87!Z2!1gZjoL4SKQq=PpL)>+Sjq&l*5GICp=);3(KYwR);lkXg zuw6@(yvj5=%tcE>?gf}-YCJE6FQETnMOpl4Gn0J!WJe#g-Ie(zxnMG1sSaq#436Yzk}#{O_18s>n?wUA}pjv zBN~R^uzAt_-WQ0#S8A*~ln;1+tN0`d{3s!QQ9{bZ+MQ}zX*@?SEw@TDwalu0gq zU#+!-uo$}5h<8FT_31OCbf#L4HJHP`OjA{3H{ zeJksJVOr5lFCJZ-Lt5ikjjr#V4#6$|uTF$i_&hryP-RxT)y6+6jc5cBkDz8g? zOtWqgZ!xCg=T zcJSR0X7-@_H1a&F>@~~bt76{O^)V9F z)JBc3=j=6pETwC8KtnX)$>guB1Y{WwLqS}(!fq9>-W#A0IQDq@ zkDe}lC_FH%4k^jO4`!`tyxsi62b96-qF2pw^c_NgmX0*iTtT+zYQK_?ye$#;OujTJ zm~Zd$1AKnLi*j?P3F(684mXXn=M5aKbU#RmZFe`Ug%2PVcs0!twEOV@VM_f+9&zOI z7rvtp={}7&(E_e)7r#s25kAi;D0^)=Tg^x+SECvGb~E;l+BA;G*f?cs10^X-ia@h_ zi_FmMvnaC(GLva4rao)shewh<{$vV@CVa`YI>i(kPKR-99TPwe&nj1MD<3_*!@;)? zpW}a*zgi;mIlNANXVGY9II!)u3#VE3j8TYa!~W@_wrw#5rTOF9@pHub<;0$>TgKGF zSJPu4jM(Yg)Fnlg$kRekx0qqC2U*M`P&t$^#pe2yt~c*Gj*CfMAq7~ZpV7_MklcAB zb$WmD8Ua<=zK0ry1<_rA|s->6>-e&KVL+Ff&{OvNi=XY}1~mvRh7R=SD^M z#4q+X9Bv|LJ9~djkGS!^Fs6=Y#mjhHSb)~*E17yNcOX`9!6I$!-rylaF52+ti(+R; zAG~0f(3htBPhblQyFxlm>sE*F_-Jy+E(x{oY3X&Zu|m1Zv9{HJ--iWcZ{w5a zB{q7HLj%V)hw7grrJhRe%6)nNNd7C!sifkkQ|Tqqt;G*-L}PN69UZwdF6(qaX2%nk z>i0b6_)hWRb(ne-{KB+n+@?YqnTj%XCJ*232;O0nBN_Sade zmfEZA6$rTRL6Ls5IElPI!6KG&U8_SP(NQS0Rm6TE)gbACRae)(k*iP(@|%|K$XLQF zog#7Vlr5h}U%nVLO3Z~qqAR7YT#**Y-@{czf{v592`4)~$Pi=Ynas~1S9!E!Se2yC zj6@Y(D9b~->-&vW{ix*naRgqww#>gv7)hF&GCTbI^`)zJFH;jq;|@mA(VW#3t>vRH zo~YHXJqU=JcO@$Db&)ULkH`~gSdi`A?2WcdfVOG6YL$&R=X`JbQI|ir=~Pgl5!`@9 zSf+t?ER=L!e$$NbpJ2g7mAU!&AfE z(UJ6qfVdm|tlie$l&T=%L7HE>V@Diy{ggIxWAeQJU_R-{>Z1P^&ix!sA#ehyUvT)N z*CKi>p}JKU+P1q~Jc-zx&fL=I<%JXlIpL>U=ADw$;DI0b&m+n=R3M_p+m7)IqaMGF0q^5}mDrVT=xdA8lEx@Pmgi LwKl0Xx*Y#EiXhaF literal 0 HcmV?d00001 diff --git a/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna40 2.png b/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna40 2.png new file mode 100644 index 0000000000000000000000000000000000000000..bde2084949044b63fcf97d40fc6039ef1757d67d GIT binary patch literal 2778 zcmY*bc|2768$M%+?E6;2P()_NzJ$qc$~F@<)KQRED1s(Z~ zQ6Pn193}ur_61meaU2=>U|BGNq5hSbNuYlnNld@l3MA%#@qsLLlCy@94hLE|-~fPA z@E}0IoqREdn}e?%8jrTJ)b+pyK-{ii-8~`U0f7f9KrdXE0RueoZqnfa{urEYxIXwN zLzjUM%22TMPYT{oAB?uLl{Ur(drE6UU=SGCfJ0hZS}*vDmoCc0?3bLe(+B(D@qxNf zXjoVnBuov04fckr=;-J`VX9D7Rb>W485e=UyM-%baI*iH{MX0C6Xy}^8;JMCVx$jz z-Q2MuczrPVpwaL3&pGkFUjKH&;C_w87!Z2!1gZjoL4SKQq=PpL)>+Sjq&l*5GICp=);3(KYwR);lkXg zuw6@(yvj5=%tcE>?gf}-YCJE6FQETnMOpl4Gn0J!WJe#g-Ie(zxnMG1sSaq#436Yzk}#{O_18s>n?wUA}pjv zBN~R^uzAt_-WQ0#S8A*~ln;1+tN0`d{3s!QQ9{bZ+MQ}zX*@?SEw@TDwalu0gq zU#+!-uo$}5h<8FT_31OCbf#L4HJHP`OjA{3H{ zeJksJVOr5lFCJZ-Lt5ikjjr#V4#6$|uTF$i_&hryP-RxT)y6+6jc5cBkDz8g? zOtWqgZ!xCg=T zcJSR0X7-@_H1a&F>@~~bt76{O^)V9F z)JBc3=j=6pETwC8KtnX)$>guB1Y{WwLqS}(!fq9>-W#A0IQDq@ zkDe}lC_FH%4k^jO4`!`tyxsi62b96-qF2pw^c_NgmX0*iTtT+zYQK_?ye$#;OujTJ zm~Zd$1AKnLi*j?P3F(684mXXn=M5aKbU#RmZFe`Ug%2PVcs0!twEOV@VM_f+9&zOI z7rvtp={}7&(E_e)7r#s25kAi;D0^)=Tg^x+SECvGb~E;l+BA;G*f?cs10^X-ia@h_ zi_FmMvnaC(GLva4rao)shewh<{$vV@CVa`YI>i(kPKR-99TPwe&nj1MD<3_*!@;)? zpW}a*zgi;mIlNANXVGY9II!)u3#VE3j8TYa!~W@_wrw#5rTOF9@pHub<;0$>TgKGF zSJPu4jM(Yg)Fnlg$kRekx0qqC2U*M`P&t$^#pe2yt~c*Gj*CfMAq7~ZpV7_MklcAB zb$WmD8Ua<=zK0ry1<_rA|s->6>-e&KVL+Ff&{OvNi=XY}1~mvRh7R=SD^M z#4q+X9Bv|LJ9~djkGS!^Fs6=Y#mjhHSb)~*E17yNcOX`9!6I$!-rylaF52+ti(+R; zAG~0f(3htBPhblQyFxlm>sE*F_-Jy+E(x{oY3X&Zu|m1Zv9{HJ--iWcZ{w5a zB{q7HLj%V)hw7grrJhRe%6)nNNd7C!sifkkQ|Tqqt;G*-L}PN69UZwdF6(qaX2%nk z>i0b6_)hWRb(ne-{KB+n+@?YqnTj%XCJ*232;O0nBN_Sade zmfEZA6$rTRL6Ls5IElPI!6KG&U8_SP(NQS0Rm6TE)gbACRae)(k*iP(@|%|K$XLQF zog#7Vlr5h}U%nVLO3Z~qqAR7YT#**Y-@{czf{v592`4)~$Pi=Ynas~1S9!E!Se2yC zj6@Y(D9b~->-&vW{ix*naRgqww#>gv7)hF&GCTbI^`)zJFH;jq;|@mA(VW#3t>vRH zo~YHXJqU=JcO@$Db&)ULkH`~gSdi`A?2WcdfVOG6YL$&R=X`JbQI|ir=~Pgl5!`@9 zSf+t?ER=L!e$$NbpJ2g7mAU!&AfE z(UJ6qfVdm|tlie$l&T=%L7HE>V@Diy{ggIxWAeQJU_R-{>Z1P^&ix!sA#ehyUvT)N z*CKi>p}JKU+P1q~Jc-zx&fL=I<%JXlIpL>U=ADw$;DI0b&m+n=R3M_p+m7)IqaMGF0q^5}mDrVT=xdA8lEx@Pmgi LwKl0Xx*Y#EiXhaF literal 0 HcmV?d00001 diff --git a/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna40.png b/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna40.png new file mode 100644 index 0000000000000000000000000000000000000000..bde2084949044b63fcf97d40fc6039ef1757d67d GIT binary patch literal 2778 zcmY*bc|2768$M%+?E6;2P()_NzJ$qc$~F@<)KQRED1s(Z~ zQ6Pn193}ur_61meaU2=>U|BGNq5hSbNuYlnNld@l3MA%#@qsLLlCy@94hLE|-~fPA z@E}0IoqREdn}e?%8jrTJ)b+pyK-{ii-8~`U0f7f9KrdXE0RueoZqnfa{urEYxIXwN zLzjUM%22TMPYT{oAB?uLl{Ur(drE6UU=SGCfJ0hZS}*vDmoCc0?3bLe(+B(D@qxNf zXjoVnBuov04fckr=;-J`VX9D7Rb>W485e=UyM-%baI*iH{MX0C6Xy}^8;JMCVx$jz z-Q2MuczrPVpwaL3&pGkFUjKH&;C_w87!Z2!1gZjoL4SKQq=PpL)>+Sjq&l*5GICp=);3(KYwR);lkXg zuw6@(yvj5=%tcE>?gf}-YCJE6FQETnMOpl4Gn0J!WJe#g-Ie(zxnMG1sSaq#436Yzk}#{O_18s>n?wUA}pjv zBN~R^uzAt_-WQ0#S8A*~ln;1+tN0`d{3s!QQ9{bZ+MQ}zX*@?SEw@TDwalu0gq zU#+!-uo$}5h<8FT_31OCbf#L4HJHP`OjA{3H{ zeJksJVOr5lFCJZ-Lt5ikjjr#V4#6$|uTF$i_&hryP-RxT)y6+6jc5cBkDz8g? zOtWqgZ!xCg=T zcJSR0X7-@_H1a&F>@~~bt76{O^)V9F z)JBc3=j=6pETwC8KtnX)$>guB1Y{WwLqS}(!fq9>-W#A0IQDq@ zkDe}lC_FH%4k^jO4`!`tyxsi62b96-qF2pw^c_NgmX0*iTtT+zYQK_?ye$#;OujTJ zm~Zd$1AKnLi*j?P3F(684mXXn=M5aKbU#RmZFe`Ug%2PVcs0!twEOV@VM_f+9&zOI z7rvtp={}7&(E_e)7r#s25kAi;D0^)=Tg^x+SECvGb~E;l+BA;G*f?cs10^X-ia@h_ zi_FmMvnaC(GLva4rao)shewh<{$vV@CVa`YI>i(kPKR-99TPwe&nj1MD<3_*!@;)? zpW}a*zgi;mIlNANXVGY9II!)u3#VE3j8TYa!~W@_wrw#5rTOF9@pHub<;0$>TgKGF zSJPu4jM(Yg)Fnlg$kRekx0qqC2U*M`P&t$^#pe2yt~c*Gj*CfMAq7~ZpV7_MklcAB zb$WmD8Ua<=zK0ry1<_rA|s->6>-e&KVL+Ff&{OvNi=XY}1~mvRh7R=SD^M z#4q+X9Bv|LJ9~djkGS!^Fs6=Y#mjhHSb)~*E17yNcOX`9!6I$!-rylaF52+ti(+R; zAG~0f(3htBPhblQyFxlm>sE*F_-Jy+E(x{oY3X&Zu|m1Zv9{HJ--iWcZ{w5a zB{q7HLj%V)hw7grrJhRe%6)nNNd7C!sifkkQ|Tqqt;G*-L}PN69UZwdF6(qaX2%nk z>i0b6_)hWRb(ne-{KB+n+@?YqnTj%XCJ*232;O0nBN_Sade zmfEZA6$rTRL6Ls5IElPI!6KG&U8_SP(NQS0Rm6TE)gbACRae)(k*iP(@|%|K$XLQF zog#7Vlr5h}U%nVLO3Z~qqAR7YT#**Y-@{czf{v592`4)~$Pi=Ynas~1S9!E!Se2yC zj6@Y(D9b~->-&vW{ix*naRgqww#>gv7)hF&GCTbI^`)zJFH;jq;|@mA(VW#3t>vRH zo~YHXJqU=JcO@$Db&)ULkH`~gSdi`A?2WcdfVOG6YL$&R=X`JbQI|ir=~Pgl5!`@9 zSf+t?ER=L!e$$NbpJ2g7mAU!&AfE z(UJ6qfVdm|tlie$l&T=%L7HE>V@Diy{ggIxWAeQJU_R-{>Z1P^&ix!sA#ehyUvT)N z*CKi>p}JKU+P1q~Jc-zx&fL=I<%JXlIpL>U=ADw$;DI0b&m+n=R3M_p+m7)IqaMGF0q^5}mDrVT=xdA8lEx@Pmgi LwKl0Xx*Y#EiXhaF literal 0 HcmV?d00001 diff --git a/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna58 1.png b/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna58 1.png new file mode 100644 index 0000000000000000000000000000000000000000..c744e9ef17a00586af4e0bfed40991576316f597 GIT binary patch literal 4426 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 literal 0 HcmV?d00001 diff --git a/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna58.png b/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna58.png new file mode 100644 index 0000000000000000000000000000000000000000..c744e9ef17a00586af4e0bfed40991576316f597 GIT binary patch literal 4426 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 literal 0 HcmV?d00001 diff --git a/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna60.png b/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna60.png new file mode 100644 index 0000000000000000000000000000000000000000..a7545655c167c0125dd7aed3fe63f5364e4c65ec GIT binary patch literal 4517 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 literal 0 HcmV?d00001 diff --git a/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna80 1.png b/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna80 1.png new file mode 100644 index 0000000000000000000000000000000000000000..7897a05ed513d7e8a318457100fca30f2f80cb85 GIT binary patch literal 6009 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 MFRcQpk~9wdAHI0$+W-In literal 0 HcmV?d00001 diff --git a/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna80.png b/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna80.png new file mode 100644 index 0000000000000000000000000000000000000000..7897a05ed513d7e8a318457100fca30f2f80cb85 GIT binary patch literal 6009 zcmY*dWmsI?!yR?JZnQC_U}%99#rFMX3K~2tMOKVRmZD zzbUS^BGfv{YLrrr&K8usY#eMH)S~E=l$65G=9YpQ5Sf3{pLZhE)~>EFL3VZz4-YmE zZZ=0}D|Su+0ReUnE_N<1)@KH*iTa&Y-)tmgsQ|9oNRWaD7}SNl0t_)k<&)fsB>T=|c_D5vn> z%>Nhrr;afDpW^?gng2}scl3EwQFLMUe~(QRoyc$<6#&3zl!r*X^+Y%_@P--u^cCG* zX{$)3)u&6TuSm_IZ5XX6r=2InM&pMk1W%xA3~?NBBr1iT$JP%jX>wV}C+4B0H!6{! zSP~`>5vOf@Lo=V^*fH_4uCbuXRoR-DBh+`)ovXV~aKBn{x%J)sD_Ki#aQuBG%uTq$ z`>&SWQ=!#|tkr`|TPn7o!jL$<`UOs^&x%V z$+1QJ4yE~Jb@U6@lK%C1F?iGY-{o@{x@rdU_AP$=u6=71#?1Hmu*&8`+;|Yz4*PID z4f0!$dT3y_9Uo@~Cw82cpbdZMz?2_8nov4-NovRp0$!V;LW>=AY)pa&Hwu*iW8baA}yaNbxn&(OHCxvkpzCX|*cpJ%L1- z*%QagqvLxV)n+OMV3aB~<>g0QjIIK|Q)X}sg^-CnFU?apR&6MGtf~c7vt6rr6fsE~ zk7$x#BB%U}+P=YUwhyxy)aOX0l9dcct5jtgC!5WdB)Xm&@b=R1CU+n+NGzIaspXq` z5DP0%ZX$kB_`WKHg^qxkDq2Qt_H!j6GMfecmPD(Xh<1;U2zc)^k=-f|!3W#g`d;W! zwA4vclH#2cr8b%akQ`Tu%6Kfdd)l0zkT66Au1>{0n;f9%O{2Wrty!IClNTA*WzPEP z4d^LE`N;Hatj5Zt@DxQ~Z~Ny%n^7?-Xluy4;U;<0hAOW864HA0QvM*eFGRt31t}7c zbb%5AxqS~Ct0%+5$CvfY9keZS#V1E-Fd6U(zc)u|1nUTVK@ids|u zX{9x)g7$H}_a@)EUwKqB$y{Z{k$XO7Ej}Y}sDl}XR!&YoH7Wz@_O=*RFE^gJP-RF#yMNVGQCR?adt5+gpiR{}Ei z%0|sfgrq-36wz^1t`a$-5jJF>AaGC?G8OL%Fh>1gTIA4*)F}pb1-jp*@+RYlUT7yw zc{fA!Xszl^?{w(}XgJeD2)Vlq%WINmnp2Eq8u;|M4pfrTUWrT=_bY@vM8B5n*SL;8 zo6fqBB15IO$)fEU%unQr<7O_vDzDGa9v28JClRx-^}v`hC9ntEz6 zWHcpuxX|J=T!$VADt=@SAUHNO6~I@+MQk+U?fRs`Bll_oqS4EG+=}l@c8qLl_CcDW zOYVnnBSD2)Xh{fm@%Y3$}Dhl02G9NLy{9_U8FT+389 z`3u=EG|=s9bBR#J(A#+hhq8LIs0*7>@w-=1Vl)MW0I&@2PoeHRkr{Eq>J?-~ehZx` z4kd|FViajJPpS|OFw@>nlr3!+f@zoHy~ncn9cCdR&>XZNPNm+)C)=9yO|g|iQm>2r zcL32T_vR*hnz~xVA{F4JBtn_;g>9FG&;;VyYZeq5=E0U06*fEh-Qcsw&+fSo(jQnm zKpCVn8(Ugf?QVpSuUe)zrbuspZ4SAerZmu0T1=?9O(@~>sOVw=zDlrj8afDk8hq#H zp^40jkApu2A5z9ADOk=@WZm_{-XK010ZfH9h~dJY1>m1 zG;W<9gcu`);sl42e5m+jV;XnC@JTYvjk}(@rKYOW_YPWV$cQ|QsDw92scaq4WFIxP z@3@S}SKB=*n}^Ack;EtBFs=`f!e4Ar?t5A8~pGItu zhKO!>qb>W5k&0C+fM};rH&MF4CEh|iDx@Jk=$uiE!BHp5dLTA}d1AoU2oypI329ESZ1${OH^HseNFS++kW=*8mX%0>|= zmL33|3T$KVQGe?K0c_Mra2`mr*7Bz-WtAg2-V3f$B4N7E7E!;!4+lRxi zlky!mTlRv6u??W+RQBwj9ZY({-n7*HP_P3ZW8Er2Jv{!uL4XD@#4xaDi*_%^_W zqFRc4SFg>PPKZZ9_9eyQ?T^66jFZJQcA|#e+c5eDda9 z#dJ^3f$>=g&CvIINgbzpR!x^d%Gsa?Xe?SoQ)A~t#=a^yz&r#1*&%9vyU z%dc~zto_k)pt;}LFgL>SuI8+to=88n@sM--Ql4=&1kVqZW`X-Pn+ckP=r53mTYcy6 z0zyJ0u^tI-!~vDk^mSAzsylq$ljnLgxWB04tCP-Z5o5D>vFMqP4A37T0PzzXcU;5NFmC{6egfmA>}#L zJlM)M+}(09zYYIjur6=FsA%aEeS;KZmdJb05{OFl@TK{!_RLk2So{5icY&z=E%bv# zX_|&#;!88h@Ugj*pz{X5{X)8I{I1*>_z)XJ<_1O?pqS#KL3s;}$_4x*(8TPEAj`Bu27;PS{hrvO+fI2Y}3!53V-+Jp^G z0@a}?B#i5zcGT(_iOo$)Pxk%v8lxRiu~H;W8Wu!xI4qmj>HVPh>nmg1$IWd{Xvki? zi%hAy$zl$#NmEj+Xcog*j}^+mC43nil4OW{;s-t%gePUm^l|S+RmSSi`xWt9*R;I` z03}V0uYg~X`}`Y|NPE}JM}>h5ZdB3@ufWrnfBiBG7U8eNp2+f}4R0?IGkR0M+f$>_ zBta7^a8kJTKAxVXkLC0@^7AaEV07jPdcYy9gyI*DiRuGZ-Np;h#v(W5^oEokMaq8Q z>rvFE`pzJ9*q!&o-Z);b0%Xf+WRpCNUE&?a0~Na?X{n7~q=s3ZIc683V4?3@&Lz%p zbweiPwjfk1k9fp7XP0E2h}y|Zp!=#b+zcC%VR!M+<_}Nn8pk#$?lw1Imfs|?{a{LD zh1umwoUFAPBron)f}5_%wgoBUti4~U-e0^B+|SQcDLL*wJ1fZq_=Q*0ux~XmVB8Kc z1$;APZFq^|jX4|+B-X`wk#@jbPrT`odBRK@nd!=D)>uRgzfe)T#NjV{qfBHn|BF?0 zr`THy!Mr33Z$bVz_|;ZCYDaU6BBM#{{9hw|xm}TZX^C<#6&Hy$R}S9zl2vb#&l%NQU-NrO7~w-Wy*Zr~Nh%q)bE6p|RUHrW zwE&>5Zu^Fc(97)0LJuJ<8aFD~SqxJ+3BaU}@K(-x25%b+nJ;G8&!Y_g!(OSv#&w!` zuVt}$xAa6t2`kk8+saR6ajsB4@*OlLCE(747pkQQ?4z%q=TZ3b8(<3>*sK^nUNT}- z4j(4S985ptJ0RV|+y3HzA^y+x(=j`MU>(``^F0E|mSGyg>YGtqVE zn>ln-5f2H#fN8nW*w#9P<%<)sYv=<$gYst^84FBH*+%G>Inl(Z122P-6^ATY(3U2kcmY~6OU}geH!nw8aiK1n$|O_oc{Iw z%0*lmfsR4C$u@q-U zq1_f~ef^4=w>gHYl7XWxJ72PPNX4m4)0(hlW5ONGKfoS|Jvwj(oRiYQ?@wB!>!z?8 zSls*k-Ys_S3c#k>ve!{y(@Sew1tEtT%;ylD!7FbXvlm7WY3L|>Or-aa>M4doV~m6c zE1^+=006+cVc}67=Tua63kdE>PE6#Eak3szYz;rQN1+$R;Na~_j#MCZck=5|7lQ#5_AU~B700ls9*MJvplmY{?#QlIk(mUK4^dO z7rA=;bVr;Eu$kf;QdbDCKmRnMnM?%IhS8XlkDOW@i{JctLIm60&EexHMt}s5hcHzR z@!7^Pm!S@5TLNIcKpCOj$ios`0YT~mTCJ}H3|4bf2&((pc*)J@1aEZg6lz>aWSidn zk|0&bkhMn|ulGyH$n5j(ikS}?JB|+Cq5WR7bfOtGlSIb4T*&&BzwoX9R2@y)y=sl{ zFffK2iMmuTrg;q{sTa=cZ%>zhO5|TlFL@Q1u8+I95>z20C<_-{l{w+Ev~icW@B+rY zHPhlLEEyEbjELqHt%Zbkpx0=iCh>?qe)-%ygKCQrF(dDjr6Pd-5Yfo#I%%neTfnz( zy+te>lblLRs+6$ilz@EZ7ryPU~XnbGBBwQ7RP;K!Rx3Byzv zAbTD#wc$vBW`)mR1&H%nQk5C0#_%E!?Tzfy-oEa|-%+^n9Pm!a-Cj@@${|S921qjv z{Mz4&jyg3Vz`E-2zaHKLU*k`2|tEB-=8!Knc^v6JJ6W_vpj%auv|Hz`k z5O)CHv1cSW^u;>ff+TK;c+bO5S&833Eo?1f*F6Q!;cZwGn%$&ZeJc(cV~kD~`I?&(4X=}G~=$)6fo9i1nF zKl)JbT(Z+SzIguE$|rW}XaH>SkEEvD!k6#lKHm5Pz`aV>S<<98zmKyE07*s+}v<>sS=tX>j9l0oefFNr&$)MZtENw(Pt%aLfKA z=yb~I8W{Htor2ojo>5ou>SXi_j_)ybV(Gc_y|(*!w3g$>6F$EmPpkT-zxf@mej!A{ za+5T5OWC_v{Ce1Wh)%j8P((Tv%bEF#| zw)FrM5J#(Ny67nc*<2?Kjw++0cBSqYh@olq0yUPpq~~MBee0u@{{E43Rt^Cm`}lQ} zyIZ=kgfq)yBHmE66gPncTO(~htBpmYpBR7zp++9;D}dM7$6qPnt(1u#6RHV2_60fEEiCbwdCqH_;dd( MFRcQpk~9wdAHI0$+W-In literal 0 HcmV?d00001 diff --git a/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna87.png b/AudioLabSwift/Assets.xcassets/AppIcon.appiconset/peruna87.png new file mode 100644 index 0000000000000000000000000000000000000000..1fc6242f7a8fa1025194b8f1646b04776f9b9c72 GIT binary patch literal 6707 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 literal 0 HcmV?d00001 diff --git a/AudioLabSwift/ViewController.swift b/AudioLabSwift/ViewController.swift index 671c5b1..a4e6175 100644 --- a/AudioLabSwift/ViewController.swift +++ b/AudioLabSwift/ViewController.swift @@ -50,18 +50,15 @@ class ViewController: UIViewController { @IBOutlet weak var labelF3: UILabel! @IBAction func setClose(_ sender: Any) { - if frequency1 == 900 { - frequency1 = 500 - frequency2 = 600 - frequency3 = 900 - }else if frequency1 == 500{ - frequency1 = 550 - frequency2 = 600 - }else if frequency1 == 550{ - frequency1 = 590 - frequency2 = 600 + let diff = abs(frequency1-frequency2) + if diff > 100 { + frequency2 = frequency1 + 99 + }else if diff > 60{ + frequency2 = frequency1 + 51 + }else if diff > 30{ + frequency2 = frequency1 + 10 }else{ - frequency1 = 900 + frequency2 = frequency1 + 300 } } From e793d513310ac631a7765eee4049f21a47cc2fb6 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Mon, 2 Oct 2023 21:46:01 -0500 Subject: [PATCH 19/33] I THINK that I fixed a memory leak and a race condition on the MTL command buffer during deallocation. Should now be fixed. --- AudioLabSwift/AudioModel.swift | 12 ++++ AudioLabSwift/Base.lproj/Main.storyboard | 83 +++++++++++++++++++----- AudioLabSwift/Utilities/CircularBuffer.m | 1 + AudioLabSwift/Utilities/FFTHelper.m | 33 ++++++++-- AudioLabSwift/Utilities/MetalGraph.swift | 55 ++++++++++++++++ AudioLabSwift/Utilities/Novocaine.m | 2 + AudioLabSwift/ViewController.swift | 25 +++++-- 7 files changed, 186 insertions(+), 25 deletions(-) diff --git a/AudioLabSwift/AudioModel.swift b/AudioLabSwift/AudioModel.swift index d4f9da1..9485601 100644 --- a/AudioLabSwift/AudioModel.swift +++ b/AudioLabSwift/AudioModel.swift @@ -75,6 +75,18 @@ class AudioModel { } } + func stop(){ + if let manager = self.audioManager{ + manager.pause() + manager.inputBlock = nil + manager.outputBlock = nil + + inputBuffer?.clear() + inputBuffer = nil + fftHelper = nil + } + } + //========================================== // MARK: Private Properties diff --git a/AudioLabSwift/Base.lproj/Main.storyboard b/AudioLabSwift/Base.lproj/Main.storyboard index bcb3deb..0b2f5d8 100644 --- a/AudioLabSwift/Base.lproj/Main.storyboard +++ b/AudioLabSwift/Base.lproj/Main.storyboard @@ -1,5 +1,5 @@ - + @@ -18,46 +18,53 @@ - + - + + + + + + + - + - + - + @@ -117,6 +124,7 @@ + @@ -126,16 +134,61 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - diff --git a/AudioLabSwift/Utilities/CircularBuffer.m b/AudioLabSwift/Utilities/CircularBuffer.m index 3190a6a..3ce5883 100644 --- a/AudioLabSwift/Utilities/CircularBuffer.m +++ b/AudioLabSwift/Utilities/CircularBuffer.m @@ -78,6 +78,7 @@ -(id)initWithNumChannels:(SInt64)numChannels -(void) dealloc{ for (int i=0; i Date: Tue, 3 Oct 2023 12:24:59 -0500 Subject: [PATCH 20/33] Still trying to track down this leak. Instruments cannot detect... but its there. --- AudioLabSwift/AudioModel.swift | 60 ++++++++++++++++++++--- AudioLabSwift/Info.plist | 2 + AudioLabSwift/Utilities/AudioFileReader.m | 13 +++-- AudioLabSwift/Utilities/CircularBuffer.m | 9 ++-- AudioLabSwift/Utilities/FFTHelper.m | 29 +++++------ AudioLabSwift/Utilities/Novocaine.m | 4 ++ 6 files changed, 88 insertions(+), 29 deletions(-) diff --git a/AudioLabSwift/AudioModel.swift b/AudioLabSwift/AudioModel.swift index 9485601..c02dc78 100644 --- a/AudioLabSwift/AudioModel.swift +++ b/AudioLabSwift/AudioModel.swift @@ -46,7 +46,8 @@ class AudioModel { // public function for starting processing of microphone data func startMicrophoneProcessing(withFps:Double){ // setup the microphone to copy to circualr buffer - if let manager = self.audioManager{ + if let manager = self.audioManager, + let fileReader = self.fileReader{ manager.inputBlock = self.handleMicrophone manager.outputBlock = self.handleSpeakerQueryWithSinusoids @@ -64,6 +65,8 @@ class AudioModel { } } + fileReader.play() + } } @@ -80,11 +83,19 @@ class AudioModel { manager.pause() manager.inputBlock = nil manager.outputBlock = nil - - inputBuffer?.clear() - inputBuffer = nil - fftHelper = nil } + + if let file = self.fileReader{ + file.pause() + file.stop() + } + + if let buffer = self.inputBuffer{ + buffer.clear() // just makes zeros + } + inputBuffer = nil + fftHelper = nil + fileReader = nil } @@ -104,12 +115,33 @@ class AudioModel { andBufferSize: Int64(BUFFER_SIZE)) }() + private lazy var fileReader:AudioFileReader? = { + // find song in the main Bundle + if let url = Bundle.main.url(forResource: "satisfaction", withExtension: "mp3"){ + // if we could find the url for the song in main bundle, setup file reader + // the file reader is doing a lot here becasue its a decoder + // so when it decodes the compressed mp3, it needs to know how many samples + // the speaker is expecting and how many output channels the speaker has (mono, left/right, surround, etc.) + var tmpFileReader:AudioFileReader? = AudioFileReader.init(audioFileURL: url, + samplingRate: Float(audioManager!.samplingRate), + numChannels: audioManager!.numOutputChannels) + + tmpFileReader!.currentTime = 0.0 // start from time zero! + + return tmpFileReader + }else{ + print("Could not initialize audio input file") + return nil + } + }() + //========================================== // MARK: Private Methods // NONE for this model + //========================================== // MARK: Model Callback Methods private func runEveryInterval(){ @@ -141,6 +173,7 @@ class AudioModel { private func handleSpeakerQueryWithSinusoids(data:Optional>, numFrames:UInt32, numChannels: UInt32){ if let arrayData = data, let manager = self.audioManager{ + var addFreq:Float = 0 var mult:Float = 1.0 if pulsing && pulseValue==1{ @@ -148,6 +181,19 @@ class AudioModel { }else if pulsing && pulseValue > 1{ mult = 0.0 } + + if let file = self.fileReader{ + // get samples from audio file, pass array by reference + file.retrieveFreshAudio(arrayData, + numFrames: numFrames, + numChannels: numChannels) + + // adjust volume of audio file output + var volume:Float = mult*0.15 // zero out song also, if needed + vDSP_vsmul(arrayData, 1, &(volume), arrayData, 1, vDSP_Length(numFrames*numChannels)) + + } + phaseIncrement1 = Float(2*Double.pi*Double(sineFrequency1+addFreq)/manager.samplingRate) phaseIncrement2 = Float(2*Double.pi*Double(sineFrequency2+addFreq)/manager.samplingRate) phaseIncrement3 = Float(2*Double.pi*Double(sineFrequency3+addFreq)/manager.samplingRate) @@ -158,7 +204,7 @@ class AudioModel { let frame = Int(numFrames) if chan==1{ while i UISupportedInterfaceOrientations + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UISupportedInterfaceOrientations~ipad diff --git a/AudioLabSwift/Utilities/AudioFileReader.m b/AudioLabSwift/Utilities/AudioFileReader.m index 1f7097e..9bdd848 100644 --- a/AudioLabSwift/Utilities/AudioFileReader.m +++ b/AudioLabSwift/Utilities/AudioFileReader.m @@ -73,17 +73,21 @@ - (void)dealloc if (self.playing) [self pause]; - self.readerBlock = nil; + _readerBlock = nil; // Close the ExtAudioFile - ExtAudioFileDispose(self.inputFile); + ExtAudioFileDispose(_inputFile); - free(self.outputBuffer); - free(self.holdingBuffer); + free(_outputBuffer); + free(_holdingBuffer); + _outputBuffer = nil; + _holdingBuffer = nil; + _callbackTimer = nil; // [ringBuffer dealloc]; // [super dealloc]; + printf("AudioFileReader object was deallocated\n"); } @@ -298,6 +302,7 @@ - (void)stop // Release the dispatch timer because it holds a reference to this class instance [self pause]; if (self.callbackTimer) { + //dispatch_release(self.callbackTimer); self.callbackTimer = nil; } diff --git a/AudioLabSwift/Utilities/CircularBuffer.m b/AudioLabSwift/Utilities/CircularBuffer.m index 3ce5883..303716f 100644 --- a/AudioLabSwift/Utilities/CircularBuffer.m +++ b/AudioLabSwift/Utilities/CircularBuffer.m @@ -77,11 +77,12 @@ -(id)initWithNumChannels:(SInt64)numChannels -(void) dealloc{ for (int i=0; i Date: Tue, 3 Oct 2023 14:58:28 -0500 Subject: [PATCH 21/33] fixing notification observer for metal graph --- AudioDSPUtils/AudioFileReader.mm | 296 ----------------------- AudioDSPUtils/AudioFileWriter.h | 66 ----- AudioDSPUtils/AudioFileWriter.m | 243 ------------------- AudioDSPUtils/BufferedOverlapQueue.h | 38 --- AudioDSPUtils/BufferedOverlapQueue.m | 175 -------------- AudioDSPUtils/CubicSpline.h | 31 --- AudioDSPUtils/CubicSpline.m | 119 --------- AudioDSPUtils/DataBufferBlock.h | 23 -- AudioDSPUtils/DataBufferBlock.m | 91 ------- AudioDSPUtils/PeakFinder.h | 37 --- AudioDSPUtils/PeakFinder.m | 156 ------------ AudioLabSwift.xcodeproj/project.pbxproj | 8 +- AudioLabSwift/Utilities/MetalGraph.swift | 14 ++ AudioLabSwift/Utilities/Novocaine.m | 9 +- AudioLabSwift/ViewController.swift | 1 - 15 files changed, 25 insertions(+), 1282 deletions(-) delete mode 100644 AudioDSPUtils/AudioFileReader.mm delete mode 100644 AudioDSPUtils/AudioFileWriter.h delete mode 100644 AudioDSPUtils/AudioFileWriter.m delete mode 100644 AudioDSPUtils/BufferedOverlapQueue.h delete mode 100644 AudioDSPUtils/BufferedOverlapQueue.m delete mode 100644 AudioDSPUtils/CubicSpline.h delete mode 100644 AudioDSPUtils/CubicSpline.m delete mode 100644 AudioDSPUtils/DataBufferBlock.h delete mode 100644 AudioDSPUtils/DataBufferBlock.m delete mode 100644 AudioDSPUtils/PeakFinder.h delete mode 100644 AudioDSPUtils/PeakFinder.m diff --git a/AudioDSPUtils/AudioFileReader.mm b/AudioDSPUtils/AudioFileReader.mm deleted file mode 100644 index 9f5c0bd..0000000 --- a/AudioDSPUtils/AudioFileReader.mm +++ /dev/null @@ -1,296 +0,0 @@ -// -// AudioFileReader.m -// Novocaine -// -// Copyright (c) 2012 Alex Wiltschko -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. - -#import "AudioFileReader.h" - -@interface AudioFileReader () -{ - RingBuffer *ringBuffer; -} - -@property AudioStreamBasicDescription outputFormat; -@property ExtAudioFileRef inputFile; -@property UInt32 outputBufferSize; -@property float *outputBuffer; -@property float *holdingBuffer; -@property UInt32 numSamplesReadPerPacket; -@property UInt32 desiredPrebufferedSamples; -@property SInt64 currentFileTime; -@property dispatch_source_t callbackTimer; - - -- (void)bufferNewAudio; - -@end - - - -@implementation AudioFileReader - -@synthesize outputFormat = _outputFormat; -@synthesize inputFile = _inputFile; -@synthesize outputBuffer = _outputBuffer; -@synthesize holdingBuffer = _holdingBuffer; -@synthesize outputBufferSize = _outputBufferSize; -@synthesize numSamplesReadPerPacket = _numSamplesReadPerPacket; -@synthesize desiredPrebufferedSamples = _desiredPrebufferedSamples; -@synthesize currentFileTime = _currentFileTime; -@synthesize callbackTimer = _callbackTimer; -@synthesize currentTime = _currentTime; -@synthesize duration = _duration; -@synthesize samplingRate = _samplingRate; -@synthesize latency = _latency; -@synthesize numChannels = _numChannels; -@synthesize audioFileURL = _audioFileURL; -@synthesize readerBlock = _readerBlock; -@synthesize playing = _playing; - -- (void)dealloc -{ - // If the dispatch timer is active, close it off - if (self.playing) - [self pause]; - - self.readerBlock = nil; - - // Close the ExtAudioFile - ExtAudioFileDispose(self.inputFile); - - free(self.outputBuffer); - free(self.holdingBuffer); - - delete ringBuffer; - - [super dealloc]; -} - - -- (id)initWithAudioFileURL:(NSURL *)urlToAudioFile samplingRate:(float)thisSamplingRate numChannels:(UInt32)thisNumChannels -{ - self = [super init]; - if (self) - { - - // Zero-out our timer, so we know we're not using our callback yet - self.callbackTimer = nil; - - - // Open a reference to the audio file - self.audioFileURL = urlToAudioFile; - CFURLRef audioFileRef = (CFURLRef)self.audioFileURL; - CheckError(ExtAudioFileOpenURL(audioFileRef, &_inputFile), "Opening file URL (ExtAudioFileOpenURL)"); - - - // Set a few defaults and presets - self.samplingRate = thisSamplingRate; - self.numChannels = thisNumChannels; - self.latency = .011609977; // 512 samples / ( 44100 samples / sec ) default - - - // We're going to impose a format upon the input file - // Single-channel float does the trick. - _outputFormat.mSampleRate = self.samplingRate; - _outputFormat.mFormatID = kAudioFormatLinearPCM; - _outputFormat.mFormatFlags = kAudioFormatFlagIsFloat; - _outputFormat.mBytesPerPacket = 4*self.numChannels; - _outputFormat.mFramesPerPacket = 1; - _outputFormat.mBytesPerFrame = 4*self.numChannels; - _outputFormat.mChannelsPerFrame = self.numChannels; - _outputFormat.mBitsPerChannel = 32; - - // Apply the format to our file - ExtAudioFileSetProperty(_inputFile, kExtAudioFileProperty_ClientDataFormat, sizeof(AudioStreamBasicDescription), &_outputFormat); - - - // Arbitrary buffer sizes that don't matter so much as long as they're "big enough" - self.outputBufferSize = 65536; - self.numSamplesReadPerPacket = 8192; - self.desiredPrebufferedSamples = self.numSamplesReadPerPacket*2; - self.outputBuffer = (float *)calloc(2*self.samplingRate, sizeof(float)); - self.holdingBuffer = (float *)calloc(2*self.samplingRate, sizeof(float)); - - - // Allocate a ring buffer (this is what's going to buffer our audio) - ringBuffer = new RingBuffer(self.outputBufferSize, self.numChannels); - - - // Fill up the buffers, so we're ready to play immediately - [self bufferNewAudio]; - - } - return self; -} - -- (void)clearBuffer -{ - ringBuffer->Clear(); -} - -- (void)bufferNewAudio -{ - - if (ringBuffer->NumUnreadFrames() > self.desiredPrebufferedSamples) - return; - - memset(self.outputBuffer, 0, sizeof(float)*self.desiredPrebufferedSamples); - - AudioBufferList incomingAudio; - incomingAudio.mNumberBuffers = 1; - incomingAudio.mBuffers[0].mNumberChannels = self.numChannels; - incomingAudio.mBuffers[0].mDataByteSize = self.outputBufferSize; - incomingAudio.mBuffers[0].mData = self.outputBuffer; - - // Figure out where we are in the file - SInt64 frameOffset = 0; - ExtAudioFileTell(self.inputFile, &frameOffset); - self.currentFileTime = (float)frameOffset / self.samplingRate; - - // Read the audio - UInt32 framesRead = self.numSamplesReadPerPacket; - ExtAudioFileRead(self.inputFile, &framesRead, &incomingAudio); - - // Update where we are in the file - ExtAudioFileTell(self.inputFile, &frameOffset); - self.currentFileTime = (float)frameOffset / self.samplingRate; - - // Add the new audio to the ring buffer - ringBuffer->AddNewInterleavedFloatData(self.outputBuffer, framesRead, self.numChannels); - - if ((self.currentFileTime - self.duration) < 0.01 && framesRead == 0) { - // modified to allow for auto-stopping. // - // Need to change your output block to check for [fileReader playing] and nuke your fileReader if it is // - // not playing and not paused, on the next frame. Otherwise, the sound clip's final buffer is not played. // -// self.currentTime = 0.0f; - [self stop]; - ringBuffer->Clear(); - } - - -} - -- (float)getCurrentTime -{ - return self.currentFileTime - ringBuffer->NumUnreadFrames()/self.samplingRate; -} - - -- (void)setCurrentTime:(float)thisCurrentTime -{ - dispatch_async(dispatch_get_main_queue(), ^{ - [self pause]; - ExtAudioFileSeek(self.inputFile, thisCurrentTime*self.samplingRate); - - [self clearBuffer]; - [self bufferNewAudio]; - - [self play]; - }); -} - -- (float)getDuration -{ - // We're going to directly calculate the duration of the audio file (in seconds) - SInt64 framesInThisFile; - UInt32 propertySize = sizeof(framesInThisFile); - ExtAudioFileGetProperty(self.inputFile, kExtAudioFileProperty_FileLengthFrames, &propertySize, &framesInThisFile); - - AudioStreamBasicDescription fileStreamFormat; - propertySize = sizeof(AudioStreamBasicDescription); - ExtAudioFileGetProperty(self.inputFile, kExtAudioFileProperty_FileDataFormat, &propertySize, &fileStreamFormat); - - return (float)framesInThisFile/(float)fileStreamFormat.mSampleRate; - -} - -- (void)configureReaderCallback -{ - - if (!self.callbackTimer) - { - self.callbackTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); - UInt32 numSamplesPerCallback = (UInt32)( self.latency * self.samplingRate ); - dispatch_source_set_timer(self.callbackTimer, dispatch_walltime(NULL, 0), self.latency*NSEC_PER_SEC, 0); - dispatch_source_set_event_handler(self.callbackTimer, ^{ - - if (self.playing) { - - if (self.readerBlock) { - // Suck some audio down from our ring buffer - [self retrieveFreshAudio:self.holdingBuffer numFrames:numSamplesPerCallback numChannels:self.numChannels]; - - // Call out with the audio that we've got. - self.readerBlock(self.holdingBuffer, numSamplesPerCallback, self.numChannels); - } - - // Asynchronously fill up the buffer (if it needs filling) - dispatch_async(dispatch_get_main_queue(), ^{ - [self bufferNewAudio]; - }); - - } - - }); - - dispatch_resume(self.callbackTimer); - } -} - - -- (void)retrieveFreshAudio:(float *)buffer numFrames:(UInt32)thisNumFrames numChannels:(UInt32)thisNumChannels -{ - ringBuffer->FetchInterleavedData(buffer, thisNumFrames, thisNumChannels); -} - - -- (void)play; -{ - - // Configure (or if necessary, create and start) the timer for retrieving audio - if (!self.playing) { - [self configureReaderCallback]; - self.playing = TRUE; - } - -} - -- (void)pause -{ - // Pause the dispatch timer for retrieving the MP3 audio - self.playing = FALSE; -} - -- (void)stop -{ - // Release the dispatch timer because it holds a reference to this class instance - [self pause]; - if (self.callbackTimer) { - dispatch_release(self.callbackTimer); - } -} - - -@end diff --git a/AudioDSPUtils/AudioFileWriter.h b/AudioDSPUtils/AudioFileWriter.h deleted file mode 100644 index 62c5c3f..0000000 --- a/AudioDSPUtils/AudioFileWriter.h +++ /dev/null @@ -1,66 +0,0 @@ -// -// AudioFileWriter.h -// Novocaine -// -// Copyright (c) 2012 Alex Wiltschko -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. - -#import -#import "Novocaine.h" - - -@interface AudioFileWriter : NSObject -{ - float currentTime; - float duration; - float samplingRate; - float latency; - UInt32 numChannels; - NSURL *audioFileURL; - - OutputBlock writerBlock; - - BOOL recording; -} - -@property (nonatomic, getter=getDuration, readonly) float currentTime; -@property (nonatomic, getter=getDuration) float duration; -@property float samplingRate; -@property UInt32 numChannels; -@property float latency; -@property (nonatomic, copy) NSURL *audioFileURL; -@property (nonatomic, copy) InputBlock writerBlock; -@property BOOL recording; - - -- (id)initWithAudioFileURL:(NSURL *)urlToAudioFile samplingRate:(float)thisSamplingRate numChannels:(UInt32)thisNumChannels; - -// You use this method to grab audio if you have your own callback. -// The buffer'll fill at the speed the audio is normally being played. -- (void)writeNewAudio:(float *)newData numFrames:(UInt32)thisNumFrames numChannels:(UInt32)thisNumChannels; - -- (void)record; -- (void)pause; - - -@end diff --git a/AudioDSPUtils/AudioFileWriter.m b/AudioDSPUtils/AudioFileWriter.m deleted file mode 100644 index 5fe5b1d..0000000 --- a/AudioDSPUtils/AudioFileWriter.m +++ /dev/null @@ -1,243 +0,0 @@ -// -// AudioFileWriter.m -// Novocaine -// -// Copyright (c) 2012 Alex Wiltschko -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -#import "AudioFileWriter.h" -#import - -@interface AudioFileWriter() - -@property AudioStreamBasicDescription outputFormat; -@property ExtAudioFileRef outputFile; -@property UInt32 outputBufferSize; -@property float *outputBuffer; -@property float *holdingBuffer; -@property SInt64 currentFileTime; -@property dispatch_source_t callbackTimer; -@property (readwrite) float currentTime; - -@end - - - -@implementation AudioFileWriter - -static pthread_mutex_t outputAudioFileLock; - -@synthesize outputFormat = _outputFormat; -@synthesize outputFile = _outputFile; -@synthesize outputBuffer = _outputBuffer; -@synthesize holdingBuffer = _holdingBuffer; -@synthesize outputBufferSize = _outputBufferSize; -@synthesize currentFileTime = _currentFileTime; -@synthesize callbackTimer = _callbackTimer; - -@synthesize currentTime = _currentTime; -@synthesize duration = _duration; -@synthesize samplingRate = _samplingRate; -@synthesize latency = _latency; -@synthesize numChannels = _numChannels; -@synthesize audioFileURL = _audioFileURL; -@synthesize writerBlock = _writerBlock; -@synthesize recording = _recording; - -- (void)dealloc -{ - [self stop]; - - free(self.outputBuffer); - free(self.holdingBuffer); - - [super dealloc]; -} - -- (id)initWithAudioFileURL:(NSURL *)urlToAudioFile samplingRate:(float)thisSamplingRate numChannels:(UInt32)thisNumChannels -{ - self = [super init]; - if (self) - { - - // Zero-out our timer, so we know we're not using our callback yet - self.callbackTimer = nil; - - - // Open a reference to the audio file - self.audioFileURL = urlToAudioFile; - CFURLRef audioFileRef = (CFURLRef)self.audioFileURL; - - AudioStreamBasicDescription outputFileDesc = {44100.0, kAudioFormatMPEG4AAC, 0, 0, 1024, 0, thisNumChannels, 0, 0}; - - CheckError(ExtAudioFileCreateWithURL(audioFileRef, kAudioFileM4AType, &outputFileDesc, NULL, kAudioFileFlags_EraseFile, &_outputFile), "Creating file"); - - - // Set a few defaults and presets - self.samplingRate = thisSamplingRate; - self.numChannels = thisNumChannels; - self.currentTime = 0.0; - self.latency = .011609977; // 512 samples / ( 44100 samples / sec ) default - - - // We're going to impose a format upon the input file - // Single-channel float does the trick. - _outputFormat.mSampleRate = self.samplingRate; - _outputFormat.mFormatID = kAudioFormatLinearPCM; - _outputFormat.mFormatFlags = kAudioFormatFlagIsFloat; - _outputFormat.mBytesPerPacket = 4*self.numChannels; - _outputFormat.mFramesPerPacket = 1; - _outputFormat.mBytesPerFrame = 4*self.numChannels; - _outputFormat.mChannelsPerFrame = self.numChannels; - _outputFormat.mBitsPerChannel = 32; - - // Apply the format to our file - ExtAudioFileSetProperty(_outputFile, kExtAudioFileProperty_ClientDataFormat, sizeof(AudioStreamBasicDescription), &_outputFormat); - - - // Arbitrary buffer sizes that don't matter so much as long as they're "big enough" - self.outputBuffer = (float *)calloc(2*self.samplingRate, sizeof(float)); - self.holdingBuffer = (float *)calloc(2*self.samplingRate, sizeof(float)); - - pthread_mutex_init(&outputAudioFileLock, NULL); - - // mutex here // - if( 0 == pthread_mutex_trylock( &outputAudioFileLock ) ) - { - CheckError( ExtAudioFileWriteAsync(self.outputFile, 0, NULL), "Initializing audio file"); - } - pthread_mutex_unlock( &outputAudioFileLock ); - - } - return self; -} - -- (void)writeNewAudio:(float *)newData numFrames:(UInt32)thisNumFrames numChannels:(UInt32)thisNumChannels -{ - UInt32 numIncomingBytes = thisNumFrames*thisNumChannels*sizeof(float); - memcpy(self.outputBuffer, newData, numIncomingBytes); - - AudioBufferList outgoingAudio; - outgoingAudio.mNumberBuffers = 1; - outgoingAudio.mBuffers[0].mNumberChannels = thisNumChannels; - outgoingAudio.mBuffers[0].mDataByteSize = numIncomingBytes; - outgoingAudio.mBuffers[0].mData = self.outputBuffer; - - if( 0 == pthread_mutex_trylock( &outputAudioFileLock ) ) - { - ExtAudioFileWriteAsync(self.outputFile, thisNumFrames, &outgoingAudio); - } - pthread_mutex_unlock( &outputAudioFileLock ); - - // Figure out where we are in the file - SInt64 frameOffset = 0; - ExtAudioFileTell(self.outputFile, &frameOffset); - self.currentTime = (float)frameOffset / self.samplingRate; - -} - - -- (float)getDuration -{ - // We're going to directly calculate the duration of the audio file (in seconds) - SInt64 framesInThisFile; - UInt32 propertySize = sizeof(framesInThisFile); - ExtAudioFileGetProperty(self.outputFile, kExtAudioFileProperty_FileLengthFrames, &propertySize, &framesInThisFile); - - AudioStreamBasicDescription fileStreamFormat; - propertySize = sizeof(AudioStreamBasicDescription); - ExtAudioFileGetProperty(self.outputFile, kExtAudioFileProperty_FileDataFormat, &propertySize, &fileStreamFormat); - - return (float)framesInThisFile/(float)fileStreamFormat.mSampleRate; - -} - - - -- (void)configureWriterCallback -{ - - if (!self.callbackTimer) - { - self.callbackTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); - } - - if (self.callbackTimer) - { - UInt32 numSamplesPerCallback = (UInt32)( self.latency * self.samplingRate ); - dispatch_source_set_timer(self.callbackTimer, dispatch_walltime(NULL, 0), self.latency*NSEC_PER_SEC, 0); - dispatch_source_set_event_handler(self.callbackTimer, ^{ - - - if (self.writerBlock) { - // Call out with the audio that we've got. - self.writerBlock(self.outputBuffer, numSamplesPerCallback, self.numChannels); - - // Get audio from the block supplier - [self writeNewAudio:self.outputBuffer numFrames:numSamplesPerCallback numChannels:self.numChannels]; - - } - - }); - - } - -} - - - -- (void)record; -{ - - // Configure (or if necessary, create and start) the timer for retrieving MP3 audio - [self configureWriterCallback]; - - if (!self.recording) - { - dispatch_resume(self.callbackTimer); - self.recording = TRUE; - } - -} - -- (void)stop -{ - // Close the - pthread_mutex_lock( &outputAudioFileLock ); - ExtAudioFileDispose(self.outputFile); - pthread_mutex_unlock( &outputAudioFileLock ); - self.recording = FALSE; -} - -- (void)pause -{ - // Pause the dispatch timer for retrieving the MP3 audio - if (self.callbackTimer) { - dispatch_suspend(self.callbackTimer); - self.recording = FALSE; - } -} - - - -@end - diff --git a/AudioDSPUtils/BufferedOverlapQueue.h b/AudioDSPUtils/BufferedOverlapQueue.h deleted file mode 100644 index 1e780ba..0000000 --- a/AudioDSPUtils/BufferedOverlapQueue.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// BufferedOverlapQueue.h -// OpenSpirometry -// -// Created by Eric Larson -// Copyright (c) 2015 Eric Larson. All rights reserved. -// - -#import -#import "DataBufferBlock.h" - -typedef void (^ConsumeBlock)(); //TODO: only processing through delegation is currently supported - -// delegation code manipulated from: http://stackoverflow.com/questions/626898/how-do-i-create-delegates-in-objective-c -@protocol DataBufferProcessDelegate -@optional --(void)didFillBuffer:(DataBufferBlock*)block; // optional so that consume blocks can be used (BUT NOT BOTH!!) --(void)didFinishProcessingAllBuffers; -@end - -@interface BufferedOverlapQueue : NSObject - -@property (nonatomic, weak) id delegate; // best way to process the data on demand in a separate concurrent queue - -@property (nonatomic,readonly) NSUInteger numOverlapSamples; -@property (nonatomic,readonly) NSUInteger numSamplesPerBuffer; -@property (nonatomic,readonly) NSUInteger numFullBuffers; - --(id)initWithBufferLength:(NSUInteger)buffLength andOverlapLength:(NSUInteger)overlapLength; --(void)addFreshFloatData:(float*)data withLength:(NSUInteger)numSamples; --(void)addFreshInterleavedFloatData:(float*)data withLength:(NSUInteger)numSamples fromChannel:(NSUInteger)whichChannel withNumChannels:(NSUInteger)numChannels; // can only add one channel at a time --(DataBufferBlock*)dequeueAndTakeOwnership; // TODO: you are responsible for freeing memory, more versatile --(void)consumeBufferWithBlock:ConsumeBlock; // TODO: process buffer and free it, less versatile --(void)deleteAt:(NSUInteger)indexToDelete; --(void)clear; --(void)processRemainingBlocks; - -@end diff --git a/AudioDSPUtils/BufferedOverlapQueue.m b/AudioDSPUtils/BufferedOverlapQueue.m deleted file mode 100644 index 7f2858f..0000000 --- a/AudioDSPUtils/BufferedOverlapQueue.m +++ /dev/null @@ -1,175 +0,0 @@ -// -// BufferedOverlapQueue.m -// OpenSpirometry -// -// Created by Eric Larson -// Copyright (c) 2015 Eric Larson. All rights reserved. -// - -#import "BufferedOverlapQueue.h" - - -@interface BufferedOverlapQueue() - -@property (strong, atomic) NSMutableArray* overlapQueue; -@property (atomic) NSUInteger currentFillQueueIndex; - -@property (nonatomic,readwrite) NSUInteger numOverlapSamples; -@property (nonatomic,readwrite) NSUInteger numSamplesPerBuffer; -@property (nonatomic,readwrite) NSUInteger numFullBuffers; - -@end - -@implementation BufferedOverlapQueue { - struct { - unsigned int didFillBuffer:1; - unsigned int didFinishProcessingAllBuffers:1; - } delegateRespondsTo; -} - -- (void)setDelegate:(id )aDelegate { - if (_delegate != aDelegate) { - _delegate = aDelegate; - delegateRespondsTo.didFillBuffer = [_delegate respondsToSelector:@selector(didFillBuffer:)]; - delegateRespondsTo.didFinishProcessingAllBuffers = [_delegate respondsToSelector:@selector(didFinishProcessingAllBuffers)]; - } -} - --(id)initWithBufferLength:(NSUInteger)buffLength andOverlapLength:(NSUInteger)overlapLength{ - if(self = [super init]){ - _numFullBuffers = 0; - _currentFillQueueIndex = 0; - _numSamplesPerBuffer = buffLength; - _numOverlapSamples = overlapLength; - _overlapQueue = [[NSMutableArray alloc]init]; - // init the first member of the queue - [_overlapQueue addObject:[[DataBufferBlock alloc]initWithCapacity:_numSamplesPerBuffer]]; - return self; - } - return nil; -} - --(id) init{ - // probably not what you want. Use the designated init above - return [self initWithBufferLength:512 andOverlapLength:256]; -} - --(void)addFreshFloatData:(float*)data withLength:(NSUInteger)numSamples{ - [self addFreshInterleavedFloatData:data withLength:numSamples - fromChannel:0 withNumChannels:1]; -} - - --(void)addFreshInterleavedFloatData:(float*)data withLength:(NSUInteger)numSamples fromChannel:(NSUInteger)whichChannel withNumChannels:(NSUInteger)numChannels{ - // copy data in a hurry, this block likely occurs in a streaming process - NSUInteger increment = self.numSamplesPerBuffer - self.numOverlapSamples; - NSUInteger dataCopyLength = numSamples; - - NSUInteger idx = 0; - - @synchronized(self){ - - if(numSamples > self.numSamplesPerBuffer){ - // this only works for input data greater than BufferSize - for(int i=0; i= [self.overlapQueue count]){ // add object if we need it - [self.overlapQueue addObject:[[DataBufferBlock alloc]initWithCapacity:self.numSamplesPerBuffer]]; - } - [self addData:&data[i] withSize:dataCopyLength fromChannel:whichChannel withNumChannels:numChannels - toBufferBlock:self.overlapQueue[self.currentFillQueueIndex+idx]]; - } - }else{ - - // this only works for input data fewer than BufferSize - DataBufferBlock* block = [self.overlapQueue lastObject]; - if(block.writePosition+numSamples>increment){ // need new entry - [self.overlapQueue addObject:[[DataBufferBlock alloc]initWithCapacity:self.numSamplesPerBuffer]]; - } - - // add given data to each block - for(DataBufferBlock* block in self.overlapQueue){ - if(!block.isFull){ - [self addData:data withSize:numSamples fromChannel:whichChannel withNumChannels:numChannels - toBufferBlock:block]; - } - } - } - - // Update write position - idx = 0; - for(DataBufferBlock* block in self.overlapQueue){ - if(block.isFull){ - idx++; - } - } - self.currentFillQueueIndex = idx; - self.numFullBuffers = idx; - - if(idx>0) { //at least one buffer to process - [self didFillDataWrapper]; - } - } -} - --(void)addData:(float*)data withSize:(NSUInteger)length fromChannel:(NSUInteger)whichChannel withNumChannels:(NSUInteger)numChannels toBufferBlock:(DataBufferBlock*)block { - if(block) - [block addInterleavedFloatData:data fromChannel:whichChannel withNumChannels:numChannels withLength:length]; - else - NSLog(@"Could not add data to block"); -} - --(DataBufferBlock*)dequeueAndTakeOwnership{ - //TODO -// self.overlapQueue - return nil; -} - --(void)consumeBufferWithBlock:ConsumeBlock{ - //TODO -} - --(void)deleteAt:(NSUInteger)indexToDelete{ - [self.overlapQueue removeObjectAtIndex:indexToDelete]; -} - --(void)didFillDataWrapper{ - if([self.overlapQueue count]>0){ - __block DataBufferBlock* block = [self.overlapQueue firstObject]; - - //TODO: mutex protection? might be too slow to use here - [self.overlapQueue removeObjectAtIndex:0]; - self.currentFillQueueIndex--; - self.numFullBuffers--; - - if(delegateRespondsTo.didFillBuffer){ - // spin off the data into high priority queue, assuming that this data analysis needs run UI - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),^{ - [self.delegate didFillBuffer:block]; // call delegate, and it does whatever it wants with data - //after this block executes ARC will clean up the data block for us! - // if we have processed all buffers, set status - if(self.numFullBuffers<=0 && delegateRespondsTo.didFinishProcessingAllBuffers){ - [self.delegate didFinishProcessingAllBuffers]; - } - - }); - } - } -} - --(void)processRemainingBlocks{ - @synchronized(self){ - while(self.numFullBuffers >0){ - // process until done - [self didFillDataWrapper]; - } - //TODO: block here until completion? - [self clear]; // and clear any blocks that are not full - } - -} - --(void)clear{ - [self.overlapQueue removeAllObjects]; -} - -@end diff --git a/AudioDSPUtils/CubicSpline.h b/AudioDSPUtils/CubicSpline.h deleted file mode 100644 index acf9773..0000000 --- a/AudioDSPUtils/CubicSpline.h +++ /dev/null @@ -1,31 +0,0 @@ -// -// CubicSpline.h -// CubicSpline -// -// Created by Sam Soffes on 12/16/13. -// Copyright (c) 2013-2014 Sam Soffes. All rights reserved. -// Manipulated from https://github.com/soffes/SAMCubicSpline -// Updated by Dr. Eric Larson July 2015 - -#import - - -@interface CubicSpline : NSObject - -/** - Initialize a new cubic spline. - - @param points An array of `NSValue` objects containing `x and y` structs. These points are the control points of the curve. - - @return A new cubic spline. - */ -- (instancetype)initWithPointsX:(NSArray *)x andY:(NSArray *)y; - -/** - Input an X value between 0 and 1. - - @return The corresponding Y value. - */ -- (float)interpolateX:(float)x; - -@end diff --git a/AudioDSPUtils/CubicSpline.m b/AudioDSPUtils/CubicSpline.m deleted file mode 100644 index b2a2ef2..0000000 --- a/AudioDSPUtils/CubicSpline.m +++ /dev/null @@ -1,119 +0,0 @@ -// -// CubicSpline.m -// CubicSpline -// -// Created by Sam Soffes on 12/16/13. -// Copyright (c) 2013-2014 Sam Soffes. All rights reserved. -// - -#import "CubicSpline.h" - -@interface CubicSpline () -@property (nonatomic, strong) NSArray *x; -@property (nonatomic, strong) NSArray *y; -@property (nonatomic, strong) NSArray *b; -@property (nonatomic, strong) NSArray *c; -@property (nonatomic, strong) NSArray *d; -@end - -@implementation CubicSpline - -- (instancetype)initWithPointsX:(NSArray *)x andY:(NSArray *)y { - if ((self = [super init])) { - self.x = x; - self.y = y; - - if (x.count > 0) { - NSUInteger count = x.count; - NSUInteger n = count; // - 1; - float x[count]; - float a[count]; - float h[count]; - float y[count]; - float l[count]; - float u[count]; - float z[count]; - float k[count]; - float s[count]; - - for (NSUInteger i = 0; i < self.x.count; i++) { - x[i] = [self.x[i] floatValue]; - a[i] = [self.y[i] floatValue]; - } - - for (NSUInteger i = 0; i < n; i++) { - h[i] = x[i + 1] - x[i]; - k[i] = a[i + 1] - a[i]; - s[i] = k[i] / h[i]; - } - - for (NSUInteger i = 1; i < n; i++) { - y[i] = 3 / h[i] * (a[i + 1] - a[i]) - 3 / h[i - 1] * (a[i] - a[i - 1]); - } - - l[0] = 1; - u[0] = 0; - z[0] = 0; - - for (NSUInteger i = 1; i < n; i++) { - l[i] = 2 * (x[i + 1] - x[i - 1]) - h[i - 1] * u[i - 1]; - u[i] = h[i] / l[i]; - z[i] = (y[i] - h[i - 1] * z[i - 1]) / l[i]; - } - - l[n] = 1; - z[n] = 0; - - NSMutableArray *b = [[NSMutableArray alloc] initWithCapacity:n]; - NSMutableArray *c = [[NSMutableArray alloc] initWithCapacity:n]; - NSMutableArray *d = [[NSMutableArray alloc] initWithCapacity:n]; - - for (NSUInteger i = 0; i <= n; i++) { - b[i] = @0; - c[i] = @0; - d[i] = @0; - } - - for (NSInteger i = n - 1; i >= 0; i--) { - c[i] = @(z[i] - u[i] * [c[i + 1] floatValue]); - b[i] = @((a[i + 1] - a[i]) / h[i] - h[i] * ([c[i + 1] floatValue] + 2.0f * [c[i] floatValue]) / 3.0f); - d[i] = @(([c[i + 1] floatValue] - [c[i] floatValue]) / (3 * h[i])); - } - - c[n] = @0; - - self.b = b; - self.c = c; - self.d = d; - } - } - return self; -} - - -- (float)interpolateX:(float)input { - if (self.x.count == 0) { - // No points. Return identity. - return input; - } - - float x[self.x.count]; - float a[self.x.count]; - - for (NSUInteger i = 0; i < self.x.count; i++) { - x[i] = [self.x[i] floatValue]; - a[i] = [self.y[i] floatValue]; - } - - NSInteger i = 0; - for (i = self.x.count - 1; i > 0; i--) { - if (x[i] <= input) { - break; - } - } - - float deltaX = input - x[i]; - return a[i] + [self.b[i] floatValue] * deltaX + [self.c[i] floatValue] * pow(deltaX, 2) + [self.d[i] floatValue] * pow(deltaX, 3); -} - -@end diff --git a/AudioDSPUtils/DataBufferBlock.h b/AudioDSPUtils/DataBufferBlock.h deleted file mode 100644 index 003dd4a..0000000 --- a/AudioDSPUtils/DataBufferBlock.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// DataBufferBlock.h -// OpenSpirometry -// -// Created by Eric Larson -// Copyright (c) 2015 Eric Larson. All rights reserved. -// - -#import - -@interface DataBufferBlock : NSObject - -@property (nonatomic) float *data; -@property (nonatomic,readonly) NSUInteger writePosition; -@property (nonatomic,readonly) NSUInteger length; -@property (nonatomic,readonly) CFTimeInterval timeCreated; -@property (nonatomic,readonly) BOOL isFull; - --(id)initWithCapacity:(NSUInteger)numItems; --(void)addFloatData:(float*)data withLength:(NSUInteger)dataLength; // if this goes over length, only partial data copy --(void)addInterleavedFloatData:(float*)data fromChannel:(NSUInteger)whichChannel withNumChannels:(NSUInteger)numChannels withLength:(NSUInteger)dataLength; - -@end diff --git a/AudioDSPUtils/DataBufferBlock.m b/AudioDSPUtils/DataBufferBlock.m deleted file mode 100644 index a9b8ae6..0000000 --- a/AudioDSPUtils/DataBufferBlock.m +++ /dev/null @@ -1,91 +0,0 @@ -// -// DataBufferBlock.m -// OpenSpirometry -// -// Created by Eric Larson -// Copyright (c) 2015 Eric Larson. All rights reserved. -// - -#import "DataBufferBlock.h" -#import - -@interface DataBufferBlock() - -@property (nonatomic,readwrite) NSUInteger writePosition; -@property (nonatomic,readwrite) NSUInteger length; -@property (nonatomic,readwrite) CFTimeInterval timeCreated; -@property (nonatomic,readwrite) BOOL isFull; - -@end - -@implementation DataBufferBlock - --(float*)data{ // on demand in case never used - if(!_data){ - _data = (float *)calloc(self.length,sizeof(float)); - } - return _data; -} - --(id)initWithCapacity:(NSUInteger)numItems{ - if(self = [super init]){ - //set backing variables - _length = numItems; - _writePosition = 0; - _timeCreated = CACurrentMediaTime(); - _isFull = NO; - return self; - } - return nil; -} - --(id)init{ - return [self initWithCapacity:512]; //probably not what you want, use the designated init above -} - --(void)addFloatData:(float*)data withLength:(NSUInteger)dataLength{ - if(self.writePosition+dataLength <= self.length){ // wont go off the end, just copy - memcpy(&self.data[self.writePosition], data, dataLength*sizeof(float)); - self.writePosition += dataLength; - }else{ // we will go over the end, only copy some - NSUInteger floatsToCopy = self.length - self.writePosition; - memcpy(&self.data[self.writePosition], data, floatsToCopy*sizeof(float)); - self.writePosition += floatsToCopy; - } - - if(self.writePosition >= self.length) - self.isFull = YES; -} - --(void)addInterleavedFloatData:(float*)data fromChannel:(NSUInteger)whichChannel - withNumChannels:(NSUInteger)numChannels withLength:(NSUInteger)dataLength -{ - if(self.writePosition+dataLength <= self.length){ // wont go off the end, just copy - float *p = &data[whichChannel]; - for(int i=0;i= self.length) - self.isFull = YES; - -} - - --(void)dealloc{ - if(_data){ - free(_data); - _data = nil; - } -} - -@end diff --git a/AudioDSPUtils/PeakFinder.h b/AudioDSPUtils/PeakFinder.h deleted file mode 100644 index 9814b67..0000000 --- a/AudioDSPUtils/PeakFinder.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// PeakFinder.h -// OpenSpirometry -// -// Created by Eric Larson -// Copyright (c) 2015 Eric Larson. All rights reserved. -// - -#import - -// helper class for the analysis (used like a struct, but allows ARC and Obj-C objects) -@interface Peak : NSObject - -@property (nonatomic) NSUInteger index; -@property (nonatomic) float frequency; -@property (strong, nonatomic) NSMutableArray *harmonics; -@property (nonatomic) NSUInteger multiple; -@property (nonatomic) float magnitude; - -@end - -@interface PeakFinder : NSObject - --(id)initWithFrequencyResolution:(float)res; - --(NSArray*)getFundamentalPeaksFromBuffer:(float *)magBuffer - withLength:(NSUInteger)length - usingWindowSize:(NSUInteger)windowSize - andPeakMagnitudeMinimum:(float)peakMagnitude - aboveFrequency:(float)minimumFrequency; - -- (float)getFrequencyFromIndex:(NSUInteger)index - usingData:(float*)data; - -@end - - diff --git a/AudioDSPUtils/PeakFinder.m b/AudioDSPUtils/PeakFinder.m deleted file mode 100644 index 7a0684a..0000000 --- a/AudioDSPUtils/PeakFinder.m +++ /dev/null @@ -1,156 +0,0 @@ -// -// PeakFinder.m -// OpenSpirometry -// -// Created by Eric Larson -// Copyright (c) 2015 Eric Larson. All rights reserved. -// - -#import "PeakFinder.h" -#import - - - - -@implementation Peak - --(id)initWithIndex:(NSUInteger)index andMagnitude:(float)mag andFreq:(float)freq{ - if(self = [super init]){ - _index = index; - _harmonics = [[NSMutableArray alloc]init]; - _frequency = freq; - _multiple = 1; - _magnitude = mag; - return self; - } - return nil; -} - -@end - -@interface PeakFinder() - -@property (nonatomic) float frequencyResolution; - -@end - -@implementation PeakFinder - --(id)initWithFrequencyResolution:(float)res{ - if(self = [super init]){ - _frequencyResolution = res; - return self; - } - return nil; -} - - -// Use dilation to find local max peaks (use harmonics to refine the peak estimation) -// Using starter code from Candie Solis, Charlie Albright, and Spencer Kaiser, MSLC 2015 -// this returns an array of peaks fundamental frequencies in the spectrum (if any) -// return type: index, frequency, magnitude, and list of harmonics --(NSArray*)getFundamentalPeaksFromBuffer:(float *)magBuffer - withLength:(NSUInteger)length - usingWindowSize:(NSUInteger)windowSize - andPeakMagnitudeMinimum:(float)peakMagnitude - aboveFrequency:(float)minimumFrequency -{ - NSMutableArray* peaks = [[NSMutableArray alloc] init]; - int startIndex = minimumFrequency / self.frequencyResolution; // must be above X Hz - - for (int i = startIndex; i < length-windowSize; i++) { - unsigned long mid = (i + windowSize/2); - - // find maximum of spectrum in window - // (this is a nested for loop, but parallelized in hardware) - float maxValue; - unsigned long maxIndex; - vDSP_maxvi(&(magBuffer[i]), 1, &maxValue, &maxIndex, windowSize); - maxIndex += i; - - if ((maxValue > peakMagnitude) && (mid == maxIndex)) { // Local max AND large enough magnitude - - Peak *peakFound = [[Peak alloc]initWithIndex:mid - andMagnitude:maxValue - andFreq:[self getFrequencyFromIndex:mid usingData:magBuffer]]; - - if ([peaks count] == 0) { // nothing to check, just add in - - [peaks addObject:peakFound]; - } - else { // Check if harmonic multiple exists below the peak - - BOOL unique = YES; - - for (Peak* peakInPeaks in peaks) { - NSUInteger numVal = peakInPeaks.index; // index of peak - NSUInteger multiple = mid / numVal; // integer value of harmonic multiple - NSUInteger modulus = mid % numVal; // num frequency bins above multiple - if (modulus > numVal/multiple) { - modulus = numVal - modulus; // num frequency bins below multiple, if closer - multiple++; // multiple is next harmonic up - } - float freqInHzAway = self.frequencyResolution * modulus; // deviation in Hz from harmonic (1 Hz tolerance) - - if (freqInHzAway <= self.frequencyResolution * multiple) { // scale difference by harmonic multiple (to account for mis-estimation of the fundamental up to 1Hz) - unique = false; - peakFound.multiple = multiple; // remember the multiple - [peakInPeaks.harmonics addObject:peakFound]; - - break; // found least common multiple and it is within deviation, add it in - } - } - - if (unique) { // it was not a harmonic - [peaks addObject:peakFound]; - } - } - } - } - - if ([peaks count] == 0) { - return nil; - } - else { - // go through and fix the frequencies - for (Peak* peak in peaks){ - if([peak.harmonics count]>0){ // only fundamental, just add in - float frequency = peak.frequency; - int numFrequenciesToAverage = 1; - for(Peak *harmonic in peak.harmonics){ - frequency += (harmonic.frequency / ((float)harmonic.multiple)); - numFrequenciesToAverage++; - } - peak.frequency= frequency/((float)numFrequenciesToAverage); - } - } - - NSArray* returnArraySortedByMagnitude = [peaks sortedArrayUsingComparator: - ^NSComparisonResult(Peak* a, Peak* b) { - return a.magnitude Date: Tue, 3 Oct 2023 15:24:42 -0500 Subject: [PATCH 22/33] add shaders??? --- AudioLabSwift.xcodeproj/project.pbxproj | 8 +++--- AudioLabSwift/Utilities/Shaders.metal | 38 +++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 AudioLabSwift/Utilities/Shaders.metal diff --git a/AudioLabSwift.xcodeproj/project.pbxproj b/AudioLabSwift.xcodeproj/project.pbxproj index b6ca726..abe97a7 100644 --- a/AudioLabSwift.xcodeproj/project.pbxproj +++ b/AudioLabSwift.xcodeproj/project.pbxproj @@ -7,7 +7,7 @@ objects = { /* Begin PBXBuildFile section */ - B60B8A032ACC977B0083315D /* Shaders.metal in Sources */ = {isa = PBXBuildFile; fileRef = B60B8A022ACC977B0083315D /* Shaders.metal */; }; + B60B8A092ACCAFFD0083315D /* Shaders.metal in Sources */ = {isa = PBXBuildFile; fileRef = B60B8A082ACCAFFD0083315D /* Shaders.metal */; }; B65B5A5A24F4409F00E74E73 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B65B5A5924F4409F00E74E73 /* AppDelegate.swift */; }; B65B5A5C24F4409F00E74E73 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B65B5A5B24F4409F00E74E73 /* SceneDelegate.swift */; }; B65B5A5E24F4409F00E74E73 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B65B5A5D24F4409F00E74E73 /* ViewController.swift */; }; @@ -24,7 +24,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - B60B8A022ACC977B0083315D /* Shaders.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = Shaders.metal; sourceTree = ""; }; + B60B8A082ACCAFFD0083315D /* Shaders.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = Shaders.metal; sourceTree = ""; }; B65B5A5624F4409F00E74E73 /* AudioLabSwift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AudioLabSwift.app; sourceTree = BUILT_PRODUCTS_DIR; }; B65B5A5924F4409F00E74E73 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; B65B5A5B24F4409F00E74E73 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -104,7 +104,7 @@ B65B5A7C24F458D700E74E73 /* FFTHelper.h */, B65B5A7B24F458D700E74E73 /* FFTHelper.m */, B65B5A8024F4AAB800E74E73 /* MetalGraph.swift */, - B60B8A022ACC977B0083315D /* Shaders.metal */, + B60B8A082ACCAFFD0083315D /* Shaders.metal */, ); path = Utilities; sourceTree = ""; @@ -186,7 +186,7 @@ B65B5A7824F444CE00E74E73 /* CircularBuffer.m in Sources */, B65B5A7324F443A400E74E73 /* AudioFileReader.m in Sources */, B65B5A7024F440D600E74E73 /* Novocaine.m in Sources */, - B60B8A032ACC977B0083315D /* Shaders.metal in Sources */, + B60B8A092ACCAFFD0083315D /* Shaders.metal in Sources */, B65B5A8124F4AAB800E74E73 /* MetalGraph.swift in Sources */, B65B5A5E24F4409F00E74E73 /* ViewController.swift in Sources */, B65B5A5A24F4409F00E74E73 /* AppDelegate.swift in Sources */, diff --git a/AudioLabSwift/Utilities/Shaders.metal b/AudioLabSwift/Utilities/Shaders.metal new file mode 100644 index 0000000..159233f --- /dev/null +++ b/AudioLabSwift/Utilities/Shaders.metal @@ -0,0 +1,38 @@ +// +// Shaders.metal +// AudioLabSwift +// +// Created by Eric Larson +// Copyright © 2020 Eric Larson. All rights reserved. +// +// started with shader code from the following very helpful repository: +// https://github.com/RedQueenCoder/Metal-Learning-Projects/blob/master/2DDrawing/Star/Star/GameViewController.swift + +#include +using namespace metal; + + + + +struct VertexInOut +{ + float4 position [[position]]; + float4 color; +}; + +vertex VertexInOut passThroughVertex(uint vid [[ vertex_id ]], + constant packed_float4* position [[ buffer(0) ]], + constant packed_float4* color [[ buffer(1) ]]) +{ + VertexInOut outVertex; + + outVertex.position = position[vid]; + outVertex.color = color[vid]; + + return outVertex; +}; + +fragment half4 passThroughFragment(VertexInOut inFrag [[stage_in]]) +{ + return half4(inFrag.color); +}; From cf862c7035f1056fe875a5799b83037ff002a5d1 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Mon, 23 Sep 2024 10:45:10 -0500 Subject: [PATCH 23/33] added F functionality --- AudioLabSwift.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AudioLabSwift.xcodeproj/project.pbxproj b/AudioLabSwift.xcodeproj/project.pbxproj index abe97a7..0deb9d3 100644 --- a/AudioLabSwift.xcodeproj/project.pbxproj +++ b/AudioLabSwift.xcodeproj/project.pbxproj @@ -337,7 +337,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = F69WJ27LN4; + DEVELOPMENT_TEAM = APD62CDC25; INFOPLIST_FILE = AudioLabSwift/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -359,7 +359,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = F69WJ27LN4; + DEVELOPMENT_TEAM = APD62CDC25; INFOPLIST_FILE = AudioLabSwift/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( From cd7b0cfe9a29fd7bdc4f0097596fc0de42610730 Mon Sep 17 00:00:00 2001 From: Naim Alibhai Date: Fri, 11 Oct 2024 15:54:40 -0500 Subject: [PATCH 24/33] changes for module A and start for B --- AudioLabSwift.xcodeproj/project.pbxproj | 12 +- .../UserInterfaceState.xcuserstate | Bin 0 -> 57217 bytes .../xcdebugger/Breakpoints_v2.xcbkptlist | 24 +++ .../xcschemes/xcschememanagement.plist | 14 ++ AudioLabSwift/Base.lproj/Main.storyboard | 146 ++++++-------- AudioLabSwift/ModuleBViewController.swift | 95 +++++++++ AudioLabSwift/ViewController.swift | 190 ++++++------------ 7 files changed, 266 insertions(+), 215 deletions(-) create mode 100644 AudioLabSwift.xcodeproj/project.xcworkspace/xcuserdata/naimalibhai.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 AudioLabSwift.xcodeproj/xcuserdata/naimalibhai.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist create mode 100644 AudioLabSwift.xcodeproj/xcuserdata/naimalibhai.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 AudioLabSwift/ModuleBViewController.swift diff --git a/AudioLabSwift.xcodeproj/project.pbxproj b/AudioLabSwift.xcodeproj/project.pbxproj index 0deb9d3..afcfd18 100644 --- a/AudioLabSwift.xcodeproj/project.pbxproj +++ b/AudioLabSwift.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 10F0C9C42CB978CF007D711E /* ModuleBViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10F0C9C32CB978CF007D711E /* ModuleBViewController.swift */; }; B60B8A092ACCAFFD0083315D /* Shaders.metal in Sources */ = {isa = PBXBuildFile; fileRef = B60B8A082ACCAFFD0083315D /* Shaders.metal */; }; B65B5A5A24F4409F00E74E73 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B65B5A5924F4409F00E74E73 /* AppDelegate.swift */; }; B65B5A5C24F4409F00E74E73 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B65B5A5B24F4409F00E74E73 /* SceneDelegate.swift */; }; @@ -24,6 +25,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 10F0C9C32CB978CF007D711E /* ModuleBViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleBViewController.swift; sourceTree = ""; }; B60B8A082ACCAFFD0083315D /* Shaders.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = Shaders.metal; sourceTree = ""; }; B65B5A5624F4409F00E74E73 /* AudioLabSwift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AudioLabSwift.app; sourceTree = BUILT_PRODUCTS_DIR; }; B65B5A5924F4409F00E74E73 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -81,6 +83,7 @@ B65B5A5924F4409F00E74E73 /* AppDelegate.swift */, B65B5A5B24F4409F00E74E73 /* SceneDelegate.swift */, B65B5A5D24F4409F00E74E73 /* ViewController.swift */, + 10F0C9C32CB978CF007D711E /* ModuleBViewController.swift */, B65B5A7924F4523C00E74E73 /* AudioModel.swift */, B65B5A5F24F4409F00E74E73 /* Main.storyboard */, B65B5A6224F440A100E74E73 /* Assets.xcassets */, @@ -183,6 +186,7 @@ buildActionMask = 2147483647; files = ( B65B5A7A24F4523C00E74E73 /* AudioModel.swift in Sources */, + 10F0C9C42CB978CF007D711E /* ModuleBViewController.swift in Sources */, B65B5A7824F444CE00E74E73 /* CircularBuffer.m in Sources */, B65B5A7324F443A400E74E73 /* AudioFileReader.m in Sources */, B65B5A7024F440D600E74E73 /* Novocaine.m in Sources */, @@ -337,14 +341,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = APD62CDC25; + DEVELOPMENT_TEAM = UR943EC4CX; INFOPLIST_FILE = AudioLabSwift/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = SMU.AudioLabSwift; + PRODUCT_BUNDLE_IDENTIFIER = SMU.NaimEphraimCam.AudioLabSwift; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "AudioLabSwift/AudioLabSwift-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -359,14 +363,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = APD62CDC25; + DEVELOPMENT_TEAM = UR943EC4CX; INFOPLIST_FILE = AudioLabSwift/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = SMU.AudioLabSwift; + PRODUCT_BUNDLE_IDENTIFIER = SMU.NaimEphraimCam.AudioLabSwift; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "AudioLabSwift/AudioLabSwift-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/AudioLabSwift.xcodeproj/project.xcworkspace/xcuserdata/naimalibhai.xcuserdatad/UserInterfaceState.xcuserstate b/AudioLabSwift.xcodeproj/project.xcworkspace/xcuserdata/naimalibhai.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..b40a0a85d10cfefec1efa8e21cb7af34a62a10c0 GIT binary patch literal 57217 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

vp;QzMwbk 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 bWdXV1|F2y6idC!rJ{mUtef;-+Vf%jp#LVL~ literal 0 HcmV?d00001 diff --git a/AudioLabSwift.xcodeproj/xcuserdata/naimalibhai.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/AudioLabSwift.xcodeproj/xcuserdata/naimalibhai.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..d7b4649 --- /dev/null +++ b/AudioLabSwift.xcodeproj/xcuserdata/naimalibhai.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,24 @@ + + + + + + + + + diff --git a/AudioLabSwift.xcodeproj/xcuserdata/naimalibhai.xcuserdatad/xcschemes/xcschememanagement.plist b/AudioLabSwift.xcodeproj/xcuserdata/naimalibhai.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..8acb524 --- /dev/null +++ b/AudioLabSwift.xcodeproj/xcuserdata/naimalibhai.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + AudioLabSwift.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/AudioLabSwift/Base.lproj/Main.storyboard b/AudioLabSwift/Base.lproj/Main.storyboard index 0b2f5d8..a25999d 100644 --- a/AudioLabSwift/Base.lproj/Main.storyboard +++ b/AudioLabSwift/Base.lproj/Main.storyboard @@ -1,9 +1,9 @@ - + - + @@ -32,12 +32,6 @@ - @@ -45,90 +39,24 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - @@ -136,6 +64,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -144,10 +110,26 @@ - +