有关重写QIODevice类readData函数来向声卡传递音频数据播放音乐的主题素材

自小编是根据这么些博客上边来做的效率须要笔者一只获取数据一边播放,现在播发什么的都平常,可是遭逢叁个十分不常才会现身的BUGBUG:有的时候候播放过众多首音乐后会造成噪音输出,有的时候候刚播放第一首一局地后也会产生噪音输出,等那首音乐广播完,播放下一首又会变健康了,反正很不常冒出那几个BUG,符合规律处境下不大概再次出现那么些BUG。不过在debug情势下在MyDevice::readData(char*data,qint64maxlen卡塔尔中打断点,然后去掉断点继续播放,多来一遍也会成为噪音,data_pcm中一定是有数据的,假使再持续打断点去掉断点操作一遍又成为健康音乐广播了,不领会是哪些来头?有未有大神扶助看看这几个难点重写QIODevice类中readData函数,data_pcm为音频数据,memcpy到声卡缓存中qint64MyDevice::readData(char*data,qint64maxlen卡塔尔(قطر‎//data为声卡的数码缓冲区地址,maxlen为声卡缓冲区最大能贮存的字节数{if(len_written=data_pcm.size(卡塔尔卡塔尔return0;intlen;//总括未播放的数量的长度len=(len_written+maxlen)data_pcm.size()?(data_pcm.size()-len_written):maxlen;memcpy(data,data_pcm.data()+len_written,len卡塔尔国;//把要播放的pcm数据存入声卡缓冲区里len_written+=len;//更新已播放的数量长度returnlen;}

SDL播放音频的流水线

SDL播放音频的流程狠轻松,分为以下步骤。

1. 初始化

1) 初始化SDL。

2State of Qatar 依据参数(SDL_奥迪oSpec)展开音频设备

2. 生生不息播放数据

1State of Qatar 播放音频数据。

2卡塔尔国 延时等待播放实现。

上边详细解析一下上文流程。

运用无锁队列(环形缓冲区)注意事项,队列环形

环形缓冲区是临盆者和客商模型中常用的数据布局。临蓐者将数据归入数组的尾端,而消费者从数组的另一端移走多少,当达到数组的尾巴部分时,临盆者绕回到数组的头顶。要是独有一个劳动者和三个买主,那么就能够做到免锁访谈环形缓冲区(Ring Buffer)。写入索引只允许分娩者访谈并改革,只要写入者在创新索引以前将新的值保存到缓冲区中,则读者将一直见到相仿的数据布局。同理,读取索引也只同意顾客访谈并改革。

环形缓冲区完毕原理图

                       图片 1

如图所示,当读者和写者指针相等时,声明缓冲区是空的,而一旦写入指针在读取指针前边时,注脚缓冲区已满。

清单 9. 2.6.10 环形缓冲区实今世码

 /*

 * __kfifo_put - puts some data into the FIFO, no locking version

 * Note that with only one concurrent reader and one concurrent

 * writer, you don't need extra locking to use these functions.

 */

 unsigned int __kfifo_put(struct kfifo *fifo,

       unsigned char *buffer, unsigned int len)

 {

  unsigned int l;

  len = min(len, fifo->size - fifo->in + fifo->out);

  /* first put the data starting from fifo->in to buffer end */

  l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));

  memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l);

  /* then put the rest (if any) at the beginning of the buffer */

  memcpy(fifo->buffer, buffer + l, len - l);

  fifo->in += len;

  return len;

 }

 

 /*

 * __kfifo_get - gets some data from the FIFO, no locking version

 * Note that with only one concurrent reader and one concurrent

 * writer, you don't need extra locking to use these functions.

 */

 unsigned int __kfifo_get(struct kfifo *fifo,

     unsigned char *buffer, unsigned int len)

 {

  unsigned int l;

  len = min(len, fifo->in - fifo->out);

  /* first get the data from fifo->out until the end of the buffer */

  l = min(len, fifo->size - (fifo->out & (fifo->size - 1)));

  memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), l);

  /* then get the rest (if any) from the beginning of the buffer */

  memcpy(buffer + l, fifo->buffer, len - l);

  fifo->out += len;

  return len;

 }

 

内需小心的是

使用ring_buffer_get(kfifo_get)或者ring_buffer_put(kfifo_put)时,假诺回到参数与传播参数len不对等时,则操作退步

 

我们定义一个

//注意student_info 共17字节 依据内部存款和储蓄器排列占24字节

typedef struct student_info

{

    uint64_t stu_id; //8个字节

    uint32_t age;  //4字节

    uint32_t score;//4字节

     char sex;//1字节

}student_info;

 

大家树立叁个环形缓冲区,里面独有64字节大小(纵然大家实在采纳时大小远不仅仅此),向在那之中反复存入24字节student_info,看有何影响

 

//打字与印刷学生新闻

void print_student_info(const student_info *stu_info)

{

    assert(stu_info);

    printf("id:%lu\t",stu_info->stu_id);

    printf("age:%u\t",stu_info->age);

     printf("sex:%d\t",stu_info->sex);

    printf("score:%u\n",stu_info->score);

}

 

student_info * get_student_info(time_t timer)

{

    student_info *stu_info = (student_info *)malloc(sizeof(student_info));

    srand(timer);

    stu_info->stu_id = 10000 + rand() % 9999;

    stu_info->age = rand() % 30;

    stu_info->score = rand() % 101;

     stu_info->sex=rand() % 2;

    print_student_info(stu_info);

    return stu_info;

}

void print_ring_buffer_len(struct ring_buffer *ring_buf)

{

     //用于打字与印刷缓冲村长度

     uint32_t ring_buf_len = 0;

     //获得已经应用缓冲村长度 size-ring_buf_len为未选择缓冲区的长短

     ring_buf_len=ring_buffer_len(ring_buf);

     printf("no use ring_buf_len:%d\n",(ring_buf->size-ring_buf_len));

}

int main(int argc, char *argv[])

{

    uint32_t size = 0;

     //用于决断存款和储蓄可能获得数据的字节数

     uint32_t oklen = 0;

    struct ring_buffer *ring_buf = NULL;

    //64字节

    size=BUFFER_SIZE;

    ring_buf = ring_buffer_alloc(size);

     printf("input student\n");

     {

         student_info *stu_info;

         student_info stu_temp;

         uint32_t student_len=sizeof(student_info);

         printf("ring_buf_len:%d\n",ring_buf->size);

         printf("student_len:%d\n",student_len);

         //那时候环形缓冲区未有数据我们去取数据当然为空

         memset(&stu_temp,0,student_len);

         oklen=ring_buffer_get(ring_buf, (void *)(&stu_temp), student_len);

         if(oklen==student_len)

         {

            printf("get student data\n");

         }

         else

         {

            printf("no student data\n");

         }

         printf("\n");

        //第三回调用时用字节甘休后还会有64-24 =40字节

         stu_info = get_student_info(976686458);

         oklen = ring_buffer_put(ring_buf, (void *)stu_info, student_len);

         if(oklen==student_len)

         {

           printf("1 put student data success\n");

         }

         else

         {

            printf("1 put student data failure\n");

         }

         print_ring_buffer_len(ring_buf);

 

         printf("\n");

         //第一回调用时用字节甘休后还可能有64-48 =16字节

         stu_info = get_student_info(976686464);

         oklen= ring_buffer_put(ring_buf, (void *)stu_info, student_len);

         if(oklen==student_len)

         {

           printf("2 put student data success\n");

         }

         else

         {

            printf("2 put student data failure\n");

         }

         print_ring_buffer_len(ring_buf);

 

         printf("\n");

         //第贰次调用时供给用字节但只有字节失利

         //把字节都写满了

        //验证了在调用__kfifo_put函数或许__kfifo_get函数时,假设回去参数与传播参数len不等于时,则操作退步

         stu_info = get_student_info(976686445);

         oklen= ring_buffer_put(ring_buf, (void *)stu_info, student_len);

         if(oklen==student_len)

         {

           printf("3 put student data success\n");

         }

         else

         {

            printf("3 put student data failure\n");

         }

         print_ring_buffer_len(ring_buf);

       

         printf("\n");

         //第五次调用时必要用字节但无字节

         ////验证了在调用__kfifo_put函数也许__kfifo_get函数时,要是回去参数与传播参数len不等于时,则操作败北

         stu_info = get_student_info(976686421);

         oklen= ring_buffer_put(ring_buf, (void *)stu_info, student_len);

         if(oklen==student_len)

         {

           printf("4 put student data success\n");

         }

         else

         {

            printf("4 put student data failure\n");

         }

         print_ring_buffer_len(ring_buf);

 

         printf("\n");

         //以后最初取学生数量之中保存了个学子数量大家取三遍看效用

         printf("output student\n");

 

         printf("\n");

         //第一遍获得数据并打字与印刷

         memset(stu_info,0,student_len);

          oklen=ring_buffer_get(ring_buf, (void *)stu_info, student_len);

         if(oklen==student_len)

         {

            print_student_info(stu_info);

           printf("1 get student data success\n");

         }

         else

         {

            printf("1 get student data failure\n");

         }

         print_ring_buffer_len(ring_buf);

 

         printf("\n");

         ////第叁遍获得数据并打字与印刷

         memset(stu_info,0,student_len);

          oklen=ring_buffer_get(ring_buf, (void *)stu_info, student_len);

         if(oklen==student_len)

         {

            print_student_info(stu_info);

           printf("2 get student data success\n");

         }

         else

         {

            printf("2 get student data failure\n");

         }

         print_ring_buffer_len(ring_buf);

 

         printf("\n");

        //第二次取得数据退步

         memset(stu_info,0,student_len);

         oklen=ring_buffer_get(ring_buf, (void *)stu_info, student_len);

         if(oklen==student_len)

         {

            print_student_info(stu_info);

           printf("3 get student data success\n");

         }

         else

         {

             printf("3 get student data failure\n");

         }

         print_ring_buffer_len(ring_buf);

 

     }

 

     return 1;

}

     图片 2

 

结论:在使用ring_buffer_get(kfifo_get)或者ring_buffer_put(kfifo_put)时,如果回去参数与传播参数len不等于时,则操作战败。代码下载:tessc.rar(

亟待注意之处:

1.唯有一个线程肩负读,另二个线程担任写的时候,数据是线程安全的。上面包车型客车得以达成是依据这些规律完结的,当有七个线程读可能五个线程写的时候,不保障数据的不易。
据此利用的时候,贰个线程写,多个线程读。网络使用中比较常用,正是开一个线程接口数据,然后把数据写入队列。然后开贰个调解线程读取互联网数据,然后分发随管理线程。

2.数目长度暗许宏定义了叁个长短,超越这些长度的时候,后续的数据会写入退步。

正文参谋文章:

 

 

1. 初始化

1) 初始化SDL。

使用SDL_Init(卡塔尔初阶化SDL。该函数能够规定希望激活的子系统。SDL_Init(State of Qatar函数原型如下:

[cpp] view plaincopy图片 3图片 4

 

  1. int SDLCALL SDL_Init(Uint32 flags)  

中间,flags能够取下列值:

SDL_INIT_TIMER:定时器
SDL_INIT_AUDIO:音频
SDL_INIT_VIDEO:视频
SDL_INIT_JOYSTICK:摇杆
SDL_INIT_HAPTIC:触摸屏
SDL_INIT_GAMECONTROLLEMurano:游戏调控器
SDL_INIT_EVENTS:事件
SDL_INIT_NOPARACHUTE:不抓获关键时限信号(这个不明了)
SDL_INIT_EVE哈弗YTHING:包括上述全体选项

 

有关SDL_Init(State of Qatar有某个索要介怀:初阶化的时候尽量做到“够用就好”,而不用用SDL_INIT_EVERAV4YTHING。因为稍微意况下行使SDL_INIT_EVEWranglerYTHING会现身成的不得预言的标题。举例,在MFC应用程序中播放纯音频,如果初叶化SDL的时候利用SDL_INIT_EVEENVISIONYTHING,那么就会见世听不到声音的图景。后来意识,去掉了SDL_INIT_VIDEO之后,难点才足以化解。

2卡塔尔国 依据参数(SDL_奥迪oSpec)展开音频设备
使用SDL_Open奥迪o(卡塔尔国张开音频设备。该函数供给传入几个SDL_奥迪(Audi卡塔尔国oSpec的布局体。DL_Open奥迪(Audi卡塔尔o(卡塔尔的原型如下。

[cpp] view plaincopy图片 5图片 6

 

  1. int SDLCALL SDL_OpenAudio(SDL_AudioSpec * desired,  
  2.                                           SDL_AudioSpec * obtained);  

它的参数是五个SDL_奥迪oSpec结构体,它们的含义:
desired:期待的参数。
obtained:实际音频设备的参数,平常景况下设置为NULL就能够。

SDL_奥迪oSpec架构体的概念如下。

[cpp] view plaincopy图片 7图片 8

 

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

中间包括了有关音频各样参数:
freq:音频数据的采集样板率。常用的有48000,44100等。
format:音频数据的格式。比如三种格式:
AUDIO_U16SYS:Unsigned 16-bit samples
AUDIO_S16SYS:Signed 16-bit samples
AUDIO_S32SYS:32-bit integer samples
AUDIO_F32SYS:32-bit floating point samples
channels:声道数。比方单声道取值为1,立体声取值为2。
silence:设置静音的值。
samples:音频缓冲区中的采集样板个数,要求必得是2的n次方。
padding:思量到兼容性的三个参数。
size:音频缓冲区的深浅,以字节为单位。
callback:填充音频缓冲区的回调函数。
userdata:顾客自定义的数据。
在这里边记录一下填充音频缓冲区的回调函数的作用。当音频设备须要更加多多少的时候会调用该回调函数。回调函数的格式必要如下。

[cpp] view plaincopy图片 9图片 10

 

  1. void (SDLCALL * SDL_AudioCallback) (void *userdata, Uint8 * stream,  
  2.                                             int len);  

回调函数的参数含义如下所示。
userdata:SDL_奥迪(Audi卡塔尔(قطر‎oSpec布局中的客户自定义数据,平时景色下得以不用。
stream:该指针指向内需填写的旋律缓冲区。
len:音频缓冲区的深浅(以字节为单位)。
在回调函数中得以行使SDL_Mix奥迪(Audi卡塔尔国o(卡塔尔国完毕混音等职业。众所周知SDL2和SDL1.x关于录制方面包车型客车API差距超大。可是SDL2和SDL1.x有关音频方面包车型大巴API是同出一辙的。唯独在回调函数中,SDL2有一个地点和SDL1.x差异等:SDL第22中学必得首先利用SDL_memset(State of Qatar将stream中的数据设置为0。

2. 循环播放数据
1卡塔尔国 播放音频数据。

使用SDL_Pause奥迪(Audi卡塔尔(قطر‎o(卡塔尔能够播放音频数据。SDL_Pause奥迪(AudiState of Qataro(卡塔尔的原型如下。

[cpp] view plaincopy图片 11图片 12

 

  1. void SDLCALL SDL_PauseAudio(int pause_on)  

当pause_on设置为0的时候就能够开首播报音频数据。设置为1的时候,将会播放静音的值。

2卡塔尔国 延时等待播放完结。
这一步正是延时等待音频播放达成了。使用像SDL_Delay(卡塔尔国那样的延时函数就能够。

由于系统缓冲区空间不足或队列已满,不得以实行套接字上的操作(1005卡塔尔,ON API \'connenct

安装一下 最大磁盘缓存 的址吧。
只要内部存款和储蓄器够的话弄大一点  

环形缓冲区是劳动者和消费者模型中常用的数据布局。分娩者将数据归入数组的尾端,而...

下载

代码坐落于“Simplest Media Play”中

SourceForge项目地址:

CSDN下载地址:

上述工程分包了接纳种种API(Direct3D,OpenGL,GDI,DirectSound,SDL2)播放多媒体例子。此中音频输入为PCM采集样板数据。输出至系统的声卡播放出来。录制输入为YUV/汉兰达GB像素数据。输出至显示屏上的二个窗口播放出来。
因而本工程的代码初读书人能够高速学习运用那多少个API播放录制和旋律的本事。
一同富含了如下多少个子工程:
simplest_audio_play_directsound:  使用DirectSound播放PCM音频采集样本数据。
simplest_audio_play_sdl2:  使用SDL2播放PCM音频采集样板数据。
simplest_video_play_direct3d:  使用Direct3D的Surface播放汉兰达GB/YUV摄像像素数据。
simplest_video_play_direct3d_texture:使用Direct3D的Texture播放奇骏GB录像像素数据。
simplest_video_play_gdi:  使用GDI播放QashqaiGB/YUV录像像素数据。
simplest_video_play_opengl:  使用OpenGL播放PAJEROGB/YUV录制像素数据。
simplest_video_play_opengl_texture: 使用OpenGL的Texture播放YUV录制像素数据。
simplest_video_play_sdl2:  使用SDL2播放奥德赛GB/YUV录制像素数据。

 

from:

环形缓冲区是哪一类缓冲区

应该归于队列把,循环队列