edu.stanford.mobisocial.dungbeetle.feed.objects.VoiceObj.java Source code

Java tutorial

Introduction

Here is the source code for edu.stanford.mobisocial.dungbeetle.feed.objects.VoiceObj.java

Source

/*
 * Copyright (C) 2011 The Stanford MobiSocial Laboratory
 *
 * This file is part of Musubi, a mobile social network.
 *
 *  This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

package edu.stanford.mobisocial.dungbeetle.feed.objects;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import mobisocial.socialkit.Obj;
import mobisocial.socialkit.SignedObj;

import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.content.Context;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaPlayer;
import android.os.Environment;
import android.util.Base64;
import android.util.Log;
import android.util.Pair;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import edu.stanford.mobisocial.dungbeetle.R;
import edu.stanford.mobisocial.dungbeetle.feed.iface.Activator;
import edu.stanford.mobisocial.dungbeetle.feed.iface.DbEntryHandler;
import edu.stanford.mobisocial.dungbeetle.feed.iface.FeedRenderer;
import edu.stanford.mobisocial.dungbeetle.feed.iface.OutgoingMessageHandler;
import edu.stanford.mobisocial.dungbeetle.feed.iface.UnprocessedMessageHandler;
import edu.stanford.mobisocial.dungbeetle.model.Contact;
import edu.stanford.mobisocial.dungbeetle.model.DbObject;
import edu.stanford.mobisocial.dungbeetle.util.FastBase64;

/**
 * A short audio clip. "Version 0" uses a sample rate of 8000, mono channel, and
 * 16bit pcm recording.
 */
public class VoiceObj extends DbEntryHandler
        implements FeedRenderer, Activator, OutgoingMessageHandler, UnprocessedMessageHandler {
    public static final String TAG = "VoiceObj";

    public static final String TYPE = "voice";
    public static final String DATA = "data";

    private static final int RECORDER_BPP = 16;
    private static final int RECORDER_SAMPLERATE = 8000;
    private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_CONFIGURATION_MONO;
    private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;

    @Override
    public String getType() {
        return TYPE;
    }

    public JSONObject mergeRaw(JSONObject objData, byte[] raw) {
        try {
            if (raw != null)
                objData = objData.put(DATA, Base64.encodeToString(raw, Base64.DEFAULT));
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return objData;
    }

    @Override
    public Pair<JSONObject, byte[]> splitRaw(JSONObject json) {
        byte[] raw = Base64.decode(json.optString(DATA), Base64.DEFAULT);
        json.remove(DATA);
        return new Pair<JSONObject, byte[]>(json, raw);
    }

    public static DbObject from(byte[] data) {
        return new DbObject(TYPE, new JSONObject(), data);
    }

    public static JSONObject json(byte[] data) {
        String encoded = Base64.encodeToString(data, Base64.DEFAULT);
        JSONObject obj = new JSONObject();
        try {
            obj.put("data", encoded);
        } catch (JSONException e) {
        }
        return obj;
    }

    public void render(Context context, ViewGroup frame, Obj obj, boolean allowInteractions) {
        ImageView imageView = new ImageView(context);
        imageView.setImageResource(R.drawable.play);
        imageView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
                LinearLayout.LayoutParams.WRAP_CONTENT));
        frame.addView(imageView);
    }

    private String getTempFilename() {
        return Environment.getExternalStorageDirectory().getAbsolutePath() + "/temp.raw";
    }

    private String getFilename() {
        return Environment.getExternalStorageDirectory().getAbsolutePath() + "/temp.wav";
    }

    @Override
    public void activate(final Context context, final SignedObj obj) {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                byte[] bytes = obj.getRaw();
                if (bytes == null) {
                    Pair<JSONObject, byte[]> p = splitRaw(obj.getJson());
                    //                content = p.first;
                    bytes = p.second;
                }

                /*AudioTrack track = new AudioTrack(AudioManager.STREAM_MUSIC, RECORDER_SAMPLERATE, RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING, bytes.length, AudioTrack.MODE_STATIC);
                track.write(bytes, 0, bytes.length);
                try { // TODO: hack.
                Thread.sleep(500);
                 } catch (InterruptedException e) {
                 }
                track.play();
                */
                /****/

                File file = new File(getTempFilename());
                try {
                    OutputStream os = new FileOutputStream(file);
                    BufferedOutputStream bos = new BufferedOutputStream(os);
                    bos.write(bytes, 0, bytes.length);
                    bos.flush();
                    bos.close();

                    copyWaveFile(getTempFilename(), getFilename());
                    deleteTempFile();

                    MediaPlayer mp = new MediaPlayer();

                    mp.setDataSource(getFilename());
                    mp.prepare();
                    mp.start();
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        };
        if (context instanceof Activity) {
            ((Activity) context).runOnUiThread(r);
        } else {
            r.run();
        }
    }

    private void deleteTempFile() {
        File file = new File(getTempFilename());

        file.delete();
    }

    private void copyWaveFile(String inFilename, String outFilename) {
        FileInputStream in = null;
        FileOutputStream out = null;
        long totalAudioLen = 0;
        long totalDataLen = totalAudioLen + 36;
        long longSampleRate = RECORDER_SAMPLERATE;
        int channels = 1;
        long byteRate = RECORDER_BPP * RECORDER_SAMPLERATE * channels / 8;

        int bufferSize = AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE, RECORDER_CHANNELS,
                RECORDER_AUDIO_ENCODING);
        byte[] data = new byte[bufferSize];

        try {
            in = new FileInputStream(inFilename);
            out = new FileOutputStream(outFilename);
            totalAudioLen = in.getChannel().size();
            totalDataLen = totalAudioLen + 36;

            Log.w("PlayAllAudioAction", "File size: " + totalDataLen);

            WriteWaveFileHeader(out, totalAudioLen, totalDataLen, longSampleRate, channels, byteRate);

            while (in.read(data) != -1) {
                out.write(data);
            }

            in.close();
            out.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void WriteWaveFileHeader(FileOutputStream out, long totalAudioLen, long totalDataLen,
            long longSampleRate, int channels, long byteRate) throws IOException {

        byte[] header = new byte[44];

        header[0] = 'R'; // RIFF/WAVE header
        header[1] = 'I';
        header[2] = 'F';
        header[3] = 'F';
        header[4] = (byte) (totalDataLen & 0xff);
        header[5] = (byte) ((totalDataLen >> 8) & 0xff);
        header[6] = (byte) ((totalDataLen >> 16) & 0xff);
        header[7] = (byte) ((totalDataLen >> 24) & 0xff);
        header[8] = 'W';
        header[9] = 'A';
        header[10] = 'V';
        header[11] = 'E';
        header[12] = 'f'; // 'fmt ' chunk
        header[13] = 'm';
        header[14] = 't';
        header[15] = ' ';
        header[16] = 16; // 4 bytes: size of 'fmt ' chunk
        header[17] = 0;
        header[18] = 0;
        header[19] = 0;
        header[20] = 1; // format = 1
        header[21] = 0;
        header[22] = (byte) channels;
        header[23] = 0;
        header[24] = (byte) (longSampleRate & 0xff);
        header[25] = (byte) ((longSampleRate >> 8) & 0xff);
        header[26] = (byte) ((longSampleRate >> 16) & 0xff);
        header[27] = (byte) ((longSampleRate >> 24) & 0xff);
        header[28] = (byte) (byteRate & 0xff);
        header[29] = (byte) ((byteRate >> 8) & 0xff);
        header[30] = (byte) ((byteRate >> 16) & 0xff);
        header[31] = (byte) ((byteRate >> 24) & 0xff);
        header[32] = (byte) (2 * 16 / 8); // block align
        header[33] = 0;
        header[34] = RECORDER_BPP; // bits per sample
        header[35] = 0;
        header[36] = 'd';
        header[37] = 'a';
        header[38] = 't';
        header[39] = 'a';
        header[40] = (byte) (totalAudioLen & 0xff);
        header[41] = (byte) ((totalAudioLen >> 8) & 0xff);
        header[42] = (byte) ((totalAudioLen >> 16) & 0xff);
        header[43] = (byte) ((totalAudioLen >> 24) & 0xff);

        out.write(header, 0, 44);
    }

    @Override
    public Pair<JSONObject, byte[]> handleOutgoing(JSONObject json) {
        byte[] bytes = Base64.decode(json.optString(DATA), Base64.DEFAULT);
        json.remove(DATA);
        return new Pair<JSONObject, byte[]>(json, bytes);
    }

    @Override
    public void handleDirectMessage(Context context, Contact from, JSONObject msg) {
        // TODO Auto-generated method stub

    }

    @Override
    public Pair<JSONObject, byte[]> handleUnprocessed(Context context, JSONObject msg) {
        byte[] bytes = FastBase64.decode(msg.optString(DATA));
        msg.remove(DATA);
        return new Pair<JSONObject, byte[]>(msg, bytes);
    }

}