RNBOのWeb出力を試す

Max8の新機能RNBOのWeb出力を試したときの備忘録です

https://effectorhack.connpass.com/event/267009/

まず
Maxで音が鳴るRnboを使ったパッチを作る。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96808/c959d21a-e900-c6da-1abc-b3f585a1dd49.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96808/2a04669b-9232-0520-08ea-da80d4a3ca16.png)


# パッチ内容
これは、frequencyとvolumeで
サイン波形が鳴るパッチ

と

パラメータ drum というのに0.00 1.00の値を入れることで
ドラムの音が鳴るパッチになります。
wavファイルはパッチと同じところに置いておきます。

export時にCopy Sample Dependencesを入れておくとmediaフォルダに波形がコピーされます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96808/375f1b21-9387-221e-0eec-cd3a948e4941.png)

# Cyclingのrnbo.exampleで動かす
https://github.com/Cycling74/rnbo.example.webpage
このページにあるものを動かしてみます。
これをクローンしてきて、

このexportフォルダにMaxで出力したファイルをコピーします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96808/275730b5-fbe1-3e61-862f-4c054ca5fcd2.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96808/37152c7d-7fbf-6b47-6869-ef06bf901c24.png)

# Webサーバーを起動する
コマンドラインから
npx http-server
を実行してサーバーをうごかします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96808/5ee3c894-3618-4083-1d8e-4166396032f8.png)

ブラウザで

URLに
http://10.16.191.211:8080
を入れて実行すると

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96808/a2a2eb64-dd89-ec4b-0249-35e476cff566.png)

スライダーのdrumを動かすと音がします

# キャッシュ無効にしておく
作成中キャッシュが残っていると更新されないので
ブラウザでF12キーを押してNetworkにある
Disable cacheをチェックしておく

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96808/fb966eb5-7754-88b5-7387-72b55c955261.png)


# P5JSで音を鳴らしてみる

index.htmlを以下のように書き換える


``` index.html
<html>
  <head>
  </head>
  <body>
    <script type="text/javascript" src="js/guardrails.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.0/p5.js"></script>
    <script src="sketch.js"></script>
  </body>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96808/9a1f5b70-f9ed-ac7c-c42d-4effb99fb406.png)

index.htmlと同じ階層に
sketch.jsを用意し

以下のようなコードを書く

``` sketch.js
function loadRNBOScript(version) {
    return new Promise((resolve, reject) => {
        if (/^\d+\.\d+\.\d+-dev$/.test(version)) {
            throw new Error("Patcher exported with a Debug Version!\nPlease specify the correct RNBO version to use in the code.");
        }
        const el = document.createElement("script");
        el.src = "https://c74-public.nyc3.digitaloceanspaces.com/rnbo/" + encodeURIComponent(version) + "/rnbo.min.js";
        el.onload = resolve;
        el.onerror = function(err) {
            console.log(err);
            reject(new Error("Failed to load rnbo.js v" + version));
        };
        document.body.append(el);
    });
}

let frequency = null;
let volume = null;
let drum = null;

async function setuptat() {
	
    // Create AudioContext
	const WAContext = window.AudioContext || window.webkitAudioContext;
	const context = new WAContext();
	
    // Create gain node and connect it to audio output
    const outputNode = context.createGain();
    outputNode.connect(context.destination);
	
    const patchExportURL = "export/patch.export.json";
	
	response = await fetch(patchExportURL);
    patcher = await response.json();
        
	if (!window.RNBO) {
        // Load RNBO script dynamically
        // Note that you can skip this by knowing the RNBO version of your patch
        // beforehand and just include it using a <script> tag
    	await loadRNBOScript(patcher.desc.meta.rnboversion);
    }
	
	rawPatcher = await fetch("export/patch.export.json");
	patcher = await rawPatcher.json();

	device = await RNBO.createDevice({ context, patcher });
  
  frequency = device.parametersById.get("frequency");
  volume = device.parametersById.get("volume");
  drum = device.parametersById.get("drum");
    
        // (Optional) Fetch the dependencies
    let dependencies = [];
    try {
        const dependenciesResponse = await fetch("export/dependencies.json");
        dependencies = await dependenciesResponse.json();

        // Prepend "export" to any file dependenciies
        dependencies = dependencies.map(d => d.file ? Object.assign({}, d, { file: "export/" + d.file }) : d);
    } catch (e) {}
    	
        // (Optional) Load the samples
    if (dependencies.length)
        await device.loadDataBufferDependencies(dependencies);
    
    // Connect the device to the web audio graph
    device.node.connect(outputNode);
}


function setup(){
    
  //キャンバスを作成
  createCanvas(600,425);
  //背景色
  background(0);
    //オブジェクトの色
  fill(0,255,0);
  //キャンバスの中心に直径100pxの丸を描画
  //ellipse(width/2,height/2,100);0
  fill(0,255,0);
    
}
class Robot {
	
	constructor( inX, inY, inDx, inDy)
	{
		this.x = inX;
		this.y = inY;
		this.dx = inDx;
		this.dy = inDy;
	}
	
	update()
	{
		this.x = this.x+this.dx;
		this.y = this.y+this.dy;
		
		if (this.x < 0 || this.x > width){
			this.dx = -this.dx;
		}
		if (this.y < 0 || this.y > height){
			this.dy = -this.dy;
		}
		
		
		ellipse(this.x, this.y, 80, 80);		
		
		if(frequency != null)
		{
			frequency.value = 220+this.x;
  			volume.value = 0.5 + (height-this.y)/height*0.5;      
  			
			drum.value = robot.x/width;	
			//print(drum.value);
  		}
	}
}

let  robot = new Robot(100,100,10,10);
var t = 0;

function draw() {
	if (isUserStarted) {
		background(0);
		t+=5;
	//	t = t%50;
	//	ellipse(mouseX+t, mouseY, 80, 80);	
	//	
	//	if(frequency != null)
	//	{
	//		frequency.value = 220+mouseX+t;
  	//		volume.value = (height-mouseY)/height;      	
  	//	}
  		
  		if(robot != null){
  			robot.update();
  		}
	}
}

var isUserStarted = false;

function touchStarted() {
    if (!isUserStarted) {
        // touchStarted()を1回だけ呼び出されるようにする
        print('touch');
		setuptat();
		
        isUserStarted = true;
    }
}




function mouseClicked() {
	robot.x = mouseX;
	robot.y = mouseY;
	
	robot.dx = mouseX;
	robot.dy = mouseY;
	

}
```

やっていることは、WebAudioの初期化と接続
RNBOのexport.json読み込み
frequency,volume,drumのパラメータ関連付け
dependenciesからbuffer読み込み
を初回タッチした時にしています。


こんな感じに動きます
（Cycleを鳴らす場合は多少修正必要）

https://twitter.com/tatmos/status/1606954071377981441?s=20&t=hTZ50JxFREQkfJbf9lIWOA

追記メモ：
音声入力とかを試したい場合、Chromeとかだとhttpの場合localhost:8080とかでないとマイクデバイスがブロック解除できないかも