diff options
| author | 2016-04-10 20:15:33 -0700 | |
|---|---|---|
| committer | 2016-04-10 21:37:31 -0700 | |
| commit | 15ab2b45e7f9f2b6ea92f4dd7e8a5768630dbd80 (patch) | |
| tree | fa866f14f2867f438abea9d95bf97758593b7f39 /tests/SoundTriggerTestApp | |
| parent | cf45224a81272d783314a77fd8671d711f4c6a68 (diff) | |
Updates to sound trigger test app.
Now supports loading model information from .properties files inside the
app's data directory. This lets you prepare models for your actual
device, rather than just fake data. If no .properties files are present,
falls back to the default fake three models.
Also adds support for playing some trigger audio, so you can use the
same device to (hopefully) have a trigger be detected.
Change-Id: I003849eb11d4995534d4c070838810d4519991f8
Diffstat (limited to 'tests/SoundTriggerTestApp')
3 files changed, 166 insertions, 78 deletions
diff --git a/tests/SoundTriggerTestApp/res/layout/main.xml b/tests/SoundTriggerTestApp/res/layout/main.xml index 702be49aac7b..06949a0b6328 100644 --- a/tests/SoundTriggerTestApp/res/layout/main.xml +++ b/tests/SoundTriggerTestApp/res/layout/main.xml @@ -60,29 +60,22 @@ android:onClick="onUnEnrollButtonClicked" android:padding="20dp" /> + <Button + android:id="@+id/play_trigger_id" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/play_trigger" + android:onClick="onPlayTriggerButtonClicked" + android:padding="20dp" /> + </LinearLayout> <RadioGroup xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/model_group_id" android:layout_width="fill_parent" android:layout_height="wrap_content" android:padding="20dp" - android:checkedButton="@+id/model_one" android:orientation="vertical"> - <RadioButton android:id="@+id/model_one" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/model_one" - android:onClick="onRadioButtonClicked"/> - <RadioButton android:id="@+id/model_two" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/model_two" - android:onClick="onRadioButtonClicked"/> - <RadioButton android:id="@+id/model_three" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/model_three" - android:onClick="onRadioButtonClicked"/> </RadioGroup> <ScrollView @@ -93,7 +86,7 @@ android:fillViewport="true"> <TextView - android:id="@+id/console" + android:id="@+id/console" android:paddingTop="20pt" android:layout_height="fill_parent" android:layout_width="fill_parent" diff --git a/tests/SoundTriggerTestApp/res/values/strings.xml b/tests/SoundTriggerTestApp/res/values/strings.xml index b4ca71b8286e..7c1f64944e7f 100644 --- a/tests/SoundTriggerTestApp/res/values/strings.xml +++ b/tests/SoundTriggerTestApp/res/values/strings.xml @@ -21,8 +21,6 @@ <string name="unenroll">Un-load</string> <string name="start_recog">Start</string> <string name="stop_recog">Stop</string> - <string name="model_one">Model One</string> - <string name="model_two">Model Two</string> - <string name="model_three">Model Three</string> + <string name="play_trigger">Play Trigger Audio</string> <string name="none">Debug messages appear here:\n</string> </resources> diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/TestSoundTriggerActivity.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/TestSoundTriggerActivity.java index 3ca96d2e7a06..5fd38e953fda 100644 --- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/TestSoundTriggerActivity.java +++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/TestSoundTriggerActivity.java @@ -16,24 +16,36 @@ package com.android.test.soundtrigger; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; import java.util.Random; import java.util.UUID; import android.app.Activity; -import android.hardware.soundtrigger.SoundTrigger; import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel; +import android.hardware.soundtrigger.SoundTrigger; import android.media.AudioFormat; +import android.media.AudioManager; +import android.media.MediaPlayer; import android.media.soundtrigger.SoundTriggerDetector; import android.media.soundtrigger.SoundTriggerManager; -import android.text.Editable; -import android.text.method.ScrollingMovementMethod; +import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.PowerManager; import android.os.UserManager; +import android.text.Editable; +import android.text.method.ScrollingMovementMethod; import android.util.Log; import android.view.View; +import android.widget.Button; +import android.widget.RadioButton; import android.widget.RadioButton; +import android.widget.RadioGroup; import android.widget.ScrollView; import android.widget.TextView; import android.widget.Toast; @@ -44,20 +56,17 @@ public class TestSoundTriggerActivity extends Activity { private SoundTriggerUtil mSoundTriggerUtil; private Random mRandom; - private UUID mModelUuid1 = UUID.randomUUID(); - private UUID mModelUuid2 = UUID.randomUUID(); - private UUID mModelUuid3 = UUID.randomUUID(); - private UUID mVendorUuid = UUID.randomUUID(); - private SoundTriggerDetector mDetector1 = null; - private SoundTriggerDetector mDetector2 = null; - private SoundTriggerDetector mDetector3 = null; + private Map<Integer, ModelInfo> mModelInfoMap; + private Map<View, Integer> mModelIdMap; private TextView mDebugView = null; - private int mSelectedModelId = 1; + private int mSelectedModelId = -1; private ScrollView mScrollView = null; + private Button mPlayTriggerButton = null; private PowerManager.WakeLock mScreenWakelock; private Handler mHandler; + private RadioGroup mRadioGroup; @Override protected void onCreate(Bundle savedInstanceState) { @@ -66,11 +75,108 @@ public class TestSoundTriggerActivity extends Activity { setContentView(R.layout.main); mDebugView = (TextView) findViewById(R.id.console); mScrollView = (ScrollView) findViewById(R.id.scroller_id); + mRadioGroup = (RadioGroup) findViewById(R.id.model_group_id); + mPlayTriggerButton = (Button) findViewById(R.id.play_trigger_id); mDebugView.setText(mDebugView.getText(), TextView.BufferType.EDITABLE); mDebugView.setMovementMethod(new ScrollingMovementMethod()); mSoundTriggerUtil = new SoundTriggerUtil(this); mRandom = new Random(); mHandler = new Handler(); + + mModelInfoMap = new HashMap(); + mModelIdMap = new HashMap(); + + setVolumeControlStream(AudioManager.STREAM_MUSIC); + + // Load all the models in the data dir. + for (File file : getFilesDir().listFiles()) { + // Find meta-data in .properties files, ignore everything else. + if (!file.getName().endsWith(".properties")) { + continue; + } + try { + Properties properties = new Properties(); + properties.load(new FileInputStream(file)); + createModelInfoAndWidget(properties); + } catch (Exception e) { + Log.e(TAG, "Failed to load properties file " + file.getName()); + } + } + + // Create a few dummy models if we didn't load anything. + if (mModelIdMap.isEmpty()) { + Properties dummyModelProperties = new Properties(); + for (String name : new String[]{"One", "Two", "Three"}) { + dummyModelProperties.setProperty("name", "Model " + name); + createModelInfoAndWidget(dummyModelProperties); + } + } + } + + private void createModelInfoAndWidget(Properties properties) { + try { + ModelInfo modelInfo = new ModelInfo(); + + if (!properties.containsKey("name")) { + throw new RuntimeException("must have a 'name' property"); + } + modelInfo.name = properties.getProperty("name"); + + if (properties.containsKey("modelUuid")) { + modelInfo.modelUuid = UUID.fromString(properties.getProperty("modelUuid")); + } else { + modelInfo.modelUuid = UUID.randomUUID(); + } + + if (properties.containsKey("vendorUuid")) { + modelInfo.vendorUuid = UUID.fromString(properties.getProperty("vendorUuid")); + } else { + modelInfo.vendorUuid = UUID.randomUUID(); + } + + if (properties.containsKey("triggerAudio")) { + modelInfo.triggerAudioPlayer = MediaPlayer.create(this, Uri.parse( + getFilesDir().getPath() + "/" + properties.getProperty("triggerAudio"))); + } + + if (properties.containsKey("dataFile")) { + File modelDataFile = new File( + getFilesDir().getPath() + "/" + properties.getProperty("dataFile")); + modelInfo.modelData = new byte[(int) modelDataFile.length()]; + FileInputStream input = new FileInputStream(modelDataFile); + input.read(modelInfo.modelData, 0, modelInfo.modelData.length); + } else { + modelInfo.modelData = new byte[1024]; + mRandom.nextBytes(modelInfo.modelData); + } + + // TODO: Add property support for keyphrase models when they're exposed by the + // service. Also things like how much audio they should record with the capture session + // provided in the callback. + + // Add a widget into the radio group. + RadioButton button = new RadioButton(this); + mRadioGroup.addView(button); + button.setText(modelInfo.name); + button.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + onRadioButtonClicked(v); + } + }); + + // Update our maps containing the button -> id and id -> modelInfo. + int newModelId = mModelIdMap.size() + 1; + mModelIdMap.put(button, newModelId); + mModelInfoMap.put(newModelId, modelInfo); + + // If we don't have something selected, select this first thing. + if (mSelectedModelId < 0) { + button.setChecked(true); + onRadioButtonClicked(button); + } + } catch (IOException e) { + Log.e(TAG, "Error parsing properties for " + properties.getProperty("name"), e); + } } private void postMessage(String msg) { @@ -91,27 +197,15 @@ public class TestSoundTriggerActivity extends Activity { } private synchronized UUID getSelectedUuid() { - if (mSelectedModelId == 2) return mModelUuid2; - if (mSelectedModelId == 3) return mModelUuid3; - return mModelUuid1; // Default. + return mModelInfoMap.get(mSelectedModelId).modelUuid; } private synchronized void setDetector(SoundTriggerDetector detector) { - if (mSelectedModelId == 2) { - mDetector2 = detector; - return; - } - if (mSelectedModelId == 3) { - mDetector3 = detector; - return; - } - mDetector1 = detector; + mModelInfoMap.get(mSelectedModelId).detector = detector; } private synchronized SoundTriggerDetector getDetector() { - if (mSelectedModelId == 2) return mDetector2; - if (mSelectedModelId == 3) return mDetector3; - return mDetector1; + return mModelInfoMap.get(mSelectedModelId).detector; } private void screenWakeup() { @@ -127,25 +221,29 @@ public class TestSoundTriggerActivity extends Activity { mScreenWakelock.release(); } + /** TODO: Should return the abstract sound model that can be then sent to the service. */ + private GenericSoundModel createNewSoundModel() { + ModelInfo modelInfo = mModelInfoMap.get(mSelectedModelId); + return new GenericSoundModel(modelInfo.modelUuid, modelInfo.vendorUuid, + modelInfo.modelData); + } + /** * Called when the user clicks the enroll button. * Performs a fresh enrollment. */ public void onEnrollButtonClicked(View v) { postMessage("Loading model: " + mSelectedModelId); - // Generate a fake model to push. - byte[] data = new byte[1024]; - mRandom.nextBytes(data); - UUID modelUuid = getSelectedUuid(); - GenericSoundModel model = new GenericSoundModel(modelUuid, mVendorUuid, data); + + GenericSoundModel model = createNewSoundModel(); boolean status = mSoundTriggerUtil.addOrUpdateSoundModel(model); if (status) { Toast.makeText( - this, "Successfully created sound trigger model UUID=" + modelUuid, + this, "Successfully created sound trigger model UUID=" + model.uuid, Toast.LENGTH_SHORT).show(); } else { - Toast.makeText(this, "Failed to enroll!!!" + modelUuid, Toast.LENGTH_SHORT).show(); + Toast.makeText(this, "Failed to enroll!!!" + model.uuid, Toast.LENGTH_SHORT).show(); } // Test the SoundManager API. @@ -185,11 +283,7 @@ public class TestSoundTriggerActivity extends Activity { Toast.makeText(this, "Sound model not found!!!", Toast.LENGTH_SHORT).show(); return; } - // Generate a fake model to push. - byte[] data = new byte[2048]; - mRandom.nextBytes(data); - GenericSoundModel updated = new GenericSoundModel(soundModel.uuid, - soundModel.vendorUuid, data); + GenericSoundModel updated = createNewSoundModel(); boolean status = mSoundTriggerUtil.addOrUpdateSoundModel(updated); if (status) { Toast.makeText(this, "Successfully re-enrolled, model UUID=" + updated.uuid, @@ -237,29 +331,32 @@ public class TestSoundTriggerActivity extends Activity { public synchronized void onRadioButtonClicked(View view) { // Is the button now checked? boolean checked = ((RadioButton) view).isChecked(); - // Check which radio button was clicked - switch(view.getId()) { - case R.id.model_one: - if (checked) { - mSelectedModelId = 1; - postMessage("Selected model one."); - } - break; - case R.id.model_two: - if (checked) { - mSelectedModelId = 2; - postMessage("Selected model two."); - } - break; - case R.id.model_three: - if (checked) { - mSelectedModelId = 3; - postMessage("Selected model three."); - } - break; + if (checked) { + mSelectedModelId = mModelIdMap.get(view); + ModelInfo modelInfo = mModelInfoMap.get(mSelectedModelId); + postMessage("Selected " + modelInfo.name); + + // Set the play trigger button to be enabled only if we actually have some audio. + mPlayTriggerButton.setEnabled(modelInfo.triggerAudioPlayer != null); } } + public synchronized void onPlayTriggerButtonClicked(View v) { + ModelInfo modelInfo = mModelInfoMap.get(mSelectedModelId); + modelInfo.triggerAudioPlayer.start(); + postMessage("Playing trigger audio for " + modelInfo.name); + } + + // Helper struct for holding information about a model. + private static class ModelInfo { + public String name; + public UUID modelUuid; + public UUID vendorUuid; + public MediaPlayer triggerAudioPlayer; + public SoundTriggerDetector detector; + public byte modelData[]; + }; + // Implementation of SoundTriggerDetector.Callback. public class DetectorCallback extends SoundTriggerDetector.Callback { public void onAvailabilityChanged(int status) { |