Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>You will need:</p> <ul> <li>A good understanding of linear PCM audio format (See <a href="http://en.wikipedia.org/wiki/Linear_pulse-code_modulation" rel="nofollow" title="Linear PCM">Wikipedia's Linear PCM page</a>).</li> <li>A good understanding of audio sample-rates and some basic maths to convert your timings into sample-offsets.</li> <li>An awareness of how <a href="http://en.wikipedia.org/wiki/Two%27s_complement" rel="nofollow" title="Two's Complement on Wikipedia">two's-complement</a> binary numbers (signed/unsigned, 16-bit, 32-bit, etc.) are stored in computers, and how the <a href="http://en.wikipedia.org/wiki/Endianness" rel="nofollow" title="Endianness on Wikipedia">endian-ness</a> of a processor affects this.</li> <li>Patience, interest in learning, and a strong desire to get this working.</li> </ul> <p>Here's what to do:</p> <ol> <li><p>Enable file sharing in your app (<code>UIFileSharingEnabled=YES</code> in info.plist and write files to <code>/Documents</code> directory). </p></li> <li><p>Render the used sounds into memory buffers containing linear PCM audio data (if they are not already, i.e. if they are compressed). You can do this using the offline rendering functionality of Audio Queues (see <a href="http://developer.apple.com/library/mac/#documentation/MusicAudio/Reference/AudioQueueReference/Reference/reference.html" rel="nofollow" title="Apple audio queue documentation">Apple audio queue docs</a>). It will make things a <strong>lot</strong> easier if you render them all to the same PCM format and sample rate (For example 16-bit signed samples @44,100Hz, I'll use this format for all examples), and use the same format for your output. I recommend starting off with a Mono format then adding stereo once you get it working.</p></li> <li><p>Choose an uncompressed output format and mix your sounds into a single stream:</p> <p>3.1. Allocate a buffer large enough, or open a file stream to write to.</p> <p>3.2. Write out any headers (for example if using WAV format output instead of raw PCM) and write zeros (or the mid-point of your sample range if not using a signed sample format) for any initial silence before your first sound starts. For example if you want 0.1 seconds silence before your first sound, write 4410 (0.1 * 44100) zero-samples i.e. write 4410 shorts (16-bit) all with zero.</p> <p>3.3. Now keep track of all 'currently playing' sounds and mix them together. Start with an empty list of 'currently playing sounds and keep track of the 'current time' of the sample you are mixing, for each sample you write out increment the 'current time' by <code>1.0/sample_rate</code>. When it gets time for another sound to start, add it to the 'currently playing' list with a sample offset of 0. Now to do the mixing, you iterate through all of the 'currently playing' sounds and add together their current sample, then increment the sample offset for each of them. Write the summed value into the output buffer. For example if soundA starts at 0.1 seconds (after the silence) and soundB starts at 0.2 seconds, you will be doing the equivalent of <code>output[8820] = soundA[4410] + soundB[0];</code> for sample 8820 and then <code>output[8821] = soundA[4411] + soundB[1];</code> for sample 8821, etc. As a sound ends (you get to the end of its samples) simply remove it from the 'currently playing' list and keep going until the end of your audio data.</p> <p>3.4. The simple mixing (sum of samples) described above does have some problems. For example if two samples have values that add up to a number larger than 32767, this cannot be stored in a signed-16-bit number, this is called clipping. For now, just clamp the value to 32767, and get it working... later on come back and implement a simple limiter (see description at end).</p></li> <li><p>Now that you have a mixed version of your track in an uncompressed linear PCM format, that might be enough, so write it to <code>/Documents</code>. If you want to write it in a compressed format, you will need to get the source for an audio encoder and run your linear PCM output through that.</p></li> </ol> <p>Simple limiter:</p> <p>Let's choose to limit the top 10% of the sample range, so if the absolute value is greater than 29490 (<code>int limitBegin = (int)(32767 * 0.9f);</code>) we will scale down the value. The maximum possible peak would be <code>int maxSampleValue = 32767 * numPlayingSounds;</code> and we want to scale values above <code>limitBegin</code> to peak at 32767. So do the summation into <code>sampleValue</code> as per the very simple mixer described above, then:</p> <pre><code>if(sampleValue &gt; limitBegin) { float overLimit = (sampleValue - limitBegin) / (float)(maxSampleValue - limitBegin); sampleValue = limitBegin + (int)(overLimit * (32767 - limitBegin)); } </code></pre> <p>If you're paying attention, you will have noticed that when <code>numPlayingSounds</code> changes (for example when a new sound starts), the limiter becomes more (or less) harsh and this may result in abrupt volume changes (within the limited range) to accommodate the extra sound. You can use the maximum number of playing sounds instead, or devise some clever way to ramp up the limiter over a few milliseconds.</p> <p>Remember that this is operating on the absolute value of <code>sampleValue</code> (which may be negative in signed formats), so the code here is just to demonstrate the idea. You'll need to write it properly to handle limiting at both ends (peak and trough) of your sample range. Also, there are some tricks you can do to optimize all of the above during the mixing - you will probably spot these while you're writing the mixer, be careful and get it working first, then go back and refactor/optimize if needed.</p> <p>Also remember to consider the endian-ness of the platform you are using and the file-format you are writing to, as you may need to do some byte-swapping.</p>
 

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