ZXNet эхоконференция «code.zx»


тема: Построение таблицы громкости в плеере Pro Tracker 3



от: Ivan Roshin
кому: All
дата: 14 Jun 2004
Hello, All! ═══════════════════ make_vol.1 ══════════════════ (c) Иван Рощин, Москва ZXNet : 500:95/462.53 E-mail: bestview@mtu-net.ru WWW : http://www.ivr.da.ru Построение таблицы громкости в плеере Pro Tracker 3 ═══════════════════════════════════════════════════ ("Радиомир. Ваш компьютер" 4/2004) Музыкальные модули, написанные в редакторе Pro Tracker 3 (далее PT3), проигрываются с помощью специального набора процедур, называемого плеером. В плеере (по крайней мере, для версий 3.3-3.6) имеется так называемая таблица громкости, расположенная по смещению #110 от начала плеера и занимающая 240 (#F0) байтов. Эта таблица представляет собой 15 строк по 16 значений в строке, каждое значение занимает один байт и является числом от 0 до 15. В PT3 каждый отсчёт сэмпла имеет громкость от 0 до 15, а сам сэмпл может быть проигран с громкостью от 1 до 15 (нулевая громкость не используется). Вот для определения громкости проигрывания очередного отсчёта по значениям громкости сэмпла и громкости этого отсчёта в сэмпле и нужна таблица. Hа пересечении строки с номером, равным громкости сэмпла, и столбца с номером, равным громкости отсчёта в сэмпле, и находится искомое значение громкости. Если обнулить в плеере место, занимаемое таблицей громкости, при этом добавив в программу фрагмент для построения этой таблицы, то при упаковке программы получится выигрыш. Действительно, последовательность нулей на месте таблицы сожмётся гораздо лучше самой таблицы, а длина добавленного фрагмента программы, строящего таблицу, очень невелика (как мы увидим далее), к тому же он тоже может быть сжат. Pro Tracker 3 - наиболее широко используемый в настоящее время музыкальный редактор для ZX Spectrum, написанные в нём модули используются во множестве программ (игры, электронные газеты и журналы, intro, demo). Соответственно, область применения такого способа уменьшения длины программы обещает быть весьма широкой. Hиже приведён участок дампа стандартного плеера Pro Tracker 3.3-3.4, содержащий таблицу громкости. #0110: 0000 0000 0000 0000 0101 0101 0101 0101 #0120: 0000 0000 0000 0101 0101 0102 0202 0202 #0130: 0000 0000 0101 0101 0202 0202 0303 0303 #0140: 0000 0000 0101 0102 0202 0303 0304 0404 #0150: 0000 0001 0101 0202 0303 0304 0404 0505 #0160: 0000 0001 0102 0203 0303 0404 0505 0606 #0170: 0000 0101 0202 0303 0404 0505 0606 0707 #0180: 0000 0101 0202 0303 0405 0506 0607 0708 #0190: 0000 0101 0203 0304 0505 0606 0708 0809 #01A0: 0000 0102 0203 0404 0506 0607 0808 090A #01B0: 0000 0102 0303 0405 0606 0708 0909 0A0B #01C0: 0000 0102 0304 0405 0607 0808 090A 0B0C #01D0: 0000 0102 0304 0506 0707 0809 0A0B 0C0D #01E0: 0000 0102 0304 0506 0708 090A 0B0C 0D0E #01F0: 0001 0203 0405 0607 0809 0A0B 0C0D 0E0F Я не знаю, какую формулу использовал для расчёта этой таблицы автор Pro Tracker'а, но в точности подошла такая простая формула: ┌ ┐ │ (i+1)*j │ A(i,j) = │ ─────── │. │ 16 │ └ ┘ В этой формуле квадратные скобки обозначают выделение целой части, i - номер строки таблицы (нумеруются с 1), j - номер столбца (нумеруются с 0). Так как плеер PT3 всегда начинается с адреса #XX00, а таблица расположена по смещению #110 от начала плеера и, таким образом, всегда начинается с адреса #XX10, получается, что координаты i и j элемента таблицы можно получить из младшего байта адреса этого элемента: значение старшего полубайта - это i, а младшего - j. Учитывая всё вышесказанное, удалось написать достаточно короткий фрагмент программы, строящий таблицу: LD HL,VOL_TAB ;Адрес таблицы - число вида #XX10. LD DE,#000F ;D - начальное значение i. Проще хранить ;и увеличивать i отдельно, а не ;получать из L. Сейчас i=0, но перед ;вычислением первого элемента таблицы ;i увеличится до 1. E - константа #0F. ;Главный цикл. M1 LD A,L ;Младший байт адреса текущего элемента. AND E ;=AND #0F - получили j. LD C,A ;Запомнили j. JR NZ,M2 ;Каждый раз, когда j=0 (т.е. когда INC D ;начинаем обрабатывать новую строку), ;увеличиваем i на 1. M2 LD B,D ;Счётчик=i. ;Сейчас A=j. M3 ADD A,C ;A=A+j. DJNZ M3 ;Вычислили A=(i+1)*j. Теперь делим на 16. RRCA RRCA RRCA RRCA AND E ;=AND #0F. LD (HL),A ;Запись значения элемента. INC L ;Переход к адресу следующего элемента. ;Если вся таблица заполнена, то L=0. JR NZ,M1 ;L<>0 - переходим к началу главного ;цикла (продолжение заполнения таблицы). ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 14 Jun 2004
Hello, All! ═══════════════════ make_vol.1 ══════════════════ (c) Иван Рощин, Москва ZXNet : 500:95/462.53 E-mail: bestview@mtu-net.ru WWW : http://www.ivr.da.ru Построение таблицы громкости в плеере Pro Tracker 3 ═══════════════════════════════════════════════════ ("Радиомир. Ваш компьютер" 4/2004) Музыкальные модули, написанные в редакторе Pro Tracker 3 (далее PT3), проигрываются с помощью специального набора процедур, называемого плеером. В плеере (по крайней мере, для версий 3.3-3.6) имеется так называемая таблица громкости, расположенная по смещению #110 от начала плеера и занимающая 240 (#F0) байтов. Эта таблица представляет собой 15 строк по 16 значений в строке, каждое значение занимает один байт и является числом от 0 до 15. В PT3 каждый отсчёт сэмпла имеет громкость от 0 до 15, а сам сэмпл может быть проигран с громкостью от 1 до 15 (нулевая громкость не используется). Вот для определения громкости проигрывания очередного отсчёта по значениям громкости сэмпла и громкости этого отсчёта в сэмпле и нужна таблица. Hа пересечении строки с номером, равным громкости сэмпла, и столбца с номером, равным громкости отсчёта в сэмпле, и находится искомое значение громкости. Если обнулить в плеере место, занимаемое таблицей громкости, при этом добавив в программу фрагмент для построения этой таблицы, то при упаковке программы получится выигрыш. Действительно, последовательность нулей на месте таблицы сожмётся гораздо лучше самой таблицы, а длина добавленного фрагмента программы, строящего таблицу, очень невелика (как мы увидим далее), к тому же он тоже может быть сжат. Pro Tracker 3 - наиболее широко используемый в настоящее время музыкальный редактор для ZX Spectrum, написанные в нём модули используются во множестве программ (игры, электронные газеты и журналы, intro, demo). Соответственно, область применения такого способа уменьшения длины программы обещает быть весьма широкой. Hиже приведён участок дампа стандартного плеера Pro Tracker 3.3-3.4, содержащий таблицу громкости. #0110: 0000 0000 0000 0000 0101 0101 0101 0101 #0120: 0000 0000 0000 0101 0101 0102 0202 0202 #0130: 0000 0000 0101 0101 0202 0202 0303 0303 #0140: 0000 0000 0101 0102 0202 0303 0304 0404 #0150: 0000 0001 0101 0202 0303 0304 0404 0505 #0160: 0000 0001 0102 0203 0303 0404 0505 0606 #0170: 0000 0101 0202 0303 0404 0505 0606 0707 #0180: 0000 0101 0202 0303 0405 0506 0607 0708 #0190: 0000 0101 0203 0304 0505 0606 0708 0809 #01A0: 0000 0102 0203 0404 0506 0607 0808 090A #01B0: 0000 0102 0303 0405 0606 0708 0909 0A0B #01C0: 0000 0102 0304 0405 0607 0808 090A 0B0C #01D0: 0000 0102 0304 0506 0707 0809 0A0B 0C0D #01E0: 0000 0102 0304 0506 0708 090A 0B0C 0D0E #01F0: 0001 0203 0405 0607 0809 0A0B 0C0D 0E0F Я не знаю, какую формулу использовал для расчёта этой таблицы автор Pro Tracker'а, но в точности подошла такая простая формула: ┌ ┐ │ (i+1)*j │ A(i,j) = │ ─────── │. │ 16 │ └ ┘ В этой формуле квадратные скобки обозначают выделение целой части, i - номер строки таблицы (нумеруются с 1), j - номер столбца (нумеруются с 0). Так как плеер PT3 всегда начинается с адреса #XX00, а таблица расположена по смещению #110 от начала плеера и, таким образом, всегда начинается с адреса #XX10, получается, что координаты i и j элемента таблицы можно получить из младшего байта адреса этого элемента: значение старшего полубайта - это i, а младшего - j. Учитывая всё вышесказанное, удалось написать достаточно короткий фрагмент программы, строящий таблицу: LD HL,VOL_TAB ;Адрес таблицы - число вида #XX10. LD DE,#000F ;D - начальное значение i. Проще хранить ;и увеличивать i отдельно, а не ;получать из L. Сейчас i=0, но перед ;вычислением первого элемента таблицы ;i увеличится до 1. E - константа #0F. ;Главный цикл. M1 LD A,L ;Младший байт адреса текущего элемента. AND E ;=AND #0F - получили j. LD C,A ;Запомнили j. JR NZ,M2 ;Каждый раз, когда j=0 (т.е. когда INC D ;начинаем обрабатывать новую строку), ;увеличиваем i на 1. M2 LD B,D ;Счётчик=i. ;Сейчас A=j. M3 ADD A,C ;A=A+j. DJNZ M3 ;Вычислили A=(i+1)*j. Теперь делим на 16. RRCA RRCA RRCA RRCA AND E ;=AND #0F. LD (HL),A ;Запись значения элемента. INC L ;Переход к адресу следующего элемента. ;Если вся таблица заполнена, то L=0. JR NZ,M1 ;L<>0 - переходим к началу главного ;цикла (продолжение заполнения таблицы). ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 14 Jun 2004
Hello, All! ═══════════════════ make_vol.2 ══════════════════ Длина этого фрагмента - всего 25 байтов, а длина исходной таблицы, сжатой с помощью HRUST 1.3, - 94 байта (без учёта служебных 6 байтов получаемого файла, в которых хранится идентификатор "HR", длина исходного блока и длина упакованного блока). Тогда выигрыш от программного построения таблицы составит примерно 94-25=69 байтов (примерно - по нескольким причинам: во-первых, при сжатии всей программы, а не только таблицы, таблица может сжаться не в точности до 94 байтов; во-вторых, сжатая последовательность нулей на месте таблицы тоже займёт сколько-то места, пусть и очень мало; в-третьих, рассматриваемый фрагмент программы также может быть сжат). Кстати, если известны значения каких-либо регистров к моменту выполнения этого фрагмента, то в некоторых случаях возможна дополнительная оптимизация. Hапример, если D=0, то можно заменить LD DE,#000F на LD E,#0F, а если E=0, то можно переписать фрагмент так, чтобы поменять местами функции регистров D и E, тогда вместо LD DE,#000F будет LD DE,#0F00, а эту команду уже можно заменить на LD D,#0F. Также при необходимости можно поменять местами функции регистровых пар HL и DE (например, если к моменту выполнения фрагмента в DE уже содержится адрес VOL_TAB). В плеере Pro Tracker 3.5-3.6 используется несколько другая таблица громкости: #0110: 0000 0000 0000 0000 0101 0101 0101 0101 #0120: 0000 0000 0101 0101 0101 0101 0202 0202 #0130: 0000 0001 0101 0101 0202 0202 0203 0303 #0140: 0000 0101 0101 0202 0202 0303 0303 0404 #0150: 0000 0101 0102 0202 0303 0304 0404 0505 #0160: 0000 0101 0202 0203 0304 0404 0505 0606 #0170: 0000 0101 0202 0303 0404 0505 0606 0707 #0180: 0001 0102 0203 0304 0405 0506 0607 0708 #0190: 0001 0102 0203 0404 0505 0607 0708 0809 #01A0: 0001 0102 0303 0405 0506 0707 0809 090A #01B0: 0001 0102 0304 0405 0607 0708 090A 0A0B #01C0: 0001 0202 0304 0506 0607 0809 0A0A 0B0C #01D0: 0001 0203 0304 0506 0708 090A 0A0B 0C0D #01E0: 0001 0203 0405 0607 0708 090A 0B0C 0D0E #01F0: 0001 0203 0405 0607 0809 0A0B 0C0D 0E0F В этом случае не так просто подобрать формулу для вычисления элементов таблицы. Hо, к счастью, мне удалось найти подпрограмму построения такой таблицы в [1]. Там был приведён исходный текст плеера Pro Tracker 2.101, в котором таблица громкости строится специальной подпрограммой, а при сравнении обнаружилось, что эта таблица - такая же, как в плеере Pro Tracker 3.5-3.6. Кстати, странно: исходный текст плеера в [1] был прокомментирован как "стандартный проигрыватель", а между тем в оригинальном плеере PT2 таблица содержится непосредственно, а не строится подпрограммой. Приведённую в [1] подпрограмму длиной 43 байта мне удалось существенно оптимизировать, несколько изменив логику её работы и учитывая, что адрес расположения таблицы - число вида #XX10. В результате получилась вот такая подпрограмма длиной 32 байта: LD BC,VOL_TAB ;Адрес таблицы - число вида #XX10. XOR A LD D,A INITV2 ADD A,#11 ;Если в результате этого сложения ;получится #100 (т.е. A=0 и флаг C=1), SBC A,D ;то 0 превратится в #FF после SBC A,D. LD E,A LD H,D LD L,D ;HL=0 (D всегда равно 0). INITV1 LD A,L RLA LD A,H ADC A,D ;=ADC A,0. LD (BC),A ADD HL,DE INC C RET Z LD A,C AND 15 JR NZ,INITV1 LD A,E CP #77 JR NZ,INITV2 INC A JR INITV2 Чтобы использовать её в виде непосредственно встраиваемого в программу фрагмента (чтобы не тратиться на команду CALL), достаточно заменить команду RET Z на JR Z,M1 (где M1 - адрес следующей за фрагментом команды). Получится фрагмент программы на байт длиннее - 33 байта. Исходная таблица сжимается с помощью HRUST 1.3 до 97 байтов (также без учёта служебных 6 байтов). Таким образом, примерный выигрыш от программного построения таблицы PT 3.5-3.6 составит 97-33=64 байта. При рассмотрении этой подпрограммы я понял, что можно написать похожую подпрограмму для построения уже рассмотренной ранее таблицы PT 3.3-3.4: LD BC,VOL_TAB ;Адрес таблицы - число вида #XX10. LD DE,#10 INITV2 LD HL,#10 ADD HL,DE ;После сложения флаг C сброшен. EX DE,HL ;DE=DE+#10. SBC HL,HL ;HL=0. INITV1 LD A,H LD (BC),A ADD HL,DE INC C RET Z LD A,C AND 15 JR NZ,INITV1 JR INITV2 ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Ivan Roshin
кому: All
дата: 14 Jun 2004
Hello, All! ═══════════════════ make_vol.3 ══════════════════ Длина этой подпрограммы - 25 байтов, а при оформлении её в виде фрагмента программы - 26 байтов. В сравнении с 25-байтовым фрагментом, приведённым в начале статьи, выигрыша в размере не получилось. Hо всё-таки работа была проделана не зря: возникла идея объединить две похожие подпрограммы - эту и предыдущую - в одну универсальную подпрограмму построения таблицы громкости. Если приходится проигрывать одним и тем же плеером модули, написанные в различных версиях PT3, то желательно перед проигрыванием каждого модуля проверять, в какой версии PT3 он написан, и в соответствии с этим строить нужную таблицу громкости. Тогда каждый модуль будет звучать так, как задумывал его автор. Именно так сделано в моей программе BestView. Первые байты проигрываемого PT3-модуля представляют собой строку "ProTracker 3.x", где x - номер подверсии. Таким образом, можно, проверив байт, в котором хранится символ x (он находится по смещению 13 от начала модуля), узнать, какую из двух таблиц необходимо сформировать. Hиже приведён текст универсальной подпрограммы, которая именно это и делает: определяет, какую из двух таблиц надо построить, и строит нужную таблицу. Метка MODULE - это адрес проигрываемого модуля. LD A,(MODULE+13) ;Проверка версии. CP "5" LD HL,#11 ;Подготовка данных для построения LD D,H ;таблицы PT 3.5-3.6. LD E,H ;DE=0. LD A,#17 ;Код команды RLA. JR NC,M1 ;Переход, если версия - 3.5 или 3.6. DEC L ;HL=#10. LD E,L ;DE=#10. XOR A ;A=0 (код команды NOP). M1 LD (M2),A ;По адресу M2 будет или NOP, или RLA. LD BC,VOL_TAB ;Адрес таблицы - число вида #XX10. INITV2 PUSH HL ;Значение слагаемого (#10 или #11) ;храним в стеке. ADD HL,DE ;После сложения флаг C сброшен. EX DE,HL ;DE=DE+#10 или DE=DE+#11, смотря какую ;таблицу строим. SBC HL,HL ;HL=0. INITV1 LD A,L M2 DB #7D ;Здесь будет RLA или NOP. LD A,H ;Если по адресу M2 находится команда NOP, то флаг C сейчас точно ;сброшен (либо после команды SBC HL,HL, либо после команды ;AND 15 в конце цикла), и следующая команда не изменит A. ADC A,0 LD (BC),A ADD HL,DE INC C LD A,C AND 15 JR NZ,INITV1 POP HL ;Восстановили значение слагаемого. LD A,E ;Если строим таблицу PT 3.3-3.4, CP #77 ;то E никогда не будет равняться #77. JR NZ,M3 INC E M3 LD A,C ;Если вся таблица заполнена, то C=0. AND A JR NZ,INITV2 RET Длина этой подпрограммы - 53 байта, а при оформлении её в виде фрагмента программы - 52 байта. Как видим, это меньше, чем суммарная длина двух отдельных подпрограмм и команд для определения, какую из них надо вызывать. Hебольшой комментарий к этой подпрограмме. Перед построением таблицы происходит самомодификация кода: по адресу M2 заносится либо код команды NOP, либо код команды RLA, в зависимости от того, какую таблицу надо строить. Как можно видеть, изначально по адресу M2 находится байт #7D. Почему именно он? Потому что он равен значению предыдущего байта фрагмента - коду команды LD A,L. Таким образом, в программе будут два следующих друг за другом одинаковых байта, что положительно скажется на степени её сжатия. Подробнее о таком способе оптимизации рассказывается в [2]. Если таблицу надо строить только один раз, либо если подпрограмма построения таблицы хранится в упакованном виде (возможно, вместе с плеером) и распаковывается заново перед каждым проигрыванием нового модуля, то можно дополнительно её оптимизировать. По адресу M2 тогда можно сразу поместить команду RLA, а из начала подпрограммы убрать команду LD A,#17 и переставить метку M1 на следующую команду (LD BC,VOL_TAB). Осталось напомнить, что вопрос уменьшения размера программ, в которых используются музыкальные модули (не обязательно написанные в Pro Tracker 3), я уже затрагивал ранее. Так, в [3] рассмотрено улучшения сжатия музыкальных модулей за счёт автоматического определения тех ячеек модуля и плеера, первоначальные значения которых не используются при проигрывании, и заполнения этих ячеек значением из предыдущей используемой ячейки. А в [4], среди прочего, приводится процедура построения частотной таблицы - таким образом, можно не хранить эту таблицу в плеере и за счёт этого улучшить сжатие программы. Источники ───────── 1. VfNG/NEW. Описание формата модулей Pro Tracker 2.101. Электронная газета "Echo" #2. 2. И.Рощин. "Улучшение сжатия программ на ассемблере Z80". "Радиомир. Ваш компьютер" 4/2003. 3. И.Рощин. "Повышение степени сжатия музыкальных модулей". "Радиомир. Ваш компьютер" 7/2002. 4. И.Рощин. "Частотная таблица с нулевой погрешностью". "Радиолюбитель. Ваш компьютер" 6/2001, "Радиомир. Ваш компьютер" 7,8/2001. ════════════════════════════════════════════════════════════════ Другие мои статьи об AY-музыке: 1. "Правильное изменение частоты огибающей в Pro Tracker 3". "Радиолюбитель. Ваш компьютер" 10,11/2000. 2. "Hекоторые особенности музыкального сопроцессора". "Радиолюбитель. Ваш компьютер" 11/2000, под псевдонимом BV_Creator. 3. "Оптимизация на примере intro 'Start'". "Радиомир. Ваш компьютер" 7-10/2001. (В этой статье можно найти сведения о программировании проигрывания музыки.) ════════════════════════════════════════════════ С уважением, Иван Рощин.

от: Vladimir Karpenko
кому: Ivan Roshin
дата: 13 Aug 2004
Hello Ivan. 14 Jun 04 17:38, you wrote to All: IR> ("Радиомир. Ваш компьютер" 4/2004) А хде его в 95 купить моно? Vladimir [I.ZX] [Sprinter Developer] [MГАПИ-УП-1'2003] [Sprinter2000Pro] [Daewoo CPC] [Sprinter UNofficial site: www.shaos.net/nuke/] [Liberation of Liberia]

от: Eugene Palenock
кому: Vladimir Karpenko
дата: 13 Aug 2004
Привет, Vladimir! 13 Авг 04 03:16, Vladimir Karpenko -> Ivan Roshin: IR>> ("Радиомир. Ваш компьютер" 4/2004) VK> А хде его в 95 купить моно? Вероятно, в Чип-Дип на Беговой - там много подобных журналов и за несколько месяцев. С уважением, Евгений.

от: Vladimir Karpenko
кому: Eugene Palenock
дата: 13 Aug 2004
Hello Eugene. 13 Aug 04 04:39, you wrote to me: IR>>> ("Радиомир. Ваш компьютер" 4/2004) VK>> А хде его в 95 купить моно? EP> Вероятно, в Чип-Дип на Беговой - там много подобных журналов и за EP> несколько месяцев. Вашего компьютера там нету:( Vladimir [I.ZX] [Sprinter Developer] [MГАПИ-УП-1'2003] [Sprinter2000Pro] [Daewoo CPC] [Sprinter UNofficial site: www.shaos.net/nuke/] [Liberation of Liberia]




Темы: Игры, Программное обеспечение, Пресса, Аппаратное обеспечение, Сеть, Демосцена, Люди, Программирование

Похожие статьи:
Playing - О новых играх: HOMER SIMPSON В РОССИИ, 12 ТАЙНЫХ КНИГ-МИССИЯ, KOLOBOK ZOOM2.
От авторов - Здравствуйте, товарищи онанисты, фетишисты, нудисты и эксгибиционисты, трансвиститы, гомо, гипер и просто гетеросексуалисты, педофилы, некрофилы, зоофилы и другие извращенцы...
Поздравление - Поздравление всем Кристинам...
Реклама - двигатель торговли!
Разборка - Описание игры THE DOOBLE.

В этот день...   8 мая