English Amiga Board

Go Back   English Amiga Board > Coders > Coders. General

Thread Tools
Old 20 May 2010, 21:22   #1
Posts: n/a
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:

  MOVE.L (A3)+,D0
  MOVE.L D0,(_SampleLoopLen,A2)
  BEQ.B lbC000C3A
  LSR.W #1,D0
  MOVE.W D0,(_SampleLength,A2)
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:

  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.
Old 20 May 2010, 22:06   #2

Photon's Avatar
Join Date: Nov 2004
Location: Eksjö / Sweden
Posts: 4,819
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.
Photon is offline  
Old 21 May 2010, 15:27   #3

phx's Avatar
Join Date: Nov 2009
Location: Herford / Germany
Posts: 1,690
Originally Posted by swirlythingy View Post
...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.
phx is offline  
Old 22 May 2010, 16:24   #4
Posts: n/a
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:

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

  ; 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
  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)

  ; 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)
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:

2 4                    ; _SampleLoopAddress = sample #4
3 A4E                  ; _SampleLoopLen = $A4E, _SampleLength = $A4E/2
13 B000 1001 FFFF 300
4 800                  ; _SampleLoopLen = $800, _SampleLength = $800/2
8 1C0                  ; _SampleLoopAddress += $1C0
6 0
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.

2 8                    ; _SampleLoopAddress = sample #8
3 68C                  ; _SampleLoopLen = $68C, _SampleLength = $68C/2
13 4000 FFFF FFFF 100
12                     ; exit and update hardware buffers
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.

Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
Thread Tools

Similar Threads
Thread Thread Starter Forum Replies Last Post
BZR Player - a new music player for Win bLAZER Retrogaming General Discussion 970 13 August 2020 19:01
Music player with Batman glr Looking for a game name ? 2 04 January 2012 14:02
Amiga 1200 Mod Player? Help STILL Needed ! hallatie Amiga scene 4 08 April 2011 11:27
Amiga music player on PC moriez Amiga scene 37 24 October 2010 02:06
Best music player quantum112 support.Apps 9 06 January 2010 09:59

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

All times are GMT +2. The time now is 08:01.

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2020, vBulletin Solutions Inc.
Page generated in 0.06215 seconds with 13 queries