Шрифт:
Как видно, версия последовательности равна 1, а темп – 30. Темп измеряется количеством ударов в минуту, но когда вы задаете темп байтовым значением, вы должны разделить значение бита на 4. В данном случае темп равен 120 ударам в минуту.
Константа BLOCK_START открывает блок «А». Обозначение «А» ничего особенного не значит, оно просто выделяет фрагмент тональной последовательности. В примере программируется песня «Mary Had a Little Lamb» («У Мэри был маленький ягненок»), а фрагменты нот воспроизводятся в следующем порядке: A-B-A-C. Иначе говоря, блок «А» воспроизводится дважды: один раз в начале, а затем после фрагмента B. Поскольку фрагменты B и C не повторяются, то нет необходимости выделять их в отдельные блоки.
Каждая нота в последовательности определяется парой значений, которые задают высоту и длительность звука. Например, в блоке А нота Е4 имеет длительность 8, что соответствует одной восьмой. В таблице 8.3 приведены значения наиболее часто используемых длительностей.
Таблица 8.3. Длительности нот и соответствующие им значенияЧтобы лучше разобраться с последовательностью нот, взгляните на рис. 8.2, на котором представлены ноты и их соответствие тоновым данным.
Вернемся к тоновой последовательности для этой песни. Я не объяснил, как используется переменная rest. Эта переменная используется для установления паузы в последовательности. Константа SILENCE означает тишину в тоновой последовательности. Ниже приведено объявление переменной rest:
byte rest = ToneControl.SILENCE;
После того как вы задали тоновую последовательность массивом типа byte, вы можете воспроизвести рингтон. Сначала необходимо создать проигрыватель, который сможет воспроизвести тоновую последовательность. Вот как можно это сделать:
Player tonePlayer = Manager.createPlayer(Manager.TONE_DEVICE_LOCATOR);
Константа TONE_DEVICE_LOCATOR говорит о том, что вы создаете проигрыватель для воспроизведения тонов. Как только вы создали проигрыватель, необходимо зарезервировать для него ресурсы:
tonePlayer.realize
Чтобы установить последовательность на воспроизведение, необходимо получить доступ к управлению тонами проигрывателя. Все, что для этого нужно, – вызвать метод getControl:
ToneControl toneControl =(ToneControl)tonePlayer.getControl(«ToneControl»);
Получив тоновый контроль, смело вызывайте метод setSequence, чтобы задать воспроизводимую последовательность. При этом понадобится передать созданный ранее массив типа byte:
toneControl.setSequence(marylambSequence);
И, наконец, чтобы воспроизвести последовательность, необходимо вызвать метод start:
tonePlayer.start;
Чтобы удостовериться, что при закрытии мидлета воспроизведение остановится, необходимо закрыть проигрыватель. Для этого необходимо вызвать метод close:
tonePlayer.close;
Важно отметить, что большинство методов для работы с медиа-данными могут вызывать исключения, а следовательно, их стоит помещать в конструкцию try-catch. Ниже приведен пример того, как можно создать проигрыватель и воспроизвести последовательность тонов:
try {
Player tonePlayer = Manager.createPlayer(Manager.TONE_DEVICE_LOCATOR);
tonePlayer.realize;
ToneControl.toneControl = (ToneControl)toneControl.getControl("ToneControl");
toneControl.setSequence(marylambSequence);
tonePlayer.start;
}
catch (IOException ioe) {
}
catch (MediaException me) {
}Хотя в MIDP 2.0 Media API, несомненно, больше возможностей работы с тонами, я думаю, что стоит пока остановиться и посмотреть, как это можно применить в реальном мидлете. Читайте дальше, и вы узнаете, как добавить космическую музыку в игру UFO.
Создание программы UFO 3
Если вы вспомните, то в программе UFO, которую мы создавали в предыдущих главах, вы управляете летающим объектом, чтобы он не столкнулся с астероидами.
В этой программе много потенциальных возможностей для применения тонов и звуковых последовательностей. Отдельные звуки целесообразно использовать для сопровождения движения НЛО и столкновения с астероидами, в то время как тоновую последовательность можно использовать для воспроизведения музыки. Итак, мы выделили три типа звуков, которые будем внедрять в программу UFO 3:
► звук, сообщающий о нажатии клавиши;
► звук, сообщающий о столкновении НЛО с астероидом;
► тоновая последовательность, используемая в качестве музыки.
В следующем разделе будет подробно рассмотрен код, выполняющий это.
Написание программного кода
Как вы узнали ранее, чтобы воспроизвести тон, нужен совсем небольшой код. Пример UFO 3 использует такой код для воспроизведения звука при нажатии на одну из клавиш со стрелками. Наиболее подходящий тон для сопровождения движения – это G4, нота G той же октавы, что и C4. Ниже приведен код, определяющий переменную G4, а затем воспроизводящий ее:byte G4 = (byte)(ToneControl.C4 + 7);
try {
Manager.playTone(G4, 100, 50);
}
catch (Exception e) {
}Звук перемещения НЛО воспроизводится в течение 100 миллисекунд (1/10 секунды), громкость составляет 50 %. Тон, сообщающий о столкновении, воспроизводится аналогично:
try {
Manager.playTone(ToneControl.C4 – 12, 500, 100);
}
catch (Exception e) {
}В случае взрыва воспроизводится звук C3, который расположен на одну октаву ниже, чем средний C (C4). Вместо того чтобы создавать переменную C3, достаточно верно указать смещение относительно ноты C4. Звук взрыва воспроизводится в течение 500 миллисекунд с громкостью 100 %. Это необходимо, потому что более низкий звук сложнее услышать.
Код, воспроизводящий отдельные тоны, находится в методе update класса UFOCanvas мидлета UFO 3. Этот класс также содержит код, который отвечает за воспроизведение тоновой последовательности в мидлете. В листинге 8.2 приведен код нового и улучшенного метода update.
Листинг 8.2. Метод update класса UFOCanvas, который воспроизводит тоны мидлета UFO 3private void update {
// случайное воспроизведение звуков
if (rand.nextInt % 500 == 0)
playTune;
// обработка пользовательского ввода для управления НЛО
byte G4 = (byte)(ToneControl.C4 + 7); //Тон G4 определен относительно тона C(C4)
int keyState = getKeyStates;
if ((keyState & LEFT_PRESSED) != 0) {
// воспроизвести звук, означающий перемещение
try {
Manager.playTone(G4, 100, 50); //Воспроизведение тона G4 на громкости 50% в течение 1/10 секунды в ответ на нажатие клавиши
}
catch (Exception e) {
}
ufoXSpeed–;
}
else if ((keyState & RIGHT_PRESSED) != 0) {
// воспроизвести звук, означающий перемещение
try {
Manager.playTone(G4, 100, 50);
}
catch (Exception e) {
}
ufoXSpeed++;
}
if ((keyState & UP_PRESSED) != 0) {
// воспроизвести звук, означающий перемещение
try {
Manager.playTone(G4, 100, 50);
}
catch (Exception e) {
}
ufoYSpeed–;
}
else if ((keyState & DOWN_PRESSED) != 0) {
// воспроизвести звук, означающий перемещение
try {
Manager.playTone(G4, 100, 50);
}
catch (Exception e) {
}
ufoYSpeed++;
}
ufoXSpeed = Math.min(Math.max(ufoXSpeed, -8), 8);
ufoYSpeed = Math.min(Math.max(ufoYSpeed, -8), 8);
// переместить спрайт НЛО
ufoSprite.move(ufoXSpeed, ufoYSpeed);
checkBounds(ufoSprite);
// обновить спрайт астероида
for (int i = 0; i < 3; i++) {
// переместить спрайт астероида
roidSprite[i].move(i + 1, 1 – i);
checkBounds(roidSprite[i]);
// увеличить номер спрайта астероида
if (i == 1)
roidSprite[i].prevFrame;
else
roidSprite[i].nextFrame;
// проверить столкновение между НЛО и астероидом
if (ufoSprite.collidesWith(roidSprite[i], true)) {
// воспроизвести звук столкновения
try {
Manager.playTone(ToneControl.C4 – 12, 500, 100); //Воспроизвести низкий звук в течение половины секунды на полной громкости при столкновении
}
catch (Exception e) {
}
// восстановить начальное положение НЛО и его скорость
ufoSprite.setPosition((getWidth – ufoSprite.getWidth) / 2,
(getHeight – ufoSprite.getHeight) / 2);
ufoXSpeed = ufoYSpeed = 0;
for (int j = 0; j < 3; j++)
roidSprite[j].setPosition(0, 0);
// нет необходимости обновлять спрайты астероидов
break;
}
}
}Если вы внимательно изучите код обработки пользовательского ввода, находящийся в начале метода, то узнаете код, который воспроизводит звук как реакцию на перемещение объекта. Звук столкновения воспроизводится ближе к концу метода. Возможно, самый интересный код этого метода располагается в самом начале, когда воспроизводится тоновая последовательность через произвольные интервалы времени, для чего вызывается метод playTune. Важно понять, как создается этот метод.
Метод initTune отвечает за инициализацию тоновой последовательности в мидлете UFO 3 (листинг 8.3).
Листинг 8.3. Метод initTune в классе UFOCanvas инициализирует тоновую последовательностьprivate void initTune {
byte tempo = 30; // 120bpm //Установить темп и длину нот
byte d4 = 16; // 1/4 ноты
byte d2 = 32; // 1/2 ноты
byte C4 = ToneControl.C4; //Определить последовательность нот на основании С4, а также тишину
byte A6 = (byte)(C4 + 21);
byte B6 = (byte)(C4 + 23);
byte G5 = (byte)(C4 + 19);
byte G4 = (byte)(C4 + 7);
byte D5 = (byte)(C4 + 14);
byte rest = ToneControl.SILENCE;
byte[] encountersSequence = {
ToneControl.VERSION, 1,
ToneControl.TEMPO, tempo,
ToneControl.BLOCK_START, 0,
A6,d4, B6,d4, G5,d4, G4,d4, D5,d2, rest,d2, //Мелодия из фильма «Близкие контакты»
ToneControl.BLOCK_END, 0,
ToneControl.PLAY_BLOCK, 0,
ToneControl.PLAY_BLOCK, 0,
};
try {
// создать тоновый проигрыватель
tonePlayer = Manager.createPlayer(Manager.TONE_DEVICE_LOCATOR);
tonePlayer.realize;
// создать тоновый проигрыватель и установить тоновую последовательность
ToneControl toneControl = (ToneControl)tonePlayer.getControl("ToneControl");
toneControl.setSequence(encountersSequence);
}
catch (IOException ioe) {
}
catch (MediaException me) {
}
}