-
Notifications
You must be signed in to change notification settings - Fork 2
/
IosAudioController.m
350 lines (279 loc) · 10.3 KB
/
IosAudioController.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
//
// IosAudioController.m
//
#import "IosAudioController.h"
#import <AudioToolbox/AudioToolbox.h>
#import "BeepingCoreLib_api.h"
#define kOutputBus 0
#define kInputBus 1
IosAudioController* iosAudio;
void checkStatus(int status){
if (status) {
printf("Status not 0! %d\n", status);
exit(1);
}
}
static OSStatus recordingCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData) {
if (iosAudio->mBeepingObject->mDecoding == 0)
return noErr;
// Because of the way our audio format (setup below) is chosen:
// we only need 1 buffer, since it is mono
// Samples are 16 bits = 2 bytes.
// 1 frame includes only 1 sample
AudioBuffer buffer;
buffer.mNumberChannels = 1;
buffer.mDataByteSize = inNumberFrames * 2;
buffer.mData = malloc( inNumberFrames * 2 );
// Put buffer in a AudioBufferList
AudioBufferList bufferList;
bufferList.mNumberBuffers = 1;
bufferList.mBuffers[0] = buffer;
// Then:
// Obtain recorded samples
OSStatus status;
status = AudioUnitRender([iosAudio audioUnit],
ioActionFlags,
inTimeStamp,
inBusNumber,
inNumberFrames,
&bufferList);
checkStatus(status);
// Now, we have the samples we just read sitting in buffers in bufferList
// Process the new data
[iosAudio processAudio:&bufferList];
//Now Decode Audio *******************
//convert from AudioBuffer format to *float buffer
iosAudio->floatBuffer = (float *)malloc(inNumberFrames * sizeof(float));
//UInt16 *frameBuffer = bufferList.mBuffers[0].mData;
SInt16 *frameBuffer = bufferList.mBuffers[0].mData;
for(int j=0;j<inNumberFrames;j++)
{
iosAudio->floatBuffer[j] = frameBuffer[j]/32768.0;
}
int ret = BEEPING_DecodeAudioBuffer(iosAudio->floatBuffer, inNumberFrames, (void*)iosAudio->mBeepingObject->mBeepingCore);
//public static final int BC_TOKEN_START = 0;
//public static final int BC_TOKEN_END_OK = 1;
//public static final int BC_TOKEN_END_BAD = 2;
//public static final int BC_END_PLAY = 3;
if (ret == -2)
{
NSLog(@"BEGIN TOKEN FOUND!");
[iosAudio->mObject performSelector:iosAudio->mSelector withObject:[NSNumber numberWithInt:0]];
}
else if (ret >= 0)
{
NSLog(@"Token found! %@",@(ret).stringValue);
}
else if (ret == -3)
{
int sizeStringDecoded = BEEPING_GetDecodedData(iosAudio->mStringDecoded, (void*)iosAudio->mBeepingObject->mBeepingCore);
NSString *tmpString = [NSString stringWithUTF8String:iosAudio->mStringDecoded];
iosAudio->mBeepingObject->mDecodedString = [NSString stringWithUTF8String:iosAudio->mStringDecoded];
if (sizeStringDecoded > 0)
{
iosAudio->mBeepingObject->mDecodedOK = 1;
NSLog(@"END DECODING OK! %@ ", tmpString);
[iosAudio->mObject performSelector:iosAudio->mSelector withObject:[NSNumber numberWithInt:1]];
}
else
{
iosAudio->mBeepingObject->mDecodedOK = -1;
NSLog(@"END DECODING BAD! %@ ", tmpString);
[iosAudio->mObject performSelector:iosAudio->mSelector withObject:[NSNumber numberWithInt:2]];
}
}
else
{
//no data found in this buffer
}
// release the malloc'ed data in the buffer we created earlier
free(bufferList.mBuffers[0].mData);
free(iosAudio->floatBuffer);
return noErr;
}
static OSStatus playbackCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData) {
// Notes: ioData contains buffers (may be more than one!)
// Fill them up as much as you can. Remember to set the size value in each buffer to match how
// much data is in the buffer.
for (int i=0; i < ioData->mNumberBuffers; i++)
{ // in practice we will only ever have 1 buffer, since audio format is mono
AudioBuffer buffer = ioData->mBuffers[i];
// NSLog(@" Buffer %d has %d channels and wants %d bytes of data.", i, buffer.mNumberChannels, buffer.mDataByteSize);
// copy temporary buffer data to output buffer
UInt32 size = min(buffer.mDataByteSize, [iosAudio tempBuffer].mDataByteSize); // dont copy more data than we have, or than fits
memcpy(buffer.mData, [iosAudio tempBuffer].mData, size);
buffer.mDataByteSize = size; // indicate how much data we wrote in the buffer
// uncomment to hear random noise
/*UInt16 *frameBuffer = buffer.mData;
for (int j = 0; j < inNumberFrames; j++)
frameBuffer[j] = rand();*/
// Play encoded buffer
if (iosAudio->mBeepingObject->mEncoding > 0)
{
int sizeSamplesRead;
float audioBuffer[2048];
sizeSamplesRead = BEEPING_GetEncodedAudioBuffer(audioBuffer, (void*)iosAudio->mBeepingObject->mBeepingCore);
if (sizeSamplesRead == 0)
iosAudio->mBeepingObject->mEncoding = 0;
SInt16 *frameBuffer = buffer.mData;
for(int j=0;j<sizeSamplesRead;j++)
{
frameBuffer[j] = audioBuffer[j]*32768.0;
}
}
else
{
SInt16 *frameBuffer = buffer.mData;
for (int j = 0; j < inNumberFrames; j++)
frameBuffer[j] = 0;
}
}
return noErr;
}
@implementation IosAudioController
@synthesize audioUnit, tempBuffer;
- (id) init {
self = [super init];
OSStatus status;
// Describe audio component
AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_RemoteIO;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
// Get component
AudioComponent inputComponent = AudioComponentFindNext(NULL, &desc);
// Get audio units
status = AudioComponentInstanceNew(inputComponent, &audioUnit);
checkStatus(status);
// Enable IO for recording
UInt32 flag = 1;
status = AudioUnitSetProperty(audioUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
kInputBus,
&flag,
sizeof(flag));
checkStatus(status);
// Enable IO for playback
status = AudioUnitSetProperty(audioUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output,
kOutputBus,
&flag,
sizeof(flag));
checkStatus(status);
// Describe format
AudioStreamBasicDescription audioFormat;
audioFormat.mSampleRate = 44100.0;
audioFormat.mFormatID = kAudioFormatLinearPCM;
audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
audioFormat.mFramesPerPacket = 1;
audioFormat.mChannelsPerFrame = 1;
audioFormat.mBitsPerChannel = 16;
audioFormat.mBytesPerPacket = 2;
audioFormat.mBytesPerFrame = 2;
// Apply format
status = AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
kInputBus,
&audioFormat,
sizeof(audioFormat));
checkStatus(status);
status = AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
kOutputBus,
&audioFormat,
sizeof(audioFormat));
checkStatus(status);
// Set input callback
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = recordingCallback;
callbackStruct.inputProcRefCon = (__bridge void *)self;
status = AudioUnitSetProperty(audioUnit,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global,
kInputBus,
&callbackStruct,
sizeof(callbackStruct));
checkStatus(status);
// Set output callback
callbackStruct.inputProc = playbackCallback;
callbackStruct.inputProcRefCon = (__bridge void *)self;
status = AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Global,
kOutputBus,
&callbackStruct,
sizeof(callbackStruct));
checkStatus(status);
// Disable buffer allocation for the recorder (optional - do this if we want to pass in our own)
flag = 0;
status = AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_ShouldAllocateBuffer,
kAudioUnitScope_Output,
kInputBus,
&flag,
sizeof(flag));
// Allocate our own buffers (1 channel, 16 bits per sample, thus 16 bits per frame, thus 2 bytes per frame).
// Practice learns the buffers used contain 512 frames, if this changes it will be fixed in processAudio.
tempBuffer.mNumberChannels = 1;
int size = 512;
#if (TARGET_OS_SIMULATOR)
size = 256; //TODO check this value!! depends on play/record callback buffer size
#else
size = 512; //TODO check this value!! depends on play/record callback buffer size
#endif
tempBuffer.mDataByteSize = size * 2;
tempBuffer.mData = malloc( size * 2);
// Initialise
status = AudioUnitInitialize(audioUnit);
checkStatus(status);
return self;
}
- (void) start {
OSStatus status = AudioOutputUnitStart(audioUnit);
checkStatus(status);
}
- (void) stop {
OSStatus status = AudioOutputUnitStop(audioUnit);
checkStatus(status);
}
- (void) processAudio: (AudioBufferList*) bufferList{
AudioBuffer sourceBuffer = bufferList->mBuffers[0];
// fix tempBuffer size if it's the wrong size
if (tempBuffer.mDataByteSize != sourceBuffer.mDataByteSize) {
free(tempBuffer.mData);
tempBuffer.mDataByteSize = sourceBuffer.mDataByteSize;
tempBuffer.mData = malloc(sourceBuffer.mDataByteSize);
}
// copy incoming audio data to temporary buffer
memcpy(tempBuffer.mData, bufferList->mBuffers[0].mData, bufferList->mBuffers[0].mDataByteSize);
}
- (void) dealloc {
AudioUnitUninitialize(audioUnit);
free(tempBuffer.mData);
}
- (void) setBeepingObject: (BeepingCore*) beepingObject
{
mBeepingObject = beepingObject;
}
- (void) setListenCallback:(id)object withSelector:(SEL)selector
{
mObject = object;
mSelector = selector;
}
@end