Воспроизведение MIDI звуков на языке JAVA

в 8:53, , рубрики: java, MIDI, звук, Работа со звуком, метки: , ,

Прежде, чем перейти к сути, я немного расскажу вам о компьютерном звуке.

Существует два основных формата воспроизведения звуков компьютером:
цифровой (WAV-формат) и синтезированный (MIDI).

Цифровой звук является основным стандартом компьютерного звука сегодня. Именно оцифрованный звук вы слышите, проигрывая композиции в mp3 формате или прослушивая компакт-диски, просматривая фильм или играя в комьютерные игры.

Оцифрованный звук представляет собой набор битов, который последовательно описывает значение уровня амплитуды звуковой волны в каждый момент времени звучания. При его воспроизведении звуковая карта лишь переводит «цифровой» звук в привычную нам «аналоговую» форму.

Но существует и другой вид компьютерного звука — синтезированный (MIDI)

Звуковая карта может служить музыкальным синтезатором, способном воспроизводить звучания до 128-ми различных музыкальных инструментов. Качество и принцип имитации инструментов зависит от вашей звуковой карты. Она может пытаться смоделировать звучание инструмента совокупностью нескольких FM (частотных) генераторов простых частот, для каждой из которых задана амплитуда, частота, фаза и куча других параметров. Но чаще она обращается к хранящимся в ней «банкам данных» образцов звучания настоящих инструментов. (Возможно комбинирование этих двух способов)

Чтобы синтезатор воспроизвел нужный звук, необходимо передать ему специальную команду. Совокупность таких команд описана стандартом MIDI.
MIDI это акроним от Musical Instruments Digital Interface, что в буквальном переводе — цифровой интерфейс музыкальных инструментов.

При помощи этих команд мы можем «сказать» синтезатору звуковой платы каким инструментом ту или иную ноту мы хотим, что бы он воспроизвел. Например Фа-диез на рояле или любом другом из 128-ми инструментов.

Допустим для каких-то целей вам понадобилось из java научиться воспроизводить ноты. И вы решили узнать, как заставить синтезатор вашей звуковой платы это делать. Окунувшись поисковиком в информационное море интернета, вы неожиданно поймете, что толковой информации по этому вопросу найти непросто.

Скажу, что существует достаточное количество библиотек для работы с midi. И все они обещают, что с их помощью процес программирования музыки будет проще. Я столкнулся с двумя такими: jfugue и jMusic. В Ютубе есть наглядные руководства о их применении. В каждой из подобных библиотек были выдуманы свои способы и правила. Чем более экзотическая библиотека, тем меньше информации и примеров. Так же придется довериться правильности и точности их работы.
Из многих соображений полагаю, что лучше начать изучение и научиться применять в начале стандартную библиотеку: javax/sound/midi

Тем, кто впервые сталкивается с этой темой, моя статья призвана помочь сделать первые шаги. Я научу вас самым базовым навыкам работы с midi звуком. А дальше вы уже сами сможете расширить их применение и, если потребуется, легко дополните информацию из описания библиотеки и примерами из интернета.

Итак, все что вам для начала нужно — научиться воспроизводить ноты.
Ваше желание включает некоторую дополнительную информацию — название ноты (номер) и длительность звучания. А так же инструмент, который вы хотите чтобы её сыграл.
Желание совершенно просто и ясно сформулировано: «Хочу, чтобы прозвучала такая-то нота, определенное время и выбранным инструментом.» Однако сходу найти соответствующую информацию, как это сделать, вам будет сложно. Вот по этому эта статья и написана — чтобы вы не тратили свое время а сразу получили то, что нужно. И начали применять.

Номера нот вы можете определить по данной таблице:
Воспроизведение MIDI звуков на языке JAVA

А номера инструментов можно посмотреть здесь

Перед непосредственным программированием воспроизведения потребуется некоторая подготовка. А именно, вам понадобится получить объект синтезатора (Synthesizer) и отрыть его. Затем получить доступ его каналам.
Звучит, возможно, не совсем понятно, но на практике всё просто:

     Synthesizer synth = MidiSystem.getSynthesizer();
     synth.open();
     MidiChannel[] channels = synth.getChannels();

Канал можно представить как универсального музыканта, который способен играть на любом из 128-ми инструментов. Вы располагаете 16-тью такими каналами.
Вот в принципе и вся подготовка. Далее необходимо научиться давать команды музыкантам.
Допустим вы хотите, чтоб звучала нота Фа первой октавы. Для этого, посмотрев таблицу с номерами нот, выясняете, что её номер 65. Выбираем одного из 16-ти музыкантов. Допустим под номером 0 (channels[0]). Воспроизведение происходит по команде noteOn.
Она принимает 2 параметра: MIDI номер ноты (от 0 до 127) и громкость звучания (так же до 127)
Выглядеть это будет так:

     channels[0].noteOn(65, 80);

По этой команде музыкант под номером «ноль» начнет воспроизведение ноты Фа первой октавы. 80 — это громкость. Это можно представить, что музыкант нажал клавишу синтезатора и льется звук.
Думаю, вы догадались, что для прекращения звучания так же потребуется дать команду. Т.е. чтобы конкретно этот музыкант отпустил нажатую клавишу. Это выполняется командой noteOff.
Таким образом:

     channels[0].noteOff(65);

Между этими командами потребуется сделать паузу, от которой и будет зависеть длительность звучания.
Вот, собственно, и всё. И не нужны никакие сторонние библиотеки. Всё необходимое теперь вы сможете удобным способом написать под себя сами. Осталось открыть вам последний секрет — научить волшебным словам, которые заставят «музыкантов» взять другой муз. инструмент.
Напомню, что выбрать инструмент можно здесь. Любому из 16-ти каналов вы можете назначить один из 128-ми инструментов. По умолчанию почти все каналы используют фортепиано.
Так производится назначение «нулевому» каналу инструмент «скрипка»:

    channels[0].programChange(41);

Понадобится импортировать следующие стандартные пакеты:

    import javax.sound.midi.MidiChannel;
    import javax.sound.midi.MidiSystem;
    import javax.sound.midi.Synthesizer;

Итак, код воспроизводящий звук будет выглядеть следующим образом:

       try {
            Synthesizer synth = MidiSystem.getSynthesizer();
            synth.open();
            MidiChannel[] channels = synth.getChannels();
            channels[0].programChange(41);
            channels[0].noteOn(65, 80);
            Thread.sleep(1000); // in milliseconds
            channels[0].noteOff(65);
            synth.close();
       }  catch (Exception e) {
            e.printStackTrace();
       } 

Далее, как уже было сказано, возможности по программированию извлечения звука ограничены только вашей фантазией.

Для примитивной демонстрации создадим свой метод воспроизведения звуков, который нажатие, паузу и отпускание будет выполнять одной командой. Отведем для этих целей отдельный класс, структура и функционал которого не нуждаются в пояснении:

package music.player;

import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sound.midi.MidiChannel;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Synthesizer;

public class Player {

    private MidiChannel[] channels = null;
    private Synthesizer synth = null;

    public Player() {
        try {
            synth = MidiSystem.getSynthesizer();
            synth.open();
            channels = synth.getChannels();
            channels[0].programChange(41);
        } catch (MidiUnavailableException ex) {
            Logger.getLogger(Player.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void close() {
        synth.close();
    }

    public void playSound(int channel, int duration, int volume, int... notes) {
        for (int note : notes) {
            channels[channel].noteOn(note, volume);
        }
        try {
            Thread.sleep(duration);
        } catch (InterruptedException ex) {
            Logger.getLogger(Player.class.getName()).log(Level.SEVERE, null, ex);
        }
        for (int note : notes) {
            channels[channel].noteOff(note);
        }
    }
}

В конструкторе класса создается и открывается синтезатор. Получаем массив каналов и первому (нулевому) назначаем инструмент «скрипка».
Метод playSound получает на вход номер канала, длительность звучания, громкость и последовательность нот, которые прозвучат одновременно. Реализация предельно проста — воспроизводятся все входящие ноты, удерживается интервал длительности и затем все выключаются.
Вот пример использования данного класса — воспроизводятся три аккорда:

    Player player = new Player();
    player.playSound(0, 1000, 80, 69, 72, 76);
    player.playSound(0, 1000, 80, 69, 74, 77);
    player.playSound(0, 1000, 80, 67, 71, 74);
    player.close();

Думаю, у вас не возникло сложностей с пониманием изложенного материала. Буду рад если эта статья сэкономит ваше время и силы, позволив с легкостью приступить к программированию воспроизведения MIDI звука на JAVA.

Автор: Alexander2014

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js