DirectSound1

音を鳴らすには、様々なハードウェア機能を使って実現します。DirectSoundはその極地といっても過言ではありません。ここでは面倒ではありますけれど、ガチガチに決まったらルールに従ってサンプルコードを追いかける形になります。さて、音とは何かというところから始めようと思います。

音とは

普段何気なく聞いている音楽。その記録媒体の1つコンパクトディスク(CD)は、バイナリデータの集まりです。円盤の1秒は44100分割されていて、1分割分の音の高さは2バイトであり、ステレオの2チャンネルで構成されています。これをwavファイルフォーマットに置き換えると以下になります。

44100(Hz) * 2バイト(16bit) * 2(ステレオ)* 1(秒) + 44(ヘッダ) = 176,444バイト
1.png

wavファイルの中身はヘッダ部とデータ部に分かれています。ヘッダは0x00-0x43までの44バイト。残りのデータは0x0000で埋まり、ファイルサイズは176,444バイトとなっています。音の高さ2バイト(16bit)の中身は、符号付きsigned(-32768 ~ +32767, 無音は0)となっています。

440Hzのラ音

次に音を人工的に作ってみましょう。以下にpythonのソースコードを載せますが話したいことの横道になりそうなので読まなくても結構です。

import numpy as np
import wave
import struct
from matplotlib import pylab as plt

fname = '1sec_440Hz.wav'

fs  = 44100
f   = 440
sec = 1
A   = 32767
samples = sec * fs

t = np.linspace(0, sec, samples)
s = A * np.sin(2 * np.pi * f * t)
s = s.astype(np.int16)

data = struct.pack("h" * samples , *s)

wf = wave.open(fname, 'w')
wf.setnchannels(1)
wf.setsampwidth(2)
wf.setframerate(fs)
wf.writeframes(data)
wf.close()

plt.plot(s[0:441])
plt.show()

このプログラムは440Hzのドレミファでいうラ音を1秒間モノラル出力したものです。以下のグラフは横軸0〜441(44100 / 100)。縦軸は繰り返しの説明になりますが、2バイト(16bit)、符号付きsigned(-32768 ~ +32767, 無音は0)です。この440Hzとは1秒間の振動数を表しています。ドなら262、レなら294、ミなら330といった感じです。githubのresフォルダにwavファイルを入れてありますので聞いたことない方はぜひ視聴してください。無茶苦茶シブいシブ柿みたいな音という感想です。

2.png

ドレミを比較してみると振動幅が短くなっているのがわかると思います。

3.png

GBAで鳴らしてみる

GBAはモノラル、符号付き8bit signed(-128 ~ +128, 無音は0)です。またGBATEKでは次のようにアドバイスしています。

 The Sample Rate
 
 The GBA hardware does internally re-sample all sound output to 32.768kHz
 (default SOUNDBIAS setting).
 
 It'd thus do not make much sense to use higher DMA/Timer rates.
 Best re-sampling accuracy can be gained by using DMA/Timer rates of
 32.768kHz, 16.384kHz, or 8.192kHz (ie. fragments of the physical output rate).

適当翻訳すると「GBAは32.768kHzで音が鳴るから1秒間を32768Hz, 16384Hz, 8192Hz」のどれかにした方がいいぜ、といっています。例でいえば、画像ファイルのアスペクト比を無視して縮小をするんじゃないぞ、と忠告してもらっているようなものです。

加工方法

またもpython先生の出番です。さきほどのサンプルコードを横幅1秒間44100→16384にして、縦幅signed 16bit→8bitに変更します。

import numpy as np
import wave
import struct
from matplotlib import pylab as plt


list_file = ['1_c3.wav', '2_d3.wav', '3_e3.wav', '4_f3.wav', '5_g3.wav', '6_a3.wav', '7_b3.wav', '8_c4.wav']
list_hz = [262, 294, 330, 349, 392, 440, 494, 523]

fs  = 44100
sec = 1
A   = 32767
samples = sec * fs

for i in range(len(list_hz)):
	# Wav用
	t = np.linspace(0, sec, samples)
	s = A * np.sin(2 * np.pi * list_hz[i] * t)
	s = s.astype(np.int16)

	data = struct.pack("h" * samples , *s)

	wf = wave.open(list_file[i], 'w')
	wf.setnchannels(1)
	wf.setsampwidth(2)
	wf.setframerate(fs)
	wf.writeframes(data)
	wf.close()
 
	plt.plot(s[0:441])
	plt.show()


	# GBA用
	fs_gba = 16384
	samples_gba = sec * fs_gba

	t = np.linspace(0, sec, fs_gba)
	s = A * np.sin(2 * np.pi * list_hz[i] * t)
	s = s.astype(np.int16)

	for j in range(len(s)):
		s[j] = s[j] >> 8

	data = struct.pack("b" * samples_gba , *s)

	wf = open(list_file[i][:-4]+'.bin', 'wb')
	wf.write(data)
	wf.close()

	plt.plot(s[0:163])
	plt.show()
4.png

縦幅の数字変更-128 ~ 127を確認できると思います。波形はおんなじですね。(^^;

wav(windows用)bin(GBA用)
ファイルサイズ88,200+ヘッダ分44バイト16,384バイト

GBAのソースコード

全体を見ていただきましたがDMA, TIMER, VBLANK機能の盛り合わせです。

動作画面

5.png

履歴


トップ   一覧 検索 最終更新   ヘルプ   最終更新のRSS