2

Я смотрел видео Computerphile о коде golf и chiptunes и был заинтересован в запуске предоставленного примера кода, но он опирается на aplay , утилиту Linux для драйвера звуковой карты ALSA, и я хотел бы запустить ее на Windows 7. Существует ли в Windows 7+ эквивалентная программа или утилита (желательно, но не обязательно для ОС), которая будет принимать поток байтов и преобразовывать его в аудиопоток?

4 ответа4

3

Я смотрел точно такое же видео и был очень разочарован тем, что не смог найти программу для окон, которая ведет себя как 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;
}
1

Используя vlc и некоторые аргументы командной строки, которые я скопировал из Интернета, вы можете воссоздать функциональность aplay в Windows.

audio.exe | vlc --demux=rawaud --rawaud-channels 2 --rawaud-samplerate 8000 -

Кредит должен перейти на этот пост и ответ FreeER.

0

так же :)

Я нашел, что вы можете использовать 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 -
0

Для потомков: кажется, что на самом деле не очень хорошее решение, но другое, достаточно приблизительное, которое я нашел, это использовать SoX со следующими опциями для генерации файла .wav:

./a.exe | sox -t raw -b 16 -e signed -r 8000 - test.wav

Это создает очень большой файл очень быстро, поэтому не позволяйте ему работать слишком долго. Около 10 секунд на моей машине (процессор Intel i7 3,4 ГГц) генерировалось около 2 часов звука (~ 125 КБ).

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .