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/1
Subdevice #0: subdevice #0
card 0: PCH [HDA Intel PCH], device 3: HDMI 0 [HDMI 0]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 0: PCH [HDA Intel PCH], device 7: HDMI 1 [HDMI 1]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 0: PCH [HDA Intel PCH], device 8: HDMI 2 [HDMI 2]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 1: Loopback [Loopback], device 0: Loopback PCM [Loopback PCM]
Subdevices: 8/8
Subdevice #0: subdevice #0
Subdevice #1: subdevice #1
Subdevice #2: subdevice #2
Subdevice #3: subdevice #3
Subdevice #4: subdevice #4
Subdevice #5: subdevice #5
Subdevice #6: subdevice #6
Subdevice #7: subdevice #7
card 1: Loopback [Loopback], device 1: Loopback PCM [Loopback PCM]
Subdevices: 8/8
Subdevice #0: subdevice #0
Subdevice #1: subdevice #1
Subdevice #2: subdevice #2
Subdevice #3: subdevice #3
Subdevice #4: subdevice #4
Subdevice #5: subdevice #5
Subdevice #6: subdevice #6
Subdevice #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 format
err = 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 channels
err = 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 rate
err = 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 buffer
err = 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 period
err = 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 format
err = 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 channels
err = 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 rate
err = 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 buffer
err = 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 period
err = 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 buffer
err = snd_pcm_hw_params_get_buffer_size(pcm_dev->params, &write_final_buffer);
printf("final buffer size :%ld \n",write_final_buffer);
//get final period
err = 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 size
if(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 capture
memset(&tp_read_node,0,sizeof(pcm_dev_t));
ringfifo_init(&tp_read_node.fifo);//ringfifo init
tp_read_node.mode = SND_PCM_STREAM_CAPTURE;//mode
tp_read_node.rate = SAMPLE_RATE_48000;//rate
tp_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 playback
memset(&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];
else
fifo->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 1024
int 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
endif
ifdef arm
CROSS_COMPILE=arm-linux-androideabi-
CFLAGS = -DCONFIG_ARM
endif
ifdef armhf
CROSS_COMPILE=arm-linux-gnueabihf-
CFLAGS = -DCONFIG_ARM
endif
ifdef aarch64
CROSS_COMPILE=aarch64-linux-gnu-
CFLAGS = -DCONFIG_ARM
endif
CC = ${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.h
PRJNAME = alsa_test
all: $(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