Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>I'm also investigating this. I think the AudioQueue API is probably the way to go.</p> <p>Here's as far as I got, seems to work okay.</p> <p>File: BleepMachine.h</p> <pre><code>// // BleepMachine.h // WgHeroPrototype // // Created by Andy Buchanan on 05/01/2010. // Copyright 2010 Andy Buchanan. All rights reserved. // #include &lt;AudioToolbox/AudioToolbox.h&gt; // Class to implement sound playback using the AudioQueue API's // Currently just supports playing two sine wave tones, one per // stereo channel. The sound data is liitle-endian signed 16-bit @ 44.1KHz // class BleepMachine { static void staticQueueCallback( void* userData, AudioQueueRef outAQ, AudioQueueBufferRef outBuffer ) { BleepMachine* pThis = reinterpret_cast&lt;BleepMachine*&gt; ( userData ); pThis-&gt;queueCallback( outAQ, outBuffer ); } void queueCallback( AudioQueueRef outAQ, AudioQueueBufferRef outBuffer ); AudioStreamBasicDescription m_outFormat; AudioQueueRef m_outAQ; enum { kBufferSizeInFrames = 512, kNumBuffers = 4, kSampleRate = 44100, }; AudioQueueBufferRef m_buffers[kNumBuffers]; bool m_isInitialised; struct Wave { Wave(): volume(1.f), phase(0.f), frequency(0.f), fStep(0.f) {} float volume; float phase; float frequency; float fStep; }; enum { kLeftWave = 0, kRightWave = 1, kNumWaves, }; Wave m_waves[kNumWaves]; public: BleepMachine(); ~BleepMachine(); bool Initialise(); void Shutdown(); bool Start(); bool Stop(); bool SetWave( int id, float frequency, float volume ); }; // Notes by name. Integer value is number of semitones above A. enum Note { A = 0, Asharp, B, C, Csharp, D, Dsharp, E, F, Fsharp, G, Gsharp, Bflat = Asharp, Dflat = Csharp, Eflat = Dsharp, Gflat = Fsharp, Aflat = Gsharp, }; // Helper function calculates fundamental frequency for a given note float CalculateFrequencyFromNote( SInt32 semiTones, SInt32 octave=4 ); float CalculateFrequencyFromMIDINote( SInt32 midiNoteNumber ); </code></pre> <p>File:BleepMachine.mm</p> <pre><code> // // BleepMachine.mm // WgHeroPrototype // // Created by Andy Buchanan on 05/01/2010. // Copyright 2010 Andy Buchanan. All rights reserved. // #include "BleepMachine.h" void BleepMachine::queueCallback( AudioQueueRef outAQ, AudioQueueBufferRef outBuffer ) { // Render the wave // AudioQueueBufferRef is considered "opaque", but it's a reference to // an AudioQueueBuffer which is not. // All the samples manipulate this, so I'm not quite sure what they mean by opaque // saying.... SInt16* coreAudioBuffer = (SInt16*)outBuffer-&gt;mAudioData; // Specify how many bytes we're providing outBuffer-&gt;mAudioDataByteSize = kBufferSizeInFrames * m_outFormat.mBytesPerFrame; // Generate the sine waves to Signed 16-Bit Stero interleaved ( Little Endian ) float volumeL = m_waves[kLeftWave].volume; float volumeR = m_waves[kRightWave].volume; float phaseL = m_waves[kLeftWave].phase; float phaseR = m_waves[kRightWave].phase; float fStepL = m_waves[kLeftWave].fStep; float fStepR = m_waves[kRightWave].fStep; for( int s=0; s&lt;kBufferSizeInFrames*2; s+=2 ) { float sampleL = ( volumeL * sinf( phaseL ) ); float sampleR = ( volumeR * sinf( phaseR ) ); short sampleIL = (int)(sampleL * 32767.0); short sampleIR = (int)(sampleR * 32767.0); coreAudioBuffer[s] = sampleIL; coreAudioBuffer[s+1] = sampleIR; phaseL += fStepL; phaseR += fStepR; } m_waves[kLeftWave].phase = fmodf( phaseL, 2 * M_PI ); // Take modulus to preserve precision m_waves[kRightWave].phase = fmodf( phaseR, 2 * M_PI ); // Enqueue the buffer AudioQueueEnqueueBuffer( m_outAQ, outBuffer, 0, NULL ); } bool BleepMachine::SetWave( int id, float frequency, float volume ) { if ( ( id &lt; kLeftWave ) || ( id &gt;= kNumWaves ) ) return false; Wave&amp; wave = m_waves[ id ]; wave.volume = volume; wave.frequency = frequency; wave.fStep = 2 * M_PI * frequency / kSampleRate; return true; } bool BleepMachine::Initialise() { m_outFormat.mSampleRate = kSampleRate; m_outFormat.mFormatID = kAudioFormatLinearPCM; m_outFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; m_outFormat.mFramesPerPacket = 1; m_outFormat.mChannelsPerFrame = 2; m_outFormat.mBytesPerPacket = m_outFormat.mBytesPerFrame = sizeof(UInt16) * 2; m_outFormat.mBitsPerChannel = 16; m_outFormat.mReserved = 0; OSStatus result = AudioQueueNewOutput( &amp;m_outFormat, BleepMachine::staticQueueCallback, this, NULL, NULL, 0, &amp;m_outAQ ); if ( result &lt; 0 ) { printf( "ERROR: %d\n", (int)result ); return false; } // Allocate buffers for the audio UInt32 bufferSizeBytes = kBufferSizeInFrames * m_outFormat.mBytesPerFrame; for ( int buf=0; buf&lt;kNumBuffers; buf++ ) { OSStatus result = AudioQueueAllocateBuffer( m_outAQ, bufferSizeBytes, &amp;m_buffers[ buf ] ); if ( result ) { printf( "ERROR: %d\n", (int)result ); return false; } // Prime the buffers queueCallback( m_outAQ, m_buffers[ buf ] ); } m_isInitialised = true; return true; } void BleepMachine::Shutdown() { Stop(); if ( m_outAQ ) { // AudioQueueDispose also chucks any audio buffers it has AudioQueueDispose( m_outAQ, true ); } m_isInitialised = false; } BleepMachine::BleepMachine() : m_isInitialised(false), m_outAQ(0) { for ( int buf=0; buf&lt;kNumBuffers; buf++ ) { m_buffers[ buf ] = NULL; } } BleepMachine::~BleepMachine() { Shutdown(); } bool BleepMachine::Start() { OSStatus result = AudioQueueSetParameter( m_outAQ, kAudioQueueParam_Volume, 1.0 ); if ( result ) printf( "ERROR: %d\n", (int)result ); // Start the queue result = AudioQueueStart( m_outAQ, NULL ); if ( result ) printf( "ERROR: %d\n", (int)result ); return true; } bool BleepMachine::Stop() { OSStatus result = AudioQueueStop( m_outAQ, true ); if ( result ) printf( "ERROR: %d\n", (int)result ); return true; } // A (A4=440) // A# f(n)=2^(n/12) * r // B where n = number of semitones // C and r is the root frequency e.g. 440 // C# // D frq -&gt; MIDI note number // D# p = 69 + 12 x log2(f/440) // E // F // F# // G // G# // // MIDI Note ref: http://www.phys.unsw.edu.au/jw/notes.html // // MIDI Node numbers: // A3 57 // A#3 58 // B3 59 // C4 60 &lt;-- // C#4 61 // D4 62 // D#4 63 // E4 64 // F4 65 // F#4 66 // G4 67 // G#4 68 // A4 69 &lt;-- // A#4 70 // B4 71 // C5 72 float CalculateFrequencyFromNote( SInt32 semiTones, SInt32 octave ) { semiTones += ( 12 * (octave-4) ); float root = 440.f; float fn = powf( 2.f, (float)semiTones/12.f ) * root; return fn; } float CalculateFrequencyFromMIDINote( SInt32 midiNoteNumber ) { SInt32 semiTones = midiNoteNumber - 69; return CalculateFrequencyFromNote( semiTones, 4 ); } //for ( SInt32 midiNote=21; midiNote&lt;=108; ++midiNote ) //{ // printf( "MIDI Note %d: %f Hz \n",(int)midiNote,CalculateFrequencyFromMIDINote( midiNote ) ); //} </code></pre> <p><strong>Update: Basic usage info</strong></p> <ol> <li><p>Initialise. Somehere near the start, I'm using initFromNib: in my code</p> <pre><code>m_bleepMachine = new BleepMachine; m_bleepMachine-&gt;Initialise(); m_bleepMachine-&gt;Start(); </code></pre></li> <li><p>Now the sound playback is running, but generating silence.</p></li> <li><p>In your code, call this when you want to change the tone generation</p> <pre><code>m_bleepMachine-&gt;SetWave( ch, frq, vol ); </code></pre> <ul> <li>where ch is the channel ( 0 or 1 )</li> <li>where frq is the frequency to set in Hz</li> <li>where vol is the volume ( 0=-Inf db, 1=-0db )</li> </ul></li> <li><p>At program termination</p> <pre><code>delete m_bleepMachine; </code></pre></li> </ol>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload