Is there a Windows equivalent to the Linux "aplay" that will accept a bitstream and output audio?
Solution 1:
I watched exactly the same video and was very disappointed that I could not find a program for windows that behaves like aplay in this example.
In the end, I wrote one myself using C++ and OpenAL. I will post the code below. You will have to link to the OpenAL library to build the executable. The library is part of the OpenAL Core SDK which you can download from their website.
If you just want the executable, you can download it here. Look for yalpa.exe
.
Syntax
Let's say you use my executable yalpa.exe
. Then you can process your raw audio data by piping it to yalpa:
a.exe | yalpa.exe
Alternatively you could first write the audio data to a file and pass that file to yalpa's stdin:
yalpa.exe < my_audio.raw
Note: yalpa works in cmd, but not in PowerShell. Pipes seem to be handled differently there (see this related SO question).
The code:
Disclaimer: I can not guarantee that this code is 100% bug-free, but I tested it on two different machines with Windows 7 and Windows 10 respectively. It was compiled and linked using the Visual Studio 2013 compiler. I was also able to compile it using g++ on Cygwin, but OpenAL failed at runtime because of problems with pulseaudio.
Feel free to edit and use my code.
#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
#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;
}
Solution 2:
Using vlc and some command-line arguments I copy-pasted from the internet you are able to recreate aplay's functionality on windows.
audio.exe | vlc --demux=rawaud --rawaud-channels 2 --rawaud-samplerate 8000 -
Credit should go to this post and FreeER's answer.