AndroidFM模塊學習之錄音功能 - 新聞資(zī)訊 - 雲南小程序開發|雲南軟件開發|雲南網站(zhàn)建設-西山區知普網絡科技工作室

159-8711-8523

雲南網建設/小程序開發/軟件開發

知識

不管是網站(zhàn),軟件還是小程序,都要直接或間接能為您産生價值,我們在追求其視覺表現的同時,更側重于功能的便捷,營銷的便利,運營的高效,讓網站(zhàn)成為營銷工具,讓軟件能切實提升企業(yè)内部管理水平和(hé)效率。優秀的程序為後期升級提供便捷的支持!

您當前位置>首頁 » 新聞資(zī)訊 » 技術(shù)分享 >

AndroidFM模塊學習之錄音功能

發表時間:2020-10-19

發布人:葵宇科技

浏覽次數:40


前些天禀析了一下(xià)FM的流程以及重要類,接下(xià)來我們分析一下(xià)FM的灌音功能;
[img]http://img.blog.csdn.net/20150106172922265?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdGZzbG92ZXhpemk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
Fm灌音時,當點擊了灌音按鈕,會發一個(gè)廣播出去,源碼在FMRadioService.java中(zhōng)
<span style="font-family:KaiTi_GB2312;font-size:18px;"> public void startRecording() {

       Log.d(LOGTAG, "In startRecording of Recorder");
       if ((true == mSingleRecordingInstanceSupported) &&
                               (true == mOverA2DP )) {
            Toast.makeText( this,
                      "playback on BT in progress,can't record now",
                       Toast.LENGTH_SHORT).show();
            return;
       }
       sendRecordIntent(RECORD_START);
   }</span>


State狀況控制FMRecordingService.java類service啟動(dòng)與封閉
if (state == 1) {
發送廣播掃描
Environment.MEDIA_CHECKING檢查sd卡預備攫取
               Log.d(TAG,"FM ONintent received");
               startService = true;
               context.startService(in);
           } elseif(state == 0){
               Log.d(TAG,"FM OFFintent received");
               startService = false;
獲取audio的uri
 
               context.stopService(in);
            }
Fm廣播接收的action publicstatic final StringACTION_FM = "codeaurora.intent.action.FM";
當FMRadioservice類的private void sendRecordServiceIntent(int action)辦法發送一個(gè)廣播并附帶一個(gè)開關(guān)灌音的狀況int值
<span style="font-family:KaiTi_GB2312;font-size:18px;">private void sendRecordServiceIntent(int action) {
       Intent intent = new Intent(ACTION_FM);
       intent.putExtra("state", action);
       intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
       Log.d(LOGTAG, "Sending Recording intent for = " +action);
       getApplicationContext().sendBroadcast(intent);
   }</span>


 State狀況控制FMRecordingService.java類service啟動(dòng)與封閉
<span style="font-family:KaiTi_GB2312;font-size:18px;">public class FMRecordingReceiver extends BroadcastReceiver {

    private static final String TAG = "FMRecordingReceiver";
    public static final String ACTION_FM =
           "codeaurora.intent.action.FM";
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        Log.d(TAG, "Received intent: " + action); if((action != null) && action.equals(ACTION_FM)) {
            Log.d(TAG, "FM intent received");
            Intent in = new Intent();
            in.putExtras(intent);
            in.setClass(context, FMRecordingService.class);
            int state = intent.getIntExtra("state", 0);
            boolean startService = true;

            if (state == 1) {Log.d(TAG, "FM ON intent received");
                startService = true;
                context.startService(in);
            } else if(state == 0){
                Log.d(TAG, "FM OFF intent received");
                startService = false;
                context.stopService(in);
            }
        }
   }
}
</span>


Fm接收廣播的action

<span style="font-family:KaiTi_GB2312;font-size:18px;"> public static final String ACTION_FM_RECORDING =
                       "codeaurora.intent.action.FM_Recording";
    public static final String ACTION_FM_RECORDING_STATUS =
                "codeaurora.intent.action.FM.Recording.Status";</span>



 
onCreat()辦法裡注冊廣播接收機制,一個(gè)廣播是灌音狀況,一個(gè)是封閉fm狀況

<span style="font-family:KaiTi_GB2312;font-size:18px;">public void onCreate() {

        super.onCreate();
        Log.d(TAG, "FMRecording Service onCreate");
        registerRecordingListner();
        registerShutdownListner();
        registerStorageMediaListener();
        
    }</span>


stopRecord();

 
onDestroy()辦法裡寫了卸載注冊停止灌音
 


起首看下(xià)賤程圖:
public void onDestroy() {
        Log.d(TAG, "FMRecording Service onDestroy");
        if (mFmRecordingOn == true) {
            Log.d(TAG, "Still recording on progress, Stoping it");
            stopRecord();
        }
        unregisterBroadCastReceiver(mFmRecordingReceiver);
        unregisterBroadCastReceiver(mFmShutdownReceiver);
        unregisterBroadCastReceiver(mSdcardUnmountReceiver);
        super.onDestroy();
    }

stopRecord();停止灌音


              status = true;
 private void stopRecord() {
        Log.d(TAG, "Enter stopRecord");
        mFmRecordingOn = false;
        if (mRecorder == null)
            return;
        try {
             mRecorder.stop();
             mRecorder.reset();
             mRecorder.release();
             mRecorder = null;
        } catch(Exception e) {
             e.printStackTrace();
        }

        sendRecordingStatusIntent(STOP);
        saveFile();
        stopForeground(true);
        stopClientStatusCheck();
    }

registerShutdownListner();注冊接收辦法中(zhōng)停止fm灌音

private void registerShutdownListner() {
        if (mFmShutdownReceiver == null) {
            mFmShutdownReceiver = new BroadcastReceiver() {
                 @Override
                 public void onReceive(Context context, Intent intent) {
                     Log.d(TAG, "Received intent " +intent);
                     String action = intent.getAction();
                     Log.d(TAG, " action = " +action);
                     if (action.equals("android.intent.action.ACTION_SHUTDOWN")) {
                         Log.d(TAG, "android.intent.action.ACTION_SHUTDOWN Intent received");
                         stopRecord();
                     }
                 }
            };
            IntentFilter iFilter = new IntentFilter();
            iFilter.addAction("android.intent.action.ACTION_SHUTDOWN");
            registerReceiver(mFmShutdownReceiver, iFilter);
        }
    }

獲取sd卡有效空間

private static long getAvailableSpace() {
        String state = Environment.getExternalStorageState();
        Log.d(TAG, "External storage state=" + state);
        if (Environment.MEDIA_CHECKING.equals(state)) {
            return PREPARING;
        }
        if (!Environment.MEDIA_MOUNTED.equals(state)) {
            return UNAVAILABLE;
        }

        try {
             File sampleDir = Environment.getExternalStorageDirectory();
             StatFs stat = new StatFs(sampleDir.getAbsolutePath());
             return stat.getAvailableBlocks() * (long) stat.getBlockSize();
        } catch (Exception e) {
             Log.i(TAG, "Fail to access external storage", e);
        }
        return UNKNOWN_SIZE;
    }

重要經由過程以下(xià)辦法實現:
FilesampleDir = Environment.getExternalStorageDirectory();
             StatFs stat = newStatFs(sampleDir.getAbsolutePath());
             return stat.getAvailableBlocks() *(long) stat.getBlockSize();
有四種值:
Environment.MEDIA_MOUNTED沒有sd卡
UNKNOWN_SIZE sd卡有效空間等于UNKNOWN_SIZE值
LOW_STORAGE_THRESHOLD低于存儲空間極限值
更新顯示存儲斷定提示辦法
private boolean updateAndShowStorageHint() {
        mStorageSpace = getAvailableSpace();
        return showStorageHint();
    }

 
發送一個(gè)灌音狀況廣播

 
private void sendRecordingStatusIntent(int status) {
        Intent intent = new Intent(ACTION_FM_RECORDING_STATUS);
        intent.putExtra("state", status);
        Log.d(TAG, "posting intent for FM Recording status as = " +status);
        getApplicationContext().sendBroadcastAsUser(intent, UserHandle.ALL);
    }

getApplicationContext().sendBroadcastAsUser(intent,UserHandle.ALL);
UserHandle.ALL一個(gè)類的對象,USER_ALL= -1返回的一個(gè)字符串UserHandle{-1}
回調辦法,去fmradioservice.java回調
mCallbacks.onRecordingStarted();辦法
mCallbacks.onRecordingStopped();辦法
public void registerFMRecordingStatus()
 
啟動(dòng)灌音

private boolean startRecord() {

        Log.d(TAG, "Enter startRecord");
        if (mRecorder != null) { /* Stop existing recording if any */
            Log.d(TAG, "Stopping existing record");
            try {
                 mRecorder.stop();
                 mRecorder.reset();
                 mRecorder.release();
                 mRecorder = null;
            } catch(Exception e) {
                 e.printStackTrace();
            }
        } if (!updateAndShowStorageHint())
            return false;
        long maxFileSize = mStorageSpace - LOW_STORAGE_THRESHOLD;
        mRecorder = new MediaRecorder();
        try {
             mRecorder.setMaxFileSize(maxFileSize);
             if(mRecordDuration >= 0)
                mRecorder.setMaxDuration(mRecordDuration);
        } catch (RuntimeException exception) {

        }
        mSampleFile = null;
		
        
        File sampleDir;
<span style="font-family:KaiTi_GB2312;">        </span>if((Environment.getExternalSDStorageState(this).equals(Environment.MEDIA_MOUNTED))){
            sampleDir = new File(Environment.getExternalSDStorageDirectory(), "/FMRecording");
        }else{
            sampleDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() +"/FMRecording");
        }  
       

        if(!(sampleDir.mkdirs() || sampleDir.isDirectory()))
            return false;

        try {
             mSampleFile = File.createTempFile("FMRecording", ".3gpp", sampleDir);
        } catch (IOException e) {
             Log.e(TAG, "Not able to access SD Card");
             Toast.makeText(this, "Not able to access SD Card", Toast.LENGTH_SHORT).show();
        }
        try {
             Log.d(TAG, "AudioSource.FM_RX" +MediaRecorder.AudioSource.FM_RX);
             mRecorder.setAudioSource(MediaRecorder.AudioSource.FM_RX);
             mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
             mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
             mAudioType = "audio/3gpp";
        } catch (RuntimeException exception) {
             Log.d(TAG, "RuntimeException while settings");
             mRecorder.reset();
             mRecorder.release();
             mRecorder = null;
             return false;
        }
        Log.d(TAG, "setOutputFile");
        mRecorder.setOutputFile(mSampleFile.getAbsolutePath());
        try {mRecorder.prepare();
             Log.d(TAG, "start");
             mRecorder.start();
        } catch (IOException e) {
             Log.d(TAG, "IOException while start");
             mRecorder.reset();
             mRecorder.release();
             mRecorder = null;
             return false;
        } catch (RuntimeException e) {
             Log.d(TAG, "RuntimeException while start");
             mRecorder.reset();
             mRecorder.release();
             mRecorder = null;
             return false;
        }
        mFmRecordingOn = true; Log.d(TAG, "mSampleFile.getAbsolutePath() " +mSampleFile.getAbsolutePath());
        mRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() {
             public void onInfo(MediaRecorder mr, int what, int extra) {
                 if ((what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) ||
                     (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED)) {
                     if (mFmRecordingOn) {
                         Log.d(TAG, "Maximum file size/duration reached, stopping the recording");
                         stopRecord();
                     }
                     if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {
                         // Show the toast.
                         Toast.makeText(FMRecordingService.this,
                                        R.string.FMRecording_reach_size_limit,
                                        Toast.LENGTH_LONG).show();
                     }
                 }
             }
             // from MediaRecorder.OnErrorListenerpublic void onError(MediaRecorder mr, int what, int extra) {
                 Log.e(TAG, "MediaRecorder error. what=" + what + ". extra=" + extra);
                 if (what == MediaRecorder.MEDIA_RECORDER_ERROR_UNKNOWN) {
                     // We may have run out of space on the sdcard.
                     if (mFmRecordingOn) {
                         stopRecord();
                     }
                     updateAndShowStorageHint();
                 }
             }
        });
        mSampleStart = System.currentTimeMillis();
        sendRecordingStatusIntent(START);
        startNotification();
        return true;
    }

灌音不為空先設置為停止灌音大年夜新設置釋放資(zī)本
mRecorder!= null
mRecorder.stop();
                 mRecorder.reset();
                 mRecorder.release();
                 mRecorder = null;
 
灌音斷定存儲空間夠用不
 if (!updateAndShowStorageHint())
            return false;
 
灌音最大年夜值為當前值前去低于存儲極限值。
longmaxFileSize = mStorageSpace - LOW_STORAGE_THRESHOLD;
 
設置灌音最大年夜值
mRecorder.setMaxFileSize(maxFileSize);
設置灌音持續
mRecorder.setMaxDuration(mRecordDuration);
 

設置灌音來源,放置的地位,灌音audio格式,audio寫入音源編碼格式
mRecorder.setAudioSource(MediaRecorder.AudioSource.FM_RX);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mRecorder.setOutputFile(mSampleFile.getAbsolutePath());
灌音監聽mediaRecorder監聽
mRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener()
what ==MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
what ==MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED
灌音過程報錯停止灌音
stopRecord();彈出提示
updateAndShowStorageHint();
 
獲取當前時光,發送灌音狀況廣播,啟動(dòng)通(tōng)知
mSampleStart= System.currentTimeMillis();
        sendRecordingStatusIntent(START);
        startNotification();
mFmRecordingOn 為true 停止灌音
 
發送通(tōng)知,設置長途控制,startForeground(102,status);設置sevice至于前台


 private void startNotification() {
        RemoteViews views = new RemoteViews(getPackageName(), R.layout.record_status_bar);
        Notification status = new Notification();
        status.contentView = views;
        status.flags |= Notification.FLAG_ONGOING_EVENT;
        status.icon = R.drawable.ic_menu_record;
        startForeground(102, status);
    }



停止灌音,保存灌音文(wén)件,停止灌音之前台,停止狀體切換
 private void stopRecord()
sendRecordingStatusIntent(STOP);
        saveFile();
        stopForeground(true);
        stopClientStatusCheck();
保存灌音辦法(灌音一開錄,就在往默認内置T中(zhōng)寫數據以字節方法寫入數據)
private void saveFile() {
        int sampleLength = (int)((System.currentTimeMillis() - mSampleStart)/1000 );
        Log.d(TAG, "Enter saveFile");
        if (sampleLength == 0)
            return;
        String state = Environment.getExternalStorageState();
        Log.d(TAG, "storage state is " + state);

        if (Environment.MEDIA_MOUNTED.equals(state)) {
            try {
                 this.addToMediaDB(mSampleFile);
                 Toast.makeText(this,getString(R.string.save_record_file,
                                               mSampleFile.getAbsolutePath( )),
                                               Toast.LENGTH_LONG).show();
            } catch(Exception e) {
                 e.printStackTrace();
            }
        } else {
            Log.e(TAG, "SD card must have removed during recording. ");
            Toast.makeText(this, "Recording aborted", Toast.LENGTH_SHORT).show();
        }
        return;
    }

 
獲取當時減去灌音肇端時光如(rú)不雅為零就彪炳保存辦法不保存數據
intsampleLength = (int)((System.currentTimeMillis() - mSampleStart)/1000 );
 
Environment.MEDIA_MOUNTED.equals(state)sd卡可(kě)用就将灌音路(lù)徑添加到多媒體數據庫中(zhōng)
this.addToMediaDB(mSampleFile);
 
将信息添加到多媒體數據庫,音頻的audio格式存入數據庫中(zhōng)

 private Uri addToMediaDB(File file) {
        Log.d(TAG, "In addToMediaDB");
        Resources res = getResources();
        ContentValues cv = new ContentValues();
        long current = System.currentTimeMillis();
        long modDate = file.lastModified();
        Date date = new Date(current);
        SimpleDateFormat formatter = new SimpleDateFormat(
                  res.getString(R.string.audio_db_title_format));
        String title = formatter.format(date);

        // Lets label the recorded audio file as NON-MUSIC so that the file
        // won't be displayed automatically, except for in the playlist.
        cv.put(MediaStore.Audio.Media.IS_MUSIC, "1");cv.put(MediaStore.Audio.Media.TITLE, title);
        cv.put(MediaStore.Audio.Media.DATA, file.getAbsolutePath());
        cv.put(MediaStore.Audio.Media.DATE_ADDED, (int) (current / 1000));
        cv.put(MediaStore.Audio.Media.DATE_MODIFIED, (int) (modDate / 1000));
        cv.put(MediaStore.Audio.Media.MIME_TYPE, mAudioType);
        cv.put(MediaStore.Audio.Media.ARTIST,
                res.getString(R.string.audio_db_artist_name));
        cv.put(MediaStore.Audio.Media.ALBUM,
                res.getString(R.string.audio_db_album_name));
        Log.d(TAG, "Inserting audio record: " + cv.toString());ContentResolver resolver = getContentResolver();
        Uri base = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
        Log.d(TAG, "ContentURI: " + base);
        Uri result = resolver.insert(base, cv);
        if (result == null) {
            Toast.makeText(this, R.string.unable_to_store, Toast.LENGTH_SHORT).show();
            return null;
        }
        if (getPlaylistId(res) == -1) {
            createPlaylist(res, resolver);
        }
        int audioId = Integer.valueOf(result.getLastPathSegment());
        addToPlaylist(resolver, audioId, getPlaylistId(res));

        // Notify those applications such as Music listening to the
        // scanner events that a recorded audio file just created.
        sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, result));
        return result;
    }

Uri base =MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
 
添加到播放列表
addToPlaylist(resolver,audioId, getPlaylistId(res));
可(kě)以存入music數據
cv.put(MediaStore.Audio.Media.IS_MUSIC,"1")
sendBroadcast(newIntent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, result));
 

 
privatevoid registerRecordingListner()廣播接收者

private void registerRecordingListner() {
        if (mFmRecordingReceiver == null) {
            mFmRecordingReceiver = new BroadcastReceiver() {
                 @Override
                 public void onReceive(Context context, Intent intent) {
                     Log.d(TAG, "Received intent " +intent);
                     String action = intent.getAction();
                     Log.d(TAG, " action = " +action);
                     if (action.equals(ACTION_FM_RECORDING)) {
                         int state = intent.getIntExtra("state", STOP);
                         Log.d(TAG, "ACTION_FM_RECORDING Intent received" + state);
                         if (state == START) {
                             Log.d(TAG, "Recording start");
                             mRecordDuration = intent.getIntExtra("record_duration", mRecordDuration);
                             if(startRecord()) {
                                clientProcessName = intent.getStringExtra("process_name");
                                clientPid = intent.getIntExtra("process_id", -1);
                                startClientStatusCheck();
                             }} else if (state == STOP) {
                             Log.d(TAG, "Stop recording");
                             stopRecord();
                         }
                     }
                 }
            };
            IntentFilter iFilter = new IntentFilter();
            iFilter.addAction(ACTION_FM_RECORDING);
            registerReceiver(mFmRecordingReceiver, iFilter);
        }
    }

廣播狀況為1的時刻就開端灌音并檢查線程
 private void startClientStatusCheck()
獲取客戶端所有過程信息進行匹配如(rú)不雅啟動(dòng)的app沒有被殺逝世就持續灌音不然停止

privateboolean getClientStatus(int pid, String processName)
ActivityManageractvityManager =
                   (ActivityManager)this.getSystemService(
                                                          this.ACTIVITY_SERVICE);
 
      List<RunningAppProcessInfo>procInfos =
                                     actvityManager.getRunningAppProcesses();
 
 
      for(RunningAppProcessInfo procInfo :procInfos) {
         if ((pid == procInfo.pid)
 &&
privateRunnable clientStatusCheckThread = new Runnable()
             (procInfo.processName.equals(processName))) {
              break;
         }
      }
      procInfos.clear();
 
停止灌今後睡眠500毫秒
 
privatevoid stopClientStatusCheck()中(zhōng)斷線程mStatusCheckThread


private void stopClientStatusCheck() {
       if(mStatusCheckThread != null) {
          mStatusCheckThread.interrupt();
       }
   }

 

相關(guān)案例查看更多