PS2 OPL SND TEST

Status
Not open for further replies.
Code:
struct audsrv_adpcm_t sfx[6];
u8* snd_buffer[6];
int snd_size[6];
char snd_path[6][256];

char device_path[128];

If you declare these outside of any function, they will be considered globals. This would cause the path array to be placed on the stack instead, so that it'll take up memory, only during the runtime of sfxInit():

Code:
int sfxInit(void)
{
    char snd_path[6][256];
    FILE* adpcm;
    int ret, i;

You are trying to assign the (uninitialized) value of the elements of the sfx_size[] array to sfx.size. At that point, snd_buffer[]'s elements would also be uninitialized (until you use malloc).
Code:
    //sfx0
    sfx[0].buffer = snd_buffer[0];
    sfx[0].size = snd_size[0];
    sprintf(snd_path[0], "%s/scrollV.adp", device_path);

There's also no need to have 6 different paths, if you divide and conquer:
Code:
#define NUM_SFX_FILES 7

static const char *sfx_files[NUM_SFX_FILES] = {
    "scrollV.adp",
    "scrollH.adp",
    "page.adp",
    "cancel.adp",
    "transition.adp",
    "alert.adp",
    "confirm.adp",
};

static audsrv_adpcm_t sfx[NUM_SFX_FILES];

//Returns 0 (AUDSRV_ERR_NOERROR) if the sound was loaded successfully.
static int sfxLoad(const char *snd_path, audsrv_adpcm_t *sfx)
{
    FILE* adpcm;
    void *buffer;
    int ret, size;

    adpcm = fopen(snd_path, "rb");
    if (adpcm == NULL)
    {
        printf("failed to open adpcm file %s\n", snd_path);
        return -ENOENT;
    }

    fseek(adpcm, 0, SEEK_END);
    size = ftell(adpcm);
    rewind(adpcm);

    buffer = malloc(size);
    if (buffer == NULL)
    {
        printf("Failed to allocate memory for SFX\n");
        return -ENOMEM;
    }

    fread(buffer, 1, size, adpcm);
    fclose(adpcm);

    ret = audsrv_load_adpcm(sfx, buffer, size);
    free(buffer);

    return ret;
}

static void getAudioDevice(char *device_path)
{
    if ((gAudioPath == 0))
       sprintf(device_path, "mc0:Sound/");
    if ((gAudioPath == 1))
       sprintf(device_path, "pfs0:Sound/");
    if ((gAudioPath == 2))
       sprintf(device_path, "mass0:Sound/");
}

//Returns number of audio files successfully loaded, < 0 if an unrecoverable error occurred.
int sfxInit(void)
{
    char snd_path[256];
    char device_path[128];
    int ret, i, loaded;

    ret = audsrv_init();
    if (ret != 0)
    {
        printf("sfx: failed to initialize audsrv\n");
        printf("audsrv returned error string: %s\n", audsrv_get_error_string());
        return -1;
    }

    audsrv_adpcm_init();
    audsrv_set_volume(MAX_VOLUME);

    getAudioDevice(device_path);

    loaded = 0;
    for (i = 0; i < NUM_SFX_FILES; i++)
    {
        sprintf(snd_path, "%s/%s", device_path, sfx_files[i]);
        ret = sfxLoad(snd_path, &sfx[i]);
        if (ret == 0)
        {
            loaded++;
        }
        else
        {
           printf("sfx: failed to load %s, error %d\n", snd_path, ret);
        }
    }

    return loaded;
}

You were using the elements of the audsrv_adpcm_t structure to handle the loading of data, like the buffer and size attributes:
Code:
    //sfx0
    sfx[0].buffer = snd_buffer[0];
    sfx[0].size = snd_size[0];

I don't think it is wrong, but this is a structure that belongs to AUDSRV and is filled in by audsrv_load_adpcm().
So by using its fields, you are creating some coupling with AUDSRV, limiting the reusability of your code, should there be some need to move to a different audio RPC in the future.

So what I did, was to use local variables instead.

Regarding the return value of malloc(), it returns a pointer. So you should compare it against NULL.
Comparing it against 0 will work, since NULL is defined as 0. But it's less readable.

Can only get 3 sfx loaded, attempting more freezes the console but hey....its 1 more than before
I don't know why it's not working. Do you know when it hangs?
The SPU2 has 2MB, but it's not 2MB for just storing ADPCM.
 
I suppose that a bit (of the) SRAM is used (just) like it is done by the GS and the VRAM, to process the 'end-composition'...

Now we only need to know, how much that is (what's used and what's free) and the management of the 'Kickout and reload' of sounds might need some rework (not necessarily needed, if the performance is not much affected by the reloads).


I am not sure, how the 'voice-channels' get mixed currently (EE or SPU2?!). I am not sure how audsrv handles it, but it should definitely be mixed via the SPU2, tho'. I suppose audsrv does support it correctly. I am just not 100% sure about the the backend/Thread on the EE (@Krah 's code), which essentially 'glues' the GUI-Sounds and GUI with the SPU2.

Here is an interesting read about it:
https://forums.pcsx2.net/Thread-blog-SPU2-is-more-than-just-sound


@Krah: 3 voice-channels at once? I think that's neat! ...and the GUI performance isn't affected? I suppose it could get affected, while the reloads of the sounds to the SRAM (are done).

So... They are currently completely loaded into the SRAM? What about streaming the 'frames'/samples ('uncompressed audio') to the SPU2(-SRAM), if the sound-file exceeds a specific size? That way we could keep small sounds in the SRAM, while sounds like BGM are streamed...

For example, you can rather stream-load big files from various devices (even much bigger than all RAM of the PS2 combined)...


On another note: That stream-loading would also enable us to pre-process and use other ([lossy&lossless] compressed, uncompressed, etc.) file-formats (at different Sample-Rates, etc.)! ;) (That would require a decoding-library, which supports those files of course + the connection to Krah's current code.)


So... I suggest 'stream-loading' for files which exceed a certain size (if the buffer is currently [of] the size of the file [dynamically allocated], that could be changed to a smaller [fixed/static] buffer)!



Edit: I like the previous reply from @sp193. All these suggestions are well-founded... (Just a bit too less stuff left for us [others], to find out... Hehe...)


Regarding the divide&conquer-approach for the path: It is code-wise more efficient in regards to redundancy, efficiency and allows for an easier integration of flexible paths! It doesn't need much more to support a variable path, via this approach! :)


Does it also crash, if you define more sounds simply instead of playing them, or only when you are playing them? Then that seems to be related to the possible stack-overflow (SP193 hinted at)... If it also happens, when you declare them, there must be an additional issue/problem (i.e. uncleared buffer or pointer, or the things SP193 already pointed out). [Not sure, if I just accidentally switched the cases... However... Both should be tested...]


Great (to) see you progressing that quickly!:encouragement:


Edit.2: A few other resources...

https://books.google.de/books?id=VbVdDgAAQBAJ&pg=PA467&lpg=PA467&dq=SPU2+RAM+Usage&source=bl&ots=2z9Q1KxTEJ&sig=pGIrXMYwLNdZkudf1qwk9RvqwoA&hl=de&sa=X&ved=2ahUKEwiGlIm8gL3dAhWEJlAKHV4aDhAQ6AEwDnoECAMQAQ#v=onepage&q=SPU2 RAM Usage&f=false

https://documentation.help/FMOD-Ex/movieplayers.htm

Edit.3: Is there anything buffered on the EE, or does it only remote-control the IOP to send the processed data (uncompressed audio) to the SPU2? I think it should not use the EE as a relaying-station, so it communicates 'back and forth' between IOP & EE... I am not sure how to explain it in a better way...

It would decrease performance if something is send from IOP to EE to IOP to SPU2.

The RAM seems to be separated into 2 blocks and the sound-processor consists of 2 cores, which each can process 24 voice-channels.
 
Last edited:
tl:dr? (I edited my previous post multiple times...)

Here are some structural suggestions combined:

  • EE should solely RPC audsrv and the needed libs on the IOP, where to get it's stream from.
  • Processing and data-transfer should be solely done via IOP and SPU2.
  • Big files should be streamed
  • ...and those big files should not be cached on the SRAM, tho'.
  • SP193's idea(s) get an up vote from me...

Is it already done that way? ;)


Edit: Btw.: Does it work on Multi-AV and optical Output? Does it solely work on one, or on both simultaneously?
 
Last edited:
Code:
#define NUM_SFX_FILES 7

static const char *sfx_files[NUM_SFX_FILES] = {
"scrollV.adp",
"scrollH.adp",
"page.adp",
"cancel.adp",
"transition.adp",
"alert.adp",
"confirm.adp",
};

static audsrv_adpcm_t sfx[NUM_SFX_FILES];

//Returns 0 (AUDSRV_ERR_NOERROR) if the sound was loaded successfully.
static int sfxLoad(const char *snd_path, audsrv_adpcm_t *sfx)
{
FILE* adpcm;
void *buffer;
int ret, size;

adpcm = fopen(snd_path, "rb");
if (adpcm == NULL)
{
printf("failed to open adpcm file %s\n", snd_path);
return -ENOENT;
}

fseek(adpcm, 0, SEEK_END);
size = ftell(adpcm);
rewind(adpcm);
That is so much more concise than my code, thanks for explaining the reasons behind your changes it all makes sense and helps me learn. I'm not entirely confident I would have been able to make it that efficient left to my own devices. Only thing i needed to change was the struct from being static as the calls are in a different source.

I don't know why it's not working. Do you know when it hangs?
Earlier stages of testing I had multiple structs not in arrays with lots of dupe code so I was calling audsrv_load_adpcm() for each struct pointer; everything up until that point was executed fine it appeared to only hang if calling audsrv_load_adpcm() more than twice, my recent code changes had seemed to allow it to be called three times before locking up. So when I saw how much better yours was I was hoping for the full loop to complete.

Your code changes work up until the point of calling audsrv_load_adpcm() more than three times. ie if the loop is executed
Code:
i = 0; i < 3; i++
its fine, any higher and it freezes.

I have multiple files of varying sizes, I did some tests.
3 files for a total of 31kb - loads.
4 files for a total of 28kb - freezes.
1 file for a total of 457kb - loads.
1 file for a total of 712kb - freezes.
4 files for a total of 448bytes - freezes.

Does it also crash, if you define more sounds simply instead of playing them, or only when you are playing them
defining, reading even calling malloc() on any number of files is fine. Its not until you try load more than 3 adpcm (or 1 above a certain size limit..biggest one i tried that froze was 712kb) into the SPU2 mem that it locks up.
also BGM/PCM would need to be streamed yes, you are right

Does it work on Multi-AV and optical Output? Does it solely work on one, or on both simultaneously?
no idea, i dont have optical to test
 
  • Like
Reactions: TnA
There was an issue within AUDSRV, which was causing it to use an uninitialized pointer due to it not initializing next to NULL for the 2nd ADPCM sample loaded.
I have also improved on the inter-task protection mechanism, to prevent multiple threads from overwriting each other's return values.
 
I have multiple files of varying sizes, I did some tests.
3 files for a total of 31kb - loads.
4 files for a total of 28kb - freezes.
1 file for a total of 457kb - loads.
1 file for a total of 712kb - freezes.
4 files for a total of 448bytes - freezes.

THX!
Would you mind testing 500KB and 520KB for a single file? ;)
There might be a limit of 512KB-n*Byte (n=some Byte up to a few KB Edit: ...or actually 'number' of Bytes.).

defining, reading even calling malloc() on any number of files is fine. Its not until you try load more than 3 adpcm (or 1 above a certain size limit..biggest one i tried that froze was 712kb) into the SPU2 mem that it locks up.

Alright, THX!
...and it seems @sp193 found it!
Hopefully that explaination/previous comment (from you) did help. ;)

also BGM/PCM would need to be streamed yes, you are right

It could be done for all bigger files, but since (basically) only BGM would be played for a longer period of time, it could be limited to it... However... Since the SRAM is limited and there also seems to be a smaller file-size-limit, a software-limit where it doesn't load it into SRAM, but rather streams it (not restricted to BGM) might be better.

no idea, i dont have optical to test

Well, we will know it atleast once it is implemented in an open build.

There was an issue within AUDSRV, which was causing it to use an uninitialized pointer due to it not initializing next to NULL for the 2nd ADPCM sample loaded.
I have also improved on the inter-task protection mechanism, to prevent multiple threads from overwriting each other's return values.

OM Freakin' G! :lol
You ought to be kidding right? Did Noone ever found that issue, since AUDSRV is available? Did Noone ever tried to mix more than 3 channels together with AUDSRV?

Unbelievable! ...and yet hilarious!

Great that you found and fixed it! THX!
 
Last edited:
You ought to be kidding right? Did Noone ever found that issue, since AUDSRV is available? Did Noone ever tried to mix more than 3 channels together with AUDSRV?
From what I understand audsrv came shortly after the peak of ps2 homebrew dev, most projects used older libs; sjpcm amigamod or hermes audio libs. I couldn't find more than 3 (open) projects using audsrv so I don't think it's been used as much as the other audio libs..Even then it's only the adpcm side that was affected those other projects may have just used the pcm side for streaming, anyway looking forward to updating SDK and testing :)
 
  • Like
Reactions: TnA
From what I understand audsrv came shortly after the peak of ps2 homebrew dev, most projects used older libs; sjpcm amigamod or hermes audio libs. I couldn't find more than 3 (open) projects using audsrv so I don't think it's been used as much as the other audio libs..
I can't quite remember, which App used what kind of lib, but it explains quite a few things, especially since it only affects the ADPCM-side.

Even then it's only the adpcm side that was affected those other projects may have just used the pcm side for streaming,

Yes, that's quite an argument why it possibly was never discovered before ;)

anyway looking forward to updating SDK and testing :)

Great! I am looking forward to a test with a file at ~500-520KB and more than 3 files/voice channels. ^^


I think it would be interesting to understand,
  • why those files can't be bigger than a certain size
  • how much SRAM we can really use
  • if we are using all the SRAM we actually could
  • if we are using the hardware properly (both cores, DMA-Channels, Voice-channels, Mixing, etc.)

Edit: Btw.: Erm... Does it freeze, when no audio-files are placed? If not, that could be an issue on my side.
 
Last edited:
after updating the SDK and recompiling i'm having the same problem, any more than 3 freezes the console.

Edit: Btw.: Erm... Does it freeze, when no audio-files are placed? If not, that could be an issue on my side.
potentially yes, if using an older build or compiling from the audio branch it is possible as i was calling audsrv_quit() if no files found which seemed to crash if calling other RPC functions after init...problem somewhere else in my code possibly, i haven't pushed SP193's sound code to the audio branch yet
 
after updating the SDK and recompiling i'm having the same problem, any more than 3 freezes the console.
Awwwww...:oops::(

potentially yes, if using an older build or compiling from the audio branch it is possible as i was calling audsrv_quit() if no files found which seemed to crash if calling other RPC functions after init...problem somewhere else in my code possibly, i haven't pushed SP193's sound code to the audio branch yet
Yes, I tested the audio-branch, but it happened to me on Test 4 as well. ;)

What about all the those other things I wrote, during this thread?!o_O Things like other file-sizes, proper structure, proper handling, etc.?!
 
@sp193
hmm weird, simply changing the call location for sfxInit() in opl.c
Code:
    // initalise sound
    //sfxInit();

    guiIntroLoop();
    sfxInit();
    guiMainLoop();
it succeeds in loading all 7 files, sorry if i caused any inconvenience with my previous post saying the update to sdk appeared to not fix the issue.

@TnA
sorry i dont have a lot of time to respond right now to everything, got a lot i want to try get done atm, thanks for your patience
 
  • Like
Reactions: TnA
@sp193
hmm weird, simply changing the call location for sfxInit() in opl.c
Code:
    // initalise sound
    //sfxInit();

    guiIntroLoop();
    sfxInit();
    guiMainLoop();
it succeeds in loading all 7 files, sorry if i caused any inconvenience with my previous post saying the update to sdk appeared to not fix the issue.

That's GREAT news!
Do they also work simultaneously (all played at once)?

@TnA
sorry i dont have a lot of time to respond right now to everything, got a lot i want to try get done atm, thanks for your patience

No problem! I didn't intend to bother you, but were just curious about some things (i.e. structure, efficiency, file-size-limit, etc.). ;)
 
@sp193
hmm weird, simply changing the call location for sfxInit() in opl.c
Code:
    // initalise sound
    //sfxInit();

    guiIntroLoop();
    sfxInit();
    guiMainLoop();
it succeeds in loading all 7 files, sorry if i caused any inconvenience with my previous post saying the update to sdk appeared to not fix the issue.

If you didn't try that, then we might not have found out what was being done wrong.

So basically you are trying to initialize the sound resources from the main thread, which is actually supposed to draw the screen while OPL is being initialized in the background.

It's all done within opl.c:
  1. main()
  2. init()
  3. _loadConfig()
  4. applyConfig()
  5. initAllSupport()
  6. initSupport()
initSupport() will begin the initialization of the device with the IO thread (ioman.c). This is done for all devices.

It is not clear what exactly is getting stuck (you need to actually go and debug this if you want to find out), but it is perhaps not really a good thing to do initialization from the main thread outside of the init() function. Not to mention that the device drivers might not even have been loaded yet, or are being loaded.

To do it right, perhaps you can initialize sound support after the configuration is loaded, after this call to _loadConfig:
Code:
    // try to restore config
    _loadConfig();
}

You can do this:
Code:
    // queue deffered init of sound effects, which will take place after the preceding initialization steps within the queue are complete.
    ioPutRequest(IO_CUSTOM_SIMPLEACTION, &deferredSfxInit);

And for the deferredSfxInit() function (which you can put in opl.c too):
Code:
static void deferredSfxInit(void)
{
    int ret;
    ret = sfxInit();
    if (ret >= 0)
        LOG("SfxInit: %d samples loaded.\n", ret);
    else
        LOG("SfxInit: failed to initialize - %d.\n", ret);
}

Remember to put a prototype of the new function with the rest of them, to avoid getting the implicit declaration compiler warning.
Code:
static void deferredSfxInit(void);
 
New clean(er) initial sfx-branch: https://github.com/KrahJohlito/Open-PS2-Loader/tree/sfx


Would you mind adding the basic sound-files? Except for BGM, they probably could be compiled into OPL (as long as they are small).

I mean these...
"scrollV.adp", "scrollH.adp", "page.adp", "cancel.adp", "transition.adp", "alert.adp", "confirm.adp"

As for 'transition-sound'... 'Changing a page in a book', or some other sounds could be quite interesting.
Hopefully these Sounds could be changed via themes later as well! ;)
 
Last edited:
New clean(er) initial sfx-branch: https://github.com/KrahJohlito/Open-PS2-Loader/tree/sfx


Would you mind adding the basic sound-files? Except for BGM, they probably could be compiled into OPL (as long as they are small).

I mean these...
"scrollV.adp", "scrollH.adp", "page.adp", "cancel.adp", "transition.adp", "alert.adp", "confirm.adp"

As for 'transition-sound'... 'Changing a page in a book', or some other sounds could be quite interesting.
Hopefully these Sounds could be changed via themes later as well! ;)

That's the plan, default compiled in sounds but if ppl want their own they can select in the settings menu which device they're stored on. Need to make sure other things are correct first though.

As for the sounds I'm using ATM I cannot distribute them, for sake of ease I'm not using royalty free SFX..I'm just using some I found for testing the code. The first 2 are royalty free but the rest are not, I will need to replace them when it comes time to compile in and/or distribute.
 
Thanks, very helpful :)

If you didn't try that, then we might not have found out what was being done wrong.

So basically you are trying to initialize the sound resources from the main thread, which is actually supposed to draw the screen while OPL is being initialized in the background.

It's all done within opl.c:
  1. main()
  2. init()
  3. _loadConfig()
  4. applyConfig()
  5. initAllSupport()
  6. initSupport()
initSupport() will begin the initialization of the device with the IO thread (ioman.c). This is done for all devices.

It is not clear what exactly is getting stuck (you need to actually go and debug this if you want to find out), but it is perhaps not really a good thing to do initialization from the main thread outside of the init() function. Not to mention that the device drivers might not even have been loaded yet, or are being loaded.

To do it right, perhaps you can initialize sound support after the configuration is loaded, after this call to _loadConfig:
Code:
    // try to restore config
    _loadConfig();
}

You can do this:
Code:
    // queue deffered init of sound effects, which will take place after the preceding initialization steps within the queue are complete.
    ioPutRequest(IO_CUSTOM_SIMPLEACTION, &deferredSfxInit);

And for the deferredSfxInit() function (which you can put in opl.c too):
Code:
static void deferredSfxInit(void)
{
    int ret;
    ret = sfxInit();
    if (ret >= 0)
        LOG("SfxInit: %d samples loaded.\n", ret);
    else
        LOG("SfxInit: failed to initialize - %d.\n", ret);
}

Remember to put a prototype of the new function with the rest of them, to avoid getting the implicit declaration compiler warning.
Code:
static void deferredSfxInit(void);
That seems to have fixed it, console no longer hangs when loading >3 files nor does it hang if 0 files are found on set device. :applause:
Thank you very much.

@TnA
compiled in sounds may be more of an annoyance than i previously realised, currently sound paths/loading of sounds are all set by strings pointing back to char(s) done very efficiently thanks to sp193. compiled in sounds would be extern void so its not so simple to just stick them in as well.
I will have a bit of a play around locally but i think it may not be worth it in the end.
 
  • Like
Reactions: TnA
compiled in sounds may be more of an annoyance than i previously realised, currently sound paths/loading of sounds are all set by strings pointing back to char(s) done very efficiently thanks to sp193. compiled in sounds would be extern void so its not so simple to just stick them in as well.
I will have a bit of a play around locally but i think it may not be worth it in the end.

Code:
extern unsigned char scrollV_adp[];
extern unsigned int size_scrollV_adp;

extern unsigned char scrollH_adp[];
extern unsigned int size_scrollH_adp;

extern unsigned char page_adp[];
extern unsigned int size_page_adp;

extern unsigned char cancel_adp[];
extern unsigned int size_cancel_adp;

extern unsigned char transition_adp[];
extern unsigned int size_transition_adp;

extern unsigned char alert_adp[];
extern unsigned int size_alert_adp;

extern unsigned char confirm_adp[];
extern unsigned int size_confirm_adp;

struct sfxEffect {
    const char *name;
    void *buffer;
    int len;
};

#define NUM_SFX_FILES 7

static struct sfxEffect sfx_files[NUM_SFX_FILES] = {
    {"scrollV.adp"},
    {"scrollH.adp"},
    {"page.adp"},
    {"cancel.adp"},
    {"transition.adp"},
    {"alert.adp"},
    {"confirm.adp"},
};

static audsrv_adpcm_t sfx[NUM_SFX_FILES];

//Returns 0 if the specified file was read. The sfxEffect structure will not be updated unless the file is successfully read.
static int sfxRead(const char *snd_path, struct sfxEffect *sfx)
{
    FILE* adpcm;
    void *buffer;
    int ret, size;

    adpcm = fopen(snd_path, "rb");
    if (adpcm == NULL)
    {
        printf("failed to open adpcm file %s\n", snd_path);
        return -ENOENT;
    }

    fseek(adpcm, 0, SEEK_END);
    size = ftell(adpcm);
    rewind(adpcm);

    buffer = memalign(64, size);
    if (buffer == NULL)
    {
        printf("Failed to allocate memory for SFX\n");
        fclose(adpcm)
        return -ENOMEM;
    }

    ret = fread(buffer, 1, size, adpcm);
    fclose(adpcm);

    if(ret != size)
    {
        printf("Failed to read SFX: %d (expected %d)\n", ret, size);
        free(buffer);
        return -EIO;
    }

    sfx->buffer = buffer;
    sfx->size = size;

    return 0;
}

static void sfxInitDefaults(void)
{
    sfx_files[0].buffer = scrollV_adp;
    sfx_files[0].size = size_scrollV_adp;
    sfx_files[1].buffer = scrollH_adp;
    sfx_files[1].size = size_scrollH_adp;
    sfx_files[2].buffer = page_adp;
    sfx_files[2].size = size_page_adp;
    sfx_files[3].buffer = cancel_adp;
    sfx_files[3].size = size_cancel_adp;
    sfx_files[4].buffer = transition_adp;
    sfx_files[4].size = size_transition_adp;
    sfx_files[5].buffer = alert_adp;
    sfx_files[5].size = size_alert_adp;
    sfx_files[6].buffer = confirm_adp;
    sfx_files[6].size = size_confirm_adp;
}

//Returns 0 (AUDSRV_ERR_NOERROR) if the sound was loaded successfully.
static int sfxLoad(const struct sfxEffect *sfxData, audsrv_adpcm_t *sfx)
{
    int ret;

    ret = audsrv_load_adpcm(sfx, sfxData->buffer, sfxData->size);
    free(sfxData->buffer);
    sfxData->buffer = NULL; //Mark the buffer as freed.

    return ret;
}

static void getAudioDevice(char *device_path)
{
    if ((gAudioPath == 0))
       sprintf(device_path, "mc0:Sound/");
    if ((gAudioPath == 1))
       sprintf(device_path, "pfs0:Sound/");
    if ((gAudioPath == 2))
       sprintf(device_path, "mass0:Sound/");
}

//Returns number of audio files successfully loaded, < 0 if an unrecoverable error occurred.
int sfxInit(void)
{
    char snd_path[256];
    char device_path[128];
    int ret, i, loaded;

    ret = audsrv_init();
    if (ret != 0)
    {
        printf("sfx: failed to initialize audsrv\n");
        printf("audsrv returned error string: %s\n", audsrv_get_error_string());
        return -1;
    }

    audsrv_adpcm_init();
    audsrv_set_volume(MAX_VOLUME);

    sfxInitDefaults();
    getAudioDevice(device_path);

    loaded = 0;
    for (i = 0; i < NUM_SFX_FILES; i++)
    {
        //Perhaps you can implement & check here, whether an external sound is set.
        sprintf(snd_path, "%s/%s", device_path, sfx_files[i].name);
        ret = sfxRead(snd_path, &sfx_files[i]);
        if (ret != 0)
        {
           LOG("sfx: %s could not be loaded. Using default sound.\n", snd_path, ret);
        }

        ret = sfxLoad(&sfx_files[i], &sfx[i])
        if (ret == 0)
        {
            loaded++;
        }
        else
        {
           printf("sfx: failed to load %s, error %d\n", snd_path, ret);
        }
    }

    return loaded;
}

Unlike the previous version, I use memalign() instead of malloc(). It may be just a better practice to get a buffer that is aligned to a 64-byte boundary, since the data cache lines over the buffer will be flushed.
While it will still work if you used a buffer that is aligned to a 16-byte boundary (as in the case of malloc), there can be weird glitches if somebody was to use the uncached segment right beside the buffer (which will dirty the cache lines).

I also realized that there is a need to close the file and/or free the buffer, if the file could not be read. The previous version did not check the return value of fread().

There might be some day to list all the sound files in a static array, but that would involve pointers to their size variable (unsigned int *), which I think is a very ugly thing to look at...
I think this is simple enough.
 
Last edited:
Code:
extern unsigned char scrollV_adp[];
extern unsigned int size_scrollV_adp;

extern unsigned char scrollH_adp[];
extern unsigned int size_scrollH_adp;

extern unsigned char page_adp[];
extern unsigned int size_page_adp;

extern unsigned char cancel_adp[];
extern unsigned int size_cancel_adp;

extern unsigned char transition_adp[];
extern unsigned int size_transition_adp;

extern unsigned char alert_adp[];
extern unsigned int size_alert_adp;

extern unsigned char confirm_adp[];
extern unsigned int size_confirm_adp;

struct sfxEffect {
    const char *name;
    void *buffer;
    int len;
};

#define NUM_SFX_FILES 7

static struct sfxEffect sfx_files[NUM_SFX_FILES] = {
    {"scrollV.adp"},
    {"scrollH.adp"},
    {"page.adp"},
    {"cancel.adp"},
    {"transition.adp"},
    {"alert.adp"},
    {"confirm.adp"},
};

static audsrv_adpcm_t sfx[NUM_SFX_FILES];

//Returns 0 if the specified file was read. The sfxEffect structure will not be updated unless the file is successfully read.
static int sfxRead(const char *snd_path, struct sfxEffect *sfx)
{
    FILE* adpcm;
    void *buffer;
    int ret, size;

    adpcm = fopen(snd_path, "rb");
    if (adpcm == NULL)
    {
        printf("failed to open adpcm file %s\n", snd_path);
        return -ENOENT;
    }

    fseek(adpcm, 0, SEEK_END);
    size = ftell(adpcm);
    rewind(adpcm);

    buffer = memalign(64, size);
    if (buffer == NULL)
    {
        printf("Failed to allocate memory for SFX\n");
        fclose(adpcm)
        return -ENOMEM;
    }

    ret = fread(buffer, 1, size, adpcm);
    fclose(adpcm);

    if(ret != size)
    {
        printf("Failed to read SFX: %d (expected %d)\n", ret, size);
        free(buffer);
        return -EIO;
    }

    sfx->buffer = buffer;
    sfx->size = size;

    return 0;
}

static void sfxInitDefaults(void)
{
    sfx_files[0].buffer = scrollV_adp;
    sfx_files[0].size = size_scrollV_adp;
    sfx_files[1].buffer = scrollH_adp;
    sfx_files[1].size = size_scrollH_adp;
    sfx_files[2].buffer = page_adp;
    sfx_files[2].size = size_page_adp;
    sfx_files[3].buffer = cancel_adp;
    sfx_files[3].size = size_cancel_adp;
    sfx_files[4].buffer = transition_adp;
    sfx_files[4].size = size_transition_adp;
    sfx_files[5].buffer = alert_adp;
    sfx_files[5].size = size_alert_adp;
    sfx_files[6].buffer = confirm_adp;
    sfx_files[6].size = size_confirm_adp;
}

//Returns 0 (AUDSRV_ERR_NOERROR) if the sound was loaded successfully.
static int sfxLoad(const struct sfxEffect *sfxData, audsrv_adpcm_t *sfx)
{
    int ret;

    ret = audsrv_load_adpcm(sfx, sfxData->buffer, sfxData->size);
    free(sfxData->buffer);
    sfxData->buffer = NULL; //Mark the buffer as freed.

    return ret;
}

static void getAudioDevice(char *device_path)
{
    if ((gAudioPath == 0))
       sprintf(device_path, "mc0:Sound/");
    if ((gAudioPath == 1))
       sprintf(device_path, "pfs0:Sound/");
    if ((gAudioPath == 2))
       sprintf(device_path, "mass0:Sound/");
}

//Returns number of audio files successfully loaded, < 0 if an unrecoverable error occurred.
int sfxInit(void)
{
    char snd_path[256];
    char device_path[128];
    int ret, i, loaded;

    ret = audsrv_init();
    if (ret != 0)
    {
        printf("sfx: failed to initialize audsrv\n");
        printf("audsrv returned error string: %s\n", audsrv_get_error_string());
        return -1;
    }

    audsrv_adpcm_init();
    audsrv_set_volume(MAX_VOLUME);

    sfxInitDefaults();
    getAudioDevice(device_path);

    loaded = 0;
    for (i = 0; i < NUM_SFX_FILES; i++)
    {
        //Perhaps you can implement & check here, whether an external sound is set.
        sprintf(snd_path, "%s/%s", device_path, sfx_files[i].name);
        ret = sfxRead(snd_path, &sfx_files[i]);
        if (ret != 0)
        {
           LOG("sfx: %s could not be loaded. Using default sound.\n", snd_path, ret);
        }

        ret = sfxLoad(&sfx_files[i], &sfx[i])
        if (ret == 0)
        {
            loaded++;
        }
        else
        {
           printf("sfx: failed to load %s, error %d\n", snd_path, ret);
        }
    }

    return loaded;
}

Unlike the previous version, I use memalign() instead of malloc(). It may be just a better practice to get a buffer that is aligned to a 64-byte boundary, since the data cache lines over the buffer will be flushed.
While it will still work if you used a buffer that is aligned to a 16-byte boundary (as in the case of malloc), there can be weird glitches if somebody was to use the uncached segment right beside the buffer (which will dirty the cache lines).

I also realized that there is a need to close the file and/or free the buffer, if the file could not be read. The previous version did not check the return value of fread().

There might be some day to list all the sound files in a static array, but that would involve pointers to their size variable (unsigned int *), which I think is a very ugly thing to look at...
I think this is simple enough.

Wow, thanks :biggrin2:
I guess I could just add another value to enum AudioPath and use
Code:
if ((gAudioPath == 0)) {
sfxInitDefaults();
}
else {
getAudioDevice(device_path);
}
or maybe
Code:
static void sfxPrep(char *device_path)
{
    if ((gAudioPath == 0))
         sfxInitDefaults();
    if ((gAudioPath == 1))
         sprintf(device_path, "mc0:Sound/");
    if ((gAudioPath == 2))
         sprintf(device_path, "pfs0:Sound/");
    if ((gAudioPath == 3))
         sprintf(device_path, "mass0:Sound/");
}

//Returns number of audio files successfully loaded, < 0 if an unrecoverable error occurred.
int sfxInit(void)
{
    char snd_path[256];
    char device_path[128];
    int ret, i, loaded;

    ret = audsrv_init();
    if (ret != 0)
    {
        printf("sfx: failed to initialize audsrv\n");
        printf("audsrv returned error string: %s\n", audsrv_get_error_string());
        return -1;
    }

    audsrv_adpcm_init();
    audsrv_set_volume(MAX_VOLUME);

    sfxPrep(device_path);

    loaded = 0;
    for (i = 0; i < NUM_SFX_FILES; i++)
 
I think it is good to initialize that array with the defaults, regardless of what the user really wants to use. Unless you intend no sound to be played when a sound effect cannot be loaded, what if some (but not all) files cannot be loaded?
 
Status
Not open for further replies.

Similar threads

Back
Top