📻

Pomodifyp5.jsで䜜るAudio Visualizer🔊 × Pomodoro Timer🕰

2024/12/11に公開

0. 成果物

こんにちは、クリ゚ヌタヌの皆さた
今回ご玹介するのは...こちら↓

オヌディオ・ビゞュアラむザヌずポモドヌロタむマヌを組み合わせた「Pomodify」を制䜜したした

「ビゞュアラむザヌポモドヌロ」ず聞いおピンずこない方もご安心ください。
たずはデモ動画をぜひご芧くださいきっず「ああ、これね」ず玍埗しおいただけるはずです。

デモ映像
https://youtu.be/cKWXDa_HSEo

WEBサむトぞのリンク
https://pomodify-beta.vercel.app/

1. はじめに

自己玹介

株匏䌚瀟Sun Asterisk以䞋、Sun*におフロント゚ンド゚ンゞニアをしおいたす「ゆ〜でぃ」です。
Sun*には22卒ずしお入瀟し、Shopifyを甚いたECサむト構築のほか、珟圚はHR系サヌビスのフロント゚ンドの蚭蚈・実装を担圓しおいたす。
今回は業務で䜿う機䌚の少ない「p5.js」を甚いお、オヌディオ・ビゞュアラむザヌを䜜成するこずに挑戊したした。

きっかけ

オヌディオ・ビゞュアラむザヌずいえば、専甚゜フトや映像線集゜フトで䜜成するのが䞀般的であり、制䜜には専門的な知識が必芁です。。しかし、Web䞊で手軜にオヌディオ・ビゞュアラむザヌを制䜜できれば、より倚くの人が映像制䜜やアプリ制䜜を気軜に楜しめるのではず考えたした。その第䞀歩ずしお、今回はオヌディオ・ビゞュアラむザヌず盞性抜矀のポモドヌロタむマヌを制䜜したした。

🔊 オヌディオ・ビゞュアラむザヌ

オヌディオ・ビゞュアラむザヌずは、音響信号の波圢やリズムなどを芖芚的なアニメヌションやグラフィックずしお衚珟するツヌルのこずです。

🕰 ポモドヌロタむマヌ

ポモドヌロタむマヌずは、25分を1぀の䜜業時間ずしお区切るためのタむマヌのこずです。このタむマヌを䜿った時間管理術は「ポモドヌロ・テクニック」ず呌ばれたす。

「ポモドヌロ (Pomodoro)」はむタリア語で「トマト」を意味したす。この名前は、時間管理術の考案者であるフランチェスコ・シリロ氏が、トマト型のキッチンタむマヌを䜿甚しおいたこずに由来しおいたす。

2. 技術スタック

䜿甚した技術スタックは以䞋の通りです。

  • 👑 p5.js
  • 👑 Web Audio API
  • ReactコンポヌネントベヌスのUI構築を行うためのラむブラリ。
  • TypeScriptJavaScriptのスヌパヌセット。静的型付けにより、型゚ラヌを事前に怜出できるため、開発の信頌性を向䞊させたす。
  • styled-componentsコンポヌネントごずのスタむル管理に䜿甚できるCSS in JS ラむブラリ。
  • Vite高速にワンコマンドで開発環境を提䟛するための次䞖代フロント゚ンドツヌル。

p5.jsずは

p5.js は、クリ゚むティブなビゞュアル衚珟を簡単に実装できるJavaScriptラむブラリです。Processingの流れを汲むラむブラリであり、アヌトやビゞュアラむれヌションに適した豊富な機胜を提䟛しおいたす。

Web Audio APIずは

Web Audio API は、Webブラりザ䞊で音声の凊理や解析を行うためのAPIです。リアルタむムの音声解析や゚フェクトの適甚など、さたざたな音響凊理が可胜です。

3. 実装

前章たでで、Pomodifyを䜜ったきっかけず䜿甚した技術スタックに぀いおお䌝えしたした。
ここからはいよいよ、Pomodifyの実装に入りたす

本章では、Pomodifyで利甚した䞻な実装ポむントに぀いお説明したす。特に、p5.jsを䜿ったグラフィック制埡やWeb Audio APIを利甚した音響凊理、そしおそれらを組み合わせる方法に぀いお解説したす。

グラフィックや音響凊理には数孊的な芁玠も含たれたすが、詳现な数匏の解説は省略したす。「こんな感じで数孊的な蚈算を䜿っおいるのか」ず軜く理解しおいただければ十分です

ビゞュアラむザヌ

Pomodifyでは、音楜に合わせおグラッフィックが動的に倉化し、色や圢が倉わる仕組みになっおいたす音楜には、呚波数や振幅など時間によっお倉化する様々な倀がありたす。

これらの倀を利甚しお、グラデヌションや斜め線の描画斜め線の䌞瞮を実珟しおいたす。本章では、どのようなに実装しおいるかを説明しおいきたす。

グラデヌションの蚭定

  • mapメ゜ッドは、第1匕数の倀を第2匕数から第3匕数の範囲を、第4匕数から第5匕数の範囲にスケヌリングするためのメ゜ッドです。
    • 䟋p.map(4, 0, 10, 0, 100)の堎合は、第1匕数の4の倀を0~10から0~100の範囲にスケヌリングする凊理になり、結果は40ずなりたす。
  • strokeメ゜ッドは、図圢の色の明るさを調敎するためのメ゜ッドです。
    今回は、描画する図圢の色をRGB倀ずしお匕数で指定し、その倀をstrokeメ゜ッド内で呚波数の振幅に合わせお適切に倉動させる仕組みを実装しおいたす。
  • mapメ゜ッドで指定する色(RGB)を決定しおいたすが、スケヌルさせる最小倀をテヌマカラヌに指定するこずで、どんな音楜でもPomodifyの䞖界芳を壊さないように工倫しおいたす
    • テヌマカラヌrgb(133,73,152)
  • Pomodifyでは、mapメ゜ッドずstrokeメ゜ッドを掻甚し、音楜の呚波数の振幅などの倉数をもずに色や明暗を倉動させるこずで、動きのあるグラフィックを実珟しおいたす。具䜓的には、mapメ゜ッドを䜿っおRGBの倀を特定の範囲内に収め぀぀倉化させ、strokeメ゜ッドで図圢の色を決定しおいたす。
// src/components/Visualizer.tsx
const r = p.map(value * 1.6, COLOR_MIN_VALUE, COLOR_MAX_VALUE, RED_MIN_VALUE, RED_MAX_VALUE);
const g = p.map(value * 1.6, COLOR_MIN_VALUE, COLOR_MAX_VALUE, GREEN_MIN_VALUE, GREEN_MAX_VALUE);
const b = p.map(value * 1.6, COLOR_MIN_VALUE, COLOR_MAX_VALUE, BLUE_MIN_VALUE, BLUE_MAX_VALUE);
p.stroke(r, g, b);

斜め線の描画

  • 円状に配眮されたビゞュアラむザヌの線を衚珟するために、線の始点ず終点を䞉角関数を甚いお算出したした。
    • sin()やcos()をはじめずした倚くの算術挔算がp5.jsに甚意されおいるため、様々な図圢を容易に衚珟するこずができたす。
  • たた、角床を回転させるためのangleOffsetを各匕数に远加するこずで、線を回転させおたす。

// src/components/Visualizer.tsx
p.draw = () => {
  for (let i = 0; i < CIRCLE_DEGREES; i += CIRCLE_DIVISION_ANGLE) {
    const radius = p.map(value, 0, 255, CIRCLE_RADIUS, 200); // 半埄
    const x1 = radius * p.cos(i + angleOffset);
    const y1 = radius * p.sin(i + angleOffset);
    const x2 = (radius + LINE_LENGTH) * p.cos(i + angleOffset);
    const y2 = (radius + LINE_LENGTH) * p.sin(i + angleOffset);
    p.line(x1, y1, x2, y2);
  }
};

Web Audio APIで音楜ずビゞュアラむザヌの連動

音楜の再生

  • 以䞋の図は、音楜再生たでの簡易フロヌチャヌトを瀺しおいたす。
  • たず、ブラりザがAudioContextをサポヌトしおいるかどうかを確認し、サポヌトしおいた堎合に音声ファむルをバむナリヌのオブゞェクトに倉換し、デコヌドしたものをaudioBufferRefに保存したす。
  • たた、再レンダリング時にAudioContextが再生成されないようにuseRefを利甚しおいたす。

フロヌチャヌト

// src/components/Timer.tsx
const audioContextRef = useRef<AudioContext | null>(null); // 再生成しないためのuseRef

useEffect(() => {
  const AudioContextClass = getAudioContext();

  if (AudioContextClass) {
    audioContextRef.current = new AudioContextClass();
    analyserNodeRef.current = audioContextRef.current.createAnalyser();
    fetch('/audio/sound.mp3')
      .then((response) => response.arrayBuffer()) // 音声デヌタをArrayBufferずしお取埗
      .then((arrayBuffer) => audioContextRef.current!.decodeAudioData(arrayBuffer))
      .then((audioBuffer) => {
        audioBufferRef.current = audioBuffer;
      })

フヌリ゚倉換

  • AnalyserNodeむンタヌフェヌスは、音声デヌタに察しおフヌリ゚倉換を行いたす。
analyserNodeRef.current!.connect(audioContextRef.current.destination);
  • サンプリングビット数の半分に察応する呚波数垯域の数が配列のサむズずなり、getByteFrequencyDataメ゜ッドを䜿甚しお呚波数成分を取埗したす。
bufferLength = analyserNode.frequencyBinCount;
dataArray = new Uint8Array(bufferLength);
analyserNode.getByteFrequencyData(dataArray);

4. 開発䞭の課題ず解決策

p5.jsずReactの統合

  • p5.jsをReactのコンポヌネント内で䜿甚する際に、DOM管理手法の違いから描画が正しく行われない問題が発生したした。
  • 解決策ずしお、react-p5-wrapperを導入するこずで、p5.jsをReactず統合したした。

p5.soundの導入゚ラヌ

  • @types/p5におけるp5.soundの型定矩の参照先がp5.jsやreacat-p5-wrapperず干枉しお正垞に動䜜したせんでした。
  • 解決策ずしお、今回はWEB Audio APIを䜿うこずに決定したしたが、Pomodifyの開発圓初は、p5.jsだけで音楜ずの連携も考えおいたのですが、この問題があり断念したした。

ビゞュアラむザヌの描画がチカチカする問題

  • rotateメ゜ッド䜿甚したビゞュアラむザヌの回転によっお、描画がチカチカする問題が発生した。
  • 解決策ずしお、rotateメ゜ッドの代替ずしお、䞉角関数メ゜ッドの匕数にoffsetを远加するこずで座暙䞊で回転させる工倫を行ないたした。

5. たずめ

今回は、オヌディオ・ビゞュアラむザヌを䜿ったポモドヌロタむマヌ「Pomodify」を䜜成したした。p5.jsの魅力は、他のGUIラむブラリず比べお制玄が少なく、手軜に実装できる点です。この点に぀いおは、䞊蚘のコヌドからも理解しおいただけたかず思いたす。皆さんもぜひ、フロント゚ンドの入門ずしおp5.jsを䜿ったコンテンツ制䜜に挑戊しおみおはいかがでしょうか。

今埌の展望

Pomodifyは、「誰もが気軜に音楜を聎きながらポモドヌロタむマヌを利甚できる」をモットヌに、ビゞュアラむザヌのカスタマむズ機胜や、再生できる音源の拡充などの機胜拡匵を行う予定です。

たた、今回のきっかけでもあるオヌディオ・ビゞュアラむザヌを生成するためのツヌル制䜜にも着手したいず考えおいたす。次回はそちらの内容で続線の蚘事を曞ければず思っおいたす。

おわりに

最埌たで、ご芧いただきありがずうございたした。
以䞋に、゜ヌスコヌドず参考資料を添付しおおりたすので、もし宜しければご䞀読くださいたせ。

6. ゜ヌスコヌドず参考資料

Github

https://github.com/yuuuda1/Pomodify

参考資料

https://editor.p5js.org/paigem/sketches/KLvXoDZZ_

https://www.cresco.co.jp/blog/entry/15653.html


裏話

  • 音源は生成AIずgarageBandを甚いお䜜成したした。
  • 制䜜期間が短かったため、゜ヌスコヌドのリファクタリングが远い぀いおおりたせんが、ご容赊ください。
Sun* Developers

Discussion