// Назначение: разбор главной структуры MIDI файла. // Параметры: Открытый FileStream поток. // Возвращаемой значение - заполненная структура типа MIDIheaderStruct. public MIDIheaderStruct CopyHeaderOfMIDIFile(MIDIReaderFile MIDIFile) { MIDIheaderStruct ST = new MIDIheaderStruct(); // Создаем пустую структуру заголовка файла. ST.nameSection = MIDIFile.ReadStringOf4byte(); // Копируем имя раздела. ST.lengthSection = MIDIFile.ReadUInt32BigEndian(); // Считываем 4 байта длины блока. Должно в итоге быть 0x6 ST.mode = MIDIFile.ReadUInt16BigEndian(); // Считываем 2 байта режима MIDI. Должно быть 0, 1 или 2. ST.channels = MIDIFile.ReadUInt16BigEndian(); // Считываем 2 байта количество каналов в MIDI файле. ST.settingTime = MIDIFile.ReadUInt16BigEndian(); // Считываем 2 байта параметров тактирования. return(ST); // Возвращаем заполненную структуру. }
// Назначение: Открытие файла для чтения. // Параметры: путь к файлу. // Возвращаемое значение: успешность операции. true - успешно, false - нет. public bool openMIDIFile(string pathToFile) { FileStream fileStream = new FileStream(pathToFile, FileMode.Open, FileAccess.Read); // Открываем файл только для чтения. MIDIReaderFile MIDIFile = new MIDIReaderFile(fileStream); // Собственный поток для работы с MIDI файлом со спец. функциями. На основе байтового потока открытого файла. MIDIheaderStruct HeaderMIDIStruct = CopyHeaderOfMIDIFile(MIDIFile); // Считываем заголовок. MIDIMTrkStruct[] MTrkStruct = new MIDIMTrkStruct[HeaderMIDIStruct.channels]; // Определяем массив для MTrkStruct. richTextBox1.Text += "Количество блоков: " + HeaderMIDIStruct.channels.ToString() + "\n"; // Количество каналов. richTextBox1.Text += "Параметры времени: " + HeaderMIDIStruct.settingTime.ToString() + "\n"; richTextBox1.Text += "Формат MIDI: " + HeaderMIDIStruct.mode.ToString() + "\n"; for (int loop = 0; loop < HeaderMIDIStruct.channels; loop++) { MTrkStruct[loop] = CopyMIDIMTrkSection(MIDIFile); // Читаем блоки MIDI файла. } outData(СreateNotesArray(MTrkStruct, HeaderMIDIStruct.channels)); // Получаем список нота/длительность. return(true); }
// Назначение: копирование блока MTrk (блок с событиями) из MIDI файла. // Параметры: поток для чтения MIDI файла. // Возвращает: структуру блока с массивом структур событий. public MIDIMTrkStruct CopyMIDIMTrkSection(MIDIReaderFile MIDIFile) { MIDIMTrkStruct ST = new MIDIMTrkStruct(); // Создаем пустую структуру блока MIDI файла. ST.arrayNoteStruct = new ArrayList(); // Создаем в структуре блока динамический массив структур событий клавиш. noteStruct bufferSTNote = new noteStruct(); // Создаем запись о новой ноте (буферная структура, будем класть ее в arrayNoteStruct). ST.nameSection = MIDIFile.ReadStringOf4byte(); // Копируем имя раздела. ST.lengthSection = MIDIFile.ReadUInt32BigEndian(); // 4 байта длинны всего блока. UInt32 LoopIndex = ST.lengthSection; // Копируем колличество оставшихся ячеек. Будем считывать события, пока счетчик не будет = 0. UInt32 realTime = 0; // Реальное время внутри блока. while (LoopIndex != 0) // Пока не считаем все события. { // Время описывается плавающим числом байт. Конечный байт не имеет 8-го разрядка справа (самого старшего). byte loopСount = 0; // Колличество считанных байт. byte buffer; // Сюда кладем считанное значение. UInt32 bufferTime = 0; // Считанное время помещаем сюда. do { buffer = MIDIFile.ReadByte(); // Читаем значение. loopСount++; // Показываем, что считали байт. bufferTime <<= 7; // Сдвигаем на 7 байт влево существующее значенеи времени (Т.к. 1 старший байт не используется). bufferTime |= (byte)(buffer & (0x7F)); // На сдвинутый участок накладываем существующее время. } while ((buffer & (1 << 7)) != 0); // Выходим, как только прочитаем последний байт времени (старший бит = 0). realTime += bufferTime; // Получаем реальное время. buffer = MIDIFile.ReadByte(); loopСount++; // Считываем статус-байт, показываем, что считали байт. // Если у нас мета-события, то... if (buffer == 0xFF) { buffer = MIDIFile.ReadByte(); // Считываем номер мета-события. buffer = MIDIFile.ReadByte(); // Считываем длину. loopСount += 2; for (int loop = 0; loop < buffer; loop++) { MIDIFile.ReadByte(); } LoopIndex = LoopIndex - loopСount - buffer; // Отнимаем от счетчика длинну считанного. } // Если не мета-событие, то смотрим, является ли событие событием первого уровня. else { switch ((byte)buffer & 0xF0) // Смотрим по старшым 4-м байтам. { // Перебираем события первого уровня. case 0x80: // Снять клавишу. bufferSTNote.channelNote = (byte)(buffer & 0x0F); // Копируем номер канала. bufferSTNote.flagNote = false; // Мы отпускаем клавишу. bufferSTNote.roomNotes = MIDIFile.ReadByte(); // Копируем номер ноты. bufferSTNote.dynamicsNote = MIDIFile.ReadByte(); // Копируем динамику ноты. bufferSTNote.noteTime = realTime; // Присваеваем реальное время ноты. ST.arrayNoteStruct.Add(bufferSTNote); // Сохраняем новую структуру. LoopIndex = LoopIndex - loopСount - 2; // Отнимаем прочитанное. break; case 0x90: // Нажать клавишу. bufferSTNote.channelNote = (byte)(buffer & 0x0F); // Копируем номер канала. bufferSTNote.flagNote = true; // Мы нажимаем. bufferSTNote.roomNotes = MIDIFile.ReadByte(); // Копируем номер ноты. bufferSTNote.dynamicsNote = MIDIFile.ReadByte(); // Копируем динамику ноты. bufferSTNote.noteTime = realTime; // Присваеваем реальное время ноты. ST.arrayNoteStruct.Add(bufferSTNote); // Сохраняем новую структуру. LoopIndex = LoopIndex - loopСount - 2; // Отнимаем прочитанное. break; case 0xA0: // Сменить силу нажатия клавишы. bufferSTNote.channelNote = (byte)(buffer & 0x0F); // Копируем номер канала. bufferSTNote.flagNote = true; // Мы нажимаем. bufferSTNote.roomNotes = MIDIFile.ReadByte(); // Копируем номер ноты. bufferSTNote.dynamicsNote = MIDIFile.ReadByte(); // Копируем НОВУЮ динамику ноты. bufferSTNote.noteTime = realTime; // Присваеваем реальное время ноты. ST.arrayNoteStruct.Add(bufferSTNote); // Сохраняем новую структуру. LoopIndex = LoopIndex - loopСount - 2; // Отнимаем прочитанное. break; // Если 2-х байтовая комманда. case 0xB0: byte buffer2level = MIDIFile.ReadByte(); // Читаем саму команду. switch (buffer2level) // Смотрим команды второго уровня. { default: // Для определения новых комманд (не описаных). MIDIFile.ReadByte(); // Считываем параметр какой-то неизвестной функции. LoopIndex = LoopIndex - loopСount - 2; // Отнимаем прочитанное. break; } break; // В случае попадания их просто нужно считать. case 0xC0: // Просто считываем байт номера. MIDIFile.ReadByte(); // Считываем номер программы. LoopIndex = LoopIndex - loopСount - 1; // Отнимаем прочитанное. break; case 0xD0: // Сила канала. MIDIFile.ReadByte(); // Считываем номер программы. LoopIndex = LoopIndex - loopСount - 1; // Отнимаем прочитанное. break; case 0xE0: // Вращения звуковысотного колеса. MIDIFile.ReadBytes(2); // Считываем номер программы. LoopIndex = LoopIndex - loopСount - 2; // Отнимаем прочитанное. break; } } } return(ST); // Возвращаем заполненную структуру. }