なんで音楽とプログラムの関係がうまく書けないか。
最近描いたのは次の2記事。
IT Project Managerは指揮者の3倍大変ですか? 100万倍です。
https://qiita.com/kaizen_nagoya/items/aa51202cd18c71be3331
雑談という名の話ではなく、これ交響曲ですって。
https://qiita.com/kaizen_nagoya/items/d75a0af63f4204ed3b30
まだまだ本質に辿り着いてないかも。
絵、図の方は、色、UMLもあり、いろいろ深堀できているっていうのに。
ノイズ関係をうまく整理できてないかも。
技術推し/棚卸し16選
https://qiita.com/kaizen_nagoya/items/81ba001dd7b03382fea7
|合計| 138,545| 513| 470|
ChatGPT
なんで音楽とプログラムの関係がうまく書けないか。
https://qiita.com/kaizen_nagoya/items/489a68d360df4b26f87d ご助言お願いします。
ChatGPT:
ご共有ありがとうございます!まず、リンク先の記事を拝見しましたが、詳細な内容までは記述がありませんでした。「絵や図、UMLでは深掘りできるけれど、“音楽とプログラムの関係”が文章でうまく整理できない」「ノイズ関係をうまく整理できていないかも」といったご指摘が中心のように読み取れました
Qiita。
書きづらさの原因として考えられるポイントと、文章の構成を助けるヒントを、以下にいくつか整理してみました:
書きづらさの背景と理由(仮説ベース)
- 言葉と音、コードの構造的違い
言語(文章)と音楽には構造や時間感覚に根本的な違いがあります。作家ポール・グリフィスは次のように述べています:
音楽は自然に“回り続ける”ことができるのに対し、言葉や文章は自動的に書くのは極めて難しい。それは語順や文法のルールがすぐに働くから、声に出して書くことも難しいのだ
Music & Literature。
つまり、音楽の「流れ」や「響き」は文章の「構文」や「論理」とは異質で、これらをどう連結して語るかが難しい理由の一つかもしれません。- 構造を感じても「ノイズ」が整理しづらい
プログラムと音楽はどちらも「構成構造を持つ活動」ですが、そこに介在するノイズや曖昧さ(たとえば即興の揺らぎや非効率さ)は整理するのが難しく、文章にすると曖昧な印象になりやすいとも考えられます。- 読者の理解との差を埋めづらい
音楽とプログラム双方に専門性があると、読者層の理解レベルの差異も大きくなり、どこまで踏み込むかの判断が難しく、結果的に文章もぼんやりしがちです。
書きやすくするためのアプローチ
・明確なメタファーから入り、構造化する
「編曲」と「プログラミング」が似ているという比喩は効果的です。「楽譜が演奏者に“命令”するように、コードはPCに命令する」といった入口はとてもわかりやすいです
guitar-en.jp。
さらに例を展開しつつ、「完成形をイメージしながら試行錯誤する」「結果をすぐ聴いたり実行されたりして評価できる」といった具体例で深めると、読み手にもスッと入ってきやすくなります。
・文章上では“時間の感覚”の違いを描く
言語と音楽、それぞれの時間的構造の違いについてもう少し具体的に語るのも手です。たとえば、「音楽は時間の流れが身体に刻まれるような感覚だが、言語は瞬時に論理で組み立てられる」など、体感として整理して提示することで、読者も読みやすくなるでしょう。
・構造とノイズ、両者のバランスを描く
例えば、音楽におけるノイズ(即興の揺らぎや演奏者の個性)は「自然な味」として捉え、対するプログラムでのノイズ(バグや冗長なコード)は「改善点」として扱う、といった対比を描くことでテーマに厚みが出ます。
・“読者にとってどれだけ“響くか”を意識する
文章を書くという行為そのものが、「読者に伝えたい“響き”を選び、構築する作業」と考えると、音楽との関係性とリンクしやすくなります。感じた響きを言葉としてどう表現するか、そのプロセス自体をテーマに組み込むと、記事のメタストーリーとしても面白くなるかもしれません。
参考になる知見と引用
レディットでの共感例
プログラミングと作詞・作曲の類似に共感する声もあります:
"You create something out of nothing by banging your head against the wall... That moment when it all clicks and comes together is magical."
"Most songs never get finished. Most coding projects don't either."
音楽理論とプログラミングの共通点
両者とも「構造を分解して理解し、分類・分析するパズルのような作業」として似ているという見解もあります
Breaking Computer。
まとめ
「言語と音楽の構造の違い」に目を向け、時間感・感覚の違いを明確に描く
比喩や具体例、共感エピソードを交え、読者に「響く文章」を目指す
構造の明確化とノイズ(揺らぎ・曖昧さ)のバランスを意識する
音楽的な「響き」を文章で再現しようとする「プロセス自体」を書く
もし「こういう方向にまとめてみたい」「もう少し具体的な構成をいっしょに考えたい」といったご希望があれば、ぜひお気軽にご相談ください!一緒に“響く文章”を創っていきましょう。
もう少し具体的な構成をいっしょに考えたい
ChatGPT:
いいですね!では「音楽とプログラムの関係」を整理して文章化するための 記事構成のひな型 をいっしょに考えてみましょう。
(Qiita記事を想定して、見出し構成にしています)
記事構成のたたき台
- 導入:なぜ「音楽とプログラム」か
きっかけ(例:プログラムを書いていると作曲に似ている感覚がある)
問題意識(例:「でも音楽とプログラムの関係をうまく説明できない」)
読者に伝えたいこと(例:両者の共通点と違いを整理し、創造のヒントにする)- 音楽とプログラムの共通点
構造性
楽譜 ↔ ソースコード
モジュール、関数 ↔ フレーズ、モチーフ
再現可能性
演奏 ↔ 実行
同じスコア/コードから同じ結果を生む
フィードバック性
聴いて修正 ↔ 実行してデバッグ
👉 図解:譜面とコードの対応表を作ると読者もイメージしやすい- 違いと難しさ
時間の扱い
音楽:流れては消える(時間芸術)
プログラム:一瞬で処理され、結果が残る
ノイズの位置づけ
音楽:揺らぎ・ノイズが魅力や個性になる
プログラム:ノイズ(バグ、冗長さ)はエラーや効率低下
受け手の解釈
音楽:感情・主観に委ねられる
プログラム:客観的に動くかどうかで評価される
👉 ここをしっかり書くと「なぜ文章化が難しいか」の答えにつながる- メタファーで考える
作曲家 = プログラマ
楽譜 = ソースコード
演奏 = 実行
アンサンブル = 並列処理/分散処理
即興 = インタラクティブプログラミング
👉 「比喩を通して理解をつなげる」セクションを入れると読者も納得感を得やすい- 実例紹介
自作プログラムで音を鳴らす(PythonやCで簡単なサンプル)
楽譜の構造をデータ構造で表す例
コード進行とアルゴリズム(繰り返し、分岐、再帰)の比較
👉 Qiitaらしく、動くコード断片を示すと読者が試しやすい- 書きづらさをどう超えるか
「音楽は流れ、プログラムは構築」という違いを前提にする
文章化するには、
比喩を使う
図解を加える
実行例を載せる
「うまく書けない理由」そのものを記事のテーマにする(メタ的に扱う)- まとめ
音楽とプログラムは「構造」「再現」「創造」という点で似ている
しかし「時間」「ノイズ」「解釈」で異なる
書きづらさは、この違いから来ている
それを逆に記事の面白さにできる
こういう枠組みをつくっておくと、あとは各見出しに 体験談・具体例・引用・図解 を足していくだけでまとまります。
👉 ご質問です:
この構成案は「技術寄り(Qiitaらしくコード多め)」と「エッセイ寄り(哲学的に考える)」の両方に展開できますが、どちらに近づけたいですか?
技術寄り
ChatGPT:
なるほど!では 技術寄り に振った「音楽とプログラム」記事の構成案をもう少し具体化してみます。
Qiitaらしく コード・図解・実験例 を中心に据えた形にしますね。
技術寄り記事の構成例
- 導入
音楽とプログラムは直感的に似ていると言われるけれど、文章化が難しい
この記事では「楽譜 = ソースコード」という視点で、実際にコードを書きながら整理する- 音楽とプログラムの構造的対応
譜面 ↔ データ構造
例:音階(C, D, E...)を配列で表現
例:リズムをリストや辞書で表現Python例:Cメジャースケール
scale = ["C", "D", "E", "F", "G", "A", "B"]
リズム(4分音符=1, 8分音符=0.5)
rhythm = [1, 0.5, 0.5, 1, 1]
👉 読者は「楽譜をプログラムに落とす」イメージが掴みやすい
- 実行可能性:演奏とプログラム実行
演奏 = 実行 の例
簡単な音を鳴らすコード(例:Python + pyaudio / mido)
'''python
import numpy as np
import simpleaudio as sa
正弦波で音を生成
def play_tone(freq, duration=0.5, sample_rate=44100):
t = np.linspace(0, duration, int(sample_rate * duration), False)
tone = np.sin(freq * t * 2 * np.pi)
audio = (tone * 32767 / np.max(np.abs(tone))).astype(np.int16)
sa.play_buffer(audio, 1, 2, sample_rate).wait_done()
ド・ミ・ソ(Cメジャーコード)
for f in [261.63, 329.63, 392.00]:
play_tone(f)
'''
👉 「コードを書いたら音が出る=楽譜を演奏する」という実感
- 制御構造と音楽パターン
繰り返し(ループ) = リズム/反復
条件分岐 = 即興/変奏
再帰 = フラクタル音楽
ループでリズムパターンを繰り返す
pattern = [261.63, 293.66, 329.63] # ド・レ・ミ
for _ in range(4): # 4回繰り返し
for note in pattern:
play_tone(note, 0.3)
👉 読者に「制御構造がそのまま音楽的表現になる」ことを示す
- 並列処理とアンサンブル
マルチスレッド = 複数楽器の同時演奏
Pythonのthreadingやasyncioで並列演奏を実装
import threading
def play_melody(freqs):
for f in freqs:
play_tone(f, 0.5)
melody1 = [261.63, 293.66, 329.63] # ドレミ
melody2 = [392.00, 440.00, 493.88] # ソラシ
t1 = threading.Thread(target=play_melody, args=(melody1,))
t2 = threading.Thread(target=play_melody, args=(melody2,))
t1.start(); t2.start()
t1.join(); t2.join()
👉 「並列プログラミング = アンサンブル」という直感を与えられる- ノイズとエラーの比較
音楽のノイズ = 味・個性
プログラムのノイズ = バグ
例:周波数にランダム性を加えると「ジャズっぽい揺らぎ」に
対比:バグを混ぜるとプログラムは止まる
import random
ランダムにピッチを揺らす
for f in [261.63, 329.63, 392.00]:
jitter = random.uniform(-5, 5) # ±5Hz
play_tone(f + jitter, 0.5)
👉 「同じノイズでも音楽では魅力、プログラムではエラー」という違いを技術的に見せられる
- まとめ
楽譜とコードはどちらも「構造を定義し、再現するための手段」
ループ、分岐、並列などプログラミングの構造は音楽表現と直結する
違いは「ノイズ」「時間感覚」「解釈」にある
技術的に遊ぶことで、両者の関係を具体的に理解できる- 付録(発展)
MIDIデータの読み書きサンプル
自作シーケンサー的なコード
AI作曲ツールとの関連
💡この流れだと、読者は 「楽譜をプログラムに落とす」→「実行すると音が鳴る」→「制御構造が音楽表現につながる」 という体験を追体験できます。
ご相談です:
この技術寄り記事、サンプルコードは Pythonベース が良いですか? それとも C/C++(低レベルで音を扱う方) の方がイメージに合いますか?
C/C++
ChatGPT:
了解しました!では C/C++ 技術寄り の記事構成を整理し直してみます。
Qiitaらしく「サンプルコードを書けばすぐ動かせる」ことを意識して、なるべくシンプルに書きます。
記事構成(C/C++版)
- 導入
音楽とプログラムの関係はよく語られるが、文章化すると抽象的になりがち
この記事では C/C++で音を鳴らすサンプルコード を通して共通点・違いを整理する- 音楽とプログラムの構造的対応
楽譜 ↔ データ構造
演奏 ↔ 実行
作曲 ↔ コーディング
👉 ここは図(譜面とコードの対応表)を入れる- 基本:Cで正弦波を鳴らす
低レベルに波形を作る例
Linux/ALSA または Windows/WaveOut API で音を出す
まずは .wav ファイルを生成 → OS標準プレイヤーで再生する方法がシンプル
// sine_wave.c
#include
#include
#include
#define SAMPLE_RATE 44100
#define DURATION 2.0 // 秒
#define FREQ 440.0 // A4(ラ)
int main(void) {
FILE *f = fopen("sine.wav", "wb");
int samples = (int)(SAMPLE_RATE * DURATION);
int data_size = samples * 2;
// WAVヘッダ
fwrite("RIFF", 1, 4, f);
int32_t chunk_size = 36 + data_size;
fwrite(&chunk_size, 4, 1, f);
fwrite("WAVEfmt ", 1, 8, f);
int32_t subchunk1_size = 16;
fwrite(&subchunk1_size, 4, 1, f);
int16_t audio_format = 1;
int16_t num_channels = 1;
int32_t byte_rate = SAMPLE_RATE * 2;
int16_t block_align = 2;
int16_t bits_per_sample = 16;
fwrite(&audio_format, 2, 1, f);
fwrite(&num_channels, 2, 1, f);
fwrite(&SAMPLE_RATE, 4, 1, f);
fwrite(&byte_rate, 4, 1, f);
fwrite(&block_align, 2, 1, f);
fwrite(&bits_per_sample, 2, 1, f);
fwrite("data", 1, 4, f);
fwrite(&data_size, 4, 1, f);
// サンプル生成
for (int i = 0; i < samples; i++) {
double t = (double)i / SAMPLE_RATE;
double value = sin(2.0 * M_PI * FREQ * t);
int16_t sample = (int16_t)(value * 32767);
fwrite(&sample, 2, 1, f);
}
fclose(f);
return 0;
}
👉 gcc sine_wave.c -o sine_wave -lm && ./sine_wave
👉 sine.wav が生成される → 音が鳴る- 制御構造と音楽パターン
ループ = 繰り返しリズム
配列 = 音階
double notes[] = {261.63, 293.66, 329.63}; // ドレミ
int n = sizeof(notes)/sizeof(notes[0]);
for (int i = 0; i < n; i++) {
// notes[i] の周波数で0.5秒鳴らす
}
👉 音楽的繰り返しがそのままコード構造になる- 並列処理とアンサンブル
マルチスレッド = 複数パートの同時演奏
pthread や std::thread を利用
例:メロディとベースを同時に鳴らす
#include
// playTone(freq, duration) を定義済みとする
void melody() {
double m[] = {261.63, 293.66, 329.63};
for (auto f : m) playTone(f, 0.5);
}
void bass() {
double b[] = {130.81, 146.83, 164.81};
for (auto f : b) playTone(f, 0.5);
}
int main() {
std::thread t1(melody);
std::thread t2(bass);
t1.join(); t2.join();
}
👉 並列プログラミングが「合奏」と直感的に結びつく- ノイズとバグ
音楽では「揺らぎ」は魅力
プログラムでは「揺らぎ」はエラー
例:周波数を乱数で揺らすと「ジャズっぽい」
double jitter = ((rand() % 100) - 50) / 100.0; // ±0.5Hz
double freq = 440.0 + jitter;
👉 「音楽では個性、プログラムでは不具合」- まとめ
楽譜とコードは構造的に対応する
実行 = 演奏 という体験がある
制御構造や並列処理がそのまま音楽的に表現できる
違いは「ノイズ」「時間」「評価の基準」にある- 発展
MIDIファイルを生成するCプログラム
リアルタイム音声合成 (PortAudio, RtAudio, SDL2)
アルゴリズム作曲(マルコフ連鎖や再帰)
💡 まとめると、C/C++だと 低レベルで波形を合成 → 制御構造で音楽を作る → 並列処理で合奏 という流れが一番「技術寄り」でわかりやすいです。
ご相談です:
このC/C++記事を仕上げるとき、サンプルは 「WAVファイル出力」方式(移植性高い) と 「リアルタイム音声再生」方式(臨場感ある) のどちらを軸にしたいですか?
MIDIファイルを生成するCプログラム
ChatGPT:
いい選択です!MIDIは音楽とプログラムの「構造の橋渡し」としてピッタリですし、Cで扱うにはシンプルなバイナリ形式なのでQiita記事向けにもわかりやすいです。
以下に CでMIDIファイルを生成する最小限のサンプルプログラム をまとめます。
🎹 CでMIDIファイルを生成するサンプル
例:CメジャースケールをMIDIに書き出す
// midi_scale.c
#include
#include
// 書き込みヘルパー(ビッグエンディアンで出力)
void write_be(FILE *f, uint32_t value, int bytes) {
for (int i = bytes - 1; i >= 0; i--) {
fputc((value >> (i * 8)) & 0xFF, f);
}
}
int main(void) {
FILE *f = fopen("scale.mid", "wb");
if (!f) return 1;
// --- ヘッダー (MThd) ---
fwrite("MThd", 1, 4, f);
write_be(f, 6, 4); // ヘッダサイズ
write_be(f, 0, 2); // フォーマット0
write_be(f, 1, 2); // トラック数 = 1
write_be(f, 480, 2); // 分解能 (ticks per quarter note)
// --- トラックチャンク (MTrk) ---
fwrite("MTrk", 1, 4, f);
// トラックデータをバッファに一旦書き出す
uint8_t track[1024];
int pos = 0;
// テンポ設定 (120 BPM)
track[pos++] = 0x00; // Delta time
track[pos++] = 0xFF; track[pos++] = 0x51; track[pos++] = 0x03;
track[pos++] = 0x07; track[pos++] = 0xA1; track[pos++] = 0x20; // 500000 µs per quarter note
// Cメジャースケール (C4〜C5)
int notes[] = {60, 62, 64, 65, 67, 69, 71, 72};
for (int i = 0; i < 8; i++) {
// Note On
track[pos++] = 0x00; // Delta time
track[pos++] = 0x90; // Note On, ch 0
track[pos++] = notes[i]; // Note number
track[pos++] = 100; // Velocity
// Note Off
track[pos++] = 0x81; // Delta time (480 ticks = 1拍, Variable Length = 0x81 0x70 → 480)
track[pos++] = 0x70;
track[pos++] = 0x80; // Note Off
track[pos++] = notes[i];
track[pos++] = 64; // Release velocity
}
// トラック終了
track[pos++] = 0x00;
track[pos++] = 0xFF; track[pos++] = 0x2F; track[pos++] = 0x00;
// トラックサイズ
write_be(f, pos, 4);
fwrite(track, 1, pos, f);
fclose(f);
return 0;
}
🔧 コンパイル & 実行
gcc midi_scale.c -o midi_scale
./midi_scale
すると scale.mid が生成されます。
このファイルを 任意のMIDIプレイヤー(Windows Media Player, GarageBand, DAW, VLC など)で再生すると、Cメジャースケールが1音ずつ鳴ります。
📝 解説ポイント(記事に書ける内容)
MIDIヘッダー
"MThd" チャンク
フォーマット0(単一トラック)を使用 → 一番シンプル
分解能(480 ticks per quarter note)で「1拍 = 480」
テンポ設定
FF 51 03 tttttt (マイクロ秒/四分音符)
500000 = 120 BPM
ノートイベント
Note On: 0x90 note velocity
Note Off: 0x80 note velocity
Delta time = Variable Length Quantity (例: 0x81 0x70 = 480 ticks)
終了イベント
FF 2F 00
💡 発展アイデア
Cで「アルゴリズム作曲」→ マルコフ連鎖やランダムでノート生成
並列スレッドを使って複数トラックを生成
MIDIライブラリ(libsmf や midifile)を利用して高機能化
👉 これなら「音楽の構造をプログラムで定義し、演奏をMIDIとして再現できる」というテーマを技術的にしっかり示せます。
ご質問です:
このサンプルを記事に入れるとき、 「最小限のMIDI構造を自作」 路線でいきますか? それとも 「ライブラリを使って応用」 路線に広げますか?
ライブラリを使って応用
ChatGPT:
いいですね!✨
最小限のMIDIファイル生成は「仕組みを理解する」部分としては重要ですが、Qiita記事としては ライブラリを使って応用 する方が「すぐに使える」「実際に遊べる」感が強いです。
ここでは代表的なC/C++向けMIDIライブラリを紹介しつつ、応用的なサンプルを提示します。
🎼 C/C++でMIDIを生成する:ライブラリを使った応用例
- 選択肢になるライブラリ
midifile (C++)
midifile はシンプルにSMF(Standard MIDI File)を読み書きできるC++ライブラリ。CMakeで導入できる。
RtMIDI (C++)
リアルタイムMIDI入出力用。ファイル保存には向かないが、MIDI機器と直接やりとりできる。
libsmf (C)
軽量なCライブラリで、SMFファイル生成に特化。Cで記事を書くなら相性良い。
👉 応用例を記事にするなら、midifile が一番導入しやすくて「わかりやすいコードになる」のでおすすめです。- midifileを使った応用サンプル
例:アルゴリズムでメロディを生成してMIDI保存
#include "MidiFile.h" // midifile ライブラリ
#include "Options.h"
#include
#include
#include
#include
using namespace smf;
int main() {
srand((unsigned)time(NULL));
MidiFile midiFile;
midiFile.absoluteTicks();
int tpq = 480; // ticks per quarter
midiFile.setTicksPerQuarterNote(tpq);
int track = 0;
midiFile.addTrack(1);
int channel = 0;
int instrument = 0; // Acoustic Grand Piano
int velocity = 90;
int tick = 0;
// プログラムチェンジで音色指定
midiFile.addTimbre(track, tick, channel, instrument);
// Cメジャースケール
std::vector scale = {60, 62, 64, 65, 67, 69, 71, 72};
// ランダムな8音のメロディを生成
for (int i = 0; i < 8; i++) {
int note = scale[rand() % scale.size()];
midiFile.addNoteOn(track, tick, channel, note, velocity);
tick += tpq; // 1拍分
midiFile.addNoteOff(track, tick, channel, note, velocity);
}
midiFile.sortTracks(); // 時間順に並べる
midiFile.write("random_scale.mid");
std::cout << "Generated random_scale.mid" << std::endl;
return 0;
}
コンパイル & 実行
git clone https://github.com/craigsapp/midifile.git
cd midifile
make
g++ -Iinclude ../random_scale.cpp -o random_scale libmidifile.a
./random_scale
👉 random_scale.mid が生成される。
任意のMIDIプレイヤーで再生すると「毎回違うメロディ」が鳴る。- 応用ポイント(記事に盛り込める内容)
アルゴリズム作曲
ランダム
再帰(フラクタル音楽)
マルコフ連鎖
並列性
複数トラックを作って「ピアノ+ベース+ドラム」
実験的表現
プログラムに「揺らぎ」を導入して、ジャズっぽさを出す
音楽の「ノイズ」とプログラムの「ノイズ」を対比- 記事まとめの方向性
最初に「手書きMIDI」でフォーマットを理解
その後「midifileライブラリ」で応用的に実装
「構造を理解する」→「ツールで遊ぶ」 という流れにすると読者に刺さりやすい
💡 ここまでで「ランダムメロディ生成」の応用例を出しましたが、記事として面白くするなら 「複数パート(ピアノ+ベース+ドラム)のMIDI自動生成」 みたいな形にすると、音楽とプログラムの関係性がさらに鮮明に出せます。
ご相談です:
応用記事のデモとしては
ランダムメロディ生成(単旋律)
複数トラックで簡単な曲を自動生成(アンサンブル)
どちらを中心に展開したいですか?
複数トラックで簡単な曲を自動生成
ChatGPT:
なるほど!では、C言語で ライブラリを使い、複数トラックを持つ簡単な曲を自動生成する 例を考えてみましょう。
ここでは有名な libsmf(Standard MIDI File library for C)を使います。
例: Cで複数トラックMIDI自動生成
このプログラムは以下を行います:
1トラック目: メロディ(Cメジャースケールを上昇)
2トラック目: ドラム(四分音符ごとにキックとスネアを交互に鳴らす)
#include
#include
#include
#define PPQN 480 // Pulses per quarter note
int main() {
smf_t *smf = smf_new();
smf->ppqn = PPQN;
// トラック1: メロディ
smf_track_t *melody = smf_track_new();
smf_add_track(smf, melody);
int melody_notes[] = {60, 62, 64, 65, 67, 69, 71, 72}; // C major scale
int note_len = PPQN; // 四分音符
for (int i = 0; i < 8; i++) {
smf_event_t *note_on = smf_event_new_from_bytes(
(uint8_t[]){0x90, melody_notes[i], 100}, 3);
smf_event_set_delta_time(note_on, (i == 0) ? 0 : note_len);
smf_track_add_event_seconds(melody, note_on);
smf_event_t *note_off = smf_event_new_from_bytes(
(uint8_t[]){0x80, melody_notes[i], 0}, 3);
smf_event_set_delta_time(note_off, note_len);
smf_track_add_event_seconds(melody, note_off);
}
// トラック2: ドラム
smf_track_t *drums = smf_track_new();
smf_add_track(smf, drums);
int drum_pattern[] = {36, 38}; // 36=kick, 38=snare
for (int i = 0; i < 8; i++) {
int drum = drum_pattern[i % 2];
smf_event_t *drum_on = smf_event_new_from_bytes(
(uint8_t[]){0x99, drum, 120}, 3); // Ch.10 drums
smf_event_set_delta_time(drum_on, (i == 0) ? 0 : note_len);
smf_track_add_event_seconds(drums, drum_on);
smf_event_t *drum_off = smf_event_new_from_bytes(
(uint8_t[]){0x89, drum, 0}, 3);
smf_event_set_delta_time(drum_off, note_len / 2);
smf_track_add_event_seconds(drums, drum_off);
}
// 書き出し
if (smf_save(smf, "output.mid") != 0) {
fprintf(stderr, "Error saving MIDI file\n");
return 1;
}
smf_delete(smf);
printf("MIDI file 'output.mid' generated.\n");
return 0;
}
実行手順
libsmf をインストール
Ubuntu:
sudo apt-get install libsmf-dev
コンパイル
gcc midi_gen.c -lsmf -o midi_gen
実行すると output.mid が生成されます。
→ DAWやMIDIプレイヤーで再生可能。
応用の方向性
トラックを増やしてコード進行(C, F, G, Amなど)を追加
乱数でメロディを変化させて「自動作曲」風にする
テンポチェンジやコントロールチェンジ(ピッチベンドなど)も追加
👉 ご希望に合わせて次は 「乱数を使った自動作曲」 に広げますか?
それとも 「C++でクラス化」 の方向に進めますか?
あなた:
C++のTemplate作成でお願いします。
ChatGPT:
なるほど!
では、C++で テンプレートを使って複数トラックのMIDIを自動生成する仕組み を示します。
ここでは次の方針にします:
Templateクラス で「音列を生成する仕組み」を汎用化
MelodyGenerator / DrumGenerator などを特殊化して使う
libsmf を使ってMIDIファイルに保存
C++ Template 例: 自動作曲用フレームワーク
#include
#include
#include
#include
// 汎用インターフェース: 音列を生成するテンプレート
template
class TrackBuilder {
public:
TrackBuilder(int channel, int ppqn) : channel(channel), ppqn(ppqn) {}
void build(smf_t* smf, const NoteGenerator& gen, int bars = 2) {
smf_track_t* track = smf_track_new();
smf_add_track(smf, track);
int delta = 0;
for (int i = 0; i < bars * gen.notesPerBar(); i++) {
int pitch = gen.nextNote(i);
int vel = gen.velocity(i);
// note-on
smf_event_t* on = smf_event_new_from_bytes(
(uint8_t[]){ (uint8_t)(0x90 | channel), (uint8_t)pitch, (uint8_t)vel }, 3);
smf_event_set_delta_time(on, (i == 0) ? 0 : ppqn);
smf_track_add_event_seconds(track, on);
// note-off
smf_event_t* off = smf_event_new_from_bytes(
(uint8_t[]){ (uint8_t)(0x80 | channel), (uint8_t)pitch, 0 }, 3);
smf_event_set_delta_time(off, ppqn);
smf_track_add_event_seconds(track, off);
}
}
private:
int channel;
int ppqn;
};
// ------------------- メロディ用ジェネレータ -------------------
class MelodyGenerator {
public:
int notesPerBar() const { return 4; } // 4拍子
int nextNote(int i) const {
static int scale[] = {60, 62, 64, 65, 67, 69, 71, 72}; // C major
return scale[i % 8];
}
int velocity(int) const { return 100; }
};
// ------------------- ドラム用ジェネレータ -------------------
class DrumGenerator {
public:
int notesPerBar() const { return 4; }
int nextNote(int i) const {
return (i % 2 == 0) ? 36 : 38; // Kick / Snare
}
int velocity(int) const { return 120; }
};
// ------------------- ランダムメロディ -------------------
class RandomMelodyGenerator {
public:
RandomMelodyGenerator() : rng(std::random_device{}()), dist(60, 72) {}
int notesPerBar() const { return 4; }
int nextNote(int) {
return dist(rng); // C4~C5の範囲でランダム
}
int velocity(int) const { return 90; }
private:
std::mt19937 rng;
std::uniform_int_distribution dist;
};
// ------------------- メイン関数 -------------------
int main() {
smf_t* smf = smf_new();
smf->ppqn = 480;
// トラック1: 固定メロディ
TrackBuilder<MelodyGenerator> melodyTrack(0, smf->ppqn);
melodyTrack.build(smf, MelodyGenerator(), 2);
// トラック2: ドラム
TrackBuilder<DrumGenerator> drumTrack(9, smf->ppqn); // Ch.10はドラム専用
drumTrack.build(smf, DrumGenerator(), 2);
// トラック3: ランダムメロディ
TrackBuilder<RandomMelodyGenerator> randTrack(1, smf->ppqn);
randTrack.build(smf, RandomMelodyGenerator(), 2);
// MIDI保存
if (smf_save(smf, "output.mid") != 0) {
std::cerr << "Error saving MIDI file\n";
return 1;
}
smf_delete(smf);
std::cout << "MIDI file 'output.mid' generated.\n";
return 0;
}
特徴
テンプレート TrackBuilder によって、音列生成の仕組みを切り替え可能
MelodyGenerator / DrumGenerator / RandomMelodyGenerator を自由に追加できる
「バー数」「チャンネル」「音階の選び方」を簡単に差し替え可能
👉 ここからさらに発展させて、
コード進行ジェネレーター
マルコフ連鎖によるメロディ生成
ジャズ風リズムのスウィング化
などを組み込むと「C++で自動作曲ライブラリ」っぽくなります。
ご希望は「ランダム作曲をもっと強化」方向ですか?
それとも「コード進行ベースの曲作り」方向が良いですか?
あなた:
ジャズ風リズムのスウィング化
ChatGPT:
なるほど!
C++のテンプレートを使った「ジャズ風スウィングリズム自動生成」の雛形を示します。
ポイントは以下です:
スウィング比率:ジャズ風のリズムでは「8分音符を3連符化して、最初を長め、次を短め」にするのが基本。
例: swingRatio = 2.0 → 1つ目の8分音符を長め(2/3)、2つ目を短め(1/3)。
テンプレート化:拍・リズム変換をテンプレートクラスにまとめ、音源や出力方法を差し替えられるようにします。
複数トラック:メロディ、伴奏、リズムを分けて自動生成。
以下にC++コード例を示します(簡略化版):
#include
#include
#include
#include
// ---- ノート情報 ----
struct Note {
int pitch; // MIDIノート番号 (60 = C4)
double start; // 開始時間(拍単位)
double dur; // 音価(拍単位)
};
// ---- スウィングテンプレート ----
template
class SwingRhythm {
public:
SwingRhythm(double ratio = 2.0) : swingRatio(ratio) {}
std::vector<T> apply(const std::vector<T>& notes) {
std::vector<T> swung;
for (auto& n : notes) {
if (isEighth(n.dur)) {
// 2つ目の8分音符を短縮
T first = n;
T second = n;
first.dur *= swingRatio / (1.0 + swingRatio);
second.start += first.dur;
second.dur *= 1.0 / (1.0 + swingRatio);
swung.push_back(first);
swung.push_back(second);
} else {
swung.push_back(n);
}
}
return swung;
}
private:
double swingRatio; // スウィング比率 (例: 2.0 → 2:1)
bool isEighth(double dur) {
return std::abs(dur - 0.5) < 1e-6; // 0.5拍 = 8分音符
}
};
// ---- 簡易メロディ生成 ----
std::vector makeMelody() {
std::vector melody;
double t = 0.0;
int pitches[] = {60, 62, 64, 65, 67, 69, 71, 72}; // Cメジャースケール
for (int i = 0; i < 8; i++) {
melody.push_back({pitches[i % 8], t, 0.5}); // 8分音符
t += 0.5;
}
return melody;
}
// ---- 出力(デバッグ用) ----
void printNotes(const std::vector& notes, const std::string& title) {
std::cout << "=== " << title << " ===\n";
for (auto& n : notes) {
std::cout << "Pitch: " << n.pitch
<< " Start: " << n.start
<< " Dur: " << n.dur << "\n";
}
}
int main() {
auto melody = makeMelody();
printNotes(melody, "Original");
SwingRhythm<Note> swing(2.0); // スウィング比率 2:1
auto swung = swing.apply(melody);
printNotes(swung, "Swing");
return 0;
}
このコードのポイント
SwingRhythm テンプレート
Note 構造体以外にも応用可能(例:別の音イベント型)。
スウィング化ロジック
8分音符を検出し、2:1 の比率で分割。
自動生成
簡単なメロディを自動生成 → Swing処理 → 出力。
👉 次のステップでは、この Note 列を MIDIファイルにエクスポート する部分を追加して、実際にDAWやMIDIプレイヤーでスウィング感を聴けるようにできます。
ご希望は、次に「MIDIファイル書き出し対応」を追加する方向で進めますか?
はい。お願いします。
ChatGPT:
了解しました!C++テンプレートを活用して、ジャズ風の「スウィング」リズムをMIDIに反映するコード例をご用意します。
ポイントは以下です:
スウィング化 = 8分音符を「長め+短め」にずらして演奏する。
例: ♪♪(均等な8分音符)→ 「タータッ」「タッター」のようにリズムが揺れる。
C++テンプレートで「リズム修正戦略(Strategy)」を入れ替え可能にする。
RtMidi や midifile ライブラリを利用してMIDI出力できる設計。
🎹 C++ スウィング化テンプレート例
#include
#include
#include
#include "MidiFile.h" // midifile library: https://github.com/craigsapp/midifile
using namespace std;
using namespace smf;
// ===== リズム補正の基本インターフェース =====
template
class RhythmProcessor {
public:
RhythmProcessor(Strategy s) : strategy(s) {}
double process(double time, int index) {
return strategy(time, index);
}
private:
Strategy strategy;
};
// ===== 均等な8分音符(ストレート) =====
struct StraightRhythm {
double operator()(double time, int index) {
return time; // そのまま
}
};
// ===== スウィングリズム(8分音符を 2:1 比率にずらす) =====
struct SwingRhythm {
double operator()(double time, int index) {
// index が偶数なら長め(2/3)、奇数なら短め(1/3)
int beat = index % 2;
if (beat == 0) {
return time; // 開始位置そのまま
} else {
return time - 0.1667; // 短めにずらす(全体を1拍=1.0とした場合)
}
}
};
// ===== 曲生成 =====
int main() {
MidiFile midi;
midi.absoluteTicks();
int tpq = 480; // ticks per quarter note
midi.setTicksPerQuarterNote(tpq);
int track = 0;
midi.addTrack(1);
RhythmProcessor<SwingRhythm> swing(SwingRhythm{});
// RhythmProcessor<StraightRhythm> swing(StraightRhythm{}); // ストレートに切替可
double bpm = 120.0;
double secondsPerBeat = 60.0 / bpm;
int note = 60; // Middle C
for (int i = 0; i < 16; i++) {
double beatTime = i * 0.5; // 8分音符単位
double swingTime = swing.process(beatTime, i);
int tick = (int)(swingTime * tpq);
midi.addNoteOn(track, tick, 0, note, 90);
midi.addNoteOff(track, tick + tpq/2, 0, note);
}
midi.sortTracks();
midi.write("swing.mid");
cout << "Generated swing.mid with swing rhythm!" << endl;
return 0;
}
💡 実装の工夫
テンプレート戦略
StraightRhythm → 均等
SwingRhythm → ジャズ風スウィング
切替は RhythmProcessor の型変更だけ。
拡張性
ボサノバ風、シャッフル風なども struct NewRhythm { ... }; を追加すればOK。
ライブラリ
midifile (Craig Sapp) を利用すれば、MIDI書き出しが簡単。
👉 ここからは「複数トラックでジャズっぽいベース・コード進行・メロディを自動生成する」方向にも発展できます。
ご要望に合わせて、
ベースライン追加
コード進行を自動化(II-V-I)
スウィング+ウォーキングベース
などもテンプレート化できます。
次は 複数トラックのジャズ風スウィング曲生成 に進めますか?
はい。お願いします。
ChatGPT:
了解です!
では C++ テンプレートを活かして 複数トラックでジャズ風スウィング曲を自動生成するフレームワーク例 を作ってみます。
ポイントは以下です:
トラック構成例
メロディ(Cメジャー・スケール)
ウォーキングベース(ジャズ風コード進行 II-V-I)
ドラム(スウィング感あるキック+スネア+ハイハット)
テンプレート戦略
RhythmProcessor で各トラックのリズムを切り替え
NoteGenerator で音程・コード進行を生成
midifile ライブラリ使用
MIDIファイル生成が簡単
複数トラック対応
C++ 複数トラックジャズ自動生成例
#include
#include
#include
#include "MidiFile.h"
using namespace std;
using namespace smf;
// ---------------- Note構造体 ----------------
struct Note {
int pitch;
double start; // 拍単位
double dur; // 拍単位
int velocity;
};
// ---------------- RhythmProcessor テンプレート ----------------
template
class RhythmProcessor {
public:
RhythmProcessor(Strategy s) : strategy(s) {}
double process(double time, int index) {
return strategy(time, index);
}
private:
Strategy strategy;
};
// ---------------- ストレート8分音符 ----------------
struct StraightRhythm {
double operator()(double time, int) { return time; }
};
// ---------------- スウィング8分音符 ----------------
struct SwingRhythm {
double operator()(double time, int index) {
return time + ((index % 2 == 0) ? 0.0 : -0.1667); // 2:1比率
}
};
// ---------------- メロディ生成 ----------------
class MelodyGenerator {
public:
int notesPerBar() const { return 4; }
Note nextNote(int index) {
static int scale[] = {60, 62, 64, 65, 67, 69, 71, 72}; // C major
return {scale[index % 8], 0.0, 0.5, 90};
}
};
// ---------------- ベース生成(II-V-I) ----------------
class BassGenerator {
public:
int notesPerBar() const { return 4; }
Note nextNote(int index) {
// 2小節ごとの II-V-I パターン
int bar = index / 4;
int chordRoot;
switch (bar % 3) {
case 0: chordRoot = 62; break; // Dm7
case 1: chordRoot = 67; break; // G7
default: chordRoot = 60; // Cmaj7
}
return {chordRoot + (index % 4 % 2 ? 7 : 0), 0.0, 0.5, 80};
}
};
// ---------------- ドラム生成 ----------------
class DrumGenerator {
public:
int notesPerBar() const { return 4; }
Note nextNote(int index) {
int drumPitch;
switch (index % 4) {
case 0: drumPitch = 36; break; // Kick
case 1: drumPitch = 38; break; // Snare
default: drumPitch = 42; // Hi-hat
}
return {drumPitch, 0.0, 0.5, 100};
}
};
// ---------------- トラック構築テンプレート ----------------
template
void buildTrack(MidiFile &midi, int trackNum, int channel, int bars) {
midi.addTrack(1);
Generator gen;
RhythmProcessor rhythm(RhythmStrategy{});
int tpq = midi.getTicksPerQuarterNote();
int tick = 0;
int totalNotes = bars * gen.notesPerBar();
for (int i = 0; i < totalNotes; i++) {
Note n = gen.nextNote(i);
double startBeat = i * n.dur;
startBeat = rhythm.process(startBeat, i);
tick = int(startBeat * tpq);
// Note On
midi.addNoteOn(trackNum, tick, channel, n.pitch, n.velocity);
// Note Off
midi.addNoteOff(trackNum, tick + int(n.dur * tpq), channel, n.pitch);
}
}
// ---------------- メイン ----------------
int main() {
MidiFile midi;
midi.absoluteTicks();
midi.setTicksPerQuarterNote(480);
int bars = 4;
// トラック0: メロディ
buildTrack<MelodyGenerator, SwingRhythm>(midi, 0, 0, bars);
// トラック1: ベース(ウォーキングベース)
buildTrack<BassGenerator, SwingRhythm>(midi, 1, 1, bars);
// トラック2: ドラム(Ch.10)
buildTrack<DrumGenerator, SwingRhythm>(midi, 2, 9, bars);
midi.sortTracks();
midi.write("jazz_swing.mid");
cout << "Generated jazz_swing.mid with multiple tracks and swing rhythm!" << endl;
return 0;
}
✨ 特徴
複数トラック自動生成
メロディ、ベース、ドラムを分離して生成
スウィングリズム反映
SwingRhythm で8分音符を 2:1 比率に調整
テンプレート設計
トラックごとのジェネレータとリズム戦略を切替可能
他のリズムやアルゴリズム作曲を簡単に差し替え可能
💡 拡張アイデア:
II-V-I コード進行をランダムに変化させる
複雑なメロディ生成(マルコフ連鎖や確率モデル)
リアルタイムMIDI出力で即演奏
希望があれば、次に 「ベースラインとコード進行をよりジャズらしく自動生成」 まで組み込んだ完全自動ジャズ作曲C++フレームワークも作れます。
作りますか?
はい。お願いします。
ChatGPT:
わかりました!
では、前回のテンプレート+スウィング化をベースにして、ジャズ風のコード進行(II-V-I)とウォーキングベースを自動生成するC++フレームワーク例を作ります。
ポイントは以下です:
コード進行をランダムまたは循環で生成(II-V-I, I-VI-II-Vなど)
ウォーキングベースをコードに合わせて生成
メロディをスケールやコードトーンからランダム生成
スウィングリズムでジャズらしいフィールを表現
複数トラック(メロディ / ベース / ドラム)
C++ ジャズ自動作曲フレームワーク例
#include
#include
#include
#include
#include "MidiFile.h"
using namespace std;
using namespace smf;
// ---------------- Note構造体 ----------------
struct Note {
int pitch;
double start; // 拍単位
double dur; // 拍単位
int velocity;
};
// ---------------- RhythmProcessor テンプレート ----------------
template
class RhythmProcessor {
public:
RhythmProcessor(Strategy s) : strategy(s) {}
double process(double time, int index) { return strategy(time, index); }
private:
Strategy strategy;
};
// ---------------- スウィング8分音符 ----------------
struct SwingRhythm {
double operator()(double time, int index) {
return time + ((index % 2 == 0) ? 0.0 : -0.1667);
}
};
// ---------------- コード進行(II-V-I) ----------------
struct Chord {
int root;
vector tones; // コードトーン
};
vector generateProgression(int bars) {
vector progression;
for (int i = 0; i < bars; i++) {
switch (i % 3) {
case 0: progression.push_back({62, {62, 65, 69}}); break; // Dm7
case 1: progression.push_back({67, {67, 71, 74}}); break; // G7
default: progression.push_back({60, {60, 64, 67}}); break; // Cmaj7
}
}
return progression;
}
// ---------------- メロディ生成 ----------------
class MelodyGenerator {
public:
MelodyGenerator(const vector& prog) : progression(prog), rng(rd()), dist(-1,1) {}
Note nextNote(int index) {
int bar = index / notesPerBar();
int noteIndex = index % notesPerBar();
const Chord& chord = progression[bar % progression.size()];
int pitch = chord.tones[noteIndex % chord.tones.size()] + dist(rng); // コードトーン±1
return {pitch, 0.0, 0.5, 90};
}
int notesPerBar() const { return 4; }
private:
const vector& progression;
random_device rd;
mt19937 rng;
uniform_int_distribution dist;
};
// ---------------- ウォーキングベース生成 ----------------
class BassGenerator {
public:
BassGenerator(const vector& prog) : progression(prog) {}
Note nextNote(int index) {
int bar = index / notesPerBar();
int pos = index % notesPerBar();
const Chord& chord = progression[bar % progression.size()];
int pitch = chord.root + ((pos % 2) ? 7 : 0); // 根音と5度交互
return {pitch, 0.0, 0.5, 80};
}
int notesPerBar() const { return 4; }
private:
const vector& progression;
};
// ---------------- ドラム生成 ----------------
class DrumGenerator {
public:
int notesPerBar() const { return 4; }
Note nextNote(int index) {
int drumPitch;
switch (index % 4) {
case 0: drumPitch = 36; break; // Kick
case 1: drumPitch = 38; break; // Snare
default: drumPitch = 42; // Hi-hat
}
return {drumPitch, 0.0, 0.5, 100};
}
};
// ---------------- トラック構築テンプレート ----------------
template
void buildTrack(MidiFile &midi, int trackNum, int channel, int bars, Generator& gen) {
midi.addTrack(1);
RhythmProcessor rhythm(RhythmStrategy{});
int tpq = midi.getTicksPerQuarterNote();
int tick = 0;
int totalNotes = bars * gen.notesPerBar();
for (int i = 0; i < totalNotes; i++) {
Note n = gen.nextNote(i);
double startBeat = i * n.dur;
startBeat = rhythm.process(startBeat, i);
tick = int(startBeat * tpq);
midi.addNoteOn(trackNum, tick, channel, n.pitch, n.velocity);
midi.addNoteOff(trackNum, tick + int(n.dur * tpq), channel, n.pitch);
}
}
// ---------------- メイン ----------------
int main() {
MidiFile midi;
midi.absoluteTicks();
midi.setTicksPerQuarterNote(480);
int bars = 8;
vector<Chord> progression = generateProgression(bars);
MelodyGenerator melodyGen(progression);
BassGenerator bassGen(progression);
DrumGenerator drumGen;
// トラック0: メロディ
buildTrack<MelodyGenerator, SwingRhythm>(midi, 0, 0, bars, melodyGen);
// トラック1: ベース
buildTrack<BassGenerator, SwingRhythm>(midi, 1, 1, bars, bassGen);
// トラック2: ドラム (Ch.10)
buildTrack<DrumGenerator, SwingRhythm>(midi, 2, 9, bars, drumGen);
midi.sortTracks();
midi.write("jazz_auto.mid");
cout << "Generated jazz_auto.mid with auto II-V-I and walking bass!" << endl;
return 0;
}
✨ 特徴
II-V-I コード進行を自動生成
ループで 8小節の II-V-I パターンを生成
ウォーキングベース
根音と5度を交互に配置
メロディ
コードトーン ±1 のランダム化でジャズらしい揺らぎ
スウィングリズム
8分音符を 2:1 比率に調整
複数トラック生成
MIDI出力して即再生可能
💡 拡張案:
テンポ変化(BPM変動でグルーブ感アップ)
コード進行バリエーション(I-VI-II-V、ブルース進行など)
ハーモニー重ね合わせ(アルト・テナー・ピアノなど)
このフレームワークを使えば C++ だけで簡単にジャズ風自動作曲 が可能です。
必要であれば、次に 「完全自動でメロディ・ベース・ドラムを生成してMIDI再生まで行うデモ」 まで拡張することもできます。
進めますか?
はい。お願いします。
ChatGPT:
わかりました!
では、前回のテンプレート+スウィング化をベースにして、ジャズ風のコード進行(II-V-I)とウォーキングベースを自動生成するC++フレームワーク例を作ります。
ポイントは以下です:
コード進行をランダムまたは循環で生成(II-V-I, I-VI-II-Vなど)
ウォーキングベースをコードに合わせて生成
メロディをスケールやコードトーンからランダム生成
スウィングリズムでジャズらしいフィールを表現
複数トラック(メロディ / ベース / ドラム)
C++ ジャズ自動作曲フレームワーク例
#include
#include
#include
#include
#include "MidiFile.h"
using namespace std;
using namespace smf;
// ---------------- Note構造体 ----------------
struct Note {
int pitch;
double start; // 拍単位
double dur; // 拍単位
int velocity;
};
// ---------------- RhythmProcessor テンプレート ----------------
template
class RhythmProcessor {
public:
RhythmProcessor(Strategy s) : strategy(s) {}
double process(double time, int index) { return strategy(time, index); }
private:
Strategy strategy;
};
// ---------------- スウィング8分音符 ----------------
struct SwingRhythm {
double operator()(double time, int index) {
return time + ((index % 2 == 0) ? 0.0 : -0.1667);
}
};
// ---------------- コード進行(II-V-I) ----------------
struct Chord {
int root;
vector tones; // コードトーン
};
vector generateProgression(int bars) {
vector progression;
for (int i = 0; i < bars; i++) {
switch (i % 3) {
case 0: progression.push_back({62, {62, 65, 69}}); break; // Dm7
case 1: progression.push_back({67, {67, 71, 74}}); break; // G7
default: progression.push_back({60, {60, 64, 67}}); break; // Cmaj7
}
}
return progression;
}
// ---------------- メロディ生成 ----------------
class MelodyGenerator {
public:
MelodyGenerator(const vector& prog) : progression(prog), rng(rd()), dist(-1,1) {}
Note nextNote(int index) {
int bar = index / notesPerBar();
int noteIndex = index % notesPerBar();
const Chord& chord = progression[bar % progression.size()];
int pitch = chord.tones[noteIndex % chord.tones.size()] + dist(rng); // コードトーン±1
return {pitch, 0.0, 0.5, 90};
}
int notesPerBar() const { return 4; }
private:
const vector& progression;
random_device rd;
mt19937 rng;
uniform_int_distribution dist;
};
// ---------------- ウォーキングベース生成 ----------------
class BassGenerator {
public:
BassGenerator(const vector& prog) : progression(prog) {}
Note nextNote(int index) {
int bar = index / notesPerBar();
int pos = index % notesPerBar();
const Chord& chord = progression[bar % progression.size()];
int pitch = chord.root + ((pos % 2) ? 7 : 0); // 根音と5度交互
return {pitch, 0.0, 0.5, 80};
}
int notesPerBar() const { return 4; }
private:
const vector& progression;
};
// ---------------- ドラム生成 ----------------
class DrumGenerator {
public:
int notesPerBar() const { return 4; }
Note nextNote(int index) {
int drumPitch;
switch (index % 4) {
case 0: drumPitch = 36; break; // Kick
case 1: drumPitch = 38; break; // Snare
default: drumPitch = 42; // Hi-hat
}
return {drumPitch, 0.0, 0.5, 100};
}
};
// ---------------- トラック構築テンプレート ----------------
template
void buildTrack(MidiFile &midi, int trackNum, int channel, int bars, Generator& gen) {
midi.addTrack(1);
RhythmProcessor rhythm(RhythmStrategy{});
int tpq = midi.getTicksPerQuarterNote();
int tick = 0;
int totalNotes = bars * gen.notesPerBar();
for (int i = 0; i < totalNotes; i++) {
Note n = gen.nextNote(i);
double startBeat = i * n.dur;
startBeat = rhythm.process(startBeat, i);
tick = int(startBeat * tpq);
midi.addNoteOn(trackNum, tick, channel, n.pitch, n.velocity);
midi.addNoteOff(trackNum, tick + int(n.dur * tpq), channel, n.pitch);
}
}
// ---------------- メイン ----------------
int main() {
MidiFile midi;
midi.absoluteTicks();
midi.setTicksPerQuarterNote(480);
int bars = 8;
vector<Chord> progression = generateProgression(bars);
MelodyGenerator melodyGen(progression);
BassGenerator bassGen(progression);
DrumGenerator drumGen;
// トラック0: メロディ
buildTrack<MelodyGenerator, SwingRhythm>(midi, 0, 0, bars, melodyGen);
// トラック1: ベース
buildTrack<BassGenerator, SwingRhythm>(midi, 1, 1, bars, bassGen);
// トラック2: ドラム (Ch.10)
buildTrack<DrumGenerator, SwingRhythm>(midi, 2, 9, bars, drumGen);
midi.sortTracks();
midi.write("jazz_auto.mid");
cout << "Generated jazz_auto.mid with auto II-V-I and walking bass!" << endl;
return 0;
}
✨ 特徴
II-V-I コード進行を自動生成
ループで 8小節の II-V-I パターンを生成
ウォーキングベース
根音と5度を交互に配置
メロディ
コードトーン ±1 のランダム化でジャズらしい揺らぎ
スウィングリズム
8分音符を 2:1 比率に調整
複数トラック生成
MIDI出力して即再生可能
💡 拡張案:
テンポ変化(BPM変動でグルーブ感アップ)
コード進行バリエーション(I-VI-II-V、ブルース進行など)
ハーモニー重ね合わせ(アルト・テナー・ピアノなど)
このフレームワークを使えば C++ だけで簡単にジャズ風自動作曲 が可能です。
必要であれば、次に 「完全自動でメロディ・ベース・ドラムを生成してMIDI再生まで行うデモ」 まで拡張することもできます。
進めますか?
ChatGPT の回答は必ずしも正しいとは限りません。重要な情報は確認するようにしてください。