如何使用 raylib 播放原始音频

How to play raw audio with raylib

提问人:Carl Younger 提问时间:10/4/2023 最后编辑:Carl Younger 更新时间:10/5/2023 访问量:199

问:

我正在尝试弄清楚 raylib API,甚至无法生成白噪声。我听到一阵短暂的噪音(可能是 100 毫秒),但随后它就安静了。AudioStream

目前只有一个使用 raylib 生成音频的官方示例(还有一些可以播放的示例),我找不到有关 API 的任何其他文档。AudioStream

我编写了一个最小的 C 程序,它应该在屏幕上显示静态文本的同时简单地输出白噪声。它“工作”了几分之一秒,然后就没有声音了。raylib 也没有错误消息(一切都报告为成功)。

我使用的是 32 位整数样本,单声道,频率为 48KHz。我更喜欢使用浮点数,但官方示例使用 ints (),所以我现在使用它。short

代码如下:

/* ====================================================================
WARNING: LOWER YOUR SPEAKERS - THEY WILL POP WHEN THIS PROGRAM EXECUTES
=======================================================================
*/

#include "raylib.h"
#include <stdlib.h>  // required for `rand` and `RAND_MAX`

#define U32_MAX 4294967296
#define U31_MAX 2147483648

int randomSample() {

    /* This helper returns a random, signed 32-bit sample. */

    double randomFraction = rand() / (double)(RAND_MAX);

    return randomFraction * U32_MAX - U31_MAX;
}

void generateSamples(void * buffer, unsigned int frames) {

    /* This audio callback fills a mono buffer with white noise. */

    int * samples = (int *)buffer;

    for (unsigned int i = 0; i < frames; i++) samples[i] = randomSample();
}

int main(void) {

    /* This is a textbook raylib main function. It sets up an audio
    callback, then loops while the window stays open, before tearing
    down the window, audio device, stream etc. */

    InitWindow(800, 480, "playlib");
    InitAudioDevice();

    AudioStream stream = LoadAudioStream(48000, 32, 1);

    SetAudioStreamBufferSizeDefault(1024);
    SetAudioStreamVolume(stream, 1.0f);
    SetAudioStreamCallback(stream, generateSamples);
    PlayAudioStream(stream);
    SetTargetFPS(60);

    while (!WindowShouldClose()) { // loop just redraws the same text
        BeginDrawing();
        ClearBackground(RAYWHITE);
        DrawText("MUSIC SHOULD BE PLAYING!", 245, 150, 20, LIGHTGRAY);
        EndDrawing();
    }

    UnloadAudioStream(stream);
    CloseAudioDevice();
    CloseWindow();

    return 0;
}

在LLDB中,我能够确定正在(重复)调用回调,正在生成样本,并写入缓冲区。然而,我听到的只是应用程序打开时发出的短促的噪音。文本呈现没有问题。

我尝试将缓冲区大小更改为 256 和 4096 之间的 2 的明显幂,但没有效果。

另一件事:当程序启动时,我笔记本电脑上的所有其他音频都会静音,然后在我关闭程序时自行取消静音。这就像 raylib 不能共享输出设备,所以它只是独占接管它。我不确定这是否就是 raylib 的工作方式,但这似乎是错误的。

这是在最新的 M1 Mac 上。

C 音频 raylib

评论

1赞 Random Davis 10/4/2023
for (unsigned int i; i < frames; i++) samples[i] = randomSample();未定义的行为。 未初始化。此外,您在创建 时将样本大小指定为 32 位,但随后您用只有 16 位的值填充缓冲区。iAudioStreamLoadAudioStream(48000, 32, 1);short
0赞 Carl Younger 10/5/2023
谢谢。我是 C 的新手(显然)。我将类型从 更改为 ,并将循环变量初始化更改为循环标头内部。结果完全一样。我将更新代码示例。再次感谢。shortintunsigned int i = 0
0赞 Random Davis 10/5/2023
该示例显示了正在填充的流(注释说),但您的代码没有调用 .此外,在此示例中,使用该函数在主循环中更新音频流。在代码中,没有此类更新调用,这可能会阻止音频连续播放。Refill audio stream if requiredIsAudioStreamProcessed(stream)UpdateAudioStream(stream, writeBuf, MAX_SAMPLES_PER_UPDATE)
0赞 Carl Younger 10/5/2023
谢谢你的建议。我假设调用 to 只是因为它们有一个效果缓冲区,而我只想在回调中复制请求的样本数量。如果我必须在动画循环中触发回调,那么回调似乎是多余的,任何实时音频输入都会变得超级潜在,因为动画循环太少了(+16ms)。UpdateAudioStream
1赞 Random Davis 10/5/2023
不幸的是,我没有足够的知识来知道这些差异是否正是导致您错误的原因;我只是指出了它们。希望它有所帮助。

答:

0赞 twelfth 10/5/2023 #1

根据评论,我不太确定您所说的放置在动画循环中的意思。这似乎不是示例中发生的情况。UpdateMusicStream(music)

尽管如此,下面是一个最小的可重现示例,只要打开它,它就会播放音频文件。此外,如果您想要此功能,它会在按下时播放声音。WindowMOUSE_BUTTON_LEFT

#include "raylib.h"

int main(void)
{
    Sound sound;
    Music music;
    int width = 800;
    int height = 450;

    InitWindow(width, height, "title");
    InitAudioDevice();

    sound = LoadSound("sound.wav");
    music = LoadMusicStream("music.mp3");
    PlayMusicStream(music);

    while (!WindowShouldClose())
    {
        UpdateMusicStream(music);

        if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
        {
            PlaySound(sound);
        }

        BeginDrawing();
            ClearBackground(GRAY);
            DrawText("MUSIC SHOULD BE PLAYING!", width / 2, height / 2, 20, LIGHTGRAY);
        EndDrawing();
    }
    StopMusicStream(music);

    CloseAudioDevice();

    CloseWindow();

    return 0;
}

评论

1赞 Carl Younger 10/5/2023
while 循环迭代(和调用)的频率是多少?我假设它每帧动画运行一次,例如等。UpdateMusicStreamBeginDrawing
1赞 Carl Younger 10/5/2023
我很欣赏这个建议,并会尝试调整它,但我真的很想在运行时生成样本,然后使用 .本示例播放现有文件。谢谢,但这不会输出白噪声。AudioStream
1赞 twelfth 10/5/2023
@CarlYounger 回答你的第一个问题; 每帧检查一次。对于您的第二条评论,会有用吗?它采用类型作为其参数之一。据我了解,an 不同于 a 或 (并且可以与其中之一结合使用)。所以也许用(代替我所拥有的地方)?您可能还会发现 raudio.c 文件很有用。WindowShouldClose()UpdateSoundconst void data*AudioStreamSoundWaveUpdateSoundUpdateMusicStreamUpdateAudioStream