#include #include #include #include #include #if defined _WIN32 #include #include #include #include #else #include #include #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; }