Simple 4-channel sample player

For anything related to sound (YM2612, PSG, Z80, PCM...)

Moderator: BigEvilCorporation

Shiru
Very interested
Posts: 786
Joined: Sat Apr 07, 2007 3:11 am
Location: Russia, Moscow
Contact:

Simple 4-channel sample player

Post by Shiru » Fri Apr 27, 2007 5:35 am

It's not question about. It's just my 4-channel sample player, which I wrote in testing purposes in past 3 days (and finished today). Maybe it can be useful for someone, possible in educational purposes - because I don't think it can be useful in real programs (until you write music player for M68K which waste many processing power).

Note that 'simple' means functionality, not 'easy to implement or understand'.


Features:

- 4 channels of 6-bit sound
- Samples can be placed anywhere in 4MB of ROM
- Samples can have any length (i.e. much more >32KB)
- Samplerate 16000Hz

Limitations:

- Samples must be 256-byte aligned in ROM
- Samples length must be 256-byte aligned too

Problems:

- Very stupid communication between Z80/M68K (I did it only for testing purposes)
- Very simple channel manager, but on Z80 side
- Empty buffer in Z80 RAM used to 'play' unused channels, and because there is no WAIT in RAM, reading of silence channels will work slightly faster, which can affect to samplerate. It possible to work around this problem (place empty buffer to ROM, or make alternative path for unused channels).
- I don't know exactly how many t-states takes POP in banked ROM, just assumed that it near to 12t (if more then real samplerate will be lower).


I also wrote tool, which converts samples in acceptable format (8-bit raw, 16000Hz, with 256-byte aligned length). But it's better to resample sound source to 16000/8/mono in any sound editor, because tool does only simple linear interpolation for resampling.

Player still have some free time, so it's possible to make samplerate higher, although I don't think it's really needed (16000Hz means you spend ~16KB per second of sound).


Source code, tool, and demo is here or here (689K). Do anything you want with it;)

Fonzie
Genny lover
Posts: 323
Joined: Tue Aug 29, 2006 11:17 am
Contact:

Post by Fonzie » Fri Apr 27, 2007 8:21 am

lol :) Shiru, you just rox :) I'm desesperated ^^
I don't think it can be useful in real programs
Really? Why? :P

Stef
Very interested
Posts: 3131
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Re: Simple 4-channel sample player

Post by Stef » Fri Apr 27, 2007 10:18 am

Shiru wrote:It's not question about. It's just my 4-channel sample player, which I wrote in testing purposes in past 3 days (and finished today). Maybe it can be useful for someone, possible in educational purposes - because I don't think it can be useful in real programs (until you write music player for M68K which waste many processing power).

Note that 'simple' means functionality, not 'easy to implement or understand'.


Features:

- 4 channels of 6-bit sound
- Samples can be placed anywhere in 4MB of ROM
- Samples can have any length (i.e. much more >32KB)
- Samplerate 16000Hz

Limitations:

- Samples must be 256-byte aligned in ROM
- Samples length must be 256-byte aligned too

Problems:

- Very stupid communication between Z80/M68K (I did it only for testing purposes)
- Very simple channel manager, but on Z80 side
- Empty buffer in Z80 RAM used to 'play' unused channels, and because there is no WAIT in RAM, reading of silence channels will work slightly faster, which can affect to samplerate. It possible to work around this problem (place empty buffer to ROM, or make alternative path for unused channels).
- I don't know exactly how many t-states takes POP in banked ROM, just assumed that it near to 12t (if more then real samplerate will be lower).


I also wrote tool, which converts samples in acceptable format (8-bit raw, 16000Hz, with 256-byte aligned length). But it's better to resample sound source to 16000/8/mono in any sound editor, because tool does only simple linear interpolation for resampling.

Player still have some free time, so it's possible to make samplerate higher, although I don't think it's really needed (16000Hz means you spend ~16KB per second of sound).


Source code, tool, and demo is here or here (689K). Do anything you want with it;)
Very impressive, you surpass by far on the first shot my 2 sample player :)
The player doesn't work on Fusion but it does work on Gens. I'll test it on real hardware asap.
Can i use your source ? I'll probably use it for my next Z80 driver in my devkit :p
Last edited by Stef on Fri Apr 27, 2007 10:42 am, edited 1 time in total.

TmEE co.(TM)
Very interested
Posts: 2440
Joined: Tue Dec 05, 2006 1:37 pm
Location: Estonia, Rapla City
Contact:

Post by TmEE co.(TM) » Fri Apr 27, 2007 10:20 am

Shiru, you kick @$$ :D :D :D That what you've done is remarkable !!! Your 4ch has same limitations like my 2ch player, which is too messed up to release. When I get home I study your code... you have lot more coding experience than me, maybe I'll learn a few tricks :wink:
Mida sa loed ? Nagunii aru ei saa ;)
http://www.tmeeco.eu
Files of all broken links and images of mine are found here : http://www.tmeeco.eu/FileDen

Shiru
Very interested
Posts: 786
Joined: Sat Apr 07, 2007 3:11 am
Location: Russia, Moscow
Contact:

Post by Shiru » Fri Apr 27, 2007 4:55 pm

Fonzie wrote:
Shiru wrote:I don't think it can be useful in real programs
Really? Why? :P
Because this player takes almost all time of Z80 for sample playing only, and it's very hard to add music player on Z80 side. Music in sample format take too much space (30sec=~469K). So there is only solution which I mentioned above - make music player on M68K side, and because YM2612 is slow (needs to wait for busy flag), it will cut much M68K time.

Although if you want only to have sounds in game with no music, you can use this sample player.

Stef wrote:The player doesn't work on Fusion but it does work on Gens. I'll test it on real hardware asap.
I'll check for Fusion later today. I now can't test player on real hardware (because I don't have real hardware and flashcart). Possible it's problems with YM2612 initialize (I just turn on DAC when player code starts) or on M68K side (it's almost my first program for this CPU).
Stef wrote:Can i use your source ? I'll probably use it for my next Z80 driver in my devkit :p
Yes, of course. But note that Z80/M68K communication is very slow (M68K needs to wait while Z80 take new sample, it can take up to 1/62 sec per new sound), so for usage in real programs it needs to be reworked. And 'looping' feature can be useful too.



I forget to say. All delays between sample outputs is equal, so this player virtually jitter-free (if I not made some errors in calculations;).

Stef
Very interested
Posts: 3131
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Post by Stef » Fri Apr 27, 2007 5:23 pm

Shiru wrote: Although if you want only to have sounds in game with no music, you can use this sample player.
We can use some small music sample with lot of looping :p
Then we still have 3 channels for the sfx ;)
Stef wrote:Can i use your source ? I'll probably use it for my next Z80 driver in my devkit :p
Yes, of course. But note that Z80/M68K communication is very slow (M68K needs to wait while Z80 take new sample, it can take up to 1/62 sec per new sound), so for usage in real programs it needs to be reworked. And 'looping' feature can be useful too.
Yep in fact i'll probably use the source as "the main concept". I now know why you need 256 bytes aligned sample :p
I also plan to add aside music play (YM2612 & PSG) so i'll probably need to reduce the number of channel or the playback rate.
I forget to say. All delays between sample outputs is equal, so this player virtually jitter-free (if I not made some errors in calculations;).
Yep i saw that, impressive work again !
Sometime some instructions do more than their 7 or 11 cycles because of the bus delay (we have one extra cycle then : 8 or 12). I think you are aware of that Z80 behavior but i'm not sure you take care of that in the cycle counting.

TmEE co.(TM)
Very interested
Posts: 2440
Joined: Tue Dec 05, 2006 1:37 pm
Location: Estonia, Rapla City
Contact:

Post by TmEE co.(TM) » Fri Apr 27, 2007 5:44 pm

I tested it on my MD2, and it started to work after few resets, but not samples, some more resets and samples started to work too. When only 1 or 2 channels play, there's some static, but if 3 or 4 then there's no static... Your code is quite complex, or big actually, but I get the idea how it works. Very impressive.
Mida sa loed ? Nagunii aru ei saa ;)
http://www.tmeeco.eu
Files of all broken links and images of mine are found here : http://www.tmeeco.eu/FileDen

Shiru
Very interested
Posts: 786
Joined: Sat Apr 07, 2007 3:11 am
Location: Russia, Moscow
Contact:

Post by Shiru » Fri Apr 27, 2007 9:49 pm

Stef wrote:Sometime some instructions do more than their 7 or 11 cycles because of the bus delay (we have one extra cycle then : 8 or 12). I think you are aware of that Z80 behavior but i'm not sure you take care of that in the cycle counting.
Afaik, Z80 in SMD works that way:

- Z80 RAM is a separate SRAM IC, M68K has access to it only when grab bus by special register. In other cases he does nothing with Z80 RAM bus, so Z80 code in Z80 RAM works without additional cycles (no wait-states).
- Banking window for Z80 always share one bus with M68K, and Z80 always wait some cycles on some operations (wait states) to allow M68K access to ROM without delays.

It's like an original ZX Spectrum with two separate memory fields - one part of RAM shares by CPU and videocontroller ('slow RAM', Z80 has wait-states), and other used by CPU only ('fast RAM', Z80 does not have wait-states).

I only don't know how wait-states acts exactly on SMD (there is some options possible). Mainly, I interested only how many cycles takes POP opcode, which lies in RAM, but reads value from banked ROM. All other things in player has access only to Z80 RAM, so there is must be no wait-states.

HardWareMan said me about this (about wait-states in banked ROM only, and he even give some values about exact wait cycles), but this information was given by PM on forum which currently down, and HardWareMan himself is busy now.


If you mean that I didn't count for busreq from M68K side (when M68K writes new sample parameters to Z80 RAM, which slowdown Z80 too) - yes, I don't count, because I can't determine this case from Z80 side (as I said, this is a very stupid communication method).

TmEE co.(TM) wrote:I tested it on my MD2, and it started to work after few resets, but not samples, some more resets and samples started to work too.
Possibly it happens by same reason that program does not works in Fusion, I now try to fix this.
TmEE co.(TM) wrote:When only 1 or 2 channels play, there's some static, but if 3 or 4 then there's no static..
It can be because 'Empty buffer in Z80 RAM' (that I said above), I can work it around.
TmEE co.(TM) wrote:Your code is quite complex, or big actually, but I get the idea how it works. Very impressive.
This is not a quite complex, nor a big code:) Really. It's even less than 3K in result, and also big part of code is just repeats 4 times.

Shiru
Very interested
Posts: 786
Joined: Sat Apr 07, 2007 3:11 am
Location: Russia, Moscow
Contact:

Post by Shiru » Sat Apr 28, 2007 2:12 am

About not working in Fusion and not stable run on real hardware - this is a my very stupid mistake, I forgot about stack pointer on Z80 startup (so return from call wrYmPortA sends Z80 to nowhere). Just add ld sp,#1fff at start of code (before or after di), and it will work.

I'll upload fixed version later, when implement empty buffer in ROM, hope it fix 'static' on real hardware, which TmEE co.(TM) mentioned.

LocalH
Very interested
Posts: 152
Joined: Tue Dec 19, 2006 5:04 pm

Post by LocalH » Sat Apr 28, 2007 5:05 am

Wait a second...4-ch sample mixer...with converted samples this could be used as a base for an Amiga modplayer on the Genesis. You'd have to either figure out how to do the modreplayer on the Z80 side, or figure out how to do it on the 68k side and figure out how to instruct the Z80 code to play a specific sample at a specific sample rate. I'm assuming, however, that as the code stands now it plays a single sample stream per channel. Modreplaying is more complicated because you will have to trigger samples while other samples are playing.

commodorejohn
Very interested
Posts: 70
Joined: Tue Mar 06, 2007 6:30 pm

Post by commodorejohn » Sat Apr 28, 2007 12:54 pm

The only problem I can see with a MOD player is whether the Z80 has very many timers. Lemme check the docs...hmm, I'm not seeing any timers other than the ones on the FM chip. Which means you could have at most two different playback frequencies. For MOD playing you'd need a different timer for each channel.

Stef
Very interested
Posts: 3131
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Post by Stef » Sat Apr 28, 2007 1:58 pm

Shiru wrote:
Stef wrote:Sometime some instructions do more than their 7 or 11 cycles because of the bus delay (we have one extra cycle then : 8 or 12). I think you are aware of that Z80 behavior but i'm not sure you take care of that in the cycle counting.
Afaik, Z80 in SMD works that way:

- Z80 RAM is a separate SRAM IC, M68K has access to it only when grab bus by special register. In other cases he does nothing with Z80 RAM bus, so Z80 code in Z80 RAM works without additional cycles (no wait-states).
- Banking window for Z80 always share one bus with M68K, and Z80 always wait some cycles on some operations (wait states) to allow M68K access to ROM without delays.
...
I wasn't speaking about these wait states, just about the Z80 behavior.

When you have the following instruction :
LD A, #1
It should take 7 cycles as mentionned in the zilog manual.
But in fact this is the "lucky case" : the instruction is 2 bytes lenght which means it need 2 bus cycles (1 bus cycle = 4 Z80 cycles) so it can takes up to 8 cycles. The same is true for the LD A, (HL) instruction as we need one bus cycle to fetch the instruction and another one to fetch data from (HL).

For all instructions where "execution cycle" < "bus execution cycle"
The "execution cycle" happens only if the bus got some free time from the previous instruction.
HardWareMan said me about this (about wait-states in banked ROM only, and he even give some values about exact wait cycles), but this information was given by PM on forum which currently down, and HardWareMan himself is busy now.
I though the Z80 was working in "bus steal mode" then very dependent from the current 68000 instruction execution...

TmEE co.(TM)
Very interested
Posts: 2440
Joined: Tue Dec 05, 2006 1:37 pm
Location: Estonia, Rapla City
Contact:

Post by TmEE co.(TM) » Sat Apr 28, 2007 5:05 pm

commodorejohn wrote:The only problem I can see with a MOD player is whether the Z80 has very many timers. Lemme check the docs...hmm, I'm not seeing any timers other than the ones on the FM chip. Which means you could have at most two different playback frequencies. For MOD playing you'd need a different timer for each channel.
YM2612 has only one PCM channel, so you can't use timers to time samples. YM2612 has timers, but these are fixed. If YM2612 would have had several PCM channels, we won't be having trouble with multi channel playback stuff :wink: .
Mida sa loed ? Nagunii aru ei saa ;)
http://www.tmeeco.eu
Files of all broken links and images of mine are found here : http://www.tmeeco.eu/FileDen

Shiru
Very interested
Posts: 786
Joined: Sat Apr 07, 2007 3:11 am
Location: Russia, Moscow
Contact:

Post by Shiru » Mon Apr 30, 2007 9:00 pm

Shiru wrote:I'll upload fixed version later, when implement empty buffer in ROM, hope it fix 'static' on real hardware, which TmEE co.(TM) mentioned.
Sorry for delay, I made new version two days ago, but not had access to internet. Here is it (or here), both source code and compiled demo. TmEE co.(TM), please test it on real hardware if you can.


LocalH wrote:Wait a second...4-ch sample mixer...with converted samples this could be used as a base for an Amiga modplayer on the Genesis. You'd have to either figure out how to do the modreplayer on the Z80 side, or figure out how to do it on the 68k side and figure out how to instruct the Z80 code to play a specific sample at a specific sample rate. I'm assuming, however, that as the code stands now it plays a single sample stream per channel. Modreplaying is more complicated because you will have to trigger samples while other samples are playing.
MOD-like player itself (logic part) is not a difficult part. Main problem is MOD-like player must have control for speed of sample playing (i.e. real-time resampling) and volume control. It's much more complicated than playing sample with fixed speed and volume. Resampling code for Z80 needs many scaler routines, which simply does not fit into Z80 RAM, or fixed point math (16:8, for example), which will be very slow. Volume control needs on multiplication per each sample (and calculations in 16 bit), which is impossible to implement on Z80 @3.5MHz directly (to be exact, it will be very-very-very slow), or big precalculated table (64*256 bytes), and it will be slow anyway. It's possible to make MOD-like player for SMD's Z80, but it's very difficult, and you can get only very low quality.
commodorejohn wrote:The only problem I can see with a MOD player is whether the Z80 has very many timers. Lemme check the docs...hmm, I'm not seeing any timers other than the ones on the FM chip. Which means you could have at most two different playback frequencies. For MOD playing you'd need a different timer for each channel.
In real life nobody uses timers for resampling (even if they can generate interrupt). For MOD-like player on dedicated Z80 you don't need any timers at all.
TmEE co.(TM) wrote:YM2612 has only one PCM channel, so you can't use timers to time samples. YM2612 has timers, but these are fixed. If YM2612 would have had several PCM channels, we won't be having trouble with multi channel playback stuff :wink: .
Number of PCM channels and number of timers are absolutely not connected. One PCM channel mean only that we must mix channels in software, and can get only 6 bit per channel (for 4 channels).


Stef wrote:I wasn't speaking about these wait states, just about the Z80 behavior.
...
It should take 7 cycles as mentionned in the zilog manual.
But in fact this is the "lucky case" : the instruction is 2 bytes lenght which means it need 2 bus cycles (1 bus cycle = 4 Z80 cycles) so it can takes up to 8 cycles. The same is true for the LD A, (HL) instruction as we need one bus cycle to fetch the instruction and another one to fetch data from (HL).
Why you think than one bus cycle is 4 Z80 cycles? It's exactly known, or you just assume?

Z80 always execute instructions in time which specified in Zilog manual. Depending from bus implementation (mainly from active devices which use this bus too, like DMA controllers) in different computers/consoles, execution of instructions can be slower, because bus logic generate /WAIT signal (add wait-states).
Stef wrote:I though the Z80 was working in "bus steal mode" then very dependent from the current 68000 instruction execution...
If it so, we can't do anything with it. Although I almost sure that time of execution Z80 instruction are fixed and not depends from current 68000 state.

TmEE co.(TM)
Very interested
Posts: 2440
Joined: Tue Dec 05, 2006 1:37 pm
Location: Estonia, Rapla City
Contact:

Post by TmEE co.(TM) » Tue May 01, 2007 8:39 am

Shiru wrote:TmEE co.(TM), please test it on real hardware if you can.
No trouble, I'm happy to help :D

Shiru wrote:Number of PCM channels and number of timers are absolutely not connected. One PCM channel mean only that we must mix channels in software, and can get only 6 bit per channel (for 4 channels).
I meant this: I meant IF there would have been several PCM channels and reprogrammable timer for each, there would not be need for software mixing...
Also, I think that one demo uses 2ch MOD like playback, Censor movie trailer... got it from Eidolon's inn's freeware Genesis ROMs section. ROM itself is less than half a MB and it plays back movie and minute long tune, there must be a MOD player in it.

Shiru wrote:If it so, we can't do anything with it. Although I almost sure that time of execution Z80 instruction are fixed and not depends from current 68000 state.
I'll try my tiny performance counter when z80 is in use, I wonder if it affects 68K...
Mida sa loed ? Nagunii aru ei saa ;)
http://www.tmeeco.eu
Files of all broken links and images of mine are found here : http://www.tmeeco.eu/FileDen

Post Reply