【问题标题】:How to reduce amplitudes array size in Java?如何减少 Java 中的振幅数组大小?
【发布时间】:2018-08-16 16:16:48
【问题描述】:

在网上搜索了数周后,我找到了以下代码 在 Java 中计算给定 .wav 文件的幅度。现在 问题是它根本不能很好地扩展大音频文件,比如 假设 30 分钟,produces 数组很大。

为了绘制它,我使用的是 JavaFX created this repository(由于我现在有几天没有提交代码,所以那里的代码可能会有所不同)。

所以:

/**                                                                                                                             
 * Get Wav Amplitudes                                                                                                           
 *                                                                                                                              
 * @param file                                                                                                                  
 * @return                                                                                                                      
 * @throws UnsupportedAudioFileException                                                                                        
 * @throws IOException                                                                                                          
 */                                                                                                                             
private int[] getWavAmplitudes(File file) throws UnsupportedAudioFileException , IOException {                                  
    System.out.println("Calculting WAV amplitudes");                                                                            
    int[] amplitudes = null;                                                                                                    

    //Get Audio input stream                                                                                                    
    try (AudioInputStream input = AudioSystem.getAudioInputStream(file)) {                                                      
        AudioFormat baseFormat = input.getFormat();                                                                             

        Encoding encoding = AudioFormat.Encoding.PCM_UNSIGNED;                                                                  
        float sampleRate = baseFormat.getSampleRate();                                                                          
        int numChannels = baseFormat.getChannels();                                                                             

        AudioFormat decodedFormat = new AudioFormat(encoding, sampleRate, 16, numChannels, numChannels * 2, sampleRate, false); 
        int available = input.available();                                                                                      
        amplitudes = new int[available];                                                                                        

        //Get the PCM Decoded Audio Input Stream                                                                                
        try (AudioInputStream pcmDecodedInput = AudioSystem.getAudioInputStream(decodedFormat, input)) {                        
            final int BUFFER_SIZE = 4096; //this is actually bytes                                                              
            System.out.println(available);                                                                                      

            //Create a buffer                                                                                                   
            byte[] buffer = new byte[BUFFER_SIZE];                                                                              

            //Read all the available data on chunks                                                                             
            int counter = 0;                                                                                                    
            while (pcmDecodedInput.readNBytes(buffer, 0, BUFFER_SIZE) > 0)                                                      
                for (int i = 0; i < buffer.length - 1; i += 2, counter += 2) {                                                  
                    if (counter == available)                                                                                   
                        break;                                                                                                  
                    amplitudes[counter] = ( ( buffer[i + 1] << 8 ) | buffer[i] & 0xff ) << 16;                                  
                    amplitudes[counter] /= 32767;                                                                               
                    amplitudes[counter] *= WAVEFORM_HEIGHT_COEFFICIENT;                                                         
                }                                                                                                               
        } catch (Exception ex) {                                                                                                
            ex.printStackTrace();                                                                                               
        }                                                                                                                       
    } catch (Exception ex) {                                                                                                    
        ex.printStackTrace();                                                                                                   
    }                                                                                                                           

    //System.out.println("Finished Calculting amplitudes");                                                                     
    return amplitudes;                                                                                                          
}   

然后我像这样处理振幅:

/**                                                                 
 * Process the amplitudes                                           
 *                                                                  
 * @param sourcePcmData                                             
 * @return An array with amplitudes                                 
 */                                                                 
private float[] processAmplitudes(int[] sourcePcmData) {            
    System.out.println("Processing WAV amplitudes");                

    //The width of the resulting waveform panel                     
    int width = waveVisualization.width;                            
    System.out.println("P Width :" + width);                        
    float[] waveData = new float[width];                            
    int samplesPerPixel = sourcePcmData.length / width;             

    //Calculate                                                     
    float nValue;                                                   
    for (int w = 0; w < width; w++) {                               
        //if (isCancelled())                                        
        //  break;                                                  

        //For performance keep it here                              
        int c = w * samplesPerPixel;                                
        nValue = 0.0f;                                              

        //Keep going                                                
        for (int s = 0; s < samplesPerPixel; s++) {                 
            //if (isCancelled())                                    
            //  break;                                              
            nValue += ( Math.abs(sourcePcmData[c + s]) / 65536.0f );
        }                                                           

        //Set WaveData                                              
        waveData[w] = nValue / samplesPerPixel;                     
    }                                                               

    System.out.println("Finished Processing amplitudes");           
    return waveData;                                                
}     

输出是这样的:

【问题讨论】:

  • 对它们进行采样。取每个 X 的平均值。或最大值。或敏。或者介于两者之间。用户不需要也无法处理每个样本的幅度 - 所以对样本进行采样。
  • @BoristheSpider 请提供代码示例。我对此很陌生。添加答案:)
  • 您的代码已经通过计算有多少像素并进行平均来进行聚合 - 只需将该代码移入并使您的数组大小与现在的 waveData 相同。我认为你理解你的代码并自己修改它会比我为你做的提供更多的信息。
  • @BoristheSpider 嗨,我已经添加了一个答案,我尽力做到最好,内存中没有保存巨大的数组,最终的振幅数组非常小。只有一件事(我不知道振幅数组的最佳最终尺寸应该是什么,所以我将其保留为 100.000)我是否也应该按照您所说的那样实现最小值和最大值?在这里需要一个意见我迷路了:)
  • 看起来是正确的方法。对于子数组的大小 - 为什么不将您需要多少样本以及从中计算数组长度的方法传递给该方法 - 就像您要使用像素数一样?在最小值/最大值上,您需要决定使用哪些汇总统计信息 - 您使用的是算术平均值,如果它适合您,请保留它!

标签: java audio javafx wav amplitude


【解决方案1】:

找到了一个非常好的解决方案,虽然我不确定最终数组的最大大小应该是多少,但经过一些实验100.000 似乎是一个不错的数字。

所有代码都在this github 仓库中。

所以 getWavAmplitudes 方法变为:

/**                                                                                                                                                
 * Get Wav Amplitudes                                                                                                                              
 *                                                                                                                                                 
 * @param file                                                                                                                                     
 * @return                                                                                                                                         
 * @throws UnsupportedAudioFileException                                                                                                           
 * @throws IOException                                                                                                                             
 */                                                                                                                                                
private int[] getWavAmplitudes(File file) throws UnsupportedAudioFileException , IOException {                                                     

    //Get Audio input stream                                                                                                                       
    try (AudioInputStream input = AudioSystem.getAudioInputStream(file)) {                                                                         
        AudioFormat baseFormat = input.getFormat();                                                                                                

        //Encoding                                                                                                                                 
        Encoding encoding = AudioFormat.Encoding.PCM_UNSIGNED;                                                                                     
        float sampleRate = baseFormat.getSampleRate();                                                                                             
        int numChannels = baseFormat.getChannels();                                                                                                

        AudioFormat decodedFormat = new AudioFormat(encoding, sampleRate, 16, numChannels, numChannels * 2, sampleRate, false);                    
        int available = input.available();                                                                                                         

        //Get the PCM Decoded Audio Input Stream                                                                                                   
        try (AudioInputStream pcmDecodedInput = AudioSystem.getAudioInputStream(decodedFormat, input)) {                                           
            final int BUFFER_SIZE = 4096; //this is actually bytes                                                                                 

            //Create a buffer                                                                                                                      
            byte[] buffer = new byte[BUFFER_SIZE];                                                                                                 

            //Now get the average to a smaller array                                                                                               
            int maximumArrayLength = 100000;                                                                                                       
            int[] finalAmplitudes = new int[maximumArrayLength];                                                                                   
            int samplesPerPixel = available / maximumArrayLength;                                                                                  

            //Variables to calculate finalAmplitudes array                                                                                         
            int currentSampleCounter = 0;                                                                                                          
            int arrayCellPosition = 0;                                                                                                             
            float currentCellValue = 0.0f;                                                                                                         

            //Variables for the loop                                                                                                               
            int arrayCellValue = 0;                                                                                                                

            //Read all the available data on chunks                                                                                                
            while (pcmDecodedInput.readNBytes(buffer, 0, BUFFER_SIZE) > 0)                                                                         
                for (int i = 0; i < buffer.length - 1; i += 2) {                                                                                   

                    //Calculate the value                                                                                                          
                    arrayCellValue = (int) ( ( ( ( ( buffer[i + 1] << 8 ) | buffer[i] & 0xff ) << 16 ) / 32767 ) * WAVEFORM_HEIGHT_COEFFICIENT );  

                    //Every time you him [currentSampleCounter=samplesPerPixel]                                                                    
                    if (currentSampleCounter != samplesPerPixel) {                                                                                 
                        ++currentSampleCounter;                                                                                                    
                        currentCellValue += Math.abs(arrayCellValue);                                                                              
                    } else {                                                                                                                       
                        //Avoid ArrayIndexOutOfBoundsException                                                                                     
                        if (arrayCellPosition != maximumArrayLength)                                                                               
                            finalAmplitudes[arrayCellPosition] = finalAmplitudes[arrayCellPosition + 1] = (int) currentCellValue / samplesPerPixel;

                        //Fix the variables                                                                                                        
                        currentSampleCounter = 0;                                                                                                  
                        currentCellValue = 0;                                                                                                      
                        arrayCellPosition += 2;                                                                                                    
                    }                                                                                                                              
                }                                                                                                                                  

            return finalAmplitudes;                                                                                                                
        } catch (Exception ex) {                                                                                                                   
            ex.printStackTrace();                                                                                                                  
        }                                                                                                                                          
    } catch (Exception ex) {                                                                                                                       
        ex.printStackTrace();                                                                                                                      

    }                                                                                                                                              

    //You don't want this to reach here...                                                                                                         
    return new int[1];                                                                                                                             
}  

非常欢迎任何建议和改进。

【讨论】:

  • 如果你不处理异常,然后返回废话,为什么要捕获它们呢?只需删除 catch 块...
  • @BoristheSpider 我会解决的。我还可以进一步改进它(速度、精度等):) 吗?
猜你喜欢
  • 2021-08-03
  • 2015-09-01
  • 2014-09-11
  • 2017-08-21
  • 2016-04-25
  • 1970-01-01
  • 2023-04-01
  • 2011-08-03
相关资源
最近更新 更多