#author("2023-05-17T01:05:55+09:00;2023-05-16T19:36:12+09:00","","")
#author("2023-05-17T08:56:05+09:00","","")
** DirectSound1 [#w29c50a4]
音を鳴らすには、様々なハードウェア機能を使って実現します。DirectSoundはその極地といっても過言ではありません。ここでは非常に面倒ではありますけれど、ガチガチに決まったらルールに従ってサンプルコードを追いかける形になります。窮屈ではありますがご容赦願います。さて、音とは何かというところから始めます。

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

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

#ref(1.png,nolink)

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

** 440Hzのラ音 [#wf9db66e]
次に音を人工的に作ってみましょう。以下にpythonのソースコードを載せますが読まなくて結構です。
次に音を人工的に作ってみましょう。以下に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秒間モノラル出力したものです。以下のグラフは横軸0〜441(44100 / 100)。縦軸は繰り返しの説明になりますが、2バイト(16bit)、符号付きsigned(-32768 ~ +32767, 無音は0)です。この440Hzとは1秒間の振動数を表しています。ドなら262、レなら294、ミなら330といった感じです。githubのresフォルダにwavファイルを入れてありますので聞いたことない方はぜひ視聴してください。私の聞いた感想としては無茶苦茶シブいですシブ柿です。

#ref(2.png,nolink)
#ref(2.png,nolink,80%)

-音階
|ドレミ記譜法 | ABC記譜法 | 周波数 |
|ド   | C | 262 |
|レ   | D | 294 |
|ミ   | E | 330 |
|ファ | F | 349 |
|ソ   | G | 392 |
|ラ   | A | 440 |
|シ   | B | 494 |
|ド   | C | 523 |

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

#ref(3.png,nolink,80%)

** GBAで鳴らしてみる [#e950c7a3]
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で音が鳴るんで32.768kHz, 16.384kHz, 8.192kHz」の
~
どれかにしといた方がいいぜ、とアドバイスしてくれてます。~

例でいえば、画像ファイルのアスペクト比を無視して~
縮小をするんじゃないぞ、と忠告してもらっているようなものです。~
音の中身はモノラルであり、音単位は8bit singed(符号付き)となっています。~
適当翻訳すると「GBAは32.768kHzで音が鳴るから1秒間を32768Hz, 16384Hz, 8192Hz」のどれかにした方がいいぜ、といっています。例でいえば、画像ファイルのアスペクト比を無視して縮小をするんじゃないぞ、と忠告してもらっているようなものです。

** 加工方法 [#q4014486]
サンプルのsrc\res\Makefileの抜粋を見てみましょう。
またもpython先生の出番です。さきほどのサンプルコードを1秒間44100→16384横幅にして、8bit符号付きsignedに変更します。
 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()

  .wav.s: $(WAVFILES)
  	@echo \# converting $<
  	@./sox $< -r 16384 -c 1 -B -s -1 $*.raw
  	@padbin 4 $*.raw
  	@bin2s $*.raw > $@
  	@rm $*.raw
#ref(4.png,nolink,80%)
縦幅の変更を確認できると思います。

データはsox(音フォーマット変換ツール)、padbin(バイト境界調整ツール)、~
bin2s(アセンブリテキスト変換ツール)、に掛けていき、GBAのプロジェクトと結合します。~
soxのコマンドオプションを詳しく見てみましょう。~
soxのコマンドオプションはこのようになっています。~
|  | wav(windows用) | bin(GBA用) |
|ファイルサイズ | 88,200+ヘッダ分44バイト | 16,384バイト |

  -r RATE       sample rate of audio
  -c CHANNELS   number of channels of audio data; e.g. 2 = stereo
  -B            force endianness to big/little
  -s            sample encoding: signed
  -1            sample size in bytes
** GBAのソースコード [#b0206fc8]

コマンドプロンプトで打ち込んだ結果を以下に表します。~

  > sox 1sec.wav -r 16384 -c 1 -B -s -1 1sec.raw
  >
  > dir 1sec.raw
  >
  > 2019/01/05  xx:xx            16,384 1sec.raw

0x00で埋まった16,384サイズのファイルが出来上がりました。~
GBAは「16384Hz モノラル 8bit(符号付き)」形式で鳴らす予定なので~
ヘッダがない、素のデータのみ、ということがわかると思います。~

** GBA側のソースコード [#b0206fc8]


*** 動作画面 [#l222045a]


** 履歴 [#l5bd00ff]
- 2023/05/09
- 2023/05/17
- 2019/01/05


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