強火で進め このページをアンテナに追加 RSSフィード

整理された情報は こちら へどうぞ。

2012年07月24日

[][]音データをジェネレートする(頑張ればファミコンのピコピコ音も作れるよ)

こちらの記事を参考にしました。

Procedural audio with Unity | Game development | Features by Develop

http://www.develop-online.net/features/1678/Procedural-audio-with-Unity

この記事、バージョン3.5から導入された OnAudioFilterRead を使って音を出すという記事です。 OnAudioFilterRead は本来は鳴らしている音に効果を付けるなど加工する為のメソッドだと思うのですがこれ単体で音を鳴らす為に使っています。

Unity Script Reference – MonoBehaviour.OnAudioFilterRead

http://docs.unity3d.com/Documentation/ScriptReference/MonoBehaviour.OnAudioFilterRead.html

以下のC#のファイルをMain Cameraなどに貼り付けると音を鳴らせます。

【Sinus.cs】

using UnityEngine;
using System;// Needed for Math

public class Sinus : MonoBehaviour
{
	// un-optimized version
	public double frequency = 440;
	public double gain = 0.05;
	private double increment;
	private double phase;
	private double sampling_frequency = 48000;
	private bool playing = false;

	void OnAudioFilterRead (float[] data, int channels)
	{
		if (!playing)
			return;
		// update increment in case frequency has changed
		increment = frequency * 2 * Math.PI / sampling_frequency;
		for (var i = 0; i < data.Length; i = i + channels) {
			phase = phase + increment;
    
			data [i] = (float)(gain * Math.Sin (phase));
			// if we have stereo, we copy the mono data to each channel
			if (channels == 2)
				data [i + 1] = data [i];
			if (phase > 2 * Math.PI)
				phase = 0;
		}
	}
  
	void OnGUI ()
	{
		int y = 10;
		if (GUI.Button (new Rect (10, y, 100, 30), "Play")) {
			playing = true;
		}
		y += 40;
		if (GUI.Button (new Rect (10, y, 100, 30), "Stop")) {
			playing = false;
		}
	}
} 

基本波形

基本波形の正弦波、矩形波三角波、鋸歯波で音を鳴らすサンプルもこちらのサイトを参考に作成しました。

Lazuli » C#での基本波形生成処理の実装(正弦波、矩形波三角波、鋸歯波)

http://nanatomo.com/program/cs/sprogram/265

【PlayWave.cs】

using UnityEngine;
using System;// Needed for Math

public class PlayWave : MonoBehaviour
{
	const double PI = System.Math.PI;
	const double PI2 = PI * 2.0;
	const double PI_2 = PI / 2.0;
	const double sampling_frequency = 48000;
	const double PI2_SR = PI2 / sampling_frequency;
	private enum PlayState {
		Stop,
		SineWave,
		SquareWave,
		TriangleWave,
		SawtoothWave
	}

	const double frequency = 440;
	public double gain = 0.05;
	private double increment;
	private double time;
	private PlayState playState = PlayState.Stop;

	void SineWave(float[] data, int channels) {
		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));
			if (channels == 2)
				data [i + 1] = data [i];
			if (time > 2 * Math.PI)
				time = 0;
		}
	}

	void SquareWave(float[] data, int channels) {
		increment = frequency * 2 * PI / sampling_frequency;
		for (var i = 0; i < data.Length; i = i + channels) {
			time = time + increment;
    
			data [i] = (float)(gain * ((time % PI2) < PI2 * 0.5 ? 1.0 : -1.0));
			if (channels == 2)
				data [i + 1] = data [i];
			if (time > 2 * Math.PI)
				time = 0;
		}
	}
	
	void TriangleWave(float[] data, int channels) {
		increment = frequency * 2 * PI / sampling_frequency;
		for (var i = 0; i < data.Length; i = i + channels) {
			time = time + increment;
			if (time > 2 * Math.PI)
				time = 0;
    
			double t = (time + PI_2) % PI2;
			data [i] = (float)(gain * ((t < PI ? t - PI : PI - t) / PI_2 + 1.0));
			if (channels == 2)
				data [i + 1] = data [i];
		}
	}
	
	void SawtoothWave(float[] data, int channels) {
		increment = frequency * 2 * PI / sampling_frequency;
		for (var i = 0; i < data.Length; i = i + channels) {
			time = time + increment;
    
			data [i] = (float)(gain * ((time + PI) % PI2) / PI - 1.0);
			if (channels == 2)
				data [i + 1] = data [i];
			if (time > 2 * Math.PI)
				time = 0;
		}
	}
	
	void OnAudioFilterRead (float[] data, int channels)
	{
		switch (playState) {
			case PlayState.SineWave:
				SineWave(data, channels);
				break;
			case PlayState.SquareWave:
				SquareWave(data, channels);
				break;
			case PlayState.TriangleWave:
				TriangleWave(data, channels);
				break;
			case PlayState.SawtoothWave:
				SawtoothWave(data, channels);
				break;
		}
	}
  
	void OnGUI ()
	{
		int y = 10;
		if (GUI.Button (new Rect (10, y, 100, 30), "サイン波")) {
			playState = PlayState.SineWave;
		}
		y += 40;
		if (GUI.Button (new Rect (10, y, 100, 30), "矩形波")) {
			playState = PlayState.SquareWave;
		}
		y += 40;
		if (GUI.Button (new Rect (10, y, 100, 30), "三角波")) {
			playState = PlayState.TriangleWave;
		}
		y += 40;
		if (GUI.Button (new Rect (10, y, 100, 30), "ノコギリ波")) {
			playState = PlayState.SawtoothWave;
		}
		y += 40;
		if (GUI.Button (new Rect (10, y, 100, 30), "Stop")) {
			playState = PlayState.Stop;
		}
	}
} 

関連情報

nakamura001 @ ウィキ - トップページ/Unity(Unity3D)/サウンド/作成

http://www32.atwiki.jp/nakamura001/pages/274.html

春条春条 2013/08/01 11:04 1年以上前の記事に対し、重箱の隅をつつくようで申し訳ないのですが、
少し気になる点がありましたので、コメントを失礼させて頂きます。


TriangleWaveの生成部分ですが、現状の方法ですと若干波形が崩れてしまいます。

そこで、
if (time > 2 * Math.PI) time = 0;
の判定を
time = time + increment;
の直下へ配置することで、出力される波形が改善されます。


また、蛇足ではありますが、厳密なピッチが必要ないのであれば
time = 0; 
とせず、
time -= PI2;
とすることで、更に出力される波形が改善されると思います。

nakamura001nakamura001 2013/08/05 02:49 波形が崩れている事を確認し、プログラム修正しました。
この度はご指摘ありがとうございました。

投稿したコメントは管理者が承認するまで公開されません。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト

コメントを書くには、なぞなぞ認証に回答する必要があります。