142 lines
3.9 KiB
C++
142 lines
3.9 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[])
|
||
|
{
|
||
|
std::cout << "OpenAL test project\n";
|
||
|
// 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;
|
||
|
}
|