はじめに
RaspberryPiでFoxDot動かして、音出すと同時にGPIOからCV信号出せたら
モジュラーシンセと同期して面白いんじゃないかと思いまして
(dommune見てたらそんなツイート流れてきたので、、アイデアは丸パクリ)
※モジュラーシンセ持ってないのでarduino(Mozzi)で作ります。CV信号も0~5V出せばいいんでしょ?という
大雑把な認識なのでそれにあわせたハードシンセになりそうです
これを流用しようかなと思います
使用するのはRaspberryPi2です
SuperColliderセットアップ
RaspberryPiには、sonicPiというライブコーディング環境があって
SuperCollider-serverが入ってますがバージョンが3.7なのでFoxDotの要求に合わないので
いったん削除しておきます。
sudo apt remove supercollider-server
その後下記の通りに進めていきます
必要なライブラリをインストール
sudo apt-get update
sudo apt-get upgrade
sudo apt-get dist-upgrade
sudo apt-get install libjack-jackd2-dev libsndfile1-dev libasound2-dev libavahi-client-dev libreadline6-dev libfftw3-dev libxt-dev libudev-dev libcwiid-dev cmake qttools5-dev-tools libqt5webkit5-dev qtpositioning5-dev libqt5sensors5-dev
コンパイル&インストール
git clone --recursive git://github.com/supercollider/supercollider
cd supercollider
git checkout 3.9 #use latest version 3.9.x on branch 3.9
git submodule init && git submodule update
mkdir build && cd build
cmake -L -DCMAKE_BUILD_TYPE="Release" -DBUILD_TESTING=OFF -DSUPERNOVA=OFF -DNATIVE=ON -DSC_WII=ON -DSC_IDE=ON -DSC_QT=ON -DSC_ED=OFF -DSC_EL=OFF -DSC_VIM=ON ..
make -j 4 #これはrpi3のひとだけやるみたいです
sudo make install
sudo ldconfig
※make installめちゃ時間かかります。。
Jackの設定
-dhw:0を指定すると元からついているサウンドカード(ステレオジャック)で、別のUSBオーディオインターフェース使うときは-dhw:1に
します。僕はこれ https://plugable.com/japanese/products/usb-audio/
使っているので-dhw:1にしてみました
/usr/bin/jackd -P75 -dalsa -dhw:1 -r44100 -p1024 -n3
SuperCollider起動&FoxDotインストール
scide
IDEが起動するので、一番右のエディタ内で
Quarks.install("FoxDot")
と入力してその行にカーソルを持って行ってctrl+ENTER
これでSuperCollider側の準備はOKです
FoxDotインストール
pipで入るので簡単です
sudo pip3 install FoxDot
python3 -m FoxDot
ターミナルに下のように入れるとFoxDotのコンソール開きます
(なぜだか2画面にして上がエディタ、下がログ画面?みたいなのになりません。。)
FoxDotのコンソールで
p1 >> pluck()
と入力してその行でctrl+ENTERを押して音が出ればOKです
p1.stop()
で音が止まります
OSCサーバー
FoxDot自体からは音が出るわけではなく、FoxDot⇒SuperColliderへOSCを送っているので
別にOSCサーバーを立てて、FoxDotからOSCを受ければいいかなと考えました
※最初はこのOSCサーバーでGPIO制御してarduinoへの入力信号としようと思ったのですが
raspberryPiのGPIO出力は3.3V、arduinoUNOは5V受け。。別の回路作るの大変なのでシリアルで制御することにしました
つまり、、FoxDotをraspberryPiで動かす必要が無くなってしまっています。。(途中からノートPCにしました。。)
OSCサーバー立ち上げ
"""Small example OSC server
This program listens to several addresses, and prints some information about
received packets.
"""
"""Small example OSC server
This program listens to several addresses, and prints some information about
received packets.
"""
import argparse
import math
import time
from pythonosc import dispatcher
from pythonosc import osc_server
import socket
import serial
ser=serial.Serial("/dev/ttyUSB0",115200)
def print_foxdot(unused_addr,*p):#FoxDotからのoscメッセージが2種類あるので可変長の配列で受けてます
if (p[0]=="makeSound" or p[0]=="startSound"):
pass
else:
midi=p[p.index("midinote")+1]
mes=s+","+str(frq)+","+str(amp)
sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
sock.sendto(mes.encode("utf-8"),("127.0.0.1",12346))
CV=int(midi).to_bytes(1,"big")
if (midi!=0.0):
ser.write(CV)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--ip",default="127.0.0.1", help="The ip to listen on")
parser.add_argument("--port",type=int, default=12345, help="The port to listen on")#FoxDotの設定にあわせます
args = parser.parse_args()
dispatcher = dispatcher.Dispatcher()
dispatcher.map("/s_new",print_foxdot)
dispatcher.map("/s_new",print)#無くてもいいのですが、ちょっと画面が派手になるかなと思って受信したOSCメッセージを表示するようにしてます
server = osc_server.ThreadingOSCUDPServer(
(args.ip, args.port), dispatcher)
print("Serving on {}".format(server.server_address))
server.serve_forever()
FoxDot送信先変更
この記事のとおりです。この投稿が無ければ実現しませんでした。感謝です!!!
https://qiita.com/rucochanman/items/c27a9acaa99d960df149
Mozzi
OSCサーバーからシリアルで(0~127)のデータを受けます
もともとはMIDI-noteなのでそれを周波数に変換しています。
シンセとしては、2VCO、1LFOという感じでそれぞれ波形をsin,saw,squ,triから選べます
また一応エンベロープもつけていてアタックとディケイを触れるようにしてます。
※本当は一回信号がきたらアタック+ディケイ+αの期間だけ鳴るようにしたかったのですが、、
うまくできませんでした。
#include <mozzi_midi.h>
#include <MozziGuts.h>
#include <Oscil.h> // oscillator template
#include <tables/sin2048_int8.h> // sine table for oscillator
#include <tables/saw2048_int8.h>//saw table for oscillator
#include <tables/square_analogue512_int8.h> // square table for oscillator
#include <tables/triangle2048_int8.h>//triangle table for oscillator
#include <Ead.h>
#include <EventDelay.h>
#define CONTROL_RATE 256
Oscil <SIN2048_NUM_CELLS, AUDIO_RATE> Sin_v1(SIN2048_DATA);
Oscil <SAW2048_NUM_CELLS, AUDIO_RATE> Saw_v1(SAW2048_DATA);
Oscil <SQUARE_ANALOGUE512_NUM_CELLS, AUDIO_RATE> Squ_v1(SQUARE_ANALOGUE512_DATA);
Oscil <TRIANGLE2048_NUM_CELLS, AUDIO_RATE> Tri_v1(TRIANGLE2048_DATA);
Oscil <SIN2048_NUM_CELLS, AUDIO_RATE> Sin_v2(SIN2048_DATA);
Oscil <SAW2048_NUM_CELLS, AUDIO_RATE> Saw_v2(SAW2048_DATA);
Oscil <SQUARE_ANALOGUE512_NUM_CELLS, AUDIO_RATE> Squ_v2(SQUARE_ANALOGUE512_DATA);
Oscil <TRIANGLE2048_NUM_CELLS, AUDIO_RATE> Tri_v2(TRIANGLE2048_DATA);
Oscil <SIN2048_NUM_CELLS, AUDIO_RATE> Sin_l(SIN2048_DATA);
Oscil <SAW2048_NUM_CELLS, AUDIO_RATE> Saw_l(SAW2048_DATA);
Oscil <SQUARE_ANALOGUE512_NUM_CELLS, AUDIO_RATE> Squ_l(SQUARE_ANALOGUE512_DATA);
Oscil <TRIANGLE2048_NUM_CELLS, AUDIO_RATE> Tri_l(TRIANGLE2048_DATA);
EventDelay kDelay;
Ead kEnvelope(CONTROL_RATE);
uint8_t CV;
int gain=0;
int v1;
int v2;
int l;
void setup() {
Serial.begin(115200);
startMozzi(CONTROL_RATE); // set a control rate of 64 (powers of 2 please)
}
void updateControl(){
int vfo1=mozziAnalogRead(0);
int vfo2=mozziAnalogRead(1);
int lfo=mozziAnalogRead(2);
int lfo_freq=mozziAnalogRead(3);
int attack=mozziAnalogRead(4);
int decay=mozziAnalogRead(5);
v1=map(vfo1,0,1023,0,3);
v2=map(vfo2,0,1023,0,3);
l=map(lfo,0,1023,0,3);
int lf=map(lfo_freq,0,1023,10,100);
int at=map(attack,0,1023,10,300);
int de=map(decay,0,1023,50,500);
if(Serial.available()>0){
CV=Serial.read();
Serial.write(CV);
}
int f=mtof(CV);
switch (v1){
case 0:
Sin_v1.setFreq(f);
break;
case 1:
Saw_v1.setFreq(f);
break;
case 2:
Squ_v1.setFreq(f);
break;
case 3:
Tri_v1.setFreq(f);
break;
}
int f2=mtof(CV+24);
switch (v2){
case 0:
Sin_v2.setFreq(f2);
break;
case 1:
Saw_v2.setFreq(f2);
break;
case 2:
Squ_v2.setFreq(f2);
break;
case 3:
Tri_v2.setFreq(f2);
break;
}
switch (l){
case 0:
Sin_l.setFreq(lf);
break;
case 1:
Saw_l.setFreq(lf);
break;
case 2:
Squ_l.setFreq(lf);
break;
case 3:
Tri_l.setFreq(lf);
break;
}
if (kDelay.ready()){
kEnvelope.start(at,de);
kDelay.start(at+de+10);
}
gain=(int)kEnvelope.next();
}
int updateAudio(){
int aSig;
int aSig_v1;
int aSig_v2;
int aSig_l;
switch (v1){
case 0:
aSig_v1=Sin_v1.next();
break;
case 1:
aSig_v1=Saw_v1.next();
break;
case 2:
aSig_v1=Squ_v1.next();
break;
case 3:
aSig_v1=Tri_v1.next();
break;
}
switch (v2){
case 0:
aSig_v2=Sin_v2.next();
break;
case 1:
aSig_v2=Saw_v2.next();
break;
case 2:
aSig_v2=Squ_v2.next();
break;
case 3:
aSig_v2=Tri_v2.next();
break;
}
switch (l){
case 0:
aSig_l=Sin_l.next();
break;
case 1:
aSig_l=Saw_l.next();
break;
case 2:
aSig_l=Squ_l.next();
break;
case 3:
aSig_l=Tri_l.next();
break;
}
aSig=((aSig_v1+aSig_v2)/4*aSig_l)>>4;
aSig=gain*aSig;
return aSig;
}
void loop() {
audioHook();
}
デモ
ハードシンセ側は結構盛大なノイズになったと思いますが、もう少し音にバリエーション出来るといいなぁと思います