參考資料: Amazon.com: Hello, Android: Introducing Google's Mobile Development Platform (Pragmatic Programmers) (9781934356562): Ed Burnette: Books
這段內容主要為:
第5章 多媒體
5.1 播放聲音
[Note] Android所支援的聲音格式
還有印象Apple的電視廣告裡頭有人的剪影隨著他們iPod的節奏跳舞? 你應該會想要你的產品有這種令人興奮的感覺(
這章主要說明如何將多媒體元素加入到Android程式中, 你不一定要讓你的程式使用者在走廊上亂蹦亂跳, 但假如適當地加入這些元素至少可以讓使用者臉上發出會心一笑.
5.1 播放聲音
It was a dark and stormy night.... There goes the starting shot, and
they’re off.... The crowd goes wild as State sinks a three-pointer with
one second remaining....
聲音的提示會瀰漫整個環境也會影響我們情緒的節奏, 聲音可以把聲音想像成可傳入使用者的腦袋的另一種方式, 就好比顯示在銀幕上的圖形可以讓使用者理解, 我們可以使用聲音輔助與更加強這樣的感覺. Android透過在android.media裡的MediaPlayer類別支援聲音與音樂的輸出. (http://d.android.com/guide/topics/media) 底下我們來實作一個簡單的範例讓使用者按下鍵盤或D-Pad就發出一點聲音.
底下一樣遵循一般“Hello, Android”專案, 以底下的參數來建立一個新的Android專案:
Project name: Audio
Build Target: Android 2.2
Application name: Audio
Package name: org.example.audio
Create Activity: Audio
Min SDK Version: 8
接下來我們需要一些可以播放的聲音檔, 可以利用Windows內建的錄音程式來產生(位置在: Start > Programs > Accessories > Entertainment > Sound Recorder on Windows XP), 搭配一個不貴的麥克風, 調整好音量錄音, 在儲存時記得要選一下聲音檔儲存的格式, 以符合Anroid可辨識的版本, 如:
將處理好的錄音檔複製進專案中的res/raw目錄, 只要複製進去res目錄, Android Eclipse plug-in會自動在R類別定義相關的Java symbol連結到這些資源. 複製好後, 專案的目錄會看起來像這樣:
[Fig 5.2]
接下來開始撰寫Audio activity的主程式部分, 首先宣告一個mp的變數用來代表MediaPlayer類別的物件, 在這整個程式中, 我們同一時間只使用一個MediaPlayer來播放聲音.
## src/org/example/audio/Audio.java
package org.example.audio;
import android.app.Activity;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.view.KeyEvent;
public class Audio extends Activity {
private MediaPlayer mp;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
}
}
setVolumeControlStream()方法告訴Android當程式正在執行時, 使用者按下音量鍵應該要調整音樂的音量, (非調整電話鈴聲大小聲). 下一步, 我們攔截使用者按下按鍵的事件與播放對應的聲音, 這部份透過改寫Activity.onKeyDown():
## Audio/src/org/example/audio/Audio.java
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
int resId;
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_UP:
resId = R.raw.up;
break;
case KeyEvent.KEYCODE_DPAD_DOWN:
resId = R.raw.down;
break;
case KeyEvent.KEYCODE_DPAD_LEFT:
resId = R.raw.left;
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
resId = R.raw.right;
break;
case KeyEvent.KEYCODE_DPAD_CENTER:
case KeyEvent.KEYCODE_ENTER:
resId = R.raw.enter;
break;
case KeyEvent.KEYCODE_A:
resId = R.raw.a;
break;
case KeyEvent.KEYCODE_S:
resId = R.raw.s;
break;
case KeyEvent.KEYCODE_D:
resId = R.raw.d;
break;
case KeyEvent.KEYCODE_F:
resId = R.raw.f;
break;
default:
return super.onKeyDown(keyCode, event);
}
// Release any resources from previous MediaPlayer
if (mp != null) {
mp.release();
}
// Create a new MediaPlayer to play this sound
mp = MediaPlayer.create(this, resId);
mp.start();
// Indicate this key was handled
return true;
}
onKeyDown()中前半部主要是利用switch(keyCode)使用者按下的按鍵來選擇要播放的聲音檔, 在播放聲音之前先呼叫release()用來停止現在正在播放的聲音與釋放舊的MediaPlayer所佔用的資源, 假如忘記做release()的動作, 程式有可能會當掉(可參考底下的說明). 資源釋放完畢後, 呼叫create()使用選到的資源ID來建立一個新的MediaPlayer, 接下來用start()開始播放. start()是一個非同步的方法, 所以不管要播放的聲音為多長, 呼叫完即回傳回呼叫的程式. 假如有必要的話, 可以撰寫setOnCompletionListener()來設定播放完通知.
這程式執行後, 只要按下上面程式對應的按鍵, 就會聽到一個聲音. 假如沒有聽到任何聲音, 檢查一下音量設計(噗), 或看一下LogCat view中的除錯訊息. 假如你的Android電話沒有鍵盤, D-pad或軌跡球, 可以按下Menu鍵讓軟體鍵盤顯示出來. 要注意聲音的輸出有時可能會斷斷續續或有延遲, 可以試試看不同的格式(如把MP3換成OGG)並降低聲音的取樣位元. 也可以試試看用SoundPool類別, 這類別可以支援多音軌播放, 但這類別在1.0時bug不少也沒什麼說明文件, 在1.5之後有比較穩定. 下一章節會繼續提到如何用一行程式碼播放影片.
[Note: 當情況變糟糕時...]
假如你寫夠多的多媒體程式, 你就會發現Android的MediaPlayer是一個反覆無常的怪獸, 新版的Android比起舊版的已經有改善許多, 但還是有時會無預警當掉, 其中之一的原因主要是MediaPlayer是一個原生的應用程式但建構了一層Java介面在其上(a native application with a thin layer of Java on top of it), 原生的播放器程式碼主要為了效率而最佳化, 裡頭就沒有做太多的錯誤檢查, 幸好Android超強的Linux程序保護機制能防止錯誤發生時所引發的不穩定. 模擬器(或真實的裝置)與其他應用程式或繼續正常的執行, 使用者只會看到當掉的應用程式被強迫關閉, 並跳出一個顯示錯誤訊息的對話盒.
在程式開發期間, 我們可以透過一些工具所提供的診斷訊息來輔助我們找到問題發生的原因. 一些追蹤的訊息會被可以從Android system log中查詢, 在Eclipse的話我們可以用LogCat view或透過adb logcat的命令來觀看(這部份可以參考: 第三章有關利用Log訊息除錯的部分)
[Note: Android支援的聲音格式]
Android聲音格式支援可以分成三個部分討論: 規格書(paper), 模擬器(emulator), 實機. 在規格書上Android支援底下的檔案類型(新版本可能會有些許改變):
• WAV (PCM uncompressed, 無壓縮)
• AAC (Apple iPod format, unprotected, 無保護)
• MP3 (MPEG-3)
• WMA (Windows media audio)
• AMR (Speech codec)
• OGG (Ogg Vorbis)(http://www.vorbis.com)
• MIDI (Instruments)
實際去測試Android的支援, 模擬器目前播放ok只有OGG, WAV, 與 MP3, 所以程式開發較建議使用這三種格式. Android原生的聲音格式是44.1kHz 16位元立體聲, 假如使用WAV檔錄製這樣的格式, 檔案會很大, 所以建議使用OGG或MP3(人聲可以使用單音, 音樂用立體聲). OGG較適合用在短音效上, 如遊戲的音效. 盡量不要使用不常用的取樣頻率如 8kHz, 播放的效果會非常差, 使用11kHz, 22kHz, 44.1kHz,的取樣頻率以達最佳的效果. 謹記Android電話會有一個小型的擴音器, 但大多數使用者都會插入耳機使用(類似iPod), 所以很細微的聲音雜訊都會很清楚, 盡可能使用較高品質的聲音.
您好,謝謝你的資訊
回覆刪除能否請教
我正在寫一各android的功能叫onKeyDown
我想要在長按音量鈕(>3秒)後能夠啟動一各class(在下面的程式碼中我先以toast代替這各class)
由於要能夠長駐背景
所以我把偵測長按音量鈕的程式碼放在service裡面
不過程式執行到onKeyDown()這一段沒有反應,
我還在onkeydown()後面放了一各ff()測試,這ff()是會被run然後跳出"測試ff是否被執行",
這代表在我的程式碼中,onKeyDown()無法執行
可以請教您這是什麼原因嗎?
謝謝
===========================================
package com.myvoulume;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.view.KeyEvent;
import android.widget.Toast;
public class MyVolumeBackground extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
public void onCreate(){
super.onCreate();
Toast.makeText(this, "On Create...", Toast.LENGTH_SHORT).show();
}
public void onStart(Intent intent, int startID){
super.onStart(intent, startID);
Toast.makeText(this, "On Start...", Toast.LENGTH_SHORT).show();
onKeyDown(startID, null);
ff();
}
public void ff(){
Toast.makeText(this, "測試ff是否被執行", Toast.LENGTH_SHORT).show();
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode==KeyEvent.KEYCODE_VOLUME_UP){
Toast.makeText(this, "按下了上音量鍵", Toast.LENGTH_SHORT).show();
return true;
}else if(keyCode==KeyEvent.KEYCODE_VOLUME_DOWN){
Toast.makeText(this, "按下了下音量鍵", Toast.LENGTH_SHORT).show();
// return true;
}
return true;
}
public void onDestroy(){
super.onDestroy();
}
}
===========================================
hi 你好,
回覆刪除我的程式範例是Overwrite Activity裡面的onKeyDown(),
onKeyDown()是由系統來呼叫,
你用Service沒有onKeyDown可以overwrite,
中間在onStart呼叫onKeyDown(startID, null);
傳過去的KeyEvent永遠是null,
當然onKeyDown都不會判斷到任何按鍵,
你可以到Google搜尋找"android KeyEvent Service"
有相關的討論跟資料,
就我目前所知,
在Services中似乎沒辦法抓到KeyEvents,
還是有新的其他方法我就不知道了,
希望有回答到你的問題.
Chipmunk
回覆刪除謝謝你阿
因為我對java不熟,
這樣子我知道拉
我在想想
用action.SCREEN_ON的方式
改用偵測電源鍵長按的方式看看
試成功在PO上來