javascript で音を鳴らしてみる

2023年5月7日

ごく簡単なサンプルです。

ソース

・html
<div id="p1970" class="h-flex">
	<div><button class="btn js-play1">再生1</button></div>
	<div><button class="btn ms-3 js-play2">再生2</button></div>
	<div class="mes"></div>
</div>
・css
.h-flex { display: flex; flex-direction: row }
.ms-3 { margin-left: 1rem }

.btn {
	padding: 0.5em 1em;
	border-radius: 5px;
	background-color: #CCC;
	transition: all 0.2s ease-in-out;
}

.btn:active {
	transform: scale(1.2);
	transition: 0.1s;
}
・javascript
jQuery(($) => {
	"use strict"

	class MySoundBase {
		constructor() {
			const audioContext =
				window.AudioContext || window.webkitAudioContext;
			this.ctx = new audioContext();
			this.init();
		}

		init() {
			this.type = 'sine';
			this.gain = 0.25;
		}

		now() {
			return this.ctx.currentTime;
		}

		setType(value) {
			this.type = value;
		}

		setGain(value) {
			this.gain = value;
		}

		sound(f = 440, duration = 1, start = null) {
			if (start === null) start = this.ctx.currentTime;
			
			const gainNode = this.ctx.createGain();
			gainNode.connect(this.ctx.destination);
			this.setGainParams(gainNode, duration, start);

			const osc = this.ctx.createOscillator();
			osc.onended = this.onEnded.bind(this);
			osc.connect(gainNode);
			osc.type = this.type;
			osc.frequency.value = f;

			osc.start(start);
			osc.stop(start + duration);
		}

		setGainParams(gainNode, duration, start) {
			gainNode.gain.linearRampToValueAtTime(this.gain, start);
			gainNode.gain.linearRampToValueAtTime(0, start + 0.5);
		}

		// note=48 -> Oct3: C..B
		// note=60 -> Oct4: C..B
		// note=72 -> Oct5: C..B
		note(note, duration, start = null) {
			this.sound(this.noteToF(note), duration, start);
		}

		noteToF(note) {
			return 440 * Math.pow(2, (note - 69) / 12);
		}

		onEnded(ev) {
		}

	}

	main();

	function main() {
		const typeNames = ['sine', 'square', 'sawtooth', 'triangle'];
		let type = 0, typeName;
		let A, B;

		$('.js-play1').click((ev) => {
			typeName = typeNames[type++ % 4];

			if (!A) A = new MySoundBase();
			A.init();
			A.setType(typeName);
			A.sound(440);
		});
			
		$('.js-play2').click((ev) => {
			const t = 0.25;
			let pos, notes;
			typeName = typeNames[type++ % 4];

			// 第一声
			if (!A) A = new MySoundBase();

			A.init();
			A.setType(typeName);
			pos = A.now();
			notes = [
				60, 62, 64, 65, 64, 62, 60, 0,
				64, 65, 67, 69, 67, 65, 64, 0,
			];
			for (const note of notes) {
				if (note != 0) A.note(note, t, pos);
				pos += t;
			}

			// 第二声
			if (!B) B = new MySoundBase();
			B.init();
			B.setType(typeName);
			pos = B.now();
			notes = [
				0, 0, 0, 0, 0, 0, 0, 0,
				60, 62, 64, 65, 64, 62, 60, 0,
			];
			for (const note of notes) {
				if (note != 0) B.note(note, t, pos);
				pos += t;
			}
		});
	}

});

javascript,sound

Posted by plkl