Search code examples
blackberryaudiojava-mecore-audiopcm

J2ME/Blackberry - get audio signal amplitude level?


Is it possible in j2me to measure signal amplitude of audio record made by JSR-135 Player?
I know I can access buffer, but then what?

Target model Bold 9000, supported formats PCM and AMR. Which format I should use?

See also Blackberry Audio Recording Sample Code
How To - Record Audio on a BlackBerry smartphone

Thank you!


Solution

  • Get raw PCM signal level

    • Use menu and trackwheel to zoom in/out and move left/right within graph.
    • Audio format: raw 8000 Hz 16 bit mono pcm.
    • Tested on Bold 9000 RIM OS 4.6
    • Algorythm should work in any mobile, where j2me and pcm is supported, of course implementation may require changes.

    Using thread for audio recording:

    class VoiceNotesRecorderThread extends Thread {
    
        private Player _player;
        private RecordControl _rcontrol;
        private ByteArrayOutputStream _output;
        private byte _data[];
    
        VoiceNotesRecorderThread() {
        }
    
        public void run() {
            try {
                _player = Manager
                    .createPlayer("capture://audio?encoding=audio/basic");
                _player.realize();
                _rcontrol = (RecordControl) _player
                    .getControl("RecordControl");
                _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 {
                _rcontrol.commit();
                _data = _output.toByteArray();
                _output.close();
                _player.close();
            } catch (Exception e) {
                synchronized (UiApplication.getEventLock()) {
                    Dialog.inform(e.toString());
                }
            }
        }
    
        byte[] getData() {
            return _data;
        }
    }
    

    And method for painting graph using byte[] buffer:

    private Bitmap getGraph(byte[] buffer, int zoom, int startFrom) {
        Bitmap result = new Bitmap(Display.getWidth(), Display.getHeight());
        Graphics g = new Graphics(result);
        g.setColor(Color.BLACK);
        int xPos = 0;
        int yPos = Display.getHeight() >> 1;
        for (int i = startFrom; i < buffer.length; i += 2 * zoom) {
            byte[] b = new byte[] { buffer[i], buffer[i + 1] };
            int level = (signedShortToInt(b) * 100 / 32767);
            if (100 < level) {
                level -= 200;
            }
    
            g.drawPoint(xPos, yPos - level);
            xPos++;
        }
        return result;
    }
    
    public static final int signedShortToInt(byte[] b) {
        int result = (b[0] & 0xff) | (b[1] & 0xff) << 8;
        return result;
    }
    

    Screen class:

    class Scr extends MainScreen {
        BitmapField mGraphField = new BitmapField(new Bitmap(Display.getWidth(),
                Display.getHeight()));
    
        private VoiceNotesRecorderThread m_thread;
    
        public Scr() {
            add(mGraphField);
            add(new NullField(FOCUSABLE));
        }
    
        boolean mRecording = false;
        private int mZoom = 1;
        private int mStartFrom = 0;
    
        byte[] mAudioData = null;
    
        protected void makeMenu(Menu menu, int instance) {
            super.makeMenu(menu, instance);
            menu.add(mRecordStopMenuItem);
    
            menu.add(mPaintZoomIn);
            menu.add(mPaintZoomOut);
            menu.add(mPaintZoomToFitScreen);
    
            menu.add(mPaintMoveRight);
            menu.add(mPaintMoveLeft);
            menu.add(mPaintMoveToBegin);
        }
    
        MenuItem mRecordStopMenuItem = new MenuItem("Record", 0, 0) {
            public void run() {
                if (!mRecording) {
                    m_thread = new VoiceNotesRecorderThread();
                    m_thread.start();
                    mRecording = true;
                    this.setText("Stop");
                } else {
                    m_thread.stop();
                    mAudioData = m_thread.getData();
                    zoomToFitScreen();
                    mRecording = false;
                    this.setText("Record");
                }
            }
        };
    
        MenuItem mPaintZoomIn = new MenuItem("Zoom In", 0, 0) {
            public void run() {
                zoomIn();
            }
        };
    
        MenuItem mPaintZoomOut = new MenuItem("Zoom Out", 0, 0) {
            public void run() {
                zoomOut();
            }
        };
    
        MenuItem mPaintZoomToFitScreen = new MenuItem("Fit Screen", 0, 0) {
            public void run() {
                zoomToFitScreen();
            }
        };
    
        MenuItem mPaintMoveLeft = new MenuItem("Left", 0, 0) {
            public void run() {
                moveLeft();
            }
        };
    
        MenuItem mPaintMoveRight = new MenuItem("Right", 0, 0) {
            public void run() {
                moveRight();
            }
        };
    
        MenuItem mPaintMoveToBegin = new MenuItem("To Begin", 0, 0) {
            public void run() {
                moveToBegin();
            }
        };
    
        private void zoomOut() {
            if (mZoom < 200)
                mZoom++;
            mGraphField.setBitmap(getGraph(mAudioData, mZoom, mStartFrom));
        }
    
        private void zoomIn() {
            if (mZoom > 1)
                mZoom--;
            mGraphField.setBitmap(getGraph(mAudioData, mZoom, mStartFrom));
        }
    
        private void zoomToFitScreen() {
            int lenght = mAudioData.length;
            mZoom = (lenght / 2) / Display.getWidth();
            mGraphField.setBitmap(getGraph(mAudioData, mZoom, mStartFrom));
        }
    
        private void moveRight() {
            if (mStartFrom < mAudioData.length - 30)
                mStartFrom += 30;
            mGraphField.setBitmap(getGraph(mAudioData, mZoom, mStartFrom));
        }
    
        private void moveLeft() {
            if (mStartFrom > 30)
                mStartFrom -= 30;
            mGraphField.setBitmap(getGraph(mAudioData, mZoom, mStartFrom));
        }
    
        private void moveToBegin() {
            mStartFrom = 0;
            mGraphField.setBitmap(getGraph(mAudioData, mZoom, mStartFrom));
        }
    
        protected boolean navigationMovement(int dx, int dy, int status, 
            int time) {
    
            if (dx < 0) {
                moveLeft();
            } else if (dx > 0) {
                moveRight();
            }
            if (dy < 0) {
                zoomIn();
            } else if (dy > 0) {
                zoomOut();
            }
            return super.navigationMovement(dx, dy, status, time);
        }
    }
    

    Was helpfull:
    ADC -> integer PCM file -> signal processing
    SO - How is audio represented with numbers?
    Convert byte array to integer