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