Hatena::ブログ(Diary)

UDONCHAN

November 05, 2010

[]疑似乱数の善し悪しを可聴化して耳で聴いて感じてみた

サイコロを振って出る目が予測不可能である一方で,コンピュータの世界は予定調和で,つまるところ,同じプログラムを同じように走らせると同じ結果が得られる.じゃあ乱数はどんなんだと言われるかもしれないが,(少しプログラミングを嗜んだ程度の人ならば分かるだろうけれど),例え乱数を仕込んだプログラムに対しても同様の入力を与えた場合,往々にして同様の結果が得られるものだ.プログラムにおける乱数とはそういうもので,こういう乱数を一般的に疑似乱数というらしい.このエントリで扱う乱数は疑似乱数とする.

さて,世の中には良い乱数と悪い乱数が存在しているらしく,良い乱数ほど処理コストが高く,専門家の皆様は解決に苦しんでいるらしい.僕は,乱数の専門家では無いので乱数の善し悪しをどう評価するかは知らないのだけど,なんとなく乱数が分散していた方が良いのではないのかと思った.じゃあ本当にそうなのか確かめるにしても結局統計的な手法を用いなくてはいけなくて,統計的な手法はやるのめんどくさいし,そもそもそういうやり方ならば正攻法で行けば良い訳で,かといって専門外の分野に首を突っ込んでアレするのもナンなので,じゃあ可視化するかとかも思ったけれど芸が無い.そういえば最近音プログラミング始めたということを思い出して,可聴化することにした.

rand()
Download
random()
Download
drand48
Download
Mersenne Twister
Download

上から順番に悪い乱数から良い乱数になっている筈だけど,OSX の rand() は,オリジナルの rand と明らかに挙動が違って(srandしなくても乱数が発行されるあたりまったく普通のrand()じゃ無い),恐らく名前だけが同じで別の処理をしているのだろう.聴いた感じだと一般的に良いとされている Mersenne Twister*1 の乱数とほとんど同じか寧ろそれよりも良いのではないかという感じ.

ちなみに,音を出すプログラムは CoreAudio を用いた.C で書いたけれど全く移植性が無く,OSXでしか動かない.QuickTime のソフトシンセを呼ぶ仕組みになっているけれど,その仕様がGMに準拠してるってこと以外どこにも見当たらなくて,適当に叩いたら Roland の SC-55 程度のことが出来ることがわかった.まぁ,いまどき midi ってのはあんまり無い感じかもしれないけど,この程度のことをするのだったら midi はなにげに楽だ.(Linux だったら /dev/dsp を叩けばよいから楽なんだけど…)

#include <stdio.h>
#include <unistd.h>
#include <CoreServices/CoreServices.h>
#include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioToolbox.h>

#define kMidiMessage_ControlChange 0xB
#define kMidiMessage_ProgramChange 0xC
#define kMidiMessage_BankMSBControl 0
#define kMidiMessage_BankLSBControl 32
#define kMidiMessage_NoteOn 0x9

OSStatus CreateAUGraph (AUGraph *outGraph, AudioUnit *outSynth) {
	OSStatus result;
	AUNode synthNode, limiterNode, outNode;
	AudioComponentDescription cd;
	cd.componentManufacturer = kAudioUnitManufacturer_Apple;
	cd.componentFlags = 0;
	cd.componentFlagsMask = 0;
	NewAUGraph (outGraph);
	cd.componentType = kAudioUnitType_MusicDevice;
	cd.componentSubType = kAudioUnitSubType_DLSSynth;
	AUGraphAddNode (*outGraph, &cd, &synthNode);
	cd.componentType = kAudioUnitType_Effect;
	cd.componentSubType = kAudioUnitSubType_PeakLimiter;  
	AUGraphAddNode (*outGraph, &cd, &limiterNode);
	cd.componentType = kAudioUnitType_Output;
	cd.componentSubType = kAudioUnitSubType_DefaultOutput;  
	AUGraphAddNode (*outGraph, &cd, &outNode);
	AUGraphOpen (*outGraph);
	AUGraphConnectNodeInput (*outGraph, synthNode, 0, limiterNode, 0);
	AUGraphConnectNodeInput (*outGraph, limiterNode, 0, outNode, 0);
	AUGraphNodeInfo(*outGraph, synthNode, 0, outSynth);
	return result;
}

int main (int argc, const char * argv[]) {
    AUGraph graph = 0;
    AudioUnit synthUnit;
    UInt8 midiChannelInUse = 0;	
    UInt8 msb_controll_value = 8;
    UInt8 program_change = 80;
    CreateAUGraph (&graph, &synthUnit);
    AUGraphInitialize (graph);
    MusicDeviceMIDIEvent(synthUnit, 
                         kMidiMessage_ControlChange << 4 | midiChannelInUse, 
                         kMidiMessage_BankMSBControl, 
                         msb_controll_value,
                         0);
    MusicDeviceMIDIEvent(synthUnit, 
                         kMidiMessage_ProgramChange << 4 | midiChannelInUse, 
                         program_change, 
                         0,
                         0); /* sine wave */
    CAShow (graph);
    AUGraphStart (graph);
    for (int i = 0; i < 100; i++) {
	int r = RANDOM_FUNC() % 88;
	UInt32 noteNum = 21 + r;
	printf("%d\n", noteNum);
	UInt32 onVelocity = 127;
	UInt32 noteOnCommand = 	kMidiMessage_NoteOn << 4 | midiChannelInUse;
	MusicDeviceMIDIEvent(synthUnit, noteOnCommand, noteNum, onVelocity, 0);
	usleep (0.1 * 1000 * 1000);
	MusicDeviceMIDIEvent(synthUnit, noteOnCommand, noteNum, 0, 0);
    }	
    usleep(3 * 1000 * 1000);
    if (graph) {
	AUGraphStop (graph);
	DisposeAUGraph (graph);
    }
    return 0;
}

動いてるコードはこの辺にある.udonchan’s PlayRandomMidi at master - GitHub

nantoka3nantoka3 2010/11/07 00:27 これ面白いですね、ただ乱数の分散の度合いを計る目的では、分布の状態を一覧できるほうがいいかもしれませんね。乱数を可聴閾の周波数にmapして生成したスペクトルを逆fftしたノイズ音(もっと厳密にすると人の耳のラウドネス曲線の逆フィルターをかける)とかがいいかもと思いました

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証