#67334 - Haiken - Tue Jan 17, 2006 2:07 am
Hello everybody,
I'm trying to play sound by sending fifo messages to the ARM7 cpu.
It works but... it plays the sound only 1 time on 3 or 4.
Here is my ARM7 code :
Code: |
typedef struct FIFO_message {
u16 command;
u16 freelen;
TransferSoundData sounddata;
} FIFO_message, *pFIFO_message;
#define MESSAGE_PLAY_SOUND 1
void FIFOHandler(void) {
do {
pFIFO_message message = (pFIFO_message) REG_IPC_FIFO_RX;
s32 chan;
if (message == 0) continue;
if (message->command != MESSAGE_PLAY_SOUND) continue;
chan = getFreeSoundChannel();
if (chan >= 0) {
startSound(message->sounddata.rate, message->sounddata.data, message->sounddata.len, chan, message->sounddata.vol, message->sounddata.pan, message->sounddata.format);
}
if (message->freelen > 0) free(message);
} while (!(REG_IPC_FIFO_CR & IPC_FIFO_RECV_EMPTY));
}
...
//in main() :
irqSet(IRQ_FIFO_NOT_EMPTY, FIFOHandler);
irqEnable(IRQ_FIFO_NOT_EMPTY);
REG_IPC_FIFO_CR = IPC_FIFO_ENABLE | IPC_FIFO_SEND_CLEAR | IPC_FIFO_RECV_IRQ;
|
My ARM9 code :
Code: |
void audio_init(void)
{
//activate FIFO
REG_IPC_FIFO_CR = IPC_FIFO_ENABLE | IPC_FIFO_SEND_CLEAR;
}
void SendArm7Command(u32 command) {
fprintf(stderr, "sendarm7 %d\n", command);
while (REG_IPC_FIFO_CR & IPC_FIFO_SEND_FULL) ;
if (REG_IPC_FIFO_CR & IPC_FIFO_ERROR) {
REG_IPC_FIFO_CR |= IPC_FIFO_SEND_CLEAR;
fprintf(stderr, "ipc error\n");
}
REG_IPC_FIFO_TX = command;
}
typedef struct FIFO_message {
u16 command;
u16 freelen;
TransferSoundData sounddata;
} FIFO_message, *pFIFO_message;
#define MESSAGE_PLAY_SOUND 1
static struct FIFO_message transfer[16];
//#define transfer ((FIFO_message volatile *)(0x026FF000))
static int lastsound=0;
void audio_play_sound(Sound * sound)
{
if (!sound || !_initiated || _mute)
return;
pFIFO_message m=malloc(sizeof(FIFO_message));
m->command = MESSAGE_PLAY_SOUND;
m->freelen=sizeof(FIFO_message);
m->sounddata.data = sound->buffer;
m->sounddata.len = sound->length;
m->sounddata.rate = 11025;
m->sounddata.vol = 127;
m->sounddata.pan = 64;
m->sounddata.format = 1 /*16bits ?*/ | 2/*ADCPM ?*/;
SendArm7Command((u32)m);
} |
I tried to store the messages in shared memory, but then sounds tend to get weird (sometimes the wrong sample or part of the sample is played, sometimes "junk" is played also).
Any help would be greatly appreciated :)
#67539 - Haiken - Wed Jan 18, 2006 9:45 pm
I made it work by using a circular message buffer in shared memory, after IPC (thanks Chris Double's tutos :)
Code: |
#define sound_fifo ((FIFO_message*)((uint32)(IPC)+sizeof(TransferRegion))) |
I can't explain why it is working when it is located around 0x027FF000... it is described as being main RAM in the nds tech wiki, so why is it acting differently for another portion of the memory ?
Is this an issue with the cache or something ?
#67591 - HyperHacker - Thu Jan 19, 2006 5:36 am
Yep. 027FF000 is an uncached mirror of 02000000. (Don't remember where the ranges end, they're both the same size though.)
#67673 - Haiken - Thu Jan 19, 2006 9:17 pm
Code: |
WifiData = (Wifi_MainStruct *) (((u32)&Wifi_Data_Struct)| 0x00400000); // should prevent the cache from eating us alive. |
This is taken from sgstair's wifilib
So you have to use memory addresses starting at 0x02400000 to avoid caching issues.
Actually I had tried that, but what I misunderstood is that you have to write with ARM9, and not read with ARM7, at 0x02400000
By the way, allocating a buffer with ARM9 and trying to free it with ARM7 seems to freeze the console for some 30 seconds (which is probably normal)
Thanks HyperHacker :)
#67705 - wintermute - Thu Jan 19, 2006 11:55 pm
Haiken wrote: |
Code: | WifiData = (Wifi_MainStruct *) (((u32)&Wifi_Data_Struct)| 0x00400000); // should prevent the cache from eating us alive. |
This is taken from sgstair's wifilib
So you have to use memory addresses starting at 0x02400000 to avoid caching issues.
Actually I had tried that, but what I misunderstood is that you have to write with ARM9, and not read with ARM7, at 0x02400000
|
The addresses are mirrored on the ARM7 as well, it should make no difference.
Quote: |
By the way, allocating a buffer with ARM9 and trying to free it with ARM7 seems to freeze the console for some 30 seconds (which is probably normal)
|
and quite stupid :P I'm surprised that doesn't just crash.
You can't malloc on one processor then expect to be able to free on the other one. There are effectively two independent C runtime libraries which are unaware of each other.
#67757 - HyperHacker - Fri Jan 20, 2006 3:20 am
You know, I was thinking about that earlier. Since ARM7 and ARM9 code is compiled separately, the compiler doesn't know what's in one when it compiles the other. That means if you declare a function in both, I imagine you'd end up with 2 copies of it. ARM7 and ARM9 use mainly the same instruction set, don't they? If they do, there must be some way to use the same code for both. Seeing how there's only 4MB of RAM... :-/
#67780 - gladius - Fri Jan 20, 2006 9:30 am
Yes, it's possible. You have to do a bit of dynamic library magic to get it to work. Overlays should work properly. Probably too much effort to be worth it though.
Wintermute: Of course it makes a difference if you write to uncached ram versus reading from it. A write to cached ram from the arm9 can sit in the cache for as long as it likes, a read then from the arm7 to the uncached region will get the last value there until the cache gets flushed.
#67781 - wintermute - Fri Jan 20, 2006 9:32 am
The ARM9 has 48k of exclusive RAM
The ARM7 has 64k of exclusive RAM
THere is a further 32k of RAM which is switchable betweeen the two processors but exclusive to the one which is given access.
The ARM9 has caches, this requires careful handling of memory which is shared between the processors.
Running both processors in the same memory incurs a performance penalty due to bus arbitration.
It's certainly possible to build a unified binary that contains code for both processors but I think this would create more problems than it would solve.
#67784 - wintermute - Fri Jan 20, 2006 10:22 am
gladius wrote: |
Yes, it's possible. You have to do a bit of dynamic library magic to get it to work. Overlays should work properly. Probably too much effort to be worth it though.
Wintermute: Of course it makes a difference if you write to uncached ram versus reading from it. A write to cached ram from the arm9 can sit in the cache for as long as it likes, a read then from the arm7 to the uncached region will get the last value there until the cache gets flushed. |
Well sure, but the statement looked like it was saying you couldn't read the 0x2400000 range on ARM7 which just isn't true.
#67846 - Haiken - Fri Jan 20, 2006 9:38 pm
Actually, I was seeing it as a read (pass-through) cache, but it looks more like a write cache. Of course you can read in either region from the ARM7 :)
#67946 - melw - Sat Jan 21, 2006 2:25 pm
Haiken, when you have all working code for the sound FIFO, could you share a full example of it?
#67952 - Haiken - Sat Jan 21, 2006 3:45 pm
Sure !
ARM7 :
Code: |
typedef struct FIFO_message {
u16 command;
TransferSoundData sounddata;
} FIFO_message, *pFIFO_message;
#define MESSAGE_PLAY_SOUND 1
#define sound_fifo ((FIFO_message*)((uint32)(IPC)+sizeof(TransferRegion)))
void FIFOHandler(void) {
while (!(REG_IPC_FIFO_CR & IPC_FIFO_RECV_EMPTY)) {
pFIFO_message message = (pFIFO_message) REG_IPC_FIFO_RX;
s32 chan;
if (message == 0) continue;
if (message->command != MESSAGE_PLAY_SOUND) continue;
chan = getFreeSoundChannel();
if (chan >= 0) {
startSound(message->sounddata.rate, message->sounddata.data, message->sounddata.len, chan, message->sounddata.vol, message->sounddata.pan, message->sounddata.format);
}
}
}
//...
//in main()
irqSet(IRQ_FIFO_NOT_EMPTY, FIFOHandler);
irqEnable(IRQ_FIFO_NOT_EMPTY);
REG_IPC_FIFO_CR = IPC_FIFO_ENABLE | IPC_FIFO_SEND_CLEAR | IPC_FIFO_RECV_IRQ;
|
ARM9:
Code: |
void audio_init(void)
{
//activate FIFO
REG_IPC_FIFO_CR = IPC_FIFO_ENABLE | IPC_FIFO_SEND_CLEAR;
}
void SendArm7Command(u32 command) {
while (REG_IPC_FIFO_CR & IPC_FIFO_SEND_FULL) ;
if (REG_IPC_FIFO_CR & IPC_FIFO_ERROR) {
REG_IPC_FIFO_CR |= IPC_FIFO_SEND_CLEAR;
}
REG_IPC_FIFO_TX = command;
}
typedef struct FIFO_message {
u16 command;
TransferSoundData sounddata;
} FIFO_message, *pFIFO_message;
#define MESSAGE_PLAY_SOUND 1
static struct FIFO_message transfer[16];
static int lastsound=0;
void audio_play_sound(Sound * sound)
{
lastsound=(lastsound+1)&15;
pFIFO_message transfer2 = (pFIFO_message)( ((u32)&transfer[0]) | 0x00400000);
transfer2[lastsound].command = MESSAGE_PLAY_SOUND;
transfer2[lastsound].sounddata.data = sound->buffer;
transfer2[lastsound].sounddata.len = sound->length;
transfer2[lastsound].sounddata.rate = 11025;
transfer2[lastsound].sounddata.vol = 127;
transfer2[lastsound].sounddata.pan = 64;
transfer2[lastsound].sounddata.format = 3 /*16bits*/;
SendArm7Command((u32)&transfer[lastsound]);
} |
A 16 elements circular buffer is enough, because the FIFO can only hold 16 elements too. Actually, I should use a 17 elements buffer to avoid writing the message the ARM7 is going to read when the buffer is full. Or make sure the FIFO is not full before filling the message.
You might also simply want to drop the sound.
If you want to malloc() your messages, I think you will have to send them back (with FIFO) to ARM9 to free them.
#74104 - melw - Thu Mar 02, 2006 1:54 pm
Got back to this after a while and got the sound FIFO working. Or at least sort of, as two problems remain:
1) Whenever IRQ_VBLANK is set on ARM7 side, the FIFO IRQ stops partially responding. Sometimes, rarely some sounds do play but mostly it's all silenced. Whenever I remove the VBLANK interrupts sound playback is 100% functional. Wouldn't care too much about this otherwise but disabling the default VBLANK means also that touchscreen doesn't respond anymore...
2) Sound volume is somehow very low. I've taken a look at various other sources and noticed that some people use more channels to play a single sound. Is this really the way to go or are there other tricks to increase the loudness?
Any help on either of the issues would be appreciated very much, thanks!
#74117 - tepples - Thu Mar 02, 2006 4:25 pm
If the sound volume is very low, try looking at the minimum and maximum values of your buffer. If those are not at least +/- 100, then you need to increase all channel volumes by a constant factor and then (if you're using software mixing) clip each final sample to +/- 127 before writing to the buffer. But if your sample values are in fact close to +/- 127, as is the case with a lot of the complaints I get about GSM Player on both GBA and Nintendo DS ("it's too quiet even when I press Up to turn on volume boosting"), then you are possibly running into the Nintendo DS's output power limit, and you'll need to use powered headphones.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#74211 - melw - Fri Mar 03, 2006 3:30 pm
I have no idea why, but I found a temporary solution to my first problem by commeting the following two lines of code in the ARM7 interrupt handler:
Code: |
// Read the time
rtcGetTime((uint8 *)ct);
BCDToInteger((uint8 *)&(ct[1]), 7);
|
Although why reading the time would mess up the FIFO is beyond my comprehension...
Tepples: Yes, of course making source sound louder is one possibility. But as the same audio files are absolutely ok on other platforms I wouldn't want to go processing/compressing/normalizing to max. them every time new sounds are needed on NDS. What I'm also talking about is that the low volume seems to be quite common problem with lot of homebrew audio applications and I'm just wondering if there's something the sound playback lacks commonly - as opposed to commercial games where audio playback is always as loud as needed.
#74248 - tepples - Fri Mar 03, 2006 8:21 pm
melw wrote: |
Tepples: Yes, of course making source sound louder is one possibility. But as the same audio files are absolutely ok on other platforms |
Other handheld platforms, or do you mean a desktop with a 30 watt per channel amplifier?
Quote: |
I wouldn't want to go processing/compressing/normalizing to max. them every time new sounds are needed on NDS. |
What you could do is make a file listing conversions from source (.wav?) format to the DS format where the converter is capable of doing audio processing during each rebuild.
Quote: |
What I'm also talking about is that the low volume seems to be quite common problem with lot of homebrew audio applications and I'm just wondering if there's something the sound playback lacks commonly - as opposed to commercial games where audio playback is always as loud as needed. |
Sounds in commercial games are equalized, normalized, limited, and sometimes even saturated to fit the characteristics of the target platform, even though that's what you claim that you wouldn't want to do. Sorry :-(
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#74452 - HyperHacker - Sun Mar 05, 2006 7:59 am
Moonshell at 150% volume is fine for me. How loud are you trying to get it?