From 36e9e8b4d2a68f19ffa59bab7633b875faa861f3 Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Sun, 13 Mar 2016 09:21:20 +0300 Subject: [PATCH] clean threads --- .../activities/MainActivity.java | 29 +- .../activities/RecordingActivity.java | 101 +++---- .../audiorecorder/app/MainApplication.java | 23 ++ .../axet/audiorecorder/app/Storage.java | 10 +- .../axet/audiorecorder/widgets/PitchView.java | 260 ++++++++++-------- 5 files changed, 222 insertions(+), 201 deletions(-) diff --git a/app/src/main/java/com/github/axet/audiorecorder/activities/MainActivity.java b/app/src/main/java/com/github/axet/audiorecorder/activities/MainActivity.java index 662bdb4..5a7de3a 100644 --- a/app/src/main/java/com/github/axet/audiorecorder/activities/MainActivity.java +++ b/app/src/main/java/com/github/axet/audiorecorder/activities/MainActivity.java @@ -122,29 +122,6 @@ public class MainActivity extends AppCompatActivity implements AbsListView.OnScr } } - String formatTime(int tt) { - return String.format("%02d", tt); - } - - String formatDuration(long diff) { - int diffMilliseconds = (int) (diff % 1000); - int diffSeconds = (int) (diff / 1000 % 60); - int diffMinutes = (int) (diff / (60 * 1000) % 60); - int diffHours = (int) (diff / (60 * 60 * 1000) % 24); - int diffDays = (int) (diff / (24 * 60 * 60 * 1000)); - - String str = ""; - - if (diffDays > 0) - str = diffDays + "d " + formatTime(diffHours) + ":" + formatTime(diffMinutes) + ":" + formatTime(diffSeconds); - else if (diffHours > 0) - str = formatTime(diffHours) + ":" + formatTime(diffMinutes) + ":" + formatTime(diffSeconds); - else - str = formatTime(diffMinutes) + ":" + formatTime(diffSeconds); - - return str; - } - @Override public View getView(final int position, View convertView, ViewGroup parent) { LayoutInflater inflater = LayoutInflater.from(getContext()); @@ -172,7 +149,7 @@ public class MainActivity extends AppCompatActivity implements AbsListView.OnScr time.setText(s.format(new Date(f.lastModified()))); TextView dur = (TextView) convertView.findViewById(R.id.recording_duration); - dur.setText(formatDuration(duration.get(f))); + dur.setText(MainApplication.formatDuration(duration.get(f))); TextView size = (TextView) convertView.findViewById(R.id.recording_size); size.setText(formatSize(f.length())); @@ -358,9 +335,9 @@ public class MainActivity extends AppCompatActivity implements AbsListView.OnScr d = player.getDuration(); } - start.setText(formatDuration(c)); + start.setText(MainApplication.formatDuration(c)); bar.setProgress(c * 100 / d); - end.setText("-" + formatDuration(d - c)); + end.setText("-" + MainApplication.formatDuration(d - c)); } } 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 288b044..d8b69f9 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 @@ -63,29 +63,30 @@ public class RecordingActivity extends AppCompatActivity { RecordingReceiver receiver; PhoneStateChangeListener pscl = new PhoneStateChangeListener(); Handler handle = new Handler(); - Thread thread; - short[] buffer; FileEncoder encoder; - TextView title; - TextView time; - TextView state; - ImageButton pause; - + Thread thread; + short[] buffer; int sampleRate; int channelConfig; int audioFormat; // how many samples count need to update view. 4410 for 100ms update. int samplesUpdate; - File file; + TextView title; + TextView time; + TextView state; + ImageButton pause; + + // output target file 2016-01-01 01.01.01.wav + File targetFile; Runnable progress; int soundMode; // how many samples passed - long samples; + long samplesTime; Storage storage; @@ -140,14 +141,14 @@ public class RecordingActivity extends AppCompatActivity { storage = new Storage(this); try { - file = storage.getNewFile(); + targetFile = storage.getNewFile(); } catch (RuntimeException e) { Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show(); finish(); return; } - title.setText(file.getName()); + title.setText(targetFile.getName()); receiver = new RecordingReceiver(); IntentFilter filter = new IntentFilter(); @@ -181,7 +182,7 @@ public class RecordingActivity extends AppCompatActivity { updateBufferSize(false); - addSamples(0); + updateSamples(); View cancel = findViewById(R.id.recording_cancel); cancel.setOnClickListener(new View.OnClickListener() { @@ -190,8 +191,9 @@ public class RecordingActivity extends AppCompatActivity { cancelDialog(new Runnable() { @Override public void run() { + stopRecording(); storage.delete(storage.getTempRecording()); - storage.delete(file); + //storage.delete(targetFile); finish(); } }); @@ -248,15 +250,15 @@ public class RecordingActivity extends AppCompatActivity { @Override protected void onResume() { super.onResume(); - pitch.onResume(); updateBufferSize(false); + pitch.resume(); } @Override protected void onPause() { super.onPause(); - pitch.onPause(); updateBufferSize(true); + pitch.pause(); } void stopRecording(String status) { @@ -271,7 +273,6 @@ public class RecordingActivity extends AppCompatActivity { thread.interrupt(); thread = null; } - pitch.onPause(); unsilent(); } @@ -308,7 +309,6 @@ public class RecordingActivity extends AppCompatActivity { if (thread == null) { record(); } - pitch.onResume(); } @Override @@ -364,14 +364,9 @@ public class RecordingActivity extends AppCompatActivity { ss = ss / 2; } - final long s = ss; - handle.post(new Runnable() { - @Override - public void run() { - samples = 0; - addSamples(s); - } - }); + synchronized (thread) { + samplesTime = ss; + } } os = new DataOutputStream(new BufferedOutputStream(storage.open(tmp))); @@ -393,6 +388,11 @@ public class RecordingActivity extends AppCompatActivity { recorder.startRecording(); + int samplesUpdateCount = 0; + int samplesTimeCount = 0; + // how many samples we need to update 'samples'. time clock. every 1000ms. + int samplesTimeUpdate = 1000 / 1000 * sampleRate * (channelConfig == AudioFormat.CHANNEL_IN_MONO ? 1 : 2); + while (!Thread.currentThread().isInterrupted()) { synchronized (thread) { final int readSize = recorder.read(buffer, 0, buffer.length); @@ -410,15 +410,26 @@ public class RecordingActivity extends AppCompatActivity { sum += buffer[i] * buffer[i]; } - final int amplitude = (int) (Math.sqrt(sum / readSize)); - pitch.add((int) (amplitude / (float) maximumAltitude * 100) + 1); + int amplitude = (int) (Math.sqrt(sum / readSize)); + int s = channelConfig == AudioFormat.CHANNEL_IN_MONO ? readSize : readSize / 2; - handle.post(new Runnable() { - @Override - public void run() { - addSamples(channelConfig == AudioFormat.CHANNEL_IN_MONO ? readSize : readSize / 2); - } - }); + samplesUpdateCount += s; + if (samplesUpdateCount >= samplesUpdate) { + pitch.add((int) (amplitude / (float) maximumAltitude * 100) + 1); + samplesUpdateCount -= samplesUpdate; + } + + samplesTime += s; + samplesTimeCount += s; + if (samplesTimeCount > samplesTimeUpdate) { + handle.post(new Runnable() { + @Override + public void run() { + updateSamples(); + } + }); + samplesTimeCount -= samplesTimeUpdate; + } } } } catch (final RuntimeException e) { @@ -455,28 +466,20 @@ public class RecordingActivity extends AppCompatActivity { } synchronized (t) { - if (pause) + if (pause) { samplesUpdate = (int) (1000 * sampleRate / 1000.0); - else + } else { samplesUpdate = (int) (pitch.getPitchTime() * sampleRate / 1000.0); + } buffer = new short[channelConfig == AudioFormat.CHANNEL_IN_MONO ? samplesUpdate : samplesUpdate * 2]; } } - void addSamples(long s) { - samples += s; + void updateSamples() { + long ms = samplesTime / sampleRate * 1000; - long ms = samples / sampleRate * 1000; - - int diffSeconds = (int) (ms / 1000 % 60); - int diffMinutes = (int) (ms / (60 * 1000) % 60); - int diffHours = (int) (ms / (60 * 60 * 1000) % 24); - int diffDays = (int) (ms / (24 * 60 * 60 * 1000)); - - String t = String.format("%02d:%02d", diffMinutes, diffSeconds); - - time.setText(t); + time.setText(MainApplication.formatDuration(ms)); } // alarm dismiss button @@ -588,7 +591,7 @@ public class RecordingActivity extends AppCompatActivity { stopRecording("encoding"); final File in = storage.getTempRecording(); - final File out = file; + final File out = targetFile; EncoderInfo info = getInfo(); @@ -608,7 +611,7 @@ public class RecordingActivity extends AppCompatActivity { final ProgressDialog d = new ProgressDialog(this); d.setTitle("Encoding..."); - d.setMessage(".../" + file.getName()); + d.setMessage(".../" + targetFile.getName()); d.setMax(100); d.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); d.setIndeterminate(false); diff --git a/app/src/main/java/com/github/axet/audiorecorder/app/MainApplication.java b/app/src/main/java/com/github/axet/audiorecorder/app/MainApplication.java index 95233eb..4cf836c 100644 --- a/app/src/main/java/com/github/axet/audiorecorder/app/MainApplication.java +++ b/app/src/main/java/com/github/axet/audiorecorder/app/MainApplication.java @@ -20,4 +20,27 @@ public class MainApplication extends Application { PreferenceManager.setDefaultValues(this, R.xml.pref_general, false); } + + static public String formatTime(int tt) { + return String.format("%02d", tt); + } + + static public String formatDuration(long diff) { + int diffMilliseconds = (int) (diff % 1000); + int diffSeconds = (int) (diff / 1000 % 60); + int diffMinutes = (int) (diff / (60 * 1000) % 60); + int diffHours = (int) (diff / (60 * 60 * 1000) % 24); + int diffDays = (int) (diff / (24 * 60 * 60 * 1000)); + + String str = ""; + + if (diffDays > 0) + str = diffDays + "d " + formatTime(diffHours) + ":" + formatTime(diffMinutes) + ":" + formatTime(diffSeconds); + else if (diffHours > 0) + str = formatTime(diffHours) + ":" + formatTime(diffMinutes) + ":" + formatTime(diffSeconds); + else + str = formatTime(diffMinutes) + ":" + formatTime(diffSeconds); + + return str; + } } diff --git a/app/src/main/java/com/github/axet/audiorecorder/app/Storage.java b/app/src/main/java/com/github/axet/audiorecorder/app/Storage.java index e9af936..42016e1 100644 --- a/app/src/main/java/com/github/axet/audiorecorder/app/Storage.java +++ b/app/src/main/java/com/github/axet/audiorecorder/app/Storage.java @@ -123,11 +123,11 @@ public class Storage { i++; } - try { - file.createNewFile(); - } catch (IOException e) { - throw new RuntimeException("Unable to create: " + file, e); - } +// try { +// file.createNewFile(); +// } catch (IOException e) { +// throw new RuntimeException("Unable to create: " + file, e); +// } return file; } 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 3807aa2..c4a5146 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 @@ -21,6 +21,8 @@ import java.util.LinkedList; import java.util.List; public class PitchView extends ViewGroup { + public static final String TAG = PitchView.class.getSimpleName(); + // pitch delimiter length in dp public static final float PITCH_DELIMITER = 1f; // pitch length in dp @@ -51,10 +53,13 @@ public class PitchView extends ViewGroup { long time = 0; + Runnable draw; + Thread thread; + + int bg; + public class PitchGraphView extends SurfaceView implements SurfaceHolder.Callback { - Thread thread; - Runnable draw; - int bg; + SurfaceHolder holder; public PitchGraphView(Context context) { this(context, null); @@ -68,8 +73,6 @@ public class PitchView extends ViewGroup { super(context, attrs, defStyleAttr); getHolder().addCallback(this); - - bg = getThemeColor(android.R.attr.windowBackground); } @Override @@ -82,116 +85,79 @@ public class PitchView extends ViewGroup { super.onLayout(changed, left, top, right, bottom); } - @Override - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - } + public void draw() { + Canvas canvas = holder.lockCanvas(null); + canvas.drawColor(bg); - @Override - public void surfaceCreated(final SurfaceHolder holder) { - draw = new Runnable() { - @Override - public void run() { + int m = Math.min(pitchMemCount, data.size()); + + float offset = 0; + + if (data.size() >= pitchMemCount) { + if (time == 0) time = System.currentTimeMillis(); - while (!Thread.currentThread().isInterrupted()) { - long time = System.currentTimeMillis(); - Canvas canvas = holder.lockCanvas(null); - if (canvas == null) - return; - drawHolder(canvas); - holder.unlockCanvasAndPost(canvas); - long cur = System.currentTimeMillis(); - long delay = UPDATE_SPEED - (cur - time); + long cur = System.currentTimeMillis(); - if (delay > 0) { - try { - Thread.sleep(delay); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return; - } - } - } + float tick = (cur - time) / (float) pitchTime; + + // force clear queue + if (data.size() > pitchMemCount + 1) { + tick = 0; + time = cur; + data.subList(0, data.size() - pitchMemCount).clear(); + m = Math.min(pitchMemCount, data.size()); } - }; - start(); - } - - void start() { - thread = new Thread(draw, "PitchView"); - thread.start(); - } - - void drawHolder(Canvas canvas) { - synchronized (data) { - canvas.drawColor(bg); - - int m = Math.min(pitchMemCount, data.size()); - - float offset = 0; - - if (data.size() >= pitchMemCount) { - if (time == 0) - time = System.currentTimeMillis(); - - - long cur = System.currentTimeMillis(); - - float tick = (cur - time) / (float) pitchTime; - - // force clear queue - if (data.size() > pitchMemCount + 1) { + if (tick > 1) { + if (data.size() > pitchMemCount) { + tick -= 1; + time += pitchTime; + } else if (data.size() == pitchMemCount) { tick = 0; time = cur; - data.subList(0, data.size() - pitchMemCount).clear(); - m = Math.min(pitchMemCount, data.size()); } - - if (tick > 1) { - if (data.size() > pitchMemCount) { - tick -= 1; - time += pitchTime; - } else if (data.size() == pitchMemCount) { - tick = 0; - time = cur; - } - data.subList(0, 1).clear(); - m = Math.min(pitchMemCount, data.size()); - } - - offset = pitchSize * tick; + data.subList(0, 1).clear(); + m = Math.min(pitchMemCount, data.size()); } - for (int i = 0; i < m; i++) { - float left = data.get(i); - float right = data.get(i); - - float mid = getHeight() / 2f; - - float x = -offset + i * pitchSize + pitchSize / 2f; - - canvas.drawLine(x, mid, x, mid - mid * left, paint); - canvas.drawLine(x, mid, x, mid + mid * right, paint); - } + offset = pitchSize * tick; } + + for (int i = 0; i < m; i++) { + float left = data.get(i); + float right = data.get(i); + + float mid = getHeight() / 2f; + + float x = -offset + i * pitchSize + pitchSize / 2f; + + canvas.drawLine(x, mid, x, mid - mid * left, paint); + canvas.drawLine(x, mid, x, mid + mid * right, paint); + } + + holder.unlockCanvasAndPost(canvas); } @Override - public void surfaceDestroyed(SurfaceHolder holder) { - if (thread != null) { - thread.interrupt(); - try { - thread.join(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } + synchronized public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + this.holder = holder; + } + + @Override + synchronized public void surfaceCreated(final SurfaceHolder holder) { + this.holder = holder; + } + + @Override + synchronized public void surfaceDestroyed(SurfaceHolder holder) { + this.holder = null; } } - public class PitchCurrentView extends View { + public class PitchCurrentView extends SurfaceView implements SurfaceHolder.Callback { Paint paint; + SurfaceHolder holder; public PitchCurrentView(Context context) { this(context, null); @@ -207,6 +173,8 @@ public class PitchView extends ViewGroup { paint = new Paint(); paint.setColor(0xff0433AE); paint.setStrokeWidth(pitchDlimiter); + + getHolder().addCallback(this); } @Override @@ -226,24 +194,40 @@ public class PitchView extends ViewGroup { super.onLayout(changed, left, top, right, bottom); } + public void draw() { + if (data.size() == 0) + return; + + Canvas canvas = holder.lockCanvas(null); + canvas.drawColor(bg); + + int end = data.size() - 1; + + float left = data.get(end); + float right = data.get(end); + + float mid = getWidth() / 2f; + + float y = getHeight() / 2f; + + canvas.drawLine(mid, y, mid - mid * left, y, paint); + canvas.drawLine(mid, y, mid + mid * right, y, paint); + holder.unlockCanvasAndPost(canvas); + } + @Override - protected void onDraw(Canvas canvas) { - synchronized (data) { - if (data.size() == 0) - return; + synchronized public void surfaceCreated(SurfaceHolder holder) { + this.holder = holder; + } - int end = data.size() - 1; + @Override + synchronized public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + this.holder = holder; + } - float left = data.get(end); - float right = data.get(end); - - float mid = getWidth() / 2f; - - float y = getHeight() / 2f; - - canvas.drawLine(mid, y, mid - mid * left, y, paint); - canvas.drawLine(mid, y, mid + mid * right, y, paint); - } + @Override + synchronized public void surfaceDestroyed(SurfaceHolder holder) { + this.holder = null; } } @@ -268,6 +252,8 @@ public class PitchView extends ViewGroup { pitchTime = pitchSize * UPDATE_SPEED; + bg = getThemeColor(android.R.attr.windowBackground); + graph = new PitchGraphView(getContext()); addView(graph); @@ -284,13 +270,22 @@ public class PitchView extends ViewGroup { paint = new Paint(); paint.setColor(0xff0433AE); paint.setStrokeWidth(pitchWidth); + + time = System.currentTimeMillis(); } public void add(int a) { - synchronized (data) { - data.add(a / 100.0f); + data.add(a / 100.0f); + } - current.postInvalidate(); + public void draw() { + synchronized (graph) { + if (graph.holder != null) + graph.draw(); + } + synchronized (current) { + if (current.holder != null) + current.draw(); } } @@ -337,16 +332,39 @@ public class PitchView extends ViewGroup { current.draw(canvas); } - public void onPause() { - if (graph.thread != null) { - graph.thread.interrupt(); - graph.thread = null; + public void pause() { + if (thread != null) { + thread.interrupt(); + thread = null; } } - public void onResume() { - if (graph.thread == null && graph.draw != null) { - graph.start(); + public void resume() { + if (thread == null) { + draw = new Runnable() { + @Override + public void run() { + time = System.currentTimeMillis(); + while (!Thread.currentThread().isInterrupted()) { + long time = System.currentTimeMillis(); + draw(); + long cur = System.currentTimeMillis(); + + long delay = UPDATE_SPEED - (cur - time); + + if (delay > 0) { + try { + Thread.sleep(delay); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return; + } + } + } + } + }; + thread = new Thread(draw, TAG); + thread.start(); } } }