关于 c : 使用 OpenAL(Stream) 播放声音

Play sound with OpenAL(Stream)

我需要播放从网络获取的声音。每 10 毫秒,需要 882 字节的声音(单声道,16 位,44100 Hz)。并这样做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
while (!ExitKey)
    {
        boost::system::error_code error;
        size_t len = VoiceSocket->read_some(boost::asio::buffer(buf), error);
        if (len==0)
        {
            continue;
        }
        alGenSources(1, &alSource);
        alGenBuffers(1, &alSampleSet);
        alBufferData(alSource, AL_FORMAT_MONO16, buf.data(), buf.size(), 44100);
        alSourcei(alSource, AL_BUFFER, alSampleSet);
        alSourcePlay(alSource);
    }

但是我的声音没有播放。在 buf 变量中我有声音,因为如果我将它发送到文件,我在文件中有声音。


您的代码未显示 alListener。这似乎有点太明显的错误,但是没有听众,没有听到声音,否则发布的代码应该会产生一些声音,假设有来自套接字的非零数据。

编辑:再看一遍,你有一个小错字,后果很严重。 alBufferData(alSource, ...) 应该是 alBufferData(alSampleSet, ...)。您要填充缓冲区对象,而不是源。否则,您正在播放一个空缓冲区,不出所料,它不会产生声音。

还要注意,您的代码通常效率低下,无法按预期工作。

您绝对不想为每 10 毫秒收到的每个数据包生成一个新的源(和缓冲区)。这会产生一些声音,但不是你想要的。即使没有网络抖动(您将不可避免地遇到),如果没有可听见的间隙,它也不会很好地播放。

在进入循环之前生成一个源和至少 3 个缓冲区(最好多几个,5 或 6 个)。只要您在流式传输,一个源就会一直存在,并且会在这段时间内持续播放。

在进入循环之前至少接收 2 个缓冲区。按照您收到它们的顺序将这些缓冲区排队到源 (alSourceQueueBuffers) 并开始播放。每当您收到新数据包时,将数据读入新缓冲区(从已分配缓冲区的空闲列表中拉出)并将其排队。

当缓冲区空闲时,取消队列并将它们重新循环到您的空闲列表中(首先查询!)。

这样,OpenAL 在播放声音时总是有一些数据要读取,这对于获得良好的结果至关重要。永远不要让它饿死。


我写了这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
void Sound::GoChat()
{
    device = alcOpenDevice(NULL);
    //
    ofstream file;
    file.open("TESTSPEAKER", std::ios_base::binary);
    context = alcCreateContext(device, NULL);
    alcMakeContextCurrent(context);
    // alGetError();
    char *alBuffer;
    ALenum alFormatBuffer;
    ALsizei alFreqBuffer;
    long alBufferLen;
    ALboolean alLoop;
    unsigned int alSource;
    unsigned int alSampleSet[3];
    boost::array <char, 882> buf;
    int NumberOfSampleSet = 0;
    alGenSources(1, &alSource);
    alGenBuffers(3, alSampleSet);
    //
    alBufferData(alSampleSet[0], AL_FORMAT_MONO16, buf.data(), buf.size(), 44100);
    alBufferData(alSampleSet[1], AL_FORMAT_MONO16, buf.data(), buf.size(), 44100);
    alBufferData(alSampleSet[2], AL_FORMAT_MONO16, buf.data(), buf.size(), 44100);
    alSourceQueueBuffers(alSource, 3, alSampleSet);
    //
    while (!ExitKey)
    {
        alSourceUnqueueBuffers(alSource, 3, alSampleSet);
        boost::system::error_code error;
        size_t len = VoiceSocket->read_some(boost::asio::buffer(buf), error);
        if (len==0)
        {
            continue;
        }
        file.write(buf.data(), 882);

        alBufferData(alSampleSet[0], AL_FORMAT_MONO16, buf.data(), buf.size(), 44100);
        alBufferData(alSampleSet[1], AL_FORMAT_MONO16, buf.data(), buf.size(), 44100);
        alBufferData(alSampleSet[2], AL_FORMAT_MONO16, buf.data(), buf.size(), 44100);

        alSourcei(alSource, AL_BUFFER, alSampleSet[0]);
        alSourcei(alSource, AL_BUFFER, alSampleSet[1]);
        alSourcei(alSource, AL_BUFFER, alSampleSet[2]);
            alSourceQueueBuffers(alSource, 3, alSampleSet);
        //
        //alSourcei(alSource, AL_LOOPING, alSampleSet);        
        alSourcePlay(alSource);
        alErr = alGetError();
        //
    }
    VoiceSocket->close();
    file.close();
}

但这也行不通(


因此,如果原始缓冲区中有有效数据,则需要使用 alGetError 检查 OpenAL 函数,以查看其中一些函数是否失败并找出它们失败的原因。从问题描述中我没有看到这个基本检查已经完成。