#32569 - DiscoStew - Fri Dec 24, 2004 12:30 am
To explain a little of what I am trying to do, I have been experimenting with using 8ad for a while (old thread is here), and I successfully got it work with AAS under it's SFX functions. It works, but now I've currently been working with HBlank effects, and now my 8ad add-on is interfering with the HBlank. The problem is that in order for 8ad decompression to work (at that time), I had to decompress the audio on the same timer interrupt as AAS's FastTimer1InterruptHandler (50 times a second), and since it takes a couple of scanlines per decomp, anything using the HBlank becomes distorted.
My only option at this time is to convert the code from decompressing on the timer interrupt at 50 times a second to decompressing on the VBlank at 60 times a second. My method was that when the 8ad audio starts playing, it first decompressed 2 sections into a wrap-around buffer that holds about 4 sections (so as to make sure that AAS has stuff to play), and then every VBlank afterwards, decompresses 1 section into that buffer. The decompression routine is set up so that when data gets to the end, it continues decompressing at the start (in a wrap-around setting). After the 2 sections are decomped, I initiate AAS's SFX playback function to start playing at the start of the wrap-around buffer, and have it loop to the start of it.
I would assume that if the newest decompressed data stays ahead of what AAS is playing and does not go faster to speed passed it, then I shouldn't have problems with actual playback, even if AAS works under 50 fps, and 8ad under 60 fps. The playback does work. However, there are problems. It turns out that for some reason AAS plays faster than what gets decompressed, even with audio at 18157 Hz being decompressed 304 bytes per go. Not only that, but the audio clicks. After a lot of tinkering around, I found out that the clicking lessened when the buffer was bigger, and after a while, I found out that the clicking was found to be at the beginning of the buffer.
At the moment I can't display code because I am on a break from work, but I will post the code when I get home. If anyone has any ideas before then, please post them. thx in advance
_________________
DS - It's all about DiscoStew
#32572 - tepples - Fri Dec 24, 2004 12:50 am
I had that problem once when making TOD's mixer. Have you made sure that 8ad's decode buffer is aligned to a 4-byte boundary?
Or AAS's frequency control might be off. Perhaps AAS can't represent the playback frequency exactly, and the clicking is the effect of a rounding error in the playback frequency calculation. You'll want to resample to the native mixer frequency before encoding with 8ad anyway; for AAS this is 16000 Hz, not 18157 Hz. Then, for every AAS call at 50 Hz, decode 320 samples.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#32685 - jd - Sat Dec 25, 2004 12:40 am
Quote: |
Or AAS's frequency control might be off. Perhaps AAS can't represent the playback frequency exactly, and the clicking is the effect of a rounding error in the playback frequency calculation. You'll want to resample to the native mixer frequency before encoding with 8ad anyway; for AAS this is 16000 Hz, not 18157 Hz.
|
AAS's mixing frequency can be 8, 12, 16, 20, 24, 28, or 32 KHz, depending on what is specified when calling AAS_SetConfig(). The playback frequency is converted to a delta relative to the mixing frequency in 2.10 fixed point format so the rounding error is likely to be very small. (Requesting 18157 Hz when mixing at 16000 Hz will actually result in the sample playing at 18156.25 Hz.) Like Tepples said, it would be best if 8ad's output was resampled to be an exact multiple (or factor) of the mixing frequency.
How much faster does AAS's playback go than 8ad's output? If it's only a few percent then that could be explained by the rounding error but if it's larger than that then I'm not sure what the problem could be. Perhaps it could be that 8ad's output is actually getting ahead of where it should be so the end of it is played at the beginning of AAS's output? Or could the long vblank interrupt be causing a big enough delay in processing the Timer 1 interrupt to cause a click in the audio? (To fix this, you'll need to use the interrupt handling scheme shown in AASExample2.)
#32693 - DiscoStew - Sat Dec 25, 2004 3:19 am
Sorry, I was meaning to post the code when I got home, but I had other things I needed to take care of. I noticed that I don't do much programming during the Christmas season, cause there is so much going on for me. It probably won't be until after Christmas when I can get back into it. Since there is quite a bit of code to go through, would anyone happen to know a good (free!) place where I can store my stuff for others to download? That way I won't be cluttering the threads with my code. I'll have to relearn HTML again, but that shouldn't take long, especially if I can find my old web programming stuff. thx, and I'll get that code up as soon as I can, and I'll try your suggestions and tips.
_________________
DS - It's all about DiscoStew
#32854 - DiscoStew - Tue Dec 28, 2004 7:46 pm
Ok, after a few days of the forums being down, I'm ready to post my code. The things you should take note (although not posted) is that AAS is initialized first before any 8ad stuff happens. After that is done, I load audio with the LoadSFX_8ad function, which sets everything up to be ready for playback. Using the PlayAudio_8ad begins the audio. In my VBlank interrupt, I've got both the AAS DoWork function, and my Update_8ad function. In my Timer1 interrupt, I have the AAS FastTimer1InterruptHandler function. The reason why I haven't been using what was shown in Example #2 is because originally I had my Update_8ad function just before the FastTimer1InterruptHandler function, with other changes like the value 4389 (60 Hz) used for determining the buffer length was 5243 (50 Hz) to sync with AAS, plus I had only 2 304-byte buffers per channel, and Update_8ad would switch between the 2, and recall AAS_SFX_Play each time so as to not have any clicks. It worked, but it now interferes with the HBlank interrupt.
I'll probably switch back to the Example #2 example now, since I don't have anything else in that function (at the moment). Hopefully you will be able to read this code and understand what I am trying to do. If you have any question, please ask me, cause I really want this to work.
This is the "8ad.h" header file
Code: |
#ifndef _8AD_STUFF_H
#define _8AD_STUFF_H
#include "gba.h"
#include "AAS.h"
#define MAX_8AD_CHANNELS 1
#define MAX_8AD_BUFCOUNT 5
//The 8ad demo used 304, so I decided to make that the limit
#define MAX_8AD_BUFLENGTH 304 * MAX_8AD_BUFCOUNT
//Structure of 8ad audio in ROM
typedef struct {
const u16 SampleFreq;
const u8 *SampleStart;
const u8 *SampleEnd;
} s_8adSampleData;
//All 8ad audio combined
typedef struct {
const u8 NumOfSFX;
const s_8adSampleData *SFXs;
} s_8ad_SFXData;
//Structure in EWRAM for each channel
typedef struct
{
s32 last_sample;
s32 last_index;
s8 mix_Buffers[MAX_8AD_BUFLENGTH];
u8 *data;
s8* cur_BufferPos;
s8* end_BufferPos;
u16 Index_8ad;
u16 cur_SampleFreq;
u16 sample_BufLen;
u8 channel_type;
u8 playback_option;
BOOL stopped_first;
BOOL buf_in_use;
} ADGlobals;
extern const s_8ad_SFXData SFX_8ad;
extern IN_EWRAM ADGlobals ad[MAX_8AD_CHANNELS];
extern CODE_IN_IWRAM void decode_8ad(u32 ChannelLoop);
extern CODE_IN_IWRAM void Update_8ad();
extern BOOL LoadSFX_8ad(u8 UseChannel_8ad, u16 SFX_8ad_Index, u8 Overwrite);
extern void UnloadChannel_8ad(u8 Channel_8ad);
extern BOOL PlayAudio_8ad(u8 Channel_8ad, BOOL Loop, BOOL Restart);
extern BOOL StopAudio_8ad(u8 Channel_8ad);
#endif |
This is the "8ad.c" file, which doesn't contain UnloadChannel_8ad or StopAudio_8ad because they aren't needed at this time
Code: |
#include "8ad.h"
IN_EWRAM ADGlobals ad[MAX_8AD_CHANNELS];
BOOL LoadSFX_8ad(u8 UseChannel_8ad, u16 SFX_8ad_Index, BOOL Overwrite)
{
if(UseChannel_8ad >= MAX_8AD_CHANNELS) return (FALSE);
if(SFX_8ad_Index >= SFX_8ad.NumOfSFX) return (FALSE);
if((ad[UseChannel_8ad].buf_in_use == FALSE) || (Overwrite))
{
AAS_SFX_Stop(UseChannel_8ad);
ad[UseChannel_8ad].Index_8ad = SFX_8ad_Index;
ad[UseChannel_8ad].channel_type = 0;
ad[UseChannel_8ad].cur_SampleFreq = SFX_8ad.SFXs[SFX_8ad_Index].SampleFreq;
//The sample_BufLen takes the sample rate, and converts it to create the buffer length needed.
//However, I believe that figuring out the buffer length (multiple of 4) to sample rate
//would be best, like a 304-byte buffer would go nicely with 18157 Hz audio
// 5243 - 50 Hz, 4389 - 60 Hz
ad[UseChannel_8ad].sample_BufLen = ((ad[UseChannel_8ad].cur_SampleFreq * 4389) >> 18);
ad[UseChannel_8ad].data = (u8*)SFX_8ad.SFXs[SFX_8ad_Index].SampleStart;
ad[UseChannel_8ad].last_sample = 0;
ad[UseChannel_8ad].last_index = 0;
ad[UseChannel_8ad].playback_option = 0x00;
ad[UseChannel_8ad].cur_BufferPos = ad[UseChannel_8ad].mix_Buffers;
ad[UseChannel_8ad].end_BufferPos = ad[UseChannel_8ad].mix_Buffers +
(ad[UseChannel_8ad].sample_BufLen * MAX_8AD_BUFCOUNT);
ad[UseChannel_8ad].stopped_first = TRUE;
ad[UseChannel_8ad].buf_in_use = TRUE;
return (TRUE);
}
return (FALSE);
};
BOOL PlayAudio_8ad(u8 Channel_8ad, BOOL Loop, BOOL Restart)
{
if(Channel_8ad >= MAX_8AD_CHANNELS) return (FALSE);
if(ad[Channel_8ad].buf_in_use == FALSE) return (FALSE);
if(Restart)
{
ad[Channel_8ad].data = (u8*)SFX_8ad.SFXs[ad[Channel_8ad].Index_8ad].SampleStart;
ad[Channel_8ad].last_sample = 0;
ad[Channel_8ad].last_index = 0;
ad[Channel_8ad].cur_BufferPos = ad[Channel_8ad].mix_Buffers;
ad[Channel_8ad].stopped_first = TRUE;
}
ad[Channel_8ad].playback_option &= ~0x04;
ad[Channel_8ad].playback_option |= (0x01 | ((Loop == 0x00) ? 0x00 : 0x04));
return (TRUE);
}; |
And last, but not least, the "8ad.iwram.c" file. With 8ad, there are functions and tables used for decoding, but since I haven't changed those, I won't post them. Go to Tepples website if you wish to see his original 8ad code. The decode_8ad function was slightly altered to accomidate the buffer, whether the end was at a selected spot, or at the end of the actual buffer.
Code: |
CODE_IN_IWRAM void decode_8ad(u32 ChannelLoop)
{
s32 last_sample = ad[ChannelLoop].last_sample;
s32 index = ad[ChannelLoop].last_index;
u32 by = 0, len = ad[ChannelLoop].sample_BufLen;
u8* src = ad[ChannelLoop].data;
s8* dst = ad[ChannelLoop].cur_BufferPos;
s8* end = ad[ChannelLoop].end_BufferPos;
while (len > 0)
{
s32 step, diff;
u32 code;
if(index < 0)
index = 0;
if(index > 88)
index = 88;
step = ima_step_table[index];
if(len & 1)
code = by >> 4;
else
{
by = *src++;
code = by & 0x0000000F;
}
diff = ima9_rescale(step, code);
index += ima9_step_indices[code & 0x00000007];
last_sample += diff;
if(last_sample < -32768)
last_sample = -32768;
if(last_sample > 32767)
last_sample = 32767;
*dst = (last_sample >> 8);
dst++;
if(dst == end)
dst = ad[ChannelLoop].mix_Buffers;
len--;
}
ad[ChannelLoop].last_index = index;
ad[ChannelLoop].last_sample = last_sample;
ad[ChannelLoop].cur_BufferPos = dst;
ad[ChannelLoop].data += ad[ChannelLoop].sample_BufLen >> 1;
}
CODE_IN_IWRAM void Update_8ad(void)
{
u32 ChannelLoop;
for(ChannelLoop = 0; ChannelLoop < MAX_8AD_CHANNELS; ChannelLoop++)
{
if(ad[ChannelLoop].buf_in_use == TRUE)
{
if(ad[ChannelLoop].playback_option & 0x01)
{
if(ad[ChannelLoop].data >= (SFX_8ad.SFXs[ad[ChannelLoop].Index_8ad].SampleEnd - ad[ChannelLoop].sample_BufLen))
{
ad[ChannelLoop].data = (u8*)SFX_8ad.SFXs[ad[ChannelLoop].Index_8ad].SampleStart;
ad[ChannelLoop].last_sample = 0;
ad[ChannelLoop].last_index = 0;
if(!(ad[ChannelLoop].playback_option & 0x04))
{
AAS_SFX_Stop(ChannelLoop);
ad[ChannelLoop].playback_option = 0;
continue;
}
}
decode_8ad(ChannelLoop);
if(ad[ChannelLoop].stopped_first == TRUE)
{
//Decoding another section just before playback, to make sure that decoding
//stays ahead of playback
decode_8ad(ChannelLoop);
ad[ChannelLoop].stopped_first = FALSE;
AAS_SFX_Play(ChannelLoop, 64, ad[ChannelLoop].cur_SampleFreq, ad[ChannelLoop].mix_Buffers, ad[ChannelLoop].end_BufferPos , ad[ChannelLoop].mix_Buffers);
}
}
}
}
return;
}; |
I went and tried resampling the audio to be exactly like the playback speed that AAS would use, but it still comes up with the same problems. I have yet to try the interrupt handling scheme shown in Example #2, so I'll try that when I get the chance.
_________________
DS - It's all about DiscoStew
#32881 - jd - Tue Dec 28, 2004 11:22 pm
DiscoStew wrote: |
I have yet to try the interrupt handling scheme shown in Example #2, so I'll try that when I get the chance. |
That's your best bet IMHO. By the way, if you do find later that you need to do other processing triggered by the Timer 1 interrupt, then you can modify crt0.s so that it calls your own function (which should call AAS_FastTimer1InterruptHandler() before doing anything else) by modifying the line below (760) to use your function name instead:
Code: |
ldr r0, =AAS_FastTimer1InterruptHandler
|
#32882 - jd - Tue Dec 28, 2004 11:29 pm
Also, unless I've misunderstood your code, shouldn't these lines..
Code: |
if(last_sample < -32768)
? last_sample = -32768;
|
..be changed to..
Code: |
if(last_sample < -32512)
? last_sample = -32512;
|
..to clip the output to the range (-127,127) as required by AAS?
#32885 - tepples - Tue Dec 28, 2004 11:46 pm
Correct.
One thing though: If you're decoding 8ad audio at 16000 Hz at 50 calls per second, your destination buffer should be 320 bytes big, not 304.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#32889 - DiscoStew - Tue Dec 28, 2004 11:59 pm
jd wrote: |
Also, unless I've misunderstood your code, shouldn't these lines..
Code:
if(last_sample < -32768)
last_sample = -32768;
..be changed to..
Code:
if(last_sample < -32512)
last_sample = -32512;
..to clip the output to the range (-127,127) as required by AAS? |
That was what it was before posting the code. It was so long since I messed with that portion of the code, that when I went back to tepples original 8ad code recently, I changed it back to how he had it, thinking that that could have been part of the problem. I had completely forgotten about the range. I did change it back, but the problems still exist.
I will try the interrupt stuff, but I haven't been using your custom crt0.s file since I started using DevKitARM. If I place that crt0.s file in my working folder, it should automatically use that file when compiling, right?
tepples wrote: |
One thing though: If you're decoding 8ad audio at 16000 Hz at 50 calls per second, your destination buffer should be 320 bytes big, not 304. |
That is correct, and that would be the case before, but I'm trying to use 8ad under 60 cps (calls per second) instead of 50 cps because of HBlank artifacting. I plan to set the code up so that the max would be 304, which represents 18157 Hz under 60 cps.
_________________
DS - It's all about DiscoStew
#32892 - jd - Wed Dec 29, 2004 12:24 am
DiscoStew wrote: |
I will try the interrupt stuff, but I haven't been using your custom crt0.s file since I started using DevKitARM. If I place that crt0.s file in my working folder, it should automatically use that file when compiling, right?
|
I haven't used DevKitARM much myself, so I can't say for sure but I'd be surprised if the default makefile did that automatically. However, the makefile included with AASExample2 can be compiled under DevKitARM, although you do need to modify it a bit as shown in the FAQ:
http://www.apex-designs.net/AASDocs/faq.html
#32893 - DiscoStew - Wed Dec 29, 2004 12:31 am
I have my own Makefile, but I just tried using the crt0.s supplied, and I got problems compiling, because DevKitARM seems to be linking a different crt0.o file in the DevKitARM folder, which as far as I know is not directly linked in my Makefile. After searching on the forums, I found this thread, in which the person replaced the one in there to get AAS working on DevKitARM with the interrupt stuff on it. I made sure of making a backup of the original just in case. Now I've got a problem, in which now I can't even get into the main entry point (AgbMain). I'll have to check into this in a bit, by making a clean project, and including all the basic stuff needed to set up AAS with that crt0.s under DevKitARM, and examining that.
EDIT:
I checked with No$GBA, and it seems that the compiled ROM has a bad header, which is why it crashes. This is when I replace the gba_crt0.o file in the DevKitARM\arm-elf\lib\interwork folder with a compiled one I moved from my folder into there. When I removed the O file from that folder, hoping that it would redirect to my uncompiled S file, it rerouted to the gba_crt0.o file just 1 folder up. I'll continue looking for a fix for this, unless someone knows how to fix it already.
_________________
DS - It's all about DiscoStew
#32904 - tepples - Wed Dec 29, 2004 2:50 am
DiscoStew wrote: |
I will try the interrupt stuff, but I haven't been using your custom crt0.s file since I started using DevKitARM. If I place that crt0.s file in my working folder, it should automatically use that file when compiling, right? |
No, it won't. You have to specify it when linking, and you also have to specify that the linker shouldn't use the default startup file.
Quote: |
tepples wrote: | If you're decoding 8ad audio at 16000 Hz at 50 calls per second, your destination buffer should be 320 bytes big, not 304. | That is correct, and that would be the case before, but I'm trying to use 8ad under 60 cps (calls per second) instead of 50 cps because of HBlank artifacting. |
You're not supposed to run the 8ad decoder from the Hblank ISR. Instead, run it 5 out of every 6 vblanks.
Quote: |
I plan to set the code up so that the max would be 304, which represents 18157 Hz under 60 cps. |
When using 8ad with AAS, you're supposed to resample the audio to 16000 Hz, not 18157 Hz. When you use 18157 Hz, AAS has to resample the audio to 16000 Hz, and nearest-neighbor resampling sounds really bad if the audio has any tonal content in the high end.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#32906 - jd - Wed Dec 29, 2004 3:29 am
tepples wrote: |
When using 8ad with AAS, you're supposed to resample the audio to 16000 Hz, not 18157 Hz. When you use 18157 Hz, AAS has to resample the audio to 16000 Hz, and nearest-neighbor resampling sounds really bad if the audio has any tonal content in the high end. |
AAS doesn't necessarily use 16 KHz as its mixing rate - it can be set to 8, 12, 16, 20, 24, 28 or 32 KHz.
#32920 - tepples - Wed Dec 29, 2004 6:15 am
True, but even if you have set AAS's mixing rate to any multiple of 4 kHz, it's best if a streaming sample is encoded at the native mixing rate or half the native mixing rate, not some 1.134 times the native mixing rate.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#32921 - DiscoStew - Wed Dec 29, 2004 6:19 am
I can link the custom crt0.s just like any other source file with ease, but how would I prevent the default one from being linked? My Makefile does not directly link it (as far as I know), but I'm thinking the compiler has something to do with it.
tepples wrote: |
You're not supposed to run the 8ad decoder from the Hblank ISR. Instead, run it 5 out of every 6 vblanks. |
LOL, I didn't say I was running it from the HBlank. I said that by calling it 50 times a second, it created artifacting on the HBlank. When I had this working before, it was running on the same Timer1 interrupt that AAS uses, which runs at 50 times a second. Because the interrupt could occur during the HBlank, calling 8ad functions that take a few scanlines to finish took priority over the HBlank interrupt, resulting in artifacting.
Running it 5 of every 6 times would seem to work, but I really want to smooth the processing over each VBlank so as to make everything evened out, even if the processing is little.
jd wrote: |
The playback frequency is converted to a delta relative to the mixing frequency in 2.10 fixed point format so the rounding error is likely to be very small. |
I was just thinking of something concerning the rounding. If I knew what the main mixing frequency was, would it help if I were to slightly change the actual mixing frequency of the SFX so that it would end up playing at what it should be playing at? If it would, I'd like to know, if you would allow, the algorithm that helped you figure out what the sample would end up playing at that you showed me.
_________________
DS - It's all about DiscoStew
#32924 - jd - Wed Dec 29, 2004 7:04 am
DiscoStew wrote: |
I can link the custom crt0.s just like any other source file with ease, but how would I prevent the default one from being linked? My Makefile does not directly link it (as far as I know), but I'm thinking the compiler has something to do with it.
|
I think "-nostartfiles" is probably what you're looking for.
DiscoStew wrote: |
I was just thinking of something concerning the rounding. If I knew what the main mixing frequency was, would it help if I were to slightly change the actual mixing frequency of the SFX so that it would end up playing at what it should be playing at?
|
No, it isn't possible to represent 18157Hz with a 2.10 delta relative to a 16 KHz mixing rate - 18156.25Hz is the closest you can get. However, as Tepples said, samples will sound best if they're played at exactly the mixing frequency as that means they won't have to be resampled. (You can also be sure that they will actually be played back at exactly that frequency rather than something close.)
DiscoStew wrote: |
If it would, I'd like to know, if you would allow, the algorithm that helped you figure out what the sample would end up playing at that you showed me. |
Convert requested frequency to 2.10 fixed point delta (assumes 16KHz mixing rate):
(18157*1024)/16000 = 1162.048 -> rounds to 1162
Convert back to get actual frequency:
(1162*16000)/1024 = 18156.25 Hz
#32925 - tepples - Wed Dec 29, 2004 7:22 am
DiscoStew wrote: |
Running [the 8ad decoder] 5 of every 6 times would seem to work, but I really want to smooth the processing over each VBlank so as to make everything evened out, even if the processing is little. |
The 8ad decoder runs so fast in practice that the 20% extra worst-case speed hit (about three scanlines) won't be noticed in practice, especially because you can run it during draw, after time-critical vblank stuff. But if you are already pushing the limits of worst-case CPU usage and battery drain, then alternate decoding 272, 272, and 256 samples every frame for the example of sound encoded at 16000 Hz.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#32982 - DiscoStew - Wed Dec 29, 2004 9:28 pm
I haven't gotten to anymore fixing since last night, but this morning I kept thinking about what tepples said...
tepples wrote: |
Instead, run it 5 out of every 6 vblanks. |
All of a sudden on my way to work, it hit me. If I change a few things to my original working code to move it from the Timer1 interrupt to the VBlank interrupt, set the buffer size to about 6 times the decoded sample length, update the buffer each VBlank with resetting to start of buffer once every 6 VBlanks, and replaying the buffer once every 5 Timer1 interrupts manually instead of letting it loop by itself, then perhaps it will work, which should also fix the problems I currently have, plus I'll still be able to use just about any mixing frequency like before. This structure is just about the same as my original working concept, except the decoding process will be on the VBlank, not the Timer1 interrupt, and the buffer size will be 3 times bigger that it was before
Now, I got to get coding on this to see if it will really work.
_________________
DS - It's all about DiscoStew
#33034 - DiscoStew - Thu Dec 30, 2004 8:31 am
Well, I'm happy. I went and worked with it a little today, and it turns out that it works.
The only problem I am facing right now with it is the algorithm that determines the size of a section of the buffer because it isn't a FIXED type. This causes some mixing frequencies to playback the audio normally for less than a second, then quickly fade to what could be very low volume with clicking. This really isn't much of problem, and should take no time at all to fix.
thx for the help guys. I'll definitely be back if problems start arising again. lol
_________________
DS - It's all about DiscoStew