Record Audio on a BlackBerry smartphone

by Retired on ‎02-16-2010 02:50 PM - edited on ‎10-20-2011 05:04 PM by Retired (11,352 Views)

Summary

 

This article applies to the following:

 

  • BlackBerry® Device Software 4.2 and later
  • BlackBerry smartphones based on Java® technology



Description

BlackBerry Device Software 4.2 implemented Java Specification Request (JSR) 135 Mobile Media API (MMAPI). This allows BlackBerry smartphone applications to record and play back multimedia content.

 

The BlackBerry smartphone supports audio recording using two different formats, Adaptive Multi-Rate (AMR) and 8kHz mono-16-bit Pulse Code Modulation (PCM). The default encoding used by the BlackBerry smartphone is AMR.

 

Note: BlackBerry Device Software 4.6 also supports Global System for Mobile communications® (GSM®) 6.10 and BlackBerry Device Software 4.7 supports Qualcomm Code Excited Linear Prediction (QCELP).

 

The encoding to be used is specified in the string passed to the Manager.createPlayer(String locator) method. The following table shows a list of supported locator strings.


String Resulting Format Minimum BlackBerry Device Software requirement 
capture://audio AMR 4.2
capture://audio?encoding=amr or capture://audio?encoding=audio/amr AMR 4.2
capture://audio?encoding=pcm or capture://audio?encoding=audio/basic PCM 4.2
capture://audio?encoding=gsm orcapture://audio?encoding=x-gsm GSM 4.6
capture://audio?encoding=qcelp QCELP 4.7

Note: The PCM format is not supported by BlackBerry smartphones that operate on the Code Division Multiple Access (CDMA) network.

 

The following code sample demonstrates how to record audio using JSR 135 on a BlackBerry smartphone. The sample encapsulates this in a thread that can be added to your application.

 

 

final class VoiceNotesRecorderThread extends Thread
{
private Player _player;
private RecordControl _rcontrol;
private ByteArrayOutputStream _output;
private byte _data[];

VoiceNotesRecorderThread() {}

private int getSize()
{
return (_output != null ? _output.size() : 0);
}

private byte[] getVoiceNote()
{
return _data;
}

public void run() {
try {
// Create a Player that captures live audio.
_player = Manager.createPlayer("capture://audio");
_player.realize();

// Get the RecordControl, set the record stream,
_rcontrol = (RecordControl)_player.getControl("RecordControl");

//Create a ByteArrayOutputStream to capture the audio stream.
_output = new ByteArrayOutputStream();
_rcontrol.setRecordStream(_output);
_rcontrol.startRecord();
_player.start();

} catch (final Exception e) {
UiApplication.getUiApplication().invokeAndWait(new Runnable() {
public void run() {
Dialog.inform(e.toString());
}
});
}
}

public void stop() {
try {
//Stop recording, capture data from the OutputStream,
//close the OutputStream and player.
_rcontrol.commit();
_data = _output.toByteArray();
_output.close();
_player.close();

} catch (Exception e) {
synchronized (UiApplication.getEventLock()) {
Dialog.inform(e.toString());
}
}
}
}

Recording in PCM format provides only raw mono PCM at 8000Hz.  This format can be played back on a BlackBerry device, however to play it back on other devices such as a PC, it must be wrapped in a WAV container.  This is easy to do, and in fact:

 

import java.io.ByteArrayOutputStream;
import java.io.IOException;

public class WavHeaderAppender {
    public static final int WAV_HEADER_SIZE = 44;
    
    private static final byte[] RIFF = {0x52, 0x49, 0x46, 0x46}; // RIFF
    private static final byte[] WAVE = {0x57, 0x41, 0x56, 0x45}; // WAVE
    private static final byte[] FMT_ = {0x66, 0x6D, 0x74, 0x20}; // fmt_
    private static final byte[] DATA = {0x64, 0x61, 0x74, 0x61}; // DATA

    private static final int BlackBerryWavChannels = 1;
    private static final int BlackBerryWavSampleRate = 8000;
    private static final int BlackBerryWavBitsPerSample = 16;
    private static final int BlackBerryWavBytesPerSecond = (BlackBerryWavSampleRate * BlackBerryWavChannels) * (BlackBerryWavBitsPerSample / 8);
    private static final int BlackBerryWavBlockAlign = BlackBerryWavChannels * (BlackBerryWavBitsPerSample / 8);

    
    /**
     * Append a generic WAV header to BlackBerry-recorded raw PCM data.
     *
     * @param pcmData the pcm data to append to
     * @return the byte[] the same array as pcmData, but with 44 bytes of header data pre-pended
     * @throws IOException 
     */
    public static byte[] appendWavHeader(byte[] pcmData) throws IOException {
        return WavHeaderAppender.appendWavHeader(BlackBerryWavChannels, BlackBerryWavSampleRate, BlackBerryWavBytesPerSecond, BlackBerryWavBlockAlign, BlackBerryWavBitsPerSample, pcmData );
    }

	/**
     * Append a generic wav header to specific raw PCM data.
     *
     * @param channels the number of channels
     * @param sampleRate the sample rate
     * @param bytesPerSecond the bytes per second of audio
     * @param blockAlign the block align (sample size)
     * @param bitsPerSample the number of bits per sample
     * @param pcmData the actual pcm sample data
     * @return the byte[] the same array as pcmData, but with 44 bytes of header data pre-pended
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public static byte[] appendWavHeader(int channels, int sampleRate, int bytesPerSecond, int blockAlign, int bitsPerSample, byte[] pcmData ) throws IOException {
	    int size = pcmData.length;
        ByteArrayOutputStream output = new ByteArrayOutputStream(WAV_HEADER_SIZE + size);
        
        // RIFF Header
        output.write( RIFF, 0, RIFF.length );
        
        // Size
        DataParserUtilities.writeUnsignedIntLittleEndian( size + 36, output );
        
        // WAVE Header
        output.write( WAVE, 0, WAVE.length );
        
        // FMT_ chunk
        output.write( FMT_, 0, FMT_.length );
        
        DataParserUtilities.writeUnsignedIntLittleEndian( 16, output ); // fmt_ size, 16 = Audio Format PCM
        DataParserUtilities.writeUnsignedShortLittleEndian ( 1 , output); // audio formaat, 1 = PCM no compression
        DataParserUtilities.writeUnsignedShortLittleEndian ( channels , output);
        DataParserUtilities.writeUnsignedIntLittleEndian( sampleRate, output );
        DataParserUtilities.writeUnsignedIntLittleEndian( bytesPerSecond, output );
        DataParserUtilities.writeUnsignedShortLittleEndian ( blockAlign, output );
        DataParserUtilities.writeUnsignedShortLittleEndian ( bitsPerSample, output );
        
        // data chunk
        output.write( DATA, 0, DATA.length );
        DataParserUtilities.writeUnsignedIntLittleEndian( size, output );
        
        // Raw PCM data
        output.write( pcmData, 0, size );
    
        return output.toByteArray();
    }
}

 

This code relies on a DataParserUtilites class, both the above WavHeaderAppender and the DataParserUtility java files can be found here.