18

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

4

7 回答 7

2

首先,为了混合两个音频文件,您需要操纵它们的原始表示;由于MP3 文件是经过压缩的,因此您无法直接访问信号的原始表示。您需要对压缩的 MP3 流进行解码,以便“理解”音频信号的波形,然后才能将它们混合。

因此,为了将两个压缩音频文件混合成一个压缩音频文件,需要以下步骤:

  1. 使用解码器解码压缩文件以获得原始数据(没有可用的公共系统 API,您需要手动完成!)。
  2. 混合两个原始未压缩数据流(必要时应用音频剪辑)。为此,您需要考虑使用解码器( PCM )获得的原始数据格式
  3. 将原始混合数据编码为压缩的 MP3 文件(根据解码器,您需要使用编码器手动完成)

更多关于 MP3 解码器的信息可以在这里找到。

于 2015-10-15T17:33:38.677 回答
0

我不确定您是否想在 Android 手机上执行此操作(由于您的标签,看起来像那样),但如果我是对的,也许可以尝试LoopStack,它是一个移动 DAW(我自己没有尝试过)。

如果您只是“混合”两个文件而不调整输出音量,您的输出可能会剪辑。但是我不确定是否可以在不解码的情况下“混合”两个 mp3 文件。

如果您可以在 PC 上合并它们,请尝试Audacity,它是一个免费的桌面 DAW。

于 2014-03-25T08:01:44.100 回答
0

我没有在 Android 中做过,但我使用 Adob​​e flex 做过。我想逻辑保持不变。我按照以下步骤操作:

  • 我将两个 mp3 提取到两个字节数组中。( song1ByteArray, song2ByteArray)
  • 找出更大的字节数组。(假设song1ByteArray是更大的)。
  • 创建一个返回混合字节数组的函数。

    private ByteArray mix2Songs(ByteArray song1ByteArray, ByteArray song2ByteArray){
    int arrLength=song1ByteArray.length; 
    for(int i=0;i<arrLength;i+=8){ // here if you see we are incrementing the length by 8 because a sterio sound has both left and right channels 4 bytes for left +4 bytes for right.
        // read left and right channel values for the first song
        float source1_L=song1ByteArray.readFloat();// I'm not sure if readFloat() function exists in android but there will be an equivalant one.
        float source1_R=song1ByteArray.readFloat(); 
        float source2_L=0;
        float source2_R=0;
        if(song2ByteArray.bytesAvailable>0){
            source2_L=song1ByteArray.readFloat();//left channel of audio song2ByteArray
            source2_R=song1ByteArray.readFloat(); //right channel of audio song2ByteArray
        }
        returnResultArr.writeFloat((source_1_L+source_2_L)/2); // average value of the source 1 and 2 left channel
        returnResultArr.writeFloat((source_1_R+source_2_R)/2); // average value of the source 1 and 2 right channel
    }
    return returnResultArr;
    }
    
于 2015-10-19T11:13:25.540 回答
0

这家伙在与您的项目非常相似的项目中使用了 JLayer 库。他还为您提供了有关如何将该库集成到您的 android 应用程序中直接重新编译 jar 的指南。

解释他的代码很容易完成你的任务:

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[] 并评估什么是最佳衰减以避免削波。

于 2015-10-21T09:32:16.023 回答
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 android wrapper使用这里解释的语法和方法

于 2015-10-20T23:51:26.673 回答
0

要合并(重叠)两个声音文件,您可以使用This FFMPEG library

这是文档

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

-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-如果它是资产,那么它应该是一个子文件夹file:///android_asset/

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

String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/File Name.mp3"
于 2017-10-10T23:10:18.693 回答
-1

我没有得到任何好的解决方案。但我们可以在这里做一些技巧.. :) 您可以将两个 mp3 文件分配给两个不同的 MediaPlayer 对象。然后使用按钮一次播放两个文件。比较两个 mp3 文件以找到最长持续时间。之后使用 AudioReorder 录制到该持续时间。它会解决你的问题..我知道这不是正确的方法,但希望它会帮助你.. :)

于 2015-10-16T12:30:08.130 回答