↓動画
https://www.youtube.com/watch?v=nUqrsvC5NPo
DDSというのはソフトシンセで言うとWave Table方式のオシレーターの一種だ(と思う)
詳しいことはNational Instrumentsのドキュメントがわかりやすかった
http://www.ni.com/white-paper/5516/ja/
ピュンピュン2号はAVRで作ったシンセを公開してくださってるサイトをいろいろ参考にして
ソースを切り貼りしながら試行錯誤してプログラムを組んだのでかなり行き当たりばったりだ。
先人に習ってDDSのフェイズアキュムレーターは32bitで実装した。
でも16bitと32bitで、どれぐらい違いがあるのか調べてみたくなった。
Arduinoは16bitのCPUで、16bitまでの演算は掛け算も2クロックぐらいで済む。
逆に言うと32bit演算とか浮動小数点演算は極端に重くなる。
Arduinoで組んでもいいんだが、別にリアルタイムでやる必要もないので
普通のCで検証してみた。
32bitアキュムレーター
// DDSTest.cpp : コンソール アプリケーションのエントリ ポイントを定義します。//#include "stdafx.h"#include <stdio.h>#include <stdlib.h>#include <stdint.h>#include <io.h>#include <fcntl.h>#define _USE_MATH_DEFINES#include <math.h>/*********************************************************phaseRegister : 32bittunigWord : 32bitlookupTable : 8bit**********************************************************/#define SAMPLE_CLOCK 44100 // 44.1kHz#define TABLE_SIZE 0x100 // Lookup Table Sizeuint32_t phaseRegister;uint32_t tuningWord;int16_t waveValue;uint8_t *lookupTable;double frequency = 440.0; // 440Hzint period = 1323000; // 10sint _tmain(int argc, _TCHAR* argv[]){_setmode(_fileno(stdout), _O_BINARY);// Lookup Table の作成lookupTable = (uint8_t*)malloc(TABLE_SIZE);for (int i = 0; i < TABLE_SIZE; i++) {lookupTable[i] = (sin(2.0 * M_PI * i / TABLE_SIZE) / 2 + 0.5) * 0xFF;}tuningWord = frequency * pow(2.0, 32) / SAMPLE_CLOCK;phaseRegister = 0x00000000;for (int i = 0; i < period; i++) {int index;phaseRegister += tuningWord;index = phaseRegister >> 24;waveValue = (lookupTable[index] << 8) - 0x8000;// printf("%d\n", waveValue);// 16bit長の raw データとして出力fwrite(&waveValue, sizeof(waveValue), 1, stdout);}free(lookupTable);return 0;}
16bitアキュムレーター
// DDSTest.cpp : コンソール アプリケーションのエントリ ポイントを定義します。//#include "stdafx.h"#include <stdio.h>#include <stdlib.h>#include <stdint.h>#include <io.h>#include <fcntl.h>#define _USE_MATH_DEFINES#include <math.h>/*********************************************************phaseRegister : 16bittunigWord : 16bitlookupTable : 8bit**********************************************************/#define SAMPLE_CLOCK 44100 // 44.1kHz#define TABLE_SIZE 0x100 // Lookup Table Sizeuint16_t phaseRegister;uint16_t tuningWord;int16_t waveValue;uint8_t *lookupTable;double frequency = 440.0; // 440Hzint period = 1323000; // 30sint _tmain(int argc, _TCHAR* argv[]){_setmode(_fileno(stdout), _O_BINARY);// Lookup Table の作成lookupTable = (uint8_t*)malloc(TABLE_SIZE);for (int i = 0; i < TABLE_SIZE; i++) {lookupTable[i] = (sin(2.0 * M_PI * i / TABLE_SIZE) / 2 + 0.5) * 0xFF;}tuningWord = frequency * pow(2.0, 16) / SAMPLE_CLOCK;phaseRegister = 0x0000;for (int i = 0; i < period; i++) {int index;phaseRegister += tuningWord;index = phaseRegister >> 8;waveValue = (lookupTable[index] << 8) - 0x8000;//printf("%d\n", waveValue);// 16bit長の raw データとして出力fwrite(&waveValue, sizeof(waveValue), 1, stdout);}free(lookupTable);return 0;}
rawデータで出力して、SOX (http://sox.sourceforge.net/)でwavに変換して
WaveSpectra(http://www.ne.jp/asahi/fa/efu/soft/ws/ws.html)でFFTして比較した。
(16bit)
(32bit)
ほとんど差がない。
静止画ではわからないが、16bitの方は-80dB以下のノイズが振動している。
が、音源としてはどっちでも一緒な気がする。
音源は、正弦波ではなくてめちゃくちゃな波形を出力した時にどういう聞こえ方をするのかが
大事なんである。
ピュンピュン2号のArduinoでは処理速度が追いつかなくなったので
次は32bitのARMベースにしようかと思っていたが
16Bit処理に抑えられればまだまだいける気がします
一応、rawデータのバイナリを直接比較したが2,584KB中61バイト違うだけだった。
0 件のコメント:
コメントを投稿