Skip to content

Commit

Permalink
add merge func
Browse files Browse the repository at this point in the history
  • Loading branch information
DrustZ committed May 1, 2019
1 parent ca79871 commit e2bd9f9
Show file tree
Hide file tree
Showing 4 changed files with 272 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package com.userempowermentlab.aasrecorder.Data;


import android.util.Log;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class AudioMediaOperations {
public interface OperationCallbacks {
public void onAudioOperationFinished();

public void onAudioOperationError(Exception e);
}

public static void MergeAudios(String[] selection, String outpath, OperationCallbacks callback) {
int RECORDER_SAMPLERATE = 0;
try {
DataOutputStream amplifyOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(outpath)));
DataInputStream[] mergeFilesStream = new DataInputStream[selection.length];
long[] sizes = new long[selection.length];
for (int i = 0; i < selection.length; i++) {
File file = new File(selection[i]);
sizes[i] = (file.length() - 44) / 2;
}
for (int i = 0; i < selection.length; i++) {
mergeFilesStream[i] = new DataInputStream(new BufferedInputStream(new FileInputStream(selection[i])));

if (i == selection.length - 1) {
mergeFilesStream[i].skip(24);
byte[] sampleRt = new byte[4];
mergeFilesStream[i].read(sampleRt);
ByteBuffer bbInt = ByteBuffer.wrap(sampleRt).order(ByteOrder.LITTLE_ENDIAN);
RECORDER_SAMPLERATE = bbInt.getInt();
mergeFilesStream[i].skip(16);
} else {
mergeFilesStream[i].skip(44);
}
}

for (int b = 0; b < selection.length; b++) {
for (int i = 0; i < (int) sizes[b]; i++) {
byte[] dataBytes = new byte[2];
try {
dataBytes[0] = mergeFilesStream[b].readByte();
dataBytes[1] = mergeFilesStream[b].readByte();
} catch (EOFException e) {
amplifyOutputStream.close();
}
short dataInShort = ByteBuffer.wrap(dataBytes).order(ByteOrder.LITTLE_ENDIAN).getShort();
float dataInFloat = (float) dataInShort / 37268.0f;

short outputSample = (short) (dataInFloat * 37268.0f);
byte[] dataFin = new byte[2];
dataFin[0] = (byte) (outputSample & 0xff);
dataFin[1] = (byte) ((outputSample >> 8) & 0xff);
amplifyOutputStream.write(dataFin, 0, 2);

}
}
amplifyOutputStream.close();
for (int i = 0; i < selection.length; i++) {
mergeFilesStream[i].close();
}

} catch (FileNotFoundException e) {
if (callback != null) {
callback.onAudioOperationError(e);
}
e.printStackTrace();
} catch (IOException e) {
if (callback != null) {
callback.onAudioOperationError(e);
}
e.printStackTrace();
}

long size = 0;
try {
FileInputStream fileSize = new FileInputStream(outpath);
size = fileSize.getChannel().size();
fileSize.close();
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

final int RECORDER_BPP = 16;

long datasize = size + 36;
long byteRate = (RECORDER_BPP * RECORDER_SAMPLERATE) / 8;
long longSampleRate = RECORDER_SAMPLERATE;
byte[] header = new byte[44];

header[0] = 'R'; // RIFF/WAVE header
header[1] = 'I';
header[2] = 'F';
header[3] = 'F';
header[4] = (byte) (datasize & 0xff);
header[5] = (byte) ((datasize >> 8) & 0xff);
header[6] = (byte) ((datasize >> 16) & 0xff);
header[7] = (byte) ((datasize >> 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) 1;
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) ((RECORDER_BPP) / 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) (size & 0xff);
header[41] = (byte) ((size >> 8) & 0xff);
header[42] = (byte) ((size >> 16) & 0xff);
header[43] = (byte) ((size >> 24) & 0xff);
// out.write(header, 0, 44);

try {
RandomAccessFile rFile = new RandomAccessFile(outpath, "rw");
rFile.seek(0);
rFile.write(header);
rFile.close();
if (callback != null) {
callback.onAudioOperationFinished();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
if (callback != null) {
callback.onAudioOperationError(e);
}
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
if (callback != null) {
callback.onAudioOperationError(e);
}
e.printStackTrace();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -296,8 +296,9 @@ public String getRecordingNameOfTimeWithPrefix(String prefix) {
* @param preceding_mode if preceding mode is on and the file shouldkeep is true,
* it will also keep most recent two files in mShouldNotKeepBuffer, and move them to mFolderFileList
* Because they are the preceding recordings of the formal recording file
* @param merge_with_preceding if shouldkeep is true, whether the current file should merge with the preceding file into one file.
*/
public void newRecordingAdded(String filename, String createdate, int duration, boolean shouldkeep, boolean preceding_mode) {
public void newRecordingAdded(String filename, String createdate, int duration, boolean shouldkeep, boolean preceding_mode, boolean merge_with_preceding) {
RecordItem newitem = new RecordItem();
newitem.path = filename;
String[] tokens = filename.split("/");
Expand All @@ -312,23 +313,44 @@ public void newRecordingAdded(String filename, String createdate, int duration,
// if in preceding mode and the shouldkeep is true, it means the recording file is triggered intentionally
// rather than the background recording. Thus we should store its preceding two clips
int bfsize = mShouldNotKeepBuffer.size();
// we set bfsize - 2 because we want preceding two file clips, as only one preceding might not be long enough
for (int i = bfsize-1; i >= Math.max(0, bfsize-2); --i){
RecordItem item = mShouldNotKeepBuffer.remove(i);
item.should_keep = true;
mFolderFileList.add(0, item);
new updateAsyncTask(recordItemDAO).execute(item);
if (autoUpload){
if (bufferSize == 0){
uploadFile(item.path);
} else {
synchronized (mFileBuffer) {
mFileBuffer.add(item.path);

//if should merge files
if (merge_with_preceding){
// we set bfsize - 2 because we want preceding two file clips, as only one preceding might not be long enough
float mtime = 0;
ArrayList<String> mergelist = new ArrayList<String>();
for (int i = bfsize - 1; i >= Math.max(0, bfsize - 2); --i) {
RecordItem item = mShouldNotKeepBuffer.remove(i);
mtime += item.duration;
new deleteAsyncTask(recordItemDAO).execute(item);
mergelist.add(item.path);
}
newitem.duration += mtime;
mergelist.add(filename);
MergeThread mtd = new MergeThread(mergelist.toArray(new String[0]));
mtd.start();
}
//else we save them one by one
else {
// we set bfsize - 2 because we want preceding two file clips, as only one preceding might not be long enough
for (int i = bfsize - 1; i >= Math.max(0, bfsize - 2); --i) {
RecordItem item = mShouldNotKeepBuffer.remove(i);
item.should_keep = true;
mFolderFileList.add(0, item);
new updateAsyncTask(recordItemDAO).execute(item);
if (autoUpload) {
if (bufferSize == 0) {
uploadFile(item.path);
} else {
synchronized (mFileBuffer) {
mFileBuffer.add(item.path);
}
}
}
}
}
}

mFolderFileList.add(0, newitem);
new insertAsyncTask(recordItemDAO).execute(newitem);
} else {
Expand All @@ -346,17 +368,19 @@ public void newRecordingAdded(String filename, String createdate, int duration,
}

deleteFilesOutOfMaxFiles();
if (autoUpload) {
processUpload(filename, shouldkeep);
}

private void processUpload(String filename, boolean shouldkeep) {
if (autoUpload && shouldkeep) {
// if no buffer, upload new files
if (bufferSize == 0 && shouldkeep) {
if (bufferSize == 0) {
//upload
uploadFile(filename);
} else {
if (shouldkeep) {
synchronized (mFileBuffer) {
mFileBuffer.add(filename);
storeBuffer();
}
synchronized (mFileBuffer) {
mFileBuffer.add(filename);
storeBuffer();
}
if (mFileBuffer.size() > bufferSize)
uploadBuffer();
Expand Down Expand Up @@ -470,4 +494,42 @@ protected Void doInBackground(final RecordItem... params) {
return null;
}
}

private class MergeThread extends Thread {
String[] fnames;
String outpath;
public MergeThread(String[] fnames){
this.fnames = fnames;
outpath = fnames[0].replace(".wav", "-merge.wav");
}

public void run() {
AudioMediaOperations.MergeAudios(fnames, outpath, new AudioMediaOperations.OperationCallbacks() {
@Override
public void onAudioOperationFinished() {
//delete fnames
// Log.e("[Log]", "onAudioOperationFinished: Merge Finished!" + fnames[fnames.length-1]);
for (String s: fnames){
File f = new File(s);
f.delete();
}
File from = new File(outpath);
File to = new File(fnames[fnames.length-1]);
from.renameTo(to);
deleteFilesOutOfMaxFiles();
processUpload(fnames[fnames.length-1], true);
}

@Override
public void onAudioOperationError(Exception e) {
// Log.e("[Log]", "onAudioOperationFinished: Merge Failed...!");
for (String s: fnames){
File f = new File(s);
f.delete();
}
}
});

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ public class RecordingManager extends Service {
private boolean alwaysRunning = false; //always run in background (useful if want to record when the app is in background)

private boolean should_precede = false; // whether to record preceding or not
private boolean should_mergeprecede = true; // whether to merge the record with its preceding or not

private int precedingTime = 0; //enable preceding time record , in ms
private boolean should_keep = true; // if should_keep && auto_upload, the file would be upload, otherwise it won't

Expand Down Expand Up @@ -74,6 +76,13 @@ public void setShouldPrecede(boolean should_precede) {
this.should_precede = should_precede;
}

/**
* whether the recorder should merge the recording with its preceding clips
*/
public void setShouldMergePrecede(boolean should_meregprecede) {
this.should_mergeprecede = should_meregprecede;
}

/**
* Set time for preceding clips before the formal recording start.
* @param precedingTime how long will be recorded before the formal recording is triggered (in second)
Expand Down Expand Up @@ -277,7 +286,7 @@ public void StopRecordingSilently(){
recorder.Stop();
}
if (manager != null) {
manager.newRecordingAdded(recorder.getFilePath(), recorder.getStartDateTime(), recorder.getDuration(), should_keep, should_precede);
manager.newRecordingAdded(recorder.getFilePath(), recorder.getStartDateTime(), recorder.getDuration(), should_keep, should_precede, should_mergeprecede);
}
}

Expand Down
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ allprojects {
repositories {
google()
jcenter()
maven {
url 'https://mvnrepository.com/artifact/com.android.tools.lint/lint-gradle-api'
}
}
}

Expand Down

0 comments on commit e2bd9f9

Please sign in to comment.