Karoshi MSX Community

Desarrollo MSX => Rutinas - Snipets => Mensaje iniciado por: Dioniso en 23 de Octubre de 2006, 12:38:01 pm



Título: 1 bit player
Publicado por: Dioniso en 23 de Octubre de 2006, 12:38:01 pm
   Hello.

   This is a small player to play songs through the 1-bit port $AA. The song I have chosen is the main title from Bubble Bobble though some notes don't correspond to the original one, I think ...   ::)  (I did it yesterday with a very big hang-over)

   The main routine (playit) has been taken/copied/ported from the "Assembly Listing of the Sea Change ROM". You'll see the t-states are wrong (you have to add 1). I just added some lines to maintain compatibility with the MSX (even reading the $aa port just to keep the first 6 bits and change only the 7th bit; otherwise you'll have strange effects, like turning on the CAPs light).

   The code has been compiled with TNIasm and the binary is just 384 bytes long. You can get it right HERE (http://www.geocities.com/dioniso072/songs/1bit.bin).

   The "replayer" is written in a very easy way. Don't complain  :P

   This is not very useful, I know. More to come ...

Código:


ORG $9000

db $fe
dw Start_Of_File, End_Of_File, Start_Of_File

Start_Of_File:

in a,($aa) ;If we don't do this we'll have
and $ef ;problems when we read this port
out ($aa),a ;later.
nop
nop

ld hl,Pseudo_Tracker
The_Loop:
xor a
cp (hl)
ret z
push hl

ld a,(hl)
add a,a
add a,a
ld h,0
ld l,a
ld de,Notes_And_Length-4
add hl,de

push hl
pop ix
ld e,(ix+0)
ld d,(ix+1)
ld l,(ix+2)
ld h,(ix+3)
call playit
pop hl
inc hl
jr The_Loop

Notes_And_Length: ;First 2 bytes = Length // Last 2 bytes = Note
dw $68,$66b ;01 - c4 3822ms
dw $75,$5b4 ;02 - d4 3405ms
dw $83,$511 ;03 - e4 3033ms
dw $8b,$4c7 ;04 - f4 2863ms
dw $b0,$5b4 ;05 - d4 (longer 50%)
dw $34,$66b ;06 - c4 (shorter 50%)
dw $9c,$66b ;07 - c4 (longer 50%)
dw $2e,$737 ;08 - a#3 (shorter 50%)
dw $5c,$737 ;09 - a#3 4290ms
dw $58,$7a6 ;10 - a3 4545ms
dw $2c,$7a6 ;11 - a3 (shorter 50%)
dw $27,$89a ;12 - g3 (shorter 50%)
dw $45,$9ab ;13 - f3 5726ms
dw $4e,$89a ;14 - g3 5102ms
dw $84,$7a6 ;15 - a3 (longer 50%)
dw $8a,$737 ;16 - a#3 (longer 50%)
dw $138,$66b ;17 - c4 (longer 300%)
dw $7c,$560 ;18 - d#4 3214ms
dw $d1,$326 ;19 - c5 1911ms
dw $eb,$2cb ;20 - d5 1702ms
dw $f8,$2a1 ;21 - d#5 1607ms
dw $107,$279 ;22 - e5 1516ms
dw $117,$254 ;23 - f5 1431ms
dw $139,$210 ;24 - g5 1275ms
dw $b0,$3c4 ;25 - a4 2272ms
dw $9c,$43e ;26 - g4 2551ms
dw $22f,$38c ;27 - a#4 2145ms (longer 300%)
dw $174,$38c ;28 - a#4 2145ms (longer 200%)
dw $210,$3c4 ;29 - a4 (longer 300%)
dw $1a3,$4c7 ;30 - f4 (longer 300%)
dw $160,$3c4 ;31 - a4 (longer 200%)
dw $1d6,$43e ;32 - g4 (longer 300%)
dw $160,$5b4 ;33 - d4 (longer 300%)
dw $2c1,$43e ;34 - g4 (longer 450%)
dw $160,$1d3 ;35 - a5 1136ms
dw $139,$66b ;36 - c4 (longer 300%)
dw $274,$4c7 ;37 - f4 (longer 450%)



Pseudo_Tracker:
;First part
db 1,2,3,4,3,5,6,3,2,7,8,2,1,8,10,17
db 11,12,13,14,15,11,14,10,16,6,1,2,3,2,1
;First part (repeat)
db 1,2,3,4,3,5,6,3,2,7,8,2,1,8,10,17
db 11,12,13,14,15,11,14,10,16,6,1,2,3,2,1
;Second part
db 1,2,18,3,19,20,21,22,1,2,3,4,19,20,22,23
db 1,2,3,26,19,20,22,24,1,2,3,25,19,20,22,35
db 4,26,25,27,28,25,28,29,30,31,32,33,31,34
db 4,26,25,27,28,25,28,29,30,31,32,36,31,37
;End and exit
db 0


playit:
di
ld a,l ;
srl l ;
srl l ; L = medium part of tone period
cpl ;
and $03 ; A = 3 - fine part of tone period
ld c,a ;
ld b,$00 ;
ld ix,BE_IX_p_3 ; Address: BE_IX+3
add ix,bc ;   IX holds address of entry into the loop
;   the loop will contain 0-3 NOPs, implementing
;   the fine part of the tone period.

in a,($aa) ;Let's be sure we just modify the 7th bit
BE_IX_p_3:
nop ;(4) optionally executed NOPs for small
;        adjustments to tone period
BE_IX_p_2:
nop ;(4)

BE_IX_p_1:
nop ;(4)

BE_IX_p_0:
inc b ;(4)
inc c ;(4)


BE_H_L_LP:
dec c ;(4)    timing loop for duration of
jr nz,BE_H_L_LP ;(12/7)  high or low pulse of waveform

ld c,$3f ;(7)
dec b ;(4)
jp nz,BE_H_L_LP ;(10)   JUMP to BE_H&L_LP

xor $80 ;(7)    toggle output beep bit
out ($aa),a ;(11)   output pulse
ld b,h ;(4)    B = coarse part of tone period
ld c,a ;(4)    save port #AA output byte
bit 4,a ;(8)    if new output bit is high, go
jr nz,BE_AGAIN ;(2/7)  to BE_AGAIN

ld a,d ;(4)    one cycle of waveform has completed
or e ;(4)      (low->low). if cycle countdown = 0
jr z,BE_END ;(12/7)   go to BE_END

ld a,c ;(4)    restore output byte for port #AA
ld c,l ;(4)    C = medium part of tone period
dec de ;(6)    decrement cycle count
jp (ix) ;(8)    do another cycle

BE_AGAIN:
ld c,l ;(4)    C = medium part of tone period
inc c ;(4)    adds 16 cycles to make duration of high = duration of low
jp (ix) ;(8)    do high pulse of tone

BE_END:
ei                    ; Enable Interrupts
ret                   ;

End_Of_File:


; From the "Assembly Listing of the Sea Change ROM"
; Documented by Alvin Albrecht.
;
; -----------------------
; THE 'BEEPER' SUBROUTINE
; -----------------------
; Outputs a square wave of given duration and frequency
; to the loudspeaker.
;   Enter with: DE = #cycles - 1
;               HL = tone period as described next
;
; The tone period is measured in T states and consists of
; three parts: a coarse part (H register), a medium part
; (bits 7..2 of L) and a fine part (bits 1..0 of L) which
; contribute to the waveform timing as follows:
;
;                          coarse    medium       fine
; duration of low  = 118 + 1024*H + 16*(L>>2) + 4*(L&0x3)
; duration of hi   = 118 + 1024*H + 16*(L>>2) + 4*(L&0x3)
; Tp = tone period = 236 + 2048*H + 32*(L>>2) + 8*(L&0x3)
;                  = 236 + 2048*H + 8*L = 236 + 8*HL
;
; As an example, to output five seconds of middle C (261.624 Hz):
;   (a) Tone period = 1/261.624 = 3.822ms
;   (b) Tone period in T-States = 3.822ms*fCPU = 13378
;         where fCPU = clock frequency of the CPU = 3.5MHz
;    ©  Find H and L for desired tone period:
;         HL = (Tp - 236) / 8 = (13378 - 236) / 8 = 1643 = 0x066B
;   (d) Tone duration in cycles = 5s/3.822ms = 1308 cycles
;         DE = 1308 - 1 = 0x051B
;
; The resulting waveform has a duty ratio of exactly 50%.
;
; ---------------------
; THE 'SEMI-TONE' TABLE
; ---------------------
;
;   Holds frequencies corresponding to semitones in middle octave.
;   To move n octaves higher or lower, frequencies are multiplied by 2^n.
;
;semi_tone:
;  261.625565290         C
;  277.182631135         C#
;  293.664768100         D
;  311.126983881         D#
;  329.627557039         E
;  349.228231549         F
;  369.994422674         F#
;  391.995436072         G
;  415.304697513         G#
;  440.000000000         A
;  466.163761616         A#
;  493.883301378         B


Título: Re: 1 bit player
Publicado por: pitpan en 23 de Octubre de 2006, 07:50:53 pm
Congratulations, Dioniso. It's really cool and the sound quality is unbeliavable for the poor old 1-bit key-click generator. I guess that the MSX engineers did not think about this use ;)

I've just ported your code to asMSX syntax to make it easier for other users.

   .bios
   .org   $9000
   .basic
   .start    INIT

INIT:

   in a,[$aa]   ;If we don't do this we'll have
   and $ef      ;problems when we read this port
   out [$aa],a   ;later.
   nop
   nop

   ld hl,Pseudo_Tracker
The_Loop:
   xor a
        cp [hl]
   ret z
   push hl
   
        ld a,[hl]
   add a,a
   add a,a
   ld h,0
   ld l,a
   ld de,Notes_And_Length-4
   add hl,de

   push hl
   pop ix
        ld e,[ix+0]
        ld d,[ix+1]
        ld l,[ix+2]
        ld h,[ix+3]
   call playit
   pop hl
   inc hl
   jr The_Loop

Notes_And_Length:   ;First 2 bytes = Length // Last 2 bytes = Note
   dw $68,$66b   ;01 - c4 3822ms
   dw $75,$5b4   ;02 - d4 3405ms
   dw $83,$511   ;03 - e4 3033ms
   dw $8b,$4c7   ;04 - f4 2863ms
        dw $b0,$5b4     ;05 - d4 [longer 50%]
        dw $34,$66b     ;06 - c4 [shorter 50%]
        dw $9c,$66b     ;07 - c4 [longer 50%]
        dw $2e,$737     ;08 - a#3 [shorter 50%]
   dw $5c,$737   ;09 - a#3 4290ms
   dw $58,$7a6   ;10 - a3 4545ms
        dw $2c,$7a6     ;11 - a3 [shorter 50%]
        dw $27,$89a     ;12 - g3 [shorter 50%]
   dw $45,$9ab   ;13 - f3 5726ms
   dw $4e,$89a   ;14 - g3 5102ms
        dw $84,$7a6     ;15 - a3 [longer 50%]
        dw $8a,$737     ;16 - a#3 [longer 50%]
        dw $138,$66b    ;17 - c4 [longer 300%]
   dw $7c,$560   ;18 - d#4 3214ms
   dw $d1,$326   ;19 - c5 1911ms
   dw $eb,$2cb   ;20 - d5 1702ms
   dw $f8,$2a1   ;21 - d#5 1607ms
   dw $107,$279   ;22 - e5 1516ms
   dw $117,$254   ;23 - f5 1431ms
   dw $139,$210   ;24 - g5 1275ms
   dw $b0,$3c4   ;25 - a4 2272ms
   dw $9c,$43e   ;26 - g4 2551ms
        dw $22f,$38c    ;27 - a#4 2145ms [longer 300%]
        dw $174,$38c    ;28 - a#4 2145ms [longer 200%]
        dw $210,$3c4    ;29 - a4 [longer 300%]
        dw $1a3,$4c7    ;30 - f4 [longer 300%]
        dw $160,$3c4    ;31 - a4 [longer 200%]
        dw $1d6,$43e    ;32 - g4 [longer 300%]
        dw $160,$5b4    ;33 - d4 [longer 300%]
        dw $2c1,$43e    ;34 - g4 [longer 450%]
   dw $160,$1d3   ;35 - a5 1136ms
        dw $139,$66b    ;36 - c4 [longer 300%]
        dw $274,$4c7    ;37 - f4 [longer 450%]



Pseudo_Tracker:
   ;First part
   db 1,2,3,4,3,5,6,3,2,7,8,2,1,8,10,17
   db 11,12,13,14,15,11,14,10,16,6,1,2,3,2,1
        ;First part [repeat]
   db 1,2,3,4,3,5,6,3,2,7,8,2,1,8,10,17
   db 11,12,13,14,15,11,14,10,16,6,1,2,3,2,1
   ;Second part
   db 1,2,18,3,19,20,21,22,1,2,3,4,19,20,22,23
   db 1,2,3,26,19,20,22,24,1,2,3,25,19,20,22,35
   db 4,26,25,27,28,25,28,29,30,31,32,33,31,34
   db 4,26,25,27,28,25,28,29,30,31,32,36,31,37
   ;End and exit
   db 0


playit:
   di
   ld a,l      ;
   srl l      ;
   srl l      ; L = medium part of tone period
   cpl      ;
   and $03      ; A = 3 - fine part of tone period
   ld c,a      ;
   ld b,$00   ;
   ld ix,BE_IX_p_3   ; Address: BE_IX+3
   add ix,bc   ;   IX holds address of entry into the loop
         ;   the loop will contain 0-3 NOPs, implementing
         ;   the fine part of the tone period.

        in a,[$aa]      ;Let's be sure we just modify the 7th bit
BE_IX_p_3:
        nop             ;[4] optionally executed NOPs for small
         ;        adjustments to tone period
BE_IX_p_2:
        nop             ;[4]

BE_IX_p_1:
        nop             ;[4]

BE_IX_p_0:
        inc b           ;[4]
        inc c           ;[4]


BE_H_L_LP:
        dec c           ;[4]    timing loop for duration of
        jr nz,BE_H_L_LP ;[12/7]  high or low pulse of waveform

        ld c,$3f        ;[7]
        dec b           ;[4]
        jp nz,BE_H_L_LP ;[10]   JUMP to BE_H&L_LP

        xor $80         ;[7]    toggle output beep bit
        out [$aa],a     ;[11]   output pulse
        ld b,h          ;[4]    B = coarse part of tone period
        ld c,a          ;[4]    save port #AA output byte
        bit 4,a         ;[8]    if new output bit is high, go
        jr nz,BE_AGAIN  ;[2/7]  to BE_AGAIN

        ld a,d          ;[4]    one cycle of waveform has completed
        or e            ;[4]      [low->low]. if cycle countdown = 0
        jr z,BE_END     ;[12/7]   go to BE_END

        ld a,c          ;[4]    restore output byte for port #AA
        ld c,l          ;[4]    C = medium part of tone period
        dec de          ;[6]    decrement cycle count
        jp [ix]         ;[8]    do another cycle

BE_AGAIN:
        ld c,l          ;[4]    C = medium part of tone period
        inc c           ;[4]    adds 16 cycles to make duration of high = duration of low
        jp [ix]         ;[8]    do high pulse of tone

BE_END:
   ei                    ; Enable Interrupts
   ret                   ;


; From the "Assembly Listing of the Sea Change ROM"
; Documented by Alvin Albrecht.
;
; -----------------------
; THE 'BEEPER' SUBROUTINE
; -----------------------
; Outputs a square wave of given duration and frequency
; to the loudspeaker.
;   Enter with: DE = #cycles - 1
;               HL = tone period as described next
;
; The tone period is measured in T states and consists of
; three parts: a coarse part [H register], a medium part
; [bits 7..2 of L] and a fine part [bits 1..0 of L] which
; contribute to the waveform timing as follows:
;
;                          coarse    medium       fine
; duration of low  = 118 + 1024*H + 16*[L>>2] + 4*[L&0x3]
; duration of hi   = 118 + 1024*H + 16*[L>>2] + 4*[L&0x3]
; Tp = tone period = 236 + 2048*H + 32*[L>>2] + 8*[L&0x3]
;                  = 236 + 2048*H + 8*L = 236 + 8*HL
;
; As an example, to output five seconds of middle C [261.624 Hz]:
;   [a] Tone period = 1/261.624 = 3.822ms
;    Tone period in T-States = 3.822ms*fCPU = 13378
;         where fCPU = clock frequency of the CPU = 3.5MHz
;   [c]  Find H and L for desired tone period:
;         HL = [Tp - 236] / 8 = [13378 - 236] / 8 = 1643 = 0x066B
;   [d] Tone duration in cycles = 5s/3.822ms = 1308 cycles
;         DE = 1308 - 1 = 0x051B
;
; The resulting waveform has a duty ratio of exactly 50%.
;
; ---------------------
; THE 'SEMI-TONE' TABLE
; ---------------------
;
;   Holds frequencies corresponding to semitones in middle octave.
;   To move n octaves higher or lower, frequencies are multiplied by 2^n.
;
;semi_tone:
;  261.625565290         C
;  277.182631135         C#
;  293.664768100         D
;  311.126983881         D#
;  329.627557039         E
;  349.228231549         F
;  369.994422674         F#
;  391.995436072         G
;  415.304697513         G#
;  440.000000000         A
;  466.163761616         A#
;  493.883301378         B


Título: Re: 1 bit player
Publicado por: Jon_Cortazar en 24 de Octubre de 2006, 06:46:41 am
This is a small player to play songs through the 1-bit port $AA.

When I grow up I want to be like you :god: :god: :god: :god: :god: :god:


Título: Re: 1 bit player
Publicado por: Dioniso en 24 de Octubre de 2006, 04:21:52 pm
Thanks Robsy and Jon. Good idea to port it to asMSX syntax. Most of the people here use your assembler.


Título: Re: 1 bit player
Publicado por: pitpan en 24 de Octubre de 2006, 07:16:11 pm
Only minor changes indeed from your source:

- () indirections replaced by []
- BASIC header generated by asMSX directives (.BASIC, .START)

Anyway, the thing is that using the 1-bit keyboard click port you can do now amazing things. What about a 1-bit tracker? I do not mean a full-sized application, but a simple Excel spreadsheet that converts notes (C, C#, etc) to your program values.

It is just a simple idea. But it would be cool to make a PSGless MSX music mini-compo!


Título: Re: 1 bit player
Publicado por: SapphiRe en 24 de Octubre de 2006, 07:18:15 pm
It is just a simple idea. But it would be cool to make a PSGless MSX music mini-compo!

GRRRRRRRRRRRRRRREEEEEEEEEEEEEAAAT!! ;D

This sounds me like: "hey! We have a PSG but we are the best and we don't need it to make music!" ;D ;D ;D ;D ;D


Título: Re: 1 bit player
Publicado por: pitpan en 24 de Octubre de 2006, 07:48:15 pm
That's the thing: let's do it the hard way  ;D


Título: Re: 1 bit player
Publicado por: WYZ en 24 de Octubre de 2006, 09:24:12 pm
So we have four channels to play music ;D. Really great Dioniso. ;)


Título: Re: 1 bit player
Publicado por: jltursan en 24 de Octubre de 2006, 10:25:16 pm
Yay!, sounds very convincing to be only a "beeper" ;). IIRC Dioniso already used this technique in some of their games, T-Virus maybe?. Great to create some extra SFX! :D


Título: Re: 1 bit player
Publicado por: ARTRAG en 25 de Octubre de 2006, 12:07:37 am
Great! it sound very well.
I remember that using SW synthesis on the zx spectrum, it was possible to have up to 6 channels on the PSG (again
using 100 cpu). The idea was to generate two square waves per channes using counters - like now.

Ok, now I remember, dioniso knows already the story :-)
http://www.west.co.tt/matt/speccy/apology/ for details


Título: Re: 1 bit player
Publicado por: Dioniso en 25 de Octubre de 2006, 01:04:14 pm
Anyway, the thing is that using the 1-bit keyboard click port you can do now amazing things. What about a 1-bit tracker? I do not mean a full-sized application, but a simple Excel spreadsheet that converts notes (C, C#, etc) to your program values.

Not only that  ;)

It's just a question of time. I hope I can finish what I (still in my head) have for the MSXDev06 ... If I just had more time  :'(


Título: Re: 1 bit player
Publicado por: WYZ en 06 de Enero de 2007, 09:34:26 pm
Here is a previous port of ZX Wham! (2 channels without percussion)



Título: Re: 1 bit player
Publicado por: Dioniso en 06 de Enero de 2007, 10:45:47 pm
Wyz, aún recuerdo cuando te dije: "Hostias, mira que demo más guapa ha hecho Mr. Beep! (Beeper Demo)" y me contestaste: "No sé qué decir, no conozco ese chip de sonido"  ;)

Lo mejor que hay ahora en música 1bit es Mr. Beep: Su web (http://republika.pl/mister_beep/)

Lo único que ocurre con este chaval es que no sabe programar ... utiliza software ya creado para hacer música; pero es una pasada. Mi canción favorita (http://parishq.net/proposed//sound/mister_beep-shpyon.mp3) de Mr. Beep.

Aunque hay una versión de Jet Set Willy (http://parishq.net/proposed//sound/zilog-jet_set_willy_cover.mp3) de otro compositor, Zilog, que quita el hipo  :o

El problema es que estas canciones ocupan un huevo.


Título: Re: 1 bit player
Publicado por: WYZ en 06 de Enero de 2007, 11:11:30 pm
Y sigo si saber que es el beeper ;D.  Esto que he colgado viene por una conversacion sobre los nuevos juegos de Na_th_an  y su posible publicacion para MSX y en cinco mintillos ha surgido una version MSX a partir del una parte de codigo que me ha proporcionado Black Hole,  del mundillo del ZX.

El Wham! es lo mas sencillo en editores de 1 bit, aunque se pueden conseguir efectos bastante buenos y es tremendo lo que se puede hacer con el simple beeper, al oido está, pero espero que no estes pensando en ... porque a mi ni se me ocurre ejecutar algo que suene como lo de Mr. Beeper en mis MSX. Ya me asusté mucho con el Beeper game.

Ahps, si llevas algo mas avanzado en esto ya estas tardando en enviarme un algo a mail!





Título: Re: 1 bit player
Publicado por: Dioniso en 06 de Enero de 2007, 11:38:32 pm
Y sigo si saber que es el beeper ;D.  Esto que he colgado viene por una conversacion sobre los nuevos juegos de Na_th_an  y su posible publicacion para MSX y en cinco mintillos ha surgido una version MSX a partir del una parte de codigo que me ha proporcionado Black Hole,  del mundillo del ZX.

Alguien de por aquí ya me ha comentado algo. Sigo ese tema desde "cerca" pero no me quiero involucrar. Por cierto, ese juego de Na_th_an (Phantomasa?  ;) ) se ejecuta desde Basic (en Basic) pero utiliza el Wham (si no he oído mal) con música 1bit? La verdad es que no corre nada mal para estar en Basic.

Citar
El Wham! es lo mas sencillo en editores de 1 bit, aunque se pueden conseguir efectos bastante buenos y es tremendo lo que se puede hacer con el simple beeper, al oido está, pero espero que no estes pensando en ... porque a mi ni se me ocurre ejecutar algo que suene como lo de Mr. Beeper en mis MSX.:D

Bueno ... no sé ...  ::) No soy muy amigo del software que existe para Speccy. Lo que yo hice fue ... una putada para mí:

-Ejecutar el BlueMSX.
-Desde Basic y con un simple PLAY ejecutar dos octavas completas.
-Capturar el audio.
-Separar cada nota con el Goldwave.
-Aplicar un poco de efecto "robot" ( :P) a cada nota y guardarlas en 8bit raw.
-Pasarlas a 1bit con el software de Edu.
-Editor hexadecimal y ver qué trozos se repetían en una misma nota para poder hacer el loop si se mantiene pulsada la tecla del piano.
-Probar las 24 notas ... y volver a crear las notas que no sonaban bien ... (una pesadilla).

Editor ... mi guitarra Beep con guitarra española de mala calidad (http://www.geocities.com/dioniso072/songs/beep.wma)  ;D


Título: Re: 1 bit player
Publicado por: WYZ en 06 de Enero de 2007, 11:59:07 pm
Citar
...Editor ... mi guitarra: Beep con guitarra española de mala calidad

Juas! cada dia mas freaks.