Working with Sound Output using the AICA SPU

If you have any questions on programming, this is the place to ask them, whether you're a newbie or an experienced programmer. Discussion on programming in general is also welcome. We will help you with programming homework, but we will not do your work for you! Any porting requests must be made in Developmental Ideas.
Post Reply
User avatar
PH3NOM
DC Developer
DC Developer
Posts: 576
https://www.artistsworkshop.eu/meble-kuchenne-na-wymiar-warszawa-gdzie-zamowic/
Joined: Fri Jun 18, 2010 9:29 pm
Has thanked: 0
Been thanked: 5 times

Working with Sound Output using the AICA SPU

Post by PH3NOM »

Whats up guys?

Out of personal interest, I have been learning about multi-media applications, and how to use them on Dreamcast.

I have ported liba52 and libfaad to DC, and i would like to make a working decoder. Their sample decoder apps are working on DC, I just need to make a audio output driver that works for Dreamcast.

At first I was using SDL_Mixer to play wave streams but that is not the best way to go on DC, as I want things to be as optimized for the hardware as possible.

So I am turning to internal KOS sound functions, which have little documentation or examples to study from.

Surprisingly, the function snd_sfx_load(const char *fn) did not work for me at first; it would return a negative value ( or incredibly large ) for the file size and fail on malloc().
I had to obtain the file size manually to get things working.

But it seems there is a limitation in the snd_sfx_load() function I am not aware of; how many bytes can this function handle? It seems to only play the first ~1sec of the audio file then stop...

Am I the only one having this problem? Or is there another function besides snd_sfx_load() that I should be working with?

Any comments will be appreciated!
Thanks in advance.
User avatar
BlueCrab
The Crabby Overlord
The Crabby Overlord
Posts: 5652
Joined: Mon May 27, 2002 11:31 am
Location: Sailing the Skies of Arcadia
Has thanked: 9 times
Been thanked: 69 times
Contact:

Re: Working with Sound Output using the AICA SPU

Post by BlueCrab »

If you want to play WAV files, use snd_sfx_load. Otherwise, you'll want to look at the snd_stream stuff instead. You might want to take a look at the Ogg Vorbis playing libraries to see how it works.
Dreamcast
DCEmu Freak
DCEmu Freak
Posts: 81
Joined: Fri Jul 27, 2007 2:23 am
Has thanked: 2 times
Been thanked: 4 times

Re: Working with Sound Output using the AICA SPU

Post by Dreamcast »

I'm having the same exact problem with the audio only playing the first second or two and then stopping. It happens for MP3, OGG and WAV. I've tried changing the bitrate and even tried mono / stereo, but it does the same thing. I've also had the audio speed up / change pitch.
User avatar
BlueCrab
The Crabby Overlord
The Crabby Overlord
Posts: 5652
Joined: Mon May 27, 2002 11:31 am
Location: Sailing the Skies of Arcadia
Has thanked: 9 times
Been thanked: 69 times
Contact:

Re: Working with Sound Output using the AICA SPU

Post by BlueCrab »

That's certainly strange... Might be something wrong with your toolchain or something, maybe?
BlackAura
DC Developer
DC Developer
Posts: 9951
Joined: Sun Dec 30, 2001 9:02 am
Has thanked: 0
Been thanked: 1 time

Re: Working with Sound Output using the AICA SPU

Post by BlackAura »

PH3NOM - As far as I remember, the snd_sfx stuff in KOS is pretty basic.

The .wav loader is primitive, and it's quite possible that it'd break on a perfectly valid .wav file. Specifically, it doesn't use a proper RIFF parser, so it only works if the chunks in the file happen to be in the right place. It also doesn't do much in the way of error checking, so it'll probably break unpredictably in that case.

That could have been your problem.

Also, it can only read .wav files. Obviously.

As for the cut-off sounds... If I remember correctly, the size of a sound sample is limited to 64k samples, in hardware. At 44KHz, that'd be around one and a half seconds. I can't see anything in the snd_sfx system that'd be able to compensate for that (buffer chaining, or whatever), so that might be your problem.

Dreamcast â„¢ - Were you using the stream stuff (snd_stream_*), or the sound effects stuff (snd_sfx_*)? The snd_stream stuff doesn't have any length limit, since it's just playing samples as they're generated by the main CPU.

You do have to keep feeding the buffers though, probably by using snd_stream_set_callback to set up a callback to generate some more samples.
User avatar
BlueCrab
The Crabby Overlord
The Crabby Overlord
Posts: 5652
Joined: Mon May 27, 2002 11:31 am
Location: Sailing the Skies of Arcadia
Has thanked: 9 times
Been thanked: 69 times
Contact:

Re: Working with Sound Output using the AICA SPU

Post by BlueCrab »

In addition to setting up the callback, note that the snd_stream stuff doesn't poll automatically to call that callback. You need to periodically call snd_stream_poll on the stream otherwise you'll never have any more data fed in. Its probably easiest to set up a thread to do this or something (that's how libtremor and liboggvorbisplay do it).
Dreamcast
DCEmu Freak
DCEmu Freak
Posts: 81
Joined: Fri Jul 27, 2007 2:23 am
Has thanked: 2 times
Been thanked: 4 times

Re: Working with Sound Output using the AICA SPU

Post by Dreamcast »

This is the function I created for playback:

Code: Select all

int hWave;
void loadPlaySound(uch *path)
{	hWave = snd_sfx_load(path);
	snd_sfx_play(hWave, 0xff, 128);
	snd_sfx_unload(hWave);
}
Each sound is played only after a button is pressed on the controller (there's a catch to only send on a button down event which resets on a button up event, so sending too many requests isn't the problem).
User avatar
BlueCrab
The Crabby Overlord
The Crabby Overlord
Posts: 5652
Joined: Mon May 27, 2002 11:31 am
Location: Sailing the Skies of Arcadia
Has thanked: 9 times
Been thanked: 69 times
Contact:

Re: Working with Sound Output using the AICA SPU

Post by BlueCrab »

You're immediately unloading the sound right after calling play. That's not a very good idea, since if you call that twice in quick succession, you're likely to run into problems where you're overwriting the buffer that's being played. You shouldn't call snd_sfx_unload() until the sound is finished playing.

Also, as BA already said, with the snd_sfx_* stuff you're limited in the length of what you can play.
User avatar
Bouz
DCEmu Junior
DCEmu Junior
Posts: 46
Joined: Mon May 10, 2010 3:42 pm
Location: St. Bauzille de Putois (France)
Has thanked: 0
Been thanked: 0

Re: Working with Sound Output using the AICA SPU

Post by Bouz »

Hi,
I have been working on an alternative AICA driver for the DC, that allows uploading S3M and WAV files in the AICA memory (2MB) and play them in parallel (multiple S3M files can be played at the same time with crescendo / descresendo, WAV files over S3M files, ...).
I have published a demo on www.dc-france.com before the site died, but nothing since.
I really plan to work on that project in the coming weeks and make an updated version available if someone is still interested in that work.
User avatar
PH3NOM
DC Developer
DC Developer
Posts: 576
Joined: Fri Jun 18, 2010 9:29 pm
Has thanked: 0
Been thanked: 5 times

Re: Working with Sound Output using the AICA SPU

Post by PH3NOM »

@BlackAura and BlueCrab -

Thanks for your responses! You guys are legendary.

Thats confirmed the limit of the wave file size that can be played with snd_sfx_load is 65536 bytes.
Why cant we use the full 2mb of SRAM?

I had a hdd fail on me, and I lost my K:OS development environment, including almost all of the sources I was working on.

I have set up a new install of K:OS and will have to start over, but it wont take long the second time around. I still plan to make something of this.

@Bouz -
Yeah that driver sounds very interesting. You should definitely finish the updated version!
User avatar
BlueCrab
The Crabby Overlord
The Crabby Overlord
Posts: 5652
Joined: Mon May 27, 2002 11:31 am
Location: Sailing the Skies of Arcadia
Has thanked: 9 times
Been thanked: 69 times
Contact:

Re: Working with Sound Output using the AICA SPU

Post by BlueCrab »

PH3NOM wrote:Thats confirmed the limit of the wave file size that can be played with snd_sfx_load is 65536 bytes.
Why cant we use the full 2mb of SRAM?
Well, you wouldn't be able to use the full 2MB, even if it weren't for other issues (since the program that the ARM runs to communicate with the SuperH does take up a bit of space). The real reason though is that the snd_sfx stuff isn't meant to be playing anything other than simple sound effects. As BlackAura said, the snd_sfx stuff doesn't do any buffer chaining or anything like that, so you're limited to what you can put in the registers to play directly. In this case, the registers in the AICA simply can't handle a sound that has more than 65534 samples (the loop start and loop end address registers are 16-bit registers -- those two together determine the length of the sound played when not looping the sound, also IIRC, the loop end address register doesn't allow a value of 0xFFFF, thus the limit of 65534 not 65535).

Also, you're not limited to 65534 bytes, but 65534 samples. The difference is quite important, especially if you're doing ADPCM or 16-bit samples.
User avatar
PH3NOM
DC Developer
DC Developer
Posts: 576
Joined: Fri Jun 18, 2010 9:29 pm
Has thanked: 0
Been thanked: 5 times

Re: Working with Sound Output using the AICA SPU

Post by PH3NOM »

I thought this might be useful for some...
I have written an application to stream a wave file using the AICA SPU.

Get the full source here.
http://www.megaupload.com/?d=B4TX72MV

Code: Select all

/*
** SPU_WAVE (C) PH3NOM 2011
** Based on modplug_test
** And the wave parser from snd_sfgmgr
*/

#include <kos.h>
#include "spu_wave.h"

uint32 fd;
uint16 sound_buffer[65536];
snd_stream_hnd_t shnd;
char filename[128];

int wave_stop() {
    spu_status = WAVE_STATUS_DONE;
    while( spu_status != WAVE_STATUS_NULL )
        thd_pass();
    printf("Successfully exited SPU_WAVE_STREAM\n");
    
    return spu_status;
}

int wave_restart() {
    
    if( spu_status == WAVE_STATUS_STREAMING ) {
       spu_status = WAVE_STATUS_PAUSING;
    
       while( spu_status != WAVE_STATUS_PAUSED )
          thd_pass();
            
       fs_seek(fd, 0x32, SEEK_SET);

       spu_status = WAVE_STATUS_STREAMING;
    }
    
    else
       printf("SPU WAVE: Cant Reset if not already streaming\n");
    
    return spu_status;    
}

void *wave_callback(snd_stream_hnd_t hnd, int len, int * actual)
{
	if (fs_read(fd,sound_buffer,len)!= len )
		spu_status = WAVE_STATUS_DONE;

	*actual = len;
	
	return sound_buffer;
}

void wave_thread ( ) {

	uint32	len, hz;
	uint16	chn, bitsize, fmt;

    printf("spu_wave_stream: checking status...\n");
    while( spu_status != WAVE_STATUS_NULL )
          thd_pass(); 

    printf("spu_wave_stream: beginning\n");

    while( spu_status != WAVE_STATUS_ERROR && spu_status != WAVE_STATUS_DONE ) {  
  
    switch( spu_status ) {
    
     case WAVE_STATUS_NULL:      
	   snd_stream_init();
	   fd=fs_open(filename, O_RDONLY);
	   if (!fd) {
            dbglog(DBG_WARNING, "spu_wave_stream:: file not opened\n");    
	    	spu_status = WAVE_STATUS_ERROR;
	    	break;
	    }
        else
            spu_status = WAVE_STATUS_INITIALIZING;
       
     case WAVE_STATUS_INITIALIZING:     
       /* Check file magic */
	   hz = 0;
	   fs_seek(fd, 8, SEEK_SET);
	   fs_read(fd, &hz, 4);
	   if (strncmp((char*)&hz, "WAVE", 4)) {
		  dbglog(DBG_WARNING, "spu_wave_stream:: file is not RIFF WAVE\n");
		  spu_status = WAVE_STATUS_ERROR;
		  break;
	   }
	
	   /* Read WAV header info */
	   fs_seek(fd, 0x14, SEEK_SET);
	   fs_read(fd, &fmt, 2);
	   fs_read(fd, &chn, 2);
	   fs_read(fd, &hz, 4);
	   fs_seek(fd, 0x22, SEEK_SET);
	   fs_read(fd, &bitsize, 2);
	
	   /* Read WAV data */
	   fs_seek(fd, 0x32, SEEK_SET);
       len = fs_total(fd)-0x32;
       
	   int o;
       o = 96*640 + 20;
       char msg1[4][128];
#ifdef VERBOSE
	   sprintf( msg1[0], "Channel Config: %s", chn==1 ? "mono" : "stereo" );     
	   sprintf( msg1[1], "Frequency: %dHZ", hz );
       sprintf( msg1[2], "Bit-Depth: %d bits/sample", bitsize ); 
    	
       bfont_draw_str(vram_s + o, 640, 1, "WAVE File Info:"); o += 640*24;	
	   bfont_draw_str(vram_s + o, 640, 1, msg1[0]); o += 640*24;
	   bfont_draw_str(vram_s + o, 640, 1, msg1[1]); o += 640*24;
	   bfont_draw_str(vram_s + o, 640, 1, msg1[2]); o += 640*24;
#endif
    	    	
	   if (fs_read(fd,sound_buffer,65536)!=65536 ) {
	   	  spu_status = WAVE_STATUS_ERROR;
	   	  break;
	   }
	
	   shnd = snd_stream_alloc(wave_callback, SND_STREAM_BUFFER_MAX);
	   
       if( spu_status != WAVE_STATUS_ERROR )
          spu_status = WAVE_STATUS_READY;
          
     case WAVE_STATUS_READY:      
       snd_stream_start(shnd, hz, chn-1);
       spu_status = WAVE_STATUS_STREAMING;
       
     case WAVE_STATUS_STREAMING:
        while( spu_status != WAVE_STATUS_DONE ) {
           if( spu_status == WAVE_STATUS_PAUSING ) {
              spu_status = WAVE_STATUS_PAUSED;
              
              while( spu_status != WAVE_STATUS_STREAMING )
                  thd_pass();
           }
           snd_stream_poll(shnd);
		   thd_sleep(50);
        }
     }
    } 
   
    printf("spu_wave_stream: finished\n");

	snd_stream_destroy(shnd);
	snd_stream_shutdown();
       
    fs_close(fd);
    sq_clr( sound_buffer, 65536 );

    spu_status = WAVE_STATUS_NULL;

}

int wave_stream( char * file_name ) {
    sprintf( filename, "%s", file_name );
    kthread_t * wave_thd;
    wave_thd = thd_create( wave_thread, NULL );
    return spu_status;
}
Any feedback appreciated!
These users thanked the author PH3NOM for the post:
Christuserloeser
User avatar
lerabot
Insane DCEmu
Insane DCEmu
Posts: 134
Joined: Sun Nov 01, 2015 8:25 pm
Has thanked: 2 times
Been thanked: 19 times

Re: Working with Sound Output using the AICA SPU

Post by lerabot »

Sorry for the huge necrobump.

I've been trying to get proper .wav file streaming on DC since OGG decoding is quite heavy on the CPU and, for some reason, the .mp3 lib is stuterring on my end.

If anyone has PH3NOM's source I'd love to use it.

trying my luck here...
User avatar
BB Hood
DC Developer
DC Developer
Posts: 189
Joined: Fri Mar 30, 2007 12:09 am
Has thanked: 41 times
Been thanked: 10 times

Re: Working with Sound Output using the AICA SPU

Post by BB Hood »

Here is my libwav: https://github.com/andressbarajas/libwav
Here is an example of it in use: https://github.com/andressbarajas/dream ... eaming/WAV
And the audio page that links to it: https://dreamcast.wiki/Streaming_audio
User avatar
PH3NOM
DC Developer
DC Developer
Posts: 576
Joined: Fri Jun 18, 2010 9:29 pm
Has thanked: 0
Been thanked: 5 times

Re: Working with Sound Output using the AICA SPU

Post by PH3NOM »

lerabot wrote: Thu Aug 06, 2020 10:15 am Sorry for the huge necrobump.

I've been trying to get proper .wav file streaming on DC since OGG decoding is quite heavy on the CPU and, for some reason, the .mp3 lib is stuterring on my end.

If anyone has PH3NOM's source I'd love to use it.

trying my luck here...
Hi there lerabot. I believe this is actually included in my project which can be found here: https://github.com/PH3NOM-PRO/in-the-li ... c/spu_wave. That said, I did not ending up using this method for the project, instead opting to use CDDA as that should completely bypass CPU, using DMA from the SPU to stream from CD directly. But if you really are interested, I have written a decoder for just about any audio codec you would likely want to use: https://github.com/PH3NOM-PRO/dreamcast-media-center
BB Hood wrote: Fri Aug 14, 2020 11:58 pm Here is my libwav: https://github.com/andressbarajas/libwav
Here is an example of it in use: https://github.com/andressbarajas/dream ... eaming/WAV
And the audio page that links to it: https://dreamcast.wiki/Streaming_audio
Hmm, I see you have copied some of my code directly without any acknowledgement... :? thanks for the credit mate. https://github.com/PH3NOM-PRO/dreamcast ... c/snddrv.h
User avatar
BB Hood
DC Developer
DC Developer
Posts: 189
Joined: Fri Mar 30, 2007 12:09 am
Has thanked: 41 times
Been thanked: 10 times

Re: Working with Sound Output using the AICA SPU

Post by BB Hood »

PH3NOM wrote: Sun Aug 23, 2020 2:06 pm
BB Hood wrote: Fri Aug 14, 2020 11:58 pm Here is my libwav: https://github.com/andressbarajas/libwav
Here is an example of it in use: https://github.com/andressbarajas/dream ... eaming/WAV
And the audio page that links to it: https://dreamcast.wiki/Streaming_audio
Hmm, I see you have copied some of my code directly without any acknowledgement... :? thanks for the credit mate. https://github.com/PH3NOM-PRO/dreamcast ... c/snddrv.h
No ill will intended. I start with some source of yours as a base and then reworked it to take the approach that libmp3 and vorbisdisplay did. I see that I still kept CONST names and some variable names the same but I believed it was different enough that credit wasn't needed. I guess I was wrong. Where would you like credit? In the README? Source? Both?
Post Reply