Главная » Программирование звука » Постобработка потока событий MIDI

0

После  прочтения  файла  MIDI  и  соответствующего  распределения  всех  имеющихся  на  дорожках  событий  стоит  один  раз  быстро  пройти  по  потоку  событий для  упрощения  предстоящего  воспроизведения.  Этого  нельзя  сделать  во  время чтения файла, так как нам необходимо обрабатывать поток событий в хронологическом порядке.

Чтобы   не   жонглировать   синхронизирующей   информацией   MIDI   во   время воспроизведения,  я  воспользуюсь  открывшейся  возможностью  и  преобразую  все задержки  в  количество  отсчетов.  Эта  модификация  упрощает  учетную  деятельность  в  методе  GetSamples.  Заданную  задержку  события  вряд  ли  удастся  точно преобразовать  в  целое  количество  аудиоотсчетов.  Здесь  необходимо  аккуратно работать с дробными отсчетами, чтобы общая синхронизация оставалась точной.

Давайте    также    воспользуемся    возможностью    сбора   кое-какой   статистики опотоке  событий  MIDI.  Мы  проследим  за  громкостью  звучания  нот  и  определим максимальную общую громкость произведения. Ha основе этой информации я могу

отмасштабировать  амплитуду  воспроизводимого  сигнала  и  избежать  переполне-

ния.  Чтобы  не  усложнять  задачу,  мы  не  будем  отслеживать  основную  громкость, громкость  канала  и  экспрессию  (временное  изменение  громкости  канала,  используемое для акцентирования.)

Листинг 22.18.

void MidiRead::PostProcess() {

cerr << "Analyzing MIDI file . . . ";

// Анализируем  MIDI-файл   …

double samplesPerTick;               // Количество  отсчетов,

// которые  необходимо

// воспроизвести на   каждый

// тик MIDI.

double samplesRemainingFraction;     // Отложено дробных

// отчетов.

samplesRemainingFraction = 0.0;

// Для  обнаружения

// максимального  значения.

long currentVolume = 0; long maxTotalVolume = 0; long simultaneousNotes = 0;

long maxSimultaneousNotes = 0;

char volume[16][128];     // Уровень  каждой

// из  воспроизводимых нот.

for(int i=0;i<16;i++)

for(int j=0;j<128;j++)

volume[i][j] = 0;

bool possible[16];

bool active[16];

for(int j=0;j<16;j++) { active[j] = false; possible[j] = true;

// Для  определения

// используемых  каналов.

}

MidiEvent *pEvent = _events; MidiEvent *pLastEvent = 0; while(pEvent) {

// Преобразуем задержку

// из  тиков в отсчеты звука.

if (pEvent->delay > 0) {

float samples = pEvent->delay * samplesPerTick

+ samplesRemainingFraction;

pEvent->delay = static_cast<unsigned long>(samples);

samplesRemainingFraction = samples pEvent->delay;

}

// Отслеживаем

// максимальный  уровень.

int ch = pEvent->status & 0x0F; switch(pEvent->status & 0xF0) { case 0x90:

if ((pEvent->data[1] != 0) && possible[ch]) { active[ch] = true;    // Этот канал  использован. if (volume[ch][ pEvent->data[0] ]) {

// Нота   уже   включена?!

// Отключаем, прежде  чем   идти

// дальше…

currentVolume = volume[ch][ pEvent->data[0] ];

simultaneousNotes–;

}

volume[ch][ pEvent->data[0] ] = pEvent->data[1];

currentVolume += volume[ch][ pEvent->data[0] ];

if (currentVolume > maxTotalVolume)

maxTotalVolume = currentVolume;

simultaneousNotes++;

if (simultaneousNotes > maxSimultaneousNotes)

maxSimultaneousNotes = simultaneousNotes;

break;

}

// Включение  ноты   с  нулевой

// скоростью  это на   самом

// деле  отключение ноты.

// Преобразуем в  отключение.

pEvent->status = 0x80 | ch;

pEvent->data[l] = 64;    // Co средней скоростью.

// Отвергаем  и  обрабатываем  его

// как  отключение ноты.

case 0x80:

currentVolume = volume[ch][ pEvent->data[0] ];

simultaneousNotes–;

volume[ch][ pEvent->data[0] ] = 0;

break;

}

pLastEvent = pEvent;

pEvent = pEvent->next;

}

// Готово.

cerr << ". . done.\n";

// Максимальное количество   одновременно

// обрабатываемых  нот.

cerr << "Maximum Simultaneous Notes: " << maxSimultaneousNotes

<< "\n";

// Максимальный  суммарный  уровень.

cerr << "Maximum Total volume: " << maxTotalVolume << "\n";

cerr << "\n";

// Устанавливаем частоту  дискретизации

// на   карте инструментов.

_instrumentMap->SamplingRate(SamplingRate() );

}

Структура   каналов   зависит   от   файла.   C   целью   ускорения   воспроизведения я  инициализирую  все  свободные  каналы  для  использования  специального  объектa MidiChannelSilent, который не осуществляет проигрывания, но является

быстрым. Используемые, но еще не проинициализированные каналы инициали-

зируются для применения значений по умолчанию General MIDI.

Листинг 22.19. Инициализация каналов

{

for(int ch=0; ch< 16; ch++) {

// Заполняем  пустые каналы.

if (_channel[ch]) continue; // Уже  здесь!

if (!active[ch])

_channel[ch] = new MidiChannelSilent();

else {

int instrumentSet;

if (ch==10) instrumentSet = MidiInstrumentMap::gmRhythmBank;

else      instrumentSet = MidiInstrumentMap::gmMelodyBank;

_channel[ch] = new MidiChannelStandard(

this, instrumentSet,

1.0/maxTotalVolume);

}

}

}

Хотя  рассмотренный  процесс  постобработки  многое  упрощает,  у  него  есть один серьезный недостаток. Сейчас многие экспериментируют с MIDI как со средством  потокового  распространения  музыкальных  данных  по  сети  Internet.  Результаты получаются неплохие, так как потоки MIDI довольно компактны, что облегчает  проблемы  с  шириной  полосы  пропускания.  Используемый  мной  подход  не допускает  подобной  «потоковости»,  так  как  до  начала  воспроизведения  необходимо прочитать весь поток событий.

Базовый и расширенный MIDI

Чтобы облегчить работу поставщикам аппаратного обеспечения для персональных   компьютеров,  спецификация   мультимедийного   ПК   (Multimedia   PC,   MPC), первоначально  опубликованная  Microsoft,  определяет  два  уровня  совместимости аппаратуры с MIDI. Идея заключается в том, что системы низкой ценовой категории могут обеспечивать минимальную базовую поддержку мулътитембрового синтезатора  (Base  Multitimbral  Synthesizer),  в  то  время  как системы  более  высокого класса могут обеспечивать расширенную поддержку мультитембрового синтезатора  (Extended  Multitimbral  Synthesizer).  Два  уровня  поддержки  известны  как  базовый  MIDI  (Base  MlDI)  и  расширенный  MIDI  (Extended  MIDI).  Поддержка  этих стандартов  постепенно  сходит  на  нет,  так  как все  более  распространенной  становится  поддержка  General  MIDI,  однако  множество  файлов  в  архивах  Internet  все еще используют эти соглашения MPC.

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

нот  ритма  на  трех  инструментах.  Расширенный  MIDI  требует  поддержки  16  параллельных нот мелодии на 9 инструментах и 16 параллельных нот ритма на 8 инструментах.  Для  сравнения  вспомним, что  стандарт  General  MIDI  требует,  чтобы устройство  воспроизведения  осуществляло  поддержку  16  параллельных  нот  мелодии на 16 инструментах и 8 параллельных нот ритма на 8 инструментах.

Одна из целей разработки этих стандартов позволить включать в один MIDIфайл  упрощенную  версию,  которую  можно  проиграть  на  синтезаторе  базового MIDI,  и более полную версию, которую  можно  проиграть  на  синтезаторе расширенного  MIDI.  Стандарт  реализует  подобный  подход  путем  разделения  каналов MIDI. Каналы с 1-го по 10-й используются расширенным MIDI, с 13-го по 16-й базовым MIDI. Определения инструментов соответствуют General MIDI, за исключением  того,  что  базовый  MIDI  применякет  канал  16  для  мелодических  ударных инструментов. Каналы 11 и 12 не используются. Стандарт MPC позволяет авторам произведений  включать  в  один  файл  MIDI  версии  для  базового  и  расширенного MIDI.  Система,  поддерживающая  Базовый  MIDI,  будет  проигрывать  только  каналы с 13-го по 16-й, а система с поддержкой расширенного MIDI будет проигрывать только каналы с 1-го по 10-й.

Определения  базовый/расширенный  MIDI  ненадежны,  так  как  они  вступают впротиворечие со стандартом General MIDI в двух аспектах. Во-первых, стандарт MPC  поддерживает  включение  авторами  в  один  файл  MIDI  двух  версий  песни и предполагает, что конкретная система будет проигрывать только одну из версий. B  стандарте  General  MIDI  активны  все  каналы.  B  результате  синтезатор  General MIDI проигрывает одновременно версии и базового, и расширенного MIDI.

Более  существенная  проблема  заключается  в  том,  что  стандарт  MPC  располагает мелодические ударные инструменты на 10 и 16 каналах. B General MIDI канал 16 это канал мелодии.

K  счастью,  Microsoft  стандартизировала  способ  пометки  файлов  MPC  MIDI. B соответствии с документом Microsoft Win32 Programmer’s Reference {Руководство программиста  в  среде  Microsoft  Win32)  файлы  MIDI,  сформированные  согласно стандарту  MPC,  должны  соответствующим  образом  помечаться.  Эта  пометка  заключается  в  использовании  специфичного  для  синтезатора  метасобытия  (оно  обсуждается   позднее),   содержащего   З-байтный  идентификатор   производителя:   ID

00 00  65.  Встречая  это  метасобытие, Windows  проигрывает  файл  сообразно  стандартам  MPC,  в  противном  случае  просит  пользователя  сконфигурировать  устройство воспроизведения MIDI.

B  нашем  случае  можно  имитировать  устройство  расширенного  MIDI,  очис-

тив каналы с 11-го по 16-й.

Листинг 22.20. Проверка на событие MPC MIDI

if(  (pEvent->status == 0xFF)          // Метасобытие.

&& (pEvent->data[0] == 127)          // Специфичное

// для секвенсора.

&& (pEvent->metaData->length >= 3)

&& (pEvent->metaData->data[0] == 0)  // 3-байтный идентификатор

// производителя.

&& (pEvent->metaData->data[1] == 0)  // 0 0 65 = Microsoft

&& (pEvent->metaData->data[2] == 65)

) {

// Это   файл   MPC MIDI

cerr << "This is an MPC MIDI file.\n";

// Освобождаем  каналы  с  11

// по   16 (у  нас

// расширенное  устройство

// MIDI).

for (int i=10;i<16;i++)

if(!_channel[i]) {

_channel[i] = new MidiChannelSilent();

possible[i] = false;       // He учитываем  ноты   на

// этих каналах.

}

}

Источник: Кинтцель Т.  Руководство программиста по работе со звуком = A Programmer’s Guide to Sound: Пер. с англ. М.: ДМК Пресс, 2000. 432 с, ил. (Серия «Для программистов»).

По теме:

  • Комментарии