Я смотрел видео Computerphile о коде golf и chiptunes и был заинтересован в запуске предоставленного примера кода, но он опирается на aplay
, утилиту Linux для драйвера звуковой карты ALSA, и я хотел бы запустить ее на Windows 7. Существует ли в Windows 7+ эквивалентная программа или утилита (желательно, но не обязательно для ОС), которая будет принимать поток байтов и преобразовывать его в аудиопоток?
4 ответа
Я смотрел точно такое же видео и был очень разочарован тем, что не смог найти программу для окон, которая ведет себя как aplay в этом примере.
В конце концов, я написал один сам, используя C++ и OpenAL. Я выложу код ниже. Вам нужно будет связаться с библиотекой OpenAL для создания исполняемого файла. Библиотека является частью OpenAL Core SDK, которую вы можете скачать с их сайта.
Если вам нужен только исполняемый файл, вы можете скачать его здесь. Ищите yalpa.exe
.
Синтаксис
Допустим, вы используете мой исполняемый файл yalpa.exe
. Затем вы можете обработать ваши необработанные аудиоданные, отправив их в yalpa:
a.exe | yalpa.exe
В качестве альтернативы вы можете сначала записать аудиоданные в файл и передать этот файл в stdin yalpa:
yalpa.exe < my_audio.raw
Примечание: ялпа работает в cmd, но не в PowerShell. С трубами, похоже, там обращаются по-другому (см. Этот связанный вопрос ТАК).
Код:
Отказ от ответственности: я не могу гарантировать, что этот код не содержит ошибок на 100%, но я протестировал его на двух разных машинах с Windows 7 и Windows 10 соответственно. Он был скомпилирован и связан с использованием компилятора Visual Studio 2013. Я также смог скомпилировать его с помощью g++ на Cygwin, но OpenAL не удалось во время выполнения из-за проблем с pulseaudio.
Не стесняйтесь редактировать и использовать мой код.
#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;
}
Используя vlc и некоторые аргументы командной строки, которые я скопировал из Интернета, вы можете воссоздать функциональность aplay в Windows.
audio.exe | vlc --demux=rawaud --rawaud-channels 2 --rawaud-samplerate 8000 -
Кредит должен перейти на этот пост и ответ FreeER.
так же :)
Я нашел, что вы можете использовать ffmpeg, чтобы конвертировать его
ffmpeg -f u8 -i music.raw music.wav
Тогда используйте то, что вам нравится, чтобы играть в нее
Теперь я попытался передать его напрямую (vlc позволяет вводить данные на stdin, но он не будет читать необработанные данные, по крайней мере, без аргументов. Я не уверен, как это передать)
music.exe | ffmpeg ... -i pipe:1 | vlc.exe -
но ffmpeg сказал, что канал не имеет достаточно места (я также пробовал pipe:0, так как я не был уверен на 100%, что это за stdin на окнах ...)
так что в итоге я в течение небольшого времени перенаправлял music.exe в файл music.raw (music.exe > music.raw
), затем я мог использовать его для прямой передачи из ffmpeg в vlc
ffmpeg.exe -f u8 -i music.raw -f wav pipe:1 | vlc.exe -
Для потомков: кажется, что на самом деле не очень хорошее решение, но другое, достаточно приблизительное, которое я нашел, это использовать SoX со следующими опциями для генерации файла .wav:
./a.exe | sox -t raw -b 16 -e signed -r 8000 - test.wav
Это создает очень большой файл очень быстро, поэтому не позволяйте ему работать слишком долго. Около 10 секунд на моей машине (процессор Intel i7 3,4 ГГц) генерировалось около 2 часов звука (~ 125 КБ).