I've been inspired by Sockmaster's MOD player running on a 68B09E @ 1.9mhz and a CPU feed DAC (not sure if he uses an interrupt system or not).
I'm writing the player for the huc6280 @ 7.16mhz (a rockwell variant) and a six channel 5bit DAC, each with a 32 sample buffer. Setting the system timer interrupt to 2hkz, I can output 64khz channels per channel. The problem is I can't do variable frequency that way. I'm going to have to (roughly) interpolate the variable frequencies into the fixed frequency of 64khz. Do you guys have any experience with this? Interpolating a sample's frequency into a fixed frequency output?
Also, I have info on the MOD format that explains all the details I need to know but.. the note frequency has me confused. The original Amiga had a max frequency playback of 28khz, but some of the "legal" octave range into 32khz and above (C for octave 3 starts at 33khz).
-Rich
Writing a MOD player
- GARTHWILSON
- Forum Moderator
- Posts: 8775
- Joined: 30 Aug 2002
- Location: Southern California
- Contact:
I'm not familiar with MOD or the HUC6280, but I hope this will be relevant anyway.
Interpolation, if you want low granularity of frequency, requires multiplication and division which won't be fast enough, and it will still introduce a little distortion unless you pour on a ton more processing power to run transforms.
However, if you have a timer like the 6522's T1 that can generate the interrupts, you can just change the interrupt rate, on the fly, by writing a new value to the latches that feed the counters when they roll over, and then feed one sample out with each interrupt. The ISR will be pretty simple. The RMS jitter you'll get from interrupts with a 6502 running at 7.16MHz will give better than 7 bits of accuracy at 5kHz for a good D/A, and more accuracy at lower frequencies. (Note that this is the analog output frequency. The sampling rate will be much higher.) Actually, there are tricks to further improve on that, but as is, an 8-bit converter giving basically every bit correct for inputs below $80 is better than the 5-bit converter anyway. If you have other interrupts going at the same time, it would be good to put this one on NMI since the timing requirements are so stringent.
My 6502 interrupts primer touches on that. For an 8-bit converter, there are lots of simple parallel-input ones like the DAC0808 which you can connect to one of the ports of the 6522, and all you have to do to update the D/A's output is to write to the port, using a single STA XXXX instruction.
Interpolation, if you want low granularity of frequency, requires multiplication and division which won't be fast enough, and it will still introduce a little distortion unless you pour on a ton more processing power to run transforms.
However, if you have a timer like the 6522's T1 that can generate the interrupts, you can just change the interrupt rate, on the fly, by writing a new value to the latches that feed the counters when they roll over, and then feed one sample out with each interrupt. The ISR will be pretty simple. The RMS jitter you'll get from interrupts with a 6502 running at 7.16MHz will give better than 7 bits of accuracy at 5kHz for a good D/A, and more accuracy at lower frequencies. (Note that this is the analog output frequency. The sampling rate will be much higher.) Actually, there are tricks to further improve on that, but as is, an 8-bit converter giving basically every bit correct for inputs below $80 is better than the 5-bit converter anyway. If you have other interrupts going at the same time, it would be good to put this one on NMI since the timing requirements are so stringent.
My 6502 interrupts primer touches on that. For an 8-bit converter, there are lots of simple parallel-input ones like the DAC0808 which you can connect to one of the ports of the 6522, and all you have to do to update the D/A's output is to write to the port, using a single STA XXXX instruction.
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
-
tomaitheous
- Posts: 24
- Joined: 22 Sep 2007
GARTHWILSON wrote:
I'm not familiar with MOD or the HUC6280, but I hope this will be relevant anyway.
Interpolation, if you want low granularity of frequency, requires multiplication and division which won't be fast enough, and it will still introduce a little distortion unless you pour on a ton more processing power to run transforms.
However, if you have a timer like the 6522's T1 that can generate the interrupts, you can just change the interrupt rate, on the fly, by writing a new value to the latches that feed the counters when they roll over, and then feed one sample out with each interrupt. The ISR will be pretty simple. The RMS jitter you'll get from interrupts with a 6502 running at 7.16MHz will give better than 7 bits of accuracy at 5kHz for a good D/A, and more accuracy at lower frequencies. (Note that this is the analog output frequency. The sampling rate will be much higher.) Actually, there are tricks to further improve on that, but as is, an 8-bit converter giving basically every bit correct for inputs below $80 is better than the 5-bit converter anyway. If you have other interrupts going at the same time, it would be good to put this one on NMI since the timing requirements are so stringent.
Interpolation, if you want low granularity of frequency, requires multiplication and division which won't be fast enough, and it will still introduce a little distortion unless you pour on a ton more processing power to run transforms.
However, if you have a timer like the 6522's T1 that can generate the interrupts, you can just change the interrupt rate, on the fly, by writing a new value to the latches that feed the counters when they roll over, and then feed one sample out with each interrupt. The ISR will be pretty simple. The RMS jitter you'll get from interrupts with a 6502 running at 7.16MHz will give better than 7 bits of accuracy at 5kHz for a good D/A, and more accuracy at lower frequencies. (Note that this is the analog output frequency. The sampling rate will be much higher.) Actually, there are tricks to further improve on that, but as is, an 8-bit converter giving basically every bit correct for inputs below $80 is better than the 5-bit converter anyway. If you have other interrupts going at the same time, it would be good to put this one on NMI since the timing requirements are so stringent.
There is a direct feed mode where the buffer is disabled and you directly write bytes to the appropriate port, but the timer isn't refined enough to handle the small differences between notes(sample frequency), let alone to handle all the channels. Plus the timer is limited to a max frequency of 7khz.
So I'm pretty much stuck with interpolating the samples variable frequencies into the fixed output. I should note that I don't have the option of adding/changing the hardware because user base.
- GARTHWILSON
- Forum Moderator
- Posts: 8775
- Joined: 30 Aug 2002
- Location: Southern California
- Contact:
Depending on the amount of memory you have available, you might be able to make up a large look-up table for the interpolation in order to avoid the calculation intensiveness. It could take a long time to calculate the table the first time, but after that the numbers are there free for the looking up. I've done this with 128KB tables of logarithms, sines, etc., for 16-bit fixed-point and scaled-integer math. If you don't have enough memory, do these computers have some kind of plug-in slot like the Commodore 64 did? You could put more memory there, or a 65c22 to get the extra counter, or something like that.
(Edited 6/25/12 to add link to look-up tables web pages for super fast, accurate 16-bit math)
(Edited 6/25/12 to add link to look-up tables web pages for super fast, accurate 16-bit math)
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
-
tomaitheous
- Posts: 24
- Joined: 22 Sep 2007
I have up to 3 memory configurations available, depending on what format I'm using for the system. The first configuration is a standard cartridge setup with 2.5 megs of cart space and 8k of ram, the second is 8k ram + 256k ram + 32k I/O ram, and the third is 8k ram + 256k ram + +32k I/O ram + 2megs of I/O port ram (I/O interface has automatic incrementing/decrementing pointers). I'm aiming more towards the second configuration since it's the most popular. If I could keep the lookup table with in the 16k-32k range, that'd be great. Though the emphasis is more on speed at the moment. I'd like to get CPU resource down to 30% or lower for the MOD playback routine (on the 65CS02 @ 7.16mhz).
Anyway, I guess I should get some working code up first, then start looking for optimizations.
Anyway, I guess I should get some working code up first, then start looking for optimizations.
-
tomaitheous
- Posts: 24
- Joined: 22 Sep 2007
I got the frequency scaler up and working. Just a simple fixed point math LUT. The 8bit whole number + 8bit float are the counter for reading the sample data to the DAC (or a buffer for DMA). I made a 100hz increment table from from 0khz to 32khz divided by my output frequency of 15.7khz (using the scanline interrupt).
It sounds great. I'm not sure I even need to add any sort of interpolation on top of the scaler for the overhead involved. Well, maybe for a 7khz PCM driver version I might.
I decided to switch to a 10bit output and mix all sound channels into a single channel to keep overhead down of writing to multiple DACs per interrupt.
It sounds great. I'm not sure I even need to add any sort of interpolation on top of the scaler for the overhead involved. Well, maybe for a 7khz PCM driver version I might.
I decided to switch to a 10bit output and mix all sound channels into a single channel to keep overhead down of writing to multiple DACs per interrupt.
Re: Writing a MOD player
Google dropped me here when I was trying to find 6502 assembly source to a MOD file player. AtariAge had a thread, http://atariage.com/forums/topic/210972 ... miga-mods/, about mod file players for the Atari 8 bit home computers. Peeking through the contents of the Atari disk images, there are no .asm files. Boy is it silly for five or six different people to re-invent this same wheel and not share their work. Any leads appreciated.
-
tomaitheous
- Posts: 24
- Joined: 22 Sep 2007
Re: Writing a MOD player
What's the target system that you're writing this player for? I ended up writing a converter and a custom MOD player. Basically re-arranging some stuffs so that they are speedier and such. The player code I wrote, is too specific to the system to really use for anything else - even if it is 65x based.
I got a lot of MOD docs from the net, and the format was pretty straight forward. If anything, it was small details like FX that were a pain. Some start on the current 'tick' and some start on the next tick. But other than that, it's pretty straight forward. I just did a straight aliasing nearest-neighbor method, since it was all interrupt driven DAC updates.
I got a lot of MOD docs from the net, and the format was pretty straight forward. If anything, it was small details like FX that were a pain. Some start on the current 'tick' and some start on the next tick. But other than that, it's pretty straight forward. I just did a straight aliasing nearest-neighbor method, since it was all interrupt driven DAC updates.
Re: Writing a MOD player
Atari XE/XL, POKEY 6bit, CPU 65816 (HighMem)
sample_loop (IRQ 15KHz)
main_loop (VBL)
sample_loop (IRQ 15KHz)
Code: Select all
irq
.ia16
sta regA
stx regX
.ia8
lda #0
sta.l IRQEN
lda #1
sta.l IRQEN
v0 lda #0
sta.l audc1
v1 lda #0
sta.l audc2
v2 lda #0
sta.l audc3
; ---
; --- AUDC 1
; ---
clc
ist_0 lda #0
iad0_m adc #0
sta ist_0+1
lda p_0c+1
iad0_s adc #0
bcc ext_0
inc p_0c+2
bne ext_0
ire0_s lda #0
sta p_0c+2
ire0_m lda #0
ext_0 sta p_0c+1
; ---
; --- AUDC 2
; ---
clc
ist_1 lda #0
iad1_m adc #0
sta ist_1+1
lda p_1c+1
iad1_s adc #0
bcc ext_1
inc p_1c+2
bne ext_1
ire1_s lda #0
sta p_1c+2
ire1_m lda #0
ext_1 sta p_1c+1
; ---
; --- AUDC 3
; ---
clc
ist_2 lda #0
iad2_m adc #0
sta ist_2+1
lda p_2c+1
iad2_s adc #0
bcc ext_2
inc p_2c+2
bne ext_2
ire2_s lda #0
sta p_2c+2
ire2_m lda #0
ext_2 sta p_2c+1
; ---
; --- AUDC 4
; ---
clc
ist_3 lda #0
iad3_m adc #0
sta ist_3+1
lda p_3c+1
iad3_s adc #0
bcc ext_3
inc p_3c+2
bne ext_3
ire3_s lda #0
sta p_3c+2
ire3_m lda #0
ext_3 sta p_3c+1
p_0c lda.l sample_start+$FFFF ; ch #1
sta ivol0+1
p_1c lda.l sample_start+$FFFF ; ch #2
sta ivol1+1
p_2c lda.l sample_start+$FFFF ; ch #3
sta ivol2+1
p_3c lda.l sample_start+$FFFF ; ch #4
sta ivol3+1
clc
ivol0 lda volume
ivol1 adc volume
ivol2 adc volume
ivol3 adc volume
tax
lda vol6bit,x
sta v0+1
lda vol6bit+$100,x
sta v1+1
lda vol6bit+$200,x
sta v2+1
.ia16
lda.w #0
regA equ *-2
ldx.w #0
regX equ *-2
rti
main_loop (VBL)
Code: Select all
mainloop
.ai16
sta regA
stx regX
sty regY
.ai8
dec cnts
seq
jmp nmiExit
stz patend
ldy track_pos
*---------------------------
* track 0
i_0 ;ldy #1
lda [pat1],y
sta i_0c+1
and #$1f
beq i_0c
tax
sta nr0
lda adr.tivol-1,x
sta playloop.ivol0+2
i_0c ldx EFFECT
beq i_0f
cpx #$40
bne @+
;ldy #2
lda [pat2],y
sta playloop.ivol0+2
@ cpx #$c0
bne @+
;ldy #2
lda [pat2],y
sta pause
@ cpx #$80
bne i_0f
stx patend
i_0f ;ldy #0
lda [pat0],y
beq i_1
tax
lda tadcl-1,x
sta playloop.iad0_m+1
lda tadch-1,x
sta playloop.iad0_s+1
; lda #0
; sta playloop.ist_0+1
ldx nr0
txa
add ^sample_start-1
sta playloop.p_0c+3
lda adr.tstrl-1,x
sta playloop.p_0c+1
lda adr.tstrh-1,x
sta playloop.p_0c+2
lda adr.trepl-1,x
sta playloop.ire0_m+1
lda adr.treph-1,x
sta playloop.ire0_s+1
* track 1
i_1 iny
;ldy #4
lda [pat1],y
sta i_1c+1
and #$1f
beq i_1c
tax
sta nr1
lda adr.tivol-1,x
sta playloop.ivol1+2
i_1c ldx EFFECT
beq i_1f
cpx #$40
bne @+
;ldy #5
lda [pat2],y
sta playloop.ivol1+2
@ cpx #$c0
bne @+
;ldy #5
lda [pat2],y
sta pause
@ cpx #$80
bne i_1f
stx patend
i_1f ;ldy #3
lda [pat0],y
beq i_2
tax
lda tadcl-1,x
sta playloop.iad1_m+1
lda tadch-1,x
sta playloop.iad1_s+1
; lda #0
; sta playloop.ist_1+1
ldx nr1
txa
add ^sample_start-1
sta playloop.p_1c+3
lda adr.tstrl-1,x
sta playloop.p_1c+1
lda adr.tstrh-1,x
sta playloop.p_1c+2
lda adr.trepl-1,x
sta playloop.ire1_m+1
lda adr.treph-1,x
sta playloop.ire1_s+1
* track 2
i_2 iny
;ldy #7
lda [pat1],y
sta i_2c+1
and #$1f
beq i_2c
tax
sta nr2
lda adr.tivol-1,x
sta playloop.ivol2+2
i_2c ldx EFFECT
beq i_2f
cpx #$40
bne @+
;ldy #8
lda [pat2],y
sta playloop.ivol2+2
@ cpx #$c0
bne @+
;ldy #8
lda [pat2],y
sta pause
@ cpx #$80
bne i_2f
stx patend
i_2f ;ldy #6
lda [pat0],y
beq i_3
tax
lda tadcl-1,x
sta playloop.iad2_m+1
lda tadch-1,x
sta playloop.iad2_s+1
; lda #0
; sta playloop.ist_2+1
ldx nr2
txa
add ^sample_start-1
sta playloop.p_2c+3
lda adr.tstrl-1,x
sta playloop.p_2c+1
lda adr.tstrh-1,x
sta playloop.p_2c+2
lda adr.trepl-1,x
sta playloop.ire2_m+1
lda adr.treph-1,x
sta playloop.ire2_s+1
* track 3
i_3 iny
;ldy #10
lda [pat1],y
sta i_3c+1
and #$1f
beq i_3c
tax
sta nr3
lda adr.tivol-1,x
sta playloop.ivol3+2
i_3c ldx EFFECT
beq i_3f
cpx #$40
bne @+
;ldy #11
lda [pat2],y
sta playloop.ivol3+2
@ cpx #$c0
bne @+
;ldy #11
lda [pat2],y
sta pause
@ cpx #$80
bne i_3f
stx patend
i_3f ;ldy #9
lda [pat0],y
beq i_e
tax
lda tadcl-1,x
sta playloop.iad3_m+1
lda tadch-1,x
sta playloop.iad3_s+1
; lda #0
; sta playloop.ist_3+1
ldx nr3
txa
add ^sample_start-1
sta playloop.p_3c+3
lda adr.tstrl-1,x
sta playloop.p_3c+1
lda adr.tstrh-1,x
sta playloop.p_3c+2
lda adr.trepl-1,x
sta playloop.ire3_m+1
lda adr.treph-1,x
sta playloop.ire3_s+1
i_e
lda patend
bne i_en
iny
sty track_pos
bne i_end
i_en inc patno
ldx patno
patmax cpx #0
bcc i_ens
lda #6
sta pause
patres ldx #0
stx patno
i_ens ldy adr.ORDER,x
sty pat0+1
iny
sty pat1+1
iny
sty pat2+1
stz track_pos
i_end
lda pause
sta cnts
nmiExit
lda.l consol
cmp #$06
bne skp
lda #$2c ; bit *
sta stop
skp
.ia16
lda.w #0
regA equ *-2
ldx.w #0
regX equ *-2
ldy.w #0
regY equ *-2
rti