コードジェネレーターベースの音楽ジェネレーター

こんにちは、habrayuzer! このトピックでは、楽曲を生成するという私の考えについてお話します。 pythonに基づいて音楽のリズムを記述するための言語を作成し、この言語のコンパイラをWaveファイルに記述して、非常に素晴らしい電子構成を取得しましょう。



猫へようこそ。



すぐにミュージシャンの話を聞くのが待ちきれず、オンラインの場合: クリック可能 (最初の数秒間は成功せず、その後は非常に正常です)。



参加する代わりに



ここで、音楽生成に関するハブ記事( google.ru>音楽生成サイト:habrahabr.ru )を読んで、気に入っています。 そして、スラッシュ遺伝子(ジャンクコードジェネレーター)に出会いました。 この間ずっと、私は音楽を聴き、それぞれの作曲に繰り返し音がしているという事実に注目しました。



例:



tynt tynts、pam pam pam、param pam pam pam ntnts tynt、pam pam、param pam pam pam puck tynts、pam pam、param pam pam



そして、これらの音のシーケンスすべてを、これらの音がエンコードされている身体のサイクルとして表現するという考えがありました。 これは、繰り返しデータの前に繰り返し回数を書き込む「学校のデータ圧縮」アルゴリズムにやや似ています。



それで、タスクがあります:



1)リズム記述言語の設計

2)コンパイラをバイトコード(音のシーケンス)で記述します

3)Waveファイルをデジタル化して書き込む



始めましょう。



リズム記述言語



コンパイラの理論についての多くの研究、レクサーの作成、トークンへの侵入の後、私はこのことにうんざりしました。 Python言語の構文を使用することが決定されました。 はい、はい、正確に。 この言語は、yieldステートメントの式をサポートしています。



yieldトピックは非常に広範囲であり、もしあなたがそれをよく知らず、知り合いになりたいなら、私は記事「How yield works」にあなたを送ります。



続けます。 同意しましょう。



いくつかの音声信号(以降、フレームと呼びます)を表すには、n(diap [0]、diap [1])の形式の関数を使用します。nはこの関数の数値です。 diapは、生成された頻度の初期値と最終範囲のリストまたはタプルです。



フォームの式を使用して、呼び出しをエンコードします。



yield "n(diap[0], diap[1])"
      
      







わかりやすくするために、コードの中央にあるジェネレーターの排気の例を次に示します。

  yield "19(400,800)" for _ in range(7): yield "0" yield "20(400,800)" yield "0" yield "21(400,800)" yield "22(400,800)" yield "0"
      
      







このソースコードにはyield "0"があります。これは、この場所にゼロのバイトシーケンスがあり、フレーム間で一時停止することを意味します(音楽がしっかりした音で出ないように)。



これは、(引き裂かれたコンテキストから)次のシーケンスを意味します:



19(400, 800); 0; 20(400, 800); 0; 20(400, 800); 0; 20(400, 800); 0; 20(400, 800); 0; 20(400, 800); 0; 20(400, 800); 0; 20(400, 800); 0; 21(400,800); 22(400,800); 0









次にフレーム関数とは何かを考えます。



n(diap [0]、diap [1])が呼び出されると、ランダム値rを持つキーnが連想配列に追加されます。ここで、diap [0] <= r <= diap [1]

これは、仮想マシンがバイトコードフレームを実行するために必要です。



フレームをバイトコードにコンパイルし、.wavでビルドする



それで、コンパイルする時です。



これをどのように行うのですか?



最初に、生成されたコードを調べて、キーが関数番号で値が範囲内のランダム変数である辞書をコンパイルする必要があります。 コードを解析するときにこれを行うか、生成中に直接行うことができます。 まさに2番目のオプションがあります。



リズム記述コード(以下CORと呼ぶ)は、次のように表すことができます。

 code = """ def temp():    """
      
      





注:コードでは、 二重引用符「



ここで、コードを文字列として保存します。 Pythonには、コードを実行できるexec関数があります。 そのアプリケーションを見てみましょう:



 def my_code(cd): namespace = {} exec(cd,namespace) return namespace["temp"]()
      
      







my_codeを呼び出して、コードをパラメーターとして文字列を渡すと、バイトコードのシーケンスを生成するリストジェネレーターが取得されます。



  print("Compiling...") lst = list(my_code(out.code)) print("Compiled!")
      
      







最初に、KORのフレーム関数への連続した呼び出しのリストがあります。



つまり、同じ例として、



19(400, 800); 0; 20(400, 800); 0; 20(400, 800); 0; 20(400, 800); 0; 20(400, 800); 0; 20(400, 800); 0; 20(400, 800); 0; 20(400, 800); 0; 21(400,800); 22(400,800); 0









すでに連想配列があります(コード生成中に生成しました)。まずlstを実行し、そこから関数(辞書のキー)の数を削除し、辞書にアクセスして値を取得します。



ここに、このプロセスとデジタル化とWaveでの記録があります。

waveファイルへのリンク
 music = wave.open('out.wav', 'w') music.setparams((2, 1, freq, 0, 'NONE', 'not compressed')) for i in lst: if (i == "0"): packed_value = wave.struct.pack('h', 0) for _ in range(100): music.writeframes(packed_value) continue key = i[0:i.find("(")] frame = Syntax.struc.num[int(key)] duration = 0.05 samplerate = freq # Hz samples = duration * samplerate frequency = frame #Hz period = samplerate / float(frequency) # in sample points omega = N.pi * 2 / period xaxis = N.arange(int(period), dtype=N.float) * omega ydata = 16384 * N.sin(xaxis) signal = N.resize(ydata, samples) # 2-  -  ssignal = b'' for i in range(len(signal)): ssignal += wave.struct.pack('h', int(signal[i])) # transform to binary music.writeframes(signal) music.close()
      
      









すべてのコードはgithubで利用可能です( shitコードはジェネレーターにあります。このコードを約20回書き換え、完全なコンセンサスになるまで言語の構文を歪めたためです。 リファクタリングは実行されませんでした )。



PS Main.pyモジュールを実行し、ジェネレーターの結果をout.pyに保存します(松葉杖のため、この名前のみを受け入れます)。



All Articles