Android/linux从usb声卡获取音频(使用libusb库)---环境,lsusb命令的介绍(一)
Android/linux从usb声卡获取音频(使用libusb库)---设备环境的确认(二)
Android/linux从usb声卡获取音频(使用libusb库)---libusb库获取“纯麦”音频数据(三)
Android/linux从usb声卡获取音频(使用libusb库)---libusb库获取“纯麦”音频数据,附(四)
Android/linux从usb声卡获取音频(使用libusb库)---监听“纯麦”(五)
使用tinyalsa 在ubuntu上实时播放usb麦克风。
基于之前利用libusb 获取到的usb mic的pcm流数据,直接往alsa写,播放。
这里没有用linux系统的 alsa库,而用tinyalsa, 将tinyalsa源码嵌入到代码中,方便移植,比如用到android上去也是很方便的。
tinyalsa源码可以直接去github tinyalsa 上下载,如果有android源码,在android源码的 external/tinyalsa 就有,移植也非常简单,这里只需取三个文件即可:pcm.c mixer.c asoundlib.h, 不需要连接其他的库,把这两个.c文件copy到播放的源码一起编译,为了方便,个人参考demo自己加了一个 tinyalsa.c文件,进一步简化调用接口:
tinyalsa.c
/* 从 android 源码的tinyalsa tinyplay.c 文件改过来的 tinyalsa.c
**
** Copyright 2011, The Android Open Source Project
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of The Android Open Source Project nor the names of
** its contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
** DAMAGE.
*/
#include"tinyalsa/asoundlib.h"
//#include "asoundlib.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <signal.h>
#include "tinyalsa.h"
#define ID_RIFF 0x46464952
#define ID_WAVE 0x45564157
#define ID_FMT 0x20746d66
#define ID_DATA 0x61746164
struct riff_wave_header {
uint32_t riff_id;
uint32_t riff_sz;
uint32_t wave_id;
};
struct chunk_header {
uint32_t id;
uint32_t sz;
};
struct chunk_fmt {
uint16_t audio_format;
uint16_t num_channels;
uint32_t sample_rate;
uint32_t byte_rate;
uint16_t block_align;
uint16_t bits_per_sample;
};
//static int close = 0;
static struct pcm_config config;
static struct pcm *g_pcm= NULL;
/*
void stream_close(int sig)
{
signal(sig, SIG_IGN);
close = 1;
}*/
#if 0
int main(int argc, char **argv)
{
FILE *file;
struct riff_wave_header riff_wave_header;
struct chunk_header chunk_header;
struct chunk_fmt chunk_fmt;
unsigned int device = 0;
unsigned int card = 0;
unsigned int period_size = 1024;
unsigned int period_count = 4;
char *filename;
int more_chunks = 1;
if (argc < 2) {
fprintf(stderr, "Usage: %s file.wav [-D card] [-d device] [-p period_size]"
" [-n n_periods] \n", argv[0]);
return 1;
}
filename = argv[1];
file = fopen(filename, "rb");
if (!file) {
fprintf(stderr, "Unable to open file '%s'\n", filename);
return 1;
}
fread(&riff_wave_header, sizeof(riff_wave_header), 1, file);
if ((riff_wave_header.riff_id != ID_RIFF) ||
(riff_wave_header.wave_id != ID_WAVE)) {
fprintf(stderr, "Error: '%s' is not a riff/wave file\n", filename);
fclose(file);
return 1;
}
do {
fread(&chunk_header, sizeof(chunk_header), 1, file);
switch (chunk_header.id) {
case ID_FMT:
fread(&chunk_fmt, sizeof(chunk_fmt), 1, file);
/* If the format header is larger, skip the rest */
if (chunk_header.sz > sizeof(chunk_fmt))
fseek(file, chunk_header.sz - sizeof(chunk_fmt), SEEK_CUR);
break;
case ID_DATA:
/* Stop looking for chunks */
more_chunks = 0;
break;
default:
/* Unknown chunk, skip bytes */
fseek(file, chunk_header.sz, SEEK_CUR);
}
} while (more_chunks);
/* parse command line arguments */
argv += 2;
while (*argv) {
if (strcmp(*argv, "-d") == 0) {
argv++;
if (*argv)
device = atoi(*argv);
}
if (strcmp(*argv, "-p") == 0) {
argv++;
if (*argv)
period_size = atoi(*argv);
}
if (strcmp(*argv, "-n") == 0) {
argv++;
if (*argv)
period_count = atoi(*argv);
}
if (strcmp(*argv, "-D") == 0) {
argv++;
if (*argv)
card = atoi(*argv);
}
if (*argv)
argv++;
}
play_sample(file, card, device, chunk_fmt.num_channels, chunk_fmt.sample_rate,
chunk_fmt.bits_per_sample, period_size, period_count);
fclose(file);
return 0;
}
#endif
int check_param(struct pcm_params *params, unsigned int param, unsigned int value,
char *param_name, char *param_unit)
{
unsigned int min;
unsigned int max;
int is_within_bounds = 1;
min = pcm_params_get_min(params, param);
if (value < min) {
fprintf(stderr, "%s is %u%s, device only supports >= %u%s\n", param_name, value,
param_unit, min, param_unit);
is_within_bounds = 0;
}
max = pcm_params_get_max(params, param);
if (value > max) {
fprintf(stderr, "%s is %u%s, device only supports <= %u%s\n", param_name, value,
param_unit, max, param_unit);
is_within_bounds = 0;
}
return is_within_bounds;
}
int sample_is_playable(unsigned int card, unsigned int device, unsigned int channels,
unsigned int rate, unsigned int bits, unsigned int period_size,
unsigned int period_count)
{
struct pcm_params *params;
int can_play;
params = pcm_params_get(card, device, PCM_OUT);
if (params == NULL) {
fprintf(stderr, "Unable to open PCM device %u.\n", device);
return 0;
}
can_play = check_param(params, PCM_PARAM_RATE, rate, "Sample rate", "Hz");
can_play &= check_param(params, PCM_PARAM_CHANNELS, channels, "Sample", " channels");
can_play &= check_param(params, PCM_PARAM_SAMPLE_BITS, bits, "Bitrate", " bits");
can_play &= check_param(params, PCM_PARAM_PERIOD_SIZE, period_size, "Period size", "Hz");
can_play &= check_param(params, PCM_PARAM_PERIODS, period_count, "Period count", "Hz");
pcm_params_free(params);
return can_play;
}
static int tinyalsa_init_internal( unsigned int card, unsigned int device, unsigned int channels,
unsigned int rate, unsigned int bits, unsigned int period_size,
unsigned int period_count)
{
char *buffer;
int size;
int num_read;
memset(&config, 0, sizeof(config));
config.channels = channels;
config.rate = rate;
config.period_size = period_size;
config.period_count = period_count;
if (bits == 32)
config.format = PCM_FORMAT_S32_LE;
else if (bits == 16)
config.format = PCM_FORMAT_S16_LE;
config.start_threshold = 0;
//config.stop_threshold = 0; // 0 是默认的参数。
config.stop_threshold = 4*period_size*period_count;
config.silence_threshold = 0;
if (!sample_is_playable(card, device, channels, rate, bits, period_size, period_count)) {
return -1;
}
g_pcm = pcm_open(card, device, PCM_OUT, &config);
if (!g_pcm || !pcm_is_ready(g_pcm)) {
fprintf(stderr, "Unable to open PCM device %u (%s)\n",
device, pcm_get_error(g_pcm));
return -1;
}
size = pcm_frames_to_bytes(g_pcm, pcm_get_buffer_size(g_pcm));
buffer = malloc(size);
if (!buffer) {
fprintf(stderr, "Unable to allocate %d bytes\n", size);
free(buffer);
pcm_close(g_pcm);
return -1;
}
printf("Playing sample: %u ch, %u hz, %u bit\n", channels, rate, bits);
#if 0
/* catch ctrl-c to shutdown cleanly */
signal(SIGINT, stream_close);
do {
num_read = fread(buffer, 1, size, file);
if (num_read > 0) {
if (pcm_write(g_pcm, buffer, num_read)) {
fprintf(stderr, "Error playing sample\n");
break;
}
}
} while (!close && num_read > 0);
free(buffer);
pcm_close(pcm);
#endif
return 0;
}
int tinyalsa_init_default()
{
if(g_pcm)
{
printf("had been inited!!\n");
return -1;
}
return tinyalsa_init_internal(0, 0, 2, 48000, 16, 1024, 4);
}
int tinyalsa_init(int card, int dev, int chans, int rate, int bitformate, int period, int period_count)
{
if(g_pcm)
{
printf("had been inited!!\n");
return -1;
}
return tinyalsa_init_internal(card, dev, chans, rate, bitformate, period, period_count);
}
int tinyalsa_getFrameSize()
{
return pcm_frames_to_bytes(g_pcm, pcm_get_buffer_size(g_pcm));
}
int tinyalsa_play(const void *data, unsigned int count)
{
if(data)
{
// printf("play pcm -------- \n");
return pcm_write(g_pcm, data, count);
}
else
{
printf("prame erro\n");
return -1;
}
}
int tinyalsa_deinit()
{
pcm_close(g_pcm);
g_pcm = NULL;
return 0;
}
tinyalsa.h
#ifndef __TINYALSA__HEAD__H
#define __TINYALSA__HEAD__H
int tinyalsa_init_default();
int tinyalsa_init(int card, int dev, int chans, int rate, int bitformate, int period, int period_count);
int tinyalsa_getFrameSize();
int tinyalsa_play(const void *data, unsigned int count);
int tinyalsa_deinit();
#endif
tinyalsa中的pcm.c mixer.c asound.h
源码太长,打包下载https://download.csdn.net/download/u012459903/12054775