Как записать качественный звук с USB-микрофона без внешних библиотек

Я изо всех сил пытаюсь захватить аудиопоток с подключенного USB-микрофона. Я пытался использовать MediaCapture с MediaRecorder.AudioSource.MIC в качестве источника, который работал, но качество записи не совсем пригодно для меня, и вы не можете быть уверены, действительно ли источником звука является USB или встроенный микрофон устройства. Мне нужно использовать функцию USB-аудио, но пока я не могу добиться какого-либо прогресса.

Использование сторонних библиотек для меня излишне, так как мне нужно только получать поток аудиоданных с микрофона, остальная обработка уже сделана и работает, проблема только в источнике звука.

Что мне нужно сделать, это:

  1. Проверьте, подключен ли к устройству USB-микрофон.
  2. Узнайте, какими характеристиками обладает это устройство (поддерживаемые частоты дискретизации, каналы и т. д.)
  3. Запись аудиоданных

Что я сделал до сих пор:

Создайте список подключенных USB-устройств класса UsbConstants.USB_CLASS_AUDIO.

private static final String ACTION_USB_PERMISSION = PACKAGE_NAME  + ".USB_PERMISSION";
private UsbManager mUsbManAndroid;
private Map<String, UsbDevice> mAndroidDeviceMap;
private PendingIntent mPermissionIntent;

private ArrayList<UsbDeviceListItem> getUSBDevicesList() {
    // Enumerate USB devices
    mAndroidDeviceMap = mUsbManAndroid.getDeviceList();
    ArrayList<UsbDeviceListItem> usbDevicesList = new ArrayList<>();

    for (String key : mAndroidDeviceMap.keySet()) {
        UsbDevice device = mAndroidDeviceMap.get(key);

        // Check the device class
        if (device.getDeviceClass() == UsbConstants.USB_CLASS_AUDIO) {
            usbDevicesList.add(usbDeviceToListItem(key, device));
        } else if (device.getDeviceClass() == UsbConstants.USB_CLASS_PER_INTERFACE) {
            UsbInterface interface;
            for (int i = 0; i < device.getInterfaceCount(); i++) {
                // Check if at least one interface is audio
                interface = device.getInterface(i);

                if (interface != null && interface.getInterfaceClass() == UsbConstants.USB_CLASS_AUDIO) {
                    usbDevicesList.add(usbDeviceToSysBusUsbDevice(key, device));
                    break;
                }
            }
        }
    }

    /////////////////////////////////////////////////////////
    // Here goes some code to identify the device using
    // linux shell commands if device SDK version is older 
    // than 21 (Lollipop). In older versions of Android
    // we can't get device's Vendor and Device names using
    // Android API, we need to use some linux shell commands.
    /////////////////////////////////////////////////////////


    return usbDevicesList;
}

Запросить разрешение для выбранного usb-устройства из списка:

 mUsbDeviceList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
     @Override
     public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
         UsbDeviceListItem usbDeviceItem = (UsbDeviceListItem) mUsbDeviceList.getItemAtPosition(i);
         UsbDevice device = mAndroidDeviceMap.get(usbDeviceItem.getDevicePath());

         manager.requestPermission(device, mPermissionIntent);
     }
 });

Разрешение вещательного приемника:

private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (ACTION_USB_PERMISSION.equals(action)) {
            synchronized (this) {
                UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

                if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                    if(device != null){
                        streamFromUsbDevice(device)
                    }
                }
                else {
                    Toast.makeText(SensorActivity.this, "Permission denied for device " + device,
                            Toast.LENGTH_SHORT).show();
                }
            }
        }
    }
};

Пример метода чтения данных с USB-устройства

private void streamFromUSBDevice(UsbDevice device) {
    UsbEndpoint endpoint;
    UsbInterface usbInterface;

    ////////////////////////////////////////////////////////////////
    // Here was code for finding the first audio interface with its
    // endpoint. But because I failed to make it work I was manually
    // getting them by index.
    ////////////////////////////////////////////////////////////////

    usbInterface = device.getInterface(2);
    endpoint = usbInterface.getEndpoint(0);

    if (endpoint == null)  {
        Log.i(TAG, getString(R.string.endpoint_not_found));
        notifyUser(R.string.endpoint_not_found);
        return;
    }

    Log.i(TAG, R.string.connecting_to_usb_device);
    notifyUser(R.string.connecting_to_usb_device);

    UsbDeviceConnection connection = manager.openDevice(device);
    connection.claimInterface(usbInterface, true);

    while (true) {
        if (!isRecording) {
            // Close the connection to the usb device
            connection.close();

            notifyUser(R.string.status_idle);                
            break;
        }

        UsbRequest request = new UsbRequest();
        request.initialize(connection, endpoint);

        byte[] buffer = new byte[endpoint.getMaxPacketSize()];

        final ByteBuffer buf = ByteBuffer.wrap(buffer);

        Log.i(TAG, "Requesting queue...");
        if (!request.queue(buf, buffer.length)) {
            Log.e(TAG, getString(R.string.error_queue_request)));
            notifyUser(R.string.error_queue_request);
            isRecording = false;
            break;
        }

        Log.i(TAG, "Requesting response...");
        final UsbRequest response = connection.requestWait();

        if (response == null) {
            Log.e(TAG, "Null response!");
            notifyUser(R.string.null_response);
            isRecording = false;
            break;
        }


        final int nRead = buf.position();

        if (nRead > 0) {
            Log.i(TAG, "Streaming " + nRead + " bytes to UI");
            Bundle args = new Bundle();
            args.putShortArray(ARG_RECORDED_DATA, byte2short(buffer));

            sendMessageToUI(SHOW_AUDIO_DATA_PACKET, args, null);
        } else {
            Log.e(TAG, "No data in buffer!");
            notifyUser(R.string_empty_buffer);

            isRecording = false;
            break;
        }
    }
}

Что я получаю от этого, так это то, что request.queue() всегда возвращает false.

Я также пытался использовать метод connection.bulkTransfer(endpoint, buffer, buffer.length, 0);, но результат всегда -1.

Если кто-то был в похожей ситуации, помогите.

P.S. Ошибка, которую я получаю в журнале: UsbRequestJNI: request is closed in native_queue.


person Marjan Slavkovski    schedule 02.12.2016    source источник
comment
Вы решили проблему?   -  person Abhinav Tyagi    schedule 08.03.2017
comment
После обновления моего проекта до API 25 и minSdkVersion до 21 я использую встроенную поддержку Android для вторичного источника звука (MIC). Это избавило меня от проблем с интеграцией любых сторонних библиотек.   -  person Marjan Slavkovski    schedule 08.03.2017
comment
Я хочу захватить звук с любого USB-входа. Будет ли приведенный выше код работать в этом состоянии?   -  person Abhinav Tyagi    schedule 08.03.2017
comment
Нет, приведенный выше код находится на совершенно неправильном пути. Вы можете использовать MediaRecorder и выбрать MIC в качестве источника. Попробуйте этот ответ: stackoverflow.com/questions/8499042/android-audiorecord -пример/   -  person Marjan Slavkovski    schedule 08.03.2017
comment
Ооо.. попробую этот подход. Спасибо   -  person Abhinav Tyagi    schedule 08.03.2017
comment
Привет, если вы можете поделиться рабочим фрагментом, это поможет   -  person Avinash Joshi    schedule 29.06.2018