【问题标题】:Sound playback not working when using QueueAudio on C++在 C++ 上使用 QueueAudio 时声音播放不起作用
【发布时间】:2019-08-11 15:15:30
【问题描述】:

我正在尝试通过使用 C++ 上的音频队列来使用 SDL2 播放正弦波声音。为了做到这一点,我创建了一个“扬声器”类,它有一个 pushBeep 函数,每次需要生成哔声时都会调用该函数。我已经成功创建了一个 AudioDevice,当我对设备执行 QueueAudio 时它也成功(我已经检查了调试器)但我似乎无法从中获得任何声音。

我已经尝试以多种方式更改生成样本的方式,而且,正如我之前所说,我已经检查了设备是否正确打开并且 QueueAudio 返回 0 表示成功。

这是课

Speaker::Speaker()
{
    SDL_AudioSpec ds;
    ds.freq = Speaker::SPEAKER_FREQUENCY;
    ds.format = AUDIO_F32;
    ds.channels = 1;
    ds.samples = 4096;
    ds.callback = NULL;
    ds.userdata = this;

    SDL_AudioSpec os;
    this->dev = SDL_OpenAudioDevice(NULL, 0, &ds, &os, NULL);

    std::cout << "DEVICE: " << this->dev << std::endl;
    SDL_PauseAudioDevice(this->dev, 0);
}

Speaker::~Speaker()
{
    SDL_CloseAudioDevice(this->dev);
}

void Speaker::pushBeep(double freq, int duration) {
    int nSamples = duration * Speaker::SPEAKER_FREQUENCY / 1000;
    float* samples = new float[nSamples];
    double v = 0.0;
    for (int idx = 0; idx < nSamples; idx++) {
        //float value = (float)Speaker::SPEAKER_AMPLITUDE * std::sin(v * 2 * M_PI / Speaker::SPEAKER_FREQUENCY);
        float value = 440.0;
        samples[idx] = value;
        v += freq;
    }
    int a = SDL_QueueAudio(this->dev, (void*)samples, nSamples * sizeof(float));
    std::cout << a << std::endl;
    delete[] samples;
    samples = NULL;
}

这就是我的称呼

Speaker s;
s.pushBeep(440.0, 1000);

当我尝试使用正弦波生成代码(已注释)时,它给了我一个“双精度浮点损失”错误。当我使用固定值(未注释)时,它不会给出错误,但它仍然不起作用。

我希望程序能够输出声音。

【问题讨论】:

  • sin 的注释行是怎么回事?应该没问题(取决于“幅度”值),常数不是。设置常数值是......好吧,我们说不是很听得见,只是一声响亮的咔嗒声,然后什么也没有。浮动应该在 1.0 达到峰值,440 太多了,很可能会被削减。
  • @keltar 正如我在 OP 上解释的那样,我也尝试了评论行,但没有效果,我什么也听不到。但是,如果浮点数需要介于 0 和 1 之间,那么该行也是错误的,因为它创建的值超出了限制……成千上万。我会试试这个并报告。关于声音的长度,我相信这取决于您在队列中放置的样本数量。
  • @dagilpe 这就是我所说的,“取决于幅度值”。幅度 1 在这里很好。长度从来都不是问题,我是说恒定值甚至不是波浪(但我们正在进入波浪定义的迂腐基础 - 我们不应该),理论上它只能在值跳跃时在第一个样本中被听到从 0 到你的常数,当它最后回落到 0 时。
  • 它不是 [0;1] 而是 [-1;1] (在这里并不重要,只是为了清楚起见,正弦波完美地包裹到负数)。

标签: c++ audio sdl-2


【解决方案1】:

您缺少一些东西,或者您没有添加到您的代码中。您没有指定音频回调,因此当您调用 SDL_QueueAudio(); 时,它不知道如何处理数据,我很确定。而且您没有在示例中调用 SDL_PauseAudioDevice() 延迟。

#include <math.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_audio.h>
#include <iostream>

namespace AudioGen
{
const int AMPLITUDE = 1;
const int SAMPLE_RATE = 44000;

// Globals
float *in_buffer;
SDL_atomic_t callback_sample_pos;
SDL_Event event;
SDL_bool running = SDL_TRUE;

/**
 * Structure for holding audio metadata such as frequency
 */
struct AudioData
{
     int sampleNum;
     float frequency;
};

void audio_callback(void *user_data, Uint8 *raw_buffer, int bytes)
{
    float *buffer = (float*)raw_buffer;
    AudioData &audio_data(*static_cast<AudioData*>(user_data));
    int nSamples = bytes / 4; // For F32
    std::cout << nSamples << std::endl;
    for(int i = 0; i < nSamples; i++, audio_data.sampleNum++)
    {
        double time = (double)audio_data.sampleNum / (double)SAMPLE_RATE;
        buffer[i] = (float)(AMPLITUDE * sin(2.0f * M_PI * audio_data.frequency * time));
    }
}

int buffer_length;
void callback(void *user_data, Uint8 *raw_buffer, int bytes)
{
    float *buffer = (float*)raw_buffer;
    int nSamples = bytes/4;
    auto local_sample_pos = SDL_AtomicGet(&callback_sample_pos);
    for(int i = 0; i < nSamples; ++i)
    {
        // Stop running audio if all samples are finished playing
        if(buffer_length == local_sample_pos)
        {
            running = SDL_FALSE;
            break;
        }
        buffer[i] = in_buffer[local_sample_pos];
        ++local_sample_pos;
    }
    SDL_AtomicSet(&callback_sample_pos, local_sample_pos);
}

class Speaker
{
    public:
        Speaker()
        {
            SDL_Init(SDL_INIT_AUDIO);
            SDL_AudioSpec ds;
            ds.freq = SAMPLE_RATE;
            ds.format = AUDIO_F32;
            ds.channels = 1;
            ds.samples = 4096;
            ds.callback = callback;
            ds.userdata = &ad; // metadata for frequency

            SDL_AudioSpec os;
            dev = SDL_OpenAudioDevice(NULL, 0, &ds, &os, SDL_AUDIO_ALLOW_FORMAT_CHANGE);
        }

        ~Speaker()
        {
            SDL_CloseAudioDevice(dev);
            SDL_Quit();
        }
        void pushBeep(float frequency, int duration)
        {
            ad.frequency = frequency; // set the frequency for the beep
            SDL_PauseAudioDevice(dev, 0);
            SDL_Delay(duration); // wait while sound is playing
            SDL_PauseAudioDevice(dev, 1);
        }

        void pushBeep2(float frequency, int duration )
        {
            int nSamples = duration * SAMPLE_RATE / 1000;
            in_buffer = new float[nSamples];
            buffer_length = nSamples;
            for (int idx = 0; idx < nSamples; idx++) {
                double time = (double)idx / (double)SAMPLE_RATE;
                in_buffer[idx] = (float)(AMPLITUDE * std::sin(2.0f * M_PI * frequency * time));
            }
            SDL_QueueAudio(dev, in_buffer, nSamples * sizeof(float));
            SDL_PauseAudioDevice(dev, 0);
            while(running){
                while(SDL_PollEvent(&event)!=0);
            }
            delete[] in_buffer;
        }

    private:
        SDL_AudioDeviceID dev;
        AudioData ad;
        int sampleNum = 0;
};
} // End of namespace AudioGen

int main(int argc, char *argv[])
{
    AudioGen::Speaker speaker;
    //speaker.pushBeep(440, 1000);
    speaker.pushBeep2(440.0f, 1000);
    return 0;
}

【讨论】:

  • SDL_PauseAudioDevice(this-&gt;dev, 0); 开始(“取消暂停”)音频播放,除非您不这样做,否则缓冲区应该会自动填充静音。是的,我同意,文档很差,这就是我在这里问的原因。这段代码的问题是你需要使用延迟,这是我想要避免的,因为它完全停止了进程。
  • @dagilpe 我已更新我的示例以不使用 SDL_Delay()。试一试,看看它是否有效。不确定这是否是您想要的,以及它是否会按照您的意愿行事。完全停止该过程是什么意思?你的意思是它是一个阻塞呼叫,你不能同时运行其他音频?
猜你喜欢
  • 2019-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-31
  • 1970-01-01
  • 2010-11-10
  • 1970-01-01
  • 2022-01-23
相关资源
最近更新 更多