Mixerの波形更新メソッド
コメントにも書きましたが、各発振器の出力波形が先に更新されている事が前提となっています。
各発振器の出力波形に含まれるエレメントの数が一致する事をチェックしていますが、これをやらないとどのような不具合が発生するかについては【失敗例】誰が出しているエラーメッセージなの?に書きましたので、そちらを参照して下さい。
//
// Mixer.m
//
#pragma mark -
#pragma mark Update path
// OscillatorのbezierPathが先に更新されている事
- (void)updateBezierPath
{
int i;
NSPoint p1,p2,p3;
NSBezierPath *path = [NSBezierPath bezierPath];
NSBezierPath *waveform1 = [[self osc1] bezierPath];
NSBezierPath *waveform2 = [[self osc2] bezierPath];
NSBezierPath *waveform3 = [[self osc3] bezierPath];
if([waveform1 elementCount] == [waveform2 elementCount] &&
[waveform2 elementCount] == [waveform3 elementCount])
{
for(i=0;i<[[[self masterMotif] resolution] intValue];i++)
{
[waveform1 elementAtIndex:i associatedPoints:&p1];
[waveform2 elementAtIndex:i associatedPoints:&p2];
[waveform3 elementAtIndex:i associatedPoints:&p3];
if(i == 0)
[path moveToPoint:NSMakePoint(i,p1.y+p2.y+p3.y)];
else
[path lineToPoint:NSMakePoint(i,p1.y+p2.y+p3.y)];
}
[self setBezierPath:path];
}
}
発振器を更新して合成波形を更新する
上記のメソッドは各発振器の出力波形が先に更新されている事が前提ですが、各発振器を更新して上記のメソッドを呼びだす事も必要になるので、そのためのメソッドfullUpdateを用意しました。
//
// Mixer.m
//
- (void)fullUpdate
{
[[self mutableSetValueForKey:@"oscillators"] makeObjectsPerformSelector:@selector(updateBezierPath)];
[self updateBezierPath];
}
MasterMotifの波形更新メソッド
波形を更新するメソッドupdateBezierPathをMasterMotifに追加します。こちらはLissajousでやっていた事とほとんど変わりませんので、説明は省略します。
ミキサーの波形を更新してから自身の波形を更新するメソッドfullUpdateはMasterMotifにも必要となりますので、追加します。
//
// MasterMotifCD.m
//
#pragma mark -
#pragma mark Update path
// MixerのbezierPathが先に更新されている事
- (void)updateBezierPath
{
int i;
NSPoint px,py;
NSBezierPath *path = [NSBezierPath bezierPath];
NSBezierPath *waveformX = [[self x] bezierPath];
NSBezierPath *waveformY = [[self y] bezierPath];
if([waveformX elementCount] != [waveformY elementCount]) return;
for(i=0;i<[[self resolution] intValue];i++)
{
[waveformX elementAtIndex:i associatedPoints:&px];
[waveformY elementAtIndex:i associatedPoints:&py];
if(i == 0)
[path moveToPoint:NSMakePoint(px.y,py.y)];
else
[path lineToPoint:NSMakePoint(px.y,py.y)];
}
[path closePath];
[self setBezierPath:path];
}
- (void)fullUpdate
{
[[self mutableSetValueForKey:@"mixers"] makeObjectsPerformSelector:@selector(fullUpdate)];
[self updateBezierPath];
}
波形の更新が必要になる時
波形の更新が必要になるのはresolutionが変更された時と、各発振器の出力波形が変更された時です。前者はresolutionのSetterで、後者は発振器のSetterで波形の更新すればOKです。
まずresolutionのSetterを修正します。resolutionに値をセットしてからfullUpdateを実行します。
//
// MasterMotifCD.m
//
- (void)setResolution:(NSNumber *)value
{
[self setValue:value forKey:@"resolution" action:@"setResolution"];
[self fullUpdate];
}
OscillatorのupdateBezierPathを修正する
初期化コードからupdateBezierPathを実行した場合、初期化コードが呼びだされる順番が関係する為か、resolutionを取得できない場合がありました。そのような場合は波形の更新は行ないません。
OscillatorCDのSetterにupdateBezierPathメッセージを送るコードを追加します。まず自分自身に送り、次にMixerに送り、最後にMasterMotifに送ります。この順番を守る必要があります。
//
// OscillatorCD.m
// RepeatingMotifGenerator
//
- (void)updateBezierPath
{
int i,resolution;
double output,normalizedPhase;
NSBezierPath *path;
MasterMotifCD *masterMotif = [[self mixer] masterMotif];
if(masterMotif == nil) return;
path = [NSBezierPath bezierPath];
resolution = [[masterMotif resolution] intValue];
for(i=0;i<resolution;i++)
{
normalizedPhase = i/(double)resolution;
output = [self output:normalizedPhase];
if(i == 0)
[path moveToPoint:NSMakePoint(i,output)];
else
[path lineToPoint:NSMakePoint(i,output)];
}
[self setBezierPath:path];
}
- (void)setAmplitude:(NSNumber *)value
{
[self setValue:value forKey:@"amplitude" action:@"setAmplitude"];
[self updateBezierPath];
[[self mixer] updateBezierPath];
[[[self mixer] masterMotif] updateBezierPath];
}
- (void)setFrequency:(NSNumber *)value
{
[self setValue:value forKey:@"frequency" action:@"setFrequency"];
[self updateBezierPath];
[[self mixer] updateBezierPath];
[[[self mixer] masterMotif] updateBezierPath];
}
- (void)setPhaseLag:(NSNumber *)value
{
[self setValue:value forKey:@"phaseLag" action:@"setPhaseLag"];
[self updateBezierPath];
[[self mixer] updateBezierPath];
[[[self mixer] masterMotif] updateBezierPath];
}
【失敗例】パラメータが1だけずれる事がある?
テスト中に、90と入力した筈のパラメータが保存後に開いてみると89になっている、という事がありました。整数を扱っているつもりでいたのに、実際には実数で処理されていた、という事が原因です。
amplitude, frequency, phaseLagはOscillatorエンティティの属性で、タイプはInt 16です。整数型に指定したのだから、このパラメータは整数として扱ってよいと思っていました。しかしこれらの実体はNSNumberというオブジェクトです。整数を取り出す事もできますが、実数を取り出す事もできるので、内部的には実数で処理されているものと思われます。
さて、これらのパラメータを設定するためにNSSliderを使いました。数値表示はNSNumberFormatterがセットされたラベルで行ないます。NSNumberFormatterで整数化されるので見過ごしてしまいましたが、NSSliderが送ってくる数値は整数ではなく実数です。
- スライダーにTick Marksを設定して"Only stop on tick marks"にチェックを入れ、Tick Marksのところの値が整数になる様にしておけば、整数値になります。小数部分がゼロの実数といった方が正確かもしれません。最初はfrequencyのスライダーだけそのような設定にしており、frequencyは数値が1だけずれる現象が発生しませんでした。
詳しくは調べていませんが、90を入力したつもりでも実際には89.?が入力されている事が1だけずれる原因の様です。なぜ入力時と保存後に開いた時で異なる数値として表示されるのかはわかりませんが、そこは追求しませんでした。そもそも90を入力したつもりでいるのに、実際には89.?が入力されている事自体が問題です。
そこでSetterを変更し、強制的に整数化した値をセットする様に変更しました。合わせてNSNumberFormatterを外しました。これで見た目と実際の値が一致する様になり、1だけずれる現象がなくなりました。
//
// MasterMotifCD.m
//
- (void)setResolution:(NSNumber *)value
{
[self setValue:[NSNumber numberWithInt:[value intValue]] forKey:@"resolution" action:@"setResolution"];
[self fullUpdate];
}
//
// OscillatorCD.m
//
- (void)setAmplitude:(NSNumber *)value
{
[self setValue:[NSNumber numberWithInt:[value intValue]] forKey:@"amplitude" action:@"setAmplitude"];
[self updateBezierPath];
[[self mixer] updateBezierPath];
[[[self mixer] masterMotif] updateBezierPath];
}
- (void)setFrequency:(NSNumber *)value
{
[self setValue:[NSNumber numberWithInt:[value intValue]] forKey:@"frequency" action:@"setFrequency"];
[self updateBezierPath];
[[self mixer] updateBezierPath];
[[[self mixer] masterMotif] updateBezierPath];
}
- (void)setPhaseLag:(NSNumber *)value
{
[self setValue:[NSNumber numberWithInt:[value intValue]] forKey:@"phaseLag" action:@"setPhaseLag"];
[self updateBezierPath];
[[self mixer] updateBezierPath];
[[[self mixer] masterMotif] updateBezierPath];
}

ホーム
前へ