我当时正在看Computerphile 关于代码高尔夫和芯片音乐的视频并且对跑步很感兴趣提供的示例代码,但它依赖于aplay
,一个用于 ALSA 声卡驱动程序的 Linux 实用程序,我想在 Windows 7 上运行它。Windows 7+ 上是否有等效的程序或实用程序(最好但不一定是操作系统提供的)可以获取字节流并将其转换为音频流?
答案1
相同的 :)
我发现你可以使用 ffmpeg 来转换它
ffmpeg -f u8 -i music.raw music.wav
然后使用你喜欢的任何方式来玩
现在,我尝试直接通过管道传输它(vlc 允许在 stdin 上输入,但它不会读取原始数据,至少在没有参数的情况下不会,我不确定如何给它哈哈)
music.exe | ffmpeg ... -i pipe:1 | vlc.exe -
但是 ffmpeg 说管道没有足够的空间(我也尝试了管道:0,因为我不能 100% 确定它是 Windows 上的标准输入……)
所以我最终将 music.exe 重定向到 music.raw 文件一小段时间(music.exe > music.raw
)然后我可以使用它直接从 ffmpeg 传输到 vlc
ffmpeg.exe -f u8 -i music.raw -f wav pipe:1 | vlc.exe -
答案2
我观看了完全相同的视频并且非常失望,因为我找不到一个适用于 Windows 的程序,其行为与本例中的 aplay 类似。
最后,我自己用 C++ 写了一个开放的AL。我将在下面发布代码。您必须链接到 OpenAL 库才能构建可执行文件。该库是 OpenAL Core SDK 的一部分,您可以从他们的网站下载。
如果你只想要可执行文件,你可以下载它这里。 寻找yalpa.exe
。
句法
假设你使用我的可执行文件yalpa.exe
。然后你可以通过将其传输到 yalpa 来处理原始音频数据:
a.exe | yalpa.exe
或者,您可以先将音频数据写入文件,然后将该文件传递给 yalpa 的标准输入:
yalpa.exe < my_audio.raw
注意:yalpa 在 cmd 中有效,但在 PowerShell 中无效。管道似乎在那里以不同的方式处理(请参阅这个相关的SO问题)。
代码:
免责声明:我无法保证此代码 100% 没有错误,但我分别在 Windows 7 和 Windows 10 的两台不同机器上对其进行了测试。它是使用 Visual Studio 2013 编译器进行编译和链接的。我也能够在 Cygwin 上使用 g++ 对其进行编译,但由于 pulseaudio 存在问题,OpenAL 在运行时失败。
请随意编辑和使用我的代码。
#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;
}
答案3
使用 vlc 和一些我从互联网上复制粘贴的命令行参数,您可以在 Windows 上重新创建 aplay 的功能。
audio.exe | vlc --demux=rawaud --rawaud-channels 2 --rawaud-samplerate 8000 -
功劳应该归于这个帖子以及 FreeER 的回答。
答案4
python 模块 simleSound 在 Windows 上使用 mciSendStringW 播放声音文件,在 osx 上运行 afplay,在 linux 上运行 aplay: https://github.com/nekumelon/simpleSound/blob/main/simpleSound.py
您可以使用它来执行播放声音的一行代码。
pip install simpleSound
安装模块