Last active
September 1, 2020 10:26
-
-
Save theoknock/bc4bb4b871542a92a57ada836ba88958 to your computer and use it in GitHub Desktop.
Uses a series of chained block literals to compute a data representation of a sine wave and store it in an AVAudioPCMBuffer, which is scheduled for playback via an AVAudioPlayerNode. After playback, the process repeats, generating a continuous series of tones (until, for example, the AVAudioPlayerNode is paused).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
typedef void (^PlayedToneCompletionBlock)(void); | |
typedef void (^BufferRenderedCompletionBlock)(AVAudioPlayerNode * _Nonnull player_node, AVAudioPCMBuffer * _Nonnull buffer, PlayedToneCompletionBlock _Nonnull playedToneCompletionBlock); | |
static void(^createAudioBuffer)(AVAudioPlayerNode *, AVAudioSession *, AVAudioFormat *, BufferRenderedCompletionBlock) = ^(AVAudioPlayerNode * player_node, AVAudioSession * audioSession, AVAudioFormat * audioFormat, BufferRenderedCompletionBlock bufferRenderedCompletionBlock) | |
{ | |
// playerNode buffer scheduler goes here | |
[player_node scheduleBuffer:^AVAudioPCMBuffer * (void (^buffer_sample)(AVAudioFrameCount, double, double, double, float *, AVAudioChannelCount)) | |
{ | |
double duration = random_source_drand48(0.25, 1.75);; | |
AVAudioFrameCount frameCount = ([audioFormat sampleRate] * duration); | |
AVAudioPCMBuffer *pcmBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:audioFormat frameCapacity:frameCount]; | |
pcmBuffer.frameLength = frameCount; | |
AVAudioChannelCount channel_count = audioFormat.channelCount; | |
double root_freq = random_source_drand48(400.0, 2000.0) * duration; | |
double harmonic_freq = root_freq * (5.0/4.0); | |
NSLog(@"\tDuration:\t%fs\t\tRoot frequency:\t%f\t\tHarmonic frequency:\t%f", duration, root_freq, harmonic_freq); | |
double device_volume = pow(audioSession.outputVolume, 3.0); | |
buffer_sample(pcmBuffer.frameLength, | |
root_freq, | |
duration, | |
device_volume, | |
pcmBuffer.floatChannelData[0], | |
channel_count); | |
buffer_sample(pcmBuffer.frameLength, | |
harmonic_freq, | |
duration, | |
device_volume, | |
(channel_count == 2) ? pcmBuffer.floatChannelData[1] : nil, | |
channel_count); | |
return pcmBuffer; | |
}(^(AVAudioFrameCount sampleCount, double frequency, double duration, double outputVolume, float * samples, AVAudioChannelCount channel_count) | |
{ | |
// for (AVAudioChannelCount channel = 0; channel < channel_count; channel++) | |
// { | |
for (int index = 0; index < sampleCount; index++) | |
{ | |
double normalized_time = normalize(0.0, 1.0, index); | |
double sine_frequency = sample_frequency(normalized_time, frequency); | |
double sample = sine_frequency * sample_amplitude(normalized_time, outputVolume); | |
if (samples) samples[index] = sample; | |
// if (samples[channel]) samples[channel][index] = sample; | |
} | |
}) atTime:nil options:AVAudioPlayerNodeBufferInterruptsAtLoop completionCallbackType:AVAudioPlayerNodeCompletionDataPlayedBack completionHandler:^(AVAudioPlayerNodeCompletionCallbackType callbackType) { | |
if (callbackType == AVAudioPlayerNodeCompletionDataPlayedBack) | |
{ | |
// playedToneCompletionBlock(); | |
} | |
}]; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment