среда, 19 сентября 2012 г.

Talking Pets: Разработка питомца под Android – часть 2


Talking Pets: Разработка питомца под Android – часть 2
Вторая часть из цикла статей о разработке своего говорящего питомца под 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);
Сначала необходимо инициализировать AudioTrack:
 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;
 }

Основным методом является run, в котором мы записываем звуковые данные в AudioTrack и воспроизводим их.
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();
  }
 }
  • Работа описанного алгоритма
Для правильной работы алгоритма я использую отправку сообщений с помощью Handler.
Есть  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);
    }




Ссылки

  • Исходные коды данного проекта можно скачать отсюда: zip
  • Пример говорящего питомца под AndroidТарабаня
  • Talking Pets: Разработка питомца под Android – часть 1, часть 3

Комментариев нет:

Отправить комментарий