如何将两个MP3音频文件混合/叠加成一个MP3文件(不是连接)

18

我想将两个MP3文件合并成一个MP3文件。例如,如果第一个文件是1分钟,第二个文件是30秒,那么输出应该是一分钟。在这一分钟中,应该播放两个文件。


所以您想要“混合”这两个音频文件? - Michael
是的,但我想要混合两个MP3文件。 - sandeep
如果您只想同时播放两个文件,那么不能将它们分别加载到不同的mediaPlayer中吗?创建一个实例来播放第一个文件,另一个实例播放第二个文件,然后再创建两个新线程并同时开始播放这些文件,使用 mediaPlayer.start() 命令。 - Apurva
7个回答

2
首先,为了混合两个音频文件,您需要操作它们的原始表示形式;因为MP3文件是压缩的,所以您无法直接访问信号的原始表示形式。您需要解码压缩的MP3流,以便"理解"您的音频信号的波形,然后您将能够混合它们。
因此,为了将两个压缩的音频文件混合成单个压缩的音频文件,需要执行以下步骤:
1. 使用解码器解码压缩文件以获取原始数据(没有公共系统API可用于此,您需要手动完成!)。 2. 混合两个原始未压缩的数据流(如果必要,应用音频削波)。为此,您需要考虑使用解码器获得的原始数据格式(PCM)。 3. 将原始混合数据编码为压缩的MP3文件(根据解码器,您需要手动使用编码器完成)。
关于MP3解码器的更多信息可以在这里找到。

如果您能在答案中添加一些代码,那就太好了。 - Advait Saravade
3
这项任务涉及很多工作,没有简单的答案。你应该先尝试实现它,然后再回来询问澄清。 - bonnyz
有没有办法与您分享我的一些代码? - Advait Saravade
2
@AdvaitS 你应该将你的代码和一个详细的解释一起发布为一个新问题,说明你已经完成了什么以及需要做什么。我认为这是寻求帮助的最佳方式。 - bonnyz
我不可能这样做。这个问题会成为重复的。你的解决方案虽然提供了指导,但仍然是不完整的。如果您提供一些代码,我很乐意为您的答案颁发奖励。 - Advait Saravade
@Advait S 人们抽出时间在这里发布他们的解决方案。你至少应该尝试一下并发布你的发现,或者至少说明为什么这些解决方案不起作用。我知道放弃你的赏金很难,但是在这里没有人会替你做功课 :) - Sagar Hatekar

0

我在Android上还没有做过,但是我用Adobe Flex做过。 我想逻辑是相同的。 我按照以下步骤进行:

  • 我将两个mp3文件提取为两个字节数组。(song1ByteArraysong2ByteArray
  • 找出较大的字节数组。(假设song1ByteArray是较大的数组)。
  • 创建一个函数,返回混合后的字节数组。

    private ByteArray mix2Songs(ByteArray song1ByteArray, ByteArray song2ByteArray){
    int arrLength=song1ByteArray.length; 
    for(int i=0;i<arrLength;i+=8){ // 这里如果你看到我们每次增加8的长度,因为立体声音频有左右两个通道,每个通道占4个字节。
        // 读取第一首歌曲的左右声道值
        float source1_L=song1ByteArray.readFloat();// 我不确定readFloat()函数是否存在于Android中,但会有相应的函数。
        float source1_R=song1ByteArray.readFloat(); 
        float source2_L=0;
        float source2_R=0;
        if(song2ByteArray.bytesAvailable>0){
            source2_L=song1ByteArray.readFloat();//歌曲2的左声道
            source2_R=song1ByteArray.readFloat(); //歌曲2的右声道
        }
        returnResultArr.writeFloat((source_1_L+source_2_L)/2); // 源1和源2左声道的平均值
        returnResultArr.writeFloat((source_1_R+source_2_R)/2); // 源1和源2右声道的平均值
    }
    return returnResultArr;
    }
    

0

1. Android中的音频混合文章

2. 另一篇关于Android音频混合的文章

3. 您可以利用Java Sound来混合两个音频文件

示例:


// First convert audiofile to audioinputstream

audioInputStream = AudioSystem.getAudioInputStream(soundFile);
audioInputStream2 = AudioSystem.getAudioInputStream(soundFile2);

// Create one collection list object using arraylist then add all AudioInputStreams

Collection list=new ArrayList();
list.add(audioInputStream2);
list.add(audioInputStream);

// Then pass the audioformat and collection list to MixingAudioInputStream constructor

MixingAudioInputStream mixer=new MixingAudioInputStream(audioFormat, list); 

// Finally read data from mixed AudionInputStream and give it to SourceDataLine

nBytesRead =mixer.read(abData, 0,abData.length);

int nBytesWritten = line.write(abData, 0, nBytesRead);

4. 尝试使用 AudioConcat,它具有混合的 -m 选项


java AudioConcat [ -D ] [ -c ] | [ -m ] | [ -f ] -o outputfile inputfile ...

Parameters. 

-c
selects concatenation mode

-m
selects mixing mode

-f
selects float mixing mode

-o outputfile
The filename of the output file

inputfile
the name(s) of input file(s)

5. 你可以使用FFmpeg安卓封装器,具体语法和方法请参见这里


我认为当时Java Sound没有移植到Android。因此,MixingAudioInputStream构造函数将无法工作。 - Advait Saravade
是的,我已经尝试过了。声音 API 还没有移植到 Android 上。这里甚至有一个讨论 - https://groups.google.com/forum/m/#!topic/android-developers/VD2V7ENFrgs - Advait Saravade

0

这个人在一个与你的项目非常相似的项目中使用了JLayer库。他还为你提供了一个指南,告诉你如何直接重新编译jar文件将该库集成到你的Android应用程序中。

简单地说,通过改写他的代码,你可以很容易地完成你的任务:

public static byte[] decode(String path, int startMs, int maxMs) 
  throws IOException, com.mindtherobot.libs.mpg.DecoderException {
  ByteArrayOutputStream outStream = new ByteArrayOutputStream(1024);

  float totalMs = 0;
  boolean seeking = true;

  File file = new File(path);
  InputStream inputStream = new BufferedInputStream(new FileInputStream(file), 8 * 1024);
  try {
    Bitstream bitstream = new Bitstream(inputStream);
    Decoder decoder = new Decoder();

    boolean done = false;
    while (! done) {
      Header frameHeader = bitstream.readFrame();
      if (frameHeader == null) {
        done = true;
      } else {
        totalMs += frameHeader.ms_per_frame();

        if (totalMs >= startMs) {
          seeking = false;
        }

        if (! seeking) {
          SampleBuffer output = (SampleBuffer) decoder.decodeFrame(frameHeader, bitstream);

          if (output.getSampleFrequency() != 44100
              || output.getChannelCount() != 2) {
            throw new com.mindtherobot.libs.mpg.DecoderException("mono or non-44100 MP3 not supported");
          }

          short[] pcm = output.getBuffer();
          for (short s : pcm) {
            outStream.write(s & 0xff);
            outStream.write((s >> 8 ) & 0xff);
          }
        }

        if (totalMs >= (startMs + maxMs)) {
          done = true;
        }
      }
      bitstream.closeFrame();
    }

    return outStream.toByteArray();
  } catch (BitstreamException e) {
    throw new IOException("Bitstream error: " + e);
  } catch (DecoderException e) {
    Log.w(TAG, "Decoder error", e);
    throw new com.mindtherobot.libs.mpg.DecoderException(e);
  } finally {
    IOUtils.safeClose(inputStream);     
  }
}

public static byte[] mix(String path1, String path2) {
    byte[] pcm1 = decode(path1, 0, 60000); 
    byte[] pcm2 = decode(path2, 0, 60000);
    int len1=pcm1.length; 
    int len2=pcm2.length;
    byte[] pcmL; 
    byte[] pcmS;
    int lenL; // length of the longest
    int lenS; // length of the shortest
    if (len2>len1) {
        lenL = len1;
        pcmL = pcm1;
        lenS = len2;                
        pcmS = pcm2;
    } else {
        lenL = len2;
        pcmL = pcm2;
        lenS = len1;                
        pcmS = pcm1;
    } 
    for (int idx = 0; idx < lenL; idx++) {
        int sample;
        if (idx >= lenS) {
            sample = pcmL[idx];
        } else {
            sample = pcmL[idx] + pcmS[idx];
        }
        sample=(int)(sample*.71);
        if (sample>127) sample=127;
        if (sample<-128) sample=-128;
        pcmL[idx] = (byte) sample;
    }
    return pcmL;
}

请注意,我在最后几行添加了衰减和削波:当混合两个波形时,您必须同时执行这两项操作。如果您没有内存/时间要求,可以创建一个int[] samples数组的总和,并评估避免削波的最佳衰减。

0

要合并(重叠)两个音频文件,您可以使用此FFMPEG库

这里是文档

在他们的示例中,您只需输入所需的命令。因此,让我们谈谈我们需要的命令。

-i [FISRST_FILE_PATH] -i [SECOND_FILE_PATH] -filter_complex amerge -ac 2 -c:a libmp3lame -q:a 4 [OUTPUT_FILE_PATH]

对于第一个和第二个文件路径,您将获得声音文件的绝对路径。 1- 如果它在存储中,则是Environment.getExternalStorageDirectory().getAbsolutePath()的子文件夹。

2- 如果它是assets文件,则应该是file:///android_asset/的子文件夹。

对于输出路径,请确保添加扩展名 例如。

String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/File Name.mp3"

0

我不确定你是否想在安卓手机上完成这个(因为你的标签看起来是这样),但如果我没错的话,也许可以试试LoopStack,它是一个移动数字音频工作站(我自己没有试过)。

如果你只是简单地“混合”两个文件而没有调整输出音量,那么你的输出可能会削峰。然而我不确定是否有可能在不解码的情况下“混合”两个mp3文件。

如果你愿意在电脑上合并它们,可以试试Audacity,这是一个免费的桌面数字音频工作站。


-1

我没找到什么比较好的解决方案,但我们可以在这里做一些小技巧.. :) 你可以将两个mp3文件分别赋值给两个不同的MediaPlayer对象。然后用一个按钮同时播放两个文件,比较这两个mp3文件以找到最长的持续时间。之后使用AudioReorder来录制该时长,这样就可以解决你的问题了。我知道这不是正确的方式,但希望能对你有所帮助.. :)


问题是关于导出混合MP3,而不是播放。 - bonnyz
@bonnyz 但是你的解决方案不完整... :) - Vanilla Boy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接