6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sun Nov 24, 2024 5:11 pm

All times are UTC




Post new topic Reply to topic  [ 9 posts ] 
Author Message
 Post subject: Writing a MOD player
PostPosted: Sat Dec 22, 2007 6:45 am 
Offline

Joined: Sat Sep 22, 2007 1:31 am
Posts: 24
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


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Dec 22, 2007 9:06 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8546
Location: Southern California
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.

_________________
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?


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Dec 22, 2007 5:55 pm 
Offline

Joined: Sat Sep 22, 2007 1:31 am
Posts: 24
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.


Well, the problem is that I only have one timer and at that the smallest interval per decrement is 1024 cycles. This doesn't work for the 32 byte buffer mode of the audio unit.

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.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Dec 22, 2007 8:04 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8546
Location: Southern California
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)

_________________
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?


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sun Dec 23, 2007 12:22 am 
Offline

Joined: Sat Sep 22, 2007 1:31 am
Posts: 24
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.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Thu Jan 31, 2008 11:40 pm 
Offline

Joined: Sat Sep 22, 2007 1:31 am
Posts: 24
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.


Top
 Profile  
Reply with quote  
 Post subject: Re: Writing a MOD player
PostPosted: Fri Jan 24, 2014 12:48 am 
Offline

Joined: Fri Jan 24, 2014 12:22 am
Posts: 1
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.


Top
 Profile  
Reply with quote  
 Post subject: Re: Writing a MOD player
PostPosted: Fri Jan 24, 2014 1:26 am 
Offline

Joined: Sat Sep 22, 2007 1:31 am
Posts: 24
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.


Top
 Profile  
Reply with quote  
 Post subject: Re: Writing a MOD player
PostPosted: Sun Oct 07, 2018 11:19 am 
Offline

Joined: Fri Oct 05, 2018 4:19 pm
Posts: 2
Atari XE/XL, POKEY 6bit, CPU 65816 (HighMem)

sample_loop (IRQ 15KHz)

Code:
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:
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


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 9 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: