Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds recording playback back in #14

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,5 @@ dependencies {
compile 'com.google.code.gson:gson:2.3'
compile 'com.github.PhilJay:MPAndroidChart:v2.1.0'
compile 'joda-time:joda-time:2.4'
compile 'commons-io:commons-io:2.5'
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package de.lilithwittmann.de.voicepitchanalyzer;
package lilithwittmann.de.voicepitchanalyzer;

import android.app.Application;
import android.test.ApplicationTestCase;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package lilithwittmann.de.voicepitchanalyzer;

import android.test.ActivityUnitTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import de.lilithwittmann.voicepitchanalyzer.activities.RecordingActivity;
import de.lilithwittmann.voicepitchanalyzer.utils.AudioPlayer;

import java.io.ByteArrayInputStream;
import java.io.InputStream;

public class AudioPlayerTest extends ActivityUnitTestCase<RecordingActivity> {
public AudioPlayerTest() {
super(RecordingActivity.class);
}

@SmallTest
public void testGivenAudioPlayerPlayedOnce_whenReplayed_thenDoesNotThrowException() throws InterruptedException {
ByteArrayInputStream blankAudio = new ByteArrayInputStream(new byte[0]);
AudioPlayer player = new AudioPlayer(blankAudio);
player.play();
Thread.sleep(100);

player.play();
}

@SmallTest
public void testGivenActiveAudioPlayer_whenStopped_thenStopsPlaying() throws InterruptedException {
InputStream audio = loadResource("/test-audio.pcm");
AudioPlayer player = new AudioPlayer(audio);
player.play();

player.stop();

assertFalse(player.isPlaying());
}

private InputStream loadResource(String fileName) {
return AudioPlayerTest.class.getResourceAsStream(fileName);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package de.lilithwittmann.voicepitchanalyzer;
package lilithwittmann.de.voicepitchanalyzer;

import android.test.ActivityUnitTestCase;
import android.test.suitebuilder.annotation.SmallTest;
Expand Down
Binary file added app/src/androidTest/resources/test-audio.pcm
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import de.lilithwittmann.voicepitchanalyzer.models.Recording;
import de.lilithwittmann.voicepitchanalyzer.models.Texts;
import de.lilithwittmann.voicepitchanalyzer.models.database.RecordingDB;
import de.lilithwittmann.voicepitchanalyzer.models.database.StorageMaintainer;
import de.lilithwittmann.voicepitchanalyzer.utils.AudioRecorder;
import de.lilithwittmann.voicepitchanalyzer.utils.PitchCalculator;
import de.lilithwittmann.voicepitchanalyzer.utils.SampleRateCalculator;
Expand Down Expand Up @@ -178,6 +179,9 @@ public void onClick(View v)
RecordingDB recordingDB = new RecordingDB(getActivity());
currentRecord = recordingDB.saveRecording(currentRecord);

StorageMaintainer maintainer = new StorageMaintainer(getActivity());
maintainer.cleanupStorage();

v.setVisibility(View.INVISIBLE);

if (mListener != null)
Expand Down Expand Up @@ -460,9 +464,9 @@ public void run()
e.printStackTrace();
}

//this.recorder = new AudioRecorder(this.sampleRate, this.bufferRate, fos);
this.recorder = new AudioRecorder(this.sampleRate, this.bufferRate, fos);
dispatcher.addAudioProcessor(p);
//dispatcher.addAudioProcessor(recorder);
dispatcher.addAudioProcessor(recorder);
this.recordThread = new Thread(dispatcher, "Audio Dispatcher");
this.recordThread.start();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
package de.lilithwittmann.voicepitchanalyzer.fragments;


import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.TextView;

import de.lilithwittmann.voicepitchanalyzer.R;
import de.lilithwittmann.voicepitchanalyzer.activities.RecordViewActivity;
import de.lilithwittmann.voicepitchanalyzer.utils.AudioPlayer;
import de.lilithwittmann.voicepitchanalyzer.utils.PitchCalculator;


/**
* A simple {@link Fragment} subclass.
*/
public class RecordingPlayFragment extends Fragment
{
private static final String ARG_SECTION_NUMBER = "section_number";
Expand Down Expand Up @@ -95,29 +94,94 @@ else if (average > PitchCalculator.maxMalePitch)
((TextView) view.findViewById(R.id.personal_range)).setText(getResources().getString(R.string.unknown));
}

// ((ImageButton) view.findViewById(R.id.play_button)).setOnClickListener(new View.OnClickListener()
// {
// @Override
// public void onClick(View v)
// {
// if (player == null)
// {
// player = new AudioPlayer(getActivity().getFileStreamPath(RecordViewActivity.currentRecord.getRecording()));
// }
//
// if (player.isPlaying())
// {
// Log.i(LOG_TAG, "stop");
// player.stop();
// }
//
// else
// {
// Log.i(LOG_TAG, "play");
// player.play();
// }
// }
// });
initializePlayButton(view);
}
}

private void initializePlayButton(View view) {
final ImageButton playButton = ((ImageButton) view.findViewById(R.id.play_button));

String audioFile = RecordViewActivity.currentRecord.getRecording();
if (audioFile == null || audioFile.isEmpty())
{
playButton.setVisibility(View.GONE);
return;
}

playButton.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
if (player == null)
{
player = createAudioPlayer(v);
}

if (player.isPlaying())
{
Log.i(LOG_TAG, "stop");
player.stop();
playButton.setImageResource(R.drawable.ic_play);
}

else
{
Log.i(LOG_TAG, "play");
player.play();
playButton.setImageResource(R.drawable.ic_pause_circle_black);
}
}
});
}

private AudioPlayer createAudioPlayer(final View view) {
AudioPlayer player = new AudioPlayer(getActivity().getFileStreamPath(RecordViewActivity.currentRecord.getRecording()));

AudioEndHandler handler = new AudioEndHandler(view);
player.setOnComplete(handler);

return player;
}

private static class AudioEndHandler extends Handler {
private final View view;

AudioEndHandler(View view) {
this.view = view;
}

public void handleMessage(Message msg)
{
final ImageButton playButton = ((ImageButton) view.findViewById(R.id.play_button));
playButton.setImageResource(R.drawable.ic_play);
}
}

public void onStop() {
super.onStop();
stopAudio();
}

public void onPause() {
super.onPause();
stopAudio();
}

public void onDestroyView() {
super.onDestroyView();
stopAudio();
}

private void stopAudio() {
try
{
if (player != null)
player.stop();
} catch (Exception ex)
{
ex.printStackTrace();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ public Recording saveRecording(Recording recording) {
}

public List<Recording> getRecordings() {
return getRecordings(null);
}

public List<Recording> getRecordings(String where) {
List<Recording> recordings = new ArrayList<Recording>();
RecordingDbHelper mDbHelper = new RecordingDbHelper(this.context);
SQLiteDatabase db = mDbHelper.getReadableDatabase();
Expand All @@ -120,7 +124,7 @@ public List<Recording> getRecordings() {
Cursor c = db.query(
RecordingEntry.TABLE_NAME, // The table to query
projection, // The columns to return
null, // The columns for the WHERE clause
where, // The columns for the WHERE clause
null, // The values for the WHERE clause
null, // don't group the rows
null, // don't filter by row groups
Expand All @@ -141,6 +145,7 @@ public List<Recording> getRecordings() {
recordings.add(recording);
c.moveToNext();
}

return recordings;
}

Expand Down Expand Up @@ -202,7 +207,23 @@ public void deleteRecording(long id) {

selection = PitchEntry._ID + " LIKE ?";
db.delete(RecordingEntry.TABLE_NAME, selection, selectionArgs);
}

public void updateRecordingFile(long id, String file) {
ContentValues updates = new ContentValues();
updates.put(RecordingEntry.COLUMN_NAME_FILE, file);

updateRecording(id, updates);
}

public void updateRecording(long id, ContentValues updates) {
RecordingDbHelper mDbHelper = new RecordingDbHelper(this.context);
SQLiteDatabase db = mDbHelper.getReadableDatabase();

String whereClause = RecordingEntry._ID + " LIKE ?";
String[] whereParams = {String.valueOf(id)};

db.update(RecordingEntry.TABLE_NAME, updates, whereClause, whereParams);
}

private List<Double> getPitch(long id) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package de.lilithwittmann.voicepitchanalyzer.models.database;


import android.content.Context;
import android.util.Log;
import com.crashlytics.android.Crashlytics;
import de.lilithwittmann.voicepitchanalyzer.models.Recording;

import java.io.File;
import java.util.List;

import static de.lilithwittmann.voicepitchanalyzer.models.database.RecordingDB.RecordingEntry.*;

/**
* Saving many recording files to disk every day can chew up phone storage space so here we make
* sure storage used does not exceed a certain threshold.
*/
public class StorageMaintainer {
private static final int SAVED_RECORDINGS_LIMIT = 100;
private final Context context;

public StorageMaintainer(Context context) {
this.context = context;
}

public void cleanupStorage() {
try
{
removeOldRecordingFiles();
} catch (Exception ex) {
// Don't want to crash the app on a non completely necessary maintenance task so ignore exceptions.
ex.printStackTrace();
Crashlytics.log(Log.DEBUG, "storage limiter", "Error removing old recording files: " + ex.getMessage());
}
}

private void removeOldRecordingFiles() {
RecordingDB db = new RecordingDB(context);

List<Recording> recordings = db.getRecordings(COLUMN_NAME_FILE + " IS NOT NULL");
Log.d("storage maintainer", "Found " + recordings.size() + " recordings with files.");

if (recordings.size() < SAVED_RECORDINGS_LIMIT)
return;

List<Recording> toDelete = recordings.subList(SAVED_RECORDINGS_LIMIT, recordings.size());
int deletedCount = 0;

for (Recording recordOn : toDelete)
deletedCount += removeRecordingFile(db, recordOn);

Log.d("storage maintainer", "Deleted " + deletedCount + " recording files.");
}

private int removeRecordingFile(RecordingDB db, Recording toDelete) {
File file = context.getFileStreamPath(toDelete.getRecording());
int status = 0;

if (file != null && file.exists())
{
if (file.delete())
status = 1;
}

db.updateRecordingFile(toDelete.getId(), null);

return status;
}
}
Loading