├── AudioTrimmer ├── .DS_Store ├── Assets.xcassets │ ├── Contents.json │ ├── .DS_Store │ ├── stop_record.imageset │ │ ├── stop_record1@3x.png │ │ ├── stop_record1@3x-1.png │ │ └── Contents.json │ ├── start_record.imageset │ │ ├── start_record1@3x.png │ │ ├── start_record1@3x-1.png │ │ └── Contents.json │ ├── audio_edit_icon.imageset │ │ ├── audio_edit_icon@2x.png │ │ ├── audio_edit_icon@3x.png │ │ └── Contents.json │ ├── audio_pause_icon.imageset │ │ ├── blue_pause_icon@3x.png │ │ ├── blue_pause_icon@3x-1.png │ │ └── Contents.json │ ├── audio_placeholder.imageset │ │ ├── audio_placeholder.png │ │ ├── audio_placeholder-1.png │ │ └── Contents.json │ ├── audio_trim_icon.imageset │ │ ├── fill_crop_icon@3x-1.png │ │ ├── fill_crop_icon@3x.png │ │ └── Contents.json │ ├── video_play_icon.imageset │ │ ├── blue_play_icon@3x-1.png │ │ ├── blue_play_icon@3x.png │ │ └── Contents.json │ ├── audio_play_icon.imageset │ │ ├── audio_start_record@3x.png │ │ ├── audio_start_record@3x-1.png │ │ └── Contents.json │ ├── audio_reset_icon.imageset │ │ ├── audio_reset_icon@3x.png │ │ ├── audio_reset_icon@3x-1.png │ │ └── Contents.json │ ├── audio_trim_fill_icon.imageset │ │ ├── fill_crop_icon@3x-1.png │ │ ├── fill_crop_icon@3x.png │ │ └── Contents.json │ ├── audiotrimmer_left_thumb.imageset │ │ ├── video_cut_line_new@3x.png │ │ ├── video_cut_line_new@3x-1.png │ │ └── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── AudioTrimmerController │ ├── .DS_Store │ ├── NSTimer │ │ ├── NSTimer+Addition.h │ │ ├── NSTimer+Addition.m │ │ ├── NSTimer+Blocks.m │ │ └── NSTimer+Blocks.h │ ├── EZAudio │ │ ├── EZAudioOSX.h │ │ ├── EZAudioiOS.h │ │ ├── EZPlot.m │ │ ├── EZAudioFloatData.h │ │ ├── EZAudioFloatData.m │ │ ├── EZAudioFloatConverter.h │ │ ├── EZAudioDisplayLink.h │ │ ├── TPCircularBuffer.c │ │ ├── EZAudioDisplayLink.m │ │ ├── EZPlot.h │ │ ├── EZAudioDevice.h │ │ ├── TPCircularBuffer.h │ │ ├── EZAudioFloatConverter.m │ │ ├── EZAudioPlot.h │ │ ├── EZAudio.m │ │ ├── EZAudioPlotGL.h │ │ ├── EZAudioPlot.m │ │ ├── EZAudioPlayer.m │ │ ├── EZAudioFFT.m │ │ └── EZRecorder.m │ └── AudioTrimmerViewController.h ├── AppDelegate.h ├── ViewController.h ├── main.m ├── Info.plist ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── ViewController.m └── AppDelegate.m ├── Screenshots ├── logo.jpg ├── Screen1.png ├── Screen2.png ├── Screen3.png ├── Screen4.png ├── AudioTrimmer.gif └── ReadMe.txt ├── AudioTrimmer.xcodeproj ├── xcuserdata │ └── martin.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ ├── xcschememanagement.plist │ │ └── AudioEditor.xcscheme └── project.xcworkspace │ ├── xcuserdata │ └── martin.xcuserdatad │ │ └── UserInterfaceState.xcuserstate │ └── contents.xcworkspacedata ├── LICENSE └── README.md /AudioTrimmer/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/AudioTrimmer/.DS_Store -------------------------------------------------------------------------------- /Screenshots/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/Screenshots/logo.jpg -------------------------------------------------------------------------------- /Screenshots/Screen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/Screenshots/Screen1.png -------------------------------------------------------------------------------- /Screenshots/Screen2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/Screenshots/Screen2.png -------------------------------------------------------------------------------- /Screenshots/Screen3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/Screenshots/Screen3.png -------------------------------------------------------------------------------- /Screenshots/Screen4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/Screenshots/Screen4.png -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Screenshots/AudioTrimmer.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/Screenshots/AudioTrimmer.gif -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/AudioTrimmer/Assets.xcassets/.DS_Store -------------------------------------------------------------------------------- /AudioTrimmer/AudioTrimmerController/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/AudioTrimmer/AudioTrimmerController/.DS_Store -------------------------------------------------------------------------------- /AudioTrimmer.xcodeproj/xcuserdata/martin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/stop_record.imageset/stop_record1@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/AudioTrimmer/Assets.xcassets/stop_record.imageset/stop_record1@3x.png -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/start_record.imageset/start_record1@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/AudioTrimmer/Assets.xcassets/start_record.imageset/start_record1@3x.png -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/stop_record.imageset/stop_record1@3x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/AudioTrimmer/Assets.xcassets/stop_record.imageset/stop_record1@3x-1.png -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/start_record.imageset/start_record1@3x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/AudioTrimmer/Assets.xcassets/start_record.imageset/start_record1@3x-1.png -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/audio_edit_icon.imageset/audio_edit_icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/AudioTrimmer/Assets.xcassets/audio_edit_icon.imageset/audio_edit_icon@2x.png -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/audio_edit_icon.imageset/audio_edit_icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/AudioTrimmer/Assets.xcassets/audio_edit_icon.imageset/audio_edit_icon@3x.png -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/audio_pause_icon.imageset/blue_pause_icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/AudioTrimmer/Assets.xcassets/audio_pause_icon.imageset/blue_pause_icon@3x.png -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/audio_placeholder.imageset/audio_placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/AudioTrimmer/Assets.xcassets/audio_placeholder.imageset/audio_placeholder.png -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/audio_trim_icon.imageset/fill_crop_icon@3x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/AudioTrimmer/Assets.xcassets/audio_trim_icon.imageset/fill_crop_icon@3x-1.png -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/audio_trim_icon.imageset/fill_crop_icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/AudioTrimmer/Assets.xcassets/audio_trim_icon.imageset/fill_crop_icon@3x.png -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/video_play_icon.imageset/blue_play_icon@3x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/AudioTrimmer/Assets.xcassets/video_play_icon.imageset/blue_play_icon@3x-1.png -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/video_play_icon.imageset/blue_play_icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/AudioTrimmer/Assets.xcassets/video_play_icon.imageset/blue_play_icon@3x.png -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/audio_pause_icon.imageset/blue_pause_icon@3x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/AudioTrimmer/Assets.xcassets/audio_pause_icon.imageset/blue_pause_icon@3x-1.png -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/audio_placeholder.imageset/audio_placeholder-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/AudioTrimmer/Assets.xcassets/audio_placeholder.imageset/audio_placeholder-1.png -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/audio_play_icon.imageset/audio_start_record@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/AudioTrimmer/Assets.xcassets/audio_play_icon.imageset/audio_start_record@3x.png -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/audio_reset_icon.imageset/audio_reset_icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/AudioTrimmer/Assets.xcassets/audio_reset_icon.imageset/audio_reset_icon@3x.png -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/audio_play_icon.imageset/audio_start_record@3x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/AudioTrimmer/Assets.xcassets/audio_play_icon.imageset/audio_start_record@3x-1.png -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/audio_reset_icon.imageset/audio_reset_icon@3x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/AudioTrimmer/Assets.xcassets/audio_reset_icon.imageset/audio_reset_icon@3x-1.png -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/audio_trim_fill_icon.imageset/fill_crop_icon@3x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/AudioTrimmer/Assets.xcassets/audio_trim_fill_icon.imageset/fill_crop_icon@3x-1.png -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/audio_trim_fill_icon.imageset/fill_crop_icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/AudioTrimmer/Assets.xcassets/audio_trim_fill_icon.imageset/fill_crop_icon@3x.png -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/audiotrimmer_left_thumb.imageset/video_cut_line_new@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/AudioTrimmer/Assets.xcassets/audiotrimmer_left_thumb.imageset/video_cut_line_new@3x.png -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/audiotrimmer_left_thumb.imageset/video_cut_line_new@3x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/AudioTrimmer/Assets.xcassets/audiotrimmer_left_thumb.imageset/video_cut_line_new@3x-1.png -------------------------------------------------------------------------------- /AudioTrimmer.xcodeproj/project.xcworkspace/xcuserdata/martin.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intuz-production/AudioTrimmer-iOS/HEAD/AudioTrimmer.xcodeproj/project.xcworkspace/xcuserdata/martin.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /AudioTrimmer.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/start_record.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "start_record1@3x-1.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "start_record1@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/stop_record.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "stop_record1@3x-1.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "stop_record1@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/audio_edit_icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "audio_edit_icon@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "audio_edit_icon@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/audio_trim_icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "fill_crop_icon@3x-1.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "fill_crop_icon@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/video_play_icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "blue_play_icon@3x-1.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "blue_play_icon@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/audio_pause_icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "blue_pause_icon@3x-1.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "blue_pause_icon@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/audio_placeholder.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "audio_placeholder.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "audio_placeholder-1.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/audio_reset_icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "audio_reset_icon@3x-1.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "audio_reset_icon@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/audio_trim_fill_icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "fill_crop_icon@3x-1.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "fill_crop_icon@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/audio_play_icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "audio_start_record@3x-1.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "audio_start_record@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/audiotrimmer_left_thumb.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "video_cut_line_new@3x-1.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "video_cut_line_new@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /AudioTrimmer/AudioTrimmerController/NSTimer/NSTimer+Addition.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSTimer+Addition.h 3 | // iOS-Categories (https://github.com/shaojiankui/iOS-Categories) 4 | // 5 | // Created by Jakey on 14/12/15. 6 | // Copyright (c) 2014年 www.skyfox.org. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSTimer (Addition) 12 | /** 13 | * @brief 暂停NSTimer 14 | */ 15 | - (void)pauseTimer; 16 | /** 17 | * @brief 开始NSTimer 18 | */ 19 | - (void)resumeTimer; 20 | /** 21 | * @brief 延迟开始NSTimer 22 | */ 23 | - (void)resumeTimerAfterTimeInterval:(NSTimeInterval)interval; 24 | @end 25 | -------------------------------------------------------------------------------- /AudioTrimmer.xcodeproj/xcuserdata/martin.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | AudioEditor.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 5BCF55161EF7E109003B7A92 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /AudioTrimmer/AudioTrimmerController/NSTimer/NSTimer+Addition.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSTimer+Addition.m 3 | // iOS-Categories (https://github.com/shaojiankui/iOS-Categories) 4 | // 5 | // Created by Jakey on 14/12/15. 6 | // Copyright (c) 2014年 www.skyfox.org. All rights reserved. 7 | // 8 | 9 | #import "NSTimer+Addition.h" 10 | 11 | @implementation NSTimer (Addition) 12 | /** 13 | * @brief 暂停NSTimer 14 | */ 15 | -(void)pauseTimer 16 | { 17 | if (![self isValid]) { 18 | return ; 19 | } 20 | [self setFireDate:[NSDate distantFuture]]; 21 | } 22 | /** 23 | * @brief 开始NSTimer 24 | */ 25 | -(void)resumeTimer 26 | { 27 | if (![self isValid]) { 28 | return ; 29 | } 30 | [self setFireDate:[NSDate date]]; 31 | } 32 | /** 33 | * @brief 延迟开始NSTimer 34 | */ 35 | - (void)resumeTimerAfterTimeInterval:(NSTimeInterval)interval 36 | { 37 | if (![self isValid]) { 38 | return ; 39 | } 40 | [self setFireDate:[NSDate dateWithTimeIntervalSinceNow:interval]]; 41 | } 42 | @end 43 | -------------------------------------------------------------------------------- /AudioTrimmer/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | } 43 | ], 44 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Intuz-production 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /AudioTrimmer/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2018 INTUZ 6 | 7 | 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: 8 | 9 | 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. 10 | 11 | */ 12 | 13 | 14 | #import 15 | 16 | @interface AppDelegate : UIResponder 17 | 18 | @property (strong, nonatomic) UIWindow *window; 19 | 20 | 21 | @end 22 | 23 | -------------------------------------------------------------------------------- /AudioTrimmer/AudioTrimmerController/NSTimer/NSTimer+Blocks.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSTimer+Blocks.m 3 | // 4 | // Created by Jiva DeVoe on 1/14/11. 5 | // Copyright 2011 Random Ideas, LLC. All rights reserved. 6 | // 7 | 8 | #import "NSTimer+Blocks.h" 9 | 10 | @implementation NSTimer (Blocks) 11 | 12 | +(id)scheduledTimerWithTimeInterval:(NSTimeInterval)inTimeInterval block:(void (^)(void))inBlock repeats:(BOOL)inRepeats 13 | { 14 | void (^block)(void) = [inBlock copy]; 15 | id ret = [self scheduledTimerWithTimeInterval:inTimeInterval target:self selector:@selector(jdExecuteSimpleBlock:) userInfo:block repeats:inRepeats]; 16 | return ret; 17 | } 18 | 19 | +(id)timerWithTimeInterval:(NSTimeInterval)inTimeInterval block:(void (^)(void))inBlock repeats:(BOOL)inRepeats 20 | { 21 | void (^block)(void) = [inBlock copy]; 22 | id ret = [self timerWithTimeInterval:inTimeInterval target:self selector:@selector(jdExecuteSimpleBlock:) userInfo:block repeats:inRepeats]; 23 | return ret; 24 | } 25 | 26 | +(void)jdExecuteSimpleBlock:(NSTimer *)inTimer; 27 | { 28 | if([inTimer userInfo]) 29 | { 30 | void (^block)(void) = (void (^)(void))[inTimer userInfo]; 31 | block(); 32 | } 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /AudioTrimmer/ViewController.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2018 INTUZ 6 | 7 | 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: 8 | 9 | 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. 10 | 11 | */ 12 | 13 | 14 | #import 15 | 16 | @interface ViewController : UIViewController 17 | 18 | - (IBAction)btnPresentAudioTrimmer:(id)sender; 19 | - (IBAction)btnPushAudioTrimmer:(id)sender; 20 | 21 | @end 22 | 23 | -------------------------------------------------------------------------------- /AudioTrimmer/main.m: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2018 INTUZ 6 | 7 | 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: 8 | 9 | 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. 10 | 11 | */ 12 | 13 | #import 14 | #import "AppDelegate.h" 15 | 16 | int main(int argc, char * argv[]) { 17 | @autoreleasepool { 18 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /AudioTrimmer/AudioTrimmerController/EZAudio/EZAudioOSX.h: -------------------------------------------------------------------------------- 1 | // 2 | // EZAudioOSX.m 3 | // EZAudio 4 | // 5 | // Created by Tommaso Piazza on 30/09/15. 6 | // Copyright © 2015 Andrew Breckenridge. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | 26 | #import 27 | -------------------------------------------------------------------------------- /AudioTrimmer/AudioTrimmerController/EZAudio/EZAudioiOS.h: -------------------------------------------------------------------------------- 1 | // 2 | // EZAudioiOS.m 3 | // EZAudio 4 | // 5 | // Created by Tommaso Piazza on 30/09/15. 6 | // Copyright © 2015 Andrew Breckenridge. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | 26 | #import 27 | -------------------------------------------------------------------------------- /AudioTrimmer/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | NSMicrophoneUsageDescription 38 | App need to access microphone. 39 | 40 | 41 | -------------------------------------------------------------------------------- /AudioTrimmer/AudioTrimmerController/NSTimer/NSTimer+Blocks.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSTimer+Blocks.h 3 | // 4 | // Created by Jiva DeVoe on 1/14/11. 5 | // Copyright 2011 Random Ideas, LLC. All rights reserved. 6 | // 7 | // Copyright (C) 2011 by Random Ideas, LLC 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software andassociated 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: 10 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | // 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. 12 | 13 | // https://github.com/jivadevoe/NSTimer-Blocks 14 | 15 | #import 16 | 17 | @interface NSTimer (Blocks) 18 | +(id)scheduledTimerWithTimeInterval:(NSTimeInterval)inTimeInterval block:(void (^)(void))inBlock repeats:(BOOL)inRepeats; 19 | +(id)timerWithTimeInterval:(NSTimeInterval)inTimeInterval block:(void (^)(void))inBlock repeats:(BOOL)inRepeats; 20 | @end 21 | -------------------------------------------------------------------------------- /AudioTrimmer/AudioTrimmerController/EZAudio/EZPlot.m: -------------------------------------------------------------------------------- 1 | // 2 | // EZPlot.m 3 | // EZAudio 4 | // 5 | // Created by Syed Haris Ali on 11/24/13. 6 | // Copyright (c) 2015 Syed Haris Ali. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | 26 | #import "EZPlot.h" 27 | 28 | @implementation EZPlot 29 | 30 | #pragma mark - Clearing 31 | -(void)clear 32 | { 33 | // Override in subclass 34 | } 35 | 36 | #pragma mark - Get Samples 37 | -(void)updateBuffer:(float *)buffer 38 | withBufferSize:(UInt32)bufferSize 39 | { 40 | // Override in subclass 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Screenshots/ReadMe.txt: -------------------------------------------------------------------------------- 1 | Audio Editor 2 | 3 | Audio Trimmer is a simple component, which lets you trim your audio files on the fly. You can record your audio and get start to trim! 4 | Feature: 5 | • You can select a range of audio to be trimmed.
• Ability to record your audio and trim it.
• Ability to play selected range of audio before trimming.
• After trimming if you don’t like the audio then you can get the original file again by pressing “Reset”. 6 | 7 | Pro’s 8 | • Easy & Fast to make audio modification.
• Audio quality remain as it have in original files.
• You can play audio range before actually trimming the audio. • You can get Original file by press “Reset”.
• You can apply trimming on trimmed audio again. 9 | 10 | Required Framework 11 | #import 12 | #import 13 | 14 | How to use: 15 | To use this component in your project you need to perform below steps: 16 | 17 | 1) Import “AudioEditorViewController.h" file where you want to implement this feature. 18 | 19 | 2) Add below code where you want to implement this component: 20 | 21 | To Present Audio Editor View: 22 | 23 | [AudioEditorViewController presentAudioEditorController:self completion:^(BOOL success, NSURL *trimedFilePath) { 24 | // Do your stuff here.. 25 | NSLog(@"%@", trimedFilePath); 26 | }]; 27 | 28 | To Push Audio Editor View: 29 | 30 | [AudioEditorViewController pushAudioEditorController:self completion:^(BOOL success, NSURL *trimedFilePath) { 31 | // Do your stuff here.. 32 | NSLog(@"%@", trimedFilePath); 33 | }]; 34 | 35 | 36 | 37 | We used below library to complete this feature 38 | 39 | • EZAudio: https://github.com/syedhali/EZAudio 40 | 41 | -------------------------------------------------------------------------------- /AudioTrimmer/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /AudioTrimmer/ViewController.m: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2018 INTUZ 6 | 7 | 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: 8 | 9 | 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. 10 | 11 | */ 12 | 13 | 14 | #import "ViewController.h" 15 | 16 | #import "AudioTrimmerViewController.h" 17 | 18 | @interface ViewController () 19 | 20 | @end 21 | 22 | @implementation ViewController 23 | 24 | - (void)viewDidLoad { 25 | [super viewDidLoad]; 26 | // Do any additional setup after loading the view, typically from a nib. 27 | 28 | self.title = @"Audio Editor"; 29 | } 30 | 31 | 32 | - (void)didReceiveMemoryWarning { 33 | [super didReceiveMemoryWarning]; 34 | // Dispose of any resources that can be recreated. 35 | } 36 | 37 | - (IBAction)btnPresentAudioTrimmer:(id)sender { 38 | [AudioTrimmerViewController presentAudioTrimmerController:self completion:^(BOOL success, NSURL *trimedFilePath) { 39 | // Do your stuff here .. 40 | NSLog(@"%@",trimedFilePath); 41 | }]; 42 | } 43 | 44 | - (IBAction)btnPushAudioTrimmer:(id)sender { 45 | [AudioTrimmerViewController pushAudioTrimmerController:self completion:^(BOOL success, NSURL *trimedFilePath) { 46 | // Do your stuff here .. 47 | NSLog(@"%@",trimedFilePath); 48 | }]; 49 | } 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /AudioTrimmer/AudioTrimmerController/EZAudio/EZAudioFloatData.h: -------------------------------------------------------------------------------- 1 | // 2 | // EZAudioFloatData.h 3 | // EZAudio 4 | // 5 | // Created by Syed Haris Ali on 6/23/15. 6 | // Copyright (c) 2015 Syed Haris Ali. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | 26 | #import 27 | 28 | //------------------------------------------------------------------------------ 29 | #pragma mark - EZAudioFloatData 30 | //------------------------------------------------------------------------------ 31 | 32 | @interface EZAudioFloatData : NSObject 33 | 34 | //------------------------------------------------------------------------------ 35 | 36 | + (instancetype)dataWithNumberOfChannels:(int)numberOfChannels 37 | buffers:(float **)buffers 38 | bufferSize:(UInt32)bufferSize; 39 | 40 | //------------------------------------------------------------------------------ 41 | 42 | @property (nonatomic, assign, readonly) int numberOfChannels; 43 | @property (nonatomic, assign, readonly) float **buffers; 44 | @property (nonatomic, assign, readonly) UInt32 bufferSize; 45 | 46 | //------------------------------------------------------------------------------ 47 | 48 | - (float *)bufferForChannel:(int)channel; 49 | 50 | //------------------------------------------------------------------------------ 51 | 52 | @end -------------------------------------------------------------------------------- /AudioTrimmer/AudioTrimmerController/AudioTrimmerViewController.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2018 INTUZ 6 | 7 | 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: 8 | 9 | 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. 10 | 11 | */ 12 | 13 | 14 | #import 15 | #import 16 | #import 17 | 18 | #import "EZAudio.h" 19 | 20 | typedef void(^CompleteAudioEditing)(BOOL success, NSURL *trimedFilePath); 21 | 22 | @interface AudioTrimmerViewController : UIViewController 23 | { 24 | IBOutlet UIView *viewStartRecord; 25 | IBOutlet UIView *viewRecording; 26 | IBOutlet UIView *viewEditRecording; 27 | IBOutlet UILabel *lblRecordingTime; 28 | 29 | IBOutlet UIView *viewAudioPlotContainer; 30 | IBOutlet EZAudioPlot *audioPlot; 31 | IBOutlet UIImageView *imgViewLeftThumb; 32 | IBOutlet UIImageView *imgViewRightThumb; 33 | IBOutlet UIImageView *imgViewAudioIndicatorLine; 34 | IBOutlet UILabel *lblAudioStartTime; 35 | IBOutlet UILabel *lblAudioEndTime; 36 | IBOutlet UILabel *lblAudioDuration; 37 | 38 | IBOutlet UIButton *btnCancel; 39 | IBOutlet UIButton *btnUpload; 40 | 41 | IBOutlet UIButton *btnReset; 42 | IBOutlet UIButton *btnPlayPause; 43 | IBOutlet UIButton *btnResetAudio; 44 | IBOutlet UIButton *btnEditTrimmedAudio; 45 | 46 | EZAudioFile *audioFile; 47 | AVAudioPlayer *audioPlayer; 48 | AVAudioRecorder *audioRecorder; 49 | NSString *strAudioURL; 50 | NSString *strOriginalAudioURL; 51 | 52 | NSTimer *timerRecording; 53 | NSTimer *timerAudioPlayer; 54 | 55 | BOOL videoTrimTimeChanged; 56 | CGFloat audioDuration; 57 | CGFloat audioTrimStartTime; 58 | CGFloat audioTrimEndTime; 59 | 60 | BOOL isPlaying; 61 | BOOL isAudioTrimmed; 62 | } 63 | 64 | @property (copy, nonatomic) CompleteAudioEditing completionBlock; 65 | 66 | + (void) presentAudioTrimmerController:(UIViewController *)controller completion:(CompleteAudioEditing)complete; 67 | + (void) pushAudioTrimmerController:(UIViewController *)controller completion:(CompleteAudioEditing)complete; 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /AudioTrimmer/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2018 INTUZ 6 | 7 | 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: 8 | 9 | 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. 10 | 11 | */ 12 | 13 | 14 | #import "AppDelegate.h" 15 | 16 | @interface AppDelegate () 17 | 18 | @end 19 | 20 | @implementation AppDelegate 21 | 22 | 23 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 24 | // Override point for customization after application launch. 25 | return YES; 26 | } 27 | 28 | 29 | - (void)applicationWillResignActive:(UIApplication *)application { 30 | // 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. 31 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 32 | } 33 | 34 | 35 | - (void)applicationDidEnterBackground:(UIApplication *)application { 36 | // 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. 37 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 38 | } 39 | 40 | 41 | - (void)applicationWillEnterForeground:(UIApplication *)application { 42 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 43 | } 44 | 45 | 46 | - (void)applicationDidBecomeActive:(UIApplication *)application { 47 | // 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. 48 | } 49 | 50 | 51 | - (void)applicationWillTerminate:(UIApplication *)application { 52 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 53 | } 54 | 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Introduction

2 | INTUZ is presenting an interesting Audio Triming Control to integrate inside your iOS based application. 3 | Please follow the below steps to integrate this control in your next project. 4 | 5 |

6 |

Features

7 | 8 | - Easy & fast audio trimming & modifications process. 9 | - You can select a range of audio to be trimmed. 10 | - Ability to record custom audio for trimming process. 11 | - Ability to play audio before trimming. 12 | - Audio quality remain as it have in original files. 13 | - Fully customizable layout. 14 | 15 |

16 | 17 | ![Alt text](Screenshots/AudioTrimmer.gif?raw=true "Title") 18 | 19 | 20 |

21 |

Getting Started

22 | 23 | > Required Frameworks 24 | 25 | ``` 26 | #import 27 | #import 28 | ``` 29 | 30 | > Steps to Integrate 31 | 32 | 1) Copy `AudioTrimmerController` folder and add it into your project. Also, make sure you select copy items if needed option. 33 | 34 | 2) Add `#import "AudioTrimmerViewController.h"` at the required place on your code. 35 | 36 | 3) Add below code where you want to open audio trimming controller: 37 | 38 | - To Present Audio Trim View: 39 | 40 | ``` 41 | [AudioTrimmerViewController presentAudioTrimmerController:self completion:^(BOOL success, NSURL *trimedFilePath) { 42 | // Do your stuff here .. 43 | NSLog(@"%@",trimedFilePath); 44 | }]; 45 | ``` 46 | 47 | - To Push Audio Trim View: 48 | ``` 49 | [AudioTrimmerViewController pushAudioTrimmerController:self completion:^(BOOL success, NSURL *trimedFilePath) { 50 | // Do your stuff here .. 51 | NSLog(@"%@",trimedFilePath); 52 | }]; 53 | ``` 54 | 55 | Note: Make sure you add below key in info.plist and provide there valid description. 56 | 57 | - Privacy - Microphone Usage Description 58 | 59 | 60 |

61 |

Bugs and Feedback

62 | For bugs, questions and discussions please use the Github Issues. 63 | 64 |

65 |

License

66 | The MIT License (MIT) 67 |

68 | Copyright (c) 2018 INTUZ 69 |

70 | 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: 71 |

72 | 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. 73 | 74 |

75 |

Acknowledgments

76 | 77 |
78 | - EZAudio 79 | 80 |
81 |
82 |

83 | 84 | -------------------------------------------------------------------------------- /AudioTrimmer/AudioTrimmerController/EZAudio/EZAudioFloatData.m: -------------------------------------------------------------------------------- 1 | // 2 | // EZAudioFloatData.m 3 | // EZAudio 4 | // 5 | // Created by Syed Haris Ali on 6/23/15. 6 | // Copyright (c) 2015 Syed Haris Ali. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | 26 | #import "EZAudioFloatData.h" 27 | #import "EZAudioUtilities.h" 28 | 29 | //------------------------------------------------------------------------------ 30 | #pragma mark - EZAudioFloatData 31 | //------------------------------------------------------------------------------ 32 | 33 | @interface EZAudioFloatData () 34 | @property (nonatomic, assign, readwrite) int numberOfChannels; 35 | @property (nonatomic, assign, readwrite) float **buffers; 36 | @property (nonatomic, assign, readwrite) UInt32 bufferSize; 37 | @end 38 | 39 | //------------------------------------------------------------------------------ 40 | 41 | @implementation EZAudioFloatData 42 | 43 | //------------------------------------------------------------------------------ 44 | 45 | - (void)dealloc 46 | { 47 | [EZAudioUtilities freeFloatBuffers:self.buffers 48 | numberOfChannels:self.numberOfChannels]; 49 | } 50 | 51 | //------------------------------------------------------------------------------ 52 | 53 | + (instancetype)dataWithNumberOfChannels:(int)numberOfChannels 54 | buffers:(float **)buffers 55 | bufferSize:(UInt32)bufferSize 56 | { 57 | id data = [[self alloc] init]; 58 | size_t size = sizeof(float) * bufferSize; 59 | float **buffersCopy = [EZAudioUtilities floatBuffersWithNumberOfFrames:bufferSize 60 | numberOfChannels:numberOfChannels]; 61 | for (int i = 0; i < numberOfChannels; i++) 62 | { 63 | memcpy(buffersCopy[i], buffers[i], size); 64 | } 65 | ((EZAudioFloatData *)data).buffers = buffersCopy; 66 | ((EZAudioFloatData *)data).bufferSize = bufferSize; 67 | ((EZAudioFloatData *)data).numberOfChannels = numberOfChannels; 68 | return data; 69 | } 70 | 71 | //------------------------------------------------------------------------------ 72 | 73 | - (float *)bufferForChannel:(int)channel 74 | { 75 | float *buffer = NULL; 76 | if (channel < self.numberOfChannels) 77 | { 78 | buffer = self.buffers[channel]; 79 | } 80 | return buffer; 81 | } 82 | 83 | //------------------------------------------------------------------------------ 84 | 85 | @end -------------------------------------------------------------------------------- /AudioTrimmer/AudioTrimmerController/EZAudio/EZAudioFloatConverter.h: -------------------------------------------------------------------------------- 1 | // 2 | // EZAudioFloatConverter.h 3 | // EZAudio 4 | // 5 | // Created by Syed Haris Ali on 6/23/15. 6 | // Copyright (c) 2015 Syed Haris Ali. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | 26 | #import 27 | #import 28 | 29 | //------------------------------------------------------------------------------ 30 | #pragma mark - Constants 31 | //------------------------------------------------------------------------------ 32 | 33 | FOUNDATION_EXPORT UInt32 const EZAudioFloatConverterDefaultPacketSize; 34 | 35 | //------------------------------------------------------------------------------ 36 | #pragma mark - EZAudioFloatConverter 37 | //------------------------------------------------------------------------------ 38 | 39 | @interface EZAudioFloatConverter : NSObject 40 | 41 | //------------------------------------------------------------------------------ 42 | #pragma mark - Class Methods 43 | //------------------------------------------------------------------------------ 44 | 45 | + (instancetype)converterWithInputFormat:(AudioStreamBasicDescription)inputFormat; 46 | 47 | //------------------------------------------------------------------------------ 48 | #pragma mark - Properties 49 | //------------------------------------------------------------------------------ 50 | 51 | @property (nonatomic, assign, readonly) AudioStreamBasicDescription inputFormat; 52 | @property (nonatomic, assign, readonly) AudioStreamBasicDescription floatFormat; 53 | 54 | //------------------------------------------------------------------------------ 55 | #pragma mark - Instance Methods 56 | //------------------------------------------------------------------------------ 57 | 58 | - (instancetype)initWithInputFormat:(AudioStreamBasicDescription)inputFormat; 59 | 60 | //------------------------------------------------------------------------------ 61 | 62 | - (void)convertDataFromAudioBufferList:(AudioBufferList *)audioBufferList 63 | withNumberOfFrames:(UInt32)frames 64 | toFloatBuffers:(float **)buffers; 65 | 66 | //------------------------------------------------------------------------------ 67 | 68 | - (void)convertDataFromAudioBufferList:(AudioBufferList *)audioBufferList 69 | withNumberOfFrames:(UInt32)frames 70 | toFloatBuffers:(float **)buffers 71 | packetDescriptions:(AudioStreamPacketDescription *)packetDescriptions; 72 | 73 | //------------------------------------------------------------------------------ 74 | 75 | @end 76 | -------------------------------------------------------------------------------- /AudioTrimmer.xcodeproj/xcuserdata/martin.xcuserdatad/xcschemes/AudioEditor.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 55 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 74 | 76 | 82 | 83 | 84 | 85 | 87 | 88 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /AudioTrimmer/AudioTrimmerController/EZAudio/EZAudioDisplayLink.h: -------------------------------------------------------------------------------- 1 | // 2 | // EZAudioDisplayLink.h 3 | // EZAudio 4 | // 5 | // Created by Syed Haris Ali on 6/25/15. 6 | // Copyright (c) 2015 Syed Haris Ali. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | 26 | #import 27 | #import 28 | 29 | @class EZAudioDisplayLink; 30 | 31 | //------------------------------------------------------------------------------ 32 | #pragma mark - EZAudioDisplayLinkDelegate 33 | //------------------------------------------------------------------------------ 34 | 35 | /** 36 | The EZAudioDisplayLinkDelegate provides a means for an EZAudioDisplayLink instance to notify a receiver when it should redraw itself. 37 | */ 38 | @protocol EZAudioDisplayLinkDelegate 39 | 40 | @required 41 | /** 42 | Required method for an EZAudioDisplayLinkDelegate to implement. This fires at the screen's display rate (typically 60 fps). 43 | @param displayLink An EZAudioDisplayLink instance used by a receiver to draw itself at the screen's refresh rate. 44 | */ 45 | - (void)displayLinkNeedsDisplay:(EZAudioDisplayLink *)displayLink; 46 | 47 | @end 48 | 49 | //------------------------------------------------------------------------------ 50 | #pragma mark - EZAudioDisplayLink 51 | //------------------------------------------------------------------------------ 52 | 53 | /** 54 | The EZAudioDisplayLink provides a cross-platform (iOS and Mac) abstraction over the CADisplayLink for iOS and CVDisplayLink for Mac. The purpose of this class is to provide an accurate timer for views that need to redraw themselves at 60 fps. This class is used by the EZAudioPlot and, eventually, the EZAudioPlotGL to provide a timer mechanism to draw real-time plots. 55 | */ 56 | @interface EZAudioDisplayLink : NSObject 57 | 58 | //------------------------------------------------------------------------------ 59 | #pragma mark - Class Methods 60 | //------------------------------------------------------------------------------ 61 | 62 | /** 63 | Class method to create an EZAudioDisplayLink. The caller should implement the EZAudioDisplayLinkDelegate protocol to receive the `displayLinkNeedsDisplay:` delegate method to know when to redraw itself. 64 | @param delegate An instance that implements the EZAudioDisplayLinkDelegate protocol. 65 | @return An instance of the EZAudioDisplayLink. 66 | */ 67 | + (instancetype)displayLinkWithDelegate:(id)delegate; 68 | 69 | //------------------------------------------------------------------------------ 70 | #pragma mark - Properties 71 | //------------------------------------------------------------------------------ 72 | 73 | /** 74 | The EZAudioDisplayLinkDelegate for which to receive the redraw calls. 75 | */ 76 | @property (nonatomic, weak) id delegate; 77 | 78 | //------------------------------------------------------------------------------ 79 | #pragma mark - Instance Methods 80 | //------------------------------------------------------------------------------ 81 | 82 | /** 83 | Method to start the display link and provide the `displayLinkNeedsDisplay:` calls to the `delegate` 84 | */ 85 | - (void)start; 86 | 87 | /** 88 | Method to stop the display link from providing the `displayLinkNeedsDisplay:` calls to the `delegate` 89 | */ 90 | - (void)stop; 91 | 92 | //------------------------------------------------------------------------------ 93 | 94 | @end -------------------------------------------------------------------------------- /AudioTrimmer/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 34 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /AudioTrimmer/AudioTrimmerController/EZAudio/TPCircularBuffer.c: -------------------------------------------------------------------------------- 1 | // 2 | // TPCircularBuffer.c 3 | // Circular/Ring buffer implementation 4 | // 5 | // https://github.com/michaeltyson/TPCircularBuffer 6 | // 7 | // Created by Michael Tyson on 10/12/2011. 8 | // 9 | // Copyright (C) 2012-2013 A Tasty Pixel 10 | // 11 | // This software is provided 'as-is', without any express or implied 12 | // warranty. In no event will the authors be held liable for any damages 13 | // arising from the use of this software. 14 | // 15 | // Permission is granted to anyone to use this software for any purpose, 16 | // including commercial applications, and to alter it and redistribute it 17 | // freely, subject to the following restrictions: 18 | // 19 | // 1. The origin of this software must not be misrepresented; you must not 20 | // claim that you wrote the original software. If you use this software 21 | // in a product, an acknowledgment in the product documentation would be 22 | // appreciated but is not required. 23 | // 24 | // 2. Altered source versions must be plainly marked as such, and must not be 25 | // misrepresented as being the original software. 26 | // 27 | // 3. This notice may not be removed or altered from any source distribution. 28 | // 29 | 30 | #include "TPCircularBuffer.h" 31 | #include 32 | #include 33 | #include 34 | 35 | #define reportResult(result,operation) (_reportResult((result),(operation),strrchr(__FILE__, '/')+1,__LINE__)) 36 | static inline bool _reportResult(kern_return_t result, const char *operation, const char* file, int line) { 37 | if ( result != ERR_SUCCESS ) { 38 | printf("%s:%d: %s: %s\n", file, line, operation, mach_error_string(result)); 39 | return false; 40 | } 41 | return true; 42 | } 43 | 44 | bool _TPCircularBufferInit(TPCircularBuffer *buffer, int32_t length, size_t structSize) { 45 | 46 | assert(length > 0); 47 | 48 | if ( structSize != sizeof(TPCircularBuffer) ) { 49 | fprintf(stderr, "TPCircularBuffer: Header version mismatch. Check for old versions of TPCircularBuffer in your project\n"); 50 | abort(); 51 | } 52 | 53 | // Keep trying until we get our buffer, needed to handle race conditions 54 | int retries = 3; 55 | while ( true ) { 56 | 57 | buffer->length = (int32_t)round_page(length); // We need whole page sizes 58 | 59 | // Temporarily allocate twice the length, so we have the contiguous address space to 60 | // support a second instance of the buffer directly after 61 | vm_address_t bufferAddress; 62 | kern_return_t result = vm_allocate(mach_task_self(), 63 | &bufferAddress, 64 | buffer->length * 2, 65 | VM_FLAGS_ANYWHERE); // allocate anywhere it'll fit 66 | if ( result != ERR_SUCCESS ) { 67 | if ( retries-- == 0 ) { 68 | reportResult(result, "Buffer allocation"); 69 | return false; 70 | } 71 | // Try again if we fail 72 | continue; 73 | } 74 | 75 | // Now replace the second half of the allocation with a virtual copy of the first half. Deallocate the second half... 76 | result = vm_deallocate(mach_task_self(), 77 | bufferAddress + buffer->length, 78 | buffer->length); 79 | if ( result != ERR_SUCCESS ) { 80 | if ( retries-- == 0 ) { 81 | reportResult(result, "Buffer deallocation"); 82 | return false; 83 | } 84 | // If this fails somehow, deallocate the whole region and try again 85 | vm_deallocate(mach_task_self(), bufferAddress, buffer->length); 86 | continue; 87 | } 88 | 89 | // Re-map the buffer to the address space immediately after the buffer 90 | vm_address_t virtualAddress = bufferAddress + buffer->length; 91 | vm_prot_t cur_prot, max_prot; 92 | result = vm_remap(mach_task_self(), 93 | &virtualAddress, // mirror target 94 | buffer->length, // size of mirror 95 | 0, // auto alignment 96 | 0, // force remapping to virtualAddress 97 | mach_task_self(), // same task 98 | bufferAddress, // mirror source 99 | 0, // MAP READ-WRITE, NOT COPY 100 | &cur_prot, // unused protection struct 101 | &max_prot, // unused protection struct 102 | VM_INHERIT_DEFAULT); 103 | if ( result != ERR_SUCCESS ) { 104 | if ( retries-- == 0 ) { 105 | reportResult(result, "Remap buffer memory"); 106 | return false; 107 | } 108 | // If this remap failed, we hit a race condition, so deallocate and try again 109 | vm_deallocate(mach_task_self(), bufferAddress, buffer->length); 110 | continue; 111 | } 112 | 113 | if ( virtualAddress != bufferAddress+buffer->length ) { 114 | // If the memory is not contiguous, clean up both allocated buffers and try again 115 | if ( retries-- == 0 ) { 116 | printf("Couldn't map buffer memory to end of buffer\n"); 117 | return false; 118 | } 119 | 120 | vm_deallocate(mach_task_self(), virtualAddress, buffer->length); 121 | vm_deallocate(mach_task_self(), bufferAddress, buffer->length); 122 | continue; 123 | } 124 | 125 | buffer->buffer = (void*)bufferAddress; 126 | buffer->fillCount = 0; 127 | buffer->head = buffer->tail = 0; 128 | buffer->atomic = true; 129 | 130 | return true; 131 | } 132 | return false; 133 | } 134 | 135 | void TPCircularBufferCleanup(TPCircularBuffer *buffer) { 136 | vm_deallocate(mach_task_self(), (vm_address_t)buffer->buffer, buffer->length * 2); 137 | memset(buffer, 0, sizeof(TPCircularBuffer)); 138 | } 139 | 140 | void TPCircularBufferClear(TPCircularBuffer *buffer) { 141 | int32_t fillCount; 142 | if ( TPCircularBufferTail(buffer, &fillCount) ) { 143 | TPCircularBufferConsume(buffer, fillCount); 144 | } 145 | } 146 | 147 | void TPCircularBufferSetAtomic(TPCircularBuffer *buffer, bool atomic) { 148 | buffer->atomic = atomic; 149 | } 150 | -------------------------------------------------------------------------------- /AudioTrimmer/AudioTrimmerController/EZAudio/EZAudioDisplayLink.m: -------------------------------------------------------------------------------- 1 | // 2 | // EZAudioDisplayLink.m 3 | // EZAudio 4 | // 5 | // Created by Syed Haris Ali on 6/25/15. 6 | // Copyright (c) 2015 Syed Haris Ali. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | 26 | #import "EZAudioDisplayLink.h" 27 | 28 | //------------------------------------------------------------------------------ 29 | #pragma mark - CVDisplayLink Callback (Declaration) 30 | //------------------------------------------------------------------------------ 31 | 32 | #if TARGET_OS_IPHONE 33 | #elif TARGET_OS_MAC 34 | static CVReturn EZAudioDisplayLinkCallback(CVDisplayLinkRef displayLinkRef, 35 | const CVTimeStamp *now, 36 | const CVTimeStamp *outputTime, 37 | CVOptionFlags flagsIn, 38 | CVOptionFlags *flagsOut, 39 | void *displayLinkContext); 40 | #endif 41 | 42 | //------------------------------------------------------------------------------ 43 | #pragma mark - EZAudioDisplayLink (Interface Extension) 44 | //------------------------------------------------------------------------------ 45 | 46 | @interface EZAudioDisplayLink () 47 | #if TARGET_OS_IPHONE 48 | @property (nonatomic, strong) CADisplayLink *displayLink; 49 | #elif TARGET_OS_MAC 50 | @property (nonatomic, assign) CVDisplayLinkRef displayLink; 51 | #endif 52 | @property (nonatomic, assign) BOOL stopped; 53 | @end 54 | 55 | //------------------------------------------------------------------------------ 56 | #pragma mark - EZAudioDisplayLink (Implementation) 57 | //------------------------------------------------------------------------------ 58 | 59 | @implementation EZAudioDisplayLink 60 | 61 | //------------------------------------------------------------------------------ 62 | #pragma mark - Dealloc 63 | //------------------------------------------------------------------------------ 64 | 65 | - (void)dealloc 66 | { 67 | #if TARGET_OS_IPHONE 68 | [self.displayLink invalidate]; 69 | #elif TARGET_OS_MAC 70 | CVDisplayLinkStop(self.displayLink); 71 | CVDisplayLinkRelease(self.displayLink); 72 | self.displayLink = nil; 73 | #endif 74 | } 75 | 76 | //------------------------------------------------------------------------------ 77 | #pragma mark - Class Initialization 78 | //------------------------------------------------------------------------------ 79 | 80 | + (instancetype)displayLinkWithDelegate:(id)delegate 81 | { 82 | EZAudioDisplayLink *displayLink = [[self alloc] init]; 83 | displayLink.delegate = delegate; 84 | return displayLink; 85 | } 86 | 87 | //------------------------------------------------------------------------------ 88 | #pragma mark - Initialization 89 | //------------------------------------------------------------------------------ 90 | 91 | - (instancetype) init 92 | { 93 | self = [super init]; 94 | if (self) 95 | { 96 | [self setup]; 97 | } 98 | return self; 99 | } 100 | 101 | //------------------------------------------------------------------------------ 102 | #pragma mark - Setup 103 | //------------------------------------------------------------------------------ 104 | 105 | - (void)setup 106 | { 107 | self.stopped = YES; 108 | #if TARGET_OS_IPHONE 109 | self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)]; 110 | [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 111 | #elif TARGET_OS_MAC 112 | CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink); 113 | CVDisplayLinkSetOutputCallback(self.displayLink, 114 | EZAudioDisplayLinkCallback, 115 | (__bridge void *)(self)); 116 | CVDisplayLinkStart(self.displayLink); 117 | #endif 118 | } 119 | 120 | //------------------------------------------------------------------------------ 121 | #pragma mark - Actions 122 | //------------------------------------------------------------------------------ 123 | 124 | - (void)start 125 | { 126 | #if TARGET_OS_IPHONE 127 | self.displayLink.paused = NO; 128 | #elif TARGET_OS_MAC 129 | CVDisplayLinkStart(self.displayLink); 130 | #endif 131 | self.stopped = NO; 132 | } 133 | 134 | //------------------------------------------------------------------------------ 135 | 136 | - (void)stop 137 | { 138 | #if TARGET_OS_IPHONE 139 | self.displayLink.paused = YES; 140 | #elif TARGET_OS_MAC 141 | CVDisplayLinkStop(self.displayLink); 142 | #endif 143 | self.stopped = YES; 144 | } 145 | 146 | //------------------------------------------------------------------------------ 147 | 148 | - (void)update 149 | { 150 | if (!self.stopped) 151 | { 152 | if ([self.delegate respondsToSelector:@selector(displayLinkNeedsDisplay:)]) 153 | { 154 | [self.delegate displayLinkNeedsDisplay:self]; 155 | } 156 | } 157 | } 158 | 159 | //------------------------------------------------------------------------------ 160 | 161 | @end 162 | 163 | //------------------------------------------------------------------------------ 164 | #pragma mark - CVDisplayLink Callback (Implementation) 165 | //------------------------------------------------------------------------------ 166 | 167 | #if TARGET_OS_IPHONE 168 | #elif TARGET_OS_MAC 169 | static CVReturn EZAudioDisplayLinkCallback(CVDisplayLinkRef displayLinkRef, 170 | const CVTimeStamp *now, 171 | const CVTimeStamp *outputTime, 172 | CVOptionFlags flagsIn, 173 | CVOptionFlags *flagsOut, 174 | void *displayLinkContext) 175 | { 176 | EZAudioDisplayLink *displayLink = (__bridge EZAudioDisplayLink*)displayLinkContext; 177 | [displayLink update]; 178 | return kCVReturnSuccess; 179 | } 180 | #endif -------------------------------------------------------------------------------- /AudioTrimmer/AudioTrimmerController/EZAudio/EZPlot.h: -------------------------------------------------------------------------------- 1 | // 2 | // EZPlot.h 3 | // EZAudio 4 | // 5 | // Created by Syed Haris Ali on 11/24/13. 6 | // Copyright (c) 2015 Syed Haris Ali. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | 26 | #import 27 | #import "EZAudioUtilities.h" 28 | 29 | //------------------------------------------------------------------------------ 30 | #pragma mark - Enumerations 31 | //------------------------------------------------------------------------------ 32 | 33 | ///----------------------------------------------------------- 34 | /// @name Plot Types 35 | ///----------------------------------------------------------- 36 | 37 | /** 38 | The types of plots that can be displayed in the view using the data. 39 | */ 40 | typedef NS_ENUM(NSInteger, EZPlotType) 41 | { 42 | /** 43 | Plot that displays only the samples of the current buffer 44 | */ 45 | EZPlotTypeBuffer, 46 | 47 | /** 48 | Plot that displays a rolling history of values using the RMS calculated for each incoming buffer 49 | */ 50 | EZPlotTypeRolling 51 | }; 52 | 53 | /** 54 | EZPlot is a cross-platform (iOS and OSX) class used to subclass the default view type (either UIView or NSView, respectively). 55 | 56 | ## Subclassing Notes 57 | 58 | This class isn't meant to be directly used in practice, but instead establishes the default properties and behaviors subclasses should obey to provide consistent behavior accross multiple types of graphs (i.e. set background color, plot type, should fill in, etc.). Subclasses should make use of the inherited properties from this class to allow all child plots to benefit from the same 59 | */ 60 | #if TARGET_OS_IPHONE 61 | #import 62 | @interface EZPlot : UIView 63 | #elif TARGET_OS_MAC 64 | #import 65 | @interface EZPlot : NSView 66 | #endif 67 | 68 | //------------------------------------------------------------------------------ 69 | #pragma mark - Properties 70 | //------------------------------------------------------------------------------ 71 | 72 | ///----------------------------------------------------------- 73 | /// @name Customizing The Plot's Appearance 74 | ///----------------------------------------------------------- 75 | /** 76 | The default background color of the plot. For iOS the color is specified as a UIColor while for OSX the color is an NSColor. The default value on both platforms is black. 77 | */ 78 | #if TARGET_OS_IPHONE 79 | @property (nonatomic, strong) IBInspectable UIColor *backgroundColor; 80 | #elif TARGET_OS_MAC 81 | @property (nonatomic, strong) IBInspectable NSColor *backgroundColor; 82 | #endif 83 | 84 | /** 85 | The default color of the plot's data (i.e. waveform, y-axis values). For iOS the color is specified as a UIColor while for OSX the color is an NSColor. The default value on both platforms is red. 86 | */ 87 | #if TARGET_OS_IPHONE 88 | @property (nonatomic, strong) IBInspectable UIColor *color; 89 | #elif TARGET_OS_MAC 90 | @property (nonatomic, strong) IBInspectable NSColor *color; 91 | #endif 92 | 93 | /** 94 | The plot's gain value, which controls the scale of the y-axis values. The default value of the gain is 1.0f and should always be greater than 0.0f. 95 | */ 96 | @property (nonatomic, assign) IBInspectable float gain; 97 | 98 | /** 99 | The type of plot as specified by the `EZPlotType` enumeration (i.e. a buffer or rolling plot type). 100 | */ 101 | @property (nonatomic, assign) IBInspectable EZPlotType plotType; 102 | 103 | /** 104 | A boolean indicating whether or not to fill in the graph. A value of YES will make a filled graph (filling in the space between the x-axis and the y-value), while a value of NO will create a stroked graph (connecting the points along the y-axis). 105 | */ 106 | @property (nonatomic, assign) IBInspectable BOOL shouldFill; 107 | 108 | /** 109 | A boolean indicating whether the graph should be rotated along the x-axis to give a mirrored reflection. This is typical for audio plots to produce the classic waveform look. A value of YES will produce a mirrored reflection of the y-values about the x-axis, while a value of NO will only plot the y-values. 110 | */ 111 | @property (nonatomic, assign) IBInspectable BOOL shouldMirror; 112 | 113 | //------------------------------------------------------------------------------ 114 | #pragma mark - Clearing 115 | //------------------------------------------------------------------------------ 116 | 117 | ///----------------------------------------------------------- 118 | /// @name Clearing The Plot 119 | ///----------------------------------------------------------- 120 | 121 | /** 122 | Clears all data from the audio plot (includes both EZPlotTypeBuffer and EZPlotTypeRolling) 123 | */ 124 | -(void)clear; 125 | 126 | //------------------------------------------------------------------------------ 127 | #pragma mark - Get Samples 128 | //------------------------------------------------------------------------------ 129 | 130 | ///----------------------------------------------------------- 131 | /// @name Updating The Plot 132 | ///----------------------------------------------------------- 133 | 134 | /** 135 | 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. 136 | @param buffer A float array of values to map to the y-axis. 137 | @param bufferSize The size of the float array that will be mapped to the y-axis. 138 | @warning The bufferSize is expected to be the same, constant value once initial triggered. For plots using OpenGL a vertex buffer object will be allocated with a maximum buffersize of (2 * the initial given buffer size) to account for any interpolation necessary for filling in the graph. Updates use the glBufferSubData(...) function, which will crash if the buffersize exceeds the initial maximum allocated size. 139 | */ 140 | -(void)updateBuffer:(float *)buffer withBufferSize:(UInt32)bufferSize; 141 | 142 | @end 143 | -------------------------------------------------------------------------------- /AudioTrimmer/AudioTrimmerController/EZAudio/EZAudioDevice.h: -------------------------------------------------------------------------------- 1 | // 2 | // EZAudioDevice.h 3 | // EZAudio 4 | // 5 | // Created by Syed Haris Ali on 6/25/15. 6 | // Copyright (c) 2015 Syed Haris Ali. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | 26 | #import 27 | #import 28 | 29 | #if TARGET_OS_IPHONE 30 | #import 31 | #elif TARGET_OS_MAC 32 | #endif 33 | 34 | /** 35 | The EZAudioDevice provides an interface for getting the available input and output hardware devices on iOS and OSX. On iOS the EZAudioDevice uses the available devices found from the AVAudioSession, while on OSX the EZAudioDevice wraps the AudioHardware API to find any devices that are connected including the built-in devices (for instance, Built-In Microphone, Display Audio). Since the AVAudioSession and AudioHardware APIs are quite different the EZAudioDevice has different properties available on each platform. The EZMicrophone now supports setting any specific EZAudioDevice from the `inputDevices` function. 36 | */ 37 | @interface EZAudioDevice : NSObject 38 | 39 | //------------------------------------------------------------------------------ 40 | #pragma mark - Class Methods 41 | //------------------------------------------------------------------------------ 42 | 43 | //------------------------------------------------------------------------------ 44 | // @name Getting The Devices 45 | //------------------------------------------------------------------------------ 46 | 47 | /** 48 | Provides the current EZAudioDevice that is being used to pull input. 49 | @return An EZAudioDevice instance representing the currently selected input device. 50 | */ 51 | + (EZAudioDevice *)currentInputDevice; 52 | 53 | //------------------------------------------------------------------------------ 54 | 55 | /** 56 | Provides the current EZAudioDevice that is being used to output audio. 57 | @return An EZAudioDevice instance representing the currently selected ouotput device. 58 | */ 59 | + (EZAudioDevice *)currentOutputDevice; 60 | 61 | //------------------------------------------------------------------------------ 62 | 63 | /** 64 | Enumerates all the available input devices and returns the result in an NSArray of EZAudioDevice instances. 65 | @return An NSArray containing EZAudioDevice instances, one for each available input device. 66 | */ 67 | + (NSArray *)inputDevices; 68 | 69 | //------------------------------------------------------------------------------ 70 | 71 | /** 72 | Enumerates all the available output devices and returns the result in an NSArray of EZAudioDevice instances. 73 | @return An NSArray of output EZAudioDevice instances. 74 | */ 75 | + (NSArray *)outputDevices; 76 | 77 | #if TARGET_OS_IPHONE 78 | 79 | //------------------------------------------------------------------------------ 80 | 81 | /** 82 | Enumerates all the available input devices. 83 | - iOS only 84 | @param block When enumerating this block executes repeatedly for each EZAudioDevice found. It contains two arguments - first, the EZAudioDevice found, then a pointer to a stop BOOL to allow breaking out of the enumeration) 85 | */ 86 | + (void)enumerateInputDevicesUsingBlock:(void(^)(EZAudioDevice *device, 87 | BOOL *stop))block; 88 | 89 | //------------------------------------------------------------------------------ 90 | 91 | /** 92 | Enumerates all the available output devices. 93 | - iOS only 94 | @param block When enumerating this block executes repeatedly for each EZAudioDevice found. It contains two arguments - first, the EZAudioDevice found, then a pointer to a stop BOOL to allow breaking out of the enumeration) 95 | */ 96 | + (void)enumerateOutputDevicesUsingBlock:(void (^)(EZAudioDevice *device, 97 | BOOL *stop))block; 98 | 99 | #elif TARGET_OS_MAC 100 | 101 | /** 102 | Enumerates all the available devices and returns the result in an NSArray of EZAudioDevice instances. 103 | - OSX only 104 | @return An NSArray of input and output EZAudioDevice instances. 105 | */ 106 | + (NSArray *)devices; 107 | 108 | //------------------------------------------------------------------------------ 109 | 110 | /** 111 | Enumerates all the available devices. 112 | - OSX only 113 | @param block When enumerating this block executes repeatedly for each EZAudioDevice found. It contains two arguments - first, the EZAudioDevice found, then a pointer to a stop BOOL to allow breaking out of the enumeration) 114 | */ 115 | + (void)enumerateDevicesUsingBlock:(void(^)(EZAudioDevice *device, 116 | BOOL *stop))block; 117 | 118 | #endif 119 | 120 | //------------------------------------------------------------------------------ 121 | #pragma mark - Properties 122 | //------------------------------------------------------------------------------ 123 | 124 | /** 125 | An NSString representing a human-reable version of the device. 126 | */ 127 | @property (nonatomic, copy, readonly) NSString *name; 128 | 129 | #if TARGET_OS_IPHONE 130 | 131 | /** 132 | An AVAudioSessionPortDescription describing an input or output hardware port. 133 | - iOS only 134 | */ 135 | @property (nonatomic, strong, readonly) AVAudioSessionPortDescription *port; 136 | 137 | //------------------------------------------------------------------------------ 138 | 139 | /** 140 | An AVAudioSessionDataSourceDescription describing a specific data source for the `port` provided. 141 | - iOS only 142 | */ 143 | @property (nonatomic, strong, readonly) AVAudioSessionDataSourceDescription *dataSource; 144 | 145 | #elif TARGET_OS_MAC 146 | 147 | /** 148 | An AudioDeviceID representing the device in the AudioHardware API. 149 | - OSX only 150 | */ 151 | @property (nonatomic, assign, readonly) AudioDeviceID deviceID; 152 | 153 | //------------------------------------------------------------------------------ 154 | 155 | /** 156 | An NSString representing the name of the manufacturer of the device. 157 | - OSX only 158 | */ 159 | @property (nonatomic, copy, readonly) NSString *manufacturer; 160 | 161 | //------------------------------------------------------------------------------ 162 | 163 | /** 164 | An NSInteger representing the number of input channels available. 165 | - OSX only 166 | */ 167 | @property (nonatomic, assign, readonly) NSInteger inputChannelCount; 168 | 169 | //------------------------------------------------------------------------------ 170 | 171 | /** 172 | An NSInteger representing the number of output channels available. 173 | - OSX only 174 | */ 175 | @property (nonatomic, assign, readonly) NSInteger outputChannelCount; 176 | 177 | //------------------------------------------------------------------------------ 178 | 179 | /** 180 | An NSString representing the persistent identifier for the AudioDevice. 181 | - OSX only 182 | */ 183 | @property (nonatomic, copy, readonly) NSString *UID; 184 | 185 | #endif 186 | 187 | @end 188 | -------------------------------------------------------------------------------- /AudioTrimmer/AudioTrimmerController/EZAudio/TPCircularBuffer.h: -------------------------------------------------------------------------------- 1 | // 2 | // TPCircularBuffer.h 3 | // Circular/Ring buffer implementation 4 | // 5 | // https://github.com/michaeltyson/TPCircularBuffer 6 | // 7 | // Created by Michael Tyson on 10/12/2011. 8 | // 9 | // 10 | // This implementation makes use of a virtual memory mapping technique that inserts a virtual copy 11 | // of the buffer memory directly after the buffer's end, negating the need for any buffer wrap-around 12 | // logic. Clients can simply use the returned memory address as if it were contiguous space. 13 | // 14 | // The implementation is thread-safe in the case of a single producer and single consumer. 15 | // 16 | // Virtual memory technique originally proposed by Philip Howard (http://vrb.slashusr.org/), and 17 | // adapted to Darwin by Kurt Revis (http://www.snoize.com, 18 | // http://www.snoize.com/Code/PlayBufferedSoundFile.tar.gz) 19 | // 20 | // 21 | // Copyright (C) 2012-2013 A Tasty Pixel 22 | // 23 | // This software is provided 'as-is', without any express or implied 24 | // warranty. In no event will the authors be held liable for any damages 25 | // arising from the use of this software. 26 | // 27 | // Permission is granted to anyone to use this software for any purpose, 28 | // including commercial applications, and to alter it and redistribute it 29 | // freely, subject to the following restrictions: 30 | // 31 | // 1. The origin of this software must not be misrepresented; you must not 32 | // claim that you wrote the original software. If you use this software 33 | // in a product, an acknowledgment in the product documentation would be 34 | // appreciated but is not required. 35 | // 36 | // 2. Altered source versions must be plainly marked as such, and must not be 37 | // misrepresented as being the original software. 38 | // 39 | // 3. This notice may not be removed or altered from any source distribution. 40 | // 41 | 42 | #ifndef TPCircularBuffer_h 43 | #define TPCircularBuffer_h 44 | 45 | #include 46 | #include 47 | #include 48 | 49 | #ifdef __cplusplus 50 | extern "C" { 51 | #endif 52 | 53 | typedef struct { 54 | void *buffer; 55 | int32_t length; 56 | int32_t tail; 57 | int32_t head; 58 | volatile int32_t fillCount; 59 | bool atomic; 60 | } TPCircularBuffer; 61 | 62 | /*! 63 | * Initialise buffer 64 | * 65 | * Note that the length is advisory only: Because of the way the 66 | * memory mirroring technique works, the true buffer length will 67 | * be multiples of the device page size (e.g. 4096 bytes) 68 | * 69 | * @param buffer Circular buffer 70 | * @param length Length of buffer 71 | */ 72 | #define TPCircularBufferInit(buffer, length) \ 73 | _TPCircularBufferInit(buffer, length, sizeof(*buffer)) 74 | bool _TPCircularBufferInit(TPCircularBuffer *buffer, int32_t length, size_t structSize); 75 | 76 | /*! 77 | * Cleanup buffer 78 | * 79 | * Releases buffer resources. 80 | */ 81 | void TPCircularBufferCleanup(TPCircularBuffer *buffer); 82 | 83 | /*! 84 | * Clear buffer 85 | * 86 | * Resets buffer to original, empty state. 87 | * 88 | * This is safe for use by consumer while producer is accessing 89 | * buffer. 90 | */ 91 | void TPCircularBufferClear(TPCircularBuffer *buffer); 92 | 93 | /*! 94 | * Set the atomicity 95 | * 96 | * If you set the atomiticy to false using this method, the buffer will 97 | * not use atomic operations. This can be used to give the compiler a little 98 | * more optimisation opportunities when the buffer is only used on one thread. 99 | * 100 | * Important note: Only set this to false if you know what you're doing! 101 | * 102 | * The default value is true (the buffer will use atomic operations) 103 | * 104 | * @param buffer Circular buffer 105 | * @param atomic Whether the buffer is atomic (default true) 106 | */ 107 | void TPCircularBufferSetAtomic(TPCircularBuffer *buffer, bool atomic); 108 | 109 | // Reading (consuming) 110 | 111 | /*! 112 | * Access end of buffer 113 | * 114 | * This gives you a pointer to the end of the buffer, ready 115 | * for reading, and the number of available bytes to read. 116 | * 117 | * @param buffer Circular buffer 118 | * @param availableBytes On output, the number of bytes ready for reading 119 | * @return Pointer to the first bytes ready for reading, or NULL if buffer is empty 120 | */ 121 | static __inline__ __attribute__((always_inline)) void* TPCircularBufferTail(TPCircularBuffer *buffer, int32_t* availableBytes) { 122 | *availableBytes = buffer->fillCount; 123 | if ( *availableBytes == 0 ) return NULL; 124 | return (void*)((char*)buffer->buffer + buffer->tail); 125 | } 126 | 127 | /*! 128 | * Consume bytes in buffer 129 | * 130 | * This frees up the just-read bytes, ready for writing again. 131 | * 132 | * @param buffer Circular buffer 133 | * @param amount Number of bytes to consume 134 | */ 135 | static __inline__ __attribute__((always_inline)) void TPCircularBufferConsume(TPCircularBuffer *buffer, int32_t amount) { 136 | buffer->tail = (buffer->tail + amount) % buffer->length; 137 | if ( buffer->atomic ) { 138 | OSAtomicAdd32Barrier(-amount, &buffer->fillCount); 139 | } else { 140 | buffer->fillCount -= amount; 141 | } 142 | assert(buffer->fillCount >= 0); 143 | } 144 | 145 | /*! 146 | * Access front of buffer 147 | * 148 | * This gives you a pointer to the front of the buffer, ready 149 | * for writing, and the number of available bytes to write. 150 | * 151 | * @param buffer Circular buffer 152 | * @param availableBytes On output, the number of bytes ready for writing 153 | * @return Pointer to the first bytes ready for writing, or NULL if buffer is full 154 | */ 155 | static __inline__ __attribute__((always_inline)) void* TPCircularBufferHead(TPCircularBuffer *buffer, int32_t* availableBytes) { 156 | *availableBytes = (buffer->length - buffer->fillCount); 157 | if ( *availableBytes == 0 ) return NULL; 158 | return (void*)((char*)buffer->buffer + buffer->head); 159 | } 160 | 161 | // Writing (producing) 162 | 163 | /*! 164 | * Produce bytes in buffer 165 | * 166 | * This marks the given section of the buffer ready for reading. 167 | * 168 | * @param buffer Circular buffer 169 | * @param amount Number of bytes to produce 170 | */ 171 | static __inline__ __attribute__((always_inline)) void TPCircularBufferProduce(TPCircularBuffer *buffer, int32_t amount) { 172 | buffer->head = (buffer->head + amount) % buffer->length; 173 | if ( buffer->atomic ) { 174 | OSAtomicAdd32Barrier(amount, &buffer->fillCount); 175 | } else { 176 | buffer->fillCount += amount; 177 | } 178 | assert(buffer->fillCount <= buffer->length); 179 | } 180 | 181 | /*! 182 | * Helper routine to copy bytes to buffer 183 | * 184 | * This copies the given bytes to the buffer, and marks them ready for reading. 185 | * 186 | * @param buffer Circular buffer 187 | * @param src Source buffer 188 | * @param len Number of bytes in source buffer 189 | * @return true if bytes copied, false if there was insufficient space 190 | */ 191 | static __inline__ __attribute__((always_inline)) bool TPCircularBufferProduceBytes(TPCircularBuffer *buffer, const void* src, int32_t len) { 192 | int32_t space; 193 | void *ptr = TPCircularBufferHead(buffer, &space); 194 | if ( space < len ) return false; 195 | memcpy(ptr, src, len); 196 | TPCircularBufferProduce(buffer, len); 197 | return true; 198 | } 199 | 200 | /*! 201 | * Deprecated method 202 | */ 203 | static __inline__ __attribute__((always_inline)) __deprecated_msg("use TPCircularBufferSetAtomic(false) and TPCircularBufferConsume instead") 204 | void TPCircularBufferConsumeNoBarrier(TPCircularBuffer *buffer, int32_t amount) { 205 | buffer->tail = (buffer->tail + amount) % buffer->length; 206 | buffer->fillCount -= amount; 207 | assert(buffer->fillCount >= 0); 208 | } 209 | 210 | /*! 211 | * Deprecated method 212 | */ 213 | static __inline__ __attribute__((always_inline)) __deprecated_msg("use TPCircularBufferSetAtomic(false) and TPCircularBufferProduce instead") 214 | void TPCircularBufferProduceNoBarrier(TPCircularBuffer *buffer, int32_t amount) { 215 | buffer->head = (buffer->head + amount) % buffer->length; 216 | buffer->fillCount += amount; 217 | assert(buffer->fillCount <= buffer->length); 218 | } 219 | 220 | #ifdef __cplusplus 221 | } 222 | #endif 223 | 224 | #endif 225 | -------------------------------------------------------------------------------- /AudioTrimmer/AudioTrimmerController/EZAudio/EZAudioFloatConverter.m: -------------------------------------------------------------------------------- 1 | // 2 | // EZAudioFloatConverter.m 3 | // EZAudio 4 | // 5 | // Created by Syed Haris Ali on 6/23/15. 6 | // Copyright (c) 2015 Syed Haris Ali. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | 26 | #import "EZAudioFloatConverter.h" 27 | #import "EZAudioUtilities.h" 28 | 29 | //------------------------------------------------------------------------------ 30 | #pragma mark - Constants 31 | //------------------------------------------------------------------------------ 32 | 33 | static UInt32 EZAudioFloatConverterDefaultOutputBufferSize = 128 * 32; 34 | UInt32 const EZAudioFloatConverterDefaultPacketSize = 2048; 35 | 36 | //------------------------------------------------------------------------------ 37 | #pragma mark - Data Structures 38 | //------------------------------------------------------------------------------ 39 | 40 | typedef struct 41 | { 42 | AudioConverterRef converterRef; 43 | AudioBufferList *floatAudioBufferList; 44 | AudioStreamBasicDescription inputFormat; 45 | AudioStreamBasicDescription outputFormat; 46 | AudioStreamPacketDescription *packetDescriptions; 47 | UInt32 packetsPerBuffer; 48 | } EZAudioFloatConverterInfo; 49 | 50 | //------------------------------------------------------------------------------ 51 | #pragma mark - Callbacks 52 | //------------------------------------------------------------------------------ 53 | 54 | OSStatus EZAudioFloatConverterCallback(AudioConverterRef inAudioConverter, 55 | UInt32 *ioNumberDataPackets, 56 | AudioBufferList *ioData, 57 | AudioStreamPacketDescription **outDataPacketDescription, 58 | void *inUserData) 59 | { 60 | AudioBufferList *sourceBuffer = (AudioBufferList *)inUserData; 61 | 62 | memcpy(ioData, 63 | sourceBuffer, 64 | sizeof(AudioBufferList) + (sourceBuffer->mNumberBuffers - 1) * sizeof(AudioBuffer)); 65 | sourceBuffer = NULL; 66 | 67 | return noErr; 68 | } 69 | 70 | //------------------------------------------------------------------------------ 71 | #pragma mark - EZAudioFloatConverter (Interface Extension) 72 | //------------------------------------------------------------------------------ 73 | 74 | @interface EZAudioFloatConverter () 75 | @property (nonatomic, assign) EZAudioFloatConverterInfo *info; 76 | @end 77 | 78 | //------------------------------------------------------------------------------ 79 | #pragma mark - EZAudioFloatConverter (Implementation) 80 | //------------------------------------------------------------------------------ 81 | 82 | @implementation EZAudioFloatConverter 83 | 84 | //------------------------------------------------------------------------------ 85 | #pragma mark - Class Methods 86 | //------------------------------------------------------------------------------ 87 | 88 | + (instancetype)converterWithInputFormat:(AudioStreamBasicDescription)inputFormat 89 | { 90 | return [[self alloc] initWithInputFormat:inputFormat]; 91 | } 92 | 93 | //------------------------------------------------------------------------------ 94 | #pragma mark - Dealloc 95 | //------------------------------------------------------------------------------ 96 | 97 | - (void)dealloc 98 | { 99 | AudioConverterDispose(self.info->converterRef); 100 | [EZAudioUtilities freeBufferList:self.info->floatAudioBufferList]; 101 | free(self.info->packetDescriptions); 102 | free(self.info); 103 | } 104 | 105 | //------------------------------------------------------------------------------ 106 | #pragma mark - Initialization 107 | //------------------------------------------------------------------------------ 108 | 109 | - (instancetype)initWithInputFormat:(AudioStreamBasicDescription)inputFormat 110 | { 111 | self = [super init]; 112 | if (self) 113 | { 114 | self.info = (EZAudioFloatConverterInfo *)malloc(sizeof(EZAudioFloatConverterInfo)); 115 | memset(self.info, 0, sizeof(EZAudioFloatConverterInfo)); 116 | self.info->inputFormat = inputFormat; 117 | [self setup]; 118 | } 119 | return self; 120 | } 121 | 122 | //------------------------------------------------------------------------------ 123 | #pragma mark - Setup 124 | //------------------------------------------------------------------------------ 125 | 126 | - (void)setup 127 | { 128 | // create output format 129 | self.info->outputFormat = [EZAudioUtilities floatFormatWithNumberOfChannels:self.info->inputFormat.mChannelsPerFrame 130 | sampleRate:self.info->inputFormat.mSampleRate]; 131 | 132 | // create a new instance of the audio converter 133 | [EZAudioUtilities checkResult:AudioConverterNew(&self.info->inputFormat, 134 | &self.info->outputFormat, 135 | &self.info->converterRef) 136 | operation:"Failed to create new audio converter"]; 137 | 138 | // get max packets per buffer so you can allocate a proper AudioBufferList 139 | UInt32 packetsPerBuffer = 0; 140 | UInt32 outputBufferSize = EZAudioFloatConverterDefaultOutputBufferSize; 141 | UInt32 sizePerPacket = self.info->inputFormat.mBytesPerPacket; 142 | BOOL isVBR = sizePerPacket == 0; 143 | 144 | // VBR 145 | if (isVBR) 146 | { 147 | // determine the max output buffer size 148 | UInt32 maxOutputPacketSize; 149 | UInt32 propSize = sizeof(maxOutputPacketSize); 150 | OSStatus result = AudioConverterGetProperty(self.info->converterRef, 151 | kAudioConverterPropertyMaximumOutputPacketSize, 152 | &propSize, 153 | &maxOutputPacketSize); 154 | if (result != noErr) 155 | { 156 | maxOutputPacketSize = EZAudioFloatConverterDefaultPacketSize; 157 | } 158 | 159 | // set the output buffer size to at least the max output size 160 | if (maxOutputPacketSize > outputBufferSize) 161 | { 162 | outputBufferSize = maxOutputPacketSize; 163 | } 164 | packetsPerBuffer = outputBufferSize / maxOutputPacketSize; 165 | 166 | // allocate memory for the packet descriptions 167 | self.info->packetDescriptions = (AudioStreamPacketDescription *)malloc(sizeof(AudioStreamPacketDescription) * packetsPerBuffer); 168 | } 169 | else 170 | { 171 | packetsPerBuffer = outputBufferSize / sizePerPacket; 172 | } 173 | self.info->packetsPerBuffer = packetsPerBuffer; 174 | 175 | // allocate the AudioBufferList to hold the float values 176 | BOOL isInterleaved = [EZAudioUtilities isInterleaved:self.info->outputFormat]; 177 | self.info->floatAudioBufferList = [EZAudioUtilities audioBufferListWithNumberOfFrames:packetsPerBuffer 178 | numberOfChannels:self.info->outputFormat.mChannelsPerFrame 179 | interleaved:isInterleaved]; 180 | } 181 | 182 | //------------------------------------------------------------------------------ 183 | #pragma mark - Events 184 | //------------------------------------------------------------------------------ 185 | 186 | - (void)convertDataFromAudioBufferList:(AudioBufferList *)audioBufferList 187 | withNumberOfFrames:(UInt32)frames 188 | toFloatBuffers:(float **)buffers 189 | { 190 | [self convertDataFromAudioBufferList:audioBufferList 191 | withNumberOfFrames:frames 192 | toFloatBuffers:buffers 193 | packetDescriptions:self.info->packetDescriptions]; 194 | } 195 | 196 | //------------------------------------------------------------------------------ 197 | 198 | - (void)convertDataFromAudioBufferList:(AudioBufferList *)audioBufferList 199 | withNumberOfFrames:(UInt32)frames 200 | toFloatBuffers:(float **)buffers 201 | packetDescriptions:(AudioStreamPacketDescription *)packetDescriptions 202 | { 203 | if (frames != 0) 204 | { 205 | // 206 | // Make sure the data size coming in is consistent with the number 207 | // of frames we're actually getting 208 | // 209 | for (int i = 0; i < audioBufferList->mNumberBuffers; i++) { 210 | audioBufferList->mBuffers[i].mDataByteSize = frames * self.info->inputFormat.mBytesPerFrame; 211 | } 212 | 213 | // 214 | // Fill out the audio converter with the source buffer 215 | // 216 | [EZAudioUtilities checkResult:AudioConverterFillComplexBuffer(self.info->converterRef, 217 | EZAudioFloatConverterCallback, 218 | audioBufferList, 219 | &frames, 220 | self.info->floatAudioBufferList, 221 | packetDescriptions ? packetDescriptions : self.info->packetDescriptions) 222 | operation:"Failed to fill complex buffer in float converter"]; 223 | 224 | // 225 | // Copy the converted buffers into the float buffer array stored 226 | // in memory 227 | // 228 | for (int i = 0; i < self.info->floatAudioBufferList->mNumberBuffers; i++) 229 | { 230 | memcpy(buffers[i], 231 | self.info->floatAudioBufferList->mBuffers[i].mData, 232 | self.info->floatAudioBufferList->mBuffers[i].mDataByteSize); 233 | } 234 | } 235 | } 236 | 237 | //------------------------------------------------------------------------------ 238 | 239 | @end -------------------------------------------------------------------------------- /AudioTrimmer/AudioTrimmerController/EZAudio/EZAudioPlot.h: -------------------------------------------------------------------------------- 1 | // 2 | // EZAudioPlot.h 3 | // EZAudio 4 | // 5 | // Created by Syed Haris Ali on 9/2/13. 6 | // Copyright (c) 2015 Syed Haris Ali. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | 26 | #import 27 | #import "EZPlot.h" 28 | #import "EZAudioDisplayLink.h" 29 | 30 | @class EZAudio; 31 | 32 | //------------------------------------------------------------------------------ 33 | #pragma mark - Constants 34 | //------------------------------------------------------------------------------ 35 | 36 | /** 37 | The default value used for the maximum rolling history buffer length of any EZAudioPlot. 38 | @deprecated This constant is deprecated starting in version 0.2.0. 39 | @note Please use EZAudioPlotDefaultMaxHistoryBufferLength instead. 40 | */ 41 | FOUNDATION_EXPORT UInt32 const kEZAudioPlotMaxHistoryBufferLength __attribute__((deprecated)); 42 | 43 | /** 44 | The default value used for the default rolling history buffer length of any EZAudioPlot. 45 | @deprecated This constant is deprecated starting in version 0.2.0. 46 | @note Please use EZAudioPlotDefaultHistoryBufferLength instead. 47 | */ 48 | FOUNDATION_EXPORT UInt32 const kEZAudioPlotDefaultHistoryBufferLength __attribute__((deprecated)); 49 | 50 | /** 51 | The default value used for the default rolling history buffer length of any EZAudioPlot. 52 | */ 53 | FOUNDATION_EXPORT UInt32 const EZAudioPlotDefaultHistoryBufferLength; 54 | 55 | /** 56 | The default value used for the maximum rolling history buffer length of any EZAudioPlot. 57 | */ 58 | FOUNDATION_EXPORT UInt32 const EZAudioPlotDefaultMaxHistoryBufferLength; 59 | 60 | //------------------------------------------------------------------------------ 61 | #pragma mark - EZAudioPlotWaveformLayer 62 | //------------------------------------------------------------------------------ 63 | 64 | /** 65 | The EZAudioPlotWaveformLayer is a lightweight subclass of the CAShapeLayer that allows implicit animations on the `path` key. 66 | */ 67 | @interface EZAudioPlotWaveformLayer : CAShapeLayer 68 | @end 69 | 70 | //------------------------------------------------------------------------------ 71 | #pragma mark - EZAudioPlot 72 | //------------------------------------------------------------------------------ 73 | 74 | /** 75 | `EZAudioPlot`, a subclass of `EZPlot`, is a cross-platform (iOS and OSX) class that plots an audio waveform using Core Graphics. 76 | 77 | The caller provides updates a constant stream of updated audio data in the `updateBuffer:withBufferSize:` function, which in turn will be plotted in one of the plot types: 78 | 79 | * Buffer (`EZPlotTypeBuffer`) - A plot that only consists of the current buffer and buffer size from the last call to `updateBuffer:withBufferSize:`. This looks similar to the default openFrameworks input audio example. 80 | * Rolling (`EZPlotTypeRolling`) - A plot that consists of a rolling history of values averaged from each buffer. This is the traditional waveform look. 81 | 82 | #Parent Methods and Properties# 83 | 84 | See EZPlot for full API methods and properties (colors, plot type, update function) 85 | 86 | */ 87 | @interface EZAudioPlot : EZPlot 88 | 89 | /** 90 | A BOOL that allows optimizing the audio plot's drawing for real-time displays. Since the update function may be updating the plot's data very quickly (over 60 frames per second) this property will throttle the drawing calls to be 60 frames per second (or whatever the screen rate is). Specifically, it disables implicit path change animations on the `waveformLayer` and sets up a display link to render 60 fps (audio updating the plot at 44.1 kHz causes it to re-render 86 fps - far greater than what is needed for a visual display). 91 | */ 92 | @property (nonatomic, assign) BOOL shouldOptimizeForRealtimePlot; 93 | 94 | //------------------------------------------------------------------------------ 95 | 96 | /** 97 | A BOOL indicating whether the plot should center itself vertically. 98 | */ 99 | @property (nonatomic, assign) BOOL shouldCenterYAxis; 100 | 101 | //------------------------------------------------------------------------------ 102 | 103 | /** 104 | An EZAudioPlotWaveformLayer that is used to render the actual waveform. By switching the drawing code to Core Animation layers in version 0.2.0 most work, specifically the compositing step, is now done on the GPU. Hence, multiple EZAudioPlot instances can be used simultaneously with very low CPU overhead so these are now practical for table and collection views. 105 | */ 106 | @property (nonatomic, strong) EZAudioPlotWaveformLayer *waveformLayer; 107 | 108 | //------------------------------------------------------------------------------ 109 | #pragma mark - Adjust Resolution 110 | //------------------------------------------------------------------------------ 111 | 112 | ///----------------------------------------------------------- 113 | /// @name Adjusting The Resolution 114 | ///----------------------------------------------------------- 115 | 116 | /** 117 | Sets the length of the rolling history buffer (i.e. the number of points in the rolling plot's buffer). Can grow or shrink the display up to the maximum size specified by the `maximumRollingHistoryLength` method. Will return the actual set value, which will be either the given value if smaller than the `maximumRollingHistoryLength` or `maximumRollingHistoryLength` if a larger value is attempted to be set. 118 | @param historyLength The new length of the rolling history buffer. 119 | @return The new value equal to the historyLength or the `maximumRollingHistoryLength`. 120 | */ 121 | -(int)setRollingHistoryLength:(int)historyLength; 122 | 123 | //------------------------------------------------------------------------------ 124 | 125 | /** 126 | Provides the length of the rolling history buffer (i.e. the number of points in the rolling plot's buffer). 127 | * @return An int representing the length of the rolling history buffer 128 | */ 129 | -(int)rollingHistoryLength; 130 | 131 | //------------------------------------------------------------------------------ 132 | #pragma mark - Subclass Methods 133 | //------------------------------------------------------------------------------ 134 | 135 | ///----------------------------------------------------------- 136 | /// @name Subclass Methods 137 | ///----------------------------------------------------------- 138 | 139 | /** 140 | Main method that handles converting the points created from the `updatedBuffer:withBufferSize:` method into a CGPathRef to store in the `waveformLayer`. In this method you can create any path you'd like using the point array (for instance, maybe mapping the points to a circle instead of the standard 2D plane). 141 | @param points An array of CGPoint structures, with the x values ranging from 0 - (pointCount - 1) and y values containing the last audio data's buffer. 142 | @param pointCount A UInt32 of the length of the point array. 143 | @param rect An EZRect (CGRect on iOS or NSRect on OSX) that the path should be created relative to. 144 | @return A CGPathRef that is the path you'd like to store on the `waveformLayer` to visualize the audio data. 145 | */ 146 | - (CGPathRef)createPathWithPoints:(CGPoint *)points 147 | pointCount:(UInt32)pointCount 148 | inRect:(EZRect)rect; 149 | 150 | //------------------------------------------------------------------------------ 151 | 152 | /** 153 | Provides the default length of the rolling history buffer when the plot is initialized. Default is `EZAudioPlotDefaultHistoryBufferLength` constant. 154 | @return An int describing the initial length of the rolling history buffer. 155 | */ 156 | - (int)defaultRollingHistoryLength; 157 | 158 | //------------------------------------------------------------------------------ 159 | 160 | /** 161 | Called after the view has been created. Subclasses should use to add any additional methods needed instead of overriding the init methods. 162 | */ 163 | - (void)setupPlot; 164 | 165 | //------------------------------------------------------------------------------ 166 | 167 | /** 168 | Provides the default number of points that will be used to initialize the graph's points data structure that holds. Essentially the plot starts off as a flat line of this many points. Default is 100. 169 | @return An int describing the initial number of points the plot should have when flat lined. 170 | */ 171 | - (int)initialPointCount; 172 | 173 | //------------------------------------------------------------------------------ 174 | 175 | /** 176 | Provides the default maximum rolling history length - that is, the maximum amount of points the `setRollingHistoryLength:` method may be set to. If a length higher than this is set then the plot will likely crash because the appropriate resources are only allocated once during the plot's initialization step. Defualt is `EZAudioPlotDefaultMaxHistoryBufferLength` constant. 177 | @return An int describing the maximum length of the absolute rolling history buffer. 178 | */ 179 | - (int)maximumRollingHistoryLength; 180 | 181 | //------------------------------------------------------------------------------ 182 | 183 | /** 184 | Method to cause the waveform layer's path to get recreated and redrawn on screen using the last buffer of data provided. This is the equivalent to the drawRect: method used to normally subclass a view's drawing. This normally don't need to be overrode though - a better approach would be to override the `createPathWithPoints:pointCount:inRect:` method. 185 | */ 186 | - (void)redraw; 187 | 188 | //------------------------------------------------------------------------------ 189 | 190 | /** 191 | Main method used to copy the sample data from the source buffer and update the 192 | plot. Subclasses can overwrite this method for custom behavior. 193 | @param data A float array of the sample data. Subclasses should copy this data to a separate array to avoid threading issues. 194 | @param length The length of the float array as an int. 195 | */ 196 | -(void)setSampleData:(float *)data length:(int)length; 197 | 198 | //------------------------------------------------------------------------------ 199 | 200 | @end 201 | 202 | @interface EZAudioPlot () 203 | @property (nonatomic, strong) EZAudioDisplayLink *displayLink; 204 | @property (nonatomic, assign) EZPlotHistoryInfo *historyInfo; 205 | @property (nonatomic, assign) CGPoint *points; 206 | @property (nonatomic, assign) UInt32 pointCount; 207 | @end -------------------------------------------------------------------------------- /AudioTrimmer/AudioTrimmerController/EZAudio/EZAudio.m: -------------------------------------------------------------------------------- 1 | // 2 | // EZAudio.m 3 | // EZAudioCoreGraphicsWaveformExample 4 | // 5 | // Created by Syed Haris Ali on 5/13/15. 6 | // Copyright (c) 2015 Syed Haris Ali. All rights reserved. 7 | // 8 | 9 | #import "EZAudio.h" 10 | 11 | @implementation EZAudio 12 | 13 | //------------------------------------------------------------------------------ 14 | #pragma mark - Debugging 15 | //------------------------------------------------------------------------------ 16 | 17 | + (void)setShouldExitOnCheckResultFail:(BOOL)shouldExitOnCheckResultFail 18 | { 19 | [EZAudioUtilities setShouldExitOnCheckResultFail:shouldExitOnCheckResultFail]; 20 | } 21 | 22 | //------------------------------------------------------------------------------ 23 | 24 | + (BOOL)shouldExitOnCheckResultFail 25 | { 26 | return [EZAudioUtilities shouldExitOnCheckResultFail]; 27 | } 28 | 29 | //------------------------------------------------------------------------------ 30 | #pragma mark - AudioBufferList Utility 31 | //------------------------------------------------------------------------------ 32 | 33 | + (AudioBufferList *)audioBufferListWithNumberOfFrames:(UInt32)frames 34 | numberOfChannels:(UInt32)channels 35 | interleaved:(BOOL)interleaved 36 | { 37 | return [EZAudioUtilities audioBufferListWithNumberOfFrames:frames 38 | numberOfChannels:channels 39 | interleaved:interleaved]; 40 | } 41 | 42 | //------------------------------------------------------------------------------ 43 | 44 | + (float **)floatBuffersWithNumberOfFrames:(UInt32)frames 45 | numberOfChannels:(UInt32)channels 46 | { 47 | return [EZAudioUtilities floatBuffersWithNumberOfFrames:frames 48 | numberOfChannels:channels]; 49 | } 50 | 51 | //------------------------------------------------------------------------------ 52 | 53 | + (void)freeBufferList:(AudioBufferList *)bufferList 54 | { 55 | [EZAudioUtilities freeBufferList:bufferList]; 56 | } 57 | 58 | //------------------------------------------------------------------------------ 59 | 60 | + (void)freeFloatBuffers:(float **)buffers numberOfChannels:(UInt32)channels 61 | { 62 | [EZAudioUtilities freeFloatBuffers:buffers numberOfChannels:channels]; 63 | } 64 | 65 | //------------------------------------------------------------------------------ 66 | #pragma mark - AudioStreamBasicDescription Utility 67 | //------------------------------------------------------------------------------ 68 | 69 | + (AudioStreamBasicDescription)AIFFFormatWithNumberOfChannels:(UInt32)channels 70 | sampleRate:(float)sampleRate 71 | { 72 | return [EZAudioUtilities AIFFFormatWithNumberOfChannels:channels 73 | sampleRate:sampleRate]; 74 | } 75 | 76 | //------------------------------------------------------------------------------ 77 | 78 | + (AudioStreamBasicDescription)iLBCFormatWithSampleRate:(float)sampleRate 79 | { 80 | return [EZAudioUtilities iLBCFormatWithSampleRate:sampleRate]; 81 | } 82 | 83 | //------------------------------------------------------------------------------ 84 | 85 | + (AudioStreamBasicDescription)floatFormatWithNumberOfChannels:(UInt32)channels 86 | sampleRate:(float)sampleRate 87 | { 88 | return [EZAudioUtilities floatFormatWithNumberOfChannels:channels 89 | sampleRate:sampleRate]; 90 | } 91 | 92 | //------------------------------------------------------------------------------ 93 | 94 | + (AudioStreamBasicDescription)M4AFormatWithNumberOfChannels:(UInt32)channels 95 | sampleRate:(float)sampleRate 96 | { 97 | return [EZAudioUtilities M4AFormatWithNumberOfChannels:channels 98 | sampleRate:sampleRate]; 99 | } 100 | 101 | //------------------------------------------------------------------------------ 102 | 103 | + (AudioStreamBasicDescription)monoFloatFormatWithSampleRate:(float)sampleRate 104 | { 105 | return [EZAudioUtilities monoFloatFormatWithSampleRate:sampleRate]; 106 | } 107 | 108 | //------------------------------------------------------------------------------ 109 | 110 | + (AudioStreamBasicDescription)monoCanonicalFormatWithSampleRate:(float)sampleRate 111 | { 112 | return [EZAudioUtilities monoCanonicalFormatWithSampleRate:sampleRate]; 113 | } 114 | 115 | //------------------------------------------------------------------------------ 116 | 117 | + (AudioStreamBasicDescription)stereoCanonicalNonInterleavedFormatWithSampleRate:(float)sampleRate 118 | { 119 | return [EZAudioUtilities stereoCanonicalNonInterleavedFormatWithSampleRate:sampleRate]; 120 | } 121 | 122 | //------------------------------------------------------------------------------ 123 | 124 | + (AudioStreamBasicDescription)stereoFloatInterleavedFormatWithSampleRate:(float)sampleRate 125 | { 126 | return [EZAudioUtilities stereoFloatInterleavedFormatWithSampleRate:sampleRate]; 127 | } 128 | 129 | //------------------------------------------------------------------------------ 130 | 131 | + (AudioStreamBasicDescription)stereoFloatNonInterleavedFormatWithSampleRate:(float)sampleRate 132 | { 133 | return [EZAudioUtilities stereoFloatNonInterleavedFormatWithSampleRate:sampleRate]; 134 | } 135 | 136 | //------------------------------------------------------------------------------ 137 | 138 | + (BOOL)isFloatFormat:(AudioStreamBasicDescription)asbd 139 | { 140 | return [EZAudioUtilities isFloatFormat:asbd]; 141 | } 142 | 143 | //------------------------------------------------------------------------------ 144 | 145 | + (BOOL)isInterleaved:(AudioStreamBasicDescription)asbd 146 | { 147 | return [EZAudioUtilities isInterleaved:asbd]; 148 | } 149 | 150 | //------------------------------------------------------------------------------ 151 | 152 | + (BOOL)isLinearPCM:(AudioStreamBasicDescription)asbd 153 | { 154 | return [EZAudioUtilities isLinearPCM:asbd]; 155 | } 156 | 157 | //------------------------------------------------------------------------------ 158 | 159 | + (void)printASBD:(AudioStreamBasicDescription)asbd 160 | { 161 | [EZAudioUtilities printASBD:asbd]; 162 | } 163 | 164 | //------------------------------------------------------------------------------ 165 | 166 | + (NSString *)displayTimeStringFromSeconds:(NSTimeInterval)seconds 167 | { 168 | return [EZAudioUtilities displayTimeStringFromSeconds:seconds]; 169 | } 170 | 171 | //------------------------------------------------------------------------------ 172 | 173 | + (NSString *)stringForAudioStreamBasicDescription:(AudioStreamBasicDescription)asbd 174 | { 175 | return [EZAudioUtilities stringForAudioStreamBasicDescription:asbd]; 176 | } 177 | 178 | //------------------------------------------------------------------------------ 179 | 180 | + (void)setCanonicalAudioStreamBasicDescription:(AudioStreamBasicDescription*)asbd 181 | numberOfChannels:(UInt32)nChannels 182 | interleaved:(BOOL)interleaved 183 | { 184 | [EZAudioUtilities setCanonicalAudioStreamBasicDescription:asbd 185 | numberOfChannels:nChannels 186 | interleaved:interleaved]; 187 | } 188 | 189 | //------------------------------------------------------------------------------ 190 | #pragma mark - Math Utilities 191 | //------------------------------------------------------------------------------ 192 | 193 | + (void)appendBufferAndShift:(float*)buffer 194 | withBufferSize:(int)bufferLength 195 | toScrollHistory:(float*)scrollHistory 196 | withScrollHistorySize:(int)scrollHistoryLength 197 | { 198 | [EZAudioUtilities appendBufferAndShift:buffer 199 | withBufferSize:bufferLength 200 | toScrollHistory:scrollHistory 201 | withScrollHistorySize:scrollHistoryLength]; 202 | } 203 | 204 | //------------------------------------------------------------------------------ 205 | 206 | + (void) appendValue:(float)value 207 | toScrollHistory:(float*)scrollHistory 208 | withScrollHistorySize:(int)scrollHistoryLength 209 | { 210 | [EZAudioUtilities appendValue:value 211 | toScrollHistory:scrollHistory 212 | withScrollHistorySize:scrollHistoryLength]; 213 | } 214 | 215 | //------------------------------------------------------------------------------ 216 | 217 | + (float)MAP:(float)value 218 | leftMin:(float)leftMin 219 | leftMax:(float)leftMax 220 | rightMin:(float)rightMin 221 | rightMax:(float)rightMax 222 | { 223 | return [EZAudioUtilities MAP:value 224 | leftMin:leftMin 225 | leftMax:leftMax 226 | rightMin:rightMin 227 | rightMax:rightMax]; 228 | } 229 | 230 | //------------------------------------------------------------------------------ 231 | 232 | + (float)RMS:(float *)buffer length:(int)bufferSize 233 | { 234 | return [EZAudioUtilities RMS:buffer length:bufferSize]; 235 | } 236 | 237 | //------------------------------------------------------------------------------ 238 | 239 | + (float)SGN:(float)value 240 | { 241 | return [EZAudioUtilities SGN:value]; 242 | } 243 | 244 | //------------------------------------------------------------------------------ 245 | #pragma mark - OSStatus Utility 246 | //------------------------------------------------------------------------------ 247 | 248 | + (void)checkResult:(OSStatus)result operation:(const char *)operation 249 | { 250 | [EZAudioUtilities checkResult:result 251 | operation:operation]; 252 | } 253 | 254 | //------------------------------------------------------------------------------ 255 | 256 | + (NSString *)stringFromUInt32Code:(UInt32)code 257 | { 258 | return [EZAudioUtilities stringFromUInt32Code:code]; 259 | } 260 | 261 | //------------------------------------------------------------------------------ 262 | #pragma mark - Plot Utility 263 | //------------------------------------------------------------------------------ 264 | 265 | + (void)updateScrollHistory:(float **)scrollHistory 266 | withLength:(int)scrollHistoryLength 267 | atIndex:(int *)index 268 | withBuffer:(float *)buffer 269 | withBufferSize:(int)bufferSize 270 | isResolutionChanging:(BOOL *)isChanging 271 | { 272 | [EZAudioUtilities updateScrollHistory:scrollHistory 273 | withLength:scrollHistoryLength 274 | atIndex:index 275 | withBuffer:buffer 276 | withBufferSize:bufferSize 277 | isResolutionChanging:isChanging]; 278 | } 279 | 280 | //------------------------------------------------------------------------------ 281 | #pragma mark - TPCircularBuffer Utility 282 | //------------------------------------------------------------------------------ 283 | 284 | + (void)appendDataToCircularBuffer:(TPCircularBuffer *)circularBuffer 285 | fromAudioBufferList:(AudioBufferList *)audioBufferList 286 | { 287 | [EZAudioUtilities appendDataToCircularBuffer:circularBuffer 288 | fromAudioBufferList:audioBufferList]; 289 | } 290 | 291 | //------------------------------------------------------------------------------ 292 | 293 | + (void)circularBuffer:(TPCircularBuffer *)circularBuffer withSize:(int)size 294 | { 295 | [EZAudioUtilities circularBuffer:circularBuffer withSize:size]; 296 | } 297 | 298 | //------------------------------------------------------------------------------ 299 | 300 | + (void)freeCircularBuffer:(TPCircularBuffer *)circularBuffer 301 | { 302 | [EZAudioUtilities freeCircularBuffer:circularBuffer]; 303 | } 304 | 305 | //------------------------------------------------------------------------------ 306 | 307 | @end -------------------------------------------------------------------------------- /AudioTrimmer/AudioTrimmerController/EZAudio/EZAudioPlotGL.h: -------------------------------------------------------------------------------- 1 | // 2 | // EZAudioPlotGL.h 3 | // EZAudio 4 | // 5 | // Created by Syed Haris Ali on 11/22/13. 6 | // Copyright (c) 2015 Syed Haris Ali. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | 26 | #import 27 | #import "EZPlot.h" 28 | #if !TARGET_OS_IPHONE 29 | #import 30 | #endif 31 | 32 | //------------------------------------------------------------------------------ 33 | #pragma mark - Data Structures 34 | //------------------------------------------------------------------------------ 35 | 36 | typedef struct 37 | { 38 | GLfloat x; 39 | GLfloat y; 40 | } EZAudioPlotGLPoint; 41 | 42 | //------------------------------------------------------------------------------ 43 | #pragma mark - EZAudioPlotGL 44 | //------------------------------------------------------------------------------ 45 | 46 | /** 47 | EZAudioPlotGL is a subclass of either a GLKView on iOS or an NSOpenGLView on OSX. As of 0.6.0 this class no longer depends on an embedded GLKViewController for iOS as the display link is just manually managed within this single view instead. The EZAudioPlotGL provides the same kind of audio plot as the EZAudioPlot, but uses OpenGL to GPU-accelerate the drawing of the points, which means you can fit a lot more points and complex geometries. 48 | */ 49 | #if TARGET_OS_IPHONE 50 | @interface EZAudioPlotGL : GLKView 51 | #elif TARGET_OS_MAC 52 | @interface EZAudioPlotGL : NSOpenGLView 53 | #endif 54 | 55 | //------------------------------------------------------------------------------ 56 | #pragma mark - Properties 57 | //------------------------------------------------------------------------------ 58 | 59 | ///----------------------------------------------------------- 60 | /// @name Customizing The Plot's Appearance 61 | ///----------------------------------------------------------- 62 | 63 | /** 64 | The default background color of the plot. For iOS the color is specified as a UIColor while for OSX the color is an NSColor. The default value on both platforms is a sweet looking green. 65 | @warning On OSX, if you set the background to a value where the alpha component is 0 then the EZAudioPlotGL will automatically set its superview to be layer-backed. 66 | */ 67 | #if TARGET_OS_IPHONE 68 | @property (nonatomic, strong) IBInspectable UIColor *backgroundColor; 69 | #elif TARGET_OS_MAC 70 | @property (nonatomic, strong) IBInspectable NSColor *backgroundColor; 71 | #endif 72 | 73 | //------------------------------------------------------------------------------ 74 | 75 | /** 76 | The default color of the plot's data (i.e. waveform, y-axis values). For iOS the color is specified as a UIColor while for OSX the color is an NSColor. The default value on both platforms is white. 77 | */ 78 | #if TARGET_OS_IPHONE 79 | @property (nonatomic, strong) IBInspectable UIColor *color; 80 | #elif TARGET_OS_MAC 81 | @property (nonatomic, strong) IBInspectable NSColor *color; 82 | #endif 83 | 84 | //------------------------------------------------------------------------------ 85 | 86 | /** 87 | The plot's gain value, which controls the scale of the y-axis values. The default value of the gain is 1.0f and should always be greater than 0.0f. 88 | */ 89 | @property (nonatomic, assign) IBInspectable float gain; 90 | 91 | //------------------------------------------------------------------------------ 92 | 93 | /** 94 | The type of plot as specified by the `EZPlotType` enumeration (i.e. a buffer or rolling plot type). Default is EZPlotTypeBuffer. 95 | */ 96 | @property (nonatomic, assign) EZPlotType plotType; 97 | 98 | //------------------------------------------------------------------------------ 99 | 100 | /** 101 | A BOOL indicating whether or not to fill in the graph. A value of YES will make a filled graph (filling in the space between the x-axis and the y-value), while a value of NO will create a stroked graph (connecting the points along the y-axis). Default is NO. 102 | */ 103 | @property (nonatomic, assign) IBInspectable BOOL shouldFill; 104 | 105 | //------------------------------------------------------------------------------ 106 | 107 | /** 108 | A boolean indicating whether the graph should be rotated along the x-axis to give a mirrored reflection. This is typical for audio plots to produce the classic waveform look. A value of YES will produce a mirrored reflection of the y-values about the x-axis, while a value of NO will only plot the y-values. Default is NO. 109 | */ 110 | @property (nonatomic, assign) IBInspectable BOOL shouldMirror; 111 | 112 | //------------------------------------------------------------------------------ 113 | #pragma mark - Updating The Plot 114 | //------------------------------------------------------------------------------ 115 | 116 | ///----------------------------------------------------------- 117 | /// @name Updating The Plot 118 | ///----------------------------------------------------------- 119 | 120 | /** 121 | 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. 122 | @param buffer A float array of values to map to the y-axis. 123 | @param bufferSize The size of the float array that will be mapped to the y-axis. 124 | */ 125 | -(void)updateBuffer:(float *)buffer withBufferSize:(UInt32)bufferSize; 126 | 127 | //------------------------------------------------------------------------------ 128 | #pragma mark - Adjust Resolution 129 | //------------------------------------------------------------------------------ 130 | 131 | ///----------------------------------------------------------- 132 | /// @name Adjusting The Resolution 133 | ///----------------------------------------------------------- 134 | 135 | /** 136 | Sets the length of the rolling history buffer (i.e. the number of points in the rolling plot's buffer). Can grow or shrink the display up to the maximum size specified by the `maximumRollingHistoryLength` method. Will return the actual set value, which will be either the given value if smaller than the `maximumRollingHistoryLength` or `maximumRollingHistoryLength` if a larger value is attempted to be set. 137 | @param historyLength The new length of the rolling history buffer. 138 | @return The new value equal to the historyLength or the `maximumRollingHistoryLength`. 139 | */ 140 | -(int)setRollingHistoryLength:(int)historyLength; 141 | 142 | //------------------------------------------------------------------------------ 143 | 144 | /** 145 | Provides the length of the rolling history buffer (i.e. the number of points in the rolling plot's buffer). 146 | * @return An int representing the length of the rolling history buffer 147 | */ 148 | -(int)rollingHistoryLength; 149 | 150 | //------------------------------------------------------------------------------ 151 | #pragma mark - Clearing The Plot 152 | //------------------------------------------------------------------------------ 153 | 154 | ///----------------------------------------------------------- 155 | /// @name Clearing The Plot 156 | ///----------------------------------------------------------- 157 | 158 | /** 159 | Clears all data from the audio plot (includes both EZPlotTypeBuffer and EZPlotTypeRolling) 160 | */ 161 | -(void)clear; 162 | 163 | //------------------------------------------------------------------------------ 164 | #pragma mark - Start/Stop Display Link 165 | //------------------------------------------------------------------------------ 166 | 167 | /** 168 | Call this method to tell the EZAudioDisplayLink to stop drawing temporarily. 169 | */ 170 | - (void)pauseDrawing; 171 | 172 | //------------------------------------------------------------------------------ 173 | 174 | /** 175 | Call this method to manually tell the EZAudioDisplayLink to start drawing again. 176 | */ 177 | - (void)resumeDrawing; 178 | 179 | //------------------------------------------------------------------------------ 180 | #pragma mark - Subclass 181 | //------------------------------------------------------------------------------ 182 | 183 | ///----------------------------------------------------------- 184 | /// @name Customizing The Drawing 185 | ///----------------------------------------------------------- 186 | 187 | /** 188 | This method is used to perform the actual OpenGL drawing code to clear the background and draw the lines representing the 2D audio plot. Subclasses can use the current implementation as an example and implement their own custom geometries. This is the analogy of overriding the drawRect: method in an NSView or UIView. 189 | @param points An array of EZAudioPlotGLPoint structures representing the mapped audio data to x,y coordinates. The x-axis goes from 0 to the number of points (pointCount) while the y-axis goes from -1 to 1. Check out the implementation of this method to see how the model view matrix of the base effect is transformed to map this properly to the viewport. 190 | @param pointCount A UInt32 representing the number of points contained in the points array. 191 | @param baseEffect An optional GLKBaseEffect to use as a default shader. Call prepareToDraw on the base effect before any glDrawArrays call. 192 | @param vbo The Vertex Buffer Object used to buffer the point data. 193 | @param vab The Vertex Array Buffer used to bind the Vertex Buffer Object. This is a Mac only thing, you can ignore this completely on iOS. 194 | @param interpolated A BOOL indicating whether the data has been interpolated. This means the point data is twice as long, where every other point is 0 on the y-axis to allow drawing triangle stripes for filled in waveforms. Typically if the point data is interpolated you will be using the GL_TRIANGLE_STRIP drawing mode, while non-interpolated plots will just use a GL_LINE_STRIP drawing mode. 195 | @param mirrored A BOOL indicating whether the plot should be mirrored about the y-axis (or whatever geometry you come up with). 196 | @param gain A float representing a gain that should be used to influence the height or intensity of your geometry's shape. A gain of 0.0 means silence, a gain of 1.0 means full volume (you're welcome to boost this to whatever you want). 197 | */ 198 | - (void)redrawWithPoints:(EZAudioPlotGLPoint *)points 199 | pointCount:(UInt32)pointCount 200 | baseEffect:(GLKBaseEffect *)baseEffect 201 | vertexBufferObject:(GLuint)vbo 202 | vertexArrayBuffer:(GLuint)vab 203 | interpolated:(BOOL)interpolated 204 | mirrored:(BOOL)mirrored 205 | gain:(float)gain; 206 | 207 | //------------------------------------------------------------------------------ 208 | 209 | /** 210 | Called during the OpenGL run loop to constantly update the drawing 60 fps. Callers can use this force update the screen while subclasses can override this for complete control over their rendering. However, subclasses are more encouraged to use the `redrawWithPoints:pointCount:baseEffect:vertexBufferObject:vertexArrayBuffer:interpolated:mirrored:gain:` 211 | */ 212 | - (void)redraw; 213 | 214 | //------------------------------------------------------------------------------ 215 | 216 | /** 217 | Called after the view has been created. Subclasses should use to add any additional methods needed instead of overriding the init methods. 218 | */ 219 | - (void)setup; 220 | 221 | //------------------------------------------------------------------------------ 222 | 223 | /** 224 | Main method used to copy the sample data from the source buffer and update the 225 | plot. Subclasses can overwrite this method for custom behavior. 226 | @param data A float array of the sample data. Subclasses should copy this data to a separate array to avoid threading issues. 227 | @param length The length of the float array as an int. 228 | */ 229 | - (void)setSampleData:(float *)data length:(int)length; 230 | 231 | ///----------------------------------------------------------- 232 | /// @name Subclass Methods 233 | ///----------------------------------------------------------- 234 | 235 | /** 236 | Provides the default length of the rolling history buffer when the plot is initialized. Default is `EZAudioPlotDefaultHistoryBufferLength` constant. 237 | @return An int describing the initial length of the rolling history buffer. 238 | */ 239 | - (int)defaultRollingHistoryLength; 240 | 241 | //------------------------------------------------------------------------------ 242 | 243 | /** 244 | Provides the default maximum rolling history length - that is, the maximum amount of points the `setRollingHistoryLength:` method may be set to. If a length higher than this is set then the plot will likely crash because the appropriate resources are only allocated once during the plot's initialization step. Defualt is `EZAudioPlotDefaultMaxHistoryBufferLength` constant. 245 | @return An int describing the maximum length of the absolute rolling history buffer. 246 | */ 247 | - (int)maximumRollingHistoryLength; 248 | 249 | //------------------------------------------------------------------------------ 250 | 251 | @end -------------------------------------------------------------------------------- /AudioTrimmer/AudioTrimmerController/EZAudio/EZAudioPlot.m: -------------------------------------------------------------------------------- 1 | // 2 | // EZAudioPlot.m 3 | // EZAudio 4 | // 5 | // Created by Syed Haris Ali on 9/2/13. 6 | // Copyright (c) 2015 Syed Haris Ali. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | 26 | #import "EZAudioPlot.h" 27 | 28 | //------------------------------------------------------------------------------ 29 | #pragma mark - Constants 30 | //------------------------------------------------------------------------------ 31 | 32 | UInt32 const kEZAudioPlotMaxHistoryBufferLength = 8192; 33 | UInt32 const kEZAudioPlotDefaultHistoryBufferLength = 512; 34 | UInt32 const EZAudioPlotDefaultHistoryBufferLength = 512; 35 | UInt32 const EZAudioPlotDefaultMaxHistoryBufferLength = 8192; 36 | 37 | //------------------------------------------------------------------------------ 38 | #pragma mark - EZAudioPlot (Implementation) 39 | //------------------------------------------------------------------------------ 40 | 41 | @implementation EZAudioPlot 42 | 43 | //------------------------------------------------------------------------------ 44 | #pragma mark - Dealloc 45 | //------------------------------------------------------------------------------ 46 | 47 | - (void)dealloc 48 | { 49 | [EZAudioUtilities freeHistoryInfo:self.historyInfo]; 50 | free(self.points); 51 | } 52 | 53 | //------------------------------------------------------------------------------ 54 | #pragma mark - Initialization 55 | //------------------------------------------------------------------------------ 56 | 57 | - (id)init 58 | { 59 | self = [super init]; 60 | if (self) 61 | { 62 | [self initPlot]; 63 | } 64 | return self; 65 | } 66 | 67 | - (id)initWithCoder:(NSCoder *)aDecoder 68 | { 69 | self = [super initWithCoder:aDecoder]; 70 | if (self) 71 | { 72 | [self initPlot]; 73 | } 74 | return self; 75 | } 76 | 77 | #if TARGET_OS_IPHONE 78 | - (id)initWithFrame:(CGRect)frameRect 79 | #elif TARGET_OS_MAC 80 | - (id)initWithFrame:(NSRect)frameRect 81 | #endif 82 | { 83 | self = [super initWithFrame:frameRect]; 84 | if (self) 85 | { 86 | [self initPlot]; 87 | } 88 | return self; 89 | } 90 | 91 | #if TARGET_OS_IPHONE 92 | - (void)layoutSubviews 93 | { 94 | [super layoutSubviews]; 95 | [CATransaction begin]; 96 | [CATransaction setDisableActions:YES]; 97 | self.waveformLayer.frame = self.bounds; 98 | [self redraw]; 99 | [CATransaction commit]; 100 | } 101 | #elif TARGET_OS_MAC 102 | - (void)layout 103 | { 104 | [super layout]; 105 | [CATransaction begin]; 106 | [CATransaction setDisableActions:YES]; 107 | self.waveformLayer.frame = self.bounds; 108 | [self redraw]; 109 | [CATransaction commit]; 110 | } 111 | #endif 112 | 113 | - (void)initPlot 114 | { 115 | self.shouldCenterYAxis = YES; 116 | self.shouldOptimizeForRealtimePlot = YES; 117 | self.gain = 1.0; 118 | self.plotType = EZPlotTypeBuffer; 119 | self.shouldMirror = NO; 120 | self.shouldFill = NO; 121 | 122 | // Setup history window 123 | [self resetHistoryBuffers]; 124 | 125 | self.waveformLayer = [EZAudioPlotWaveformLayer layer]; 126 | self.waveformLayer.frame = self.bounds; 127 | self.waveformLayer.lineWidth = 1.0f; 128 | self.waveformLayer.fillColor = nil; 129 | self.waveformLayer.backgroundColor = nil; 130 | self.waveformLayer.opaque = YES; 131 | 132 | #if TARGET_OS_IPHONE 133 | self.color = [UIColor colorWithHue:0 saturation:1.0 brightness:1.0 alpha:1.0]; 134 | #elif TARGET_OS_MAC 135 | self.color = [NSColor colorWithCalibratedHue:0 saturation:1.0 brightness:1.0 alpha:1.0]; 136 | self.wantsLayer = YES; 137 | self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay; 138 | #endif 139 | self.backgroundColor = nil; 140 | [self.layer insertSublayer:self.waveformLayer atIndex:0]; 141 | 142 | // 143 | // Allow subclass to initialize plot 144 | // 145 | [self setupPlot]; 146 | 147 | self.points = calloc(EZAudioPlotDefaultMaxHistoryBufferLength, sizeof(CGPoint)); 148 | self.pointCount = [self initialPointCount]; 149 | [self redraw]; 150 | } 151 | 152 | //------------------------------------------------------------------------------ 153 | 154 | - (void)setupPlot 155 | { 156 | // 157 | // Override in subclass 158 | // 159 | } 160 | 161 | //------------------------------------------------------------------------------ 162 | #pragma mark - Setup 163 | //------------------------------------------------------------------------------ 164 | 165 | - (void)resetHistoryBuffers 166 | { 167 | // 168 | // Clear any existing data 169 | // 170 | if (self.historyInfo) 171 | { 172 | [EZAudioUtilities freeHistoryInfo:self.historyInfo]; 173 | } 174 | 175 | self.historyInfo = [EZAudioUtilities historyInfoWithDefaultLength:[self defaultRollingHistoryLength] 176 | maximumLength:[self maximumRollingHistoryLength]]; 177 | } 178 | 179 | //------------------------------------------------------------------------------ 180 | #pragma mark - Setters 181 | //------------------------------------------------------------------------------ 182 | 183 | - (void)setBackgroundColor:(id)backgroundColor 184 | { 185 | [super setBackgroundColor:backgroundColor]; 186 | self.layer.backgroundColor = [backgroundColor CGColor]; 187 | } 188 | 189 | //------------------------------------------------------------------------------ 190 | 191 | - (void)setColor:(id)color 192 | { 193 | [super setColor:color]; 194 | self.waveformLayer.strokeColor = [color CGColor]; 195 | if (self.shouldFill) 196 | { 197 | self.waveformLayer.fillColor = [color CGColor]; 198 | } 199 | } 200 | 201 | //------------------------------------------------------------------------------ 202 | 203 | - (void)setShouldOptimizeForRealtimePlot:(BOOL)shouldOptimizeForRealtimePlot 204 | { 205 | _shouldOptimizeForRealtimePlot = shouldOptimizeForRealtimePlot; 206 | if (shouldOptimizeForRealtimePlot && !self.displayLink) 207 | { 208 | self.displayLink = [EZAudioDisplayLink displayLinkWithDelegate:self]; 209 | [self.displayLink start]; 210 | } 211 | else 212 | { 213 | [self.displayLink stop]; 214 | self.displayLink = nil; 215 | } 216 | } 217 | 218 | //------------------------------------------------------------------------------ 219 | 220 | - (void)setShouldFill:(BOOL)shouldFill 221 | { 222 | [super setShouldFill:shouldFill]; 223 | self.waveformLayer.fillColor = shouldFill ? [self.color CGColor] : nil; 224 | } 225 | 226 | //------------------------------------------------------------------------------ 227 | #pragma mark - Drawing 228 | //------------------------------------------------------------------------------ 229 | 230 | - (void)clear 231 | { 232 | if (self.pointCount > 0) 233 | { 234 | [self resetHistoryBuffers]; 235 | float data[self.pointCount]; 236 | memset(data, 0, self.pointCount * sizeof(float)); 237 | [self setSampleData:data length:self.pointCount]; 238 | [self redraw]; 239 | } 240 | } 241 | 242 | //------------------------------------------------------------------------------ 243 | 244 | - (void)redraw 245 | { 246 | EZRect frame = [self.waveformLayer frame]; 247 | CGPathRef path = [self createPathWithPoints:self.points 248 | pointCount:self.pointCount 249 | inRect:frame]; 250 | if (self.shouldOptimizeForRealtimePlot) 251 | { 252 | [CATransaction begin]; 253 | [CATransaction setDisableActions:YES]; 254 | self.waveformLayer.path = path; 255 | [CATransaction commit]; 256 | } 257 | else 258 | { 259 | self.waveformLayer.path = path; 260 | } 261 | CGPathRelease(path); 262 | } 263 | 264 | //------------------------------------------------------------------------------ 265 | 266 | - (CGPathRef)createPathWithPoints:(CGPoint *)points 267 | pointCount:(UInt32)pointCount 268 | inRect:(EZRect)rect 269 | { 270 | CGMutablePathRef path = NULL; 271 | if (pointCount > 0) 272 | { 273 | path = CGPathCreateMutable(); 274 | double xscale = (rect.size.width) / ((float)self.pointCount); 275 | double halfHeight = floor(rect.size.height / 2.0); 276 | int deviceOriginFlipped = [self isDeviceOriginFlipped] ? -1 : 1; 277 | CGAffineTransform xf = CGAffineTransformIdentity; 278 | CGFloat translateY = 0.0f; 279 | if (!self.shouldCenterYAxis) 280 | { 281 | #if TARGET_OS_IPHONE 282 | translateY = CGRectGetHeight(rect); 283 | #elif TARGET_OS_MAC 284 | translateY = 0.0f; 285 | #endif 286 | } 287 | else 288 | { 289 | translateY = halfHeight + rect.origin.y; 290 | } 291 | xf = CGAffineTransformTranslate(xf, 0.0, translateY); 292 | double yScaleFactor = halfHeight; 293 | if (!self.shouldCenterYAxis) 294 | { 295 | yScaleFactor = 2.0 * halfHeight; 296 | } 297 | xf = CGAffineTransformScale(xf, xscale, deviceOriginFlipped * yScaleFactor); 298 | CGPathAddLines(path, &xf, self.points, self.pointCount); 299 | if (self.shouldMirror) 300 | { 301 | xf = CGAffineTransformScale(xf, 1.0f, -1.0f); 302 | CGPathAddLines(path, &xf, self.points, self.pointCount); 303 | } 304 | if (self.shouldFill) 305 | { 306 | CGPathCloseSubpath(path); 307 | } 308 | } 309 | return path; 310 | } 311 | 312 | //------------------------------------------------------------------------------ 313 | #pragma mark - Update 314 | //------------------------------------------------------------------------------ 315 | 316 | - (void)updateBuffer:(float *)buffer withBufferSize:(UInt32)bufferSize 317 | { 318 | // append the buffer to the history 319 | [EZAudioUtilities appendBufferRMS:buffer 320 | withBufferSize:bufferSize 321 | toHistoryInfo:self.historyInfo]; 322 | 323 | // copy samples 324 | switch (self.plotType) 325 | { 326 | case EZPlotTypeBuffer: 327 | [self setSampleData:buffer 328 | length:bufferSize]; 329 | break; 330 | case EZPlotTypeRolling: 331 | 332 | [self setSampleData:self.historyInfo->buffer 333 | length:self.historyInfo->bufferSize]; 334 | break; 335 | default: 336 | break; 337 | } 338 | 339 | // update drawing 340 | if (!self.shouldOptimizeForRealtimePlot) 341 | { 342 | [self redraw]; 343 | } 344 | } 345 | 346 | //------------------------------------------------------------------------------ 347 | 348 | - (void)setSampleData:(float *)data length:(int)length 349 | { 350 | CGPoint *points = self.points; 351 | for (int i = 0; i < length; i++) 352 | { 353 | points[i].x = i; 354 | points[i].y = data[i] * self.gain; 355 | } 356 | points[0].y = points[length - 1].y = 0.0f; 357 | self.pointCount = length; 358 | } 359 | 360 | //------------------------------------------------------------------------------ 361 | #pragma mark - Adjusting History Resolution 362 | //------------------------------------------------------------------------------ 363 | 364 | - (int)rollingHistoryLength 365 | { 366 | return self.historyInfo->bufferSize; 367 | } 368 | 369 | //------------------------------------------------------------------------------ 370 | 371 | - (int)setRollingHistoryLength:(int)historyLength 372 | { 373 | self.historyInfo->bufferSize = MIN(EZAudioPlotDefaultMaxHistoryBufferLength, historyLength); 374 | return self.historyInfo->bufferSize; 375 | } 376 | 377 | //------------------------------------------------------------------------------ 378 | #pragma mark - Subclass 379 | //------------------------------------------------------------------------------ 380 | 381 | - (int)defaultRollingHistoryLength 382 | { 383 | return EZAudioPlotDefaultHistoryBufferLength; 384 | } 385 | 386 | //------------------------------------------------------------------------------ 387 | 388 | - (int)initialPointCount 389 | { 390 | return 100; 391 | } 392 | 393 | //------------------------------------------------------------------------------ 394 | 395 | - (int)maximumRollingHistoryLength 396 | { 397 | return EZAudioPlotDefaultMaxHistoryBufferLength; 398 | } 399 | 400 | //------------------------------------------------------------------------------ 401 | #pragma mark - Utility 402 | //------------------------------------------------------------------------------ 403 | 404 | - (BOOL)isDeviceOriginFlipped 405 | { 406 | BOOL isDeviceOriginFlipped = NO; 407 | #if TARGET_OS_IPHONE 408 | isDeviceOriginFlipped = YES; 409 | #elif TARGET_OS_MAC 410 | #endif 411 | return isDeviceOriginFlipped; 412 | } 413 | 414 | //------------------------------------------------------------------------------ 415 | #pragma mark - EZAudioDisplayLinkDelegate 416 | //------------------------------------------------------------------------------ 417 | 418 | - (void)displayLinkNeedsDisplay:(EZAudioDisplayLink *)displayLink 419 | { 420 | [self redraw]; 421 | } 422 | 423 | //------------------------------------------------------------------------------ 424 | 425 | @end 426 | 427 | ////------------------------------------------------------------------------------ 428 | #pragma mark - EZAudioPlotWaveformLayer (Implementation) 429 | ////------------------------------------------------------------------------------ 430 | 431 | @implementation EZAudioPlotWaveformLayer 432 | 433 | - (id)actionForKey:(NSString *)event 434 | { 435 | if ([event isEqualToString:@"path"]) 436 | { 437 | if ([CATransaction disableActions]) 438 | { 439 | return nil; 440 | } 441 | else 442 | { 443 | CABasicAnimation *animation = [CABasicAnimation animation]; 444 | animation.timingFunction = [CATransaction animationTimingFunction]; 445 | animation.duration = [CATransaction animationDuration]; 446 | return animation; 447 | } 448 | return nil; 449 | } 450 | return [super actionForKey:event]; 451 | } 452 | 453 | @end -------------------------------------------------------------------------------- /AudioTrimmer/AudioTrimmerController/EZAudio/EZAudioPlayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // EZAudioPlayer.m 3 | // EZAudio 4 | // 5 | // Created by Syed Haris Ali on 1/16/14. 6 | // Copyright (c) 2014 Syed Haris Ali. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | 26 | #import "EZAudioPlayer.h" 27 | #import "EZAudioUtilities.h" 28 | 29 | //------------------------------------------------------------------------------ 30 | #pragma mark - Notifications 31 | //------------------------------------------------------------------------------ 32 | 33 | NSString * const EZAudioPlayerDidChangeAudioFileNotification = @"EZAudioPlayerDidChangeAudioFileNotification"; 34 | NSString * const EZAudioPlayerDidChangeOutputDeviceNotification = @"EZAudioPlayerDidChangeOutputDeviceNotification"; 35 | NSString * const EZAudioPlayerDidChangePanNotification = @"EZAudioPlayerDidChangePanNotification"; 36 | NSString * const EZAudioPlayerDidChangePlayStateNotification = @"EZAudioPlayerDidChangePlayStateNotification"; 37 | NSString * const EZAudioPlayerDidChangeVolumeNotification = @"EZAudioPlayerDidChangeVolumeNotification"; 38 | NSString * const EZAudioPlayerDidReachEndOfFileNotification = @"EZAudioPlayerDidReachEndOfFileNotification"; 39 | NSString * const EZAudioPlayerDidSeekNotification = @"EZAudioPlayerDidSeekNotification"; 40 | 41 | //------------------------------------------------------------------------------ 42 | #pragma mark - EZAudioPlayer (Interface Extension) 43 | //------------------------------------------------------------------------------ 44 | 45 | @interface EZAudioPlayer () 46 | @property (nonatomic, assign, readwrite) EZAudioPlayerState state; 47 | @end 48 | 49 | //------------------------------------------------------------------------------ 50 | #pragma mark - EZAudioPlayer (Implementation) 51 | //------------------------------------------------------------------------------ 52 | 53 | @implementation EZAudioPlayer 54 | 55 | //------------------------------------------------------------------------------ 56 | #pragma mark - Class Methods 57 | //------------------------------------------------------------------------------ 58 | 59 | + (instancetype)audioPlayer 60 | { 61 | return [[self alloc] init]; 62 | } 63 | 64 | //------------------------------------------------------------------------------ 65 | 66 | + (instancetype)audioPlayerWithDelegate:(id)delegate 67 | { 68 | return [[self alloc] initWithDelegate:delegate]; 69 | } 70 | 71 | //------------------------------------------------------------------------------ 72 | 73 | + (instancetype)audioPlayerWithAudioFile:(EZAudioFile *)audioFile 74 | { 75 | return [[self alloc] initWithAudioFile:audioFile]; 76 | } 77 | 78 | //------------------------------------------------------------------------------ 79 | 80 | + (instancetype)audioPlayerWithAudioFile:(EZAudioFile *)audioFile 81 | delegate:(id)delegate 82 | { 83 | return [[self alloc] initWithAudioFile:audioFile 84 | delegate:delegate]; 85 | } 86 | 87 | //------------------------------------------------------------------------------ 88 | 89 | + (instancetype)audioPlayerWithURL:(NSURL *)url 90 | { 91 | return [[self alloc] initWithURL:url]; 92 | } 93 | 94 | //------------------------------------------------------------------------------ 95 | 96 | + (instancetype)audioPlayerWithURL:(NSURL *)url 97 | delegate:(id)delegate 98 | { 99 | return [[self alloc] initWithURL:url delegate:delegate]; 100 | } 101 | 102 | //------------------------------------------------------------------------------ 103 | #pragma mark - Initialization 104 | //------------------------------------------------------------------------------ 105 | 106 | - (instancetype)init 107 | { 108 | self = [super init]; 109 | if (self) 110 | { 111 | [self setup]; 112 | } 113 | return self; 114 | } 115 | 116 | //------------------------------------------------------------------------------ 117 | 118 | - (instancetype)initWithDelegate:(id)delegate 119 | { 120 | self = [self init]; 121 | if (self) 122 | { 123 | self.delegate = delegate; 124 | } 125 | return self; 126 | } 127 | 128 | //------------------------------------------------------------------------------ 129 | 130 | - (instancetype)initWithAudioFile:(EZAudioFile *)audioFile 131 | { 132 | return [self initWithAudioFile:audioFile delegate:nil]; 133 | } 134 | 135 | //------------------------------------------------------------------------------ 136 | 137 | - (instancetype)initWithAudioFile:(EZAudioFile *)audioFile 138 | delegate:(id)delegate 139 | { 140 | self = [self initWithDelegate:delegate]; 141 | if (self) 142 | { 143 | self.audioFile = audioFile; 144 | } 145 | return self; 146 | } 147 | 148 | //------------------------------------------------------------------------------ 149 | 150 | - (instancetype)initWithURL:(NSURL *)url 151 | { 152 | return [self initWithURL:url delegate:nil]; 153 | } 154 | 155 | //------------------------------------------------------------------------------ 156 | 157 | - (instancetype)initWithURL:(NSURL *)url 158 | delegate:(id)delegate 159 | { 160 | self = [self initWithDelegate:delegate]; 161 | if (self) 162 | { 163 | self.audioFile = [EZAudioFile audioFileWithURL:url delegate:self]; 164 | } 165 | return self; 166 | } 167 | 168 | //------------------------------------------------------------------------------ 169 | #pragma mark - Singleton 170 | //------------------------------------------------------------------------------ 171 | 172 | + (instancetype)sharedAudioPlayer 173 | { 174 | static EZAudioPlayer *player; 175 | static dispatch_once_t onceToken; 176 | dispatch_once(&onceToken, ^ 177 | { 178 | player = [[self alloc] init]; 179 | }); 180 | return player; 181 | } 182 | 183 | //------------------------------------------------------------------------------ 184 | #pragma mark - Setup 185 | //------------------------------------------------------------------------------ 186 | 187 | - (void)setup 188 | { 189 | self.output = [EZOutput output]; 190 | self.state = EZAudioPlayerStateReadyToPlay; 191 | } 192 | 193 | //------------------------------------------------------------------------------ 194 | #pragma mark - Getters 195 | //------------------------------------------------------------------------------ 196 | 197 | - (NSTimeInterval)currentTime 198 | { 199 | return [self.audioFile currentTime]; 200 | } 201 | 202 | //------------------------------------------------------------------------------ 203 | 204 | - (EZAudioDevice *)device 205 | { 206 | return [self.output device]; 207 | } 208 | 209 | //------------------------------------------------------------------------------ 210 | 211 | - (NSTimeInterval)duration 212 | { 213 | return [self.audioFile duration]; 214 | } 215 | 216 | //------------------------------------------------------------------------------ 217 | 218 | - (NSString *)formattedCurrentTime 219 | { 220 | return [self.audioFile formattedCurrentTime]; 221 | } 222 | 223 | //------------------------------------------------------------------------------ 224 | 225 | - (NSString *)formattedDuration 226 | { 227 | return [self.audioFile formattedDuration]; 228 | } 229 | 230 | //------------------------------------------------------------------------------ 231 | 232 | - (SInt64)frameIndex 233 | { 234 | return [self.audioFile frameIndex]; 235 | } 236 | 237 | //------------------------------------------------------------------------------ 238 | 239 | - (BOOL)isPlaying 240 | { 241 | return [self.output isPlaying]; 242 | } 243 | 244 | //------------------------------------------------------------------------------ 245 | 246 | - (float)pan 247 | { 248 | return [self.output pan]; 249 | } 250 | 251 | //------------------------------------------------------------------------------ 252 | 253 | - (SInt64)totalFrames 254 | { 255 | return [self.audioFile totalFrames]; 256 | } 257 | 258 | //------------------------------------------------------------------------------ 259 | 260 | - (float)volume 261 | { 262 | return [self.output volume]; 263 | } 264 | 265 | //------------------------------------------------------------------------------ 266 | #pragma mark - Setters 267 | //------------------------------------------------------------------------------ 268 | 269 | - (void)setAudioFile:(EZAudioFile *)audioFile 270 | { 271 | _audioFile = [audioFile copy]; 272 | _audioFile.delegate = self; 273 | AudioStreamBasicDescription inputFormat = _audioFile.clientFormat; 274 | [self.output setInputFormat:inputFormat]; 275 | [[NSNotificationCenter defaultCenter] postNotificationName:EZAudioPlayerDidChangeAudioFileNotification 276 | object:self]; 277 | } 278 | 279 | //------------------------------------------------------------------------------ 280 | 281 | - (void)setCurrentTime:(NSTimeInterval)currentTime 282 | { 283 | [self.audioFile setCurrentTime:currentTime]; 284 | [[NSNotificationCenter defaultCenter] postNotificationName:EZAudioPlayerDidSeekNotification 285 | object:self]; 286 | } 287 | 288 | //------------------------------------------------------------------------------ 289 | 290 | - (void)setDevice:(EZAudioDevice *)device 291 | { 292 | [self.output setDevice:device]; 293 | } 294 | 295 | //------------------------------------------------------------------------------ 296 | 297 | - (void)setOutput:(EZOutput *)output 298 | { 299 | _output = output; 300 | _output.dataSource = self; 301 | _output.delegate = self; 302 | } 303 | 304 | //------------------------------------------------------------------------------ 305 | 306 | - (void)setPan:(float)pan 307 | { 308 | [self.output setPan:pan]; 309 | [[NSNotificationCenter defaultCenter] postNotificationName:EZAudioPlayerDidChangePanNotification 310 | object:self]; 311 | } 312 | 313 | //------------------------------------------------------------------------------ 314 | 315 | - (void)setVolume:(float)volume 316 | { 317 | [self.output setVolume:volume]; 318 | [[NSNotificationCenter defaultCenter] postNotificationName:EZAudioPlayerDidChangeVolumeNotification 319 | object:self]; 320 | } 321 | 322 | //------------------------------------------------------------------------------ 323 | #pragma mark - Actions 324 | //------------------------------------------------------------------------------ 325 | 326 | - (void)play 327 | { 328 | [self.output startPlayback]; 329 | self.state = EZAudioPlayerStatePlaying; 330 | } 331 | 332 | //------------------------------------------------------------------------------ 333 | 334 | - (void)playAudioFile:(EZAudioFile *)audioFile 335 | { 336 | // 337 | // stop playing anything that might currently be playing 338 | // 339 | [self pause]; 340 | 341 | // 342 | // set new stream 343 | // 344 | self.audioFile = audioFile; 345 | 346 | // 347 | // begin playback 348 | // 349 | [self play]; 350 | } 351 | 352 | //------------------------------------------------------------------------------ 353 | 354 | - (void)pause 355 | { 356 | [self.output stopPlayback]; 357 | self.state = EZAudioPlayerStatePaused; 358 | } 359 | 360 | //------------------------------------------------------------------------------ 361 | 362 | - (void)seekToFrame:(SInt64)frame 363 | { 364 | self.state = EZAudioPlayerStateSeeking; 365 | [self.audioFile seekToFrame:frame]; 366 | self.state = self.isPlaying ? EZAudioPlayerStatePlaying : EZAudioPlayerStatePaused; 367 | [[NSNotificationCenter defaultCenter] postNotificationName:EZAudioPlayerDidSeekNotification 368 | object:self]; 369 | } 370 | 371 | //------------------------------------------------------------------------------ 372 | #pragma mark - EZOutputDataSource 373 | //------------------------------------------------------------------------------ 374 | 375 | - (OSStatus) output:(EZOutput *)output 376 | shouldFillAudioBufferList:(AudioBufferList *)audioBufferList 377 | withNumberOfFrames:(UInt32)frames 378 | timestamp:(const AudioTimeStamp *)timestamp 379 | { 380 | if (self.audioFile) 381 | { 382 | UInt32 bufferSize; 383 | BOOL eof; 384 | [self.audioFile readFrames:frames 385 | audioBufferList:audioBufferList 386 | bufferSize:&bufferSize 387 | eof:&eof]; 388 | if (eof && [self.delegate respondsToSelector:@selector(audioPlayer:reachedEndOfAudioFile:)]) 389 | { 390 | [self.delegate audioPlayer:self reachedEndOfAudioFile:self.audioFile]; 391 | } 392 | if (eof && self.shouldLoop) 393 | { 394 | [self seekToFrame:0]; 395 | } 396 | else if (eof) 397 | { 398 | [self pause]; 399 | [self seekToFrame:0]; 400 | self.state = EZAudioPlayerStateEndOfFile; 401 | [[NSNotificationCenter defaultCenter] postNotificationName:EZAudioPlayerDidReachEndOfFileNotification 402 | object:self]; 403 | } 404 | } 405 | return noErr; 406 | } 407 | 408 | //------------------------------------------------------------------------------ 409 | #pragma mark - EZAudioFileDelegate 410 | //------------------------------------------------------------------------------ 411 | 412 | - (void)audioFileUpdatedPosition:(EZAudioFile *)audioFile 413 | { 414 | if ([self.delegate respondsToSelector:@selector(audioPlayer:updatedPosition:inAudioFile:)]) 415 | { 416 | [self.delegate audioPlayer:self 417 | updatedPosition:[audioFile frameIndex] 418 | inAudioFile:audioFile]; 419 | } 420 | } 421 | 422 | //------------------------------------------------------------------------------ 423 | #pragma mark - EZOutputDelegate 424 | //------------------------------------------------------------------------------ 425 | 426 | - (void)output:(EZOutput *)output changedDevice:(EZAudioDevice *)device 427 | { 428 | [[NSNotificationCenter defaultCenter] postNotificationName:EZAudioPlayerDidChangeOutputDeviceNotification 429 | object:self]; 430 | } 431 | 432 | //------------------------------------------------------------------------------ 433 | 434 | - (void)output:(EZOutput *)output changedPlayingState:(BOOL)isPlaying 435 | { 436 | [[NSNotificationCenter defaultCenter] postNotificationName:EZAudioPlayerDidChangePlayStateNotification 437 | object:self]; 438 | } 439 | 440 | //------------------------------------------------------------------------------ 441 | 442 | - (void) output:(EZOutput *)output 443 | playedAudio:(float **)buffer 444 | withBufferSize:(UInt32)bufferSize 445 | withNumberOfChannels:(UInt32)numberOfChannels 446 | { 447 | if ([self.delegate respondsToSelector:@selector(audioPlayer:playedAudio:withBufferSize:withNumberOfChannels:inAudioFile:)]) 448 | { 449 | [self.delegate audioPlayer:self 450 | playedAudio:buffer 451 | withBufferSize:bufferSize 452 | withNumberOfChannels:numberOfChannels 453 | inAudioFile:self.audioFile]; 454 | } 455 | } 456 | 457 | //------------------------------------------------------------------------------ 458 | 459 | @end 460 | -------------------------------------------------------------------------------- /AudioTrimmer/AudioTrimmerController/EZAudio/EZAudioFFT.m: -------------------------------------------------------------------------------- 1 | // 2 | // EZAudioFFT.m 3 | // EZAudio 4 | // 5 | // Created by Syed Haris Ali on 7/10/15. 6 | // Copyright (c) 2015 Syed Haris Ali. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | 26 | #import "EZAudioFFT.h" 27 | #import "EZAudioUtilities.h" 28 | 29 | //------------------------------------------------------------------------------ 30 | #pragma mark - Data Structures 31 | //------------------------------------------------------------------------------ 32 | 33 | typedef struct EZAudioFFTInfo 34 | { 35 | FFTSetup fftSetup; 36 | COMPLEX_SPLIT complexA; 37 | float *outFFTData; 38 | vDSP_Length outFFTDataLength; 39 | float *inversedFFTData; 40 | vDSP_Length maxFrequencyIndex; 41 | float maxFrequencyMangitude; 42 | float maxFrequency; 43 | } EZAudioFFTInfo; 44 | 45 | //------------------------------------------------------------------------------ 46 | #pragma mark - EZAudioFFT (Interface Extension) 47 | //------------------------------------------------------------------------------ 48 | 49 | @interface EZAudioFFT () 50 | @property (assign, nonatomic) EZAudioFFTInfo *info; 51 | @property (readwrite, nonatomic) vDSP_Length maximumBufferSize; 52 | @end 53 | 54 | //------------------------------------------------------------------------------ 55 | #pragma mark - EZAudioFFT (Implementation) 56 | //------------------------------------------------------------------------------ 57 | 58 | @implementation EZAudioFFT 59 | 60 | //------------------------------------------------------------------------------ 61 | #pragma mark - Dealloc 62 | //------------------------------------------------------------------------------ 63 | 64 | - (void)dealloc 65 | { 66 | vDSP_destroy_fftsetup(self.info->fftSetup); 67 | free(self.info->complexA.realp); 68 | free(self.info->complexA.imagp); 69 | free(self.info->outFFTData); 70 | free(self.info->inversedFFTData); 71 | } 72 | 73 | //------------------------------------------------------------------------------ 74 | #pragma mark - Initializers 75 | //------------------------------------------------------------------------------ 76 | 77 | - (instancetype)initWithMaximumBufferSize:(vDSP_Length)maximumBufferSize 78 | sampleRate:(float)sampleRate 79 | { 80 | return [self initWithMaximumBufferSize:maximumBufferSize 81 | sampleRate:sampleRate 82 | delegate:nil]; 83 | } 84 | 85 | //------------------------------------------------------------------------------ 86 | 87 | - (instancetype)initWithMaximumBufferSize:(vDSP_Length)maximumBufferSize 88 | sampleRate:(float)sampleRate 89 | delegate:(id)delegate 90 | { 91 | self = [super init]; 92 | if (self) 93 | { 94 | self.maximumBufferSize = (vDSP_Length)maximumBufferSize; 95 | self.sampleRate = sampleRate; 96 | self.delegate = delegate; 97 | [self setup]; 98 | } 99 | return self; 100 | } 101 | 102 | //------------------------------------------------------------------------------ 103 | #pragma mark - Class Initializers 104 | //------------------------------------------------------------------------------ 105 | 106 | + (instancetype)fftWithMaximumBufferSize:(vDSP_Length)maximumBufferSize 107 | sampleRate:(float)sampleRate 108 | { 109 | return [[self alloc] initWithMaximumBufferSize:maximumBufferSize 110 | sampleRate:sampleRate]; 111 | } 112 | 113 | //------------------------------------------------------------------------------ 114 | 115 | + (instancetype)fftWithMaximumBufferSize:(vDSP_Length)maximumBufferSize 116 | sampleRate:(float)sampleRate 117 | delegate:(id)delegate 118 | { 119 | return [[self alloc] initWithMaximumBufferSize:maximumBufferSize 120 | sampleRate:sampleRate 121 | delegate:delegate]; 122 | } 123 | 124 | //------------------------------------------------------------------------------ 125 | #pragma mark - Setup 126 | //------------------------------------------------------------------------------ 127 | 128 | - (void)setup 129 | { 130 | NSAssert(self.maximumBufferSize > 0, @"Expected FFT buffer size to be greater than 0!"); 131 | 132 | // 133 | // Initialize FFT 134 | // 135 | float maximumBufferSizeBytes = self.maximumBufferSize * sizeof(float); 136 | self.info = (EZAudioFFTInfo *)calloc(1, sizeof(EZAudioFFTInfo)); 137 | vDSP_Length log2n = log2f(self.maximumBufferSize); 138 | self.info->fftSetup = vDSP_create_fftsetup(log2n, FFT_RADIX2); 139 | long nOver2 = maximumBufferSizeBytes / 2; 140 | size_t maximumSizePerComponentBytes = nOver2 * sizeof(float); 141 | self.info->complexA.realp = (float *)malloc(maximumSizePerComponentBytes); 142 | self.info->complexA.imagp = (float *)malloc(maximumSizePerComponentBytes); 143 | self.info->outFFTData = (float *)malloc(maximumSizePerComponentBytes); 144 | memset(self.info->outFFTData, 0, maximumSizePerComponentBytes); 145 | self.info->inversedFFTData = (float *)malloc(maximumSizePerComponentBytes); 146 | } 147 | 148 | //------------------------------------------------------------------------------ 149 | #pragma mark - Actions 150 | //------------------------------------------------------------------------------ 151 | 152 | - (float *)computeFFTWithBuffer:(float *)buffer withBufferSize:(UInt32)bufferSize 153 | { 154 | if (buffer == NULL) 155 | { 156 | return NULL; 157 | } 158 | 159 | // 160 | // Calculate real + imaginary components and normalize 161 | // 162 | vDSP_Length log2n = log2f(bufferSize); 163 | long nOver2 = bufferSize / 2; 164 | float mFFTNormFactor = 10.0 / (2 * bufferSize); 165 | vDSP_ctoz((COMPLEX*)buffer, 2, &(self.info->complexA), 1, nOver2); 166 | vDSP_fft_zrip(self.info->fftSetup, &(self.info->complexA), 1, log2n, FFT_FORWARD); 167 | vDSP_vsmul(self.info->complexA.realp, 1, &mFFTNormFactor, self.info->complexA.realp, 1, nOver2); 168 | vDSP_vsmul(self.info->complexA.imagp, 1, &mFFTNormFactor, self.info->complexA.imagp, 1, nOver2); 169 | vDSP_zvmags(&(self.info->complexA), 1, self.info->outFFTData, 1, nOver2); 170 | vDSP_fft_zrip(self.info->fftSetup, &(self.info->complexA), 1, log2n, FFT_INVERSE); 171 | vDSP_ztoc(&(self.info->complexA), 1, (COMPLEX *) self.info->inversedFFTData , 2, nOver2); 172 | self.info->outFFTDataLength = nOver2; 173 | 174 | // 175 | // Calculate max freq 176 | // 177 | if (self.sampleRate > 0.0f) 178 | { 179 | vDSP_maxvi(self.info->outFFTData, 1, &self.info->maxFrequencyMangitude, &self.info->maxFrequencyIndex, nOver2); 180 | self.info->maxFrequency = [self frequencyAtIndex:self.info->maxFrequencyIndex]; 181 | } 182 | 183 | // 184 | // Notify delegate 185 | // 186 | if ([self.delegate respondsToSelector:@selector(fft:updatedWithFFTData:bufferSize:)]) 187 | { 188 | [self.delegate fft:self 189 | updatedWithFFTData:self.info->outFFTData 190 | bufferSize:nOver2]; 191 | } 192 | 193 | // 194 | // Return the FFT 195 | // 196 | return self.info->outFFTData; 197 | } 198 | 199 | //------------------------------------------------------------------------------ 200 | 201 | - (float)frequencyAtIndex:(vDSP_Length)index 202 | { 203 | if (!(self.info->outFFTData == NULL || self.sampleRate == 0.0f)) 204 | { 205 | float nyquistMaxFreq = self.sampleRate / 2.0; 206 | return ((float)index / (float)self.info->outFFTDataLength) * nyquistMaxFreq; 207 | } 208 | return NSNotFound; 209 | } 210 | 211 | //------------------------------------------------------------------------------ 212 | 213 | - (float)frequencyMagnitudeAtIndex:(vDSP_Length)index 214 | { 215 | if (self.info->outFFTData != NULL) 216 | { 217 | return self.info->outFFTData[index]; 218 | } 219 | return NSNotFound; 220 | } 221 | 222 | //------------------------------------------------------------------------------ 223 | #pragma mark - Getters 224 | //------------------------------------------------------------------------------ 225 | 226 | - (COMPLEX_SPLIT)complexSplit 227 | { 228 | return self.info->complexA; 229 | } 230 | 231 | //------------------------------------------------------------------------------ 232 | 233 | - (float *)fftData 234 | { 235 | return self.info->outFFTData; 236 | } 237 | 238 | //------------------------------------------------------------------------------ 239 | 240 | - (FFTSetup)fftSetup 241 | { 242 | return self.info->fftSetup; 243 | } 244 | 245 | //------------------------------------------------------------------------------ 246 | 247 | - (float *)inversedFFTData 248 | { 249 | return self.info->inversedFFTData; 250 | } 251 | 252 | //------------------------------------------------------------------------------ 253 | 254 | - (vDSP_Length)maxFrequencyIndex 255 | { 256 | return self.info->maxFrequencyIndex; 257 | } 258 | 259 | //------------------------------------------------------------------------------ 260 | 261 | - (float)maxFrequencyMagnitude 262 | { 263 | return self.info->maxFrequencyMangitude; 264 | } 265 | 266 | //------------------------------------------------------------------------------ 267 | 268 | - (float)maxFrequency 269 | { 270 | return self.info->maxFrequency; 271 | } 272 | 273 | @end 274 | 275 | //------------------------------------------------------------------------------ 276 | #pragma mark - EZAudioFFTRolling 277 | //------------------------------------------------------------------------------ 278 | 279 | @interface EZAudioFFTRolling () 280 | @property (assign, nonatomic) EZPlotHistoryInfo *historyInfo; 281 | @property (readwrite, nonatomic) vDSP_Length windowSize; 282 | 283 | @end 284 | 285 | @implementation EZAudioFFTRolling 286 | 287 | //------------------------------------------------------------------------------ 288 | #pragma mark - Dealloc 289 | //------------------------------------------------------------------------------ 290 | 291 | - (void)dealloc 292 | { 293 | [EZAudioUtilities freeHistoryInfo:self.historyInfo]; 294 | } 295 | 296 | //------------------------------------------------------------------------------ 297 | #pragma mark - Initialization 298 | //------------------------------------------------------------------------------ 299 | 300 | - (instancetype)initWithWindowSize:(vDSP_Length)windowSize 301 | sampleRate:(float)sampleRate 302 | { 303 | return [self initWithWindowSize:windowSize 304 | historyBufferSize:windowSize * 8 305 | sampleRate:sampleRate 306 | delegate:nil]; 307 | } 308 | 309 | //------------------------------------------------------------------------------ 310 | 311 | - (instancetype)initWithWindowSize:(vDSP_Length)windowSize 312 | sampleRate:(float)sampleRate 313 | delegate:(id)delegate 314 | { 315 | return [self initWithWindowSize:windowSize 316 | historyBufferSize:windowSize * 8 317 | sampleRate:sampleRate 318 | delegate:delegate]; 319 | } 320 | 321 | //------------------------------------------------------------------------------ 322 | 323 | - (instancetype)initWithWindowSize:(vDSP_Length)windowSize 324 | historyBufferSize:(vDSP_Length)historyBufferSize 325 | sampleRate:(float)sampleRate 326 | { 327 | return [self initWithWindowSize:windowSize 328 | historyBufferSize:historyBufferSize 329 | sampleRate:sampleRate 330 | delegate:nil]; 331 | } 332 | 333 | //------------------------------------------------------------------------------ 334 | 335 | - (instancetype)initWithWindowSize:(vDSP_Length)windowSize 336 | historyBufferSize:(vDSP_Length)historyBufferSize 337 | sampleRate:(float)sampleRate 338 | delegate:(id)delegate 339 | { 340 | self = [super initWithMaximumBufferSize:historyBufferSize 341 | sampleRate:sampleRate]; 342 | if (self) 343 | { 344 | self.delegate = delegate; 345 | self.windowSize = windowSize; 346 | 347 | // 348 | // Allocate an appropriately sized history buffer in bytes 349 | // 350 | self.historyInfo = [EZAudioUtilities historyInfoWithDefaultLength:(UInt32)windowSize 351 | maximumLength:(UInt32)historyBufferSize]; 352 | } 353 | return self; 354 | } 355 | 356 | //------------------------------------------------------------------------------ 357 | #pragma mark - Class Initializers 358 | //------------------------------------------------------------------------------ 359 | 360 | + (instancetype)fftWithWindowSize:(vDSP_Length)windowSize 361 | sampleRate:(float)sampleRate 362 | { 363 | return [[self alloc] initWithWindowSize:windowSize 364 | sampleRate:sampleRate]; 365 | } 366 | 367 | //------------------------------------------------------------------------------ 368 | 369 | + (instancetype)fftWithWindowSize:(vDSP_Length)windowSize 370 | sampleRate:(float)sampleRate 371 | delegate:(id)delegate 372 | { 373 | return [[self alloc] initWithWindowSize:windowSize 374 | sampleRate:sampleRate 375 | delegate:delegate]; 376 | } 377 | 378 | //------------------------------------------------------------------------------ 379 | 380 | + (instancetype)fftWithWindowSize:(vDSP_Length)windowSize 381 | historyBufferSize:(vDSP_Length)historyBufferSize 382 | sampleRate:(float)sampleRate 383 | { 384 | return [[self alloc] initWithWindowSize:windowSize 385 | historyBufferSize:historyBufferSize 386 | sampleRate:sampleRate]; 387 | } 388 | 389 | //------------------------------------------------------------------------------ 390 | 391 | + (instancetype)fftWithWindowSize:(vDSP_Length)windowSize 392 | historyBufferSize:(vDSP_Length)historyBufferSize 393 | sampleRate:(float)sampleRate 394 | delegate:(id)delegate 395 | { 396 | return [[self alloc] initWithWindowSize:windowSize 397 | historyBufferSize:historyBufferSize 398 | sampleRate:sampleRate 399 | delegate:delegate]; 400 | } 401 | 402 | //------------------------------------------------------------------------------ 403 | #pragma mark - Actions 404 | //------------------------------------------------------------------------------ 405 | 406 | - (float *)computeFFTWithBuffer:(float *)buffer 407 | withBufferSize:(UInt32)bufferSize 408 | { 409 | if (buffer == NULL) 410 | { 411 | return NULL; 412 | } 413 | 414 | // 415 | // Append buffer to history window 416 | // 417 | [EZAudioUtilities appendBuffer:buffer 418 | withBufferSize:bufferSize 419 | toHistoryInfo:self.historyInfo]; 420 | 421 | // 422 | // Call super to calculate the FFT of the window 423 | // 424 | return [super computeFFTWithBuffer:self.historyInfo->buffer 425 | withBufferSize:self.historyInfo->bufferSize]; 426 | } 427 | 428 | //------------------------------------------------------------------------------ 429 | #pragma mark - Getters 430 | //------------------------------------------------------------------------------ 431 | 432 | - (UInt32)timeDomainBufferSize 433 | { 434 | return self.historyInfo->bufferSize; 435 | } 436 | 437 | //------------------------------------------------------------------------------ 438 | 439 | - (float *)timeDomainData 440 | { 441 | return self.historyInfo->buffer; 442 | } 443 | 444 | @end 445 | -------------------------------------------------------------------------------- /AudioTrimmer/AudioTrimmerController/EZAudio/EZRecorder.m: -------------------------------------------------------------------------------- 1 | // 2 | // EZRecorder.m 3 | // EZAudio 4 | // 5 | // Created by Syed Haris Ali on 12/1/13. 6 | // Copyright (c) 2015 Syed Haris Ali. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | 26 | #import "EZRecorder.h" 27 | #import "EZAudioUtilities.h" 28 | 29 | //------------------------------------------------------------------------------ 30 | #pragma mark - Data Structures 31 | //------------------------------------------------------------------------------ 32 | 33 | typedef struct 34 | { 35 | AudioFileTypeID audioFileTypeID; 36 | ExtAudioFileRef extAudioFileRef; 37 | AudioStreamBasicDescription clientFormat; 38 | BOOL closed; 39 | CFURLRef fileURL; 40 | AudioStreamBasicDescription fileFormat; 41 | } EZRecorderInfo; 42 | 43 | //------------------------------------------------------------------------------ 44 | #pragma mark - EZRecorder (Interface Extension) 45 | //------------------------------------------------------------------------------ 46 | 47 | @interface EZRecorder () 48 | @property (nonatomic, assign) EZRecorderInfo *info; 49 | @end 50 | 51 | //------------------------------------------------------------------------------ 52 | #pragma mark - EZRecorder (Implementation) 53 | //------------------------------------------------------------------------------ 54 | 55 | @implementation EZRecorder 56 | 57 | //------------------------------------------------------------------------------ 58 | #pragma mark - Dealloc 59 | //------------------------------------------------------------------------------ 60 | 61 | - (void)dealloc 62 | { 63 | if (!self.info->closed) 64 | { 65 | [self closeAudioFile]; 66 | } 67 | free(self.info); 68 | } 69 | 70 | //------------------------------------------------------------------------------ 71 | #pragma mark - Initializers 72 | //------------------------------------------------------------------------------ 73 | 74 | - (instancetype)initWithURL:(NSURL *)url 75 | clientFormat:(AudioStreamBasicDescription)clientFormat 76 | fileType:(EZRecorderFileType)fileType 77 | { 78 | return [self initWithURL:url 79 | clientFormat:clientFormat 80 | fileType:fileType 81 | delegate:nil]; 82 | } 83 | 84 | //------------------------------------------------------------------------------ 85 | 86 | - (instancetype)initWithURL:(NSURL *)url 87 | clientFormat:(AudioStreamBasicDescription)clientFormat 88 | fileType:(EZRecorderFileType)fileType 89 | delegate:(id)delegate 90 | { 91 | AudioStreamBasicDescription fileFormat = [EZRecorder formatForFileType:fileType 92 | withSourceFormat:clientFormat]; 93 | AudioFileTypeID audioFileTypeID = [EZRecorder fileTypeIdForFileType:fileType 94 | withSourceFormat:clientFormat]; 95 | return [self initWithURL:url 96 | clientFormat:clientFormat 97 | fileFormat:fileFormat 98 | audioFileTypeID:audioFileTypeID 99 | delegate:delegate]; 100 | } 101 | 102 | //------------------------------------------------------------------------------ 103 | 104 | - (instancetype)initWithURL:(NSURL *)url 105 | clientFormat:(AudioStreamBasicDescription)clientFormat 106 | fileFormat:(AudioStreamBasicDescription)fileFormat 107 | audioFileTypeID:(AudioFileTypeID)audioFileTypeID 108 | { 109 | return [self initWithURL:url 110 | clientFormat:clientFormat 111 | fileFormat:fileFormat 112 | audioFileTypeID:audioFileTypeID 113 | delegate:nil]; 114 | } 115 | 116 | //------------------------------------------------------------------------------ 117 | 118 | - (instancetype)initWithURL:(NSURL *)url 119 | clientFormat:(AudioStreamBasicDescription)clientFormat 120 | fileFormat:(AudioStreamBasicDescription)fileFormat 121 | audioFileTypeID:(AudioFileTypeID)audioFileTypeID 122 | delegate:(id)delegate 123 | { 124 | 125 | self = [super init]; 126 | if (self) 127 | { 128 | // Set defaults 129 | self.info = (EZRecorderInfo *)calloc(1, sizeof(EZRecorderInfo)); 130 | self.info->audioFileTypeID = audioFileTypeID; 131 | self.info->fileURL = (__bridge CFURLRef)url; 132 | self.info->clientFormat = clientFormat; 133 | self.info->fileFormat = fileFormat; 134 | self.delegate = delegate; 135 | [self setup]; 136 | } 137 | return self; 138 | } 139 | 140 | //------------------------------------------------------------------------------ 141 | 142 | - (instancetype)initWithDestinationURL:(NSURL*)url 143 | sourceFormat:(AudioStreamBasicDescription)sourceFormat 144 | destinationFileType:(EZRecorderFileType)destinationFileType 145 | { 146 | return [self initWithURL:url 147 | clientFormat:sourceFormat 148 | fileType:destinationFileType]; 149 | } 150 | 151 | //------------------------------------------------------------------------------ 152 | #pragma mark - Class Initializers 153 | //------------------------------------------------------------------------------ 154 | 155 | + (instancetype)recorderWithURL:(NSURL *)url 156 | clientFormat:(AudioStreamBasicDescription)clientFormat 157 | fileType:(EZRecorderFileType)fileType 158 | { 159 | return [[self alloc] initWithURL:url 160 | clientFormat:clientFormat 161 | fileType:fileType]; 162 | } 163 | 164 | //------------------------------------------------------------------------------ 165 | 166 | + (instancetype)recorderWithURL:(NSURL *)url 167 | clientFormat:(AudioStreamBasicDescription)clientFormat 168 | fileType:(EZRecorderFileType)fileType 169 | delegate:(id)delegate 170 | { 171 | return [[self alloc] initWithURL:url 172 | clientFormat:clientFormat 173 | fileType:fileType 174 | delegate:delegate]; 175 | } 176 | 177 | //------------------------------------------------------------------------------ 178 | 179 | + (instancetype)recorderWithURL:(NSURL *)url 180 | clientFormat:(AudioStreamBasicDescription)clientFormat 181 | fileFormat:(AudioStreamBasicDescription)fileFormat 182 | audioFileTypeID:(AudioFileTypeID)audioFileTypeID 183 | { 184 | return [[self alloc] initWithURL:url 185 | clientFormat:clientFormat 186 | fileFormat:fileFormat 187 | audioFileTypeID:audioFileTypeID]; 188 | } 189 | 190 | //------------------------------------------------------------------------------ 191 | 192 | + (instancetype)recorderWithURL:(NSURL *)url 193 | clientFormat:(AudioStreamBasicDescription)clientFormat 194 | fileFormat:(AudioStreamBasicDescription)fileFormat 195 | audioFileTypeID:(AudioFileTypeID)audioFileTypeID 196 | delegate:(id)delegate 197 | { 198 | return [[self alloc] initWithURL:url 199 | clientFormat:clientFormat 200 | fileFormat:fileFormat 201 | audioFileTypeID:audioFileTypeID 202 | delegate:delegate]; 203 | } 204 | 205 | //------------------------------------------------------------------------------ 206 | 207 | + (instancetype)recorderWithDestinationURL:(NSURL*)url 208 | sourceFormat:(AudioStreamBasicDescription)sourceFormat 209 | destinationFileType:(EZRecorderFileType)destinationFileType 210 | { 211 | return [[EZRecorder alloc] initWithDestinationURL:url 212 | sourceFormat:sourceFormat 213 | destinationFileType:destinationFileType]; 214 | } 215 | 216 | //------------------------------------------------------------------------------ 217 | #pragma mark - Class Methods 218 | //------------------------------------------------------------------------------ 219 | 220 | + (AudioStreamBasicDescription)formatForFileType:(EZRecorderFileType)fileType 221 | withSourceFormat:(AudioStreamBasicDescription)sourceFormat 222 | { 223 | AudioStreamBasicDescription asbd; 224 | switch (fileType) 225 | { 226 | case EZRecorderFileTypeAIFF: 227 | asbd = [EZAudioUtilities AIFFFormatWithNumberOfChannels:sourceFormat.mChannelsPerFrame 228 | sampleRate:sourceFormat.mSampleRate]; 229 | break; 230 | case EZRecorderFileTypeM4A: 231 | asbd = [EZAudioUtilities M4AFormatWithNumberOfChannels:sourceFormat.mChannelsPerFrame 232 | sampleRate:sourceFormat.mSampleRate]; 233 | break; 234 | 235 | case EZRecorderFileTypeWAV: 236 | asbd = [EZAudioUtilities stereoFloatInterleavedFormatWithSampleRate:sourceFormat.mSampleRate]; 237 | break; 238 | 239 | default: 240 | asbd = [EZAudioUtilities stereoCanonicalNonInterleavedFormatWithSampleRate:sourceFormat.mSampleRate]; 241 | break; 242 | } 243 | return asbd; 244 | } 245 | 246 | //------------------------------------------------------------------------------ 247 | 248 | + (AudioFileTypeID)fileTypeIdForFileType:(EZRecorderFileType)fileType 249 | withSourceFormat:(AudioStreamBasicDescription)sourceFormat 250 | { 251 | AudioFileTypeID audioFileTypeID; 252 | switch (fileType) 253 | { 254 | case EZRecorderFileTypeAIFF: 255 | audioFileTypeID = kAudioFileAIFFType; 256 | break; 257 | 258 | case EZRecorderFileTypeM4A: 259 | audioFileTypeID = kAudioFileM4AType; 260 | break; 261 | 262 | case EZRecorderFileTypeWAV: 263 | audioFileTypeID = kAudioFileWAVEType; 264 | break; 265 | 266 | default: 267 | audioFileTypeID = kAudioFileWAVEType; 268 | break; 269 | } 270 | return audioFileTypeID; 271 | } 272 | 273 | //------------------------------------------------------------------------------ 274 | 275 | - (void)setup 276 | { 277 | // Finish filling out the destination format description 278 | UInt32 propSize = sizeof(self.info->fileFormat); 279 | [EZAudioUtilities checkResult:AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 280 | 0, 281 | NULL, 282 | &propSize, 283 | &self.info->fileFormat) 284 | operation:"Failed to fill out rest of destination format"]; 285 | 286 | // 287 | // Create the audio file 288 | // 289 | [EZAudioUtilities checkResult:ExtAudioFileCreateWithURL(self.info->fileURL, 290 | self.info->audioFileTypeID, 291 | &self.info->fileFormat, 292 | NULL, 293 | kAudioFileFlags_EraseFile, 294 | &self.info->extAudioFileRef) 295 | operation:"Failed to create audio file"]; 296 | 297 | // 298 | // Set the client format 299 | // 300 | [self setClientFormat:self.info->clientFormat]; 301 | } 302 | 303 | //------------------------------------------------------------------------------ 304 | #pragma mark - Events 305 | //------------------------------------------------------------------------------ 306 | 307 | - (void)appendDataFromBufferList:(AudioBufferList *)bufferList 308 | withBufferSize:(UInt32)bufferSize 309 | { 310 | // 311 | // Make sure the audio file is not closed 312 | // 313 | NSAssert(!self.info->closed, @"Cannot append data when EZRecorder has been closed. You must create a new instance.;"); 314 | 315 | // 316 | // Perform the write 317 | // 318 | [EZAudioUtilities checkResult:ExtAudioFileWrite(self.info->extAudioFileRef, 319 | bufferSize, 320 | bufferList) 321 | operation:"Failed to write audio data to recorded audio file"]; 322 | 323 | // 324 | // Notify delegate 325 | // 326 | if ([self.delegate respondsToSelector:@selector(recorderUpdatedCurrentTime:)]) 327 | { 328 | [self.delegate recorderUpdatedCurrentTime:self]; 329 | } 330 | } 331 | 332 | //------------------------------------------------------------------------------ 333 | 334 | - (void)closeAudioFile 335 | { 336 | if (!self.info->closed) 337 | { 338 | // 339 | // Close, audio file can no longer be written to 340 | // 341 | [EZAudioUtilities checkResult:ExtAudioFileDispose(self.info->extAudioFileRef) 342 | operation:"Failed to close audio file"]; 343 | self.info->closed = YES; 344 | 345 | // 346 | // Notify delegate 347 | // 348 | if ([self.delegate respondsToSelector:@selector(recorderDidClose:)]) 349 | { 350 | [self.delegate recorderDidClose:self]; 351 | } 352 | } 353 | } 354 | 355 | //------------------------------------------------------------------------------ 356 | #pragma mark - Getters 357 | //------------------------------------------------------------------------------ 358 | 359 | - (AudioStreamBasicDescription)clientFormat 360 | { 361 | return self.info->clientFormat; 362 | } 363 | 364 | //----------------------------------------------------------------------------- 365 | 366 | - (NSTimeInterval)currentTime 367 | { 368 | NSTimeInterval currentTime = 0.0; 369 | NSTimeInterval duration = [self duration]; 370 | if (duration != 0.0) 371 | { 372 | currentTime = (NSTimeInterval)[EZAudioUtilities MAP:(float)[self frameIndex] 373 | leftMin:0.0f 374 | leftMax:(float)[self totalFrames] 375 | rightMin:0.0f 376 | rightMax:duration]; 377 | } 378 | return currentTime; 379 | } 380 | 381 | //------------------------------------------------------------------------------ 382 | 383 | - (NSTimeInterval)duration 384 | { 385 | NSTimeInterval frames = (NSTimeInterval)[self totalFrames]; 386 | return (NSTimeInterval) frames / self.info->fileFormat.mSampleRate; 387 | } 388 | 389 | //------------------------------------------------------------------------------ 390 | 391 | - (AudioStreamBasicDescription)fileFormat 392 | { 393 | return self.info->fileFormat; 394 | } 395 | 396 | //------------------------------------------------------------------------------ 397 | 398 | - (NSString *)formattedCurrentTime 399 | { 400 | return [EZAudioUtilities displayTimeStringFromSeconds:[self currentTime]]; 401 | } 402 | 403 | //------------------------------------------------------------------------------ 404 | 405 | - (NSString *)formattedDuration 406 | { 407 | return [EZAudioUtilities displayTimeStringFromSeconds:[self duration]]; 408 | } 409 | 410 | //------------------------------------------------------------------------------ 411 | 412 | - (SInt64)frameIndex 413 | { 414 | SInt64 frameIndex; 415 | [EZAudioUtilities checkResult:ExtAudioFileTell(self.info->extAudioFileRef, 416 | &frameIndex) 417 | operation:"Failed to get frame index"]; 418 | return frameIndex; 419 | } 420 | 421 | //------------------------------------------------------------------------------ 422 | 423 | - (SInt64)totalFrames 424 | { 425 | SInt64 totalFrames; 426 | UInt32 propSize = sizeof(SInt64); 427 | [EZAudioUtilities checkResult:ExtAudioFileGetProperty(self.info->extAudioFileRef, 428 | kExtAudioFileProperty_FileLengthFrames, 429 | &propSize, 430 | &totalFrames) 431 | operation:"Recorder failed to get total frames."]; 432 | return totalFrames; 433 | } 434 | 435 | //------------------------------------------------------------------------------ 436 | 437 | - (NSURL *)url 438 | { 439 | return (__bridge NSURL*)self.info->fileURL; 440 | } 441 | 442 | //------------------------------------------------------------------------------ 443 | #pragma mark - Setters 444 | //------------------------------------------------------------------------------ 445 | 446 | - (void)setClientFormat:(AudioStreamBasicDescription)clientFormat 447 | { 448 | [EZAudioUtilities checkResult:ExtAudioFileSetProperty(self.info->extAudioFileRef, 449 | kExtAudioFileProperty_ClientDataFormat, 450 | sizeof(clientFormat), 451 | &clientFormat) 452 | operation:"Failed to set client format on recorded audio file"]; 453 | self.info->clientFormat = clientFormat; 454 | } 455 | 456 | @end --------------------------------------------------------------------------------