Stepping through a sample at varying rates

All 680x0 related coding posts in this section please.

Moderators: simonsunnyboy, Mug UK, Zorro 2, exxos, Moderator Team

Stepping through a sample at varying rates

Postby unseenmenace » Thu Nov 11, 2010 6:44 pm

In the following thread:- http://www.atari-forum.com/viewtopic.php?f=68&t=12146&start=50 there is a discussion about using addx in 3D graphics and Gunstick and TAD_uk mention that a similar technique is used in soundtrackers:

TAD_uk wrote:
Gunstick wrote:also note that bresenham routine (that's what we are talking about here) for polygons or lines is also very useful to play modfiles. Maybe addx was first used for music players. That's where I first heard about it.


Thats where I used to use the ADDX trick to step through the sample at different rates.

Using 16.16 fixed point maths to store the position (offset.part = 32 bits) and step (integer.fraction = 32 bits) a 'normal' 16.16 fixed point add.l would be like this:

d0 = (offset.0) << 16 + (part)
d4 = (integer.0) << 16 + (fraction)

add.l d4, d0

but you'll need a swap to access the top 31..16 bits of d0 (the offset.0 value)

swap d0 ; swap offset <--> part
move.w d0, (a0)+ ; store the offset value
swap d0 ; restore d0 value

If you are careful you can also chain multiple ADDX together for more than 1 polygon edge OR sample voice step.

(example from my memory, so hopefully correct)

move.w d0-d3, (a0)+ ; store the integer offsets d0,d1,d2,d3
addx.l d4, d0
addx.l d5, d1
addx.l d6, d2
addx.l d7, d3

the set up of the d4 to d7 register use bits 31-16 as fractional step and bits 15-0 as the integer step BUT not in the normal way !!

If you set up your registers something like this:-

d0 = (part.1) << 16 + (offset.0)
d1 = (part.2) << 16 + (offset.1)
d2 = (part.3) << 16 + (offset.2)
d3 = (part.0) << 16 + (offset.3)


and STEP value like this:-

d4 = (fraction.1) << 16 + (integer.0)
d5 = (fraction.2) << 16 + (integer.1)
d6 = (fraction.3) << 16 + (integer.2)
d7 = (fraction.0) << 16 + (integer.3)

NOTE: The part.0 ... part.3 and fraction.0 ... 3 are 'out of sync' so one ADDX will increment both integer.X value as well as fraction.x+1 value.

Hope this all makes senses.

Cheers

TAD_uk


Can anyone explain in a bit more detail how this works?
UNSEEN MENACE
Several STFM's, 4MB STE, 2MB TT with 1.2GB Hard Drive and 14MB Falcon with 540MB Hard Drive,
Lynx 2 and Jaguar with JagCD
Member of GamebaseST and AtariLegend team
Check out my website at http://unseenmenace.110mb.com
User avatar
unseenmenace
Atari God
Atari God
 
Posts: 1957
Joined: Tue Sep 21, 2004 9:33 pm
Location: Margate, Kent, UK

Re: Stepping through a sample at varying rates

Postby !cube » Fri Nov 12, 2010 9:42 am

Consider you have four 16.16 fixed point adders: ADDER0, ADDER1, ADDER2 and ADDER3 and four 16.16 fixed point sample offsets: OFFSET0, OFFSET1, OFFSET2 and OFFSET3 all corresponding to your four channels. When stepping the samples, normally you'd just add the values together like this:

add.l d4,d0
add.l d5,d1
add.l d6,d2
add.l d7,d3

where:

d0...d3 = OFFSET0...OFFSET3
d4...d7 = ADDER0...ADDER3

But to get to the integer part of the 16.16 value which is the actual offset you need to read the sample data from, you'll need to use the SWAP on each of the offset registers. To get around that problem, you can shuffle the integer and fractional parts of the fixed point values so that d0 longer holds the integer and fractional parts of OFFSET0 but the integer part of OFFSET0 and fractional part of OFFSET1. BUT, you'll have to swap them around so that the integer part is no longer in the upper 16 bits of the register but in the lower 16 bits and vice versa for the fractional part. Now you can and actually NEED to use ADDX instead of ADD:

addx.l d4,d0
addx.l d5,d1
addx.l d6,d2
addx.l d7,d3

where:

d0 = OFFSET1.fraction << 16 | OFFSET0.integer
d1 = OFFSET2.fraction << 16 | OFFSET1.integer
d2 = OFFSET3.fraction << 16 | OFFSET2.integer
d3 = OFFSET0.fraction << 16 | OFFSET3.integer

d4 = ADDER1.fraction << 16 | ADDER0.integer
d5 = ADDER2.fraction << 16 | ADDER1.integer
d6 = ADDER3.fraction << 16 | ADDER2.integer
d7 = ADDER0.fraction << 16 | ADDER3.integer

Now the overflow from the first ADDX gets accounted for in the second ADDX and so forth, so you get the same result as with the normal 16.16 fixed point calculations but you no longer need to use SWAP to get to the integer part of the offset. The only problem is that the integer part of OFFSET0 doesn't get calculated correctly since its fractional part overflow doesn't get calculated before the first ADDX but in the last ADDX so you'll need to figure out a way to fix it.
Kludge power since 1976.
!cube
Atari freak
Atari freak
 
Posts: 71
Joined: Thu Jun 14, 2007 6:37 am
Location: Vantaa, Finland

Re: Stepping through a sample at varying rates

Postby unseenmenace » Sun Nov 14, 2010 5:57 pm

Thank you kind sir for the explanation. I think I follow what you are saying. I'll do some experiments soon :)
UNSEEN MENACE
Several STFM's, 4MB STE, 2MB TT with 1.2GB Hard Drive and 14MB Falcon with 540MB Hard Drive,
Lynx 2 and Jaguar with JagCD
Member of GamebaseST and AtariLegend team
Check out my website at http://unseenmenace.110mb.com
User avatar
unseenmenace
Atari God
Atari God
 
Posts: 1957
Joined: Tue Sep 21, 2004 9:33 pm
Location: Margate, Kent, UK

Re: Stepping through a sample at varying rates

Postby Gunstick » Sat Apr 21, 2012 9:54 am

Would this work?
it's not tested at all, I just made it up right now

I was not able to make code to mix 4 voices at once, so this is to mix 2 voices (with volume)

Code: Select all
; Init for 2 voices like this
;       d0=speed1.SPEED0
;       d1=playp1.PLAYP0
;       d2=speed0.SPEED1
;       d3=playp0.PLAYP1
;  d5=volume 0
;  d6=volume 1
;  a0=sample 0
;  a1=sample 1
;  a2=sum
;  a4=output
;  a6,a3=tmp regs

        lea     sample0,a0
        lea     sample1,a1

        move.l  #voltab,d5
        move.l  #voltab,d6
        move.w  speed1+2,d0
        swap    d0
        move.w  speed0,d0
        move.w  play1+2,d1
        swap    d1
        move.w  play0,d1
        move.w  speed0+2,d2
        swap    d2
        move.w  speed1,d2
        move.w  play0+2,d3
        swap    d3
        move.w  play1,d3

        swap    d2
        swap    d3
        addx.w  d2,d3           ; playp0+=speed0 get X
        swap    d2
        swap    d3


        rept 312
        addx.l  d0,d1           ; playp1+=speed1 & PLAYP0+=SPEED0+X
        move.b  0(a0,d1.w),d5   ; read sample
        movea.l d5,a6           ; offset in voltab
        move.b  (a6),d4         ; adapt to volume
        movea.w d4,a2           ; add to sound (An not modif X!)
        addx.l  d2,d3           ; playp0+=speed0 & PLAYP1+=SPEED1+X
        move.b  0(a1,d3.w),d6   ; read sample
        movea.l d6,a6           ; offset in voltab
        move.b  (a6),d4         ; adapt to volume
        adda.w  d4,a2           ; add to sound (An not modif X!)
        move.w  a2,(a4)+        ; write 2 added channels
        endr

        addx.l  d0,d1           ; playp1+=speed1 & PLAYP0+=SPEED0+X
        move.b  0(a0,d1.w),d5   ; read sample
        movea.l d5,a6           ; offset in voltab
        move.b  (a6),d4         ; adapt to volume
        movea.w d4,a2           ; add to sound (An not modif X!)
        addx.w  d2,d3           ;              PLAYP1+=SPEED1+X
        move.b  0(a1,d3.w),d6   ; read sample
        movea.l d6,a6           ; offset in voltab
        move.b  (a6),d4         ; adapt to volume
        adda.w  d4,a2           ; add to sound (An not modif X!)
        move.w  a2,(a4)+        ; write 2 added channels



; voice 0
speed0: dc.l $018000            ; play speed 16.16
play0:  dc.l $05                ; offset into sample 16.16
; voice 1
speed1: dc.l $017000            ; play speed 16.16
play1:  dc.l $03                ; offset into sample 16.16

; silly example volume table, simply inverses the values
voltab:
        dc.b 254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239
        dc.b 238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223
        dc.b 222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207
        dc.b 206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191
        dc.b 190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175
        dc.b 174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159
        dc.b 158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143
        dc.b 142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,127
        dc.b 126,125,124,123,122,121,120,119,118,117,116,115,114,113,112,111
        dc.b 110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94
        dc.b 93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74
        dc.b 73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54
        dc.b 53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34
        dc.b 33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14
        dc.b 13,12,11,10,9,8,7,6,5,4,3,2,1,0

; instruments
sample0:
        dc.b 127,128,200,250,100,60,20,120,130
sample1:
        dc.b 5,6,7,8,9,10,11,12


Gunstick
Captain Atari
Captain Atari
 
Posts: 230
Joined: Thu Jun 20, 2002 6:49 pm
Location: Luxembourg

Re: Stepping through a sample at varying rates

Postby TADUK » Sun May 20, 2012 7:25 pm

Hi Gunstick,

Thanks for helping provide an answer to the question concerning my previous ADDX stepping method post.

The ADDX method means you kinda "skew" the order in which you would normally do a ADD then ADDX :-

For example, say you wanted to step through memory using a step of 0.5 - if you scale up this value by 65536 decimal (00010000 hex) you would use the value of 32768 decimal (00008000 hex) - in code this could look like this:-

moveq.l #0, d0
move.l #$00008000, d1

you can then repeat the following code to step through

add.l d1, d0
swap d0

.. use the value in d0.w

swap d0
... repeat the above code.

I remember that the old Quatert 4-channel sample player used this technique to step through the samples and I think the demo group The Lost Boys used a tweaked version in their Mindbomb megademo playing at 7.5KHz.
TADUK
Atarian
Atarian
 
Posts: 2
Joined: Sun May 20, 2012 7:08 pm

Re: Stepping through a sample at varying rates

Postby TADUK » Sun May 20, 2012 7:35 pm

Another *untested* method is to use the 24-bit address range of the Atari ST and use the top 8-bits as the fraction part of the sample position/address.

example:

;--- setup ---
move.l #00xxxxxx, d0 ; address of the sample
move.l #80000001, d1 ; stepper value of 1.5

;--- repeat --
move.l d0, a0
move.b (a0), d4 ; get the sample byte into D4
addx.l d1, d0 ; step the address and fraction ***

*** NOTE: the carry from the fraction part will be added on the next loop ADDX to the address in d0

I'm not sure how bad this would sound only having a 8-bit fraction but the MOVE.L D0, A0 is faster than any indexing address modes.
TADUK
Atarian
Atarian
 
Posts: 2
Joined: Sun May 20, 2012 7:08 pm


Return to 680x0

Who is online

Users browsing this forum: CommonCrawl [Bot] and 0 guests