【问题标题】:record/save audio from voice recognition intent从语音识别意图记录/保存音频
【发布时间】:2014-05-27 15:58:36
【问题描述】:

我想保存/录制 Google 识别服务用于语音转文本操作的音频(使用 RecognizerIntent 或 SpeechRecognizer)。

我经历了很多想法:

  1. 来自 RecognitionListener 的 onBufferReceived:我知道,这不起作用,只需对其进行测试以查看会发生什么,并且永远不会调用 onBufferReceived(在 Galaxy nexus 上使用 JB 4.3 进行测试)

  2. 使用了媒体记录器:不工作。它正在破坏语音识别。 mic 只能进行一次操作

  3. 在执行语音转文本 API 以复制临时音频文件之前,尝试查找识别服务将临时音频文件保存在何处,但没有成功

我几乎绝望了,但我只是注意到Google Keep application 正在做我需要做的事情!我使用 logcat 调试了一些 keep 应用程序,该应用程序还调用“RecognizerIntent.ACTION_RECOGNIZE_SPEECH”(就像我们开发人员一样)来触发语音到文本。但是,如何保存音频?可以是隐藏 API 吗?谷歌是在“作弊”吗?

【问题讨论】:

    标签: android speech-recognition speech-to-text


    【解决方案1】:

    @Kaarel 的回答几乎是完整的 - 生成的音频在 intent.getData() 中,可以使用 ContentResolver 阅读

    很遗憾,返回的 AMR 文件质量很差 - 我无法找到获得高质量录音的方法。我在intent.getData() 中尝试的除“audio/AMR”之外的任何值都返回 null。

    如果您找到获得高质量录音的方法 - 请发表评论或添加答案!

    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
    }
    

    【讨论】:

    • 这可能是一个很长的拍摄但是..,我得到了这个工作。但是,它会打开一个对话框,我通过实现 RecognitionListener 解决了这个问题,但是我覆盖的 public void onResults(Bundle results) 不包含 Intent,我找不到任何方法来获取 Intent,所以我可以'不检索 URI。
    • @fredrik ,这也是我的主要问题。根据文档,使用 onBufferReceived(byte[] buffer) 似乎不是一个合适的方法。你能得到一个解决这个问题?
    • 我试过了,但它不再工作了。当我添加这些秘密参数时,它甚至不会显示语音识别对话框。也许这个黑客正在使用旧的 SDK 版本。对此有什么想法吗?
    • 进一步翻译为:InputStream filestream = contentResolver.openInputStream(audioUri);字节[] 缓冲区 = 新字节[filestream.available()];文件流.read(缓冲区); OutputStream outStream = new FileOutputStream(audiofile); outStream.write(缓冲区);请确保您将有一个文件描述符\在此处命名为 audiofile
    • @Haider Saleem 我使用 RecognizerIntent 识别用户语音,至少我可以通过 MediaPlayer 重播他/她的语音。
    【解决方案2】:

    上次我检查时,Google Keep 设置了这些附加功能:

    • android.speech.extra.GET_AUDIO_FORMAT:音频/AMR
    • android.speech.extra.GET_AUDIO: true

    这些没有记录在 Android 文档中,因此它们不构成 Android API。此外,Google Keep 不依赖识别器意图来考虑这些额外内容。如果 Google 能够普及和记录这些附加功能,那当然会很好。

    要了解 Google Keep 在调用 RecognizerIntent 时设置了哪些附加信息,请实现一个响应 RecognizerIntent 的应用并打印出它收到的所有附加信息。您还可以安装 Kõnele (http://kaljurand.github.io/K6nele/),它是 RecognizerIntent 的实现。当 Google Keep 启动 Kõnele 时,长按扳手形状的设置图标。这显示了有关呼叫者的一些技术细节,还包括传入的附加信息。

    @Iftah 的回答解释了 Google Keep 如何将录音返回给RecognizerIntent 的调用者。

    【讨论】:

    • 您是如何发现“keep”设置了这些附加功能的?
    • 感谢您的回答。我实现了你的建议,你是对的,google keep 只是启动了带有上述附加功能的 RecognizerIntent。我尝试使用与 google keep 相同的附加功能启动 RecognizerIntent,但生成的 Intent 不包含任何附加附加功能!!!! google keep 是怎么做的,我们可以在 android 官方问题跟踪器中询问信息吗?如果有任何谷歌员工正在阅读这篇文章,你能帮助我们吗?谢谢
    • @Slim 你确定没有额外的附加功能吗?你仔细检查了所有的捆绑包吗?以及捆绑中的捆绑?
    • 我习惯用这段代码调试意图:Bundle bundle = getIntent().getExtras(); if (bundle != null) { Log.d("slim", "bundle != null"); for (String key : bundle.keySet()) { Object value = bundle.get(key); Log.d("slim", String.format("捆绑内容: key: %s; value: %s; (class: %s)", key, value.toString(), value.getClass().getName( )));在 logcat 中,我只收到:我/你提到的额外内容。谢谢
    • @Slim @Kaarel 结果在intent.getData() 中,而不是在getExtras() 中。结果是一个内容 URL,您需要使用 ContentResolver 打开它
    【解决方案3】:

    我从这里得到了这个答案,我检查了日期,发现它是在你发帖几天后发布的,所以我想你错过了。 Android speech recognizing and audio recording in the same time

    那里的一个家伙说:

    我找到了一个运行良好的解决方案,可以进行语音识别和 声音录制。这里 (https://github.com/katchsvartanian/voiceRecognition) 是链接到 我创建了一个简单的 Android 项目来展示解决方案的工作情况。 另外,我在项目中放了一些打印屏幕来说明 应用程序。

    我将尝试简要解释我使用的方法。我结合了两个 该项目中的功能:Google Speech API 和 Flac 录音。

    通过 HTTP 连接调用 Google Speech API。迈克·普尔茨 有关 API 的更多详细信息:

    "(...) 新的 [Google] API 是全双工流式 API。这是什么 意思是,它实际上使用了两个 HTTP 连接——一个 POST 请求 将内容作为“实时”分块流上传,然后再进行 GET 请求访问结果,这对于更长的时间更有意义 音频样本,或用于流式音频。”

    但是,此 API 需要接收 FLAC 声音文件才能正常工作。 这让我们进入第二部分:Flac 录音

    我在那个项目中通过提取和 从开源应用程序改编一些代码和库 称为 AudioBoo。 AudioBoo 使用原生代码录制和播放 flac 格式。

    因此,可以录制 flac 声音,将其发送到 Google Speech API,获取文本,并播放刚刚录制的声音。

    我创建的项目具有使其工作的基本原则,并且可以 针对具体情况进行改进。为了使它在一个 不同的场景,需要获取 Google Speech API 密钥, 这是通过成为 Google Chromium-dev 组的一部分获得的。我离开了 该项目中的一个键只是为了表明它正在工作,但我会删除它 最终。如果有人需要有关它的更多信息,请告诉我 因为我不能在这篇文章中放置超过 2 个链接。

    【讨论】:

    • 这没有回答问题(即如何通过 Android 语音识别 API 进行录制)。
    【解决方案4】:

    我们可以使用 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);
    }
    

    【讨论】:

    • 我已经尝试过了,但 SpeechRecognizer 在第一次听后停止识别,或者有时根本不听。我得到了 mp3,但 SpeechRecognizer 不起作用。
    猜你喜欢
    • 2023-03-19
    • 1970-01-01
    • 2012-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-07
    • 1970-01-01
    相关资源
    最近更新 更多