Skip to content

Commit

Permalink
Store audio and make available to the caller
Browse files Browse the repository at this point in the history
  • Loading branch information
Kaljurand committed Jul 13, 2014
1 parent 8606a8d commit 664824f
Show file tree
Hide file tree
Showing 12 changed files with 290 additions and 74 deletions.
20 changes: 9 additions & 11 deletions app/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="ee.ioc.phon.android.speak"
android:installLocation="auto"
android:versionCode="843"
android:versionName="0.8.43" >

<!-- API level 3: some default button was not found, but we didn't explore it further. -->
<!-- API level 7: MediaRecorder.AudioSource.VOICE_RECOGNITION -->
<!-- API level 8: android.speech.SpeechRecognizer and android.speech.RecognitionService -->
<!-- TODO: setting targetSdkVersion=15 would switch on StrictMode -->
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="8" />
android:installLocation="auto" >


<uses-permission android:name="android.permission.INTERNET" >
</uses-permission>
Expand Down Expand Up @@ -120,6 +111,7 @@
</activity>
<activity
android:name=".demo.ExtrasDemo"
android:exported="true"
android:label="@string/labelActivityExtrasDemo" >
</activity>
<activity
Expand Down Expand Up @@ -163,6 +155,12 @@
android:name=".provider.AppsContentProvider"
android:authorities="ee.ioc.phon.android.speak.provider.AppsContentProvider"
android:exported="false" />

<provider
android:name=".provider.FileContentProvider"
android:authorities="ee.ioc.phon.android.speak.provider.FileContentProvider"
android:permission="android.permission.RECORD_AUDIO"
android:exported="true" />
</application>

</manifest>
16 changes: 15 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,21 @@ android {
// We support API level 8, but use isChangingConfigurations() when API 11+
// is available.
compileSdkVersion 19
buildToolsVersion "19.0.3"
buildToolsVersion "19.1.0"


// API level 3: some default button was not found, but we didn't explore it further.
// API level 7: MediaRecorder.AudioSource.VOICE_RECOGNITION
// API level 8: android.speech.SpeechRecognizer and android.speech.RecognitionService
// TODO: setting targetSdkVersion=15 would switch on StrictMode
defaultConfig {
packageName 'ee.ioc.phon.android.speak'
minSdkVersion 8
targetSdkVersion 8
versionCode 843
versionName '0.8.43'
}


sourceSets {
main {
Expand Down
4 changes: 4 additions & 0 deletions app/res/values-et/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@
<string name="dialogTitleHypotheses">Kas ütlesite &#8230;</string>
<string name="toastForwardedMatches">Tuvastasin: %1$s</string>
<string name="toastAssignGrammar">Omistasin grammatika: %1$s</string>
<string name="toastRequestedAudioFormatNotSupported">Audioformaat pole toetatud: %1$s</string>
<string name="toastPlayingAudio">Mängin lindistust: %1$s, %2$s, %3$d ms</string>
<string name="toastPlayingAudioDone">Lindistuse lõpp</string>
<string name="entryGrammarName1">[piiramata sisend]</string>
<string name="entryGrammarDesc1">Selle grammatika saab omistada rakendustele, kus te
soovite igal juhul vabas vormis rääkida, sõltumata sellest, kas
Expand All @@ -195,6 +198,7 @@
rakendus</string>

<!-- Error messages used in exceptions and toasts -->
<string name="errorFailedPlayAudio">Lindistatud heli mahamängimine luhtus: %1$s, %2$s</string>
<string name="errorFailedLaunchApp">See rakendus pole käivitatav või pole (enam)
installeeritud: %1$s</string>
<string name="errorFailedGetGrammarUrl">VIGA: grammatika aadressi valimine luhtus</string>
Expand Down
4 changes: 4 additions & 0 deletions app/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,15 @@ More grammars are available at
<string name="emptylistServers">No servers. Add a new server via the Add-menu.</string>
<string name="toastForwardedMatches">Recognized: %1$s</string>
<string name="toastAssignGrammar">Assigned grammar: %1$s</string>
<string name="toastRequestedAudioFormatNotSupported">Requested audio format not supported: %1$s</string>
<string name="toastPlayingAudio">Playing the recorded audio: %1$s, %2$s, %3$d ms</string>
<string name="toastPlayingAudioDone">Done playing the audio</string>
<string name="errorRecognizerNotPresent">There is no activity that provides speech recognition service.</string>

<!-- Error messages used in exceptions and toasts -->
<string name="exceptionMalformedUrl">ERROR: malformed URL</string>
<string name="errorFailedLaunchApp">This app is not launchable or is not installed (anymore): %1$s</string>
<string name="errorFailedPlayAudio">Failed to play the recorded audio: %1$s, %2$s</string>
<string name="errorResultAudioError">Audio recording failed. Maybe another app is currently recording?</string>
<string name="errorResultNetworkError">Something is wrong with the internet connection. Maybe it is not switched on?</string>
<string name="errorResultClientError">General client error</string>
Expand Down
9 changes: 9 additions & 0 deletions app/src/ee/ioc/phon/android/speak/Constants.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
package ee.ioc.phon.android.speak;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class Constants {

// When does the chunk sending start and what is its interval
public static final int TASK_INTERVAL_SEND = 300;
public static final int TASK_DELAY_SEND = 100;

public static final String AUDIO_FILENAME = "audio.wav";

public static final String DEFAULT_AUDIO_FORMAT = "audio/wav";
public static final Set<String> SUPPORTED_AUDIO_FORMATS =
new HashSet<String>(Arrays.asList(DEFAULT_AUDIO_FORMAT));
}
69 changes: 43 additions & 26 deletions app/src/ee/ioc/phon/android/speak/DetailsActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,28 +31,31 @@
import android.widget.Toast;

/**
* <p>Simple activity for displaying String-arrays.
* <p>Simple activity that displays the String-array attached to the intent and plays the
* audio data contained in the intent data.
* Not really meant for the end-users.</p>
*
*
* @author Kaarel Kaljurand
*/
public class DetailsActivity extends ListActivity {

public static final String EXTRA_TITLE = "EXTRA_TITLE";
public static final String EXTRA_STRING_ARRAY = "EXTRA_STRING_ARRAY";

private MediaPlayer mMediaPlayer;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

Intent intent = getIntent();
Intent intent = getIntent();

Uri audioUri = intent.getData();
if (audioUri != null) {
playAudio(audioUri);
}
Uri audioUri = intent.getData();
if (audioUri != null) {
playAudio(audioUri, intent.getType());
}

Bundle extras = intent.getExtras();
Bundle extras = intent.getExtras();
if (extras != null) {
String title = extras.getString(EXTRA_TITLE);
if (title == null) {
Expand All @@ -78,21 +81,35 @@ public void onItemClick(AdapterView<?> parent, View view, int position, long id)
}
}

/**
* @param uri audio URI to be played
* TODO: do error checking and put strings to resources
*/
private void playAudio(Uri uri) {
final MediaPlayer mp = MediaPlayer.create(this, uri);
int duration = mp.getDuration();
mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
mp.release();
Toast.makeText(getApplicationContext(), "Done playing the audio", Toast.LENGTH_LONG).show();
}
});
mp.start();
Toast.makeText(this, "Playing the recorded audio: " + duration + " ms", Toast.LENGTH_LONG).show();
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (mMediaPlayer != null) {
mMediaPlayer.release();
}
}

/**
* @param uri audio URI to be played
*/
private void playAudio(Uri uri, String type) {
mMediaPlayer = MediaPlayer.create(this, uri);
if (mMediaPlayer == null) {
toast(String.format(getString(R.string.errorFailedPlayAudio), uri.toString(), type));
} else {
int duration = mMediaPlayer.getDuration();
mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
toast(getString(R.string.toastPlayingAudioDone));
}
});
mMediaPlayer.start();
toast(String.format(getString(R.string.toastPlayingAudio), uri.toString(), type, duration));
}
}

private void toast(String message) {
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
}
}
5 changes: 5 additions & 0 deletions app/src/ee/ioc/phon/android/speak/Extras.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ public class Extras {
// should be transcribed.
public static final String EXTRA_PHRASE = "ee.ioc.phon.android.extra.PHRASE";

// Caller is interested in the recorded audio data (boolean)
public static final String GET_AUDIO = "android.speech.extra.GET_AUDIO";

// Caller wants to have the audio data in a certain format (String)
public static final String GET_AUDIO_FORMAT = "android.speech.extra.GET_AUDIO_FORMAT";

/**
* <p>Key used to retrieve an {@code ArrayList<String>} from the {@link Bundle} passed to the
Expand Down
64 changes: 64 additions & 0 deletions app/src/ee/ioc/phon/android/speak/RawAudioRecorder.java
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,70 @@ public byte[] getCompleteRecording() {
}


/**
* @return bytes that have been recorded since the beginning, with wav-header
*/
public byte[] getCompleteRecordingAsWav() {
byte[] pcm = getCompleteRecording();
int headerLen = 44;
int totalDataLen = pcm.length + headerLen;
int byteRate = mSampleRate * RESOLUTION_IN_BYTES; // mSampleRate*(16/8)*1 ???
int totalAudioLen = pcm.length;

byte[] header = new byte[headerLen];

header[0] = 'R'; // RIFF/WAVE header
header[1] = 'I';
header[2] = 'F';
header[3] = 'F';
header[4] = (byte) (totalDataLen & 0xff);
header[5] = (byte) ((totalDataLen >> 8) & 0xff);
header[6] = (byte) ((totalDataLen >> 16) & 0xff);
header[7] = (byte) ((totalDataLen >> 24) & 0xff);
header[8] = 'W';
header[9] = 'A';
header[10] = 'V';
header[11] = 'E';
header[12] = 'f'; // 'fmt ' chunk
header[13] = 'm';
header[14] = 't';
header[15] = ' ';
header[16] = 16; // 4 bytes: size of 'fmt ' chunk
header[17] = 0;
header[18] = 0;
header[19] = 0;
header[20] = 1; // format = 1
header[21] = 0;
header[22] = (byte) CHANNELS;
header[23] = 0;
header[24] = (byte) (mSampleRate & 0xff);
header[25] = (byte) ((mSampleRate >> 8) & 0xff);
header[26] = (byte) ((mSampleRate >> 16) & 0xff);
header[27] = (byte) ((mSampleRate >> 24) & 0xff);
header[28] = (byte) (byteRate & 0xff);
header[29] = (byte) ((byteRate >> 8) & 0xff);
header[30] = (byte) ((byteRate >> 16) & 0xff);
header[31] = (byte) ((byteRate >> 24) & 0xff);
header[32] = (byte) (2 * 16 / 8); // block align
header[33] = 0;
header[34] = (byte) 8*RESOLUTION_IN_BYTES; // bits per sample
header[35] = 0;
header[36] = 'd';
header[37] = 'a';
header[38] = 't';
header[39] = 'a';
header[40] = (byte) (totalAudioLen & 0xff);
header[41] = (byte) ((totalAudioLen >> 8) & 0xff);
header[42] = (byte) ((totalAudioLen >> 16) & 0xff);
header[43] = (byte) ((totalAudioLen >> 24) & 0xff);

byte[] wav = new byte[header.length + pcm.length];
System.arraycopy(header, 0, wav, 0, header.length);
System.arraycopy(pcm, 0, wav, header.length, pcm.length);
return wav;
}


/**
* @return bytes that have been recorded since this method was last called
*/
Expand Down
Loading

0 comments on commit 664824f

Please sign in to comment.