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

Posted

技术标签:

【中文标题】使用以下 java 中的代码修改文件中特定位置的音频文件的音高【英文标题】:To modify the pitch of an audio file at specific positions in the files using the codes in java below 【发布时间】:2015-03-18 06:20:57 【问题描述】:

我想根据用户在不同时刻的输入动态修改音频剪辑的音高,比如如果用户输入在 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

中的代码更改 Edit 2 中设置的持续时间的音高,即 10 毫秒至 50 毫秒

如果我可以用除 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 之外的任何其他方式做同样的事情,谁能建议我?那么欢迎提出建议... 【参考方案1】:

据我所知,Java 不会公开 Clip 中的数据进行编辑。

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

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

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

(2) 有一个浮点数,它将是一个正在运行的“磁带头”

(3) 从从 AudioInputStream 读取并输出到 SourceDataLine 的普通代码开始(上述 Java 教程链接中“读取声音文件”中的好示例。

(4)在有评论的地方

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

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

如何做到这一点的示例,使用 16 位编码、立体声、little-endian(“CD 质量”)。这使用大小为 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) 获取当前的“速度因子”并编写一个循环遍历 PCM 帧值(请记住,对于立体声,该轨道的“下一个”帧是 +2):

tapehead += speedfactor;

(c) 这通常会落在一个小数值上。使用线性插值计算该中间点的值。例如,如果您降落在磁带头 = 10.25,第 10 帧为 0.5,第 11 帧为 0.6,您将返回 0.525。

(d) 将插值转换回字节(步骤 4a 的逆向)

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

在管理输入和输出字节缓冲区不会一对一匹配这一事实方面,我遗漏了一些细节。但是如果你掌握了基本概念,那么我认为你将能够解决这个问题。

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

【讨论】:

感谢您的回复 :) 但我对音频没有很好的掌握.. 特别是我不明白如何将传入的数据转换为 PCM 数据.. 和后记......所以如果你能提供给我这个代码而不是伪代码,那将是一个很大的帮助......因为我的时间不多了,我不太擅长java ..但别无选择.. *** 已经解决了每个步骤。我认为,如果您搜索它们,您将获得一些成功。我的时间也很短。可能您的准备工作比您准备的要多一些,因为这确实需要的不仅仅是 Java 编码初学者的技能水平。 感谢您的建议,我会在这方面做一些事情......你能告诉我一件事吗......我已经在***.com/questions/29138169/… 上说明了确切的问题......你能告诉我是否您所说的方式将帮助我实现这一目标..?在此先感谢:) 取两个字节(假设 16 位编码,little-endian)并转换为从 -32767 到 32767 的 PCM,试试这行代码: int audioVal = ( buffer[i+1]

以上是关于使用以下 java 中的代码修改文件中特定位置的音频文件的音高的主要内容,如果未能解决你的问题,请参考以下文章

使用scanner类更改文本文件中的特定文本(java)

Java中查找文本中特定内容后进行修改

java io流对文件的增删改查

解析java源代码以查找评论中的特定单词,然后使用下面的代码[重复]

如何在循环中修改文本文件中的特定行?

如何强制使用 NSPredicate 从硬盘检索图像的特定顺序