XML Feeds

.

[java-dev] Bug in AudioFileBuffer with different sample sizes?

topher lafata topher at topher.com
Sat Mar 22 11:44:42 MDT 2008


i dont think you have overlooked anything. it is very possible there  
are bugs with those bit depths.
Regardless here is the source for the class. You could copy it and  
make your own.

package com.cycling74.msp;
import com.cycling74.max.MessageReceiver;
import javax.sound.sampled.*;
import java.io.FileNotFoundException;
import java.io.*;

/**
  * A utility class for loading audio data off of disk and into  
memory. The data is translated from the
  * native file format into the the floating point format used by msp.
  * @author Topher LaFata
  */
public class AudioFileBuffer
{
	/**
	* If an instance of MessageReceiver is passed into the constructor  
the FINISHED_READING message will be
	* sent to it when the requested file is successfully loaded into  
memory. File loading occurs asynchronously.	
	*/
	public static final int FINISHED_READING = 1;
	//file writing is not yet implemented
	//public static final int FINISHED_WRITING = 2;
	
     private File              _file;
     private AudioInputStream _ais;
     private AudioFormat      _aformat;
     private int              _num_channels;
     private long _framelength; //length of current file is frames
     private int _framesize;//size of each frame in bytes
     private int _sample_size_in_bits;
     private boolean _big_endian;
     private float            _sr;

	/**
	* buf contains the audio samples loaded off of disk deinterleaved by  
channel into a 2 dimensional
	* floating point array. The first dimension corresponds to the audio  
channel and the second dimension
	* corresponds to the audio data itself. Thus a stereo audio file  
would have the samples for the left channel
	* at buf[0][0...framelength - 1] and the right channel at buf[1] 
[0...framelength - 1]
	*/
     public float[][] buf;

     //this is affected by sample rate
     private MessageReceiver _client = null;
     private buf_filler _bft;
	
	/**
      * Constructor.
	 * @param filename Absolute native path of the audio file to be  
loaded into memory.
      */
     public AudioFileBuffer(String filename)throws  
FileNotFoundException, IOException, UnsupportedAudioFileException
     {
		_client = null;
		openmp3(filename);
     }

	/**
      * Constructor.
	 * @param filename Absolute native path of the audio file to be  
loaded into memory.
	 * @param client instance of MessageReceiver which will be notified  
when file is finished being loaded into memory.
	 * This information may or may not be relevant since the member  
buffer,buf[][], containing the audio data will be valid and zero filled
	 * when the constructor returns.
      */
     public AudioFileBuffer(String filename, MessageReceiver client) 
throws FileNotFoundException, IOException, UnsupportedAudioFileException
     {
		_client = client;
		open(filename);
     }
	
	
	public void openmp3(String filename)throws FileNotFoundException,  
IOException, UnsupportedAudioFileException
	{
		_file = new File(filename);
		if(!_file.exists())
			throw new FileNotFoundException("Unable to find file "+filename);
		
		kill_buf_filler();

			
		AudioFileFormat baseFileFormat = null;
		AudioFormat baseFormat = null;
		baseFileFormat = AudioSystem.getAudioFileFormat(_file);
		baseFormat = baseFileFormat.getFormat();
		// Audio type such as MPEG1 Layer3, or Layer 2, or ...
		System.out.println("FORMAT "+baseFileFormat.getType().toString());
		
	//	System.out.println("sr is          "+baseFormat.getSampleRate());
	//	System.out.println("channels is	   "+baseFormat.getChannels());
	//	System.out.println("frame rate is  "+baseFormat.getFrameRate());
	//	System.out.println("frame size is  "+baseFormat.getFrameSize());
	//	System.out.println("is big endian "+baseFormat.isBigEndian());
		
		
	
		AudioInputStream in = AudioSystem.getAudioInputStream(_file);
		System.out.println("ms is "+in.markSupported());
		System.out.println("mp3 fl is "+in.getFrameLength());
		//AudioFormat baseFormat = in.getFormat();
		AudioFormat decodedFormat =
		new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
						baseFormat.getSampleRate(),
						16,
						baseFormat.getChannels(),
						baseFormat.getChannels()*2,
						baseFormat.getSampleRate(),true) ;

		_ais = AudioSystem.getAudioInputStream(decodedFormat, in);
		
		//find the length of the converted mp3 in frames.
		//unfortunately we don't get this with the whole new _ais decoded crap
		//we make ll one frame in length
		int framelength = baseFormat.getChannels()*2;
		
		System.out.println("framelength is "+framelength);
		
		byte[] ll = new byte[framelength];
		int l = ll.length;
		int totframes = 0;
		int bytesread = 0;
		while((bytesread = _ais.read(ll,0,framelength)) != -1)
		{
			totframes++;
			if(bytesread < framelength)
				{
					System.out.println("read lesss than framelength "+bytesread);
				}
		}
		
		System.out.println("frame length is "+totframes);
		System.out.println("ais is "+_ais.markSupported());
		
		_ais = AudioSystem.getAudioInputStream(decodedFormat, in);
		
		
		_aformat	  = _ais.getFormat();
	//	System.out.println("sr is          "+_aformat.getSampleRate());
	//	System.out.println("channels is	   "+_aformat.getChannels());
	//	System.out.println("frame rate is  "+_aformat.getFrameRate());
	//	System.out.println("frame length is  "+_ais.getFrameLength());
	//	System.out.println("frame size is  "+_aformat.getFrameSize());
	//	System.out.println("is big endian "+_aformat.isBigEndian());
	//	System.out.println("s size in bits "+_aformat.getSampleSizeInBits 
());
		
		
		_sr           = _aformat.getSampleRate();
		_num_channels = _aformat.getChannels();
		_framelength  = totframes;
		_framesize    = framelength;
		_sample_size_in_bits = _aformat.getSampleSizeInBits();
		_big_endian   = _aformat.isBigEndian();

		buf          = new float[_num_channels][(int)_framelength];
		_bft = new buf_filler(this);
		_bft.start();	
	}
	
	/**
      * Load a different audio file into memory using this instance  
of AudioFileBuffer. Previous audio data is disgarded and
	 * buf[][] member variable reflects the number of channels and  
framesize of the new audio file..
	 * @param filename Absolute native path of the audio file to be  
loaded into memory.
      */
     public void open(String filename)throws FileNotFoundException,  
IOException, UnsupportedAudioFileException
     {

		_file = new File(filename);
		if(!_file.exists())
		    throw new FileNotFoundException("Unable to find file "+filename);
		//kill the previous buf filler if it is already running
		kill_buf_filler();
		//This is so we have mark supported and get efficiency in our disk  
reads
		InputStream fileInputStream = new FileInputStream(_file);
		InputStream inputStream = new BufferedInputStream(fileInputStream);	
		try{
		    _ais = AudioSystem.getAudioInputStream(inputStream);
		
		}catch(IOException ioe)
		{
			throw ioe;
		}
		catch(UnsupportedAudioFileException uafe)
		{
			throw uafe;
		}	

		_aformat = _ais.getFormat();
		if(_aformat.getEncoding() != AudioFormat.Encoding.PCM_SIGNED)
		{
			throw new UnsupportedAudioFileException("AudioBuffer currently   
only supports"+
								" PCM_SIGNED encodings");
		}
	
		_sr           = _aformat.getSampleRate();
		_num_channels = _aformat.getChannels();
		_framelength  = _ais.getFrameLength();
		_framesize    = _aformat.getFrameSize();
		_sample_size_in_bits = _aformat.getSampleSizeInBits();
		_big_endian   = _aformat.isBigEndian();

		buf          = new float[_num_channels][(int)_framelength];
		_bft = new buf_filler(this);
		_bft.start();

	}
   /**
   * Get the sample rate of the current audio file. It is important  
to note that
   * the current msp sampling rate is currently not considered when  
the audio file
   * is being decoded from disk. This means that an audio file saved  
with a different
   * sampling rate from the current msp sampling rate needs  
additional sample rate conversion
   * done on its audio data after it is loaded to play back at the  
expected speed.
   * This conversion is currently not done by default.
   */
     public float getSampleRate()
     {
		return _sr;
     }
   /*
     public AudioFormat getAudioFormat()
     {
		return _aformat;
     }
*/
	/**
	* Get the sample size in bits of the current audio file. For  
example, 8,16,24.
	*/
     public int getSampleSizeInBits()
     {
		return _sample_size_in_bits;
     }
	/**
	* Was the current audio file big endian format.
	*/
     public boolean isBigEndian()
     {
		return _big_endian;
     }
     /**
	* Get the number of sample frames in the audio file.A frame consists of
	* sample data for all channels at a particular instant in time.Thus  
a mono
	* audio file which has 1000 samples will have a frame length of 1000  
with
	* each frame containing one sample. A stereo audio file with 2000  
samples would
	* have a frame length of 1000 with each frame containing 2 samples,  
one for each
	* the left and right channels.
	*/
     public long getFrameLength()
     {
		return _framelength;
     }
	/**
	* Get the number of channels of the current audio file.
	*/
     public int getChannels()
     {
		return _num_channels;
     }

     /**
	*Get the length of the current audio file in milliseconds.
	* This is equivalent to:
	*<pre>
	* frame length / (sample rate / 1000)
	*</pre>
	*/
     public float getLengthMs()
     {
		return (float)(_framelength / (getSampleRate() / 1000));
     }



    private void fill_buf()
     {	
	System.out.println("filling buf");
		byte[] tmp = new byte[2048];
		int bytesread = 0;
		short ss = 0;
		int si = 0;
		int wh = 0;//points to sample frame

	try{
	    while((bytesread = _ais.read(tmp,0,tmp.length)) > 0)
		{
			if(wh % 1000 == 0)
				System.out.println("wh is "+wh);
		    for(int i = 0; i < bytesread;i+= _framesize)
			{
			    //if it is not bigendian make it so now...
			    //this is happening per frame..maybe not the best approach
			    if(!_big_endian && _sample_size_in_bits > 8)
				{
				    byte t1 = 0;
				    switch(_sample_size_in_bits)
					{
					case 16:
					    for(int ii = i; ii < i + _framesize;ii += 2)
						{
						    t1 = tmp[ii];
						    tmp[ii] = tmp[ii+1];
						    tmp[ii +1] = t1;
						}
					    break;
					case 24:
					    for(int ii = i; ii < i + _framesize;ii += 3)
						{
						    t1 = tmp[ii];
						    tmp[ii] = tmp[ii+3];
						    tmp[ii +3] = t1;
						}
					    break;
					default:
					}
				}
			    //deinterleave and convert to float
			    for(int c = 0;c < _num_channels;c++)
				{
				    int ch_offset = (c * _num_channels);
				    switch (_sample_size_in_bits)
					{
					case 8:
					    ss = (short)(tmp[i+c] & 0xff);
					    buf[c][wh] = (float)ss/16384;
					    break;
					case 16:
					    ss = (short)(((tmp[i+ch_offset] & 0xff) << 8) | (tmp[i 
+ch_offset+1] & 0xff));
					    buf[c][wh] = (float)ss/Short.MAX_VALUE;
						if(wh % 1024 == 0)
						System.out.println(ss);
						break;
					case 24:
					    si = (int)( ((tmp[i+ch_offset] & 0xff) << 16) | ((tmp[i 
+ch_offset+1] & 0xff) << 8) | (tmp[i+ch_offset+2] & 0xff));
					    buf[c][wh] = (float)si/8388608;
					
			 		    break;
					default:
					}

				}
			    wh++;
			}
		}
	    _ais.close();
		if(_client != null)
		{
			System.out.println("done converting to float");
			_client.messageReceived(this,FINISHED_READING,null);
		}
	}catch(Exception e)
	    {
		e.printStackTrace();
	    }

     }




t

On Mar 22, 2008, at 04:30 AM, volker böhm wrote:

> hallo,
> i'm using AudioFileBuffer from the max mxj api to read soundfiles  
> with varying sample sizes.
> when i try to output the floating point data from the buf field, i  
> get correct results only for 16 bit files.
> 8 bit, 24 bit and 32 bit are all wrong, which to me looks like  
> errors in int-to-float-conversion.
> do i overlook something here?
> thanks, volker.
>
> simple test class:
>
> import com.cycling74.max.*;
> import com.cycling74.msp.*;
>
> public class test_afb extends MaxObject
> {
> 	private AudioFileBuffer afb = null;
> 	
> 	public test_afb (Atom[] args) {
> 		declareInlets(new int[]{DataTypes.ALL});
> 		declareOutlets(new int[]{DataTypes.ALL});
> 	}
> 		
> 	 /* load  
> soundfile----------------------------------------------------*/
> 	 publicvoid open( String path ) {		
> 		 String fname = MaxSystem.locateFile(path);
> 		 post("filename: "+fname);
> 		
> 		 try {
> 			 afb = new AudioFileBuffer(fname);
> 			 System.out.println("frame length: "+ afb.getFrameLength());
> 			 System.out.println("samp size in bits: "+  
> afb.getSampleSizeInBits());
> 			 System.out.println("num channels: "+ afb.getChannels());
> 			 System.out.println("big endian: "+ afb.isBigEndian());
> 			 System.out.println("sample rate: "+ afb.getSampleRate());
> 		 }
> 		 catch(Exception e) {
> 			 error("mxj test_afb: sorry, can't open that file!");
> 		 }		
> 	 }
> 	
> 	 /* output sample value at index from channel 1-------------------*/
> 	 publicvoid inlet (int index) {	
> 		outlet(0, afb.buf[0][index]);	
> 	 }
> }
>
>
> simple test patch:
>
> #P window setfont "Sans Serif" 9.;
> #P flonum 225 196 67 9 0 0 0 3 0 0 0 221 221 221 222 222 222 0 0 0;
> #P flonum 93 194 67 9 0 0 0 3 0 0 0 221 221 221 222 222 222 0 0 0;
> #P number 225 121 44 9 0 0 1 3 0 0 0 221 221 221 222 222 222 0 0 0;
> #P window linecount 1;
> #P newex 225 166 54 196617 peek~ aha;
> #P newex 93 117 68 196617 prepend open;
> #P newex 93 166 93 196617 mxj test_afb;
> #P newex 351 118 81 196617 prepend replace;
> #P button 93 39 15 0;
> #P newex 93 62 56 196617 opendialog;
> #P newex 351 140 83 196617 buffer~ aha 100;
> #P comment 217 105 100 196617 sample index;
> #P fasten 8 0 5 0 230 151 98 151;
> #P connect 8 0 7 0;
> #P connect 5 0 9 0;
> #P connect 6 0 5 0;
> #P connect 3 0 2 0;
> #P connect 2 0 6 0;
> #P connect 7 0 10 0;
> #P fasten 2 0 4 0 98 88 356 88;
> #P connect 4 0 1 0;
> #P window clipboard copycount 11;
> _______________________________________________
> java-dev mailing list
> java-dev at cycling74.com
> http://www.cycling74.com/mailman/listinfo/java-dev




More information about the java-dev mailing list