ZXNet эхоконференция «music.zx»
тема: Hекоторые особенности музыкального сопроцессора
от: Ivan Roshin
кому: All
дата: 07 Dec 2000
Hello, All!
═══════════════════ mus_sopr.1 ══════════════════
(c) Иван Рощин, Москва
Fido : 2:5020/689.53
ZXNet : 500:95/462.53
E-mail: asder_ffc@softhome.net
WWW : http://www.zx.ru/echo/roschin
Некоторые особенности музыкального сопроцессора
───────────────────────────────────────────────
("Радиолюбитель. Ваш компьютер" 11/2000)
(под псевдонимом BV_Creator)
1. Необходимые пояснения
Эта статья написана на основе материалов, ранее помещенных
мной в конференцию ZX.SPECTRUM, и на основе последовавших за
этим обсуждений, в которых приняли участие Vadik Akimoff, Roman
Panteleyev и Ilya Vinogradov. Благодарю их за помощь! Также
спасибо Алексею Летаеву, который помог мне приобрести
музыкальный сопроцессор AY и обеспечил доступ к Fido и Internet.
Приведенные в статье сведения были проверены мной на двух
экземплярах сопроцессоров YM2149F (Yamaha, Japan) 1990 и 1992
годов выпуска и на сопроцессоре AY38910A/P (Microchip, Taiwan).
2. Изменение громкости при сложении звуков одинаковой частоты
Изучая возможности музыкального редактора 'Pro Tracker 3
от: Ivan Roshin
кому: All
дата: 07 Dec 2000
Hello, All!
═══════════════════ mus_sopr.2 ══════════════════
По-видимому, в "on interrupt" мелодиях, т.е. когда
музыкальный сопроцессор программируется 50 раз в секунду, с
возникновением разности фаз ничего не поделаешь. Преодолеть
проблему можно лишь с помощью "цифрового звука", когда программа
непосредственно устанавливает уровень сигнала на выходе
сопроцессора в каждый момент времени, без использования
генераторов тона (т.е. сопроцессор работает в режиме ЦАП). Может
быть, кто-то сможет написать основанный на этом принципе
проигрыватель "on interrupt"-музыки?
Не знаю, как с этой проблемой обстоят дела в различных
эмуляторах Спектрума/эмуляторах AY. Вполне возможно, что там
разность фаз не возникает (а значит, музыка звучит лучше, чем
на реальном Спектруме!). Как писал известный спектрумовский
музыкант EA/Antares, "нельзя программно сэмулировать микросхему
вместе с ее глюками".
В то же время, для звуков с использованием только огибающей
или только шума вполне возможно манипулировать громкостью,
включая звук в один, два или три канала сразу. Дело в том, что
генераторы тона в сопроцессоре - свои для каждого канала, а
генераторы огибающей и шума существуют в единственном
экземпляре, так что при их использовании разности фаз просто
неоткуда взяться.
A┼ A┼ A┼
│ │ │ ┌─┐ ┌─┐ ┌─
│ + │ = │ │ │ │ │ │
│ ┌─┐ ┌─┐ ┌─ │ ┌─┐ ┌─┐ ┌─ │ ┌┘ └┐ ┌┘ └┐ ┌┘
│ ┌┘ └┐ ┌┘ └┐ ┌┘ │ ┌┘ └┐ ┌┘ └┐ ┌┘ │ │ │ │ │ │
└─┴───┴─┴───┴─┴──> └─┴───┴─┴───┴─┴──> └─┴───┴─┴───┴─┴──>
t t t
Такая возможность представляется еще более ценной, если
учесть, что громкостью звуков, полученных с использованием
огибающей, по-другому управлять нельзя.
3. Посторонние призвуки при изменении частоты звука
Этот неприятный эффект был обнаружен опять-таки при изучении
возможностей редактора 'Pro Tracker'. Установим в этом редакторе
частотную таблицу "SOUNDTRACKER" и создадим такой sample:
┌────────────────────────────────────────┐
│ EDIT SAMPLE 01 │
│ ▒00 000 -0004 00 T-E ███████████████ F │
│ ▒01 000 -0003 00 T-E ███████████████ F │
Теперь попробуем, как будет звучать нота B-4 (клавиша 'M').
Сравните ее звучание со звучанием других нот. Ну как, услышали?
Как мне удалось выяснить, при проигрывании этой ноты
сопроцессор поочередно выводит звук с частотой, соответствующей
коэффициенту деления #0100 (далее - звук #0100) и звук #00FF.
Между двумя изменениями частоты звука проходит 1/50 секунды.
Для определенности примем, что звук выводится в канале B.
Коэффициент деления частоты для этого канала задается в
регистрах R2 (младший байт) и R3 (старший байт). Так как
обращение к регистрам сопроцессора происходит по очереди, и Pro
Tracker сначала изменяет регистр R2, а потом R3, при изменении
значения #00FF на #0100 происходит вот что:
R3 R2
00 FF ;начальное значение #00FF
00 00 ;изменили R2 - получили #0000
01 00 ;изменили R3 - получили #0100
При обратном изменении:
R3 R2
01 00 ;начальное значение #0100
01 FF ;изменили R2 - получили #01FF
00 FF ;изменили R3 - получили #00FF
Видно, что в определенные моменты времени значение делителя
частоты оказывается равным #0000 и #01FF, и каким-то образом это
приводит к возникновению посторонних призвуков.
Я попробовал изменять значения регистров в другом порядке,
написав небольшую программу на ассемблере. Пусть, когда надо
перейти от звука #00FF к #0100, первым изменяется регистр R3, а
когда надо перейти от #0100 к #00FF, первым изменяется регистр
R2:
Способ 1
────────
R3 R2
00 FF ;начальное значение #00FF
01 FF ;изменили R3 - получили #01FF
01 00 ;изменили R2 - получили #0100
01 00 ;начальное значение #0100
01 FF ;изменили R2 - получили #01FF
00 FF ;изменили R3 - получили #00FF
При этом постороннее значение делителя частоты - только
#01FF, но ситуация от этого не улучшилась. Пусть теперь
наоборот, когда надо перейти от звука #00FF к #0100, первым
изменяется регистр R2, а когда надо перейти от #0100 к #00FF,
первым изменяется регистр R3:
Способ 2
────────
R3 R2
00 FF ;начальное значение #00FF
00 00 ;изменили R2 - получили #0000
01 00 ;изменили R3 - получили #0100
01 00 ;начальное значение #0100
00 00 ;изменили R3 - получили #0000
00 FF ;изменили R2 - получили #00FF
Постороннее значение делителя частоты в этом случае равно
#0000, но звучание не стало лучше. Тогда я попробовал запрещать
вывод частоты тона на время изменения R2 и R3, а потом -
разрешать. Для этого используется регистр R7 (микшер). Три
младших бита этого регистра управляют выводом частоты тона в
каналах A, B и C (0 - вывод разрешен, 1 - запрещен), а три
старших бита аналогично управляют выводом шума.
════════════════════════════════════════════════
С уважением, Иван Рощин.
[ZX] [BestView 3.0 - 26%]
от: Ivan Roshin
кому: All
дата: 07 Dec 2000
Hello, All!
═══════════════════ mus_sopr.3 ══════════════════
Способ 3
────────
R7 R3 R2
%00111000 00 FF ;начальное значение #00FF
%00111010 00 FF ;запретили вывод частоты тона
%00111010 00 00 ;изменили R2 - получили #0000
%00111010 01 00 ;изменили R3 - получили #0100
%00111000 01 00 ;разрешили вывод частоты тона
Но при этом появились какие-то потрескивания, что тоже
неприятно, хотя и лучше, чем в предыдущем случае. Тогда я
попробовал сделать так: установить в канале A значение делителя
частоты #0100, в канале B - #00FF, а затем переключать эти
каналы, управляя микшером. Так как при этом одновременно
разрешается вывод частоты тона в одном канале и запрещается в
другом, звук должен был стать лучше...
Способ 4
────────
R7 R3 R2 R1 R0
%00111001 00 FF 01 00 ;A - запрещен, B - разрешен
%00111010 00 FF 01 00 ;A - разрешен, B - запрещен
Звук, однако же, только ухудшился. Итак, ни одним из четырех
рассмотренных способов проблему решить не удалось.
Попробуем разобраться более подробно. Что мы имеем? При
чередовании звуков #00FF и #0100 становятся слышны посторонние
призвуки (вместо плавного перехода). Очевидно, существенную роль
в этом играет тот факт, что #00FF и #0100 различаются и младшим,
и старшим байтом, а значит, делитель частоты при переходе от
одного звука к другому приобретает посторонние значения (#0000
и #01FF). Ведь если чередовать звуки, которые различаются лишь в
одном байте (к примеру, #00FE и #00FF), никаких призвуков не
возникает.
Делаем обобщение: при чередовании любых звуков, у которых
делители частоты различаются и младшим, и старшим байтом, должны
возникать посторонние призвуки. В результате эксперимента это
предположение подтверждается. Вы и сами можете проверить его на
парах звуков #01FF и #0200, #02FF и #0300 ... #0EFF и #0F00
(значение делителя частоты ограничено числом #0FFF).
С увеличением значений призвуки становится менее заметны, но
совсем не пропадают. Разница между двумя чередуемыми значениями
не обязательно должна быть единичной - призвуки появятся и при
чередовании звуков #00FE и #0101.
Совершенно ясно, что это сокращает возможности использования
музыкального сопроцессора. Музыканты знают об этом неприятном
явлении - ведь не используют же они подобные чередования звуков
в своих произведениях!
При чередовании звуков, у которых делители частоты
различаются и младшим, и старшим байтом, делитель частоты будет
приобретать посторонние значения. Понятно, что это происходит
потому, что невозможно мгновенно изменить значения сразу двух
регистров сопроцессора. А если бы это было возможно, то никаких
посторонних призвуков не было бы...
Разумеется, можно было бы вообще отказаться от использования
генератора тона, при этом все проблемы, связанные с установкой
значения делителя частоты для этого генератора, отпали бы сами
собой. Используем цифровой звук - и все! Качество - идеальное!
Да вот только это потребует абсолютно всех ресурсов Z80, что нас
совершенно не устраивает.
Ну что же, будем разбираться дальше. Во-первых, может ли
вообще появление на очень короткое время посторонних делителей
частоты оказать какое-либо влияние на звук? Мы-то с вами знаем,
что может, да еще как! Но посмотрим, что по этому поводу говорит
теория...
Генератор тона в музыкальном сопроцессоре работает довольно
просто: имеется счетчик, значение которого последовательно
уменьшается, и как только оно уменьшится до нуля, уровень
сигнала на выходе меняется, а в счетчик загружается значение из
регистров делителя частоты. Таким образом, формируются
прямоугольные импульсы:
A ┼
│
├────┐ ┌────┐ ┌────┐ ┌────┐ ┌───
│ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │
└────┴────┴────┴────┴────┴────┴────┴────┴────>
t
Из этого следует, что пока в счетчике не 0, изменение
регистров делителя частоты не должно оказывать влияние на звук.
А вот если счетчик обнулится в тот момент, когда в регистрах
делителя частоты находится постороннее значение, это несомненно
окажет влияние на звук. Попробуем подсчитать вероятность этого
события.
Пусть значение делителя частоты меняется с #0100 на #00FF,
промежуток между изменением регистров делителя частоты равен
50 тактов Z80 (именно в течение этого промежутка делитель
частоты будет принимать постороннее значение #01FF), тактовая
частота Z80 равна 3,5 МГц, а тактовая частота сопроцессора равна
1,75 МГц.
Когда делитель частоты равен #0100, генератор тона формирует
звук со следующей частотой:
(1750000/16)/256 = 427,2 Гц
Счетчик генератора тона, соответственно, перезагружается
вдвое чаще, с частотой 854,4 Гц. Посчитаем, сколько тактов Z80
проходит между двумя перезагрузками счетчика:
3500000/854,4 = 4096
Тогда вероятность того, что перезагрузка счетчика произойдет
в тот момент, когда делитель частоты будет равен #01FF, равна:
50/4096 = 0,012
Как видим, вероятность того, что генератор тона сделает хотя
бы один полупериод с посторонней частотой, очень мала - чуть
более 1%. Но посторонние призвуки мы слышим с вероятностью 100%!
Что-то здесь не так. Может быть, счетчик частоты перезагружается
сразу же при записи нового значения в регистры делителя частоты?
════════════════════════════════════════════════
С уважением, Иван Рощин.
[ZX] [BestView 3.0 - 26%]
от: Ivan Roshin
кому: All
дата: 07 Dec 2000
Hello, All!
═══════════════════ mus_sopr.4 ══════════════════
Если это так, появление посторонних призвуков становится
понятным. Рассмотрим форму сигнала во время изменения делителя
частоты с #0100 на #00FF:
A ┼
│
├────┐ ┌──┐┌────┐ ┌────┐ ┌────┐
│ │ │ ││ │ │ │ │ │
│ │ │ ││ │ │ │ │ │
│ │ │ ││ │ │ │ │ │
└────┴────┴──┴┴────┴────┴────┴────┴────┴───>
xy t
Сначала делитель частоты был равен #0100 - то есть R3=#01,
R2=#00. Пусть в момент изменения регистра R2 на выходе был
высокий уровень сигнала, как это показано на графике. В точке x
в R2 записывается #FF - делитель частоты принимает постороннее
значение #01FF, счетчик частоты перезагружается, уровень сигнала
меняется с высокого на низкий. В точке y в R3 записывается #00 -
делитель частоты стал равен #00FF, счетчик частоты
перезагружается, уровень сигнала меняется с низкого на высокий.
Из-за этих двух перепадов уровня сигнала с очень малым
промежутком между ними, по-видимому, и появляются призвуки.
У меня не было возможности проверить, действительно ли на
выходе сопроцессора получается сигнал именно такой формы, как
на графике. Тогда я сделал по-другому: используя "цифровой
звук", попробовал воспроизвести изображенный на графике сигнал и
оценить его звучание. Как и следовало ожидать, звучание
оказалось таким же, как и при использовании генератора тона, с
такими же посторонними призвуками. Из этого можно сделать вывод,
что для значений, рассмотренных в примере, счетчик частоты
перезагружается сразу же при записи нового значения в регистры
делителя частоты.
Кстати, нетрудно проверить на слух, что счетчик частоты
огибающей тоже перезагружается сразу же при установке нового
делителя частоты огибающей (он задается в регистрах R11 и R12).
Вот способ проверки: если занести в регистр формы огибающей
(R13) значение %1000 (постепенное уменьшение уровня сигнала от
15 до 0 в каждом периоде огибающей), а в регистры R11, R12
занести #FFFF (чтобы период огибающей был максимальным), на
выходе сопроцессора будет вот что:
A ┼
│ │ │\n
│ │ │ \n
│ │ │ \n
│ │ │ \n
│ │ │ \n
└──────────────────>
<──T──> t
Частота огибающей: F = (1750000/256)/#FFFF = 0,1043 Hz.
Период огибающей: T = 1/F = 9,59 с.
Т.е. счетчик частоты огибающей перезагружается каждые
9,59/16 = 0,6 секунды (это для сопроцессора AY, а на YM счетчик
будет перезагружаться вдвое чаще - там формируется 32 ступеньки
огибающей вместо 16). Если считать, что счетчик перезагружается,
только когда он уменьшится до нуля, то придется признать, что
как бы ни изменялось значение регистров R11, R12, звучание
изменится только через 0,6 секунды - а при проведении
эксперимента звучание изменялось мгновенно!
Максимальное значение делителя частоты было выбрано потому,
что при таком значении время обнуления счетчика тоже
максимально, и можно с уверенностью отличить на слух два
возможных случая - когда звучание изменяется мгновенно, и когда
оно изменяется после обнуления счетчика. Для меньших значений
делителя частоты эксперименты не проводились.
В различных эмуляторах Спектрума/эмуляторах AY, насколько
мне известно, перезагрузка счетчика происходит лишь при его
обнулении, поэтому воспроизводимый звук отличается от звука
реального сопроцессора (например, при смене делителя частоты с
#0100 на #00FF посторонних призвуков слышно не будет). Можно
было бы сказать, что под эмулятором сопроцессор звучит лучше
настоящего - но только пользы от этого никакой, никто же не
будет писать музыку исключительно для прослушивания под
эмулятором. А вот плохая сторона неточной эмуляции очевидна -
тот, кто будет писать музыку под эмулятором, может допустить
такие изменения частоты звуков, которые на реальном сопроцессоре
будут воспроизводиться с посторонними призвуками.
4. Определение типа музыкального сопроцессора
В восьмом номере электронной газеты 'KrNews' я прочитал, что
можно программным путем различить тип используемого
сопроцессора - AY или YM, и что такая проверка была в demo
'ACTION'. Правда, способ определения в этой газете не
описывался. В процессе изучения указанной demo я обнаружил
следующий фрагмент кода:
#8021: LD BC,#FFFD
LD A,0
OUT (C),A ;установили R0
LD B,#BF
LD A,#40 ;записали #40 в R0
OUT (C),A
LD B,#FF
IN A,(C) ;читаем из R0
CP #40 ;прочитали то, что записали?
JR Z,#803B ;да -> сопроцессор есть.
LD HL,#84FA ;адрес сообщения "NO"
JR #804E ;переход к проц. печати сообщ-я
#803B: LD BC,#FFFD
LD A,#10
OUT (C),A ;установили "недокументированный"
;регистр R16
IN A,(C) ;читаем из него
CP #FF ;сравниваем с #FF
LD HL,#84E6 ;адрес сообщения "AY-8910/12"
JR NZ,#804E ;не #FF -> выводим это сообщ-е.
LD HL,#84F0 ;адрес сообщения "YM2149F"
#804E: ............ ;проц. печати сообщения
Таким образом, если попытаться прочитать содержимое
"недокументированного регистра", то на YM всегда будет читаться
#FF. А на AY, как я выяснил при экспериментах, прочитается номер
регистра (в данном примере - число #10).
Если сопроцессор подключен так, что его регистры недоступны
для чтения, тест покажет отсутствие сопроцессора.
════════════════════════════════════════════════
С уважением, Иван Рощин.
[ZX] [BestView 3.0 - 26%]
|