Разработка аудио для Android (2): запись аудио (формат WAV и MP3)

1. Разработка аудио для Android (1): основы работы со звуком
2. Разработка аудио для Android (2): запись аудио (форматы WAV и MP3)
3. Разработка аудио для Android (3): использование ExoPlayer для воспроизведения аудио
4. Разработка аудио для Android (4) : Режим воспроизведения аудио
5. Разработка аудио для Android (5): управление индукцией (экран выключен/яркий экран)

Прикрепленный исходный код GitHub: MultimediaExplore


Сначала взгляните на диаграмму эффектов аудиозаписи и воспроизведения:

 CSDN не поддерживает локальную загрузку видео, поэтому я сначала загрузил скриншот:

Вышеуказанная запись : длительное нажатие для записи, поддержка анимации звуковой волны, пролистывание вправо для удаления и т. д. Поддержка записи аудио в форматах pcm, wav, mp3.

Воспроизведение осуществляется следующим образом : щелкните значок динамика слева, чтобы начать воспроизведение только что записанного локального аудиофайла [также поддерживается воспроизведение аудио в режиме онлайн], поддерживает ход воспроизведения и поддерживает переключение режимов воспроизведения (трубка/динамик/наушники) и т. д.

1. Разрешение на аудиозапись:

Независимо от того, прежде чем разрабатывать какие-либо функции, вы должны сначала добавить и подать заявку на соответствующие разрешения, чтобы последующая работа могла проходить нормально. Разрешения, необходимые для аудиозаписи, следующие, и эти конфиденциальные разрешения должны динамически применяться в коде и могут быть записаны в обычном режиме только после согласия:

    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Во-вторых, конфигурация файла записи:

Из базовой концепции звука, упомянутой в первом разделе, мы можем знать, что перед записью звука необходимо выполнить соответствующую настройку записи, которая напрямую определяет качество звука, размер файла, формат звука и т. д. записываемого файла.

    /**
     * 录音音频的相关配置
     */
    private void initConfig() {
        recordConfig = new RecordConfig();
        //采样位宽
        recordConfig.setEncodingConfig(AudioFormat.ENCODING_PCM_16BIT);
        //录音格式
        recordConfig.setFormat(RecordConfig.RecordFormat.MP3);
        // recordConfig.setFormat(RecordConfig.RecordFormat.WAV);
        //采样频率
        recordConfig.setSampleRate(16000);
        String recordDir = String.format(Locale.getDefault(), "%s/Record/zhongyao/",
                Environment.getExternalStorageDirectory().getAbsolutePath());
        //存储目录
        recordConfig.setRecordDir(recordDir);
        AudioRecordManager.getInstance().setCurrentConfig(recordConfig);
    }

3. Аудиозапись:

Класс аудиозаписи в основном имеет два класса инкапсуляции: AudioRecorder и AudioRecordManager.

AudioRecorder: в основном для записи используйте AudioRecord системы . И объединить записанные аудиофайлы, перекодировать и т. д., чтобы сгенерировать нужные нам аудиофайлы. Этот файл является глобальным синглтоном, что гарантирует наличие только одного экземпляра класса аудиозаписи.

AudioRecordManager: Инкапсуляция и управление AudioRecorder, а также взаимодействие с внешним миром выполняются через этот класс, включая различные вызовы управления жизненным циклом для записи. Сокращено прямое взаимодействие между внешним миром и AudioRecorder, улучшено управление классом записи, который также является глобальным одноэлементным классом.

1. Инициализируйте объект записи:

Здесь объекты bufferSizeInBytes [размер буфера в байтах] и audioRecord генерируются в основном на основе предыдущей конфигурации записи .

    /**
     * 创建默认的录音对象
     */
    public void prepareRecord() {
        // 获得缓冲区字节大小
        if (bufferSizeInBytes == 0) {
            bufferSizeInBytes = AudioRecord.getMinBufferSize(currentConfig.getSampleRate(),
                    currentConfig.getChannelConfig(), currentConfig.getEncodingConfig());
        }
        if (audioRecord == null) {
            audioRecord = new AudioRecord(AUDIO_INPUT, currentConfig.getSampleRate(),
                    currentConfig.getChannelConfig(), currentConfig.getEncodingConfig(), bufferSizeInBytes);
        }

        audioRecordStatus = AudioRecordStatus.AUDIO_RECORD_PREPARE;
    }

2. Запись аудиофайлов wav:

Аудиофайл wav без потерь, поэтому качество звука будет близко к оригиналу, но именно потому, что он без потерь, поэтому аудиофайл wav почти несжатый, и, условно говоря, он будет относительно большим.

Чтобы записать аудио в формате wav, вы должны сначала записать и использовать для получения файлов PCM, затем объединить файлы PCM и, наконец, преобразовать их в аудиофайлы wav.

(1) Начать запись файлов PCM:

    private void startPcmRecorder() {
        audioRecordStatus = AudioRecordStatus.AUDIO_RECORD_START;
        notifyState();
        Logger.d(TAG, "开始录制 Pcm");
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(tmpFile);
            audioRecord.startRecording();
            byte[] byteBuffer = new byte[bufferSizeInBytes];

            while (audioRecordStatus == AudioRecordStatus.AUDIO_RECORD_START) {
                int end = audioRecord.read(byteBuffer, 0, byteBuffer.length);
                notifyData(byteBuffer);
                fos.write(byteBuffer, 0, end);
                fos.flush();
            }
            audioRecord.stop();
            files.add(tmpFile);
            if (audioRecordStatus == AudioRecordStatus.AUDIO_RECORD_STOP) {
                makeFile();
            } else {
                Logger.d(TAG, "取消录制...");
            }
        } catch (Exception e) {
            Logger.e(e, TAG, e.getMessage());
            notifyError("录音失败");
        } finally {
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (audioRecordStatus != AudioRecordStatus.AUDIO_RECORD_PAUSE) {
            audioRecordStatus = AudioRecordStatus.AUDIO_RECORD_IDLE;
            notifyState();
            Logger.d(TAG, "录音结束");
        }
    }

(2) Объедините несколько сгенерированных файлов PCM:

    /**
     * 合并pcm文件
     */
    private void mergePcmFile() {
        boolean mergeSuccess = mergePcmFiles(resultFile, files);
        if (!mergeSuccess) {
            notifyError("合并失败");
        }
    }

 (3) Преобразуйте объединенный файл PCM в файл WAV:

    /**
     * 添加Wav头文件
     */
    private void makeWav() {
        if (!FileUtil.isFile(resultFile) || resultFile.length() == 0) {
            return;
        }
        byte[] header = WavUtils.generateWavFileHeader((int) resultFile.length(), currentConfig.getSampleRate(), currentConfig.getChannelCount(), currentConfig.getEncoding());
        WavUtils.writeHeader(resultFile, header);
    }

3. Запись аудиофайлов MP3

По сравнению с аудиофайлами WAV, аудиофайлы MP3 более распространены и используются больше в коммерческих целях, потому что аудио MP3 сжато, а размер файла составляет всего одну двенадцатую от WAV, но лучшего качества звука почти нет.Большая разница. Когда нет высоких требований к качеству звука, например, при записи файлов, формат MP3 является отличным выбором.

(1) Начать запись аудиобуфера:

Вот, чтобы открыть поток Mp3EncodeThread, массив байтов byteBuffer, созданный при записи, непрерывно кодируется и декодируется для создания файлов MP3.

    private void startMp3Recorder() {
        audioRecordStatus = AudioRecordStatus.AUDIO_RECORD_START;
        notifyState();

        try {
            audioRecord.startRecording();
            short[] byteBuffer = new short[bufferSizeInBytes];

            while (audioRecordStatus == AudioRecordStatus.AUDIO_RECORD_START) {
                int end = audioRecord.read(byteBuffer, 0, byteBuffer.length);
                if (mp3EncodeThread != null) {
                    mp3EncodeThread.addChangeBuffer(new Mp3EncodeThread.ChangeBuffer(byteBuffer, end));
                }
                notifyData(ByteUtils.toBytes(byteBuffer));
            }
            audioRecord.stop();
        } catch (Exception e) {
            Logger.e(e, TAG, e.getMessage());
            notifyError("录音失败");
        }
        if (audioRecordStatus != AudioRecordStatus.AUDIO_RECORD_PAUSE) {
            if (audioRecordStatus == AudioRecordStatus.AUDIO_RECORD_CANCEL) {
                deleteMp3Encoded();
            } else {
                stopMp3Encoded();
            }
        } else {
            Logger.d(TAG, "暂停");
        }
    }

(2) Аудиокодек MP3:

Собственная аудиозапись Android поддерживает прямое создание файлов WAV, но на самом деле не поддерживает прямое создание файлов MP3. Это соответствует кодеку MP3, который в основном использует библиотеку аудиокодеков с открытым исходным кодом libmp3lame.so  . Ниже приведены хромающие методы кодирования и декодирования, а также класс Mp3Encoder:

Метод кодека MP3:

    private void lameData(ChangeBuffer changeBuffer) {
        if (changeBuffer == null) {
            return;
        }
        short[] buffer = changeBuffer.getData();
        int readSize = changeBuffer.getReadSize();
        if (readSize > 0) {
            int encodedSize = Mp3Encoder.encode(buffer, buffer, readSize, mp3Buffer);
            if (encodedSize < 0) {
                Logger.e(TAG, "Lame encoded size: " + encodedSize);
            }
            try {
                os.write(mp3Buffer, 0, encodedSize);
            } catch (IOException e) {
                Logger.e(e, TAG, "Unable to write to file");
            }
        }
    }

Класс MP3Encoder:


public class Mp3Encoder {

    static {
        System.loadLibrary("mp3lame");
    }

    public native static void close();

    public native static int encode(short[] buffer_l, short[] buffer_r, int samples, byte[] mp3buf);

    public native static int flush(byte[] mp3buf);

    public native static void init(int inSampleRate, int outChannel, int outSampleRate, int outBitrate, int quality);

    public static void init(int inSampleRate, int outChannel, int outSampleRate, int outBitrate) {
        init(inSampleRate, outChannel, outSampleRate, outBitrate, 7);
    }
}

(3) Создайте libmp3lame.so :

Исходный код, предоставленный этим проектом, включает исходный код jni lame.Если вы хотите создать файл libmp3lame.so для своего собственного проекта, вам необходимо изменить файлы Mp3Encoder.c и Mp3Encoder.h, а затем создать файл so через сборку ndk.

Измененный контент в основном предназначен для изменения пути Mp3Encoder в файле на путь Mp3Encoder в вашем собственном проекте , иначе соответствующий собственный метод не будет найден.

 Кроме того, в целом тип процессора поддерживает armeabi-v7a и arm64-v8a.Если вы хотите поддерживать другие, вы можете добавить их в Application.mk.

 После изменения вышеуказанного файла соответствующий  файл libmp3lame.so можно скомпилировать и сгенерировать с помощью команды ndk-build .

Поместите файлы столь разных типов ЦП в jniLibs или по пути, настроенному с помощью исходных наборов, например:

    sourceSets.main {
        jni.srcDirs = []//disable automatic ndk-build call
        jniLibs.srcDirs = ['libs']
    }

Вы можете записывать аудио в формате MP3.

4. Управление аудиозаписями [AudioRecordManager]:

AudioRecorderManager в глобальном одноэлементном режиме взаимодействует с бизнесом, что не только обеспечивает единичность экземпляра аудиозаписи, но и лучше управляет жизненным циклом аудио.

/**
 * Create by zhongyao on 2021/8/18
 * Description:音频录制管理类
 */
public class AudioRecordManager {

    private static final String TAG = "AudioRecordManager";

    private AudioRecordManager() {
    }

    public static AudioRecordManager getInstance() {
        return AudioRecordManagerHolder.instance;
    }

    public static class AudioRecordManagerHolder {
        public static AudioRecordManager instance = new AudioRecordManager();
    }

    public void setCurrentConfig(RecordConfig recordConfig) {
        AudioRecorder.getInstance().setCurrentConfig(recordConfig);
    }

    public RecordConfig getCurrentConfig() {
        return AudioRecorder.getInstance().getCurrentConfig();
    }

    /**
     * 录音状态监听回调
     */
    public void setRecordStateListener(RecordStateListener listener) {
        AudioRecorder.getInstance().setRecordStateListener(listener);
    }

    /**
     * 录音数据监听回调
     */
    public void setRecordDataListener(RecordDataListener listener) {
        AudioRecorder.getInstance().setRecordDataListener(listener);
    }

    /**
     * 录音可视化数据回调,傅里叶转换后的频域数据
     */
    public void setRecordFftDataListener(RecordFftDataListener recordFftDataListener) {
        AudioRecorder.getInstance().setRecordFftDataListener(recordFftDataListener);
    }

    /**
     * 录音文件转换结束回调
     */
    public void setRecordResultListener(RecordResultListener listener) {
        AudioRecorder.getInstance().setRecordResultListener(listener);
    }

    /**
     * 录音音量监听回调
     */
    public void setRecordSoundSizeListener(RecordSoundSizeListener listener) {
        AudioRecorder.getInstance().setRecordSoundSizeListener(listener);
    }

    public void setStatus(AudioRecordStatus curAudioRecordStatus) {
        switch (curAudioRecordStatus) {
            case AUDIO_RECORD_IDLE:

                break;

            case AUDIO_RECORD_PREPARE:
                AudioRecorder.getInstance().prepareRecord();
                break;

            case AUDIO_RECORD_START:
                AudioRecorder.getInstance().startRecord();
                break;

            case AUDIO_RECORD_PAUSE:
                AudioRecorder.getInstance().pauseRecord();
                break;

            case AUDIO_RECORD_STOP:
                AudioRecorder.getInstance().stopRecord();
                break;

            case AUDIO_RECORD_CANCEL:
                AudioRecorder.getInstance().cancelRecord();
                break;

            case AUDIO_RECORD_RELEASE:
                AudioRecorder.getInstance().releaseRecord();
                break;

            default:
                break;
        }
    }

    public AudioRecordStatus getStatus() {
        return AudioRecorder.getInstance().getAudioRecordStatus();
    }

    public String getAudioPath() {
        return AudioRecorder.getInstance().getAudioPath();
    }
}

Je suppose que tu aimes

Origine blog.csdn.net/u012440207/article/details/121719075
conseillé
Classement