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.

DS development > Impossible sound problem.

#115648 - relpats_eht - Thu Jan 18, 2007 3:36 am

According to myself and everyone else's whom I have asked, this problem cannot possible exist, and yet, I'm posting.

What I really want to know here is how this is even possible. If you know a solution to the problem, you may feel free to tell me it, but what I am really asking is how this bug can possibly exist. I feel it is most easily explained through code.

ARM7:
Code:
IPC->aux = 0
if(snd->data[i].rate == 8000) IPC->aux += 10;
SCHANNEL_TIMER(i) = SOUND_FREQ(snd->data[i].rate); // Does not work
SCHANNEL_TIMER(i) = SOUND_FREQ(8000); // Does work
if(snd->data[i].rate == 8000) IPC->aux += 10;
if(snd->data[i].len == 54661) IPC->aux += 100;
SCHANNEL_LENGTH(i) = snd->data[i].len; // Does not work
SCHANNEL_LENGTH(i) = snd->data[i].len >> 4; // Does work, but only plays a portion of the sample
SCHANNEL_LENGTH(i) = 13665; // Does work (54661 >> 2)
if(snd->data[i].len == 54661) IPC->aux += 100;


If after the sound is loaded and played IPC->aux is printed out, it will equal 220, meaning that the variables equal what they should, both before and after they are used; however, the sample will not play correctly unless constants are used in place of them. I should also probably note that even while using constants so that the sample, rather than garbage, does play, it is a bit distorted.

If you would like further explanation or code before you can adequately tell me how this conundrum is possible, please, feel free to ask for it.
_________________
- relpats_eht

#115651 - Lick - Thu Jan 18, 2007 4:11 am

maybe cuz of some overflow. try doing the frequencycalculations without the defines using 2 steps.
_________________
http://licklick.wordpress.com

#115659 - HyperHacker - Thu Jan 18, 2007 7:14 am

Most of the time I have bugs that defy the laws of logic, it ends up being a massive oversight on my part or memory/stack corruption.
_________________
I'm a PSP hacker now, but I still <3 DS.

#115665 - sajiimori - Thu Jan 18, 2007 7:34 am

Could you post the definitions of SCHANNEL_TIMER, SOUND_FREQ, and snd->data?

You may also want to inspect the assembler output. It may give hints as to the internal difference between what works and what doesn't.

(My first guess is sign extension.)

#115699 - relpats_eht - Thu Jan 18, 2007 1:42 pm

I've tried using no defines and multiple steps to no avail, and my definitions of SCHANNEL_TIMER, SOUND_FREQ, and snd->data should be the same as everyone else's; nevertheless.
snd->data:
Code:
typedef struct sTransferSoundData {
  const void *data;
  u32 len;
  u32 rate;
  u8 vol;
  u8 pan;
  u8 format;    // Note: I use this as a bitfield, but only the first 4 bits
  u8 PADDING;
} TransferSoundData, * pTransferSoundData;


Defines:
Code:
#define SCHANNEL_TIMER(n)         (*(vint16*)(0x04000408 + ((n)<<4)))
#define SOUND_FREQ(n)   ((-0x1000000 / (n)))


Also, I do flush sound data before I submit it to the IPC. Sjiimori, I do not know enough ASM to inspect its output.
_________________
- relpats_eht

#115700 - Lick - Thu Jan 18, 2007 1:45 pm

What about using u16 for the rate?
_________________
http://licklick.wordpress.com

#115729 - sajiimori - Thu Jan 18, 2007 7:57 pm

First question: If 'data' is a void pointer, why is the compiler letting you dereference it? Maybe I take the typesafety of C++ for granted, but I didn't even think that was allowed in C.

Try to make the right-hand side of each assignment the same type as the left-hand side. With the mix of signed/unsigned and wide/narrow types, I wouldn't be surprised if sign extension really is the problem.

#115732 - Lick - Thu Jan 18, 2007 8:16 pm

It's probably

TransferSoundData data[n];

so data[n].data is void*, not data.
_________________
http://licklick.wordpress.com

#115734 - sajiimori - Thu Jan 18, 2007 8:27 pm

Oh, right. :)

ARM assembler is pretty easy. Compile something like this with -S and check out what it says:
Code:

void broken()
{
  SCHANNEL_TIMER(i) = SOUND_FREQ(snd->data[i].rate);
}

void works()
{
  SCHANNEL_TIMER(i) = SOUND_FREQ(8000);
}

#115735 - josath - Thu Jan 18, 2007 8:33 pm

another test you could try is an intermediate variable or two:
Code:

void maybeworks() {
  u16 rate = SOUND_FREQ(snd->data[i].rate)
  SCHANNEL_TIMER(i) = rate;
}

void maybeworks2() {
  u32 rate1 = snd->data[i].rate;
  u16 rate2 = SOUND_FREQ(rate2);
  SCHANNEL_TIMER(i) = rate2;
}

#115736 - DekuTree64 - Thu Jan 18, 2007 8:54 pm

I think sajiimori may be right, that it's the negative number in that macro that's causing the problem. It's probably converting it to a typeless 32-bit value, and then deciding to do an unsigned division because the variable is unsigned. Try moving the negative outside the parentheses:
Code:
#define SOUND_FREQ(n)   (-(0x1000000 / (n)))


As for the length setting, the registers take the length in words (32-bit units), so if the sample format is 8-bit, then you'll want length >> 2, if 16-bit, then length >> 1, if ADPCM, then length >> 4.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku

#115789 - relpats_eht - Fri Jan 19, 2007 2:39 am

Thank you, the problem was the negative in the macro for frequency and length was fixed by using another variable. I can understand why the macro failed but still don't see the difference between a variable and a variable accessed through a structure and shifted before assignment, but I suppose that is largely irrelevant until I come across such a problem again.
_________________
- relpats_eht