音の高さを変える(1)

スポンサーリンク

今まではずっと同じ周波数の音でしたが今度は高さを変えたいと思います。今回は3秒間のサイン波のwavファイルを作ります。最初は440Hzで2秒後に1オクターブ上の880Hzになりそのまま1秒その周波数のままにします。滑らかに周波数を変化させるためサンプルを取得するときに指定する周波数を計算するクラスをまず作ります。

クラスの機能を決める

3秒間にとるサンプル数はサンプリングレートを48,000とすると144,000です。一番最初は440Hzですが2秒後の96,000番目のサンプルでは880Hzにします。あとは最後までそのままとします。

周波数の変化の目印となるポイントの情報を保持するようにします。

  • 0番目で440
  • 96000番目で880

JavaではTreeMapを使うことにします。鍵をサンプルの順序、値を周波数をします。鍵の順序を保持する連想配列なので鍵がないときはその前後の値を見て計算するようにします。

TreeMap<Integer, Double> map = new TreeMap();
map.put(0, 440.0);
map.put(96000, 880.0);

この連想配列から周波数を取得するようにします。index番目のサンプルの周波数を計算するメソッドを作ります。

public static double getFreq(TreeMap<Integer, Double> map, int index){
  if(map.containsKey(index)){
    return map.get(index);
  }

まずindexを鍵としたエントリがあればそのまま値を返します。indexが0または96000の時はこのifに入ります。
これ以外の場合はindexの前と後にあるエントリを取得します。floorが自分より前のエントリ、ceilingが後のエントリです。

Entry<Integer, Double> a = map.floorEntry(index);
Entry<Integer, Double> b = map.ceilingEntry(index);
if(a == null && b == null){
  throw new IllegalStateException("map should contain at least one entry");
}

両方ないということはありえないですし一つもエントリがないと周波数は決められないので例外を投げます。
前か後ろのどっちかがない場合にはある方の値を返すようにします。96001番目以降は後ろがないのでずっと96000番目で設定された値880が返されるはずです。

if(a == null){
  return b.getValue();
}else if(b == null){
  return a.getValue();
}

最後に両方ある場合です。この場合はindexがこの間のどのくらいにいるかを按分して返します。

  double xa = a.getKey();
  double xb = b.getKey();
  double ya = a.getValue();
  double yb = b.getValue();

  return ya + (yb - ya) * (index - xa) / (xb - xa);
}

計算できているか確認する

144000個の値を確認するのは大変なので2000個おきに一つ確認するようにしましょう。

for(int i = 0;i < 144000;i += 2000){
  System.out.println(i + ":" + getFreq(map, i));
}

以下実行結果です。予想通りに変化していると思います。

0:440.0
2000:449.1666666666667
4000:458.3333333333333
6000:467.5
8000:476.6666666666667
10000:485.8333333333333
12000:495.0
14000:504.1666666666667
16000:513.3333333333334
18000:522.5
20000:531.6666666666666
22000:540.8333333333334
24000:550.0
26000:559.1666666666666
28000:568.3333333333334
30000:577.5
32000:586.6666666666666
34000:595.8333333333334
36000:605.0
38000:614.1666666666666
40000:623.3333333333334
42000:632.5
44000:641.6666666666666
46000:650.8333333333334
48000:660.0
50000:669.1666666666666
52000:678.3333333333334
54000:687.5
56000:696.6666666666667
58000:705.8333333333333
60000:715.0
62000:724.1666666666667
64000:733.3333333333333
66000:742.5
68000:751.6666666666667
70000:760.8333333333333
72000:770.0
74000:779.1666666666667
76000:788.3333333333333
78000:797.5
80000:806.6666666666667
82000:815.8333333333333
84000:825.0
86000:834.1666666666667
88000:843.3333333333333
90000:852.5
92000:861.6666666666667
94000:870.8333333333333
96000:880.0
98000:880.0
100000:880.0
102000:880.0
104000:880.0
106000:880.0
108000:880.0
110000:880.0
112000:880.0
114000:880.0
116000:880.0
118000:880.0
120000:880.0
122000:880.0
124000:880.0
126000:880.0
128000:880.0
130000:880.0
132000:880.0
134000:880.0
136000:880.0
138000:880.0
140000:880.0
142000:880.0

コード

最後にコード全体を載せておきます。

package mocha.sound;

import java.util.Map.Entry;
import java.util.TreeMap;

public class FrequencyChangeTest {

  public static void main(String[] args) {
    TreeMap<Integer, Double> map = new TreeMap();
    map.put(0, 440.0);
    map.put(96000, 880.0);

    for (int i = 0; i < 144000; i += 2000) {
      System.out.println(i + ":" + getFreq(map, i));

    }
  }

  public static double getFreq(TreeMap<Integer, Double> map, int index) {
    if (map.containsKey(index)) {
      return map.get(index);
    }
    Entry<Integer, Double> a = map.floorEntry(index);
    Entry<Integer, Double> b = map.ceilingEntry(index);
    if (a == null && b == null) {
      throw new IllegalStateException("map should contain at least one entry");
    }
    if (a == null) {
      return b.getValue();
    } else if (b == null) {
      return a.getValue();
    }
    double xa = a.getKey();
    double xb = b.getKey();
    double ya = a.getValue();
    double yb = b.getValue();

    return ya + (yb - ya) * (index - xa) / (xb - xa);

  }

}

音の高さを変える(2)

スポンサーリンク

シェアする

  • このエントリーをはてなブックマークに追加

フォローする

%d人のブロガーが「いいね」をつけました。