当我启动或关闭Thread的时候几乎每次都会出现闪退现象,感觉就是看运气,这次只是加了个判断thread是否为null就完全呵呵了。没有系统地去学习过thread(唉,真是)。
下面是一些相关代码:(线程是pitch_detector_thread_
)
SheetPage.java
package com.fyp.flipfreely; import android.app.Activity; import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.content.Context; import android.content.Intent; import android.media.MediaPlayer; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.view.GestureDetector; import android.view.GestureDetector.OnGestureListener; import android.view.Menu; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.animation.AnimationUtils; import android.widget.Button; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.Toast; import android.widget.ViewFlipper; import com.example.AndroidTuner.PitchDetector; import com.fyp.fileIO.mXMLReader; import com.fyp.midi.MidiDriver; import com.fyp.midi.SimpleAnalysis; import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.util.HashMap; import java.util.Map; import jm.music.data.Score; /** * A simple {@link Fragment} subclass. * */ public class SheetPage extends Activity implements OnGestureListener,OnClickListener, MidiDriver.OnMidiStartListener { protected MidiDriver midi; protected MediaPlayer player; /*SimpleOnGestureListener可以用来只复写自己想要的手势,从而避免垃圾代码*/ private ViewFlipper sheetFlipper; private GestureDetector detector; private String title=""; //three tab, each button for each tab private LinearLayout mTabSearch; private LinearLayout mTabCollection; private LinearLayout mTabSetting; private LinearLayout mTabCollect; private LinearLayout mTabPlay; private Button mTabStart; private SearchPage searchF; private SettingPage settingF; private CollectionPage collectionF; private Score s=new Score(); private String directoryName="SmartSheet"; private String dirRoot = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator+directoryName+File.separator; private String temptitle=""; int[] pitches=null; int[] tmp=null;/*store the pitch from the real-time sound*/ int correctHit=0; int counter=0; int totalHits=0; private static final int advance=4;/*提前四个音翻页*/ private static double accuracy=0.999; private static int speed=50; private static boolean auto=true; Thread pitch_detector_thread_; // private String[] collectionArray;//用来获取arrays中的数据之后进行数据的添加 public SheetPage() { // Required empty public constructor } private void init() throws IOException { mXMLReader xmlReader=new mXMLReader(); Mapmap=new HashMap (); map=xmlReader.getSystemInitInfo(); accuracy=Double.valueOf(map.get("accuracy")); speed=Integer.valueOf(map.get("speed")); auto=Boolean.valueOf(map.get("automation")); } @Override protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.sheet_page); try { init(); } catch (IOException e) { e.printStackTrace(); } // Toast.makeText(SheetPage.this,"zsdd", Toast.LENGTH_SHORT).show(); Intent intent= getIntent(); Bundle b = intent.getExtras(); if(b!=null) { title =(String) b.get("title"); } // 初始化控件和声明事件 mTabSearch = (LinearLayout) findViewById(R.id.searchTab); mTabCollection = (LinearLayout) findViewById(R.id.collectionTab); mTabSetting = (LinearLayout) findViewById(R.id.settingTab); mTabCollect = (LinearLayout) findViewById(R.id.collectBtn); mTabPlay = (LinearLayout) findViewById(R.id.playBtn); mTabStart = (Button) findViewById(R.id.startBtn); mTabSearch.setOnClickListener(this); mTabCollection.setOnClickListener(this); mTabSetting.setOnClickListener(this); mTabCollect.setOnClickListener(this); mTabPlay.setOnClickListener(this); mTabStart.setOnClickListener(this); if(auto){ mTabStart.setEnabled(true); }else{ mTabStart.setEnabled(false); } detector = new GestureDetector(this); sheetFlipper = (ViewFlipper) this.findViewById(R.id.sheetFlipper); /*initialize the title of the midi file to be found*/ //TODO unfinished function here. Only two pages can be added here. temptitle=title.toLowerCase(); temptitle=temptitle.replaceAll(" ","_"); sheetFlipper.addView(addImageView(getResourceId(temptitle,"drawable")));/*change to pages, rather than one page*/ sheetFlipper.addView(addImageView(getResourceId("finish","drawable"))); /*获取Accuracy的阈值*/ // String acc=SettingPage.defaultAccuracy; // if(acc.equals("Low")){ // accuracy=10.0; // }else if(acc.equals("Medium")){ // accuracy=8.0; // }else if(acc.equals("High")){ // accuracy=5.0; // } try { /*Create directory if there doesn't exist one*/ createDir(dirRoot); String fileName=temptitle+".mid"; String filePath=dirRoot+fileName; /*Create midi file refers to filePath*/ File midiFile=new File(filePath); InputStream input = this.getAssets().open(fileName);//打开这个midi的stream。 /*Convert InputStream to File*/ inputstreamtofile(input,midiFile); StringBuffer resultStringBuffer = new StringBuffer(); String lineToRead = ""; int exitValue = 0; Process proc = Runtime.getRuntime().exec("perl midi2chord.pl -v -s120,5,5 "+fileName); InputStream inputStream = proc.getInputStream(); BufferedReader bufferedRreader = new BufferedReader(new InputStreamReader(inputStream)); // save first line if ((lineToRead = bufferedRreader.readLine()) != null) { resultStringBuffer.append(lineToRead); } // save next lines while ((lineToRead = bufferedRreader.readLine()) != null) { resultStringBuffer.append("\r\n"); resultStringBuffer.append(lineToRead); } // Always reading STDOUT first, then STDERR, exitValue last proc.waitFor(); // wait for reading STDOUT and STDERR over exitValue = proc.exitValue(); System.out.print(resultStringBuffer); /*Analysis midi file and get pitches*/ SimpleAnalysis sa; sa=new SimpleAnalysis(filePath); s=sa.getScore(); totalHits=sa.getPitches().length; /*initialize the pitch array*/ pitches=new int[totalHits]; pitches=sa.getPitches(); // for (int i=0;i 90) { this.sheetFlipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.push_left_in)); this.sheetFlipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.push_left_out)); this.sheetFlipper.showNext(); return true; } else if (e1.getX() - e2.getX() < -90) { this.sheetFlipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.push_right_out)); this.sheetFlipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.push_right_in)); this.sheetFlipper.showPrevious(); return true; } return false; }
@Override public void onLongPress(MotionEvent e) { // TODO Auto-generated method stub } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { // TODO Auto-generated method stub return false; } @Override public void onShowPress(MotionEvent e) { // TODO Auto-generated method stub } @Override public boolean onSingleTapUp(MotionEvent e) { // TODO Auto-generated method stub return false; } @Override public void onClick(View v) { // Obtain FragmentManager object FragmentManager fm = getFragmentManager(); // Start Fragment event FragmentTransaction transaction = fm.beginTransaction(); Intent intent = new Intent(); switch (v.getId()) { case R.id.startBtn: //when music is on, close the music first. if (player != null) { player.stop(); player.release(); player=null; } if(!pitch_detector_thread_.isAlive()) { pitch_detector_thread_.start(); }else if(pitch_detector_thread_.isInterrupted()){ pitch_detector_thread_.run(); } break; case R.id.searchTab: if (searchF == null) { searchF = new SearchPage(); } intent.putExtra("tab", "search"); intent.setClass(this, MainActivity.class); startActivity(intent); finish();/*finish the sheetPage activity*/ break; case R.id.collectionTab: if (collectionF == null) { collectionF = new CollectionPage(); } intent.putExtra("tab", "collection"); intent.setClass(this, MainActivity.class); startActivity(intent); finish(); break; case R.id.settingTab: if (settingF == null) { settingF = new SettingPage(); } intent.putExtra("tab", "setting"); intent.setClass(this, MainActivity.class); startActivity(intent); finish(); break; case R.id.playBtn: if (player != null) { player.stop(); player.release(); player=null; }else{ //A MediaPlayer element can play midi by using a method MediaPlayer.create(Activity,midi Id); if(pitch_detector_thread_.isAlive()){ pitch_detector_thread_.interrupt(); } /*play the midi file*/ player = MediaPlayer.create(this, getResourceId(temptitle,"raw")); player.start(); //Play.midi(s); } break; case R.id.collectBtn:break; } // transaction.addToBackStack(); // ?????? transaction.commit(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } /*Real-time Thread*/ @Override public void onStop() { super.onStop(); //sometimes work if(pitch_detector_thread_!=null) { if (pitch_detector_thread_.isAlive()) { pitch_detector_thread_.interrupt(); pitch_detector_thread_ = null; } } } /*Midi Player Stuffs*/ // On resume @Override protected void onResume() { super.onResume(); // Start midi if (midi != null) midi.start(); } // On pause @Override protected void onPause() { super.onPause(); // Stop midi if (midi != null) midi.stop(); // Stop player if (player != null) player.stop(); //sometimes work if(pitch_detector_thread_!=null) { if (pitch_detector_thread_.isAlive()) { pitch_detector_thread_.interrupt(); pitch_detector_thread_ = null; } } } @Override public void onMidiStart() { sendMidi(0xc0, 6); } // Send a midi message protected void sendMidi(int m, int p) { byte msg[] = new byte[2]; msg[0] = (byte) m; msg[1] = (byte) p; midi.write(msg); } // Send a midi message protected void sendMidi(int m, int n, int v) { byte msg[] = new byte[3]; msg[0] = (byte) m; msg[1] = (byte) n; msg[2] = (byte) v; midi.write(msg); } public void inputstreamtofile(InputStream ins,File file) throws IOException{ OutputStream os = new FileOutputStream(file); int bytesRead = 0; byte[] buffer = new byte[8192]; while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) { os.write(buffer, 0, bytesRead); } os.close(); ins.close(); } public static boolean createDir(String destDirName) { File dir = new File(destDirName); if (dir.exists()) { // System.out.println("Create directory " + destDirName + " Failed, directory already exists"); return false; } if (!destDirName.endsWith(File.separator)) { destDirName = destDirName + File.separator; } //?????? if (dir.mkdirs()) { // System.out.println("Create directory " + destDirName + " Successfully"); return true; } else { // System.out.println("Create directory " + destDirName + " Failed"); return false; } } public void ShowPitchDetectionResult(double pitch) { double frequency=Math.round(pitch * 10) / 10.0; int p=0; if(frequency>120){ p=FrequencyToPitch(frequency); Toast.makeText(SheetPage.this,p+"", Toast.LENGTH_SHORT).show(); if(counter=accuracy){ this.sheetFlipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.push_left_out)); this.sheetFlipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.push_left_in)); this.sheetFlipper.showNext(); Toast.makeText(SheetPage.this,"Finish Playing, Sim="+sim, Toast.LENGTH_SHORT).show(); }else{ Toast.makeText(SheetPage.this,"Fail to flipped, Sim="+sim, Toast.LENGTH_SHORT).show(); } tmp=null; tmp=new int[totalHits]; counter=0; if(pitch_detector_thread_.isAlive()) { pitch_detector_thread_.interrupt(); } } } } public int FrequencyToPitch(double frenquence){ int pitch=0; pitch=(int)Math.round((69+12*Math.log(frenquence/440)/Math.log((double)2))); return pitch; } /** * 两个向量可以为任意维度,但必须保持维度相同,表示n维度中的两点,获取欧几里德距离 * @param s1 * @param s2 * @return 两点间距离 * */ public double getEuclideanDistance(int[] s1, int[] s2) { double distance = 0; if (s1.length == s2.length) { for (int i = 0; i < s1.length; i++) { double temp = Math.pow((s1[i] - s2[i]), 2); distance += temp; } distance = Math.sqrt(distance); } return distance; } public double getCosineSimilarity(int[] s1,int[] s2){ double similarity=0; double topNum=0; double bottomA=0; double bottomB=0; if(s1.length==s2.length){ for(int i=0;i
PitchDetection代码:
/** Copyright (C) 2009 by Aleksey Surkov. ** ** Permission to use, copy, modify, and distribute this software and its ** documentation for any purpose and without fee is hereby granted, provided ** that the above copyright notice appear in all copies and that both that ** copyright notice and this permission notice appear in supporting ** documentation. This software is provided "as is" without express or ** implied warranty. */ package com.example.AndroidTuner; import android.app.AlertDialog; import android.media.AudioFormat; import android.media.AudioRecord; import android.media.MediaRecorder.AudioSource; import android.os.Handler; import android.util.Log; import com.fyp.flipfreely.SheetPage; import java.util.ArrayList; import java.util.HashMap; import java.util.List; public class PitchDetector implements Runnable { private static String LOG_TAG = "PitchDetector"; // Currently, only this combination of rate, encoding and channel mode // actually works. private final static int RATE = 8000; private final static int CHANNEL_MODE = AudioFormat.CHANNEL_CONFIGURATION_MONO; private final static int ENCODING = AudioFormat.ENCODING_PCM_16BIT; private final static int BUFFER_SIZE_IN_MS = 3000; private final static int CHUNK_SIZE_IN_SAMPLES = 4096; // = 2 ^ // CHUNK_SIZE_IN_SAMPLES_POW2 private final static int CHUNK_SIZE_IN_MS = 1000 * CHUNK_SIZE_IN_SAMPLES / RATE; private final static int BUFFER_SIZE_IN_BYTES = RATE * BUFFER_SIZE_IN_MS / 1000 * 2; private final static int CHUNK_SIZE_IN_BYTES = RATE * CHUNK_SIZE_IN_MS / 1000 * 2; private final static int MIN_FREQUENCY = 49; // 49.0 HZ of G1 - lowest note // for crazy Russian choir. private final static int MAX_FREQUENCY = 1568; // 1567.98 HZ of G6 - highest // demanded note in the // classical repertoire private final static int DRAW_FREQUENCY_STEP = 5; public native void DoFFT(double[] data, int size); // an NDK library // 'fft-jni' public PitchDetector(SheetPage parent, Handler handler) { parent_ = parent; handler_ = handler; System.loadLibrary("fft-jni"); } private static class FreqResult { public HashMapfrequencies; public double best_frequency; } public static class FrequencyCluster { public double average_frequency = 0; public double total_amplitude = 0; public void add(double freq, double amplitude) { double new_total_amp = total_amplitude + amplitude; average_frequency = (total_amplitude * average_frequency + freq * amplitude) / new_total_amp; total_amplitude = new_total_amp; } public boolean isNear(double freq) { if (Math.abs(1 - (average_frequency / freq)) < 0.05) { // only 5% difference return true; } else { return false; } } public boolean isHarmonic(double freq) { double harmonic_factor = freq / average_frequency; double distance_from_int = Math.abs(Math.round(harmonic_factor) - harmonic_factor); if (distance_from_int < 0.05) { // only 5% distance return true; } else { return false; } } public void addHarmony(double freq, double amp) { total_amplitude += amp; } @Override public String toString() { return "(" + average_frequency + ", " + total_amplitude + ")"; } }
public FreqResult AnalyzeFrequencies(short[] audio_data) { FreqResult fr = new FreqResult(); double[] data = new double[CHUNK_SIZE_IN_SAMPLES * 2]; final int min_frequency_fft = Math.round(MIN_FREQUENCY * CHUNK_SIZE_IN_SAMPLES / RATE); final int max_frequency_fft = Math.round(MAX_FREQUENCY * CHUNK_SIZE_IN_SAMPLES / RATE); for (int i = 0; i < CHUNK_SIZE_IN_SAMPLES; i++) { data[i * 2] = audio_data[i]; data[i * 2 + 1] = 0; } DoFFT(data, CHUNK_SIZE_IN_SAMPLES); double best_frequency = min_frequency_fft; HashMapfrequencies = new HashMap (); //best_frequency = min_frequency_fft; //fr.frequencies = new HashMap (); double best_amplitude = 0; final double draw_frequency_step = 1.0 * RATE / CHUNK_SIZE_IN_SAMPLES; List best_frequencies = new ArrayList (); List best_amps = new ArrayList (); for (int i = min_frequency_fft; i <= max_frequency_fft; i++) { final double current_frequency = i * 1.0 * RATE / CHUNK_SIZE_IN_SAMPLES; final double draw_frequency = Math .round((current_frequency - MIN_FREQUENCY) / DRAW_FREQUENCY_STEP) * DRAW_FREQUENCY_STEP + MIN_FREQUENCY; final double current_amplitude = Math.pow(data[i * 2], 2) + Math.pow(data[i * 2 + 1], 2); final double normalized_amplitude = current_amplitude * Math.pow(MIN_FREQUENCY * MAX_FREQUENCY, 0.5) / current_frequency; Double current_sum_for_this_slot = frequencies.get(draw_frequency); if (current_sum_for_this_slot == null) { current_sum_for_this_slot = 0.0; } frequencies.put(draw_frequency, Math.pow(current_amplitude, 0.5) / draw_frequency_step + current_sum_for_this_slot); if (normalized_amplitude > best_amplitude) { best_frequency = current_frequency; best_amplitude = normalized_amplitude; best_frequencies.add(current_frequency); best_amps.add(best_amplitude); } // test for harmonics // e.g. 220 is a harmonic of 110, so the harmonic factor is 2.0 // and thus the decimal part is 0.0. // 230 isn't a harmonic of 110, the harmonic_factor would be // 2.09 and 0.09 > 0.05 //double harmonic_factor = current_frequency / best_frequency; //if ((best_amplitude == 0) || (harmonic_factor - Math.floor(harmonic_factor) > 0.05)) { } List clusters = new ArrayList (); FrequencyCluster currentCluster = new FrequencyCluster(); clusters.add(currentCluster); FrequencyCluster bestCluster = currentCluster; if (best_frequencies.size() > 0) { currentCluster.add(best_frequencies.get(0), best_amps.get(0)); } // join clusters for(int i = 1; i < best_frequencies.size(); i++) { double freq = best_frequencies.get(i); double amp = best_amps.get(i); if (currentCluster.isNear(freq)) { currentCluster.add(freq, amp); continue; } // this isn't near, and isn't harmonic, it's a different one. // NOTE: assuming harmonies are consecutive (no unharmonics in between harmonies) currentCluster = new FrequencyCluster(); clusters.add(currentCluster); currentCluster.add(freq, amp); } // join harmonies FrequencyCluster nextCluster; for(int i = 1; i < clusters.size(); i ++) { currentCluster = clusters.get(i - 1); nextCluster = clusters.get(i); if (currentCluster.isHarmonic(nextCluster.average_frequency)) { currentCluster.total_amplitude += nextCluster.total_amplitude; } } best_amplitude = 0; best_frequency = 0; for(int i = 0; i < clusters.size(); i ++) { FrequencyCluster clu = clusters.get(i); if (best_amplitude < clu.total_amplitude) { best_amplitude = clu.total_amplitude; best_frequency = clu.average_frequency; } } fr.best_frequency = best_frequency; fr.frequencies = frequencies; return fr; } public void run() { Log.e(LOG_TAG, "starting to detect pitch"); android.os.Process .setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO); recorder_ = new AudioRecord(AudioSource.MIC, RATE, CHANNEL_MODE, ENCODING, 6144); if (recorder_.getState() != AudioRecord.STATE_INITIALIZED) { ShowError("Can't initialize AudioRecord"); return; } recorder_.startRecording(); while (!Thread.interrupted()) { try{ short[] audio_data = new short[BUFFER_SIZE_IN_BYTES / 2]; recorder_.read(audio_data, 0, CHUNK_SIZE_IN_BYTES / 2); FreqResult fr = AnalyzeFrequencies(audio_data); PostToUI(fr.frequencies, fr.best_frequency); Thread.sleep(100);//阻塞过程捕获中断异常来退出,解决了too much output to process问题 }catch(InterruptedException e){ e.printStackTrace(); break;//捕获到异常之后,执行break跳出循环。 } } recorder_.stop(); } private void PostToUI(final HashMap frequencies, final double pitch) { handler_.post(new Runnable() { public void run() { /*here is how the pitch transmits to SheetPage*/ // parent_.ShowPitchDetectionResult(frequencies, pitch); parent_.ShowPitchDetectionResult(pitch); } }); } private void ShowError(final String msg) { handler_.post(new Runnable() { public void run() { new AlertDialog.Builder(parent_).setTitle("GuitarTuner") .setMessage(msg).show(); } }); } private SheetPage parent_; private AudioRecord recorder_; private Handler handler_; }
以下是报错内容:
05-19 06:14:36.946 23613-23613/com.fyp.flipfreely E/AndroidRuntime: FATAL EXCEPTION: main java.lang.NullPointerException at com.fyp.flipfreely.SheetPage.onClick(SheetPage.java:373) at android.view.View.performClick(View.java:4204) at android.view.View$PerformClick.run(View.java:17355) at android.os.Handler.handleCallback(Handler.java:725) at android.os.Handler.dispatchMessage(Handler.java:92) at android.os.Looper.loop(Looper.java:137) at android.app.ActivityThread.main(ActivityThread.java:5041) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:511) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560) at dalvik.system.NativeStart.main(Native Method)
[已解决]多谢chuyao的解答。把解决方法放这给有需要的同志们~
对于thread的操作可以间接地使用一个boolean来判断是否要执行run()里面的代码。
http://stackoverflow.com/questions/10961714/how-to-properly-stop-the-thread-in-java
线程的run方法执行完毕后,线程就会自动停止
如果线程的run方法中是个定时循环,那么需要通过调用方法设置标志信息,例如canstop,但是设置完这个标志信息后,线程不会马上停止,等下一次循环到来时先check这个标志位是否满足线程退出条件,如果OK,那么线程就可以安全退出
用异步任务就不会这么麻烦了
用线程池来吧
你的PitchDetector的实现是怎样的?报错日志分析是哪里、是什么异常都知道了吗?你得把问题关键点描述的更清楚一点。