具有增益调整功能的AudioRecord无法在Samsung Device上运行

时间:2012-07-02 10:33:55

标签: android audio audio-recording android-audiorecord

我已经编写了使用AudioRecord录制音频文件的代码,而在SD卡上写文件的时候我正在制作两个版本。

版本1 录制的文件原样保存在SD卡上。

版本2 我在录制的文件上应用Gain功能并保存在SD卡上。

这在索尼爱立信手机上非常棒。同时音量也有很大提升。

但我正努力让它在三星设备上运作。

当我播放录制的文件时,听起来像会说话的汤姆:P

最初我认为Samusung设备不喜欢我曾经用过的组合 AudioRecorder

所以我使用了以下方法,其中我循环进入可用配置并使用最佳配置来初始化AudioRecord。

public AudioRecord findAudioRecord() {
    for (int rate: mSampleRates) {
        for (short audioFormat: new short[] {
            AudioFormat.ENCODING_PCM_8BIT, AudioFormat.ENCODING_PCM_16BIT
        }) {
            for (short channelConfig: new short[] {
                AudioFormat.CHANNEL_IN_MONO, AudioFormat.CHANNEL_IN_STEREO
            }) {
                try {
                    Log.i("vipul", "Attempting rate " + rate + "Hz, bits: " + audioFormat + ", channel: " + channelConfig);
                    int bufferSize = AudioRecord.getMinBufferSize(rate, channelConfig, audioFormat);

                    if (bufferSize != AudioRecord.ERROR_BAD_VALUE) {
                        // check if we can instantiate and have a success
                        AudioRecord recorder = new AudioRecord(
                        AudioSource.DEFAULT, rate, channelConfig, audioFormat, bufferSize);

                        if (recorder.getState() == AudioRecord.STATE_INITIALIZED) return recorder;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
    return null;
}

以下是在索尼手机上运行良好的代码。但是正在努力在三星设备上工作。

public class EnvironmentRecorder extends Activity implements OnClickListener {

    private static final int RECORDER_BPP = 16;
    private static final String AUDIO_RECORDER_FILE_EXT_WAV = ".wav";
    private static final String AUDIO_RECORDER_FOLDER = "MyRecorder";
    private static final String AUDIO_RECORDER_TEMP_FILE = "record_temp.raw";
    private static final int RECORDER_SAMPLERATE = 44100;
    private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_STEREO;
    private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;
    private Button start, stop;
    private AudioRecord recorder = null;
    private int bufferSize = 0;
    private Thread recordingThread = null;
    private boolean isRecording = false;
    private static int[] mSampleRates = new int[] {
        8000, 11025, 22050, 44100
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        start = (Button) findViewById(R.id.start);
        stop = (Button) findViewById(R.id.stop);
        start.setOnClickListener(this);
        stop.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
        case R.id.start:
            startRecord();
            break;
        case R.id.stop:
            stopRecording();
            break;
        }
    }

    public EnvironmentRecorder() {

        try {
            bufferSize = AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE, RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private String getFilename1() {
        String filepath = Environment.getExternalStorageDirectory().getPath();
        File file = new File(filepath, AUDIO_RECORDER_FOLDER);

        if (!file.exists()) {
            file.mkdirs();
        }

        return (file.getAbsolutePath() + "/" + "NotGained" + AUDIO_RECORDER_FILE_EXT_WAV);
    }

    private String getFilename2() {
        String filepath = Environment.getExternalStorageDirectory().getPath();
        File file = new File(filepath, AUDIO_RECORDER_FOLDER);

        if (!file.exists()) {
            file.mkdirs();
        }

        return (file.getAbsolutePath() + "/" + "Gained" + AUDIO_RECORDER_FILE_EXT_WAV);
    }

    private String getTempFilename() {
        String filepath = Environment.getExternalStorageDirectory().getPath();
        File file = new File(filepath, AUDIO_RECORDER_FOLDER);

        if (!file.exists()) {
            file.mkdirs();
        }

        File tempFile = new File(filepath, AUDIO_RECORDER_TEMP_FILE);

        if (tempFile.exists()) tempFile.delete();

        return (file.getAbsolutePath() + "/" + AUDIO_RECORDER_TEMP_FILE);
    }

    public AudioRecord findAudioRecord() {
        for (int rate: mSampleRates) {
            for (short audioFormat: new short[] {
                AudioFormat.ENCODING_PCM_8BIT, AudioFormat.ENCODING_PCM_16BIT
            }) {
                for (short channelConfig: new short[] {
                    AudioFormat.CHANNEL_IN_MONO, AudioFormat.CHANNEL_IN_STEREO
                }) {
                    try {
                        Log.v("vipul", "Attempting rate " + rate + "Hz, bits: " + audioFormat + ", channel: " + channelConfig);
                        int bufferSize = AudioRecord.getMinBufferSize(rate, channelConfig, audioFormat);

                        if (bufferSize != AudioRecord.ERROR_BAD_VALUE) {
                            // check if we can instantiate and have a success
                            AudioRecord recorder = new AudioRecord(
                            AudioSource.DEFAULT, rate, channelConfig, audioFormat, bufferSize);

                            if (recorder.getState() == AudioRecord.STATE_INITIALIZED) return recorder;
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return null;
    }

    public void startRecord() {
        /*
         * recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,
         * RECORDER_SAMPLERATE, RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING,
         * bufferSize);
         */

        recorder = findAudioRecord();

        recorder.startRecording();

        isRecording = true;

        recordingThread = new Thread(new Runnable() {

            @Override
            public void run() {
                writeAudioDataToFile();
            }
        }, "AudioRecorder Thread");

        recordingThread.start();
    }

    private void writeAudioDataToFile() {
        byte data[] = new byte[bufferSize];
        String filename = getTempFilename();
        FileOutputStream os = null;

        try {
            os = new FileOutputStream(filename);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        int read = 0;

        if (null != os) {
            while (isRecording) {
                read = recorder.read(data, 0, bufferSize);

                if (AudioRecord.ERROR_INVALID_OPERATION != read) {
                    try {
                        os.write(data);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

            try {
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public void stopRecording() {
        if (null != recorder) {
            isRecording = false;

            recorder.stop();
            recorder.release();

            recorder = null;
            recordingThread = null;
            copyWaveFile(getTempFilename(), getFilename1(), getFilename2());
            deleteTempFile();
        }

    }

    private void deleteTempFile() {
        File file = new File(getTempFilename());

        file.delete();
    }

    private void copyWaveFile(String inFilename, String outFileName1, String outFileName2) {
        FileInputStream in = null;
        FileOutputStream out1 = null, out2 = null;
        long totalAudioLen = 0;
        long totalDataLen = totalAudioLen + 36;
        long longSampleRate = RECORDER_SAMPLERATE;
        int channels = 2;
        long byteRate = RECORDER_BPP * RECORDER_SAMPLERATE * channels / 8;

        byte[] data = new byte[bufferSize];

        try { in = new FileInputStream(inFilename);
            out1 = new FileOutputStream(outFileName1);
            out2 = new FileOutputStream(outFileName2);
            totalAudioLen = in .getChannel().size();
            totalDataLen = totalAudioLen + 36;

            WriteWaveFileHeader(out1, totalAudioLen, totalDataLen, longSampleRate, channels, byteRate);
            WriteWaveFileHeader(out2, totalAudioLen, totalDataLen, longSampleRate, channels, byteRate);

            while ( in .read(data) != -1) {

                out1.write(data); // Writing Non-Gained Data

                float rGain = 2.5f;
                for (int i = 0; i < data.length / 2; i++) {

                    short curSample = getShort(data[i * 2], data[i * 2 + 1]);
                    if (rGain != 1) {
                        // apply gain
                        curSample *= rGain;
                        // convert back from short sample that was "gained" to
                        // byte data
                        byte[] a = getByteFromShort(curSample);
                        // modify buffer to contain the gained sample
                        data[i * 2] = a[0];
                        data[i * 2 + 1] = a[1];
                    }

                }

                out2.write(data); // Writing Gained Data
            }
            out1.close();
            out2.close(); in .close();

            Toast.makeText(this, "Done!!", Toast.LENGTH_LONG).show();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private short getShort(byte argB1, byte argB2) {
        return (short)((argB1 & 0xff) | (argB2 << 8));

    }

    private byte[] getByteFromShort(short x) {
        // variant 1 - noise
        byte[] a = new byte[2];
        a[0] = (byte)(x & 0xff);
        a[1] = (byte)((x >> 8) & 0xff);

        // variant 2 - noise and almost broke my ears - very loud
        // ByteBuffer buffer = ByteBuffer.allocate(2);
        // buffer.putShort(x);
        // buffer.flip();

        return a;
    }

    private void WriteWaveFileHeader(FileOutputStream out, long totalAudioLen, long totalDataLen, long longSampleRate, int channels, long byteRate)
    throws IOException {

        byte[] header = new byte[44];

        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);
        header[8] = 'W';
        header[9] = 'A';
        header[10] = 'V';
        header[11] = 'E';
        header[12] = 'f';
        header[13] = 'm';
        header[14] = 't';
        header[15] = ' ';
        header[16] = 16;
        header[17] = 0;
        header[18] = 0;
        header[19] = 0;
        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);
        header[32] = (byte)(2 * 16 / 8);
        header[33] = 0;
        header[34] = RECORDER_BPP;
        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);

        out.write(header, 0, 44);
    }

}

我想知道是否需要添加任何额外的loc,以使我的AudioRecord适应三星设备。

2 个答案:

答案 0 :(得分:6)

我们也在一些三星Android设备上进行录音。不幸的是,它似乎非常破碎,因为相同手机型号的不同版本在相同的代码库中表现不同。

以下是我目前的发现,希望您找到有用的东西:

<强> 1。初始化中断:

不幸的是,用于查询有效录制配置的策略至少会在运行Android 2.3的Samsung Galaxy Young和Ace机型上失败 问题是一些无效的AudioRecord配置而不是简单的失败,如果尝试的话,将完全阻止音频捕获子系统。您需要重置手机才能从此状态恢复。

<强> 2。对同一手机型号的修订提供不一致的采样率支持

在较旧的Galaxy Ace手机上,录制@ 11025Hz,16位单声道将成功。在较新的Ace版本中,此AudioRecord配置将被接受为有效,但结果录制将失真,具有“花栗鼠”效果。一个非常受欢迎的吉他调谐器应用程序硬编码了这个采样率,因为这个问题无法在这些手机上提供正确的调谐读数!

第3。某些配置的音量捕获量非常低。

在Galaxy Young和Galaxy Ace中,从麦克风或默认音频源@ 44,100Hz录制(所谓的规范速率,一切都应该正常工作)会产生无失真但极低音量的录音。除了软件放大之外,我还没有找到解决这个问题的方法(相当于放大一个非常低分辨率的图像,结果的结果是“jageddnes”。)

<强> 4。无法在每个音频捕获源上支持规范的44,100Hz采样率。

在Galaxy Young和Galaxy Ace中,来自摄像机源的录音失败@ 44,100Hz。 (再次,配置将被接受为有效)产生完整的垃圾。但是,录制@ 8,000Hz,16,000Hz和48,000Hz工作正常并产生具有非常可接受的音量水平的录音。令人沮丧的是,根据Android文档,44,100Hz是所有设备应该支持的采样率。

<强> 5。 OpenSL无法解决报告的任何问题。

使用NDK和OpenSL产生相同的描述结果。似乎AudioRecorder类只是包含对OpenSL的调用,问题是基于硬件,或者埋没在内核代码中的较低级别层。

这种情况非常不幸,因为这些模型正变得非常受欢迎 - 至少在墨西哥。

祝你好运 - 如果你有幸使用这些手机,请报告。 =)

答案 1 :(得分:1)

音频增益控制要增加音频幅度,需要计算增益因子,并将每个采样样本乘以计算的增益系数。以下代码执行此操作。附:忽略不相关的代码

public class MainActivity extends Activity {

public static final int SAMPLE_RATE = 16000;

private AudioRecord mRecorder;
private File mRecording;
private short[] mBuffer;
private final String startRecordingLabel = "Start recording";
private final String stopRecordingLabel = "Stop recording";
private boolean mIsRecording = false;
private ProgressBar mProgressBar;
float iGain = 1.0f;
CheckBox gain;

protected int bitsPerSamples = 16;

@Override
public void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.fragment_main);

    initRecorder();

    Button bluetooth = (Button)findViewById(R.id.blue);
    gain = (CheckBox) findViewById(R.id.checkBox1);
    mProgressBar = (ProgressBar) findViewById(R.id.progressBar);

    final Button button = (Button) findViewById(R.id.start);
    button.setText(startRecordingLabel);

    bluetooth.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            Intent i = new Intent("");
        }
    });
    gain.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

        @Override
        public void onCheckedChanged(CompoundButton buttonView,
                boolean isChecked) {

            if (gain.isChecked()) {
                iGain = 5.0f;

            } else {
                iGain = 2.0f;
            }
        }
    });

    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(final View v) {
            if (!mIsRecording) {
                button.setText(stopRecordingLabel);
                mIsRecording = true;
                mRecorder.startRecording();
                mRecording = getFile("raw");
                startBufferedWrite(mRecording);
            } else {
                button.setText(startRecordingLabel);
                mIsRecording = false;
                mRecorder.stop();
                File waveFile = getFile("wav");
                try {
                    rawToWave(mRecording, waveFile);
                } catch (IOException e) {
                    Toast.makeText(MainActivity.this, e.getMessage(),
                            Toast.LENGTH_SHORT).show();
                }
                Toast.makeText(MainActivity.this,
                        "Recorded to " + waveFile.getName(),
                        Toast.LENGTH_SHORT).show();
            }
        }
    });
}

@Override
public void onDestroy() {
    mRecorder.release();
    super.onDestroy();
}

private void initRecorder() {
    int bufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE,
            AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
    mBuffer = new short[bufferSize];
    mRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLE_RATE,
            AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT,
            bufferSize);
}

private void startBufferedWrite(final File file) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            DataOutputStream output = null;
            try {
                output = new DataOutputStream(new BufferedOutputStream(
                        new FileOutputStream(file)));
                while (mIsRecording) {
                    double sum = 0;

                    int readSize = mRecorder.read(mBuffer, 0,
                            mBuffer.length);

                    final int bytesPerSample = bitsPerSamples / 8;
                    final int emptySpace = 64 - bitsPerSamples;
                    int byteIndex = 0;
                    int byteIndex2 = 0;
                    int temp = 0;
                    int mLeftTemp = 0;
                    int mRightTemp = 0;
                    int a = 0;
                    int x = 0;

                    for (int frameIndex = 0; frameIndex < readSize; frameIndex++) {

                        for (int c = 0; c < 1; c++) {

                            if (iGain != 1) {

                                long accumulator = 0;
                                for (int b = 0; b < bytesPerSample; b++) {

                                    accumulator += ((long) (mBuffer[byteIndex++] & 0xFF)) << (b * 8 + emptySpace);
                                }

                                double sample = ((double) accumulator / (double) Long.MAX_VALUE);
                                sample *= iGain;
                                int intValue = (int) ((double) sample * (double) Integer.MAX_VALUE);

                                for (int i = 0; i < bytesPerSample; i++) {
                                    mBuffer[i + byteIndex2] = (byte) (intValue >>> ((i + 2) * 8) & 0xff);
                                }
                                byteIndex2 += bytesPerSample;

                            }
                        }// end for(channel)

                        // mBuffer[frameIndex] *=iGain;
                        if (mBuffer[frameIndex] > 32765) {
                            mBuffer[frameIndex] = 32767;

                        } else if (mBuffer[frameIndex] < -32767) {
                            mBuffer[frameIndex] = -32767;
                        }
                        output.writeShort(mBuffer[frameIndex]);
                        sum += mBuffer[frameIndex] * mBuffer[frameIndex];

                    }

                    if (readSize > 0) {
                        final double amplitude = sum / readSize;
                        mProgressBar.setProgress((int) Math.sqrt(amplitude));
                    }
                }
            } catch (IOException e) {
                Toast.makeText(MainActivity.this, e.getMessage(),
                        Toast.LENGTH_SHORT).show();
            } finally {
                mProgressBar.setProgress(0);
                if (output != null) {
                    try {
                        output.flush();
                    } catch (IOException e) {
                        Toast.makeText(MainActivity.this, e.getMessage(),
                                Toast.LENGTH_SHORT).show();
                    } finally {
                        try {
                            output.close();
                        } catch (IOException e) {
                            Toast.makeText(MainActivity.this, e.getMessage(),
                                    Toast.LENGTH_SHORT).show();
                        }
                    }
                }
            }
        }
    }).start();
}

private void rawToWave(final File rawFile, final File waveFile)
        throws IOException {

    byte[] rawData = new byte[(int) rawFile.length()];
    DataInputStream input = null;
    try {

        input = new DataInputStream(new FileInputStream(rawFile));
        input.read(rawData);
    } finally {
        if (input != null) {
            input.close();
        }
    }

    DataOutputStream output = null;
    try {
        output = new DataOutputStream(new FileOutputStream(waveFile));
        // WAVE header
        // see http://ccrma.stanford.edu/courses/422/projects/WaveFormat/
        writeString(output, "RIFF"); // chunk id
        writeInt(output, 36 + rawData.length); // chunk size
        writeString(output, "WAVE"); // format
        writeString(output, "fmt "); // subchunk 1 id
        writeInt(output, 16); // subchunk 1 size
        writeShort(output, (short) 1); // audio format (1 = PCM)
        writeShort(output, (short) 1); // number of channels
        writeInt(output, SAMPLE_RATE); // sample rate
        writeInt(output, SAMPLE_RATE * 2); // byte rate
        writeShort(output, (short) 2); // block align
        writeShort(output, (short) 16); // bits per sample
        writeString(output, "data"); // subchunk 2 id
        writeInt(output, rawData.length); // subchunk 2 size
        // Audio data (conversion big endian -> little endian)
        short[] shorts = new short[rawData.length / 2];
        ByteBuffer.wrap(rawData).order(ByteOrder.LITTLE_ENDIAN)
                .asShortBuffer().get(shorts);
        ByteBuffer bytes = ByteBuffer.allocate(shorts.length * 2);

        for (short s : shorts) {

            // Apply Gain
            /*
             * s *= iGain; if(s>32767) { s=32767; } else if(s<-32768) {
             * s=-32768; }
             */
            bytes.putShort(s);
        }
        output.write(bytes.array());
    } finally {
        if (output != null) {
            output.close();
        }
    }
}

private File getFile(final String suffix) {
    Time time = new Time();
    time.setToNow();
    return new File(Environment.getExternalStorageDirectory(),
            time.format("%Y%m%d%H%M%S") + "." + suffix);
}





private void writeInt(final DataOutputStream output, final int value)
        throws IOException {
    output.write(value >> 0);
    output.write(value >> 8);
    output.write(value >> 16);
    output.write(value >> 24);
}

private void writeShort(final DataOutputStream output, final short value)
        throws IOException {
    output.write(value >> 0);
    output.write(value >> 8);
}

private void writeString(final DataOutputStream output, final String value)
        throws IOException {
    for (int i = 0; i < value.length(); i++) {
        output.write(value.charAt(i));
    }
}
}