博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
音频 PCM 数据的采集和播放
阅读量:7031 次
发布时间:2019-06-28

本文共 8356 字,大约阅读时间需要 27 分钟。

PCM(Pulse Code Modulation)脉冲编码调制 —— 音频的采集与量化过程。

PCM数据是最原始的音频数据完全无损,所以PCM数据虽然音质优秀但体积庞大。

为了解决这个问题先后诞生了一系列的音频格式,这些音频格式运用不同的方法对音频数据进行压缩,其中有无损压缩(ALAC、APE、FLAC)和有损压缩(MP3、AAC、OGG、WMA)两种。

 

代码实现逻辑过

使用AudioRecord录制pcm音频 ——> PCM转WAV(只要加上wav头文件即可)——> 使用AudioTrack播放pcm音频

——> 使用 AudioTrack 播放音频

 

使用AudioRecord录制PCM音频代码:/** * 采样率,现在能够保证在所有设备上使用的采样率是44100Hz, 但是其他的采样率(22050, 16000, 11025)在一些设备上也可以使用。 */private static final int SAMPLE_RATE_INHZ = 44100;/** * 声道数。CHANNEL_IN_MONO and CHANNEL_IN_STEREO. 其中CHANNEL_IN_MONO是可以保证在所有设备能够使用的。 */private static final int CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_MONO;/** * 返回的音频数据的格式。 ENCODING_PCM_8BIT, ENCODING_PCM_16BIT, and ENCODING_PCM_FLOAT. */private static final int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;final int minBufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE_INHZ, CHANNEL_CONFIG, AUDIO_FORMAT);audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLE_RATE_INHZ,    CHANNEL_CONFIG, AUDIO_FORMAT, minBufferSize);final byte data[] = new byte[minBufferSize];final File file = new File(getExternalFilesDir(Environment.DIRECTORY_MUSIC), "test.pcm");if (!file.mkdirs()) {    Log.e(TAG, "Directory not created");}if (file.exists()) {    file.delete();}audioRecord.startRecording();isRecording = true;new Thread(new Runnable() {    @Override public void run() {        FileOutputStream os = null;        try {            os = new FileOutputStream(file);        } catch (FileNotFoundException e) {            e.printStackTrace();        }        if (null != os) {            while (isRecording) {                int read = audioRecord.read(data, 0, minBufferSize);                // 如果读取音频数据没有出现错误,就将数据写入到文件                if (AudioRecord.ERROR_INVALID_OPERATION != read) {                    try {                        os.write(data);                    } catch (IOException e) {                        e.printStackTrace();                    }                }            }            try {                Log.i(TAG, "run: close file output stream !");                os.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }}).start();

 

PCM转WAV:// 音频数据的大小long totalAudioLen = fileInputStream.getChannel().size();// wav总区块大小long totalDataLen = totalAudioLen + 36;// 声道数量int channels;// 采样率long longSampleRate;// 位元率long byteRate = 16 * longSampleRate * channels / 8;byte[] header = new byte[44];        // RIFF/WAVE header        header[0] = 'R';        header[1] = 'I';        header[2] = 'F';        header[3] = 'F';        header[4] = (byte) (totalDataLen & 0xff);        header[5] = (byte) ((totalDataLen >> 8) & 0xff);        header[6] = (byte) ((totalDataLen >> 16) & 0xff);        header[7] = (byte) ((totalDataLen >> 24) & 0xff);        //WAVE        header[8] = 'W';        header[9] = 'A';        header[10] = 'V';        header[11] = 'E';        // 'fmt ' chunk        header[12] = 'f';        header[13] = 'm';        header[14] = 't';        header[15] = ' ';        // 4 bytes: size of 'fmt ' chunk        header[16] = 16;        header[17] = 0;        header[18] = 0;        header[19] = 0;        // format = 1        header[20] = 1;        header[21] = 0;        header[22] = (byte) channels;        header[23] = 0;        header[24] = (byte) (longSampleRate & 0xff);        header[25] = (byte) ((longSampleRate >> 8) & 0xff);        header[26] = (byte) ((longSampleRate >> 16) & 0xff);        header[27] = (byte) ((longSampleRate >> 24) & 0xff);        header[28] = (byte) (byteRate & 0xff);        header[29] = (byte) ((byteRate >> 8) & 0xff);        header[30] = (byte) ((byteRate >> 16) & 0xff);        header[31] = (byte) ((byteRate >> 24) & 0xff);        // block align        header[32] = (byte) (2 * 16 / 8);        header[33] = 0;        // bits per sample        header[34] = 16;        header[35] = 0;        //data        header[36] = 'd';        header[37] = 'a';        header[38] = 't';        header[39] = 'a';        header[40] = (byte) (totalAudioLen & 0xff);        header[41] = (byte) ((totalAudioLen >> 8) & 0xff);        header[42] = (byte) ((totalAudioLen >> 16) & 0xff);        header[43] = (byte) ((totalAudioLen >> 24) & 0xff);

 

使用AudioTrack 播放音频
/**     * 播放,使用stream模式     */    private void playInModeStream() {        /*        * SAMPLE_RATE_INHZ 对应pcm音频的采样率        * channelConfig 对应pcm音频的声道        * AUDIO_FORMAT 对应pcm音频的格式        * */        int channelConfig = AudioFormat.CHANNEL_OUT_MONO;        final int minBufferSize = AudioTrack.getMinBufferSize(SAMPLE_RATE_INHZ, channelConfig, AUDIO_FORMAT);        audioTrack = new AudioTrack(            new AudioAttributes.Builder()                .setUsage(AudioAttributes.USAGE_MEDIA)                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)                .build(),            new AudioFormat.Builder().setSampleRate(SAMPLE_RATE_INHZ)                .setEncoding(AUDIO_FORMAT)                .setChannelMask(channelConfig)                .build(),            minBufferSize,            AudioTrack.MODE_STREAM,            AudioManager.AUDIO_SESSION_ID_GENERATE);        audioTrack.play();        File file = new File(getExternalFilesDir(Environment.DIRECTORY_MUSIC), "test.pcm");        try {            fileInputStream = new FileInputStream(file);            new Thread(new Runnable() {                @Override public void run() {                    try {                        byte[] tempBuffer = new byte[minBufferSize];                        while (fileInputStream.available() > 0) {                            int readCount = fileInputStream.read(tempBuffer);                            if (readCount == AudioTrack.ERROR_INVALID_OPERATION ||                                readCount == AudioTrack.ERROR_BAD_VALUE) {                                continue;                            }                            if (readCount != 0 && readCount != -1) {                                audioTrack.write(tempBuffer, 0, readCount);                            }                        }                    } catch (IOException e) {                        e.printStackTrace();                    }                }            }).start();        } catch (IOException e) {            e.printStackTrace();        }    }    /**     * 播放,使用static模式     */    private void playInModeStatic() {        // static模式,需要将音频数据一次性write到AudioTrack的内部缓冲区        new AsyncTask
() { @Override protected Void doInBackground(Void... params) { try { InputStream in = getResources().openRawResource(R.raw.ding); try { ByteArrayOutputStream out = new ByteArrayOutputStream(); for (int b; (b = in.read()) != -1; ) { out.write(b); } Log.d(TAG, "Got the data"); audioData = out.toByteArray(); } finally { in.close(); } } catch (IOException e) { Log.wtf(TAG, "Failed to read", e); } return null; } @Override protected void onPostExecute(Void v) { Log.i(TAG, "Creating track...audioData.length = " + audioData.length); // R.raw.ding铃声文件的相关属性为 22050Hz, 8-bit, Mono audioTrack = new AudioTrack( new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA) .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .build(), new AudioFormat.Builder().setSampleRate(22050) .setEncoding(AudioFormat.ENCODING_PCM_8BIT) .setChannelMask(AudioFormat.CHANNEL_OUT_MONO) .build(), audioData.length, AudioTrack.MODE_STATIC, AudioManager.AUDIO_SESSION_ID_GENERATE); Log.d(TAG, "Writing audio data..."); audioTrack.write(audioData, 0, audioData.length); Log.d(TAG, "Starting playback"); audioTrack.play(); Log.d(TAG, "Playing"); } }.execute(); }

 

demo代码: 

转载于:https://www.cnblogs.com/Jackie-zhang/p/9700143.html

你可能感兴趣的文章
iOS数据持久化存储之归档NSKeyedArchiver
查看>>
JavaScript面向对象
查看>>
Intellij修改模板代码
查看>>
2.页面布局示例笔记
查看>>
一些老游戏CPU 100%占用的解决方法
查看>>
f5基本介绍
查看>>
博前语
查看>>
Java SE核心之一:常用类,包装类,其他基本数据类型包装类。
查看>>
郑捷《机器学习算法原理与编程实践》学习笔记(第二章 中文文本分类(一))...
查看>>
python (ploit)
查看>>
Android 用achartengine 画折线图怎么显示不正确
查看>>
程序简单的测试与升级(暨实践第一次作业)
查看>>
信号处理
查看>>
【100题】第五十九题 用C++编写不能被继承的类
查看>>
轻描淡写
查看>>
mysql基本操作
查看>>
39.CSS3弹性伸缩布局【下】
查看>>
[javascript]图解+注释版 Ext.extend()
查看>>
我的前端工具集(七)div背景网格
查看>>
linux 下mongo 基础配置
查看>>