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 |
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
--------------------------------------------------------------------------------