強火で進め

このブログではプログラム関連の記事を中心に書いてます。

Unityで簡易ピアノアプリを作成


前回紹介した「音データをジェネレート」を応用して簡易ピアノアプリを作ってみました。

画面上部のボタンを押すかピアノの画像に書かれているキーを押すと音が鳴ります。
アプリは以下のリンクからダウンロード出来ます。

※Web Player版だと遅れて音が鳴る様な事が有ったのでアプリ版を用意しました。比較用にWeb Player版もこちらに置いておきます。

自分はピアノは弾けないですがキーボードの Z から , まで滑らせたりしただけでも楽しめました。

各音階の周波数はこちらを参考しました。

八角研究所 : Javaでピコピコシンセを作ってみよう!(2) - 音程と音長の計算
http://www.hakkaku.net/articles/20090518-438

ソースコードはこんな感じ。これをMain Cameraなどに貼り付けると動作します。

【PlayWave.cs】

using UnityEngine;
using System;

public class PlayWave : MonoBehaviour
{
	const double PI = System.Math.PI;
	private enum PlayState {
		None,
		C,
		CS,
		D,
		DS,
		E,
		F,
		FS,
		G,
		GS,
		A,
		AS,
		B,
		C2
	}

	public double gain = 1.5;
	private double increment;
	private double sampling_frequency = 48000;
	private double time;
	private PlayState playState = PlayState.None;
	private bool nowPlaying = false;
	private float fadeScale;

	void SineWave(float[] data, int channels, double frequency) {
		increment = frequency * 2 * PI / sampling_frequency;
		for (var i = 0; i < data.Length; i = i + channels) {
			time = time + increment;
			data [i] = (float)(gain * Math.Sin (time) * fadeScale);

			if (nowPlaying) {
				fadeScale *= 1.5f;
				if (fadeScale > 1.0f) fadeScale = 1.0f;
			} else {
				fadeScale -= .025f;
				if (fadeScale < 0.001f) {
					fadeScale = 0.0f;
					playState = PlayState.None;
				}
			}
			if (channels == 2)
				data [i + 1] = data [i];
			if (time > 2 * Math.PI)
				time = 0;
		}
	}
	
	void OnAudioFilterRead (float[] data, int channels)
	{
		double scale = 1;
		switch (playState) {
			case PlayState.C:
				SineWave(data, channels, 261.6255653005985 * scale);
				break;
			case PlayState.CS:
				SineWave(data, channels, 277.18263097687196 * scale);
				break;
			case PlayState.D:
				SineWave(data, channels, 293.66476791740746 * scale);
				break;
			case PlayState.DS:
				SineWave(data, channels, 311.1269837220808 * scale);
				break;
			case PlayState.E:
				SineWave(data, channels, 329.62755691286986 * scale);
				break;
			case PlayState.F:
				SineWave(data, channels, 349.2282314330038 * scale);
				break;
			case PlayState.FS:
				SineWave(data, channels, 369.99442271163434 * scale);
				break;
			case PlayState.G:
				SineWave(data, channels, 391.99543598174927 * scale);
				break;
			case PlayState.GS:
				SineWave(data, channels, 415.3046975799451 * scale);
				break;
			case PlayState.A:
				SineWave(data, channels, 440.0 * scale);
				break;
			case PlayState.AS:
				SineWave(data, channels, 466.1637615180899 * scale);
				break;
			case PlayState.B:
				SineWave(data, channels, 493.8833012561241 * scale);
				break;
			case PlayState.C2:
				SineWave(data, channels, 523.2511306011974 * scale);
				break;
		}
	}
  
	void Play(PlayState playState_) {
		if (playState == PlayState.None) {
			fadeScale = 0.1f;
			time = 0.0f;
		}
		playState = playState_;
		nowPlaying = true;
	}
	
	void OnGUI ()
	{
		int x = 10;
		const int dist = 50;
		if (GUI.RepeatButton (new Rect (x, 10, 40, 30), "ド")) {
			Play(PlayState.C);
		}
		x += dist;
		if (GUI.RepeatButton (new Rect (x, 10, 40, 30), "ド#")) {
			Play(PlayState.CS);
		}
		x += dist;
		if (GUI.RepeatButton (new Rect (x, 10, 40, 30), "レ")) {
			Play(PlayState.D);
		}
		x += dist;
		if (GUI.RepeatButton (new Rect (x, 10, 40, 30), "レ#")) {
			Play(PlayState.DS);
		}
		x += dist;
		if (GUI.RepeatButton (new Rect (x, 10, 40, 30), "ミ")) {
			Play(PlayState.E);
		}
		x += dist;
		if (GUI.RepeatButton (new Rect (x, 10, 40, 30), "ファ")) {
			Play(PlayState.F);
		}
		x += dist;
		if (GUI.RepeatButton (new Rect (x, 10, 40, 30), "ファ#")) {
			Play(PlayState.FS);
		}
		x += dist;
		if (GUI.RepeatButton (new Rect (x, 10, 40, 30), "ソ")) {
			Play(PlayState.G);
		}
		x += dist;
		if (GUI.RepeatButton (new Rect (x, 10, 40, 30), "ソ#")) {
			Play(PlayState.GS);
		}
		x += dist;
		if (GUI.RepeatButton (new Rect (x, 10, 40, 30), "ラ")) {
			Play(PlayState.A);
		}
		x += dist;
		if (GUI.RepeatButton (new Rect (x, 10, 40, 30), "ラ#")) {
			Play(PlayState.AS);
		}
		x += dist;
		if (GUI.RepeatButton (new Rect (x, 10, 40, 30), "シ")) {
			Play(PlayState.B);
		}
		x += dist;
		if (GUI.RepeatButton (new Rect (x, 10, 40, 30), "ド")) {
			Play(PlayState.C2);
		}
	}
	
	void Update () {
		nowPlaying = false;
		if (Input.GetKey ("z")) {
			Play(PlayState.C);
		}
		if (Input.GetKey ("s")) {
			Play(PlayState.CS);
		}
		if (Input.GetKey ("x")) {
			Play(PlayState.D);
		}
		if (Input.GetKey ("d")) {
			Play(PlayState.DS);
		}
		if (Input.GetKey ("c")) {
			Play(PlayState.E);
		}
		if (Input.GetKey ("f")) {
			Play(PlayState.F);
		}
		if (Input.GetKey ("v")) {
			Play(PlayState.FS);
		}
		if (Input.GetKey ("b")) {
			Play(PlayState.G);
		}
		if (Input.GetKey ("h")) {
			Play(PlayState.GS);
		}
		if (Input.GetKey ("n")) {
			Play(PlayState.A);
		}
		if (Input.GetKey ("j")) {
			Play(PlayState.AS);
		}
		if (Input.GetKey ("m")) {
			Play(PlayState.B);
		}
		if (Input.GetKey (",")) {
			Play(PlayState.C2);
		}
	}
} 

単純に音を鳴らすと押した瞬間にブツッという音が鳴ってしまいこれを防ぐのに苦労しました。最終的には出だしにいきなり音を鳴らさず、多少フェードさせる形にすると防げました。きっと音関連に強い人には常識だったりするのかも知れませんが多少は音関連の知識も増やさねばと思うエピソードでしたw

関連情報

nakamura001 @ ウィキ - トップページ/Unity(Unity3D)/サウンド/作成
http://www32.atwiki.jp/nakamura001/pages/274.html