5.基于SDL2播放PCM音频

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011003120/article/details/81709768

接上一篇<基于FFMPEG将音频解码为PCM>,接下来就是需要将PCM音频进行播放,查阅资料是通过SDL进行音视频的播放,因此这里记录一下SDL相关的笔记。。。

一.简介

摘抄自百度百科:

SDL(Simple DirectMedia Layer)是一套开放源代码的跨平台多媒体开发库,使用C语言写成。SDL提供了数种控制图像、声音、输出入的函数,让开发者只要用相同或是相似的代码就可以开发出跨多个平台(Linux、Windows、Mac OS X等)的应用软件。目前SDL多用于开发游戏、模拟器、媒体播放器等多媒体应用领域。

从上面介绍可以得知,SDL是一套开源的多媒体开发库,对内封装了与底层硬件交互的接口,对外提供统一的接口,我们使用者只需要调用接口,而不需要关注平台、底层硬件等参数,即可正常进行的编码工作。

SDL除了用于音视频的播放外,还提供了其他的功能,例如:摇杆、光盘驱动器、视窗管理等等,而我们现在只需要关注的则是音视频的播放,其他的暂且不提。

目前,使用的是SDL2。

二、流程及函数

1)音频播放流程

只涉及到音频的播放流程,初始化SDL—->根据提供参数打开音频设备—>获取音频流—>循环播放—->结束

2)常用函数

1.SDL_Init()

函数原型: int SDLCALL SDL_Init(Uint32 flags);

初始化SDL系统,其中flag可以选择的选项有:

/**
 *  \name SDL_INIT_*
 *
 *  These are the flags which may be passed to SDL_Init().  You should
 *  specify the subsystems which you will be using in your application.
 */
/* @{ */
#define SDL_INIT_TIMER          0x00000001
#define SDL_INIT_AUDIO          0x00000010
#define SDL_INIT_VIDEO          0x00000020  /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
#define SDL_INIT_JOYSTICK       0x00000200  /**< SDL_INIT_JOYSTICK implies SDL_INIT_EVENTS */
#define SDL_INIT_HAPTIC         0x00001000
#define SDL_INIT_GAMECONTROLLER 0x00002000  /**< SDL_INIT_GAMECONTROLLER implies SDL_INIT_JOYSTICK */
#define SDL_INIT_EVENTS         0x00004000
#define SDL_INIT_NOPARACHUTE    0x00100000  /**< Don't catch fatal signals */
#define SDL_INIT_EVERYTHING ( \
                SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_EVENTS | \
                SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER \
            )
/* @} */

可以根据自己代码里面的需求进行选择,例如只有音频播放,则可以选择SDL_INIT_AUDIO

2.SDL_INIT_AUDIO()

函数原型:int SDLCALL SDL_OpenAudio(SDL_AudioSpec * desired,
 SDL_AudioSpec * obtained);

根据提供的参数打开音频设备,其中desired 为期望的音频设备参数, obtained为实际的音频设备参数,一般将其置为NULL即可。

结构体参数如下:

typedef struct SDL_AudioSpec
{
    int freq;                   /**< DSP frequency -- samples per second */
    SDL_AudioFormat format;     /**< Audio data format */
    Uint8 channels;             /**< Number of channels: 1 mono, 2 stereo */
    Uint8 silence;              /**< Audio buffer silence value (calculated) */
    Uint16 samples;             /**< Audio buffer size in samples (power of 2) */
    Uint16 padding;             /**< Necessary for some compile environments */
    Uint32 size;                /**< Audio buffer size in bytes (calculated) */
    SDL_AudioCallback callback;
    void *userdata;
} SDL_AudioSpec;

主要的参数含义如下:
freq:采样率,例如44K、16K、48K等。
format:音频数据格式,常见的有如下格式:

#define AUDIO_U16SYS    AUDIO_U16LSB
#define AUDIO_S16SYS    AUDIO_S16LSB
#define AUDIO_S32SYS    AUDIO_S32LSB
#define AUDIO_F32SYS    AUDIO_F32LSB

Channels:通道数 1 单声道 2 双声道
silence:用于将缓冲区设置为静默的值,由SDL_OpenAudio()计算
Samples:音频缓冲区所需大小,必需是2的n次方
Size:音频缓冲区的大小
Callback:音频回调函数
userdata:作为回调函数的第一个参数传递。

其中,音频回调函数的原型为:

typedef void (SDLCALL * SDL_AudioCallback) (void *userdata, Uint8 * stream, int len);

当开始播放音频的时候,如果音频设备需要音频数据,就会调用此接口用来获取音频数据,
其中,stream是指向音频数据缓冲区的指针,len则为数据的长度。
另外,在SDL2中必须首先使用SDL_memset()将stream中的数据设置为0。
在这个回调函数中,既可以直接将音频数据赋给stream,也可以调用其他函数进行一些音频操作,例如调用SDL_MixAudio进行混音。

3.SDL_PauseAudio

函数原型: void SDLCALL SDL_PauseAudio(int pause_on);

这个函数用于暂停或者取消暂停音频回调处理,当我们开始播放音频的时候,需要pause_on设置为0即可,当设置为1的时候,则不会调用回调函数,即为静音状态。

三、测试代码

测试代码使用的是雷博的代码,在回调函数中没有采用混音,直接将音频数据赋值给stream。

/**
 * 最简单的SDL2播放音频的例子(SDL2播放PCM)
 * Simplest Audio Play SDL2 (SDL2 play PCM) 
 *
 * 雷霄骅 Lei Xiaohua
 * [email protected]
 * 中国传媒大学/数字电视技术
 * Communication University of China / Digital TV Technology
 * http://blog.csdn.net/leixiaohua1020
 *
 * 本程序使用SDL2播放PCM音频采样数据。SDL实际上是对底层绘图
 * API(Direct3D,OpenGL)的封装,使用起来明显简单于直接调用底层
 * API。
 *
 * 函数调用步骤如下: 
 *
 * [初始化]
 * SDL_Init(): 初始化SDL。
 * SDL_OpenAudio(): 根据参数(存储于SDL_AudioSpec)打开音频设备。
 * SDL_PauseAudio(): 播放音频数据。
 *
 * [循环播放数据]
 * SDL_Delay(): 延时等待播放完成。
 *
 * This software plays PCM raw audio data using SDL2.
 * SDL is a wrapper of low-level API (DirectSound).
 * Use SDL is much easier than directly call these low-level API.
 *
 * The process is shown as follows:
 *
 * [Init]
 * SDL_Init(): Init SDL.
 * SDL_OpenAudio(): Opens the audio device with the desired 
 *                  parameters (In SDL_AudioSpec).
 * SDL_PauseAudio(): Play Audio.
 *
 * [Loop to play data]
 * SDL_Delay(): Wait for completetion of playback.
 */

#include <stdio.h>
//#include <tchar.h>

extern "C"
{
#include "sdl/SDL.h"
};

//Buffer:
//|-----------|-------------|
//chunk-------pos---len-----|
static  Uint8  *audio_chunk; 
static  Uint32  audio_len; 
static  Uint8  *audio_pos; 

/* Audio Callback
 * The audio function callback takes the following parameters: 
 * stream: A pointer to the audio buffer to be filled 
 * len: The length (in bytes) of the audio buffer 
 * 
*/ 
void  fill_audio(void *udata,Uint8 *stream,int len){ 
    //SDL 2.0
    SDL_memset(stream, 0, len);
    if(audio_len==0)        /*  Only  play  if  we  have  data  left  */ 
            return; 
    len=(len>audio_len?audio_len:len);  /*  Mix  as  much  data  as  possible  */ 

  //SDL_MixAudio(stream,audio_pos,len,SDL_MIX_MAXVOLUME);
    memcpy(stream, audio_pos, len);
    audio_pos += len; 
    audio_len -= len; 
} 

int main(int argc, char* argv[])
{
    //Init
    if(SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER)) {  
        printf( "Could not initialize SDL - %s\n", SDL_GetError()); 
        return -1;
    }

    //SDL_AudioSpec
    SDL_AudioSpec wanted_spec;
    wanted_spec.freq = 44100; 
    wanted_spec.format = AUDIO_S16SYS; 
    wanted_spec.channels = 2; 
    wanted_spec.silence = 0; 
    wanted_spec.samples = 1024; 
    wanted_spec.callback = fill_audio; 

    if (SDL_OpenAudio(&wanted_spec, NULL)<0){ 
        printf("can't open audio.\n"); 
        return -1; 
    } 

    FILE *fp=fopen("NocturneNo2inEflat_44.1k_s16le.pcm","rb+");
    if(fp==NULL){
        printf("cannot open this file\n");
        return -1;
    }

    int pcm_buffer_size=4096;
    char *pcm_buffer=(char *)malloc(pcm_buffer_size);
    int data_count=0;

    //Play
    SDL_PauseAudio(0);

    while(1){
        if (fread(pcm_buffer, 1, pcm_buffer_size, fp) != pcm_buffer_size){
            // Loop
            fseek(fp, 0, SEEK_SET);
            fread(pcm_buffer, 1, pcm_buffer_size, fp);
            data_count=0;
        }
        printf("Now Playing %10d Bytes data.\n",data_count);
        data_count+=pcm_buffer_size;
        //Set audio buffer (PCM data)
        audio_chunk = (Uint8 *) pcm_buffer; 
        //Audio buffer length
        audio_len =pcm_buffer_size;
        audio_pos = audio_chunk;

        while(audio_len>0)//Wait until finish
            SDL_Delay(1); 
    }
    free(pcm_buffer);
    SDL_Quit();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/u011003120/article/details/81709768