Code: Select all
#define kAuFramesPerOutputCall     (2048)
#define kAuBytesPerOutputCall      (kAuFramesPerOutputCall * 4)
int              gAuMusicChannel;
int     volatile gAuMusicVolume;
SceUID           gAuMusicThreadID;
SceBool volatile gAuMusicThreadShouldExit;
SceBool volatile gAuMusicThreadShouldPause;
SceBool volatile gAuMusicThreadIsSleeping;
AuMusic volatile gAuCurrentMusic;
Code: Select all
AuErr AuInitializeMusic()
{
	// Reserve hardware channel.
	
	gAuMusicChannel = sceAudioChReserve(PSP_AUDIO_NEXT_CHANNEL,
	                                    kAuFramesPerOutputCall,
	                                    PSP_AUDIO_FORMAT_STEREO);
	
	if (gAuMusicChannel < 0)
		return kAuErrNoMoreChannels;
		
	gAuMusicVolume = kAuMaximumVolume;
	
	
	// Start rendering thread.
	
	gAuMusicThreadID = sceKernelCreateThread("Music",
	                                         AuMusicRenderingThread,
	                                         16,
	                                         65536,
	                                         0,
	                                         NULL);
	
	gAuMusicThreadShouldExit  = NO;
	gAuMusicThreadShouldPause = NO;
	gAuCurrentMusic           = NULL;
	sceKernelStartThread(gAuMusicThreadID, 0, NULL);
	
	
	return kAuErrNone;
}
Code: Select all
int AuMusicRenderingThread(SceSize unused1, void *unused2)
{
	void *buffer1;
	void *buffer2;
	void *buffer;
	
	// Allocate rendering buffers.
	
	buffer1 = malloc(kAuBytesPerOutputCall);
	buffer2 = malloc(kAuBytesPerOutputCall);
	
	if (! (buffer1 && buffer2))
	{
		if (buffer1) free(buffer1);
		if (buffer2) free(buffer2);
		
		sceKernelExitThread(kAuErrMemory);
		return kAuErrMemory;
	}
	
	buffer1 = mCachelessPointer(buffer1);
	buffer2 = mCachelessPointer(buffer2);
	
	buffer = buffer1;
	
	
	// Go to sleep and wait for a wakeup call when music needs to be played.
	
	gAuMusicThreadIsSleeping = YES;
	sceKernelSleepThread();
	gAuMusicThreadIsSleeping = NO;
	
	
	// Rendering loop.
	
	for (;;)
	{
		// Check for messages from the main thread.
		
		if (gAuMusicThreadShouldPause)
		{
			gAuMusicThreadIsSleeping = YES;
			sceKernelSleepThread();
			gAuMusicThreadIsSleeping = NO;
		}
		
		if (gAuMusicThreadShouldExit) break;
		
		
		// Make sure some music has been set.
		
		if (! gAuCurrentMusic)
		{
			gAuMusicThreadShouldPause = YES;
			continue;
		}
		
		
		// Render music.
		
		long bytesSoFar = 0;
		int unused;
		
		while (bytesSoFar < kAuBytesPerOutputCall)
		{
			long bytes = ov_read(&gAuCurrentMusic->vorbis,
			                     (void *) ((unsigned) buffer + bytesSoFar),
			                     kAuBytesPerOutputCall - bytesSoFar,
			                     &unused);
			
			if (bytes == 0)
			{
				ov_time_seek(&gAuCurrentMusic->vorbis, gAuCurrentMusic->loopPoint);
				continue;
			}
			
			if (bytes < 0)
			{
				gAuCurrentMusic = NULL;
				break;
			}
			
			bytesSoFar += bytes;
		}
		
		if (! gAuCurrentMusic) continue;
		
		
		// Send rendered music to audio hardware.
		
		sceKernelDelayThread(20 * sceAudioGetChannelRestLen(gAuMusicChannel));
		while (sceAudioGetChannelRestLen(gAuMusicChannel) > 0) { }
		
		sceAudioOutput(gAuMusicChannel, gAuMusicVolume, buffer);
		
		
		// Swap buffers.
		
		buffer = (buffer == buffer1) ? buffer2 : buffer1;
	}
	
	
	// Clean up.
	
	free(buffer1);
	free(buffer2);
	
	sceKernelExitThread(kAuErrNone);
	return kAuErrNone;
}
