AndroidFM模块学习之录音功能 - 新闻资讯 - 云南小程序开发|云南软件开发|云南网站建设-昆明葵宇信息科技有限公司

159-8711-8523

云南网建设/小程序开发/软件开发

知识

不管是网站,软件还是小程序,都要直接或间接能为您产生价值,我们在追求其视觉表现的同时,更侧重于功能的便捷,营销的便利,运营的高效,让网站成为营销工具,让软件能切实提升企业内部管理水平和效率。优秀的程序为后期升级提供便捷的支持!

您当前位置>首页 » 新闻资讯 » 技术分享 >

AndroidFM模块学习之录音功能

发表时间:2020-10-19

发布人:葵宇科技

浏览次数:42


前些天禀析了一下FM的流程以及重要类,接下来我们分析一下FM的灌音功能;
[img]http://img.blog.csdn.net/20150106172922265?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdGZzbG92ZXhpemk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
Fm灌音时,当点击了灌音按钮,会发一个广播出去,源码在FMRadioService.java中
<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启动与封闭
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)办法发送一个广播并附带一个开关灌音的状况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启动与封闭
<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()办法里注册广播接收机制,一个广播是灌音状况,一个是封闭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()办法里写了卸载注册停止灌音
 


起首看下贱程图:
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();注册接收办法中停止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;
    }

重要经由过程以下办法实现:
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();
    }

 
发送一个灌音状况广播

 
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一个类的对象,USER_ALL= -1返回的一个字符串UserHandle{-1}
回调办法,去fmradioservice.java回调
mCallbacks.onRecordingStarted();办法
mCallbacks.onRecordingStopped();办法
public void registerFMRecordingStatus()
 
启动灌音

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;
    }

灌音不为空先设置为停止灌音大年夜新设置释放资本
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();
 
获取当前时光,发送灌音状况广播,启动通知
mSampleStart= System.currentTimeMillis();
        sendRecordingStatusIntent(START);
        startNotification();
mFmRecordingOn 为true 停止灌音
 
发送通知,设置长途控制,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);
    }



停止灌音,保存灌音文件,停止灌音之前台,停止状体切换
 private void stopRecord()
sendRecordingStatusIntent(STOP);
        saveFile();
        stopForeground(true);
        stopClientStatusCheck();
保存灌音办法(灌音一开录,就在往默认内置T中写数据以字节方法写入数据)
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;
    }

 
获取当时减去灌音肇端时光如不雅为零就彪炳保存办法不保存数据
intsampleLength = (int)((System.currentTimeMillis() - mSampleStart)/1000 );
 
Environment.MEDIA_MOUNTED.equals(state)sd卡可用就将灌音路径添加到多媒体数据库中
this.addToMediaDB(mSampleFile);
 
将信息添加到多媒体数据库,音频的audio格式存入数据库中

 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));
可以存入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()
获取客户端所有过程信息进行匹配如不雅启动的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()中断线程mStatusCheckThread


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

 

相关案例查看更多