当我录制手机的麦克风时,我的Mediarecorder
给了我PCM File
作为输出。然后,我尝试将收到的PCM File
和另一个PCM audio
与mixPCMFileSignals()
相加(如下所示)。它把音频放在一起,但是当我收听输出的音频时,我可以听到录制的声音不清楚,并且有很多嗡嗡声(我希望这是正确的词)或静态的。
我像这样通过麦克风录音来制作output File
:
public class UAudioRecorder{
private static final int CHANNEL = AudioFormat.CHANNEL_IN_STEREO;
private static final int AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;
private static final int SAMPLE_RATE = 22050; //44.1kHz
private static final int BUFFER_SIZE = 2048;
public Status status = Status.IDLE;
private AudioRecordingHandler arh;
private File outputFile;
private Context context;
AudioRecord record;
AACEncoder aacEncoder;
enum Status {
IDLE,
RECORDING;
}
public UAudioRecorder(Context context) {
this.context = context;
init();
}
private void init() {
record = new AudioRecord(1, SAMPLE_RATE,
CHANNEL, AUDIO_ENCODING, BUFFER_SIZE*2 );
try {
outputFile = createOutputFile();
} catch (Exception ex) {
Log.e(TAG, "Failed to init AudioRecorder!");
ex.printStackTrace();
}
}
/**
* Creates new temp folder and temp output file.
* If temp file exists, overrides it.
* Returns temp file
*/
private File createOutputFile() throws IOException {
File tempDir = new File(context.getFilesDir()+ Common.TEMP_LOCAL_DIR);
File file = new File(context.getFilesDir()+ Common.TEMP_LOCAL_DIR + "/temp.aac");
if (!tempDir.exists()) {
tempDir.mkdirs();
}
if (file.exists()) {
file.delete();
}
file.createNewFile();
return file;
}
/**
* Starts recording user mic input. Activate thread for recording.
* Takes in AudioRecordingHandler. Thread cannot start if outputFile
* is still not set.
* @param arh
*/
public void record(AudioRecordingHandler arh) {
this.arh = arh;
status = Status.RECORDING;
start();
}
/**
* Starts script for running. Needs output file to work!
*/
public void start() {
if (outputFile == null) { return; }
System.out.println("Start reading stream...");
aacEncoder = new AACEncoder(SAMPLE_RATE, outputFile);
new Thread(new Runnable() {
@Override
public void run() {
record.startRecording();
byte[] data = new byte[BUFFER_SIZE];
float[] audioFloatBuffer = new float[BUFFER_SIZE/2];
Yin y = new Yin(SAMPLE_RATE, BUFFER_SIZE/2);
while(status == Status.RECORDING) {
record.read(data, 0, BUFFER_SIZE);
audioFloatBuffer = ConverterUtil.toFloatArray(data, 0, audioFloatBuffer,
0, audioFloatBuffer.length);
PitchDetectionResult pdr = y.getPitch(audioFloatBuffer);
arh.handlePitch(pdr.getPitch());
aacEncoder.writeIntoOutputfile(data);
}
aacEncoder.stopEncoding();
}
}).start();
}
/**
* Stops script
*/
public void stop() {
status = Status.IDLE;
record.stop();
arh.finishedRecording(outputFile);
}
}
现在,output file
通过将它们转换为byte[]
并将它们合并在一起来添加另一种声音。
SimpleAACEncoder.mixPCMFileSignals(micOutputPCM, backgroundAudio, outfile);
还有SimpleAACEncoder
,这是我将两个信号加在一起的地方:
public class SimpleAACEncoder {
public static File mixPCMFileSignals(File pcmFile1, File pcmFile2,
File output) throws IOException {
FileInputStream fis1 = new FileInputStream(pcmFile1);
FileInputStream fis2 = new FileInputStream(pcmFile2);
int bufferSize = 2048;
byte[] buf1 = new byte[bufferSize];
byte[] buf2 = new byte[bufferSize];
int state = 1;
int longerFile = (pcmFile1.length() > pcmFile2.length()) ? 0 : 1;
if (!output.exists()) {
output.createNewFile();
}
AACEncoder encoder = new AACEncoder(44100, output);
while (state == 1) {
int bytesread1 = fis1.read(buf1, 0, bufferSize);
int bytesread2 = fis2.read(buf2, 0, bufferSize);
byte[] byteStream = mixSignals(buf1, buf2);
encoder.encodeAudioFrameToAAC(byteStream);
/*fos.write(byteStream, 0, bufferSize);*/
if (longerFile == 1) {
if (bytesread1 == -1) {
state = 0;
}
} else {
if (bytesread2 == -1) {
state = 0;
}
}
}
return output;
}
/**
* Mixes bytes stream values into one. Designed to use with PCM 16bit data.
* @param signalOne - signal 1
* @param signalTwo - signal 2
* @return mixed signals. Clips away values that are not fitting (means lossy quality (rarely happens))
*/
public static byte[] mixSignals(byte[] signalOne, byte[] signalTwo) {
byte[] final_arrayb = new byte[2048];
for (int i = 0; i < final_arrayb.length; i++) {
float samplef1 = signalOne[i] / 128.0f * 3.5f; //2^7
float samplef2 = signalTwo[i] / 128.0f;
float mixed = samplef1 + samplef2;
// volume change -- currently works on both tracks togethers;
mixed *= 1;
// hard clipping.. removes values that does not fit into scale -1...1
if (mixed > 1.0f) mixed = 1.0f;
if (mixed < -1.0f) mixed = -1.0f;
byte outputSample = (byte) (mixed * 128.0f);
final_arrayb[i] = outputSample;
}
return final_arrayb;
}
}
最后是我的AACEncoder
:
public class AACEncoder {
final String TAG = "UEncoder Processor";
final int sampleRate;
File outputFile;
FileOutputStream fos;
final int TIMEOUT_USEC = 10000 ;
MediaCodec encoder;
boolean isEncoderRunning = false;
boolean outputDone = false;
MediaCodec.BufferInfo info;
public AACEncoder(final int sampleRate, File outputFile) {
this.sampleRate = sampleRate;
this.info = new MediaCodec.BufferInfo();
this.outputFile = outputFile;
openFileStream();
initEncoder();
}
/**
* Initializes encoder for AAC-LC (Low complexity)
* @throws Exception
*/
public void initEncoder() {
try {
encoder = MediaCodec.createEncoderByType("audio/mp4a-latm");
MediaFormat format = new MediaFormat();
format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100);
format.setInteger(MediaFormat.KEY_BIT_RATE, 128000);
format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
} catch (IOException ex) {
Log.e(TAG, "Failed to create encoder");
ex.printStackTrace();
}
}
int generateIndex = 0;
public void encodeAudioFrameToAAC(byte[] frameData) {
if (encoder == null) return;
if (!isEncoderRunning) {
encoder.start();
isEncoderRunning = true;
}
ByteBuffer[] encoderInputBuffers = encoder.getInputBuffers();
if (fos != null) {
int inputBufIndex = encoder.dequeueInputBuffer(TIMEOUT_USEC);
if (inputBufIndex >= 0) {
long ptsUsec = (System.currentTimeMillis() * 1000) / 10000;
if (outputDone) {
encoder.queueInputBuffer(inputBufIndex, 0, 0, ptsUsec,
MediaCodec.BUFFER_FLAG_END_OF_STREAM);
} else {
ByteBuffer inputBuf = encoderInputBuffers[inputBufIndex];
inputBuf.clear();
inputBuf.put(frameData);
encoder.queueInputBuffer(inputBufIndex, 0, frameData.length, ptsUsec, 0);
}
generateIndex++;
}
tryEncodeOutputBuffer();
}
checkIfOutputDone();
}
/**
* Gets data from output buffer and encodes it to
* AAC-LC encoding with ADTS header attached before every frame
*/
private void tryEncodeOutputBuffer() {
ByteBuffer[] encoderOutputBuffers = encoder.getOutputBuffers();
//If >= 0 then valid response
int encoderStatus = encoder.dequeueOutputBuffer(info, TIMEOUT_USEC);
if (encoderStatus >= 0) {
ByteBuffer encodedData = encoderOutputBuffers[encoderStatus];
encodedData.position(info.offset);
encodedData.limit(info.offset + info.size + 7);
byte[] data = new byte[info.size + 7];
addADTStoPacket(data, info.size + 7);
encodedData.get(data, 7, info.size);
encodedData.position(info.offset);
writeIntoOutputfile(data);
encoder.releaseOutputBuffer(encoderStatus, false);
}
}
private void checkIfOutputDone() {
if (outputDone) {
if (fos != null) {
try {
fos.close();
} catch (IOException ioe) {
Log.w(TAG, "failed closing debug file");
throw new RuntimeException(ioe);
}
fos = null;
}
}
}
/**
* Add ADTS header at the beginning of each and every AAC packet.
* This is needed as MediaCodec encoder generates a packet of raw
* AAC data.
*
* Note the packetLen must count in the ADTS header itself.
**/
private void addADTStoPacket(byte[] packet, int packetLen) {
int profile = 2; //AAC LC
//39=MediaCodecInfo.CodecProfileLevel.AACObjectELD;
int freqIdx = 4; //44.1KHz
int chanCfg = 2; //CPE
// fill in ADTS data
packet[0] = (byte)0xFF;
packet[1] = (byte)0xF9;
packet[2] = (byte)(((profile-1)<<6) + (freqIdx<<2) +(chanCfg>>2));
packet[3] = (byte)(((chanCfg&3)<<6) + (packetLen>>11));
packet[4] = (byte)((packetLen&0x7FF) >> 3);
packet[5] = (byte)(((packetLen&7)<<5) + 0x1F);
packet[6] = (byte)0xFC;
}
private void openFileStream() {
fos = null;
try {
fos = new FileOutputStream(outputFile, false);
} catch (FileNotFoundException e) {
Log.e("AudioRecorder", e.getMessage());
}
}
/**
* Writes data into file
* @param data
*/
public void writeIntoOutputfile(byte[] data) {
try {
fos.write(data);
} catch (IOException ioe) {
Log.w(TAG, "failed writing debug data to file");
throw new RuntimeException(ioe);
}
}
public void stopEncoding() {
isEncoderRunning = false;
encoder.stop();
closeStream();
}
private void closeStream() {
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
Log.e("AudioRecorder", e.getMessage());
}
}
}
我的问题是,如何消除音频中的嗡嗡声/静态噪声。