source: orbit/iOS/Orbit/Orbit/AudioGenerator.m @ c9b9edd

Servo
Last change on this file since c9b9edd was c9b9edd, checked in by Steve Castellotti <sc@…>, 6 years ago

iOS:

  • Signal generation update by Hao Zhang
  • Property mode set to 100644
File size: 17.9 KB
Line 
1//
2//  AudioGenerator.m
3//  orbit
4//
5//  Created by Jonathon Horsman on 19/07/2013.
6//  Copyright (c) 2013 Puzzlebox Productions, LLC. All rights reserved.
7//
8
9#import "AudioGenerator.h"
10
11
12
13#define SAMPLE_RATE_F 44100.f
14#define SIGNAL_WAVE_FREQUENCY 100.f
15
16
17/**
18 * Half periods in the audio code, in seconds.
19 */
20#define longHIGH 0.000829649
21#define longLOW 0.000797027
22#define shortHIGH 0.000412649
23#define shortLOW 0.000378351
24
25#define IDLE_TIME_MS 20
26
27
28volatile BOOL g_refreshCtrCode = NO;
29
30
31@interface AudioGenerator()
32{
33   
34    AudioUnit   m_audioUnit;
35    Float32     m_sampleRate;
36    BOOL        m_isPlaying;
37    double      m_signalPhaseCur;
38
39   
40    int         waveBit_01_len[2];
41    Float32     *waveBit_01[2];
42   
43    int         ctrWaveMaxLen;
44    int         ctrWaveDataLen;
45    Float32     *ctrWave;
46   
47    int         ctrWaveHeadLen;
48    Float32     *ctrWaveHead;
49   
50    int         ctrWaveReadIdx;
51   
52    int         idleWaveLen;
53    int         idleWaveReadIdx;
54}
55
56@end
57
58
59@implementation AudioGenerator {
60   
61    float sampleTime;
62    int longHighLength, shortHighLength, longZeroLength, longLowLength, mediumLowLength, shortLowLength, waveBitLength;
63    float *waveLongHigh, *waveShortHigh, *waveLongZero, *waveLongLow, *waveMediumLow, *waveShortLow, *waveBit;
64   
65   
66   
67    int MAX_BUFFER_SIZE;
68
69}
70
71@synthesize yaw, pitch, throttle;
72
73- (id) init
74{
75    self = [super init];
76    if (self) {
77        sampleTime = 1.0f / SAMPLE_RATE_F;
78       
79        [self prepareStaticArrays];
80    }
81    return self;
82}
83
84#pragma mark - ---- interface ----
85
86- (void) playWithThrottle: (int)t yaw: (int)y pitch: (int)p
87{
88    self.yaw = y;
89    self.pitch = p;
90    self.throttle = t;
91   
92    [self audioUnitStart];
93   
94}
95
96
97- (void) stop
98{
99    [self audioUnitStop];
100}
101
102
103#pragma mark - ---- Audio Unit func ----
104
105- (void) audioUnitStart
106{
107
108    if (m_audioUnit == nil) {
109        [self prepareAudioUnit];
110    }
111   
112    if (!m_isPlaying) {
113       
114        [self updateCtrCode:NO];
115       
116        OSStatus status = AudioUnitInitialize(m_audioUnit);
117       
118        if (status != noErr) {
119            NSLog(@"AudioUnitInitialize - error");
120        }
121
122       
123        status = AudioOutputUnitStart(m_audioUnit);
124        if (status != noErr) {
125            NSLog(@"AudioOutputUnitStart - error");
126        }
127       
128        m_isPlaying = YES;
129    }
130    else {
131        [self updateCtrCode:YES];
132    }
133
134   
135}
136
137
138- (void) audioUnitStop
139{
140   
141    if (m_isPlaying) {
142        OSStatus status = AudioOutputUnitStop(m_audioUnit);
143        if (status != noErr) {
144            NSLog(@"AudioOutputUnitStop - error");
145        }
146       
147        m_isPlaying = NO;
148    }
149   
150    if (m_audioUnit) {
151        OSStatus status = AudioUnitUninitialize(m_audioUnit);
152        if (status != noErr) {
153            NSLog(@"AudioUnitUninitialize - error");
154        }
155       
156        status = AudioComponentInstanceDispose(m_audioUnit);
157        if (status != noErr) {
158            NSLog(@"AudioComponentInstanceDispose - error");
159        }
160       
161        m_audioUnit = nil;
162    }
163   
164   
165}
166
167
168
169- (void) prepareAudioUnit
170{
171    m_sampleRate = SAMPLE_RATE_F;
172    m_isPlaying = NO;
173    m_signalPhaseCur = 0.0;
174   
175    OSStatus status = noErr;
176   
177    AudioComponentDescription au_cd;
178    au_cd.componentType = kAudioUnitType_Output;
179    au_cd.componentSubType = kAudioUnitSubType_RemoteIO;
180    au_cd.componentManufacturer = kAudioUnitManufacturer_Apple;
181    au_cd.componentFlags = 0;
182    au_cd.componentFlagsMask = 0;
183   
184    AudioComponent component = AudioComponentFindNext(NULL, &au_cd);
185    if (component == NULL) {
186        NSLog(@"AudioComponentFindNext - error");
187    }
188   
189   
190    status = AudioComponentInstanceNew(component, &m_audioUnit);
191    if (status != noErr) {
192        NSLog(@"AudioComponentInstanceNew - error");
193    }
194   
195   
196    AURenderCallbackStruct callbackStruct;
197    callbackStruct.inputProc = renderAudioUnitCallback;
198    callbackStruct.inputProcRefCon = (__bridge void *)(self);
199   
200    status = AudioUnitSetProperty(m_audioUnit,
201                         kAudioUnitProperty_SetRenderCallback,
202                         kAudioUnitScope_Input,
203                         0,
204                         &callbackStruct,
205                         sizeof(AURenderCallbackStruct));
206   
207    if (status != noErr) {
208        NSLog(@"kAudioUnitProperty_SetRenderCallback - error");
209    }
210   
211   
212        AudioStreamBasicDescription streamFormat;
213        streamFormat.mSampleRate         = m_sampleRate;
214        streamFormat.mFormatID           = kAudioFormatLinearPCM;
215        streamFormat.mFormatFlags        = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
216        streamFormat.mChannelsPerFrame   = 2;
217        streamFormat.mBytesPerPacket     = sizeof(Float32);
218        streamFormat.mBytesPerFrame      = sizeof(Float32);
219        streamFormat.mFramesPerPacket    = 1;
220        streamFormat.mBitsPerChannel     = 8 * sizeof(Float32);
221   
222        status = AudioUnitSetProperty(m_audioUnit,
223                                                 kAudioUnitProperty_StreamFormat,
224                                                 kAudioUnitScope_Input,
225                                                 0,
226                                                 &streamFormat,
227                                                 sizeof(AudioStreamBasicDescription));
228   
229    if (status != noErr) {
230        NSLog(@"kAudioUnitProperty_StreamFormat - error");
231    }
232   
233}
234
235
236OSStatus renderAudioUnitCallback(void*                       inRefCon,
237                                   AudioUnitRenderActionFlags* ioActionFlags,
238                                   const AudioTimeStamp*       inTimeStamp,
239                                   UInt32                      inBusNumber,
240                                   UInt32                      inNumberFrames,
241                                   AudioBufferList*            ioData)
242{
243    AudioGenerator *generator = (__bridge AudioGenerator*)inRefCon;
244   
245    Float32 *ctr_buffer = ioData->mBuffers[0].mData;
246    [generator writeCtrWaveToBuffer:ctr_buffer maxSize:inNumberFrames];
247   
248    Float32 *signal_buffer = ioData->mBuffers[1].mData;
249    [generator writeSignalWaveToBuffer:signal_buffer maxSize:inNumberFrames];
250   
251    return noErr;
252}
253
254
255- (void)writeCtrWaveToBuffer:(Float32*)buffer maxSize:(UInt32)frames
256{
257   
258    for (int i=0; i < frames; ++i) {
259       
260        if (g_refreshCtrCode) {
261            ctrWaveReadIdx = 0;
262            idleWaveReadIdx = 0;
263            g_refreshCtrCode = NO;
264            break;
265        }
266       
267        if (ctrWaveReadIdx <= ctrWaveDataLen && idleWaveReadIdx == 0) {
268            *buffer++ = ctrWave[ctrWaveReadIdx++];
269        }
270        else if (ctrWaveReadIdx > ctrWaveDataLen && idleWaveReadIdx <= idleWaveLen)
271        {
272            *buffer++ = 0.0f;
273            idleWaveReadIdx++;
274        }
275        else if (ctrWaveReadIdx > ctrWaveDataLen && idleWaveReadIdx > idleWaveLen)
276        {
277            ctrWaveReadIdx = 0;
278            idleWaveReadIdx = 0;
279            *buffer++ = ctrWave[ctrWaveReadIdx++];
280        }
281       
282    }
283}
284
285- (void)writeSignalWaveToBuffer:(Float32*)buffer maxSize:(UInt32)frames
286{
287    double phase_step = SIGNAL_WAVE_FREQUENCY * 2.0 * M_PI / SAMPLE_RATE_F;
288       
289        for (int i = 0; i < frames; ++i){
290                float wave = sin(m_signalPhaseCur);
291                *buffer++ = wave;
292        m_signalPhaseCur += phase_step;
293       
294        if (m_signalPhaseCur > 2.0 * M_PI) {
295            m_signalPhaseCur -= 2.0 * M_PI;
296        }
297    }
298}
299
300
301
302#pragma mark - ---- Audio Generator func ----
303
304
305
306- (void) prepareStaticArrays
307{
308   
309    waveLongHigh = [self generateHalfSine:true halfPeriod:longHIGH];
310    longHighLength = [self arraySizeWithHalfPeriod:longHIGH];
311    waveLongLow = [self generateHalfSine:false halfPeriod:longLOW];
312    longLowLength = [self arraySizeWithHalfPeriod:longLOW];
313    waveShortHigh = [self generateHalfSine:true halfPeriod:shortHIGH];
314    shortHighLength = [self arraySizeWithHalfPeriod:shortHIGH];
315    waveShortLow = [self generateHalfSine:false halfPeriod:shortLOW];
316    shortLowLength = [self arraySizeWithHalfPeriod:shortLOW];
317   
318    float mediumLow = 0.0005 - sampleTime;
319    waveMediumLow = [self generateHalfSine:false halfPeriod:mediumLow];
320    mediumLowLength = [self arraySizeWithHalfPeriod:mediumLow];
321   
322   
323    longZeroLength = (int)floor((0.002 + 1 / SAMPLE_RATE_F) * SAMPLE_RATE_F);
324    waveLongZero = malloc(longZeroLength);
325   
326    [self generateWaveBit];
327   
328    [self generateCtrWaveHead];
329    [self generateWaveBit_01];
330   
331    [self setupCtrWave];
332}
333
334- (void)setupCtrWave
335{
336    ctrWaveMaxLen = ctrWaveHeadLen +
337                27 * (waveBit_01_len[0] > waveBit_01_len[1] ? waveBit_01_len[0] : waveBit_01_len[1]) +
338                longHighLength;
339   
340    idleWaveLen = (int)floor(IDLE_TIME_MS * 0.001f * SAMPLE_RATE_F);
341   
342    ctrWave = malloc(sizeof(Float32) * ctrWaveMaxLen);
343   
344    memcpy(ctrWave, ctrWaveHead, sizeof(Float32) * ctrWaveHeadLen);
345   
346    ctrWaveReadIdx = 0;
347    idleWaveReadIdx = 0;
348}
349
350- (void)generateCtrWaveHead
351{
352    int ctr_longH_len = [self arraySizeWithHalfPeriod:longHIGH - sampleTime * 2];
353    int ctr_shortL_len = [self arraySizeWithHalfPeriod:shortLOW + sampleTime * 2];
354   
355    ctrWaveHeadLen = longLowLength + 2 * ctr_longH_len + 2 * ctr_shortL_len;
356    ctrWaveHead = malloc(sizeof(Float32) * ctrWaveHeadLen);
357   
358    int idx = 0;
359   
360    for (int i=0; i < longLowLength; ++i) {
361        ctrWaveHead[idx++] = waveLongLow[i];
362    }
363   
364    double increment = M_PI/((longHIGH - sampleTime * 2) * SAMPLE_RATE_F);
365    double angle = 0;
366   
367    for (int i=0; i < ctr_longH_len; ++i) {
368        Float32 value = sinf(angle);
369        ctrWaveHead[idx + ctr_longH_len + ctr_shortL_len] = value;
370        ctrWaveHead[idx++] = value;
371        angle += increment;
372    }
373   
374    memset(ctrWaveHead + idx, 0, sizeof(Float32) * ctr_shortL_len);
375    memset(ctrWaveHead + (idx + ctr_longH_len + ctr_shortL_len), 0, sizeof(Float32) * ctr_shortL_len);
376
377}
378
379- (void) generateWaveBit_01
380{
381    waveBit_01_len[0] = shortHighLength + shortLowLength;
382    waveBit_01_len[1] = longHighLength + longLowLength;
383   
384    waveBit_01[0] = malloc(sizeof(Float32) * waveBit_01_len[0]);
385    waveBit_01[1] = malloc(sizeof(Float32) * waveBit_01_len[1]);
386   
387    int idx = 0;
388    for (int i=0; i < shortHighLength; ++i) {
389        waveBit_01[0][idx++] = waveShortHigh[i];
390    }
391    for (int i=0; i < shortLowLength; ++i) {
392        waveBit_01[0][idx++] = waveShortLow[i];
393    }
394   
395    idx = 0;
396    for (int i=0; i < longHighLength; ++i) {
397        waveBit_01[1][idx++] = waveLongHigh[i];
398    }
399    for (int i=0; i < longLowLength; ++i) {
400        waveBit_01[1][idx++] = waveLongLow[i];
401    }
402   
403}
404
405- (void) generateWaveBit
406{
407    waveBitLength = longHighLength + longLowLength + shortHighLength + shortLowLength;
408    waveBit = malloc(sizeof(waveBit) * waveBitLength);
409    int c = 0;
410    for (int i = 0; i < longHighLength; i++) {
411        waveBit[c++] = waveLongHigh[i];
412    }
413    for (int i = 0; i < longLowLength; i++) {
414        waveBit[c++] = waveLongLow[i];
415    }
416    for (int i = 0; i < shortHighLength; i++) {
417        waveBit[c++] = waveShortHigh[i];
418    }
419    for (int i = 0; i < shortLowLength; i++) {
420        waveBit[c++] = waveShortLow[i];
421    }
422}
423
424
425
426// calculate the checksum for the generated code used to generate the WAV array
427- (int) codeChecksum:(int)code
428{
429    int checksum = 0;
430    for (int i = 0; i < 7; i++) {
431        checksum += (code >> 4*i) & 15;
432    }
433    return 16 - (checksum & 15);
434}
435
436// Generate the code used to create the WAV file based on the given throttle, yaw and pitch.
437// Copied from AudioService.java in the Android app (command2code method)
438// throttle: 0~127, nothing will happen if this value is below 30.
439// yaw: 0~127, normally 78 will keep orbit from rotating.
440// pitch: 0~63, normally 31 will stop the top propeller.
441// channel: 1=Channel A, 0=Channel B 2= Channel C, depend on which channel you want to pair to the orbit. You can fly at most 3 orbit in a same room.
442- (int) generateCode
443{
444    int channel = 1;
445    int code = throttle << 21;
446    code += 1 << 20;
447    code += yaw << 12;
448    code += pitch << 4;
449    code += ((channel >> 1) & 1) << 19;
450    code += (channel & 1) << 11;
451    return code + [self codeChecksum:code];
452}
453
454- (void)updateCtrCode:(BOOL)refresh
455{
456    int ctr_code = [self generateCode];
457   
458    //ctr_code = 0;
459   
460    [self codeToWave:ctr_code];
461   
462    if (refresh) {
463        g_refreshCtrCode = YES;
464    }
465   
466}
467
468- (void)codeToWave:(int)code
469{
470    Float32 *pos = ctrWave + ctrWaveHeadLen;
471   
472    for (int i=27; i >= 0 ; --i) {
473        int bit = (code >> i) & 1;
474        memcpy(pos, waveBit_01[bit], sizeof(Float32) * waveBit_01_len[bit]);
475        pos += waveBit_01_len[bit];
476    }
477   
478    memcpy(pos, waveLongHigh, sizeof(Float32) * longHighLength);
479    pos += longHighLength;
480   
481    ctrWaveDataLen = pos - ctrWave;
482    int lost_len = ctrWaveMaxLen - ctrWaveDataLen;
483   
484    memset(pos, 0, sizeof(Float32) * lost_len);
485}
486
487
488
489
490- (int) writeBytesToBuffer:(Float32 *) buffer maxSize:(UInt32) frames
491{
492    MAX_BUFFER_SIZE = sizeof(buffer) * frames;
493    int position = 0;
494    [self writeWaveToBuffer:buffer        at:&position];
495    [self writeInitialWaveToBuffer:buffer at:&position];
496   
497    return position;
498}
499
500- (void) writeInitialWaveToBuffer:(Float32 *) buffer at: (int *) position
501{
502    [self writeWave123To: buffer at: position];
503    [self writeWave123To: buffer at: position];
504    [self writeWave456To: buffer at: position];
505    [self writeWave456To: buffer at: position];
506    [self writeWave456To: buffer at: position];
507}
508
509- (void) writeWave123To:(Float32 *) buffer at:(int *) position
510{
511    [self writeOriginalTo:buffer at: position];
512    [self writeArray:waveMediumLow to:buffer length:mediumLowLength at:position];
513    for (int i = 0; i < 4; i++) {
514        [self writeShortHShortLTo:buffer at:position];
515    }
516    [self writeArray:waveShortHigh to:buffer length:shortHighLength at:position];
517    [self writeMediumLShortHTo:buffer at:position];
518    [self writePauseInSamplesTo:buffer at:position];
519}
520
521- (void) writeWave456To:(Float32 *) buffer at:(int *) position
522{
523    [self writeOriginalTo:buffer at:position];
524    [self writeArray:waveShortLow to:buffer length:shortLowLength at:position];
525    for (int i = 0; i < 4; i++) {
526        [self writeShortHShortLTo:buffer at:position];
527    }
528    [self writeArray:waveShortHigh to:buffer length:shortHighLength at:position];
529    [self writeMediumLShortHTo:buffer at:position];
530    [self writePauseInSamplesTo:buffer at:position];
531}
532
533
534- (void) writeOriginalTo:(Float32 *) buffer at:(int *) position
535{
536    [self writeLongHLongZeroTo: buffer at: position];
537    [self writeLongHLongZeroTo: buffer at: position];
538    [self writeArray:waveLongHigh to:buffer length:longHighLength at:position];
539    [self writeMediumLShortHTo:buffer at:position];
540}
541
542- (void) writeLongHLongZeroTo:(Float32 *) buffer at:(int *) position
543{
544    [self writeArray:waveLongHigh to:buffer length:longHighLength at:position];
545    [self writeArray:waveLongZero to:buffer length:longZeroLength at:position];
546}
547
548- (void) writeMediumLShortHTo:(Float32 *) buffer at:(int *) position
549{
550    [self writeArray:waveMediumLow to:buffer length:mediumLowLength at:position];
551    [self writeArray:waveShortHigh to:buffer length:shortHighLength at:position];
552}
553
554- (void) writeShortHShortLTo:(Float32 *) buffer at:(int *) position
555{
556    [self writeArray:waveShortHigh to:buffer length:shortHighLength at:position];
557    [self writeArray:waveShortLow to:buffer length:shortLowLength at:position];
558}
559
560- (void) writePauseInSamplesTo:(Float32 *) buffer at:(int *) position
561{
562    int length = (int)floor(0.010 * SAMPLE_RATE_F); // length of pause to insert
563    float arr[length];
564    [self writeArray:arr to:buffer length:length at:position];
565}
566
567- (void) writeArray:(float *) array to:(Float32 *) buffer length: (int)length at:(int *) position
568{
569    for (int i = 0; i < length; i++) {
570        if (*position < MAX_BUFFER_SIZE) buffer[(*position)++] = array[i];
571    }
572}
573
574- (void) writeWaveToBuffer:(Float32 *) buffer at: (int *) position
575{
576    [self halfSineGen: false halfPeriod: longLOW                   toBuffer:buffer at:position];
577    [self halfSineGen: true  halfPeriod: longHIGH - sampleTime * 2 toBuffer:buffer at:position];
578    [self halfSineGen: false halfPeriod: shortLOW + sampleTime * 2 toBuffer:buffer at:position];
579    [self halfSineGen: true  halfPeriod: longHIGH - sampleTime * 2 toBuffer:buffer at:position]; // duplicate?
580    [self halfSineGen: false halfPeriod: shortLOW + sampleTime * 2 toBuffer:buffer at:position]; // duplicate?
581   
582    int code = [self generateCode];
583    for (int i=0; i < 27; i++) {
584        buffer[(*position)++] = waveBit[((code >> (27 - i)) & 1)];
585    }
586    for (int i = 0; i < longHighLength; i++) {
587        buffer[(*position)++] = waveLongHigh[i];
588    }
589   
590}
591
592/**
593 * Generate half sine signal.
594 * Copied from AudioService.java in the AndroidApp
595 * @param upper, means it's the upper half or lower half or sine wave.
596 * @param halfPeriod: half of the period of sine wave, in seconds
597 * @param toBuffer the buffer (float array) to write to
598 * @param position the position in the array to start writing to
599 */
600- (void) halfSineGen:(BOOL)upper halfPeriod: (double)halfPeriod toBuffer:(Float32 *) buffer at:(int *) position
601{
602   
603   // TODO - testing
604   //halfPeriod =true;
605   
606    int sampleCount = [self arraySizeWithHalfPeriod:halfPeriod];
607    double increment = M_PI/(halfPeriod * SAMPLE_RATE_F);
608    double angle = upper ? 0 : M_PI;
609       
610    for (int i = 0; i < sampleCount; i++) {
611        buffer[(*position)++] = sinf(angle);
612        angle += increment;
613    }
614}
615
616- (int) arraySizeWithHalfPeriod: (double)halfPeriod
617{
618    return (int)floor(halfPeriod * SAMPLE_RATE_F);
619}
620
621/**
622 * Generate half sine signal.
623 * Copied from AudioService.java in the AndroidApp
624 * @param upper, means it's the upper half or lower half of sine wave.
625 * @param halfPeriod: half of the period of sine wave, in seconds
626 * @return the length of the array
627 */
628- (float *) generateHalfSine:(BOOL)upper halfPeriod: (double)halfPeriod
629{
630    int sampleCount = [self arraySizeWithHalfPeriod:halfPeriod];
631    Float32 *array = malloc(sizeof(Float32) * sampleCount);
632    double increment = M_PI/(halfPeriod * SAMPLE_RATE_F);
633    double angle = 0;
634   
635    for (int i = 0; i < sampleCount; i++) {
636        if (upper) {
637            array[i] = sinf(angle);
638            angle += increment;
639        }
640        else {
641            array[i] = 0.0f;
642        }
643       
644    }
645    return array;
646}
647
648@end
Note: See TracBrowser for help on using the repository browser.