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

AMG*_*AMG 5 arrays audio merge android 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();
Run Code Online (Sandbox Code Playgroud)

然后......我做了同样的事情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( );
Run Code Online (Sandbox Code Playgroud)

然后将组合的字节数组写入一个新的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();
        }
Run Code Online (Sandbox Code Playgroud)

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

UPDATE

这是我在代码中采用的方向的更新.

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

mixSamples(String filePathOne, String filePathTwo)

然后在该方法中,我想使用媒体提取器从每个mp3文件中提取数据,然后解码每个文件.在解码文件之后,我想将每个文件存储在a中short[],然后调用mix()如下所示的方法将两者short[]'s合并为一个short[]然后将新创建的数组编码回mp3.

    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;
    }
Run Code Online (Sandbox Code Playgroud)

Vol*_*nis 4

您想要使用 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;
}
Run Code Online (Sandbox Code Playgroud)

更新 发自内心地提供代码:) 我稍后将在我的博客 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();
}
Run Code Online (Sandbox Code Playgroud)