Compare commits

..

131 Commits

Author SHA1 Message Date
Syed Haris Ali c4fb3e27e7 moving these to new examples repo 2015-02-14 21:10:59 -08:00
Syed Haris Ali a6252b02dd adding utility classes 2015-02-14 21:08:35 -08:00
Syed Haris Ali 7f0bf3bb6c more cleanup 2015-02-14 19:23:48 -08:00
Syed Haris Ali 62b79ce6c2 got waveform data generating for interleaved and non-interleaved types 2015-02-14 19:17:50 -08:00
Syed Haris Ali 1415b5591e added copy 2015-02-14 19:01:42 -08:00
Syed Haris Ali ab892b9f74 added benchmark for speed test 2015-02-14 18:52:02 -08:00
Syed Haris Ali fde27c2db7 starting work on reading all data from audio file and then parsing data 2015-02-14 18:47:47 -08:00
Syed Haris Ali 1055e3b1dc cleaned up waveform getters and added synchronous fetching 2015-02-14 18:36:36 -08:00
Syed Haris Ali ceba0dd415 removed logging statement 2015-02-14 18:23:55 -08:00
Syed Haris Ali 6d6ae3e761 added property for total client frames in addition to total file frames 2015-02-14 18:02:30 -08:00
Syed Haris Ali 5261cb2961 fixed issue with total frames not properly recalculated for different sample rates 2015-02-14 16:33:40 -08:00
Syed Haris Ali 65491d453d added better way to obtain waveform data for all channels 2015-02-14 16:16:29 -08:00
Syed Haris Ali 4389a48875 made more progress, read is working and added mutex to prevent bogus buffer list err 2015-02-12 00:31:51 -08:00
Syed Haris Ali 3b4382986f trying to figure out why read is failing 2015-02-11 23:15:00 -08:00
Syed Haris Ali b327e5b140 added file format to initializer 2015-02-11 23:02:21 -08:00
Syed Haris Ali 73adf239ec added back metadata method 2015-02-11 23:02:21 -08:00
Syed Haris Ali c2c05b683b bit more cleanup in header 2015-02-11 23:02:21 -08:00
Syed Haris Ali 0bdade977d got simple initWithURL method back 2015-02-11 23:02:21 -08:00
Syed Haris Ali 3b0f32abaa added more robust file opening method 2015-02-11 23:02:21 -08:00
Syed Haris Ali 804cb1c3ef refactored process of creating EZAudioFile to allow read/write permissions on existing or new files 2015-02-11 23:02:21 -08:00
Syed Haris Ali faf64915d9 Merge pull request #131 from AlexEdunov/master
Fixed warnings with deprecated "canonical" flags
2015-02-08 01:06:29 -08:00
Alex Edunov b8b7a27261 Fixed warnings with deprecated "canonical" flags 2015-02-08 02:02:43 +03:00
Syed Haris Ali 7a751bcf53 Merge branch 'master' of https://github.com/syedhali/EZAudio 2014-12-09 23:14:34 -08:00
Syed Haris Ali 1588ca62f6 bumping to 0.0.6 2014-12-09 23:14:26 -08:00
Syed Haris Ali e21d17da26 Update README.md
Updated for version 0.0.6 cocoapod release
2014-09-21 12:01:30 +02:00
Syed Haris Ali 6f225bfcb5 final commit before updating Cocoapod 2014-09-21 11:24:48 +02:00
Syed Haris Ali d0181ce2eb tested all examples with code changes 2014-09-19 09:45:39 +02:00
Syed Haris Ali f274e6a330 removed breakpoints from testing 2014-09-19 09:40:45 +02:00
Syed Haris Ali ea04f3a508 Merge branch 'master' of https://github.com/syedhali/EZAudio 2014-09-19 09:37:10 +02:00
Syed Haris Ali 5487488318 updated project for xcode 6 2014-09-19 09:36:55 +02:00
Syed Haris Ali 55cdbfeaec got project working with Xcode 6 and removed old AudioUnitSampleType macros and replaced with floats 2014-09-16 21:33:21 +02:00
Syed Haris Ali 5215465565 Merge pull request #69 from dyelax/master
Thank you for contributing back to EZAudio! Fixed iOS 7 Deprecation Warnings.
2014-07-20 16:18:38 -07:00
dyelax 934e55a104 Fixed iOS 7 Deprecation Warnings.
Replaced AudioSessionSetProperty and AudioSessionGetProperty, which
were deprecated in iOS 7
2014-07-15 18:17:56 -04:00
Syed Haris Ali 5e3a299a8e Merge pull request #64 from kobelb/master
Uncommented lines of code that were causing the AudioPlot to have a gap ...
2014-06-30 11:23:44 -07:00
kobelb c2ae03aaae Uncommented lines of code that were causing the AudioPlot to have a gap when reading from the buffer 2014-06-29 11:05:43 -04:00
Syed Haris Ali b443b8d385 Updated for 0.0.5 version 2014-06-21 15:18:36 -07:00
Syed Haris Ali fb777f81ed Updated for 0.0.5 cocoapod release (removed warnings) 2014-05-29 00:03:18 -07:00
Syed Haris Ali 78dc908a6d Cleaned up some stuff 2014-05-28 23:46:53 -07:00
Syed Haris Ali b5880bce5a Added AudioStreamBasicDescription for iLBC format 2014-05-07 16:41:05 +08:00
Syed Haris Ali 9277585014 Updated for 0.0.5 release 2014-05-07 15:51:46 +08:00
Syed Haris Ali 346d0f93fc Finished documenting new recording types 2014-05-07 15:51:12 +08:00
Syed Haris Ali 77af3da1a3 Cleaned up documentation blocks 2014-05-07 15:50:38 +08:00
Syed Haris Ali 1705daf0d8 Refactored plot to make it easier to subclass 2014-05-07 15:49:33 +08:00
Syed Haris Ali ddadfc512c Added pitch shifting to play example 2014-05-05 16:31:32 +08:00
Syed Haris Ali afcfa793ca Updated EZRecorder to allow multiple formats (compressed + uncompressed) 2014-05-05 16:29:58 +08:00
Syed Haris Ali 895606b46e Merge pull request #43 from andykorth/master
Set EAGLContext.currentContext before each set of GL calls.
2014-04-04 12:57:57 -07:00
Andy Korth 873ce017ea Set EAGLContext.currentContext before each set of GL calls. 2014-04-04 14:38:09 -05:00
Syed Haris Ali 584256f20f Updated for 0.0.4 updates 2014-03-17 00:18:11 -07:00
Syed Haris Ali a957fed152 Added getter for AudioStreamBasicDescription for EZOutput and made EZRecorder erase file if it already exists 2014-03-15 14:38:10 -07:00
Syed Haris Ali 7eac04b186 Updated podspec 2014-03-05 22:03:45 -08:00
Syed Haris Ali 3030569caa Tweaked changelog 2014-03-05 21:59:43 -08:00
Syed Haris Ali 7a5971208c Merge pull request #34 from srubin/master
waveform data was only getting computed for part of an audio file, not the entire file
2014-03-05 21:25:37 -08:00
Syed Haris Ali 86601e7079 Merge pull request #36 from cjhanson/master
Fixes for very small wave files
2014-03-05 21:25:13 -08:00
CJ Hanson e0161288b4 Fixes for very small wave files 2014-03-05 21:18:22 -08:00
Syed Haris Ali 1b8fa4f1eb Merge pull request #35 from cjhanson/master
Fix crash on very short wave files by setting min frames to at least 1
2014-03-05 21:12:54 -08:00
CJ Hanson ab20d29a71 Fix crash on very short wave files by setting min frames to at least 1 2014-03-05 20:05:33 -08:00
Steve Rubin 3da3f8ad3d waveform data was only getting computed for part of an audio file, not the entire file 2014-02-27 10:41:46 -08:00
Syed Haris Ali 4cdccac2e0 Merge branch 'master' of https://github.com/syedhali/EZAudio 2014-02-26 23:24:35 -08:00
Syed Haris Ali 54f82b9657 Adding in final tweaks for next cocoapods release 2014-02-26 23:24:19 -08:00
Syed Haris Ali 25fef28b40 Merge pull request #26 from chrisballinger/delegatefix
Remove call to EZMicrophone float delegate if selector is not implemented
2014-02-19 07:57:33 -08:00
Chris Ballinger ec72b33b2f Remove call to EZMicrophone float delegate if selector is not implemented 2014-02-17 15:23:14 -08:00
Syed Haris Ali 993883488b Added AVFoundation framework to all iOS example projects 2014-02-16 12:27:56 -08:00
Syed Haris Ali 56b17904e3 Tweaked EZOutputDataSource formatting 2014-02-16 12:25:53 -08:00
Syed Haris Ali 9f2b6b9c3e Adding EZAudioPlayer to EZAudio main includes 2014-02-16 12:25:05 -08:00
Syed Haris Ali aa23d9e375 Merge branch 'master' of https://github.com/syedhali/EZAudio 2014-02-16 12:24:19 -08:00
Syed Haris Ali b91675349c Adding EZAudioPlayer 2014-02-16 12:23:54 -08:00
Syed Haris Ali 29ce0141ce Updated EZOutput section
Updated EZOutput section for new EZOutputDataSource method
2014-02-11 10:30:40 -08:00
Syed Haris Ali e90a48a367 Removed old EZOutputDataSource method output:needsBufferListWithFrames:withBufferSize: and replaced with output:shouldFillAudioBufferList:withNumberOfFrames: (passes in allocated AudioBufferList that just needs to be filled 2014-02-10 23:12:43 -08:00
Syed Haris Ali f482234fae Updated examples to use updated EZOutputDataSource method output:shouldFillAudioBufferList:withNumberOfFrames: instead of the old method. Much cleaner and much more stable 2014-02-10 23:11:39 -08:00
Syed Haris Ali 01352aed7d Modified to use new the EZAudio audioBufferListWithNumberOfFrames:numberOfChannels:interleaved: allocator for async waveform generation 2014-02-10 23:09:34 -08:00
Syed Haris Ali b992ce964a Removed internal AudioBufferList allocations for each buffer. Instead the EZAudio audioBufferList class method needs frames, channels, and interleaved parameters to properly allocate the resources ahead of time so the read function in the EZAudioFile assumes a proper AudioBufferList with internal buffer allocations is passed in 2014-02-10 23:08:16 -08:00
Syed Haris Ali 76f1e35774 Rewrote audioBufferList allocator to also allocate internal buffers. Also, added stereo float non-interleaved description helper 2014-02-10 23:06:48 -08:00
Syed Haris Ali ead5c84ee3 Reconfigured EZOutputDataSource to provide AudioBufferList to fill with audio data instead of asking the EZOutputDataSource to allocate its own buffers (much more stable) 2014-02-10 23:05:50 -08:00
Syed Haris Ali 5c21e8d097 Removed excess logging 2014-02-10 23:04:42 -08:00
Syed Haris Ali 10208fdc27 Fixed casting issue from unsigned long to int32_t 2014-02-10 23:04:15 -08:00
Syed Haris Ali da8f9dfcbe Added clear (virtual-isc) method to EZPlot for subclasses to override 2014-02-10 23:03:24 -08:00
Syed Haris Ali 4a8fd7bee5 Merge branch 'master' of https://github.com/syedhali/EZAudio 2014-02-03 10:32:14 -08:00
Syed Haris Ali 4b6d2327d4 Added manual close function 2014-02-03 10:31:54 -08:00
Syed Haris Ali ede1fdc404 Update README.md 2014-01-30 19:15:43 -08:00
Syed Haris Ali 0363d977f0 Update README.md 2014-01-26 23:49:11 -08:00
Syed Haris Ali c5e8dbc368 Updated pod to 0.0.3 2014-01-25 19:57:31 -08:00
Syed Haris Ali 21205284f5 Tweaked PlayExamples to use global AudioBufferList instead of instances depending on EZOutput for freeing. Fixed memory issues with bogus AudioBufferLists 2014-01-21 23:55:05 -08:00
Syed Haris Ali cd5ca676eb Output no longer frees AudioBufferList from datasource (it is assumed the caller if providing a global preallocated audio buffer list). Also added tweaks to make sure silence occurs on nil or NULL values 2014-01-21 23:53:44 -08:00
Syed Haris Ali cf0e36e858 Removed autorelease pool from microphone callbacks and tweaked number of channels on AudioBufferList (was accidently 1 instead of pulling from stream format) 2014-01-21 23:50:56 -08:00
Syed Haris Ali 684056541f Added _sampleData initialization and check before freeing 2014-01-21 23:43:54 -08:00
Syed Haris Ali 5b9f5ec8ef Tweaked to only allocate read buffers if they haven't been previously allocated (lighter memory footprint) 2014-01-21 23:43:15 -08:00
Syed Haris Ali b4d607e9d5 Added check to make sure AudioBufferList has AudioBuffers to free 2014-01-21 23:41:36 -08:00
Syed Haris Ali 47531a99a0 Updated podspec for 0.0.3 2014-01-17 14:25:02 -08:00
Syed Haris Ali 618be0dc70 Last change to CHANGELOG before 0.0.3 release 2014-01-17 14:15:40 -08:00
Syed Haris Ali 579f924aef Added stereo to EZOutput for circular buffer. This only copies the channel over though, need a EZConverter to do this properly to interleave the data 2014-01-17 14:12:03 -08:00
Syed Haris Ali 43418bb825 removed accidental add 2014-01-17 12:59:54 -08:00
Syed Haris Ali f474b55375 Cleaned up a bit 2014-01-17 12:59:37 -08:00
Syed Haris Ali 232b02904c Updated for 0.0.3 release 2014-01-17 12:59:05 -08:00
Syed Haris Ali b093e479d7 Added ability to adjust the resolution of the waveform for the audio file and added getter for audio file's URL 2014-01-17 12:55:39 -08:00
Syed Haris Ali 98707d0cf1 Added adjustable rolling waveform resolution 2014-01-17 12:52:22 -08:00
Syed Haris Ali fc091642d9 Fixed a few bugs related to allocating the proper write buffers for the AudioBufferList and added stereo support 2014-01-17 12:34:31 -08:00
Syed Haris Ali 5cd022a510 Refactored EZMicrophone for readability - broke configuration into smaller functions and organized layout 2014-01-17 12:31:20 -08:00
Syed Haris Ali c8b144667a Cleaned up for 0.0.3 release 2014-01-12 00:08:44 -05:00
Syed Haris Ali 4d0bd00c79 Removed excess logging 2014-01-12 00:07:23 -05:00
Syed Haris Ali fb0e1cff2d Modified example to use custom AudioStreamBasicDescription 2014-01-12 00:05:58 -05:00
Syed Haris Ali 4d2c8f3fef Updated OSX PlayFile example to use custom AudioStreamBasicDescription on EZOutput 2014-01-12 00:04:20 -05:00
Syed Haris Ali 1dd7c7bec9 Accidently deleted EZMicrophone object, added back...whoops 2014-01-11 23:19:50 -05:00
Syed Haris Ali 4622815a19 Updated PlayFile to use custom AudioStreamBasicDescription pulled from file client format 2014-01-11 23:18:36 -05:00
Syed Haris Ali 512f896dff Updated FFT example for memory cleanup 2014-01-11 23:18:00 -05:00
Syed Haris Ali fd837d46ea Updated CHANGELOG to reflect new rolling plot and custom EZMicrophone and EZOutput 2014-01-11 23:16:47 -05:00
Syed Haris Ali 689e004814 Added custom class AudioStreamBasicDescription initializer to output 2014-01-11 22:36:02 -05:00
Syed Haris Ali ef6b6ee5a7 Modified EZPlotTypeRolling to scroll instead of wipe screen 2014-01-11 22:20:35 -05:00
Syed Haris Ali 8d8246c3c6 Added utility functions for AudioStreamBasicDescription, Math (update history and shift array contents), and Plot (shared rolling history update function) 2014-01-11 22:19:02 -05:00
Syed Haris Ali 1dc2fc2ba9 Screwed up something in EZAudioFile memory cleanup, removing floatBuffers cleanup 2014-01-08 23:16:32 -05:00
Syed Haris Ali 2431b25204 Fixed getWaveformData function to not use float converter and added proper memory management to EZAudioFile 2014-01-08 23:03:39 -05:00
Syed Haris Ali 2e9af21099 Merge branch 'master' of https://github.com/syedhali/EZAudio 2014-01-08 22:28:25 -05:00
Syed Haris Ali b06bbb99fb Fixed EZAudioFile and EZOutput to provide true stereo 2014-01-08 22:28:00 -05:00
Syed Haris Ali 2ac8e4ec42 Merge pull request #5 from wdwen/master
EZAudioPlotGLKViewController add _bgcolor member data, record bgcolor. _...
2014-01-04 18:41:15 -08:00
wdwen 6739c70063 remove two call of glClear in glkView:drawInRect: 2014-01-05 10:37:53 +08:00
wdwen ec55542b8d remove bgcolor data member.
add glClear(GL_COLOR_BUFFER_BIT) method at begin of glkView:drawInRect
2014-01-05 10:14:32 +08:00
daiwen 08874048fc EZAudioPlotGLKViewController add _bgcolor member data, record bgcolor. _bgcolor used for hand glkView:drawInRect else condition 2014-01-02 19:13:54 +08:00
Syed Haris Ali cd887d002d Merge branch 'master' of https://github.com/syedhali/EZAudio 2013-12-31 11:15:40 -08:00
Syed Haris Ali 0e274696bf Client format for audio file uses sample rate pulled from file format 2013-12-31 11:15:27 -08:00
Syed Haris Ali 820e0d3f21 Added EZAudioFFTExample to README 2013-12-30 14:54:05 -08:00
Syed Haris Ali a5ff527a3a Merge branch 'master' of https://github.com/syedhali/EZAudio 2013-12-30 14:22:41 -08:00
Syed Haris Ali d725db31bc Added MIT license to example files 2013-12-30 14:21:18 -08:00
Syed Haris Ali b9f80eeee7 Adding in FFT example projects 2013-12-30 14:15:03 -08:00
Syed Haris Ali 54345f2d6e Fixed typo in EZAudioPlotGL readme section 2013-12-30 08:54:42 -08:00
Syed Haris Ali 6cafae6236 Update documentation section 2013-12-25 09:32:59 -08:00
Syed Haris Ali e0c4d3adca Update README.md 2013-12-24 18:45:13 -08:00
Syed Haris Ali b8513ab62d Added Acknowledgements 2013-12-24 18:43:39 -08:00
Syed Haris Ali f31b90a4e2 Adding Cocoapod how to 2013-12-24 00:56:05 -08:00
Syed Haris Ali da4fdb6692 Updated version 2013-12-23 21:48:31 -08:00
Syed Haris Ali 567e0f61e9 Updated podspec 2013-12-23 21:40:22 -08:00
Syed Haris Ali cd160bd3f6 Added .c extension to Cocoapod podspec 2013-12-23 21:37:38 -08:00
Syed Haris Ali c0c4d2f205 Finished podspec 2013-12-22 19:20:37 -08:00
125 changed files with 7968 additions and 1894 deletions
Vendored
BIN
View File
Binary file not shown.
+13 -129
View File
@@ -1,133 +1,17 @@
#
# Be sure to run `pod spec lint EZAudio.podspec' to ensure this is a
# valid spec and to remove all comments including this before submitting the spec.
#
# To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html
# To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/
#
Pod::Spec.new do |s|
# ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# These will help people to find your library, and whilst it
# can feel like a chore to fill in it's definitely to your advantage. The
# summary should be tweet-length, and the description more in depth.
#
s.name = "EZAudio"
s.version = "0.0.1"
s.summary = "A short description of EZAudio."
s.description = <<-DESC
A longer description of EZAudio in Markdown format.
* Think: Why did you write this? What is the focus? What does it do?
* CocoaPods will be using this to generate tags, and improve search results.
* Try to keep it short, snappy and to the point.
* Finally, don't worry about the indent, CocoaPods strips it!
DESC
s.homepage = "http://EXAMPLE/EZAudio"
# s.screenshots = "www.example.com/screenshots_1", "www.example.com/screenshots_2"
# ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# Licensing your code is important. See http://choosealicense.com for more info.
# CocoaPods will detect a license file if there is a named LICENSE*
# Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'.
#
s.license = 'MIT (example)'
# s.license = { :type => 'MIT', :file => 'FILE_LICENSE' }
# ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# Specify the authors of the library, with email addresses. Email addresses
# of the authors by using the SCM log. E.g. $ git log. If no email can be
# found CocoaPods accept just the names.
#
s.version = "0.0.6"
s.summary = "A simple, intuitive audio framework for iOS and OSX useful for anyone doing audio processing and/or audio-based visualizations."
s.homepage = "http://syedharisali.com/projects/EZAudio/getting-started"
s.screenshots = "https://s3-us-west-1.amazonaws.com/ezaudio-media/EZAudioSummary.png"
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { "Syed Haris Ali" => "syedhali07@gmail.com" }
# s.authors = { "Syed Haris Ali" => "syedhali07@gmail.com", "other author" => "email@address.com" }
# s.author = 'Syed Haris Ali', 'other author'
# ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# If this Pod runs only on iOS or OS X, then specify the platform and
# the deployment target. You can optionally include the target after the platform.
#
# s.platform = :ios
# s.platform = :ios, '5.0'
# When using multiple platforms
# s.ios.deployment_target = '5.0'
# s.osx.deployment_target = '10.7'
# ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# Specify the location from where the source should be retrieved.
# Supports git, hg, svn and HTTP.
#
s.source = { :git => "http://EXAMPLE/EZAudio.git", :tag => "0.0.1" }
# ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# CocoaPods is smart about how it includes source code. For source files
# giving a folder will include any h, m, mm, c & cpp files. For header
# files it will include any header in the folder.
# Not including the public_header_files will make all headers public.
#
s.source_files = 'Classes', 'Classes/**/*.{h,m}'
s.exclude_files = 'Classes/Exclude'
# s.public_header_files = 'Classes/**/*.h'
# ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# A list of resources included with the Pod. These are copied into the
# target bundle with a build phase script. Anything else will be cleaned.
# You can preserve files from being cleaned, please don't preserve
# non-essential files like tests, examples and documentation.
#
# s.resource = "icon.png"
# s.resources = "Resources/*.png"
# s.preserve_paths = "FilesToSave", "MoreFilesToSave"
# ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# Link your library with frameworks, or libraries. Libraries do not include
# the lib prefix of their name.
#
# s.framework = 'SomeFramework'
# s.frameworks = 'SomeFramework', 'AnotherFramework'
# s.library = 'iconv'
# s.libraries = 'iconv', 'xml2'
# ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# If your library depends on compiler flags you can set them in the xcconfig hash
# where they will only apply to your library. If you depend on other Podspecs
# you can include multiple dependencies to ensure it works.
# s.requires_arc = true
# s.xcconfig = { 'HEADER_SEARCH_PATHS' => '$(SDKROOT)/usr/include/libxml2' }
# s.dependency 'JSONKit', '~> 1.4'
s.ios.deployment_target = '6.0'
s.osx.deployment_target = '10.8'
s.source = { :git => "https://github.com/syedhali/EZAudio.git", :tag => "0.0.6" }
s.source_files = 'EZAudio/*.{h,m,c}'
s.exclude_files = 'EZAudio/VERSION'
s.ios.frameworks = 'AudioToolbox','AVFoundation','GLKit'
s.osx.frameworks = 'AudioToolbox','AudioUnit','CoreAudio','QuartzCore','OpenGL','GLKit'
s.requires_arc = true;
end
-124
View File
@@ -1,124 +0,0 @@
//
// AEFloatConverter.h
// The Amazing Audio Engine
//
// Created by Michael Tyson on 25/10/2012.
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
#ifdef __cplusplus
extern "C" {
#endif
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
/*!
* Universal converter to float format
*
* Use this class to easily convert arbitrary audio formats to floating point
* for use with utilities like the Accelerate framework.
*/
@interface AEFloatConverter : NSObject
/*!
* Initialize
*
* @param sourceFormat The audio format to use
*/
- (id)initWithSourceFormat:(AudioStreamBasicDescription)sourceFormat;
/*!
* Convert audio to floating-point
*
* This C function, safe to use in a Core Audio realtime thread context, will take
* an audio buffer list of audio in the format you provided at initialisation, and
* convert it into a noninterleaved float array.
*
* @param converter Pointer to the converter object.
* @param sourceBuffer An audio buffer list containing the source audio.
* @param targetBuffers An array of floating-point arrays to store the converted float audio into.
* Note that you must provide the correct number of arrays, to match the number of channels.
* @param frames The number of frames to convert.
* @return YES on success; NO on failure
*/
BOOL AEFloatConverterToFloat(AEFloatConverter* converter, AudioBufferList *sourceBuffer, float * const * targetBuffers, UInt32 frames);
/*!
* Convert audio to floating-point, in a buffer list
*
* This C function, safe to use in a Core Audio realtime thread context, will take
* an audio buffer list of audio in the format you provided at initialisation, and
* convert it into a noninterleaved float format.
*
* @param converter Pointer to the converter object.
* @param sourceBuffer An audio buffer list containing the source audio.
* @param targetBuffer An audio buffer list to store the converted floating-point audio.
* @param frames The number of frames to convert.
* @return YES on success; NO on failure
*/
BOOL AEFloatConverterToFloatBufferList(AEFloatConverter* converter, AudioBufferList *sourceBuffer, AudioBufferList *targetBuffer, UInt32 frames);
/*!
* Convert audio from floating-point
*
* This C function, safe to use in a Core Audio realtime thread context, will take
* an audio buffer list of audio in the format you provided at initialisation, and
* convert it into a float array.
*
* @param converter Pointer to the converter object.
* @param sourceBuffers An array of floating-point arrays containing the floating-point audio to convert.
* Note that you must provide the correct number of arrays, to match the number of channels.
* @param targetBuffer An audio buffer list to store the converted audio into.
* @param frames The number of frames to convert.
* @return YES on success; NO on failure
*/
BOOL AEFloatConverterFromFloat(AEFloatConverter* converter, float * const * sourceBuffers, AudioBufferList *targetBuffer, UInt32 frames);
/*!
* Convert audio from floating-point, in a buffer list
*
* This C function, safe to use in a Core Audio realtime thread context, will take
* an audio buffer list of audio in the format you provided at initialisation, and
* convert it into a float array.
*
* @param converter Pointer to the converter object.
* @param sourceBuffer An audio buffer list containing the source audio.
* @param targetBuffer An audio buffer list to store the converted audio into.
* @param frames The number of frames to convert.
* @return YES on success; NO on failure
*/
BOOL AEFloatConverterFromFloatBufferList(AEFloatConverter* converter, AudioBufferList *sourceBuffer, AudioBufferList *targetBuffer, UInt32 frames);
/*!
* The AudioStreamBasicDescription representing the converted floating-point format
*/
@property (nonatomic, readonly) AudioStreamBasicDescription floatingPointAudioDescription;
/*!
* The source audio format set at initialization
*/
@property (nonatomic, readonly) AudioStreamBasicDescription sourceFormat;
@end
#ifdef __cplusplus
}
#endif
-211
View File
@@ -1,211 +0,0 @@
//
// AEFloatConverter.m
// The Amazing Audio Engine
//
// Created by Michael Tyson on 25/10/2012.
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
#import "AEFloatConverter.h"
#define checkResult(result,operation) (_checkResult((result),(operation),strrchr(__FILE__, '/')+1,__LINE__))
static inline BOOL _checkResult(OSStatus result, const char *operation, const char* file, int line) {
if ( result != noErr ) {
NSLog(@"%s:%d: %s result %d %08X %4.4s", file, line, operation, (int)result, (int)result, (char*)&result);
return NO;
}
return YES;
}
#define kNoMoreDataErr -2222
struct complexInputDataProc_t {
AudioBufferList *sourceBuffer;
};
@interface AEFloatConverter () {
AudioStreamBasicDescription _sourceAudioDescription;
AudioStreamBasicDescription _floatAudioDescription;
AudioConverterRef _toFloatConverter;
AudioConverterRef _fromFloatConverter;
AudioBufferList *_scratchFloatBufferList;
}
static OSStatus complexInputDataProc(AudioConverterRef inAudioConverter,
UInt32 *ioNumberDataPackets,
AudioBufferList *ioData,
AudioStreamPacketDescription **outDataPacketDescription,
void *inUserData);
@end
@implementation AEFloatConverter
@synthesize sourceFormat = _sourceAudioDescription;
-(id)initWithSourceFormat:(AudioStreamBasicDescription)sourceFormat {
if ( !(self = [super init]) ) return nil;
_floatAudioDescription.mFormatID = kAudioFormatLinearPCM;
_floatAudioDescription.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved;
_floatAudioDescription.mChannelsPerFrame = sourceFormat.mChannelsPerFrame;
_floatAudioDescription.mBytesPerPacket = sizeof(float);
_floatAudioDescription.mFramesPerPacket = 1;
_floatAudioDescription.mBytesPerFrame = sizeof(float);
_floatAudioDescription.mBitsPerChannel = 8 * sizeof(float);
_floatAudioDescription.mSampleRate = sourceFormat.mSampleRate;
_sourceAudioDescription = sourceFormat;
if ( memcmp(&sourceFormat, &_floatAudioDescription, sizeof(AudioStreamBasicDescription)) != 0 ) {
checkResult(AudioConverterNew(&sourceFormat, &_floatAudioDescription, &_toFloatConverter), "AudioConverterNew");
checkResult(AudioConverterNew(&_floatAudioDescription, &sourceFormat, &_fromFloatConverter), "AudioConverterNew");
_scratchFloatBufferList = (AudioBufferList*)malloc(sizeof(AudioBufferList) + (_floatAudioDescription.mChannelsPerFrame-1)*sizeof(AudioBuffer));
_scratchFloatBufferList->mNumberBuffers = _floatAudioDescription.mChannelsPerFrame;
for ( int i=0; i<_scratchFloatBufferList->mNumberBuffers; i++ ) {
_scratchFloatBufferList->mBuffers[i].mNumberChannels = 1;
}
}
return self;
}
-(void)dealloc {
if ( _toFloatConverter ) AudioConverterDispose(_toFloatConverter);
if ( _fromFloatConverter ) AudioConverterDispose(_fromFloatConverter);
if ( _scratchFloatBufferList ) free(_scratchFloatBufferList);
// [super dealloc];
}
BOOL AEFloatConverterToFloat(AEFloatConverter* THIS, AudioBufferList *sourceBuffer, float * const * targetBuffers, UInt32 frames) {
if ( frames == 0 ) return YES;
if ( THIS->_toFloatConverter ) {
UInt32 priorDataByteSize = sourceBuffer->mBuffers[0].mDataByteSize;
for ( int i=0; i<sourceBuffer->mNumberBuffers; i++ ) {
sourceBuffer->mBuffers[i].mDataByteSize = frames * THIS->_sourceAudioDescription.mBytesPerFrame;
}
for ( int i=0; i<THIS->_scratchFloatBufferList->mNumberBuffers; i++ ) {
THIS->_scratchFloatBufferList->mBuffers[i].mData = targetBuffers[i];
THIS->_scratchFloatBufferList->mBuffers[i].mDataByteSize = frames * sizeof(float);
}
OSStatus result = AudioConverterFillComplexBuffer(THIS->_toFloatConverter,
complexInputDataProc,
&(struct complexInputDataProc_t) { .sourceBuffer = sourceBuffer },
&frames,
THIS->_scratchFloatBufferList,
NULL);
for ( int i=0; i<sourceBuffer->mNumberBuffers; i++ ) {
sourceBuffer->mBuffers[i].mDataByteSize = priorDataByteSize;
}
if ( !checkResult(result, "AudioConverterConvertComplexBuffer") ) {
return NO;
}
} else {
for ( int i=0; i<sourceBuffer->mNumberBuffers; i++ ) {
memcpy(targetBuffers[i], sourceBuffer->mBuffers[i].mData, frames * sizeof(float));
}
}
return YES;
}
BOOL AEFloatConverterToFloatBufferList(AEFloatConverter* converter, AudioBufferList *sourceBuffer, AudioBufferList *targetBuffer, UInt32 frames) {
assert(targetBuffer->mNumberBuffers == converter->_floatAudioDescription.mChannelsPerFrame);
float *targetBuffers[targetBuffer->mNumberBuffers];
for ( int i=0; i<targetBuffer->mNumberBuffers; i++ ) {
targetBuffers[i] = (float*)targetBuffer->mBuffers[i].mData;
}
return AEFloatConverterToFloat(converter, sourceBuffer, targetBuffers, frames);
}
BOOL AEFloatConverterFromFloat(AEFloatConverter* THIS, float * const * sourceBuffers, AudioBufferList *targetBuffer, UInt32 frames) {
if ( frames == 0 ) return YES;
if ( THIS->_fromFloatConverter ) {
for ( int i=0; i<THIS->_scratchFloatBufferList->mNumberBuffers; i++ ) {
THIS->_scratchFloatBufferList->mBuffers[i].mData = sourceBuffers[i];
THIS->_scratchFloatBufferList->mBuffers[i].mDataByteSize = frames * sizeof(float);
}
UInt32 priorDataByteSize = targetBuffer->mBuffers[0].mDataByteSize;
for ( int i=0; i<targetBuffer->mNumberBuffers; i++ ) {
targetBuffer->mBuffers[i].mDataByteSize = frames * THIS->_sourceAudioDescription.mBytesPerFrame;
}
OSStatus result = AudioConverterFillComplexBuffer(THIS->_fromFloatConverter,
complexInputDataProc,
&(struct complexInputDataProc_t) { .sourceBuffer = THIS->_scratchFloatBufferList },
&frames,
targetBuffer,
NULL);
for ( int i=0; i<targetBuffer->mNumberBuffers; i++ ) {
targetBuffer->mBuffers[i].mDataByteSize = priorDataByteSize;
}
if ( !checkResult(result, "AudioConverterConvertComplexBuffer") ) {
return NO;
}
} else {
for ( int i=0; i<targetBuffer->mNumberBuffers; i++ ) {
memcpy(targetBuffer->mBuffers[i].mData, sourceBuffers[i], frames * sizeof(float));
}
}
return YES;
}
BOOL AEFloatConverterFromFloatBufferList(AEFloatConverter* converter, AudioBufferList *sourceBuffer, AudioBufferList *targetBuffer, UInt32 frames) {
assert(sourceBuffer->mNumberBuffers == converter->_floatAudioDescription.mChannelsPerFrame);
float *sourceBuffers[sourceBuffer->mNumberBuffers];
for ( int i=0; i<sourceBuffer->mNumberBuffers; i++ ) {
sourceBuffers[i] = (float*)sourceBuffer->mBuffers[i].mData;
}
return AEFloatConverterFromFloat(converter, sourceBuffers, targetBuffer, frames);
}
static OSStatus complexInputDataProc(AudioConverterRef inAudioConverter,
UInt32 *ioNumberDataPackets,
AudioBufferList *ioData,
AudioStreamPacketDescription **outDataPacketDescription,
void *inUserData) {
struct complexInputDataProc_t *arg = (struct complexInputDataProc_t*)inUserData;
if ( !arg->sourceBuffer ) {
return kNoMoreDataErr;
}
memcpy(ioData, arg->sourceBuffer, sizeof(AudioBufferList) + (arg->sourceBuffer->mNumberBuffers-1)*sizeof(AudioBuffer));
arg->sourceBuffer = NULL;
return noErr;
}
-(AudioStreamBasicDescription)floatingPointAudioDescription {
return _floatAudioDescription;
}
@end
+148 -15
View File
@@ -25,25 +25,29 @@
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
#pragma mark - 3rd Party Utilties
#import "AEFloatConverter.h"
#import "TPCircularBuffer.h"
#pragma mark - Core Components
#import "EZAudioFile.h"
#import "EZMicrophone.h"
#import "EZOutput.h"
#import "EZRecorder.h"
#pragma mark - Interface Components
#import "EZPlot.h"
#import "EZAudioPlot.h"
#import "EZAudioPlotGL.h"
#import "EZAudioPlotGLKViewController.h"
//#pragma mark - Core Components
//#import "EZAudioFile.h"
//#import "EZMicrophone.h"
//#import "EZOutput.h"
//#import "EZRecorder.h"
//
//#pragma mark - Extended Components
//#import "EZAudioPlayer.h"
//
//#pragma mark - Interface Components
//#import "EZPlot.h"
//#import "EZAudioPlot.h"
//#import "EZAudioPlotGL.h"
//#import "EZAudioPlotGLKViewController.h"
/**
EZAudio is a simple, intuitive framework for iOS and OSX. The goal of EZAudio was to provide a modular, cross-platform framework to simplify performing everyday audio operations like getting microphone input, creating audio waveforms, recording/playing audio files, etc. The visualization tools like the EZAudioPlot and EZAudioPlotGL were created to plug right into the framework's various components and provide highly optimized drawing routines that work in harmony with audio callback loops. All components retain the same namespace whether you're on an iOS device or a Mac computer so an EZAudioPlot understands it will subclass an UIView on an iOS device or an NSView on a Mac.
Class methods for EZAudio are provided as utility methods used throughout the other modules within the framework. For instance, these methods help make sense of error codes (checkResult:operation:), map values betwen coordinate systems (MAP:leftMin:leftMax:rightMin:rightMax:), calculate root mean squared values for buffers (RMS:length:), etc.
*/
@interface EZAudio : NSObject
@@ -55,9 +59,14 @@
/**
Allocates an AudioBufferList structure. Make sure to call freeBufferList when done using AudioBufferList or it will leak.
@param frames The number of frames that will be stored within each audio buffer
@param channels The number of channels (e.g. 2 for stereo, 1 for mono, etc.)
@param interleaved Whether the samples will be interleaved (if not it will be assumed to be non-interleaved and each channel will have an AudioBuffer allocated)
@return An AudioBufferList struct that has been allocated in memory
*/
+(AudioBufferList*)audioBufferList;
+(AudioBufferList *)audioBufferListWithNumberOfFrames:(UInt32)frames
numberOfChannels:(UInt32)channels
interleaved:(BOOL)interleaved;
/**
Deallocates an AudioBufferList structure from memory.
@@ -66,6 +75,86 @@
+(void)freeBufferList:(AudioBufferList*)bufferList;
#pragma mark - AudioStreamBasicDescription Utilties
///-----------------------------------------------------------
/// @name Creating An AudioStreamBasicDescription
///-----------------------------------------------------------
/**
@param channels The desired number of channels
@param sampleRate The desired sample rate
@return A new AudioStreamBasicDescription with the specified format.
*/
+(AudioStreamBasicDescription)AIFFFormatWithNumberOfChannels:(UInt32)channels
sampleRate:(float)sampleRate;
/**
@param sampleRate The desired sample rate
@return A new AudioStreamBasicDescription with the specified format.
*/
+(AudioStreamBasicDescription)iLBCFormatWithSampleRate:(float)sampleRate;
/**
Checks an AudioStreamBasicDescription for an interleaved flag, meaning samples are
stored in one buffer one after another instead of two (or n channels) parallel buffers
@param asbd A valid AudioStreamBasicDescription
@return A BOOL indicating whether or not the AudioStreamBasicDescription is interleaved
*/
+ (BOOL) isInterleaved:(AudioStreamBasicDescription)asbd;
/**
Checks an AudioStreamBasicDescription to see if it is linear PCM, which is an
uncompressed, non-variable bit rate type format
@param asbd A valid AudioStreamBasicDescription
@return A BOOL indicating whether or not the AudioStreamBasicDescription is linear PCM
*/
+ (BOOL) isLinearPCM:(AudioStreamBasicDescription)asbd;
/**
@param channels The desired number of channels
@param sampleRate The desired sample rate
@return A new AudioStreamBasicDescription with the specified format.
*/
+(AudioStreamBasicDescription)M4AFormatWithNumberOfChannels:(UInt32)channels
sampleRate:(float)sampleRate;
/**
@param sampleRate The desired sample rate
@return A new AudioStreamBasicDescription with the specified format.
*/
+(AudioStreamBasicDescription)monoFloatFormatWithSampleRate:(float)sampleRate;
/**
@param sampleRate The desired sample rate
@return A new AudioStreamBasicDescription with the specified format.
*/
+(AudioStreamBasicDescription)monoCanonicalFormatWithSampleRate:(float)sampleRate;
/**
@param sampleRate The desired sample rate
@return A new AudioStreamBasicDescription with the specified format.
*/
+(AudioStreamBasicDescription)stereoCanonicalNonInterleavedFormatWithSampleRate:(float)sampleRate;
/**
@param sampleRate The desired sample rate
@return A new AudioStreamBasicDescription with the specified format.
*/
+(AudioStreamBasicDescription)stereoFloatInterleavedFormatWithSampleRate:(float)sampleRate;
/**
@param sampleRate The desired sample rate
@return A new AudioStreamBasicDescription with the specified format.
*/
+(AudioStreamBasicDescription)stereoFloatNonInterleavedFormatWithSampleRate:(float)sameRate;
///-----------------------------------------------------------
/// @name AudioStreamBasicDescription Utilities
///-----------------------------------------------------------
@@ -82,7 +171,7 @@
@param nChannels The number of expected channels on the description
@param interleaved A flag indicating whether the stereo samples should be interleaved in the buffer
*/
+(void)setCanonicalAudioStreamBasicDescription:(AudioStreamBasicDescription)asbd
+(void)setCanonicalAudioStreamBasicDescription:(AudioStreamBasicDescription*)asbd
numberOfChannels:(UInt32)nChannels
interleaved:(BOOL)interleaved;
@@ -91,6 +180,28 @@
/// @name Math Utilities
///-----------------------------------------------------------
/**
Appends an array of values to a history buffer and performs an internal shift to add the values to the tail and removes the same number of values from the head.
@param buffer A float array of values to append to the tail of the history buffer
@param bufferLength The length of the float array being appended to the history buffer
@param scrollHistory The target history buffer in which to append the values
@param scrollHistoryLength The length of the target history buffer
*/
+(void)appendBufferAndShift:(float*)buffer
withBufferSize:(int)bufferLength
toScrollHistory:(float*)scrollHistory
withScrollHistorySize:(int)scrollHistoryLength;
/**
Appends a value to a history buffer and performs an internal shift to add the value to the tail and remove the 0th value.
@param value The float value to append to the history array
@param scrollHistory The target history buffer in which to append the values
@param scrollHistoryLength The length of the target history buffer
*/
+(void) appendValue:(float)value
toScrollHistory:(float*)scrollHistory
withScrollHistorySize:(int)scrollHistoryLength;
/**
Maps a value from one coordinate system into another one. Takes in the current value to map, the minimum and maximum values of the first coordinate system, and the minimum and maximum values of the second coordinate system and calculates the mapped value in the second coordinate system's constraints.
@param value The value expressed in the first coordinate system
@@ -115,6 +226,16 @@
+(float)RMS:(float*)buffer
length:(int)bufferSize;
/**
Calculate the sign function sgn(x) =
{ -1 , x < 0,
{ 0 , x = 0,
{ 1 , x > 0
@param value The float value for which to use as x
@return The float sign value
*/
+(float)SGN:(float)value;
#pragma mark - OSStatus Utility
///-----------------------------------------------------------
/// @name OSStatus Utility
@@ -128,6 +249,18 @@
+(void)checkResult:(OSStatus)result
operation:(const char*)operation;
#pragma mark - Plot Utility
///-----------------------------------------------------------
/// @name Plot Utility
///-----------------------------------------------------------
+(void)updateScrollHistory:(float**)scrollHistory
withLength:(int)scrollHistoryLength
atIndex:(int*)index
withBuffer:(float*)buffer
withBufferSize:(int)bufferSize
isResolutionChanging:(BOOL*)isChanging;
#pragma mark - TPCircularBuffer Utility
///-----------------------------------------------------------
/// @name TPCircularBuffer Utility
+286 -49
View File
@@ -28,52 +28,220 @@
@implementation EZAudio
#pragma mark - AudioBufferList Utility
+(AudioBufferList *)audioBufferList {
return (AudioBufferList*)malloc(sizeof(AudioBufferList));
+(AudioBufferList *)audioBufferListWithNumberOfFrames:(UInt32)frames
numberOfChannels:(UInt32)channels
interleaved:(BOOL)interleaved
{
AudioBufferList *audioBufferList = (AudioBufferList*)malloc(sizeof(AudioBufferList) + sizeof(AudioBuffer)*(channels-1));
UInt32 outputBufferSize = 32 * frames; // 32 KB
audioBufferList->mNumberBuffers = interleaved ? 1 : channels;
for( int i = 0; i < audioBufferList->mNumberBuffers; i++ )
{
audioBufferList->mBuffers[i].mNumberChannels = channels;
audioBufferList->mBuffers[i].mDataByteSize = channels * outputBufferSize;
audioBufferList->mBuffers[i].mData = (float*)malloc(channels * sizeof(float) * outputBufferSize);
}
return audioBufferList;
}
+(void)freeBufferList:(AudioBufferList *)bufferList {
if( bufferList ){
for( int i = 0; i < bufferList->mNumberBuffers; i++ ){
if( bufferList->mBuffers[i].mData ){
free(bufferList->mBuffers[i].mData);
}
+(void)freeBufferList:(AudioBufferList *)bufferList
{
if( bufferList )
{
if( bufferList->mNumberBuffers )
{
for( int i = 0; i < bufferList->mNumberBuffers; i++ )
{
if( bufferList->mBuffers[i].mData )
{
free(bufferList->mBuffers[i].mData);
}
}
}
free(bufferList);
}
free(bufferList);
}
bufferList = NULL;
}
#pragma mark - AudioStreamBasicDescription Utility
+(void)printASBD:(AudioStreamBasicDescription)asbd {
char formatIDString[5];
UInt32 formatID = CFSwapInt32HostToBig(asbd.mFormatID);
bcopy (&formatID, formatIDString, 4);
formatIDString[4] = '\0';
NSLog (@" Sample Rate: %10.0f", asbd.mSampleRate);
NSLog (@" Format ID: %10s", formatIDString);
NSLog (@" Format Flags: %10X", (unsigned int)asbd.mFormatFlags);
NSLog (@" Bytes per Packet: %10d", (unsigned int)asbd.mBytesPerPacket);
NSLog (@" Frames per Packet: %10d", (unsigned int)asbd.mFramesPerPacket);
NSLog (@" Bytes per Frame: %10d", (unsigned int)asbd.mBytesPerFrame);
NSLog (@" Channels per Frame: %10d", (unsigned int)asbd.mChannelsPerFrame);
NSLog (@" Bits per Channel: %10d", (unsigned int)asbd.mBitsPerChannel);
+(AudioStreamBasicDescription)AIFFFormatWithNumberOfChannels:(UInt32)channels
sampleRate:(float)sampleRate
{
AudioStreamBasicDescription asbd;
memset(&asbd, 0, sizeof(asbd));
asbd.mFormatID = kAudioFormatLinearPCM;
asbd.mFormatFlags = kAudioFormatFlagIsBigEndian|kAudioFormatFlagIsPacked|kAudioFormatFlagIsSignedInteger;
asbd.mSampleRate = sampleRate;
asbd.mChannelsPerFrame = channels;
asbd.mBitsPerChannel = 32;
asbd.mBytesPerPacket = (asbd.mBitsPerChannel / 8) * asbd.mChannelsPerFrame;
asbd.mFramesPerPacket = 1;
asbd.mBytesPerFrame = (asbd.mBitsPerChannel / 8) * asbd.mChannelsPerFrame;
return asbd;
}
+(void)setCanonicalAudioStreamBasicDescription:(AudioStreamBasicDescription)asbd
+(AudioStreamBasicDescription)iLBCFormatWithSampleRate:(float)sampleRate
{
AudioStreamBasicDescription asbd;
memset(&asbd, 0, sizeof(asbd));
asbd.mFormatID = kAudioFormatiLBC;
asbd.mChannelsPerFrame = 1;
asbd.mSampleRate = sampleRate;
// Fill in the rest of the descriptions using the Audio Format API
UInt32 propSize = sizeof(asbd);
[EZAudio checkResult:AudioFormatGetProperty(kAudioFormatProperty_FormatInfo,
0,
NULL,
&propSize,
&asbd)
operation:"Failed to fill out the rest of the m4a AudioStreamBasicDescription"];
return asbd;
}
+ (BOOL)isInterleaved:(AudioStreamBasicDescription)asbd
{
return !(asbd.mFormatFlags & kAudioFormatFlagIsNonInterleaved);
}
+ (BOOL)isLinearPCM:(AudioStreamBasicDescription)asbd
{
return asbd.mFormatID == kAudioFormatLinearPCM;
}
+(AudioStreamBasicDescription)M4AFormatWithNumberOfChannels:(UInt32)channels
sampleRate:(float)sampleRate
{
AudioStreamBasicDescription asbd;
memset(&asbd, 0, sizeof(asbd));
asbd.mFormatID = kAudioFormatMPEG4AAC;
asbd.mChannelsPerFrame = channels;
asbd.mSampleRate = sampleRate;
// Fill in the rest of the descriptions using the Audio Format API
UInt32 propSize = sizeof(asbd);
[EZAudio checkResult:AudioFormatGetProperty(kAudioFormatProperty_FormatInfo,
0,
NULL,
&propSize,
&asbd)
operation:"Failed to fill out the rest of the m4a AudioStreamBasicDescription"];
return asbd;
}
+(AudioStreamBasicDescription)monoFloatFormatWithSampleRate:(float)sampleRate
{
AudioStreamBasicDescription asbd;
UInt32 byteSize = sizeof(float);
asbd.mBitsPerChannel = 8 * byteSize;
asbd.mBytesPerFrame = byteSize;
asbd.mBytesPerPacket = byteSize;
asbd.mChannelsPerFrame = 1;
asbd.mFormatFlags = kAudioFormatFlagIsPacked|kAudioFormatFlagIsFloat;
asbd.mFormatID = kAudioFormatLinearPCM;
asbd.mFramesPerPacket = 1;
asbd.mSampleRate = sampleRate;
return asbd;
}
+(AudioStreamBasicDescription)monoCanonicalFormatWithSampleRate:(float)sampleRate
{
AudioStreamBasicDescription asbd;
UInt32 byteSize = sizeof(float);
asbd.mBitsPerChannel = 8 * byteSize;
asbd.mBytesPerFrame = byteSize;
asbd.mBytesPerPacket = byteSize;
asbd.mChannelsPerFrame = 1;
asbd.mFormatFlags = kAudioFormatFlagsNativeFloatPacked|kAudioFormatFlagIsNonInterleaved;
asbd.mFormatID = kAudioFormatLinearPCM;
asbd.mFramesPerPacket = 1;
asbd.mSampleRate = sampleRate;
return asbd;
}
+(AudioStreamBasicDescription)stereoCanonicalNonInterleavedFormatWithSampleRate:(float)sampleRate
{
AudioStreamBasicDescription asbd;
UInt32 byteSize = sizeof(float);
asbd.mBitsPerChannel = 8 * byteSize;
asbd.mBytesPerFrame = byteSize;
asbd.mBytesPerPacket = byteSize;
asbd.mChannelsPerFrame = 2;
asbd.mFormatFlags = kAudioFormatFlagsNativeFloatPacked|kAudioFormatFlagIsNonInterleaved;
asbd.mFormatID = kAudioFormatLinearPCM;
asbd.mFramesPerPacket = 1;
asbd.mSampleRate = sampleRate;
return asbd;
}
+(AudioStreamBasicDescription)stereoFloatInterleavedFormatWithSampleRate:(float)sampleRate
{
AudioStreamBasicDescription asbd;
UInt32 floatByteSize = sizeof(float);
asbd.mChannelsPerFrame = 2;
asbd.mBitsPerChannel = 8 * floatByteSize;
asbd.mBytesPerFrame = asbd.mChannelsPerFrame * floatByteSize;
asbd.mBytesPerPacket = asbd.mChannelsPerFrame * floatByteSize;
asbd.mFormatFlags = kAudioFormatFlagIsPacked|kAudioFormatFlagIsFloat;
asbd.mFormatID = kAudioFormatLinearPCM;
asbd.mFramesPerPacket = 1;
asbd.mSampleRate = sampleRate;
return asbd;
}
+(AudioStreamBasicDescription)stereoFloatNonInterleavedFormatWithSampleRate:(float)sampleRate
{
AudioStreamBasicDescription asbd;
UInt32 floatByteSize = sizeof(float);
asbd.mBitsPerChannel = 8 * floatByteSize;
asbd.mBytesPerFrame = floatByteSize;
asbd.mBytesPerPacket = floatByteSize;
asbd.mChannelsPerFrame = 2;
asbd.mFormatFlags = kAudioFormatFlagIsFloat|kAudioFormatFlagIsNonInterleaved;
asbd.mFormatID = kAudioFormatLinearPCM;
asbd.mFramesPerPacket = 1;
asbd.mSampleRate = sampleRate;
return asbd;
}
+(void)printASBD:(AudioStreamBasicDescription)asbd {
char formatIDString[5];
UInt32 formatID = CFSwapInt32HostToBig(asbd.mFormatID);
bcopy (&formatID, formatIDString, 4);
formatIDString[4] = '\0';
NSLog (@" Sample Rate: %10.0f", asbd.mSampleRate);
NSLog (@" Format ID: %10s", formatIDString);
NSLog (@" Format Flags: %10X", (unsigned int)asbd.mFormatFlags);
NSLog (@" Bytes per Packet: %10d", (unsigned int)asbd.mBytesPerPacket);
NSLog (@" Frames per Packet: %10d", (unsigned int)asbd.mFramesPerPacket);
NSLog (@" Bytes per Frame: %10d", (unsigned int)asbd.mBytesPerFrame);
NSLog (@" Channels per Frame: %10d", (unsigned int)asbd.mChannelsPerFrame);
NSLog (@" Bits per Channel: %10d", (unsigned int)asbd.mBitsPerChannel);
}
+(void)setCanonicalAudioStreamBasicDescription:(AudioStreamBasicDescription*)asbd
numberOfChannels:(UInt32)nChannels
interleaved:(BOOL)interleaved {
asbd.mFormatID = kAudioFormatLinearPCM;
int sampleSize = ((UInt32)sizeof(AudioSampleType));
asbd.mFormatFlags = kAudioFormatFlagsCanonical;
asbd.mBitsPerChannel = 8 * sampleSize;
asbd.mChannelsPerFrame = nChannels;
asbd.mFramesPerPacket = 1;
if (interleaved)
asbd.mBytesPerPacket = asbd.mBytesPerFrame = nChannels * sampleSize;
else {
asbd.mBytesPerPacket = asbd.mBytesPerFrame = sampleSize;
asbd.mFormatFlags |= kAudioFormatFlagIsNonInterleaved;
}
asbd->mFormatID = kAudioFormatLinearPCM;
#if TARGET_OS_IPHONE
int sampleSize = sizeof(float);
asbd->mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
#elif TARGET_OS_MAC
int sampleSize = sizeof(Float32);
asbd->mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
#endif
asbd->mBitsPerChannel = 8 * sampleSize;
asbd->mChannelsPerFrame = nChannels;
asbd->mFramesPerPacket = 1;
if (interleaved)
asbd->mBytesPerPacket = asbd->mBytesPerFrame = nChannels * sampleSize;
else {
asbd->mBytesPerPacket = asbd->mBytesPerFrame = sampleSize;
asbd->mFormatFlags |= kAudioFormatFlagIsNonInterleaved;
}
}
#pragma mark - OSStatus Utility
@@ -94,39 +262,108 @@
}
#pragma mark - Math Utility
+(void)appendBufferAndShift:(float*)buffer
withBufferSize:(int)bufferLength
toScrollHistory:(float*)scrollHistory
withScrollHistorySize:(int)scrollHistoryLength {
NSAssert(scrollHistoryLength>=bufferLength,@"Scroll history array length must be greater buffer length");
NSAssert(scrollHistoryLength>0,@"Scroll history array length must be greater than 0");
NSAssert(bufferLength>0,@"Buffer array length must be greater than 0");
int shiftLength = scrollHistoryLength - bufferLength;
size_t floatByteSize = sizeof(float);
size_t shiftByteSize = shiftLength * floatByteSize;
size_t bufferByteSize = bufferLength * floatByteSize;
memmove(&scrollHistory[0],
&scrollHistory[bufferLength],
shiftByteSize);
memmove(&scrollHistory[shiftLength],
&buffer[0],
bufferByteSize);
}
+(void) appendValue:(float)value
toScrollHistory:(float*)scrollHistory
withScrollHistorySize:(int)scrollHistoryLength {
float val[1]; val[0] = value;
[self appendBufferAndShift:val
withBufferSize:1
toScrollHistory:scrollHistory
withScrollHistorySize:scrollHistoryLength];
}
+(float)MAP:(float)value
leftMin:(float)leftMin
leftMax:(float)leftMax
rightMin:(float)rightMin
rightMax:(float)rightMax {
float leftSpan = leftMax - leftMin;
float rightSpan = rightMax - rightMin;
float valueScaled = ( value - leftMin ) / leftSpan;
return rightMin + (valueScaled * rightSpan);
float leftSpan = leftMax - leftMin;
float rightSpan = rightMax - rightMin;
float valueScaled = ( value - leftMin ) / leftSpan;
return rightMin + (valueScaled * rightSpan);
}
+(float)RMS:(float *)buffer
length:(int)bufferSize {
float sum = 0.0;
for(int i = 0; i < bufferSize; i++)
sum += buffer[i] * buffer[i];
return sqrtf( sum / bufferSize );
float sum = 0.0;
for(int i = 0; i < bufferSize; i++)
sum += buffer[i] * buffer[i];
return sqrtf( sum / bufferSize );
}
+(float)SGN:(float)value
{
return value < 0 ? -1.0f : ( value > 0 ? 1.0f : 0.0f );
}
#pragma mark - Plot Utility
+(void)updateScrollHistory:(float **)scrollHistory
withLength:(int)scrollHistoryLength
atIndex:(int*)index
withBuffer:(float *)buffer
withBufferSize:(int)bufferSize
isResolutionChanging:(BOOL*)isChanging {
//
size_t floatByteSize = sizeof(float);
//
if( *scrollHistory == NULL ){
// Create the history buffer
*scrollHistory = (float*)calloc(1024,floatByteSize);
}
//
if( !*isChanging ){
float rms = [EZAudio RMS:buffer length:bufferSize];
if( *index < scrollHistoryLength ){
float *hist = *scrollHistory;
hist[*index] = rms;
(*index)++;
}
else {
[EZAudio appendValue:rms
toScrollHistory:*scrollHistory
withScrollHistorySize:scrollHistoryLength];
}
}
}
#pragma mark - TPCircularBuffer Utility
+(void)circularBuffer:(TPCircularBuffer *)circularBuffer withSize:(int)size {
TPCircularBufferInit(circularBuffer,size);
TPCircularBufferInit(circularBuffer,size);
}
+(void)appendDataToCircularBuffer:(TPCircularBuffer*)circularBuffer
fromAudioBufferList:(AudioBufferList*)audioBufferList {
TPCircularBufferProduceBytes(circularBuffer,
audioBufferList->mBuffers[0].mData,
audioBufferList->mBuffers[0].mDataByteSize);
TPCircularBufferProduceBytes(circularBuffer,
audioBufferList->mBuffers[0].mData,
audioBufferList->mBuffers[0].mDataByteSize);
}
+(void)freeCircularBuffer:(TPCircularBuffer *)circularBuffer {
TPCircularBufferClear(circularBuffer);
TPCircularBufferClear(circularBuffer);
TPCircularBufferCleanup(circularBuffer);
}
@end
+20
View File
@@ -0,0 +1,20 @@
//
// EZAudioConverter.h
// EZAudioPlayFileExample
//
// Created by Syed Haris Ali on 2/14/15.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
@interface EZAudioConverter : NSObject
@property (nonatomic, assign, readonly) AudioStreamBasicDescription inputFormat;
@property (nonatomic, assign, readonly) AudioStreamBasicDescription outputFormat;
+ (instancetype) converterWithInputFormat:(AudioStreamBasicDescription)inputFormat
outputFormat:(AudioStreamBasicDescription)outputFormat;
@end
+38
View File
@@ -0,0 +1,38 @@
//
// EZAudioConverter.m
// EZAudioPlayFileExample
//
// Created by Syed Haris Ali on 2/14/15.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
//
#import "EZAudioConverter.h"
typedef struct
{
AudioConverterRef converterRef;
AudioStreamBasicDescription inputFormat;
AudioStreamBasicDescription outputFormat;
} EZAudioConverterInfo;
@interface EZAudioConverter ()
@property (nonatomic, assign) EZAudioConverterInfo info;
@end
@implementation EZAudioConverter
+ (instancetype)converterWithInputFormat:(AudioStreamBasicDescription)inputFormat
outputFormat:(AudioStreamBasicDescription)outputFormat
{
id converter = [[self alloc] init];
EZAudioConverterInfo info;
memset(&info, 0, sizeof(info));
info.inputFormat = inputFormat;
info.outputFormat = outputFormat;
((EZAudioConverter *)converter).info = info;
return converter;
}
@end
+237 -62
View File
@@ -25,10 +25,26 @@
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
#import "EZAudioWaveformData.h"
//------------------------------------------------------------------------------
@class EZAudio;
@class EZAudioConverter;
@class EZAudioFile;
//------------------------------------------------------------------------------
typedef NS_ENUM(NSUInteger, EZAudioFilePermission)
{
EZAudioFilePermissionRead = kAudioFileReadPermission,
EZAudioFilePermissionWrite = kAudioFileWritePermission,
EZAudioFilePermissionReadWrite = kAudioFileReadWritePermission,
};
//------------------------------------------------------------------------------
#pragma mark - EZAudioFileDelegate
//------------------------------------------------------------------------------
/**
The EZAudioFileDelegate provides event callbacks for the EZAudioFile object. These type of events are triggered by reads and seeks on the file and gives feedback such as the audio data read as a float array for visualizations and the new seek position for UI updating.
*/
@@ -42,97 +58,192 @@
@param bufferSize The length of the buffers float arrays
@param numberOfChannels The number of channels. 2 for stereo, 1 for mono.
*/
-(void) audioFile:(EZAudioFile*)audioFile
readAudio:(float**)buffer
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels;
- (void) audioFile:(EZAudioFile*)audioFile
readAudio:(float**)buffer
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels;
//------------------------------------------------------------------------------
/**
Occurs when the audio file's internal seek position has been updated by the EZAudioFile functions `readFrames:audioBufferList:bufferSize:eof:` or `audioFile:updatedPosition:`.
@param audioFile The instance of the EZAudio in which the change occured
@param framePosition The new frame index as a 64-bit signed integer
*/
-(void)audioFile:(EZAudioFile*)audioFile
updatedPosition:(SInt64)framePosition;
- (void) audioFile:(EZAudioFile*)audioFile
updatedPosition:(SInt64)framePosition;
@end
//------------------------------------------------------------------------------
#pragma mark - EZAudioFile
//------------------------------------------------------------------------------
/**
The EZAudioFile provides a lightweight and intuitive way to asynchronously interact with audio files. These interactions included reading audio data, seeking within an audio file, getting information about the file, and pulling the waveform data for visualizing the contents of the audio file. The EZAudioFileDelegate provides event callbacks for when reads, seeks, and various updates happen within the audio file to allow the caller to interact with the action in meaningful ways. Common use cases here could be to read the audio file's data as AudioBufferList structures for output (see EZOutput) and visualizing the audio file's data as a float array using an audio plot (see EZAudioPlot).
*/
@interface EZAudioFile : NSObject
//------------------------------------------------------------------------------
#pragma mark - Blocks
//------------------------------------------------------------------------------
/**
A block used when returning back the waveform data. The waveform data itself will be an array of float values and the length indicates the total length of the float array.
@param waveformData An array of float values representing the amplitude data from the audio waveform
@param length The length of the waveform data's float array
*/
typedef void (^WaveformDataCompletionBlock)(float *waveformData, UInt32 length);
typedef void (^WaveformDataCompletionBlock)(EZAudioWaveformData *waveformData);
//------------------------------------------------------------------------------
#pragma mark - Properties
//------------------------------------------------------------------------------
/**
A EZAudioFileDelegate for the audio file that is used to return events such as new seek positions within the file and the read audio data as a float array.
*/
@property (nonatomic,assign) id<EZAudioFileDelegate> audioFileDelegate;
@property (nonatomic, weak) id<EZAudioFileDelegate> delegate;
#pragma mark - Initializers
///-----------------------------------------------------------
/// @name Initializers
///-----------------------------------------------------------
//------------------------------------------------------------------------------
#pragma mark - Initialization
//------------------------------------------------------------------------------
/**
@name Initialization
*/
/**
Creates a new instance of the EZAudioFile using a file path URL. Read only.
@param url The file path reference of the audio file as an NSURL.
@return The newly created EZAudioFile instance.
*/
- (instancetype)initWithURL:(NSURL*)url;
//------------------------------------------------------------------------------
/**
Creates a new instance of the EZAudioFile using a file path URL.
@param url The file path reference of the audio file as an NSURL.
@param permission A constant describing what we intend on doing with the audio file (read, write, or both)
@param fileFormat An AudioStreamBasicDescription that will be used to create the audio file if it does not exist if the permission argument is set to EZAudioFilePermissionWrite or EZAudioFilePermissionReadWrite. Not used for EZAudioFilePermissionRead permission.
@return The newly created EZAudioFile instance.
*/
-(EZAudioFile*)initWithURL:(NSURL*)url;
- (instancetype)initWithURL:(NSURL*)url
permission:(EZAudioFilePermission)permission
fileFormat:(AudioStreamBasicDescription)fileFormat;
//------------------------------------------------------------------------------
/**
Creates a new instance of the EZAudioFile using a file path URL and allows specifying an EZAudioFileDelegate.
@param url The file path reference of the audio file as an NSURL.
@param delegate The audio file delegate that receives events specified by the EZAudioFileDelegate protocol
@param permission A constant describing what we intend on doing with the audio file (read, write, or both)
@param fileFormat An AudioStreamBasicDescription that will be used to create the audio file if it does not exist if the permission argument is set to EZAudioFilePermissionWrite or EZAudioFilePermissionReadWrite. Not used for EZAudioFilePermissionRead permission.
@return The newly created EZAudioFile instance.
*/
-(EZAudioFile*)initWithURL:(NSURL*)url
andDelegate:(id<EZAudioFileDelegate>)delegate;
- (instancetype)initWithURL:(NSURL*)url
delegate:(id<EZAudioFileDelegate>)delegate
permission:(EZAudioFilePermission)permission
fileFormat:(AudioStreamBasicDescription)fileFormat;
//------------------------------------------------------------------------------
/**
Class method that creates a new instance of the EZAudioFile using a file path URL and allows specifying an EZAudioFileDelegate, a read/write permission, a file format incase a new file is being written, and a client format for a format that will be used when read samples (different from file format).
@param url The file path reference of the audio file as an NSURL.
@param delegate The audio file delegate that receives events specified by the EZAudioFileDelegate protocol
@param permission A constant describing what we intend on doing with the audio file (read, write, or both)
@param fileFormat An AudioStreamBasicDescription that will be used to create the audio file if it does not exist if the permission argument is set to EZAudioFilePermissionWrite or EZAudioFilePermissionReadWrite. Not used for EZAudioFilePermissionRead permission.
@param clientFormat An AudioStreamBasicDescription that will be used as the client format on the audio file. For instance, the audio file might be in a 22.5 kHz sample rate format in its file format, but your app wants to read the samples at a sample rate of 44.1 kHz so it can iterate with other components (like a audio processing graph) without any weird playback effects. If this initializer is not used then a non-interleaved float format will be assumed.
@return The newly created EZAudioFile instance.
*/
- (instancetype)initWithURL:(NSURL*)url
delegate:(id<EZAudioFileDelegate>)delegate
permission:(EZAudioFilePermission)permission
fileFormat:(AudioStreamBasicDescription)fileFormat
clientFormat:(AudioStreamBasicDescription)clientFormat;
//------------------------------------------------------------------------------
#pragma mark - Class Initializers
///-----------------------------------------------------------
/// @name Class Initializers
///-----------------------------------------------------------
//------------------------------------------------------------------------------
/**
@name Class Initializers
*/
/**
Class method that creates a new instance of the EZAudioFile using a file path URL.
@param url The file path reference of the audio file as an NSURL.
@return The newly created EZAudioFile instance.
*/
+(EZAudioFile*)audioFileWithURL:(NSURL*)url;
+ (instancetype)audioFileWithURL:(NSURL*)url;
//------------------------------------------------------------------------------
/**
Class method that creates a new instance of the EZAudioFile using a file path URL.
@param url The file path reference of the audio file as an NSURL.
@param permission A constant describing what we intend on doing with the audio file (read, write, or both)
@param fileFormat An AudioStreamBasicDescription that will be used to create the audio file if it does not exist if the permission argument is set to EZAudioFilePermissionWrite or EZAudioFilePermissionReadWrite. Not used for EZAudioFilePermissionRead permission.
@return The newly created EZAudioFile instance.
*/
+ (instancetype)audioFileWithURL:(NSURL*)url
permission:(EZAudioFilePermission)permission
fileFormat:(AudioStreamBasicDescription)fileFormat;
//------------------------------------------------------------------------------
/**
Class method that creates a new instance of the EZAudioFile using a file path URL and allows specifying an EZAudioFileDelegate.
@param url The file path reference of the audio file as an NSURL.
@param delegate The audio file delegate that receives events specified by the EZAudioFileDelegate protocol
@param permission A constant describing what we intend on doing with the audio file (read, write, or both)
@param fileFormat An AudioStreamBasicDescription that will be used to create the audio file if it does not exist if the permission argument is set to EZAudioFilePermissionWrite or EZAudioFilePermissionReadWrite. Not used for EZAudioFilePermissionRead permission.
@return The newly created EZAudioFile instance.
*/
+(EZAudioFile*)audioFileWithURL:(NSURL*)url
andDelegate:(id<EZAudioFileDelegate>)delegate;
+ (instancetype) audioFileWithURL:(NSURL*)url
delegate:(id<EZAudioFileDelegate>)delegate
permission:(EZAudioFilePermission)permission
fileFormat:(AudioStreamBasicDescription)fileFormat;
//------------------------------------------------------------------------------
/**
Class method that creates a new instance of the EZAudioFile using a file path URL and allows specifying an EZAudioFileDelegate, a read/write permission, a file format incase a new file is being written, and a client format for a format that will be used when read samples (different from file format).
@param url The file path reference of the audio file as an NSURL.
@param delegate The audio file delegate that receives events specified by the EZAudioFileDelegate protocol
@param permission A constant describing what we intend on doing with the audio file (read, write, or both)
@param fileFormat An AudioStreamBasicDescription that will be used to create the audio file if it does not exist if the permission argument is set to EZAudioFilePermissionWrite or EZAudioFilePermissionReadWrite. Not used for EZAudioFilePermissionRead permission.
@param clientFormat An AudioStreamBasicDescription that will be used as the client format on the audio file. A client format is different from the file format in that it is the format of the other components interacting with this file. For instance, the file on disk could be a 22.5 kHz, float format, but we might have an audio processing graph that has a 44.1 kHz, signed integer format that we'd like to interact with. The client format lets us set that 44.1 kHz format on the audio file to properly read samples from it with any interpolation or format conversion that must take place. If not specified the default value is equal to the class method, 'defaultClientFormat'
@return The newly created EZAudioFile instance.
*/
+ (instancetype)audioFileWithURL:(NSURL*)url
delegate:(id<EZAudioFileDelegate>)delegate
permission:(EZAudioFilePermission)permission
fileFormat:(AudioStreamBasicDescription)fileFormat
clientFormat:(AudioStreamBasicDescription)clientFormat;;
//------------------------------------------------------------------------------
#pragma mark - Class Methods
///-----------------------------------------------------------
/// @name Class Methods
///-----------------------------------------------------------
//------------------------------------------------------------------------------
/**
@name Class Methods
*/
/**
A class method that subclasses can override to specify the default client format that will be used to read audio data from this file. A client format is different from the file format in that it is the format of the other components interacting with this file. For instance, the file on disk could be a 22.5 kHz, float format, but we might have an audio processing graph that has a 44.1 kHz, signed integer format that we'd like to interact with. The client format lets us set that 44.1 kHz format on the audio file to properly read samples from it with any interpolation or format conversion that must take place. Default is stereo, non-interleaved, 44.1 kHz.
@return An AudioStreamBasicDescription that serves as the audio file's client format.
*/
+ (AudioStreamBasicDescription)defaultClientFormat;
/**
Provides an array of the supported audio files types. Each audio file type is provided as a string, i.e. @"caf". Useful for filtering lists of files in an open panel to only the types allowed.
@return An array of NSString objects representing the represented file types.
*/
+(NSArray*)supportedAudioFileTypes;
+ (NSArray *)supportedAudioFileTypes;
//------------------------------------------------------------------------------
#pragma mark - Events
///-----------------------------------------------------------
/// @name Reading The Audio File
///-----------------------------------------------------------
//------------------------------------------------------------------------------
/**
@name Reading The Audio File
*/
/**
Reads a specified number of frames from the audio file. In addition, this will notify the EZAudioFileDelegate (if specified) of the read data as a float array with the audioFile:readAudio:withBufferSize:withNumberOfChannels: event and the new seek position within the file with the audioFile:updatedPosition: event.
@@ -142,77 +253,141 @@ typedef void (^WaveformDataCompletionBlock)(float *waveformData, UInt32 length);
@param eof A pointer to a BOOL in which to store whether the read operation reached the end of the audio file.
*/
-(void)readFrames:(UInt32)frames
audioBufferList:(AudioBufferList*)audioBufferList
bufferSize:(UInt32*)bufferSize
eof:(BOOL*)eof;
audioBufferList:(AudioBufferList *)audioBufferList
bufferSize:(UInt32 *)bufferSize
eof:(BOOL *)eof;
///-----------------------------------------------------------
/// @name Seeking Through The Audio File
///-----------------------------------------------------------
//------------------------------------------------------------------------------
#pragma mark - Seeking Through The Audio File
//------------------------------------------------------------------------------
/**
@name Seeking Through The Audio File
*/
/**
Seeks through an audio file to a specified frame. This will notify the EZAudioFileDelegate (if specified) with the audioFile:updatedPosition: function.
@param frame The new frame position to seek to as a SInt64.
*/
-(void)seekToFrame:(SInt64)frame;
- (void)seekToFrame:(SInt64)frame;
//------------------------------------------------------------------------------
#pragma mark - Getters
///-----------------------------------------------------------
/// @name Getting Information About The Audio File
///-----------------------------------------------------------
//------------------------------------------------------------------------------
/**
@name Getting Information About The Audio File
*/
/**
Provides the AudioStreamBasicDescription structure used within the app. The file's format will be converted to this format and then sent back as either a float array or a `AudioBufferList` pointer. Use this when communicating with other EZAudio components.
@return An AudioStreamBasicDescription structure describing the format of the audio file.
*/
-(AudioStreamBasicDescription)clientFormat;
- (AudioStreamBasicDescription)clientFormat;
//------------------------------------------------------------------------------
/**
Provides the AudioStreamBasicDescription structure containing the format of the file.
@return An AudioStreamBasicDescription structure describing the format of the audio file.
*/
-(AudioStreamBasicDescription)fileFormat;
- (AudioStreamBasicDescription)fileFormat;
//------------------------------------------------------------------------------
/**
Provides the frame index (a.k.a the seek positon) within the audio file as an integer. This can be helpful when seeking through the audio file.
@return The current frame index within the audio file as a SInt64.
*/
-(SInt64)frameIndex;
- (SInt64)frameIndex;
//------------------------------------------------------------------------------
/**
Provides a dictionary containing the metadata (ID3) tags that are included in the header for the audio file. Typically this contains stuff like artist, title, release year, etc.
@return An NSDictionary containing the metadata for the audio file.
*/
- (NSDictionary *)metadata;
//------------------------------------------------------------------------------
/**
Provides the total duration of the audio file in seconds.
@return The total duration of the audio file as a Float32.
*/
-(Float32)totalDuration;
- (NSTimeInterval)totalDuration;
//------------------------------------------------------------------------------
/**
Provides the total frame count of the audio file.
@return The total number of frames in the audio file as a SInt64.
Provides the total frame count of the audio file in the client format.
@return The total number of frames in the audio file in the AudioStreamBasicDescription representing the client format as a SInt64.
*/
-(SInt64)totalFrames;
- (SInt64)totalClientFrames;
//------------------------------------------------------------------------------
/**
Provides the total frame count of the audio file in the file format.
@return The total number of frames in the audio file in the AudioStreamBasicDescription representing the file format as a SInt64.
*/
- (SInt64)totalFrames;
//------------------------------------------------------------------------------
/**
Provides the NSURL for the audio file.
@return An NSURL representing the path of the EZAudioFile instance.
*/
- (NSURL*)url;
//------------------------------------------------------------------------------
#pragma mark - Setters
//------------------------------------------------------------------------------
/**
A client format is different from the file format in that it is the format of the other components interacting with this file. For instance, the file on disk could be a 22.5 kHz, float format, but we might have an audio processing graph that has a 44.1 kHz, signed integer format that we'd like to interact with. The client format lets us set that 44.1 kHz format on the audio file to properly read samples from it with any interpolation or format conversion that must take place. Default is stereo, non-interleaved, 44.1 kHz.
@param clientFormat An AudioStreamBasicDescription that should serve as the audio file's client format.
*/
- (void)setClientFormat:(AudioStreamBasicDescription)clientFormat;
//------------------------------------------------------------------------------
#pragma mark - Helpers
///-----------------------------------------------------------
/// @name Manipulating The Audio Data
///-----------------------------------------------------------
//------------------------------------------------------------------------------
/**
Synchronously pulls the waveform amplitude data into a float array for the receiver. This returns a waveform with a default resolution of 1024, meaning there are 1024 data points to plot the waveform.
@param numberOfPoints A UInt32 representing the number of data points you need. The higher the number of points the more detailed the waveform will be.
@return A EZAudioWaveformData instance containing the audio data for all channels of the audio.
*/
- (EZAudioWaveformData *)getWaveformData;
//------------------------------------------------------------------------------
/**
Synchronously pulls the waveform amplitude data into a float array for the receiver.
@param numberOfPoints A UInt32 representing the number of data points you need. The higher the number of points the more detailed the waveform will be.
@return A EZAudioWaveformData instance containing the audio data for all channels of the audio.
*/
- (EZAudioWaveformData *)getWaveformDataWithNumberOfPoints:(UInt32)numberOfPoints;
//------------------------------------------------------------------------------
/**
Asynchronously pulls the waveform amplitude data into a float array for the receiver. This returns a waveform with a default resolution of 1024, meaning there are 1024 data points to plot the waveform.
@param completion A WaveformDataCompletionBlock that executes when the waveform data has been extracted. Provides a `EZAudioWaveformData` instance containing the waveform data for all audio channels.
*/
- (void)getWaveformDataWithCompletionBlock:(WaveformDataCompletionBlock)completion;
//------------------------------------------------------------------------------
/**
Asynchronously pulls the waveform amplitude data into a float array for the receiver.
@param waveformDataCompletionBlock A WaveformDataCompletionBlock that executes when the waveform data has been extracted. Provides the waveform data as a float array and the length of the array.
@param numberOfPoints A UInt32 representing the number of data points you need. The higher the number of points the more detailed the waveform will be.
@param completion A WaveformDataCompletionBlock that executes when the waveform data has been extracted. Provides a `EZAudioWaveformData` instance containing the waveform data for all audio channels.
*/
-(void)getWaveformDataWithCompletionBlock:(WaveformDataCompletionBlock)waveformDataCompletionBlock;
- (void)getWaveformDataWithNumberOfPoints:(UInt32)numberOfPoints
completion:(WaveformDataCompletionBlock)completion;
/**
Provides the minimum number of buffers that would be required with the constant frames read rate provided.
@param frameRate A constant frame rate to use when calculating the number of buffers needed as a UInt32.
@return The minimum number of buffers required for the constant frames read rate provided as a UInt32.
*/
-(UInt32)minBuffersWithFrameRate:(UInt32)frameRate;
/**
Provides a frame rate to use when drawing and averaging a bin of values to create each point in a graph. The ideal amount of end buffers seems to be between 1000-3000 so we determine a frame rate per audio file that can achieve a high degree of detail for the entire waveform.
@return A frame rate value as a UInt32 to use when reading frames in a file.
*/
-(UInt32)recommendedDrawingFrameRate;
//------------------------------------------------------------------------------
@end
+558 -233
View File
@@ -25,275 +25,600 @@
#import "EZAudioFile.h"
#import "EZAudio.h"
//------------------------------------------------------------------------------
@interface EZAudioFile (){
// Reading from the audio file
ExtAudioFileRef _audioFile;
AudioStreamBasicDescription _clientFormat;
AudioStreamBasicDescription _fileFormat;
float **_floatBuffers;
AEFloatConverter *_floatConverter;
SInt64 _frameIndex;
CFURLRef _sourceURL;
Float32 _totalDuration;
SInt64 _totalFrames;
// Waveform Data
float *_waveformData;
UInt32 _waveformFrameRate;
UInt32 _waveformTotalBuffers;
}
#import "EZAudio.h"
#import "EZAudioConverter.h"
#import "EZAudioWaveformData.h"
#include <pthread.h>
//------------------------------------------------------------------------------
// errors
static OSStatus EZAudioFileReadPermissionFileDoesNotExistCode = -88776;
// constants
static UInt32 EZAudioFileWaveformDefaultResolution = 1024;
static NSString *EZAudioFileWaveformDataQueueIdentifier = @"com.ezaudio.waveformQueue";
//------------------------------------------------------------------------------
typedef struct
{
AudioFileID audioFileID;
AudioStreamBasicDescription clientFormat;
Float32 duration;
ExtAudioFileRef extAudioFileRef;
AudioStreamBasicDescription fileFormat;
SInt64 frames;
EZAudioFilePermission permission;
CFURLRef sourceURL;
} EZAudioFileInfo;
//------------------------------------------------------------------------------
#pragma mark - EZAudioFile
//------------------------------------------------------------------------------
@interface EZAudioFile ()
@property (nonatomic) EZAudioFileInfo info;
@property (nonatomic) pthread_mutex_t lock;
@property (nonatomic) dispatch_queue_t waveformQueue;
@end
//------------------------------------------------------------------------------
@implementation EZAudioFile
@synthesize audioFileDelegate = _audioFileDelegate;
#pragma mark - Initializers
-(EZAudioFile*)initWithURL:(NSURL*)url {
self = [super init];
if(self){
_sourceURL = (__bridge CFURLRef)url;
[self _configureAudioFile];
}
return self;
//------------------------------------------------------------------------------
#pragma mark - Initialization
//------------------------------------------------------------------------------
- (instancetype)init
{
self = [super init];
if (self)
{
memset(&_info, 0, sizeof(_info));
_info.permission = EZAudioFilePermissionRead;
pthread_mutex_init(&_lock, NULL);
_waveformQueue = dispatch_queue_create(EZAudioFileWaveformDataQueueIdentifier.UTF8String, DISPATCH_QUEUE_PRIORITY_DEFAULT);
}
return self;
}
-(EZAudioFile *)initWithURL:(NSURL *)url andDelegate:(id<EZAudioFileDelegate>)delegate {
self = [self initWithURL:url];
if(self){
self.audioFileDelegate = delegate;
}
return self;
//------------------------------------------------------------------------------
- (instancetype)initWithURL:(NSURL *)url
{
AudioStreamBasicDescription asbd;
return [self initWithURL:url
permission:EZAudioFilePermissionRead
fileFormat:asbd];
}
//------------------------------------------------------------------------------
- (instancetype)initWithURL:(NSURL*)url
permission:(EZAudioFilePermission)permission
fileFormat:(AudioStreamBasicDescription)fileFormat
{
return [self initWithURL:url
delegate:nil
permission:permission
fileFormat:fileFormat];
}
//------------------------------------------------------------------------------
- (instancetype)initWithURL:(NSURL*)url
delegate:(id<EZAudioFileDelegate>)delegate
permission:(EZAudioFilePermission)permission
fileFormat:(AudioStreamBasicDescription)fileFormat
{
return [self initWithURL:url
delegate:delegate
permission:permission
fileFormat:fileFormat
clientFormat:[self.class defaultClientFormat]];
}
//------------------------------------------------------------------------------
- (instancetype)initWithURL:(NSURL*)url
delegate:(id<EZAudioFileDelegate>)delegate
permission:(EZAudioFilePermission)permission
fileFormat:(AudioStreamBasicDescription)fileFormat
clientFormat:(AudioStreamBasicDescription)clientFormat
{
self = [self init];
if(self)
{
_info.clientFormat = clientFormat;
_info.fileFormat = fileFormat;
_info.permission = permission;
_info.sourceURL = (__bridge CFURLRef)url;
self.delegate = delegate;
[self setup];
}
return self;
}
//------------------------------------------------------------------------------
#pragma mark - Class Initializers
+(EZAudioFile*)audioFileWithURL:(NSURL*)url {
return [[EZAudioFile alloc] initWithURL:url];
//------------------------------------------------------------------------------
+ (instancetype)audioFileWithURL:(NSURL*)url
{
return [[self alloc] initWithURL:url];
}
+(EZAudioFile *)audioFileWithURL:(NSURL *)url andDelegate:(id<EZAudioFileDelegate>)delegate {
return [[EZAudioFile alloc] initWithURL:url andDelegate:delegate];
//------------------------------------------------------------------------------
+ (instancetype)audioFileWithURL:(NSURL*)url
permission:(EZAudioFilePermission)permission
fileFormat:(AudioStreamBasicDescription)fileFormat
{
return [[self alloc] initWithURL:url
permission:permission
fileFormat:fileFormat];
}
//------------------------------------------------------------------------------
+ (instancetype)audioFileWithURL:(NSURL*)url
delegate:(id<EZAudioFileDelegate>)delegate
permission:(EZAudioFilePermission)permission
fileFormat:(AudioStreamBasicDescription)fileFormat
{
return [[self alloc] initWithURL:url
delegate:delegate
permission:permission
fileFormat:fileFormat];
}
//------------------------------------------------------------------------------
+ (instancetype)audioFileWithURL:(NSURL*)url
delegate:(id<EZAudioFileDelegate>)delegate
permission:(EZAudioFilePermission)permission
fileFormat:(AudioStreamBasicDescription)fileFormat
clientFormat:(AudioStreamBasicDescription)clientFormat
{
return [[self alloc] initWithURL:url
delegate:delegate
permission:permission
fileFormat:fileFormat
clientFormat:clientFormat];
}
//------------------------------------------------------------------------------
#pragma mark - Class Methods
+(NSArray *)supportedAudioFileTypes {
return @[ @"aac",
@"caf",
@"aif",
@"aiff",
@"aifc",
@"mp3",
@"mp4",
@"m4a",
@"snd",
@"au",
@"sd2",
@"wav" ];
//------------------------------------------------------------------------------
+ (AudioStreamBasicDescription)defaultClientFormat
{
return [EZAudio stereoFloatInterleavedFormatWithSampleRate:44100];
}
#pragma mark - Private Configuation
-(void)_configureAudioFile {
// Source URL should not be nil
NSAssert(_sourceURL,@"Source URL was not specified correctly.");
// Try to open the file for reading
[EZAudio checkResult:ExtAudioFileOpenURL(_sourceURL,&_audioFile)
operation:"Failed to open audio file for reading"];
// Try pulling the stream description
UInt32 size = sizeof(_fileFormat);
[EZAudio checkResult:ExtAudioFileGetProperty(_audioFile,kExtAudioFileProperty_FileDataFormat, &size, &_fileFormat)
operation:"Failed to get audio stream basic description of input file"];
[EZAudio printASBD:_fileFormat];
// Try pulling the total frame size
size = sizeof(_totalFrames);
[EZAudio checkResult:ExtAudioFileGetProperty(_audioFile,kExtAudioFileProperty_FileLengthFrames, &size, &_totalFrames)
operation:"Failed to get total frames of input file"];
// Total duration
_totalDuration = _totalFrames / _fileFormat.mSampleRate;
// Set the client format on the stream
_clientFormat.mBitsPerChannel = 8 * sizeof(AudioUnitSampleType);
_clientFormat.mBytesPerFrame = sizeof(AudioUnitSampleType);
_clientFormat.mBytesPerPacket = sizeof(AudioUnitSampleType);
_clientFormat.mChannelsPerFrame = 1;
_clientFormat.mFormatFlags = kAudioFormatFlagsCanonical | kAudioFormatFlagIsNonInterleaved;
_clientFormat.mFormatID = kAudioFormatLinearPCM;
_clientFormat.mFramesPerPacket = 1;
_clientFormat.mSampleRate = 44100;
[EZAudio checkResult:ExtAudioFileSetProperty(_audioFile,
kExtAudioFileProperty_ClientDataFormat,
sizeof (AudioStreamBasicDescription),
&_clientFormat)
operation:"Couldn't set client data format on input ext file"];
// Allocate the float buffers
_floatConverter = [[AEFloatConverter alloc] initWithSourceFormat:_clientFormat];
_floatBuffers = (float**)malloc( sizeof(float*) * _clientFormat.mChannelsPerFrame );
UInt32 outputBufferSize = 32 * 1024; // 32 KB
for ( int i=0; i< _clientFormat.mChannelsPerFrame; i++ ) {
_floatBuffers[i] = (float*)malloc(outputBufferSize);
}
//------------------------------------------------------------------------------
+ (NSArray *)supportedAudioFileTypes
{
return @[
@"aac",
@"caf",
@"aif",
@"aiff",
@"aifc",
@"mp3",
@"mp4",
@"m4a",
@"snd",
@"au",
@"sd2",
@"wav"
];
}
//------------------------------------------------------------------------------
#pragma mark - Setup
//------------------------------------------------------------------------------
- (void)setup
{
// we open the file differently depending on the permissions specified
[EZAudio checkResult:[self openAudioFile]
operation:"Failed to create/open audio file"];
// set the client format
self.clientFormat = self.info.clientFormat;
}
//------------------------------------------------------------------------------
#pragma mark - Creating/Opening Audio File
//------------------------------------------------------------------------------
- (OSStatus)openAudioFile
{
// need a source url
NSAssert(_info.sourceURL, @"EZAudioFile cannot be created without a source url!");
// determine if the file actually exists
CFURLRef url = self.info.sourceURL;
NSURL *fileURL = (__bridge NSURL *)(url);
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:fileURL.path];
// create the file wrapper slightly differently depending what we are
// trying to do with it
OSStatus result = noErr;
EZAudioFilePermission permission = self.info.permission;
UInt32 propSize;
if (fileExists)
{
result = AudioFileOpenURL(url,
permission,
0,
&_info.audioFileID);
[EZAudio checkResult:result
operation:"failed to open audio file"];
}
else
{
// read permission is not applicable because the file does not exist
if (permission == EZAudioFilePermissionRead)
{
result = EZAudioFileReadPermissionFileDoesNotExistCode;
}
else
{
result = AudioFileCreateWithURL(url,
0,
&_info.fileFormat,
kAudioFileFlags_EraseFile,
&_info.audioFileID);
}
}
// get the ExtAudioFile wrapper
if (result == noErr)
{
[EZAudio checkResult:ExtAudioFileWrapAudioFileID(self.info.audioFileID,
false,
&_info.extAudioFileRef)
operation:"Failed to wrap audio file ID in ext audio file ref"];
}
// store the file format if we opened an existing file
if (fileExists)
{
propSize = sizeof(self.info.fileFormat);
[EZAudio checkResult:ExtAudioFileGetProperty(self.info.extAudioFileRef,
kExtAudioFileProperty_FileDataFormat,
&propSize,
&_info.fileFormat)
operation:"Failed to get file audio format on existing audio file"];
}
NSLog(@"file format......................");
[EZAudio printASBD:self.info.fileFormat];
NSLog(@"client format....................");
[EZAudio printASBD:self.info.clientFormat];
// done
return result;
}
//------------------------------------------------------------------------------
#pragma mark - Events
-(void)readFrames:(UInt32)frames
audioBufferList:(AudioBufferList *)audioBufferList
bufferSize:(UInt32 *)bufferSize
eof:(BOOL *)eof {
@autoreleasepool {
// Setup the buffers
UInt32 outputBufferSize = 32 * frames; // 32 KB
audioBufferList->mNumberBuffers = 1;
audioBufferList->mBuffers[0].mNumberChannels = _clientFormat.mChannelsPerFrame;
audioBufferList->mBuffers[0].mDataByteSize = outputBufferSize;
audioBufferList->mBuffers[0].mData = (AudioUnitSampleType*)malloc(sizeof(AudioUnitSampleType*)*outputBufferSize);
[EZAudio checkResult:ExtAudioFileRead(_audioFile,
&frames,
audioBufferList)
operation:"Failed to read audio data from audio file"];
*bufferSize = audioBufferList->mBuffers[0].mDataByteSize/sizeof(AudioUnitSampleType);
*eof = frames == 0;
_frameIndex += frames;
if( self.audioFileDelegate ){
if( [self.audioFileDelegate respondsToSelector:@selector(audioFile:updatedPosition:)] ){
[self.audioFileDelegate audioFile:self
updatedPosition:_frameIndex];
}
if( [self.audioFileDelegate respondsToSelector:@selector(audioFile:readAudio:withBufferSize:withNumberOfChannels:)] ){
AEFloatConverterToFloat(_floatConverter,audioBufferList,_floatBuffers,frames);
[self.audioFileDelegate audioFile:self
readAudio:_floatBuffers
withBufferSize:frames
withNumberOfChannels:_clientFormat.mChannelsPerFrame];
}
//------------------------------------------------------------------------------
- (void)readFrames:(UInt32)frames
audioBufferList:(AudioBufferList *)audioBufferList
bufferSize:(UInt32 *)bufferSize
eof:(BOOL *)eof
{
if (pthread_mutex_trylock(&_lock) == 0)
{
// perform read
[EZAudio checkResult:ExtAudioFileRead(self.info.extAudioFileRef,
&frames,
audioBufferList)
operation:"Failed to read audio data from file"];
*bufferSize = frames;
*eof = frames == 0;
pthread_mutex_unlock(&_lock);
// notify delegate
if ([self.delegate respondsToSelector:@selector(audioFile:updatedPosition:)])
{
[self.delegate audioFile:self
updatedPosition:self.frameIndex];
}
if ([self.delegate respondsToSelector:@selector(audioFile:readAudio:withBufferSize:withNumberOfChannels:)])
{
[self.delegate audioFile:self
readAudio:nil
withBufferSize:*bufferSize
withNumberOfChannels:self.info.clientFormat.mChannelsPerFrame];
}
}
}
}
-(void)seekToFrame:(SInt64)frame {
[EZAudio checkResult:ExtAudioFileSeek(_audioFile,frame)
operation:"Failed to seek frame position within audio file"];
_frameIndex = frame;
if( self.audioFileDelegate ){
if( [self.audioFileDelegate respondsToSelector:@selector(audioFile:updatedPosition:)] ){
[self.audioFileDelegate audioFile:self updatedPosition:_frameIndex];
//------------------------------------------------------------------------------
- (void)seekToFrame:(SInt64)frame
{
if (pthread_mutex_trylock(&_lock) == 0)
{
[EZAudio checkResult:ExtAudioFileSeek(self.info.extAudioFileRef,
frame)
operation:"Failed to seek frame position within audio file"];
pthread_mutex_unlock(&_lock);
// notify delegate
if ([self.delegate respondsToSelector:@selector(audioFile:updatedPosition:)])
{
[self.delegate audioFile:self
updatedPosition:self.frameIndex];
}
}
}
}
//------------------------------------------------------------------------------
#pragma mark - Getters
-(void)getWaveformDataWithCompletionBlock:(WaveformDataCompletionBlock)waveformDataCompletionBlock {
SInt64 currentFramePosition = _frameIndex;
if( _waveformData ){
//------------------------------------------------------------------------------
- (EZAudioWaveformData *)getWaveformData
{
return [self getWaveformDataWithNumberOfPoints:EZAudioFileWaveformDefaultResolution];
}
//------------------------------------------------------------------------------
- (EZAudioWaveformData *)getWaveformDataWithNumberOfPoints:(UInt32)numberOfPoints
{
EZAudioWaveformData *waveformData;
if (pthread_mutex_trylock(&_lock) == 0)
{
// store current frame
SInt64 currentFrame = self.frameIndex;
UInt32 channels = self.clientFormat.mChannelsPerFrame;
BOOL interleaved = [EZAudio isInterleaved:self.clientFormat];
SInt64 totalFrames = self.totalClientFrames;
SInt64 framesPerBuffer = ((SInt64) totalFrames / numberOfPoints);
SInt64 framesPerChannel = framesPerBuffer / channels;
float **data = (float **)malloc( sizeof(float*) * channels );
for (int i = 0; i < channels; i++)
{
data[i] = (float *)malloc( sizeof(float) * numberOfPoints );
}
// seek to 0
[EZAudio checkResult:ExtAudioFileSeek(self.info.extAudioFileRef,
0)
operation:"Failed to seek frame position within audio file"];
// allocate an audio buffer list
AudioBufferList *audioBufferList = [EZAudio audioBufferListWithNumberOfFrames:(UInt32)totalFrames
numberOfChannels:self.info.clientFormat.mChannelsPerFrame
interleaved:interleaved];
UInt32 bufferSize = (UInt32)totalFrames;
[EZAudio checkResult:ExtAudioFileRead(self.info.extAudioFileRef,
&bufferSize,
audioBufferList)
operation:"Failed to read audio data from file waveform"];
// read through file and calculate rms at each point
SInt64 offset = 0;
for (SInt64 i = 0; i < numberOfPoints; i++)
{
float buffer[framesPerBuffer];
if (interleaved)
{
float *samples = (float *)audioBufferList->mBuffers[0].mData;
memcpy(buffer, &samples[offset], framesPerBuffer * sizeof(float));
for (int channel = 0; channel < channels; channel++)
{
float channelData[framesPerChannel];
for (int frame = 0; frame < framesPerChannel; frame++)
{
channelData[frame] = buffer[frame * channels + channel];
}
float rms = [EZAudio RMS:channelData length:(UInt32)framesPerChannel];
data[channel][i] = rms;
}
offset += channels * framesPerBuffer;
}
else
{
for (int channel = 0; channel < channels; channel++)
{
float *samples = (float *)audioBufferList->mBuffers[channel].mData;
memcpy(buffer, &samples[offset], framesPerBuffer * sizeof(float));
float rms = [EZAudio RMS:buffer length:(UInt32)framesPerBuffer];
data[channel][i] = rms;
}
offset += framesPerBuffer;
}
}
// clean up
[EZAudio freeBufferList:audioBufferList];
// seek back to previous position
[EZAudio checkResult:ExtAudioFileSeek(self.info.extAudioFileRef,
currentFrame)
operation:"Failed to seek frame position within audio file"];
pthread_mutex_unlock(&_lock);
waveformData = [EZAudioWaveformData dataWithNumberOfChannels:channels
buffers:(float **)data
bufferSize:numberOfPoints];
// cleanup
for (int i = 0; i < channels; i++)
{
free(data[i]);
}
free(data);
}
return waveformData;
}
//------------------------------------------------------------------------------
- (void)getWaveformDataWithCompletionBlock:(WaveformDataCompletionBlock)waveformDataCompletionBlock
{
[self getWaveformDataWithNumberOfPoints:EZAudioFileWaveformDefaultResolution
completion:waveformDataCompletionBlock];
}
//------------------------------------------------------------------------------
- (void)getWaveformDataWithNumberOfPoints:(UInt32)numberOfPoints
completion:(WaveformDataCompletionBlock)completion
{
if (!completion)
{
return;
}
// async get waveform data
dispatch_async(self.waveformQueue, ^{
EZAudioWaveformData *waveformData = [self getWaveformDataWithNumberOfPoints:numberOfPoints];
dispatch_async(dispatch_get_main_queue(), ^{
completion(waveformData);
});
});
}
//------------------------------------------------------------------------------
- (AudioStreamBasicDescription)clientFormat
{
return self.info.clientFormat;
}
//------------------------------------------------------------------------------
- (AudioStreamBasicDescription)fileFormat
{
return self.info.fileFormat;
}
//------------------------------------------------------------------------------
- (SInt64)frameIndex
{
SInt64 frameIndex;
[EZAudio checkResult:ExtAudioFileTell(self.info.extAudioFileRef, &frameIndex)
operation:"Failed to get frame index"];
return frameIndex;
}
//------------------------------------------------------------------------------
- (NSDictionary *)metadata
{
// get size of metadata property (dictionary)
UInt32 propSize = sizeof(_info.audioFileID);
CFDictionaryRef metadata;
UInt32 writable;
[EZAudio checkResult:AudioFileGetPropertyInfo(self.info.audioFileID,
kAudioFilePropertyInfoDictionary,
&propSize,
&writable)
operation:"Failed to get the size of the metadata dictionary"];
waveformDataCompletionBlock( _waveformData, _waveformTotalBuffers );
// pull metadata
[EZAudio checkResult:AudioFileGetProperty(self.info.audioFileID,
kAudioFilePropertyInfoDictionary,
&propSize,
&metadata)
operation:"Failed to get metadata dictionary"];
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0ul), ^{
_waveformFrameRate = [self recommendedDrawingFrameRate];
_waveformTotalBuffers = [self minBuffersWithFrameRate:_waveformFrameRate];
_waveformData = (float*)malloc(sizeof(float)*_waveformTotalBuffers);
// cast to NSDictionary
return (__bridge NSDictionary*)metadata;
}
//------------------------------------------------------------------------------
- (NSTimeInterval)totalDuration
{
SInt64 totalFrames = [self totalFrames];
return (NSTimeInterval) totalFrames / self.info.fileFormat.mSampleRate;
}
//------------------------------------------------------------------------------
- (SInt64)totalClientFrames
{
SInt64 totalFrames = [self totalFrames];
for( int i = 0; i < _waveformTotalBuffers; i++ ){
// Take a snapshot of each buffer through the audio file to form the waveform
AudioBufferList *bufferList = [EZAudio audioBufferList];
UInt32 bufferSize;
BOOL eof;
// Setup the buffers
UInt32 outputBufferSize = 32 * _waveformFrameRate; // 32 KB
bufferList->mNumberBuffers = 1;
bufferList->mBuffers[0].mNumberChannels = _clientFormat.mChannelsPerFrame;
bufferList->mBuffers[0].mDataByteSize = outputBufferSize;
bufferList->mBuffers[0].mData = (AudioUnitSampleType*)malloc(sizeof(AudioUnitSampleType*)*outputBufferSize);
// Read in the specified number of frames
[EZAudio checkResult:ExtAudioFileRead(_audioFile,
&_waveformFrameRate,
bufferList)
operation:"Failed to read audio data from audio file"];
bufferSize = bufferList->mBuffers[0].mDataByteSize/sizeof(AudioUnitSampleType);
eof = _waveformFrameRate == 0;
_frameIndex += _waveformFrameRate;
// Convert to floats
AEFloatConverterToFloat(_floatConverter,bufferList,_floatBuffers,_waveformFrameRate);
// Calculate RMS of each buffer
float rms = [EZAudio RMS:_floatBuffers[0]
length:bufferSize];
_waveformData[i] = rms;
// Since we malloc'ed, we should cleanup
[EZAudio freeBufferList:bufferList];
// check sample rate of client vs file format
AudioStreamBasicDescription clientFormat = self.info.clientFormat;
AudioStreamBasicDescription fileFormat = self.info.fileFormat;
BOOL sameSampleRate = clientFormat.mSampleRate == fileFormat.mSampleRate;
if (!sameSampleRate)
{
NSTimeInterval duration = [self totalDuration];
totalFrames = duration * clientFormat.mSampleRate;
}
// Seek the audio file back to the beginning
[EZAudio checkResult:ExtAudioFileSeek(_audioFile,currentFramePosition)
operation:"Failed to seek frame position within audio file"];
_frameIndex = currentFramePosition;
return totalFrames;
}
//------------------------------------------------------------------------------
- (SInt64)totalFrames
{
SInt64 totalFrames;
UInt32 size = sizeof(SInt64);
[EZAudio checkResult:ExtAudioFileGetProperty(self.info.extAudioFileRef,
kExtAudioFileProperty_FileLengthFrames,
&size,
&totalFrames)
operation:"Failed to get total frames"];
return totalFrames;
}
//------------------------------------------------------------------------------
- (NSURL*)url
{
return (__bridge NSURL*)self.info.sourceURL;
}
//------------------------------------------------------------------------------
#pragma mark - Setters
//------------------------------------------------------------------------------
- (void)setClientFormat:(AudioStreamBasicDescription)clientFormat
{
NSAssert([EZAudio isLinearPCM:clientFormat], @"Client format must be linear PCM");
// Once we're done send off the waveform data
dispatch_async(dispatch_get_main_queue(), ^{
waveformDataCompletionBlock( _waveformData, _waveformTotalBuffers );
});
});
// store the client format
_info.clientFormat = clientFormat;
// set the client format on the extended audio file ref
[EZAudio checkResult:ExtAudioFileSetProperty(self.info.extAudioFileRef,
kExtAudioFileProperty_ClientDataFormat,
sizeof(clientFormat),
&clientFormat)
operation:"Couldn't set client data format on file"];
}
-(AudioStreamBasicDescription)clientFormat {
return _clientFormat;
//------------------------------------------------------------------------------
-(void)dealloc
{
pthread_mutex_destroy(&_lock);
[EZAudio checkResult:AudioFileClose(self.info.audioFileID) operation:"Failed to close audio file"];
[EZAudio checkResult:ExtAudioFileDispose(self.info.extAudioFileRef) operation:"Failed to dispose of ext audio file"];
}
-(AudioStreamBasicDescription)fileFormat {
return _fileFormat;
}
-(SInt64)frameIndex {
return _frameIndex;
}
-(Float32)totalDuration {
return _totalDuration;
}
-(SInt64)totalFrames {
return _totalFrames;
}
#pragma mark - Helpers
-(UInt32)minBuffersWithFrameRate:(UInt32)frameRate {
frameRate = frameRate > 0 ? frameRate : 1;
return (UInt32) _totalFrames / frameRate + 1;
}
-(UInt32)recommendedDrawingFrameRate {
return (UInt32) _totalFrames / 2057 - 1;
}
#pragma mark - Cleanup
-(void)dealloc {
if( _waveformData ){
free(_waveformData);
}
[EZAudio checkResult:ExtAudioFileDispose(_audioFile)
operation:"Failed to dispose of extended audio file."];
}
//------------------------------------------------------------------------------
@end
+300
View File
@@ -0,0 +1,300 @@
//
// EZAudioPlayer.h
// EZAudio
//
// Created by Syed Haris Ali on 1/16/14.
// Copyright (c) 2014 Syed Haris Ali. All rights reserved.
//
// 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 <Foundation/Foundation.h>
#import "TargetConditionals.h"
#import "EZAudio.h"
#if TARGET_OS_IPHONE
#import <AVFoundation/AVFoundation.h>
#elif TARGET_OS_MAC
#endif
@class EZAudioPlayer;
/**
The EZAudioPlayerDelegate provides event callbacks for the EZAudioPlayer. These type of events are triggered by changes in the EZAudioPlayer's state and allow someone implementing the EZAudioPlayer to more easily update their user interface. Events are triggered anytime the EZAudioPlayer resumes/pauses playback, reaches the end of the file, reads audio data and converts it to float data visualizations (using the EZAudioFile), and updates its cursor position within the audio file during playback (use this for the play position on a slider on the user interface).
@warning These callbacks don't necessarily occur on the main thread so make sure you wrap any UI code in a GCD block like: dispatch_async(dispatch_get_main_queue(), ^{ // Update UI });
*/
@protocol EZAudioPlayerDelegate <NSObject>
@optional
/**
Triggered by the EZAudioPlayer when the playback has been resumed or started.
@param audioPlayer The instance of the EZAudioPlayer that triggered the event
@param audioFile The instance of the EZAudioFile that the event was triggered from
*/
-(void)audioPlayer:(EZAudioPlayer*)audioPlayer didResumePlaybackOnAudioFile:(EZAudioFile*)audioFile;
/**
Triggered by the EZAudioPlayer when the playback has been paused.
@param audioPlayer The instance of the EZAudioPlayer that triggered the event
@param audioFile The instance of the EZAudioFile that the event was triggered from
*/
-(void)audioPlayer:(EZAudioPlayer*)audioPlayer didPausePlaybackOnAudioFile:(EZAudioFile*)audioFile;
/**
Triggered by the EZAudioPlayer when the output has reached the end of the EZAudioFile it's playing. If the EZAudioPlayer has its `shouldLoop` property set to true this will trigger, but playback will continue to loop once its hit the end of the audio file.
@param audioPlayer The instance of the EZAudioPlayer that triggered the event
@param audioFile The instance of the EZAudioFile that the event was triggered from
*/
-(void)audioPlayer:(EZAudioPlayer*)audioPlayer reachedEndOfAudioFile:(EZAudioFile*)audioFile;
/**
Triggered by the EZAudioPlayer's internal EZAudioFile's EZAudioFileDelegate callback and notifies the delegate of the read audio data as a float array instead of a buffer list. Common use case of this would be to visualize the float data using an audio plot or audio data dependent OpenGL sketch.
@param audioPlayer The instance of the EZAudioPlayer that triggered the event
@param buffer A float array of float arrays holding the audio data. buffer[0] would be the left channel's float array while buffer[1] would be the right channel's float array in a stereo file.
@param bufferSize The length of the buffers float arrays
@param numberOfChannels The number of channels. 2 for stereo, 1 for mono.
@param audioFile The instance of the EZAudioFile that the event was triggered from
*/
-(void) audioPlayer:(EZAudioPlayer*)audioPlayer
readAudio:(float**)buffer
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels
inAudioFile:(EZAudioFile*)audioFile;;
/**
Triggered by EZAudioPlayer's internal EZAudioFile's EZAudioFileDelegate callback and notifies the delegate of the current playback position. The framePosition provides the current frame position and can be calculated against the EZAudioPlayer's total frames using the `totalFrames` function from the EZAudioPlayer.
@param audioPlayer The instance of the EZAudioPlayer that triggered the event
@param framePosition The new frame index as a 64-bit signed integer
@param audioFile The instance of the EZAudioFile that the event was triggered from
*/
-(void)audioPlayer:(EZAudioPlayer*)audioPlayer
updatedPosition:(SInt64)framePosition
inAudioFile:(EZAudioFile*)audioFile;
@end
/**
The EZAudioPlayer acts as the master delegate (the EZAudioFileDelegate) over whatever EZAudioFile it is using for playback. Classes that want to get the EZAudioFileDelegate callbacks should implement the EZAudioPlayer's EZAudioPlayerDelegate on the EZAudioPlayer instance.
*/
@interface EZAudioPlayer : NSObject
#pragma mark - Properties
///-----------------------------------------------------------
/// @name Properties
///-----------------------------------------------------------
/**
The EZAudioPlayerDelegate that will handle the audio player callbacks
*/
@property (nonatomic,assign) id<EZAudioPlayerDelegate> audioPlayerDelegate;
/**
A BOOL indicating whether the player should loop the file
*/
@property (nonatomic,assign) BOOL shouldLoop;
#pragma mark - Initializers
///-----------------------------------------------------------
/// @name Initializers
///-----------------------------------------------------------
/**
Initializes the EZAudioPlayer with an EZAudioFile instance. This does not use the EZAudioFile by reference, but instead creates a separate EZAudioFile instance with the same file at the given file path provided by the internal NSURL to use for internal seeking so it doesn't cause any locking between the caller's instance of the EZAudioFile.
@param audioFile The instance of the EZAudioFile to use for initializing the EZAudioPlayer
@return The newly created instance of the EZAudioPlayer
*/
-(EZAudioPlayer*)initWithEZAudioFile:(EZAudioFile*)audioFile;
/**
Initializes the EZAudioPlayer with an EZAudioFile instance and provides a way to assign the EZAudioPlayerDelegate on instantiation. This does not use the EZAudioFile by reference, but instead creates a separate EZAudioFile instance with the same file at the given file path provided by the internal NSURL to use for internal seeking so it doesn't cause any locking between the caller's instance of the EZAudioFile.
@param audioFile The instance of the EZAudioFile to use for initializing the EZAudioPlayer
@param audioPlayerDelegate The receiver that will act as the EZAudioPlayerDelegate. Set to nil if it should have no delegate or use the initWithEZAudioFile: function instead.
@return The newly created instance of the EZAudioPlayer
*/
-(EZAudioPlayer*)initWithEZAudioFile:(EZAudioFile*)audioFile
withDelegate:(id<EZAudioPlayerDelegate>)audioPlayerDelegate;
/**
Initializes the EZAudioPlayer with an NSURL instance representing the file path of the audio file.
@param url The NSURL instance representing the file path of the audio file.
@return The newly created instance of the EZAudioPlayer
*/
-(EZAudioPlayer*)initWithURL:(NSURL*)url;
/**
Initializes the EZAudioPlayer with an NSURL instance representing the file path of the audio file and a caller to assign as the EZAudioPlayerDelegate on instantiation.
@param url The NSURL instance representing the file path of the audio file.
@param audioPlayerDelegate The receiver that will act as the EZAudioPlayerDelegate. Set to nil if it should have no delegate or use the initWithEZAudioFile: function instead.
@return The newly created instance of the EZAudioPlayer
*/
-(EZAudioPlayer*)initWithURL:(NSURL*)url
withDelegate:(id<EZAudioPlayerDelegate>)audioPlayerDelegate;
#pragma mark - Class Initializers
///-----------------------------------------------------------
/// @name Class Initializers
///-----------------------------------------------------------
/**
Class initializer that initializes the EZAudioPlayer with an EZAudioFile instance. This does not use the EZAudioFile by reference, but instead creates a separate EZAudioFile instance with the same file at the given file path provided by the internal NSURL to use for internal seeking so it doesn't cause any locking between the caller's instance of the EZAudioFile.
@param audioFile The instance of the EZAudioFile to use for initializing the EZAudioPlayer
@return The newly created instance of the EZAudioPlayer
*/
+(EZAudioPlayer*)audioPlayerWithEZAudioFile:(EZAudioFile*)audioFile;
/**
Class initializer that initializes the EZAudioPlayer with an EZAudioFile instance and provides a way to assign the EZAudioPlayerDelegate on instantiation. This does not use the EZAudioFile by reference, but instead creates a separate EZAudioFile instance with the same file at the given file path provided by the internal NSURL to use for internal seeking so it doesn't cause any locking between the caller's instance of the EZAudioFile.
@param audioFile The instance of the EZAudioFile to use for initializing the EZAudioPlayer
@param audioPlayerDelegate The receiver that will act as the EZAudioPlayerDelegate. Set to nil if it should have no delegate or use the audioPlayerWithEZAudioFile: function instead.
@return The newly created instance of the EZAudioPlayer
*/
+(EZAudioPlayer*)audioPlayerWithEZAudioFile:(EZAudioFile*)audioFile
withDelegate:(id<EZAudioPlayerDelegate>)audioPlayerDelegate;
/**
Class initializer that initializes the EZAudioPlayer with an NSURL instance representing the file path of the audio file.
@param url The NSURL instance representing the file path of the audio file.
@return The newly created instance of the EZAudioPlayer
*/
+(EZAudioPlayer*)audioPlayerWithURL:(NSURL*)url;
/**
Class initializer that initializes the EZAudioPlayer with an NSURL instance representing the file path of the audio file and a caller to assign as the EZAudioPlayerDelegate on instantiation.
@param url The NSURL instance representing the file path of the audio file.
@param audioPlayerDelegate The receiver that will act as the EZAudioPlayerDelegate. Set to nil if it should have no delegate or use the audioPlayerWithURL: function instead.
@return The newly created instance of the EZAudioPlayer
*/
+(EZAudioPlayer*)audioPlayerWithURL:(NSURL*)url
withDelegate:(id<EZAudioPlayerDelegate>)audioPlayerDelegate;
#pragma mark - Singleton
///-----------------------------------------------------------
/// @name Shared Instance
///-----------------------------------------------------------
/**
The shared instance (singleton) of the audio player. Most applications will only have one instance of the EZAudioPlayer that can be reused with multiple different audio files.
* @return The shared instance of the EZAudioPlayer.
*/
+(EZAudioPlayer*)sharedAudioPlayer;
#pragma mark - Getters
///-----------------------------------------------------------
/// @name Getting The Audio Player's Properties
///-----------------------------------------------------------
/**
Provides the EZAudioFile instance that is being used as the datasource for playback.
@return The EZAudioFile instance that is currently being used for playback.
*/
-(EZAudioFile*)audioFile;
/**
Provides the current time (a.k.a. the seek position) in seconds within the audio file that's being used for playback. This can be helpful when displaying the audio player's current time over duration.
@return A float representing the current time within the audio file used for playback.
*/
-(float)currentTime;
/**
Provides a flag indicating whether the EZAudioPlayer has reached the end of the audio file used for playback.
@return A BOOL indicating whether or not the EZAudioPlayer has reached the end of the file it is using for playback.
*/
-(BOOL)endOfFile;
/**
Provides the frame index (a.k.a the seek positon) within the audio file being used for playback. This can be helpful when seeking through the audio file.
@return An SInt64 representing the current frame index within the audio file used for playback.
*/
-(SInt64)frameIndex;
/**
Provides a flag indicating whether the EZAudioPlayer is currently playing back any audio.
@return A BOOL indicating whether or not the EZAudioPlayer is performing playback,
*/
-(BOOL)isPlaying;
/**
Provides the EZOutput instance that is being used to provide playback to the system output.
@return The EZOutput instance that is currently being used for output playback.
*/
-(EZOutput*)output;
/**
Provides the total duration of the current audio file being used for playback (in seconds).
@return A float representing the total duration of the current audio file being used for playback in seconds.
*/
-(float)totalDuration;
/**
Provides the total amount of frames in the current audio file being used for playback.
@return A SInt64 representing the total amount of frames in the current audio file being used for playback.
*/
-(SInt64)totalFrames;
/**
Provides the file path that's currently being used by the player for playback.
@return The NSURL representing the file path of the audio file being used for playback.
*/
-(NSURL*)url;
#pragma mark - Setters
///-----------------------------------------------------------
/// @name Setting The File/Output
///-----------------------------------------------------------
/**
Sets the EZAudioFile to use for playback. This does not use the EZAudioFile by reference, but instead creates a separate EZAudioFile instance with the same file at the given file path provided by the internal NSURL to use for internal seeking so it doesn't cause any locking between the caller's instance of the EZAudioFile.
@param audioFile The new EZAudioFile instance that should be used for playback
*/
-(void)setAudioFile:(EZAudioFile*)audioFile;
/**
Sets the EZOutput to route playback. By default this uses the [EZOutput sharedOutput] singleton.
@param output The new EZOutput instance that should be used for playback
*/
-(void)setOutput:(EZOutput*)output;
#pragma mark - Methods
///-----------------------------------------------------------
/// @name Play/Pause/Seeking the Player
///-----------------------------------------------------------
/**
Starts or resumes playback.
*/
-(void)play;
/**
Pauses playback.
*/
-(void)pause;
/**
Stops playback.
*/
-(void)stop;
/**
Seeks playback to a specified frame within the internal EZAudioFile. This will notify the EZAudioFileDelegate (if specified) with the audioPlayer:updatedPosition:inAudioFile: function.
@param frame The new frame position to seek to as a SInt64.
*/
-(void)seekToFrame:(SInt64)frame;
@end
+296
View File
@@ -0,0 +1,296 @@
//
// EZAudioPlayer.m
// EZAudio
//
// Created by Syed Haris Ali on 1/16/14.
// Copyright (c) 2014 Syed Haris Ali. All rights reserved.
//
// 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 "EZAudioPlayer.h"
#if TARGET_OS_IPHONE
#elif TARGET_OS_MAC
#endif
@interface EZAudioPlayer () <EZAudioFileDelegate,EZOutputDataSource>
{
BOOL _eof;
}
@property (nonatomic,strong,setter=setAudioFile:) EZAudioFile *audioFile;
@property (nonatomic,strong,setter=setOutput:) EZOutput *output;
@end
@implementation EZAudioPlayer
@synthesize audioFile = _audioFile;
@synthesize audioPlayerDelegate = _audioPlayerDelegate;
@synthesize output = _output;
@synthesize shouldLoop = _shouldLoop;
#pragma mark - Initializers
-(id)init {
self = [super init];
if(self){
[self _configureAudioPlayer];
}
return self;
}
-(EZAudioPlayer*)initWithEZAudioFile:(EZAudioFile *)audioFile {
return [self initWithEZAudioFile:audioFile withDelegate:nil];
}
-(EZAudioPlayer *)initWithEZAudioFile:(EZAudioFile *)audioFile
withDelegate:(id<EZAudioPlayerDelegate>)audioPlayerDelegate {
self = [super init];
if(self){
// This should make a separate reference to the audio file
[self _configureAudioPlayer];
self.audioFile = audioFile;
self.audioPlayerDelegate = audioPlayerDelegate;
}
return self;
}
-(EZAudioPlayer *)initWithURL:(NSURL *)url {
return [self initWithURL:url withDelegate:nil];
}
-(EZAudioPlayer *)initWithURL:(NSURL *)url
withDelegate:(id<EZAudioPlayerDelegate>)audioPlayerDelegate {
self = [super init];
if(self){
[self _configureAudioPlayer];
self.audioFile = [EZAudioFile audioFileWithURL:url andDelegate:self];
self.audioPlayerDelegate = audioPlayerDelegate;
}
return self;
}
#pragma mark - Class Initializers
+(EZAudioPlayer *)audioPlayerWithEZAudioFile:(EZAudioFile *)audioFile {
return [[EZAudioPlayer alloc] initWithEZAudioFile:audioFile];
}
+(EZAudioPlayer *)audioPlayerWithEZAudioFile:(EZAudioFile *)audioFile
withDelegate:(id<EZAudioPlayerDelegate>)audioPlayerDelegate {
return [[EZAudioPlayer alloc] initWithEZAudioFile:audioFile
withDelegate:audioPlayerDelegate];
}
+(EZAudioPlayer *)audioPlayerWithURL:(NSURL *)url {
return [[EZAudioPlayer alloc] initWithURL:url];
}
+(EZAudioPlayer *)audioPlayerWithURL:(NSURL *)url
withDelegate:(id<EZAudioPlayerDelegate>)audioPlayerDelegate {
return [[EZAudioPlayer alloc] initWithURL:url
withDelegate:audioPlayerDelegate];
}
#pragma mark - Singleton
+(EZAudioPlayer *)sharedAudioPlayer {
static EZAudioPlayer *_sharedAudioPlayer = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedAudioPlayer = [[EZAudioPlayer alloc] init];
});
return _sharedAudioPlayer;
}
#pragma mark - Private Configuration
-(void)_configureAudioPlayer {
// Defaults
self.output = [EZOutput sharedOutput];
#if TARGET_OS_IPHONE
// Configure the AVSession
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *err = NULL;
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&err];
if( err ){
NSLog(@"There was an error creating the audio session");
}
[audioSession overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:NULL];
if( err ){
NSLog(@"There was an error sending the audio to the speakers");
}
#elif TARGET_OS_MAC
#endif
}
#pragma mark - Getters
-(EZAudioFile*)audioFile {
return _audioFile;
}
-(float)currentTime {
NSAssert(_audioFile,@"No audio file to perform the seek on, check that EZAudioFile is not nil");
return [EZAudio MAP:self.audioFile.frameIndex
leftMin:0
leftMax:self.audioFile.totalFrames
rightMin:0
rightMax:self.audioFile.totalDuration];
}
-(BOOL)endOfFile {
return _eof;
}
-(SInt64)frameIndex {
NSAssert(_audioFile,@"No audio file to perform the seek on, check that EZAudioFile is not nil");
return _audioFile.frameIndex;
}
-(BOOL)isPlaying {
return self.output.isPlaying;
}
-(EZOutput*)output {
NSAssert(_output,@"No output was found, this should by default be the EZOutput shared instance");
return _output;
}
-(float)totalDuration {
NSAssert(_audioFile,@"No audio file to perform the seek on, check that EZAudioFile is not nil");
return _audioFile.totalDuration;
}
-(SInt64)totalFrames {
NSAssert(_audioFile,@"No audio file to perform the seek on, check that EZAudioFile is not nil");
return _audioFile.totalFrames;
}
-(NSURL *)url {
NSAssert(_audioFile,@"No audio file to perform the seek on, check that EZAudioFile is not nil");
return _audioFile.url;
}
#pragma mark - Setters
-(void)setAudioFile:(EZAudioFile *)audioFile {
if( _audioFile ){
_audioFile.audioFileDelegate = nil;
}
_eof = NO;
_audioFile = [EZAudioFile audioFileWithURL:audioFile.url andDelegate:self];
NSAssert(_output,@"No output was found, this should by default be the EZOutput shared instance");
[_output setAudioStreamBasicDescription:self.audioFile.clientFormat];
}
-(void)setOutput:(EZOutput*)output {
_output = output;
_output.outputDataSource = self;
}
#pragma mark - Methods
-(void)play {
NSAssert(_audioFile,@"No audio file to perform the seek on, check that EZAudioFile is not nil");
if( _audioFile ){
[_output startPlayback];
if( self.frameIndex != self.totalFrames ){
_eof = NO;
}
if( self.audioPlayerDelegate ){
if( [self.audioPlayerDelegate respondsToSelector:@selector(audioPlayer:didResumePlaybackOnAudioFile:)] ){
// Notify the delegate we're starting playback
[self.audioPlayerDelegate audioPlayer:self didResumePlaybackOnAudioFile:_audioFile];
}
}
}
}
-(void)pause {
NSAssert(self.audioFile,@"No audio file to perform the seek on, check that EZAudioFile is not nil");
if( _audioFile ){
[_output stopPlayback];
if( self.audioPlayerDelegate ){
if( [self.audioPlayerDelegate respondsToSelector:@selector(audioPlayer:didPausePlaybackOnAudioFile:)] ){
// Notify the delegate we're pausing playback
[self.audioPlayerDelegate audioPlayer:self didPausePlaybackOnAudioFile:_audioFile];
}
}
}
}
-(void)seekToFrame:(SInt64)frame {
NSAssert(_audioFile,@"No audio file to perform the seek on, check that EZAudioFile is not nil");
if( _audioFile ){
[_audioFile seekToFrame:frame];
}
if( self.frameIndex != self.totalFrames ){
_eof = NO;
}
}
-(void)stop {
NSAssert(_audioFile,@"No audio file to perform the seek on, check that EZAudioFile is not nil");
if( _audioFile ){
[_output stopPlayback];
[_audioFile seekToFrame:0];
_eof = NO;
}
}
#pragma mark - EZAudioFileDelegate
-(void)audioFile:(EZAudioFile *)audioFile
readAudio:(float **)buffer
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels {
if( self.audioPlayerDelegate ){
if( [self.audioPlayerDelegate respondsToSelector:@selector(audioPlayer:readAudio:withBufferSize:withNumberOfChannels:inAudioFile:)] ){
[self.audioPlayerDelegate audioPlayer:self
readAudio:buffer
withBufferSize:bufferSize
withNumberOfChannels:numberOfChannels
inAudioFile:audioFile];
}
}
}
-(void)audioFile:(EZAudioFile *)audioFile updatedPosition:(SInt64)framePosition {
if( self.audioPlayerDelegate ){
if( [self.audioPlayerDelegate respondsToSelector:@selector(audioPlayer:updatedPosition:inAudioFile:)] ){
[self.audioPlayerDelegate audioPlayer:self
updatedPosition:framePosition
inAudioFile:audioFile];
}
}
}
#pragma mark - EZOutputDataSource
-(void) output:(EZOutput *)output
shouldFillAudioBufferList:(AudioBufferList *)audioBufferList
withNumberOfFrames:(UInt32)frames
{
if( self.audioFile )
{
UInt32 bufferSize;
[self.audioFile readFrames:frames
audioBufferList:audioBufferList
bufferSize:&bufferSize
eof:&_eof];
if( _eof && self.shouldLoop )
{
[self seekToFrame:0];
}
}
}
@end
+35 -4
View File
@@ -28,10 +28,9 @@
@class EZAudio;
/**
The default since of the rolling audio plot. TODO: In future release make this real-time adjustable.
*/
#define kEZAudioPlotHistoryBufferSize 1024
#define kEZAudioPlotMaxHistoryBufferLength (8192)
#define kEZAudioPlotDefaultHistoryBufferLength (1024)
/**
`EZAudioPlot`, a subclass of `EZPlot`, is a cross-platform (iOS and OSX) class that plots an audio waveform using Core Graphics.
@@ -47,5 +46,37 @@
*/
@interface EZAudioPlot : EZPlot
{
CGPoint *plotData;
UInt32 plotLength;
}
#pragma mark - Adjust Resolution
///-----------------------------------------------------------
/// @name Adjusting The Resolution
///-----------------------------------------------------------
/**
Sets the length of the rolling history display. Can grow or shrink the display up to the maximum size specified by the kEZAudioPlotMaxHistoryBufferLength macro. Will return the actual set value, which will be either the given value if smaller than the kEZAudioPlotMaxHistoryBufferLength or kEZAudioPlotMaxHistoryBufferLength if a larger value is attempted to be set.
@param historyLength The new length of the rolling history buffer.
@return The new value equal to the historyLength or the kEZAudioPlotMaxHistoryBufferLength.
*/
-(int)setRollingHistoryLength:(int)historyLength;
/**
Provides the length of the rolling history buffer
* @return An int representing the length of the rolling history buffer
*/
-(int)rollingHistoryLength;
#pragma mark - Subclass Methods
/**
<#Description#>
@param data <#theplotData description#>
@param length <#length description#>
*/
-(void)setSampleData:(float *)data
length:(int)length;
@end
+70 -59
View File
@@ -27,14 +27,16 @@
#import "EZAudio.h"
#define kCircularBufferSizeTemp 512
@interface EZAudioPlot () {
BOOL _hasData;
TPCircularBuffer _historyBuffer;
CGPoint *_sampleData;
UInt32 _sampleLength;
// BOOL _hasData;
// TPCircularBuffer _historyBuffer;
// Rolling History
BOOL _setMaxLength;
float *_scrollHistory;
int _scrollHistoryIndex;
UInt32 _scrollHistoryLength;
BOOL _changingHistorySize;
}
@end
@@ -87,6 +89,9 @@
self.plotType = EZPlotTypeRolling;
self.shouldMirror = NO;
self.shouldFill = NO;
plotData = NULL;
_scrollHistory = NULL;
_scrollHistoryLength = kEZAudioPlotDefaultHistoryBufferLength;
}
#pragma mark - Setters
@@ -129,18 +134,18 @@
}
#pragma mark - Get Data
- (void)_setSampleData:(float *)the_sampleData
length:(int)length {
if( _sampleData != nil ){
free(_sampleData);
-(void)setSampleData:(float *)data
length:(int)length {
if( plotData != nil ){
free(plotData);
}
_sampleData = (CGPoint *)calloc(sizeof(CGPoint),length);
_sampleLength = length;
plotData = (CGPoint *)calloc(sizeof(CGPoint),length);
plotLength = length;
for(int i = 0; i < length; i++) {
the_sampleData[i] = i == 0 || i == length - 1 ? 0 : the_sampleData[i];
_sampleData[i] = CGPointMake(i,the_sampleData[i] * _gain);
data[i] = i == 0 ? 0 : data[i];
plotData[i] = CGPointMake(i,data[i] * _gain);
}
[self _refreshDisplay];
@@ -150,45 +155,24 @@
-(void)updateBuffer:(float *)buffer withBufferSize:(UInt32)bufferSize {
if( _plotType == EZPlotTypeRolling ){
// Initialize the circular buffer
!_hasData ? TPCircularBufferInit(&_historyBuffer,kCircularBufferSizeTemp) : 0;
_hasData = YES;
// Initialize
_sampleLength = bufferSize;
// Copy into the circular buffer
float rmsValue = [EZAudio RMS:buffer length:bufferSize];
float snapshot[1];
snapshot[0] = rmsValue;
// self.rmsLabel.text = [NSString stringWithFormat:@"RMS: %.4f",rmsValue];
BOOL result = TPCircularBufferProduceBytes(&_historyBuffer,
snapshot,
sizeof(snapshot));
if(result){
// Populate the graph
int32_t availableBytes;
float *cBuf = TPCircularBufferTail(&_historyBuffer,&availableBytes);
for(int i = 0; i < kCircularBufferSizeTemp; i++) {
if( availableBytes == kCircularBufferSizeTemp*sizeof(float) ){
cBuf[i] = 0.0f;
}
}
[self _setSampleData:cBuf
length:kCircularBufferSizeTemp];
if( availableBytes == kCircularBufferSizeTemp*sizeof(float) ){
TPCircularBufferConsume(&_historyBuffer,kCircularBufferSizeTemp*sizeof(float));
}
}
else {
NSLog(@"Failed to copy bytes to circular buffer 1");
}
// Update the scroll history datasource
[EZAudio updateScrollHistory:&_scrollHistory
withLength:_scrollHistoryLength
atIndex:&_scrollHistoryIndex
withBuffer:buffer
withBufferSize:bufferSize
isResolutionChanging:&_changingHistorySize];
//
[self setSampleData:_scrollHistory
length:(!_setMaxLength?kEZAudioPlotMaxHistoryBufferLength:_scrollHistoryLength)];
_setMaxLength = YES;
}
else if( _plotType == EZPlotTypeBuffer ){
[self _setSampleData:buffer
length:bufferSize];
[self setSampleData:buffer
length:bufferSize];
}
else {
@@ -197,9 +181,7 @@
}
}
#pragma mark - Drawing
#if TARGET_OS_IPHONE
- (void)drawRect:(CGRect)rect
{
@@ -227,16 +209,18 @@
[(NSColor*)self.color set];
#endif
if(_sampleLength > 0) {
if(plotLength > 0) {
plotData[plotLength-1] = CGPointMake(plotLength-1,0.0f);
CGMutablePathRef halfPath = CGPathCreateMutable();
CGPathAddLines(halfPath,
NULL,
_sampleData,
_sampleLength);
plotData,
plotLength);
CGMutablePathRef path = CGPathCreateMutable();
double xscale = (frame.size.width) / (float)_sampleLength;
double xscale = (frame.size.width) / (float)plotLength;
double halfHeight = floor( frame.size.height / 2.0 );
// iOS drawing origin is flipped by default so make sure we account for that
@@ -278,10 +262,37 @@
#elif TARGET_OS_MAC
[[NSGraphicsContext currentContext] restoreGraphicsState];
#endif
}
#pragma mark - Adjust Resolution
-(int)setRollingHistoryLength:(int)historyLength {
historyLength = MIN(historyLength,kEZAudioPlotMaxHistoryBufferLength);
size_t floatByteSize = sizeof(float);
_changingHistorySize = YES;
if( _scrollHistoryLength != historyLength ){
_scrollHistoryLength = historyLength;
}
_scrollHistory = realloc(_scrollHistory,_scrollHistoryLength*floatByteSize);
if( _scrollHistoryIndex < _scrollHistoryLength ){
memset(&_scrollHistory[_scrollHistoryIndex],
0,
(_scrollHistoryLength-_scrollHistoryIndex)*floatByteSize);
}
else {
_scrollHistoryIndex = _scrollHistoryLength;
}
_changingHistorySize = NO;
return historyLength;
}
-(int)rollingHistoryLength {
return _scrollHistoryLength;
}
-(void)dealloc {
free(_sampleData);
if( plotData ){
free(plotData);
}
}
@end
+27
View File
@@ -124,7 +124,34 @@ typedef struct {
#endif
#pragma mark - Adjust Resolution
///-----------------------------------------------------------
/// @name Adjusting The Resolution
///-----------------------------------------------------------
/**
Sets the length of the rolling history display. Can grow or shrink the display up to the maximum size specified by the kEZAudioPlotMaxHistoryBufferLength macro. Will return the actual set value, which will be either the given value if smaller than the kEZAudioPlotMaxHistoryBufferLength or kEZAudioPlotMaxHistoryBufferLength if a larger value is attempted to be set.
@param historyLength The new length of the rolling history buffer.
@return The new value equal to the historyLength or the kEZAudioPlotMaxHistoryBufferLength.
*/
-(int)setRollingHistoryLength:(int)historyLength;
/**
Provides the length of the rolling history buffer
* @return An int representing the length of the rolling history buffer
*/
-(int)rollingHistoryLength;
#pragma mark - Shared Methods
///-----------------------------------------------------------
/// @name Clearing The Plot
///-----------------------------------------------------------
/**
Clears all data from the audio plot (includes both EZPlotTypeBuffer and EZPlotTypeRolling)
*/
-(void)clear;
///-----------------------------------------------------------
/// @name Shared OpenGL Methods
///-----------------------------------------------------------
+123 -51
View File
@@ -26,6 +26,7 @@
#import "EZAudioPlotGL.h"
#import "EZAudio.h"
#import "EZAudioPlot.h"
#if TARGET_OS_IPHONE
#import "EZAudioPlotGLKViewController.h"
@@ -54,9 +55,17 @@
UInt32 _bufferPlotGraphSize;
UInt32 _rollingPlotGraphSize;
// History buffer
UInt32 _historyIndex;
float _historyBuffer[kEZAudioPlotHistoryBufferSize];
// Rolling History
BOOL _setMaxLength;
float *_scrollHistory;
int _scrollHistoryIndex;
UInt32 _scrollHistoryLength;
BOOL _changingHistorySize;
// Copied buffer data
float *_copiedBuffer;
UInt32 _copiedBufferSize;
}
@property (nonatomic,assign,readonly) EZAudioPlotGLDrawType drawingType;
@property (nonatomic,strong) GLKBaseEffect *baseEffect;
@@ -105,15 +114,14 @@
#pragma mark - Initialize Properties Here
-(void)initializeView {
#if TARGET_OS_IPHONE
// Initialize the subview controller
_glViewController = [[EZAudioPlotGLKViewController alloc] init];
_glViewController.view.frame = self.frame;
_glViewController.view.frame = self.bounds;
[self insertSubview:self.glViewController.view atIndex:0];
#elif TARGET_OS_MAC
_copiedBuffer = NULL;
#endif
// Set the default properties
self.gain = 1.0;
self.plotType = EZPlotTypeBuffer;
@@ -121,10 +129,11 @@
self.backgroundColor = [UIColor colorWithRed:0.796 green:0.749 blue:0.663 alpha:1];
self.color = [UIColor colorWithRed:0.481 green:0.548 blue:0.637 alpha:1];
#elif TARGET_OS_MAC
_scrollHistory = NULL;
_scrollHistoryLength = kEZAudioPlotDefaultHistoryBufferLength;
#endif
self.shouldFill = NO;
self.shouldMirror = NO;
}
#pragma mark - Setters
@@ -199,15 +208,26 @@
[self.glViewController updateBuffer:buffer
withBufferSize:bufferSize];
#elif TARGET_OS_MAC
if( _copiedBuffer == NULL ){
_copiedBuffer = (float*)malloc(bufferSize*sizeof(float));
}
_copiedBufferSize = bufferSize;
// Copy the buffer
memcpy(_copiedBuffer,
buffer,
bufferSize*sizeof(float));
// Draw based on plot type
switch(_plotType) {
case EZPlotTypeBuffer:
[self _updateBufferPlotBufferWithAudioReceived:buffer
withBufferSize:bufferSize];
[self _updateBufferPlotBufferWithAudioReceived:_copiedBuffer
withBufferSize:_copiedBufferSize];
break;
case EZPlotTypeRolling:
[self _updateRollingPlotBufferWithAudioReceived:buffer
withBufferSize:bufferSize];
[self _updateRollingPlotBufferWithAudioReceived:_copiedBuffer
withBufferSize:_copiedBufferSize];
break;
default:
break;
@@ -246,12 +266,12 @@
NSOpenGLPixelFormatAttribute attrs[] =
{
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAMultisample,
NSOpenGLPFASampleBuffers, 1,
NSOpenGLPFASamples, 4,
NSOpenGLPFAMultisample,
NSOpenGLPFASampleBuffers, 1,
NSOpenGLPFASamples, 4,
NSOpenGLPFADepthSize, 24,
NSOpenGLPFAOpenGLProfile,
NSOpenGLProfileVersion3_2Core, 0,
NSOpenGLProfileVersion3_2Core, 0
};
NSOpenGLPixelFormat *pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
@@ -261,10 +281,10 @@
NSLog(@"No OpenGL pixel format");
}
NSOpenGLContext* context = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:nil];
NSOpenGLContext* context = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:nil];
// Debug only
CGLEnable([context CGLContextObj], kCGLCECrashOnRemovedFunctions);
CGLEnable([context CGLContextObj], kCGLCECrashOnRemovedFunctions);
self.pixelFormat = pf;
self.openGLContext = context;
@@ -272,7 +292,7 @@
-(void)_setupView {
self.backgroundColor = [NSColor colorWithCalibratedRed: 0.796 green: 0.749 blue: 0.663 alpha: 1];
self.color = [NSColor colorWithCalibratedRed: 0.481 green: 0.548 blue: 0.637 alpha: 1];
self.color = [NSColor colorWithCalibratedRed: 0.481 green: 0.548 blue: 0.637 alpha: 1];
}
#pragma mark - Prepare
@@ -280,7 +300,7 @@
[super prepareOpenGL];
GLint swapInt = 1;
[self.openGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
[self.openGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
////////////////////////////////////////////////////////////////////////////
// Setup VABs and VBOs //
@@ -302,13 +322,17 @@
glBindBuffer(GL_ARRAY_BUFFER,_rollingPlotVBO);
}
// Enable anti-aliasing
glEnable(GL_MULTISAMPLE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClearColor(0, 0, 0, 0);
self.layer = nil;
// Set the background color
[self _refreshWithBackgroundColor:self.backgroundColor];
[self _refreshWithColor:self.color];
// Enable anti-aliasing
glEnable(GL_MULTISAMPLE);
// Setup the display link (rendering loop)
[self _setupDisplayLink];
@@ -429,35 +453,35 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink,
// If starting with a VBO of half of our max size make sure we initialize it to anticipate
// a filled graph (which needs 2 * bufferSize) to allocate its resources properly
if( !_hasRollingPlotData && _drawingType == EZAudioPlotGLDrawTypeLineStrip ){
EZAudioPlotGLPoint maxGraph[2*kEZAudioPlotHistoryBufferSize];
if( !_hasRollingPlotData ){
EZAudioPlotGLPoint maxGraph[2*kEZAudioPlotMaxHistoryBufferLength];
glBufferData(GL_ARRAY_BUFFER, sizeof(maxGraph), maxGraph, GL_STREAM_DRAW );
_hasRollingPlotData = YES;
}
// Setup the plot
_rollingPlotGraphSize = [EZAudioPlotGL graphSizeForDrawingType:_drawingType
withBufferSize:kEZAudioPlotHistoryBufferSize];
withBufferSize:_scrollHistoryLength];
// Fill the graph with data
EZAudioPlotGLPoint graph[_rollingPlotGraphSize];
// Update the history buffer
_historyBuffer[_historyIndex] = [EZAudio RMS:buffer length:bufferSize];
if( _historyIndex == kEZAudioPlotHistoryBufferSize - 1 ){
_historyIndex = 0;
for(int i = 0; i < kEZAudioPlotHistoryBufferSize; i++) _historyBuffer[i] = 0.0f;
}
else {
_historyIndex++;
}
// Update the scroll history datasource
[EZAudio updateScrollHistory:&_scrollHistory
withLength:_scrollHistoryLength
atIndex:&_scrollHistoryIndex
withBuffer:buffer
withBufferSize:bufferSize
isResolutionChanging:&_changingHistorySize];
// Fill in graph data
[EZAudioPlotGL fillGraph:graph
withGraphSize:_rollingPlotGraphSize
forDrawingType:_drawingType
withBuffer:_historyBuffer
withBufferSize:kEZAudioPlotHistoryBufferSize
withBuffer:_scrollHistory
withBufferSize:_scrollHistoryLength
withGain:self.gain];
// Update the drawing
@@ -484,18 +508,20 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink,
CGLLockContext([[self openGLContext] CGLContextObj]);
// Draw frame
glClear(GL_COLOR_BUFFER_BIT);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
// Plot either a buffer plot or a rolling plot
switch(_plotType) {
case EZPlotTypeBuffer:
[self _drawBufferPlot];
break;
case EZPlotTypeRolling:
[self _drawRollingPlot];
break;
default:
break;
if( _hasBufferPlotData || _hasRollingPlotData ){
// Plot either a buffer plot or a rolling plot
switch(_plotType) {
case EZPlotTypeBuffer:
[self _drawBufferPlot];
break;
case EZPlotTypeRolling:
[self _drawRollingPlot];
break;
default:
break;
}
}
// Flush and unlock
@@ -543,8 +569,8 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink,
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeXRotation(0);
// Enable the vertex data
glEnableVertexAttribArray(GLKVertexAttribPosition);
// Define the vertex data size & layout
glEnableVertexAttribArray(GLKVertexAttribPosition);
// Define the vertex data size & layout
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(EZAudioPlotGLPoint), NULL);
// Draw the triangle
glDrawArrays(_drawingType, 0,_rollingPlotGraphSize);
@@ -623,9 +649,56 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink,
// when it encounters something that has been release
CVDisplayLinkStop(_displayLink);
CVDisplayLinkRelease(_displayLink);
if( _copiedBuffer != NULL ){
free( _copiedBuffer );
}
}
#endif
#pragma mark - Adjust Resolution
-(int)setRollingHistoryLength:(int)historyLength {
#if TARGET_OS_IPHONE
int result = [self.glViewController setRollingHistoryLength:historyLength];
return result;
#elif TARGET_OS_MAC
historyLength = MIN(historyLength,kEZAudioPlotMaxHistoryBufferLength);
size_t floatByteSize = sizeof(float);
_changingHistorySize = YES;
if( _scrollHistoryLength != historyLength ){
_scrollHistoryLength = historyLength;
}
_scrollHistory = realloc(_scrollHistory,_scrollHistoryLength*floatByteSize);
if( _scrollHistoryIndex < _scrollHistoryLength ){
memset(&_scrollHistory[_scrollHistoryIndex],
0,
(_scrollHistoryLength-_scrollHistoryIndex)*floatByteSize);
}
else {
_scrollHistoryIndex = _scrollHistoryLength;
}
_changingHistorySize = NO;
return historyLength;
#endif
return kEZAudioPlotDefaultHistoryBufferLength;
}
-(int)rollingHistoryLength {
#if TARGET_OS_IPHONE
return self.glViewController.rollingHistoryLength;
#elif TARGET_OS_MAC
return _scrollHistoryLength;
#endif
}
#pragma mark - Clearing
-(void)clear {
#if TARGET_OS_IPHONE
[self.glViewController clear];
#elif TARGET_OS_MAC
#endif
}
#pragma mark - Graph Methods
+(void)fillGraph:(EZAudioPlotGLPoint*)graph
withGraphSize:(UInt32)graphSize
@@ -648,8 +721,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink,
else if( drawingType == EZAudioPlotGLDrawTypeTriangleStrip ) {
// graph size = 2 * buffer size to draw triangles and fill regions properly
for(int i = 0; i < graphSize; i+=2){
int bufferIndex = (int)[EZAudio
MAP:i
int bufferIndex = (int)[EZAudio MAP:i
leftMin:0
leftMax:graphSize
rightMin:0
+28
View File
@@ -80,6 +80,34 @@
*/
@property (nonatomic,assign,setter=setShouldMirror:) BOOL shouldMirror;
#pragma mark - Adjust Resolution
///-----------------------------------------------------------
/// @name Adjusting The Resolution
///-----------------------------------------------------------
/**
Sets the length of the rolling history display. Can grow or shrink the display up to the maximum size specified by the kEZAudioPlotMaxHistoryBufferLength macro. Will return the actual set value, which will be either the given value if smaller than the kEZAudioPlotMaxHistoryBufferLength or kEZAudioPlotMaxHistoryBufferLength if a larger value is attempted to be set.
@param historyLength The new length of the rolling history buffer.
@return The new value equal to the historyLength or the kEZAudioPlotMaxHistoryBufferLength.
*/
-(int)setRollingHistoryLength:(int)historyLength;
/**
Provides the length of the rolling history buffer
* @return An int representing the length of the rolling history buffer
*/
-(int)rollingHistoryLength;
#pragma mark - Clearing
///-----------------------------------------------------------
/// @name Clearing The Plot
///-----------------------------------------------------------
/**
Clears all data from the audio plot (includes both EZPlotTypeBuffer and EZPlotTypeRolling)
*/
-(void)clear;
#pragma mark - Get Samples
///-----------------------------------------------------------
/// @name Updating The Plot
+161 -33
View File
@@ -42,9 +42,12 @@
UInt32 _bufferPlotGraphSize;
UInt32 _rollingPlotGraphSize;
// History buffer
UInt32 _historyIndex;
float _historyBuffer[kEZAudioPlotHistoryBufferSize];
// Rolling History
BOOL _setMaxLength;
float *_scrollHistory;
int _scrollHistoryIndex;
UInt32 _scrollHistoryLength;
BOOL _changingHistorySize;
}
@end
@@ -88,6 +91,8 @@
self.baseEffect = [[GLKBaseEffect alloc] init];
self.baseEffect.useConstantColor = GL_TRUE;
self.preferredFramesPerSecond = 60;
_scrollHistory = NULL;
_scrollHistoryLength = kEZAudioPlotDefaultHistoryBufferLength;
}
#pragma mark - View Did Load
@@ -95,7 +100,15 @@
[super viewDidLoad];
// Setup the context
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if( ![EAGLContext currentContext] )
{
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
}
else
{
self.context = [EAGLContext currentContext];
}
if (!self.context) {
NSLog(@"Failed to create ES context");
}
@@ -121,6 +134,85 @@
}
#pragma mark - Adjust Resolution
-(int)setRollingHistoryLength:(int)historyLength {
_changingHistorySize = YES;
historyLength = MIN(historyLength,kEZAudioPlotMaxHistoryBufferLength);
size_t floatByteSize = sizeof(float);
if( _scrollHistoryLength != historyLength ){
_scrollHistoryLength = historyLength;
}
_scrollHistory = realloc(_scrollHistory,_scrollHistoryLength*floatByteSize);
if( _scrollHistoryIndex < _scrollHistoryLength ){
memset(&_scrollHistory[_scrollHistoryIndex],
0,
(_scrollHistoryLength-_scrollHistoryIndex)*floatByteSize);
}
else {
_scrollHistoryIndex = _scrollHistoryLength;
}
[self _updateRollingPlotDisplay];
_changingHistorySize = NO;
return historyLength;
}
-(int)rollingHistoryLength {
return _scrollHistoryLength;
}
#pragma mark - Clearing
-(void)clear
{
_scrollHistoryIndex = 0;
[self _clearBufferPlot];
[self _clearRollingPlot];
}
-(void)_clearBufferPlot
{
if( _hasBufferPlotData )
{
float empty[_bufferPlotGraphSize];
memset( empty, 0.0f, sizeof(float) );
[self _updateBufferPlotBufferWithAudioReceived:empty
withBufferSize:_bufferPlotGraphSize];
}
}
-(void)_clearRollingPlot
{
if( _hasRollingPlotData )
{
float empty[_rollingPlotGraphSize];
EZAudioPlotGLPoint graph[_rollingPlotGraphSize];
// Figure out better way to do this
for(int i = 0; i < _rollingPlotGraphSize; i++ )
{
empty[i] = 0.0f;
}
for(int i = 0; i < _scrollHistoryLength; i++)
{
_scrollHistory[i] = 0.0f;
}
// Update the scroll history datasource
[EZAudio updateScrollHistory:&_scrollHistory
withLength:_scrollHistoryLength
atIndex:&_scrollHistoryIndex
withBuffer:empty
withBufferSize:_rollingPlotGraphSize
isResolutionChanging:&_changingHistorySize];
// Fill in graph data
[EZAudioPlotGL fillGraph:graph
withGraphSize:_rollingPlotGraphSize
forDrawingType:_drawingType
withBuffer:_scrollHistory
withBufferSize:_scrollHistoryLength
withGain:self.gain];
// Update the drawing
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(graph), graph);
}
}
#pragma mark - Get Samples
-(void)updateBuffer:(float *)buffer
withBufferSize:(UInt32)bufferSize {
@@ -128,6 +220,9 @@
// Make sure the update render loop is active
if( self.paused ) self.paused = NO;
// Make sure we are updating the buffers on the correct gl context.
EAGLContext.currentContext = self.context;
// Draw based on plot type
switch(_plotType) {
case EZPlotTypeBuffer:
@@ -181,6 +276,8 @@
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(graph), graph);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
-(void)_updateRollingPlotBufferWithAudioReceived:(float*)buffer
@@ -188,38 +285,38 @@
glBindBuffer(GL_ARRAY_BUFFER, _rollingPlotVBO);
// If starting with a VBO of half of our max size make sure we initialize it to anticipate
// a filled graph (which needs 2 * bufferSize) to allocate its resources properly
if( !_hasRollingPlotData && _drawingType == EZAudioPlotGLDrawTypeLineStrip ){
EZAudioPlotGLPoint maxGraph[2*kEZAudioPlotHistoryBufferSize];
glBufferData(GL_ARRAY_BUFFER, sizeof(maxGraph), maxGraph, GL_STREAM_DRAW );
if( !_hasRollingPlotData ){
EZAudioPlotGLPoint maxGraph[2*kEZAudioPlotMaxHistoryBufferLength];
glBufferData( GL_ARRAY_BUFFER, sizeof(maxGraph), maxGraph, GL_STREAM_DRAW );
_hasRollingPlotData = YES;
}
// Setup the plot
_rollingPlotGraphSize = [EZAudioPlotGL graphSizeForDrawingType:_drawingType
withBufferSize:kEZAudioPlotHistoryBufferSize];
withBufferSize:_scrollHistoryLength];
// Fill the graph with data
EZAudioPlotGLPoint graph[_rollingPlotGraphSize];
// Update the history buffer
_historyBuffer[_historyIndex] = [EZAudio RMS:buffer length:bufferSize];
if( _historyIndex == kEZAudioPlotHistoryBufferSize - 1 ){
_historyIndex = 0;
for(int i = 0; i < kEZAudioPlotHistoryBufferSize; i++) _historyBuffer[i] = 0.0f;
}
else {
_historyIndex++;
}
// Update the scroll history datasource
[EZAudio updateScrollHistory:&_scrollHistory
withLength:_scrollHistoryLength
atIndex:&_scrollHistoryIndex
withBuffer:buffer
withBufferSize:bufferSize
isResolutionChanging:&_changingHistorySize];
// Fill in graph data
[EZAudioPlotGL fillGraph:graph
withGraphSize:_rollingPlotGraphSize
forDrawingType:_drawingType
withBuffer:_historyBuffer
withBufferSize:kEZAudioPlotHistoryBufferSize
withGain:self.gain];
withGraphSize:_rollingPlotGraphSize
forDrawingType:_drawingType
withBuffer:_scrollHistory
withBufferSize:_scrollHistoryLength
withGain:self.gain];
// Update the drawing
if( !_hasRollingPlotData ){
@@ -230,17 +327,43 @@
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(graph), graph);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
-(void)_updateRollingPlotDisplay {
// Setup the plot
_rollingPlotGraphSize = [EZAudioPlotGL graphSizeForDrawingType:_drawingType
withBufferSize:_scrollHistoryLength];
// Fill the graph with data
EZAudioPlotGLPoint graph[_rollingPlotGraphSize];
// Fill in graph data
[EZAudioPlotGL fillGraph:graph
withGraphSize:_rollingPlotGraphSize
forDrawingType:_drawingType
withBuffer:_scrollHistory
withBufferSize:_scrollHistoryLength
withGain:self.gain];
// Update the drawing
if( _hasRollingPlotData ){
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(graph), graph);
}
}
#pragma mark - Drawing
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
EAGLContext.currentContext = self.context;
// Clear the context
glClear(GL_COLOR_BUFFER_BIT);
if( _hasBufferPlotData || _hasRollingPlotData ){
// Prepare the effect for drawing
[self.baseEffect prepareToDraw];
// Clear the context
glClear(GL_COLOR_BUFFER_BIT);
// Plot either a buffer plot or a rolling plot
switch(_plotType) {
case EZPlotTypeBuffer:
@@ -267,20 +390,23 @@
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(EZAudioPlotGLPoint), NULL);
// Normal plot
glPushMatrix();
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeXRotation(0);
glDrawArrays(_drawingType, 0, _bufferPlotGraphSize);
glPopMatrix();
if( self.shouldMirror ){
// Mirrored plot
[self.baseEffect prepareToDraw];
glPushMatrix();
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeXRotation(M_PI);
glDrawArrays(_drawingType, 0, _bufferPlotGraphSize);
glPopMatrix();
}
glBindBuffer(GL_ARRAY_BUFFER,0);
}
}
@@ -293,20 +419,22 @@
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(EZAudioPlotGLPoint), NULL);
// Normal plot
glPushMatrix();
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeXRotation(0);
glDrawArrays(_drawingType, 0, _rollingPlotGraphSize);
glPopMatrix();
if( self.shouldMirror ){
// Mirrored plot
[self.baseEffect prepareToDraw];
glPushMatrix();
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeXRotation(3.14159265359);
glDrawArrays(_drawingType, 0, _rollingPlotGraphSize);
glPopMatrix();
}
glBindBuffer(GL_ARRAY_BUFFER,0);
}
}
+35
View File
@@ -0,0 +1,35 @@
//
// EZAudioWaveformData.h
// EZAudioPlayFileExample
//
// Created by Syed Haris Ali on 2/14/15.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
//
#import <Foundation/Foundation.h>
//------------------------------------------------------------------------------
#pragma mark - EZAudioWaveformData
//------------------------------------------------------------------------------
@interface EZAudioWaveformData : NSObject
//------------------------------------------------------------------------------
+ (instancetype) dataWithNumberOfChannels:(int)numberOfChannels
buffers:(float **)buffers
bufferSize:(UInt32)bufferSize;
//------------------------------------------------------------------------------
@property (nonatomic, assign, readonly) int numberOfChannels;
@property (nonatomic, assign, readonly) float **buffers;
@property (nonatomic, assign, readonly) UInt32 bufferSize;
//------------------------------------------------------------------------------
- (float *) bufferForChannel:(int)channel;
//------------------------------------------------------------------------------
@end
+74
View File
@@ -0,0 +1,74 @@
//
// EZAudioWaveformData.m
// EZAudioPlayFileExample
//
// Created by Syed Haris Ali on 2/14/15.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
//
#import "EZAudioWaveformData.h"
//------------------------------------------------------------------------------
#pragma mark - EZAudioWaveformData
//------------------------------------------------------------------------------
@interface EZAudioWaveformData ()
@property (nonatomic, assign, readwrite) int numberOfChannels;
@property (nonatomic, assign, readwrite) float **buffers;
@property (nonatomic, assign, readwrite) UInt32 bufferSize;
@end
//------------------------------------------------------------------------------
@implementation EZAudioWaveformData
//------------------------------------------------------------------------------
- (void)dealloc
{
for (int i = 0; i < self.numberOfChannels; i++)
{
free(self.buffers[i]);
}
free(self.buffers);
}
//------------------------------------------------------------------------------
+ (instancetype)dataWithNumberOfChannels:(int)numberOfChannels
buffers:(float **)buffers
bufferSize:(UInt32)bufferSize
{
id waveformData = [[self alloc] init];
size_t size = sizeof(float *) * numberOfChannels;
float **buffersCopy = (float **)malloc(size);
for (int i = 0; i < numberOfChannels; i++)
{
size = sizeof(float) * bufferSize;
buffersCopy[i] = (float *)malloc(size);
memcpy(buffersCopy[i], buffers[i], size);
}
((EZAudioWaveformData *)waveformData).buffers = buffersCopy;
((EZAudioWaveformData *)waveformData).bufferSize = bufferSize;
((EZAudioWaveformData *)waveformData).numberOfChannels = numberOfChannels;
return waveformData;
}
//------------------------------------------------------------------------------
- (float *)bufferForChannel:(int)channel
{
float *buffer = NULL;
if (channel < self.numberOfChannels)
{
buffer = self.buffers[channel];
}
return buffer;
}
//------------------------------------------------------------------------------
@end
+49 -9
View File
@@ -25,7 +25,6 @@
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
#import "AEFloatConverter.h"
#import "TargetConditionals.h"
@class EZAudio;
@@ -119,17 +118,35 @@
*/
-(EZMicrophone*)initWithMicrophoneDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate;
/**
Creates an instance of the EZMicrophone with a custom AudioStreamBasicDescription and provides the caller to specify a delegate to respond to the audioReceived callback. This will not start fetching the audio until startFetchingAudio has been called. Use initWithMicrophoneDelegate:startsImmediately: to instantiate this class and immediately start fetching audio data.
@param microphoneDelegate A EZMicrophoneDelegate delegate that will receive the audioReceived callback.
@param audioStreamBasicDescription A custom AudioStreamBasicFormat for the microphone input.
@return An instance of the EZMicrophone class. This should be strongly retained.
*/
-(EZMicrophone*)initWithMicrophoneDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription;
/**
Creates an instance of the EZMicrophone with a delegate to respond to the audioReceived callback and allows the caller to specify whether they'd immediately like to start fetching the audio data.
@param microphoneDelegate A EZMicrophoneDelegate delegate that will receive the audioReceived callback.
@param startsImmediately A boolean indicating whether to start fetching the data immediately. IF YES, the delegate's audioReceived callback will immediately start getting called.
@return An instance of the EZMicrophone class. This should be strongly retained.
*/
-(EZMicrophone*)initWithMicrophoneDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate
startsImmediately:(BOOL)startsImmediately;
/**
Creates an instance of the EZMicrophone with a custom AudioStreamBasicDescription and provides the caller with a delegate to respond to the audioReceived callback and allows the caller to specify whether they'd immediately like to start fetching the audio data.
@param microphoneDelegate A EZMicrophoneDelegate delegate that will receive the audioReceived callback.
@param audioStreamBasicDescription A custom AudioStreamBasicFormat for the microphone input.
@param startsImmediately A boolean indicating whether to start fetching the data immediately. IF YES, the delegate's audioReceived callback will immediately start getting called.
@return An instance of the EZMicrophone class. This should be strongly retained.
*/
-(EZMicrophone*)initWithMicrophoneDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription
startsImmediately:(BOOL)startsImmediately;
#pragma mark - Class Initializers
///-----------------------------------------------------------
/// @name Class Initializers
@@ -137,20 +154,19 @@
/**
Creates an instance of the EZMicrophone with a delegate to respond to the audioReceived callback. This will not start fetching the audio until startFetchingAudio has been called. Use microphoneWithDelegate:startsImmediately: to instantiate this class and immediately start fetching audio data.
@param microphoneDelegate A EZMicrophoneDelegate delegate that will receive the audioReceived callback.
@return An instance of the EZMicrophone class. This should be declared as a strong property!
*/
+(EZMicrophone*)microphoneWithDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate;
/**
Creates an instance of the EZMicrophone with a delegate to respond to the audioReceived callback and allows the caller to specify whether they'd immediately like to start fetching the audio data.
Creates an instance of the EZMicrophone with a delegate to respond to the audioReceived callback. This will not start fetching the audio until startFetchingAudio has been called. Use microphoneWithDelegate:startsImmediately: to instantiate this class and immediately start fetching audio data.
@param microphoneDelegate A EZMicrophoneDelegate delegate that will receive the audioReceived callback.
@param startsImmediately A boolean indicating whether to start fetching the data immediately. IF YES, the delegate's audioReceived callback will immediately start getting called.
@return An instance of the EZMicrophone class. This should be strongly retained.
@param audioStreamBasicDescription A custom AudioStreamBasicFormat for the microphone input.
@return An instance of the EZMicrophone class. This should be declared as a strong property!
*/
+(EZMicrophone*)microphoneWithDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription;
/**
Creates an instance of the EZMicrophone with a delegate to respond to the audioReceived callback and allows the caller to specify whether they'd immediately like to start fetching the audio data.
@@ -162,6 +178,18 @@
+(EZMicrophone*)microphoneWithDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate
startsImmediately:(BOOL)startsImmediately;
/**
Creates an instance of the EZMicrophone with a delegate to respond to the audioReceived callback and allows the caller to specify whether they'd immediately like to start fetching the audio data.
@param microphoneDelegate A EZMicrophoneDelegate delegate that will receive the audioReceived callback.
@param audioStreamBasicDescription A custom AudioStreamBasicFormat for the microphone input.
@param startsImmediately A boolean indicating whether to start fetching the data immediately. IF YES, the delegate's audioReceived callback will immediately start getting called.
@return An instance of the EZMicrophone class. This should be strongly retained.
*/
+(EZMicrophone*)microphoneWithDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription
startsImmediately:(BOOL)startsImmediately;
#pragma mark - Singleton
///-----------------------------------------------------------
/// @name Shared Instance
@@ -199,4 +227,16 @@
*/
-(AudioStreamBasicDescription)audioStreamBasicDescription;
#pragma mark - Setters
///-----------------------------------------------------------
/// @name Customizing The Microphone Input Format
///-----------------------------------------------------------
/**
Sets the AudioStreamBasicDescription on the microphone input.
@warning Do not set this while fetching audio (startFetchingAudio)
@param asbd The new AudioStreamBasicDescription to use in place of the current audio format description.
*/
-(void)setAudioStreamBasicDescription:(AudioStreamBasicDescription)asbd;
@end
+362 -316
View File
@@ -32,27 +32,44 @@
#import "EZAudio.h"
/// Buses
static const AudioUnitScope kEZAudioMicrophoneInputBus = 1;
static const AudioUnitScope kEZAudioMicrophoneOutputBus = 0;
/// Flags
#if TARGET_OS_IPHONE
static const UInt32 kEZAudioMicrophoneDisableFlag = 1;
#elif TARGET_OS_MAC
static const UInt32 kEZAudioMicrophoneDisableFlag = 0;
#endif
static const UInt32 kEZAudioMicrophoneEnableFlag = 1;
@interface EZMicrophone (){
/// Internal
BOOL _customASBD;
BOOL _isConfigured;
BOOL _isFetching;
/// Stream Description
AEFloatConverter *converter;
AudioStreamBasicDescription streamFormat;
/// Audio Graph and Input/Output Units
AUGraph graph;
AudioUnit microphoneInput;
AudioUnit outputUnit;
/// Audio Buffers
float **floatBuffers;
AudioBufferList *microphoneInputBuffer;
/// Sample Time Offsets
Float64 firstInputSampleTime;
Float64 inToOutSampleTimeOffset;
/// Device Parameters
Float64 _deviceSampleRate;
Float32 _deviceBufferDuration;
UInt32 _deviceBufferFrameSize;
#if TARGET_OS_IPHONE
#elif TARGET_OS_MAC
Float64 inputScopeSampleRate;
#endif
}
@end
@@ -67,51 +84,38 @@ static OSStatus inputCallback(void *inRefCon,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData ) {
EZMicrophone *microphone = (__bridge EZMicrophone*)inRefCon;
OSStatus result = noErr;
#if TARGET_OS_IPHONE
OSStatus result = noErr;
// Render audio into buffer
result = AudioUnitRender(microphone->microphoneInput,
ioActionFlags,
inTimeStamp,
1,
inNumberFrames,
microphone->microphoneInputBuffer);
#elif TARGET_OS_MAC
// Retrive the captured samples from the input
result = AudioUnitRender(microphone->microphoneInput,
ioActionFlags,
inTimeStamp,
inBusNumber,
inNumberFrames,
microphone->microphoneInputBuffer);
#endif
if( !result ){
// Notify delegate (OF-style)
@autoreleasepool {
// Audio Received (float array)
if( microphone.microphoneDelegate ){
// THIS IS NOT OCCURING ON THE MAIN THREAD
if( [microphone.microphoneDelegate respondsToSelector:@selector(microphone:hasAudioReceived:withBufferSize:withNumberOfChannels:)] ){
AEFloatConverterToFloat(microphone->converter,
microphone->microphoneInputBuffer,
microphone->floatBuffers,
inNumberFrames);
}
// ----- Notify delegate (OF-style) -----
// Audio Received (float array)
if( microphone.microphoneDelegate ){
// THIS IS NOT OCCURING ON THE MAIN THREAD
if( [microphone.microphoneDelegate respondsToSelector:@selector(microphone:hasAudioReceived:withBufferSize:withNumberOfChannels:)] ){
// AEFloatConverterToFloat(microphone->converter,
// microphone->microphoneInputBuffer,
// microphone->floatBuffers,
// inNumberFrames);
[microphone.microphoneDelegate microphone:microphone
hasAudioReceived:microphone->floatBuffers
withBufferSize:inNumberFrames
withNumberOfChannels:microphone->streamFormat.mChannelsPerFrame];
}
// Audio Received (buffer list)
if( microphone.microphoneDelegate ){
if( [microphone.microphoneDelegate respondsToSelector:@selector(microphone:hasBufferList:withBufferSize:withNumberOfChannels:)] ){
[microphone.microphoneDelegate microphone:microphone
hasBufferList:microphone->microphoneInputBuffer
withBufferSize:inNumberFrames
withNumberOfChannels:microphone->streamFormat.mChannelsPerFrame];
}
}
// Audio Received (buffer list)
if( microphone.microphoneDelegate ){
if( [microphone.microphoneDelegate respondsToSelector:@selector(microphone:hasBufferList:withBufferSize:withNumberOfChannels:)] ){
[microphone.microphoneDelegate microphone:microphone
hasBufferList:microphone->microphoneInputBuffer
withBufferSize:inNumberFrames
withNumberOfChannels:microphone->streamFormat.mChannelsPerFrame];
}
}
}
@@ -127,14 +131,41 @@ static OSStatus inputCallback(void *inRefCon,
// We're not fetching anything yet
_isConfigured = NO;
_isFetching = NO;
if( !_isConfigured ){
// Create the input audio graph
[self _createInputUnit];
// We're configured meow
_isConfigured = YES;
}
}
return self;
}
-(EZMicrophone *)initWithMicrophoneDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate {
self = [self init];
self = [super init];
if(self){
self.microphoneDelegate = microphoneDelegate;
// Clear the float buffer
floatBuffers = NULL;
// We're not fetching anything yet
_isConfigured = NO;
_isFetching = NO;
if( !_isConfigured ){
// Create the input audio graph
[self _createInputUnit];
// We're configured meow
_isConfigured = YES;
}
}
return self;
}
-(EZMicrophone *)initWithMicrophoneDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription {
self = [self initWithMicrophoneDelegate:microphoneDelegate];
if(self){
_customASBD = YES;
streamFormat = audioStreamBasicDescription;
}
return self;
}
@@ -143,9 +174,17 @@ static OSStatus inputCallback(void *inRefCon,
startsImmediately:(BOOL)startsImmediately {
self = [self initWithMicrophoneDelegate:microphoneDelegate];
if(self){
if(startsImmediately){
[self startFetchingAudio];
}
startsImmediately ? [self startFetchingAudio] : -1;
}
return self;
}
-(EZMicrophone *)initWithMicrophoneDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription
startsImmediately:(BOOL)startsImmediately {
self = [self initWithMicrophoneDelegate:microphoneDelegate withAudioStreamBasicDescription:audioStreamBasicDescription];
if(self){
startsImmediately ? [self startFetchingAudio] : -1;
}
return self;
}
@@ -155,12 +194,26 @@ static OSStatus inputCallback(void *inRefCon,
return [[EZMicrophone alloc] initWithMicrophoneDelegate:microphoneDelegate];
}
+(EZMicrophone *)microphoneWithDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription {
return [[EZMicrophone alloc] initWithMicrophoneDelegate:microphoneDelegate
withAudioStreamBasicDescription:audioStreamBasicDescription];
}
+(EZMicrophone *)microphoneWithDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate
startsImmediately:(BOOL)startsImmediately {
return [[EZMicrophone alloc] initWithMicrophoneDelegate:microphoneDelegate
startsImmediately:startsImmediately];
}
+(EZMicrophone *)microphoneWithDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription
startsImmediately:(BOOL)startsImmediately {
return [[EZMicrophone alloc] initWithMicrophoneDelegate:microphoneDelegate
withAudioStreamBasicDescription:audioStreamBasicDescription
startsImmediately:startsImmediately];
}
#pragma mark - Singleton
+(EZMicrophone*)sharedMicrophone {
static EZMicrophone *_sharedMicrophone = nil;
@@ -174,12 +227,6 @@ static OSStatus inputCallback(void *inRefCon,
#pragma mark - Events
-(void)startFetchingAudio {
if( !_isFetching ){
if( !_isConfigured ){
// Create the input audio graph
[self _createInputUnit];
// We're configured meow
_isConfigured = YES;
}
// Start fetching input
[EZAudio checkResult:AudioOutputUnitStart(self->microphoneInput)
operation:"Microphone failed to start fetching audio"];
@@ -216,218 +263,142 @@ static OSStatus inputCallback(void *inRefCon,
}
}
#if TARGET_OS_IPHONE
-(void)setAudioStreamBasicDescription:(AudioStreamBasicDescription)asbd {
if( self.microphoneOn ){
NSAssert(self.microphoneOn,@"Cannot set the AudioStreamBasicDescription while microphone is fetching audio");
}
else {
_customASBD = YES;
streamFormat = asbd;
[self _configureStreamFormatWithSampleRate:_deviceSampleRate];
}
}
#pragma mark - Configure The Input Unit
-(void)_createInputUnit {
// Create localized copy of self
EZMicrophone *microphone = (EZMicrophone*)self;
// Get component description for input
AudioComponentDescription inputComponentDescription = [self _getInputAudioComponentDescription];
AudioComponentDescription inputComponentDescription = {
.componentType = kAudioUnitType_Output,
.componentSubType = kAudioUnitSubType_RemoteIO,
.componentManufacturer = kAudioUnitManufacturer_Apple,
.componentFlags = 0,
.componentFlagsMask = 0
};
// Try and find the component
AudioComponent inputComponent = AudioComponentFindNext( NULL , &inputComponentDescription );
if( inputComponent == NULL ){
NSLog(@"Couldn't get input component unit!");
return;
}
// Get the input component
AudioComponent inputComponent = [self _getInputComponentWithAudioComponentDescription:inputComponentDescription];
// Create a new instance of the component and store it for internal use
[EZAudio checkResult:AudioComponentInstanceNew(inputComponent,
&microphone->microphoneInput)
operation:"Couldn't open component for microphone input unit."];
[self _createNewInstanceForInputComponent:inputComponent];
// Enable I/O on microphone input unit
UInt32 disableFlag = 1; UInt32 enableFlag = 1;
AudioUnitScope outputBus = 0; AudioUnitScope inputBus = 1;
[EZAudio checkResult:AudioUnitSetProperty(microphone->microphoneInput,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
inputBus,
&enableFlag,
sizeof(enableFlag))
operation:"Couldn't enable input on the remote i/o unit"];
// Enable Input Scope
[self _enableInputScope];
// Could set the output if we wanted to play out the mic's buffer
[EZAudio checkResult:AudioUnitSetProperty(microphone->microphoneInput,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output,
outputBus,
&disableFlag,
sizeof(disableFlag))
operation:"Couldn't enable input on the remote i/o unit"];
// Disable Output Scope
[self _disableOutputScope];
// Get the hardware sample rate
Float64 hardwareSampleRate = 44100;
#if !(TARGET_IPHONE_SIMULATOR)
UInt32 propSize = sizeof(hardwareSampleRate);
[EZAudio checkResult:AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareSampleRate,
&propSize,
&hardwareSampleRate)
operation:"Could not get hardware sample rate"];
#endif
// Get the default device if we need to (OSX only, iOS uses RemoteIO)
#if TARGET_OS_IPHONE
// Do nothing (using RemoteIO)
#elif TARGET_OS_MAC
[self _configureDefaultDevice];
#endif
// Set the stream format
microphone->streamFormat.mBitsPerChannel = 8 * sizeof(AudioUnitSampleType);
microphone->streamFormat.mBytesPerFrame = sizeof(AudioUnitSampleType);
microphone->streamFormat.mBytesPerPacket = sizeof(AudioUnitSampleType);
microphone->streamFormat.mChannelsPerFrame = 2;
microphone->streamFormat.mFormatFlags = kAudioFormatFlagsCanonical | kAudioFormatFlagIsNonInterleaved;
microphone->streamFormat.mFormatID = kAudioFormatLinearPCM;
microphone->streamFormat.mFramesPerPacket = 1;
microphone->streamFormat.mSampleRate = hardwareSampleRate;
// Configure device and pull hardware specific sampling rate (default = 44.1 kHz)
_deviceSampleRate = [self _configureDeviceSampleRateWithDefault:44100.0];
// Get the buffer duration (approximate for simulator, real device will have it's preferred value set)
Float32 bufferDuration = 0.0232;
UInt32 propertySize = sizeof(bufferDuration);
#if !(TARGET_IPHONE_SIMULATOR)
[EZAudio checkResult:AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration,
sizeof(propertySize),
&bufferDuration)
operation:"Couldn't set the preferred buffer duration"];
// Get the preferred buffer size
[EZAudio checkResult:AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareIOBufferDuration,
&propertySize,
&bufferDuration)
operation:"Could not get preferred buffer size"];
#endif
// Configure device and pull hardware specific buffer duration (default = 0.0232)
_deviceBufferDuration = [self _configureDeviceBufferDurationWithDefault:0.0232];
// Configure the stream format with the hardware sample rate
[self _configureStreamFormatWithSampleRate:_deviceSampleRate];
// Notify delegate the audio stream basic description was successfully created
[self _notifyDelegateOfStreamFormat];
// Get buffer frame size
_deviceBufferFrameSize = [self _getBufferFrameSize];
// Create the audio buffer list and pre-malloc the buffers in the list
propertySize = offsetof( AudioBufferList, mBuffers[0] ) + ( sizeof( AudioBuffer ) * microphone->streamFormat.mChannelsPerFrame );
microphone->microphoneInputBuffer = (AudioBufferList*)malloc(propertySize);
[self _configureAudioBufferListWithFrameSize:_deviceBufferFrameSize];
// Get the maximum number of frames
UInt32 bufferSizeFrames;
propertySize = sizeof(UInt32);
[EZAudio checkResult:AudioUnitGetProperty(microphone->microphoneInput,
kAudioUnitProperty_MaximumFramesPerSlice,
kAudioUnitScope_Global,
outputBus,
&bufferSizeFrames,
&propertySize)
operation:"Failed to get maximum number of frames"];
microphone->microphoneInputBuffer->mNumberBuffers = microphone->streamFormat.mChannelsPerFrame;
UInt32 bufferSizeBytes = bufferSizeFrames * microphone->streamFormat.mBytesPerFrame;
for( UInt32 i = 0; i < microphone->microphoneInputBuffer->mNumberBuffers; i++ ){
microphone->microphoneInputBuffer->mBuffers[i].mNumberChannels = 1;
microphone->microphoneInputBuffer->mBuffers[i].mDataByteSize = bufferSizeBytes;
microphone->microphoneInputBuffer->mBuffers[i].mData = malloc(bufferSizeBytes);
}
// Setup the converter, but do a lazy set for the float buffer
microphone->converter = [[AEFloatConverter alloc] initWithSourceFormat:microphone->streamFormat];
microphone->floatBuffers = (float**)malloc(sizeof(float*)*microphone->streamFormat.mChannelsPerFrame);
for ( int i=0; i<microphone->streamFormat.mChannelsPerFrame; i++ ) {
microphone->floatBuffers[i] = (float*)malloc(bufferSizeBytes);
assert(microphone->floatBuffers[i]);
}
// Set the stream format for output on the microphone's input scope
[EZAudio checkResult:AudioUnitSetProperty(microphone->microphoneInput,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
outputBus,
&microphone->streamFormat,
sizeof(microphone->streamFormat))
operation:"Could not set microphone's stream format bus 0"];
// Set the stream format for the input on the microphone's output scope
[EZAudio checkResult:AudioUnitSetProperty(microphone->microphoneInput,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
inputBus,
&microphone->streamFormat,
sizeof(microphone->streamFormat))
operation:"Could not set microphone's stream format bus 1"];
// Notify delegate the audio stream basic description was successfully created
if( microphone.microphoneDelegate ){
if( [microphone.microphoneDelegate respondsToSelector:@selector(microphone:hasAudioStreamBasicDescription:) ] ){
[microphone.microphoneDelegate microphone:microphone
hasAudioStreamBasicDescription:microphone->streamFormat];
}
}
// Set the float converter's stream format
[self _configureFloatConverterWithFrameSize:_deviceBufferFrameSize];
// Setup input callback
AURenderCallbackStruct microphoneCallbackStruct;
microphoneCallbackStruct.inputProc = inputCallback;
microphoneCallbackStruct.inputProcRefCon = (__bridge void *)microphone;
[EZAudio checkResult:AudioUnitSetProperty(microphone->microphoneInput,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global,
inputBus,
&microphoneCallbackStruct,
sizeof(microphoneCallbackStruct))
operation:"Couldn't set input callback"];
[self _configureInputCallback];
// Disable buffer allocation for the recorder (optional - do this if we want to pass in our own)
[EZAudio checkResult:AudioUnitSetProperty(microphone->microphoneInput,
kAudioUnitProperty_ShouldAllocateBuffer,
kAudioUnitScope_Output,
inputBus,
&disableFlag,
sizeof(disableFlag))
operation:"Could not disable audio unit allocating its own buffers"];
// Disable buffer allocation (optional - do this if we want to pass in our own)
[self _disableCallbackBufferAllocation];
// Initialize the audio unit
[EZAudio checkResult:AudioUnitInitialize( microphone->microphoneInput )
[EZAudio checkResult:AudioUnitInitialize( microphoneInput )
operation:"Couldn't initialize the input unit"];
}
#elif TARGET_OS_MAC
-(void)_createInputUnit {
#pragma mark - Audio Component Initialization
-(AudioComponentDescription)_getInputAudioComponentDescription {
// Create localized copy of self
EZMicrophone *microphone = (EZMicrophone*)self;
// Create component description for input HAL
AudioComponentDescription inputComponentDescription = {0};
// Create an input component description for mic input
AudioComponentDescription inputComponentDescription;
inputComponentDescription.componentType = kAudioUnitType_Output;
inputComponentDescription.componentSubType = kAudioUnitSubType_HALOutput;
inputComponentDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
inputComponentDescription.componentFlags = 0;
inputComponentDescription.componentFlagsMask = 0;
#if TARGET_OS_IPHONE
inputComponentDescription.componentSubType = kAudioUnitSubType_RemoteIO;
#elif TARGET_OS_MAC
inputComponentDescription.componentSubType = kAudioUnitSubType_HALOutput;
#endif
// Return the successfully created input component description
return inputComponentDescription;
}
-(AudioComponent)_getInputComponentWithAudioComponentDescription:(AudioComponentDescription)audioComponentDescription {
// Try and find the component
AudioComponent inputComponent = AudioComponentFindNext( NULL , &inputComponentDescription );
if( inputComponent == NULL ){
NSLog(@"Couldn't get input component unit!");
return;
}
AudioComponent inputComponent = AudioComponentFindNext( NULL , &audioComponentDescription );
NSAssert(inputComponent,@"Couldn't get input component unit!");
return inputComponent;
// Create a new instance of the component and store it for internal use
[EZAudio checkResult:AudioComponentInstanceNew(inputComponent,
&microphone->microphoneInput )
}
-(void)_createNewInstanceForInputComponent:(AudioComponent)audioComponent {
[EZAudio checkResult:AudioComponentInstanceNew(audioComponent,
&microphoneInput )
operation:"Couldn't open component for microphone input unit."];
// Enable I/O on microphone input unit
UInt32 disableFlag = 0; UInt32 enableFlag = 1;
AudioUnitScope outputBus = 0; AudioUnitScope inputBus = 1;
// Input Scope
[EZAudio checkResult:AudioUnitSetProperty( microphone->microphoneInput,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
inputBus,
&enableFlag,
sizeof(enableFlag))
operation:"Couldn't enable input on I/O unit."];
// Output Scope
[EZAudio checkResult:AudioUnitSetProperty( microphone->microphoneInput,
}
#pragma mark - Input/Output Scope Initialization
-(void)_disableOutputScope {
[EZAudio checkResult:AudioUnitSetProperty(microphoneInput,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output,
outputBus,
&disableFlag,
sizeof(enableFlag))
kEZAudioMicrophoneOutputBus,
&kEZAudioMicrophoneDisableFlag,
sizeof(kEZAudioMicrophoneDisableFlag))
operation:"Couldn't disable output on I/O unit."];
}
-(void)_enableInputScope {
[EZAudio checkResult:AudioUnitSetProperty(microphoneInput,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
kEZAudioMicrophoneInputBus,
&kEZAudioMicrophoneEnableFlag,
sizeof(kEZAudioMicrophoneEnableFlag))
operation:"Couldn't enable input on I/O unit."];
}
#pragma mark - Pull Default Device (OSX)
#if TARGET_OS_IPHONE
// Not needed, using RemoteIO
#elif TARGET_OS_MAC
-(void)_configureDefaultDevice {
// Get the default audio input device (pulls an abstract type from system preferences)
AudioDeviceID defaultDevice = kAudioObjectUnknown;
UInt32 propertySize = sizeof(defaultDevice);
AudioDeviceID defaultDevice = kAudioObjectUnknown;
UInt32 propSize = sizeof(defaultDevice);
AudioObjectPropertyAddress defaultDeviceProperty;
defaultDeviceProperty.mSelector = kAudioHardwarePropertyDefaultInputDevice;
defaultDeviceProperty.mScope = kAudioObjectPropertyScopeGlobal;
@@ -436,117 +407,192 @@ static OSStatus inputCallback(void *inRefCon,
&defaultDeviceProperty,
0,
NULL,
&propertySize,
&propSize,
&defaultDevice)
operation:"Couldn't get default input device"];
// Set the default device on the microphone input unit
[EZAudio checkResult:AudioUnitSetProperty(microphone->microphoneInput,
propSize = sizeof(defaultDevice);
[EZAudio checkResult:AudioUnitSetProperty(microphoneInput,
kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global,
outputBus,
kEZAudioMicrophoneOutputBus,
&defaultDevice,
sizeof(defaultDevice))
propSize)
operation:"Couldn't set default device on I/O unit"];
// Get the stream format description from the newly created input unit and assign it to the output of the input unit
propertySize = sizeof(AudioStreamBasicDescription);
[EZAudio checkResult:AudioUnitGetProperty( microphone->microphoneInput,
AudioStreamBasicDescription inputScopeFormat;
propSize = sizeof(AudioStreamBasicDescription);
[EZAudio checkResult:AudioUnitGetProperty(microphoneInput,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
inputBus,
&microphone->streamFormat,
&propertySize)
kEZAudioMicrophoneInputBus,
&inputScopeFormat,
&propSize)
operation:"Couldn't get ASBD from input unit (1)"];
// Assign the same stream format description from the output of the input unit and pull the sample rate
AudioStreamBasicDescription deviceFormat;
[EZAudio checkResult:AudioUnitGetProperty( microphone->microphoneInput,
AudioStreamBasicDescription outputScopeFormat;
propSize = sizeof(AudioStreamBasicDescription);
[EZAudio checkResult:AudioUnitGetProperty(microphoneInput,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
inputBus,
&deviceFormat,
&propertySize)
kEZAudioMicrophoneInputBus,
&outputScopeFormat,
&propSize)
operation:"Couldn't get ASBD from input unit (2)"];
microphone->streamFormat.mBitsPerChannel = 8 * sizeof(AudioUnitSampleType);
microphone->streamFormat.mBytesPerFrame = sizeof(AudioUnitSampleType);
microphone->streamFormat.mBytesPerPacket = sizeof(AudioUnitSampleType);
microphone->streamFormat.mChannelsPerFrame = 2;
microphone->streamFormat.mFormatFlags = kAudioFormatFlagsCanonical | kAudioFormatFlagIsNonInterleaved;
microphone->streamFormat.mFormatID = kAudioFormatLinearPCM;
microphone->streamFormat.mFramesPerPacket = 1;
microphone->streamFormat.mSampleRate = deviceFormat.mSampleRate;
// Readjust property size for ASBD and set the value on the input unit
propertySize = sizeof(AudioStreamBasicDescription);
[EZAudio checkResult:AudioUnitSetProperty(microphone->microphoneInput,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
inputBus,
&microphone->streamFormat,
propertySize)
operation:"Couldn't set ASBD on input unit"];
// Notify delegate the audio stream basic description was successfully created
if( microphone.microphoneDelegate ){
if( [microphone.microphoneDelegate respondsToSelector:@selector(microphone:hasAudioStreamBasicDescription:) ] ){
[microphone.microphoneDelegate microphone:microphone
hasAudioStreamBasicDescription:microphone->streamFormat];
}
}
// Setup the audio buffers to capture the input audio
UInt32 bufferSizeFrames = 0;
propertySize = sizeof(UInt32);
[EZAudio checkResult:AudioUnitGetProperty(microphone->microphoneInput,
kAudioDevicePropertyBufferFrameSize,
kAudioUnitScope_Global,
outputBus,
&bufferSizeFrames,
&propertySize)
operation:"Could not get buffer frame size from input unit"];
UInt32 bufferSizeBytes = bufferSizeFrames * sizeof(Float32);
// Create the audio buffer list and pre-malloc the buffers in the list
propertySize = offsetof( AudioBufferList, mBuffers[0] ) + ( sizeof( AudioBuffer ) * microphone->streamFormat.mChannelsPerFrame );
microphone->microphoneInputBuffer = (AudioBufferList*)malloc(propertySize);
microphone->microphoneInputBuffer->mNumberBuffers = microphone->streamFormat.mChannelsPerFrame;
for( UInt32 i = 0; i < microphone->microphoneInputBuffer->mNumberBuffers; i++ ){
microphone->microphoneInputBuffer->mBuffers[i].mNumberChannels = 1;
microphone->microphoneInputBuffer->mBuffers[i].mDataByteSize = bufferSizeBytes;
microphone->microphoneInputBuffer->mBuffers[i].mData = malloc(bufferSizeBytes);
}
// Set the convert's stream format
microphone->converter = [[AEFloatConverter alloc] initWithSourceFormat:microphone->streamFormat];
microphone->floatBuffers = (float**)malloc(sizeof(float*)*microphone->streamFormat.mChannelsPerFrame);
assert(microphone->floatBuffers);
for ( int i=0; i<microphone->streamFormat.mChannelsPerFrame; i++ ) {
microphone->floatBuffers[i] = (float*)malloc(bufferSizeBytes);
assert(microphone->floatBuffers[i]);
}
// Setup input callback
AURenderCallbackStruct microphoneCallbackStruct;
microphoneCallbackStruct.inputProc = inputCallback;
microphoneCallbackStruct.inputProcRefCon = (__bridge void *)microphone;
[EZAudio checkResult:AudioUnitSetProperty( microphone->microphoneInput,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global,
0,
&microphoneCallbackStruct,
sizeof(microphoneCallbackStruct))
operation:"Couldn't set input callback"];
// Initialize the audio unit
[EZAudio checkResult:AudioUnitInitialize( microphone->microphoneInput )
operation:"Couldn't initialize the input unit"];
microphone->firstInputSampleTime = -1;
microphone->inToOutSampleTimeOffset = -1;
// Store the input scope's sample rate
inputScopeSampleRate = inputScopeFormat.mSampleRate;
}
#endif
#pragma mark - Pull Sample Rate
-(Float64)_configureDeviceSampleRateWithDefault:(float)defaultSampleRate {
Float64 hardwareSampleRate = defaultSampleRate;
#if TARGET_OS_IPHONE
// Use approximations for simulator and pull from real device if connected
#if !(TARGET_IPHONE_SIMULATOR)
// Sample Rate
hardwareSampleRate = [[AVAudioSession sharedInstance] sampleRate];
#endif
#elif TARGET_OS_MAC
hardwareSampleRate = inputScopeSampleRate;
#endif
return hardwareSampleRate;
}
#pragma mark - Pull Buffer Duration
-(Float32)_configureDeviceBufferDurationWithDefault:(float)defaultBufferDuration {
Float32 bufferDuration = defaultBufferDuration; // Type 1/43 by default
#if TARGET_OS_IPHONE
// Use approximations for simulator and pull from real device if connected
#if !(TARGET_IPHONE_SIMULATOR)
NSError *err;
[[AVAudioSession sharedInstance] setPreferredIOBufferDuration:bufferDuration error:&err];
if (err) {
NSLog(@"Error setting preferredIOBufferDuration for audio session: %@", err.localizedDescription);
}
// Buffer Size
bufferDuration = [[AVAudioSession sharedInstance] IOBufferDuration];
#endif
#elif TARGET_OS_MAC
#endif
return bufferDuration;
}
#pragma mark - Pull Buffer Frame Size
-(UInt32)_getBufferFrameSize {
UInt32 bufferFrameSize;
UInt32 propSize = sizeof(bufferFrameSize);
[EZAudio checkResult:AudioUnitGetProperty(microphoneInput,
#if TARGET_OS_IPHONE
kAudioUnitProperty_MaximumFramesPerSlice,
#elif TARGET_OS_MAC
kAudioDevicePropertyBufferFrameSize,
#endif
kAudioUnitScope_Global,
kEZAudioMicrophoneOutputBus,
&bufferFrameSize,
&propSize)
operation:"Failed to get buffer frame size"];
return bufferFrameSize;
}
#pragma mark - Stream Format Initialization
-(void)_configureStreamFormatWithSampleRate:(Float64)sampleRate {
// Set the stream format
if( !_customASBD ){
streamFormat = [EZAudio stereoCanonicalNonInterleavedFormatWithSampleRate:sampleRate];
}
else {
streamFormat.mSampleRate = sampleRate;
}
UInt32 propSize = sizeof(streamFormat);
// Set the stream format for output on the microphone's input scope
[EZAudio checkResult:AudioUnitSetProperty(microphoneInput,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
kEZAudioMicrophoneOutputBus,
&streamFormat,
propSize)
operation:"Could not set microphone's stream format bus 0"];
// Set the stream format for the input on the microphone's output scope
[EZAudio checkResult:AudioUnitSetProperty(microphoneInput,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
kEZAudioMicrophoneInputBus,
&streamFormat,
propSize)
operation:"Could not set microphone's stream format bus 1"];
}
-(void)_notifyDelegateOfStreamFormat {
if( _microphoneDelegate ){
if( [_microphoneDelegate respondsToSelector:@selector(microphone:hasAudioStreamBasicDescription:) ] ){
[_microphoneDelegate microphone:self
hasAudioStreamBasicDescription:streamFormat];
}
}
}
#pragma mark - AudioBufferList Initialization
-(void)_configureAudioBufferListWithFrameSize:(UInt32)bufferFrameSize {
UInt32 bufferSizeBytes = bufferFrameSize * streamFormat.mBytesPerFrame;
UInt32 propSize = offsetof( AudioBufferList, mBuffers[0] ) + ( sizeof( AudioBuffer ) *streamFormat.mChannelsPerFrame );
microphoneInputBuffer = (AudioBufferList*)malloc(propSize);
microphoneInputBuffer->mNumberBuffers = streamFormat.mChannelsPerFrame;
for( UInt32 i = 0; i < microphoneInputBuffer->mNumberBuffers; i++ ){
microphoneInputBuffer->mBuffers[i].mNumberChannels = streamFormat.mChannelsPerFrame;
microphoneInputBuffer->mBuffers[i].mDataByteSize = bufferSizeBytes;
microphoneInputBuffer->mBuffers[i].mData = malloc(bufferSizeBytes);
}
}
#pragma mark - Float Converter Initialization
-(void)_configureFloatConverterWithFrameSize:(UInt32)bufferFrameSize {
// UInt32 bufferSizeBytes = bufferFrameSize * streamFormat.mBytesPerFrame;
// converter = [[AEFloatConverter alloc] initWithSourceFormat:streamFormat];
// floatBuffers = (float**)malloc(sizeof(float*)*streamFormat.mChannelsPerFrame);
// assert(floatBuffers);
// for ( int i=0; i<streamFormat.mChannelsPerFrame; i++ ) {
// floatBuffers[i] = (float*)malloc(bufferSizeBytes);
// assert(floatBuffers[i]);
// }
}
#pragma mark - Input Callback Initialization
-(void)_configureInputCallback {
AURenderCallbackStruct microphoneCallbackStruct;
microphoneCallbackStruct.inputProc = inputCallback;
microphoneCallbackStruct.inputProcRefCon = (__bridge void *)self;
[EZAudio checkResult:AudioUnitSetProperty(microphoneInput,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global,
// output bus for mac
#if TARGET_OS_IPHONE
kEZAudioMicrophoneInputBus,
#elif TARGET_OS_MAC
kEZAudioMicrophoneOutputBus,
#endif
&microphoneCallbackStruct,
sizeof(microphoneCallbackStruct))
operation:"Couldn't set input callback"];
}
-(void)_disableCallbackBufferAllocation {
[EZAudio checkResult:AudioUnitSetProperty(microphoneInput,
kAudioUnitProperty_ShouldAllocateBuffer,
kAudioUnitScope_Output,
kEZAudioMicrophoneInputBus,
&kEZAudioMicrophoneDisableFlag,
sizeof(kEZAudioMicrophoneDisableFlag))
operation:"Could not disable audio unit allocating its own buffers"];
}
@end
+48 -5
View File
@@ -70,16 +70,17 @@ inNumberFrames:(UInt32)inNumberFrames
*/
-(TPCircularBuffer*)outputShouldUseCircularBuffer:(EZOutput *)output;
/**
Alternate way to provide output with data anytime the EZOutput needs audio data to play. This function expects you to allocate a chunk of memory for an AudioBufferList (see EZAudio function `audioBufferList`) and will try to free it on a seperate thread (see EZAudio function `freeBufferList:`) when it is done to prevent leaking since this is expected to be the end of the road for the audio signal. If the EZOutputDataSource receives a nil or NULL AudioBufferList then the EZOutput component will output silence.
Provides a way to provide output with data anytime the EZOutput needs audio data to play. This function provides an already allocated AudioBufferList to use for providing audio data into the output buffer.
@param output The instance of the EZOutput that asked for the data.
@param audioBufferList The AudioBufferList structure pointer that needs to be filled with audio data
@param frames The amount of frames as a UInt32 that output will need to properly fill its output buffer.
@param bufferSize The pointer to the bufferSize the dataSource is expected to set. For instance, if the bufferSize ended up being 512 you'd say *bufferSize = 512.
@return A pointer to the AudioBufferList structure holding the audio data. If nil or NULL, will output silence.
*/
-(AudioBufferList*) output:(EZOutput*)output
needsBufferListWithFrames:(UInt32)frames
withBufferSize:(UInt32*)bufferSize;
-(void) output:(EZOutput *)output
shouldFillAudioBufferList:(AudioBufferList*)audioBufferList
withNumberOfFrames:(UInt32)frames;
@end
@@ -106,6 +107,16 @@ inNumberFrames:(UInt32)inNumberFrames
*/
-(id)initWithDataSource:(id<EZOutputDataSource>)dataSource;
/**
Creates a new instance of the EZOutput and allows the caller to specify an EZOutputDataSource.
@param dataSource The EZOutputDataSource that will be used to pull the audio data for the output callback.
@param audioStreamBasicDescription The AudioStreamBasicDescription of the EZOutput.
@warning AudioStreamBasicDescriptions that are invalid will cause the EZOutput to fail to initialize
@return A newly created instance of the EZOutput class.
*/
-(id) initWithDataSource:(id<EZOutputDataSource>)dataSource
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription;
#pragma mark - Class Initializers
///-----------------------------------------------------------
/// @name Class Initializers
@@ -118,6 +129,16 @@ inNumberFrames:(UInt32)inNumberFrames
*/
+(EZOutput*)outputWithDataSource:(id<EZOutputDataSource>)dataSource;
/**
Class method to create a new instance of the EZOutput and allows the caller to specify an EZOutputDataSource.
@param dataSource The EZOutputDataSource that will be used to pull the audio data for the output callback.
@param audioStreamBasicDescription The AudioStreamBasicDescription of the EZOutput.
@warning AudioStreamBasicDescriptions that are invalid will cause the EZOutput to fail to initialize
@return A newly created instance of the EZOutput class.
*/
+(EZOutput*)outputWithDataSource:(id<EZOutputDataSource>)dataSource
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription;
#pragma mark - Singleton
///-----------------------------------------------------------
/// @name Shared Instance
@@ -145,6 +166,16 @@ inNumberFrames:(UInt32)inNumberFrames
-(void)stopPlayback;
#pragma mark - Getters
///-----------------------------------------------------------
/// @name Getting The Output Audio Format
///-----------------------------------------------------------
/**
Provides the AudioStreamBasicDescription structure containing the format of the microphone's audio.
@return An AudioStreamBasicDescription structure describing the format of the microphone's audio.
*/
-(AudioStreamBasicDescription)audioStreamBasicDescription;
///-----------------------------------------------------------
/// @name Getting The State Of The Output
///-----------------------------------------------------------
@@ -155,4 +186,16 @@ inNumberFrames:(UInt32)inNumberFrames
*/
-(BOOL)isPlaying;
#pragma mark - Setters
///-----------------------------------------------------------
/// @name Customizing The Output Format
///-----------------------------------------------------------
/**
Sets the AudioStreamBasicDescription on the output.
@warning Do not set this during playback.
@param asbd The new AudioStreamBasicDescription to use in place of the current audio format description.
*/
-(void)setAudioStreamBasicDescription:(AudioStreamBasicDescription)asbd;
@end
+132 -76
View File
@@ -28,8 +28,10 @@
#import "EZAudio.h"
@interface EZOutput (){
BOOL _isPlaying;
AudioUnit _outputUnit;
BOOL _customASBD;
BOOL _isPlaying;
AudioStreamBasicDescription _outputASBD;
AudioUnit _outputUnit;
}
@end
@@ -43,6 +45,8 @@ static OSStatus OutputRenderCallback(void *inRefCon,
UInt32 inNumberFrames,
AudioBufferList *ioData){
// NSLog(@"output something");
EZOutput *output = (__bridge EZOutput*)inRefCon;
// Manual override
if( [output.outputDataSource respondsToSelector:@selector(output:callbackWithActionFlags:inTimeStamp:inBusNumber:inNumberFrames:ioData:)] ){
@@ -57,8 +61,8 @@ static OSStatus OutputRenderCallback(void *inRefCon,
TPCircularBuffer *circularBuffer = [output.outputDataSource outputShouldUseCircularBuffer:output];
if( !circularBuffer ){
Float32 *left = (Float32*)ioData->mBuffers[0].mData;
Float32 *right = (Float32*)ioData->mBuffers[1].mData;
float *left = (float*)ioData->mBuffers[0].mData;
float *right = (float*)ioData->mBuffers[1].mData;
for(int i = 0; i < inNumberFrames; i++ ){
left[ i ] = 0.0f;
right[ i ] = 0.0f;
@@ -72,63 +76,27 @@ static OSStatus OutputRenderCallback(void *inRefCon,
// Get the desired amount of bytes to copy
int32_t bytesToCopy = ioData->mBuffers[0].mDataByteSize;
AudioSampleType *targetBuffer = (AudioSampleType*)ioData->mBuffers[0].mData;
float *left = (float*)ioData->mBuffers[0].mData;
float *right = (float*)ioData->mBuffers[1].mData;
// Get the available bytes in the circular buffer
int32_t availableBytes;
AudioSampleType *buffer = TPCircularBufferTail(circularBuffer,&availableBytes);
float *buffer = TPCircularBufferTail(circularBuffer,&availableBytes);
// Ideally we'd have all the bytes to be copied, but compare it against the available bytes (get min)
int32_t amount = MIN(bytesToCopy,availableBytes);
memcpy(targetBuffer,buffer,amount);
memcpy( left, buffer, amount );
memcpy( right, buffer, amount );
// Consume those bytes ( this will internally push the head of the circular buffer )
TPCircularBufferConsume(circularBuffer,amount);
}
// Provided an AudioBufferList (defaults to silence)
else {
UInt32 bufferSize;
AudioBufferList *bufferList = [output.outputDataSource output:output
needsBufferListWithFrames:inNumberFrames
withBufferSize:&bufferSize];
if( !bufferList ){
Float32 *left = (Float32*)ioData->mBuffers[0].mData;
Float32 *right = (Float32*)ioData->mBuffers[1].mData;
for(int i = 0; i < inNumberFrames; i++ ){
left[ i ] = 0.0f;
right[ i ] = 0.0f;
}
return noErr;
};
// Interleaved
if( !(ioData->mNumberBuffers == 1) ){
Float32 *left = (Float32*)ioData->mBuffers[0].mData;
Float32 *right = (Float32*)ioData->mBuffers[1].mData;
for(int i = 0; i < inNumberFrames; i++ ){
if( bufferList ){
Float32 *interleaved = (Float32*)bufferList->mBuffers[0].mData;
left[i] = interleaved[i];
right[i] = interleaved[i];
}
else {
left[ i ] = 0.0f;
right[ i ] = 0.0f;
}
}
}
// Non-interleaved
else {
memcpy(ioData,
bufferList,
sizeof(AudioBufferList)+(bufferList->mNumberBuffers-1)*sizeof(AudioBuffer));
}
if( bufferList ){
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0ul),^{
[EZAudio freeBufferList:bufferList];
});
}
else if( [output.outputDataSource respondsToSelector:@selector(output:shouldFillAudioBufferList:withNumberOfFrames:)] ) {
[output.outputDataSource output:output
shouldFillAudioBufferList:ioData
withNumberOfFrames:inNumberFrames];
}
return noErr;
@@ -152,11 +120,28 @@ static OSStatus OutputRenderCallback(void *inRefCon,
return self;
}
-(id) initWithDataSource:(id<EZOutputDataSource>)dataSource
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription {
self = [super init];
if(self){
_customASBD = YES;
_outputASBD = audioStreamBasicDescription;
self.outputDataSource = dataSource;
[self _configureOutput];
}
return self;
}
#pragma mark - Class Initializers
+(EZOutput*)outputWithDataSource:(id<EZOutputDataSource>)dataSource {
return [[EZOutput alloc] initWithDataSource:dataSource];
}
+(EZOutput *)outputWithDataSource:(id<EZOutputDataSource>)dataSource
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription {
return [[EZOutput alloc] initWithDataSource:dataSource withAudioStreamBasicDescription:audioStreamBasicDescription];
}
#pragma mark - Singleton
+(EZOutput*)sharedOutput {
static EZOutput *_sharedOutput = nil;
@@ -167,18 +152,60 @@ static OSStatus OutputRenderCallback(void *inRefCon,
return _sharedOutput;
}
#pragma mark - Private Configuration
#pragma mark - Audio Component Initialization
-(AudioComponentDescription)_getOutputAudioComponentDescription {
// Create an output component description for default output device
AudioComponentDescription outputComponentDescription;
outputComponentDescription.componentFlags = 0;
outputComponentDescription.componentFlagsMask = 0;
outputComponentDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
#if TARGET_OS_IPHONE
outputComponentDescription.componentSubType = kAudioUnitSubType_RemoteIO;
#elif TARGET_OS_MAC
outputComponentDescription.componentSubType = kAudioUnitSubType_DefaultOutput;
#endif
outputComponentDescription.componentType = kAudioUnitType_Output;
return outputComponentDescription;
}
-(AudioComponent)_getOutputComponentWithAudioComponentDescription:(AudioComponentDescription)outputComponentDescription {
// Try and find the component
AudioComponent outputComponent = AudioComponentFindNext( NULL , &outputComponentDescription );
NSAssert(outputComponent,@"Couldn't get input component unit!");
return outputComponent;
}
-(void)_createNewInstanceForOutputComponent:(AudioComponent)outputComponent {
//
[EZAudio checkResult:AudioComponentInstanceNew( outputComponent, &_outputUnit )
operation:"Failed to open component for output unit"];
}
#pragma mark - Configure The Output Unit
//-(void)_configureOutput {
//
// // Get component description for output
// AudioComponentDescription outputComponentDescription = [self _getOutputAudioComponentDescription];
//
// // Get the output component
// AudioComponent outputComponent = [self _getOutputComponentWithAudioComponentDescription:outputComponentDescription];
//
// // Create a new instance of the component and store it for internal use
// [self _createNewInstanceForOutputComponent:outputComponent];
//
//}
#if TARGET_OS_IPHONE
-(void)_configureOutput {
//
AudioComponentDescription outputcd;
outputcd.componentFlags = 0;
outputcd.componentFlagsMask = 0;
outputcd.componentFlags = 0;
outputcd.componentFlagsMask = 0;
outputcd.componentManufacturer = kAudioUnitManufacturer_Apple;
outputcd.componentSubType = kAudioUnitSubType_RemoteIO;
outputcd.componentType = kAudioUnitType_Output;
outputcd.componentSubType = kAudioUnitSubType_RemoteIO;
outputcd.componentType = kAudioUnitType_Output;
//
AudioComponent comp = AudioComponentFindNext(NULL,&outputcd);
@@ -186,8 +213,8 @@ static OSStatus OutputRenderCallback(void *inRefCon,
operation:"Failed to get output unit"];
// Setup the output unit for playback
UInt32 oneFlag = 1;
AudioUnitElement bus0 = 0;
UInt32 oneFlag = 1;
AudioUnitElement bus0 = 0;
[EZAudio checkResult:AudioUnitSetProperty(_outputUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output,
@@ -199,32 +226,21 @@ static OSStatus OutputRenderCallback(void *inRefCon,
// Get the hardware sample rate
Float64 hardwareSampleRate = 44100;
#if !(TARGET_IPHONE_SIMULATOR)
UInt32 propSize = sizeof(hardwareSampleRate);
[EZAudio checkResult:AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareSampleRate,
&propSize,
&hardwareSampleRate)
operation:"Could not get hardware sample rate"];
hardwareSampleRate = [[AVAudioSession sharedInstance] sampleRate];
#endif
// Setup an ASBD in canonical format
AudioStreamBasicDescription asbd;
memset(&asbd, 0, sizeof(asbd));
asbd.mBitsPerChannel = 8 * sizeof(AudioUnitSampleType);
asbd.mBytesPerFrame = sizeof(AudioUnitSampleType);
asbd.mBytesPerPacket = sizeof(AudioUnitSampleType);
asbd.mChannelsPerFrame = 2;
asbd.mFormatFlags = kAudioFormatFlagsCanonical | kAudioFormatFlagIsNonInterleaved;
asbd.mFormatID = kAudioFormatLinearPCM;
asbd.mFramesPerPacket = 1;
asbd.mSampleRate = hardwareSampleRate;
// Setup an ASBD in canonical format by default
if( !_customASBD ){
_outputASBD = [EZAudio stereoCanonicalNonInterleavedFormatWithSampleRate:hardwareSampleRate];
}
// Set the format for output
[EZAudio checkResult:AudioUnitSetProperty(_outputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
bus0,
&asbd,
sizeof(asbd))
&_outputASBD,
sizeof(_outputASBD))
operation:"Couldn't set the ASBD for input scope/bos 0"];
//
@@ -262,9 +278,22 @@ static OSStatus OutputRenderCallback(void *inRefCon,
}
[EZAudio checkResult:AudioComponentInstanceNew(comp,&_outputUnit)
operation:"Failed to open component for output unit"];
// Setup an ASBD in canonical format by default
if( !_customASBD ){
_outputASBD = [EZAudio stereoFloatNonInterleavedFormatWithSampleRate:44100];
}
// Set the format for output
[EZAudio checkResult:AudioUnitSetProperty(_outputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
0,
&_outputASBD,
sizeof(_outputASBD))
operation:"Couldn't set the ASBD for input scope/bos 0"];
//
AURenderCallbackStruct input;
input.inputProc = OutputRenderCallback;
@@ -302,10 +331,37 @@ static OSStatus OutputRenderCallback(void *inRefCon,
}
#pragma mark - Getters
-(AudioStreamBasicDescription)audioStreamBasicDescription {
return _outputASBD;
}
-(BOOL)isPlaying {
return _isPlaying;
}
#pragma mark - Setters
-(void)setAudioStreamBasicDescription:(AudioStreamBasicDescription)asbd {
BOOL wasPlaying = NO;
if( self.isPlaying ){
[self stopPlayback];
wasPlaying = YES;
}
_customASBD = YES;
_outputASBD = asbd;
// Set the format for output
[EZAudio checkResult:AudioUnitSetProperty(_outputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
0,
&_outputASBD,
sizeof(_outputASBD))
operation:"Couldn't set the ASBD for input scope/bos 0"];
if( wasPlaying )
{
[self startPlayback];
}
}
-(void)dealloc {
[EZAudio checkResult:AudioOutputUnitStop(_outputUnit)
operation:"Failed to uninitialize output unit"];
+13 -2
View File
@@ -33,11 +33,11 @@
*/
typedef NS_ENUM(NSInteger,EZPlotType){
/**
* Plot that displays only the samples of the current buffer
Plot that displays only the samples of the current buffer
*/
EZPlotTypeBuffer,
/**
* Plot that displays a rolling history of values using the RMS calculated for each incoming buffer
Plot that displays a rolling history of values using the RMS calculated for each incoming buffer
*/
EZPlotTypeRolling
};
@@ -91,10 +91,21 @@ typedef NS_ENUM(NSInteger,EZPlotType){
*/
@property (nonatomic,assign,setter=setShouldMirror:) BOOL shouldMirror;
#pragma mark - Clearing
///-----------------------------------------------------------
/// @name Clearing The Plot
///-----------------------------------------------------------
/**
Clears all data from the audio plot (includes both EZPlotTypeBuffer and EZPlotTypeRolling)
*/
-(void)clear;
#pragma mark - Get Samples
///-----------------------------------------------------------
/// @name Updating The Plot
///-----------------------------------------------------------
/**
Updates the plot with the new buffer data and tells the view to redraw itself. Caller will provide a float array with the values they expect to see on the y-axis. The plot will internally handle mapping the x-axis and y-axis to the current view port, any interpolation for fills effects, and mirroring.
@param buffer A float array of values to map to the y-axis.
+8 -1
View File
@@ -31,9 +31,16 @@
@implementation EZPlot
#pragma mark - Clearing
-(void)clear
{
// Override in subclass
}
#pragma mark - Get Samples
-(void)updateBuffer:(float *)buffer
withBufferSize:(UInt32)bufferSize {
withBufferSize:(UInt32)bufferSize
{
// Override in subclass
}
+45 -19
View File
@@ -26,12 +26,32 @@
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
/**
To ensure valid recording formats are used when recording to a file the EZRecorderFileType describes the most common file types that a file can be encoded in. Each of these types can be used to output recordings as such:
EZRecorderFileTypeAIFF - .aif, .aiff, .aifc, .aac
EZRecorderFileTypeM4A - .m4a, .mp4
EZRecorderFileTypeWAV - .wav
*/
typedef NS_ENUM(NSInteger, EZRecorderFileType)
{
/**
Recording format that describes AIFF file types. These are uncompressed, LPCM files that are completely lossless, but are large in file size.
*/
EZRecorderFileTypeAIFF,
/**
Recording format that describes M4A file types. These are compressed, but yield great results especially when file size is an issue.
*/
EZRecorderFileTypeM4A,
/**
Recording format that describes WAV file types. These are uncompressed, LPCM files that are completely lossless, but are large in file size.
*/
EZRecorderFileTypeWAV
};
/**
The EZRecorder provides a flexible way to create an audio file and append raw audio data to it. The EZRecorder will convert the incoming audio on the fly to the destination format so no conversion is needed between this and any other component. Right now the only supported output format is 'caf'. Each output file should have its own EZRecorder instance (think 1 EZRecorder = 1 audio file).
#Future Plans#
Extend EZRecorder to allow any destination AudioStreamBasicDescription and any file extension.
*/
@interface EZRecorder : NSObject
@@ -44,10 +64,12 @@
Creates a new instance of an EZRecorder using a destination file path URL and the source format of the incoming audio.
@param url An NSURL specifying the file path location of where the audio file should be written to.
@param sourceFormat The AudioStreamBasicDescription for the incoming audio that will be written to the file.
@param destinationFileType A constant described by the EZRecorderFileType that corresponds to the type of destination file that should be written. For instance, an AAC file written using an '.m4a' extension would correspond to EZRecorderFileTypeM4A. See EZRecorderFileType for all the constants and mapping combinations.
@return The newly created EZRecorder instance.
*/
-(EZRecorder*)initWithDestinationURL:(NSURL*)url
andSourceFormat:(AudioStreamBasicDescription)sourceFormat;
sourceFormat:(AudioStreamBasicDescription)sourceFormat
destinationFileType:(EZRecorderFileType)destinationFileType;
#pragma mark - Class Initializers
@@ -59,27 +81,22 @@
Class method to create a new instance of an EZRecorder using a destination file path URL and the source format of the incoming audio.
@param url An NSURL specifying the file path location of where the audio file should be written to.
@param sourceFormat The AudioStreamBasicDescription for the incoming audio that will be written to the file.
@param destinationFileType A constant described by the EZRecorderFileType that corresponds to the type of destination file that should be written. For instance, an AAC file written using an '.m4a' extension would correspond to EZRecorderFileTypeM4A. See EZRecorderFileType for all the constants and mapping combinations.
@return The newly created EZRecorder instance.
*/
+(EZRecorder*)recorderWithDestinationURL:(NSURL*)url
andSourceFormat:(AudioStreamBasicDescription)sourceFormat;
sourceFormat:(AudioStreamBasicDescription)sourceFormat
destinationFileType:(EZRecorderFileType)destinationFileType;
#pragma mark - Class Methods
#pragma mark - Getters
///-----------------------------------------------------------
/// @name Class Methods
/// @name Getting The Recorder's Properties
///-----------------------------------------------------------
/**
Class method returning the format used for the output file.
@return An AudioStreamBasicDescription describing the output file's format.
Provides the file path that's currently being used by the recorder.
@return The NSURL representing the file path of the audio file path being used for recording.
*/
+(AudioStreamBasicDescription)defaultDestinationFormat;
/**
Class method returning the default format extension to use for output audio file (caf).
@return An NSString representing the default output audio file's extension @"caf"
*/
+(NSString*)defaultDestinationFormatExtension;
-(NSURL*)url;
#pragma mark - Events
///-----------------------------------------------------------
@@ -94,4 +111,13 @@
-(void)appendDataFromBufferList:(AudioBufferList*)bufferList
withBufferSize:(UInt32)bufferSize;
@end
///-----------------------------------------------------------
/// @name Closing The Audio File
///-----------------------------------------------------------
/**
Finishes writes to the audio file and closes it.
*/
-(void)closeAudioFile;
@end
+136 -118
View File
@@ -28,148 +28,166 @@
#import "EZAudio.h"
@interface EZRecorder (){
AudioConverterRef _audioConverter;
AudioStreamBasicDescription _clientFormat;
ExtAudioFileRef _destinationFile;
CFURLRef _destinationFileURL;
AudioStreamBasicDescription _destinationFormat;
AudioStreamBasicDescription _sourceFormat;
ExtAudioFileRef _destinationFile;
AudioFileTypeID _destinationFileTypeID;
CFURLRef _destinationFileURL;
AudioStreamBasicDescription _destinationFormat;
AudioStreamBasicDescription _sourceFormat;
}
typedef struct {
AudioBufferList *sourceBuffer;
} EZRecorderConverterStruct;
@end
@implementation EZRecorder
#pragma mark - Initializers
-(EZRecorder*)initWithDestinationURL:(NSURL*)url
andSourceFormat:(AudioStreamBasicDescription)sourceFormat {
self = [super init];
if(self){
_destinationFileURL = (__bridge CFURLRef)url;
_sourceFormat = sourceFormat;
_destinationFormat = [EZRecorder defaultDestinationFormat];
[self _configureRecorder];
}
return self;
sourceFormat:(AudioStreamBasicDescription)sourceFormat
destinationFileType:(EZRecorderFileType)destinationFileType
{
self = [super init];
if( self )
{
// Set defaults
_destinationFile = NULL;
_destinationFileURL = (__bridge CFURLRef)url;
_sourceFormat = sourceFormat;
_destinationFormat = [EZRecorder recorderFormatForFileType:destinationFileType
withSourceFormat:_sourceFormat];
_destinationFileTypeID = [EZRecorder recorderFileTypeIdForFileType:destinationFileType
withSourceFormat:_sourceFormat];
// Initializer the recorder instance
[self _initializeRecorder];
}
return self;
}
#pragma mark - Class Initializers
+(EZRecorder*)recorderWithDestinationURL:(NSURL*)url
andSourceFormat:(AudioStreamBasicDescription)sourceFormat {
return [[EZRecorder alloc] initWithDestinationURL:url
andSourceFormat:sourceFormat];
sourceFormat:(AudioStreamBasicDescription)sourceFormat
destinationFileType:(EZRecorderFileType)destinationFileType
{
return [[EZRecorder alloc] initWithDestinationURL:url
sourceFormat:sourceFormat
destinationFileType:destinationFileType];
}
#pragma mark - Class Format Helper
+(AudioStreamBasicDescription)defaultDestinationFormat {
AudioStreamBasicDescription destinationFormat;
destinationFormat.mFormatID = kAudioFormatLinearPCM;
destinationFormat.mChannelsPerFrame = 1;
destinationFormat.mBitsPerChannel = 16;
destinationFormat.mBytesPerPacket = destinationFormat.mBytesPerFrame = 2 * destinationFormat.mChannelsPerFrame;
destinationFormat.mFramesPerPacket = 1;
destinationFormat.mFormatFlags = kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger; // little-endian
destinationFormat.mSampleRate = 44100.0;
return destinationFormat;
#pragma mark - Private Configuration
+(AudioStreamBasicDescription)recorderFormatForFileType:(EZRecorderFileType)fileType
withSourceFormat:(AudioStreamBasicDescription)sourceFormat
{
AudioStreamBasicDescription asbd;
switch ( fileType )
{
case EZRecorderFileTypeAIFF:
asbd = [EZAudio AIFFFormatWithNumberOfChannels:sourceFormat.mChannelsPerFrame
sampleRate:sourceFormat.mSampleRate];
break;
case EZRecorderFileTypeM4A:
asbd = [EZAudio M4AFormatWithNumberOfChannels:sourceFormat.mChannelsPerFrame
sampleRate:sourceFormat.mSampleRate];
break;
case EZRecorderFileTypeWAV:
asbd = [EZAudio stereoFloatInterleavedFormatWithSampleRate:sourceFormat.mSampleRate];
break;
default:
asbd = [EZAudio stereoCanonicalNonInterleavedFormatWithSampleRate:sourceFormat.mSampleRate];
break;
}
return asbd;
}
+(NSString *)defaultDestinationFormatExtension {
return @"caf";
+(AudioFileTypeID)recorderFileTypeIdForFileType:(EZRecorderFileType)fileType
withSourceFormat:(AudioStreamBasicDescription)sourceFormat
{
AudioFileTypeID audioFileTypeID;
switch ( fileType )
{
case EZRecorderFileTypeAIFF:
audioFileTypeID = kAudioFileAIFFType;
break;
case EZRecorderFileTypeM4A:
audioFileTypeID = kAudioFileM4AType;
break;
case EZRecorderFileTypeWAV:
audioFileTypeID = kAudioFileWAVEType;
break;
default:
audioFileTypeID = kAudioFileWAVEType;
break;
}
return audioFileTypeID;
}
#pragma mark - Private Configuation
-(void)_configureRecorder {
// Create the extended audio file
[EZAudio checkResult:ExtAudioFileCreateWithURL(_destinationFileURL,
kAudioFileCAFType,
&_destinationFormat,
NULL,
kAudioFileFlags_EraseFile,
&_destinationFile)
operation:"Failed to create ExtendedAudioFile reference"];
// Set the client format
_clientFormat = _destinationFormat;
if( _destinationFormat.mFormatID != kAudioFormatLinearPCM ){
[EZAudio setCanonicalAudioStreamBasicDescription:_destinationFormat
numberOfChannels:_destinationFormat.mChannelsPerFrame
interleaved:YES];
}
UInt32 propertySize = sizeof(_clientFormat);
[EZAudio checkResult:ExtAudioFileSetProperty(_destinationFile,
kExtAudioFileProperty_ClientDataFormat,
propertySize,
&_clientFormat)
operation:"Failed to set client data format on destination file"];
// Instantiate the writer
[EZAudio checkResult:ExtAudioFileWriteAsync(_destinationFile, 0, NULL)
operation:"Failed to initialize with ExtAudioFileWriteAsync"];
// Setup the audio converter
[EZAudio checkResult:AudioConverterNew(&_sourceFormat, &_clientFormat, &_audioConverter)
operation:"Failed to create new audio converter"];
-(void)_initializeRecorder
{
// Finish filling out the destination format description
UInt32 propSize = sizeof(_destinationFormat);
[EZAudio checkResult:AudioFormatGetProperty(kAudioFormatProperty_FormatInfo,
0,
NULL,
&propSize,
&_destinationFormat)
operation:"Failed to fill out rest of destination format"];
// Create the audio file
[EZAudio checkResult:ExtAudioFileCreateWithURL(_destinationFileURL,
_destinationFileTypeID,
&_destinationFormat,
NULL,
kAudioFileFlags_EraseFile,
&_destinationFile)
operation:"Failed to create audio file"];
// Set the client format (which should be equal to the source format)
[EZAudio checkResult:ExtAudioFileSetProperty(_destinationFile,
kExtAudioFileProperty_ClientDataFormat,
sizeof(_sourceFormat),
&_sourceFormat)
operation:"Failed to set client format on recorded audio file"];
}
#pragma mark - Events
-(void)appendDataFromBufferList:(AudioBufferList*)bufferList
withBufferSize:(UInt32)bufferSize {
// Setup output buffers
UInt32 outputBufferSize = 32 * 1024; // 32 KB
AudioBufferList *convertedData = [EZAudio audioBufferList];
convertedData->mNumberBuffers = 1;
convertedData->mBuffers[0].mNumberChannels = _clientFormat.mChannelsPerFrame;
convertedData->mBuffers[0].mDataByteSize = outputBufferSize;
convertedData->mBuffers[0].mData = (UInt8*)malloc(sizeof(UInt8)*outputBufferSize);
[EZAudio checkResult:AudioConverterFillComplexBuffer(_audioConverter,
complexInputDataProc,
&(EZRecorderConverterStruct){ .sourceBuffer = bufferList },
&bufferSize,
convertedData,
NULL) operation:"Failed while converting buffers"];
// Write the destination audio buffer list into t
[EZAudio checkResult:ExtAudioFileWriteAsync(_destinationFile, bufferSize, convertedData)
operation:"Failed to write audio data to file"];
// Free resources
[EZAudio freeBufferList:convertedData];
-(void)appendDataFromBufferList:(AudioBufferList *)bufferList
withBufferSize:(UInt32)bufferSize
{
if( _destinationFile )
{
[EZAudio checkResult:ExtAudioFileWriteAsync(_destinationFile,
bufferSize,
bufferList)
operation:"Failed to write audio data to recorded audio file"];
}
}
static OSStatus complexInputDataProc(AudioConverterRef inAudioConverter,
UInt32 *ioNumberDataPackets,
AudioBufferList *ioData,
AudioStreamPacketDescription **outDataPacketDescription,
void *inUserData) {
EZRecorderConverterStruct *recorderStruct = (EZRecorderConverterStruct*)inUserData;
if ( !recorderStruct->sourceBuffer ) {
return -2222; // No More Data
}
memcpy(ioData,
recorderStruct->sourceBuffer,
sizeof(AudioBufferList) + (recorderStruct->sourceBuffer->mNumberBuffers-1)*sizeof(AudioBuffer));
recorderStruct->sourceBuffer = NULL;
return noErr;
-(void)closeAudioFile
{
if( _destinationFile )
{
// Dispose of the audio file reference
[EZAudio checkResult:ExtAudioFileDispose(_destinationFile)
operation:"Failed to close audio file"];
// Null out the file reference
_destinationFile = NULL;
}
}
#pragma mark - Cleanup
-(void)dealloc {
[EZAudio checkResult:AudioConverterDispose(_audioConverter)
operation:"Failed to dispose audio converter in recorder"];
[EZAudio checkResult:ExtAudioFileDispose(_destinationFile)
operation:"Failed to dispose extended audio file in recorder"];
-(NSURL *)url
{
return (__bridge NSURL*)_destinationFileURL;
}
@end
#pragma mark - Dealloc
-(void)dealloc
{
[self closeAudioFile];
}
@end
+1 -1
View File
@@ -46,7 +46,7 @@ bool TPCircularBufferInit(TPCircularBuffer *buffer, int length) {
int retries = 3;
while ( true ) {
buffer->length = round_page(length); // We need whole page sizes
buffer->length = (int32_t)round_page(length); // We need whole page sizes
// Temporarily allocate twice the length, so we have the contiguous address space to
// support a second instance of the buffer directly after
+22
View File
@@ -27,3 +27,25 @@ THE SOFTWARE.
Initial release.
Components include: EZAudioFile, EZAudioPlot, EZAudioPlotGL, EZMicrophone, EZOutput, and EZRecorder.
Provided 6 example projects: CoreGraphicsWaveform, OpenGLWaveform, WaveformFromFile, PassThrough, Record, and PlayFile.
0.0.2
Fix for Cocoapod spec. Forgot to include files with .c extension.
0.0.3
Changing EZAudioPlot and EZAudioPlotGL to scroll for the EZPlotTypeRolling instead of wiping the screen clean when hitting the end
Allowed EZMicrophone EZOutput to have custom AudioStreamBasicDescription setters
Fixed bug in EZAudioFile's getWaveformData function where it was not exiting after sending back cached waveform data (rereading from the audio file)
Added stereo support for EZMicrophone, EZAudioFile, EZRecorder, and EZOutput
Added more memory cleanup for EZAudioFile
Added adjustable rolling length for EZAudioPlot and EZAudioPlotGL so those rolling graphs can now range from 128 to 8192 whereas before it was fixed at 1024
Added adjustable resolution for waveform data coming from the EZAudioFile so output from the getWaveformDataWithCompletionBlock: function can literally be of any size. Try 128 for a low resolution waveform or 8192 for a much higher resolution waveform.
Added quick fix for EZOutput to properly route stereo data coming from a circular buffer datasource. Next version (0.0.4) needs to add EZConverter to allow quick conversions between non-interleaved and interleaved formats.
0.0.4
Added closeAudioFile to EZRecorder to properly dispose of internal audio file prior to trying to reload it using the EZAudioFile.
Added new EZOutputDataSource method that provides pre-allocated AudioBufferList to fill instead of caller allocating and disposing on AudioBufferList. Much less errors.
Added EZAudioPlayer for playback and visualization of local audio files (no network streaming yet).
Merged bug fixes from community for EZAudio file.
0.0.5
Added multiple destination recording formats to the EZRecorder (EZRecorderFileType)
+1 -1
View File
@@ -23,4 +23,4 @@ THE SOFTWARE.
==========================================================================================
0.0.1
0.0.5
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
@@ -6,6 +6,20 @@
objectVersion = 46;
objects = {
/* Begin PBXAggregateTarget section */
94F8DF4A18C84203005C4CBD /* Generate Documentation */ = {
isa = PBXAggregateTarget;
buildConfigurationList = 94F8DF4D18C84204005C4CBD /* Build configuration list for PBXAggregateTarget "Generate Documentation" */;
buildPhases = (
94F8DF4E18C8420C005C4CBD /* ShellScript */,
);
dependencies = (
);
name = "Generate Documentation";
productName = "Generate Documentation";
};
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
94056D88185B97E300EB94BA /* CoreGraphicsWaveformViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 94056D86185B97E300EB94BA /* CoreGraphicsWaveformViewController.m */; };
94056D89185B97E300EB94BA /* CoreGraphicsWaveformViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 94056D87185B97E300EB94BA /* CoreGraphicsWaveformViewController.xib */; };
@@ -331,6 +345,7 @@
targets = (
94373020185B931C00F315F0 /* EZAudioCoreGraphicsWaveformExample */,
94373041185B931C00F315F0 /* EZAudioCoreGraphicsWaveformExampleTests */,
94F8DF4A18C84203005C4CBD /* Generate Documentation */,
);
};
/* End PBXProject section */
@@ -360,6 +375,22 @@
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
94F8DF4E18C8420C005C4CBD /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/usr/local/bin/appledoc \\\n--project-name \"EZAudio\" \\\n--project-company \"Syed Haris Ali\" \\\n--company-id \"com.sha\" \\\n--output documentation \\\n--create-docset \\\n--install-docset \\\n--create-html \\\n--exit-threshold 2 \\\n--no-repeat-first-par \\\n/../EZAudio";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
9437301D185B931C00F315F0 /* Sources */ = {
isa = PBXSourcesBuildPhase;
@@ -570,6 +601,20 @@
};
name = Release;
};
94F8DF4B18C84204005C4CBD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
94F8DF4C18C84204005C4CBD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@@ -600,6 +645,14 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
94F8DF4D18C84204005C4CBD /* Build configuration list for PBXAggregateTarget "Generate Documentation" */ = {
isa = XCConfigurationList;
buildConfigurations = (
94F8DF4B18C84204005C4CBD /* Debug */,
94F8DF4C18C84204005C4CBD /* Release */,
);
defaultConfigurationIsVisible = 0;
};
/* End XCConfigurationList section */
};
rootObject = 94373019185B931C00F315F0 /* Project object */;
@@ -19,4 +19,7 @@
<FileRef
location = "group:EZAudioPassThroughExample/EZAudioPassThroughExample.xcodeproj">
</FileRef>
<FileRef
location = "group:EZAudioFFTExample/EZAudioFFTExample.xcodeproj">
</FileRef>
</Workspace>
@@ -10,29 +10,29 @@
<string>EZAudioExamplesOSX</string>
<key>IDESourceControlProjectOriginsDictionary</key>
<dict>
<key>F77EC8D9-F815-4829-9274-5DA08EA98D6B</key>
<key>9D6FF97A89F512CD81EAE1A971A1D2EB03E03F7C</key>
<string>https://github.com/syedhali/EZAudio.git</string>
</dict>
<key>IDESourceControlProjectPath</key>
<string>EZAudioExamples/OSX/EZAudioExamplesOSX.xcworkspace</string>
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
<dict>
<key>F77EC8D9-F815-4829-9274-5DA08EA98D6B</key>
<key>9D6FF97A89F512CD81EAE1A971A1D2EB03E03F7C</key>
<string>../../..</string>
</dict>
<key>IDESourceControlProjectURL</key>
<string>https://github.com/syedhali/EZAudio.git</string>
<key>IDESourceControlProjectVersion</key>
<integer>110</integer>
<integer>111</integer>
<key>IDESourceControlProjectWCCIdentifier</key>
<string>F77EC8D9-F815-4829-9274-5DA08EA98D6B</string>
<string>9D6FF97A89F512CD81EAE1A971A1D2EB03E03F7C</string>
<key>IDESourceControlProjectWCConfigurations</key>
<array>
<dict>
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
<string>public.vcs.git</string>
<key>IDESourceControlWCCIdentifierKey</key>
<string>F77EC8D9-F815-4829-9274-5DA08EA98D6B</string>
<string>9D6FF97A89F512CD81EAE1A971A1D2EB03E03F7C</string>
<key>IDESourceControlWCCName</key>
<string>EZAudio</string>
</dict>
@@ -0,0 +1,610 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
9417A8F71871492000D9D37B /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A8F61871492000D9D37B /* Cocoa.framework */; };
9417A9011871492000D9D37B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9417A8FF1871492000D9D37B /* InfoPlist.strings */; };
9417A9031871492000D9D37B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9021871492000D9D37B /* main.m */; };
9417A9071871492100D9D37B /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 9417A9051871492100D9D37B /* Credits.rtf */; };
9417A90A1871492100D9D37B /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9091871492100D9D37B /* AppDelegate.m */; };
9417A90D1871492100D9D37B /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9417A90B1871492100D9D37B /* MainMenu.xib */; };
9417A90F1871492100D9D37B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9417A90E1871492100D9D37B /* Images.xcassets */; };
9417A9161871492100D9D37B /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A9151871492100D9D37B /* XCTest.framework */; };
9417A9171871492100D9D37B /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A8F61871492000D9D37B /* Cocoa.framework */; };
9417A91F1871492100D9D37B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9417A91D1871492100D9D37B /* InfoPlist.strings */; };
9417A9211871492100D9D37B /* EZAudioFFTExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9201871492100D9D37B /* EZAudioFFTExampleTests.m */; };
9417A9441871493900D9D37B /* AEFloatConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A92C1871493900D9D37B /* AEFloatConverter.m */; };
9417A9451871493900D9D37B /* EZAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A92E1871493900D9D37B /* EZAudio.m */; };
9417A9461871493900D9D37B /* EZAudioFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9301871493900D9D37B /* EZAudioFile.m */; };
9417A9471871493900D9D37B /* EZAudioPlot.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9321871493900D9D37B /* EZAudioPlot.m */; };
9417A9481871493900D9D37B /* EZAudioPlotGL.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9341871493900D9D37B /* EZAudioPlotGL.m */; };
9417A9491871493900D9D37B /* EZAudioPlotGLKViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9361871493900D9D37B /* EZAudioPlotGLKViewController.m */; };
9417A94A1871493900D9D37B /* EZMicrophone.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9381871493900D9D37B /* EZMicrophone.m */; };
9417A94B1871493900D9D37B /* EZOutput.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A93A1871493900D9D37B /* EZOutput.m */; };
9417A94C1871493900D9D37B /* EZPlot.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A93C1871493900D9D37B /* EZPlot.m */; };
9417A94D1871493900D9D37B /* EZRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A93E1871493900D9D37B /* EZRecorder.m */; };
9417A94E1871493900D9D37B /* TPCircularBuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 9417A93F1871493900D9D37B /* TPCircularBuffer.c */; };
9417A94F1871493900D9D37B /* CHANGELOG in Resources */ = {isa = PBXBuildFile; fileRef = 9417A9421871493900D9D37B /* CHANGELOG */; };
9417A9501871493900D9D37B /* VERSION in Resources */ = {isa = PBXBuildFile; fileRef = 9417A9431871493900D9D37B /* VERSION */; };
9417A954187149EA00D9D37B /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A951187149EA00D9D37B /* AudioToolbox.framework */; };
9417A955187149EA00D9D37B /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A952187149EA00D9D37B /* AudioUnit.framework */; };
9417A956187149EA00D9D37B /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A953187149EA00D9D37B /* CoreAudio.framework */; };
9417A959187149F000D9D37B /* GLKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A957187149F000D9D37B /* GLKit.framework */; };
9417A95A187149F000D9D37B /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A958187149F000D9D37B /* OpenGL.framework */; };
9417A95C18714A1000D9D37B /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A95B18714A1000D9D37B /* QuartzCore.framework */; };
9417A95E18714A2A00D9D37B /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A95D18714A2A00D9D37B /* Accelerate.framework */; };
9417A9D61872130200D9D37B /* FFTViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9D41872130200D9D37B /* FFTViewController.m */; };
9417A9D71872130200D9D37B /* FFTViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9417A9D51872130200D9D37B /* FFTViewController.xib */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
9417A9181871492100D9D37B /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 9417A8EB1871492000D9D37B /* Project object */;
proxyType = 1;
remoteGlobalIDString = 9417A8F21871492000D9D37B;
remoteInfo = EZAudioFFTExample;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
9417A8F31871492000D9D37B /* EZAudioFFTExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = EZAudioFFTExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
9417A8F61871492000D9D37B /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
9417A8F91871492000D9D37B /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
9417A8FA1871492000D9D37B /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
9417A8FB1871492000D9D37B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
9417A8FE1871492000D9D37B /* EZAudioFFTExample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "EZAudioFFTExample-Info.plist"; sourceTree = "<group>"; };
9417A9001871492000D9D37B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
9417A9021871492000D9D37B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
9417A9041871492000D9D37B /* EZAudioFFTExample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "EZAudioFFTExample-Prefix.pch"; sourceTree = "<group>"; };
9417A9061871492100D9D37B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = "<group>"; };
9417A9081871492100D9D37B /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
9417A9091871492100D9D37B /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
9417A90C1871492100D9D37B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
9417A90E1871492100D9D37B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
9417A9141871492100D9D37B /* EZAudioFFTExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = EZAudioFFTExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
9417A9151871492100D9D37B /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
9417A91C1871492100D9D37B /* EZAudioFFTExampleTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "EZAudioFFTExampleTests-Info.plist"; sourceTree = "<group>"; };
9417A91E1871492100D9D37B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
9417A9201871492100D9D37B /* EZAudioFFTExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EZAudioFFTExampleTests.m; sourceTree = "<group>"; };
9417A92B1871493900D9D37B /* AEFloatConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEFloatConverter.h; sourceTree = "<group>"; };
9417A92C1871493900D9D37B /* AEFloatConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AEFloatConverter.m; sourceTree = "<group>"; };
9417A92D1871493900D9D37B /* EZAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudio.h; sourceTree = "<group>"; };
9417A92E1871493900D9D37B /* EZAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudio.m; sourceTree = "<group>"; };
9417A92F1871493900D9D37B /* EZAudioFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioFile.h; sourceTree = "<group>"; };
9417A9301871493900D9D37B /* EZAudioFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioFile.m; sourceTree = "<group>"; };
9417A9311871493900D9D37B /* EZAudioPlot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlot.h; sourceTree = "<group>"; };
9417A9321871493900D9D37B /* EZAudioPlot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlot.m; sourceTree = "<group>"; };
9417A9331871493900D9D37B /* EZAudioPlotGL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlotGL.h; sourceTree = "<group>"; };
9417A9341871493900D9D37B /* EZAudioPlotGL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlotGL.m; sourceTree = "<group>"; };
9417A9351871493900D9D37B /* EZAudioPlotGLKViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlotGLKViewController.h; sourceTree = "<group>"; };
9417A9361871493900D9D37B /* EZAudioPlotGLKViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlotGLKViewController.m; sourceTree = "<group>"; };
9417A9371871493900D9D37B /* EZMicrophone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZMicrophone.h; sourceTree = "<group>"; };
9417A9381871493900D9D37B /* EZMicrophone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZMicrophone.m; sourceTree = "<group>"; };
9417A9391871493900D9D37B /* EZOutput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZOutput.h; sourceTree = "<group>"; };
9417A93A1871493900D9D37B /* EZOutput.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZOutput.m; sourceTree = "<group>"; };
9417A93B1871493900D9D37B /* EZPlot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZPlot.h; sourceTree = "<group>"; };
9417A93C1871493900D9D37B /* EZPlot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZPlot.m; sourceTree = "<group>"; };
9417A93D1871493900D9D37B /* EZRecorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZRecorder.h; sourceTree = "<group>"; };
9417A93E1871493900D9D37B /* EZRecorder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZRecorder.m; sourceTree = "<group>"; };
9417A93F1871493900D9D37B /* TPCircularBuffer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = TPCircularBuffer.c; sourceTree = "<group>"; };
9417A9401871493900D9D37B /* TPCircularBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPCircularBuffer.h; sourceTree = "<group>"; };
9417A9421871493900D9D37B /* CHANGELOG */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CHANGELOG; sourceTree = "<group>"; };
9417A9431871493900D9D37B /* VERSION */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = VERSION; sourceTree = "<group>"; };
9417A951187149EA00D9D37B /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
9417A952187149EA00D9D37B /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; };
9417A953187149EA00D9D37B /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
9417A957187149F000D9D37B /* GLKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLKit.framework; path = System/Library/Frameworks/GLKit.framework; sourceTree = SDKROOT; };
9417A958187149F000D9D37B /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; };
9417A95B18714A1000D9D37B /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
9417A95D18714A2A00D9D37B /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; };
9417A9D31872130200D9D37B /* FFTViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FFTViewController.h; sourceTree = "<group>"; };
9417A9D41872130200D9D37B /* FFTViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FFTViewController.m; sourceTree = "<group>"; };
9417A9D51872130200D9D37B /* FFTViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = FFTViewController.xib; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
9417A8F01871492000D9D37B /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
9417A95E18714A2A00D9D37B /* Accelerate.framework in Frameworks */,
9417A95C18714A1000D9D37B /* QuartzCore.framework in Frameworks */,
9417A959187149F000D9D37B /* GLKit.framework in Frameworks */,
9417A95A187149F000D9D37B /* OpenGL.framework in Frameworks */,
9417A954187149EA00D9D37B /* AudioToolbox.framework in Frameworks */,
9417A955187149EA00D9D37B /* AudioUnit.framework in Frameworks */,
9417A956187149EA00D9D37B /* CoreAudio.framework in Frameworks */,
9417A8F71871492000D9D37B /* Cocoa.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
9417A9111871492100D9D37B /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
9417A9171871492100D9D37B /* Cocoa.framework in Frameworks */,
9417A9161871492100D9D37B /* XCTest.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
9417A8EA1871492000D9D37B = {
isa = PBXGroup;
children = (
9417A8FC1871492000D9D37B /* EZAudioFFTExample */,
9417A91A1871492100D9D37B /* EZAudioFFTExampleTests */,
9417A8F51871492000D9D37B /* Frameworks */,
9417A8F41871492000D9D37B /* Products */,
);
sourceTree = "<group>";
};
9417A8F41871492000D9D37B /* Products */ = {
isa = PBXGroup;
children = (
9417A8F31871492000D9D37B /* EZAudioFFTExample.app */,
9417A9141871492100D9D37B /* EZAudioFFTExampleTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
9417A8F51871492000D9D37B /* Frameworks */ = {
isa = PBXGroup;
children = (
9417A95D18714A2A00D9D37B /* Accelerate.framework */,
9417A95B18714A1000D9D37B /* QuartzCore.framework */,
9417A957187149F000D9D37B /* GLKit.framework */,
9417A958187149F000D9D37B /* OpenGL.framework */,
9417A951187149EA00D9D37B /* AudioToolbox.framework */,
9417A952187149EA00D9D37B /* AudioUnit.framework */,
9417A953187149EA00D9D37B /* CoreAudio.framework */,
9417A8F61871492000D9D37B /* Cocoa.framework */,
9417A9151871492100D9D37B /* XCTest.framework */,
9417A8F81871492000D9D37B /* Other Frameworks */,
);
name = Frameworks;
sourceTree = "<group>";
};
9417A8F81871492000D9D37B /* Other Frameworks */ = {
isa = PBXGroup;
children = (
9417A8F91871492000D9D37B /* AppKit.framework */,
9417A8FA1871492000D9D37B /* CoreData.framework */,
9417A8FB1871492000D9D37B /* Foundation.framework */,
);
name = "Other Frameworks";
sourceTree = "<group>";
};
9417A8FC1871492000D9D37B /* EZAudioFFTExample */ = {
isa = PBXGroup;
children = (
9417A92A1871493900D9D37B /* EZAudio */,
9417A9081871492100D9D37B /* AppDelegate.h */,
9417A9091871492100D9D37B /* AppDelegate.m */,
9417A9D31872130200D9D37B /* FFTViewController.h */,
9417A9D41872130200D9D37B /* FFTViewController.m */,
9417A9D51872130200D9D37B /* FFTViewController.xib */,
9417A90B1871492100D9D37B /* MainMenu.xib */,
9417A90E1871492100D9D37B /* Images.xcassets */,
9417A8FD1871492000D9D37B /* Supporting Files */,
);
path = EZAudioFFTExample;
sourceTree = "<group>";
};
9417A8FD1871492000D9D37B /* Supporting Files */ = {
isa = PBXGroup;
children = (
9417A8FE1871492000D9D37B /* EZAudioFFTExample-Info.plist */,
9417A8FF1871492000D9D37B /* InfoPlist.strings */,
9417A9021871492000D9D37B /* main.m */,
9417A9041871492000D9D37B /* EZAudioFFTExample-Prefix.pch */,
9417A9051871492100D9D37B /* Credits.rtf */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
9417A91A1871492100D9D37B /* EZAudioFFTExampleTests */ = {
isa = PBXGroup;
children = (
9417A9201871492100D9D37B /* EZAudioFFTExampleTests.m */,
9417A91B1871492100D9D37B /* Supporting Files */,
);
path = EZAudioFFTExampleTests;
sourceTree = "<group>";
};
9417A91B1871492100D9D37B /* Supporting Files */ = {
isa = PBXGroup;
children = (
9417A91C1871492100D9D37B /* EZAudioFFTExampleTests-Info.plist */,
9417A91D1871492100D9D37B /* InfoPlist.strings */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
9417A92A1871493900D9D37B /* EZAudio */ = {
isa = PBXGroup;
children = (
9417A92B1871493900D9D37B /* AEFloatConverter.h */,
9417A92C1871493900D9D37B /* AEFloatConverter.m */,
9417A92D1871493900D9D37B /* EZAudio.h */,
9417A92E1871493900D9D37B /* EZAudio.m */,
9417A92F1871493900D9D37B /* EZAudioFile.h */,
9417A9301871493900D9D37B /* EZAudioFile.m */,
9417A9311871493900D9D37B /* EZAudioPlot.h */,
9417A9321871493900D9D37B /* EZAudioPlot.m */,
9417A9331871493900D9D37B /* EZAudioPlotGL.h */,
9417A9341871493900D9D37B /* EZAudioPlotGL.m */,
9417A9351871493900D9D37B /* EZAudioPlotGLKViewController.h */,
9417A9361871493900D9D37B /* EZAudioPlotGLKViewController.m */,
9417A9371871493900D9D37B /* EZMicrophone.h */,
9417A9381871493900D9D37B /* EZMicrophone.m */,
9417A9391871493900D9D37B /* EZOutput.h */,
9417A93A1871493900D9D37B /* EZOutput.m */,
9417A93B1871493900D9D37B /* EZPlot.h */,
9417A93C1871493900D9D37B /* EZPlot.m */,
9417A93D1871493900D9D37B /* EZRecorder.h */,
9417A93E1871493900D9D37B /* EZRecorder.m */,
9417A93F1871493900D9D37B /* TPCircularBuffer.c */,
9417A9401871493900D9D37B /* TPCircularBuffer.h */,
9417A9411871493900D9D37B /* VERSION */,
);
name = EZAudio;
path = ../../../../EZAudio;
sourceTree = "<group>";
};
9417A9411871493900D9D37B /* VERSION */ = {
isa = PBXGroup;
children = (
9417A9421871493900D9D37B /* CHANGELOG */,
9417A9431871493900D9D37B /* VERSION */,
);
path = VERSION;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
9417A8F21871492000D9D37B /* EZAudioFFTExample */ = {
isa = PBXNativeTarget;
buildConfigurationList = 9417A9241871492100D9D37B /* Build configuration list for PBXNativeTarget "EZAudioFFTExample" */;
buildPhases = (
9417A8EF1871492000D9D37B /* Sources */,
9417A8F01871492000D9D37B /* Frameworks */,
9417A8F11871492000D9D37B /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = EZAudioFFTExample;
productName = EZAudioFFTExample;
productReference = 9417A8F31871492000D9D37B /* EZAudioFFTExample.app */;
productType = "com.apple.product-type.application";
};
9417A9131871492100D9D37B /* EZAudioFFTExampleTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 9417A9271871492100D9D37B /* Build configuration list for PBXNativeTarget "EZAudioFFTExampleTests" */;
buildPhases = (
9417A9101871492100D9D37B /* Sources */,
9417A9111871492100D9D37B /* Frameworks */,
9417A9121871492100D9D37B /* Resources */,
);
buildRules = (
);
dependencies = (
9417A9191871492100D9D37B /* PBXTargetDependency */,
);
name = EZAudioFFTExampleTests;
productName = EZAudioFFTExampleTests;
productReference = 9417A9141871492100D9D37B /* EZAudioFFTExampleTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
9417A8EB1871492000D9D37B /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0500;
ORGANIZATIONNAME = "Syed Haris Ali";
TargetAttributes = {
9417A9131871492100D9D37B = {
TestTargetID = 9417A8F21871492000D9D37B;
};
};
};
buildConfigurationList = 9417A8EE1871492000D9D37B /* Build configuration list for PBXProject "EZAudioFFTExample" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 9417A8EA1871492000D9D37B;
productRefGroup = 9417A8F41871492000D9D37B /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
9417A8F21871492000D9D37B /* EZAudioFFTExample */,
9417A9131871492100D9D37B /* EZAudioFFTExampleTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
9417A8F11871492000D9D37B /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9417A9011871492000D9D37B /* InfoPlist.strings in Resources */,
9417A9D71872130200D9D37B /* FFTViewController.xib in Resources */,
9417A90F1871492100D9D37B /* Images.xcassets in Resources */,
9417A9071871492100D9D37B /* Credits.rtf in Resources */,
9417A9501871493900D9D37B /* VERSION in Resources */,
9417A90D1871492100D9D37B /* MainMenu.xib in Resources */,
9417A94F1871493900D9D37B /* CHANGELOG in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
9417A9121871492100D9D37B /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9417A91F1871492100D9D37B /* InfoPlist.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
9417A8EF1871492000D9D37B /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9417A94D1871493900D9D37B /* EZRecorder.m in Sources */,
9417A9471871493900D9D37B /* EZAudioPlot.m in Sources */,
9417A94C1871493900D9D37B /* EZPlot.m in Sources */,
9417A9D61872130200D9D37B /* FFTViewController.m in Sources */,
9417A9441871493900D9D37B /* AEFloatConverter.m in Sources */,
9417A94A1871493900D9D37B /* EZMicrophone.m in Sources */,
9417A94B1871493900D9D37B /* EZOutput.m in Sources */,
9417A9451871493900D9D37B /* EZAudio.m in Sources */,
9417A94E1871493900D9D37B /* TPCircularBuffer.c in Sources */,
9417A9481871493900D9D37B /* EZAudioPlotGL.m in Sources */,
9417A9461871493900D9D37B /* EZAudioFile.m in Sources */,
9417A90A1871492100D9D37B /* AppDelegate.m in Sources */,
9417A9491871493900D9D37B /* EZAudioPlotGLKViewController.m in Sources */,
9417A9031871492000D9D37B /* main.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
9417A9101871492100D9D37B /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9417A9211871492100D9D37B /* EZAudioFFTExampleTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
9417A9191871492100D9D37B /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 9417A8F21871492000D9D37B /* EZAudioFFTExample */;
targetProxy = 9417A9181871492100D9D37B /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
9417A8FF1871492000D9D37B /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
9417A9001871492000D9D37B /* en */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
9417A9051871492100D9D37B /* Credits.rtf */ = {
isa = PBXVariantGroup;
children = (
9417A9061871492100D9D37B /* en */,
);
name = Credits.rtf;
sourceTree = "<group>";
};
9417A90B1871492100D9D37B /* MainMenu.xib */ = {
isa = PBXVariantGroup;
children = (
9417A90C1871492100D9D37B /* Base */,
);
name = MainMenu.xib;
sourceTree = "<group>";
};
9417A91D1871492100D9D37B /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
9417A91E1871492100D9D37B /* en */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
9417A9221871492100D9D37B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.9;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
};
name = Debug;
};
9417A9231871492100D9D37B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.9;
SDKROOT = macosx;
};
name = Release;
};
9417A9251871492100D9D37B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
COMBINE_HIDPI_IMAGES = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioFFTExample/EZAudioFFTExample-Prefix.pch";
INFOPLIST_FILE = "EZAudioFFTExample/EZAudioFFTExample-Info.plist";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
name = Debug;
};
9417A9261871492100D9D37B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
COMBINE_HIDPI_IMAGES = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioFFTExample/EZAudioFFTExample-Prefix.pch";
INFOPLIST_FILE = "EZAudioFFTExample/EZAudioFFTExample-Info.plist";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
name = Release;
};
9417A9281871492100D9D37B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/EZAudioFFTExample.app/Contents/MacOS/EZAudioFFTExample";
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(DEVELOPER_FRAMEWORKS_DIR)",
"$(inherited)",
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioFFTExample/EZAudioFFTExample-Prefix.pch";
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = "EZAudioFFTExampleTests/EZAudioFFTExampleTests-Info.plist";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
};
name = Debug;
};
9417A9291871492100D9D37B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/EZAudioFFTExample.app/Contents/MacOS/EZAudioFFTExample";
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(DEVELOPER_FRAMEWORKS_DIR)",
"$(inherited)",
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioFFTExample/EZAudioFFTExample-Prefix.pch";
INFOPLIST_FILE = "EZAudioFFTExampleTests/EZAudioFFTExampleTests-Info.plist";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
9417A8EE1871492000D9D37B /* Build configuration list for PBXProject "EZAudioFFTExample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
9417A9221871492100D9D37B /* Debug */,
9417A9231871492100D9D37B /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
9417A9241871492100D9D37B /* Build configuration list for PBXNativeTarget "EZAudioFFTExample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
9417A9251871492100D9D37B /* Debug */,
9417A9261871492100D9D37B /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
9417A9271871492100D9D37B /* Build configuration list for PBXNativeTarget "EZAudioFFTExampleTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
9417A9281871492100D9D37B /* Debug */,
9417A9291871492100D9D37B /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 9417A8EB1871492000D9D37B /* Project object */;
}
@@ -0,0 +1,39 @@
//
// AppDelegate.h
// EZAudioFFTExample
//
// Created by Syed Haris Ali on 12/30/13.
// Copyright (c) 2013 Syed Haris Ali. All rights reserved.
//
// 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 <Cocoa/Cocoa.h>
#import "FFTViewController.h"
@interface AppDelegate : NSObject <NSApplicationDelegate>
@property (assign) IBOutlet NSWindow *window;
/**
The FFTViewController
*/
@property (nonatomic,strong) FFTViewController *fftViewController;
@end
@@ -0,0 +1,43 @@
//
// AppDelegate.m
// EZAudioFFTExample
//
// Created by Syed Haris Ali on 12/30/13.
// Copyright (c) 2013 Syed Haris Ali. All rights reserved.
//
// 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 "AppDelegate.h"
@implementation AppDelegate
@synthesize fftViewController;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Swap in our view controller in the window's content view
self.fftViewController = [[FFTViewController alloc] init];
// Resize view controller to content view's current size
self.fftViewController.view.frame = [self.window.contentView frame];
// Add resizing flags to make the view controller resize with the window
self.fftViewController.view.autoresizingMask = (NSViewWidthSizable|NSViewHeightSizable);
// Add in the core graphics view controller as subview
[self.window.contentView addSubview:self.fftViewController.view];
}
@end
@@ -0,0 +1,679 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="4514" systemVersion="13A603" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="4514"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
<connections>
<outlet property="delegate" destination="494" id="495"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application"/>
<menu title="AMainMenu" systemMenu="main" id="29">
<items>
<menuItem title="EZAudioFFTExample" id="56">
<menu key="submenu" title="EZAudioFFTExample" systemMenu="apple" id="57">
<items>
<menuItem title="About EZAudioFFTExample" id="58">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="-2" id="142"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="236">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Preferences…" keyEquivalent="," id="129"/>
<menuItem isSeparatorItem="YES" id="143">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Services" id="131">
<menu key="submenu" title="Services" systemMenu="services" id="130"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="144">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Hide EZAudioFFTExample" keyEquivalent="h" id="134">
<connections>
<action selector="hide:" target="-1" id="367"/>
</connections>
</menuItem>
<menuItem title="Hide Others" keyEquivalent="h" id="145">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="hideOtherApplications:" target="-1" id="368"/>
</connections>
</menuItem>
<menuItem title="Show All" id="150">
<connections>
<action selector="unhideAllApplications:" target="-1" id="370"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="149">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Quit EZAudioFFTExample" keyEquivalent="q" id="136">
<connections>
<action selector="terminate:" target="-3" id="449"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="File" id="83">
<menu key="submenu" title="File" id="81">
<items>
<menuItem title="New" keyEquivalent="n" id="82">
<connections>
<action selector="newDocument:" target="-1" id="373"/>
</connections>
</menuItem>
<menuItem title="Open…" keyEquivalent="o" id="72">
<connections>
<action selector="openDocument:" target="-1" id="374"/>
</connections>
</menuItem>
<menuItem title="Open Recent" id="124">
<menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="125">
<items>
<menuItem title="Clear Menu" id="126">
<connections>
<action selector="clearRecentDocuments:" target="-1" id="127"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="79">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Close" keyEquivalent="w" id="73">
<connections>
<action selector="performClose:" target="-1" id="193"/>
</connections>
</menuItem>
<menuItem title="Save…" keyEquivalent="s" id="75">
<connections>
<action selector="saveDocument:" target="-1" id="362"/>
</connections>
</menuItem>
<menuItem title="Revert to Saved" id="112">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="revertDocumentToSaved:" target="-1" id="364"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="74">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Page Setup..." keyEquivalent="P" id="77">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="runPageLayout:" target="-1" id="87"/>
</connections>
</menuItem>
<menuItem title="Print…" keyEquivalent="p" id="78">
<connections>
<action selector="print:" target="-1" id="86"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Edit" id="217">
<menu key="submenu" title="Edit" id="205">
<items>
<menuItem title="Undo" keyEquivalent="z" id="207">
<connections>
<action selector="undo:" target="-1" id="223"/>
</connections>
</menuItem>
<menuItem title="Redo" keyEquivalent="Z" id="215">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="redo:" target="-1" id="231"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="206">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Cut" keyEquivalent="x" id="199">
<connections>
<action selector="cut:" target="-1" id="228"/>
</connections>
</menuItem>
<menuItem title="Copy" keyEquivalent="c" id="197">
<connections>
<action selector="copy:" target="-1" id="224"/>
</connections>
</menuItem>
<menuItem title="Paste" keyEquivalent="v" id="203">
<connections>
<action selector="paste:" target="-1" id="226"/>
</connections>
</menuItem>
<menuItem title="Paste and Match Style" keyEquivalent="V" id="485">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteAsPlainText:" target="-1" id="486"/>
</connections>
</menuItem>
<menuItem title="Delete" id="202">
<connections>
<action selector="delete:" target="-1" id="235"/>
</connections>
</menuItem>
<menuItem title="Select All" keyEquivalent="a" id="198">
<connections>
<action selector="selectAll:" target="-1" id="232"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="214">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Find" id="218">
<menu key="submenu" title="Find" id="220">
<items>
<menuItem title="Find…" tag="1" keyEquivalent="f" id="209">
<connections>
<action selector="performFindPanelAction:" target="-1" id="241"/>
</connections>
</menuItem>
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="534">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="performFindPanelAction:" target="-1" id="535"/>
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="208">
<connections>
<action selector="performFindPanelAction:" target="-1" id="487"/>
</connections>
</menuItem>
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="213">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="performFindPanelAction:" target="-1" id="488"/>
</connections>
</menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="221">
<connections>
<action selector="performFindPanelAction:" target="-1" id="489"/>
</connections>
</menuItem>
<menuItem title="Jump to Selection" keyEquivalent="j" id="210">
<connections>
<action selector="centerSelectionInVisibleArea:" target="-1" id="245"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Spelling and Grammar" id="216">
<menu key="submenu" title="Spelling and Grammar" id="200">
<items>
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="204">
<connections>
<action selector="showGuessPanel:" target="-1" id="230"/>
</connections>
</menuItem>
<menuItem title="Check Document Now" keyEquivalent=";" id="201">
<connections>
<action selector="checkSpelling:" target="-1" id="225"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="453"/>
<menuItem title="Check Spelling While Typing" id="219">
<connections>
<action selector="toggleContinuousSpellChecking:" target="-1" id="222"/>
</connections>
</menuItem>
<menuItem title="Check Grammar With Spelling" id="346">
<connections>
<action selector="toggleGrammarChecking:" target="-1" id="347"/>
</connections>
</menuItem>
<menuItem title="Correct Spelling Automatically" id="454">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticSpellingCorrection:" target="-1" id="456"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Substitutions" id="348">
<menu key="submenu" title="Substitutions" id="349">
<items>
<menuItem title="Show Substitutions" id="457">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontSubstitutionsPanel:" target="-1" id="458"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="459"/>
<menuItem title="Smart Copy/Paste" tag="1" keyEquivalent="f" id="350">
<connections>
<action selector="toggleSmartInsertDelete:" target="-1" id="355"/>
</connections>
</menuItem>
<menuItem title="Smart Quotes" tag="2" keyEquivalent="g" id="351">
<connections>
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="356"/>
</connections>
</menuItem>
<menuItem title="Smart Dashes" id="460">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDashSubstitution:" target="-1" id="461"/>
</connections>
</menuItem>
<menuItem title="Smart Links" tag="3" keyEquivalent="G" id="354">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="toggleAutomaticLinkDetection:" target="-1" id="357"/>
</connections>
</menuItem>
<menuItem title="Text Replacement" id="462">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticTextReplacement:" target="-1" id="463"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Transformations" id="450">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Transformations" id="451">
<items>
<menuItem title="Make Upper Case" id="452">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="uppercaseWord:" target="-1" id="464"/>
</connections>
</menuItem>
<menuItem title="Make Lower Case" id="465">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowercaseWord:" target="-1" id="468"/>
</connections>
</menuItem>
<menuItem title="Capitalize" id="466">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="capitalizeWord:" target="-1" id="467"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Speech" id="211">
<menu key="submenu" title="Speech" id="212">
<items>
<menuItem title="Start Speaking" id="196">
<connections>
<action selector="startSpeaking:" target="-1" id="233"/>
</connections>
</menuItem>
<menuItem title="Stop Speaking" id="195">
<connections>
<action selector="stopSpeaking:" target="-1" id="227"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Format" id="375">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Format" id="376">
<items>
<menuItem title="Font" id="377">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Font" systemMenu="font" id="388">
<items>
<menuItem title="Show Fonts" keyEquivalent="t" id="389">
<connections>
<action selector="orderFrontFontPanel:" target="420" id="424"/>
</connections>
</menuItem>
<menuItem title="Bold" tag="2" keyEquivalent="b" id="390">
<connections>
<action selector="addFontTrait:" target="420" id="421"/>
</connections>
</menuItem>
<menuItem title="Italic" tag="1" keyEquivalent="i" id="391">
<connections>
<action selector="addFontTrait:" target="420" id="422"/>
</connections>
</menuItem>
<menuItem title="Underline" keyEquivalent="u" id="392">
<connections>
<action selector="underline:" target="-1" id="432"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="393"/>
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="394">
<connections>
<action selector="modifyFont:" target="420" id="425"/>
</connections>
</menuItem>
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="395">
<connections>
<action selector="modifyFont:" target="420" id="423"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="396"/>
<menuItem title="Kern" id="397">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Kern" id="415">
<items>
<menuItem title="Use Default" id="416">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardKerning:" target="-1" id="438"/>
</connections>
</menuItem>
<menuItem title="Use None" id="417">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffKerning:" target="-1" id="441"/>
</connections>
</menuItem>
<menuItem title="Tighten" id="418">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="tightenKerning:" target="-1" id="431"/>
</connections>
</menuItem>
<menuItem title="Loosen" id="419">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="loosenKerning:" target="-1" id="435"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Ligatures" id="398">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Ligatures" id="411">
<items>
<menuItem title="Use Default" id="412">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardLigatures:" target="-1" id="439"/>
</connections>
</menuItem>
<menuItem title="Use None" id="413">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffLigatures:" target="-1" id="440"/>
</connections>
</menuItem>
<menuItem title="Use All" id="414">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useAllLigatures:" target="-1" id="434"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Baseline" id="399">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Baseline" id="405">
<items>
<menuItem title="Use Default" id="406">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unscript:" target="-1" id="437"/>
</connections>
</menuItem>
<menuItem title="Superscript" id="407">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="superscript:" target="-1" id="430"/>
</connections>
</menuItem>
<menuItem title="Subscript" id="408">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="subscript:" target="-1" id="429"/>
</connections>
</menuItem>
<menuItem title="Raise" id="409">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="raiseBaseline:" target="-1" id="426"/>
</connections>
</menuItem>
<menuItem title="Lower" id="410">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowerBaseline:" target="-1" id="427"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="400"/>
<menuItem title="Show Colors" keyEquivalent="C" id="401">
<connections>
<action selector="orderFrontColorPanel:" target="-1" id="433"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="402"/>
<menuItem title="Copy Style" keyEquivalent="c" id="403">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="copyFont:" target="-1" id="428"/>
</connections>
</menuItem>
<menuItem title="Paste Style" keyEquivalent="v" id="404">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteFont:" target="-1" id="436"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Text" id="496">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Text" id="497">
<items>
<menuItem title="Align Left" keyEquivalent="{" id="498">
<connections>
<action selector="alignLeft:" target="-1" id="524"/>
</connections>
</menuItem>
<menuItem title="Center" keyEquivalent="|" id="499">
<connections>
<action selector="alignCenter:" target="-1" id="518"/>
</connections>
</menuItem>
<menuItem title="Justify" id="500">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="alignJustified:" target="-1" id="523"/>
</connections>
</menuItem>
<menuItem title="Align Right" keyEquivalent="}" id="501">
<connections>
<action selector="alignRight:" target="-1" id="521"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="502"/>
<menuItem title="Writing Direction" id="503">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Writing Direction" id="508">
<items>
<menuItem title="Paragraph" enabled="NO" id="509">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="510">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionNatural:" target="-1" id="525"/>
</connections>
</menuItem>
<menuItem id="511">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionLeftToRight:" target="-1" id="526"/>
</connections>
</menuItem>
<menuItem id="512">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionRightToLeft:" target="-1" id="527"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="513"/>
<menuItem title="Selection" enabled="NO" id="514">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="515">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionNatural:" target="-1" id="528"/>
</connections>
</menuItem>
<menuItem id="516">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionLeftToRight:" target="-1" id="529"/>
</connections>
</menuItem>
<menuItem id="517">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionRightToLeft:" target="-1" id="530"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="504"/>
<menuItem title="Show Ruler" id="505">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleRuler:" target="-1" id="520"/>
</connections>
</menuItem>
<menuItem title="Copy Ruler" keyEquivalent="c" id="506">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="copyRuler:" target="-1" id="522"/>
</connections>
</menuItem>
<menuItem title="Paste Ruler" keyEquivalent="v" id="507">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="pasteRuler:" target="-1" id="519"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="View" id="295">
<menu key="submenu" title="View" id="296">
<items>
<menuItem title="Show Toolbar" keyEquivalent="t" id="297">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="toggleToolbarShown:" target="-1" id="366"/>
</connections>
</menuItem>
<menuItem title="Customize Toolbar…" id="298">
<connections>
<action selector="runToolbarCustomizationPalette:" target="-1" id="365"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Window" id="19">
<menu key="submenu" title="Window" systemMenu="window" id="24">
<items>
<menuItem title="Minimize" keyEquivalent="m" id="23">
<connections>
<action selector="performMiniaturize:" target="-1" id="37"/>
</connections>
</menuItem>
<menuItem title="Zoom" id="239">
<connections>
<action selector="performZoom:" target="-1" id="240"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="92">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Bring All to Front" id="5">
<connections>
<action selector="arrangeInFront:" target="-1" id="39"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Help" id="490">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="491">
<items>
<menuItem title="EZAudioFFTExample Help" keyEquivalent="?" id="492">
<connections>
<action selector="showHelp:" target="-1" id="493"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
<window title="EZAudioFFTExample" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="371">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="335" y="390" width="480" height="360"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="878"/>
<view key="contentView" id="372">
<rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="PCv-Jm-jzO" customClass="EZAudioPlot">
<rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</customView>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="PCv-Jm-jzO" secondAttribute="trailing" id="A2i-gs-EWe"/>
<constraint firstItem="PCv-Jm-jzO" firstAttribute="leading" secondItem="372" secondAttribute="leading" id="Gex-TR-aqy"/>
<constraint firstAttribute="bottom" secondItem="PCv-Jm-jzO" secondAttribute="bottom" id="Kqa-V4-EDP"/>
<constraint firstItem="PCv-Jm-jzO" firstAttribute="top" secondItem="372" secondAttribute="top" id="MeC-7d-nwe"/>
</constraints>
</view>
</window>
<customObject id="494" customClass="AppDelegate">
<connections>
<outlet property="audioPlot" destination="PCv-Jm-jzO" id="aGl-Mf-G6W"/>
<outlet property="window" destination="371" id="532"/>
</connections>
</customObject>
<customObject id="420" customClass="NSFontManager"/>
</objects>
</document>
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>com.sha.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2013 Syed Haris Ali. All rights reserved.</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>
@@ -0,0 +1,9 @@
//
// Prefix header
//
// The contents of this file are implicitly included at the beginning of every source file.
//
#ifdef __OBJC__
#import <Cocoa/Cocoa.h>
#endif
@@ -0,0 +1,59 @@
//
// FFTViewController.h
// EZAudioFFTExample
//
// Created by Syed Haris Ali on 12/30/13.
// Copyright (c) 2013 Syed Haris Ali. All rights reserved.
//
// 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 <Cocoa/Cocoa.h>
/**
EZAudio
*/
#import "EZAudio.h"
/**
Accelerate
*/
#import <Accelerate/Accelerate.h>
/**
The FFTViewController demonstrates how to use the Accelerate framework to calculate the real-time FFT of audio data provided by an EZAudioMicrophone.
*/
@interface FFTViewController : NSViewController <EZMicrophoneDelegate>
#pragma mark - Components
/**
EZAudioPlot for frequency plot
*/
@property (nonatomic,weak) IBOutlet EZAudioPlot *audioPlotFreq;
/**
EZAudioPlot for time plot
*/
@property (nonatomic,weak) IBOutlet EZAudioPlotGL *audioPlotTime;
/**
Microphone
*/
@property (nonatomic,strong) EZMicrophone *microphone;
@end
@@ -0,0 +1,176 @@
//
// FFTViewController.m
// EZAudioFFTExample
//
// Created by Syed Haris Ali on 12/30/13.
// Copyright (c) 2013 Syed Haris Ali. All rights reserved.
//
// 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 "FFTViewController.h"
@interface FFTViewController (){
COMPLEX_SPLIT _A;
FFTSetup _FFTSetup;
BOOL _isFFTSetup;
vDSP_Length _log2n;
}
@end
@implementation FFTViewController
@synthesize audioPlotFreq;
@synthesize audioPlotTime;
@synthesize microphone;
#pragma mark - Initialization
-(id)init {
self = [super initWithNibName:NSStringFromClass(self.class) bundle:nil];
if(self){
[self initializeViewController];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithNibName:NSStringFromClass(self.class) bundle:nil];
if(self){
[self initializeViewController];
}
return self;
}
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:NSStringFromClass(self.class) bundle:nil];
if(self){
[self initializeViewController];
}
return self;
}
#pragma mark - Initialize View Controller
-(void)initializeViewController {
// Create an instance of the microphone and tell it to use this view controller instance as the delegate
self.microphone = [EZMicrophone microphoneWithDelegate:self
startsImmediately:YES];
}
#pragma mark - Customize the Audio Plot
-(void)awakeFromNib {
// Setup time domain audio plot
self.audioPlotTime.backgroundColor = [NSColor colorWithCalibratedRed: 0.569 green: 0.82 blue: 0.478 alpha: 1];
self.audioPlotTime.color = [NSColor colorWithCalibratedRed:1.0 green:1.0 blue:1.0 alpha:1.0];
self.audioPlotTime.shouldFill = YES;
self.audioPlotTime.shouldMirror = YES;
self.audioPlotTime.plotType = EZPlotTypeRolling;
// Setup frequency domain audio plot
self.audioPlotFreq.backgroundColor = [NSColor colorWithCalibratedRed: 0.984 green: 0.471 blue: 0.525 alpha: 1];
self.audioPlotFreq.color = [NSColor colorWithCalibratedRed:1.0 green:1.0 blue:1.0 alpha:1.0];
self.audioPlotFreq.shouldFill = YES;
self.audioPlotFreq.plotType = EZPlotTypeBuffer;
}
#pragma mark - FFT
/**
Adapted from http://batmobile.blogs.ilrt.org/fourier-transforms-on-an-iphone/
*/
-(void)createFFTWithBufferSize:(float)bufferSize withAudioData:(float*)data {
// Setup the length
_log2n = log2f(bufferSize);
// Calculate the weights array. This is a one-off operation.
_FFTSetup = vDSP_create_fftsetup(_log2n, FFT_RADIX2);
// For an FFT, numSamples must be a power of 2, i.e. is always even
int nOver2 = bufferSize/2;
// Populate *window with the values for a hamming window function
float *window = (float *)malloc(sizeof(float)*bufferSize);
vDSP_hamm_window(window, bufferSize, 0);
// Window the samples
vDSP_vmul(data, 1, window, 1, data, 1, bufferSize);
// Define complex buffer
_A.realp = (float *) malloc(nOver2*sizeof(float));
_A.imagp = (float *) malloc(nOver2*sizeof(float));
}
-(void)updateFFTWithBufferSize:(float)bufferSize withAudioData:(float*)data {
// For an FFT, numSamples must be a power of 2, i.e. is always even
int nOver2 = bufferSize/2;
// Pack samples:
// C(re) -> A[n], C(im) -> A[n+1]
vDSP_ctoz((COMPLEX*)data, 2, &_A, 1, nOver2);
// Perform a forward FFT using fftSetup and A
// Results are returned in A
vDSP_fft_zrip(_FFTSetup, &_A, 1, _log2n, FFT_FORWARD);
// Convert COMPLEX_SPLIT A result to magnitudes
float amp[nOver2];
float maxMag = 0;
for(int i=0; i<nOver2; i++) {
// Calculate the magnitude
float mag = _A.realp[i]*_A.realp[i]+_A.imagp[i]*_A.imagp[i];
maxMag = mag > maxMag ? mag : maxMag;
}
for(int i=0; i<nOver2; i++) {
// Calculate the magnitude
float mag = _A.realp[i]*_A.realp[i]+_A.imagp[i]*_A.imagp[i];
// Bind the value to be less than 1.0 to fit in the graph
amp[i] = [EZAudio MAP:mag leftMin:0.0 leftMax:maxMag rightMin:0.0 rightMax:1.0];
}
// Update the frequency domain plot
[self.audioPlotFreq updateBuffer:amp
withBufferSize:nOver2];
}
#pragma mark - EZMicrophoneDelegate
-(void) microphone:(EZMicrophone *)microphone
hasAudioReceived:(float **)buffer
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels {
dispatch_async(dispatch_get_main_queue(), ^{
// Update time domain plot
[self.audioPlotTime updateBuffer:buffer[0]
withBufferSize:bufferSize];
// Setup the FFT if it's not already setup
if( !_isFFTSetup ){
[self createFFTWithBufferSize:bufferSize withAudioData:buffer[0]];
_isFFTSetup = YES;
}
// Get the FFT data
[self updateFFTWithBufferSize:bufferSize withAudioData:buffer[0]];
});
}
@end
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="4514" systemVersion="13A603" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment version="1070" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="4514"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="FFTViewController">
<connections>
<outlet property="audioPlotFreq" destination="4V2-1I-w64" id="SkA-7R-4V8"/>
<outlet property="audioPlotTime" destination="Zcc-CT-67u" id="lE0-d7-KUs"/>
<outlet property="view" destination="1" id="2"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application"/>
<customView id="1">
<rect key="frame" x="0.0" y="0.0" width="480" height="272"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<splitView dividerStyle="thin" translatesAutoresizingMaskIntoConstraints="NO" id="78b-rz-Bpl">
<rect key="frame" x="0.0" y="0.0" width="480" height="272"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<customView fixedFrame="YES" id="Zcc-CT-67u" customClass="EZAudioPlotGL">
<rect key="frame" x="0.0" y="0.0" width="480" height="132"/>
<autoresizingMask key="autoresizingMask"/>
</customView>
<customView fixedFrame="YES" id="4V2-1I-w64" customClass="EZAudioPlot">
<rect key="frame" x="0.0" y="133" width="480" height="139"/>
<autoresizingMask key="autoresizingMask"/>
</customView>
</subviews>
<holdingPriorities>
<real value="250"/>
<real value="250"/>
</holdingPriorities>
</splitView>
</subviews>
<constraints>
<constraint firstAttribute="bottom" secondItem="78b-rz-Bpl" secondAttribute="bottom" id="CZL-aU-xpy"/>
<constraint firstAttribute="trailing" secondItem="78b-rz-Bpl" secondAttribute="trailing" id="eXt-EL-upz"/>
<constraint firstItem="78b-rz-Bpl" firstAttribute="top" secondItem="1" secondAttribute="top" id="lFP-vc-nHC"/>
<constraint firstItem="78b-rz-Bpl" firstAttribute="leading" secondItem="1" secondAttribute="leading" id="lWl-1T-nHB"/>
</constraints>
</customView>
</objects>
</document>
@@ -0,0 +1,58 @@
{
"images" : [
{
"idiom" : "mac",
"size" : "16x16",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "16x16",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "32x32",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "32x32",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "128x128",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "128x128",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "256x256",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "256x256",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "512x512",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "512x512",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -0,0 +1,29 @@
{\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;}
{\colortbl;\red255\green255\blue255;}
\paperw9840\paperh8400
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural
\f0\b\fs24 \cf0 Engineering:
\b0 \
Some people\
\
\b Human Interface Design:
\b0 \
Some other people\
\
\b Testing:
\b0 \
Hopefully not nobody\
\
\b Documentation:
\b0 \
Whoever\
\
\b With special thanks to:
\b0 \
Mom\
}
@@ -0,0 +1,2 @@
/* Localized versions of Info.plist keys */
@@ -0,0 +1,14 @@
//
// main.m
// EZAudioFFTExample
//
// Created by Syed Haris Ali on 12/29/13.
// Copyright (c) 2013 Syed Haris Ali. All rights reserved.
//
#import <Cocoa/Cocoa.h>
int main(int argc, const char * argv[])
{
return NSApplicationMain(argc, argv);
}
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>com.sha.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
@@ -0,0 +1,34 @@
//
// EZAudioFFTExampleTests.m
// EZAudioFFTExampleTests
//
// Created by Syed Haris Ali on 12/29/13.
// Copyright (c) 2013 Syed Haris Ali. All rights reserved.
//
#import <XCTest/XCTest.h>
@interface EZAudioFFTExampleTests : XCTestCase
@end
@implementation EZAudioFFTExampleTests
- (void)setUp
{
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown
{
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testExample
{
XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__);
}
@end
@@ -0,0 +1,2 @@
/* Localized versions of Info.plist keys */
@@ -30,7 +30,6 @@
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Swap in our view controller in the window's content view
self.openGLWaveformViewController = [[OpenGLWaveformViewController alloc] init];
// Resize view controller to content view's current size
@@ -151,7 +151,7 @@ withNumberOfChannels:(UInt32)numberOfChannels {
-(void)microphone:(EZMicrophone *)microphone hasAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription {
// The AudioStreamBasicDescription of the microphone stream. This is useful when configuring the EZRecorder or telling another component what audio format type to expect.
// Here's a print function to allow you to inspect it a little easier
[EZAudio printASBD:audioStreamBasicDescription];
// [EZAudio printASBD:audioStreamBasicDescription];
}
-(void)microphone:(EZMicrophone *)microphone
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="4514" systemVersion="13A603" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6185.11" systemVersion="13E28" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment version="1070" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="4514"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6185.11"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="OpenGLWaveformViewController">
@@ -12,18 +12,16 @@
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="Zy4-iU-8jm">
<rect key="frame" x="0.0" y="0.0" width="480" height="272"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="sjT-Ri-IOJ" customClass="EZAudioPlotGL">
<rect key="frame" x="0.0" y="42" width="480" height="230"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</customView>
<button translatesAutoresizingMaskIntoConstraints="NO" id="0kM-1N-88d">
<rect key="frame" x="18" y="14" width="119" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<constraints>
<constraint firstAttribute="height" constant="14" id="Da9-ZF-OaJ"/>
</constraints>
@@ -37,11 +35,10 @@
</button>
<segmentedControl verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="M6h-53-a8o">
<rect key="frame" x="333" y="11" width="129" height="24"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<constraints>
<constraint firstAttribute="height" constant="21" id="a5H-w4-lUa"/>
</constraints>
<segmentedCell key="cell" alignment="left" style="rounded" trackingMode="selectOne" id="NWl-cZ-1Qx">
<segmentedCell key="cell" borderStyle="border" alignment="left" style="rounded" trackingMode="selectOne" id="NWl-cZ-1Qx">
<font key="font" metaFont="system"/>
<segments>
<segment label="Buffer" selected="YES"/>
@@ -66,4 +63,4 @@
</constraints>
</customView>
</objects>
</document>
</document>
@@ -86,7 +86,7 @@
941D71C31864C457007D52D8 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
941D71C51864C457007D52D8 /* EZAudioPassThroughExample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "EZAudioPassThroughExample-Prefix.pch"; sourceTree = "<group>"; };
941D71C71864C457007D52D8 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = "<group>"; };
941D71C91864C457007D52D8 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
941D71C91864C457007D52D8 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = EZAudioPassThroughExample/AppDelegate.h; sourceTree = SOURCE_ROOT; };
941D71CA1864C457007D52D8 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
941D71CD1864C457007D52D8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
941D71CF1864C457007D52D8 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
@@ -5,6 +5,23 @@
// Created by Syed Haris Ali on 12/20/13.
// Copyright (c) 2013 Syed Haris Ali. All rights reserved.
//
// 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 <Cocoa/Cocoa.h>
@@ -5,6 +5,23 @@
// Created by Syed Haris Ali on 12/20/13.
// Copyright (c) 2013 Syed Haris Ali. All rights reserved.
//
// 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 "AppDelegate.h"
@@ -5,6 +5,23 @@
// Created by Syed Haris Ali on 12/20/13.
// Copyright (c) 2013 Syed Haris Ali. All rights reserved.
//
// 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 <Cocoa/Cocoa.h>
@@ -21,6 +38,11 @@
*/
@property (nonatomic,weak) IBOutlet EZAudioPlotGL *audioPlot;
/**
The Microphone
*/
@property (nonatomic,strong) EZMicrophone *microphone;
#pragma mark - Actions
/**
Switches the plot drawing type between a buffer plot (visualizes the current stream of audio data from the update function) or a rolling plot (visualizes the audio data over time, this is the classic waveform look)
@@ -5,6 +5,23 @@
// Created by Syed Haris Ali on 12/20/13.
// Copyright (c) 2013 Syed Haris Ali. All rights reserved.
//
// 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 "PassThroughViewController.h"
@@ -14,6 +31,8 @@
@end
@implementation PassThroughViewController
@synthesize audioPlot;
@synthesize microphone;
#pragma mark - Initialization
-(id)init {
@@ -44,8 +63,7 @@
/**
Initialize the circular buffer
*/
[EZAudio circularBuffer:&_circularBuffer
withSize:1024];
[EZAudio circularBuffer:&_circularBuffer withSize:2048];
}
#pragma mark - Customize the Audio Plot
@@ -70,6 +88,7 @@
/**
Start the output
*/
[[EZOutput sharedOutput] setAudioStreamBasicDescription:[EZMicrophone sharedMicrophone].audioStreamBasicDescription];
[EZOutput sharedOutput].outputDataSource = self;
[[EZOutput sharedOutput] startPlayback];
Binary file not shown.
@@ -7,6 +7,9 @@
objects = {
/* Begin PBXBuildFile section */
668E4F821A905DAF00F4B814 /* EZAudioConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 668E4F811A905DAF00F4B814 /* EZAudioConverter.m */; };
668E4F851A905F8700F4B814 /* EZAudioWaveformData.m in Sources */ = {isa = PBXBuildFile; fileRef = 668E4F841A905F8700F4B814 /* EZAudioWaveformData.m */; };
668E4F881A90607500F4B814 /* EZMicrophone.m in Sources */ = {isa = PBXBuildFile; fileRef = 668E4F871A90607500F4B814 /* EZMicrophone.m */; };
94056EFB185BD83400EB94BA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94056EFA185BD83400EB94BA /* Cocoa.framework */; };
94056F05185BD83400EB94BA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 94056F03185BD83400EB94BA /* InfoPlist.strings */; };
94056F07185BD83400EB94BA /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 94056F06185BD83400EB94BA /* main.m */; };
@@ -27,13 +30,11 @@
94056F66185BDB4700EB94BA /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94056F63185BDB4700EB94BA /* AudioUnit.framework */; };
94056F67185BDB4700EB94BA /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94056F64185BDB4700EB94BA /* CoreAudio.framework */; };
9417A6D51865928C00D9D37B /* simple-drum-beat.wav in Resources */ = {isa = PBXBuildFile; fileRef = 9417A6D41865928C00D9D37B /* simple-drum-beat.wav */; };
9417A73E1867DD3400D9D37B /* AEFloatConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7261867DD3400D9D37B /* AEFloatConverter.m */; };
9417A73F1867DD3400D9D37B /* EZAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7281867DD3400D9D37B /* EZAudio.m */; };
9417A7401867DD3400D9D37B /* EZAudioFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A72A1867DD3400D9D37B /* EZAudioFile.m */; };
9417A7411867DD3400D9D37B /* EZAudioPlot.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A72C1867DD3400D9D37B /* EZAudioPlot.m */; };
9417A7421867DD3400D9D37B /* EZAudioPlotGL.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A72E1867DD3400D9D37B /* EZAudioPlotGL.m */; };
9417A7431867DD3400D9D37B /* EZAudioPlotGLKViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7301867DD3400D9D37B /* EZAudioPlotGLKViewController.m */; };
9417A7441867DD3400D9D37B /* EZMicrophone.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7321867DD3400D9D37B /* EZMicrophone.m */; };
9417A7451867DD3400D9D37B /* EZOutput.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7341867DD3400D9D37B /* EZOutput.m */; };
9417A7461867DD3400D9D37B /* EZPlot.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7361867DD3400D9D37B /* EZPlot.m */; };
9417A7471867DD3400D9D37B /* EZRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7381867DD3400D9D37B /* EZRecorder.m */; };
@@ -53,6 +54,12 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
668E4F801A905DAF00F4B814 /* EZAudioConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioConverter.h; sourceTree = "<group>"; };
668E4F811A905DAF00F4B814 /* EZAudioConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioConverter.m; sourceTree = "<group>"; };
668E4F831A905F8700F4B814 /* EZAudioWaveformData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioWaveformData.h; sourceTree = "<group>"; };
668E4F841A905F8700F4B814 /* EZAudioWaveformData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioWaveformData.m; sourceTree = "<group>"; };
668E4F861A90607500F4B814 /* EZMicrophone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZMicrophone.h; sourceTree = "<group>"; };
668E4F871A90607500F4B814 /* EZMicrophone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZMicrophone.m; sourceTree = "<group>"; };
94056EF7185BD83400EB94BA /* EZAudioPlayFileExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = EZAudioPlayFileExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
94056EFA185BD83400EB94BA /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
94056EFD185BD83400EB94BA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
@@ -82,8 +89,6 @@
94056F63185BDB4700EB94BA /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; };
94056F64185BDB4700EB94BA /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
9417A6D41865928C00D9D37B /* simple-drum-beat.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = "simple-drum-beat.wav"; path = "../../../simple-drum-beat.wav"; sourceTree = "<group>"; };
9417A7251867DD3400D9D37B /* AEFloatConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEFloatConverter.h; sourceTree = "<group>"; };
9417A7261867DD3400D9D37B /* AEFloatConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AEFloatConverter.m; sourceTree = "<group>"; };
9417A7271867DD3400D9D37B /* EZAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudio.h; sourceTree = "<group>"; };
9417A7281867DD3400D9D37B /* EZAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudio.m; sourceTree = "<group>"; };
9417A7291867DD3400D9D37B /* EZAudioFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioFile.h; sourceTree = "<group>"; };
@@ -94,8 +99,6 @@
9417A72E1867DD3400D9D37B /* EZAudioPlotGL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlotGL.m; sourceTree = "<group>"; };
9417A72F1867DD3400D9D37B /* EZAudioPlotGLKViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlotGLKViewController.h; sourceTree = "<group>"; };
9417A7301867DD3400D9D37B /* EZAudioPlotGLKViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlotGLKViewController.m; sourceTree = "<group>"; };
9417A7311867DD3400D9D37B /* EZMicrophone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZMicrophone.h; sourceTree = "<group>"; };
9417A7321867DD3400D9D37B /* EZMicrophone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZMicrophone.m; sourceTree = "<group>"; };
9417A7331867DD3400D9D37B /* EZOutput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZOutput.h; sourceTree = "<group>"; };
9417A7341867DD3400D9D37B /* EZOutput.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZOutput.m; sourceTree = "<group>"; };
9417A7351867DD3400D9D37B /* EZPlot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZPlot.h; sourceTree = "<group>"; };
@@ -188,8 +191,8 @@
94056F0D185BD83400EB94BA /* AppDelegate.m */,
94056F2E185BD86D00EB94BA /* PlayFileViewController.h */,
94056F2F185BD86D00EB94BA /* PlayFileViewController.m */,
9417A6D41865928C00D9D37B /* simple-drum-beat.wav */,
94056F30185BD86D00EB94BA /* PlayFileViewController.xib */,
9417A6D41865928C00D9D37B /* simple-drum-beat.wav */,
94056F0F185BD83400EB94BA /* MainMenu.xib */,
94056F12185BD83400EB94BA /* Images.xcassets */,
94056F01185BD83400EB94BA /* Supporting Files */,
@@ -230,10 +233,12 @@
9417A7241867DD3400D9D37B /* EZAudio */ = {
isa = PBXGroup;
children = (
9417A7251867DD3400D9D37B /* AEFloatConverter.h */,
9417A7261867DD3400D9D37B /* AEFloatConverter.m */,
9417A7271867DD3400D9D37B /* EZAudio.h */,
9417A7281867DD3400D9D37B /* EZAudio.m */,
668E4F801A905DAF00F4B814 /* EZAudioConverter.h */,
668E4F811A905DAF00F4B814 /* EZAudioConverter.m */,
668E4F831A905F8700F4B814 /* EZAudioWaveformData.h */,
668E4F841A905F8700F4B814 /* EZAudioWaveformData.m */,
9417A7291867DD3400D9D37B /* EZAudioFile.h */,
9417A72A1867DD3400D9D37B /* EZAudioFile.m */,
9417A72B1867DD3400D9D37B /* EZAudioPlot.h */,
@@ -242,8 +247,8 @@
9417A72E1867DD3400D9D37B /* EZAudioPlotGL.m */,
9417A72F1867DD3400D9D37B /* EZAudioPlotGLKViewController.h */,
9417A7301867DD3400D9D37B /* EZAudioPlotGLKViewController.m */,
9417A7311867DD3400D9D37B /* EZMicrophone.h */,
9417A7321867DD3400D9D37B /* EZMicrophone.m */,
668E4F861A90607500F4B814 /* EZMicrophone.h */,
668E4F871A90607500F4B814 /* EZMicrophone.m */,
9417A7331867DD3400D9D37B /* EZOutput.h */,
9417A7341867DD3400D9D37B /* EZOutput.m */,
9417A7351867DD3400D9D37B /* EZPlot.h */,
@@ -371,16 +376,17 @@
files = (
9417A7481867DD3400D9D37B /* TPCircularBuffer.c in Sources */,
9417A7451867DD3400D9D37B /* EZOutput.m in Sources */,
668E4F821A905DAF00F4B814 /* EZAudioConverter.m in Sources */,
9417A7431867DD3400D9D37B /* EZAudioPlotGLKViewController.m in Sources */,
94056F31185BD86D00EB94BA /* PlayFileViewController.m in Sources */,
9417A73E1867DD3400D9D37B /* AEFloatConverter.m in Sources */,
94056F0E185BD83400EB94BA /* AppDelegate.m in Sources */,
9417A7411867DD3400D9D37B /* EZAudioPlot.m in Sources */,
9417A7401867DD3400D9D37B /* EZAudioFile.m in Sources */,
668E4F881A90607500F4B814 /* EZMicrophone.m in Sources */,
668E4F851A905F8700F4B814 /* EZAudioWaveformData.m in Sources */,
9417A7421867DD3400D9D37B /* EZAudioPlotGL.m in Sources */,
94056F07185BD83400EB94BA /* main.m in Sources */,
9417A7461867DD3400D9D37B /* EZPlot.m in Sources */,
9417A7441867DD3400D9D37B /* EZMicrophone.m in Sources */,
9417A7471867DD3400D9D37B /* EZRecorder.m in Sources */,
9417A73F1867DD3400D9D37B /* EZAudio.m in Sources */,
);
@@ -27,6 +27,9 @@
// Import EZAudio header
#import "EZAudio.h"
#import "EZAudioFile.h"
#import "EZOutput.h"
#import "EZAudioPlot.h"
/**
Here's the default audio file included with the example
@@ -47,7 +50,8 @@
/**
The CoreGraphics based audio plot
*/
@property (nonatomic,weak) IBOutlet EZAudioPlotGL *audioPlot;
@property (nonatomic,weak) IBOutlet EZAudioPlot *audioPlotLeft;
@property (nonatomic,weak) IBOutlet EZAudioPlot *audioPlotRight;
#pragma mark - UI Extras
/**
@@ -60,12 +64,22 @@
*/
@property (nonatomic,weak) IBOutlet NSSlider *framePositionSlider;
/**
A slider to adjust the sample rate.
*/
@property (nonatomic,weak) IBOutlet NSSlider *sampleRateSlider;
/**
A BOOL indicating whether or not we've reached the end of the file
*/
@property (nonatomic,assign) BOOL eof;
#pragma mark - Actions
/**
Changes the sampling frequency on the output unit
*/
-(IBAction)changeOutputSamplingFrequency:(id)sender;
/**
Switches the plot drawing type between a buffer plot (visualizes the current stream of audio data from the update function) or a rolling plot (visualizes the audio data over time, this is the classic waveform look)
*/
@@ -32,7 +32,6 @@
@implementation PlayFileViewController
@synthesize audioFile;
@synthesize audioPlot;
@synthesize eof = _eof;
@synthesize framePositionSlider;
@@ -72,15 +71,20 @@
Customizing the audio plot's look
*/
// Background color
self.audioPlot.backgroundColor = [NSColor colorWithCalibratedRed: 0.816 green: 0.349 blue: 0.255 alpha: 1];
self.audioPlotLeft.backgroundColor = [NSColor colorWithCalibratedRed:0.1 green:0.3 blue:0.2 alpha:1.0];
self.audioPlotRight.backgroundColor = [NSColor colorWithCalibratedRed:0.1 green:0.3 blue:0.2 alpha:1.0];
// Waveform color
self.audioPlot.color = [NSColor colorWithCalibratedRed: 1.000 green: 1.000 blue: 1.000 alpha: 1];
self.audioPlotLeft.color = [NSColor colorWithCalibratedRed: 1.000 green: 1.000 blue: 1.000 alpha: 1];
self.audioPlotRight.color = [NSColor colorWithCalibratedRed: 1.000 green: 1.000 blue: 1.000 alpha: 1];
// Plot type
self.audioPlot.plotType = EZPlotTypeBuffer;
self.audioPlotLeft.plotType = EZPlotTypeBuffer;
self.audioPlotRight.plotType = EZPlotTypeBuffer;
// Fill
self.audioPlot.shouldFill = YES;
self.audioPlotLeft.shouldFill = YES;
self.audioPlotRight.shouldFill = YES;
// Mirror
self.audioPlot.shouldMirror = YES;
self.audioPlotLeft.shouldMirror = YES;
self.audioPlotRight.shouldMirror = YES;
/*
Try opening the sample file
@@ -104,6 +108,14 @@
}
}
-(void)changeOutputSamplingFrequency:(id)sender
{
AudioStreamBasicDescription asbd = [EZOutput sharedOutput].audioStreamBasicDescription;
float samplingFrequency = ((NSSlider *)sender).floatValue;
asbd.mSampleRate = samplingFrequency;
[[EZOutput sharedOutput] setAudioStreamBasicDescription:asbd];
}
-(void)openFile:(id)sender {
NSOpenPanel* openDlg = [NSOpenPanel openPanel];
openDlg.canChooseFiles = YES;
@@ -111,6 +123,7 @@
openDlg.delegate = self;
if( [openDlg runModal] == NSOKButton ){
NSArray *selectedFiles = [openDlg URLs];
NSLog(@"selected files: %@", selectedFiles);
[self openFileWithFilePathURL:selectedFiles.firstObject];
}
}
@@ -120,10 +133,6 @@
if( self.eof ){
[self.audioFile seekToFrame:0];
}
if( self.audioPlot.plotType == EZPlotTypeBuffer &&
self.audioPlot.shouldFill == YES ){
self.audioPlot.plotType = EZPlotTypeRolling;
}
[EZOutput sharedOutput].outputDataSource = self;
[[EZOutput sharedOutput] startPlayback];
}
@@ -143,11 +152,11 @@
*/
-(void)drawBufferPlot {
// Change the plot type to the buffer plot
self.audioPlot.plotType = EZPlotTypeBuffer;
self.audioPlotLeft.plotType = EZPlotTypeBuffer;
// Don't fill
self.audioPlot.shouldFill = NO;
self.audioPlotLeft.shouldFill = NO;
// Don't mirror over the x-axis
self.audioPlot.shouldMirror = NO;
self.audioPlotLeft.shouldMirror = NO;
}
/*
@@ -155,51 +164,77 @@
*/
-(void)drawRollingPlot {
// Change the plot type to the rolling plot
self.audioPlot.plotType = EZPlotTypeRolling;
self.audioPlotLeft.plotType = EZPlotTypeRolling;
// Fill the waveform
self.audioPlot.shouldFill = YES;
self.audioPlotLeft.shouldFill = YES;
// Mirror over the x-axis
self.audioPlot.shouldMirror = YES;
self.audioPlotLeft.shouldMirror = YES;
}
-(void)openFileWithFilePathURL:(NSURL*)filePathURL {
// Stop playback
[[EZOutput sharedOutput] stopPlayback];
self.audioFile = [EZAudioFile audioFileWithURL:filePathURL andDelegate:self];
self.eof = NO;
self.filePathLabel.stringValue = filePathURL.lastPathComponent;
self.framePositionSlider.minValue = 0.0f;
self.framePositionSlider.maxValue = (double)self.audioFile.totalFrames;
self.playButton.state = NSOffState;
AudioStreamBasicDescription asbd;
self.audioFile = [EZAudioFile audioFileWithURL:filePathURL
delegate:self
permission:EZAudioFilePermissionRead
fileFormat:asbd];
[[EZOutput sharedOutput] setAudioStreamBasicDescription:self.audioFile.clientFormat];
self.eof = NO;
self.filePathLabel.stringValue = filePathURL.lastPathComponent;
self.framePositionSlider.minValue = 0.0f;
self.framePositionSlider.maxValue = (double)self.audioFile.totalFrames;
self.playButton.state = NSOffState;
self.plotSegmentControl.selectedSegment = 1;
// Plot the whole waveform
self.audioPlot.plotType = EZPlotTypeBuffer;
self.audioPlot.shouldFill = YES;
self.audioPlot.shouldMirror = YES;
[self.audioFile getWaveformDataWithCompletionBlock:^(float *waveformData, UInt32 length) {
self.audioPlot.shouldFill = YES;
self.audioPlot.shouldMirror = YES;
[self.audioPlot updateBuffer:waveformData withBufferSize:length];
}];
// Set the client format from the EZAudioFile on the output
#pragma mark Mess Around With Audio Stream Basic Description Here!
self.sampleRateSlider.floatValue = self.audioFile.clientFormat.mSampleRate;
// Plot the whole waveform
self.audioPlotLeft.plotType = EZPlotTypeBuffer;
self.audioPlotLeft.shouldFill = YES;
self.audioPlotLeft.shouldMirror = YES;
self.audioPlotRight.plotType = EZPlotTypeBuffer;
self.audioPlotRight.shouldFill = YES;
self.audioPlotRight.shouldMirror = YES;
[self.audioFile getWaveformDataWithCompletionBlock:^(EZAudioWaveformData *data) {
self.audioPlotLeft.shouldFill = YES;
self.audioPlotLeft.shouldMirror = YES;
self.audioPlotRight.shouldFill = YES;
self.audioPlotRight.shouldMirror = YES;
if (data.numberOfChannels > 1)
{
[self.audioPlotLeft updateBuffer:[data bufferForChannel:0] withBufferSize:data.bufferSize];
[self.audioPlotRight updateBuffer:[data bufferForChannel:1] withBufferSize:data.bufferSize];
}
else
{
[self.audioPlotLeft updateBuffer:[data bufferForChannel:0] withBufferSize:data.bufferSize];
}
}];
}
#pragma mark - EZAudioFileDelegate
-(void)audioFile:(EZAudioFile *)audioFile readAudio:(float **)buffer withBufferSize:(UInt32)bufferSize withNumberOfChannels:(UInt32)numberOfChannels {
if( [EZOutput sharedOutput].isPlaying ){
dispatch_async(dispatch_get_main_queue(), ^{
if( self.audioPlot.plotType == EZPlotTypeBuffer &&
self.audioPlot.shouldFill == YES &&
self.audioPlot.shouldMirror == YES ){
self.audioPlot.shouldFill = NO;
self.audioPlot.shouldMirror = NO;
}
[self.audioPlot updateBuffer:buffer[0] withBufferSize:bufferSize];
});
}
-(void)audioFile:(EZAudioFile *)audioFile
readAudio:(float **)buffer
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels {
// if( [EZOutput sharedOutput].isPlaying ){
// dispatch_async(dispatch_get_main_queue(), ^{
// if( self.audioPlotLeft.plotType == EZPlotTypeBuffer &&
// self.audioPlotLeft.shouldFill == YES &&
// self.audioPlotLeft.shouldMirror == YES ){
// self.audioPlotLeft.shouldFill = NO;
// self.audioPlotLeft.shouldMirror = NO;
// }
// [self.audioPlotLeft updateBuffer:buffer[0] withBufferSize:bufferSize];
// });
// }
}
-(void)audioFile:(EZAudioFile *)audioFile
@@ -212,36 +247,22 @@
}
#pragma mark - EZOutputDataSource
-(AudioBufferList *)output:(EZOutput *)output
needsBufferListWithFrames:(UInt32)frames
withBufferSize:(UInt32 *)bufferSize {
if( self.audioFile ){
// Reached the end of the file
if( self.eof ){
// Here's what you do to loop the file
[self.audioFile seekToFrame:0];
self.eof = NO;
}
// Allocate a buffer list to hold the file's data
AudioBufferList *bufferList = [EZAudio audioBufferList];
BOOL eof;
-(void) output:(EZOutput*)output
shouldFillAudioBufferList:(AudioBufferList*)audioBufferList
withNumberOfFrames:(UInt32)frames
{
if( self.audioFile )
{
UInt32 bufferSize;
[self.audioFile readFrames:frames
audioBufferList:bufferList
bufferSize:bufferSize
eof:&eof];
self.eof = eof;
// Reached the end of the file on the last read
if( eof ){
[EZAudio freeBufferList:bufferList];
return nil;
audioBufferList:audioBufferList
bufferSize:&bufferSize
eof:&_eof];
if( _eof )
{
[self seekToFrame:0];
}
return bufferList;
}
return nil;
}
#pragma mark - NSOpenSavePanelDelegate
@@ -1,37 +1,30 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="4514" systemVersion="13A603" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6254" systemVersion="14C109" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment version="1070" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="4514"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6254"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="PlayFileViewController">
<connections>
<outlet property="audioPlot" destination="Lz1-Gs-1lD" id="V5w-yH-ZVR"/>
<outlet property="audioPlotLeft" destination="aHI-vj-Ccv" id="Pwl-P4-MyY"/>
<outlet property="audioPlotRight" destination="Lz1-Gs-1lD" id="GKN-Pb-Ejy"/>
<outlet property="filePathLabel" destination="0eT-7c-7fJ" id="IGv-mA-5Hw"/>
<outlet property="framePositionSlider" destination="CFP-v0-TzQ" id="3oy-Xn-4JK"/>
<outlet property="playButton" destination="OQp-Lr-dlS" id="K5R-Qg-7DY"/>
<outlet property="plotSegmentControl" destination="bZW-tA-C61" id="4ic-Ou-qh2"/>
<outlet property="sampleRateSlider" destination="rRH-oS-VV3" id="8ij-Ff-CZK"/>
<outlet property="view" destination="Xpo-HP-Ost" id="zlj-bW-4iz"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="Xpo-HP-Ost">
<rect key="frame" x="0.0" y="0.0" width="480" height="272"/>
<rect key="frame" x="0.0" y="0.0" width="480" height="421"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="Lz1-Gs-1lD" customClass="EZAudioPlotGL">
<rect key="frame" x="0.0" y="0.0" width="480" height="148"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</customView>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="2Ma-jj-U3z">
<rect key="frame" x="14" y="224" width="125" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<constraints>
<constraint firstAttribute="height" constant="21" id="Lm5-0d-A72"/>
<constraint firstAttribute="width" constant="113" id="Tij-5V-y1Q"/>
</constraints>
<rect key="frame" x="14" y="373" width="125" height="32"/>
<buttonCell key="cell" type="push" title="Choose File..." bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="KLq-bf-Xkh">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@@ -40,9 +33,8 @@
<action selector="openFile:" target="-2" id="3QB-hU-LDl"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0eT-7c-7fJ">
<rect key="frame" x="141" y="235" width="321" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="0eT-7c-7fJ">
<rect key="frame" x="141" y="384" width="38" height="17"/>
<textFieldCell key="cell" lineBreakMode="truncatingMiddle" sendsActionOnEndEditing="YES" title="Label" id="vXQ-HF-vLX">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@@ -50,12 +42,7 @@
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="OQp-Lr-dlS">
<rect key="frame" x="14" y="191" width="125" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<constraints>
<constraint firstAttribute="height" constant="21" id="geC-A9-48j"/>
<constraint firstAttribute="width" constant="113" id="lc3-4H-QK6"/>
</constraints>
<rect key="frame" x="14" y="340" width="125" height="32"/>
<buttonCell key="cell" type="push" title="Play" alternateTitle="Pause" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Z2A-7U-sb6">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
@@ -65,9 +52,11 @@
</connections>
</button>
<segmentedControl verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="bZW-tA-C61">
<rect key="frame" x="333" y="196" width="129" height="24"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<segmentedCell key="cell" alignment="left" style="rounded" trackingMode="selectOne" id="8U1-ER-vPJ">
<rect key="frame" x="333" y="345" width="129" height="24"/>
<constraints>
<constraint firstAttribute="width" constant="125" id="3Yc-x7-gJk"/>
</constraints>
<segmentedCell key="cell" borderStyle="border" alignment="left" style="rounded" trackingMode="selectOne" id="8U1-ER-vPJ">
<font key="font" metaFont="system"/>
<segments>
<segment label="Buffer" selected="YES"/>
@@ -79,32 +68,55 @@
</connections>
</segmentedControl>
<slider verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="CFP-v0-TzQ">
<rect key="frame" x="18" y="159" width="444" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<sliderCell key="cell" alignment="left" maxValue="100" doubleValue="9.3380614657210401" tickMarkPosition="above" sliderType="linear" id="gPc-pN-dmP"/>
<rect key="frame" x="18" y="308" width="444" height="20"/>
<sliderCell key="cell" continuous="YES" alignment="left" maxValue="100" doubleValue="9.3380614657210401" tickMarkPosition="above" sliderType="linear" id="gPc-pN-dmP"/>
<connections>
<action selector="seekToFrame:" target="-2" id="iVY-so-6X2"/>
</connections>
</slider>
<slider verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rRH-oS-VV3">
<rect key="frame" x="141" y="348" width="96" height="20"/>
<constraints>
<constraint firstAttribute="width" constant="92" id="Ul6-1Z-zf6"/>
</constraints>
<sliderCell key="cell" state="on" alignment="left" minValue="8000" maxValue="88200" doubleValue="44100" tickMarkPosition="above" sliderType="linear" id="xbX-Ce-da5"/>
<connections>
<action selector="changeOutputSamplingFrequency:" target="-2" id="yWM-Ei-ztA"/>
</connections>
</slider>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="aHI-vj-Ccv" customClass="EZAudioPlot">
<rect key="frame" x="0.0" y="148" width="480" height="148"/>
</customView>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="Lz1-Gs-1lD" customClass="EZAudioPlot">
<rect key="frame" x="0.0" y="0.0" width="480" height="148"/>
</customView>
</subviews>
<constraints>
<constraint firstItem="CFP-v0-TzQ" firstAttribute="top" secondItem="OQp-Lr-dlS" secondAttribute="bottom" constant="20" id="6uf-rh-zEf"/>
<constraint firstItem="0eT-7c-7fJ" firstAttribute="leading" secondItem="2Ma-jj-U3z" secondAttribute="trailing" constant="10" id="AcA-Rv-Lwl"/>
<constraint firstItem="Lz1-Gs-1lD" firstAttribute="top" secondItem="CFP-v0-TzQ" secondAttribute="bottom" constant="13" id="JjU-ri-rxV"/>
<constraint firstItem="0eT-7c-7fJ" firstAttribute="top" secondItem="Xpo-HP-Ost" secondAttribute="top" constant="20" id="T86-Jj-i0N"/>
<constraint firstAttribute="bottom" secondItem="Lz1-Gs-1lD" secondAttribute="bottom" id="U2b-77-5uo"/>
<constraint firstAttribute="trailing" secondItem="bZW-tA-C61" secondAttribute="trailing" constant="20" id="UGO-OL-Dmk"/>
<constraint firstAttribute="trailing" secondItem="CFP-v0-TzQ" secondAttribute="trailing" constant="20" id="UpE-fD-Skp"/>
<constraint firstItem="2Ma-jj-U3z" firstAttribute="top" secondItem="Xpo-HP-Ost" secondAttribute="top" constant="20" id="a1a-7J-lzc"/>
<constraint firstItem="Lz1-Gs-1lD" firstAttribute="leading" secondItem="Xpo-HP-Ost" secondAttribute="leading" id="bZz-am-fqe"/>
<constraint firstItem="2Ma-jj-U3z" firstAttribute="leading" secondItem="Xpo-HP-Ost" secondAttribute="leading" constant="20" id="dc1-KX-H5W"/>
<constraint firstItem="OQp-Lr-dlS" firstAttribute="leading" secondItem="Xpo-HP-Ost" secondAttribute="leading" constant="20" id="g5e-2M-sHn"/>
<constraint firstAttribute="trailing" secondItem="Lz1-Gs-1lD" secondAttribute="trailing" id="jDC-Iz-9c4"/>
<constraint firstItem="bZW-tA-C61" firstAttribute="top" secondItem="0eT-7c-7fJ" secondAttribute="bottom" constant="16" id="ll5-1d-SaQ"/>
<constraint firstItem="CFP-v0-TzQ" firstAttribute="leading" secondItem="Xpo-HP-Ost" secondAttribute="leading" constant="20" id="qhq-Io-tdF"/>
<constraint firstItem="Lz1-Gs-1lD" firstAttribute="leading" secondItem="Xpo-HP-Ost" secondAttribute="leading" id="xtr-M9-Uot"/>
<constraint firstItem="OQp-Lr-dlS" firstAttribute="top" secondItem="2Ma-jj-U3z" secondAttribute="bottom" constant="12" id="yGn-40-QqT"/>
<constraint firstItem="CFP-v0-TzQ" firstAttribute="top" secondItem="OQp-Lr-dlS" secondAttribute="bottom" constant="21" id="2Xm-ZL-QU7"/>
<constraint firstItem="rRH-oS-VV3" firstAttribute="leading" secondItem="0eT-7c-7fJ" secondAttribute="leading" id="3CU-am-fxR"/>
<constraint firstItem="2Ma-jj-U3z" firstAttribute="leading" secondItem="Xpo-HP-Ost" secondAttribute="leading" constant="20" symbolic="YES" id="5ch-hJ-6Kp"/>
<constraint firstItem="CFP-v0-TzQ" firstAttribute="trailing" secondItem="bZW-tA-C61" secondAttribute="trailing" id="7rr-w2-gvA"/>
<constraint firstAttribute="trailing" secondItem="bZW-tA-C61" secondAttribute="trailing" constant="20" symbolic="YES" id="A17-rX-9Sa"/>
<constraint firstItem="OQp-Lr-dlS" firstAttribute="top" secondItem="2Ma-jj-U3z" secondAttribute="bottom" constant="12" symbolic="YES" id="Fpt-Cg-5Ur"/>
<constraint firstItem="OQp-Lr-dlS" firstAttribute="trailing" secondItem="2Ma-jj-U3z" secondAttribute="trailing" id="GoP-Jj-F0w"/>
<constraint firstItem="Lz1-Gs-1lD" firstAttribute="top" secondItem="aHI-vj-Ccv" secondAttribute="bottom" id="Izh-a4-03r"/>
<constraint firstItem="2Ma-jj-U3z" firstAttribute="top" secondItem="Xpo-HP-Ost" secondAttribute="top" constant="20" symbolic="YES" id="KHJ-an-Hqi"/>
<constraint firstItem="0eT-7c-7fJ" firstAttribute="top" secondItem="2Ma-jj-U3z" secondAttribute="top" id="NAh-en-Pw9"/>
<constraint firstItem="Lz1-Gs-1lD" firstAttribute="leading" secondItem="Xpo-HP-Ost" secondAttribute="leading" id="P2a-D3-NNl"/>
<constraint firstItem="0eT-7c-7fJ" firstAttribute="leading" secondItem="2Ma-jj-U3z" secondAttribute="trailing" constant="10" id="PBC-BN-wJn"/>
<constraint firstItem="aHI-vj-Ccv" firstAttribute="height" secondItem="Lz1-Gs-1lD" secondAttribute="height" id="RkJ-OA-oUM"/>
<constraint firstItem="OQp-Lr-dlS" firstAttribute="leading" secondItem="2Ma-jj-U3z" secondAttribute="leading" id="YaB-GO-JhC"/>
<constraint firstItem="Lz1-Gs-1lD" firstAttribute="trailing" secondItem="aHI-vj-Ccv" secondAttribute="trailing" id="Z3I-LF-Cja"/>
<constraint firstItem="CFP-v0-TzQ" firstAttribute="centerX" secondItem="aHI-vj-Ccv" secondAttribute="centerX" id="Zdn-mM-g9p"/>
<constraint firstItem="Lz1-Gs-1lD" firstAttribute="leading" secondItem="aHI-vj-Ccv" secondAttribute="leading" id="cTW-4H-R0G"/>
<constraint firstItem="bZW-tA-C61" firstAttribute="top" secondItem="OQp-Lr-dlS" secondAttribute="top" id="jls-iH-yCV"/>
<constraint firstItem="rRH-oS-VV3" firstAttribute="baseline" secondItem="OQp-Lr-dlS" secondAttribute="baseline" id="mHm-mA-sbt"/>
<constraint firstItem="Lz1-Gs-1lD" firstAttribute="top" secondItem="Xpo-HP-Ost" secondAttribute="top" constant="273" id="oy9-te-LMx"/>
<constraint firstAttribute="bottom" secondItem="Lz1-Gs-1lD" secondAttribute="bottom" id="sl1-b2-YvQ"/>
<constraint firstItem="OQp-Lr-dlS" firstAttribute="leading" secondItem="CFP-v0-TzQ" secondAttribute="leading" id="tLV-2q-F9W"/>
<constraint firstItem="aHI-vj-Ccv" firstAttribute="top" secondItem="CFP-v0-TzQ" secondAttribute="bottom" constant="14" id="z81-ib-E9q"/>
</constraints>
<point key="canvasLocation" x="241" y="331.5"/>
</customView>
</objects>
</document>
</document>
@@ -32,7 +32,7 @@
#import <AVFoundation/AVFoundation.h>
// By default this will record a file to /Users/YOUR_USERNAME/Documents/test.caf
#define kAudioFilePath [NSString stringWithFormat:@"%@%@",NSHomeDirectory(),@"/Documents/test.caf"]
#define kAudioFilePath [NSString stringWithFormat:@"%@%@",NSHomeDirectory(),@"/Documents/test.m4a"]
/**
We will allow this view controller to act as an EZMicrophoneDelegate. This is how we listen for the microphone callback.
@@ -75,56 +75,64 @@
}
#pragma mark - Customize the Audio Plot
-(void)awakeFromNib {
/*
Customizing the audio plot's look
*/
// Background color
self.audioPlot.backgroundColor = [NSColor colorWithCalibratedRed: 0.984 green: 0.71 blue: 0.365 alpha: 1];
// Waveform color
self.audioPlot.color = [NSColor colorWithCalibratedRed: 1.000 green: 1.000 blue: 1.000 alpha: 1];
// Plot type
self.audioPlot.plotType = EZPlotTypeRolling;
// Fill
self.audioPlot.shouldFill = YES;
// Mirror
self.audioPlot.shouldMirror = YES;
// Configure the play button
[self.playButton setHidden:YES];
/*
Start the microphone
*/
[self.microphone startFetchingAudio];
-(void)awakeFromNib
{
/*
Customizing the audio plot's look
*/
// Background color
self.audioPlot.backgroundColor = [NSColor colorWithCalibratedRed: 0.175 green: 0.151 blue: 0.137 alpha: 1];
// Waveform color
self.audioPlot.color = [NSColor colorWithCalibratedRed: 1.000 green: 1.000 blue: 1.000 alpha: 1];
// Plot type
self.audioPlot.plotType = EZPlotTypeRolling;
// Fill
self.audioPlot.shouldFill = YES;
// Mirror
self.audioPlot.shouldMirror = YES;
// Configure the play button
[self.playButton setHidden:YES];
/*
Start the microphone
*/
[self.microphone startFetchingAudio];
}
#pragma mark - Actions
-(void)playFile:(id)sender {
// Update microphone state
[self.microphone stopFetchingAudio];
self.microphoneToggle.state = NSOffState;
[self.microphoneToggle setEnabled:NO];
// Update recording state
self.isRecording = NO;
self.recordingToggle.state = NSOffState;
[self.recordingToggle setEnabled:NO];
// Create Audio Player
if( self.audioPlayer ){
if( self.audioPlayer.playing ) [self.audioPlayer stop];
self.audioPlayer = nil;
}
NSError *err;
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:kAudioFilePath]
// Update microphone state
[self.microphone stopFetchingAudio];
self.microphoneToggle.state = NSOffState;
[self.microphoneToggle setEnabled:NO];
// Update recording state
self.isRecording = NO;
self.recordingToggle.state = NSOffState;
[self.recordingToggle setEnabled:NO];
// Create Audio Player
if( self.audioPlayer )
{
if( self.audioPlayer.playing ) [self.audioPlayer stop];
self.audioPlayer = nil;
}
// Close the audio file
if( self.recorder )
{
[self.recorder closeAudioFile];
}
NSError *err;
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:kAudioFilePath]
error:&err];
[self.audioPlayer play];
self.audioPlayer.delegate = self;
[self.audioPlayer play];
self.audioPlayer.delegate = self;
}
@@ -141,9 +149,28 @@
}
}
-(void)toggleRecording:(id)sender {
[self.playButton setHidden:NO];
self.isRecording = (BOOL)[sender state];
-(void)toggleRecording:(id)sender
{
[self.playButton setHidden:NO];
switch( [sender state] )
{
case NSOffState:
[self.recorder closeAudioFile];
break;
case NSOnState:
/*
Create the recorder
*/
self.recorder = [EZRecorder recorderWithDestinationURL:[NSURL fileURLWithPath:kAudioFilePath]
sourceFormat:self.microphone.audioStreamBasicDescription
destinationFileType:EZRecorderFileTypeM4A];
break;
default:
break;
}
self.isRecording = (BOOL)[sender state];
}
#pragma mark - EZMicrophoneDelegate
@@ -162,18 +189,6 @@ withNumberOfChannels:(UInt32)numberOfChannels {
});
}
-(void)microphone:(EZMicrophone *)microphone hasAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription {
// The AudioStreamBasicDescription of the microphone stream. This is useful when configuring the EZRecorder or telling another component what audio format type to expect.
// Here's a print function to allow you to inspect it a little easier
[EZAudio printASBD:audioStreamBasicDescription];
// We can initialize the recorder with this ASBD
self.recorder = [EZRecorder recorderWithDestinationURL:[NSURL fileURLWithPath:kAudioFilePath]
andSourceFormat:audioStreamBasicDescription];
}
// Append the microphone data coming as a AudioBufferList with the specified buffer size to the recorder
-(void)microphone:(EZMicrophone *)microphone
hasBufferList:(AudioBufferList *)bufferList
@@ -63,4 +63,6 @@
*/
-(IBAction)openFile:(id)sender;
-(IBAction)snapshot:(id)sender;
@end
@@ -66,25 +66,16 @@
#pragma mark - Customize the Audio Plot
-(void)awakeFromNib {
/*
Customizing the audio plot's look
*/
// Background color
self.audioPlot.backgroundColor = [NSColor colorWithCalibratedRed: 0.169 green: 0.643 blue: 0.675 alpha: 1];
// Waveform color
self.audioPlot.color = [NSColor colorWithCalibratedRed: 1.000 green: 1.000 blue: 1.000 alpha: 1];
// Plot type
self.audioPlot.plotType = EZPlotTypeBuffer;
// Fill
self.audioPlot.shouldFill = YES;
// Mirror
self.audioPlot.shouldMirror = YES;
/*
Try opening the sample file
*/
[self openFileWithFilePathURL:[NSURL fileURLWithPath:kAudioFileDefault]];
self.audioPlot.wantsLayer = YES;
self.audioPlot.backgroundColor = [NSColor clearColor];
self.audioPlot.plotType = EZPlotTypeBuffer;
self.audioPlot.shouldFill = YES;
self.audioPlot.shouldMirror = YES;
self.audioPlot.color = [NSColor colorWithCalibratedRed:0
green:0.676
blue:0.575
alpha:1];
[self openFileWithFilePathURL:[NSURL fileURLWithPath:kAudioFileDefault]];
}
@@ -100,6 +91,14 @@
}
}
- (void)snapshot:(id)sender
{
NSBitmapImageRep* imageRep = [self.audioPlot bitmapImageRepForCachingDisplayInRect:self.audioPlot.bounds];
[self.audioPlot cacheDisplayInRect:self.audioPlot.bounds toBitmapImageRep:imageRep];
NSData* data = [imageRep representationUsingType:NSPNGFileType properties:nil];
[data writeToFile:@"/Users/haris/Documents/waveform.png" atomically:NO];
}
#pragma mark - Action Extensions
-(void)openFileWithFilePathURL:(NSURL*)filePathURL {
@@ -110,9 +109,10 @@
// Plot the whole waveform
self.audioPlot.plotType = EZPlotTypeBuffer;
self.audioPlot.shouldFill = YES;
self.audioPlot.shouldMirror = YES;
[self.audioFile getWaveformDataWithCompletionBlock:^(float *waveformData, UInt32 length) {
[self.audioPlot updateBuffer:waveformData withBufferSize:length];
self.audioPlot.shouldMirror = YES;
[self.audioFile getWaveformDataWithCompletionBlock:^(EZAudioWaveformData *waveformData) {
[self.audioPlot updateBuffer:[waveformData bufferForChannel:1]
withBufferSize:waveformData.bufferSize];
}];
}
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="4514" systemVersion="13A603" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6245" systemVersion="13F34" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment version="1070" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="4514"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6245"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="WaveformFromFileViewController">
@@ -13,18 +13,16 @@
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="0tP-1Y-eJu">
<rect key="frame" x="0.0" y="0.0" width="480" height="272"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="gA7-R7-vbt" customClass="EZAudioPlot">
<rect key="frame" x="0.0" y="0.0" width="480" height="211"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</customView>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xHD-WQ-u4W">
<rect key="frame" x="14" y="224" width="125" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<constraints>
<constraint firstAttribute="width" constant="113" id="0jv-eA-dSV"/>
<constraint firstAttribute="height" constant="21" id="1Yn-bF-mfb"/>
@@ -37,18 +35,28 @@
<action selector="openFile:" target="-2" id="oIG-3z-prU"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="zS7-9z-YB1">
<rect key="frame" x="141" y="235" width="321" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="zS7-9z-YB1">
<rect key="frame" x="141" y="235" width="213" height="17"/>
<textFieldCell key="cell" lineBreakMode="truncatingMiddle" sendsActionOnEndEditing="YES" title="Label" id="jWL-Kx-s9B">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="2Q5-5T-qQ4">
<rect key="frame" x="375" y="224" width="97" height="32"/>
<buttonCell key="cell" type="push" title="snapshot" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="8ht-Cu-65q">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="snapshot:" target="-2" id="RLS-Qr-Gbu"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="zS7-9z-YB1" firstAttribute="leading" secondItem="xHD-WQ-u4W" secondAttribute="trailing" constant="10" id="4RW-fI-JJs"/>
<constraint firstItem="2Q5-5T-qQ4" firstAttribute="top" secondItem="0tP-1Y-eJu" secondAttribute="top" constant="20" id="E3b-p3-BoQ"/>
<constraint firstAttribute="trailing" relation="lessThanOrEqual" secondItem="zS7-9z-YB1" secondAttribute="trailing" constant="20" id="NTC-UB-Fma"/>
<constraint firstAttribute="trailing" secondItem="gA7-R7-vbt" secondAttribute="trailing" id="Tsg-V9-2HL"/>
<constraint firstItem="gA7-R7-vbt" firstAttribute="leading" secondItem="0tP-1Y-eJu" secondAttribute="leading" id="WAi-gw-Gx4"/>
@@ -56,8 +64,9 @@
<constraint firstItem="zS7-9z-YB1" firstAttribute="top" secondItem="0tP-1Y-eJu" secondAttribute="top" constant="20" id="gjc-Xh-3J0"/>
<constraint firstItem="xHD-WQ-u4W" firstAttribute="leading" secondItem="0tP-1Y-eJu" secondAttribute="leading" constant="20" id="ide-qF-Q25"/>
<constraint firstItem="xHD-WQ-u4W" firstAttribute="top" secondItem="0tP-1Y-eJu" secondAttribute="top" constant="20" id="iiM-ji-9ME"/>
<constraint firstAttribute="trailing" secondItem="2Q5-5T-qQ4" secondAttribute="trailing" constant="14" id="j3N-CF-2Rt"/>
<constraint firstAttribute="bottom" secondItem="gA7-R7-vbt" secondAttribute="bottom" id="nf1-Zn-U5V"/>
</constraints>
</customView>
</objects>
</document>
</document>
BIN
View File
Binary file not shown.
@@ -37,6 +37,7 @@
9417A7E41867DDD600D9D37B /* TPCircularBuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7D51867DDD600D9D37B /* TPCircularBuffer.c */; };
9417A7E51867DDD600D9D37B /* CHANGELOG in Resources */ = {isa = PBXBuildFile; fileRef = 9417A7D81867DDD600D9D37B /* CHANGELOG */; };
9417A7E61867DDD600D9D37B /* VERSION in Resources */ = {isa = PBXBuildFile; fileRef = 9417A7D91867DDD600D9D37B /* VERSION */; };
94FBB77318B15690007CAE45 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94FBB77218B15690007CAE45 /* AVFoundation.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -96,6 +97,7 @@
9417A7D61867DDD600D9D37B /* TPCircularBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPCircularBuffer.h; sourceTree = "<group>"; };
9417A7D81867DDD600D9D37B /* CHANGELOG */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CHANGELOG; sourceTree = "<group>"; };
9417A7D91867DDD600D9D37B /* VERSION */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = VERSION; sourceTree = "<group>"; };
94FBB77218B15690007CAE45 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -103,6 +105,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
94FBB77318B15690007CAE45 /* AVFoundation.framework in Frameworks */,
94057026185E612A00EB94BA /* GLKit.framework in Frameworks */,
94057024185E612100EB94BA /* AudioToolbox.framework in Frameworks */,
94056F80185E593500EB94BA /* CoreGraphics.framework in Frameworks */,
@@ -146,6 +149,7 @@
94056F7C185E593500EB94BA /* Frameworks */ = {
isa = PBXGroup;
children = (
94FBB77218B15690007CAE45 /* AVFoundation.framework */,
94057025185E612A00EB94BA /* GLKit.framework */,
94057023185E612000EB94BA /* AudioToolbox.framework */,
94056F7D185E593500EB94BA /* Foundation.framework */,
@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "94056F79185E593500EB94BA"
BuildableName = "EZAudioCoreGraphicsWaveformExample.app"
BlueprintName = "EZAudioCoreGraphicsWaveformExample"
ReferencedContainer = "container:EZAudioCoreGraphicsWaveformExample.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "94056F9D185E593500EB94BA"
BuildableName = "EZAudioCoreGraphicsWaveformExampleTests.xctest"
BlueprintName = "EZAudioCoreGraphicsWaveformExampleTests"
ReferencedContainer = "container:EZAudioCoreGraphicsWaveformExample.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "94056F79185E593500EB94BA"
BuildableName = "EZAudioCoreGraphicsWaveformExample.app"
BlueprintName = "EZAudioCoreGraphicsWaveformExample"
ReferencedContainer = "container:EZAudioCoreGraphicsWaveformExample.xcodeproj">
</BuildableReference>
</MacroExpansion>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<BuildableProductRunnable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "94056F79185E593500EB94BA"
BuildableName = "EZAudioCoreGraphicsWaveformExample.app"
BlueprintName = "EZAudioCoreGraphicsWaveformExample"
ReferencedContainer = "container:EZAudioCoreGraphicsWaveformExample.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<BuildableProductRunnable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "94056F79185E593500EB94BA"
BuildableName = "EZAudioCoreGraphicsWaveformExample.app"
BlueprintName = "EZAudioCoreGraphicsWaveformExample"
ReferencedContainer = "container:EZAudioCoreGraphicsWaveformExample.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>EZAudioCoreGraphicsWaveformExample.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>94056F79185E593500EB94BA</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>94056F9D185E593500EB94BA</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="4514" systemVersion="13A603" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" initialViewController="vXZ-lx-hvc">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="4514" systemVersion="13B42" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" initialViewController="vXZ-lx-hvc">
<dependencies>
<deployment defaultVersion="1536" identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="3747"/>
</dependencies>
<scenes>
@@ -8,7 +8,9 @@
#import "CoreGraphicsWaveformViewController.h"
@interface CoreGraphicsWaveformViewController ()
@interface CoreGraphicsWaveformViewController (){
float scale;
}
#pragma mark - UI Extras
@property (nonatomic,weak) IBOutlet UILabel *microphoneTextLabel;
@end
@@ -63,6 +65,10 @@
}
-(void)pinch:(UIPinchGestureRecognizer*)pinch {
}
#pragma mark - Actions
-(void)changePlotType:(id)sender {
NSInteger selectedSegment = [sender selectedSegmentIndex];
@@ -19,4 +19,7 @@
<FileRef
location = "group:EZAudioCoreGraphicsWaveformExample/../EZAudioPassThroughExample/EZAudioPassThroughExample.xcodeproj">
</FileRef>
<FileRef
location = "group:EZAudioFFTExample/EZAudioFFTExample.xcodeproj">
</FileRef>
</Workspace>
@@ -10,29 +10,29 @@
<string>EZAudioExamplesiOS</string>
<key>IDESourceControlProjectOriginsDictionary</key>
<dict>
<key>F77EC8D9-F815-4829-9274-5DA08EA98D6B</key>
<key>9D6FF97A89F512CD81EAE1A971A1D2EB03E03F7C</key>
<string>https://github.com/syedhali/EZAudio.git</string>
</dict>
<key>IDESourceControlProjectPath</key>
<string>EZAudioExamples/iOS/EZAudioExamplesiOS.xcworkspace</string>
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
<dict>
<key>F77EC8D9-F815-4829-9274-5DA08EA98D6B</key>
<key>9D6FF97A89F512CD81EAE1A971A1D2EB03E03F7C</key>
<string>../../..</string>
</dict>
<key>IDESourceControlProjectURL</key>
<string>https://github.com/syedhali/EZAudio.git</string>
<key>IDESourceControlProjectVersion</key>
<integer>110</integer>
<integer>111</integer>
<key>IDESourceControlProjectWCCIdentifier</key>
<string>F77EC8D9-F815-4829-9274-5DA08EA98D6B</string>
<string>9D6FF97A89F512CD81EAE1A971A1D2EB03E03F7C</string>
<key>IDESourceControlProjectWCConfigurations</key>
<array>
<dict>
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
<string>public.vcs.git</string>
<key>IDESourceControlWCCIdentifierKey</key>
<string>F77EC8D9-F815-4829-9274-5DA08EA98D6B</string>
<string>9D6FF97A89F512CD81EAE1A971A1D2EB03E03F7C</string>
<key>IDESourceControlWCCName</key>
<string>EZAudio</string>
</dict>
@@ -0,0 +1,598 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
9417A96C1871E88300D9D37B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A96B1871E88300D9D37B /* Foundation.framework */; };
9417A96E1871E88300D9D37B /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A96D1871E88300D9D37B /* CoreGraphics.framework */; };
9417A9701871E88300D9D37B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A96F1871E88300D9D37B /* UIKit.framework */; };
9417A9761871E88300D9D37B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9417A9741871E88300D9D37B /* InfoPlist.strings */; };
9417A9781871E88300D9D37B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9771871E88300D9D37B /* main.m */; };
9417A97C1871E88300D9D37B /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A97B1871E88300D9D37B /* AppDelegate.m */; };
9417A97F1871E88300D9D37B /* Main_iPhone.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9417A97D1871E88300D9D37B /* Main_iPhone.storyboard */; };
9417A9821871E88300D9D37B /* Main_iPad.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9417A9801871E88300D9D37B /* Main_iPad.storyboard */; };
9417A9871871E88300D9D37B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9417A9861871E88300D9D37B /* Images.xcassets */; };
9417A98E1871E88300D9D37B /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A98D1871E88300D9D37B /* XCTest.framework */; };
9417A98F1871E88300D9D37B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A96B1871E88300D9D37B /* Foundation.framework */; };
9417A9901871E88300D9D37B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A96F1871E88300D9D37B /* UIKit.framework */; };
9417A9981871E88300D9D37B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9417A9961871E88300D9D37B /* InfoPlist.strings */; };
9417A99A1871E88300D9D37B /* EZAudioFFTExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9991871E88300D9D37B /* EZAudioFFTExampleTests.m */; };
9417A9A41871E89500D9D37B /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A9A31871E89500D9D37B /* AudioToolbox.framework */; };
9417A9A61871E8A100D9D37B /* GLKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A9A51871E8A100D9D37B /* GLKit.framework */; };
9417A9C11871E96300D9D37B /* AEFloatConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9A91871E96300D9D37B /* AEFloatConverter.m */; };
9417A9C21871E96300D9D37B /* EZAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9AB1871E96300D9D37B /* EZAudio.m */; };
9417A9C31871E96300D9D37B /* EZAudioFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9AD1871E96300D9D37B /* EZAudioFile.m */; };
9417A9C41871E96300D9D37B /* EZAudioPlot.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9AF1871E96300D9D37B /* EZAudioPlot.m */; };
9417A9C51871E96300D9D37B /* EZAudioPlotGL.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9B11871E96300D9D37B /* EZAudioPlotGL.m */; };
9417A9C61871E96300D9D37B /* EZAudioPlotGLKViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9B31871E96300D9D37B /* EZAudioPlotGLKViewController.m */; };
9417A9C71871E96300D9D37B /* EZMicrophone.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9B51871E96300D9D37B /* EZMicrophone.m */; };
9417A9C81871E96300D9D37B /* EZOutput.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9B71871E96300D9D37B /* EZOutput.m */; };
9417A9C91871E96300D9D37B /* EZPlot.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9B91871E96300D9D37B /* EZPlot.m */; };
9417A9CA1871E96300D9D37B /* EZRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9BB1871E96300D9D37B /* EZRecorder.m */; };
9417A9CB1871E96300D9D37B /* TPCircularBuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9BC1871E96300D9D37B /* TPCircularBuffer.c */; };
9417A9CC1871E96300D9D37B /* CHANGELOG in Resources */ = {isa = PBXBuildFile; fileRef = 9417A9BF1871E96300D9D37B /* CHANGELOG */; };
9417A9CD1871E96300D9D37B /* VERSION in Resources */ = {isa = PBXBuildFile; fileRef = 9417A9C01871E96300D9D37B /* VERSION */; };
9417A9D01871E97D00D9D37B /* FFTViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9CF1871E97D00D9D37B /* FFTViewController.m */; };
9417A9D21871EA5900D9D37B /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A9D11871EA5900D9D37B /* Accelerate.framework */; };
94FBB77918B156B8007CAE45 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94FBB77818B156B8007CAE45 /* AVFoundation.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
9417A9911871E88300D9D37B /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 9417A9601871E88300D9D37B /* Project object */;
proxyType = 1;
remoteGlobalIDString = 9417A9671871E88300D9D37B;
remoteInfo = EZAudioFFTExample;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
9417A9681871E88300D9D37B /* EZAudioFFTExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = EZAudioFFTExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
9417A96B1871E88300D9D37B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
9417A96D1871E88300D9D37B /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
9417A96F1871E88300D9D37B /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
9417A9731871E88300D9D37B /* EZAudioFFTExample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "EZAudioFFTExample-Info.plist"; sourceTree = "<group>"; };
9417A9751871E88300D9D37B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
9417A9771871E88300D9D37B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
9417A9791871E88300D9D37B /* EZAudioFFTExample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "EZAudioFFTExample-Prefix.pch"; sourceTree = "<group>"; };
9417A97A1871E88300D9D37B /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
9417A97B1871E88300D9D37B /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
9417A97E1871E88300D9D37B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main_iPhone.storyboard; sourceTree = "<group>"; };
9417A9811871E88300D9D37B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main_iPad.storyboard; sourceTree = "<group>"; };
9417A9861871E88300D9D37B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
9417A98C1871E88300D9D37B /* EZAudioFFTExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = EZAudioFFTExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
9417A98D1871E88300D9D37B /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
9417A9951871E88300D9D37B /* EZAudioFFTExampleTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "EZAudioFFTExampleTests-Info.plist"; sourceTree = "<group>"; };
9417A9971871E88300D9D37B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
9417A9991871E88300D9D37B /* EZAudioFFTExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EZAudioFFTExampleTests.m; sourceTree = "<group>"; };
9417A9A31871E89500D9D37B /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
9417A9A51871E8A100D9D37B /* GLKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLKit.framework; path = System/Library/Frameworks/GLKit.framework; sourceTree = SDKROOT; };
9417A9A81871E96300D9D37B /* AEFloatConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEFloatConverter.h; sourceTree = "<group>"; };
9417A9A91871E96300D9D37B /* AEFloatConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AEFloatConverter.m; sourceTree = "<group>"; };
9417A9AA1871E96300D9D37B /* EZAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudio.h; sourceTree = "<group>"; };
9417A9AB1871E96300D9D37B /* EZAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudio.m; sourceTree = "<group>"; };
9417A9AC1871E96300D9D37B /* EZAudioFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioFile.h; sourceTree = "<group>"; };
9417A9AD1871E96300D9D37B /* EZAudioFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioFile.m; sourceTree = "<group>"; };
9417A9AE1871E96300D9D37B /* EZAudioPlot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlot.h; sourceTree = "<group>"; };
9417A9AF1871E96300D9D37B /* EZAudioPlot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlot.m; sourceTree = "<group>"; };
9417A9B01871E96300D9D37B /* EZAudioPlotGL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlotGL.h; sourceTree = "<group>"; };
9417A9B11871E96300D9D37B /* EZAudioPlotGL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlotGL.m; sourceTree = "<group>"; };
9417A9B21871E96300D9D37B /* EZAudioPlotGLKViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlotGLKViewController.h; sourceTree = "<group>"; };
9417A9B31871E96300D9D37B /* EZAudioPlotGLKViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlotGLKViewController.m; sourceTree = "<group>"; };
9417A9B41871E96300D9D37B /* EZMicrophone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZMicrophone.h; sourceTree = "<group>"; };
9417A9B51871E96300D9D37B /* EZMicrophone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZMicrophone.m; sourceTree = "<group>"; };
9417A9B61871E96300D9D37B /* EZOutput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZOutput.h; sourceTree = "<group>"; };
9417A9B71871E96300D9D37B /* EZOutput.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZOutput.m; sourceTree = "<group>"; };
9417A9B81871E96300D9D37B /* EZPlot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZPlot.h; sourceTree = "<group>"; };
9417A9B91871E96300D9D37B /* EZPlot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZPlot.m; sourceTree = "<group>"; };
9417A9BA1871E96300D9D37B /* EZRecorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZRecorder.h; sourceTree = "<group>"; };
9417A9BB1871E96300D9D37B /* EZRecorder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZRecorder.m; sourceTree = "<group>"; };
9417A9BC1871E96300D9D37B /* TPCircularBuffer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = TPCircularBuffer.c; sourceTree = "<group>"; };
9417A9BD1871E96300D9D37B /* TPCircularBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPCircularBuffer.h; sourceTree = "<group>"; };
9417A9BF1871E96300D9D37B /* CHANGELOG */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CHANGELOG; sourceTree = "<group>"; };
9417A9C01871E96300D9D37B /* VERSION */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = VERSION; sourceTree = "<group>"; };
9417A9CE1871E97D00D9D37B /* FFTViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FFTViewController.h; sourceTree = "<group>"; };
9417A9CF1871E97D00D9D37B /* FFTViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FFTViewController.m; sourceTree = "<group>"; };
9417A9D11871EA5900D9D37B /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; };
94FBB77818B156B8007CAE45 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
9417A9651871E88300D9D37B /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
94FBB77918B156B8007CAE45 /* AVFoundation.framework in Frameworks */,
9417A9D21871EA5900D9D37B /* Accelerate.framework in Frameworks */,
9417A9A61871E8A100D9D37B /* GLKit.framework in Frameworks */,
9417A9A41871E89500D9D37B /* AudioToolbox.framework in Frameworks */,
9417A96E1871E88300D9D37B /* CoreGraphics.framework in Frameworks */,
9417A9701871E88300D9D37B /* UIKit.framework in Frameworks */,
9417A96C1871E88300D9D37B /* Foundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
9417A9891871E88300D9D37B /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
9417A98E1871E88300D9D37B /* XCTest.framework in Frameworks */,
9417A9901871E88300D9D37B /* UIKit.framework in Frameworks */,
9417A98F1871E88300D9D37B /* Foundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
9417A95F1871E88300D9D37B = {
isa = PBXGroup;
children = (
9417A9711871E88300D9D37B /* EZAudioFFTExample */,
9417A9931871E88300D9D37B /* EZAudioFFTExampleTests */,
9417A96A1871E88300D9D37B /* Frameworks */,
9417A9691871E88300D9D37B /* Products */,
);
sourceTree = "<group>";
};
9417A9691871E88300D9D37B /* Products */ = {
isa = PBXGroup;
children = (
9417A9681871E88300D9D37B /* EZAudioFFTExample.app */,
9417A98C1871E88300D9D37B /* EZAudioFFTExampleTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
9417A96A1871E88300D9D37B /* Frameworks */ = {
isa = PBXGroup;
children = (
94FBB77818B156B8007CAE45 /* AVFoundation.framework */,
9417A9D11871EA5900D9D37B /* Accelerate.framework */,
9417A9A51871E8A100D9D37B /* GLKit.framework */,
9417A9A31871E89500D9D37B /* AudioToolbox.framework */,
9417A96B1871E88300D9D37B /* Foundation.framework */,
9417A96D1871E88300D9D37B /* CoreGraphics.framework */,
9417A96F1871E88300D9D37B /* UIKit.framework */,
9417A98D1871E88300D9D37B /* XCTest.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
9417A9711871E88300D9D37B /* EZAudioFFTExample */ = {
isa = PBXGroup;
children = (
9417A9A71871E96300D9D37B /* EZAudio */,
9417A97A1871E88300D9D37B /* AppDelegate.h */,
9417A97B1871E88300D9D37B /* AppDelegate.m */,
9417A97D1871E88300D9D37B /* Main_iPhone.storyboard */,
9417A9801871E88300D9D37B /* Main_iPad.storyboard */,
9417A9CE1871E97D00D9D37B /* FFTViewController.h */,
9417A9CF1871E97D00D9D37B /* FFTViewController.m */,
9417A9861871E88300D9D37B /* Images.xcassets */,
9417A9721871E88300D9D37B /* Supporting Files */,
);
path = EZAudioFFTExample;
sourceTree = "<group>";
};
9417A9721871E88300D9D37B /* Supporting Files */ = {
isa = PBXGroup;
children = (
9417A9731871E88300D9D37B /* EZAudioFFTExample-Info.plist */,
9417A9741871E88300D9D37B /* InfoPlist.strings */,
9417A9771871E88300D9D37B /* main.m */,
9417A9791871E88300D9D37B /* EZAudioFFTExample-Prefix.pch */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
9417A9931871E88300D9D37B /* EZAudioFFTExampleTests */ = {
isa = PBXGroup;
children = (
9417A9991871E88300D9D37B /* EZAudioFFTExampleTests.m */,
9417A9941871E88300D9D37B /* Supporting Files */,
);
path = EZAudioFFTExampleTests;
sourceTree = "<group>";
};
9417A9941871E88300D9D37B /* Supporting Files */ = {
isa = PBXGroup;
children = (
9417A9951871E88300D9D37B /* EZAudioFFTExampleTests-Info.plist */,
9417A9961871E88300D9D37B /* InfoPlist.strings */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
9417A9A71871E96300D9D37B /* EZAudio */ = {
isa = PBXGroup;
children = (
9417A9A81871E96300D9D37B /* AEFloatConverter.h */,
9417A9A91871E96300D9D37B /* AEFloatConverter.m */,
9417A9AA1871E96300D9D37B /* EZAudio.h */,
9417A9AB1871E96300D9D37B /* EZAudio.m */,
9417A9AC1871E96300D9D37B /* EZAudioFile.h */,
9417A9AD1871E96300D9D37B /* EZAudioFile.m */,
9417A9AE1871E96300D9D37B /* EZAudioPlot.h */,
9417A9AF1871E96300D9D37B /* EZAudioPlot.m */,
9417A9B01871E96300D9D37B /* EZAudioPlotGL.h */,
9417A9B11871E96300D9D37B /* EZAudioPlotGL.m */,
9417A9B21871E96300D9D37B /* EZAudioPlotGLKViewController.h */,
9417A9B31871E96300D9D37B /* EZAudioPlotGLKViewController.m */,
9417A9B41871E96300D9D37B /* EZMicrophone.h */,
9417A9B51871E96300D9D37B /* EZMicrophone.m */,
9417A9B61871E96300D9D37B /* EZOutput.h */,
9417A9B71871E96300D9D37B /* EZOutput.m */,
9417A9B81871E96300D9D37B /* EZPlot.h */,
9417A9B91871E96300D9D37B /* EZPlot.m */,
9417A9BA1871E96300D9D37B /* EZRecorder.h */,
9417A9BB1871E96300D9D37B /* EZRecorder.m */,
9417A9BC1871E96300D9D37B /* TPCircularBuffer.c */,
9417A9BD1871E96300D9D37B /* TPCircularBuffer.h */,
9417A9BE1871E96300D9D37B /* VERSION */,
);
name = EZAudio;
path = ../../../../EZAudio;
sourceTree = "<group>";
};
9417A9BE1871E96300D9D37B /* VERSION */ = {
isa = PBXGroup;
children = (
9417A9BF1871E96300D9D37B /* CHANGELOG */,
9417A9C01871E96300D9D37B /* VERSION */,
);
path = VERSION;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
9417A9671871E88300D9D37B /* EZAudioFFTExample */ = {
isa = PBXNativeTarget;
buildConfigurationList = 9417A99D1871E88300D9D37B /* Build configuration list for PBXNativeTarget "EZAudioFFTExample" */;
buildPhases = (
9417A9641871E88300D9D37B /* Sources */,
9417A9651871E88300D9D37B /* Frameworks */,
9417A9661871E88300D9D37B /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = EZAudioFFTExample;
productName = EZAudioFFTExample;
productReference = 9417A9681871E88300D9D37B /* EZAudioFFTExample.app */;
productType = "com.apple.product-type.application";
};
9417A98B1871E88300D9D37B /* EZAudioFFTExampleTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 9417A9A01871E88300D9D37B /* Build configuration list for PBXNativeTarget "EZAudioFFTExampleTests" */;
buildPhases = (
9417A9881871E88300D9D37B /* Sources */,
9417A9891871E88300D9D37B /* Frameworks */,
9417A98A1871E88300D9D37B /* Resources */,
);
buildRules = (
);
dependencies = (
9417A9921871E88300D9D37B /* PBXTargetDependency */,
);
name = EZAudioFFTExampleTests;
productName = EZAudioFFTExampleTests;
productReference = 9417A98C1871E88300D9D37B /* EZAudioFFTExampleTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
9417A9601871E88300D9D37B /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0500;
ORGANIZATIONNAME = "Syed Haris Ali";
TargetAttributes = {
9417A98B1871E88300D9D37B = {
TestTargetID = 9417A9671871E88300D9D37B;
};
};
};
buildConfigurationList = 9417A9631871E88300D9D37B /* Build configuration list for PBXProject "EZAudioFFTExample" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 9417A95F1871E88300D9D37B;
productRefGroup = 9417A9691871E88300D9D37B /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
9417A9671871E88300D9D37B /* EZAudioFFTExample */,
9417A98B1871E88300D9D37B /* EZAudioFFTExampleTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
9417A9661871E88300D9D37B /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9417A9821871E88300D9D37B /* Main_iPad.storyboard in Resources */,
9417A9871871E88300D9D37B /* Images.xcassets in Resources */,
9417A97F1871E88300D9D37B /* Main_iPhone.storyboard in Resources */,
9417A9CD1871E96300D9D37B /* VERSION in Resources */,
9417A9761871E88300D9D37B /* InfoPlist.strings in Resources */,
9417A9CC1871E96300D9D37B /* CHANGELOG in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
9417A98A1871E88300D9D37B /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9417A9981871E88300D9D37B /* InfoPlist.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
9417A9641871E88300D9D37B /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9417A9CB1871E96300D9D37B /* TPCircularBuffer.c in Sources */,
9417A9C81871E96300D9D37B /* EZOutput.m in Sources */,
9417A9C61871E96300D9D37B /* EZAudioPlotGLKViewController.m in Sources */,
9417A9C11871E96300D9D37B /* AEFloatConverter.m in Sources */,
9417A97C1871E88300D9D37B /* AppDelegate.m in Sources */,
9417A9C41871E96300D9D37B /* EZAudioPlot.m in Sources */,
9417A9C31871E96300D9D37B /* EZAudioFile.m in Sources */,
9417A9D01871E97D00D9D37B /* FFTViewController.m in Sources */,
9417A9C51871E96300D9D37B /* EZAudioPlotGL.m in Sources */,
9417A9781871E88300D9D37B /* main.m in Sources */,
9417A9C91871E96300D9D37B /* EZPlot.m in Sources */,
9417A9C71871E96300D9D37B /* EZMicrophone.m in Sources */,
9417A9CA1871E96300D9D37B /* EZRecorder.m in Sources */,
9417A9C21871E96300D9D37B /* EZAudio.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
9417A9881871E88300D9D37B /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9417A99A1871E88300D9D37B /* EZAudioFFTExampleTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
9417A9921871E88300D9D37B /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 9417A9671871E88300D9D37B /* EZAudioFFTExample */;
targetProxy = 9417A9911871E88300D9D37B /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
9417A9741871E88300D9D37B /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
9417A9751871E88300D9D37B /* en */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
9417A97D1871E88300D9D37B /* Main_iPhone.storyboard */ = {
isa = PBXVariantGroup;
children = (
9417A97E1871E88300D9D37B /* Base */,
);
name = Main_iPhone.storyboard;
sourceTree = "<group>";
};
9417A9801871E88300D9D37B /* Main_iPad.storyboard */ = {
isa = PBXVariantGroup;
children = (
9417A9811871E88300D9D37B /* Base */,
);
name = Main_iPad.storyboard;
sourceTree = "<group>";
};
9417A9961871E88300D9D37B /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
9417A9971871E88300D9D37B /* en */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
9417A99B1871E88300D9D37B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
9417A99C1871E88300D9D37B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
9417A99E1871E88300D9D37B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioFFTExample/EZAudioFFTExample-Prefix.pch";
INFOPLIST_FILE = "EZAudioFFTExample/EZAudioFFTExample-Info.plist";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
name = Debug;
};
9417A99F1871E88300D9D37B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioFFTExample/EZAudioFFTExample-Prefix.pch";
INFOPLIST_FILE = "EZAudioFFTExample/EZAudioFFTExample-Info.plist";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
name = Release;
};
9417A9A11871E88300D9D37B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/EZAudioFFTExample.app/EZAudioFFTExample";
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
"$(DEVELOPER_FRAMEWORKS_DIR)",
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioFFTExample/EZAudioFFTExample-Prefix.pch";
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = "EZAudioFFTExampleTests/EZAudioFFTExampleTests-Info.plist";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
};
name = Debug;
};
9417A9A21871E88300D9D37B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/EZAudioFFTExample.app/EZAudioFFTExample";
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
"$(DEVELOPER_FRAMEWORKS_DIR)",
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioFFTExample/EZAudioFFTExample-Prefix.pch";
INFOPLIST_FILE = "EZAudioFFTExampleTests/EZAudioFFTExampleTests-Info.plist";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
9417A9631871E88300D9D37B /* Build configuration list for PBXProject "EZAudioFFTExample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
9417A99B1871E88300D9D37B /* Debug */,
9417A99C1871E88300D9D37B /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
9417A99D1871E88300D9D37B /* Build configuration list for PBXNativeTarget "EZAudioFFTExample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
9417A99E1871E88300D9D37B /* Debug */,
9417A99F1871E88300D9D37B /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
9417A9A01871E88300D9D37B /* Build configuration list for PBXNativeTarget "EZAudioFFTExampleTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
9417A9A11871E88300D9D37B /* Debug */,
9417A9A21871E88300D9D37B /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 9417A9601871E88300D9D37B /* Project object */;
}
@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9417A9671871E88300D9D37B"
BuildableName = "EZAudioFFTExample.app"
BlueprintName = "EZAudioFFTExample"
ReferencedContainer = "container:EZAudioFFTExample.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9417A98B1871E88300D9D37B"
BuildableName = "EZAudioFFTExampleTests.xctest"
BlueprintName = "EZAudioFFTExampleTests"
ReferencedContainer = "container:EZAudioFFTExample.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9417A9671871E88300D9D37B"
BuildableName = "EZAudioFFTExample.app"
BlueprintName = "EZAudioFFTExample"
ReferencedContainer = "container:EZAudioFFTExample.xcodeproj">
</BuildableReference>
</MacroExpansion>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<BuildableProductRunnable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9417A9671871E88300D9D37B"
BuildableName = "EZAudioFFTExample.app"
BlueprintName = "EZAudioFFTExample"
ReferencedContainer = "container:EZAudioFFTExample.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<BuildableProductRunnable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9417A9671871E88300D9D37B"
BuildableName = "EZAudioFFTExample.app"
BlueprintName = "EZAudioFFTExample"
ReferencedContainer = "container:EZAudioFFTExample.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>EZAudioFFTExample.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>6</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>9417A9671871E88300D9D37B</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>9417A98B1871E88300D9D37B</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>
@@ -0,0 +1,32 @@
//
// AppDelegate.h
// EZAudioFFTExample
//
// Created by Syed Haris Ali on 12/30/13.
// Copyright (c) 2013 Syed Haris Ali. All rights reserved.
//
// 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 <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end
@@ -0,0 +1,63 @@
//
// AppDelegate.m
// EZAudioFFTExample
//
// Created by Syed Haris Ali on 12/30/13.
// Copyright (c) 2013 Syed Haris Ali. All rights reserved.
//
// 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 "AppDelegate.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication *)application
{
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
@end
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="4514" systemVersion="13A603" targetRuntime="iOS.CocoaTouch.iPad" propertyAccessControl="none" useAutolayout="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="3747"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FFTViewController" sceneMemberID="viewController">
<connections>
<outlet property="audioPlotFreq" destination="DSa-vR-ySS" id="JP5-yB-Jqo"/>
<outlet property="audioPlotTime" destination="Za4-u7-8h1" id="ox5-AQ-DaQ"/>
<outlet property="view" destination="e0f-bI-AGV" id="oLT-9I-NIK"/>
</connections>
</viewController>
<view contentMode="scaleToFill" id="e0f-bI-AGV">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Za4-u7-8h1" customClass="EZAudioPlotGL">
<rect key="frame" x="0.0" y="0.0" width="320" height="284"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" relation="lessThanOrEqual" constant="284" id="lUL-Pa-hHs"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="DSa-vR-ySS" customClass="EZAudioPlot">
<rect key="frame" x="0.0" y="284" width="320" height="284"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" relation="lessThanOrEqual" priority="86" constant="284" id="aNL-Q8-MOw"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="DSa-vR-ySS" secondAttribute="trailing" id="FOI-4L-3b8"/>
<constraint firstItem="DSa-vR-ySS" firstAttribute="leading" secondItem="e0f-bI-AGV" secondAttribute="leading" id="LTm-AL-Oqz"/>
<constraint firstAttribute="trailing" secondItem="Za4-u7-8h1" secondAttribute="trailing" id="SLb-8B-cNK"/>
<constraint firstItem="DSa-vR-ySS" firstAttribute="top" secondItem="Za4-u7-8h1" secondAttribute="bottom" id="cef-HS-Uiw"/>
<constraint firstItem="Za4-u7-8h1" firstAttribute="leading" secondItem="e0f-bI-AGV" secondAttribute="leading" id="fe0-ef-NkA"/>
<constraint firstAttribute="bottom" secondItem="DSa-vR-ySS" secondAttribute="bottom" id="oDL-Qh-0Ei"/>
<constraint firstItem="Za4-u7-8h1" firstAttribute="top" secondItem="e0f-bI-AGV" secondAttribute="top" id="zKR-dO-8wP"/>
</constraints>
</view>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
<simulatedMetricsContainer key="defaultSimulatedMetrics">
<simulatedStatusBarMetrics key="statusBar" statusBarStyle="blackOpaque"/>
<simulatedOrientationMetrics key="orientation"/>
<simulatedScreenMetrics key="destination"/>
</simulatedMetricsContainer>
</document>
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6185.11" systemVersion="13E28" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" initialViewController="vXZ-lx-hvc">
<dependencies>
<deployment defaultVersion="1792" identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6190.4"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="ufC-wZ-h7g">
<objects>
<viewController id="vXZ-lx-hvc" customClass="FFTViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="SDG-bW-D9N"/>
<viewControllerLayoutGuide type="bottom" id="Ghx-UD-SsY"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="kh9-bI-dsS">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="0IN-AU-L5p" customClass="EZAudioPlotGL">
<rect key="frame" x="0.0" y="0.0" width="320" height="284"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="tRL-re-aMl" customClass="EZAudioPlot">
<rect key="frame" x="0.0" y="284" width="320" height="284"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="tRL-re-aMl" firstAttribute="height" secondItem="0IN-AU-L5p" secondAttribute="height" id="0x5-uT-6oH"/>
<constraint firstItem="tRL-re-aMl" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leading" id="1PO-c6-Jpi"/>
<constraint firstItem="0IN-AU-L5p" firstAttribute="top" secondItem="kh9-bI-dsS" secondAttribute="top" id="3j1-j9-0EV"/>
<constraint firstItem="Ghx-UD-SsY" firstAttribute="top" secondItem="tRL-re-aMl" secondAttribute="bottom" id="9sP-5w-dcD"/>
<constraint firstAttribute="trailing" secondItem="tRL-re-aMl" secondAttribute="trailing" id="KSC-e5-GVL"/>
<constraint firstItem="0IN-AU-L5p" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leading" id="Ut5-w2-ONV"/>
<constraint firstItem="tRL-re-aMl" firstAttribute="top" secondItem="0IN-AU-L5p" secondAttribute="bottom" id="V11-Sm-jlk"/>
<constraint firstAttribute="trailing" secondItem="0IN-AU-L5p" secondAttribute="trailing" id="XFq-Me-fvw"/>
</constraints>
</view>
<connections>
<outlet property="audioPlotFreq" destination="tRL-re-aMl" id="s8q-c6-u9E"/>
<outlet property="audioPlotTime" destination="0IN-AU-L5p" id="tej-Kb-ycI"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="x5A-6p-PRh" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
<simulatedMetricsContainer key="defaultSimulatedMetrics">
<simulatedStatusBarMetrics key="statusBar"/>
<simulatedOrientationMetrics key="orientation"/>
<simulatedScreenMetrics key="destination" type="retina4">
<size key="portraitSize" width="320" height="568"/>
<size key="landscapeSize" width="568" height="320"/>
</simulatedScreenMetrics>
</simulatedMetricsContainer>
</document>
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>com.sha.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIMainStoryboardFile</key>
<string>Main_iPhone</string>
<key>UIMainStoryboardFile~ipad</key>
<string>Main_iPad</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
@@ -0,0 +1,16 @@
//
// Prefix header
//
// The contents of this file are implicitly included at the beginning of every source file.
//
#import <Availability.h>
#ifndef __IPHONE_5_0
#warning "This project uses features only available in iOS SDK 5.0 and later."
#endif
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#endif
@@ -0,0 +1,59 @@
//
// FFTViewController.h
// EZAudioFFTExample
//
// Created by Syed Haris Ali on 12/30/13.
// Copyright (c) 2013 Syed Haris Ali. All rights reserved.
//
// 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 <UIKit/UIKit.h>
/**
EZAudio
*/
#import "EZAudio.h"
/**
Accelerate
*/
#import <Accelerate/Accelerate.h>
/**
The FFTViewController demonstrates how to use the Accelerate framework to calculate the real-time FFT of audio data provided by an EZAudioMicrophone.
*/
@interface FFTViewController : UIViewController <EZMicrophoneDelegate>
#pragma mark - Components
/**
EZAudioPlot for frequency plot
*/
@property (nonatomic,weak) IBOutlet EZAudioPlot *audioPlotFreq;
/**
EZAudioPlot for time plot
*/
@property (nonatomic,weak) IBOutlet EZAudioPlotGL *audioPlotTime;
/**
Microphone
*/
@property (nonatomic,strong) EZMicrophone *microphone;
@end
@@ -0,0 +1,156 @@
//
// FFTViewController.m
// EZAudioFFTExample
//
// Created by Syed Haris Ali on 12/30/13.
// Copyright (c) 2013 Syed Haris Ali. All rights reserved.
//
// 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 "FFTViewController.h"
@interface FFTViewController (){
COMPLEX_SPLIT _A;
FFTSetup _FFTSetup;
BOOL _isFFTSetup;
vDSP_Length _log2n;
}
@end
@implementation FFTViewController
@synthesize audioPlotFreq;
@synthesize audioPlotTime;
@synthesize microphone;
#pragma mark - Customize the Audio Plot
- (void)viewDidLoad
{
[super viewDidLoad];
/*
Customizing the audio plot's look
*/
// Setup time domain audio plot
self.audioPlotTime.backgroundColor = [UIColor colorWithRed: 0.569 green: 0.82 blue: 0.478 alpha: 1];
self.audioPlotTime.color = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];
self.audioPlotTime.shouldFill = YES;
self.audioPlotTime.shouldMirror = YES;
self.audioPlotTime.plotType = EZPlotTypeRolling;
// Setup frequency domain audio plot
self.audioPlotFreq.backgroundColor = [UIColor colorWithRed: 0.984 green: 0.471 blue: 0.525 alpha: 1];
self.audioPlotFreq.color = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];
self.audioPlotFreq.shouldFill = YES;
self.audioPlotFreq.plotType = EZPlotTypeBuffer;
/*
Start the microphone
*/
self.microphone = [EZMicrophone microphoneWithDelegate:self
startsImmediately:YES];
}
#pragma mark - FFT
/**
Adapted from http://batmobile.blogs.ilrt.org/fourier-transforms-on-an-iphone/
*/
-(void)createFFTWithBufferSize:(float)bufferSize withAudioData:(float*)data {
// Setup the length
_log2n = log2f(bufferSize);
// Calculate the weights array. This is a one-off operation.
_FFTSetup = vDSP_create_fftsetup(_log2n, FFT_RADIX2);
// For an FFT, numSamples must be a power of 2, i.e. is always even
int nOver2 = bufferSize/2;
// Populate *window with the values for a hamming window function
float *window = (float *)malloc(sizeof(float)*bufferSize);
vDSP_hamm_window(window, bufferSize, 0);
// Window the samples
vDSP_vmul(data, 1, window, 1, data, 1, bufferSize);
free(window);
// Define complex buffer
_A.realp = (float *) malloc(nOver2*sizeof(float));
_A.imagp = (float *) malloc(nOver2*sizeof(float));
}
-(void)updateFFTWithBufferSize:(float)bufferSize withAudioData:(float*)data {
// For an FFT, numSamples must be a power of 2, i.e. is always even
int nOver2 = bufferSize/2;
// Pack samples:
// C(re) -> A[n], C(im) -> A[n+1]
vDSP_ctoz((COMPLEX*)data, 2, &_A, 1, nOver2);
// Perform a forward FFT using fftSetup and A
// Results are returned in A
vDSP_fft_zrip(_FFTSetup, &_A, 1, _log2n, FFT_FORWARD);
// Convert COMPLEX_SPLIT A result to magnitudes
float amp[nOver2];
float maxMag = 0;
for(int i=0; i<nOver2; i++) {
// Calculate the magnitude
float mag = _A.realp[i]*_A.realp[i]+_A.imagp[i]*_A.imagp[i];
maxMag = mag > maxMag ? mag : maxMag;
}
for(int i=0; i<nOver2; i++) {
// Calculate the magnitude
float mag = _A.realp[i]*_A.realp[i]+_A.imagp[i]*_A.imagp[i];
// Bind the value to be less than 1.0 to fit in the graph
amp[i] = [EZAudio MAP:mag leftMin:0.0 leftMax:maxMag rightMin:0.0 rightMax:1.0];
}
// Update the frequency domain plot
[self.audioPlotFreq updateBuffer:amp
withBufferSize:nOver2];
}
#pragma mark - EZMicrophoneDelegate
-(void) microphone:(EZMicrophone *)microphone
hasAudioReceived:(float **)buffer
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels {
dispatch_async(dispatch_get_main_queue(), ^{
// Update time domain plot
[self.audioPlotTime updateBuffer:buffer[0]
withBufferSize:bufferSize];
// Setup the FFT if it's not already setup
if( !_isFFTSetup ){
[self createFFTWithBufferSize:bufferSize withAudioData:buffer[0]];
_isFFTSetup = YES;
}
// Get the FFT data
[self updateFFTWithBufferSize:bufferSize withAudioData:buffer[0]];
});
}
@end
@@ -0,0 +1,53 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -0,0 +1,51 @@
{
"images" : [
{
"orientation" : "portrait",
"idiom" : "iphone",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "iphone",
"subtype" : "retina4",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "ipad",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "1x"
},
{
"orientation" : "landscape",
"idiom" : "ipad",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "1x"
},
{
"orientation" : "portrait",
"idiom" : "ipad",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "2x"
},
{
"orientation" : "landscape",
"idiom" : "ipad",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -0,0 +1,2 @@
/* Localized versions of Info.plist keys */
@@ -0,0 +1,18 @@
//
// main.m
// EZAudioFFTExample
//
// Created by Syed Haris Ali on 12/30/13.
// Copyright (c) 2013 Syed Haris Ali. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>com.sha.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
@@ -0,0 +1,34 @@
//
// EZAudioFFTExampleTests.m
// EZAudioFFTExampleTests
//
// Created by Syed Haris Ali on 12/30/13.
// Copyright (c) 2013 Syed Haris Ali. All rights reserved.
//
#import <XCTest/XCTest.h>
@interface EZAudioFFTExampleTests : XCTestCase
@end
@implementation EZAudioFFTExampleTests
- (void)setUp
{
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown
{
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testExample
{
XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__);
}
@end
@@ -0,0 +1,2 @@
/* Localized versions of Info.plist keys */
@@ -37,6 +37,7 @@
9417A80B1867DDE300D9D37B /* TPCircularBuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7FC1867DDE300D9D37B /* TPCircularBuffer.c */; };
9417A80C1867DDE300D9D37B /* CHANGELOG in Resources */ = {isa = PBXBuildFile; fileRef = 9417A7FF1867DDE300D9D37B /* CHANGELOG */; };
9417A80D1867DDE300D9D37B /* VERSION in Resources */ = {isa = PBXBuildFile; fileRef = 9417A8001867DDE300D9D37B /* VERSION */; };
94FBB77518B15698007CAE45 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94FBB77418B15698007CAE45 /* AVFoundation.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -96,6 +97,7 @@
9417A7FD1867DDE300D9D37B /* TPCircularBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPCircularBuffer.h; sourceTree = "<group>"; };
9417A7FF1867DDE300D9D37B /* CHANGELOG */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CHANGELOG; sourceTree = "<group>"; };
9417A8001867DDE300D9D37B /* VERSION */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = VERSION; sourceTree = "<group>"; };
94FBB77418B15698007CAE45 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -103,6 +105,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
94FBB77518B15698007CAE45 /* AVFoundation.framework in Frameworks */,
9417A6CD18658FCD00D9D37B /* GLKit.framework in Frameworks */,
9417A6CB18658FC900D9D37B /* AudioToolbox.framework in Frameworks */,
94056FEE185E5EAF00EB94BA /* CoreGraphics.framework in Frameworks */,
@@ -146,6 +149,7 @@
94056FEA185E5EAF00EB94BA /* Frameworks */ = {
isa = PBXGroup;
children = (
94FBB77418B15698007CAE45 /* AVFoundation.framework */,
9417A6CC18658FCD00D9D37B /* GLKit.framework */,
9417A6CA18658FC900D9D37B /* AudioToolbox.framework */,
94056FEB185E5EAF00EB94BA /* Foundation.framework */,
@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "94056FE7185E5EAF00EB94BA"
BuildableName = "EZAudioOpenGLWaveformExample.app"
BlueprintName = "EZAudioOpenGLWaveformExample"
ReferencedContainer = "container:EZAudioOpenGLWaveformExample.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9405700B185E5EAF00EB94BA"
BuildableName = "EZAudioOpenGLWaveformExampleTests.xctest"
BlueprintName = "EZAudioOpenGLWaveformExampleTests"
ReferencedContainer = "container:EZAudioOpenGLWaveformExample.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "94056FE7185E5EAF00EB94BA"
BuildableName = "EZAudioOpenGLWaveformExample.app"
BlueprintName = "EZAudioOpenGLWaveformExample"
ReferencedContainer = "container:EZAudioOpenGLWaveformExample.xcodeproj">
</BuildableReference>
</MacroExpansion>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<BuildableProductRunnable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "94056FE7185E5EAF00EB94BA"
BuildableName = "EZAudioOpenGLWaveformExample.app"
BlueprintName = "EZAudioOpenGLWaveformExample"
ReferencedContainer = "container:EZAudioOpenGLWaveformExample.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<BuildableProductRunnable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "94056FE7185E5EAF00EB94BA"
BuildableName = "EZAudioOpenGLWaveformExample.app"
BlueprintName = "EZAudioOpenGLWaveformExample"
ReferencedContainer = "container:EZAudioOpenGLWaveformExample.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

Some files were not shown because too many files have changed in this diff Show More