みんなの「作ってみた」

Nuxt.js + Tone.jsで画面を撫でると音が出るやつ作った

2019/04/04

mi_ki_ri
mi_ki_ri

概要

画面を撫でる(ドラッグ, スワイプ)で画面のXY軸に対応した音が出るWebアプリを作りました。

NadeNadeSynth ( https://nadenadesynth.herokuapp.com/ )

nadesynthdemo.gif

X軸(左右)が音程、Y軸(上下)が音量に対応しています。

(本当は上下はフィルターに対応させたかったんだけど、うまく行かなかった)

適当に画面を触ると適当な音が出るというオモチャ的アプリです。

Mac, iOS, Androidでは確認しました(肝心のWinがまだという…)

本記事はその開発記になります。

開発記

実のところ、このアプリは過去に一度作ったもののドメイン引っ越しの際に消えたアプリのリベンジ版です。

(だからバージョン番号が0.0.2)

前に作ったものはスマホスワイプに対応していなかったので、そこをやりたかったのと、

今風な開発にしたかったので再挑戦しました。

要件定義

再挑戦なので機能を決めるところはすんなりできました。

X軸音程、Y軸がフィルターで、その部分はグラデーション背景になっている、というものです。

ライブラリなどの選定は

  • Tone.js : Web Audio APIを扱いやすくしたもの。使うのはほぼ前提
  • Nuxt.js : ペライチなので機能的に多すぎるのではないかとちょっと迷ったが、使い慣れているのでこれにした
  • heroku : firebaseappと迷ったが、firebaseappは一旦 nuxt generate をかまさないと行けない(あるいはCircleCI?)ので開発時のストレスの無さで選定

こんな感じで、ここまではすんなり来ました。

コーディング

new Tone.Monosynth()

コーディング時に気を使ったのは、 new Tone.MonoSynth() する タイミングです。

Tone.MonoSynth はその名の通りシンセサイザーのクラスです。

使い回すのでnuxtの mounted() に入れられたらいいのですが、

Web Audio APIはユーザーの動作を起点に発動しなければならないという(妥当な)制限があるようで、

mounted() ではまずいのでちょっと苦労しました。

最終的には nadeZone (撫でるゾーン)の@clickで呼ばれるイベントに置きましたが、

そこまでせずともよかったかもしれません。

(画面読み込み時に出るボタンを押したタイミングでよかったかも)

音の出る出ない

触っているときだけ音を出すというアプリなのですが、

マウスイベントから常に周波数を送っているという関係上、

シンセクラスを作ったり捨てたりするのは現実的ではないなと思い、使いまわしています。

しかしそれでは音が出っぱなしになるので、

音量を、

  • 触っているとき: Y軸に合わせた値
  • 触っていないとき: -96DB固定

にしました。

つまり結局音は出っぱなしということです…。

スマホ対応

撫でるというコンセプト上、やはりスマホ対応はしたかったのです。

そこでたぶん知っている人は知っているけれど自分は知らなかったスマホ独特のイベントオブジェクトについて書いておきます。

PCブラウザの場合、mousemoveに渡されるeventは直にclientX, clientYを持っているけれど、

スマホは複数タッチが可能な関係上、XY軸の情報は@touchmoveevent直下にはありません。

event.touches という配列の下に格納されています。

{
  event: {
    // 略
    touches: [ // ここに配列として含まれる
      { // 最初の指がtouches[0]
        // 略
        clientX: 100,
        clientY: 100
      },
      {
        // 略
        clientX: 50,
        clientY: 50
      }
    ]
  }
}

だいたいこんな感じです。
なので event.touchesundefined ではなく、なおかつ event.touches.length が1個以上のときという条件分岐を組みました。

Tone.js の読み込み方

単純に読み込んだだけではうまく使えません。

(おそらく、参照しているWeb Audio APIが別のものになっている)

@sKawashimaさんの記事を参考に、

  • importではなくrequireを使う
  • nuxtjsにあるno-ssrを使う(サーバーサイドレンダリングしない)

という対処をすれば読み込めました。

まとめ

  • Web Audio API は気難しい
  • スマホとPCでは座標のとり方が少し違う

それでは、 Let's NadeNade!