android pcm播放器:有进度条同步、快进、快退、倍速功能

2023-09-18 17:52:20

PCM(脉冲编码调制)是一种常见的数字音频编码格式,它代表原始音频数据的一种无损编码方式。以下是关于PCM格式的一些重要信息:

定义:PCM 是一种脉冲编码调制技术,它将模拟音频信号转换为数字形式,通过对模拟信号进行采样和量化,然后将样本表示为二进制编码来实现。PCM 不压缩音频数据,因此每个音频样本都以其原始值表示。

采样率:PCM 文件中的音频数据以一定的采样率(Samples Per Second,通常以Hz表示)进行采样。常见的采样率包括 44100 Hz(CD音质)、48000 Hz(DVD音质)和16000 Hz(电话音质)等。更高的采样率通常意味着更高的音频质量,但也会占用更多的存储空间。

位深度:PCM 数据以一定的位深度来表示每个样本的值,通常以位数表示。常见的位深度包括16位和24位。较高的位深度可以提供更好的音频质量,因为它可以更准确地表示音频振幅,但也会占用更多的存储空间。

声道数:PCM 可以是单声道(单声道)或立体声(双声道)等多声道格式。单声道表示音频仅具有一个声道,而立体声表示音频具有左右两个声道,允许立体声效果。

文件格式:PCM 数据通常存储在不同的文件格式中,如WAV(Waveform Audio File Format)或AIFF(Audio Interchange File Format)等。这些文件格式包含了PCM音频数据以及元数据,以描述音频的参数和格式。

总之,PCM 是一种直观的音频编码格式,它以原始的数字形式表示音频数据,没有压缩,因此在需要高音质的应用中很常见,如音乐制作和专业音频处理。但是,由于它不进行压缩,所以文件大小通常较大。

播放器工具类:

package com.realtop.translatemodule.utils;

import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import androidx.lifecycle.MutableLiveData;

import java.io.File;
import java.io.FileInputStream;

public class AudioTrackUtils {
    private static final String TAG = "audio_track_utils";
    private AudioTrack audioTrack;
    private int sampleRate = 16000;
    private int channelConfig = AudioFormat.CHANNEL_OUT_MONO;
    private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;

    private boolean isPlaying;
    private Thread mThread;
    private FileInputStream mFileInput;

    private boolean isUseSpeed;

    // 开启两倍速度
    public void setUseSpeed(boolean useSpeed) {
        isUseSpeed = useSpeed;
    }

    public final MutableLiveData<Float> PROGRESS_OBSERVER = new MutableLiveData<>();

    public final MutableLiveData<Integer> PLAY_START_END = new MutableLiveData<>();

    private final Handler mHandler = new Handler(Looper.getMainLooper());

    public void playAudio(String filePath, int ratio) {
        if (audioTrack != null) {
            Log.i(TAG, "playAudio: is init");
            return;
        }
        int bufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat);
        Log.i(TAG, "playAudio: file path:" + filePath + "; buffer size:" + bufferSize);
        audioTrack = new AudioTrack(
                AudioManager.STREAM_MUSIC,
                sampleRate,
                channelConfig,
                audioFormat,
                bufferSize,
                AudioTrack.MODE_STREAM
        );
        audioTrack.play();
        isPlaying = true;

        File sourceFile = new File(filePath);

        try {
            mFileInput = new FileInputStream(sourceFile);
            long currentPos = (long) (ratio * 1.0f / 100 * sourceFile.length());
            long skip = mFileInput.skip(currentPos);
            Log.i(TAG, "playAudio: skip:" + skip);
        } catch (Exception e) {
            Log.i(TAG, "playAudio: error:" + e.getMessage());
        }

        mThread = new Thread(() -> {
            byte[] bytes = new byte[bufferSize * 2];
            byte[] real = new byte[bufferSize];// 倍速使用
            int len = -1;
            while (isPlaying) {
                try {
                    len = mFileInput.read(bytes);
                } catch (Exception e) {
                    Log.i(TAG, "playAudio: read file end:" + e.getMessage());
                    len = -1;// 默认异常退出
                }
                if (len == -1)
                    break;


                if (isUseSpeed) {
                    for (int i = 0, j = 0; i < len * 0.25f; i += 4, j += 2) {
                        // damage
                        try {
                            real[j] = bytes[i];
                            real[j + 1] = bytes[i + 1];
                        } catch (Exception e) {
                            Log.i(TAG, "playAudio: speed change error:" + e.getMessage());
                        }
                    }
                    audioTrack.write(real, 0, (int) (len * 0.5f));
                } else {
                    audioTrack.write(bytes, 0, len);
                }

                // 处理进度
                handleProgress(sourceFile);
            }
            // 自动停止了
            mHandler.post(this::stopAndRelease);
            Log.i(TAG, "playAudio: looper end");
        });
        mThread.start();

        PLAY_START_END.postValue(1);
        Log.i(TAG, "playAudio: begin record");
    }

    private void handleProgress(File sourceFile) {
        try {
            long position = mFileInput.getChannel().position();
            long length = sourceFile.length();
            float progress = 1.0f * position / length;
            if (progress < 0 || progress > 1) {
                progress = 0;
            }
            PROGRESS_OBSERVER.postValue(progress * 100);
            Log.i(TAG, "playAudio: pos:" + position + "; " + length);
        } catch (Exception e) {
            Log.i(TAG, "playAudio: error get pos:" + e.getMessage());
        }
    }

    public void stopAndRelease() {
        if (audioTrack == null) {
            Log.i(TAG, "release: is ended");
            return;
        }
        isPlaying = false;
        try {
            mThread.join();
            mFileInput.close();
            audioTrack.flush();
            audioTrack.stop();
            audioTrack.release();
            Log.i(TAG, "release: end");
        } catch (Exception e) {
            Log.i(TAG, "release: error:" + e.getMessage());
        }
        audioTrack = null;
        PLAY_START_END.postValue(2);
    }
}

更多推荐

竞赛选题 基于深度学习的动物识别 - 卷积神经网络 机器视觉 图像识别

文章目录0前言1背景2算法原理2.1动物识别方法概况2.2常用的网络模型2.2.1B-CNN2.2.2SSD3SSD动物目标检测流程4实现效果5部分相关代码5.1数据预处理5.2构建卷积神经网络5.3tensorflow计算图可视化5.4网络模型训练5.5对猫狗图像进行2分类6最后0前言🔥优质竞赛项目系列,今天要分享

close和fclose

在Linux系统中,close函数并不会主动调用fsync接口。close函数只是关闭了文件描述符,而不保证数据被写入到磁盘。如果你想确保数据被写入到磁盘,你需要在close函数之前调用fsync函数。这是因为Linux使用了缓存机制来提高磁盘的读写性能,当你写入数据时,数据首先被写入到缓存中,然后在适当的时候(例如缓

CCG超级标记

1.定义组合范畴语法(CombinatoryCategorialGrammar,CCG)是一种用于自然语言语法分析的语言学理论和计算模型。它是一种形式文法,旨在描述句子的结构和语法规则(通过简练的描述形式表现出句子中各成分的句法语义关系)。CCG的关键思想是使用组合范畴(category)来表示词汇和短语的语法信息,然

【强化学习】01—— 强化学习简介

文章目录两种机器学习类型强化学习定义强化学习交互过程强化学习系统要素历史(History)状态(State)策略(Policy)奖励(Reward)价值函数(ValueFunction)模型(Model)迷宫例子强化学习智能体分类参考两种机器学习类型监督学习/无监督学习/强化学习/机器学习之间的关系预测根据数据预测所需

Rocketmq--消息发送和接收演示

使用Java代码来演示消息的发送和接收<dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId><version>2.0.2</version></dependency>1

服务器搭建(TCP套接字)-基础版(客户端)

一、socket1.1、vimman查看socket:!mansocket1.2、依赖的头文件#include<sys/types.h>#include<sys/socket.h>1.3、原型intsocket(intdomain,inttype,intprotocol);domain说明AF_INETIPV4协议AF

c语言练习题55:IP 地址⽆效化

IP地址⽆效化题⽬描述:给你⼀个有效的IPv4地址address,返回这个IP地址的⽆效化版本。所谓⽆效化IP地址,其实就是⽤"[.]"代替了每个"."。•⽰例1:输⼊:address="1.1.1.1"输出:"1[.]1[.]1[.]1"•⽰例2:输⼊:address="255.100.50.0"输出:"255[.]

Base64、AES、MD5的区别与应用

最近有个加密的需求,想起以前做过的验签、加密等内容,就一起总结一下吧,具体的技术细节就不展开了,我们只讨论核心以及如何应用Base64一句话解释就是用来将二进制数据进行文本化显示的编码方式想了解base64就一定要先了解什么是字符集字符集是由a-z,A-Z,0-9以及+/一共64个常见字符组成的,2*6=64(其实还有

SpringBoot运行原理

目录@SpringBootApplication@ComponentScan@SpringBootConfiguration@EnableAutoConfiguration结论@SpringbootApplication(主入口)@SpringBootApplicationpublicclassSpringbootCo

电力安全智慧云平台:引领更安全的用电新时

电力能源是人类社会不可或缺的重要资源,其安全稳定供应关系到各行各业的正常运转和千家万户的生活质量。然而,随着电力使用的普及,电力安全问题也日益凸显,一旦发生电力事故,不仅会造成巨大的经济损失,还会对人们的生命安全构成严重威胁。因此,如何保障电力安全已成为全社会共同关注的焦点。近年来,随着云计算、大数据、人工智能等新一代

【云计算】虚拟私有云 VPC

虚拟私有云VPC1.前言1.1基本介绍1.2VPC的作用1.3VPC的适用人群2.VPC基本概念2.1VPC相关基本概念2.2其他相关基本概念3.VPC通信场景3.1VPC内部互通3.2VPC间互通3.2.1对等连接3.2.2TransitGateway或者云联网3.3访问Internet3.3.1Internet网关

热文推荐