Воспроизведение произвольного звука в Windows?

#winapi #audio #wave

#winapi #Аудио #волна

Вопрос:

Как мне, скажем, воспроизвести звук с заданной амплитудой и заданным частотным составом (скажем, состоящий из частот 2 кГц и 3 кГц) изначально в Windows (32-разрядная и 64-разрядная версии, вплоть до Windows 7)?

(Под «изначально» я подразумеваю без использования внешней библиотеки.)

Я считаю, что для этого нужен метод waveOutWrite, но я понятия не имею, как это работает.

Ответ №1:

У меня что-то работает…

 #define _USE_MATH_DEFINES 1
#include <math.h>
#include <stdio.h>
#include <tchar.h>

#include <windows.h>
#include <mmreg.h>
#include <complex>

#pragma comment(lib, "Winmm.lib")

MMRESULT play(float nSeconds,
    float signal(float timeInSeconds, unsigned short channel, void *context),
    void *context = NULL, unsigned long samplesPerSecond = 48000)
{
    UINT timePeriod = 1;

    MMRESULT mmresult = MMSYSERR_NOERROR;
    WAVEFORMATEX waveFormat = {0};
    waveFormat.cbSize = 0;
    waveFormat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
    waveFormat.nChannels = 2;
    waveFormat.nSamplesPerSec = samplesPerSecond;
    const size_t nBuffer =
        (size_t)(nSeconds * waveFormat.nChannels * waveFormat.nSamplesPerSec);
    float *buffer;
    waveFormat.wBitsPerSample = CHAR_BIT * sizeof(buffer[0]);
    waveFormat.nBlockAlign =
        waveFormat.nChannels * waveFormat.wBitsPerSample / CHAR_BIT;
    waveFormat.nAvgBytesPerSec =
        waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;

    buffer = (float *)calloc(nBuffer, sizeof(*buffer));
    __try
    {
        for (size_t i = 0; i < nBuffer; i  = waveFormat.nChannels)
            for (unsigned short j = 0; j < waveFormat.nChannels; j  )
                buffer[i j] = signal((i j) * nSeconds / nBuffer, j, context);
        HWAVEOUT hWavOut = NULL;
        mmresult = waveOutOpen(amp;hWavOut, WAVE_MAPPER,
            amp;waveFormat, NULL, NULL, CALLBACK_NULL);
        if (mmresult == MMSYSERR_NOERROR)
        {
            __try
            {
                timeBeginPeriod(timePeriod);
                __try
                {
                    WAVEHDR hdr = {0};
                    hdr.dwBufferLength =
                        (ULONG)(nBuffer * sizeof(buffer[0]));
                    hdr.lpData = (LPSTR)amp;buffer[0];
                    mmresult = waveOutPrepareHeader(hWavOut,
                        amp;hdr, sizeof(hdr));
                    if (mmresult == MMSYSERR_NOERROR)
                    {
                        __try
                        {
                            ULONG start = GetTickCount();
                            mmresult =
                                waveOutWrite(hWavOut, amp;hdr, sizeof(hdr));
                            Sleep((ULONG)(1000 * nSeconds
                                - (GetTickCount() - start)));
                        }
                        __finally
                        { waveOutUnprepareHeader(hWavOut, amp;hdr, sizeof(hdr)); }
                    }
                }
                __finally { timeEndPeriod(timePeriod); }
            }
            __finally { waveOutClose(hWavOut); }
        }
    }
    __finally { free(buffer); }
    return mmresu<
}

// Triangle wave generator
float triangle(float timeInSeconds, unsigned short channel, void *context)
{
    const float frequency = *(const float *)context;
    const float angle = (float)(frequency * 2 * M_PI * timeInSeconds);
    switch (channel)
    {
        case  0: return (float)asin(sin(angle   0 * M_PI / 2));
        default: return (float)asin(sin(angle   1 * M_PI / 2));
    }
}

// Pure tone generator
float pure(float timeInSeconds, unsigned short channel, void *context)
{
    const float frequency = *(const float *)context;
    const float angle = (float)(frequency * 2 * M_PI * timeInSeconds);
    switch (channel)
    {
        case  0: return (float)sin(angle   0 * M_PI / 2);
        default: return (float)sin(angle   1 * M_PI / 2);
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    float frequency = 2 * 261.626F;
    play(1, pure, amp;frequency);
    return 0;
}
  

Комментарии:

1. 1. Но я думаю, что генератор треугольных волн ошибочен. Согласно Википедии : return (float)asin(sin(angle 0 * M_PI / 2)) * 2 / M_PI; .

Ответ №2:

Звуковой сигнал

 BOOL WINAPI Beep(
    __in  DWORD dwFreq,
    __in  DWORD dwDuration
);
  

Ответ №3:

waveOut Функции обрабатывают данные о форме звукового сигнала (в формате WAV, если я правильно помню).

Хотя это предназначено для приложений WPF, следующая ссылка должна оказаться полезной для любого настольного приложения:

Генерация звука в приложениях WPF

Ответ №4:

Звуковой сигнал подается через динамик ПК или с помощью Directx Sound. Я могу предложить несколько фрагментов, если вам нужно.

Комментарии:

1. Ну, функция звукового сигнала не работает, потому что она не может генерировать комбинацию частот. Звук DirectX кажется излишним, поскольку я просто хочу сгенерировать простой звуковой сигнал … есть ли какой-нибудь способ сделать это с помощью чего-то вроде waveOutWrite ? Я думаю, что это правильный способ, но я понятия не имею, какие данные ему предоставить.

2. @Mehrdad: wavOutWrite ожидает фрагменты звука в формате WAV, iirc.

3. @Adam: Ах, это отличный ответ. =) Не стесняйтесь опубликовать это, чтобы я мог принять это! (Кстати, это полные данные wave или только их часть? Нужно ли мне также отправлять все заголовки RIFF?)