メモ

メモ、雑記、etc

piano roll with python (1-2)

2010-12-25 23:05:47 | programming python

その2の本体。 関数が文字数制限にひっかかってしまって、さらに分割しました。


def draw_pianoroll(filename):
    ''' fft を行い、ピアノロール上にドットを描く '''
    global wav_chunk
    global wav_db

    # ファイルチェック
    fin = wave.open(filename, 'rb')
    (_channel, _sampwidth, _framerate, _nframes, _, _) = fin.getparams()
    print 'channel:%d, samplewidth:%d[byte], framerate:%d[Hz], frames:%d' % (_channel, _sampwidth, _framerate, _nframes)
    if _channel > 2:
        print 'モノラル/ステレオのみに対応しています.'
        return 0
    if _sampwidth > 2:
        print '未対応のデータ並びです.'
        return 0
    dtype = scipy.int16
    if _sampwidth == 1:
        dtype = scipy.int8
   
    # ピアノロール
    image = create_roll()
    (img_width, img_height) = image.size
    draw = ImageDraw.Draw(image)
    # ピアノロール A4 での座標
    pos_a4 = (pf_keywidth + 1) * (pf_inoct * 3 + 9) + 1 + (pf_keywidth / 2)
    # FFT結果書き込み開始位置
    img_start = pf_depth + 1
    # dB最大値保持map -80[dB]-0[dB] -> 0-255
    if mod_fine:
        img_max = Image.new('L', image.size, 0)
        draw_max = ImageDraw.Draw(img_max)
        def db2pix(db):
            return int( (min(wav_db_max, max(-80,db)) + 80.0) / 80.0 * 255 )
    print 'image:%d x %d, total lines for this songs:%d' % (img_width, img_height, int(_nframes / _framerate / pix_linesec))

    # FFT基数
    # 平均律の計算は http://en.wikipedia.org/wiki/Piano_key_frequencies を参照
    const_nth_key = -49
    base_cent_n = -8
    base_freqs = [wav_tuning * math.pow(2.0, 1.0/pf_inoct) ** (i + const_nth_key) for i in range(base_cent_n, base_cent_n + pf_inoct)]
    #base_keys  = ['A0 ','A#0','B0 ','C1 ','C#1','D1 ','D#1','E1 ','F1 ','F#1','G1 ','G#1']
    base_keys  = ['C0 ','C#0','D0 ','D#0','E0 ','F0 ','F#0','G0 ','G#0','A0 ','A#0','B0 ']
    color_size = 30
    color_base = get_hue_color(color_size)  # wav_db[dB] <-> -10[dB]
   
    # C0からB0の基数で倍音を計算し、オクターブ(バンド?)だけ拾ってみる
    for base_cnt, base_freq in enumerate(base_freqs):
        if mod_fine == 0:
            if base_cnt == 1: break #wav_chunk *= 2
            if base_cnt == 2: break

        # マップ用座標の準備 (周波数 <=> cent(C1~C8) <=> pixel位置)
        # A4 (const_nth_key) を中心として、その座標からの相対値とする.
        # C1:N=-45, C8:N=39
        # 2の累乗じゃなくなるけど、遅いなりに動作はしているようだ...
        if mod_fine:
            wav_chunk = int(_framerate / base_freq + 0.5)
        print '%s freq:%4.1f, chunk:%4d, ' % (base_keys[base_cnt], base_freq, wav_chunk),
        oct_cent_n = [const_nth_key + base_cent_n + base_cnt + pf_inoct * i for i in range(0, 10)]
        min_res = (_framerate / float(wav_chunk)) / wav_tuning
        cent = [1200.0 * math.log(0.5 * min_res, 2)] + [1200.0 * math.log(i * min_res, 2) for i in range(1, wav_chunk)]
        pos_cent = []   # pixel位置
        # ピアノロールの範囲(C1-B7)で制限
        min_cent = 100.0 * -45 - 50 # -45 = C1
        max_cent = 100.0 *  38 + 50 #  38 = B7
        min_resolut = 100 / pf_keywidth
        dis_min = 1
        dis_max = wav_chunk
        for cnt in range(0, wav_chunk):
            if cent[cnt] < min_cent:
                pos_cent += [-1]    # 負数で座標無効
                dis_min = cnt
                continue
            if cent[cnt] >= max_cent:
                dis_max = cnt - 1
                break
            cent_n = int(abs(cent[cnt]) / 100.0 + 0.5)  # cent_nは-45(C1)~39(C8)のはず
            if cent[cnt] < 0:
                cent_n = -cent_n
            #print cent_n,    # Nの値
            cent_sub = cent[cnt] - cent_n * 100         # cent_subは-50~49[cent]のはず
            pix_fluct = int((cent_sub + 50) / min_resolut - pf_keywidth / 2)
            pos_cent += [pos_a4 + cent_n * (pf_keywidth + 1) + pix_fluct]
           
            # オクターブのみ有効
            # cent_n が oct_cent_n のどれかに一致しなければオクターブ以外として無効にする
            if mod_fine:
                _check = 0
                for oct in oct_cent_n:
                    if cent_n == oct:
                        _check = 1
                        break
                if _check == 0:
                    pos_cent[len(pos_cent)-1] = -1

        # 1FFTあたりの時間比率
        cur_linerate = (wav_chunk / float(_framerate)) / pix_linesec

        # (1-3)に続く
</pre>

1-3へ続く



最新の画像もっと見る

コメントを投稿