尝试使用 SDL 播放 WAV 但出现嗡嗡声

Trying to play a WAV with SDL but getting buzzing noise

我正在编写这个简单的程序来学习如何使用 SDL 处理音频。该程序打开一个 window,其中有一个按钮,上面写着开始,当您按下按钮时,它应该播放鼓样本的 WAV。没有错误,程序也没有崩溃,只是它没有播放鼓声,而是发出奇怪的嗡嗡声,直到您退出程序才停止。我认为这与音频回调函数有关,但我对此很陌生,所以我不确定它到底是如何工作的。

#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <SDL2/SDL_audio.h>

TTF_Font* font;
SDL_Color white = {255, 255, 255};
SDL_AudioSpec frmt;
bool run = true;
bool playing = false;

struct sample
{
    Uint8 *data;
    Uint32 dpos;
    Uint32 dlen;
} sound;

bool buttonPress(int x, int y, SDL_Rect r)
{
    if (x < (r.x + r.w) && x > r.x)
    {
        if (y < (r.y + r.h) && y > r.y)
        {
            return true;
        }
    }
    return false;
}

void mixing(void *unused, Uint8 *stream, int len)
{
    Uint32 amount;

    if (playing)
    {
        amount = sound.dlen - sound.dpos;
        if ( amount > len ) {
            amount = len;
        }
        SDL_MixAudio(stream, &sound.data[sound.dpos], amount, SDL_MIX_MAXVOLUME);
        sound.dpos += amount;
    }
}

int main(int argc, char *argv[])
{
    SDL_Init(SDL_INIT_EVERYTHING);
    TTF_Init();

    frmt.channels = 1;
    frmt.format = AUDIO_S16;
    frmt.freq = 22050;
    frmt.samples = 512;
    frmt.userdata = NULL;
    frmt.callback = mixing;

    int mX, mY;
    Uint32 fpsTimer = SDL_GetTicks();

    font = TTF_OpenFont("octaFont.TTF", 20);

    SDL_Window* window = SDL_CreateWindow("Test", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 400, 200, 0);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    SDL_Surface* surface = TTF_RenderText_Solid(font, "Go!", white);
    SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
    SDL_Rect button;
    button.w = 50;
    button.h = 35;
    button.x = 10;
    button.y = 10;
    SDL_Rect bg;
    bg.w = 400;
    bg.h = 200;
    bg.x = 0;
    bg.y = 0;
    SDL_Event e;

    if (SDL_OpenAudio(&frmt, NULL) < 0)
    {
        fprintf(stderr, "Unable to open audio: %s\n", SDL_GetError());
        return 1;
    }

    SDL_PauseAudio(0);

    SDL_AudioSpec wave;
    SDL_AudioCVT cvt;
    Uint8 *data;
    Uint32 dlen;
    if (SDL_LoadWAV("sound.wav", &wave, &data, &dlen) == NULL)
    {
        fprintf(stderr, "Unable to load wave: %s\n", SDL_GetError());
        return 1;
    }
    SDL_BuildAudioCVT(&cvt, wave.format, wave.channels, wave.freq, AUDIO_S16, 1, 22050);
    cvt.buf = malloc(dlen*cvt.len_mult);
    memcpy(cvt.buf, data, dlen);
    cvt.len = dlen;
    SDL_ConvertAudio(&cvt);
    SDL_FreeWAV(data);
    SDL_LockAudio();
    free(sound.data);
    sound.data = cvt.buf;
    sound.dlen = cvt.len_cvt;
    sound.dpos = 0;
    SDL_UnlockAudio();

    while (run)
    {
        while (SDL_PollEvent(&e))
        {
            switch (e.type)
            {
                case SDL_QUIT:
                    run = false;
                    break;
                case SDL_KEYDOWN:
                    switch (e.key.keysym.sym)
                    {
                        case SDLK_SPACE:
                            playing = false;
                            break;
                        default:
                            break;
                    }
                    break;
                case SDL_MOUSEBUTTONDOWN:
                    SDL_GetMouseState(&mX, &mY);
                    if (buttonPress(mX, mY, button))
                    {
                        playing = true;
                    }
                    break;
                default:
                    break;
            }
        }
        if (SDL_GetTicks() - fpsTimer >= 16)
        {
            SDL_RenderClear(renderer);
            SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
            SDL_RenderFillRect(renderer, &bg);
            SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
            SDL_RenderDrawRect(renderer, &button);
            SDL_RenderCopy(renderer, texture, NULL, &button);
            SDL_RenderPresent(renderer);
            fpsTimer = SDL_GetTicks();
        }
    }

    SDL_CloseAudio();
    SDL_DestroyWindow(window);
    SDL_DestroyRenderer(renderer);
    TTF_Quit();
    SDL_Quit();
    return 0;
}

问题可能在于您正在使用 SDL_MixAudio

您的代码混入了 .wav 文件中的数据,但混入了什么?

stream调用SDL_MixAudio之前是什么?

我得到同样的嗡嗡声,但是当我使用 memsetstream 缓冲区清零时 调用 SDL_MixAudio 之前,我得到来自 .wav 文件的正确声音。

我不确定,但我相信在对您的 mixing 函数的第二次和后续调用中,来自 prior 回调的数据仍在stream 缓冲区。

旁注:.wav数据播放完毕后,我们可以重置sound.dpos重新播放文件。

这是重构后的代码:

void
mixing(void *unused, Uint8 *stream, int len)
{
    Uint32 amount;

// NOTE/DEBUG: force sound on
#if 1
    playing = 1;
#endif

// NOTE/FIX: zero out stream data so we don't mix in random data (from the
// prior round)
#if 1
        memset(stream,0,len);
#endif

    if (playing) {
        amount = sound.dlen - sound.dpos;
        if (amount > len) {
            amount = len;
        }
#if 1
        SDL_MixAudio(stream, &sound.data[sound.dpos], amount, SDL_MIX_MAXVOLUME);
#else
        memcpy(stream,&sound.data[sound.dpos],amount);
#endif
        sound.dpos += amount;

// NOTE/FIX(?) -- start wav at beginning after all data has been output
#if 1
        if (sound.dpos >= sound.dlen)
            sound.dpos = 0;
#endif
    }
}