Granular Synthesisの音響パラメータ
1995年11月 長嶋洋一
コンピュータ音楽の分野で注目される音響合成手法Granular Synthesisの概要を紹介し、 コンパクトなシステムとしての実現手法について紹介する。 次いで、Granular Synthesisの音響を特徴づける音響パラメータについて検討するとともに、 コンピュータ音楽の生成環境としてのリアルタイム制御のための手法の実現について述べる。 また、この応用による新しい音響合成手法として登場したGranular Samplingについて紹介し、 具体的な音楽作品への応用事例について述べる。 最後に、まだ世界的に十分な検討のされていないこのサウンドの音響心理学的な評価実験について、 その課題の検討を行うとともに研究への協力を呼びかける。 1. はじめにコンピュータ音楽の分野ではいろいろな楽音合成方式が提案され実現されてきている[1][2][3]。 その一つの手法であるGranular Synthesis[4]については、楽音信号として「ピッチ」に相当する 周期的な成分を持たないこと、音響を聴取した心理パラメータと楽音合成パラメータの対応について 未知であること、実際の楽音合成システムとしての実時間処理量が膨大であることなどから、歴史の 古いアイデアでありながらこれまで注目されることはあまり多くなかった。この分野での従来研究としては、専用のDSPによるリアルタイムGranular Synthesisの実験[5] 、 複数のパラメータを制御するためのGranular Synthesis専用コントロールGUI [6]、 PCM音源のルーピング機能を応用した疑似Granular Synthesis[7]、 ニューラルネットワークによるパラメータ補間制御[8]、 楽音合成の基本要素であるGrainにサンプリング音響断片を用いたGranular Sampling[9]などがある。 このように、Granular Synthesisとは、もともと音楽の要素であるメロディ・ハーモニー・リズムなどを 生成しにくい楽音合成方式であるために、まだ十分に検討されていない領域も多いが、Granular Samplingの 特異な音響とともに、最近になって再び注目されている。 2. Granular Synthesisの実現2-1. Granular Synthesisの概要Granular Synthesisとは、「Grain」と呼ばれる基本要素から構成される。
もともとのGranular SynthesisにおけるGrainとは、上図の ような「なめらかなパルス状の粒」であり、単独では「音」として知覚 されることはない。 また、このGrainを周期的にバースト状に繰り返し発生させた場合には、 いわゆるエンジン音のようなピッチ感とともにある種のサウンドとして知覚 できるが、この「等間隔」というのもGranular Synthesisにおいては 避けられるのが一般的である。
一般にGranular Synthesisにおいては、上図のように、このGrainを 非常に多数にわたって、空間的・時間的にランダムに配置する、という手法 によって、全体として一つの音響を生成する。 おおよそのオーダとしては、 Grainの幅として数msecから数十msec、Grainの幅は得られる音響のレベルに 対してかなり小さく、Grainの平均的な密度としては1秒間に数千から 数万、というものが一般的である。 2-2. 疑似Granular SynthesisこのようなGrainをそれぞれ「非実時間的」に配置演算したデータを再生してGranular Synthesis音響 を生成したのがいわゆる第一世代であり、 これに続いてコンピュータの処理能力の向上とともに、 リアルタイムにGranular Synthesisを行うというリバイバルが、約10年後に 第二世代として登場した。 これらは主に、ワークステーションに付加した 専用の信号処理演算ボード等を用いるか、あるいは専用のDSPエンジンによる 強力な信号処理能力によってリアルタイムGranular Synthesisを実現したものである。
これに対して、筆者は上図のような考え方で、安価でシンプルな、PCM音源を 利用した疑似Granular Synthesisによるコンパクトなリアルタイム楽音合成の制御を 提案した[10][11]。 最近のパソコンに内蔵された「PCM音源」とは、ステレオ16ビットで44.1KHz程度の 固定サンプリングデータを単純に「再生」するだけのものである。 これでは1秒間に数千から数万という個数のGrainを生成するGranular Synthesisをリアルタイム に実現するのは困難である。そこで、このシステムでは市販のパソコン用MIDI音源を利用 している[7]。 これは内部に16音ポリフォニック・ステレオ音源となる専用DSPと制御CPUを持った 一種のシンセサイザであり、多種の楽器音データを内蔵した大容量マスクROMとともに、 波形データ用のRAMを搭載しているものである。 この波形RAM部分にGrainとなる音響要素をダウンロードするとともに、一般にPCM音源で 用いられる「ルーピング」のパラメータを活用して「Grainの再生領域」「無音データの 再生領域」をうまく設定することで、非周期的楽音要素のGrainが多量に生成され、 さらに頻繁にルーピングポイントを変更することで(固定していると固定ピッチが 知覚されてしまう)疑似Granular Synthesis音響を得る、というのがこの手法の中核である。 2-3. パラメータのニューラルネットワーク制御この疑似Granular Synthesisシステムにおいては、リアルタイムにパラメータを 制御する「動的補間手法」としてニューラルネットワークを利用した[8]。 システムとしては標準的な3層構成とし、ジョイスティック形状のセンサから 与えられた2次元情報を入力データとして、「Grainの幅」「平均的なループ長」 という異なる二つのパラメータ出力のマッピングを与えた。入力データとしてそれぞれ16段階の値をとる2次元のパラメータ空間の内部の16点 だけを教師データとして設定し、BP学習によって得られたニューラルネットワーク にリアルタイムに任意の256点の情報をセンサから与えた。 この実験の結果、パラメータ空間で離散的に与えられて学習された特性が、実際に 音響としてGranular Synthesisパラメータを「良好に補間」していることが確認 できた。 ただしそれ以上の効果として特筆すべき成果もなく、ニューラルネット ワークの異種パラメータ補間という特質を単に追試したにとどまった。 これだけでは各種の学習結果をテーブルとして参照しても結果は同じであり、 リアルタイム制御を行った実際の作品においてはテーブル参照方式を採用した。 2-4. PCM音源の並列処理によるGranular Synthesis16音ポリフォニックPCM音源によって、同時に16Grainを生成するとともに1秒間に 数百から数千というオーダのGrainを生成できるにもかかわらず、このシステムを 「疑似」Granular Synthesisと呼んだ理由は、ルーピングパラメータの設定に よって「ピッチ感」が生じてしまう問題点によるものであった。 これは、Grainの密度を上げるために16個の発音チャンネル全てに対して ループ長を短く設定すると、繰り返し周期が短くなって特定のピッチを知覚できて しまうものであり、音楽的にGrain密度の上昇が要求されても対応できない限度 があった。
そこで上図のように、PCM音源を並列動作させることで、よりGrain数の多い Granular Synthesisの実現を目指した[12]。 2-4-1. システムの概要本システムではまず基本構成として、16発音チャンネルを持つPCM音源DSPシステムを 10台用意して、同時発生Grain総数を160個に拡張した。 ここでポイントとなるのは、10台の電子楽器を単純に並べるような拡張ではなく、 統一された制御によって動作する一体化されたシステムとして構築するところである。 幸い、使用したPCM音源はパソコン拡張スロットに挿入する形状をしているため に、拡張ラックに多数を並べるという方法で外見的には容易に実現できた。各ボードは固定された共通のI/Oアドレスと割り込みアドレスを保有しておりバス上 で競合するために、ハードウェア的な改造により個別のアドレス割り当てを設 定するとともに、パソコン側とのインターフェースとして割り込みでなくポーリング 方式を採用した。 これはボード上のCPUファームウェアとしてGranular Synthesis 処理を実現していることもあり、10台並列処理によるホスト側の負担はそれほど重く ないと判断したからである。 それぞれのボードにはMIDIインターフェースも 搭載されているが、後述するようにこの部分は使用せず、MIDI経由の制御情報も統一 的にバス経由でホスト側から与えることとした。また、ステレオのオーディ オ出力については10チャンネルのステレオミキサーを共通規格ボード上に製作して 同じラックに格納した。 2-4-2. ファームウェアのダウンロードPCM音源ボード上の制御CPUのファームウェア・プログラムについては、本来ROM化 されていて機能変更は不可能なものであり、基本的にマルチティンバーMIDI音源 としてしか使用できないものである。 しかしホスト側パソコンとのインターフェース 仕様が公開されるとともに、ボード上のワークRAM領域にファームウェアのサ ブルーチンをダウンロードしてここに制御を移行させる、いわば「パッチを当てる」 ための手段が公開されている[7]。 本システムではこれを利用して、ホスト側 パソコンからそれぞれのボードのワークRAM領域の一部にGranular Synthesis処理の ためのファームウェアをダウンロードする方式を採用した。一旦この専用ファ ームウェアに移行したボードはリセットによってのみ通常状態に復帰する。 ファームウェアとしてMIDI監視やマルチティンバー音源処理に関する部分を省略し、 16発音チャンネル全てが一つのGranular Synthesis音響を生成するために使 用されるようにした。必要なパラメータはバスから与えて変更されるが、たとえば MIDIのノートオン情報に相当する情報すら省略されている。すなわち、全パラ メータのセットとともに16チャンネルは楽音信号の生成を無限に続けており、出力 信号が現れるかどうかはGrainの振幅制御である「音量」パラメータに依存する。 2-4-3. GrainデータのダウンロードPCM音源でGranular Synthesisを実現するためには、周期的信号源としての波形 メモリを非周期的に読み出すための工夫が必要である。本システムでは、ボード上 に提供されている波形RAMのバスに改造を加え、アドレス空間の拡張とバスのプル ダウンを行っている。すなわち、簡略形でないフルデコード方式の波形アドレス 制御とともに波形メモリバスをプルダウン方式とすることで、ルーピングポイントに 非該当領域が指定された区間は「無音」信号を再生することで、非周期的要 素のGrain波形が生成されるようになっている[7][12]。このGrainデータについては、従来は波形ROMを変更して一周期の正弦波形状Grainに 対して、2倍・3倍成分を段階的に重み付けしたGrainを作成して切り換えてみた が、聴感上はあまり劇的な変化が得られなかった。 そこで本システムでは、ホスト 側パソコンとのバスインターフェース経路より、波形RAMデータを直接にダウン ロードする方法を採用した。この場合、実際にはボード上のCPUと通信してワーク エリアに一旦転送したデータを内部波形RAMに転送するようなサービスルーチンを 呼び出す必要があり、Granular Synthesis処理に入ってしまうとGrain形状の再 ロードを行えない制限がある。また、もともと波形RAM容量が小さいために、古典的な Granular Synthesisの意味でのGrainについては任意形状のものが使用できるが、 Granular Samplingのような長時間のGrainについては不可能である、という制限が ある。 3. Granular Synthesisのパラメータ
この並列分散処理Granular Synthesisシステムにおいては、上図のように、 パラメータとして
なお、実験的に各種パラメータを変化させた聴取実験[1MB] および具体的な作品に応用した 実験 [13][14] によると、上記パラメータのうちもっとも聴覚上の影響 の大きなものは「Grainの幅」「Grainの時間的頻度」の二つであり、PCM音源利用の 場合にはそれぞれ「ピッチ」「ループ長」という一般的なパラメータとして容易に 制御できた。 Grainの形状についてはGranular Samplingほど劇的な形状特性のGrain については、まだ十分な調査・検討はできていない。 また、コンピュータ音楽システムとして、具体的にはMIDIデータとして Granular Synthesisのパラメータを転送している。 この情報トラフィック量が問題となるが、従来システムにおいて実際に Granular Synthesis音響を使用した局面ではかなり多量のMIDI情報 が流れている[13][14]が、データ精度を適当に粗く量子化 することで、現実には問題なく「音素材」として利用できた。 4. Granular SamplingGranular Synthesisに関する最近数年間の最大のトピックといえば、古典的な 単純波形であったGrainを、リアルタイムにサンプリングされた音響断片に 適当なウインドウをかけたものとした、Granular Samplingで ある。 これだけの違いでどのような音響が生成されるかは直観的には想像 しにくいが、具体的な音楽作品のレベルにまで到達した実例が世界的に 数多く報告されている[9]。筆者はこのGranular Samplingについて、SGI社Indyワークステーション上で 開発して実装し、具体的な音楽作品のための音響[1.3MB] として利用した。
上図はその画面であり、ユーザはマウス操作でリアルタイムに 各種のパラメータを変更できる。 まだ簡易型で同時生成Grain数が20--40個と比較的小規模であるが、 ある程度の効果については検証できている。 筆者の開発した、このソフトウェアのソースプログラムは以下のようなものである。 |
#include <stdio.h> #include <math.h> #include <limits.h> #include <signal.h> #include <unistd.h> #include <audio.h> #include <sys/types.h> #include <sys/prctl.h> #include <sys/schedctl.h> #include <X11/Intrinsic.h> #include <X11/StringDefs.h> #include <X11/Xaw/Box.h> #include <Xm/Xm.h> #include <Xm/PushB.h> #include <Xm/Label.h> #include <Xm/Scale.h> #define FIFO_SIZE 48000 #define pi 3.1415926535 unsigned int audio_pid; int audio_mode, sample_param, pitch_param, offset_param; char *audio_menu[] = { "through", "distrotion", "oscillator", "granular synth", "granular sampling" }; short buffer[FIFO_SIZE]; void self_kill(), create_sound(), sound_off(), scaler_volume(), scaler_sample(), scaler_pitch(), scaler_offset(); void audio_through(), audio_distrotion(), audio_oscillator(), audio_granular(), audio_effector(); void main(unsigned int argc, char** argv) { Widget toplevel, title, bigbox, sbox[10], pb2[5], pb3[3], volume, sample, pitch, offset; XmString xmstr; Arg args[10]; char buf[80]; int i,n; /***** Motif Screen Define *****/ toplevel = XtInitialize( argv[0], "", NULL, 0, &argc, argv ); bigbox = XtCreateManagedWidget( "", boxWidgetClass, toplevel, NULL, 0 ); xmstr = XmStringCreateSimple(" (^_^) Audio Test by Yoichi Nagashima (^_^) "); XtSetArg( args[0], XmNlabelString, xmstr ); title = XtCreateManagedWidget( "", xmLabelWidgetClass, bigbox, args, 1 ); for(i=0;i<10;i++){ sbox[i] = XtCreateManagedWidget( "", boxWidgetClass, bigbox, NULL, 0 ); } pb3[0] = XtCreateManagedWidget( "Sound Port OFF", xmPushButtonWidgetClass, sbox[0], NULL, 0 ); pb3[1] = XtCreateManagedWidget( "Quit to System", xmPushButtonWidgetClass, sbox[0], NULL, 0 ); pb2[0] = XtCreateManagedWidget( "Mode(1) Through", xmPushButtonWidgetClass, sbox[1], NULL, 0 ); pb2[1] = XtCreateManagedWidget( "Mode(2) Distortion", xmPushButtonWidgetClass, sbox[1], NULL, 0 ); pb2[2] = XtCreateManagedWidget( "Mode(3) Oscillator", xmPushButtonWidgetClass, sbox[1], NULL, 0 ); pb2[3] = XtCreateManagedWidget( "Mode(4) Granular Synth", xmPushButtonWidgetClass, sbox[1], NULL, 0 ); pb2[4] = XtCreateManagedWidget( "Mode(5) Granular Sampling", xmPushButtonWidgetClass, sbox[1], NULL, 0 ); n = 0; XtSetArg( args[n], XmNminimum, 0 ); n++; XtSetArg( args[n], XmNmaximum, 255 ); n++; XtSetArg( args[n], XmNscaleHeight, 30 ); n++; XtSetArg( args[n], XmNscaleWidth, 250 ); n++; XtSetArg( args[n], XmNorientation, XmHORIZONTAL ); n++; xmstr = XmStringCreateSimple("Speaker Gain Control"); XtSetArg( args[n], XmNtitleString, xmstr ); n++; XtSetArg( args[n], XmNshowValue, True ); n++; volume = XtCreateManagedWidget( "", xmScaleWidgetClass, sbox[2], args, n ); n = 0; XtSetArg( args[n], XmNminimum, 1 ); n++; XtSetArg( args[n], XmNmaximum, 99 ); n++; XtSetArg( args[n], XmNscaleHeight, 30 ); n++; XtSetArg( args[n], XmNscaleWidth, 250 ); n++; XtSetArg( args[n], XmNorientation, XmHORIZONTAL ); n++; xmstr = XmStringCreateSimple("Gr. Density Control"); XtSetArg( args[n], XmNtitleString, xmstr ); n++; XtSetArg( args[n], XmNshowValue, True ); n++; sample = XtCreateManagedWidget( "", xmScaleWidgetClass, sbox[3], args, n ); n = 0; XtSetArg( args[n], XmNminimum, 1 ); n++; XtSetArg( args[n], XmNmaximum, 99 ); n++; XtSetArg( args[n], XmNscaleHeight, 30 ); n++; XtSetArg( args[n], XmNscaleWidth, 250 ); n++; XtSetArg( args[n], XmNorientation, XmHORIZONTAL ); n++; xmstr = XmStringCreateSimple("Grain Width Control"); XtSetArg( args[n], XmNtitleString, xmstr ); n++; XtSetArg( args[n], XmNshowValue, True ); n++; pitch = XtCreateManagedWidget( "", xmScaleWidgetClass, sbox[4], args, n ); n = 0; XtSetArg( args[n], XmNminimum, 1 ); n++; XtSetArg( args[n], XmNmaximum, 32 ); n++; XtSetArg( args[n], XmNscaleHeight, 30 ); n++; XtSetArg( args[n], XmNscaleWidth, 250 ); n++; XtSetArg( args[n], XmNorientation, XmHORIZONTAL ); n++; xmstr = XmStringCreateSimple("Grain Offset Control"); XtSetArg( args[n], XmNtitleString, xmstr ); n++; XtSetArg( args[n], XmNshowValue, True ); n++; offset = XtCreateManagedWidget( "", xmScaleWidgetClass, sbox[5], args, n ); /***** Callback Routine Define *****/ for(i=0;i<5;i++){ XtAddCallback( pb2[i], XmNactivateCallback, create_sound, audio_menu[i] ); } XtAddCallback( pb3[0], XmNactivateCallback, sound_off, NULL ); XtAddCallback( pb3[1], XmNactivateCallback, self_kill, NULL ); XtAddCallback( volume, XmNvalueChangedCallback, scaler_volume, NULL ); XtAddCallback( volume, XmNdragCallback, scaler_volume, NULL ); XtAddCallback( sample, XmNvalueChangedCallback, scaler_sample, NULL ); XtAddCallback( sample, XmNdragCallback, scaler_sample, NULL ); XtAddCallback( pitch, XmNvalueChangedCallback, scaler_pitch, NULL ); XtAddCallback( pitch, XmNdragCallback, scaler_pitch, NULL ); XtAddCallback( offset, XmNvalueChangedCallback, scaler_offset, NULL ); XtAddCallback( offset, XmNdragCallback, scaler_offset, NULL ); /***** Start Realize ! *****/ XtRealizeWidget(toplevel); XtMainLoop(); } void self_kill(Widget w, caddr_t message, caddr_t para) { sound_off(); printf("\nBye Bye ! (^o^)/\n\n"); exit(0); } void create_sound(Widget w, caddr_t message, caddr_t para) { sound_off(); if( !strcmp( message, "distrotion" ) ){ sample_param = 1; audio_pid = sproc( audio_distrotion, PR_SALL ); audio_mode = 1; } else if( !strcmp( message, "oscillator" ) ){ sample_param = 1; audio_pid = sproc( audio_oscillator, PR_SALL ); audio_mode = 2; } else if( !strcmp( message, "granular synth" ) ){ sample_param = 1; pitch_param = 1; audio_pid = sproc( audio_granular, PR_SALL ); audio_mode = 3; } else if( !strcmp( message, "granular sampling" ) ){ sample_param = 16; pitch_param = 1; offset_param = 1; audio_pid = sproc( audio_effector, PR_SALL ); audio_mode = 4; } else if( !strcmp( message, "through" ) ){ audio_pid = sproc( audio_through, PR_SALL ); audio_mode = 5; } if (audio_pid == -1) exit(-1); printf(" Registered Audio PID = %d\n",audio_pid); } void sound_off() { long parameter_buffer[4]; if(audio_pid != 0){ parameter_buffer[0] = AL_LEFT_SPEAKER_GAIN; parameter_buffer[2] = AL_RIGHT_SPEAKER_GAIN; ALgetparams( AL_DEFAULT_DEVICE, parameter_buffer, 4 ); parameter_buffer[1] = 50; parameter_buffer[3] = 50; ALsetparams( AL_DEFAULT_DEVICE, parameter_buffer, 4 ); kill(audio_pid, SIGKILL); printf(" Killed : Audio PID = %d\n",audio_pid); audio_pid = 0; audio_mode = 0; } } void scaler_volume(Widget w, caddr_t message, caddr_t para) { long parameter_buffer[4]; XmScaleCallbackStruct *callback = (XmScaleCallbackStruct *) para; parameter_buffer[0] = AL_LEFT_SPEAKER_GAIN; parameter_buffer[2] = AL_RIGHT_SPEAKER_GAIN; ALgetparams( AL_DEFAULT_DEVICE, parameter_buffer, 4 ); parameter_buffer[1] = callback->value; parameter_buffer[3] = callback->value; ALsetparams( AL_DEFAULT_DEVICE, parameter_buffer, 4 ); } void scaler_sample(Widget w, caddr_t message, caddr_t para) { XmScaleCallbackStruct *callback = (XmScaleCallbackStruct *) para; sample_param = callback->value; } void scaler_pitch(Widget w, caddr_t message, caddr_t para) { int i, width; XmScaleCallbackStruct *callback = (XmScaleCallbackStruct *) para; pitch_param = callback->value; if( audio_mode == 3){ width = 64 + 4 * pitch_param; for(i=0;i<width;i++) buffer[i] = (short)( 800.0 * ( 1.0 + sin(2.0*pi*(double)i/(double)width + 2.0*pi*3.0/4.0))); for(i=width;i<FIFO_SIZE;i++) buffer[i] = 0; } } void scaler_offset(Widget w, caddr_t message, caddr_t para) { XmScaleCallbackStruct *callback = (XmScaleCallbackStruct *) para; offset_param = callback->value; } void audio_through() { ALconfig config; ALport input_port, output_port; short buff[256]; int result, i = 256; config = ALnewconfig(); ALsetqueuesize( config, 2048 ); input_port = ALopenport( "input", "r", config ); output_port = ALopenport( "output", "w", config ); if ( input_port == NULL || output_port == NULL ) exit(-1); result = prctl(PR_RESIDENT); if(result == -1) fprintf(stderr, "permission denied to be [memory-resident]. (;_;)\n"); result = schedctl(NDPRI, 0, NDPHIMAX); if(result == -1) fprintf(stderr, "permission denied to set process priority. (;_;)\n"); while (1) { ALreadsamps( input_port, buff, i ); ALwritesamps( output_port, buff, i ); } } void audio_distrotion() { ALconfig config; ALport input_port, output_port; short buff[256]; int result, i = 256; config = ALnewconfig(); ALsetqueuesize( config, 2048 ); input_port = ALopenport( "input", "r", config ); output_port = ALopenport( "output", "w", config ); if ( input_port == NULL || output_port == NULL ) exit(-1); result = prctl(PR_RESIDENT); if(result == -1) fprintf(stderr, "permission denied to be [memory-resident]. (;_;)\n"); result = schedctl(NDPRI, 0, NDPHIMAX); if(result == -1) fprintf(stderr, "permission denied to set process priority. (;_;)\n"); while (1) { ALreadsamps( input_port, buff, sample_param ); for(i=0;i<sample_param;i++){ ALwritesamps( output_port, buff, 1 ); } } } void audio_oscillator() { ALconfig config; ALport output_port; short buff[256]; unsigned int read_pointer=0; int i; config = ALnewconfig(); ALsetqueuesize( config, FIFO_SIZE ); output_port = ALopenport( "output", "w", config ); if ( output_port == NULL ) exit(-1); for(i=0;i<256;i++) buffer[i] = (short)( 15000.0 * sin( 2.0 * pi * (double)i / 256.0 )); for(i=256;i<FIFO_SIZE;i++) buffer[i] = 0; while (1) { if( ALgetfillable(output_port) > 256 ){ for(i=0;i<256;i++){ buff[i] = buffer[read_pointer++]; if( read_pointer > sample_param * 256 ) read_pointer=0; } ALwritesamps( output_port, buff, 256 ); } } } void audio_granular() { ALconfig config; ALport output_port; short buff[256]; unsigned int read_pointer[20]; int i, j; config = ALnewconfig(); ALsetqueuesize( config, FIFO_SIZE ); output_port = ALopenport( "output", "w", config ); if ( output_port == NULL ) exit(-1); for(i=0;i<20;i++) read_pointer[i] = 0; for(i=0;i<128;i++) buffer[i] = (short)( 800.0 * ( 1.0 + sin( 2.0 * pi * (double)i / 128.0 + 2.0 * pi * 3.0 / 4.0 ))); for(i=128;i<FIFO_SIZE;i++) buffer[i] = 0; while (1) { if( ALgetfillable(output_port) > 256 ){ for(i=0;i<256;i++){ buff[i] = 0; for(j=0;j<20;j++){ buff[i] = buff[i] + buffer[read_pointer[j]++]; if( read_pointer[j] > ( sample_param + 10 ) * 10 * (j+5) ) read_pointer[j] = 0; } } ALwritesamps( output_port, buff, 256 ); } } } void audio_effector() { ALconfig config; ALport input_port, output_port; short buff[1024]; float shift, weight[1024]; unsigned int read_pointer[20]; int i, j, result; config = ALnewconfig(); ALsetqueuesize( config, 8192 ); input_port = ALopenport( "input", "r", config ); output_port = ALopenport( "output", "w", config ); if ( input_port == NULL || output_port == NULL ) exit(-1); result = prctl(PR_RESIDENT); if(result == -1) fprintf(stderr, "permission denied to be [memory-resident]. (;_;)\n"); result = schedctl(NDPRI, 0, NDPHIMAX); if(result == -1) fprintf(stderr, "permission denied to set process priority. (;_;)\n"); for(i=0;i<20;i++) read_pointer[i] = 0; for(i=0;i<1024;i++) weight[i] = ( 1.0 + sin( 2.0 * pi * (float)i / 1024.0 + 2.0 * pi * 3.0 / 4.0 )) / 2.0; for(i=0;i<FIFO_SIZE;i++) buffer[i] = 0; while (1) { ALreadsamps( input_port, buff, 1024 ); shift = 1024.0 - ( (float)pitch_param - 1.0 ) * 9.8; for(i=0;i<(int)shift;i++){ j = (int)( (float)i * 1024.0 / shift ); buffer[i] = (short)( (float)buff[j] * weight[j] / 40.0 ); } for(i=(int)shift;i<1024;i++) buffer[i] = 0; for(i=0;i<1024;i++){ buff[i] = 0; for(j=0;j<20;j++){ buff[i] = buff[i] + buffer[read_pointer[j]++]; if( read_pointer[j] > 1001 + 20 * sample_param + j * offset_param ) read_pointer[j] = 0; } } ALwritesamps( output_port, buff, 1024 ); } }
リアルタイム信号処理としてのGranular Samplingというのは、全てのパラメータ
が時間的に変化していない場合には単なるイコライザ的な効果でしかないものの、
入力音響とともにリアルタイムにパラメータを変化させると、従来のエフェクタ
では得られない独特の音響が容易に得られることから、現在では世界中の
コンピュータ音楽家が作品において利用を始めている。
5. おわりにGranular SynthesisおよびGranular Synthesisについて紹介し、具体的な実現方法 とパラメータについて検討した。 この手法は、実際の音楽作品に活用できるレベル を目標とした現実的なアプローチで追求することが重要であると考えている。また、本研究に関連した今後の課題としては、
参考文献
|