Иллюстрированный самоучитель по Java
Синтез
и запись звука в Java 2
Синтез звука заключается в создании
MIDI-последовательности — объекта класса sequence — каким-либо способом: с микрофона,
линейного входа, синтезатора, из файла, или просто создать в программе, как
это делается в листинге 15.18.
Сначала создается пустая последовательность
одним из двух конструкторов:
Sequence(float
divisionType, int resolution)
Sequence(float
divisionType, int resolution, int numTracks)
Первый аргумент divisionType определяет
способ отсчета моментов (ticks) MIDI-событий — это одна из констант:
-
PPQ
(Pulses Per Quarter note) — отсчеты замеряются в долях от длительности звука
в четверть;
-
SMPTE_24, SMPTE_25,
SMPTE_so, SMPTE_30DROP
(Society
of Motion Picture and Television Engineers) — отсчеты в долях одного кадра,
при указанном числе кадров в секунду.
Второй аргумент resolution задает
количество отсчетов в указанную единицу, например,
Sequence seq =
new Sequence)Sequence.PPQ, 10);
задает 10 отсчетов в звуке длительностью
в четверть.
Третий аргумент numTracks определяет
количество дорожек в MIDI-после-довательности.
Потом, если применялся первый конструктор,
в последовательности создается одна или несколько дорожек:
Track
tr = seq.createTrack()
;
Если применялся второй конструктор,
то надб получить уже созданные конструктором дорожки:
Track[]
trs = seq.getTracks();
Затем дорожки заполняются MIDI-событиями
с помощью MIDl-сообще-ний. Есть несколько типов сообщений для разных типов событий.
Наиболее часто встречаются сообщения типа shortMessage, которые создаются конструктором
по умолчанию и потом заполняются методом setMessageo:
ShortMessage msg
= new ShortMessage();
rasg.setMessage(ShortMessage.NOTEJDN,
60, 93);
Первый аргумент указывает тип сообщения:
NOTE_ON
— начать звучание,
NOTE_OFF
— прекратить звучание и т. д. Второй аргумент для типа
NOTE_ОN
показывает высоту звука, в стандарте MIDI это числа от 0 до 127, 60 — нота "до"
первой октавы. Третий аргумент означает "скорость" нажатия клавиши
MIDI-инструмента и по-разному понимается различными устройствами.
Далее создается MIDI-событие:
MidiEvent me =
new MidiEvent{msg, ticks);
Первый аргумент конструктора msg
— это сообщение, второй аргумент ticks — время наступления события (в нашем
примере проигрывания ноты "до") в единицах последовательности seq
(в нашем примере в десятых долях четверти). Время отсчитывается от начала проигрывания
последовательности.
Наконец, событие заносится на дорожку:
tr.add(me);
Указанные действия продолжаются,
пока все дорожки не будут заполнены всеми событиями. В листинге 15.18 это делается
в цикле, но обычно MIDI-события создаются в методах обработки нажатия клавиш
на обычной или специальной MIDI-клавиатуре. Еще один способ — вывести на экран
изображение клавиатуры и создавать MIDI-собьгшя в методах обработки нажатий
кнопки мыши на этой клавиатуре.
После создания последовательности
ее можно проиграть, как в листинге 15.17, или записать в файл или выходной поток.
Для этого вместо метода start() надо применить метод startRecording (), который
одновременно и проигрывает последовательность, и подготавливает ее к записи,
которую осуществляют статические методы:
write(Sequence
in, int type, File out)
write(Sequence
in, int type, OutputStream out)
Второй аргумент type задает тип
MIDI-файла, который лучше всего определить для заданной последовательности seq
статическим методом getMidiFiieTypes(seq). Данный метод возвращает массив возможных
типов. Надо воспользоваться нулевым элементом массива, ,Все это. показало в
листинге 15.18.
Листинг 15.18.
Создание MIDI-последовательности нот звукоряда
import
javax.sound.midi. *;
import java.io.*;
class SynMIDI
{
SynMIDI() {
play(synth());
}
public Sequence
synth(){
Sequence seq
= null;
try{
// Последовательность
будет отсчитывать по 10
// MIDI-событий
на Звук длительйостью в четверть
seq = new Sequence(Sequence.PPQ,
10);
// Создаем в последовательности
одну дорожку
Track tr = seq.createTrack();
for (int k = 0;
k < 100; k++){
ShortMessage msg
= new ShortMessage();
// Пробегаем MIDI-ноты
от номера 10 до 109
msg.setMessage(ShortMessage.NOTE_ON,
10+k, 93);
// Будем проигрывать
ноты через каждые 5 отсчетов
tr.add(new MidiEvent(msg,
5*k));
msg = null;
}
} catch (Exception
e) {
System, err.printing
"From synth(): "+e);
System.exit (0);
}
return seq;
}
public void play
(Sequence seq) {
try{
Sequencer sequencer
= MidiSystem.getSequencer();
if (sequencer
= null){
System.err.println("Sequencer
is not supported");
System.exit(0);
}
sequencer.open();
sequencer.setSequence(seq);
sequencer.startRecording();
int[] type = MidiSystem.getMidiFileTypes(seq);
MidiSystem.write(seq,
type[0], new File("gammas.mid"));
}catch(Exception
e) {
System.err.println("From
play(): " + e);
}
}
public static
void main(String[] args)(
new SynMIDI();
}
}
К сожалению, объем книги не позволяет
коснуться темы о работе с синтезатором (synthesizer), микширования звука, работы
с несколькими инструментами и прочих возможностей Java Sound API. В документации
SUN J2SDK, в каталоге docs\guide\sound\prog_guide, есть подробное руководство
программиста, а в каталоге demo\sound\src лежат исходные тексты синтезатора,
использующего Java Sound API.