записывать/сохранять аудио из намерения распознавания голоса

Прежде чем задать этот вопрос, я безуспешно проверил все потоки stackoverflow, связанные с этой проблемой, поэтому, пожалуйста, не отвечайте ссылками на другие потоки :)

Я хочу сохранить/записать аудио, которое служба распознавания Google использовала для преобразования речи в текст (используя RecognizerIntent или SpeechRecognizer).

Я испытал много идей:

  1. onBufferReceived от RecognitionListener: я знаю, что это не работает, просто проверьте, что происходит, и onBufferReceived никогда не вызывается (проверено на галактике нексус с JB 4.3)
  2. использовал медиа-рекордер: не работает. это нарушает распознавание речи. для микрофона разрешена только одна операция
  3. пытался найти, где служба распознавания сохраняет временный аудиофайл перед выполнением речи в текстовый API, чтобы скопировать его, но безуспешно

Я был почти в отчаянии, но только что заметил, что приложение Google Keep делает то, что мне нужно!! !! Я немного отладил приложение keep с помощью logcat, и приложение также вызывает «RecognizerIntent.ACTION_RECOGNIZE_SPEECH» (как это делаем мы, разработчики), чтобы инициировать преобразование речи в текст. но как сохранить звук? может это скрыть апи? Google "обманывает" :) ?

Спасибо за помощь

С наилучшими пожеланиями


person Slim    schedule 13.04.2014    source источник


Ответы (4)


Ответ @Kaarel почти завершен - результирующий звук находится в intent.getData() и может быть прочитан с помощью ContentResolver

К сожалению, возвращенный файл AMR имеет низкое качество — мне не удалось найти способ получить высококачественную запись. Любое значение, которое я пробовал, кроме «audio/AMR», возвращало значение null в intent.getData().

Если вы найдете способ получить качественную запись - пожалуйста, прокомментируйте или добавьте ответ!

public void startSpeechRecognition() {
   // Fire an intent to start the speech recognition activity.
   Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
   // secret parameters that when added provide audio url in the result
   intent.putExtra("android.speech.extra.GET_AUDIO_FORMAT", "audio/AMR");
   intent.putExtra("android.speech.extra.GET_AUDIO", true);

   startActivityForResult(intent, "<some code you choose>");
}

// handle result of speech recognition
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    // the resulting text is in the getExtras:
    Bundle bundle = data.getExtras();
    ArrayList<String> matches = bundle.getStringArrayList(RecognizerIntent.EXTRA_RESULTS)
    // the recording url is in getData:
    Uri audioUri = data.getData();
    ContentResolver contentResolver = getContentResolver();
    InputStream filestream = contentResolver.openInputStream(audioUri);
    // TODO: read audio file from inputstream
}
person Iftah    schedule 25.06.2014
comment
Это может быть очень долгая съемка, но..., я заставил это работать. Однако он открывает диалоговое окно для разговора, которое я получил, внедрив RecognitionListener, однако public void onResults (результаты Bundle), поскольку я переопределяю, не содержат Intent, и я не могу найти какой-либо способ получить доступ к Intent, чтобы я мог не получить URI. - person Fredrik; 10.02.2016
comment
@fredrik, это также серьезная проблема для меня. Использование onBufferReceived(byte[] buffer) не кажется подходящим способом, основываясь на документации. Удалось ли вам обойти это? - person nonybrighto; 07.07.2016
comment
@nonybrighto жаль говорить, что на данный момент нет законного способа сделать это - person Fredrik; 07.07.2016
comment
вау.. большое спасибо. Я даже пытался использовать Google Voice API V2 в качестве обходного пути для достижения того, что мне нужно, но, похоже, он больше не работает. @fredrik - person nonybrighto; 08.07.2016
comment
Я попробовал это, и это больше не работает. Когда я добавляю эти секретные параметры, он даже не показывает диалоговое окно для распознавания речи. Возможно, этот хак работал на старых версиях SDK. Есть идеи по этому поводу? - person Rahul Bansal; 29.08.2018
comment
дальнейший перевод будет таким: InputStream filestream = contentResolver.openInputStream(audioUri); байт [] буфер = новый байт [файловый поток.доступный()]; файлпоток.чтение(буфер); OutputStream outStream = новый FileOutputStream (аудиофайл); outStream.write(буфер); Пожалуйста, убедитесь, что у вас будет дескриптор файла, названный здесь как аудиофайл. - person Abhishek Chudekar; 06.03.2019
comment
Не забудьте установить разрешение RECORD_AUDIO. Обычное распознавание речи работает без этого разрешения, но если вы хотите получить звук, вам необходимо разрешение RECORD_AUDIO. - person Andrey Epifantsev; 30.05.2020
comment
@AndreyEpifantsev, но после всего этого звук, записанный с помощью речевого намерения, недоступен из-за проблемы с разрешением. - person Haider Saleem; 04.06.2020
comment
@Haider Saleem Я использую RecognizerIntent для распознавания речи пользователей и, по крайней мере, могу воспроизвести его/ее речь с помощью MediaPlayer. - person Andrey Epifantsev; 05.06.2020

В прошлый раз, когда я проверял, Google Keep установил следующие дополнения:

  • android.speech.extra.GET_AUDIO_FORMAT: аудио/AMR
  • android.speech.extra.GET_AUDIO: правда

Они не задокументированы как часть документации Android, поэтому они не представляют собой Android API. Кроме того, Google Keep не полагается на намерение распознавателя учитывать эти дополнительные функции. Конечно, было бы неплохо, если бы такие дополнения были популяризированы и задокументированы Google.

Чтобы узнать, какие дополнения устанавливает Google Keep при вызове RecognizerIntent, создайте приложение, которое отвечает на RecognizerIntent, и распечатайте все полученные дополнения. Вы также можете установить Kõnele (http://kaljurand.github.io/K6nele/), который является реализацией RecognizerIntent. Когда Kõnele запускается Google Keep, нажмите и удерживайте значок настроек в форме гаечного ключа. Это показывает некоторые технические подробности о вызывающем абоненте, а также включает входящие дополнения.

Ответ @Iftah объясняет, как Google Keep возвращает аудиозапись вызывающему абоненту RecognizerIntent.

person Kaarel    schedule 14.04.2014
comment
как вы узнали, что Keep устанавливает эти дополнения? - person Slim; 15.04.2014
comment
спасибо за ваши ответы. Я реализовал то, что вы предложили, и вы правы, Google Keep запускает RecognizerIntent только с упомянутыми дополнениями. Я попытался запустить RecognizerIntent с теми же дополнениями, что и Google Keep, но полученный интент не содержит никаких дополнительных дополнений!!!! Как поживает Google, можем ли мы запросить информацию в официальном трекере Android? Если кто-то из сотрудников Google читает это, не могли бы вы нам помочь? Спасибо - person Slim; 16.04.2014
comment
@Slim Вы уверены, что дополнительных дополнений нет? Вы внимательно проверяли все комплекты? А связки внутри связки? - person Kaarel; 16.04.2014
comment
Я привык использовать этот код для отладки намерений: Bundle bundle = getIntent().getExtras(); if (bundle!= null) { Log.d(slim, bundle!= null); for (Строковый ключ: bundle.keySet()) { Значение объекта = bundle.get(key); Log.d(slim, String.format(содержимое пакета: ключ: %s; значение: %s; (класс: %s), ключ, value.toString(), value.getClass().getName())); } } в logcat я получил только те дополнения, которые я/вы упомянули. Спасибо - person Slim; 16.04.2014
comment
@Slim @Kaarel результат находится в intent.getData(), а не в getExtras(). Результатом является URL-адрес контента, который вам нужно открыть с помощью ContentResolver. - person Iftah; 25.06.2014
comment
Кто-нибудь знает, как сохранить что-нибудь, кроме аудио в кодировке AMR? Подойдет любой формат 16 кГц X 16 бит. - person Tal Weiss; 02.07.2014
comment
@Kaarel Любое решение для Marshmallow? С указанными дополнениями больше не работает... - person davidOhara; 09.05.2016

Я получил этот ответ отсюда, я проверил даты и увидел, что он был опубликован через несколько дней после вашего сообщения, поэтому я решил, что вы его пропустили. Распознавание речи Android и запись звука одновременно

там один чувак говорит:

У меня есть решение, которое хорошо работает для распознавания речи и записи звука. Здесь (https://github.com/katchsvartanian/voiceRecognition) ссылка на простой проект Android Я создал, чтобы показать работу решения. Кроме того, я поместил в проект несколько принтскринов, чтобы проиллюстрировать приложение.

Я попытаюсь кратко объяснить подход, который я использовал. В этом проекте я объединил две функции: Google Speech API и запись во Flac.

Google Speech API вызывается через HTTP-соединения. Майк Пульц дает более подробную информацию об API:

"(...) новый API [Google] представляет собой API для полнодуплексной потоковой передачи. Это означает, что он фактически использует два HTTP-соединения: один запрос POST для загрузки контента в виде "живого" фрагментированного потока и второй запрос GET для доступа к результатам, что имеет гораздо больше смысла для более длинных аудиосэмплов или для потокового аудио».

Однако для правильной работы этому API необходимо получить звуковой файл FLAC. Это заставляет нас перейти ко второй части: запись во Flac.

Я реализовал запись Flac в этом проекте, извлекая и адаптируя некоторые фрагменты кода и библиотеки из приложения с открытым исходным кодом под названием AudioBoo. AudioBoo использует собственный код для записи и воспроизведения формата flac.

Таким образом, можно записать звук в формате flac, отправить его в Google Speech API, получить текст и воспроизвести только что записанный звук.

Созданный мной проект содержит базовые принципы работы и может быть улучшен для конкретных ситуаций. Чтобы заставить его работать в другом сценарии, необходимо получить ключ Google Speech API, который можно получить, будучи частью группы Google Chromium-dev. Я оставил один ключ в этом проекте просто для того, чтобы показать, что он работает, но в конце концов я его удалю. Если кому-то нужна дополнительная информация об этом, дайте мне знать, потому что я не могу разместить более двух ссылок в этом посте.

person Kappa    schedule 01.05.2014
comment
Это не отвечает на вопрос (например, как записывать через этот API распознавания речи Android). - person Kaarel; 04.05.2014

Мы можем сохранить это аудио с помощью класса AudioRecord. . Я сделал это успешно.

public class MainActivity extends AppCompatActivity {
TextView textView;
ImageView imageView;
static int request = 1;
private static final int RECORDER_SAMPLERATE = 8000;
private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_MONO;
private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;
private AudioRecord recorder = null;
private Thread recordingThread = null;
private boolean isRecording = false;
private int[] mSampleRates = new int[]{8000, 11025, 22050, 44100};
int bufferSize;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    textView = findViewById(R.id.textView);
    imageView = findViewById(R.id.mic);


    int bufferSize = AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE,
            RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING);


    recorder = findAudioRecord();

    if (ContextCompat.checkSelfPermission(this,
            Manifest.permission.RECORD_AUDIO)
            != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this,
                new String[]{Manifest.permission.RECORD_AUDIO, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE},
                1234);
    }
    
    imageView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent speech = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
            speech.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
            speech.putExtra(RecognizerIntent.EXTRA_PROMPT, "Speak to Text");

            if (ContextCompat.checkSelfPermission(MainActivity.this,
                    Manifest.permission.RECORD_AUDIO)
                    == PackageManager.PERMISSION_GRANTED) {
                startRecording();
                startActivityForResult(speech, request);
            }

        }
    });

    textView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            stopRecording();
        }
    });
}

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == request && resultCode == RESULT_OK) {
        stopRecording();
        ArrayList<String> dataa = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
        textView.setText(dataa.get(0).toString());
    }
}

int BufferElements2Rec = 1024; // want to play 2048 (2K) since 2 bytes we use only 1024
int BytesPerElement = 2; // 2 bytes in 16bit format

private void startRecording() {

    recorder.startRecording();
    isRecording = true;
    recordingThread = new Thread(new Runnable() {
        public void run() {
            writeAudioDataToFile();
        }
    }, "AudioRecorder Thread");
    recordingThread.start();
}

@Override
public void onRequestPermissionsResult(int requestCode,
                                       String permissions[], int[] grantResults) {
    switch (requestCode) {
        case 1234: {
            if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            } else {
                Log.d("TAG", "permission denied by user");
            }
            return;
        }
    }
}
private byte[] short2byte(short[] sData) {
    int shortArrsize = sData.length;
    byte[] bytes = new byte[shortArrsize * 2];
    for (int i = 0; i < shortArrsize; i++) {
        bytes[i * 2] = (byte) (sData[i] & 0x00FF);
        bytes[(i * 2) + 1] = (byte) (sData[i] >> 8);
        sData[i] = 0;
    }
    return bytes;

}
public AudioRecord findAudioRecord() {
    for (int rate : mSampleRates) {
        for (short audioFormat : new short[]{
                AudioFormat.ENCODING_PCM_8BIT,
                AudioFormat.ENCODING_PCM_16BIT}) {
            for (short channelConfig : new short[]{
                    AudioFormat.CHANNEL_IN_MONO,
                    AudioFormat.CHANNEL_IN_STEREO}) {
                try {
                    Log.d("Mic2", "Attempting rate " + rate
                            + "Hz, bits: " + audioFormat
                            + ", channel: " + channelConfig);
                    bufferSize = AudioRecord.getMinBufferSize(rate,
                            channelConfig, audioFormat);

                        AudioRecord recorder = new AudioRecord(
                                MediaRecorder.AudioSource.DEFAULT, rate,
                                channelConfig, audioFormat, bufferSize);
                        if (recorder.getState() == AudioRecord.STATE_INITIALIZED)
                            rate = rate;
                        return recorder;
                } catch (Exception e) {
                    Log.e("TAG", rate + "Exception, keep trying.", e);
                }
            }
        }
    }
    return null;
}

private void writeAudioDataToFile() {
    String filePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/file.pcm";
    short sData[] = new short[BufferElements2Rec];

    FileOutputStream os = null;
    try {
        os = new FileOutputStream(filePath);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }

    while (isRecording) {

        recorder.read(sData, 0, BufferElements2Rec);
        System.out.println("Short writing to file" + sData.toString());
        try {
            byte bData[] = short2byte(sData);
            os.write(bData, 0, BufferElements2Rec * BytesPerElement);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    try {
        os.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

private void stopRecording() {
    if (null != recorder) {
        isRecording = false;
        recorder.stop();
        recorder.release();
        recorder = null;
        recordingThread = null;
    }
}

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK) {
        finish();
    }
    return super.onKeyDown(keyCode, event);
}
person Ghayas Ahmad    schedule 25.12.2020