====== Пример использования OSA: QUARTET ====== ===== Проект ===== Программа **quartet** синтезирует мелодию по заданным нотам (используется фрагмент произведения И.С.Баха N1067). Файл {{osa:quartet4.rar|quartet4.rar}} нужно распаковать в папку "C:\TEST\QUARTET". В папке проект для **PIC16F628A**. Программа генерирует 4-канальную мелодию, используя 8-разрядный ШИМ с частотой 78 КГц. Мелодия играется четырьмя инструментами: басс, скрипка и 2 гитары (конечно, названия условны, т.к. звуки не очень похожи на настоящие инструменты). Каждый канал может быть включен или выключен с помощью переключателей. Вот ##MP3## ({{osa:quartet_mp3.rar|скачать}}), записанный с выхода этого устройства. ===== Принципиальная схема ===== Ниже приведена схема включения ПИКа. 8-разрядный цифровой ШИМ-сигнал сглаживается RC-фильтром R22-C3 и через резистор R24 (для регулирования громкости) поступает на наушники. {{osa:ref:appendix:quartet_pwm.jpg?850}} Переключатель ##SW1## включает/отключает басс.\\ Переключатель ##SW2## включает/отключает скрипку.\\ Переключатель ##SW3## включает/отключает первую гитару.\\ Переключатель ##SW4## включает/отключает вторую гитару.\\ ===== Описание работы ===== Программа состоит из следующих файлов: * **quartet_main.c** - основная программа: задачи, функции по чтению нот. * **music.c** - формальное описание музыки: содержит 4 массива с нотами для каждого канала. * **sinus.c** - содержит по одному периоду синуса для каждого иструмента. Из этих таблиц генерируется сигнал. * **interrupt.c** - содержит код синтезатора звука. Он весь помещен в прерывание по TMR2 (вызывается раз в 100 мкс). Ниже подробно рассмотрим работу задач из файла **quartet_main.c**. Наша программа состоит из 5 задач. Грубо говоря, 4 задачи - музыканты, каждый со своим инструментом, и 5-я задача - дирижер, который синхронизирует работу первых 4-х. ==== Управление звуком ==== Для уравления звуком в прорамме определен тип **TSound**, который содержит всю информацию о звуке для "музыканта": * какую ноту играем; * и из какой нотной тетради; * держим ли паузу; * повторяем ли фрагмент; для "дирижера": * когда он должен дать следующую команду; * закончились ли ноты у "музыканта"; для синтезатора: * частота текущей ноты; * текущее время ее звучания (для правильного формирования огибающей); ==== "Музыканты" ==== В программе 4 "музыканта": Task_BASS, Task_VIOLIN, Task_GUITAR1, Task_GUITAR2. Все "музыканты" постоянно находятся в режиме ожидания команды от "дирижера" (в виде бинарного семафора). Получив команду, "музыкант" вычитывает следующую ноту из своего нотного стана, играет ее (формирует данные для синтезатора звука в **interrupt.c**). Обработка ноты происходит в функции **NoteWork()**, куда в качестве параметра передается переменная управления звуком TSound. По ней вычитывается следуюая нота (или инструкция: повтор, пауза и пр.) и выполняются операции по формированию указаний синтезатору, а также формируется длительность текущей ноты (паузы), которая уменьшается на 1 каждый раз при получении команды от "дирижeра". Когда "музыкант" доходит до конца нотной тетради, он сообщает "дирижеру", что у него закончились ноты (сбрасывает флаг в переменной **flag_Playing**). После чего становится в ожидание команды от "дирижера" начать музыку сначала (в виде бинарного семафора). Получив эту команду, "музыкант" переходит в начало нотной теради (переменная управления звуком переинициализируется) и передает команду "дирижера" следующему "музыканту". ==== "Дирижер" ==== Задача Task_CONDUCTOR ("дирижер") имеет низший приоритет, чтобы не мешать выполнению задач "музыкантов". Дирижер" отсчитывает такты (как метроном) и следит за тем, чтобы музыканты вовремя получали команды. Задача "дирижер" запускается один раз в 150 мс и при каждом запуске проверяет состояние проигрываемой мелодии. Как только "дирижер" замечает, что все "музыканты" доиграли свои партии до конца (т.е. все биты во флаге **flag_Playing** сброшены), он дает команду "музыкантам" играть сначала. ==== Два слова о синтезаторе ==== Синтезатор реализован в функции прерывания в файле **interrupt.c**. Подпрограмма пробегается по всем четырем переменным управления звуком и формирует цифровой сигнал. Для формирования цифрового сигнала у синтезатора есть: * три таблицы "паттернов": bass[], violin[], guitar[] - где записано по одному периоду синусоиды с разными частотными характеристиками; * формулы для формирования огибающей сигнала (помечены комментариями FORMING_SIGNAL_xxx): атака, спад, сустейн. * макрос быстрого перемножения (signed char)*(unsigned char), чтобы перемножать синусоиду на амплитуду огибающей. Все четыре мгновенных значения амплитуды для каждого инструмента суммируются, делятся на 4 и записываются в переменную m_cDAC, которая при следующем входе в прерывание будет скопирована в буферный регистр PWM CCPR1L (два младших разряда копируются в биты CCP1X и CCP1Y регистра CCP1CON). ==== О нотах ==== Здесь приведу немного информации на случай, если кому-то захочется свою мелодию запрограммировать). В файле **music.c** есть буквенные определения нот (C0 - до первой октавы, C0_ - до диез первой октавы, D0 - ре и т.д.) Всего в программе выделены частоты под 4 с половиной октавы (57 нот). Но при задании ноты в нотном стане (в массивах **notelist_xxx**) для задания номера ноты (макрос **play()**) используются только 5 бит, т.е. можно пронумеровать только 32 ноты. Поэтому введена возможность установки базовой октавы (макрос **setbase()**). Длительность ноты задается только двумя битами, поэтому всего 4 варианта длительностей. Если нужно нота большей длительности, то следует ее либо добить паузой (макрос **pause()**), либо командой продолжения звучания (макрос **playmore()**). Список нот должен заканчиваться макросом **stop()**, чтобы "музыканты" не обращались к неинициализированным ячейкам пямяти.