Skip to content

Instantly share code, notes, and snippets.

@wutipong
Created October 16, 2017 17:24
Show Gist options
  • Save wutipong/053131074621928602441aa0f5b1d1d8 to your computer and use it in GitHub Desktop.
Save wutipong/053131074621928602441aa0f5b1d1d8 to your computer and use it in GitHub Desktop.
/**************************************************************************
* AlStream : Play streaming OGG file with custom loop points.
* License : Public Domain.
* Usage: alStream input.ogg
*
* Note:
*
* The input file will be played infintely until the program close. If the
* input file has the `loop_end` comment in its header, the file will be played
* to that point then loop back to start. If the input file has the `loop_start`
* comment in its header, the file will be loop to that point instead of the
* begining of the file.
*
* *************************************************************************/
#include <AL/al.h>
#include <AL/alc.h>
#include <SDL.h>
#include <vorbis/codec.h>
#include <vorbis/vorbisfile.h>
constexpr int BUFFER_COUNT = 3;
constexpr int BUFFER_SIZE = 64 * 1024;
constexpr int FREQUENCY = 44100;
void fillBuffer(OggVorbis_File *pVf, const ALuint &alBuffer,
const int &channels, const long &rate,
const long int &start_loop, const long int &end_loop) {
int bitstream;
int read_total = 0;
char buffer[BUFFER_SIZE];
while (true) {
if (read_total >= BUFFER_SIZE)
break;
auto pos = ov_pcm_tell(pVf);
if (pos > end_loop)
ov_pcm_seek(pVf, start_loop);
int read = ov_read(pVf, buffer + read_total, BUFFER_SIZE - read_total, 0, 2,
1, &bitstream);
read_total += read;
}
alBufferData(alBuffer,
(channels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, buffer,
read_total, rate);
}
int main(int argc, char **argv) {
if (argc != 2)
return -1;
SDL_Init(SDL_INIT_VIDEO);
SDL_Window *pWindow;
SDL_Renderer *pRenderer;
SDL_CreateWindowAndRenderer(800, 600, 0, &pWindow, &pRenderer);
SDL_SetWindowTitle(pWindow, "Test");
auto alcDevice = alcOpenDevice(NULL);
auto alcContext = alcCreateContext(alcDevice, NULL);
alcMakeContextCurrent(alcContext);
ALuint source;
alGenSources(1, &source);
ALuint buffers[BUFFER_COUNT];
alGenBuffers(BUFFER_COUNT, buffers);
OggVorbis_File vf;
ov_fopen(argv[1], &vf);
auto info = ov_info(&vf, -1);
auto comment = ov_comment(&vf, -1);
auto loop_start_cmt = vorbis_comment_query(comment, "loop_start", 0);
auto loop_end_cmt = vorbis_comment_query(comment, "loop_end", 0);
auto loop_start = loop_start_cmt ? atol(loop_start_cmt) : 0L;
auto loop_end = loop_end_cmt ? atol(loop_end_cmt) : ov_pcm_total(&vf, -1);
for (auto buffer : buffers) {
fillBuffer(&vf, buffer, info->channels, info->rate, loop_start, loop_end);
}
alSourceQueueBuffers(source, BUFFER_COUNT, buffers);
alSourcePlay(source);
int currentBufferIndex = 0;
int bufferProcessed = 0;
while (true) {
SDL_Event event;
SDL_PollEvent(&event);
if (event.type == SDL_QUIT)
break;
SDL_Delay(10);
SDL_RenderPresent(pRenderer);
int processed;
alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
if (processed != bufferProcessed) {
int count = processed - bufferProcessed;
for (int i = 0; i < count; i++) {
currentBufferIndex++;
if (currentBufferIndex == BUFFER_COUNT)
currentBufferIndex = 0;
alDeleteBuffers(1, &buffers[currentBufferIndex]);
alGenBuffers(1, &buffers[currentBufferIndex]);
fillBuffer(&vf, buffers[currentBufferIndex], info->channels, info->rate,
loop_start, loop_end);
alSourceQueueBuffers(source, 1, &buffers[currentBufferIndex]);
}
bufferProcessed = processed;
}
}
alcDestroyContext(alcContext);
alcCloseDevice(alcDevice);
ov_clear(&vf);
SDL_DestroyRenderer(pRenderer);
SDL_DestroyWindow(pWindow);
SDL_Quit();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment