Играйте в синусоиду до бесконечности, пока не скажете остановиться в Яве
Я пытаюсь сделать кусок кода, который может воспроизводить синусоидальную волну, пока не получит приказ остановиться. Я хочу иметь возможность воспроизводить частоту MIDI-ноты. Библиотека MIDI в java чувствует себя немного неаккуратно (когда я говорю ей, чтобы играть, есть небольшая задержка между нажатием клавиши и игрой ноты).
Я видел пример, когда вы генерируете синусоидальную волну определенной длины, а затем воспроизводите ее, давая SourceDataLine
байтовый массив. Это сработало, но байтовый массив может быть только таким длинным, и он в конечном итоге перестанет играть.
Моя следующая идея состояла в том, чтобы постоянно записывать один байт в строку, вычислять следующую строку и продолжать. Вот мой код:
int i = 0;
int sampleRate = 8000;
int freq = 440;
while (true) {
double samplingInterval = (double) (sampleRate / freq);
double angle = (2.0 * Math.PI * i) / samplingInterval;
byte toPlay = (byte) (Math.sin(angle) * 127);
line.write(new byte[] {toPlay}, 0, 1);
i++;
}
Я надеялся, что это даст мне постоянный выход с частотой 440 Гц, но это дало мне эту ошибку:
java.lang.IllegalArgumentException: illegal request to write non-integral number of frames (1 bytes, frameSize = 2 bytes)
Если нет, есть ли способ ускорить MIDI-библиотеку в Java или я только что сделал глупую ошибку? Заранее спасибо.
2 ответа
Кадр аудио представляет собой один или несколько одновременно синхронизированных аудиосэмплов. В вашем случае стерео (frames ==2
). В потоке они обычно чередуются (например, L, R, L, R....).
Вы можете исправить это, написав каждый образец дважды.
В то время как Java (или любой другой язык JITd со сборкой мусора Stop-the-World) никогда не является выбором реализации для аудио программного обеспечения с низкой задержкой, я подозреваю, что обнаруживаемая вами "небрежность" на самом деле является длительным периодом аудио буфера: настройка по умолчанию за буферный период может составить существенную долю секунды.
Для этой конкретной идеи вы можете использовать
while (true) {
double samplingInterval = (double) (sampleRate / freq);
double angle = (2.0 * Math.PI * i) / samplingInterval;
byte toPlay = (byte) (Math.sin(angle) * 127);
byte[] data=new byte[1024];
for(int j=0; j<data.length; j++) data[j]=toPlay;
line.write(data, 0, data.length);
i++;
}
или длины 4 будет достаточно.