diff options
-rw-r--r-- | Android.mk | 10 | ||||
-rw-r--r-- | AndroidManifest.xml | 20 | ||||
-rw-r--r-- | res/layout/recorder.xml | 89 | ||||
-rw-r--r-- | res/values/strings.xml | 27 | ||||
-rw-r--r-- | src/com/android/speechrecorder/SpeechRecorderActivity.java | 389 |
5 files changed, 535 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000..3d2809a --- /dev/null +++ b/Android.mk @@ -0,0 +1,10 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := $(call all-subdir-java-files) \ + +LOCAL_PACKAGE_NAME := SpeechRecorder + +include $(BUILD_PACKAGE) diff --git a/AndroidManifest.xml b/AndroidManifest.xml new file mode 100644 index 0000000..2197a97 --- /dev/null +++ b/AndroidManifest.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> + +<manifest package="com.android.speechrecorder" + xmlns:android="http://schemas.android.com/apk/res/android"> + <uses-permission android:name="android.permission.RECORD_AUDIO"/> + <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> + + <application android:label="@string/app_name"> + <!-- Initial Page --> + <activity android:name="SpeechRecorderActivity" + android:label="@string/speechrecorderlabel"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + + </application> + +</manifest> diff --git a/res/layout/recorder.xml b/res/layout/recorder.xml new file mode 100644 index 0000000..0e0c215 --- /dev/null +++ b/res/layout/recorder.xml @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="vertical"> + + <LinearLayout android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView android:id="@+id/commandText" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:textSize="12pt"/> + + <TextView android:id="@+id/statusText" + android:layout_width="fill_parent" + android:layout_height="wrap_content"/> + </LinearLayout> + + <LinearLayout android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <Button android:id="@+id/recordButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/recordButton"/> + + <Button android:id="@+id/redoButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/redoButton"/> + </LinearLayout> + + <RadioGroup android:layout_width="wrap_content" + android:layout_height="fill_parent" + android:padding="10dip" + android:orientation="vertical" android:layout_weight="30"> + + <RadioButton android:id="@+id/codec11KHzRadioButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + + <RadioButton android:id="@+id/codec8KHzRadioButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + </RadioGroup> + + <RadioGroup android:layout_width="wrap_content" + android:layout_height="fill_parent" + android:orientation="vertical" android:layout_weight="30"> + + <RadioButton android:id="@+id/callRadioButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/callRadioButton"/> + + <RadioButton android:id="@+id/dialNanpRadioButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/dialNanpRadioButton"/> + + <RadioButton android:id="@+id/dialPairsRadioButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/dialPairsRadioButton"/> + </RadioGroup> + +</LinearLayout> diff --git a/res/values/strings.xml b/res/values/strings.xml new file mode 100644 index 0000000..3961bf2 --- /dev/null +++ b/res/values/strings.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2006 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <!-- application name and title of error dialogs--> + <string name="app_name">Speech Recorder</string> + + <string name="speechrecorderlabel">Speech Recorder</string> + <string name="recordButton">Record</string> + <string name="redoButton">Redo</string> + <string name="callRadioButton">\"call ...\"\nUtterances</string> + <string name="dialNanpRadioButton">\"dial NANP\"\nUtterances</string> + <string name="dialPairsRadioButton">\"dial NANP PAIRS\"\nUtterances</string> +</resources> diff --git a/src/com/android/speechrecorder/SpeechRecorderActivity.java b/src/com/android/speechrecorder/SpeechRecorderActivity.java new file mode 100644 index 0000000..d1d5855 --- /dev/null +++ b/src/com/android/speechrecorder/SpeechRecorderActivity.java @@ -0,0 +1,389 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.speechrecorder; + +import android.app.Activity; +import android.os.Bundle; +import android.os.Handler; +import android.speech.srec.Recognizer; +import android.speech.srec.WaveHeader; +import android.speech.srec.MicrophoneInputStream; +import android.util.Config; +import android.util.Log; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.RadioButton; +import android.widget.TextView; +import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileFilter; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class SpeechRecorderActivity extends Activity { + private static final String TAG = "SpeechRecorderActivity"; + + private static final int DURATION_SEC = 7; + + private Handler mHandler; + + private TextView mCommand; + private TextView mStatus; + private Button mRecord; + private Button mRedo; + private RadioButton m8KHz; + private RadioButton m11KHz; + private RadioButton mCall; + private RadioButton mDialNanp; + private RadioButton mDialPairs; + + private InputStream mMicrophone; + private ByteArrayOutputStream mBaos; + + private File mUtterance; + private int mSampleRate; + private Thread mThread; + private boolean mStoppedListening; + + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + + mHandler = new Handler(); + + setContentView(R.layout.recorder); + mCommand = (TextView) findViewById(R.id.commandText); + mStatus = (TextView) findViewById(R.id.statusText); + mRecord = (Button) findViewById(R.id.recordButton); + mRedo = (Button) findViewById(R.id.redoButton); + m8KHz = (RadioButton)findViewById(R.id.codec8KHzRadioButton); + m11KHz = (RadioButton)findViewById(R.id.codec11KHzRadioButton); + mCall = (RadioButton)findViewById(R.id.callRadioButton); + mDialNanp = (RadioButton)findViewById(R.id.dialNanpRadioButton); + mDialPairs = (RadioButton)findViewById(R.id.dialPairsRadioButton); + + mCommand.setText("Please click 'Record' to begin"); + mRecord.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + if (Config.LOGD) { + Log.d(TAG, "mRecord.OnClickListener.onClick"); + } + + setupRecording(); + } + }); + + mRedo.setEnabled(false); + mRedo.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + if (Config.LOGD) { + Log.d(TAG, "mRedo.onClickListener.onClick"); + } + + mUtterance.delete(); + + setupRecording(); + } + }); + + m8KHz.setText("PCM/16bit/8KHz"); + m11KHz.setText("PCM/16bit/11KHz"); + m11KHz.setChecked(true); + mCall.setChecked(true); + } + + private void setupRecording() { + Log.d(TAG, "setupRecording"); + // disable buttons + mRedo.setEnabled(false); + mRecord.setEnabled(false); + m8KHz.setFocusable(false); + m11KHz.setFocusable(false); + mCall.setFocusable(false); + mDialNanp.setFocusable(false); + mDialPairs.setFocusable(false); + + // find the first utterance not covered + String[] utterances = mCall.isChecked() ? mCallUtterances : + mDialNanp.isChecked() ? mDialNanpUtterances : + mDialPairs.isChecked() ? mDialPairsUtterances : + null; + mUtterance = null; + int index = -1; + for (int i = 0; i < utterances.length; i++) { + File u = new File(getDir("recordings", MODE_PRIVATE), + utterances[i].toLowerCase().replace(' ', '_') + ".wav"); + if (!u.exists()) { + mUtterance = u; + index = i; + break; + } + } + + // check if done + if (mUtterance == null) { + mCommand.setText("Finished: Thank You!"); + return; + } + Log.d(TAG, "going to record " + mUtterance.toString()); + + // fix up UI + mCommand.setText("Say: \"" + utterances[index] + "\""); + final String status = "item " + (index + 1) + "/" + utterances.length; + + // start the microphone + mSampleRate = m8KHz.isChecked()? 8000 : + m11KHz.isChecked() ? 11025 : + 11025; + mBaos = new ByteArrayOutputStream(mSampleRate * 2 * 20); + try { + mMicrophone = new MicrophoneInputStream(mSampleRate, mSampleRate * 15); + +// mMicrophone = logInputStream(mUtterance.toString(), mMicrophone, mSampleRate); + } catch (IOException e) { + + } + + // post a number of delayed events to update the UI and to stop recording + // after a few seconds. + for (int i = 0; i <= DURATION_SEC; i++) { + final int remain = DURATION_SEC - i; + mHandler.postDelayed(new Runnable() { + public void run() { + if (remain > 0) { + mStatus.setText(status + " Recording... " + remain); + } + else { + mStatus.setText(status); + stopRecording(); + } + } + }, i * 1000); + } + + // now start a thread to store the audio. + mStoppedListening = false; + mThread = new Thread() { + public void run() { + Log.d(TAG, "run audio capture thread"); + byte buffer[] = new byte[512]; + while (!mStoppedListening) { + try { + int rtn = 0; + rtn = mMicrophone.read(buffer, 0, 512); + if (rtn > 0) mBaos.write(buffer, 0, rtn); + } catch (IOException e) { + } + } + } + }; + mThread.start(); + + // to avoid the button click + try { + Thread.sleep(100); + } catch (InterruptedException ie) { + } + + } + + private void stopRecording() { + Log.d(TAG, "stopRecording"); + mStoppedListening = true; + try { + mThread.join(); + } catch (InterruptedException e) { + + } + try { + OutputStream out = new FileOutputStream(mUtterance.toString()); + try { + byte[] pcm = mBaos.toByteArray(); + Log.d(TAG, "byteArray length " + pcm.length); + WaveHeader hdr = new WaveHeader(WaveHeader.FORMAT_PCM, + (short)1, mSampleRate, (short)16, pcm.length); + hdr.write(out); + out.write(pcm); + } finally { + out.close(); + mMicrophone.close(); + mBaos.close(); + } + } catch (IOException e) { + + + } finally { + } + + // stop the recording + mRecord.setEnabled(true); + + mRedo.setEnabled(true); + + mCommand.setText("Got it!"); + } + + + private final static String[] mCallUtterances = new String[] { + "Call Adam Varro", + "Call Alex Lloyd", + "Call Amod Karve", + "Call Ana Maria Lopez", + "Call Ben Sigelman", + "Call Chris Vennard", + "Call Dana Pogoda", + "Call Daryl Pregibon", + "Call Davi Robison", + "Call David Barrett Kahn", + "Call David Hyman", + "Call Douglas Gordin", + "Call Gregor Rothfuss", + "Call James Sheridan", + "Call Jason Charo", + "Call Jeff Reynar", + "Call Joel Ward", + "Call John Milton", + "Call Lajos Nagy", + "Call Lori Sobel", + "Call Martin Jansche", + "Call Meghan McGarry", + "Call Meghan Shakar", + "Call Nilka Thomas", + "Call Pedro Colijn", + "Call Pramod Adiddam", + "Call Rajeev Sivaram", + "Call Rich Armstrong", + "Call Robin Watson", + "Call Sam Morales", + }; + + private final static String[] mDialPairsUtterances = new String[] { + // all possible pairs + "Dial 000 000 0000", + + "Dial 101 010 1010", + "Dial 111 111 1111", + + "Dial 202 020 2020", + "Dial 212 121 2121", + "Dial 222 222 2222", + + "Dial 303 030 3030", + "Dial 313 131 3131", + "Dial 323 232 3232", + "Dial 333 333 3333", + + "Dial 404 040 4040", + "Dial 414 141 4141", + "Dial 424 242 4242", + "Dial 434 343 4343", + "Dial 444 444 4444", + + "Dial 505 050 5050", + "Dial 515 151 5151", + "Dial 525 252 5252", + "Dial 535 353 5353", + "Dial 545 454 5454", + "Dial 555 555 5555", + + "Dial 606 060 6060", + "Dial 616 161 6161", + "Dial 626 262 6262", + "Dial 636 363 6363", + "Dial 646 464 6464", + "Dial 656 565 6565", + "Dial 666 666 6666", + + "Dial 707 070 7070", + "Dial 717 171 7171", + "Dial 727 272 7272", + "Dial 737 373 7373", + "Dial 747 474 7474", + "Dial 757 575 7575", + "Dial 767 676 7676", + "Dial 777 777 7777", + + "Dial 808 080 8080", + "Dial 818 181 8181", + "Dial 828 282 8282", + "Dial 838 383 8383", + "Dial 848 484 8484", + "Dial 858 585 8585", + "Dial 868 686 8686", + "Dial 878 787 8787", + "Dial 888 888 8888", + + "Dial 909 090 9090", + "Dial 919 191 9191", + "Dial 929 292 9292", + "Dial 939 393 9393", + "Dial 949 494 9494", + "Dial 959 595 9595", + "Dial 969 696 9696", + "Dial 979 797 9797", + "Dial 989 898 9898", + "Dial 999 999 9999", + + }; + + + private final static String[] mDialNanpUtterances = new String[] { + "Dial 211", + "Dial 411", + "Dial 511", + "Dial 811", + "Dial 911", + // random numbers + "Dial 653 5763", + "Dial 263 9072", + "Dial 202 9781", + "Dial 379 8229", + "Dial 874 9139", + "Dial 236 0163", + "Dial 656 7455", + "Dial 474 5254", + "Dial 348 8687", + "Dial 629 8602", + + //"Dial 272 717 8405", + //"Dial 949 516 0162", + //"Dial 795 117 7190", + //"Dial 493 656 3767", + //"Dial 588 093 9218", + "Dial 511 658 3690", + "Dial 440 301 8489", + "Dial 695 713 6744", + "Dial 581 475 8712", + "Dial 981 388 3579", + + "Dial 840 683 3346", + "Dial 303 467 7988", + "Dial 649 504 5290", + "Dial 184 577 4229", + "Dial 212 286 3982", + "Dial 646 258 0115", + "Dial 427 482 6852", + "Dial 231 809 9260", + "Dial 681 930 4301", + "Dial 246 650 8339", + }; +} |