總網頁瀏覽量

關於我自己

我的相片
人生的必修課是接受無常,人生的選修課是放下執著。
顯示具有 MediaPlayer 標籤的文章。 顯示所有文章
顯示具有 MediaPlayer 標籤的文章。 顯示所有文章

2012年2月15日 星期三

MediaRecorder & MediaPlayer ~ 個人錄音(五)


以Service來紀錄各種state,
在 Android 有四種 application components:
1. Activities
2. Services
3. Content providers
4. Intent
在 Android 應用程式裡,有一種沒有 UI 的類別(android.app.Service),稱之為 Service。

Service (import android.app.Service) 是一個 background process(背景程序),不像 Activity 有著介面可以讓使用者操作, Service 的生命周期都是比較長,可以在背景長駐執行一些不中斷的程式, 像是背景錄音, 播放音樂, 定期自動更新








PersonRecordService.java
public class PersonRecordService extends Service{
    static final int IDLE_STATE = 0;
    static final int RECORDING_STATE = 1;
    static final int PLAYING_STATE = 2;
    static final int PAUSE_STATE = 3;
    int mState = 0;

    public interface OnStateChangedListener {
        public void onStateChanged(int state);
        public void onError(int error);
    }

    OnStateChangedListener mOnStateChangedListener = null;

    public void setOnStateChangedListener(OnStateChangedListener listener) {
        mOnStateChangedListener = listener;
    }

    private void setState(int state) {
        mState = state;
        signalStateChanged(mState);
    }
    private void signalStateChanged(int state) {
        if (mOnStateChangedListener != null)
            mOnStateChangedListener.onStateChanged(state);
    }
}

PersonRecordActivity.java
public class PersonRecordActivity extends Activity implements PersonRecordService.OnStateChangedListener {

    PersonRecordService personRecordService = new PersonRecordService();
    personRecordService.setOnStateChangedListener(this);
    WakeLock mWakeLock;
    public void onCreate(Bundle savedInstanceState) {
        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "PersonRecord");

        @Override
        public void onStateChanged(int state) {
            if (state == PersonRecordService.PLAYING_STATE || state == PersonRecordService.RECORDING_STATE) {
                // 背景恆亮
                mWakeLock.acquire(); 
            } else {
                if (mWakeLock.isHeld())
                    // 取消背景恆亮
                    mWakeLock.release();
            }
            updateUi();
        }

        @Override
        public void onError(int error) {
  
        }
    }
}

當手機在螢幕醒著的狀態, 長時間不觸碰面板則螢幕會自動暗掉, 進而進入待機狀態,

為防止手機進入待機狀態, 則需使用到 PowerManage 這個功能

AndroidManifest.xml

2012年2月14日 星期二

MediaRecorder & MediaPlayer ~ 個人錄音(四)

Implement 「Play」function







PersonRecordActivity.java
public class PersonRecordActivity extends Activity implements OnCompletionListener, OnErrorListener{

    static final int IDLE_STATE = 0;
    // 播放狀態以2來表示
    static final int PLAYING_STATE = 2;
    // 此值紀錄record的起始時間
    long recordingStart =0;
    MediaPlayer mPlayer = null;
    MediaRecorder mPlayer = null;

    public void onClick(View ImageButton) {
        switch (ImageButton.getId()) {
            // 當user按下play按鈕
            case R.id.ib_play:
                startPlayback();
                updateUi();
                break;
        .
        .
        .
        }
    }

    public void startPlayback() {
        try {
            if(mPlayer == null) {
                mPlayer = new MediaPlayer();
            }        
        // 欲播放檔案之完整路徑
        mPlayer.setDataSource(recordingFile.getAbsolutePath());
        // 監聽"檔案播放完"的事件
        mPlayer.setOnCompletionListener(this);
        // 監聽"檔案播放中產生錯誤"的事件
        mPlayer.setOnErrorListener(this);
        // 緩衝
        mPlayer.prepare(); 
        // 開始播放            
        mPlayer.start();
    } catch (NullPointerException e) {
        mPlayer = null;
        return;       
    } catch (IllegalArgumentException e) {
        mPlayer = null;
        return;
    } catch (IOException e) {
        mPlayer = null;
        return;
    }

        recordingStart = System.currentTimeMillis();
        setState(PLAYING_STATE);
    }

    private void setState(int state) {
        mState = state;
    }

    public void onCompletion(MediaPlayer mp) {
        // 把狀態設為IDLE
        mState = IDLE_STATE;
        stop();
    }

    public boolean onError(MediaPlayer mp, int what, int extra) {
        stop();
        return true;
    }

    // stop()的狀況有兩種, 1. 錄音狀態的stop 2.播放狀態的stop
    public void stop() {
        stopRecording();
        stopPlayback();
    }

    public void stopRecording() {
        if (mRecorder == null)
            return;

        mRecorder.stop();
        mRecorder.release();
        mRecorder = null;  

        setState(IDLE_STATE);
    }

    public void stopPlayback() {
        if (mPlayer == null)
            return;

        mPlayer.stop();
        mPlayer.release();
        mPlayer = null;

        setState(IDLE_STATE);
    }
}

2012年2月13日 星期一

MediaRecorder & MediaPlayer ~ 個人錄音(三)







Implement 「Record」function

PersonRecordActivity.java
// 給一個布林值sdCardExit判別SD card是否存在
boolean sdCardExit = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
// 自動抓當前的系統時間, 格式自訂為yyyy.MM.dd_HH.mm.ss
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy.MM.dd_HH.mm.ss");
// 錄音狀態以1來表示
static final int RECORDING_STATE = 1;
// 錄音檔名的部份命名
final String SAMPLE_PREFIX = "PersonRecord_";
// 此值紀錄record的起始時間
long recordingStart =0;
MediaRecorder mRecorder = null;

public void onClick(View ImageButton) {
    switch (ImageButton.getId()) {
        // 當user按下record按鈕
        case R.id.ib_record:
            // 若SD card存在則執行以下動作
            if (sdCardExit) {
                // 先將狀態設為錄音中
                int mState = RECORDING_STATE;
                // 若正在播放檔案, 則先停止它
                stopAudioPlayback();
                // 開始錄音
                startRecording(MediaRecorder.OutputFormat.THREE_GPP, ".3gp", this);
                // 更新各ImageButton的圖示
                updateUi();
            } else {
                // 若SD card不存在則show toast提醒user
                Toast.makeText(context, "SD Card not exist", Toast.LENGTH_SHORT).show();
            }
            break;
    .
    .
    .
    }
}

private void stopAudioPlayback() {
  Intent i = new Intent("com.android.music.musicservicecommand");
  i.putExtra("command", "pause");

  sendBroadcast(i);
 }

public void startRecording(int outputfileformat, String extension, Context context) {
    stop();
    try {
        File sampleDir = new File(PersonRecordActivity.SAVE_PATH);
        File recordingFile = new File(sampleDir + java.io.File.separator
        + SAMPLE_PREFIX + sdf.format(new java.util.Date())
        + extension);
    } catch (Exception e) {

    return;
}

    mRecorder = new MediaRecorder();
    // 呼叫setAudioSource 指定錄音來源為麥克風錄音
    mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    // 呼叫setOutputFormat設定錄音檔案的輸出格式
    mRecorder.setOutputFormat(outputfileformat);
    // 呼叫MediaRecorder 的setAudioEncoder設定錄音的編碼方式
    mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
    // 呼叫setOutputFile 設定錄音檔案的存放位置
    mRecorder.setOutputFile(recordingFile.getAbsolutePath());

    try {
        // 呼叫prepare 準備錄音
        mRecorder.prepare();
        // 呼叫start開始錄音
        mRecorder.start();
    } catch (IOException exception) {
        // 呼叫reset可以重複使用已經產生的MediaRecorder 物件,但必須回到setAudioSource重新設定
        mRecorder.reset();
        // 呼叫release 會立即釋放MediaRecorder 佔用的資源。之後如果想要再使用MediaRecorder 功能,必須重新建立MediaRecorder 物件實體而無法重複使用已經被釋放的MediaRecorder 物件
        mRecorder.release();
        mRecorder = null;
        return;
    }
    // 給定錄音起始時間為當前系統時間
    recordingStart = System.currentTimeMillis();
  
    setState(RECORDING_STATE);
}

private void setState(int state) {
    mState = state;
}

2012年2月12日 星期日

MediaRecorder & MediaPlayer ~ 個人錄音(二)








按下錄音的ImageButton後, 啟動Runnable開始計時, 並改變TextView tv_timerView的值.

PersonRecordActivity.java
Runnable mUpdateTimer = new Runnable() {
    public void run() {
        updateTimerView();
    }
};

TextView tv_timerView = (TextView) findViewById(R.id.tv_timerView); 
String mTimerFormat = getResources().getString(R.string.timer_format);

private void updateTimerView() {
    long time = progress();
    Handler mHandler = new Handler(); 
    mHandler.postDelayed(mUpdateTimer, 1000); //延遲1000毫秒執行一次mUpdateTimer
    String timeStr = String.format(mTimerFormat, time / 60, time % 60);
    tv_timerView.setText(timeStr);
}

public int progress() {
    //(當前系統時間-開始錄音時的時間)/1000←轉為秒數
    return (int) ((System.currentTimeMillis() - recordingStart) / 1000);
 }

重點是String mTimerFormat載入
strings.xml
%02d:%02d

XML當地語系化資料交換格式 Xliff是XML Localization Interchange File Format 的縮寫
這就跟在strings.xml中宣告字串一樣
只是不寫死字串是什麼
而用%d, %s去取代
這樣在.java中getResources()這個字串資源時 
可以隨意塞給它string或數字


按下播放的ImageButton後, 以ProgressBar呈現檔案播放進度.

PersonRecordActivity.java
ProgressBar pb_timerView = (ProgressBar) findViewById(R.id.pb_timerView);
int recordingLength = (int) ((System.currentTimeMillis() - recordingStart) / 1000);

private void updateTimerView() {
long time = progress();
if(mState == PersonRecordService.PLAYING_STATE)
    pb_timerView.setProgress((int)(100*time / recordingLength));
}

public int progress() {
    if (mState == RECORDING_STATE || mState == PLAYING_STATE)
        return (int) ((System.currentTimeMillis() - recordingStart) / 1000);
    return 0;
}