The MASM Forum

General => The Workshop => Topic started by: FlySky on July 23, 2019, 02:03:10 AM

Title: mod player using waveoutopen
Post by: FlySky on July 23, 2019, 02:03:10 AM
Hi Guys,

It's been a while, been busy with a lot of different things which had nothing to do with programming or computers in general.

I am wondering if anyone has any examples/ tips on how to code a .mod/ .xm player using waveoutopen API.
The projects around like ufmod are x86 based, so I've decided to start working on a player from scratch for my own learning curve.
Any documentation you guys can suggest reading?
Title: Re: mod player using waveoutopen
Post by: Siekmanski on July 23, 2019, 08:15:19 AM
There are many resources on the net how to code a mod player but, I haven't seen or heard one that works 100% correct as the original Amiga Protracker.
There is a lot of guessing how to program the Protracker modplayer, and a lot of useless and or wrong information.

To do it correctly, you have to emulate the PAL Amiga hardware chips:
Paula ( 2*8 bit PWM sound modulation )
Agnus ( DMA engine )
CIA ( Timer )

So, I have checked all that is needed for a mod player, on my Amiga 1200 using an oscilloscope and a logic analyzer.
And of course the info from the Amiga Hardware Reference Guide. (still on my bookshelf)

Here it goes,

PAL Amiga computers are running on a 28.375160 MHz crystal.

Amiga PAL CPU speed = 28375160 / 4 = 7.093790 MHz.
Amiga PAL Clock Constant = 7.093790 / 2 = 3.546895 MHz.
Amiga PAL 1 clock cycle = 1/3.546895 MHz = 0.0000002819367362157605454912 seconds = 281.9367362157605454911972302535 nanoseconds.
Amiga PAL horizontal line frequency = 15625 Hz. ( 320x256 monitor resolution as used by Protracker2.3 )
Maximum DMA transfer for audio = 2 signed 8 bit samples per horizontal line = 31250 samples per second.

Amiga PAL vertical scan lines -> 625/2 = 312.5

Maximum Audio sample rate:
PAL -> 2 samples per line * 312.5 lines per frame * 50.0 frames per second = 31250 samples per second
PAL -> 1/50/312.5 = 0.000064 seconds per horizontal scanline (64 µs)
PAL -> Line frequency = 15625 Hz ( 1/0.000064 = 15625 )

Wait untill the next pair of samples are done ( wait_dma ready ), then set new position in sample ( and start_dma )( trigger and looping )
maybe use some extra time ( 6 - 11 scanlines as in the original replay routine )

Note that if sound is playing and you switch off the DMA, the audio hardware will not notice until it has finished playing the current sample pair (DMA word).
If you switch the DMA on again before that time, the sound will continue playing as if nothing happened. Thus, if you need to stop a sound and start a new one,
you must switch off the DMA and then wait for at least two times the period of the currently playing sound, before you can set up the new sound and switch on DMA again.
   
Maximum PAL samplerate = 31250   samples/second, 3546895 / 31250   = Period 113.50064 = 15625 Hz
   
So, the highest note in Protracker B-3 = period 113 and lower periods will keep repeating the actual 2 samples loaded in the buffer register.
It sounds like distortion or silence if both samples have the same value.
This is not a Quirk but a Feature.
If the filter on the Amiga is bypassed and the volume is set to the maximum (64), this feature can be used to make modulated carriers up to 1.79 MHz.
   
The first 4 bytes of each of the ( maximum 31 ) instruments ( real digitized sounds ) need to be set to zero, this is an indicator for the DMA transfer control to loop an instrument or stop it as a one shot instrument.
Also the time of the 4 empty bytes is used to be sure that the last 2 bytes are played till the end at low samplerates. This is very important to implement else, it doesn't sound as it does on the Amiga!

Emulating the Timers of the 8520 Complex Interface Adapters Chips (CIA).
Each of the two 16 bit timers, count down at 1/10 of the CPU speed.
It is an integer countdown timer!!!
CIA clock = Amiga PAL CPU speed / 10 = 0.7093790 MHz.
CIAtimer Constant = 1 / 0.7093790 = 1.4096836810788027274559861512675 microseconds per countdown tick.

The original Protracker v2.3A CIA Replayer source code uses 1773447 ( PAL value )
as the CIA timer constant to calculate the Beats Per Minute ( not really BPM but better see it as Tempo ).

Amiga CIA timervalue constant = 1773447
CIA Tempo Constant = 1,4096836810788027274559861512675 * 1773447 = 2499999.2951581594605986362720069 microseconds = 2.4999992951581594605986362720069 seconds.
For example: 125 BPM = 2.499999295158159 / 125 = 0,01999999436126527568478909017606 seconds
1 / 0,01999999436126527568478909017606 = 50,000014096840785205309208563888 Hz.
   
We can calculate the BPM time as follows:  2.499999295158159 * Samplerate / BPM.

This is exact:
tempo = 125
1773447 / tempo = 14187.576 ( get rid of the fractional part ) = 14187 * 1.409683681078803 * Samplerate / 1000000

IMPORTANT NOTE !!!!
The Amiga hardware uses PWM ( Pulse Width Modulation ) AKA Sample and Hold to generate audio signals.
Each 8 bit signed sample value is send to the Amiga audio DAC for the duration of a choosen period value!
DO NOT interpolate between the samples to create a PCM audio signal!
The Audio signal will be shaped later in the Mixer routine using a band pass filter to simulate the Amiga hardware specs.
After the 4 audio channels are mixed and filtered it will be send to the PC-soundcard as a PCM audio signal.
This will emulate the Amiga sound characteristics.

The audio filter is a first order low pass filter with a cutoff frequency of +/- 13.5 kHz.

This was a longer piece of text then I thought it would be.
But now you have all the hardware based info how to emulate an Amiga modplayer on a PC.

BTW I'm working on a modplayer myself but, on a very low priority.... ( always something else to do.   :biggrin:)

Marinus
Title: Re: mod player using waveoutopen
Post by: FlySky on July 24, 2019, 03:14:41 PM
Siekmanski thanks for the detailed explanation!!