zoukankan      html  css  js  c++  java
  • 音频 PCM 数据的采集和播放

    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<Void, Void, Void>() {
                @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://i.cnblogs.com/Files.aspx

  • 相关阅读:
    Java实现 LeetCode 833 字符串中的查找与替换(暴力模拟)
    Java实现 LeetCode 833 字符串中的查找与替换(暴力模拟)
    Java实现 LeetCode 833 字符串中的查找与替换(暴力模拟)
    Java实现 LeetCode 832 翻转图像(位运算)
    Java实现 LeetCode 832 翻转图像(位运算)
    Java实现 LeetCode 832 翻转图像(位运算)
    Java实现 LeetCode 831 隐藏个人信息(暴力)
    Java实现 LeetCode 831 隐藏个人信息(暴力)
    Java实现 LeetCode 831 隐藏个人信息(暴力)
    how to use automapper in c#, from cf~
  • 原文地址:https://www.cnblogs.com/Jackie-zhang/p/9700143.html
Copyright © 2011-2022 走看看