如何将一个音频文件覆盖另一个音频文件并保存?

时间:2015-06-23 15:58:52

标签: android arrays audio merge overlay

我想要完成的是将音轨覆盖在音轨上以形成新的歌曲轨道。

这是我的一些代码。我正在使用vocal.mp3阅读FileInputStream,然后将其保存为类似的字节数组...

        try {
            fis = new FileInputStream(myFile);
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        bos = new ByteArrayOutputStream();
        byte[] buf = new byte[2048];
        try {
            for (int readNum; (readNum = fis.read(buf)) != -1;) {
                bos.write(buf, 0, readNum);
                System.out.println("read " + readNum + " bytes,");
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        } 

        bytes = bos.toByteArray();

然后......我为music.mp3执行相同的操作,并将其读入单独的字节数组中。我不打算为此显示代码,因为它与上面相同。

在我有两个单独的字节数组后,我可以将它们组合起来......

        outputStream = new ByteArrayOutputStream( );
        try {
            outputStream.write( bytes );
            outputStream.write( bytes2 );
        } catch (IOException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        mixData = new byte[bytes.length + bytes2.length];
        mixData = outputStream.toByteArray( );

然后将组合的字节数组写入一个新的song.mp3文件,以便像这样保存...

        File someFile = new File(songOutPath);

        try {
            fos2 = new FileOutputStream(someFile);
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        try {
            fos2.write(mixData);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        try {
            fos2.flush();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        try {
            fos2.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

这段代码会将两个mp3文件合并为一个...但是它们会一个接一个播放......我需要知道是否有人可以帮我找到让它们同时播放的方法。这样,人声和音乐曲目将在我生成的新歌曲文件中同时播放。

更新

以下是我在代码中所采用方向的更新。

我想调用一个方法并为每个单独的mp3文件传递两个文件路径,如下所示:

mixSamples(String filePathOne, String filePathTwo)

然后在该方法中,我想使用媒体提取器从每个mp3文件中提取数据,然后解码每个文件。文件解码后,我想将每个文件存储在short[]中,然后调用mix()方法,如下所示将两个short[]'s混合成一个short[]组合 public void mixSamples(String filePathOne, String filePathTwo){ MediaCodec codec = null; MediaExtractor extractor = new MediaExtractor(); try { extractor.setDataSource(filePathOne); return create(extractor); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { extractor.release(); } // ... Do I create another extractor here for my second file? MediaFormat format = extractor.getTrackFormat(0); String mime = format.getString(MediaFormat.KEY_MIME); format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 2); format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100); try { codec = MediaCodec.createDecoderByType(mime); codec.configure(format, null, null, 0); codec.start(); ByteBuffer[] codecInputBuffers = codec.getInputBuffers(); ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers(); extractor.selectTrack(0); MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); final long timeoutUs = 5000; boolean sawInputEOS = false; boolean sawOutputEOS = false; int noOutputCounter = 0; while (!sawOutputEOS && noOutputCounter < 50) { noOutputCounter++; if (!sawInputEOS) { int inputBufferIndex = codec.dequeueInputBuffer(timeoutUs); if (inputBufferIndex >= 0) { ByteBuffer buffer = codecInputBuffers[inputBufferIndex]; int sampleSize = extractor.readSampleData(buffer, 0); long presentationTimeUs = 0; if (sampleSize < 0) { sawInputEOS = true; sampleSize = 0; } else { presentationTimeUs = extractor.getSampleTime(); } codec.queueInputBuffer(inputBufferIndex, 0, sampleSize, presentationTimeUs, sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0); if (!sawInputEOS) { extractor.advance(); } } } int outputBufferIndex = codec.dequeueOutputBuffer(info, timeoutUs); if (outputBufferIndex >= 0) { if (info.size > 0) { noOutputCounter = 0; } ByteBuffer buffer = codecOutputBuffers[outputBufferIndex]; if (info.size > 0) { // Do something... Maybe create my short[] here... } codec.releaseOutputBuffer(outputBufferIndex, false); if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { sawOutputEOS = true; } } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { codecOutputBuffers = codec.getOutputBuffers(); } } } catch (IOException e){ }finally { codec.stop(); codec.release(); } } static short[] mix(short[] buffer, short[] mixWith, int numberOfMixSamples) { final int length = Math.min(buffer.length, numberOfMixSamples); int mixed; for (int i = 0; i < length; i++) { mixed = (int) buffer[i] + (int) mixWith[i]; if (mixed > 32767) mixed = 32767; if (mixed < -32768) mixed = -32768; buffer[i] = (short) mixed; } return buffer; } 然后将新创建的数组编码回mp3。

/home/me/project

2 个答案:

答案 0 :(得分:3)

您希望将MediaCodec与MediaExtractor一起使用,以将mp3(或任何其他音频格式)解码为样本。每个样本都以short而非字节表示。最终你会有短[](样本数)。解码两个音频文件后,您可以将样本混合在一起以生成新样本。然后使用结果样本将处理恢复为编码为音频格式。我用PCM16作为中间格式。将音频混合在一起的方法之一可以是:

static short[] mix(short[] buffer, short[] mixWith, int numberOfMixSamples) {
    final int length = Math.min(buffer.length, numberOfMixSamples);
    int mixed;
    for (int i = 0; i < length; i++) {
        mixed = (int) buffer[i] + (int) mixWith[i];
        if (mixed > 32767) mixed = 32767;
        if (mixed < -32768) mixed = -32768;
        buffer[i] = (short) mixed;
    }
    return buffer;
}

<强>更新 从我心里给我代码:)我稍后会在我的博客android.vladli.com上写文章。这段代码适用于已经弃用的代码,它可以使用,而新的API稍微更清晰,即使没有太大的不同。

MediaExtractor extractor = new MediaExtractor();
extractor.setDataSource(file.getAbsolutePath());
try {
   return create(extractor);
} finally {
   extractor.release();
}

// ...

MediaFormat format = extractor.getTrackFormat(0);
String mime = format.getString(MediaFormat.KEY_MIME);
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 2);
format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100);

MediaCodec codec = MediaCodec.createDecoderByType(mime);
codec.configure(format, null, null, 0);
codec.start();

try {
    ByteBuffer[] codecInputBuffers = codec.getInputBuffers();
    ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers();

    extractor.selectTrack(0);

    MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
    final long timeoutUs = 5000;
    boolean sawInputEOS = false;
    boolean sawOutputEOS = false;
    int noOutputCounter = 0;

    while (!sawOutputEOS && noOutputCounter < 50) {
        noOutputCounter++;
        if (!sawInputEOS) {
            int inputBufferIndex = codec.dequeueInputBuffer(timeoutUs);
            if (inputBufferIndex >= 0) {
                ByteBuffer buffer = codecInputBuffers[inputBufferIndex];
                int sampleSize = extractor.readSampleData(buffer, 0);
                long presentationTimeUs = 0;
                if (sampleSize < 0) {
                    sawInputEOS = true;
                    sampleSize = 0;
                } else {
                    presentationTimeUs = extractor.getSampleTime();
                }
                codec.queueInputBuffer(inputBufferIndex, 0, sampleSize,
                        presentationTimeUs,
                        sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
                if (!sawInputEOS) {
                    extractor.advance();
                }
            }
        }

        int outputBufferIndex = codec.dequeueOutputBuffer(info, timeoutUs);
        if (outputBufferIndex >= 0) {
            if (info.size > 0) {
                noOutputCounter = 0;
            }
            ByteBuffer buffer = codecOutputBuffers[outputBufferIndex];
            if (info.size > 0) {
                // data.writePcm16(buffer, info.offset, info.size);
                // data here is my class to gather buffer (samples) in a queue for further playback. In your case can write them down into disk or do something else
            }
            codec.releaseOutputBuffer(outputBufferIndex, false);
            if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                sawOutputEOS = true;
            }
        } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
            codecOutputBuffers = codec.getOutputBuffers();
        }
    }
} finally {
    codec.stop();
    codec.release();
}

答案 1 :(得分:0)

几年前,我做了同样的事情,我曾经同时演奏过4首音乐。我用线程。每个线程都使用媒体播放器播放音乐,您可以将它们与Cyclbarrier同步。

https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CyclicBarrier.html