实现android系统通过usb麦克风采集声音功能,能够兼容多款anroid设备。
设想方案有两个:
1.采用通过libusb库,直接访问usb驱动,分析usb协议中的音频数据。
2.通过tinyalsa访问音频设备的pcm节点,通过节点直接获取音频数据。
因第二种方式音频节点id并不可控,并不能适配多款android设备,遂采用第一种方式。
通过代码调用libusb库,通过usb驱动获取数据,然后封装成jni库,供apk调用。
背景知识
每一个usb设备都有自己固定的VID和PID地址,根据这个地址可以寻找到指定的usb设备。
usb有config,然后下面有多个interface,interface下面有多个endpoint。根据interface的class和subclass值可以区分interface类型,比如video的class值是14,audio的class值是1等,根据这个可以识别复合设备的interface。然后每个interface下面有多个endpoint,endpoint存在address,这个是数据传输的通道。每个endpoint存在不同的数据格式,比如我手上的这个usb麦克风,每个endpoint对应一种格式,比如双通道/16位/48K。但也有一个endpoint对应多种格式的。
usb的传输类型有四种,分别是控制传输(Control Transfer)、中断传输(Interrupt Transfer)、批量传输(Bulk Transfer)、同步传输(Isochronous Transfer)。音频设备只用到控制传输和同步传输。
代码
1.初始化
先进行init,然后根据vid、pid查找usb设备,进行open,然后因为要使用无驱设计,需要将音频alsa和usb驱动进行解绑。最后扫描所有config,根据所需要的class找到所需的interface,进行选择设置。
int UsbAudio::open(int vid, int pid, int fd, const char *usbfs)
{
int r; //for return values
ssize_t cnt; //holding number of devices in list
printf("start open %d\n", errno);
if (mUsbFs)
free(mUsbFs);
mUsbFs = strdup(usbfs);
printf("before 11111 errno:%d\n", errno);
r = libusb_init2(&ctx, usbfs); //initialize a library session
if(r < 0) {
printf("Init Error \n"); //there was an error
return -1;
}
usb_dev = libusb_find_device(ctx, vid, pid, NULL, fd);
if (usb_dev) {
libusb_set_device_fd(usb_dev, fd); // assign fd to libusb_device for non-rooted Android devices
libusb_ref_device(usb_dev);
}
printf("before 444444 errno:%d\n", errno);
r = libusb_open(usb_dev, &dev_handle);
if(r != LIBUSB_SUCCESS)
{
printf("open device err %d\n", errno);
return -1;
}
//libusb_reset_device(dev_handle);
printf("before scan_audio_interface errno:%d\n", errno);
if (scan_audio_interface(usb_dev) < 0)
{
printf("scan_audio_interface err: errno:%d\n", errno);
cancel();
return -1;
}
printf("before interface_claim_if errno:%d\n", errno);
interface_claim_if(dev_handle, interfaceNumber);
}
int UsbAudio::interface_claim_if(libusb_device_handle *dev, int interface_number)
{
int r = 0;
r = libusb_kernel_driver_active(dev, interface_number);
printf("libusb_kernel_driver_active2 %d\n", r);
if(r == 1)
{ //find out if kernel driver is attached
printf("Kernel Driver Active\n");
if(libusb_detach_kernel_driver(dev, interface_number) == 0) //detach it
printf("Kernel Driver Detached!\n&#