返回

SDL播放PCM音频文件—拉方式(pull)

Android

前言

在前几篇文章中,我们已经学习了如何使用SDL播放YUV视频文件。接下来,我们将学习如何使用SDL播放PCM音频文件。SDL播放音频文件有两种方法,可以理解为推(push)和拉(pull)两种模式。推就是我们主动向设备缓冲区填充Buffer,而拉就是由设备拉取Buffer。

推模式

推模式是通过调用SDL_QueueAudio()函数来实现的。SDL_QueueAudio()函数会把一段音频数据复制到SDL的音频缓冲区中。当缓冲区满了之后,SDL就会开始播放音频数据。推模式的优点是简单易用,但缺点是容易造成音频数据的丢失。

拉模式

拉模式是通过调用SDL_LockAudioDevice()和SDL_UnlockAudioDevice()函数来实现的。SDL_LockAudioDevice()函数会锁定SDL的音频缓冲区,SDL_UnlockAudioDevice()函数会解锁SDL的音频缓冲区。在锁定音频缓冲区之后,我们就可以往缓冲区中填充音频数据了。当缓冲区满了之后,SDL就会开始播放音频数据。拉模式的优点是能够避免音频数据的丢失,但缺点是比较复杂。

SDL相关函数和结构体介绍

在使用SDL播放PCM音频文件之前,我们需要先了解一下SDL的相关函数和结构体。

SDL_AudioSpec结构体

SDL_AudioSpec结构体用于音频格式和参数。SDL_AudioSpec结构体的定义如下:

typedef struct SDL_AudioSpec {
    Uint16 freq;             /**< sample rate in Hz */
    Uint8 format;            /**< audio data format */
    Uint8 channels;          /**< number of channels (1 mono, 2 stereo) */
    Uint8 silence;           /**< 'silence' value in 16-bit signed audio */
    Uint32 samples;          /**< sample size in bytes */
    Uint32 padding;          /**< non-audio data padding in bytes */
    Uint32 size;             /**< total size in bytes */
    SDL_AudioCallback callback; /**< callback function called when buffer needs filling */
    void *userdata;          /**< passed as argument to callback */
} SDL_AudioSpec;

其中,freq表示采样率,format表示音频数据格式,channels表示声道数,silence表示静音值,samples表示采样大小(以字节为单位),padding表示非音频数据填充(以字节为单位),size表示总大小(以字节为单位),callback表示当缓冲区需要填充时调用的回调函数,userdata表示传递给回调函数的参数。

SDL_AudioDeviceID类型

SDL_AudioDeviceID类型表示音频设备的ID。SDL_AudioDeviceID类型的定义如下:

typedef Uint32 SDL_AudioDeviceID;

SDL_LockAudioDevice()函数

SDL_LockAudioDevice()函数用于锁定SDL的音频缓冲区。SDL_LockAudioDevice()函数的定义如下:

SDL_AudioDeviceID SDL_LockAudioDevice(SDL_AudioDeviceID devid);

其中,devid表示音频设备的ID。

SDL_UnlockAudioDevice()函数

SDL_UnlockAudioDevice()函数用于解锁SDL的音频缓冲区。SDL_UnlockAudioDevice()函数的定义如下:

void SDL_UnlockAudioDevice(SDL_AudioDeviceID devid);

其中,devid表示音频设备的ID。

SDL_GetAudioDeviceStatus()函数

SDL_GetAudioDeviceStatus()函数用于获取SDL音频设备的状态。SDL_GetAudioDeviceStatus()函数的定义如下:

SDL_AudioStatus SDL_GetAudioDeviceStatus(SDL_AudioDeviceID devid);

其中,devid表示音频设备的ID。

SDL播放PCM音频文件

现在,我们已经了解了SDL的相关函数和结构体,接下来就可以开始学习如何使用SDL播放PCM音频文件了。

步骤1:打开SDL

首先,我们需要打开SDL。SDL的打开方式如下:

SDL_Init(SDL_INIT_AUDIO);

步骤2:设置音频格式和参数

接下来,我们需要设置音频格式和参数。音频格式和参数可以通过SDL_AudioSpec结构体来设置。SDL_AudioSpec结构体的设置方法如下:

SDL_AudioSpec spec;
spec.freq = 44100;
spec.format = AUDIO_S16SYS;
spec.channels = 2;
spec.silence = 0;
spec.samples = 1024;
spec.padding = 0;
spec.size = 0;
spec.callback = NULL;
spec.userdata = NULL;

其中,freq表示采样率,format表示音频数据格式,channels表示声道数,silence表示静音值,samples表示采样大小(以字节为单位),padding表示非音频数据填充(以字节为单位),size表示总大小(以字节为单位),callback表示当缓冲区需要填充时调用的回调函数,userdata表示传递给回调函数的参数。

步骤3:打开音频设备

接下来,我们需要打开音频设备。音频设备的打开方式如下:

SDL_AudioDeviceID devid = SDL_OpenAudioDevice(NULL, 0, &spec, NULL, 0);

其中,第一个参数表示音频设备的名称,第二个参数表示音频设备的索引,第三个参数表示音频格式和参数,第四个参数表示回调函数,第五个参数表示传递给回调函数的参数。

步骤4:播放音频文件

接下来,我们可以开始播放音频文件了。音频文件的播放方式如下:

while (1) {
    SDL_AudioStatus status = SDL_GetAudioDeviceStatus(devid);
    if (status == SDL_AUDIO_PLAYING) {
        // 填充音频数据
        SDL_LockAudioDevice(devid);
        // ...
        SDL_UnlockAudioDevice(devid);
    } else if (status == SDL_AUDIO_STOPPED) {
        break;
    }
}

其中,SDL_GetAudioDeviceStatus()函数用于获取SDL音频设备的状态,SDL_LockAudioDevice()函数用于锁定SDL的音频缓冲区,SDL_UnlockAudioDevice()函数用于解锁SDL的音频缓冲区。

步骤5:关闭音频设备

最后,我们需要关闭音频设备。音频设备的关闭方式如下:

SDL_CloseAudioDevice(devid);

总结

以上就是SDL播放PCM音频文件的方法。SDL播放音频文件有两种方法,可以理解成推(push)和拉(pull)两种模式。推就是我们主动向设备缓冲区填充Buffer,而拉就是由设备拉取Buffer。