You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android平台使用JTransform库实现PCM数据频谱柱状图可视化咨询

Got it, let's walk through how to turn your PCM data into frequency band data for that bar chart using JTransforms—this is exactly the kind of thing I’ve tinkered with for audio visualizers before. Here's a step-by-step breakdown with code examples to make it concrete:

Step 1: Set Up JTransforms

First, you need to get the library into your project. If you're using Maven, add this dependency to your pom.xml:

<dependency>
    <groupId>com.github.wendykierp</groupId>
    <artifactId>JTransforms</artifactId>
    <version>3.1</version>
</dependency>

If you're not using a build tool, just download the JAR from the Maven repository and add it to your project's classpath.

Step 2: Prepare Your PCM Data

PCM data is usually stored as a byte array (16-bit signed is the most common format). JTransforms' FFT works with double arrays, so you’ll need to convert and normalize your PCM samples:

  • For 16-bit PCM, each sample is 2 bytes. Combine them into a signed short, then normalize to the range [-1.0, 1.0] by dividing by 32768.0 (the max value of a 16-bit signed short).

Here’s a helper method for this conversion:

public static double[] pcmBytesToDoubles(byte[] pcmBytes) {
    int sampleCount = pcmBytes.length / 2;
    double[] normalizedSamples = new double[sampleCount];
    
    for (int i = 0; i < sampleCount; i++) {
        // Combine two bytes into a signed short (little-endian, adjust if your PCM is big-endian)
        short rawSample = (short) ((pcmBytes[2*i] & 0xFF) | (pcmBytes[2*i+1] << 8));
        // Normalize to [-1.0, 1.0]
        normalizedSamples[i] = rawSample / 32768.0;
    }
    
    return normalizedSamples;
}
Step 3: Run the FFT

JTransforms uses a DoubleFFT_1D class for 1-dimensional FFTs. You’ll need to:

  1. Choose a sample window size (common choices are 1024, 2048, or 4096—bigger sizes give better frequency resolution but slower updates).
  2. Convert your normalized PCM samples into a complex array (JTransforms expects real and imaginary parts interleaved: [real0, imag0, real1, imag1, ...]).
  3. Run the forward FFT.

Example code:

// Configuration (adjust these to match your audio setup)
int sampleWindowSize = 4096;
double sampleRate = 44100; // e.g., 44.1kHz for CD-quality audio

// Get your normalized PCM data (make sure it's exactly sampleWindowSize long—pad or truncate if needed)
double[] pcmSamples = pcmBytesToDoubles(yourPcmByteArray);

// Initialize FFT transformer
DoubleFFT_1D fft = new DoubleFFT_1D(sampleWindowSize);

// Prepare complex input array (imaginary parts start at 0)
double[] complexInput = new double[2 * sampleWindowSize];
System.arraycopy(pcmSamples, 0, complexInput, 0, sampleWindowSize);

// Execute FFT
fft.complexForward(complexInput);
Step 4: Calculate Frequency Bin Magnitudes

The FFT output gives you complex numbers for each frequency bin. To get a readable amplitude value, calculate the magnitude of each complex number (sqrt(real² + imag²)). Also, since FFT results are symmetric, you only need the first half of the bins (the second half is the mirror of the first).

Optional: Convert magnitudes to decibels (dB) for a more natural visualization (this matches how human hearing perceives loudness):

double[] magnitudes = new double[sampleWindowSize / 2];
double[] dbMagnitudes = new double[magnitudes.length];

for (int i = 0; i < sampleWindowSize / 2; i++) {
    double real = complexInput[2 * i];
    double imag = complexInput[2 * i + 1];
    
    // Calculate raw magnitude
    magnitudes[i] = Math.sqrt(real * real + imag * imag);
    
    // Convert to dB (normalize by window size to account for FFT scaling)
    dbMagnitudes[i] = 20 * Math.log10(magnitudes[i] / sampleWindowSize);
    // Clip very quiet values to avoid -infinity (adjust -60 to your needs)
    if (dbMagnitudes[i] < -60) {
        dbMagnitudes[i] = -60;
    }
}
Step 5: Group Bins into Frequency Bands

A bar chart usually doesn’t show every single frequency bin—you’ll want to group bins into logical bands (like octave bands or custom ranges). For example, you might use bands like 20-60Hz, 60-200Hz, up to 6kHz-20kHz.

Here’s how to calculate the average magnitude for each band:

// Define your desired frequency bands (customize these to match your chart)
String[] bandLabels = {"20-60Hz", "60-200Hz", "200-600Hz", "600-2kHz", "2kHz-6kHz", "6kHz-20kHz"};
double[] bandEdges = {20, 60, 200, 600, 2000, 6000, 20000};

// Calculate Hz per bin (frequency resolution)
double binFrequencyStep = sampleRate / sampleWindowSize;

double[] bandAmplitudes = new double[bandLabels.length];

for (int bandIdx = 0; bandIdx < bandLabels.length; bandIdx++) {
    double startFreq = bandEdges[bandIdx];
    double endFreq = bandEdges[bandIdx + 1];
    
    // Find the first and last bin in this band
    int startBin = (int) Math.floor(startFreq / binFrequencyStep);
    int endBin = (int) Math.ceil(endFreq / binFrequencyStep);
    
    // Clamp bins to valid range (0 to sampleWindowSize/2 - 1)
    startBin = Math.max(0, startBin);
    endBin = Math.min(magnitudes.length - 1, endBin);
    
    // Calculate average amplitude for the band
    double total = 0;
    int binCount = 0;
    for (int bin = startBin; bin <= endBin; bin++) {
        total += dbMagnitudes[bin]; // Use magnitudes if you don't want dB
        binCount++;
    }
    
    bandAmplitudes[bandIdx] = binCount > 0 ? total / binCount : 0;
}
Step 6: Plot Your Bar Chart

Now you have two arrays ready for plotting:

  • bandLabels: The x-axis labels for your bars (e.g., "20-60Hz")
  • bandAmplitudes: The y-axis values (dB or raw magnitude) for each bar

You can use any Java plotting library (JavaFX Charts, Swing's JFreeChart, etc.) to render these into a bar chart. For example, with JavaFX:

// Quick JavaFX example (simplified)
CategoryAxis xAxis = new CategoryAxis();
xAxis.setCategories(FXCollections.observableArrayList(bandLabels));

NumberAxis yAxis = new NumberAxis();
yAxis.setLabel("Amplitude (dB)");

BarChart<String, Number> barChart = new BarChart<>(xAxis, yAxis);
XYChart.Series<String, Number> series = new XYChart.Series<>();
series.setName("Frequency Spectrum");

for (int i = 0; i < bandLabels.length; i++) {
    series.getData().add(new XYChart.Data<>(bandLabels[i], bandAmplitudes[i]));
}

barChart.getData().add(series);

A few quick tips to refine your visualization:

  • Use a window function (like Hann) on your PCM samples before FFT to reduce spectral leakage. JTransforms has built-in support for this, or you can apply it manually.
  • Adjust the sample window size: bigger windows give more precise frequency data but slower updates; smaller windows are more responsive but less detailed.
  • Normalize the y-axis so your bars don’t go off-screen (e.g., set the range from -60dB to 0dB if using dB).

内容的提问来源于stack exchange,提问作者Robin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:36:53