Вторая часть из цикла статей о разработке своего говорящего
питомца под 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);
}
Комментариев нет:
Отправить комментарий