JavaFX多媒體(4)Equalizer 與 Audio Spectrum by 黃嘉輝 | CodeData
top

JavaFX多媒體(4)Equalizer 與 Audio Spectrum

分享:

JavaFX多媒體(3)Metadata與Marker << 前情

等化器 (Equalizer) 原應用於通訊傳輸,由於信號經傳送路徑到接收器的過程中,常會受到干擾與遮蔽物阻擋而造成遮蔽效應,此效應將造成訊號錯誤,為了降低傳送通訊的錯誤率,因而對通訊兩端做估測,經由估測結果對通訊兩端做頻率補償以降低傳送錯誤率。現已運用於聲音處理,將聲音優化,以調整聲音適合某類曲風,例如搖滾樂、流行樂等。

等化器由數個不同頻率的音訊頻譜 (Audio Spectrum) 所組成,可使用MediaPlayer類別以下的方法處理頻譜:

  • getAudioSpectrumInterval():取得音訊頻譜的間隔,單位為秒。
  • setAudioSpectrumInterval():設定音訊頻譜的間隔。
  • getAudioSpectrumNumBands():取得音訊頻譜的頻帶 (Band) 數量。
  • setAudioSpectrumNumBands():設定音訊頻譜的頻帶數量,其中參數value須大於或等於2。
  • getAudioSpectrumThreshold():取得音訊頻譜的閾值 (Threshold),單位為分貝。
  • setAudioSpectrumThreshold():設定音訊頻譜的閾值,其中參數value須小於或等於0。
  • getAudioSpectrumListener():取得音訊頻譜的事件Listener。
  • setAudioSpectrumListener():設定音訊頻譜的事件Listener。

請參考以下範例,示範如何處理頻譜與等化器,範例首先以自訂之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;
    }
  }
});
【執行結果】
18-10

【參考資料】

[1] Java Official Web Site
[2] JavaFX
[3] NetBeans
[4] JavaFX 2.2 API Specification.
[5] Java Platform, Standard Edition 7 API Specification.

後續 >> JavaFX事件(1)EventHandler處理架構

分享:
按讚!加入 CodeData Facebook 粉絲群

相關文章

留言

留言請先。還沒帳號註冊也可以使用FacebookGoogle+登錄留言

關於作者

黃嘉輝副教授,目前任職於國立臺北商業大學企業管理學系,喜歡寫程式,特別愛Java,範例可參考教學網站

熱門論壇文章

熱門技術文章