jackdaw - 新たなDTMの可能性について
現在、電子音楽では主に12平均律が使われています。しかしそれ以外の5平均律や純正律等の響きを利用したいこともあります。そんなとき普通のDAWでは12平均律以外の音がなかなか出せずに苦労します。特に1オクターブ中に13個以上の音を入れたいときには策がほとんど無いのが現状です。
そこでこの度新たなDTMの可能性を探るための試みとして、jackdawという、音階の概念の無いDAWを開発することにしました。
jackdawは純正律に特化しており、トニックという音を基準として全ての音の高さが整数比で扱われます。例えばトニックをドの音にしておくと、<3/2>はソの音、<2/1>は1オクターブ高いドの音を表します。このようにして全ての音を比のみで表すことで、自由に音階を作ることができます。
ダウンロード・インストール
現在ソースコードはここにあります。
github.comダウンロードしてmakeして下さい(make jackdaw)。
./jackdawに楽譜ファイル(書き方は後述)を渡して実行すると、その中身に即してwavファイルが作られます。出力ファイル名はオプション-oで指定しますが、指定されなければa.wavになります。
楽譜ファイルの書き方
exampleに例がいくつかあるので分からなかったらそれを見てください。
score文
「score{」で始まって「}」で終わる部分の中身が楽譜となります。
(tonic 440) |
トニックを440Hzに設定します。 |
(modulate 3/2) |
トニックを現在の3/2倍にします。転調に使います。 |
(tempo 120) |
BPMを120にします。 |
(velocity 0.5) |
全体の音量を0.5倍にします。 |
<3/2>:4 |
トニックの3/2倍の音程の、長さ4の音符です。 |
<3/2>0.5:4 |
トニックの3/2倍の音程で、音量が0.5倍になった、長さ4の音符です。 |
0:4 |
長さ4の休符です。 |
(rhythm 1,1,2) |
リズムを(長さ1,長さ1,長さ2)の繰り返し(タタタン)に設定します。 音符の長さを省略するとリズムに従って長さが決まります。 |
音符をカンマ「,」で区切って並べるとメロディーが作れます。
<5/4>:1, <9/8>:.5, <1/1>:.5, <9/8>:1 (『七つの子』冒頭)
「/」を使うことで2つ以上のメロディーを重ねて同時に鳴らすことができます。
<5/4>:1, <9/8>:.5, <1/1>:.5, <9/8>:1 / <1/2>:2, <3/8>:1 (ベースラインを追加)
「|」を使うことで曲を区切ることができます。
score文の中では#〜#で囲まれた部分と##から行末までがコメントとして扱われます。
set文
今のところ使い道は
set samplerate = 44100
くらいです。サンプルレートが設定できます。
他の書き方:
set {samplerate = 44100}
こう書いても同じです。こちらは途中で改行をはさむことができます。
define文
マクロを定義できます。定義したマクロはその後波括弧{}で括ることで使うことができます。
define melody = <1/1>:1,<3/2>:1,<2/1>:2
score{{melody},{melody},{melody}} ## メロディーが3回繰り返される
message文
標準出力にメッセージを出力できます。
message this is message.
最後に改行されます。
message {this is message.}
の方だと最後に改行されません。
comment文
何もしません。
import文
同じディレクトリ内にある他のファイルを読み込みます。
header.jkdhファイルに"message header is loaded."、main.jkdファイルに"import header"と書いておいて./jackdaw main.jkdを実行すると、"header is loaded."と出力されます。
tonicやtempo、define文などを書いたヘッダファイルを作っておくと便利です。
system文
C言語のsystem関数を呼び出します。"system rm -rf /"とすると"rm -rf /"が実行できます。
今後の課題
音源という概念が今の所存在しません。 (jackdaw.cを見ると分かるのですが今後音源をどう扱っていくかについては色々考えています)
あとはまだ色々バグとかあると思います、じっくり潰していきます
このプロジェクトがあなたの役に立てればこの上なく光栄です。
目の前に暖簾があったとき
目の前に暖簾があったとき、それを1万回押そうと試みる、私のような人間がいる。
暖簾を1回押す労力がどれほど軽いものであるかは皆が知っている。しかしそれが1万回になると誰も知らない。
私は、周りに誰もいないのを確認して、暖簾を押し始める。しかし1万回も続けることはできず、1000回押したところで疲れて諦めてしまう。しかしたとえ予定の1割であろうと、初めに思っていたよりは遥かに大変だったから、私はそれで十分とする。
私の労力が、例えば企業して1億円稼ぐことや勉強して司法試験に受かることと比べるとどれほどのものなのかなど誰も知らない。だから私は、自分の主観を存分に挟んでそれを評価することができる。
私は見えない努力をした。
しかし私は、外からの評価が無いと満たされない。そこで身近にいる人間を1人捕まえてきて、こっそり打ち明ける。「暖簾を1000回押した。これはお前の思うよりずっと大変なことだぞ」と。暖簾を押すために必要な仕事量に関するきちんとしたデータは与えず、なんとなくぼやかした言い方をする。
もちろんその人は暖簾を1000回はおろか3回も続けて押した経験が無い。その人が聞かされるのは、「暖簾を1000回押すことは、予想よりも大変なことだ」ということだけだ。
莫迦はそれを信じる。
かつて私は莫迦だった。そして今は、莫迦を捕まえる側にもなった。
私のような人間を、ある者は「哀れな世間知らず」と呼ぶかもしれないし、またある者は「こじらせた中二病」と呼ぶかもしれない。実際それで概ね正しいのだろう。しかしそういった声に対しては耳を塞ぐことができる。実際この世には十分に評価されない努力というものが存在するから、それを隠れ蓑にすることができる。
私は幸せなのだろうか。井の中を泳ぎ回るためだけに、私は生まれてきたのだろうか。本人が幸せだと思えば幸せなのだろうか。割り切ってしまえばよいのだろうか。もし世界中の人間がそうやって何もしなかったら、どうなってしまうのだろうか。
私は、なんとなくだが、この生き方が嫌いだと思った。
C言語で電卓を作ろう
C言語で簡単な数式を読んで計算するプログラムを作ります。
数式内で使える演算子は「+」「-」「*」の3つで、括弧は使えません。
使える数は0以上の整数で、負の数は使えません。
優先順位は「*」が高く、「+」と「-」が低くなるようにします。「2 + 3 * 4」は 20 ではなく 14 になります。
3つの変数 a、b、c を用意し、a と c を 0、b を 1 で初期化しておきます。
受け取った数式を最初から順番に読んでいって、「*」があったら b に a をかけ、「+」「-」があったら c に a * b を足します。これらの演算子を読んだ後、a に 0 を代入し、bに「+」なら 1、「-」なら -1 を代入します。数字を読み込んだ場合は a に 10 をかけて足します。
これを文字列の終わりまで繰り返したら、a * b + c が計算の答えです。
コードはこちら
+と-と*しか無いからこそ実現できるシンプルさです。
でも変数を1つ増やせば「/」も追加できないことはなさそう。
ただ、括弧を使いたいときにこのやり方は使えないので他のやり方を考える必要があります。
X
Arch Linux に GUI環境を構築した。GUI環境は欲しい時にだけ使いたくて、普段の作業はコンソールでやるつもりだったので、startxコマンドでXを起動→$mod+EでXを終了ができるようにした。ウィンドウマネージャはi3を選んだ。
xorg-server, xorg-apps, xorg-xinitをインストール。この時点でstartxとするとXが立ち上がり、即座に終了する。
i3, dmenu, xtermをインストール。さらにフォントを扱うfontconfig, xorg-font-utils も必要だが、これはXフォントを何か1つインストールすると手に入る(公式リポジトリにある otf-ipafont など)。
/etc/X11/xinit/xinitircを~にコピーして編集する。最後のtwmとかxclockとかxtermとかの行は向こうが勝手に走らせているだけなので消す(そもそもtwmとxclockはインストールしてない)。そしてexec i3と書き加える。
この状態でstartxとするとi3が起動する。言われるがままにmodキーなどを設定する。$mod+Enterでxtermを起動する。psでi3を探して殺し、コンソール環境に戻る。すると/.config/i3にconfigという設定ファイルが作られているので、これを編集して$mod+Shift+eのショートカットが書かれた行を bindsym $mod+Shift+e exit と書き換える。この状態でstartxとすると$mod+Shift+eで即座にXが閉じるようになっている。
キーボードレイアウトは/etc/X11/xorg.conf.dにファイルを作って指定する。00-keyboard.confの1つだけを書いた。
Section "InpurClass"
Identifier "system-keyboard"
MatchIsKeyboard "on"
Option "XKbLayout" "us"
Option "XKbVariant" "dvp"
EndSection
設定は完了した。firefoxをインストールして挙動を確認したりする。あと僕は/.config/i3/configの最後にbindsym $mod+Escape exec i3lock -c000000と書き加えて、即座に画面ロックができるようにしたりした。
Arch Linux
今年の5月7日にWindowsを捨ててArch Linuxに切り替えてからしばらく経ったので、今の所どういう感じで使っているのかをメモしておきたいと思います。
最初はBIOS, MBRだったけど8/26にUEFI, GPTでやり直した。パーティションは/boot(512M), swap(1G), /(残り464.3G)。メモリ4Gもあるんだからswapとか作るだけ無駄だった。次気をつける
ブートローダはsystemd-boot。espは/boot。esp/loader/entriesにはarch.confとarch-fallback.confの2つを書いて、timeoutとか面倒だったのでesp/loader/loader.confにはdefaultだけ書いといた。
キーマップはDvorak派。/etc/vconsole.confには
KEYMAP=dvorak-programmer.map
FONT=Lat2-Terminus16
と書いた。ちなみにテンキーがないので数字入力するのちょっと面倒。
ここからしばらく有線接続を使う。dhcpcdをsystemd startしたら普通にping通った。普段は部屋でスタンドアローン、ネット環境が欲しいときだけ家のリビングでイーサネットみたいなのを数カ月間続けた。
git, wgetをインストール。
pacman使ってたら周りの人が他のを使うことを勧められた。たくさんあってどれを選べばいいのか分からなかったので常務くんイチオシのyayをインストール。yay自体が公式リポジトリに無いのでgitで入れた。
C++書くのでboostを入れた。
音を鳴らしたかったのでalsa-utilsを入れた。頑張ったら音が鳴った。/etc/modprobe.d/alsa-base.confと/etc/asound.confを頑張って書いて良い感じにした。結構大変だった。
コンソール上で画像を表示したかったのでfbidaを入れた。そのままだとfbiで画像が表示できなかった。解決策は何らかのXフォントをインストールして/usr/share/fontsに入れることだった。Notoフォントを入れた。
さて。この辺りでなんかGUI環境が欲しくなった。
でもそんなにGUIばっかりに頼りたくはなくて、好きなときにCLIに戻れるようにしたかった。
そこで、Xをインストールするもののディスプレイマネージャは入れず、GUI環境が欲しいときにstartxコマンドを打つことにした。ウィンドウマネージャはi3。
xorg-server、xorg-apps、xorg-init を入れる。この時点でstartxを打つとXが起動して即座に終了する。
Dvorak派なので/etc/X11/xorg.conf.dにあった00-keyboard.confを書き換えた。ここではdvorak-programmerは"dvp"という名前だった。
i3、dmenuを入れて普通にやった。
突然WiFiが使いたくなったのでnetworkmanagerを入れてnmtuiを使ってWiFiに繋いだ。
日本語が入力したくなったのでfcitx mozcを入れた。
動画再生にmplayerを入れた。
画像編集にmyPaintを入れた。
動画編集にflowbladeを入れた。
以上、大体こんな感じ。
ネタ切れ(・ω・`)
最近ブログに書くネタが無くてですね…
最後に更新してからもう半年が経とうとしているんです。切れたんですよネタが。ホント、書くことがない。
もういくら考えてもネタが思い付かなくて。
それでですね、ついに苦肉の策に出ようと思ったんですよ。
「最近ブログに書くネタが無くてですね…」とブログ自体に書くことにしたんです。ネタが無いということをネタにする。メタですね。
でもそれだけじゃつまらない。単に一度メタ化するだけじゃ、ネタが無いのと同じですよ。
そこでこうすることにしたんです。
「「最近ブログに書くネタが無くてですね…」とブログ自体に書くことにしたんです。ネタが無いということをネタにする。メタですね。」という文をブログに書く。つまり2重のメタ化です。
さらに、「「「最近ブログに書くネタが無くてですね…」とブログ自体に書くことにしたんです。ネタが無いということをネタにする。メタですね。」という文をブログに書く。つまり2重のメタ化です。」と書くと3重にメタ化することもできます。
ここで気付くわけですね、「さらに、「「「最近ブログに書くネタが無くてですね…」とブログ自体に書くことにしたんです。ネタが無いということをネタにする。メタですね。」という文をブログに書く。つまり2重のメタ化です。」と書くと3重にメタ化することもできます。」と書いた時点で既に4重のメタ化が起こっていると。
ペアノの公理ってご存じですか?あれに似ていますね。つまり任意の自然数nに対してn重のメタ化が可能となります。
おや…?私は「任意の自然数nに対してn重のメタ化が可能となります。」と書いてしまいました。これをここに書くと、いったい何重のメタ化になるんでしょう。
自然数の範囲を超えてしまいましたね。じゃあこれを、「どんな自然数よりも大きい数」としてωと名付けましょう。
さて、ω重のメタ化ができました。
…ん?「ω重のメタ化ができました」?これは何重のメタ化ですか?
ωの次の数。ω+1です。私はω+1重のメタ化を行ったわけです。
はい、たった今「私はω+1重のメタ化を行ったわけです。」と書くことでω+2重のメタ化が行われましたね。
そして「はい、たった今「私はω+1重のメタ化を行ったわけです。」と書くことでω+2重のメタ化が行われましたね。」と書いたのでメタ化はω+3重です。
これを先程と同様に繰り返すと、自然数nに対しω+n重のメタ化が可能です。
……んん?「自然数nに対しω+n重のメタ化が可能です」???
そうですね。ω+ω、即ちω✕2です。
「ω✕2です」はω✕2+1。「「ω✕2です」はω✕2+1。」はω✕2+2。「「「ω✕2です」はω✕2+1。」はω✕2+2。」はω✕2+3。これを繰り返せばω✕2+nが作れます。そして「ω✕2+nが作れます。」はω✕3です。
これを繰り返していくと、ωと任意の自然数nの積ω✕nが作れます。
おや???「ω✕nが作れます」?????(しつこいですよ)
はい、ω×ωですね。
これが順序数の世界です。
ω^ωも作ってみてください。
ではまた(・ω・`)
みんな大好き,ポインタ/配列/参照 ~多次元配列まとめ~
汎整数型(論理型(bool),文字型(charなど),整数型(intやlongなど))と浮動小数点型を合わせて算術型といい,算術型とvoid型を合わせて基本型といいます。基本型からは「ポインタ型」「配列型」「参照型」の3種類の組込み型が得られますが,この記事ではその中でも特に(単なる基本型へのポインタ型,配列型,参照型を除いた)2重以上のポインタ型,配列型,参照型の扱い方について説明します。constについては説明しません。templateについては説明しません。変数の型だけを見るので,変数名は全てハンガリアン記法の接頭辞のみとします。
まずCにあるポインタ型と配列型から先に説明します。
int i;
iはint型です。関数hoge(int)を宣言しておくと,hoge(i)でhoge(int)が呼び出されます。
intに*,[1],[2]を付けるとそれぞれ次の型が得られます。
int *pi, a1i[1], a2i[2];
piはint*型,a1iはint[1]型,a2iはint[2]型です。関数呼び出しではこれらは全て同じものとして扱われます。つまり関数hoge(int*)を宣言しておくと,hoge(pi),hoge(a1i),hoge(a2i)の全てにおいてhoge(int*)が呼び出されるということです。
int*に*,[1],[2]を付けるとそれぞれ次の型が得られます。
int **ppi, *a1pi[1], *a2pi[2];
ppiはint**型,a1piはint*[1]型,a2piはint*[2]型です。関数呼び出しではこれらは全て同じものとして扱われます。つまり関数hoge(int**)を宣言しておくと,hoge(ppi),hoge(a1pi),hoge(a2pi)の全てにおいてhoge(int**)が呼び出されるということです。
int[1]に*,[1],[2]を付けるとそれぞれ次の型が得られます。
int (*pa1i)[1], a1a1i[1][1], a2a1i[2][1];
pa1iはint(*)[1]型,a1a1iはint[1][1]型,a2a1iはint[2][1]型です。関数呼び出しではこれらは全て同じものとして扱われます。つまり関数hoge(int(*)[1])を宣言しておくと,hoge(pa1i),hoge(a1a1i),hoge(a2a1i)の全てにおいてhoge(int(*)[1])が呼び出されるということです。hoge(int**)に渡したいときはキャストします。
int[2]に*,[1],[2]を付けるとそれぞれ次の型が得られます。
int (*pa2i)[2], a1a2i[1][2], a2a2i[2][2];
pa2iはint(*)[2]型,a1a2iはint[1][2]型,a2a2iはint[2][2]型です。関数呼び出しではこれらは全て同じものとして扱われます。つまり関数hoge(int(*)[2])を宣言しておくと,hoge(pa2i),hoge(a1a2i),hoge(a2a2i)の全てにおいてhoge(int(*)[2])が呼び出されるということです。
変数Aの型がTであるとき,&AはT*型です。即ちint(**[2])[3]型変数a3ppa2iのアドレス&a3ppa2iはint(**(*)[2])[3]型です。
変数Bの型がT*であるとき,*BはT型です。即ちint(*(**)[4])[2]型変数a2pa4ppiの参照*a2pa4ppiはint(*(*)[4])[2]型です。
変数を配列型にキャストすることはできません。reinterpret_castとかそういう問題ではなく,言語仕様上絶対に不可能です。
int a0i[0]はint[0]型です。
sizeof iが4,sizeof piが8であった場合,sizeof a1iは4×1=4,sizeof a2iは4×2=8,sizeof ppiは8,sizeof a1piは8×1=8,sizeof a2piは8×2=16,sizeof pa1iは8,sizeof a1a1iは4×1×1=4,sizeof a2a1iは4×1×2=8,sizeof pa2iは8,sizeof a1a2iは4×2×1=8,sizeof a2a2iは4×2×2=16です。配列+整数のsizeof,即ちsizeof(a2i+1)やsizeof (a2a1i+0)などは全てsizeof pi(=8)に等しくなります。sizeof *a2a1iは*a2a1iがint[1]型なのでsizeof a1i(=4)に等しくなります。sizeof *(a2a1i+0)も同じくsizeof a1iに等しくなります。a2a1i[0]は*(a2a1i+0)の糖衣構文なので,sizeof a2a1i[0]も同じくsizeof a1iに等しくなります。
C++にはポインタ,配列に加え,参照というものがあります。
関数hoge_cpp(int&)をhoge_cpp(i)で,関数hoge_cpp(int&&)をhoge_cpp(i+0)で,関数hoge_cpp(int*&)をhoge_cpp(pi)で,関数hoge_cpp(int*&&)をhoge_cpp(pi+0)やhoge_cpp(a1i)やhoge_cpp(a1i+0)で,関数hoge_cpp(int(&)[1])をhoge_cpp(a1i)で,関数hoge_cpp(int(&)[2])をhoge_cpp(a2i)で呼び出せます。hoge_cpp(a1i)でhoge_cpp(int(&)[2])を呼び出すことはできないので,テンプレートを使って配列の大きさごとに関数を作れば(使うたびに関数が増えますが)関数内で配列の大きさを知ることができます。
とりあえずこの記事はここまでです。部分的に情報密度が高くなってしまいましたが,その一部でもあなたの役に立てば幸いです。