Аудиогенератор сигналов прослушивания с гироскопа - PullRequest
0 голосов
/ 10 марта 2019

Я пытаюсь создать генератор сигналов, частотой которого можно управлять по данным датчика гироскопа (перемещая телефон).Проблема в том, что я звоню AudioTrack внутри onSensorChanged, и у меня есть "щелчки" на выходе, потому что каждый раз, когда обновляется сенсор, я звоню

stopSinus();
setSinus(Frequency);
startSinus();

, это определяется следующим образом

 siAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, SAMPLE_RATE,
 AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT,
 buffsize, AudioTrack.MODE_STATIC);

public void stopSinus() {
        int checkPlay = siAudioTrack.getPlayState();
        if (checkPlay == 3) {  //
         siaudioTrack.stop();
         }
}

public void startSinus() {
        siAudioTrack.reloadStaticData();
        siAudioTrack.setLoopPoints(0, sampleCount, -1);  
        siAudioTrack.play();

}

public void setSinus(int frequency) {

    sampleCount = (int) ((float) SAMPLE_RATE / frequency);
    short sample[] = new short[sampleCount];
    int amplitude = 32767;
    double twoPi = 2 * Math.PI;;
    double phase = 0.0;

    for (int i = 0; i < sampleCount; i++) {
        sample[i] = (short) (amplitude * Math.sin(phase));
        phase += twoPi * frequency / SAMPLE_RATE;
    }
    siAudioTrack.write(sample, 0, sampleCount);
}

onSensorChanged изменяется несколько раз в секунду, и это остановит генератор в середине волны - отсюда и идут "щелчки".

Может кто-нибудь сказать мне, как остановитьгенератор после одной целой волны?Как узнать, когда phase os проходит ноль?Или какое-то другое решение?

1 Ответ

0 голосов
/ 13 марта 2019

Нет практического способа определить, когда фаза, подаваемая на динамик, проходит 0. Лучше просто отказаться от этого.

Вот «другое» решение, которое, я думаю, вы найдете, работает намного лучше:

  1. Переключиться на MODE_STREAM. Для минимальной задержки используйте наименьший доступный размер буфера (getMinBufferSize()).
  2. Используйте выделенный поток для подачи сигнала на AudioTrack. У этого потока есть одно задание: записывать небольшие фрагменты сигнала в AudioTrack, основываясь на текущей частоте. (Вы также можете использовать основной поток и постоянно перетаскивать AudioTrack с неблокирующими записями, но, по моему мнению, использование отдельного потока более элегантно. Поток будет проводить большую часть своего времени, застряв в блокирующем вызове write() Это означает, что буфер остается максимально заполненным с минимальными затратами.)
  3. Избавьтесь от startSinus() и stopSinus(). Начните воспроизведение аудио в начале и не останавливайте его, пока не выйдете из приложения.
  4. Измените setSinus(), чтобы он просто передавал текущее значение частоты потоку, описанному в шаге 2. Используйте любой стандартный механизм связи между потоками.
  5. Когда пришло время выходить из приложения, используйте стандартный механизм связи между потоками, чтобы сообщить потоку, что он должен завершиться, затем join() его, очистить и т. Д.

Дополнительные детали:

  • Сделайте свой write()s маленьким. Может быть, только 100 образцов за раз. Это помогает минимизировать задержку при сохранении желаемого поведения блокировки и избавляет от необходимости беспокоиться о том, сколько свободного места доступно в AudioTrack.
  • В рабочем потоке используйте float для отслеживания фазы (так же, как вы делаете сейчас). Увеличивайте фазу на основе частоты, но не позволяйте ей становиться слишком большой! Держите его в диапазоне от 0 до 2 * PI, позволяя обернуть его (в противном случае из-за квантования будут появляться большие фазовые ошибки, а частота неожиданно изменится). Очевидно, вы не сбрасываете phase после заполнения каждого крошечного буфера на 100 выборок. Вы просто позволяете ему продолжать кататься.
...