Difference between revisions of "Z80:Sound Routines"
KermMartian (talk | contribs) (Created page with "Sound require headphones to plug in. Since nearly no one has these, generally do not include sound into a game. ONLY use sound if that is the main purpose of the program. Thi...") |
KermMartian (talk | contribs) |
||
(One intermediate revision by the same user not shown) | |||
Line 5: | Line 5: | ||
('''Note''' to readers : the first real addition to this page is by Zeda, and I am not very familiar with the finer points of making sound. I only experimented, took pointers from others and eventually used a routine in Axe (after optimising it slightly). Anyways, there are others that are much more versed in this topic!) | ('''Note''' to readers : the first real addition to this page is by Zeda, and I am not very familiar with the finer points of making sound. I only experimented, took pointers from others and eventually used a routine in Axe (after optimising it slightly). Anyways, there are others that are much more versed in this topic!) | ||
− | = 1-Channel Sound | + | = 1-Channel Sound = |
− | |||
The idea behind making sound on the calculators is to toggle a line on the link port at a given frequency. The link port has two lines making 1 and 2 channel sound relatively okay, but there are some who have made 4-channel sound work using clever masking. Here is a very simple routine for 1-channel sound (optimised from Axe) : | The idea behind making sound on the calculators is to toggle a line on the link port at a given frequency. The link port has two lines making 1 and 2 channel sound relatively okay, but there are some who have made 4-channel sound work using clever masking. Here is a very simple routine for 1-channel sound (optimised from Axe) : | ||
Line 13: | Line 12: | ||
; HL is the duration of the note | ; HL is the duration of the note | ||
; BC is the frequency | ; BC is the frequency | ||
− | + | xor a | |
__FreqOutLoop1: | __FreqOutLoop1: | ||
− | + | push bc | |
− | + | xor %00000011 ;this will toggle the lower two bits (the data being sent to the link port) | |
− | + | ld e,a | |
__FreqOutLoop2: | __FreqOutLoop2: | ||
− | + | ld a,h | |
− | + | or l | |
− | + | jr z,__FreqOutDone | |
− | + | cpd | |
− | + | jp pe,__FreqOutLoop2 | |
− | + | ld a,e | |
scf | scf | ||
__FreqOutDone: | __FreqOutDone: | ||
− | + | pop bc | |
− | + | out (0),a | |
− | + | jr c,__FreqOutLoop1 | |
− | + | xor b | |
− | + | out (0),a ;reset the port, else the user will be really annoyed. | |
− | + | ret | |
An example duration would be 4096 and frequencies can be made to correspond to certain notes. If Duration is shorter than Frequency, nothing is done to the link port. Here is an LUT (Lookup Table) of frequencies that correspond to 96 notes (starting with the lowest, going to highest): | An example duration would be 4096 and frequencies can be made to correspond to certain notes. If Duration is shorter than Frequency, nothing is done to the link port. Here is an LUT (Lookup Table) of frequencies that correspond to 96 notes (starting with the lowest, going to highest): | ||
Line 77: | Line 76: | ||
; DHL is the duration of the note | ; DHL is the duration of the note | ||
; BC is the frequency | ; BC is the frequency | ||
− | + | xor a | |
__FreqOutLoop1: | __FreqOutLoop1: | ||
− | + | push bc | |
− | + | xor %00000011 | |
− | + | ld e,a | |
__FreqOutLoop2: | __FreqOutLoop2: | ||
− | + | ld a,h | |
− | + | or l | |
− | + | jr nz,$+6 | |
− | + | dec d | |
− | + | jp m,__FreqOutDone | |
− | + | ||
− | + | cpd | |
− | + | jp pe,__FreqOutLoop2 | |
− | + | ld a,e | |
scf | scf | ||
__FreqOutDone: | __FreqOutDone: | ||
− | + | pop bc | |
− | + | out (0),a | |
− | + | jr c,__FreqOutLoop1 | |
− | + | xor b | |
− | + | out (0),a | |
− | + | ret | |
− | Now that should remove some of the choppiness from the sound for lower notes with longer durations, though the change might not be noticeable. | + | Now that should remove some of the choppiness from the sound for lower notes with longer durations, though the change might not be noticeable. |
{{lowercase}} | {{lowercase}} | ||
[[Category:Z80 Assembly]] | [[Category:Z80 Assembly]] | ||
[[Category:Z80 Heaven]] | [[Category:Z80 Heaven]] |
Latest revision as of 05:45, 5 February 2016
Sound require headphones to plug in. Since nearly no one has these, generally do not include sound into a game. ONLY use sound if that is the main purpose of the program.
This page needs more info. Members of this site can edit pages.
(Note to readers : the first real addition to this page is by Zeda, and I am not very familiar with the finer points of making sound. I only experimented, took pointers from others and eventually used a routine in Axe (after optimising it slightly). Anyways, there are others that are much more versed in this topic!)
1-Channel Sound
The idea behind making sound on the calculators is to toggle a line on the link port at a given frequency. The link port has two lines making 1 and 2 channel sound relatively okay, but there are some who have made 4-channel sound work using clever masking. Here is a very simple routine for 1-channel sound (optimised from Axe) :
p_FreqOut: ;Inputs: ; HL is the duration of the note ; BC is the frequency xor a __FreqOutLoop1: push bc xor %00000011 ;this will toggle the lower two bits (the data being sent to the link port) ld e,a __FreqOutLoop2: ld a,h or l jr z,__FreqOutDone cpd jp pe,__FreqOutLoop2 ld a,e scf __FreqOutDone: pop bc out (0),a jr c,__FreqOutLoop1 xor b out (0),a ;reset the port, else the user will be really annoyed. ret
An example duration would be 4096 and frequencies can be made to correspond to certain notes. If Duration is shorter than Frequency, nothing is done to the link port. Here is an LUT (Lookup Table) of frequencies that correspond to 96 notes (starting with the lowest, going to highest):
FrequencyLUT: .dw 2100,1990,1870,1770,1670,1580,1490,1400,1320,1250,1180,1110 .dw 1050, 996, 940, 887, 837, 790, 746, 704, 665, 627, 592, 559 .dw 527, 498, 470, 444, 419, 395, 373, 352, 332, 314, 296, 279 .dw 264, 249, 235, 222, 209, 198, 186, 176, 166, 157, 148, 140 .dw 132, 124, 117, 111, 105, 99, 93, 88, 83, 78, 74, 70 .dw 66, 62, 59, 55, 52, 49, 47, 44, 42, 39, 37, 35 .dw 33, 31, 29, 28, 26, 25, 23, 22, 21, 20, 19, 18 .dw 17, 16, 15, 14, 13, 12, 11, 10, 10, 9, 9, 8 .dw 8, 7, 7, 7, 7, 6, 6, 5, 5, 5, 5, 4
So to play a given note A for a duration of D, your routine might look like this:
ld hl,FrequencyLUT add a,l ld l,a jr nc,$+3 inc h ld c,(hl) inc hl ld b,(hl) ;now BC is the frequency for the note SoundLoop: push bc ld hl,4096 call p_FreqOut pop bc dec bc ld a,b or c jr nz,SoundLoop ret
In this case, if input D is 16, the note will last a noticeable amount of time. This method allows you to make the duration up to 4096*256 (and a multiple of 4096), but there is a downside. If we look at the first value of the LUT, 2100, using a duration of 4096 means that the port is toggled only once (we need 2100*2 for the duration in order to toggle twice). The toggling is what creates the sound. If we had used a duration of 16384, the port would be toggled 7 times, but with this method, 16384=4*4096, so we would get it toggled only 4 times. This makes for choppy sound.
That is not to say that the above routine is bad-- it is a great option for playing sound in games where you cannot devote the full processor time to sound and it allows you to extend duration beyond 16 bits. However, if the latter is what is sought, we can use a modified routine to have 24-bit durations:
p_FreqOut: ;Inputs: ; DHL is the duration of the note ; BC is the frequency xor a __FreqOutLoop1: push bc xor %00000011 ld e,a __FreqOutLoop2: ld a,h or l jr nz,$+6 dec d jp m,__FreqOutDone cpd jp pe,__FreqOutLoop2 ld a,e scf __FreqOutDone: pop bc out (0),a jr c,__FreqOutLoop1 xor b out (0),a ret
Now that should remove some of the choppiness from the sound for lower notes with longer durations, though the change might not be noticeable.