2014年5月27日火曜日

DDSのフェイズアキュムレーターのbit長の検証

ピュンピュン2号はArduinoでDDSで波形を生成している。


↓動画
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   : 32bit
tunigWord       : 32bit
lookupTable     :  8bit
**********************************************************/

#define SAMPLE_CLOCK 44100 // 44.1kHz
#define TABLE_SIZE    0x100   // Lookup Table Size

uint32_t phaseRegister;
uint32_t tuningWord;
int16_t waveValue;
uint8_t *lookupTable;

double frequency = 440.0;  // 440Hz
int period = 1323000;      // 10s

int _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   : 16bit
tunigWord       : 16bit
lookupTable     :  8bit
**********************************************************/

#define SAMPLE_CLOCK 44100 // 44.1kHz
#define TABLE_SIZE    0x100   // Lookup Table Size

uint16_t phaseRegister;
uint16_t tuningWord;
int16_t waveValue;
uint8_t *lookupTable;

double frequency = 440.0;  // 440Hz
int period = 1323000;       // 30s

int _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 件のコメント:

コメントを投稿