diff --git a/app/build.gradle b/app/build.gradle index 724a11d..a76e372 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,8 +8,8 @@ android { applicationId "com.github.axet.audiorecorder" minSdkVersion 16 targetSdkVersion 23 - versionCode 21 - versionName "1.1.0" + versionCode 22 + versionName "1.1.1" } signingConfigs { release { diff --git a/app/src/main/java/com/github/axet/audiorecorder/activities/RecordingActivity.java b/app/src/main/java/com/github/axet/audiorecorder/activities/RecordingActivity.java index e9b4249..512e600 100644 --- a/app/src/main/java/com/github/axet/audiorecorder/activities/RecordingActivity.java +++ b/app/src/main/java/com/github/axet/audiorecorder/activities/RecordingActivity.java @@ -335,6 +335,7 @@ public class RecordingActivity extends AppCompatActivity { super.onPause(); Log.d(TAG, "onPause"); updateBufferSize(true); + edit(false); pitch.pause(); } @@ -368,7 +369,6 @@ public class RecordingActivity extends AppCompatActivity { void edit(boolean b) { if (b) { state.setText("edit"); - editPlay(false); View box = findViewById(R.id.recording_edit_box); @@ -391,9 +391,9 @@ public class RecordingActivity extends AppCompatActivity { public void onClick(View v) { if (play != null) { editPlay(false); - return; + } else { + editPlay(true); } - editPlay(true); } }); @@ -407,6 +407,7 @@ public class RecordingActivity extends AppCompatActivity { } else { editSample = -1; state.setText("pause"); + editPlay(false); pitch.pause(); View box = findViewById(R.id.recording_edit_box); box.setVisibility(View.GONE); @@ -422,7 +423,30 @@ public class RecordingActivity extends AppCompatActivity { playIndex = editSample; - playUpdate = samplesUpdate; + playUpdate = PitchView.UPDATE_SPEED * sampleRate / 1000; + + final Handler handler = new Handler(); + + AudioTrack.OnPlaybackPositionUpdateListener listener = new AudioTrack.OnPlaybackPositionUpdateListener() { + @Override + public void onMarkerReached(AudioTrack track) { + handler.post(new Runnable() { + @Override + public void run() { + editPlay(false); + } + }); + } + + @Override + public void onPeriodicNotification(AudioTrack track) { + if (play != null) { + playIndex += playUpdate; + float p = playIndex / (float) samplesUpdate; + pitch.play(p); + } + } + }; RawSamples rs = new RawSamples(storage.getTempRecording()); int len = (int) (rs.getSamples() - editSample); @@ -432,25 +456,13 @@ public class RecordingActivity extends AppCompatActivity { play = generateTrack(buf, r); play.play(); play.setPositionNotificationPeriod(playUpdate); - play.setPlaybackPositionUpdateListener(new AudioTrack.OnPlaybackPositionUpdateListener() { - @Override - public void onMarkerReached(AudioTrack track) { - editPlay(false); - pitch.play(-1); - } - - @Override - public void onPeriodicNotification(AudioTrack track) { - playIndex += playUpdate; - long p = playIndex / samplesUpdate; - pitch.play(p); - } - }); + play.setPlaybackPositionUpdateListener(listener, handler); } else { if (play != null) { play.release(); play = null; } + pitch.play(-1); playButton.setImageResource(R.drawable.play); } } @@ -533,6 +545,10 @@ public class RecordingActivity extends AppCompatActivity { Log.e(TAG, "Unable to set Thread Priority " + android.os.Process.THREAD_PRIORITY_URGENT_AUDIO); } + long startTime = System.currentTimeMillis(); + // start recording after 0.5 sec + long goTime = startTime + 500; + RawSamples rs = null; AudioRecord recorder = null; try { @@ -565,6 +581,8 @@ public class RecordingActivity extends AppCompatActivity { short[] buffer = null; while (!Thread.currentThread().isInterrupted()) { + long cur = System.currentTimeMillis(); + synchronized (bufferSize) { if (buffer == null || buffer.length != bufferSize) buffer = new short[bufferSize]; @@ -575,29 +593,31 @@ public class RecordingActivity extends AppCompatActivity { break; } - rs.write(buffer); + if (cur > goTime) { + rs.write(buffer); - int pa = getPa(buffer, 0, readSize); + int pa = getPa(buffer, 0, readSize); - int s = CHANNEL_CONFIG == AudioFormat.CHANNEL_IN_MONO ? readSize : readSize / 2; + int s = CHANNEL_CONFIG == AudioFormat.CHANNEL_IN_MONO ? readSize : readSize / 2; - samplesUpdateCount += s; - if (samplesUpdateCount >= samplesUpdate) { - pitch.add(pa); - samplesUpdateCount -= samplesUpdate; - } + samplesUpdateCount += s; + if (samplesUpdateCount >= samplesUpdate) { + pitch.add(pa); + samplesUpdateCount -= samplesUpdate; + } - samplesTime += s; - samplesTimeCount += s; - if (samplesTimeCount > samplesTimeUpdate) { - final long m = samplesTime; - handle.post(new Runnable() { - @Override - public void run() { - updateSamples(m); - } - }); - samplesTimeCount -= samplesTimeUpdate; + samplesTime += s; + samplesTimeCount += s; + if (samplesTimeCount > samplesTimeUpdate) { + final long m = samplesTime; + handle.post(new Runnable() { + @Override + public void run() { + updateSamples(m); + } + }); + samplesTimeCount -= samplesTimeUpdate; + } } } } catch (final RuntimeException e) { diff --git a/app/src/main/java/com/github/axet/audiorecorder/widgets/PitchView.java b/app/src/main/java/com/github/axet/audiorecorder/widgets/PitchView.java index d6e2a86..5b25110 100644 --- a/app/src/main/java/com/github/axet/audiorecorder/widgets/PitchView.java +++ b/app/src/main/java/com/github/axet/audiorecorder/widgets/PitchView.java @@ -11,6 +11,7 @@ import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.os.Build; import android.os.Handler; +import android.os.Looper; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; @@ -66,7 +67,9 @@ public class PitchView extends ViewGroup { // index int editPos = 0; int editCount = 0; - int playPos = -1; + // current playing position in samples + float playPos = -1; + Runnable play; Runnable draw; Thread thread; @@ -191,7 +194,7 @@ public class PitchView extends ViewGroup { canvas.drawLine(x, 0, x, getHeight(), editPaint); } - if (edit != null && playPos != -1) { + if (edit != null && playPos > 0) { float x = playPos * pitchSize + pitchSize / 2f; canvas.drawLine(x, 0, x, getHeight(), playPaint); } @@ -378,6 +381,11 @@ public class PitchView extends ViewGroup { public void add(int a) { data.add(a / 100.0f); + + // after pause, we still may get one last sample. force view redraw. + if (thread == null) { + draw(); + } } public void draw() { @@ -447,10 +455,13 @@ public class PitchView extends ViewGroup { thread.interrupt(); thread = null; } + if (edit != null) edit = null; if (draw != null) draw = null; + if (play != null) + play = null; drawEdit(); } @@ -478,6 +489,13 @@ public class PitchView extends ViewGroup { thread = null; } } + + edit(); + + return samples + editPos; + } + + public void edit() { if (thread == null) { edit = new Runnable() { @Override @@ -508,8 +526,6 @@ public class PitchView extends ViewGroup { thread = new Thread(edit, TAG); thread.start(); } - - return samples + editPos; } public void resume() { @@ -520,6 +536,13 @@ public class PitchView extends ViewGroup { thread = null; } } + if (play != null) { + play = null; + if (thread != null) { + thread.interrupt(); + thread = null; + } + } if (thread == null) { draw = new Runnable() { @Override @@ -548,14 +571,60 @@ public class PitchView extends ViewGroup { } } - public void play(long pos) { + // current paying pos in actual samples + public void play(float pos) { synchronized (this) { - playPos = (int) (pos - samples); + playPos = pos - samples; + + editCount = 0; if (playPos < 0) playPos = -1; - drawEdit(); + if (playPos < 0) { + if (play != null) { + play = null; + if (thread != null) { + thread.interrupt(); + thread = null; + } + } + if (thread == null) { + edit(); + } + return; + } + } + + if (play == null && thread != null) { + thread.interrupt(); + thread = null; + } + + if (thread == null) { + play = new Runnable() { + @Override + public void run() { + time = System.currentTimeMillis(); + while (!Thread.currentThread().isInterrupted()) { + long time = System.currentTimeMillis(); + drawEdit(); + long cur = System.currentTimeMillis(); + + long delay = UPDATE_SPEED - (cur - time); + + if (delay > 0) { + try { + Thread.sleep(delay); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + } + }; + thread = new Thread(play, TAG); + thread.start(); } } }