English Amiga Board

English Amiga Board (https://eab.abime.net/index.php)
-   Coders. General (https://eab.abime.net/forumdisplay.php?f=37)
-   -   Help needed with music player (https://eab.abime.net/showthread.php?t=52896)

swirlythingy 20 May 2010 21:22

Help needed with music player
 
Hello everybody!

I'm a retro gaming and demoscene enthusiast (I say 'retro' because I was about one year old when it happened!), with particular interest in module formats and some experience in decoding esoteric ones. It is this latter occupation which I must now pester you lot about.

I've never actually owned or used an Amiga - all my past experience and programming knowledge is on RISC OS (Acorn Archimedes as-was) and ARM assembler, which was why I found it rather daunting when I took on the project of decoding a module format based on a disassembly of the original 68000 player machine code.

The guilty party in this case is the Jason Page format, as used in Fire and Ice, Uridium 2, and other games. Try as I might, I couldn't find any documentation at all (when I've solved this last conundrum, I hope to write some of my own), and so I've been writing a converter based first on the unbelievably bug-ridden Archimedes conversion of Fire and Ice, and latterly on the disassembly of the original player available from AMP.

It is with this that I have become confused. I posted on the AMP forums about my problem, and was recommended to come here and ask where there were likely to be more 68000 programmers reading.

The problem I have is with this piece of code which is called for a particular instrument command:

Code:

  MOVE.L (A3)+,D0
  MOVE.L D0,(_SampleLoopLen,A2)
  BEQ.B lbC000C3A
  LSR.W #1,D0
  MOVE.W D0,(_SampleLength,A2)
lbC000C3A
  RTS

If I'm reading that right, which I'm almost certainly not, then it reads four bytes from pointer A3 and stores them in address (A2 + _SampleLoopLen). Then, only if the four bytes are not 0, it additionally divides them by 2 and stores them in address (A2 + _SampleLength).

There is another instrument command which does the same thing, but without checking if the value read is 0.

These two commands combined mean that the only two values _SampleLoopLen can take are either 0, or that of _SampleLength * 2.

All of which makes the code which actually programs the Paula chip rather odd:

Code:

  TST.L (_SampleLoopLen,A2)
  BEQ.B lbC000A98    ; silence channel
  MOVEQ #0,D0
  MOVE.W (_SampleLength,A2),D0
  ADD.L D0,D0
  MOVE.L (_SampleLoopAddress,A2),D1
  ADD.L D0,D1
  MOVE.L (_SampleLoopLen,A2),D0
  SUB.L D0,D1
  MOVE.L D1,(_HSampleStartAddr,A4)
  MOVE.W (_SampleLength,A2),(_HSampleLength,A4)

...as it only gets executed if _SampleLoopLen is not 0, which means, by extension, that it must be equal to twice _SampleLength.

Hence the only possible value the sample start address can take is that of _SampleLoopAddress!

I welcome any corrections to the above diatribe, and I hope somebody can point out where I've gone wrong.

Photon 20 May 2010 22:06

Jason did some cool work on Paradroid 90 and the subsequent games, always liked how the audio turned out.

Maybe you're the first to rip his player, but it might be that there's someone out there who has a working player source.

As for helping, I'll check back when I'm less tired. Sample lengths/loop lengths are usually supplied as half the byte size, as the audio hardware works that way. Loop lengths (which is the same as sample lengths, as audio is always played looped) is always given in words, not bytes. DMA is 16-bit, so it reads a word on each access and then plays two sample-bytes, reads another word, and so on.

Here's another Acorn fan btw, welcome and kudos for putting your first post in the right subforum! Hehe.

phx 21 May 2010 15:27

Quote:

Originally Posted by swirlythingy (Post 671008)
...as it only gets executed if _SampleLoopLen is not 0, which means, by extension, that it must be equal to twice _SampleLength.

Hence the only possible value the sample start address can take is that of _SampleLoopAddress!

You are right, when those source fragments are really the only parts which deal with SampleLength/SampleLoopLen and HSampleStartAddr.

Are you sure this is the only location where the program writes to HSampleStartAddr?

I could imagine the following: The sample's base address is written to HSampleStartAddr and HSampleLength is set to the full length in words from SampleLength. Then audio DMA for the channel is started.

As the next step some effects will be applied (while DMA is running) and SampleLoopLen is rewritten, which also makes SampleLength smaller than its initial value (because the loop takes place with a range at the end of the sample).

Now the audio registers HSampleStartAddr and HSampleLength are written again with the new values, defining the loop.

Paula works that way that it will reload the HSampleStartAddr and HSampleLength into an internal latch whenever the DMA finishes (end of sample is reached). The sample is automatically restarted playing the loop range.

swirlythingy 22 May 2010 16:24

Oh bother. :(

That's more or less what I was expecting it to be doing as well, but I can't tell how it's happening!

Before I provide more complicated examples, here's a brief grounding on how Jason Page instrument definitions work: A sample number is specified with the note as normal, except it points not to an instrument, but to a list of instructions which give all the details of the address of the instrument, its loops, vibrato, fades, etc. Data is read from this block every 50 Hz clock tick, stopping whenever a special instruction code of $12 is encountered, at which point it updates the current volume and pitch, and fills the hardware buffer.

Here's an example from the sixth world of Fire and Ice:

Code:

2 0                  <- _SampleLoopAddress set to beginning of sample #0
3 22C8                <- _SampleLoopLen = $22C8, _SampleLength = $22C8/2
D 0 0                <- vibrato
13 6000 FFFF FFFF 30  <- fade
12                    <- exit and update hardware buffers
-------------
10                    <- no idea... (see below)
D 3 3                <- another vibrato
4 1A00                <- _SampleLoopLen = $1A00, _SampleLength = $1A00/2
6 0                  <- start infinite loop
  12                  <- exit and update hardware buffers
-------------
7                    <- end infinite loop

Command $4 is the one which checks if the following parameter is 0 before writing it to _SampleLength, and command $3 is the one which doesn't. $3 always comes first. If a command of $4 with a parameter of 0 turns up, that means an unlooped sample. Note that $3 sometimes occurs twice instead of first $3 and then $4.

Command $10 executes a single instruction:
Code:

  ST (_KeyON,A2)
What does this do, and if TST.W (_KeyON,A2) is then executed, will it return EQ or NE?

One pattern I have noticed is that command $10 is always preceded by a command $12, which means that by the time _KeyON has been ST'd, the hardware buffers have already been updated once.

I have a feeling this is important.

Now, from the data above, you would assume that this produced a looping sample, $22C8 bytes long, with the repeat start at offset $8C8 and a length of &1A00. But, judging from what I understand of the code below, which is very little, what actually happens is that the entire $22C8 bytes gets played through once, and then it loops on the first $1A00. That can't be right, surely?

Here's the complete unabridged hardware updating function:

Code:

lbC0009FC
  ; Compare current volume to master volume, and clip if bigger
  MOVEQ #0,D1
  MOVE.B (MasterVolumeB),D1
  LSR.B #2,D1
  MOVEQ #0,D0
  MOVE.B (_Volume,A2),D0
  LSR.B #2,D0
  CMP.B D1,D0
  BLE.B lbC000A12
  MOVE.B D1,D0
lbC000A12
  MOVE.W D0,(_HSampleVolume,A4)
  ORI.B #$10,(_HUpdateHardwareFlag,A4)

  ; The 'Update Hardware Flag' is not mentioned anywhere else
  ; in the file.  I have a nasty feeling there is some code
  ; missing which translated this buffer into values read by
  ; the Paula chip.

  ; If _SampleLoopLen is 0, write blank data instead
  TST.L (_SampleLoopLen,A2)
  BEQ.B lbC000A98

  ; D1 = _SampleLoopAddress + 2*_SampleLength - _SampleLoopLen
  MOVEQ #0,D0
  MOVE.W (_SampleLength,A2),D0
  ADD.L D0,D0
  MOVE.L (_SampleLoopAddress,A2),D1
  ADD.L D0,D1
  MOVE.L (_SampleLoopLen,A2),D0
  SUB.L D0,D1

  ; Not a clue what this does...
  TST.W (_KeyON,A2)
  BNE.B lbC000A4C
  MOVE.L D1,(_HSampleStartAddr,A4)
  MOVE.W (_SampleLength,A2),(_HSampleLength,A4)
  ORI.B #2,(_HUpdateHardwareFlag,A4)
lbC000A4C

  ; I don't know what these two values are at all, nor are they
  ; mentioned anywhere else.  As above, I think there's code
  ; missing.  The most likely explanation is that they indicate
  ; the new values to be written into the variables above.
  MOVE.L D1,($18,A4)
  MOVE.W (_SampleLength,A2),(_HSampleLoopLength,A4)
  ORI.B #4,(_HUpdateHardwareFlag,A4)

  ; Finally, write the period value.
  MOVE.W (_Pitch,A2),(_HSamplePitch,A4)
  ORI.B #8,(_HUpdateHardwareFlag,A4)
  RTS

And here's a couple more interesting instrument definitions to give a better idea of what it's used for:

A synth chord from Uridium 2:

Code:

2 4                    ; _SampleLoopAddress = sample #4
3 A4E                  ; _SampleLoopLen = $A4E, _SampleLength = $A4E/2
13 B000 1001 FFFF 300
12
-------------
10
4 800                  ; _SampleLoopLen = $800, _SampleLength = $800/2
8 1C0                  ; _SampleLoopAddress += $1C0
6 0
  12
-------------
7

Effect $8, as you see, can be used to alter the value of _SampleLoopAddress, and hence is the only effect which alters the address of the sample data.

This next one is especially interesting. Judging by experimentation, I believe the correct translation of the parameters is a repeat start of $600, and length of $420. The sample itself is $1BC8 bytes long, and is a sort of high 'plink' sound used in the middle of the Uridium 2 title music.

Code:

2 8                    ; _SampleLoopAddress = sample #8
3 68C                  ; _SampleLoopLen = $68C, _SampleLength = $68C/2
13 4000 FFFF FFFF 100
12                    ; exit and update hardware buffers
-------------
10
8 600                  ; _SampleLoopAddress += $600
4 420                  ; _SampleLoopLen = $420, _SampleLength = $420/2
5 100                  ; update hardware buffers, exit and wait 256 ticks
-------------
0                      ; cut note


tl;dr version:

What does ST (_KeyON,A2) mean? The hardware buffers aren't mentioned anywhere except in the one place described here. _HSampleStartAddr never changes except when specified by command $8, therefore a loop is only possible at the start of the sample, not the end. I don't think this is correct, so what am I doing wrong? I bet _KeyON has something to do with it.


All times are GMT +2. The time now is 22:12.

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.

Page generated in 0.04242 seconds with 11 queries