1.前言

  在笔者的上篇,中篇中给你介绍了alsa库的交叉编译和alsa官网提供的几个工具的应用,在下篇中,笔者将会介绍在实际项目中的应用。所有的alsa-lib提供的api可以在官网:http://www.alsa-project.org/alsa-doc/alsa-lib/index.html 中详细介绍各种api的用法和参数说明,在http://alsa-lib.sourcearchive.com/documentation/1.0.15/files.html介绍了alsa-lib各种api之间的调用关系,思维导图画的很不错,让人一看就明白。笔者这里不详细介绍这些函数,而是介绍笔者的一个项目。

2.软件架构设计

在这套设计中,笔者将会用到aloop这个驱动,这个驱动从linux-2.6之后就引入内核,是alsa的虚拟声卡驱动。在ubuntu系统上面运行aplay -l 命令如下:

ccion@ubuntu:~$ aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: PCH [HDA Intel PCH], device 0: ALC3202 Analog [ALC3202 Analog]Subdevices: 1/1Subdevice #0: subdevice #0
card 0: PCH [HDA Intel PCH], device 3: HDMI 0 [HDMI 0]Subdevices: 1/1Subdevice #0: subdevice #0
card 0: PCH [HDA Intel PCH], device 7: HDMI 1 [HDMI 1]Subdevices: 1/1Subdevice #0: subdevice #0
card 0: PCH [HDA Intel PCH], device 8: HDMI 2 [HDMI 2]Subdevices: 1/1Subdevice #0: subdevice #0
card 1: Loopback [Loopback], device 0: Loopback PCM [Loopback PCM]Subdevices: 8/8Subdevice #0: subdevice #0Subdevice #1: subdevice #1Subdevice #2: subdevice #2Subdevice #3: subdevice #3Subdevice #4: subdevice #4Subdevice #5: subdevice #5Subdevice #6: subdevice #6Subdevice #7: subdevice #7
card 1: Loopback [Loopback], device 1: Loopback PCM [Loopback PCM]Subdevices: 8/8Subdevice #0: subdevice #0Subdevice #1: subdevice #1Subdevice #2: subdevice #2Subdevice #3: subdevice #3Subdevice #4: subdevice #4Subdevice #5: subdevice #5Subdevice #6: subdevice #6Subdevice #7: subdevice #7

可以看到,card1就是aloop生成的虚拟声卡,总共提供了8个虚拟声卡。如果你的ubuntu系统中没有,请运行命令:

ccion@ubuntu:~$ sudo modprobe snd_aloop

在开发板中,在内核的编译选项中打开:CONFIG_SND_ALOOP=m 将其以模块的形式编进内核,然后通过modprobe来注册进内核。整个软件的音频路劲如下图:

解释:1.其中hw:1,0,0和hw:1,0,1是aloop提供的虚拟声卡,它们是一起的,你可以将其看做管道类似的东西,从这端扔数据进去,从另外一端出来。当然hw:1,0,0和hw:1,0,1既可以做capture也能做playback,但不能二者同时做一样的功能,当hw:1,0,0做playback模式,hw:1,0,1就作为capture功能了。

   2.从hw:1,0,1到真实的物理声卡会经过capture(开一个线程),然后在放入ringfifo中,最后通过playback(开一个线程)来将声音送入真实的物理声卡。关于声音怎么从上层流入到底层,请参考我的博客《Linux ALSA音频系统:声卡》一文。

  整体设计大概就是这样,这跟之前的有些aplay应用不一样,二个线程capture和playback是会一直工作,即playback在这里当音频播放完后,这里不会调用snd_pcm_close()函数,将声卡关闭,然后当音频数据来的时候,在次通过snd_pcm_open()来打开声卡。在aplay线程的部分应用中,你可以做EQ等等的处理。接下来就是放源码了,不多解释。

3.源码

首先是主程序部分alsa.c

#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
#include <pthread.h>#include "alsa.h"pcm_dev_t  tp_read_node;
pcm_dev_t  tp_write_node;static int snd_pcm_dev_capture_init(pcm_dev_t *pcm_dev)
{int dir;int err;int write_dir;unsigned int val;snd_pcm_uframes_t frames;snd_pcm_uframes_t  periodSize = PERIOD_SIZE;snd_pcm_uframes_t  bufferSize = BUFFER_SIZE;snd_pcm_uframes_t write_final_buffer;snd_pcm_uframes_t write_final_period;if(pcm_dev == NULL){printf("pcm_dev null\n");goto error_pcm;}/* open PCM device for recording (capture) */err = snd_pcm_open(&pcm_dev->handle, pcm_dev->dev, pcm_dev->mode, 0);if(err){printf("Unable to open capture PCM device!\n");goto error_pcm;}/* Allocate a hardware parameters object*/snd_pcm_hw_params_alloca(&pcm_dev->params);snd_pcm_hw_params_any(pcm_dev->handle,pcm_dev->params);err = snd_pcm_hw_params_set_access(pcm_dev->handle, pcm_dev->params, SND_PCM_ACCESS_RW_INTERLEAVED);if(err){printf("Error setting interleaved mode\n");goto error_pcm;}//set formaterr = snd_pcm_hw_params_set_format(pcm_dev->handle, pcm_dev->params, pcm_dev->formats);if(err){printf("Error setting format:%s\n",snd_strerror(err));goto error_pcm;}//set channelserr = snd_pcm_hw_params_set_channels(pcm_dev->handle, pcm_dev->params , pcm_dev->channels);if(err){printf("Error setting channels:%s\n",snd_strerror(err));goto error_pcm;}//set rateerr = snd_pcm_hw_params_set_rate_near(pcm_dev->handle,pcm_dev->params, &pcm_dev->rate, &dir);if(err){printf("Error setting sampling rate (%d):%s\n",pcm_dev->rate, snd_strerror(err));goto error_pcm;}//set buffererr = snd_pcm_hw_params_set_buffer_size_near(pcm_dev->handle,pcm_dev->params, &bufferSize);if(err){printf("Error setting buffer size (%d) :%s\n",bufferSize,snd_strerror(err));goto error_pcm;}//set perioderr = snd_pcm_hw_params_set_period_size_near(pcm_dev->handle,pcm_dev->params, &periodSize, 0);if(err){printf("Error setting period time (%d) :%s\n",periodSize, snd_strerror(err));goto error_pcm;}/*write the parameters to the driver */err = snd_pcm_hw_params(pcm_dev->handle,pcm_dev->params);if(err < 0){printf("Unable to set HW parameters:%s\n",snd_strerror(err));goto error_pcm;}return 1;
error_pcm:if(pcm_dev->handle) snd_pcm_close(pcm_dev->handle);return 0;
}static int snd_pcm_dev_playback_init(pcm_dev_t *pcm_dev)
{int dir;int err;int write_dir;unsigned int  val;snd_pcm_uframes_t  frames;snd_pcm_uframes_t  periodSize = PERIOD_SIZE;snd_pcm_uframes_t  bufferSize = BUFFER_SIZE;snd_pcm_uframes_t  write_final_buffer;snd_pcm_uframes_t  write_final_period;if(pcm_dev == NULL){printf("pcm_dev null\n");goto  error_pcm;}/*open  playback dev */err = snd_pcm_open(&pcm_dev->handle, pcm_dev->dev, pcm_dev->mode, 0);if(err){printf("Unable to open capture PCM device!\n");goto error_pcm;}snd_pcm_hw_params_alloca(&pcm_dev->params);snd_pcm_hw_params_any(pcm_dev->handle , pcm_dev->params);err = snd_pcm_hw_params_set_access(pcm_dev->handle, pcm_dev->params, SND_PCM_ACCESS_RW_INTERLEAVED);if(err){printf("Error setting interleaved mode\n");goto error_pcm;}//set formaterr = snd_pcm_hw_params_set_format(pcm_dev->handle, pcm_dev->params, pcm_dev->formats);if(err){printf("Error setting format:%s\n",snd_strerror(err));goto error_pcm;}//set channelserr = snd_pcm_hw_params_set_channels(pcm_dev->handle, pcm_dev->params , pcm_dev->channels);if(err){printf("Error setting channels:%s\n",snd_strerror(err));goto error_pcm;}//set rateerr = snd_pcm_hw_params_set_rate_near(pcm_dev->handle,pcm_dev->params, &pcm_dev->rate, &dir);if(err){printf("Error setting sampling rate (%d):%s\n",pcm_dev->rate, snd_strerror(err));goto error_pcm;}//set buffererr = snd_pcm_hw_params_set_buffer_size_near(pcm_dev->handle,pcm_dev->params, &bufferSize);if(err){printf("Error setting buffer size (%d) :%s\n",bufferSize,snd_strerror(err));goto error_pcm;}//set perioderr = snd_pcm_hw_params_set_period_size_near(pcm_dev->handle,pcm_dev->params, &periodSize, 0);if(err){printf("Error setting period time (%d) :%s\n",periodSize, snd_strerror(err));goto error_pcm;}//get final buffererr = snd_pcm_hw_params_get_buffer_size(pcm_dev->params, &write_final_buffer);printf("final buffer size :%ld \n",write_final_buffer);//get final perioderr = snd_pcm_hw_params_get_period_size(pcm_dev->params, &write_final_period,&write_dir);printf("final period size :%ld \n",write_final_period);/*attach sound for control */snd_mixer_selem_id_alloca(&pcm_dev->sid);if((err = snd_mixer_open(&pcm_dev->mixer_handle,0)) < 0){printf("mixer open err\n");}if((err = snd_mixer_attach(pcm_dev->mixer_handle,pcm_dev->card) <0)){printf("snd_attach error\n");}if((err = snd_mixer_selem_register(pcm_dev->mixer_handle, NULL, NULL)) < 0 ){printf("mixer register error\n");}err = snd_mixer_load(pcm_dev->mixer_handle);if(err == 0 ){printf("mixer load error\n");goto error_pcm;}/*write the parameters to the driver */err = snd_pcm_hw_params(pcm_dev->handle,pcm_dev->params);if(err < 0){printf("Unable to set HW parameters:%s\n",snd_strerror(err));goto error_pcm;}return 1;error_pcm:if(pcm_dev->handle) snd_pcm_close(pcm_dev->handle);if(pcm_dev->mixer_handle) snd_mixer_close(pcm_dev->mixer_handle);return 0;}
static int xrun_recovery(snd_pcm_t *handle, int err){if(err == -EPIPE){/*under-run*/err = snd_pcm_prepare(handle);if(err<0)printf("can't recovery from underrun ,prepare failed:%s\n",snd_strerror(err));return 0;}else if (err == -ESTRPIPE){while((err = snd_pcm_resume(handle)) == -EAGAIN)sleep(1);if(err < 0){err = snd_pcm_prepare(handle);if(err <0)printf("can't recovery from suspend,prepare failed:%s\n",snd_strerror(err));}return 0;}return err;
}
int pcm_dev_check_status(pcm_dev_t *pcm_dev){int  in;char line[255];int count = pcm_dev->count, cnt=pcm_dev->cnt;int last_running = pcm_dev->last_running;if (count++%cnt != 0){pcm_dev->count = count;pcm_dev->cnt = cnt;return pcm_dev->is_running;}in = open(pcm_dev->cdev, O_RDONLY);if(in){int size=0;size =read(in,line,sizeof(line));if(size >0){if(!strncmp(line,"state: RUNNING",14)){pcm_dev->is_running = 1;}else {pcm_dev->is_running = 0;}}}if(pcm_dev->is_running){cnt = 20;}else{cnt = 1;}if(last_running != pcm_dev->is_running && last_running == 0){count = 1;cnt = 40;}last_running = pcm_dev->is_running;pcm_dev->last_running = last_running;close(in);pcm_dev->count = count;pcm_dev->cnt = cnt;return pcm_dev->is_running;
}void* pcm_data_queue(void* arg){int16_t size;int16_t buffer[READ_FRAME*4];int count = 0;int running =0;pcm_dev_t *p =(pcm_dev_t*)arg;while(1){// check each dev status  if  status = running ,readi data into ringfifo.// others   break  this loop ,jump other one loop.// usleep  = 20ms (there someting wrong  when the time = 10 ms)running = pcm_dev_check_status(p);if(!running){usleep(5000);continue;}//memset(&buffer,0,READ_FRAME*2);size = snd_pcm_readi(p->handle,buffer, READ_FRAME*2);if (size == -EAGAIN){continue;}if(size <0){if((size =snd_pcm_recover(p->handle,size,0))<0)continue;}// ringfifo  full   must  clear READ_FRAME*2 sizeif(ringfifo_is_full(&p->fifo)){ringfifo_get_none(&p->fifo, READ_FRAME*4);     if(count % 500 == 0){printf("warning: ringfifo is full !!!,--->%s\n",p->dev);}}ringfifo_put(&p->fifo,buffer,size*2);memset(buffer,0,sizeof(buffer));}
}void* audio_write_hw(void *arg)
{int streams_len;short *buffer_out;int size =0;int err;pthread_detach(pthread_self());buffer_out =(short *)malloc(READ_FRAME * 4);while(1){streams_len = ringfifo_get_entries(&tp_write_node.fifo);if(streams_len){size = ringfifo_get(&tp_write_node.fifo,buffer_out,READ_FRAME*2 );err = snd_pcm_writei(tp_write_node.handle,buffer_out,READ_FRAME);if(err == -EAGAIN)continue;if(err < 0){if(xrun_recovery(tp_write_node.handle,err) <0){err = snd_pcm_recover(tp_write_node.handle,err,0);if(err <0){printf("Error occured while writing:%s\n",snd_strerror(err));}continue;}}}else{usleep(100*1000L);continue;}}pthread_exit(NULL);  }void main()
{int ret;//init capturememset(&tp_read_node,0,sizeof(pcm_dev_t));ringfifo_init(&tp_read_node.fifo);//ringfifo inittp_read_node.mode = SND_PCM_STREAM_CAPTURE;//modetp_read_node.rate = SAMPLE_RATE_48000;//ratetp_read_node.channels = SAMPLE_CHANNELS_2;tp_read_node.formats = SND_PCM_FORMAT_S16_LE;tp_read_node.dev = "hw:1,0,1";tp_read_node.cdev = "/proc/asound/card1/pcm0p/sub0/status";snd_pcm_dev_capture_init(&tp_read_node);//init playbackmemset(&tp_write_node,0,sizeof(pcm_dev_t));tp_write_node.mode = SND_PCM_STREAM_PLAYBACK;tp_write_node.rate = SAMPLE_RATE_48000;tp_write_node.channels = SAMPLE_CHANNELS_2;tp_write_node.dev = "softvol";tp_write_node.formats = SND_PCM_FORMAT_S16_LE;tp_write_node.card = "default";snd_pcm_dev_playback_init(&tp_write_node);ret = pthread_create(&tp_read_node.au_pthreads, NULL,&pcm_data_queue,&tp_read_node);if(ret !=0){printf("Note: create pthread error\n");return;}ret = pthread_create(&tp_write_node.au_pthreads, NULL,&audio_write_hw,&tp_write_node);if(ret != 0){printf("Note: create pthread error\n");return;}while(1){sleep(1);}
}

其中:snd_pcm_dev_capture_init()是初始化capture部分,snd_pcm_dev_playback_init()是初始化playback部分。在主程序中你可以看到tp_write_node.dev = "softvol", tp_write_node.card="default",这个跟asound.conf(/etc/)有关,在之后的篇幅中,我将单独讲解一篇asound.conf,它很重要,alsa的很多接口,插件通过这个文件来配置。

然后再是alsa.h

#ifndef  __ALSA_H__
#define  __ALSA_H__
#ifdef  __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
#include <unistd.h>
#include <pthread.h>
#include "ring.h"#define SAMPLE_RATE_48000      48000
#define SAMPLE_RATE            48000
#define SAMPLE_CHANNELS_2      2
#define READ_FRAME             512
#define BUFFER_SIZE            (READ_FRAME * 8)
#define PERIOD_SIZE            (READ_FRAME * 2)typedef struct{snd_pcm_t *handle;snd_pcm_hw_params_t *params;snd_mixer_t  *mixer_handle;snd_mixer_elem_t *elem;snd_mixer_selem_id_t *sid;snd_pcm_stream_t  mode;snd_pcm_format_t formats;const char   *dev;const char   *cdev;const char   *card;unsigned  int  rate;unsigned int channels;ring_fifo_t fifo;pthread_t au_pthreads;int count;int cnt;int is_running;int last_running;}pcm_dev_t;#ifdef __cplusplus
}
#endif
#endif

再就是ring.c你直接调用就行,不需要把整个ring看懂。

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <stdint.h>#include "ring.h"int ringfifo_init(ring_fifo_t * fifo)
{fifo->head = 0;fifo->tail = 0;fifo->cur_entries = 0;fifo->max_entries = FIFO_MAX_ENTRIES;fifo->len_input = 0;fifo->len_output = 0;memset(fifo->buf, 0, fifo->max_entries * 2);pthread_mutex_init(&fifo->mutex, NULL);return 0;
}
int ringfifo_close(ring_fifo_t * fifo)
{pthread_mutex_destroy(&fifo->mutex);
//  free(fifo->buf);return 0;
}
inline long get_fifo_pos(int pos)
{return 0;
}
int ringfifo_get_none(ring_fifo_t *fifo, int len)
{int i;if(fifo->cur_entries < len)len = fifo->cur_entries;if(len == 0) return 0;pthread_mutex_lock(&fifo->mutex);for(i=0; i<len; i++){fifo->head++;if(fifo->head == fifo->max_entries)fifo->head = 0;}fifo->cur_entries -= len;fifo->len_output += len;pthread_mutex_unlock(&fifo->mutex);return len;
}
int ringfifo_get(ring_fifo_t *fifo, int16_t *buf, int len)
{int i;if(fifo->cur_entries < len)len = fifo->cur_entries;if(len == 0) return 0;pthread_mutex_lock(&fifo->mutex);for(i=0; i<len; i++){buf[i] = fifo->buf[fifo->head++];if(fifo->head == fifo->max_entries)fifo->head = 0;}fifo->cur_entries -= len;fifo->len_output += len;pthread_mutex_unlock(&fifo->mutex);return len;
}
int ringfifo_put(ring_fifo_t *fifo, int16_t *buf, int len)
{int i, residue = fifo->max_entries - fifo->cur_entries;if(len > residue) len = residue;pthread_mutex_lock(&fifo->mutex);if(len == 1){fifo->buf[fifo->tail++] = buf[0];if(fifo->tail == fifo->max_entries)fifo->tail = 0;}else{for(i=0; i<len; i++){fifo->buf[fifo->tail++] = buf[i];if(fifo->tail == fifo->max_entries)fifo->tail = 0; }}fifo->cur_entries += len;fifo->len_input += len;pthread_mutex_unlock(&fifo->mutex);return len;
}
int ringfifo_put_mix(ring_fifo_t *fifo, int16_t *buf, int len)
{int i, residue = fifo->max_entries - fifo->cur_entries;if(len > residue) len = residue;pthread_mutex_lock(&fifo->mutex);if(fifo->cur_entries != 0){int flag = 0;int pos = fifo->head;for(i=0; i<len; i++){if(pos == fifo->tail) flag = 1;if(!flag)fifo->buf[pos++] += buf[i];elsefifo->buf[pos++] = buf[i];if(pos == fifo->max_entries)pos = 0; }fifo->tail = (len > fifo->cur_entries) ? (fifo->head + len): fifo->tail;if(fifo->tail >= fifo->max_entries) fifo->tail -= fifo->max_entries;fifo->cur_entries = (len > fifo->cur_entries) ? len : fifo->cur_entries;}else{if(len == 1){fifo->buf[fifo->tail++] = buf[0];if(fifo->tail == fifo->max_entries)fifo->tail = 0;}else{for(i=0; i<len; i++){fifo->buf[fifo->tail++] = buf[i];if(fifo->tail == fifo->max_entries)fifo->tail = 0; }}fifo->cur_entries += len;}pthread_mutex_unlock(&fifo->mutex);return len;
}int ringfifo_is_full(ring_fifo_t *fifo){int full = 0;pthread_mutex_lock(&fifo->mutex);if(fifo->cur_entries == fifo->max_entries){full = 1;}pthread_mutex_unlock(&fifo->mutex);return full;
}
int ringfifo_get_entries(ring_fifo_t *fifo){int entries;pthread_mutex_lock(&fifo->mutex);entries = fifo->cur_entries;pthread_mutex_unlock(&fifo->mutex);return entries;
}int ringfifo_test(void)
{
#define DATA_LEN 1024int i, j, len, index = 0, put, get;int16_t buf[DATA_LEN], data[48];ring_fifo_t fifo;ringfifo_init(&fifo);for(i=0; i< DATA_LEN; i++)buf[i] = i;put = 8;get = 4;for(i=0; i<100; i++){printf("\ncount:%d => ", i);if(fifo.cur_entries < fifo.max_entries){if((index + put) < DATA_LEN){len = ringfifo_put(&fifo, &buf[index], put);index += len;printf("put[%d] ", len);}}len = ringfifo_get(&fifo, data, get);printf("get: ");for(j=0; j<len ; j++)printf("%d ", data[j]); }ringfifo_close(&fifo);printf("\n\nRing FiFo Test End!!!\n");exit(1);return 0;
}
#ifndef __RING_H__
#define __RING_H__
#ifdef __cplusplus
extern "C" {
#endif#include <stdint.h>
#include <pthread.h>//#define FIFO_MAX_ENTRIES (1024*1024)
#define FIFO_MAX_ENTRIES (200*1024)
//#define FIFO_MAX_ENTRIES (64)
/* Ring FiFo */
typedef struct ring_fifo {
int head;
int tail;
int cur_entries;
int max_entries;
int len_input;
int len_output;
int data_ready;
int data_type;
int data_size;
int data_index;
int data_offset;
int data_pos;
pthread_mutex_t mutex;
int16_t buf[FIFO_MAX_ENTRIES];
} ring_fifo_t;int ringfifo_init(ring_fifo_t * fifo);
int ringfifo_close(ring_fifo_t * fifo);
int ringfifo_get(ring_fifo_t *fifo, int16_t *buf, int len);
int ringfifo_put(ring_fifo_t *fifo, int16_t *buf, int len);
int ringfifo_put_mix(ring_fifo_t *fifo, int16_t *buf, int len);
int ringfifo_get_entries(ring_fifo_t *fifo);
int ringfifo_is_full(ring_fifo_t *fifo);
int ringfifo_get_none(ring_fifo_t *fifo, int len);
#ifdef __cplusplus
}
#endif
#endif

最后,基于笔者的开发平台的makefile.

TOPDIR=~/work/muno/
CUR_DIR=$(dirname $0)ifdef mips
CROSS_COMPILE=mipsel-linux-
CFLAGS = -DCONFIG_MIPS
endififdef arm
CROSS_COMPILE=arm-linux-androideabi-
CFLAGS = -DCONFIG_ARM
endififdef armhf
CROSS_COMPILE=arm-linux-gnueabihf-
CFLAGS = -DCONFIG_ARM
endififdef aarch64
CROSS_COMPILE=aarch64-linux-gnu-
CFLAGS = -DCONFIG_ARM
endifCC = ${CROSS_COMPILE}gcc
CXX = ${CROSS_COMPILE}g++
LIBS =CFLAGS += -O2
CPPFLAGS += $(CFLAGS) -std=c++11
LDFLAGS += -lasound -lpthread INC=OBJECTS = $(SOURCES:.cpp=.o)
PRJOBJS = alsa.o ring.o
OBJECTS_INC = ring.h alsa.hPRJNAME = alsa_testall: $(PRJNAME) $(PRJNAME):$(OBJECTS) $(PRJOBJS) $(OBJECTS_INC)$(CXX) -o $@ $(LDFLAGS) $(OBJECTS) $(PRJOBJS) $(LIBS)
.c.o:$(CC)  $(INC) $(CFLAGS) -c -o $@ $<.cpp.o:$(CXX)  $(INC) $(CPPFLAGS) -c -o $@ $<clean:rm -rf $(OBJECTS) $(PRJOBJS) $(PRJNAME) *.so *.o

通过运行make armhf=1.笔者的电脑上面得到,alsa_test的可执行程序。

ccion@ubuntu:~/ccion/alsa/test$ make armhf=1
arm-linux-gnueabihf-gcc   -DCONFIG_ARM -O2 -c -o alsa.o alsa.c
arm-linux-gnueabihf-gcc   -DCONFIG_ARM -O2 -c -o ring.o ring.c
arm-linux-gnueabihf-g++ -o alsa_test -lasound -lpthread   alsa.o ring.o
ccion@ubuntu:~/ccion/alsa/test$ ls
alsa.c  alsa.h  alsa.o  alsa_test  Makefile  ring.c  ring.h  ring.o

Alsa 调试下篇:应用篇相关推荐

  1. js 延迟几秒执行ifarme_Node.js调试之llnode篇

    概述:本文简单介绍一下如何使用llnode来分析Node应用挂掉之后产生的Core dump文件,帮助您快速定位问题. Core dump 首先介绍一下什么是Core dump,根据维基百科,它是应用 ...

  2. 基于Ardupilot/PX4固件,APM/PIXhawk硬件的VTOL垂直起降固定翼软硬件参数调试(第二篇)软硬件参数调试

    基于Ardupilot/PX4固件,APM/PIXhawk硬件的VTOL垂直起降固定翼软硬件参数调试(第二篇)软硬件参数调试 本文内容大部分来自Kris,我们的K大,在VTOL领域的大牛,在此,非常感 ...

  3. 基于Ardupilot/PX4固件,APM/PIXhawk硬件的VTOL垂直起降固定翼软硬件参数调试(第一篇)安装调试

    基于Ardupilot/PX4固件,APM/PIXhawk硬件的VTOL垂直起降固定翼软硬件参数调试(第一篇)安装调试 本文内容大部分来自Kris,我们的K大,在VTOL领域的大牛,在此,非常感谢K大 ...

  4. 最全Pycharm教程(10)——Pycharm调试器总篇

    最全Pycharm教程(1)--定制外观 最全Pycharm教程(2)--代码风格 最全Pycharm教程(3)--代码的调试.执行 最全Pycharm教程(4)--有关Python解释器的相关配置 ...

  5. 佛祖保佑,永无 BUG,永不修改 | KEIL 调试系列总结篇

    本篇将总结前面关于KEIL调试系列文章,方便后来人系统查看文章. 在此之前,请看以下视频: (视频请看原文链接) 这个视频展示了鱼鹰平时是如何运用前面的KEIL调试方法进行调试的. 为了让自己能够在遗 ...

  6. IntelliJ IDEA-Debug断点调试 看这篇文章就够了

    详解IntelliJ IDEA-Debug断点调试 如今,IntelliJ IDEA 目前深受广大开发者喜爱,我们在实际开发工作中,不管是用来阅读源码还是在开发过程中都需要进行代码调试. 以下为大家准 ...

  7. 有 Bug 不会调试 ? 这篇文章很详细 !

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:bojiangzhou 出处:http://www.cnbl ...

  8. 高通Android平台硬件调试之Camera篇

    之前一段时间有幸在高通android平台上调试2款camera sensor,一款是OV的5M YUV sensor,支持jpeg out,同时也支持AF,调试比较比较简单,因为别的项目已经在使用了, ...

  9. JavaScript/VBScript脚本程序调试(Wscript篇)

    在实际工作中,我发现程序员对脚本抱怨最多的就是脚本程序很难调试这个缺点,特别是调试.vbs等WSH程序的时候,总是: 1. 在资源管理器里面双击一个.vbs文件. 2. 程序里面发生了一个错误,例如异 ...

  10. 基于 webdriver 的测试代码日常调试方python 篇

    看到论坛有人写了JAVA的测试代码日常设计,就给大家分享一下偶自己平时是如何测试测试 代码的.主要基于python语言.基于 webdriver 的日常调试在 python交互模式下非常方便, 打开p ...

最新文章

  1. Solr部署如何启动
  2. oracle netca 乱码,Oracle 11g 安装及netca,dbca乱码之解决
  3. .NET开发辅助工具-ANTS Performance Profiler【转载】
  4. 【视频版】有一种机房叫处女座的机房。。。
  5. win10安装tensorflow (cpu版)
  6. 如何利用火狐获取网址中的提交链接
  7. 第1章 ASP.NET 3.5与开发工具
  8. 设计模式之Flyweight模式(笔记)
  9. Sql loader使用教程
  10. c语言程序求对称矩阵,C中使用CBLAS/LAPACK的对称矩阵求逆
  11. Linux系统装intel网卡,在Centos下安装intel网卡的方法
  12. php中eregi,PHP 函数 eregi()
  13. 泛微oa流程表单之流程阻止提交
  14. 丧心病狂的外挂:透视穿墙,带老板坐飞机,打不过就炸房
  15. 超级简单的Python爬虫教程,python爬虫菜鸟教程官网
  16. Javascript:ES6-ES11(1)
  17. android 模拟摄像头,在安卓模拟器中使用 VCam
  18. Java程序:如何将句子(字符串)中的字母大小写转化
  19. 用Unity进行网络游戏开发(一)
  20. 小西的快乐乘法表(想不到怎么做到同时输入两个数,并同时输出两张乘法表,求助)

热门文章

  1. 端口和网络安全的关系
  2. 京东白条技术架构进化分享,这篇总算是讲清楚了 ~
  3. 开发流程与管理--《10人以下小团队管理手册》读后有感
  4. 管理小故事100例1
  5. android 谷歌上传appid,Android之获取AppId
  6. uva10066-双塔
  7. C++基础笔记——汇总版(上)
  8. 【科研人应该知道的网站】查阅文献+学习+代码+开发+其他——研究生必备学习网站,研究生应该知道的学习网站
  9. 联想电脑win11修改默认浏览器的方法
  10. 计算机毕业设计(附源码)python在线答题系统