Вторая часть из цикла статей о разработке своего говорящего
питомца под Android. В
этой статье мы рассмотрим, как воспроизвести записанные данные. Также создадим
рабочее приложение, которое будет постоянно записывать и воспроизводить речь.
Что использовалось
AudioTrack может работать в двух режимах: статический или
потоковый.
В потоковом режиме, приложение пишет непрерывный поток
данных AudioTrack, используя один из методов записи.
Статический режим должен быть выбран при работе с небольшими
звуками, которые помещаются в памяти, и которые нужно проиграть с минимальным
временем ожидания. Такой режим предпочтительнее для звуков пользовательского
интерфейса и игр, которые часто проигрываются и с наименьшими возможными
затратами.
После создания, объект AudioTrack инициализирует связанный
аудио буфер. Размер этого буфера, указанного в ходе создания, определяет, как
долго AudioTrack может проигрывать звук.
Практика
- Воспроизведение речи
Основные константы и атрибуты класса:
private static int FREQUENCY = 40000; // Частота записи private static final int CHANNEL = AudioFormat.CHANNEL_CONFIGURATION_STEREO; // Моно или стерео канал private static final int ENCODING = AudioFormat.ENCODING_PCM_16BIT; // Кодирование данных // Частота дискретизации private static final int[] mSampleRates = new int[] { 44100, 22050, 16000, 11025, 8000 }; private boolean isPlay = false; // Проверка, проигрывается ли звук // Данные для воспроизведения private int size; private byte[] array; private int bufSize = AudioTrack.getMinBufferSize(FREQUENCY, CHANNEL, ENCODING); // Размер буфера private Handler handler;// Для отправки сообщений UI потоку приложения // Используется для воспроизведения звуковых данных private AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_SYSTEM, FREQUENCY, CHANNEL, ENCODING, bufSize, AudioTrack.MODE_STREAM);
public Playback(Handler handler) { DebugLog.i(TAG, "Playback()"); this.handler = handler; this.audioTrack = findAudioTrack(); } /** * Используется для поиска возможного AudioTrack с заданными параметрами для устройства */ public AudioTrack findAudioTrack() { DebugLog.i(TAG, "findAudioTrack()"); // Поиск минимального размера буфера this.bufSize = AudioTrack.getMinBufferSize(FREQUENCY, CHANNEL, ENCODING); if (this.bufSize != AudioRecord.ERROR_BAD_VALUE) { // В данном случае будет создан AudioTrack с частотой, большей, чем при записи. // За счет этого получается более "писклявый" голос персонажа. AudioTrack at = new AudioTrack(AudioManager.STREAM_SYSTEM, FREQUENCY, CHANNEL, ENCODING, bufSize, AudioTrack.MODE_STREAM); if (at.getState() == AudioRecord.STATE_INITIALIZED) return at; } // Если создать AudioTrack не удалось, то пробуем его создать с частотой, большей ровно в 2 раза, чем при записи FREQUENCY = 2 * Record.FREQUENCY; this.bufSize = AudioTrack.getMinBufferSize(FREQUENCY, CHANNEL, ENCODING); if (this.bufSize != AudioRecord.ERROR_BAD_VALUE) { AudioTrack at = new AudioTrack(AudioManager.STREAM_SYSTEM, FREQUENCY, CHANNEL, ENCODING, this.bufSize, AudioTrack.MODE_STREAM); if (at.getState() == AudioRecord.STATE_INITIALIZED) return at; } // Если все попытки без успешны, пробуем создать хоть какой-нибудь AudioTrack for (int rate : mSampleRates) { try { this.bufSize = AudioTrack.getMinBufferSize(rate, CHANNEL, ENCODING); if (this.bufSize != AudioRecord.ERROR_BAD_VALUE) { FREQUENCY = rate; AudioTrack at = new AudioTrack(AudioManager.STREAM_SYSTEM, FREQUENCY, CHANNEL, ENCODING, this.bufSize, AudioTrack.MODE_STREAM); if (at.getState() == AudioTrack.STATE_INITIALIZED) return at; } } catch (Exception e) { e.printStackTrace(); } } return null; }
public void run() { try { DebugLog.i(TAG, "run()"); isPlay = true; // воспроизведение речи audioTrack.play(); //sleep(300); audioTrack.write(array, 0, size); audioTrack.stop(); audioTrack.release(); if (isPlay) { // запуск записи речи handler.sendEmptyMessage(Constants.MSG_RECORD); } isPlay = false; } catch (Exception e) { isPlay = false; e.printStackTrace(); } }
- Работа описанного алгоритма
Есть 4 состояния:
- запись данных;
- запись речи, анимация;
- воспроизведение данных;
- воспроизведение, анимация.
// обработка сообщений от объектов private Handler handler = new Handler() { public void handleMessage(Message msg) { DebugLog.i(TAG, "msg.what = " + msg.what); if (isPause) return; // вывод состояния if (msg.what >= 0 && msg.what < array_condition.length) txt_condition.setText(array_condition[msg.what]); switch (msg.what) { case Constants.MSG_RECORD: // запись речи if ((record != null) && (!record.isRec())) record.startRecord(); break; case Constants.MSG_PLAYBACK: // Воспроизведение речи playback = new Playback(handler); int size = msg.getData().getInt(Constants.DATA_SIZE); byte[] array = msg.getData().getByteArray(Constants.DATA_ARRAY); playback.setData(size, array); playback.start(); break; case Constants.MSG_LISTEN: // персонаж слушает break; } } };
- Основное Activity
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); DebugLog.i(TAG, "onCreate()"); this.txt_condition = (TextView)findViewById(R.id.txt_condition); this.array_condition = getResources().getStringArray(R.array.array_condition); PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); this.wl = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, "TAG"); } protected void onPause() { super.onPause(); DebugLog.i(TAG, "onPause()"); this.wl.release(); isPause = true; if ((this.playback != null) && (this.playback.isPlay())) { this.playback.stopPlay(); } // останавливаем запись if (this.record != null) { this.record.stopRecord(); this.record.close(); } } protected void onResume(){ super.onResume(); DebugLog.i(TAG, "onResume()"); this.wl.acquire(); isPause = false; // запускаем поток для записи речи this.record = new Record(handler); this.record.start(); handler.sendEmptyMessage(Constants.MSG_RECORD); }
Комментариев нет:
Отправить комментарий