• 音频 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

  • 相关阅读:
    关于效应量
    sc 与 net 命令
    隐函数求导
    关于向量空间的基本性质,与子空间的最最基本性质
    生成随机数(C++)
    关于 setw() 函数(C++)
    关于 加减乘除 基本运算的性质
    为什么文件无法用wps打开,甚至wps.exe本身都无法打开?
    with open()函数中,如何在文件名设置中引用变量(python)
    pygame 运行心理学问卷
  • 原文地址:https://www.cnblogs.com/Jackie-zhang/p/9700143.html
Copyright © 2020-2023  润新知