如何减小 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];
}
非常欢迎任何建议和改进。
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];
}
非常欢迎任何建议和改进。