gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

Audio > Module player (Was: NeIMOD NMOD listings)

#6581 - wizardgsz - Thu May 29, 2003 8:12 pm

Thank you very much, guys. Hi really greet everyone gave an answer to my previous post!

Let me explain my request.

I'm going to code my own XM-player and I'm searching for good references (ie. mod players and docs, GBA specific stuff and not - I found a great C=64 XM player yeeaah!).
Today I've got an experimental player/mixer skeleton working (badly)... but how many troubles! :(
I'm not searching for speed or assembly code, I'd like to fix myself the
code I cut&pasted - I'm going to describe how I got my GBA playing... again, badly;-)

At first
========
At first I used various sources (mikmod and fmod docs mainly) handling
modules playback routines filling a mono single buffer (GBA implementing
details later) with songs being played at wrong rates/frequencies, FASTER AS I DECREMENT THE (RE)SAMPLING FREQ.
My "sound culture" is really poor (as my english) and I soon realized I've
to code it using double buffering techniques hoping it resolves my playback
problem. Unfortunately I cannot set up it properly:(

This is the preamble, so let's go with details.
Ehm excuse me if I got laborious...

GBA registers (DSound, timers, DMA)
===================================
I got sound so, likely, the sound registers setup is exact...
I'm using Timer0 for DSound to count mixing freq as stated worldwide, I'm
using also Timer1 to count mixing buffer overflows (interrupt), no VBlank
IRQ method...

* Timer1 at 0x10000 - the number of samples to play and enable it with irq
generation and cascade from Timer 0

* Timer0 enable for directsound at playback (resampling) frequency 0x10000 - (cpuFreq / playbackFreq)

* DMA1 to DS-A FIFO (source inc, dest inc, start fifo, repeat, 32bit)

* DMA2 to DS-B FIFO

* On Timer1 interrupts (ie mixing buffer overflows)
a- stop DMAs
b- swap mixing buffers
c- restart/reinit DMAs

Everything should be ok as I read so many posts/listings doing it the same
way but who knows;)

The mod player
==============
And what about song module-handling? Probably I am just missing something here (while cut&pasting from other source code).

* Pattern playback
The row/order/break/jump/loop handling taken from listings I mentioned above and should be exact.

* Effects
I don't handle any effect (except for pattern and tempo settings)

* Tempo setup
I use the following formula to compute samples per beat:
(playbackFreq * 5) / (BPM * 2)
ie.
playbackFreq / (BPM * 2 / 5)
but I do not reset any GBA register, shouldn't I (timer??)?

* The worker / sound poll function
This is were the song being mainly updated, frame-by-frame. The skeleton is the one you already should know, it mixes MIXBUFLEN samples at a time, in pseudo code:

Code:

function Update()
begin

 u32 tickdata;
 u32 samples_to_mix;

 u32 numsamples = MIXBUFLEN;

 // Set up buffers for the sound to be mixed into
 s8 * left  = "MixerBufferLeft";
 s8 * right = "MixerBufferRight";

 // Keep looping until we've filled the buffer
 tickdata = 0;
 samples_to_mix = numsamples;

 while ( samples_to_mix )
 {
  u32 thiscount;

  // Only move on to the next tick if we finished mixing the last
  if ( "PlayingSong"->mixer_samplesleft == 0 )
  {
   // general song pattern handling
   // row/order
   // tick0 and inbetween fx
   "UpdateTick( PlayingSong )"

   // Set the number of samples to mix in this chunk
   "PlayingSong"->mixer_samplesleft =
   "PlayingSong"->mixer_samplespertick;
  }

  // Ok, so we know that we gotta mix 'mixer_samplesleft'
  // samples into this buffer, see how much room we actually got
  thiscount = "PlayingSong"->mixer_samplesleft;
  if ( thiscount > samples_to_mix )
    thiscount = samples_to_mix;

  // Make a note that we've added this amount
  "PlayingSong"->mixer_samplesleft -= thiscount;
  samples_to_mix -= thiscount;

  // Now mix it!
  "Mix( &left[ tickdata ], &right[ tickdata ], thiscount )"
  tickdata += thiscount;
 }

end function Update();



// using 16.16 fixed point math (FRAC_BITS = 16)
function Mix( s8 * left, s8 * right, u32 numsamples )
begin

 "clear numsamples bytes of left/right buffers"

 // Loop through each channel and process note data
 "for each channel"
 {
  u32 samples_to_mix;

  // Set up for the mix loop
  s8 * mixed = "left/right according to pan settings"

  const s8 * sample = "sample data according to channel being mixed"
  fixedpoint nLength = "16.16 sample length"
  fixedpoint nLoopStart = "16.16 sample repeat offset"
  fixedpoint nLoopLength = ...
  fixedpoint nLoopEnd = ...

  fixedpoint freq = freq[ channel ];
  fixedpoint pos = start[ channel ];
  fixedpoint deltapos = fixedpoint( freq / playbackFreq );
  fixedpoint mixpos = 0;

  // Make sure I'm actually playing something
  // ...

  samples_to_mix = numsamples;
  while ( samples_to_mix )
  {
   u32 thiscount, i;

   // If I'm a looping sample then I need to check
   // if it's time to loop back. I also need to figure out
   // how many samples I can mix before I need to loop again
   // omissis...

   // inner loop
   for (i=thiscount; i!=0; i--)
   {
    // omitting volume handling...
    mixed[ mixpos++ ] += sample[ pos >> FRAC_BITS ];
    pos += deltapos;
   }
   // inner loop end

  }
  // Save current position
  start[ channel ] = pos;
 }

end function Mix();



The tricky code, my doubts
==========================
Hopefully I wrote this piece of code avoiding omissions.
I doubt the tempo settings are treated properly, I dread to think I should
change some DSound/timer register while setting up new BMP values...
The way I use the mixing buffers (MixerBufferLeft/Right above) is probably
incorrect also, I reset pointers to them frame-by-frame within the "sound
poll" function (fun Update()), is it the way I have to proceed?

And now?
========
Thanks again to everybody, I'd really appreciate your help.
Anyhow hear you soon, bye bye.


Last edited by wizardgsz on Mon Jun 23, 2003 2:25 pm; edited 1 time in total

#6602 - gb_feedback - Fri May 30, 2003 9:29 am

I was interested to see this as I am expecting to add mod capabilities to my player engine, in the near future. For reasons not to be gone into here I have made a midi player, which contains a mixer just like any other sound program.

What I was going to suggest is that you look on my site, click on the Midi link at the LEFT of the page, and on this midi page scroll down to the bottom. In the yellow box is a simplified explanation of how I organise the buffers; further up the page is a description of how I made it slightly more complicated for better sound. This approach works well and is certainly not original, as tepples will no doubt tell you. But it saves using the second timer, and a lot of uncertainty.
_________________
http://www.bookreader.co.uk/

#6613 - wizardgsz - Fri May 30, 2003 3:28 pm

Hi everybody

Ok, I know already the method you mentioned but my problem is probably elsewhere.
Thanks

#6615 - gb_feedback - Fri May 30, 2003 4:25 pm

I've read your description through 4 times. At the moment I'm having difficulty understanding what you perceive your problem to be. I'll need to read it again slowly with a piece of paper to follow the detail, but first I was trying to know what you are trying to fix.

You say you have sound - that's always a good start! Are you saying it's distorted, or the wrong rate, or just random noise? My approach would be to get one thing right at a time - what's the one thing which most bothers you?

I only mentioned the buffer business because you seemed to query it yourself and it's the thing which caused me most problems.
_________________
http://www.bookreader.co.uk/

#6686 - wizardgsz - Sun Jun 01, 2003 11:02 pm

I must thank you again for your time and your answers!
Sorry you have to read my posts 4 times;)

I'll certainly change my code and use the VBlank to swap the active mixing
buffer, probably as soon as I bugfix my tempo (bpm/speed) setup so a module being tracked to play for X seconds will not play Y seconds in my player (Y < X).

I confirm I got sound, it isn't distorted or just random noise, it is the
module notes being played (with so many PC listings/docs available I cannot miss it).
I haven't got panning and volume settings so I'm just playing a single
channel to the left and one to the right one.
When using VisualBoyAdvance to save the wave while playing a module with no effects I got shorten wave I should have.

Without double buffering techniques I also got different wave files using
different mixing frequencies - this is gone introducing double buffering.
Past, decreasing mixing freq I got faster waves, being played faster as I
reduce the resampling/mixing freq.

To recap
========
First trouble I'm trying to fix:
when using VisualBoyAdvance to save the wave while playing a module with no effects I got shorten wave I should have. For example a module tracked for 52 seconds is played for about 45 secs.

Second thought:
I doubt the tempo settings are treated properly instead, I dread to think I
should change some DSound/timer register while setting up new BPM values.

Third thought:
the way I use the mixing buffers (MixerBufferLeft/Right in my previous post) is probably incorrect also, I reset pointers to them frame-by-frame within the "sound poll" function (fun Update()), is it the way I have to proceed? Shouldn't I use some "circular" buffer?

#6721 - Quirky - Mon Jun 02, 2003 8:13 am

First trouble I'm trying to fix:
when using VisualBoyAdvance to save the wave while playing a module with no effects I got shorten wave I should have. For example a module tracked for 52 seconds is played for about 45 secs.


Although I don't know much about GBA sound, I do know not to trust emulators 100%, especially on timing issues. The difference could be due to the speed of your computer making VBA run too fast. Have you tried running your code on hardware with an on screen clock to time the mod track? That would be a more accurate assesment of your timing code.

#6731 - wizardgsz - Mon Jun 02, 2003 11:48 am

Quirky wrote:

Although I don't know much about GBA sound, I do know not to trust emulators 100%, especially on timing issues. The difference could be due to the speed of your computer making VBA run too fast. Have you tried running your code on hardware with an on screen clock to time the mod track? That would be a more accurate assesment of your timing code.


Well, my k6-3 should be.. ehm, too slow:(
I print also onscreen seconds/timer value while playing my song.
Unfortunately I cannot test it on real hardware as I haven't got it.

Thanks