如何减小 Java 中的振幅数组大小?

How to reduce amplitudes array size in Java?

Searching the web for weeks i came to the following code for calculating the amplitudes of a given .wav file in Java . Now the problem is it doesn't scale well at all with big audio files like let's say 30 minutes , the produces array is huge.

为了绘制它,我正在使用 JavaFX created this repository(ps 那里的代码可能有所不同,因为我现在已经有几天没有提交了)。

所以:

/**                                                                                                                             
 * 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;                                                
}     

输出是这样的:

找到了一个非常好的解决方案,虽然我不确定最终数组的最大大小应该是多少,但经过一些实验后 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];                                                                                                                             
}  

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