🎨

MIDIをTableauで可視化してアート作品にしてみた

に公開

はじめに

8月30日~31日に開催されたJTUG(Japan Tableau User Group)のViz展示イベント「VizSpace2025 Summer ~Home&Journey~」に応募した作品の制作について書いてみたいと思います。

VizSpaceとは

VizSpace = Tableau × アート × コミュニティが出会う展示イベント
Viz Space は、BIツール・Tableau を用いた可視化作品を“アート”として展示するイベントです。
今回のテーマは「Home & Journey」。
変わりゆくものと変わらないもの ーその境界にある想いや問いを、ビジュアライズで描き出します。
(TechPlayより抜粋)

今回はオンラインでの展示もありました。
https://visit.virtualartgallery.com/vizspace2025summer

制作した作品について


https://public.tableau.com/views/MusicIsOurHome/sheet0?:language=ja-JP&:sid=&:redirect=auth&:display_count=n&:origin=viz_share_link

コンセプト

作品テーマはHome(変わらぬ故郷)とJourney(新たな旅路)のどちらかを選択することになっており、私はHomeを選択しました。
自分にとってのHomeとは?と考えたときに、真っ先に思い浮かんだのは音楽でした。音楽は再生ボタンを押せばいつでも帰れる変わらぬ故郷と捉え、音楽を可視化してみることにしました。
私は趣味でDTMを齧っているのですが、DAWソフトに打ち込んだメロディーはMIDIと呼ばれるデータとして扱われます。MIDIデータとは、どの楽器で、いつ、どのくらいの強さで、どのくらいの長さの音を出すかといった演奏情報を記録したものです。
要は数値データなので、もしかしてTableauで可視化したら面白いのでは?と思い、MIDIの可視化に挑戦してみました。

データの作り方

  1. MIDIファイルを作る
    DAWソフトを使用して好きな楽曲を耳コピして打ち込み、MIDIファイルとしてエクスポートします。
  2. MIDIファイルをCSVに変換する
    Pythonのmidoというライブラリを使用してMIDIファイルをCSVに変換します。
    (コードはGeminiにサクッと書いてもらいました)
import mido
import csv

# MIDIファイルを読み込み
mid = mido.MidiFile("mymusic.mid")

tempo = 500000  # デフォルトテンポ(マイクロ秒)
ticks_per_beat = mid.ticks_per_beat

def ticks_to_seconds(ticks):
    return mido.tick2second(ticks, ticks_per_beat, tempo)

# 出力用CSVファイルをオープン
with open("midi.csv", mode="w", newline="") as csvfile:
    csv_writer = csv.writer(csvfile)

    # ヘッダー行の書き込み
    csv_writer.writerow(["Track", "Channel", "Start_Tick", "End_Tick", "Start_Second", "End_Second", "Pitch", "Velocity"])

    # 各トラックを処理
    for i, track in enumerate(mid.tracks):
        abs_tick = 0
        note_on_dict = {}

        for msg in track:
            abs_tick += msg.time

            if msg.type == "set_tempo":
                tempo = msg.tempo  # テンポ変更に対応

            if msg.type == "note_on" and msg.velocity > 0:
                key = (msg.channel, msg.note)
                note_on_dict[key] = (abs_tick, msg.velocity)

            elif msg.type == "note_off" or (msg.type == "note_on" and msg.velocity == 0):
                key = (msg.channel, msg.note)
                if key in note_on_dict:
                    start_tick, velocity = note_on_dict.pop(key)
                    end_tick = abs_tick
                    start_sec = ticks_to_seconds(start_tick)
                    end_sec = ticks_to_seconds(end_tick)
                    csv_writer.writerow([i, msg.channel, start_tick, end_tick, start_sec, end_sec, msg.note, velocity])

こんな感じのデータが出力されます。

Track,Channel,Start_Tick,End_Tick,Start_Second,End_Second,Pitch,Velocity
3,0,15360,15840,21.333312,21.999978,56,100
3,0,15360,15840,21.333312,21.999978,61,100
3,0,15360,15840,21.333312,21.999978,64,100
3,0,15360,15840,21.333312,21.999978,68,100
3,0,15840,16080,21.999978,22.333311,56,100
3,0,15840,16080,21.999978,22.333311,61,100
3,0,15840,16080,21.999978,22.333311,64,100
3,0,15840,16080,21.999978,22.333311,68,100
3,0,16080,16200,22.333311,22.4999775,56,100
3,0,16080,16200,22.333311,22.4999775,61,100
...

今回の可視化では、以下の2つのデータを使用しました。

  • Start_Tick:音の開始位置
  • Pitch:音の高さ

Vizの作り方

  1. 計算フィールド
    初めは直線的な可視化をしてみようと考えていたのですが、思っていたような見た目にならなかったので、急遽円形に配置することにしました。
    というわけで、AngleとXとYを用意します。

    Angle

    2 * PI() * ([Start Tick] - { FIXED : MIN([Start Tick]) })
    / ({ FIXED : MAX([Start Tick]) } - { FIXED : MIN([Start Tick]) })
    

    X

    [Pitch]*COS([Angle])
    

    Y

    [Pitch]*SIN([Angle])
    
  2. シート
    メインのシートはこんな感じです。

    列にX、行にY、パスにPitch、詳細にStart Tickを入れています。
    時間の経過が角度で、音の高さが円の中心からの距離というイメージです。
    謎の四角形の重なりはパスの線のタイプを以下に選択することで偶然現れたものです。

    普通(左のやつ)だとこんな感じになります。これはこれで良い気もしますが、今回はより映えそうな方を選びました。

    真ん中の再生ボタンは形状で作成して浮動で配置しています。

まとめ

今回の音楽とデータ可視化を結びつけた作品制作は、Tableauの新たな楽しみ方を知る貴重な経験となりました。自分の作品を公開するのは少し緊張しましたが、いろいろな方から反応をいただけて、挑戦してみて良かったなと思います。
このブログが、データ可視化の面白さを知るきっかけになれば幸いです。

truestarテックブログ
設定によりコメント欄が無効化されています