Created
April 3, 2021 19:04
-
-
Save yyny/81e7e623f732c820001955ab2a2ffe21 to your computer and use it in GitHub Desktop.
Advanced Looping using `miniaudio` library
This file contains 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
#define MINIAUDIO_IMPLEMENTATION | |
#include <miniaudio.h> | |
#include <stdio.h> | |
ma_uint32 first_frame; // frame to start looping | |
ma_uint32 last_frame; // frame to end looping (exclusive) | |
ma_uint32 num_frames; // number of frames to loop | |
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) | |
{ | |
ma_decoder* pDecoder = (ma_decoder*)pDevice->pUserData; | |
if (pDecoder == NULL) { | |
return; | |
} | |
ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); | |
ma_uint64 frameCursor; | |
ma_decoder_get_cursor_in_pcm_frames(pDecoder, &frameCursor); | |
if (frameCursor < first_frame) { | |
// The cursor is before first_frame | |
// Should never happen, but just in case, seek to first_frame. | |
// If you comment this out, | |
// the audio will play normally until reaching first_frame, | |
// and only then start looping. | |
ma_decoder_seek_to_pcm_frame(pDecoder, first_frame); | |
frameCursor = first_frame; | |
} else if (frameCursor > last_frame) { | |
// The cursor is after last_frame | |
// Should never happen, but just in case, seek to last_frame. | |
// If you comment this out, | |
// the audio will stop looping if you ever seek past last_frame. | |
ma_decoder_seek_to_pcm_frame(pDecoder, last_frame); | |
frameCursor = last_frame; | |
} else if (frameCursor + frameCount > last_frame) { | |
// Read frames until the cursor hits last_frame, | |
// then keep looping back to first_frame until we filled the buffer. | |
ma_uint64 offset = 0; // frame offset to write to | |
ma_uint32 numFramesToRead = last_frame - frameCursor; // number of frames to read | |
// We will first read enough frames to reach last_frame, and seek back to first_frame. | |
// After that, we will keep reading num_frames (or less) and seek back, until we filled up the output buffer. | |
while (frameCount > 0) { // keep looping until we filled the buffer... | |
ma_uint64 numFramesRead = ma_decoder_read_pcm_frames(pDecoder, ma_offset_ptr(pOutput, bpf * offset), numFramesToRead); | |
if (numFramesRead < numFramesToRead) { | |
// last_frame is past the end of our decoded audio data, adjust values | |
last_frame = frameCursor + numFramesRead; | |
num_frames = last_frame - first_frame; | |
} | |
frameCount -= numFramesRead; | |
if (frameCount < 0) break; // Our buffer is full, and we are not yet done with our current loop, so we shouldn't seek back yet. | |
offset += numFramesToRead; | |
numFramesToRead = frameCount; // try to read as many frames as necessairy... | |
if (numFramesToRead > num_frames) numFramesToRead = num_frames; // ...but read at most num_frames | |
ma_decoder_seek_to_pcm_frame(pDecoder, first_frame); // seek back to the start. | |
} | |
} else { | |
// The normal case | |
ma_decoder_read_pcm_frames(pDecoder, pOutput, frameCount); | |
} | |
(void)pInput; | |
} | |
int main(int argc, char** argv) | |
{ | |
ma_result result; | |
ma_decoder decoder; | |
ma_device_config deviceConfig; | |
ma_device device; | |
if (argc < 2) { | |
printf("No input file.\n"); | |
return -1; | |
} | |
result = ma_decoder_init_file(argv[1], NULL, &decoder); | |
if (result != MA_SUCCESS) { | |
return -2; | |
} | |
deviceConfig = ma_device_config_init(ma_device_type_playback); | |
deviceConfig.playback.format = decoder.outputFormat; | |
deviceConfig.playback.channels = decoder.outputChannels; | |
deviceConfig.sampleRate = decoder.outputSampleRate; | |
deviceConfig.dataCallback = data_callback; | |
deviceConfig.pUserData = &decoder; | |
if (ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS) { | |
printf("Failed to open playback device.\n"); | |
ma_decoder_uninit(&decoder); | |
return -3; | |
} | |
// loop the first five seconds of audio | |
first_frame = ma_calculate_buffer_size_in_frames_from_milliseconds(0 * 1000, decoder.internalSampleRate); | |
num_frames = ma_calculate_buffer_size_in_frames_from_milliseconds(5 * 1000, decoder.internalSampleRate); | |
last_frame = first_frame + num_frames; | |
if (ma_decoder_seek_to_pcm_frame(&decoder, first_frame) != MA_SUCCESS) { | |
printf("Failed to seek to start of loop.\n"); | |
ma_device_uninit(&device); | |
ma_decoder_uninit(&decoder); | |
return -4; | |
} | |
if (ma_device_start(&device) != MA_SUCCESS) { | |
printf("Failed to start playback device.\n"); | |
ma_device_uninit(&device); | |
ma_decoder_uninit(&decoder); | |
return -5; | |
} | |
printf("Press Enter to quit..."); | |
getchar(); | |
ma_device_uninit(&device); | |
ma_decoder_uninit(&decoder); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment