Help us understand the problem. What is going on with this article?

ブラウザを誰でも簡単に扱える楽器にしてみた

More than 1 year has passed since last update.

uzura です。
ファーストサーバ Advent Calendar 2017 の8日目を担当させて頂きます。

僕は会社では主にフロントエンドの開発を行っています。
今回 Advent Calendar に記事を投稿するということで、弊社サービスの Zenlogic にも使用されている Ruby on Rails や React.js を使いつつ、せっかくなので以前から作ってみようと思っていたサービスを(個人で)作ってみて、それについての記事を書きました。

rechord

logo.png
https://rechord.cc
ソースコード (GitHub)

どんなサービスなの?

コード進行を入力するだけで誰でも簡単に演奏/共有することができる Web サービスです。

コード進行って何??

音楽の知識が無い方には少々分かりにくい概念と思いますが、伴奏をする時の指針となるものです。
簡単に言うと、これ通りに弾けばその曲の伴奏になるよ!という簡易的な楽譜みたいなものです。
このサービスではそれを 入力してボタン一発で再生する ことができます。

サンプル

百聞は一見に如かず、みんな知ってそうな曲でサンプルを用意しましたのでこちらをご覧ください。
空も飛べるはず / スピッツ (サビだけ)
(コード進行には著作権がありません。やったね!)
image.png

この状態で play ボタンを押すと演奏が始まり、空も飛べるはず(っぽい伴奏)が再生されます。
このサービスがあれば、コード進行を再生できる状態で手軽に保存・共有できるので、作曲やバンド演奏の補助ツールとして使用できると思います。
興味がある方はぜひご登録を。

どういう仕組みで動いてるの?

さて、ここからやっと Qiita らしい技術的な話です。
そもそも音を自由に鳴らせる系のサービスって比較的少ないですね。 「音楽が鳴るサイトは重いしウザい」 「音が鳴る = MIDI or Flash。どちらも時代遅れ」 という古くからの常識が邪魔をしているのかもしれません。
しかし、実は最近では簡単に音を鳴らすことができるようになっているんです。

Web Audio API

Web Audio API - Web API インターフェイス | MDN
Web Audio API の基礎 - HTML5 Rocks
Web Audio API (日本語訳)

Web Audio API は、HTML5 で導入された、音を扱うための javascript API です。
IE 11 は対応していませんが、最近のブラウザは基本的に対応しています。
https://caniuse.com/#feat=audio-api

以下は、ボタンを押すと音楽ファイルが鳴るというだけのスクリプトです。
ES6 で書いてしまいましたが、最低限こんな感じでファイルを鳴らすことが出来ます。

const context = new AudioContext()

// サウンドを再生
const playSound = (buffer) => {
  const source = context.createBufferSource()
  source.buffer = buffer
  source.connect(context.destination)
  source.start(0)
}

// サウンドデータを読み込んでクリックイベントを設定
const getAudioBuffer = () => {
  const request = new XMLHttpRequest()
  request.responseType = "arraybuffer"
  request.onload = () => (
    context.decodeAudioData(req.response, (buffer) => (
      document.getElementById("play-button").onclick = () => playSound(buffer)
    ))
  )
  request.open("GET", "hogehoge.mp3", true)
  request.send("")
}

window.onload = () => getAudioBuffer()

…全然簡単じゃないですね。
そうです。Web Audio API 自体は結構難しいんです。
僕も最初はここで心が折れかけました。
ではどうやって今回のサービスを作ったのか?というと、 Tone.js というライブラリを使用しました。

Tone.js

Tone.js は Web Audio API を簡単に扱うためのライブラリです。
どのくらい簡単かと言うと、

import { Synth } from "tone"
const synth = new Synth().toMaster()
synth.triggerAttackRelease("C4", "8n")

これだけでドの音が鳴ります。す、すげー!
しかも Synth を使えば、音源データが無くても合成音でできた楽器(=シンセサイザー)を使えます。非常に便利。

とは言え僕はあまりシンセでの音作りに慣れてないので、今回のサービスでは音源データをロードするようにしています。
ここでは Sampler を使います。
Sampler は音源データを読み込んで鳴らすのですが、1つの音源データしかなくても、指定した高さの音に合わせて鳴らしてくれます。
どういうことかと言うと、 ドの音のデータを「レの高さで鳴らして!」って書くとレの高さで鳴らしてくれる というわけです(まさにサンプラーなのですが)
実際に書くとこんな感じです。

import { Sampler } from "tone"
const piano = new Tone.Sampler({
  C4: "C4.mp3"
}, {
  baseUrl: "./audio/"
}).toMaster()
piano.triggerAttackRelease("D4", "8n")

簡単ですね!
C4 というのは4オクターブ目のC(=ド)の音ということです。
rechord では1つの楽器につき C2, C3, C4, C5 の4つのデータを用意して、あとは Sampler の機能で高さを合わせて鳴らしています。
どうでしょう、この時点で既にだいぶ楽器ができそうな感じではありませんか?
次はコードを鳴らす仕組みを見ていきましょう。

tonal

コードとは和音のことです。
C と書かれている場合は「ド・ミ・ソ」、 Dm7/G と書かれている場合は「ソ・レ・ファ・ラ・ド」の音を鳴らす必要があります。
とはいえコードにはたくさん種類があって、それを全部正確に翻訳するのはなかなか骨が折れる作業。
そこで見つけてきたのが tonal というライブラリです。

tonal は楽譜的な意味での音楽に関する様々な機能を持ったライブラリなのですが、今回はその中でも Chord というコードに関する部分を使用しています。
例えば以下のように書けば簡単に C が ド・ミ・ソ であることを翻訳してくれます。

import { Chord } from "tonal"
Chord.notes("C4") // => ["C4", "E4", "G4"]

tonal を通して翻訳された音を Tone.js が演奏しているというわけです。
結構複雑なコードにも対応しているので、翻訳は tonal に任せておけば安心ですね!

さて、最後は画面の UI についてです。

Draft.js

入力したコードに自動的に色がついたりする仕組みはこの Draft.js が担っています。
一見 textarea にも見えますが実は違います(未だに信じられない…)

最初は以下のような感じで、入力部分と表示部分が分かれてたんですが、一緒にすれば使いやすいなと思って Draft.js にがんばって貰いました。
image.png

Draft.js には CompositeDecorator という機能があって、正規表現でマッチした部分を自由に操作することができます。
例えば以下のように書けば、 rootChordRegex にマッチした部分を rootChordComponent というコンポーネントで囲って、任意のスタイルを当てることが出来ます。

// このメソッドは公式ドキュメントほぼそのままです
const baseDecorator = (regex, block) => ({
  strategy: (contentBlock, callback) => {
    const text = contentBlock.getText()
    let matchArr = regex.exec(text)
    let start
    let end
    while (matchArr !== null) {
      start = matchArr.index
      end = start + matchArr[0].length
      callback(start, end)
      matchArr = regex.exec(text)
    }
  },
  component: (props) => block(props)
})

const rootChordRegex = /C#|Db|D#|Eb|F#|Gb|G#|Ab|A#|Bb|C|D|E|F|G|A|B|%|=|_/g
const rootChordComponent = (props) => (
  <span className={rootChordClass(props.decoratedText)}>
    <wbr />
    {props.children}
  </span>
)

const ScoreDecorator = new CompositeDecorator([
  baseDecorator(rootChordRegex, rootChordComponent)
])

基本的にはこれの応用で様々なスタイルを適用してます。
個人的には、スペースを入れずとも | を入力すれば小節が区切れるのが、コード進行を素早く入力する上で便利だなと思ってます。
image.png

まとめ

components.png
非常に雑な図ですがこんな感じの構成になっております。本当に雑だ…
rechord はコード進行の演奏に特化させていますが、これを応用すれば、例えば PC のキーボードを楽器としてのキーボードとして使うサービスとか、いろんなことができると思います。

フロント部分はそもそも React を使った SPA だったり、
サーバ側は Ruby on Rails で、
CSS のフレームワークは bulma を使ってたりと、
いろいろ語りたいことはありますが長くなるのでこの辺りで。

以上、ブラウザが簡単に楽器にできる時代になったよ!という記事でした。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away