zoukankan      html  css  js  c++  java
  • Beginning SDL 2.0(6) 音频渲染及wav播放

    前面几篇关于SDL的文章介绍的是以画面为主,这里介绍下SDL中针对音频播放提供的机制,以及如何应用。

    对于音频而言,有几个概念需要事先了解下,采样率、声道数、量化位数,如果你不清楚的话,麻烦先了解下这几本参数的概念。

    常用的采样率有8kHz(语音、电话)、48kHz(电视),声道数常用的是双声道或5.1声道,量化位数8bit或16bit。

     SDL提供了音频设备、音频输入/输出的控制机制。

    比较常规的做法是先创建音频设备,然后按照你的实际需求进行音频输入输出。

    由于音频处理相对简单,这里仅在一个cpp中实现所有wav播放的功能。

    工程创建

     为了演示期间,我们在vs10中创建控制台程序,并包含SDL头文件和库的路径。,工程名为3_wav_play。我从网上下了一个wav音频,名字为“congtouzailai.wav”。(音量比较大的,播放的时候注意下)

    wav文件加载

    SDL提供了SDL_LoadWAV函数,用于加载wav文件,加载的代码如下:

        SDL_AudioSpec wav_spec;
        Uint32 wav_length;
        Uint8 *wav_buffer;
    
        /* Load the WAV */
        if (SDL_LoadWAV("congtouzailai.wav", &wav_spec, &wav_buffer, &wav_length) == NULL) 
        {
            fprintf(stderr, "Could not open test.wav: %s
    ", SDL_GetError());
            return -1;
        }

    函数调用成功之后,就可以获得wav文件中的音频参数,比如采样率、声道数、量化位数等,其中原始的音频数据(PCM)存储在wav_buffer中,长度为wav_length。

    创建回调参数上下文

    struct AudioPlayContext 
    {
        Uint8 * data; // pcm buffer
        int offset; // current read pos
        int data_len; // left data length
        bool is_exit; // is audio play buffer empty?
    };

    提供这个参数,只是为了提供数据从主函数到回调函数传递参数的机制。

    创建音频设备

        AudioPlayContext context;
        context.data = wav_buffer;
        context.offset = 0;
        context.data_len = wav_length;
        context.is_exit = false;
    
        // open audio device
        SDL_AudioSpec want, have;
    
        want = wav_spec;
        want.callback = MyAudioCallback;  // you wrote this function elsewhere.
        want.userdata = &context;
    
        if (SDL_OpenAudio(&want, &have) < 0) 
        {
            printf("Failed to open audio: %s
    ", SDL_GetError());
            return -2;
        }
        
        if (have.format != want.format)
            printf("We didn't get Float32 audio format.
    ");

    这里最主要的结构体SDL_AudioSpec的初始化,这里的回调函数MyAudioCallback,提供了向播放设备写入音频数据的方式。

    播放音频数据

    在主线程中,我们启动播放,并等待当前wav文件播放完成。代码如下

        SDL_PauseAudio(0);  // start audio playing.
        // wait for the end
        while(!context.is_exit)
        {
            SDL_Delay(1000);
        }

    在回调函数中我们需要将数据填充到音频设备的播放缓冲中,并设置是否播放完成的标志位,代码如下

    void MyAudioCallback(void* userdata, Uint8* stream, int len)
    {
        AudioPlayContext * context = reinterpret_cast<AudioPlayContext *>(userdata);
        int copy_len = __min(len, context->data_len);
        memcpy(stream, context->data+context->offset, copy_len);
        context->data_len -= copy_len;
        context->offset += copy_len;
        if (context->data_len <= 0)
        {
            context->is_exit = true;
        }
    
        if (copy_len < len)
        {
            memset(stream+copy_len, 0, len-copy_len);
        }
    }

    反初始化操作

    程序退出时,别忘记释放wav文件加载时的数据。

        SDL_CloseAudio();
        /* Do stuff with the WAV data, and then... */
        SDL_FreeWAV(wav_buffer);
        SDL_Quit();

    上面几个合并在一起就是最简单的wav文件播放器。

    相关代码可以从我的git下载,url如下:https://git.oschina.net/Tocy/SampleCode.git,位于TocySDL2VisualTutorial目录下。

  • 相关阅读:
    柯西恒等式 FPGA中信号的跨时钟域处理模板(二)
    OSPF
    Windows多网卡路由设置
    使用线程时需要注意的地方
    dicom 影像通信(scu、scp)的c-echo、c-store、c-find、c-move
    关于python3没有numpy和matplotlib库怎么办
    使用centos6.5时的几个小问题
    关于用Apache Beam跑WordCount
    MarkdownPad2的安装、破解和汉化
    安装Photoshop CS64
  • 原文地址:https://www.cnblogs.com/tocy/p/Beginning-SDL2-6-WAV-Player.html
Copyright © 2011-2022 走看看