在提出这个问题之前,我检查了所有与此问题相关的stackoverflow其他线程没有任何成功,所以请不要回复其他线程的链接,:))
我想保存/录制谷歌识别服务用于语音操作的音频(使用RecognizerIntent或SpeechRecognizer)。
我经历了很多想法:
我几乎绝望但我只是注意到Google Keep application正在做我需要做的事!!!!我使用logcat稍微调试了keep应用程序,app也调用了“RecognizerIntent.ACTION_RECOGNIZE_SPEECH”(就像我们开发人员一样)来触发语音到文本。但是,如何继续保存音频?它可以成为隐藏的api吗?谷歌“作弊”:)?
感谢您的帮助
祝你好运
答案 0 :(得分:19)
@ Kaarel的回答几乎完成 - 结果音频在intent.getData()
,可以使用ContentResolver
不幸的是,返回的AMR文件质量很低 - 我无法找到获得高质量录制的方法。除了" audio / AMR"在intent.getData()
中返回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
}
答案 1 :(得分:9)
上次我查看时,Google Keep会设置这些额外内容:
这些未作为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
的来电者。
答案 2 :(得分: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。 Mike Pultz给出了 有关API的更多详细信息:
“(...)新的[Google] API是全双工流式API。这是什么 意思是,它实际上使用两个HTTP连接 - 一个POST请求 将内容上传为“实时”分块流和第二个GET 请求访问结果,这使得更长时间更有意义 音频样本,或用于流音频。“
但是,此API需要接收FLAC声音文件才能正常工作。 这使我们进入第二部分:Flac录音
我通过提取和实现了该项目中的Flac记录 从开源应用程序中调整一些代码和库 叫做AudioBoo。 AudioBoo使用本机代码来记录和播放flac 格式。
因此,可以录制flac声音,将其发送到Google Speech API,获取文本,播放刚录制的声音。
我创建的项目有基本原则,可以让它发挥作用 针对具体情况进行改进。为了使它在一个工作 不同的情况,有必要获得Google Speech API密钥, 这是通过成为Google Chromium-dev组的一部分获得的。我离开了 该项目中的一个关键只是为了表明它正在运行,但我将删除它 最终。如果有人需要更多相关信息,请告诉我 因为我在这篇文章中不能放2个以上的链接。
答案 3 :(得分:1)
我们可以使用 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);
}