月 の 上

VEDAのサイトを作った

VEDA.glのスクリーンショット

veda.gl

VEDAはVJやGLSLのライブコーディングを行うソフトウェアだ。
Atomのパッケージとして実装されており、ユーザーは編集したGLSLコードをリアルタイムに実行して表示できる。

このブログでもこれまでに何度か紹介している。

blog.gmork.in

VEDAは去年の夏から開発を始めて、ドキュメントは全てREADME.mdに書いてきたんだけど、機能が増えてくるにつれてREADMEが読みにくくなってきた。
また、GLSLのフレームワークということで、実際に動作するコードをユーザーに見せたいという気持ちがでてきた。

というわけで作ったのが今回のWebサイト。

veda.gl

veda.glは、VEDAプロジェクト全体のドキュメントになっている。
VEDAのインストール方法や使い方、機能紹介に加え、FAQやコントリビュート方法をまとめている。

PCでは画面右上の 日本語 ボタンを押すと日本語版ドキュメントが表示される。

f:id:amagitakayosi:20180214232125g:plain

機能紹介のページでは、ブラウザの音声入力とかキーボード入力を使った実例が見れます。

f:id:amagitakayosi:20180214234108g:plainf:id:amagitakayosi:20180214234118g:plain

ドキュメントに貢献するには

ドキュメントは現在、英語と日本語で書いてます。
足りないドキュメントを追加したり、他の言語に翻訳していただける方がいたら、是非ご協力をおねがいしたいです🙇

veda.glはNext.js + VEDA.jsで開発して、GitHub Pagesにホストしてる。
レポジトリはこちら。

GitHub - fand/veda.gl: VEDA Website

ドキュメントはMarkdownで書かれています。
Next.jsでMarkdownを読み込んだり多言語対応してたりするため、ちょっと複雑なしくみになってしまった。

例えば、トップページの中国語版を追加する場合、まず pages/index.cn.md を追加し、次にpages/index.js を編集してロードしてあげるようにします。

// https://github.com/fand/veda.gl/blob/master/pages/index.js
import cn from './index.cn.md';

const article = {  
  en: parse(en),  
  ja: parse(ja),
  cn: parse(cn),
};

この辺のコード変な感じになってるのはわかってる……😅

まあ変なところは直してくつもりなので、とりあえずドキュメントみてわかんない所あったら気軽にissue立てていただけると助かります!!

https://github.com/fand/veda.gl/issues


もしVEDAプロジェクトに興味をもってくれたら、 CONTRIBUTING をみてissue立てるなりPRくれたらメッチャ嬉しいです!!
他にも、わからないことはTwitterとかで聞いてくれたらすぐ答えます🤘

twitter.com

よろしく〜〜

VEDA 2.4: GLSLで音楽を演奏できるようになったぞ!!!

こんにちは id:amagitakayosi です。
Atom用GLSL実行環境 VEDA を開発しています。

github.com

昨日リリースしたVEDAの最新版で、GLSLで音楽を演奏できるようになりました!

VEDAでは、この機能を Sound Shader と呼んでいます。
mainSound 関数に時刻から音声を合成する処理を書くことで、GPU上で音声合成できてしまいます!

Sound Shaderの使い方

  • mainSound() 関数を定義
  • alt-enter で実行
  • alt-. で停止

普通のフラグメントシェーダーや頂点シェーダーを ctrl-enter で実行すると、映像と音声を同時にGLSLで生成できます。

Shadertoyとの違い

Sound Shader機能は、ShadertoyのSoundバッファと同様の機能です。
基本的にShadertoyのコードがそのまま動きますが、以下のような違いがあります。

長さを変更できる

Shadertoyでは、生成する音声の長さは180秒固定でした。
VEDAでは、実行したいGLSLファイルの先頭に /*{ soundLength: 10 }*/ 等と指定することで、生成する音声の長さを変更できます。

ループ再生される

Shadertoyでは、180秒を過ぎたら音声が停止してしまいます。
VEDAでは、音声は soundLength の長さでループするようになっています。

ループ再生しつつ徐々に演奏内容を変更できるため、よりライブコーディングに向いた仕様となっています。
音声を停止したい時は alt-. を入力してください。

音声ファイルをロードできる

mp3及びwav形式のファイルをテクスチャとして読み込み、GLSL上で利用できます。

loadSound にテクスチャ名と時刻を渡すと、音声ファイルのその時刻の値が取得できます。 時刻の値を変更することで、再生速度を変更したりもできます。

実装解説

先月のWebGLアドベントカレンダーの記事で、ShadertoyのSound Shaderの実装について解説しました。

blog.gmork.in

VEDAでは、レンダリングのタイミング等は少々異なりますが、基本的にはShadertoyと同じ処理をしています。
大まかな流れは以下の通りです。

  1. 必要なサンプル数を計算する
  2. 音声バッファを作成し、再生を開始する
  3. 必要なサンプルが集まるまで、以下を繰り返す

1. 必要なサンプル数を計算

soundLength から、必要なサンプル数を計算します。
soundLength = 10, sampleRate = 48000 の時、必要なサンプルは 480000 個となります。

2. 音声バッファを作成し、再生

次に、生成したデータを格納するための音声バッファを作成します。

Web Audio APIの AudioBufferSourceNodeでは、Uint8Array で音声を生成できます。
今回は Uint8Array(480000) することになります。

AudioBufferSourceNodeでは、再生開始後にバッファにデータを詰める事もできます。
そのため、レンダリング前に再生を開始することで、ユーザーの操作から再生までのタイムラグを抑えています。

3. レンダリング

続いて、GPUで音声のレンダリングを開始します。
一度のレンダリングでは必要なサンプルが集まらないので、繰り返しレンダリングする必要があります。

VEDAでは、アニメーションのラグを回避するため、一度にレンダリングするサイズを小さくしています。
具体的には 32x64 としているので、音声をすべて生成するには 234 (= 480000 / (32 * 64)) 回のレンダリングが必要です。

レンダリングでは、各ピクセルごとに mainSound(time) を実行し、各時刻に対応する音声サンプルを計算します。
time の値は、ピクセルの位置とレンダリング回数から求めます。 例えば、(x,y) = (10, 20) のピクセルの3回目のレンダリングでは、

10 + (20 * 32) + (32 * 64 * 3) = 6794

より、6794個目のサンプルを計算することになります。

レンダリング結果はRGBAで出力されるので、計4byteとなります。
mainSound の結果はステレオであり、floatの値が2つ生成されますが、画像に出力する際に1byteずつ出力してしまうと精度が悪くなってしまいます。
そのため、次のようにして、各チャンネルの値を2byteに分割して保存するようにします。

vec2 v = mainSound(t);
vec2 h = floor(v/256.0)/255.0;
vec2 l = mod(v,256.0)/255.0;
gl_FragColor = vec4(h.x, l.x, h.y, l.y);

画像を生成したら、これを再度JSで音声に変換してあげます。

outputDataL[i] = (pixels[i * 4 + 0] * 256 + pixels[i * 4 + 1]) / 65535 * 2 - 1;
outputDataR[i] = (pixels[i * 4 + 2] * 256 + pixels[i * 4 + 3]) / 65535 * 2 - 1;

これを繰り返すことで、VEDAで alt-enter を押したら即再生を開始し、アニメーションを続けつつ音声を生成することができるのです。

音声ファイルをロードする実装

音声ファイルは、JS側で画像に変更してテクスチャとしてGPUにロードしています。
この時、画像から音声に変換するのとちょうど逆の処理をしてあげれば良いのです。

// 音声データを格納するUint8Arrayを作成
const array = new Uint8Array(_constants.SAMPLE_WIDTH * _constants.SAMPLE_HEIGHT * 4);

// 音声を画像に変換
for (let i = 0; i < c0.length; i++) {
  const off = i * 4;

  // -1〜1 の値を 0〜65536 に変換
  const l = c0[i] * 32768 + 32768;
  const r = c1[i] * 32768 + 32768;

  // 4bytesに分割
  array[off] = l / 256;
  array[off + 1] = l % 256;
  array[off + 2] = r / 256;
  array[off + 3] = r % 256;
}

// Uint8Arrayからテクスチャを作成
const texture = new THREE.DataTexture(array, _constants.SAMPLE_WIDTH, _constants.SAMPLE_HEIGHT, THREE.RGBAFormat);

簡単ですね!

現在、音声テクスチャのサイズは 1280x720 固定にしています。
その為、ロードできる音声の長さは19.2秒までとなっています (サンプルレートが48000の場合)。

AtomDAWになる日も近い……!!!

ところで

VEDAはもうすぐ100 stars!!
いますぐ fand/veda を Star してください!!!