自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Android簡易“吹一吹實現(xiàn)”以及錄音和播放示例

移動開發(fā) Android
最近在做一些跟傳感器相關(guān)的東西,有注意到以前騰訊微博以前出過一個吹一吹交互,雖然和傳感器無關(guān),但是感覺也比較有興趣,就寫了一個拙劣的demo,因為接觸媒體文件操作比較少,順帶寫了一個錄音和播放的例子,總結(jié)了一下一些小坑的地方,一并在此分享給大家。

最近在做一些跟傳感器相關(guān)的東西,有注意到以前騰訊微博以前出過一個吹一吹交互,雖然和傳感器無關(guān),但是感覺也比較有興趣,就寫了一個拙劣的demo,因為接觸媒體文件操作比較少,順帶寫了一個錄音和播放的例子,總結(jié)了一下一些小坑的地方,一并在此分享給大家。

主要思路和坑的地方

主要的思路是通過MediaRecorder提供的getMaxAmplitude()函數(shù),獲取一段時間內(nèi)輸入的音頻最大幅值來進行檢測,所以除了吹的動作,其他聲音也會被錄進來。

“吹”這個動作如果想和其他動作進行區(qū)分,其實本質(zhì)在于吹的時候靠近聽筒,即便吹這個動作本身音量不大,但是麥克風看來它的分貝是很大的,所以我們可以通過檢測分貝來判斷這個動作是否是吹(如果其他聲音更大……那……算了不管了)。

這里附上前人的一些參考資料:

http://www.jb51.net/article/6...

一看到這個網(wǎng)站后面是htm,仿佛就明白了這個網(wǎng)站的框架…

這個東西坑的地方在于Mediaplayer和MediaRecorder這兩個東西stop和start的順序經(jīng)常是嚴格被限制的,在退出時如果沒有成功釋放資源,有時候Activity再啟動時,由于上次退出沒有stop,再重新start也會拋出異常。

權(quán)限添加

  1. <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> 
  2. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 
  3. <uses-permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS"/> 
  4. <uses-permission android:name="android.permission.RECORD_AUDIO" /> 

 

主要界面

大概想了一個簡單的界面,好吧其實是左下角的音響閃動,忘記修改文字描述了

 

 

 

 

布局文件: 

  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  3. xmlns:tools="http://schemas.android.com/tools" 
  4. android:id="@+id/activity_sound" 
  5. android:layout_width="match_parent" 
  6. android:layout_height="match_parent" 
  7. android:paddingBottom="@dimen/activity_vertical_margin" 
  8. android:paddingLeft="@dimen/activity_horizontal_margin" 
  9. android:paddingRight="@dimen/activity_horizontal_margin" 
  10. android:paddingTop="@dimen/activity_vertical_margin" 
  11. tools:context=".SoundActivity"
  12. <TextView 
  13.     android:layout_width="wrap_content" 
  14.     android:layout_height="wrap_content" 
  15.     android:text="@string/introduction_of_sound"/> 
  16. <LinearLayout 
  17.     android:layout_width="match_parent" 
  18.     android:layout_height="wrap_content" 
  19.     android:orientation="vertical" 
  20.     android:gravity="center" 
  21.     android:layout_centerInParent="true"
  22.  
  23.     <Button 
  24.         android:layout_width="70dp" 
  25.         android:layout_height="70dp" 
  26.         android:id="@+id/btn_start_record" 
  27.         android:background="@drawable/ic_mic_none_black_24dp" 
  28.         /> 
  29.     <TextView 
  30.         android:layout_width="wrap_content" 
  31.         android:layout_height="wrap_content" 
  32.         android:id="@+id/tv_record_tips"/> 
  33.  
  34.     <Button 
  35.         android:layout_width="70dp" 
  36.         android:layout_height="70dp" 
  37.         android:id="@+id/btn_start_play" 
  38.         android:background="@drawable/ic_play_circle_filled_black_24dp" 
  39.         /> 
  40. </LinearLayout> 
  41. <ImageView 
  42.     android:layout_width="50dp" 
  43.     android:layout_height="50dp" 
  44.     android:src="@drawable/ic_volume_mute_gray_24dp" 
  45.     android:layout_alignParentBottom="true" 
  46.     android:id="@+id/imv_sound"/> 
  47. </RelativeLayout>  

主要代碼

  1. import android.app.ProgressDialog; 
  2. import android.media.AudioManager; 
  3. import android.media.MediaPlayer; 
  4. import android.media.MediaRecorder; 
  5. import android.os.Environment; 
  6. import android.os.Handler; 
  7. import android.os.Message; 
  8. import android.support.v7.app.AppCompatActivity; 
  9. import android.os.Bundle; 
  10. import android.view.View
  11. import android.widget.Button; 
  12. import android.widget.ImageView; 
  13. import android.widget.TextView; 
  14. import android.widget.Toast; 
  15.  
  16. import java.io.IOException; 
  17. import java.util.Timer; 
  18. import java.util.TimerTask; 
  19.  
  20.  
  21. public class SoundActivity extends AppCompatActivity { 
  22.     static final int RECORDING = 1; 
  23.     static final int PLAYING = 2; 
  24.     static final int PAUSING = 3; 
  25.     static String TAG = "SoundActivity"
  26.     static int STATUS = RECORDING; 
  27.     //用于音頻錄制 
  28.     MediaRecorder mediaRecorder; 
  29.     //用于音頻播放 
  30.     MediaPlayer mediaPlayer; 
  31.  
  32.     //錄制按鈕 
  33.     Button btnRecord; 
  34.     //播放按鈕 
  35.     Button btnPlay; 
  36.     //提示信息 
  37.     TextView tvTips; 
  38.     //吹一吹小音箱 
  39.     ImageView imvSound; 
  40.     //播放進度條 
  41.     static String PATH_NAME = Environment.getExternalStorageDirectory().getAbsolutePath() + "/SensorDemoRecorder.mp3"
  42.     @Override 
  43.     protected void onCreate(Bundle savedInstanceState) { 
  44.         super.onCreate(savedInstanceState); 
  45.         setContentView(R.layout.activity_sound); 
  46.         init(); 
  47.     } 
  48.  
  49.     public void init(){ 
  50.         //控件初始化 
  51.         btnPlay = (Button)findViewById(R.id.btn_start_play); 
  52.         btnRecord = (Button)findViewById(R.id.btn_start_record); 
  53.         tvTips = (TextView)findViewById(R.id.tv_record_tips); 
  54.         imvSound = (ImageView)findViewById(R.id.imv_sound); 
  55.         mediaplayerPreparingDialog = new ProgressDialog(this); 
  56.         btnPlay.setOnClickListener(new View.OnClickListener() { 
  57.             @Override 
  58.             public void onClick(View v) { 
  59.                 if (STATUS == RECORDING ){ 
  60.                     //如果是在錄制,點擊則停止錄制并且播放 
  61.                     stopRecording(); 
  62.                     startPlay(); 
  63.                 }else if (STATUS == PAUSING){ 
  64.                     startPlay(); 
  65.                 } else { 
  66.                     //如果是在播放,點擊則暫停 
  67.                     pausePlay(); 
  68.                 } 
  69.             } 
  70.         }); 
  71.         btnRecord.setOnClickListener(new View.OnClickListener() { 
  72.             @Override 
  73.             public void onClick(View v) { 
  74.                 if (STATUS == PLAYING || STATUS == PAUSING){ 
  75.                     //如果是在播放或者暫停,點擊開始錄制 
  76.                     startRecording(); 
  77.                 }else { 
  78.                     //如果在錄制,點擊開始播放 
  79.                     stopRecording(); 
  80.                     startPlay(); 
  81.                 } 
  82.             } 
  83.         }); 
  84.  
  85.         mediaRecorder = new MediaRecorder(); 
  86.         //設(shè)置到達最大錄制長度時重頭開始錄制 
  87.         mediaRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() { 
  88.             @Override 
  89.             public void onInfo(MediaRecorder mr, int what, int extra) { 
  90.                 switch (what){ 
  91.                     case MediaRecorder.MEDIA_RECORDER_ERROR_UNKNOWN: 
  92.                         Toast.makeText(SoundActivity.this, "未知錯誤", Toast.LENGTH_SHORT).show(); 
  93.                         finish(); 
  94.                         break; 
  95.                     case MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED: 
  96.                         Toast.makeText(SoundActivity.this, "已達到最大錄制長度,開始重新錄制", Toast.LENGTH_SHORT).show( ); 
  97.                         startRecording(); 
  98.                         break; 
  99.                     case MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED: 
  100.                         Toast.makeText(SoundActivity.this, "空間不足,無法錄制", Toast.LENGTH_SHORT).show(); 
  101.                         mediaRecorder.stop(); 
  102.                         break; 
  103.  
  104.                 } 
  105.             } 
  106.         }); 
  107.         //默認開始錄制 
  108.         startRecording(); 
  109.         btnRecord.setBackgroundResource(R.drawable.ic_mic_black_24dp); 
  110.         //默認開始吹一吹檢測以及播放進度檢測 
  111.         startCheckSound(); 
  112.     } 
  113.  
  114.  
  115.     @Override 
  116.     protected void onDestroy() { 
  117.         super.onDestroy(); 
  118.         if (PLAYING == STATUS){ 
  119.             mediaPlayer.stop(); 
  120.             mediaPlayer.release(); 
  121.         } 
  122.         if (RECORDING == STATUS){ 
  123.             mediaRecorder.stop(); 
  124.             mediaRecorder.release(); 
  125.         } 
  126.         //為了防止Activity結(jié)束后有時候這個timer還在定時執(zhí)行任務(wù)(很坑) 
  127.         timer.cancel(); 
  128.     } 
  129.  
  130.     public void startRecording(){ 
  131.         if (PLAYING == STATUS){ 
  132.             stopPlay(); 
  133.         } 
  134.         STATUS = RECORDING; 
  135.         //設(shè)置為錄制狀態(tài) 
  136.         tvTips.setText("正在錄制,點擊播放按鈕或者麥克風停止錄制"); 
  137.         btnRecord.setBackgroundResource(R.drawable.ic_mic_black_24dp); 
  138.  
  139.         //開始錄制的設(shè)置 
  140.         mediaRecorder.reset();  // You can reuse the object by going back to setAudioSource() step 
  141.         mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); 
  142.         mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); 
  143.         mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); 
  144.         try{ 
  145.             mediaRecorder.setOutputFile(PATH_NAME); 
  146.             mediaRecorder.prepare(); 
  147.             mediaRecorder.start();   // Recording is now started 
  148.         }catch (IOException e){ 
  149.             Toast.makeText(this, "準備錄制文件失敗", Toast.LENGTH_SHORT).show(); 
  150.             e.printStackTrace(); 
  151.             finish(); 
  152.         } 
  153.     } 
  154.  
  155.     public void stopRecording(){ 
  156.         if (RECORDING == STATUS){ 
  157.             //說明正在錄制,設(shè)置停止信息 
  158.             tvTips.setText("已停止錄制,開始播放"); 
  159.             btnRecord.setBackgroundResource(R.drawable.ic_mic_none_black_24dp); 
  160.             mediaRecorder.stop(); 
  161.         } 
  162.     } 
  163.  
  164.  
  165.  
  166.     Handler handler= new Handler(new Handler.Callback() { 
  167.         @Override 
  168.         public boolean handleMessage(Message msg) { 
  169.             if ((Double)msg.obj > 70){ 
  170.                 imvSound.setImageResource(R.drawable.ic_volume_mute_valid_24dp); 
  171.             }else { 
  172.                 imvSound.setImageResource(R.drawable.ic_volume_mute_gray_24dp); 
  173.             } 
  174.             return false
  175.         } 
  176.     }); 
  177.  
  178.     Timer timer = new Timer(); 
  179.     public void startCheckSound(){ 
  180.         //定時檢測峰值,以及檢測播放進度 
  181.         timer.schedule(new TimerTask() { 
  182.             @Override 
  183.             public void run() { 
  184.                 if (mediaRecorder != null) { 
  185.                     double amplitude = (double)mediaRecorder.getMaxAmplitude(); 
  186.                     double db = 0; 
  187.                     //計算分貝 
  188.                     if (amplitude > 1) 
  189.                         db = 20 * Math.log10(amplitude); 
  190.                     Message msg = new Message(); 
  191.                     msg.obj = db; 
  192.                     handler.sendMessage(msg); 
  193.                     //如果需要檢測播放進度可以使用 
  194.                     //mediaPlayer.getCurrentPosition()/mediaPlayer.getDuration(); 
  195.                 } 
  196.             } 
  197.         },0,100); 
  198.     } 
  199.  
  200.  
  201.     ProgressDialog mediaplayerPreparingDialog; 
  202.     public void startPlay(){ 
  203.         if (RECORDING == STATUS){ 
  204.             //如果是從錄制狀態(tài)開始播放,則重新讀取新的錄制文件 
  205.             STATUS = PLAYING; 
  206.             //設(shè)置音頻播放器 
  207.             mediaPlayer = new MediaPlayer(); 
  208.             mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); 
  209.             mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { 
  210.                 @Override 
  211.                 public void onCompletion(MediaPlayer mp) { 
  212.                     //播放完設(shè)置 
  213.                     tvTips.setText("播放完畢,可點擊麥克風重新錄制"); 
  214.                     btnPlay.setBackgroundResource(R.drawable.ic_play_circle_filled_black_24dp); 
  215.                 } 
  216.             }); 
  217.             try { 
  218.                 mediaPlayer.setDataSource(PATH_NAME); 
  219.                 mediaPlayer.prepareAsync(); 
  220.             } catch (IOException e) { 
  221.                 e.printStackTrace(); 
  222.                 Toast.makeText(this, "錄音文件已丟失", Toast.LENGTH_SHORT).show(); 
  223.                 finish(); 
  224.             } 
  225.             mediaplayerPreparingDialog.setTitle("正在準備播放錄音"); 
  226.             mediaplayerPreparingDialog.show(); 
  227.  
  228.             mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { 
  229.                 @Override 
  230.                 public void onPrepared(MediaPlayer mp) { 
  231.                     mediaplayerPreparingDialog.dismiss(); 
  232.                     mediaPlayer.start(); 
  233.                 } 
  234.             }); 
  235.  
  236.         }else if(PAUSING == STATUS){ 
  237.             //從暫停狀態(tài)開始播放則直接播放 
  238.             mediaPlayer.start(); 
  239.         } 
  240.         //開始播放,設(shè)置按鈕為暫停 
  241.         btnPlay.setBackgroundResource(R.drawable.ic_pause_circle_filled_black_24dp); 
  242.  
  243.     } 
  244.  
  245.     public void pausePlay(){ 
  246.         if (PLAYING == STATUS) { 
  247.             //暫停播放,設(shè)置按鈕為開始播放 
  248.             mediaPlayer.pause(); 
  249.             btnPlay.setBackgroundResource(R.drawable.ic_play_circle_filled_black_24dp); 
  250.             STATUS = PAUSING; 
  251.         } 
  252.     } 
  253.  
  254.     public void stopPlay(){ 
  255.         if (mediaPlayer != null) mediaPlayer.stop(); 
  256.     } 
  257.  

Media和IllegalStateException

這個就是之前提到的由于沒有按順序釋放資源或者stop掉這兩個破玩意兒,可能會導致的各種錯誤,所以我很無奈地設(shè)置了一個STATUS變量,并且在Activity的OnDestoy里對兩個東西進行了stop,其實一般還會使用release釋放掉資源…大家隨意吧…

QCMediaPlayer mediaplayer NOT present

!!!我就知道,如果你看到這個地方,一定也對這個錯誤感到莫名其妙。我記得好像上古時期,也就是上次我寫這個的時候也被坑了。

論壇上有人說這個東西在4.4以下的系統(tǒng)就容易出現(xiàn),但是我也只能感覺不明覺厲,我一開始用的是MediaPlayer.create(this,Uri.parse(PATH_NAME))來創(chuàng)建MediaPlayer,于是換成了 

  1. mediaPlayer = new MediaPlayer(); 
  2. mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); 
  3. mediaPlayer.setDataSource(PATH_NAME);  

好吧,然后問題就解決了,我也是無語了。我覺得這個地方是一個很久遠的坑了,查原因一時也沒查到。我只能推測大概因為create函數(shù)創(chuàng)建時沒有指定AudioStreamType導致使用了默認的

  1. private int mStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE; 

在某些設(shè)備上可能不支持,于是就出了問題= =好吧,我也不知道還能說啥,就醬…

Vector Asset添加的圖標顏色不變化

如上,我的播放按鈕啊,音響啊,之類的圖標都是通過Vector Asset添加的,這也是一個比較久遠的坑了,但是以前也沒有記下來,即在Android L以下的版本中,Vector Asset添加的圖標,修改顏色時不能使用顏色的引用,而要直接寫顏色,例如:

  1. <vector xmlns:android="http://schemas.android.com/apk/res/android" 
  2.     android:width="24dp" 
  3.     android:height="24dp" 
  4.     android:viewportWidth="24.0" 
  5.     android:viewportHeight="24.0"
  6. <path 
  7.     android:fillColor="#3F51B5" 
  8.     android:pathData="***"/> 
  9. </vector>  

使用 

  1. <vector xmlns:android="http://schemas.android.com/apk/res/android" 
  2.     android:width="24dp" 
  3.     android:height="24dp" 
  4.     android:viewportWidth="24.0" 
  5.     android:viewportHeight="24.0"
  6. <path 
  7.     android:fillColor="@color/colorPrimary" 
  8.     android:pathData="***"/> 
  9. </vector>  

則導致顏色并不會修改,依然是黑色

責任編輯:龐桂玉 來源: segmentfault
相關(guān)推薦

2021-11-22 11:05:20

Vue 3setup前端

2017-02-07 11:35:26

Android動畫蠟燭動畫

2013-06-17 15:45:54

中國敏捷軟件開發(fā)大會

2009-10-27 11:15:13

信息化

2013-08-07 09:39:55

大數(shù)據(jù)

2009-07-14 17:16:33

吹制光纖綜合布線

2023-05-06 16:26:28

??Vue??UI組件

2014-06-03 09:59:13

光纖光纜微管氣吹技術(shù)

2011-07-18 15:32:14

iPhone 錄音 播放

2012-01-06 09:48:54

云計算IBM

2024-02-29 13:59:28

2012-04-18 09:22:31

云計算

2015-06-24 10:24:34

解放號

2019-10-31 10:25:19

算法Java數(shù)據(jù)結(jié)構(gòu)

2015-06-24 10:13:01

中軟國際解放號IT眾包服務(wù)

2015-07-08 16:28:28

2013-08-12 09:39:37

大數(shù)據(jù)大數(shù)據(jù)農(nóng)業(yè)

2012-04-18 13:58:21

QQ影像

2025-03-24 13:00:00

點贊
收藏

51CTO技術(shù)棧公眾號