Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difficulty porting raw PCM output code from Java to Android AudioTrack API

I'm attempting to port an application that plays chiptunes (NSF, SPC, etc) music files from Java SE to Android. The Android API seems to lack the javax multimedia classes that this application uses to output raw PCM audio. The closest analog I've found in the API is AudioTrack and so I've been wrestling with that.

However, when I try to run one of my sample music files through my port-in-progress, all I get back is static. My suspicion is that it's the AudioTrack I've setup which is at fault. I've tried various different constructors but it all just outputs static in the end.

The DataLine setup in the original code is something like:

AudioFormat audioFormat = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED,
                44100, 16, 2, 4, 44100, true );
DataLine.Info lineInfo = new DataLine.Info( SourceDataLine.class, audioFormat );
DataLine line = (SourceDataLine)AudioSystem.getLine( lineInfo );

The constructor I'm using right now is:

AudioTrack = new AudioTrack( AudioManager.STREAM_MUSIC,
        44100,
        AudioFormat.CHANNEL_CONFIGURATION_STEREO,
        AudioFormat.ENCODING_PCM_16BIT,
        AudioTrack.getMinBufferSize( 44100,
                AudioFormat.CHANNEL_CONFIGURATION_STEREO,
                AudioFormat.ENCODING_PCM_16BIT ),
        AudioTrack.MODE_STREAM );

I've replaced constants and variables in those so they make sense as concisely as possible, but my basic question is if there are any obvious problems in the assumptions I made when going from one format to the other.

like image 460
alexanderfb Avatar asked Apr 25 '10 05:04

alexanderfb


1 Answers

So I had a little time to look at this further today, and I think I've nailed it down. The AudioFormat declaration in the first code sample above has the big endian parameter set to "true", but the Android AudioTrack expects PCM data to be in little endian format.

So I wrote a quick little loop to test out my hunch like so:

 for( int i = 0; i + LEN_PCM_SAMPLE_BYTES < LEN_PCM_BUFFER; i += LEN_PCM_SAMPLE_BYTES ) {
    // Really rude endian conversion.
    byte bytTemp = a_bytBuffer[i];
    a_bytBuffer[i] = a_bytBuffer[i + 1];
    a_bytBuffer[i + 1] = bytTemp;
 }

Basically, this loop flips the bytes of every (16-bit) sample in the buffer. This works great, except it's a little choppy since it's terribly inefficient. I tried using a ByteBuffer but that doesn't seem to flip the bytes in the individual samples.

I'll figure something even better out going forward, but the basic problem here is solved. Hope someone else finds this useful!

like image 59
alexanderfb Avatar answered Sep 22 '22 02:09

alexanderfb