• Об авторе
  • Программы и скрипты
    • Windows
      • BeatCalc
      • Segment Code Generator
    • Плагины для WordPress
      • IE6 Support for Twenty Ten Theme
      • WM-Footnotes
      • WM-Toc
  • Учебные материалы ТТУ
  • Фотогалерея
  • Legal notice

Alex.StarSpirals.Net

инженерный блог

Музыка на ATtiny2313: Управление при помощи MIDI-протокола

31 августа, 2011 by Алексей

Данная статья серии посвящена использованию протокола MIDI для воспроизведения музыки на устройстве, построенном на микроконтроллере ATtiny2313. В ней я расскажу о приёме MIDI-сообщений для управления ранее разработанным синтезатором.

Содержание [Скрыть]
  1. 1 Введение
  2. 2 Связь с ПК посредством MIDI
  3. 3 Краткое описание MIDI-протокола
    1. 3.1 Формат сообщений
    2. 3.2 Note On сообщение — 0X9
    3. 3.3 Note Off сообщение — 0X8
    4. 3.4 Controller Change (CC) сообщение — 0XB
  4. 4 Схема устройства
  5. 5 Программный код
    1. 5.1 Приём MIDI-сообщений
    2. 5.2 Обработка команд
    3. 5.3 Полный код программы
    4. 5.4 Результат
  6. 6 Заключение
  7. 7 Примечания
  8. 8 Использованные материалы

Введение

Протоколу MIDI (Musical Instrument Digital Interface, дигитальный интерфейс для музыкальных инструментов) в 2013 году исполняется 30 лет. Однако он настолько хорошо удовлетворяет потребности музыкантов и музыкальных инженеров, что вряд ли в ближайшее время он будет вытеснен каким-либо другим стандартом.

Статья организована так: сперва я расскажу, как можно подключить два MIDI-устройства друг к другу (рассмотрим вариант ATtiny2313 и ПК), затем в двух словах опишу основные используемые в стандарте команды, и, наконец, покажу как можно ранее разработанное устройство подружить с MIDI.

Связь с ПК посредством MIDI

Строго говоря, для использования MIDI протокола желательно соответствующее оборудование. Например, аудио-карта, оснащённая MIDI-интерфейсом (здесь до сих пор используется популярный некогда DIN5), или специальное USB-устройство. Это не всегда под рукой, а использовать MIDI всё-таки хочется. Тут можно вспомнить про некий стандарт RS-232 и поискать на заднице компьютера COM-порт. У меня на ноутбуке конечно ничего такого не найдёшь, но сгодится и USB-COM конвертер.

О подключении ATtiny2313 к RS-232-совместимому интерфейсу можно писать отдельную статью, но это не входит в мои цели. Напоминаю, что USART-устройство ATtiny2313 выдаёт TTL-уровни (0/5В), а RS-232 хочет -12/+12В в качестве обозначения логических состояний. Поэтому чтобы связать tiny с COM-портом можно использовать либо специальную микросхему (MAX232), либо более дешёвый и простой способ, тут уж смотря какие конечные цели[1].

Общая схема подключения выглядит следующим образом:

Конечно, без определённых ухищрений музыкальные программы напрямую работать с COM-портом не будут. А поэтому надо как-то наладить коммуникацию. На выручку приходит драйвер от компании Roland, подходящий для произвольного устройства и создающий в системе виртуальный MIDI-порт, присылающий и принимающий сообщения от COM-порта (своеобразный мост). Скачать можно на официальном сайте, например для Windows XP можно забрать отсюда, там же на сайте есть подходящий драйвер и для MacOS.

Во время установки надо проследить, чтобы был выбран правильный идентификатор COM-порта. У меня, например, это COM1. Кстати, драйвер от Roland хочет обязательно работать с портами в диапазоне COM1…4, так что в случае назначения системой физическому порту номера 5 и выше, нужно будет изменить эту настройку. Если у вас Windows XP, то порты можно проверить в диспетчере устройств. Если всё правильно настроено, в системе появится дополнительный MIDI порт: входы Roland Serial MIDI Input и два выхода Roland Serial MIDI Out A и B. Согласно этому настраиваем и любую программу, принимающую/отправляющую MIDI-сообщения через COM-порт.

Внимание! Теперь ваш COM-порт будет занят MIDI-приложениями, и для доступа к нему нужно закрыть MIDI-соединения.

Краткое описание MIDI-протокола

Для передачи информации посредством MIDI используется асинхронный последовательный протокол, для пересылки байта нужно 10 бит: стартовый бит, 8 бит информации и стоп-бит. Скорость передачи по стандарту равна 31.25 кбод. Как видно, USART-компонент ATtiny2313 идеально подходит для физической реализации протокола.

Формат сообщений

Вообще говоря, сообщение может состоять как из одного, так и из нескольких байт. Однако первый байт всегда является идентификатором команды (статусный байт):

Старший полубайт (биты 7…4) задают команду, а младший полубайт (биты 3…0) — MIDI-канал, к которому относится команда.

Мы с вами рассмотрим далее сообщения, состоящие из 3 байт информации (статусный байт и два байта данных). Этого вполне достаточно для упрощённой реализации протокола. Полубайты команд буду задавать в шестнадцатиричном представлении. Таким образом, рассматриваемые сообщения выглядят так:

Поговорим теперь по порядку об интересующих нас MIDI-сообщениях.

Note On сообщение — 0X9

Данная команда используется для «включения», то есть начала проигрывания ноты на заданном канале. Она имеет вид 0X9K 0XNN 0XVV, где K — номер канала (0X0…0XF, то есть от 1 до 16), NN — номер ноты (0X00…0X7F; 0…127), VV — так называемая скорость (velocity) извлечения ноты (0X00…0X7F; 0…127), что соответствует динамике исполнения.

Приведу пример. Допустим на инструмент поступило сообщение 0X91 0X45 0X60. Это значит, что инструмент должен начать воспроизведение ноты под номером 0X45, что есть нота ля первой октавы (440 Гц) с динамикой 0X60 на втором канале.

Note Off сообщение — 0X8

Эта команда выполняет прямо противоположное действие предыдущей, выключая ноту на заданном канале, при этом её формат 0X8K 0XNN 0XVV. Поскольку динамика «выключения» учитывается редко, то содержание VV обычно 0X00.

Рассматривая предыдущий пример: для выключения ноты ля первой октавы на втором канале можно отправить сообщение 0X81 0X45 0X00.

Тут есть одно важное замечание. Дело в том, что спецификация MIDI позволяет заменять сообщения Note Off сообщениями Note On с нулевой динамикой.

Так что выключить ноту в нашем примере можно и при помощи сообщения 0X91 0X45 0X00. Это важно помнить при программировании MIDI-устройств.

Controller Change (CC) сообщение — 0XB

Сообщения изменения контроллеров используются для изменения таких параметров исполнения, как, например, громкость. Формат следующий: 0XBK 0xNN 0xVV, где K — номер канала, на котором необходимо изменить параметры контроллера, NN — номер контроллера (0X00…0X7F), VV — значение настройки контроллера (0X00…0X7F).

Нас здесь может заинтересовать сообщение контроллера 0XBK 0x7B 0x00 — «All Notes Off», которое используется для одновременного отключения всех нот на заданном канале K.

Схема устройства

Схема практически не изменилась, разве что добавился коннектор для подключения конвертера уровней, оговоренного выше[2].

Программный код

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

 ; Проект для ATtiny2313 
; Управляемый MIDI Двухголосый Орган v1

.INCLUDE "tn2313def.inc"

.DEF	Velocity	= R8

.DEF	SrSave		= R9

.DEF	Tone1H	= R10
.DEF	Tone1L	= R11
.DEF	Tone2H	= R12
.DEF	Tone2L	= R13

.DEF	ToneTH	= R14
.DEF	ToneTL	= R15

.DEF	ReadBufferL = R16
.DEF	ReadBufferH	= R17

.DEF	Temp	= R18

.DEF	Temp1 	= R19
.DEF	Temp2	= R20
.DEF	Temp3	= R21
.DEF 	Temp4	= R22

.DEF	ADDRH	= R23
.DEF 	ADDRL	= R24

.DEF	RxByte  = R25
.DEF	ByteCnt	= R26

.MACRO OUTI
	ldi R16,@1 			
	out @0,R16
.ENDM

.MACRO ADI
	subi @0, -@1
.ENDM

.MACRO MEMR
	lds @0, @1
.ENDM

.MACRO MEMW
	sts @0, @1
.ENDM

.MACRO MEMWI
	ldi R16, @1
	sts @0, R16
.ENDM	

.EQU BUFFERSIZE 	= 3		; Размер буфера приёмника
.EQU VOICENUM		= 2		; Число доступных голосов

; ---------
; Константы
; ---------

; MIDI протокол

; Используем MIDI канал 1 ---

; Note on
.EQU NoteOn 	= 0x90

; Note off
.EQU NoteOff 	= 0x80

; Controller change
.EQU ControllerChange = 0xB0

; Контроллеры
.EQU AllNotesOff = 0x7B

; ---------------------------

; Самая низкая MIDI-нота (C2 = 36)
.EQU LowNote = 36

; Порты

.EQU Voice1 = PB3
.EQU Voice2 = PB4
.EQU LED	= PB0

; --------------
; Разметрка SRAM
; --------------
.DSEG

notebuffer:
	.BYTE BUFFERSIZE

voices:
	.BYTE VOICENUM

; ----------------
; Начало программы
; ----------------
.CSEG
.ORG 0x0000

RJMP RESET ; Reset Handler
RETI ;RJMP INT0 ; External Interrupt0 Handler
RETI ;RJMP INT1 ; External Interrupt1 Handler
RETI ;RJMP TIM1_CAPT ; Timer1 Capture Handler
RJMP TIMER1_COMPA ; Timer1 CompareA Handler
RETI ;RJMP TIM1_OVF ; Timer1 Overflow Handler
RETI ;RJMP TIM0_OVF ; Timer0 Overflow Handler
RJMP USART0_RXC ; USART0 RX Complete Handler
RETI ;RJMP USART0_DRE ; USART0,UDR Empty Handler
RETI ;RJMP USART0_TXC ; USART0 TX Complete Handler
RETI ;RJMP ANA_COMP ; Analog Comparator Handler
RETI ;RJMP PCINT ; Pin Change Interrupt
RJMP TIMER1_COMPB ; Timer1 Compare B Handler
RETI ;RJMP TIMER0_COMPA ; Timer0 Compare A Handler
RETI ;RJMP TIMER0_COMPB ; Timer0 Compare B Handler
RETI ;RJMP USI_START ; USI Start Handler
RETI ;RJMP USI_OVERFLOW ; USI Overflow Handler
RETI ;RJMP EE_READY ; EEPROM Ready Handler
RETI ;RJMP WDT_OVERFLOW ; Watchdog Overflow Handler

Здесь, пожалуй, отдельных комментариев не требуется. Стандартные макросы, которые хорошо бы выносить для удобства в отдельный файл, и константы. Буфер USART-приёмника составляет 3 байта, ровно столько, сколько нужно для приёма 3-байтовых сообщений NoteOn, NoteOff и ControllerChange, то есть это полностью удовлетворяет упрощённой реализации протокола.

Следом за шапкой идёт стандартная инициализация периферии — это описывать не буду, посмотрите это в полном коде[3]. Реализация двухголосого синтезатора также не изменилась. А вот на приёме сообщений и их обработке остановимся более подробно.

Приём MIDI-сообщений

За приём любых сообщений по USART отвечает прерывание USART0_RXC. Оттуда и посмотрим:

 ; USART receive byte
USART0_RXC:

	; Отключить прерывания
	cli
	
	in SrSave, SREG
	
	; Загрузить байт из регистра
	in RxByte, UDR

	; Записать байт в буффер
	ldi YL, low(notebuffer)
	ldi YH, high(notebuffer)
	
	; Сместив позицию на число в счётчике
	clr temp
	add YL, ByteCnt
	adc YH, temp

	st Y, RxByte

	; Проверить счётчик байтов
	cpi ByteCnt, 0
	breq checkNoteOnOffMessage

	cpi ByteCnt, 2
	breq handleMidiNote

	rjmp incCounter

checkNoteOnOffMessage:

	; Проверить тип сообщения и
	; пропустить только NoteOn, NoteOff, CC
	cpi RxByte, NoteOn
	breq incCounter

	cpi RxByte, NoteOff
	breq incCounter

	cpi RxByte, ControllerChange
	breq incCounter

DiscardByte:

	; Отменить сообщение, сбросить счётчик
	clr ByteCnt
	rjmp getOutNoteCheck

incCounter:

	inc ByteCnt

getOutNoteCheck:

	; Выйти из процедуры
	out SREG, SrSave

	sei
	reti

Что же здесь происходит? Байты, прилетающие по USART записываются в память по указателю парного регистра Y, со смещением, указанным в счётчике. Таким образом в память приходит MIDI-сообщение. Но только в том случае, если оно соответствует фильтру: пропускаются только сообщения NoteOn, NoteOff и относящиеся к изменению контроллеров через команду ControllerChange. Всё остальное откидывается, счётчик обнуляется и ожидается правильная команда. Как только счётчик указывает на поступление третьего байта, управление передаётся процедуре handleMidiNote, о которой поговорим далее.

Обработка команд

Процедура обработки сообщений выглядит так:

handleMidiNote:

	; Читаем из памяти: temp - действие, temp1 - нота
	memr temp, notebuffer
	memr temp1, notebuffer+1

	mov temp2, temp1		; Сохраним temp1 для CC

    memr velocity, notebuffer+2	; Сохраним также динамику

	subi temp1, LowNote		; Найти правильный номер ноты
	inc temp1
	
	; Выбрать правильное действие относительно команды
	cpi temp, NoteOn
	breq doNoteOn

	cpi temp, NoteOff
	breq doNoteOff

	cpi temp, ControllerChange
	breq doControllerChange

doNoteOn:

	; Проверить на нулевую скорость нажатия (динамику)
	clr temp2
	cp velocity, temp2
	breq doNoteOff			; Тогда нужно NoteOff

	; Далее включим ноту

	; Проверяем первый голос
	memr temp2, voices
	cpi temp2, 0
	brne NotFree1

	; Если свободен, ставим ноту сюда
	memw voices, temp1
	rcall setCh1

DoneNoteOnOff:
	; Всё сделано, очищаем счётчик
	clr ByteCnt
	rjmp getOutNoteCheck

NotFree1:
	
	; Проверим, свободен ли этот голос
	memr temp2, voices+1
	cpi temp2, 0
	brne DoneNoteOnOff	; Нет, нота не будет исполнена

	memw voices+1, temp1
	rcall setCh2

	rjmp DoneNoteOnOff

doNoteOff:

	; Проверим первый голос
	memr temp2, voices
	cp temp1, temp2
	brne notOnCh1
	
	; Нота найдена, освобождаем первый голос
	clr temp1
	rcall setCh1
	memwi voices, 0
	rjmp DoneNoteOnOff

notOnCh1:
	
	; Проверим второй голос
	memr temp2, voices+1
	cp temp1, temp2
	brne DoneNoteOnOff	 ; Нота нигде не найдена, выход

	; Нота занимает второй голос, очищаем
	clr temp1
	rcall setCh2
	memwi voices+1, 0
	rjmp DoneNoteOnOff

doControllerChange:

	; Проверим temp2 на "All notes off" сообщение
	cpi temp2, AllNotesOff
	brne DoneNoteOnOff

	; Такое сообщение поступило, очищаем оба канала
	memwi voices, 0
	memwi voices+1, 0
	
	cbi DDRB, voice1
	cbi DDRB, voice2

	rjmp DoneNoteOnOff

Здесь вся логика действий обусловлена условными переходами. В случае включения ноты — ищется свободный канал, и если он найден, то нота записывается туда и включается воспроизведение (иначе нота игнорируется). При нулевой динамике происходит переход к обработке NoteOff, равно как и при поступлении соответствующего сообщения. Там отключаемая нота ищется сначала на первом голосе, потом и на втором, если она найдена, то отключается — иначе игнорируется. Единственное воспринимаемое CC-сообщение — All Notes Off, отключить все ноты. При его поступлении вне зависимости от содержания голосов они очищаются и воспроизведение немедленно прекращается.

Полный код программы

А вот и полный код (комментарии на английском):
ATTiny2313MIDIMusicPlayer_BlogVersion.asm

Результат

Далее можно посмотреть, как ATtiny2313 исполняет 3-ую часть «Лунной сонаты» Бетховена (для воспроизведения используется программа Guitar Pro 5):

Заключение

Надеюсь, статья была полезной! На пути следующие статьи, посвящённые MIDI и DDS (Direct Digital Synthesis). 2 канала по 4 голоса, независимо воспроизводящие синусоидальные, треугольные, пилообразные, прямоугольные сигналы при помощи этой же платы? Переделка старого синтезатора в MIDI-клавиатуру с возможностью выбора динамики? Всё это на старом знакомом ATtiny2313? Да, это есть у нас :-) Если что-то из этого особенно интересно, пишите в комментариях.


Примечания

  1. ↑Вообще, я пробовал оба варианта, и второй оказался довольно эффективным, хотя и не строго соответствующим спецификациям.
  2. ↑Вашему вниманию предлагается статья на соответствующую тему.
  3. ↑Обратите внимание, что USART я настраиваю на значение 38400 бод. С этим значением у драйвера Roland проблем не возникает.

Использованные материалы

  1. http://www.gweep.net/~prefect/eng/reference/protocol/midispec.html
  2. http://www.phys.unsw.edu.au/jw/notes.html
  3. http://www.midi.org/techspecs/midimessages.php
Posted in: Видео, Заметки, Музыка, Электроника Tagged: atmel, attiny2313, avr, avr assembler, com, diy, max232, midi, midi avr, midi через com порт, midi-com, music, roland serial midi, serial, бетховен, лунная соната, миди, миди на attiny2313, музыка, подключение midi, программирование, протокол, сделай сам, синтезатор, Электроника
Август 2011
Пн Вт Ср Чт Пт Сб Вс
1234567
891011121314
15161718192021
22232425262728
293031  
« Мар   Мар »
Сайт работает при поддержке лучшего хостинга в Эстонии

Рубрики

  • ProgTime
  • Арт
  • Видео
  • Заметки
  • Музыка
  • Программирование
  • Размышления
  • Разное
  • Рецензии
  • События
  • Ссылки
  • ТТУ
  • Фото
  • Электроника

Метки

apache art-rock assembler atmel attiny13 attiny2313 avr avr assembler avrdude design diy ftp iq megadrum metal midi mp3 music php prog progressive ProgTime usbasp wap Арт Электроника арт-рок блог гитара музыка настройка сервера подсознание программирование прогрессив прогрессивная музыка рок сделай сам синтезатор скачать снег сознание соло фото фотография хороший дизайн

Мета

  • Войти
  • Лента записей
  • Лента комментариев
  • WordPress.org

Copyright © 2023 Alex.StarSpirals.Net.

Omega WordPress Theme by ThemeHall