Java - 调整 WAV 文件的播放速度

Java - Adjust playback speed of a WAV file

我可能很笨,但我似乎找不到解决我的问题的方法

(注意: 我可以找到很多人报告这个问题,似乎它是由于较新的 Java(可能是 1.5 ?)。也许 SAMPLE_RATE 不再受支持?我找不到任何解决方案)

我正在尝试调整 SAMPLE_RATE 以加快 up/slow 歌曲的播放速度。我可以毫无问题地成功播放 .wav 文件,所以我查看了用于调整音量的 FloatControl:

public void adjustVolume(String audioType, float gain) {
        FloatControl gainControl = null;

        gainControl = (FloatControl) clipSFX.getControl(FloatControl.Type.MASTER_GAIN);
                if(gain > MAX_VOLUME)
                    gain = MAX_VOLUME;
                if(gain < MIN_VOLUME)
                    gain = MIN_VOLUME;

            //set volume
            gainControl.setValue(gain);
    }

但是当试图将这个原则转化为 SAMPLE_RATE 时,我很早就在这个阶段得到了一个错误:

    public void adjustVolume(String audioType, float gain) {
        FloatControl gainControl = null;

        gainControl = (FloatControl) clipSFX.getControl(FloatControl.Type.SAMPLE_RATE);
        //ERROR: Exception in thread "Thread-3" java.lang.IllegalArgumentException: Unsupported control type: Sample Rate

        //I haven't gotten this far yet since the above breaks, but in theory will then set value?
            gainControl.setValue(gain);
}

我在网上找到的所有内容似乎都与从麦克风或某些外部线路获取输入有关,而且似乎没有转换为使用音频文件,所以我不确定我遗漏了什么。任何帮助,将不胜感激!谢谢!

这里我们有一个改变速度的方法——通过加倍采样率。基本上步骤如下:

  • 打开文件的音频流
  • 获取格式
  • 创建一个更改了采样率的新格式
  • 打开该格式的数据行
  • 从 file/audio 流中读取并在线播放

这里的概念是SourceDataLine、AudioFormat和AudioInputStream。如果您查看 javax.sound 教程,您会发现它们,甚至是 类 的页面。您现在可以创建自己的方法(如 adjust(factor)),它只获取新格式而其他所有内容保持不变。

  public void play() {
    try {
      File fileIn = new File(" ....);
      AudioInputStream audioInputStream=AudioSystem.getAudioInputStream(fileIn);
      AudioFormat formatIn=audioInputStream.getFormat();
      AudioFormat format=new AudioFormat(formatIn.getSampleRate()*2, formatIn.getSampleSizeInBits(), formatIn.getChannels(), true, formatIn.isBigEndian());
          System.out.println(formatIn.toString());
          System.out.println(format.toString());
      byte[] data=new byte[1024];
      DataLine.Info dinfo=new DataLine.Info(SourceDataLine.class, format);
      SourceDataLine line=(SourceDataLine)AudioSystem.getLine(dinfo);
      if(line!=null) {
        line.open(format);
        line.start();
        while(true) {
          int k=audioInputStream.read(data, 0, data.length);
          if(k<0) break;
          line.write(data, 0, k);
        }
        line.stop();
        line.close();
      }
    }
    catch(Exception ex) { ex.printStackTrace(); }
  }

也可以在播放音频数据时使用线性插值来改变速度。

音频值排列在一个数组中,光标通常从一个值移动到另一个值。但是您可以设置任意数量的进度,例如 1.5 帧,并在需要时创建加权值。

假设数据如下:

  1. 0.5
  2. 0.8
  3. 0.2
  4. -0.1
  5. -0.5
  6. -0.7

您的播放数据(对于 1.5 速率)将是

  1. 0.5
  2. (0.8 + 0.2)/2
  3. -0.1
  4. (-0.5 + -0.7)/2

我知道之前在 Stack Overflow 上有帖子更全面地解释了这个算法。原谅我没有追踪到他们。

我使用此方法允许在以下开源库中播放 .wav 时实时改变速度:AudioCue。随意检查代码并利用其中的想法。

以下是从位于两个音频帧之间的点创建一对立体声音频值的方法(数据是带符号的浮点数,范围从 -1 到 1)。它来自 AudioCue.java 中的内部 class AudioCuePlayer。可能不是最容易阅读的。正在读取的声音数据在数组 cue 中,而 idx 是当前正在通过此数组进行的 "play head" 位置。 'intIndex'是音频帧,'flatIndex'是帧在数组中的实际位置。我使用 frames 来跟踪播放头的位置并计算插值权重,然后使用 flatIndex 从数组中获取相应的值。

private float[] readFractionalFrame(float[] audioVals, float idx)
{
    final int intIndex = (int) idx;
    final int flatIndex = intIndex * 2;

    audioVals[0] = cue[flatIndex + 2] * (idx - intIndex) 
            + cue[flatIndex] * ((intIndex + 1) - idx);

    audioVals[1] = cue[flatIndex + 3] * (idx - intIndex) 
            + cue[flatIndex + 1] * ((intIndex + 1) - idx);

    return audioVals;
}

如果有问题,我很乐意澄清。