1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.example.android.voiceinteractor; 18 19 import static android.media.AudioTrack.PLAYSTATE_PAUSED; 20 import static android.media.AudioTrack.PLAYSTATE_STOPPED; 21 22 import android.app.Activity; 23 import android.content.ComponentName; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.ServiceConnection; 27 import android.media.AudioAttributes; 28 import android.media.AudioFormat; 29 import android.media.AudioManager; 30 import android.media.AudioTrack; 31 import android.os.Bundle; 32 import android.os.IBinder; 33 import android.service.voice.HotwordDetector; 34 import android.util.Log; 35 import android.view.View; 36 import android.widget.Button; 37 38 public class MainActivity extends Activity { 39 private static final String TAG = "VIS"; 40 41 @Override onCreate(Bundle savedInstanceState)42 protected void onCreate(Bundle savedInstanceState) { 43 super.onCreate(savedInstanceState); 44 setContentView(R.layout.main_activity); 45 46 attachClickListener(R.id.buffer1, "1"); 47 attachClickListener(R.id.buffer2, "2"); 48 49 Button button = (Button) findViewById(R.id.startReco); 50 button.setOnClickListener(new View.OnClickListener() { 51 public void onClick(View v) { 52 if (mService == null) { 53 Log.e(TAG, "No service"); 54 return; 55 } 56 mService.mHotwordDetector.startRecognition(); 57 } 58 }); 59 button = (Button) findViewById(R.id.directRecord); 60 button.setOnClickListener(v -> { 61 if (mService == null) { 62 Log.e(TAG, "No service"); 63 return; 64 } 65 mService.mHotwordDetectorCallback.onDetected(mService.mLastPayload, true); 66 }); 67 } 68 attachClickListener(int id, String key)69 private void attachClickListener(int id, String key) { 70 Button button = (Button) findViewById(id); 71 button.setOnClickListener(v -> { 72 if (mService == null) { 73 Log.e(TAG, "No service"); 74 return; 75 } 76 playAudio(mService.mData.getByteArray(key)); 77 }); 78 } 79 80 @Override onStart()81 protected void onStart() { 82 super.onStart(); 83 // Bind to LocalService 84 Intent intent = new Intent(this, SampleVoiceInteractionService.class).setAction("local"); 85 bindService(intent, connection, Context.BIND_AUTO_CREATE); 86 } 87 88 SampleVoiceInteractionService mService; 89 boolean mBound = false; 90 91 private final ServiceConnection connection = new ServiceConnection() { 92 93 @Override 94 public void onServiceConnected(ComponentName className, 95 IBinder service) { 96 // We've bound to LocalService, cast the IBinder and get LocalService instance 97 SampleVoiceInteractionService.LocalBinder binder = (SampleVoiceInteractionService.LocalBinder) service; 98 mService = binder.getService(); 99 mBound = true; 100 Log.i(TAG, "Connected to local service"); 101 } 102 103 @Override 104 public void onServiceDisconnected(ComponentName arg0) { 105 mService = null; 106 mBound = false; 107 } 108 }; 109 playAudio(byte[] buffer)110 private void playAudio(byte[] buffer) { 111 AudioTrack track = new AudioTrack( 112 new AudioAttributes.Builder() 113 .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) 114 .setUsage(AudioAttributes.USAGE_MEDIA) 115 // .setInternalCapturePreset(MediaRecorder.AudioSource.HOTWORD) 116 .build(), 117 new AudioFormat.Builder() 118 .setChannelMask(AudioFormat.CHANNEL_IN_DEFAULT) 119 .setSampleRate(mService.mAudioFormat.getSampleRate()) 120 .setEncoding(mService.mAudioFormat.getEncoding()) 121 .build(), 122 // mService.mAudioFormat, 123 buffer.length, 124 AudioTrack.MODE_STATIC, 125 AudioManager.AUDIO_SESSION_ID_GENERATE 126 ); 127 Log.i(TAG, "track state=" + track.getState()); 128 if (track.getState() == AudioTrack.STATE_UNINITIALIZED) { 129 return; 130 } 131 track.write(buffer, 0, buffer.length); 132 // track.setNotificationMarkerPosition(track.getP) 133 track.play(); 134 // TODO: Doesn't work.. fix the releasing. 135 track.setPlaybackPositionUpdateListener(new AudioTrack.OnPlaybackPositionUpdateListener() { 136 137 @Override 138 public void onMarkerReached(AudioTrack track) { 139 140 } 141 142 @Override 143 public void onPeriodicNotification(AudioTrack track) { 144 if (track.getPlayState() == PLAYSTATE_STOPPED 145 || track.getPlayState() == PLAYSTATE_PAUSED) { 146 Log.i(TAG, "Stopped/paused playback; releasing."); 147 track.release(); 148 } 149 } 150 }); 151 // try { 152 // Thread.sleep(4000); 153 // } catch (InterruptedException e) { 154 // Thread.interrupted(); 155 // throw new RuntimeException(e); 156 // } 157 // track.release(); 158 159 // MediaPlayer player = new MediaPlayer(); 160 // player.setAudioAttributes( 161 // new AudioAttributes.Builder() 162 // .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) 163 // .setUsage(AudioAttributes.USAGE_MEDIA) 164 // .set 165 // .build()); 166 // player.setDataSource( 167 // "data:audio/mp3;base64," 168 //// new ByteArrayMediaSource(buffer) 169 // ); 170 // try { 171 // player.prepare(); 172 // } catch (IOException e) { 173 // Log.e(TAG, "Failed to play: " + e); 174 // } 175 // player.start(); 176 } 177 178 // private static class ByteArrayMediaSource extends MediaDataSource { 179 // final byte[] mData; 180 // 181 // public ByteArrayMediaSource(byte[] data) { 182 // mData = data; 183 // } 184 // 185 // @Override 186 // public int readAt(long position, byte[] buffer, int offset, int size) throws IOException { 187 // if (position >= mData.length) { 188 // return -1; // end of stream 189 // } 190 // if (position + size > mData.length) { 191 // size = (int) (mData.length - position); 192 // } 193 // 194 // System.arraycopy(mData, (int) position, buffer, offset, size); 195 // return size; 196 // } 197 // 198 // @Override 199 // public long getSize() throws IOException { 200 // return 0; 201 // } 202 // 203 // @Override 204 // public void close() throws IOException { 205 // 206 // } 207 // } 208 } 209