Clicking audio

Discuss the development of new homebrew software, tools and libraries.

Moderators: cheriff, TyRaNiD

Post Reply
User avatar
dsn
Posts: 47
Joined: Wed Nov 09, 2005 11:48 am
Location: Indianapolis, Indiana, USA

Clicking audio

Post by dsn »

I'm putting together a little audio library. Everything works swimmingly except that I get clicks in my audio with every other buffer swap. I've tried changing the thread delay time (and removing it altogether), as well as changing the number of frames per output call, both with no luck. Can anyone tell me what I'm doing wrong? I've posted the relevant code below; I can post more if necessary.

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 &#40;gAuMusicChannel < 0&#41;
		return kAuErrNoMoreChannels;
		
	gAuMusicVolume = kAuMaximumVolume;
	
	
	// Start rendering thread.
	
	gAuMusicThreadID = sceKernelCreateThread&#40;"Music",
	                                         AuMusicRenderingThread,
	                                         16,
	                                         65536,
	                                         0,
	                                         NULL&#41;;
	
	gAuMusicThreadShouldExit  = NO;
	gAuMusicThreadShouldPause = NO;
	gAuCurrentMusic           = NULL;
	sceKernelStartThread&#40;gAuMusicThreadID, 0, NULL&#41;;
	
	
	return kAuErrNone;
&#125;

Code: Select all

int AuMusicRenderingThread&#40;SceSize unused1, void *unused2&#41;
&#123;
	void *buffer1;
	void *buffer2;
	void *buffer;
	
	// Allocate rendering buffers.
	
	buffer1 = malloc&#40;kAuBytesPerOutputCall&#41;;
	buffer2 = malloc&#40;kAuBytesPerOutputCall&#41;;
	
	if &#40;! &#40;buffer1 && buffer2&#41;&#41;
	&#123;
		if &#40;buffer1&#41; free&#40;buffer1&#41;;
		if &#40;buffer2&#41; free&#40;buffer2&#41;;
		
		sceKernelExitThread&#40;kAuErrMemory&#41;;
		return kAuErrMemory;
	&#125;
	
	buffer1 = mCachelessPointer&#40;buffer1&#41;;
	buffer2 = mCachelessPointer&#40;buffer2&#41;;
	
	buffer = buffer1;
	
	
	// Go to sleep and wait for a wakeup call when music needs to be played.
	
	gAuMusicThreadIsSleeping = YES;
	sceKernelSleepThread&#40;&#41;;
	gAuMusicThreadIsSleeping = NO;
	
	
	// Rendering loop.
	
	for &#40;;;&#41;
	&#123;
		// Check for messages from the main thread.
		
		if &#40;gAuMusicThreadShouldPause&#41;
		&#123;
			gAuMusicThreadIsSleeping = YES;
			sceKernelSleepThread&#40;&#41;;
			gAuMusicThreadIsSleeping = NO;
		&#125;
		
		if &#40;gAuMusicThreadShouldExit&#41; break;
		
		
		// Make sure some music has been set.
		
		if &#40;! gAuCurrentMusic&#41;
		&#123;
			gAuMusicThreadShouldPause = YES;
			continue;
		&#125;
		
		
		// Render music.
		
		long bytesSoFar = 0;
		int unused;
		
		while &#40;bytesSoFar < kAuBytesPerOutputCall&#41;
		&#123;
			long bytes = ov_read&#40;&gAuCurrentMusic->vorbis,
			                     &#40;void *&#41; &#40;&#40;unsigned&#41; buffer + bytesSoFar&#41;,
			                     kAuBytesPerOutputCall - bytesSoFar,
			                     &unused&#41;;
			
			if &#40;bytes == 0&#41;
			&#123;
				ov_time_seek&#40;&gAuCurrentMusic->vorbis, gAuCurrentMusic->loopPoint&#41;;
				continue;
			&#125;
			
			if &#40;bytes < 0&#41;
			&#123;
				gAuCurrentMusic = NULL;
				break;
			&#125;
			
			bytesSoFar += bytes;
		&#125;
		
		if &#40;! gAuCurrentMusic&#41; continue;
		
		
		// Send rendered music to audio hardware.
		
		sceKernelDelayThread&#40;20 * sceAudioGetChannelRestLen&#40;gAuMusicChannel&#41;&#41;;
		while &#40;sceAudioGetChannelRestLen&#40;gAuMusicChannel&#41; > 0&#41; &#123; &#125;
		
		sceAudioOutput&#40;gAuMusicChannel, gAuMusicVolume, buffer&#41;;
		
		
		// Swap buffers.
		
		buffer = &#40;buffer == buffer1&#41; ? buffer2 &#58; buffer1;
	&#125;
	
	
	// Clean up.
	
	free&#40;buffer1&#41;;
	free&#40;buffer2&#41;;
	
	sceKernelExitThread&#40;kAuErrNone&#41;;
	return kAuErrNone;
&#125;
bengarney
Posts: 24
Joined: Sat Oct 22, 2005 6:48 pm

Post by bengarney »

Tried triple buffering it? :)
User avatar
dsn
Posts: 47
Joined: Wed Nov 09, 2005 11:48 am
Location: Indianapolis, Indiana, USA

Post by dsn »

At first I thought you were kidding, but that actually worked. Thanks for the tip. :)

I only added three lines and changed one, so unless someone specifically requests it, I won't bother posting the new code.

EDIT: Make that added four and changed two.
bengarney
Posts: 24
Joined: Sat Oct 22, 2005 6:48 pm

Post by bengarney »

C'mon, if you're going to go to the trouble of counting the changes TWICE, surely you could post the changed lines. :P
User avatar
dsn
Posts: 47
Joined: Wed Nov 09, 2005 11:48 am
Location: Indianapolis, Indiana, USA

Post by dsn »

I suppose I should mention that my second line count was wrong too. :) Sometimes I wonder why I'm even allowed to operate a computer.

The only part that changed is the playback thread, listed here.

Code: Select all

int AuMusicRenderingThread&#40;SceSize unused1, void *unused2&#41;
&#123;
	void *buffer1;
	void *buffer2;
	void *buffer3;
	void *buffer;

	
	// Allocate rendering buffers.
	
	buffer1 = malloc&#40;kAuBytesPerOutputCall&#41;;
	buffer2 = malloc&#40;kAuBytesPerOutputCall&#41;;
	buffer3 = malloc&#40;kAuBytesPerOutputCall&#41;;
	
	if &#40;! &#40;buffer1 && buffer2 && buffer3&#41;&#41;
	&#123;
		if &#40;buffer1&#41; free&#40;buffer1&#41;;
		if &#40;buffer2&#41; free&#40;buffer2&#41;;
		if &#40;buffer3&#41; free&#40;buffer3&#41;;
		
		sceKernelExitThread&#40;kAuErrMemory&#41;;
		return kAuErrMemory;
	&#125;
	
	buffer1 = mCachelessPointer&#40;buffer1&#41;;
	buffer2 = mCachelessPointer&#40;buffer2&#41;;
	buffer3 = mCachelessPointer&#40;buffer3&#41;;
	
	buffer = buffer1;
	
	
	// Go to sleep and wait for a wakeup call when music needs to be played.
	
	gAuMusicThreadIsSleeping = YES;
	sceKernelSleepThread&#40;&#41;;
	gAuMusicThreadIsSleeping = NO;
	
	
	// Rendering loop.
	
	for &#40;;;&#41;
	&#123;
		// Check for messages from the main thread.
		
		if &#40;gAuMusicThreadShouldPause&#41;
		&#123;
			gAuMusicThreadIsSleeping = YES;
			sceKernelSleepThread&#40;&#41;;
			gAuMusicThreadIsSleeping = NO;
		&#125;
		
		if &#40;gAuMusicThreadShouldExit&#41; break;
		
		
		// Make sure some music has been set.
		
		if &#40;! gAuCurrentMusic&#41;
		&#123;
			gAuMusicThreadShouldPause = YES;
			continue;
		&#125;
		
		
		// Render music.
		
		long bytesSoFar = 0;
		int unused;
		
		while &#40;bytesSoFar < kAuBytesPerOutputCall&#41;
		&#123;
			long bytes = ov_read&#40;&gAuCurrentMusic->vorbis,
			                     &#40;void *&#41; &#40;&#40;unsigned&#41; buffer + bytesSoFar&#41;,
			                     kAuBytesPerOutputCall - bytesSoFar,
			                     &unused&#41;;
			
			if &#40;bytes == 0&#41;
			&#123;
				ov_time_seek&#40;&gAuCurrentMusic->vorbis, gAuCurrentMusic->loopPoint&#41;;
				continue;
			&#125;
			
			if &#40;bytes < 0&#41;
			&#123;
				gAuCurrentMusic = NULL;
				break;
			&#125;
			
			bytesSoFar += bytes;
		&#125;
		
		if &#40;! gAuCurrentMusic&#41; continue;
		
		
		// Send rendered music to audio hardware.
		
		sceKernelDelayThread&#40;20 * sceAudioGetChannelRestLen&#40;gAuMusicChannel&#41;&#41;;
		while &#40;sceAudioGetChannelRestLen&#40;gAuMusicChannel&#41; > 0&#41; &#123; &#125;
		
		sceAudioOutput&#40;gAuMusicChannel, gAuMusicVolume, buffer&#41;;
		
		
		// Swap buffers.
		
		buffer = &#40;buffer == buffer1&#41; ? buffer2 &#58; &#40;buffer == buffer2&#41; ? buffer3 &#58; buffer1;
	&#125;
	
	
	// Clean up.
	
	free&#40;buffer1&#41;;
	free&#40;buffer2&#41;;
	free&#40;buffer3&#41;;
	
	sceKernelExitThread&#40;kAuErrNone&#41;;
	return kAuErrNone;
&#125;
bengarney
Posts: 24
Joined: Sat Oct 22, 2005 6:48 pm

Post by bengarney »

Cool, thanks! :)
Post Reply