Android音頻播放 支付金額播報(SoundPool、MediaPlayer)
demo地址
demo采用了Android音頻播放的兩種方式
SoundPool 和 MediaPlayer
兩者區別是 SoundPool需要優先初始化加載 將音頻加載到記憶體中 播放時從記憶體中獲取音頻檔案 不加載無法播放
MediaPlayer不需要初始化加載 隨時都可以進行播放
由此可見 SoundPool 播放會比MediaPlayer 更快一些
SoundUtil 對SoundPool的封裝 由于SoundPool是提前加載、緩沖在進行播放 所以播放組合音頻時會同時播放
為了解決這一問題 我們可以對相應的音頻進行延遲 詳情可見SoundUtil代碼
MediaPlayUtil 對MediaPlayer的封裝 由于某些極短音頻無法播放(幾毫秒狀態) 這是建議使用SoundPool方法播放
對于音頻播放是異步的 否則會阻塞主執行緒 導致程式卡頓 所以使用了執行緒池 ----> ExecutorService
MediaPlayer的基本使用:
(1) 創建MediaPlayer實體
可以使用直接new的方式:
MediaPlayer mp = new MediaPlayer();
也可以使用create的方式,如:
//這時就不用呼叫setDataSource了
MediaPlayer mp = MediaPlayer.create(this, R.raw.test);
(2)設定播放源
MediaPlayer要播放的檔案主要包括3個來源:
a. 用戶在應用中事先自帶的resource資源
例如:MediaPlayer.create(this, R.raw.test);
b. 存盤在SD卡或其他檔案路徑下的媒體檔案或存放在assets目錄下
例如:mp.setDataSource("/sdcard/test.mp3");
c. 網路上的媒體檔案
例如:mp.setDataSource(" http://www.citynorth.cn/music/confucius.mp3");
MediaPlayer的setDataSource一共四個方法:
setDataSource (String path)
setDataSource (FileDescriptor fd)
setDataSource (Context context, Uri uri)
setDataSource (FileDescriptor fd, long offset, long length)
(3)控制播放器的幾個方法:
serDataSource() 設定要播放的音頻檔案的位置
prepare() 在開始播放之前呼叫這個方法完成準備作業 同步---->create方法創建的,那么第一次啟動播放前不需要再呼叫prepare()了,因為create方法里已經呼叫過了,
prepareAsync() 在開始播放之前呼叫這個方法完成準備作業 異步
start() 開始或繼續播放音頻
pause() 暫停播放音頻
reset() 將MediaPlayer物件重置到剛剛創建的狀態 播放器從Error狀態中恢復過來,重新會到Idle狀態
seekTo() 從指定的位置開始播放音頻 可以讓播放器從指定的位置開始播放,需要注意的是該方法是個異步方法,也就是說該方法回傳時并不意味著定位完成,尤其是播放的網路檔案,真正定位完成時會觸發OnSeekComplete.onSeekComplete()監聽
stop() 停止播放音頻,呼叫這個方法后的MediaPlayer物件無法在播放音頻
release() 釋放掉與MediaPlayer物件相關的資源 一旦確定不再使用播放器時應當盡早呼叫它釋放資源
isPlaying() 判斷當前MediaPlayer是否正在播放音頻
getDuration() 獲取載入的音頻檔案時長
setLooping(boolean looping):設定是否回圈播放,
getCurrentPosition():獲取當前流媒體的播放的位置,單位是毫秒
isLooping():判斷是否回圈播放
setAudioStreamType(int streamtype):設定播放流媒體型別
setNextMediaPlayer(MediaPlayer next):設定當前流媒體播放完畢,下一個播放的MediaPlayer
(4) 設定不同的監聽器 監聽不同的播放狀態
例如:
setOnCompletionListener(MediaPlayer.OnCompletionListener listener)
setOnErrorListener(MediaPlayer.OnErrorListener listener)等
設定播放器時需要考慮到播放器可能出現的情況設定好監聽和處理邏輯,以保持播放器的健壯性和穩定性
SoundPool簡單使用:
(1)創建SoundPool實體
SoundPool mSoundPool = new SoundPool.Builder()
.setMaxStreams(16)//同時播放流的最大數量,當播放的流的數目大于此值,則會選擇性停止優先級較低的流
.build();
構造器如下:
SoundPool(int maxStreams, int streamType, int srcQuality)
引數maxStreams:指定支持多少個聲音;
引數streamType:指定聲音型別:
引數srcQuality:指定聲音品質,
(2)加載音頻 load()
SoundPool提供了如下4個load方法:
//從 resld 所對應的資源加載聲音,
int load(Context context, int resld, int priority)
//加載 fd 所對應的檔案的offset開始、長度為length的聲音,
int load(FileDescriptor fd, long offset, long length, int priority)
//從afd 所對應的檔案中加載聲音,
int load(AssetFileDescriptor afd, int priority)
//從path 對應的檔案去加載聲音,
int load(String path, int priority)
(3)播放 play()
play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)
引數soundID:指定播放哪個聲音;
引數leftVolume、rightVolume:指定左、右的音量:
引數priority:指定播放聲音的優先級,數值越大,優先級越高;
引數loop:指定是否回圈,0:不回圈,-1:回圈,其他值表示要重復播放的次數;
引數rate:指定播放的比率,數值可從0.5到2, 1為正常比率,
(4)釋放資源 release()
注意* SoundPool 必須、必須、必須 先加載load在播放play*****
/**
* @author renquan
* SoundPool 方式播放
*/
public class SoundUtil {
private ExecutorService mExecutorService;
private static SoundUtil soundUtil;
private final SoundPool soundPool;
private String[] voice = {"1", "9", "dot"
, "hundred", "hundred_million", "success", "ten", "ten_thousand", "thousand", "yuan"};
private static HashMap<String, Integer> soundMap = new HashMap<>();
private SoundUtil(Context context) {
this.mExecutorService = Executors.newCachedThreadPool();
//構建物件
SoundPool.Builder spb = new SoundPool.Builder();
spb.setMaxStreams(100);
soundPool = spb.build(); //創建SoundPool物件
}
public static SoundUtil getInstance() {
if (soundUtil == null) {
init();
}
return soundUtil;
}
public static SoundUtil init() {
if (soundUtil == null) {
synchronized (SoundPool.class) {
if (soundUtil == null) {
soundUtil = new SoundUtil(MyApp.context);
try {
for (int i = 0; i < soundUtil.voice.length; i++) {
String soundName = soundUtil.voice[i];
int soundId = soundUtil.soundPool.load(MyApp.context.getAssets().openFd(String.format(VoiceConstants.FILE_PATH, soundName)), 1);
soundMap.put(soundName, soundId);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
return soundUtil;
}
//數字播報
public void playNum(final String soundName) {
mExecutorService.execute(new Runnable() {
@Override
public void run() {
synchronized (SoundUtil.class) {
try {
if (null == soundName || soundName.isEmpty()) {
return;
}
Integer soundId = soundMap.get(soundName);
if (null != soundId) {
soundPool.play(soundId, 1, 1, 1, 0, 1);
if (soundName.length() == 1 || soundName.equals("dot")) {
Thread.sleep(300);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
}
//組合播報
public void play(final List<String> voicePlayer) {
mExecutorService.execute(new Runnable() {
@Override
public void run() {
if (voicePlayer.size() <= 0) {
return;
}
start(voicePlayer);
}
});
}
public void playMoney(final String start, final String money) {
mExecutorService.execute(new Runnable() {
@Override
public void run() {
VoiceBuilder voiceBuilder = new VoiceBuilder.Builder()
.start(start)
.money(money)
.unit(VoiceConstants.YUAN)
.checkNum(false)
.builder();
List<String> voicePlay = VoiceTextTemplate.genVoiceList(voiceBuilder);
start(voicePlay);
}
});
}
private void start(final List<String> voicePlayer) {
mExecutorService.execute(new Runnable() {
@Override
public void run() {
synchronized (this) {
for (int i = 0; i < voicePlayer.size(); i++) {
String soundName = voicePlayer.get(i);
if (null == soundName || soundName.isEmpty()) {
continue;
}
Integer soundId = soundMap.get(soundName);
if (null != soundId) {
soundPool.play(soundId, 1, 1, 1, 0, 1);
}
try {
if (soundName.equals("success")) {
Thread.sleep(800);
} else {
Thread.sleep(350);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
});
}
}
/**
* @author renquan
* @describe 音頻播放
* MediaPlayer 方式播放音頻
* @ideas
*/
public class MediaPlayUtil {
private ExecutorService mExecutorService;
private Context mContext;
private MediaPlayUtil(Context context) {
this.mContext = context;
this.mExecutorService = Executors.newCachedThreadPool();
}
@Nullable
private volatile static MediaPlayUtil mVoicePlay = null;
/**
* 單例
*
* @return
*/
@Nullable
public static MediaPlayUtil with(Context context) {
if (mVoicePlay == null) {
synchronized (MediaPlayUtil.class) {
if (mVoicePlay == null) {
mVoicePlay = new MediaPlayUtil(context);
}
}
}
return mVoicePlay;
}
/**
* 默認收款成功樣式
*
* @param money
*/
public void play(String money) {
play(money, false);
}
/**
* 設定播報數字
*
* @param money
* @param checkNum
*/
public void play(String money, boolean checkNum) {
VoiceBuilder voiceBuilder = new VoiceBuilder.Builder()
.start(VoiceConstants.SUCCESS)
.money(money)
.unit(VoiceConstants.YUAN)
.checkNum(checkNum)
.builder();
executeStart(voiceBuilder);
}
public void checkMoney(String money, boolean checkNum) {
VoiceBuilder voiceBuilder = new VoiceBuilder.Builder()
.start(VoiceConstants.PLEASEPAY)
.money(money)
.unit(VoiceConstants.YUAN)
.checkNum(checkNum)
.builder();
executeStart(voiceBuilder);
}
/**
* 接收自定義
*
* @param voiceBuilder
*/
public void play(@NonNull VoiceBuilder voiceBuilder) {
executeStart(voiceBuilder);
}
/**
* 開啟執行緒
*
* @param builder
*/
private void executeStart(@NonNull VoiceBuilder builder) {
final List<String> voicePlay = VoiceTextTemplate.genVoiceList(builder);
if (voicePlay == null || voicePlay.isEmpty()) {
return;
}
mExecutorService.execute(new Runnable() {
@Override
public void run() {
start(voicePlay);
}
});
}
/**
* 開始播報
*
* @param voicePlay
*/
private void start(@NonNull final List<String> voicePlay) {
synchronized (MediaPlayUtil.this) {
final CountDownLatch mCountDownLatch = new CountDownLatch(1);
AssetFileDescriptor assetFileDescription = null;
try {
final int[] counter = {0};
assetFileDescription = FileUtils.getAssetFileDescription(mContext,
String.format(VoiceConstants.FILE_PATH, voicePlay.get(counter[0])));
final MediaPlayer mMediaPlayer = new MediaPlayer();
mMediaPlayer.setDataSource(
assetFileDescription.getFileDescriptor(),
assetFileDescription.getStartOffset(),
assetFileDescription.getLength());
mMediaPlayer.prepareAsync();
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mMediaPlayer.start();
}
});
mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
mediaPlayer.reset();
counter[0]++;
if (counter[0] < voicePlay.size()) {
try {
AssetFileDescriptor fileDescription2 = FileUtils.getAssetFileDescription(mContext,
String.format(VoiceConstants.FILE_PATH, voicePlay.get(counter[0])));
mediaPlayer.setDataSource(
fileDescription2.getFileDescriptor(),
fileDescription2.getStartOffset(),
fileDescription2.getLength());
mediaPlayer.prepare();
} catch (IOException e) {
e.printStackTrace();
mCountDownLatch.countDown();
}
} else {
mediaPlayer.release();
mCountDownLatch.countDown();
}
}
});
} catch (Exception e) {
e.printStackTrace();
mCountDownLatch.countDown();
} finally {
if (assetFileDescription != null) {
try {
assetFileDescription.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
try {
mCountDownLatch.await();
notifyAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* @author renquan
* @date on 2020-12-09 15:25
* @describe 音頻組合
* @ideas
*/
public class VoiceTextTemplate {
/**
* 音頻組合
*
* @param voiceBean
* @return
*/
@NonNull
public static List<String> genVoiceList(@NonNull VoiceBuilder voiceBean) {
List<String> result = new ArrayList<>();
String start = voiceBean.getStart();
String money = voiceBean.getMoney();
String unit = voiceBean.getUnit();
boolean checkNum = voiceBean.isCheckNum();
if (!TextUtils.isEmpty(start)) {
result.add(start);
}
if (!TextUtils.isEmpty(money)) {
if (checkNum) {
result.addAll(createReadableNumList(money));
} else {
result.addAll(genReadableMoney(money));
}
}
if (!TextUtils.isEmpty(unit)) {
result.add(unit);
}
return result;
}
/**
* 全轉成 中文 RMB
*
* @param numString
* @return
*/
@NonNull
private static List<String> genReadableMoney(@NonNull String numString) {
List<String> result = new ArrayList<>();
if (!TextUtils.isEmpty(numString)) {
if (numString.contains(VoiceConstants.DOT_POINT)) {
String integerPart = numString.split("\\.")[0];
String decimalPart = numString.split("\\.")[1];
List<String> intList = readIntPart(integerPart);
List<String> decimalList = readDecimalPart(decimalPart);
result.addAll(intList);
if (!decimalList.isEmpty()) {
result.add(VoiceConstants.DOT);
result.addAll(decimalList);
}
} else {
result.addAll(readIntPart(numString));
}
}
return result;
}
@NonNull
private static List<String> readDecimalPart(@NonNull String decimalPart) {
List<String> result = new ArrayList<>();
if (!"00".equals(decimalPart)) {
char[] chars = decimalPart.toCharArray();
for (char ch : chars) {
result.add(String.valueOf(ch));
}
}
return result;
}
/**
* 全轉成數字
*
* @param numString
* @return
*/
@NonNull
private static List<String> createReadableNumList(@NonNull String numString) {
List<String> result = new ArrayList<>();
if (!TextUtils.isEmpty(numString)) {
int len = numString.length();
for (int i = 0; i < len; i++) {
if ('.' == numString.charAt(i)) {
result.add(VoiceConstants.DOT);
} else {
result.add(String.valueOf(numString.charAt(i)));
}
}
}
return result;
}
/**
* 回傳數字對應的音頻
*
* @param integerPart
* @return
*/
@NonNull
private static List<String> readIntPart(@NonNull String integerPart) {
List<String> result = new ArrayList<>();
String intString = MoneyUtils.readInt(Integer.parseInt(integerPart));
int len = intString.length();
for (int i = 0; i < len; i++) {
char current = intString.charAt(i);
if (current == '拾') {
result.add(VoiceConstants.TEN);
} else if (current == '佰') {
result.add(VoiceConstants.HUNDRED);
} else if (current == '仟') {
result.add(VoiceConstants.THOUSAND);
} else if (current == '萬') {
result.add(VoiceConstants.TEN_THOUSAND);
} else if (current == '億') {
result.add(VoiceConstants.TEN_MILLION);
} else {
result.add(String.valueOf(current));
}
}
return result;
}
}
/**
* @author renquan
* @date on 2020-12-09 17:54
* @describe 關于金錢的工具類
* @ideas
*/
public class MoneyUtils {
private static final char[] NUM = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
private static final char[] CHINESE_UNIT = {'元', '拾', '佰', '仟', '萬', '拾', '佰', '仟', '億', '拾', '佰', '仟'};
/**
* 回傳關于錢的中文式大寫數字,支僅持到億
*/
@NonNull
public static String readInt(int moneyNum) {
String res = "";
int i = 0;
if (moneyNum == 0) {
return "0";
}
if (moneyNum == 10) {
return "拾";
}
if (moneyNum > 10 && moneyNum < 20) {
return "拾" + moneyNum % 10;
}
while (moneyNum > 0) {
res = CHINESE_UNIT[i++] + res;
res = NUM[moneyNum % 10] + res;
moneyNum /= 10;
}
return res.replaceAll("0[拾佰仟]", "0")
.replaceAll("0+億", "億")
.replaceAll("0+萬", "萬")
.replaceAll("0+元", "元")
.replaceAll("0+", "0")
.replace("元", "");
}
}
/**
* @author renquan
* @date on 2020-12-10 09:17
* @describe 字符相關的工具類
* @ideas
*/
public class StringUtils {
/**
* 提取字串中的 數字 帶小數點 ,沒有就回傳""
*
* @param money
* @return
*/
public static String getMoney(String money) {
Pattern pattern = Pattern.compile("(\\d+\\.\\d+)");
Matcher m = pattern.matcher(money);
if (m.find()) {
money = m.group(1) == null ? "" : m.group(1);
} else {
pattern = Pattern.compile("(\\d+)");
m = pattern.matcher(money);
if (m.find()) {
money = m.group(1) == null ? "" : m.group(1);
} else {
money = "";
}
}
return money;
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/233071.html
標籤:其他
上一篇:發現一款線性恒流芯片
下一篇:施耐德開放自動化平臺初體驗(1)
