使用下面java中的代码修改文件中特定位置的音频文件的音高

To modify the pitch of an audio file at specific positions in the files using the codes in java below

我想根据用户在不同时刻的输入动态修改音频片段的音高,比如说如果用户在 10 秒后输入更改音频的音高,我该如何实现?

我发现这个 link 描述了如何修改音高,但我想在音频剪辑的不同时刻重复这个过程,并且只持续很短的时间。有人可以指导我吗?

一些编辑

编辑 1

我找到了这段代码,正如我之前提到的那样

//source file
final File file1 = new File(“Sample.mp3”);
//destination file
final File file2 = new File(“Sample_cat.wav”);
//audio stream of file1
final AudioInputStream in1 = getAudioInputStream(file1);
//get audio format for targetted sound
final AudioFormat inFormat = getOutFormat(in1.getFormat());
//change the frequency of Audio format
private AudioFormat getOutFormat(AudioFormat inFormat) {
        int ch = inFormat.getChannels();
        float rate = inFormat.getSampleRate();    
        return new AudioFormat(PCM_SIGNED, 72000, 16, ch, ch * 2, rate,
                inFormat.isBigEndian());
    }
//get the target file audio stream using file format
final AudioInputStream in2 = getAudioInputStream(inFormat, in1);
//write the audio file in targeted pitch file
AudioSystem.write(in2, AudioFileFormat.Type.WAVE, file2);

编辑 2 我找到了另一个代码,它设置了音频文件从你想要开始和停止音频的位置。

        File audioFile = new File(audioFilePath);


        AudioInputStream audioStream = AudioSystem.getAudioInputStream(audioFile);

        AudioFormat format = audioStream.getFormat();

        DataLine.Info info = new DataLine.Info(Clip.class, format);

        Clip audioClip = (Clip) AudioSystem.getLine(info); 
        audioClip.open(audioStream);
        audioClip.setLoopPoints(10_000, 500_000);
        audioClip.loop(1);

现在,如何使用 Edit 1[=43] 中的代码更改 Edit 2 中设置的持续时间的音高,即 10 ms 到 50 ms =]

如果我可以用 Java 以外的任何其他方式做同样的事情,有人能建议我吗?那么欢迎提出建议... 请帮忙。我是新来的。

**编辑 3 **

可以参考这个 link 的确切问题:

这些是我指的值(以毫秒为单位):

public static void convertMsgToAudio(String msg){

        int len = msg.length();
        duration = new double[len];
        msg = msg.toUpperCase();
        System.out.println("Msg 2 : " + msg);

        int i;
        //char ch;
        for(i=0;i<msg.length();i++){

            if(msg.charAt(i) == 'A'){
                duration[i] = 50000;
            }
            else if (msg.charAt(i) == 'B'){
                duration[i] = 100000;
            }
            else if (msg.charAt(i) == 'C'){
                duration[i] = 150000;
            }
            else if (msg.charAt(i) == 'D'){
                duration[i] = 200000;               
            }
            else if (msg.charAt(i) == 'E'){
                duration[i] = 250000;
            }
            else if (msg.charAt(i) == 'F'){
                duration[i] = 300000;
            }
            else if (msg.charAt(i) == 'G'){
                duration[i] = 350000;
            }
            else if (msg.charAt(i) == 'H'){
                duration[i] = 400000;
            }
            else if (msg.charAt(i) == 'I'){
                duration[i] = 450000;
            }
            else if (msg.charAt(i) == 'J'){
                duration[i] = 500000;
            }
            else if (msg.charAt(i) == 'K'){
                duration[i] = 550000;
            }
            else if (msg.charAt(i) == 'L'){
                duration[i] = 600000;
            }
            else if (msg.charAt(i) == 'M'){
                duration[i] = 650000;
            }
            else if (msg.charAt(i) == 'N'){
                duration[i] = 700000;
            }
            else if (msg.charAt(i) == 'O'){
                duration[i] = 750000;
            }
            else if (msg.charAt(i) == 'P'){
                duration[i] = 800000;
            }
            else if (msg.charAt(i) == 'Q'){
                duration[i] = 850000;
            }
            else if (msg.charAt(i) == 'R'){
                duration[i] = 900000;
            }
            else if (msg.charAt(i) == 'S'){
                duration[i] = 950000;
            }
            else if (msg.charAt(i) == 'T'){
                duration[i] = 1000000;
            }
            else if (msg.charAt(i) == 'U'){
                duration[i] = 1100000;
            }
            else if (msg.charAt(i) == 'V'){
                duration[i] = 1200000;
            }
            else if (msg.charAt(i) == 'W'){
                duration[i] = 1300000;
            }
            else if (msg.charAt(i) == 'X'){
                duration[i] = 1400000;
            }
            else if (msg.charAt(i) == 'Y'){
                duration[i] = 1500000;
            }
            else if (msg.charAt(i) == 'Z'){
                duration[i] = 1600000;
            }

        }

    }
据我所知,

Java 不会公开剪辑中的数据进行编辑。

我从来没有尝试通过扰乱采样率来改变音高。也许这是一个好方法。 Java 教程中有一节介绍了 wav 文件格式的更改:Using Files and Format Converters。这似乎是很好的背景信息,甚至可能涵盖您正在尝试的解决方案。

这是我所做的,称之为 VarispeedWavPlayer。

(1) 有一个 volatile 实例浮点变量,它是一个速度因子(1 是相同的速度,1.1 是 110%,0.5 是一半的速度,等等

(2) 有一个浮点数 运行 'tapehead'

(3) 从普通代码开始,用于从 AudioInputStream 读取并输出到 SourceDataLine(上面 Java 教程 link 中的 "Reading Sound Files" 中的好例子 link.

(4)在评论区

// Here, do something useful with the audio data that's 
// now in the audioBytes array...

(a) 将传入字节转换为 PCM 数据。

使用 16 位编码、立体声、little-endian ("CD quality") 执行此操作的示例。这使用 1024 字节的读取缓冲区大小,它转换为 256 帧(记住,有两个轨道,左和右)的短数据,范围从 -32767 到 32767(或者可能是 32768,我不记得那个细节在那一刻)。

while((bytesRead = audioInputStream.read(rawByteBuffer, 0, 1024)) != -1)
{
    for (int i = 0, n = bytesRead / 2); i < n; i ++)
    {
        pcmBuffer[i] =  ( rawByteBuffer[i * 2] & 0xff )
                        | ( rawByteBuffer[(i * 2) + 1)] << 8 ) ;
    }
}   

以上内容已编辑清楚,可以使用一些性能优化。

(b) 获取当前 "speedfactor" 并编写循环遍历 PCM 帧值(请记住,对于立体声,该轨道的 "next" 帧为 +2):

tapehead += speedfactor;

(c) 这通常会以分数值着陆。使用线性插值计算该中间点的值。例如,如果您在 tapehead = 10.25 着陆,第 10 帧为 0.5,第 11 帧为 0.6,则您将 return 0.525。

(d) 将内插值转换回字节(与步骤 4a 相反)

(e) 累积字节并通过 SourceDataLine 将它们发送出去。

关于管理输入和输出字节缓冲区不会一对一匹配这一事实,我省略了一些细节。但是如果你掌握了基本概念,那么我想你就能解决这个问题。

注意,这只会在查询 "speedFactor" 变量时更新速度,每个输入缓冲区一次。所以你不希望输入缓冲区太大。