1

我正在尝试从原始 pcm 数据生成具有 ulaw 编码的 wav 文件。我已经通过在谷歌和这里找到了一些解决方案,但我似乎无法让音频很好地播放。它的嘈杂和筛选的声音。可能是音频播放太快,不确定。

所以,这是我迄今为止尝试过的代码。

我有我的 AudioRecorder.java,它记录了数据并将其保存为原始 pcm 文件,将原始 pcm 数据转换为 ulaw 编码数据,创建 wav 标头,然后将数据流合并并保存为一个 .wav 文件

public class AudioRecorder {
private static final String TAG = "AudioRecorder";
private int audioInput = MediaRecorder.AudioSource.MIC;
private int audioSampleRate = 8000; //frequency which ranges from 8K Hz to 44.1K Hz
private int audioChannel = AudioFormat.CHANNEL_IN_MONO;
private int audioEncode = AudioFormat.ENCODING_PCM_16BIT;

private int bufferSizeInBytes = 0;
private AudioRecord audioRecord;
private Status status = Status.STATUS_NO_READY;
protected String pcmFileName;

private int currentPosition = 0;
private int lastVolumn = 0;
private FileOutputStream fosPcm = null;

public AudioRecorder() {
    pcmFileName = AudioFileUtils.getPcmFileAbsolutePath(RECORDED_FILE_NAME);
    status = Status.STATUS_READY;
}

public void setAudioInput(int audioInput) {
    this.audioInput = audioInput;
}

public void setAudioSampleRate(int audioSampleRate) {
    this.audioSampleRate = audioSampleRate;
}

public void setAudioChannel(int audioChannel) {
    this.audioChannel = audioChannel;
}

/**
 * This method is to start recording using AudioRecord, also has NoiseSuppressor and AutomaticGainControl enabled
 */
public void startRecord() {

    bufferSizeInBytes = AudioRecord.getMinBufferSize(audioSampleRate,
            audioChannel, audioEncode);
    audioRecord = new AudioRecord(audioInput, audioSampleRate, audioChannel, audioEncode, bufferSizeInBytes);
    if (status == Status.STATUS_NO_READY) {
        throw new IllegalStateException("not init");
    }
    if (status == Status.STATUS_START) {
        throw new IllegalStateException("is recording ");
    }
    Log.d("AudioRecorder", "===startRecord===" + audioRecord.getState());
    audioRecord.startRecording();

    new Thread(new Runnable() {
        @Override
        public void run() {
            NoiseSuppressor noiseSuppressor = NoiseSuppressor.create(audioRecord.getAudioSessionId());
            if (noiseSuppressor != null) {
                noiseSuppressor.setEnabled(true);
            }

            AutomaticGainControl automaticGainControl = AutomaticGainControl.create(audioRecord.getAudioSessionId());
            if (automaticGainControl != null) {
                automaticGainControl.setEnabled(true);
            }

            recordToFile();
        }
    }).start();
}

public void stop() {
    if (status != Status.STATUS_START) {
        throw new IllegalStateException("not recording");
    } else {
        stopRecorder();
//            convertPCmFile();
            makeDestFile();
            status = Status.STATUS_READY;
    }
}

private void convertPCmFile() {
    File file = new File(AudioFileUtils.getPcmFileAbsolutePath(RECORDED_GREETING_FILE_NAME)); // for ex. path= "/sdcard/samplesound.pcm" or "/sdcard/samplesound.wav"
    byte[] byteData = new byte[(int) file.length()];
    try {
        FileInputStream in = new FileInputStream(file);
        in.read(byteData);
        in.close();

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
// Set and push to audio track..
        int intSize = android.media.AudioTrack.getMinBufferSize(audioSampleRate, AudioFormat.CHANNEL_CONFIGURATION_MONO,
                AudioFormat.ENCODING_PCM_16BIT);
        AudioTrack at = new AudioTrack(AudioManager.STREAM_MUSIC, audioSampleRate, AudioFormat.CHANNEL_CONFIGURATION_MONO,
                AudioFormat.ENCODING_PCM_16BIT, intSize, AudioTrack.MODE_STREAM);
        if (at != null) {
            at.play();
// Write the byte array to the track
            at.write(byteData, 0, byteData.length);
            at.stop();
            at.release();
        } else
            Log.d("TCAudio", "audio track is not initialised ");
    }

private void makeDestFile() {
    new Thread() {
        @Override
        public void run() {
            File file = new File(pcmFileName);
            try {

                //Step 1: create input stream from generated pcm audio data
                byte[] pcmData = new byte[(int) file.length()];
                FileInputStream inputStream1 = new FileInputStream(file);
                int readBytes = inputStream1.read(pcmData);
                inputStream1.close();

                //Step 2: calculate the size that has to be sent to constructor which is half the size of actual pcm audio data
                int size = UlawEncoderInputStream.maxAbsPcm(pcmData, 0, pcmData.length / 2);

                //Step 3: send the input stream as well as the size to the constructor
                FileInputStream inputStream2 = new FileInputStream(file);
                UlawEncoderInputStream ulawEncoderInputStream = new UlawEncoderInputStream(inputStream2, size);

                //Step 4: create byte[] with size of half of bytes of pcm audio data
                byte[] ulawData = new byte[pcmData.length / 2];

                //Step 5: call read from UlawEncoderInputStream with above pcmData which is newly created
                int nRead;
                nRead = ulawEncoderInputStream.read(ulawData);

                //Step 6: create wav header
                byte[] wavHeader = wavFileHeader(ulawData.length, ulawData.length + 36, audioSampleRate, audioChannel, audioSampleRate);

                //Step 7: combine wav header and encodedUlawBuffer in one byte[]
                byte[] allByteArray = new byte[wavHeader.length + ulawData.length];

                ByteBuffer buff = ByteBuffer.wrap(allByteArray);
                buff.put(wavHeader);
                buff.put(ulawData);

                //Step 8 : writing the combined data into a new file
                OutputStream outputStream = new FileOutputStream(new File(AudioFileUtils.getWavFileAbsolutePath(RECORDED_FILE_NAME)));
                outputStream.write(allByteArray);
                outputStream.close();

            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            releaseRecorder();
        }
    }.run();
}

private byte[] wavFileHeader(long totalAudioLen, long totalDataLen, long longSampleRate,
                             int channels, long byteRate) {
    byte[] header = new byte[44];
    header[0] = 'R'; // RIFF/WAVE header
    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);
    header[8] = 'W';
    header[9] = 'A';
    header[10] = 'V';
    header[11] = 'E';
    header[12] = 'f'; // 'fmt ' chunk
    header[13] = 'm';
    header[14] = 't';
    header[15] = ' ';
    header[16] = 16; // 4 bytes: size of 'fmt ' chunk
    header[17] = 0;
    header[18] = 0;
    header[19] = 0;
    header[20] = 7; // format = 7, for ulaw
    header[21] = 0;
    header[22] = (byte) (channels & 0xff);
    header[23] = (byte) ((channels >> 8) & 0xFF);
    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);
    header[32] = (byte) ((channels * 8) / 8);//
    // block align
    header[33] = 0;
    header[34] = 8; // bits per sample
    header[35] = 0;
    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);
    return header;
}

public void release() {
    stopRecorder();
    releaseRecorder();
    status = Status.STATUS_READY;
}

private void releaseRecorder() {
    if (audioRecord != null) {
        audioRecord.release();
        audioRecord = null;
    }
}

private void stopRecorder() {
    if (audioRecord != null) {
        try {
            audioRecord.stop();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

/**
 * linear PCM data recorded to file
 */
private void recordToFile() {
    byte[] audiodata = new byte[bufferSizeInBytes];

    int readsize = 0;
    try {
        fosPcm = new FileOutputStream(pcmFileName, true);
    } catch (FileNotFoundException e) {
        Log.e("AudioRecorder", e.getMessage());
    }
    status = Status.STATUS_START;
    while (status == Status.STATUS_START && audioRecord != null) {
        readsize = audioRecord.read(audiodata, 0, bufferSizeInBytes);
        if (AudioRecord.ERROR_INVALID_OPERATION != readsize && fosPcm != null) {
            try {

                //get the volumn  1--10
                int sum = 0;
                for (int i = 0; i < readsize; i++) {
                    sum += Math.abs(audiodata[i]);
                }

                if (readsize > 0) {
                    int raw = sum / readsize;
                    lastVolumn = raw > 32 ? raw - 32 : 0;
                    Log.i(TAG, "writeDataTOFile: volumn -- " + raw + " / lastvolumn -- " + lastVolumn);
                }


                if (readsize > 0 && readsize <= audiodata.length)
                    fosPcm.write(audiodata, 0, readsize);
            } catch (IOException e) {
                Log.e("AudioRecorder", e.getMessage());
            }
        }
    }
    try {
        if (fosPcm != null) {
            fosPcm.close();
        }
    } catch (IOException e) {
        Log.e("AudioRecorder", e.getMessage());
    }
}

public Status getStatus() {
    return status;
}

public enum Status {
    STATUS_NO_READY,
    STATUS_READY,
    STATUS_START,
    STATUS_PAUSE,
    STATUS_STOP
}

}

我有 AudioFileUtils.java,它只提供要保存的扩展名的路径和附加

public class AudioFileUtils {
private static String rootPath = "audiorecord";
private final static String AUDIO_PCM_BASEPATH = "/" + rootPath + "/pcm/";
private final static String AUDIO_WAV_BASEPATH = "/" + rootPath + "/wav/";

private static void setRootPath(String rootPath) {
    AudioFileUtils.rootPath = rootPath;
}

public static String getPcmFileAbsolutePath(String fileName) {
    if (TextUtils.isEmpty(fileName)) {
        throw new NullPointerException("fileName isEmpty");
    }
    String mAudioRawPath = "";
    if (!fileName.endsWith(".pcm")) {
        fileName = fileName + ".pcm";
    }
    String fileBasePath = SampleApp.getInstance().getApplicationContext().getExternalFilesDir(null) + AUDIO_PCM_BASEPATH;
    File file = new File(fileBasePath);
    if (!file.exists()) {
        file.mkdirs();
    }
    mAudioRawPath = fileBasePath + fileName;

    return mAudioRawPath;
}


public static String getWavFileAbsolutePath(String fileName) {
    if (fileName == null) {
        throw new NullPointerException("fileName can't be null");
    }

    String mAudioWavPath = "";
    if (!fileName.endsWith(".wav")) {
        fileName = fileName + ".wav";
    }
    String fileBasePath = SampleApp.getInstance().getApplicationContext().getExternalFilesDir(null) + AUDIO_WAV_BASEPATH;
    CoxApplication.getInstance().getAccountManager().setMessageFilePath(fileBasePath);
    File file = new File(fileBasePath);
    if (!file.exists()) {
        file.mkdirs();
    }
    mAudioWavPath = fileBasePath + fileName;
    return mAudioWavPath;
}

}

我正在使用 UlawEncoderInputStream.java,我在这里找到了它并对其进行了一些更改,可以在下面找到

public class UlawEncoderInputStream extends InputStream {
private final static String TAG = "UlawEncoderInputStream";

private final static int MAX_ULAW = 8192;
private final static int SCALE_BITS = 16;

private InputStream mIn;

private int mMax = 0;

// this buffer needs to be LARGER than the largest possible file size for
// a 30 second PCM file recorded at either 8000 Hz or 44.1 KHz.
// If it is smaller, the file will be cut off.
private final byte[] mBuf = new byte[1048576];
private int mBufCount = 0; // should be 0 or 1

private final byte[] mOneByte = new byte[1];


public static void encode(byte[] pcmBuf, int pcmOffset,
                          byte[] ulawBuf, int ulawOffset, int length, int max) {

    // from  'ulaw' in wikipedia
    // +8191 to +8159                          0x80
    // +8158 to +4063 in 16 intervals of 256   0x80 + interval number
    // +4062 to +2015 in 16 intervals of 128   0x90 + interval number
    // +2014 to  +991 in 16 intervals of  64   0xA0 + interval number
    //  +990 to  +479 in 16 intervals of  32   0xB0 + interval number
    //  +478 to  +223 in 16 intervals of  16   0xC0 + interval number
    //  +222 to   +95 in 16 intervals of   8   0xD0 + interval number
    //   +94 to   +31 in 16 intervals of   4   0xE0 + interval number
    //   +30 to    +1 in 15 intervals of   2   0xF0 + interval number
    //     0                                   0xFF

    //    -1                                   0x7F
    //   -31 to    -2 in 15 intervals of   2   0x70 + interval number
    //   -95 to   -32 in 16 intervals of   4   0x60 + interval number
    //  -223 to   -96 in 16 intervals of   8   0x50 + interval number
    //  -479 to  -224 in 16 intervals of  16   0x40 + interval number
    //  -991 to  -480 in 16 intervals of  32   0x30 + interval number
    // -2015 to  -992 in 16 intervals of  64   0x20 + interval number
    // -4063 to -2016 in 16 intervals of 128   0x10 + interval number
    // -8159 to -4064 in 16 intervals of 256   0x00 + interval number
    // -8192 to -8160                          0x00

    // set scale factors
    if (max <= 0) max = MAX_ULAW;

    int coef = MAX_ULAW * (1 << SCALE_BITS) / max;

    for (int i = 0; i < length; i++) {
        int pcm = (0xff & pcmBuf[pcmOffset++]) + (pcmBuf[pcmOffset++] << 8);
        pcm = (pcm * coef) >> SCALE_BITS;

        int ulaw;
        if (pcm >= 0) {
            ulaw = pcm <= 0 ? 0xff :
                    pcm <=   30 ? 0xf0 + ((  30 - pcm) >> 1) :
                            pcm <=   94 ? 0xe0 + ((  94 - pcm) >> 2) :
                                    pcm <=  222 ? 0xd0 + (( 222 - pcm) >> 3) :
                                            pcm <=  478 ? 0xc0 + (( 478 - pcm) >> 4) :
                                                    pcm <=  990 ? 0xb0 + (( 990 - pcm) >> 5) :
                                                            pcm <= 2014 ? 0xa0 + ((2014 - pcm) >> 6) :
                                                                    pcm <= 4062 ? 0x90 + ((4062 - pcm) >> 7) :
                                                                            pcm <= 8158 ? 0x80 + ((8158 - pcm) >> 8) :
                                                                                    0x80;
        } else {
            ulaw = -1 <= pcm ? 0x7f :
                    -31 <= pcm ? 0x70 + ((pcm -   -31) >> 1) :
                            -95 <= pcm ? 0x60 + ((pcm -   -95) >> 2) :
                                    -223 <= pcm ? 0x50 + ((pcm -  -223) >> 3) :
                                            -479 <= pcm ? 0x40 + ((pcm -  -479) >> 4) :
                                                    -991 <= pcm ? 0x30 + ((pcm -  -991) >> 5) :
                                                            -2015 <= pcm ? 0x20 + ((pcm - -2015) >> 6) :
                                                                    -4063 <= pcm ? 0x10 + ((pcm - -4063) >> 7) :
                                                                            -8159 <= pcm ? 0x00 + ((pcm - -8159) >> 8) :
                                                                                    0x00;
        }
        ulawBuf[ulawOffset++] = (byte)ulaw;
    }
}

/**
 * Compute the maximum of the absolute value of the pcm samples.
 * The return value can be used to set ulaw encoder scaling.
 * @param pcmBuf array containing 16 bit pcm data.
 * @param offset offset of start of 16 bit pcm data.
 * @param length number of pcm samples (not number of input bytes)
 * @return maximum abs of pcm data values
 */
public static int maxAbsPcm(byte[] pcmBuf, int offset, int length) {
    int max = 0;
    for (int i = 0; i < length; i++) {
        int pcm = (0xff & pcmBuf[offset++]) + (pcmBuf[offset++] << 8);
        if (pcm < 0) pcm = -pcm;
        if (pcm > max) max = pcm;
    }
    return max;
}

/**
 * Create an InputStream which takes 16 bit pcm data and produces ulaw data.
 * @param in InputStream containing 16 bit pcm data.
 * @param max pcm value corresponding to maximum ulaw value.
 */
public UlawEncoderInputStream(InputStream in, int max) {
    mIn = in;
    mMax = max;
}

@Override
public int read(byte[] buf, int offset, int length) throws IOException {
    if (mIn == null) throw new IllegalStateException("not open");

    // return at least one byte, but try to fill 'length'
    while (mBufCount < 2) {
        int n = mIn.read(mBuf, mBufCount, Math.min(length * 2, mBuf.length - mBufCount));
        if (n == -1) return -1;
        mBufCount += n;
    }

    // compand data
    int n = Math.min(mBufCount / 2, length);
    encode(mBuf, 0, buf, offset, n, mMax);

    // move data to bottom of mBuf
    mBufCount -= n * 2;
    for (int i = 0; i < mBufCount; i++) mBuf[i] = mBuf[i + n * 2];

    return n;
}

/*public byte[] getUpdatedBuffer(){
    return mBuf;
}*/

@Override
public int read(byte[] buf) throws IOException {
    return read(buf, 0, buf.length);
}

@Override
public int read() throws IOException {
    int n = read(mOneByte, 0, 1);
    if (n == -1) return -1;
    return 0xff & (int)mOneByte[0];
}

@Override
public void close() throws IOException {
    if (mIn != null) {
        InputStream in = mIn;
        mIn = null;
        in.close();
    }
}

@Override
public int available() throws IOException {
    return (mIn.available() + mBufCount) / 2;
}
}

在做完这一切之后,正如我所说的那样,声音太刺耳和嘈杂。不知道我错在哪里。

任何帮助,不胜感激!先感谢您。

4

0 回答 0