
JavaFX多媒體(4)Equalizer 與 Audio Spectrum
JavaFX多媒體(3)Metadata與Marker << 前情 等化器 (Equalizer) 原應用於通訊傳輸,由於信號經傳送路徑到接收器的過程中,常會受到干擾與遮蔽物阻擋而造成遮蔽效應,此效應將造成訊號錯誤,為了降低傳送通訊的錯誤率,因而對通訊兩端做估測,經由估測結果對通訊兩端做頻率補償以降低傳送錯誤率。現已運用於聲音處理,將聲音優化,以調整聲音適合某類曲風,例如搖滾樂、流行樂等。 等化器由數個不同頻率的音訊頻譜 (Audio Spectrum) 所組成,可使用MediaPlayer類別以下的方法處理頻譜:
請參考以下範例,示範如何處理頻譜與等化器,範例首先以自訂之Equalizer類別建立等化器,其中各頻譜以不同的顏色處理,範例以javafx.scene.paint.Stop類別設定各頻譜比率的顏色: public class Equalizer extends VBox { int maxValue; int barCount; double lastWidth = 0.0; double lastHeight = 0.0; public Equalizer(int maxValue, int barCount) { this.maxValue = maxValue; this.barCount = barCount; setSpacing(1.0); setAlignment(Pos.BOTTOM_CENTER); setStyle("-fx-background-color: black; -fx-padding: 5;"); Stop[] stop = new Stop[5]; stop[0] = new Stop(0.2, Color.RED); stop[1] = new Stop(0.4, Color.ORANGE); stop[2] = new Stop(0.6, Color.YELLOW); stop[3] = new Stop(0.8, Color.LIGHTGREEN); stop[4] = new Stop(0.9, Color.GREEN); for (int i = 0; i < barCount; i++) { int color = (int)((double)i / (double)barCount * 255.0); Rectangle rectangle = new Rectangle(); rectangle.setVisible(false); rectangle.setFill(Utils.ladder( Color.rgb(color, color, color), stop)); getChildren().add(rectangle); } } public void setValue(double value) { int barsLit = Math.min( barCount, (int)Math.round(value/maxValue*barCount)); ObservableList lists = getChildren(); for (int i = 0; i < lists.size(); i++) { lists.get(i).setVisible(i > barCount - barsLit); } } ... } 接著由250 Hz開始分別建立七條等化器: GridPane gridpane = new GridPane(); final Equalizer[] equalizer = new Equalizer[7]; Reflection reflection = new Reflection(); reflection.setFraction(1.0); for (int i = 0; i < equalizer.length; i++) { equalizer[i] = new Equalizer(100, 20); equalizer[i].setMaxWidth(50); equalizer[i].setMaxHeight(200); equalizer[i].setEffect(reflection); GridPane.setHalignment(equalizer[i], HPos.CENTER); gridpane.add(equalizer[i], i, 0); } gridpane.setTranslateX(120.0); // 取得音訊頻譜的閾值 final double minValue = mediaplayer.getAudioSpectrumThreshold(); final double[] norms = new double[equalizer.length]; double currentNorm = 0.05; for (int i = 0; i < norms.length; i++) { norms[i] = 1 + currentNorm; currentNorm *= 2; } // 取得音訊頻譜的頻帶數量 int bandCount = mediaplayer.getAudioSpectrumNumBands(); final int[] counts = new int[equalizer.length]; double startFreq = 250.0; double bandwidth = 22050.0 / bandCount; double currentSpectrumFreq = bandwidth / 2.0; double currentEQFreq = startFreq / 2.0; double currentCutoff = 0.0; int currentBucketIndex = -1; for (int i = 0; i < bandCount; i++) { if (currentSpectrumFreq > currentCutoff) { currentEQFreq *= 2; currentCutoff = currentEQFreq + currentEQFreq / 2; ++currentBucketIndex; if (currentBucketIndex == counts.length) { break; } } ++counts[currentBucketIndex]; currentSpectrumFreq += bandwidth; } 此外,並以setAudioSpectrumListener()方法設定音訊頻譜的事件Listener,當音訊頻譜改變時,則更新頻譜比率長度: // 設定音訊頻譜的事件Listener mediaplayer.setAudioSpectrumListener(new AudioSpectrumListener() { @Override public void spectrumDataUpdate(double timestamp, double duration, float[] magnitudes, float[] phases) { int index = 0; int bucketIndex = 0; int currentBucketCount = 0; double sum = 0.0; while (index < magnitudes.length) { sum += magnitudes[index] - minValue; ++currentBucketCount; if (currentBucketCount >= counts[bucketIndex]) { equalizer[bucketIndex].setValue(sum / norms[bucketIndex]); currentBucketCount = 0; sum = 0.0; ++bucketIndex; } ++index; } } }); 【執行結果】
【參考資料】 [1] Java Official Web Site |