yalpa/yalpa.cpp

141 lines
3.8 KiB
C++

#include <iostream>
#include <cstdio>
#include <cstdint>
#include <thread>
#include <chrono>
#if defined _WIN32
#include <al.h>
#include <alc.h>
#include <io.h>
#include <fcntl.h>
#else
#include <AL/al.h>
#include <AL/alc.h>
#endif
#pragma comment(lib, "libs/Win64/OpenAL32.lib")
#if 0 || defined _DEBUG
#define AL_CHECK_ERROR(msg) (checkALError(msg))
#else
#define AL_CHECK_ERROR(msg)
#endif
const uint8_t numBuf = 3;
const ALsizei bufSize = 1000;
const ALenum format = AL_FORMAT_MONO8;
const ALsizei freq = 8000;
char readBuf[bufSize];
void checkALError(const char * msg)
{
while (ALuint err = alGetError() != AL_NO_ERROR)
std::cerr << "Caught AL Error at " << msg << ": " << err << "\n";
}
ALsizei fillBufferFromStdin(ALuint buf)
{
// read
const ALsizei bytesRead = (ALsizei) fread(readBuf, sizeof(uint8_t), bufSize, stdin);
// copy to OpenAL buffer
alBufferData(buf, format, (void *) readBuf, bytesRead, freq);
AL_CHECK_ERROR("buffer data");
return bytesRead;
}
void updateBuffers(ALuint src, ALuint bufs[numBuf])
{
ALint srcState;
do
{
// wait until a buffer is free
ALint val = 0;
do
{
alGetSourcei(src, AL_BUFFERS_PROCESSED, &val);
AL_CHECK_ERROR("get num processed");
if (val > 0) break;
// sleep for a quarter of the duration a buffer plays
std::this_thread::sleep_for(std::chrono::milliseconds((bufSize / freq) * 1000 / 4));
} while (true);
while (val--)
{
// remove oldest buffer from queue and get its id
ALuint buf;
alSourceUnqueueBuffers(src, 1, &buf);
AL_CHECK_ERROR("unqueue buffer");
// fill buffer
const ALsizei bytesRead = fillBufferFromStdin(buf);
// add buffer to queue
alSourceQueueBuffers(src, 1, &buf);
AL_CHECK_ERROR("queue buffer");
// if end of stdin was reached, return
if (bytesRead < bufSize) return;
}
// check if source is still playing
alGetSourcei(src, AL_SOURCE_STATE, &srcState);
} while (AL_PLAYING == srcState);
}
int main(int argc, char * argv[])
{
// set stdin to binary mode
#ifdef _WIN32
_setmode(_fileno(stdin), _O_BINARY);
#else
freopen(nullptr, "rb", stdin);
#endif
// initialization: open default device
ALCdevice * dev = alcOpenDevice(nullptr);
// reset error state
AL_CHECK_ERROR("open device");
// create a context
ALCcontext * context = alcCreateContext(dev, nullptr);
AL_CHECK_ERROR("create context");
alcMakeContextCurrent(context);
AL_CHECK_ERROR("activate context");
// create buffers for audio streaming
ALuint bufs[numBuf];
alGenBuffers(numBuf, bufs);
AL_CHECK_ERROR("create buffer");
// create source to play buffer
ALuint src;
alGenSources(1, &src);
AL_CHECK_ERROR("create source");
// initially fill buffers
for (uint8_t i = 0; i < numBuf; ++i) fillBufferFromStdin(bufs[i]);
alSourceQueueBuffers(src, numBuf, bufs);
AL_CHECK_ERROR("queue buffer");
// play source
alSourcePlay(src);
AL_CHECK_ERROR("play");
// fill buffers in loop
updateBuffers(src, bufs);
// when stream is read completely, wait until source stops playing
ALint srcState;
do
{
alGetSourcei(src, AL_SOURCE_STATE, &srcState);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} while (AL_PLAYING == srcState);
// delete source
alDeleteSources(1, &src);
AL_CHECK_ERROR("delete source");
// delete buffers
alDeleteBuffers(numBuf, bufs);
AL_CHECK_ERROR("delete buffer");
// destroy context
alcDestroyContext(context);
AL_CHECK_ERROR("destroy context");
// close device
alcCloseDevice(dev);
AL_CHECK_ERROR("close device");
std::cout << "Exiting\n";
return 0;
}