Audio with sceIoRead() and sceAudioOutputPannedBlocking()

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

Moderators: cheriff, TyRaNiD

Post Reply
donlebon
Posts: 2
Joined: Tue Sep 19, 2006 4:35 am

Audio with sceIoRead() and sceAudioOutputPannedBlocking()

Post by donlebon »

I am planning an audio software for the psp but already stumbled doing my first steps. I read a lot of info and the well known tutorials to get started with audio. But without success.

I have problems playing uncompressed audio data that come from a raw-file (that is a wave-file without header, 16bit 44.1kHz, little-endian, mono in my case). I chose a raw-file just for testing purposes. The file works fine but I have those crackles coming from the file read or whatever.

I know, that this problem already occured in the past, e.g. in this thread http://forums.ps2dev.org/viewtopic.php?t=5089. But the solution shown there isn't a real solution to my problem I think. I am searching for an explanation of that phenomenon without doing all that stuff of MP3 decoding.

Please take a look at my source:

Code: Select all

#include <pspkernel.h>
#include <pspdebug.h>
#include <pspaudio.h>
#include <pspdisplay.h>

#include <stdlib.h>
#include <limits.h>

/* 
   This part of the code is more or less identical to the sdktest sample 
*/

/* Define the module info section */
PSP_MODULE_INFO&#40;"AUDIOTEST", 0, 1, 1&#41;;
/* Define the main thread's attribute value &#40;optional&#41; */
PSP_MAIN_THREAD_ATTR&#40;THREAD_ATTR_USER | THREAD_ATTR_VFPU&#41;;
/* Define printf, just to make typing easier */
#define printf	pspDebugScreenPrintf

/* Exit callback */
int exitCallback&#40;int arg1, int arg2, void *common&#41; &#123;
	sceKernelExitGame&#40;&#41;;
	return 0;
&#125;

/* Callback thread */
int callbackThread&#40;SceSize args, void *argp&#41; &#123;
	int cbid;

	cbid = sceKernelCreateCallback&#40;"Exit Callback", exitCallback, NULL&#41;;
	sceKernelRegisterExitCallback&#40;cbid&#41;;
	sceKernelSleepThreadCB&#40;&#41;;

	return 0;
&#125;

/* Sets up the callback thread and returns its thread id */
int setupCallbacks&#40;void&#41; &#123;
	int thid = 0;

	thid = sceKernelCreateThread&#40;"update_thread", callbackThread, 0x11, 0xFA0, 0, 0&#41;;
	if &#40;thid >= 0&#41; &#123;
		sceKernelStartThread&#40;thid, 0, 0&#41;;
	&#125;
	return thid;
&#125;

/*
  Below this point is the interesting code in this sample
*/

typedef struct &#123;
        short l, r;
&#125; sample_t;


int main&#40;void&#41; &#123;
	pspDebugScreenInit&#40;&#41;;
	setupCallbacks&#40;&#41;;	

	printf&#40;"Opening test file.\n"&#41;;
	
	//opening the test-file
	int fd = sceIoOpen&#40;"ms0&#58;/mono.raw", PSP_O_RDONLY, 0777&#41;;
	
	if&#40;fd <= 0&#41; &#123;
        printf&#40;"Error opening file."&#41;;
		return -1;
	&#125;
	
	printf&#40;"Opening Audio Channel...\n"&#41;;
	
	//common multiple of 64 and 100
	int sampleBufferSize = 1600;
	
	int channel = sceAudioChReserve&#40;PSP_AUDIO_NEXT_CHANNEL,
									PSP_AUDIO_SAMPLE_ALIGN&#40;sampleBufferSize&#41;,
									PSP_AUDIO_FORMAT_STEREO&#41;;
									
	if&#40;channel < 0&#41;&#123;
		printf&#40;"Error opening Audio Channel."&#41;;
		sceIoClose&#40;fd&#41;;	
		return -1;
	&#125;
									
									
	sample_t sampleBuffer&#91;sampleBufferSize&#93;;	
	char byteBuffer&#91;sampleBufferSize*2&#93;; //buffer for reading from file
	short sampleValue = 0;
	
	//read the first bytes
	int readBytes = sceIoRead&#40;fd, byteBuffer, sampleBufferSize*2&#41;;
	//samples are 16bit
	int readSamples = readBytes/2;
	
	while&#40;readSamples > 0&#41;&#123;
	
		int i;
		//copy the bytes to the sampleBuffer and convert them to short
		for&#40;i = 0; i < readSamples; ++i&#41;&#123;
			//little endian conversion to short
			//wave/raw uses -32768 to 32767
			sampleValue = &#40;&#40;short&#41; byteBuffer&#91;2*i&#93;&#41; + &#40;&#40;&#40;short&#41;byteBuffer&#91;2*i+1&#93;&#41; << 8&#41;;
			sampleBuffer&#91;i&#93;.l = sampleValue;
			sampleBuffer&#91;i&#93;.r = sampleValue;		
		&#125;
		
		sceAudioOutputPannedBlocking&#40;channel, PSP_AUDIO_VOLUME_MAX, PSP_AUDIO_VOLUME_MAX, sampleBuffer&#41;;
		
		//reading the next samples
		readSamples = sceIoRead&#40;fd, byteBuffer, sampleBufferSize*2&#41; / 2;
	
	&#125;
	
	sceAudioChRelease&#40;channel&#41;;
	sceIoClose&#40;fd&#41;;	
	
	printf&#40;"Reached Audio File End.\n"&#41;;
	
	return 0;
&#125;
The crackles occur at every loop (also when changing the buffer size). So when recording the audio, I can see a lot of samples obviously damaged at the start/end of every single loop, in the upper example every 1600 samples.

Studying the sources, I could not really imagine, how the solution in the thread I cited above fits to my needs. I also took a look at the PSPRadio-sources. But also that software is much more complex than this (simple?) problem. They do a lot of messaging stuff because they have to load the audio from the internet. Or did I oversee something?

I also tried the callback-function method from pspaudiolib.h. That didn't work either. I took a look into pspaudiolib.c, but no magic, just the way I'm trying to do it here? Or am I wrong?

Please help.
User avatar
dot_blank
Posts: 498
Joined: Wed Sep 28, 2005 8:47 am
Location: Brasil

Post by dot_blank »

the psp handles 3200 samples for stereo
in your case you use mono thus 1600 samples
so you must give the audio buffer TWICE the amount
to properly play smoothly ...you output as stereo
but your input is mono ...so double your outputing
and you will remove crackles ...hope this helps

Code: Select all

//common multiple of 64 and 100
   int sampleBufferSize = 3200; 
   sample_t sampleBuffer&#91;sampleBufferSize&#93;; //now its correct
   char byteBuffer&#91;sampleBufferSize&#93;;        //changed 
//read the first bytes
   int readBytes = sceIoRead&#40;fd, byteBuffer, sampleBufferSize&#41;;  //changed
   //samples are 32bit
   int readSamples = readBytes;  //changed
ill leave you to continue changes ...but i think you see what i mean :)
10011011 00101010 11010111 10001001 10111010
donlebon
Posts: 2
Joined: Tue Sep 19, 2006 4:35 am

Post by donlebon »

Thanks dot_blank for your answer. Your suggestion unfortunately isn't the solution to the problem. My code was allright according to sample buffer size etc. because

Code: Select all

sceAudioChReserve&#40;PSP_AUDIO_NEXT_CHANNEL, 
                           PSP_AUDIO_SAMPLE_ALIGN&#40;sampleBufferSize&#41;, 
                           PSP_AUDIO_FORMAT_STEREO&#41;;
will reserve the memory needed for STEREO samples.

But now I found the solution, I think that is very important to everyone:
The crackles will disappear when allocating the sample-buffer within the HEAP memory instead of the STACK! So take new or malloc() to build the buffer.

Another issue I found after solving that prob, was a noise that came from a wrong computation (my fault). I had to change the byteBuffer from char* to unsigned char*. Then the conversion to short works right.

Here the working (!yeah!) code:

Code: Select all

#include <pspkernel.h> 
#include <pspdebug.h> 
#include <pspaudio.h> 
#include <pspdisplay.h> 

#include <stdlib.h> 
#include <limits.h> 

/* 
   This part of the code is more or less identical to the sdktest sample 
*/ 

/* Define the module info section */ 
PSP_MODULE_INFO&#40;"AUDIOTEST", 0, 1, 1&#41;; 
/* Define the main thread's attribute value &#40;optional&#41; */ 
PSP_MAIN_THREAD_ATTR&#40;THREAD_ATTR_USER | THREAD_ATTR_VFPU&#41;; 
/* Define printf, just to make typing easier */ 
#define printf   pspDebugScreenPrintf 

/* Exit callback */ 
int exitCallback&#40;int arg1, int arg2, void *common&#41; &#123; 
   sceKernelExitGame&#40;&#41;; 
   return 0; 
&#125; 

/* Callback thread */ 
int callbackThread&#40;SceSize args, void *argp&#41; &#123; 
   int cbid; 

   cbid = sceKernelCreateCallback&#40;"Exit Callback", exitCallback, NULL&#41;; 
   sceKernelRegisterExitCallback&#40;cbid&#41;; 
   sceKernelSleepThreadCB&#40;&#41;; 

   return 0; 
&#125; 

/* Sets up the callback thread and returns its thread id */ 
int setupCallbacks&#40;void&#41; &#123; 
   int thid = 0; 

   thid = sceKernelCreateThread&#40;"update_thread", callbackThread, 0x11, 0xFA0, 0, 0&#41;; 
   if &#40;thid >= 0&#41; &#123; 
      sceKernelStartThread&#40;thid, 0, 0&#41;; 
   &#125; 
   return thid; 
&#125; 

/* 
  Below this point is the interesting code in this sample 
*/ 

typedef struct &#123; 
        short l, r; 
&#125; sample_t; 


int main&#40;void&#41; &#123; 
   pspDebugScreenInit&#40;&#41;; 
   setupCallbacks&#40;&#41;;    

   printf&#40;"Opening test file.\n"&#41;; 
    
   //opening the test-file 
   int fd = sceIoOpen&#40;"ms0&#58;/mono.raw", PSP_O_RDONLY, 0777&#41;; 
    
   if&#40;fd <= 0&#41; &#123; 
        printf&#40;"Error opening file."&#41;; 
      return -1; 
   &#125; 
    
   printf&#40;"Opening Audio Channel...\n"&#41;; 
   
   //number of Stereo Samples to be allocated by the channel
   int sampleBufferSize = 1024; 
   
   //opening the channel in stereo mode
   int channel = sceAudioChReserve&#40;PSP_AUDIO_NEXT_CHANNEL,
							sampleBufferSize, 
							PSP_AUDIO_FORMAT_STEREO&#41;; 
                            
   if&#40;channel < 0&#41;&#123; 
      printf&#40;"Error opening Audio Channel."&#41;; 
      sceIoClose&#40;fd&#41;;    
      return -1; 
   &#125; 
                            
   //allocating buffers on the HEAP &#40;!!!&#41; -> using the Stack would result in crackles!
   //if you don't like C++, take malloc&#40;&#41; here                      
   sample_t* sampleBuffer = new sample_t&#91;sampleBufferSize&#93;;
   
   //Using this buffer to read from the file, it is important to take unsigned char.
   //because we are doing mono, we need only twice the size of the sampleBufferSize
   //if we were reading stereo samples from the raw-file, we should take four times
   //the sampleBufferSize
   unsigned char* byteBuffer = new unsigned char&#91;sampleBufferSize*2&#93;;
   
   //helper variable
   short sampleValue = 0; 
    
   //read the first bytes 
   int readBytes = sceIoRead&#40;fd, byteBuffer, sampleBufferSize*2&#41;; 
   //samples are 16bit 
   int readSamples = readBytes/2; 
    
   while&#40;readSamples > 0&#41;&#123; 
    
      int i; 
      //copy the bytes to the sampleBuffer and convert them to short 
      for&#40;i = 0; i < readSamples; ++i&#41;&#123; 
         //little endian conversion to short 
         //wave/raw uses -32768 to 32767 
         sampleValue = &#40;&#40;short&#41; byteBuffer&#91;2*i&#93;&#41; + &#40;&#40;&#40;short&#41;byteBuffer&#91;2*i+1&#93;&#41; << 8&#41;;
		 
		 //here we are going to stereo by just copying the value to both channels
         sampleBuffer&#91;i&#93;.l = sampleValue; 
         sampleBuffer&#91;i&#93;.r = sampleValue;       
      &#125;
	  
	  //if there are less samples than the buffer is expecting
	  //fill the rest of the buffer with silence &#40;will occur on the last cycle&#41;
	  for&#40;i = readSamples; i < sampleBufferSize; ++i&#41;&#123;
		 sampleBuffer&#91;i&#93;.l = 0; 
         sampleBuffer&#91;i&#93;.r = 0;
	  &#125;
       
      sceAudioOutputPannedBlocking&#40;channel, PSP_AUDIO_VOLUME_MAX, PSP_AUDIO_VOLUME_MAX, sampleBuffer&#41;; 
       
      //reading the next samples 
      readSamples = sceIoRead&#40;fd, byteBuffer, sampleBufferSize*2&#41; / 2; 
    
   &#125; 
    
   //avoid a click when releasing the channel by turning volume down before
   sceAudioChangeChannelVolume&#40;channel, 0, 0&#41;;
   
   //close the channel
   sceAudioChRelease&#40;channel&#41;;
 
   //close the file
   sceIoClose&#40;fd&#41;;
   
   //delete the buffers
   delete &#91;&#93; sampleBuffer;
   delete &#91;&#93; byteBuffer;
    
   printf&#40;"Reached Audio File End.\n"&#41;; 
    
   return 0; 
&#125;
So the work can go on! Keep your eyes open, we will come with a very cool audio homebrew in a few months, hopefully.

Greetz
Don
Post Reply