GP32 Playing Sound


pea

developer
Joined
Oct 3, 2004
Messages
1,089
Age
46
Location
New Zealand
Website
www.projectitis.com
Hi all,

Another hurdle on the way to getting libmikmod to work :) The following functions available in the official SDK have no equivalents using Mr. Mirkos SDK. I am wondering if anybody has written equivalents for their own software, and would be willing to share :)

GpPcmInit
GpPcmPlay
GpPcmLock
GpPcmStop

Any ideas?
 
ahh. yes. I see them now. Thanks Aquafish. Although, now looking back at the code, I see I don't know enough about it to attempt to convert to Mr.Mirkos SDK.

Is there anyone who can help me convert the following functions to remove dependence on official SDK?

- GP32_Init
- GP32_Exit
- GP32_PlayStop
- GP32_Stop
- GP32_Restart
- GP32_Update (called continuously on an interrupt)

Code:
#define NUMBUFFERS	6    /* number of buffers */
#define BUFFERSIZE	120    /* buffer size in milliseconds */

//HWAVEOUT	hwaveout;
//WAVEHDR  header[NUMBUFFERS];
//LPSTR  buffer[NUMBUFFERS];  /* pointers to buffers */
//WORD  buffersout;    /* number of buffers playing/about to be played */
//WORD  nextbuffer;    /* next buffer to be mixed */
//ULONG  buffersize;    /* buffer size in bytes */

unsigned short *buffer=NULL;
unsigned short *buffer_end=NULL;

int *play_pos;
int *play_chan;
int buffer_size;
int writting_buffer=0;

#define SAMPLERATE 44100
#define BUFFER1_SIZE ((SAMPLERATE*sizeof(unsigned short)*2*BUFFERSIZE)/1000)

static BOOL GP32_Init(void){
	md_mode|=DMODE_SOFT_MUSIC|DMODE_SOFT_SNDFX;
	GpPcmInit ( PCM_S44, PCM_16BIT );
	
	buffer_size=BUFFER1_SIZE*NUMBUFFERS;
	buffer= malloc(buffer_size);
	memset(buffer,0,buffer_size);
	
	if (!buffer) {
  	_mm_errno = MMERR_OUT_OF_MEMORY;
  	return 0;
	
	}
	
	GpPcmPlay((unsigned short *)buffer,buffer_size,1);
  GpPcmLock((unsigned short *)buffer,(int *)&play_chan,(unsigned int *)&play_pos);
	
	writting_buffer=0;
	
	return VC_Init();

}

void GP32_Stop(void) {
	GpPcmStop();
}

void GP32_Restart(void) {
	GpPcmPlay((unsigned short *)buffer,buffer_size,1);
	GpPcmLock((unsigned short *)buffer,(int *)&play_chan,(unsigned int *)&play_pos);
}

static void GP32_Exit(void){
	int n;
	GpPcmStop();
	free(buffer);
	VC_Exit();
}

int last_data;
int next_data;

static void GP32_Update(void){
	ULONG done;
	int playing_buffer;
	char *writeTo;
	unsigned short *bTO;
	int samples;
	int top_buffer;
	
	playing_buffer=(*play_pos-(int)buffer)/BUFFER1_SIZE;

	while(writting_buffer!=playing_buffer) {
  writeTo=(char *)(((unsigned int)buffer)+writting_buffer*BUFFER1_SIZE);
  bTO=writeTo;
  done=VC_WriteBytes(writeTo,BUFFER1_SIZE);  
  
  for (samples=0;samples<(BUFFER1_SIZE/2);samples++) {
  	
  	//next_data=*bTO;    
  	*bTO=*bTO+0x8000; // to unsigned... sdk..
  	bTO++;
  	//last_data=next_data;
  }

  if(!done) break;	
  writting_buffer=(writting_buffer+1)%NUMBUFFERS;
	}
}

static void GP32_PlayStop(void){
	//GpPcmStop();
	memset(buffer,0,buffer_size);
	VC_PlayStop();
}
 
Its something like this...

GpPcmInit(whatever); = gpstartSoundmixer(n) [0 is normal mode, works for all clockspeeds. 1 is fast mode and only works on some clockspeeds]
GpPcmStop(); = gp=PcmStop(); or gp_stopSoundmixer(); [if your at the end as in the GP32_Exit subroutine]
VC_Exit(); = gp_Reset();


So...
Heres Gp32_Exit:
Code:
static void GP32_Exit(void){
int n;
gp_stopSoundmixer(); //Stop the sound
free(buffer); // Free the buffer
gp_Reset(); // Reset the GP32
}

Heres some psuedo-code:

GP32_PlayStop = Stop playing the module
GP32_Restart = Restart the module
GP32_Update = Update the soundbuffer with the newly generated sound

Most of the other stuff is already in the init1330.c
 
pea posted on Mar 2 2005 at 08:49 AM said:
Hi all,

Another hurdle on the way to getting libmikmod to work :) The following functions available in the official SDK have no equivalents using Mr. Mirkos SDK. I am wondering if anybody has written equivalents for their own software, and would be willing to share :)

GpPcmInit
GpPcmPlay
GpPcmLock
GpPcmStop

Any ideas?

I dont know what the GpPcm... functions do exactly, but you can use the nice ringbuffer routines in my sdk.

void gp_initSound( int freq, int bit, int ringsize);
To init the lowlevel soundsystem, and create a ringbuffer.

void gp_addRingsegment( u16 *add_buffer );
To add one segment to the ringbuffer.

void gp_clearRingbuffer();
Clear ringbuffer, but still playing



Okay, how to setup a ringbuffer, easy going ....

void gp_initSound( 44100, 16, 1024*8);
// Will init the soundsystem in gp32 with 44100khz and 16Bit stereo Playback
// The Ringbuffer is 8kb in lenght.
// There are 2 Segments in the Ringbuffer, with Ringbuffer/2 lenght.
// This means now, one Segment is 4kb now.

while (1) {
char sample_buffer[1024*4]; // one segment is 4kb , remember ?
do_some_sound_calculating_here(char *sample_buffer);
// sample_buffer is now filled with sound :)

void gp_addRingsegment( (u16*)sample_buffer );
//add the segment to the ringbuffer
}

easy, or ?
 
Last edited by a moderator:
Cool, thanks guys.

Mr. Mirko, I'll try to implement the ring buffer... but how to actually play the sound?
And also, does each segment added to the ring buffer always need to be BUFFERSIZE/2 ?

The problem is that each time 'update' is called, there may be an arbitrary number of bytes available that may be more or less than BUFFERSIZE/2.

EDIT: And another question:

How do I exit (stop sound all together) without causing a memory leak due to allocated buffer? There is no gp_killSound function by the looks of it :)
 
A little off note, what's a ringbuffer? o_O

Does it have anything to do with ring modulation, or is it called a ring buffer because it goes around in a circle?
 
eeermmm.... don't know. I assumed that it goes around in a circle. From my understanding (from looking at the code):

Ring buffer is in two sections. Music starts streaming from start of buffer and moves along. If the playhead (sorry, flash terminology, its what I know :) - for me, playhead = current position being read from buffer) gets to the end, it starts again from the start.

If you update the buffer (with one section worth of data) it fills the upper section if the playhead is in the lower section, and then waits for the playhead to get to the upper section. If the playhead is in the lower section, it updates the upper section, and then waits for the playhead to get there.

If this is correct, then there seems to be a lot of 'waiting' :( Maybe a longer buffer is the key (more sections) and no waiting unless the buffer is full? i.e. With two sections you have to wait because you might overwrite the current section, but with, say, five sections, you can update 4 sections withouit waiting at all, but you need a bigger buffer.

EDIT: hmm. looking back at the libmikmod code, maybe this is what it's doing :) ... with 6 sections...
 
pea posted on Mar 3 2005 at 11:12 PM said:
eeermmm.... don't know. I assumed that it goes around in a circle. From my understanding (from looking at the code):

Ring buffer is in two sections. Music starts streaming from start of buffer and moves along. If the playhead (sorry, flash terminology, its what I know :) - for me, playhead = current position being read from buffer) gets to the end, it starts again from the start.

If you update the buffer (with one section worth of data) it fills the upper section if the playhead is in the lower section, and then waits for the playhead to get to the upper section. If the playhead is in the lower section, it updates the upper section, and then waits for the playhead to get there.

If this is correct, then there seems to be a lot of 'waiting' :( Maybe a longer buffer is the key (more sections) and no waiting unless the buffer is full? i.e. With two sections you have to wait because you might overwrite the current section, but with, say, five sections, you can update 4 sections withouit waiting at all, but you need a bigger buffer.

EDIT: hmm. looking back at the libmikmod code, maybe this is what it's doing :) ... with 6 sections...

A ring buffer with 2 sections is enought. Look at my modplayer irq stuff, it uses the ringbuffer, and no waiting at all. If you do this ringbuffer stuff inteligent there is no waiting, the key is, that you only render the bytes you need, until the buffer is played. So if you call the ringbuffer 50 times a second, you only need sound for 1/50 second each time. And voila, no waiting. A very clever solution is to using a irq ...

Using more then 2 buffers will only delay the problem with the waiting. But if it reaches the last buffer, it is waiting.

To Drag:
Yes a ringbuffer is a sample, playing in a loop. If the sample ends, it will start again. automaticly. But the differents between a normal sample playback, is that the sample is split into 2 (or more) parts.

To Blah:
How mutch would you bet on it ?
The L3 routines are the same, yes, but the other parts are rewritten from scratch.
Btw i was using the L3 stuff from spivvys sound examples. And not hitmans code.
Next time look a little closer to the code.
 
Last edited by a moderator:
the key is, that you only render the bytes you need, until the buffer is played. So if you call the ringbuffer 50 times a second, you only need sound for 1/50 second each time

Thanks Mr.Mirko - I figured that out too by going through the code :) I now have a working gp32 sound driver for libmikmod - that is, it compiles, and it works in my head :) When I tried it, it sits infinitely on the 'load song' code - so my file io routines are now the problem...

I'll let you know how I get on.

BTW Mr.Mirko, to stop the sound I can simply clear the ringbuffer, but have you got some code to clear the IRQs and other things associated with the sound? Even if it isn't in the SDK, I'd like to add it manually to the code. I have also found a couple of places to speed up the ringbuffer code (heh, always looking for optimisation) so I will email it to you when its done.
 
Back
Top