Fluid waveform layers (#53)

* layers cause race conditions

* front method keeps race conditions from happening

* allow for image color to be base on alpha

* bump

* bump

* more tests

* updated FluidWaveform help file examples
nix
Ted Moore 4 years ago committed by GitHub
parent a918e8f6e4
commit ec17cdc964
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -17,7 +17,7 @@ FluidPlotterPoint {
}
FluidPlotter : FluidViewer {
var <parent, <userView, <xmin, <xmax, <ymin, <ymax, <pointSize = 6, pointSizeScale = 1, dict_internal, <dict, shape = \circle, highlightIdentifiersArray;
var <parent, <userView, <xmin, <xmax, <ymin, <ymax, <pointSize = 6, pointSizeScale = 1, dict_internal, <dict, shape = \circle, highlightIdentifiersArray, categoryColors;
*new {
arg parent, bounds, dict, mouseMoveAction,xmin = 0,xmax = 1,ymin = 0,ymax = 1;
@ -33,7 +33,7 @@ FluidPlotter : FluidViewer {
ymin = ymin_;
ymax = ymax_;
this.createCatColors;
categoryColors = this.createCatColors;
dict_internal = Dictionary.new;
if(dict_.notNil,{this.dict_(dict_)});
this.createPlotWindow(bounds,parent_,mouseMoveAction,dict_);

@ -1,247 +1,432 @@
FluidViewer {
var categoryColors;
createCatColors {
^FluidViewer.createCatColors;
}
*createCatColors {
// colors from: https://github.com/d3/d3-scale-chromatic/blob/main/src/categorical/category10.js
categoryColors = "1f77b4ff7f0e2ca02cd627289467bd8c564be377c27f7f7fbcbd2217becf".clump(6).collect{
^"1f77b4ff7f0e2ca02cd627289467bd8c564be377c27f7f7fbcbd2217becf".clump(6).collect{
arg six;
Color(*six.clump(2).collect{
arg two;
"0x%".format(two).interpret / 255;
});
};
}
}
}
FluidWaveform : FluidViewer {
classvar <lin = 0, <log = 1;
var <win;
FluidWaveformAudioLayer {
var audioBuffer, waveformColor;
*new {
arg audioBuffer, indicesBuffer, featuresBuffer, parent, bounds, lineWidth = 1, waveformColor, stackFeatures = false, imageBuffer, imageColorScheme = 0, imageAlpha = 1, normalizeFeaturesIndependently = true, colorScaling = 1;
^super.new.init(audioBuffer,indicesBuffer, featuresBuffer, parent, bounds, lineWidth, waveformColor,stackFeatures,imageBuffer,imageColorScheme,imageAlpha,normalizeFeaturesIndependently,colorScaling);
}
close {
win.close;
}
loadColorFile {
arg filename;
^CSVFileReader.readInterpret(FluidFilesPath("../Resources/color-schemes/%.csv".format(filename))).collect{
arg row;
Color.fromArray(row);
}
arg audioBuffer, waveformColor;
^super.new.init(audioBuffer,waveformColor);
}
init {
arg audio_buf, slices_buf, feature_buf, parent_, bounds, lineWidth, waveformColor,stackFeatures = false, imageBuffer, imageColorScheme = 0, imageAlpha = 1, normalizeFeaturesIndependently = true, colorScaling = 1;
Task{
var sfv, categoryCounter = 0, xpos, ypos;
arg audioBuffer_, waveformColor_;
waveformColor = waveformColor ? Color(*0.6.dup(3));
this.createCatColors;
if(bounds.isNil && imageBuffer.notNil,{
bounds = Rect(0,0,imageBuffer.numFrames,imageBuffer.numChannels);
});
audioBuffer = audioBuffer_;
waveformColor = waveformColor_ ? Color.gray;
}
bounds = bounds ? Rect(0,0,800,200);
draw {
arg win, bounds;
fork({
var path = "%%_%_FluidWaveform.wav".format(PathName.tmp,Date.localtime.stamp,UniqueID.next);
var sfv;
if(parent_.isNil,{
xpos = 0;
ypos = 0;
win = Window("FluidWaveform",bounds);
win.background_(Color.white);
},{
xpos = bounds.left;
ypos = bounds.top;
win = parent_;
UserView(win,Rect(xpos,ypos,bounds.width,bounds.height))
.drawFunc_{
Pen.fillColor_(Color.white);
Pen.addRect(Rect(0,0,bounds.width,bounds.height));
Pen.fill;
};
});
audioBuffer.write(path,"wav");
if(imageBuffer.notNil,{
var condition = Condition.new;
var colors;
audioBuffer.server.sync;
// TODO: no need for this to be a switch statement.
imageColorScheme.switch(
0,{
colors = this.loadColorFile("CET-L02");
},
1,{
colors = this.loadColorFile("CET-L16");
},
2,{
colors = this.loadColorFile("CET-L08");
},
3,{
colors = this.loadColorFile("CET-L03");
},
4,{
colors = this.loadColorFile("CET-L04");
},
{
"% imageColorScheme: % is not valid.".format(thisMethod,imageColorScheme).warn;
}
);
sfv = SoundFileView(win,bounds);
sfv.peakColor_(waveformColor);
sfv.drawsBoundingLines_(false);
sfv.rmsColor_(Color.clear);
sfv.background_(Color.clear);
sfv.readFile(SoundFile(path));
sfv.gridOn_(false);
imageBuffer.loadToFloatArray(action:{
arg vals;
fork({
var img = Image(imageBuffer.numFrames,imageBuffer.numChannels);
colorScaling.switch(
FluidWaveform.lin,{
var minItem = vals.minItem;
vals = (vals - minItem) / (vals.maxItem - minItem);
vals = (vals * 255).asInteger;
},
FluidWaveform.log,{
vals = (vals + 1e-6).log;
vals = vals.linlin(vals.minItem,vals.maxItem,0.0,255.0).asInteger;
// vals.postln;
},
{
"% colorScaling argument % is invalid.".format(thisMethod,colorScaling).warn;
}
);
vals.do{
arg val, index;
img.setColor(colors[val], index.div(imageBuffer.numChannels), imageBuffer.numChannels - 1 - index.mod(imageBuffer.numChannels));
};
UserView(win,Rect(xpos,ypos,bounds.width,bounds.height))
.drawFunc_{
img.drawInRect(Rect(0,0,bounds.width,bounds.height),fraction:imageAlpha);
};
condition.unhang;
},AppClock)
});
condition.hang;
});
File.delete(path);
},AppClock);
^audioBuffer.server;
}
}
if(audio_buf.notNil,{
var path = "%%_%_FluidWaveform.wav".format(PathName.tmp,Date.localtime.stamp,UniqueID.next);
FluidWaveformIndicesLayer : FluidViewer {
var indicesBuffer, audioBuffer, color, lineWidth;
audio_buf.write(path,"wav");
*new {
arg indicesBuffer, audioBuffer, color, lineWidth = 1;
^super.new.init(indicesBuffer, audioBuffer, color, lineWidth);
}
audio_buf.server.sync;
init {
arg indicesBuffer_, audioBuffer_, color_, lineWidth_;
indicesBuffer = indicesBuffer_;
audioBuffer = audioBuffer_;
color = color_ ? Color.red;
lineWidth = lineWidth_;
}
sfv = SoundFileView(win,Rect(xpos,ypos,bounds.width,bounds.height));
sfv.peakColor_(waveformColor);
// sfv.rmsColor_(Color.black);
sfv.drawsBoundingLines_(false);
sfv.rmsColor_(Color.clear);
// sfv.background_(Color.white);
sfv.background_(Color.clear);
sfv.readFile(SoundFile(path));
sfv.gridOn_(false);
File.delete(path);
});
draw {
arg win, bounds;
if(slices_buf.notNil,{
slices_buf.numChannels.switch(
if(audioBuffer.notNil,{
fork({
indicesBuffer.numChannels.switch(
1,{
slices_buf.loadToFloatArray(action:{
indicesBuffer.loadToFloatArray(action:{
arg slices_fa;
UserView(win,Rect(xpos,ypos,bounds.width,bounds.height))
UserView(win,bounds)
.drawFunc_({
Pen.width_(lineWidth);
slices_fa.do{
arg start_samp;
var x = start_samp.linlin(0,audio_buf.numFrames,0,bounds.width);
var x = start_samp.linlin(0,audioBuffer.numFrames,0,bounds.width);
Pen.line(Point(x,0),Point(x,bounds.height));
Pen.color_(categoryColors[categoryCounter]);
Pen.color_(color);
Pen.stroke;
};
categoryCounter = categoryCounter + 1;
});
});
},
2,{
slices_buf.loadToFloatArray(action:{
indicesBuffer.loadToFloatArray(action:{
arg slices_fa;
slices_fa = slices_fa.clump(2);
UserView(win,Rect(xpos,ypos,bounds.width,bounds.height))
UserView(win,bounds)
.drawFunc_({
Pen.width_(lineWidth);
slices_fa.do{
arg arr;
var start = arr[0].linlin(0,audio_buf.numFrames,0,bounds.width);
var end = arr[1].linlin(0,audio_buf.numFrames,0,bounds.width);
var start = arr[0].linlin(0,audioBuffer.numFrames,0,bounds.width);
var end = arr[1].linlin(0,audioBuffer.numFrames,0,bounds.width);
Pen.addRect(Rect(start,0,end-start,bounds.height));
Pen.color_(categoryColors[categoryCounter].alpha_(0.25));
Pen.color_(color.alpha_(0.25));
Pen.fill;
};
categoryCounter = categoryCounter + 1;
});
});
},{
"FluidWaveform - indices_buf has neither 1 nor 2 channels. Not sure what to do with this.".warn;
Error("% indicesBuffer must have either 1 nor 2 channels.".format(this.class)).throw;
}
);
},AppClock);
^indicesBuffer.server;
},{
Error("% In order to display an indicesBuffer an audioBuffer must be included.".format(this.class)).throw;
});
}
}
FluidWaveformFeaturesLayer : FluidViewer {
var featuresBuffer, colors, stackFeatures, normalizeFeaturesIndependently;
*new {
arg featuresBuffer, colors, stackFeatures = false, normalizeFeaturesIndependently = true;
^super.new.init(featuresBuffer,colors,stackFeatures,normalizeFeaturesIndependently);
}
init {
arg featuresBuffer_, colors_, stackFeatures_ = false, normalizeFeaturesIndependently_ = true;
featuresBuffer = featuresBuffer_;
normalizeFeaturesIndependently = normalizeFeaturesIndependently_;
stackFeatures = stackFeatures_;
colors = colors_ ?? {this.createCatColors};
// we'll index into it to draw, so just in case the user passed just one color, this will ensure it can be "indexed" into
if(colors.isKindOf(SequenceableCollection).not,{colors = [colors]});
}
draw {
arg win, bounds;
featuresBuffer.loadToFloatArray(action:{
arg fa;
var minVal = 0, maxVal = 0;
var stacked_height;
if(stackFeatures,{
stacked_height = bounds.height / featuresBuffer.numChannels;
});
if(feature_buf.notNil,{
var stacked_height = bounds.height / feature_buf.numChannels;
feature_buf.loadToFloatArray(action:{
arg fa;
var minVal = 0, maxVal = 0;
if(normalizeFeaturesIndependently.not,{
minVal = fa.minItem;
maxVal = fa.maxItem;
});
if(normalizeFeaturesIndependently.not,{
minVal = fa.minItem;
maxVal = fa.maxItem;
});
fa = fa.clump(feature_buf.numChannels).flop;
fa = fa.clump(featuresBuffer.numChannels).flop;
fa.do({
arg channel, channel_i;
var maxy;// a lower value;
var miny; // a higher value;
fork({
fa.do({
arg channel, channel_i;
var maxy;// a lower value;
var miny;// a higher value;
if(stackFeatures,{
miny = stacked_height * (channel_i + 1);
maxy = stacked_height * channel_i;
},{
miny = bounds.height;
maxy = 0;
});
if(stackFeatures,{
miny = stacked_height * (channel_i + 1);
maxy = stacked_height * channel_i;
},{
miny = bounds.height;
maxy = 0;
});
if(normalizeFeaturesIndependently,{
minVal = channel.minItem;
maxVal = channel.maxItem;
});
if(normalizeFeaturesIndependently,{
minVal = channel.minItem;
maxVal = channel.maxItem;
});
channel = channel.resamp1(bounds.width).linlin(minVal,maxVal,miny,maxy);
UserView(win,Rect(xpos,ypos,bounds.width,bounds.height))
.drawFunc_({
Pen.moveTo(Point(0,channel[0]));
channel[1..].do{
arg val, i;
Pen.lineTo(Point(i+1,val));
};
Pen.color_(categoryColors[categoryCounter]);
categoryCounter = categoryCounter + 1;
Pen.stroke;
});
channel = channel.resamp1(bounds.width).linlin(minVal,maxVal,miny,maxy);
UserView(win,bounds)
.drawFunc_({
Pen.moveTo(Point(0,channel[0]));
channel[1..].do{
arg val, i;
Pen.lineTo(Point(i+1,val));
};
Pen.color_(colors[channel_i % colors.size]);
Pen.stroke;
});
})
});
},AppClock);
});
^featuresBuffer.server;
}
}
FluidWaveformImageLayer {
var imageBuffer, imageColorScheme, imageColorScaling, imageAlpha;
*new {
arg imageBuffer, imageColorScheme = 0, imageColorScaling = 0, imageAlpha = 1;
^super.new.init(imageBuffer,imageColorScheme,imageColorScaling,imageAlpha);
}
init {
arg imageBuffer_, imageColorScheme_ = 0, imageColorScaling_ = 0, imageAlpha_ = 1;
imageBuffer = imageBuffer_;
imageColorScheme = imageColorScheme_;
imageColorScaling = imageColorScaling_;
imageAlpha = imageAlpha_;
}
draw {
arg win, bounds;
var colors;
if(imageColorScheme.isKindOf(Color),{
// "imageColorScheme is a kind of Color".postln;
colors = 256.collect{
arg i;
Color(imageColorScheme.red,imageColorScheme.green,imageColorScheme.blue,i.linlin(0,255,0.0,1.0));
};
},{
imageColorScheme.switch(
0,{
colors = this.loadColorFile("CET-L02");
},
1,{
colors = this.loadColorFile("CET-L16");
},
2,{
colors = this.loadColorFile("CET-L08");
},
3,{
colors = this.loadColorFile("CET-L03");
},
4,{
colors = this.loadColorFile("CET-L04");
},
{
"% imageColorScheme: % is not valid.".format(thisMethod,imageColorScheme).warn;
}
);
});
imageBuffer.loadToFloatArray(action:{
arg vals;
fork({
var img = Image(imageBuffer.numFrames,imageBuffer.numChannels);
imageColorScaling.switch(
FluidWaveform.lin,{
var minItem = vals.minItem;
vals = (vals - minItem) / (vals.maxItem - minItem);
vals = (vals * 255).asInteger;
},
FluidWaveform.log,{
vals = (vals + 1e-6).log;
vals = vals.linlin(vals.minItem,vals.maxItem,0.0,255.0).asInteger;
// vals.postln;
},
{
"% colorScaling argument % is invalid.".format(thisMethod,imageColorScaling).warn;
}
);
// colors.postln;
vals.do{
arg val, index;
img.setColor(colors[val], index.div(imageBuffer.numChannels), imageBuffer.numChannels - 1 - index.mod(imageBuffer.numChannels));
};
UserView(win,bounds)
.drawFunc_{
img.drawInRect(Rect(0,0,bounds.width,bounds.height),fraction:imageAlpha);
};
},AppClock);
});
^imageBuffer.server;
}
loadColorFile {
arg filename;
^CSVFileReader.readInterpret(FluidFilesPath("../Resources/color-schemes/%.csv".format(filename))).collect{
arg row;
Color.fromArray(row);
}
}
}
FluidWaveform : FluidViewer {
classvar <lin = 0, <log = 1;
var <win, bounds, display_bounds, <layers;
*new {
arg audioBuffer, indicesBuffer, featuresBuffer, parent, bounds, lineWidth = 1, waveformColor, stackFeatures = false, imageBuffer, imageColorScheme = 0, imageAlpha = 1, normalizeFeaturesIndependently = true, imageColorScaling = 0;
^super.new.init(audioBuffer,indicesBuffer, featuresBuffer, parent, bounds, lineWidth, waveformColor,stackFeatures,imageBuffer,imageColorScheme,imageAlpha,normalizeFeaturesIndependently,imageColorScaling);
}
init {
arg audio_buf, slices_buf, feature_buf, parent_, bounds_, lineWidth = 1, waveformColor,stackFeatures = false, imageBuffer, imageColorScheme = 0, imageAlpha = 1, normalizeFeaturesIndependently = true, imageColorScaling = 0;
layers = List.new;
fork({
var plotImmediately = false;
bounds = bounds_;
waveformColor = waveformColor ? Color(*0.6.dup(3));
if(bounds.isNil && imageBuffer.notNil,{
bounds = Rect(0,0,imageBuffer.numFrames,imageBuffer.numChannels);
});
bounds = bounds ? Rect(0,0,800,200);
if(parent_.isNil,{
win = Window("FluidWaveform",bounds);
win.background_(Color.white);
display_bounds = Rect(0,0,bounds.width,bounds.height);
},{
win = parent_;
display_bounds = bounds;
});
if(imageBuffer.notNil,{
this.addImageLayer(imageBuffer,imageColorScheme,imageColorScaling,imageAlpha);
imageBuffer.server.sync;
plotImmediately = true;
});
if(audio_buf.notNil,{
this.addAudioLayer(audio_buf,waveformColor);
audio_buf.server.sync;
plotImmediately = true;
});
if(feature_buf.notNil,{
this.addFeaturesLayer(feature_buf,this.createCatColors,stackFeatures,normalizeFeaturesIndependently);
feature_buf.server.sync;
plotImmediately = true;
});
if(slices_buf.notNil,{
this.addIndicesLayer(slices_buf,audio_buf,Color.red,lineWidth);
slices_buf.server.sync;
plotImmediately = true;
});
if(plotImmediately,{this.front;});
},AppClock);
}
addImageLayer {
arg imageBuffer, imageColorScheme = 0, imageColorScaling = 0, imageAlpha = 1;
var l = FluidWaveformImageLayer(imageBuffer,imageColorScheme,imageColorScaling,imageAlpha);
// l.postln;
layers.add(l);
// layers.postln;
// l.draw(win,display_bounds);
}
addAudioLayer {
arg audioBuffer, waveformColor;
var l = FluidWaveformAudioLayer(audioBuffer,waveformColor);
// l.postln;
layers.add(l);
// layers.postln;
// l.draw(win,display_bounds);
}
addIndicesLayer {
arg indicesBuffer, audioBuffer, color, lineWidth = 1;
var l = FluidWaveformIndicesLayer(indicesBuffer,audioBuffer,color,lineWidth);
// l.postln;
layers.add(l);
// layers.postln;
// l.draw(win,display_bounds);
}
addFeaturesLayer {
arg featuresBuffer, colors, stackFeatures = false, normalizeFeaturesIndependently = true;
var l = FluidWaveformFeaturesLayer(featuresBuffer,colors,stackFeatures,normalizeFeaturesIndependently);
// l.postln;
layers.add(l);
// layers.postln;
// l.draw(win,display_bounds);
}
addLayer {
arg fluidWaveformLayer;
layers.add(fluidWaveformLayer);
}
front {
fork({
UserView(win,display_bounds)
.drawFunc_{
Pen.fillColor_(Color.white);
Pen.addRect(Rect(0,0,bounds.width,bounds.height));
Pen.fill;
};
layers.do{
arg layer;
// layer.postln;
layer.draw(win,display_bounds).sync;
};
win.front;
}.play(AppClock);
},AppClock);
}
close {
win.close;
}
}

@ -215,6 +215,9 @@ d = ~dummy_data.value;
// a different one
~fp.highlight_("example-94");
// highlight a few
~fp.highlight_(["example-1","example-2","example-3"]);
// none
~fp.highlight_(nil);
@ -235,9 +238,6 @@ d = ~dummy_data.value;
// change back to circles
~fp.shape_(\circle);
// change the color of just one point
~fp.pointColor_("example-7",Color.red);
// change the background color
~fp.background_(Color.red)
~fp.background_(Color.white)

@ -17,7 +17,7 @@ The audio buffer to plot.
ARGUMENT:: indicesBuffer
A link::Classes/Buffer:: of slice indices. This will very likely be in the form of a link::Classes/Buffer:: output from a FluCoMa slicer object. If this link::Classes/Buffer:: is only one channel it will plot lines at these slice points. If the link::Classes/Buffer:: is two channels it will consider the 0th channel to contain onsets and the 1st channel to contain offsets. This matches the output of link::Classes/FluidBufAmpGate::.
ARGUMENT:: featureBuffer
ARGUMENT:: featuresBuffer
A link::Classes/Buffer:: containing features to plot over the waveform. If this link::Classes/Buffer:: is multiple channels, it will plot each channel as a separate feature.
ARGUMENT:: parent
@ -39,14 +39,14 @@ ARGUMENT:: imageBuffer
A link::Classes/Buffer:: that will be turned into a raster image and displayed. The buffer's frames will comprise the y axis, the buffer's channels will comprise the x axis (channel 0 at the bottom). Very likely this will come from the output of a Fluid analysis object, such as link::Classes/FluidBufSTFT:: which can be used to plot a spectrogram. Using FluidBufMelBands can be used to plot a Mel-frequency spectrum.
ARGUMENT:: imageColorScheme
An integer indicating which color scheme footnote::The color schemes used are from https://colorcet.com/ Kovesi, Peter. "Good colour maps: How to design them." arXiv preprint arXiv:1509.03700 (2015). https://arxiv.org/abs/1509.03700 :: to use to distinguish differences in the values in strong::imageBuffer::. The default is 0.
table::
## 0 || Grey scale with slightly reduced contrast to avoid display saturation problems
## 1 || Black - Blue - Green - Yellow - White
## 2 || Blue - Magenta - Yellow highly saturated
## 3 || Black - Red - Yellow - White
## 4 || Black - Red - Yellow
::
An integer indicating which color scheme footnote::The color schemes used are from https://colorcet.com/ Kovesi, Peter. "Good colour maps: How to design them." arXiv preprint arXiv:1509.03700 (2015). https://arxiv.org/abs/1509.03700 :: to use to distinguish differences in the values in strong::imageBuffer::. The default is 0. One can also pass a link::Classes/Color:: and differences will be shown using alpha.
table::
## 0 || Grey scale with slightly reduced contrast to avoid display saturation problems
## 1 || Black - Blue - Green - Yellow - White
## 2 || Blue - Magenta - Yellow highly saturated
## 3 || Black - Red - Yellow - White
## 4 || Black - Red - Yellow
::
ARGUMENT:: imageAlpha
An transparency value (0-1) for displaying the waveform. 0 is fully transparent, 1 is fully visible. The default is 1.
@ -54,7 +54,7 @@ An transparency value (0-1) for displaying the waveform. 0 is fully transparent,
ARGUMENT:: normalizeFeaturesIndependently
Boolean. All the features in STRONG::featureBuf:: need to be normalized for plotting. If STRONG::true::, this normalization will happen per feature, so that each will use the full visual range allowed to them. If STRONG::false::, the normalization will happen over all the values in the STRONG::featureBuf:: (in all the channels), so that the features relative strengths will be preserved. The default is STRONG::true::.
ARGUMENT:: colorScaling
ARGUMENT:: imageColorScaling
An integer indicating how to scale the values in strong::imageBuffer:: before applying the strong::imageColorScheme::. 0 indicates linear scaling, 1 indicates logarithmic scaling. The default is 1. These integers can also be accessed via FluidWaveform.lin and FluidWaveform.log.
returns:: A new instance of FluidWaveform.
@ -70,8 +70,62 @@ returns:: 1
INSTANCEMETHODS::
METHOD:: addAudioLayer
Add a graphic layer that shows a waveform.
ARGUMENT:: audioBuffer
See this argument in the class method 'new' above.
ARGUMENT:: waveformColor
See this argument in the class method 'new' above. The default is gray.
METHOD:: addIndicesLayer
Add a grapic layer that shows indices.
ARGUMENT:: indicesBuffer
See this argument in the class method 'new' above.
ARGUMENT:: audioBuffer
The audioBuffer that the indices refer to.
ARGUMENT:: color
The link::Classes/Color:: to display the indices. The default is red.
ARGUMENT:: lineWidth
How wide to draw the indices lines. The default is 1.
METHOD:: addFeaturesLayer
Add a grapic layer that shows feature curves.
ARGUMENT:: featuresBuffer
See this argument in the class method 'new' above.
ARGUMENT:: colors
An array of link::Classes/Color:: objects to use to display the different features. If there are more features than colors, FluidWaveform will modulo around the colors. If this is left blank, default colors will be used.
ARGUMENT:: stackFeatures
See this argument in the class method 'new' above.
ARGUMENT:: normalizeFeaturesIndependently
See this argument in the class method 'new' above.
METHOD:: addImageLayer
Add a grapic layer that shows an image derived from a buffer.
ARGUMENT:: imageBuffer
See this argument in the class method 'new' above.
ARGUMENT:: imageColorScheme
See this argument in the class method 'new' above.
ARGUMENT:: imageColorScaling
See this argument in the class method 'new' above.
ARGUMENT:: imageAlpha
See this argument in the class method 'new' above.
METHOD:: front
Similar to link::Classes/Window::'s strong::front:: method. Shows the FluidWaveform. This must be called after layers have been added in order to see the layers.
METHOD:: close
Close the FluidWaveform window. If parent is not STRONG::nil::, this method will close the parent window.
@ -103,15 +157,15 @@ w.front;
// show spectrogram
~mags = Buffer(s);
FluidBufSTFT.processBlocking(s,~drums,magnitude:~mags,action:{"stft done".postln;});
FluidWaveform(bounds:Rect(0,0,1200,300),imageBuffer:~mags,imageColorScheme:1);
FluidWaveform(bounds:Rect(0,0,1200,300),imageBuffer:~mags,imageColorScheme:1,imageColorScaling:1);
// show mels
~mels = Buffer(s);
FluidBufMelBands.processBlocking(s,~drums,features:~mels,numBands:200,windowSize:2048,action:{"done".postln});
FluidWaveform(bounds:Rect(0,0,1600,400),imageBuffer:~mels,imageColorScheme:1);
FluidBufMelBands.processBlocking(s,~drums,features:~mels,numBands:400,windowSize:4096,hopSize:256,action:{"done".postln});
FluidWaveform(bounds:Rect(0,0,1600,400),imageBuffer:~mels,imageColorScheme:1,imageColorScaling:1);
// spectrogram with some nice colors and a bit of styling...
FluidWaveform(~drums,bounds:Rect(0,0,1200,300),imageBuffer:~mags,imageColorScheme:1,waveformColor:Color.magenta(1,0.5));
FluidWaveform(~drums,bounds:Rect(0,0,1200,300),imageBuffer:~mags,imageColorScheme:1,waveformColor:Color.magenta(1,0.5),imageColorScaling:1);
// create a buffer to put indices into
~indices = Buffer(s);
@ -147,16 +201,16 @@ FluidWaveform(~drums,~gate_analysis,~features2,bounds:Rect(0,0,1200,300));
FluidBufPitch.processBlocking(s,~noisy,features:~pitch_analysis,action:{"done".postln;});
// plot not stacked:
FluidWaveform(~noisy,featureBuffer:~pitch_analysis,bounds:Rect(0,0,1200,300));
FluidWaveform(~noisy,featuresBuffer:~pitch_analysis,bounds:Rect(0,0,1200,300));
// plot stacked:
FluidWaveform(~noisy,featureBuffer:~pitch_analysis,bounds:Rect(0,0,1200,300),stackFeatures:true,waveformColor:Color(*0.9.dup(3)));
FluidWaveform(~noisy,featuresBuffer:~pitch_analysis,bounds:Rect(0,0,1200,300),stackFeatures:true,waveformColor:Color(*0.9.dup(3)));
~mags = Buffer(s);
FluidBufSTFT.processBlocking(s,~noisy,magnitude:~mags,action:{"done".postln;});
// add spectrogram:
FluidWaveform(~noisy,featureBuffer:~pitch_analysis,imageBuffer:~mags,bounds:Rect(0,0,1200,300),stackFeatures:true,waveformColor:Color(0,0,0,0.5),imageAlpha:0.5);
FluidWaveform(~noisy,featuresBuffer:~pitch_analysis,imageBuffer:~mags,bounds:Rect(0,0,1200,300),stackFeatures:true,waveformColor:Color(0,0,0,0.5),imageAlpha:0.5,imageColorScaling:1);
// plot in another window with all the things!
@ -164,7 +218,7 @@ FluidWaveform(~noisy,featureBuffer:~pitch_analysis,imageBuffer:~mags,bounds:Rect
w = Window("FluidWaveform Test",Rect(0,0,1000,500));
FluidWaveform(
~noisy,
featureBuffer:~pitch_analysis,
featuresBuffer:~pitch_analysis,
parent:w,
bounds:Rect(100,100,800,300),
stackFeatures:true,
@ -174,4 +228,77 @@ FluidWaveform(
);
w.front;
)
::
Adding Layers One at a Time
code::
(
// do some analysis
~audio = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav"));
~mels = Buffer(s);
FluidBufMelBands.processBlocking(s,~audio,features:~mels);
~indices = Buffer(s);
FluidBufOnsetSlice.processBlocking(s,~audio,metric:9,indices:~indices);
~pitch_analysis = Buffer(s);
FluidBufPitch.processBlocking(s,~audio,features:~pitch_analysis,action:{"done".postln;});
)
(
// plot it all
~fw = FluidWaveform(bounds:Rect(100,100,1200,300)); // if no buffers of any kind are passed, then you'll need to call `.front` after adding layers
~fw.addImageLayer(~mels,2,imageColorScaling:1);
~fw.addAudioLayer(~audio,Color(1,1,1,0.5));
~fw.addIndicesLayer(~indices,~audio,Color.black);
~fw.addFeaturesLayer(~pitch_analysis,[Color.cyan,Color.magenta]);
~fw.front; // // if no buffers of any kind are passed, then you'll need to call `.front` after adding layers
)
(
// plot most of it, but then...
~fw = FluidWaveform(bounds:Rect(100,100,1200,300));
~fw.addImageLayer(~mels,2,imageColorScaling:1);
~fw.addAudioLayer(~audio,Color(1,1,1,0.5));
~fw.addIndicesLayer(~indices,~audio,Color.black);
~fw.front;
)
// add one more
~fw.addFeaturesLayer(~pitch_analysis,[Color.cyan,Color.yellow]).front; // <<<----- notice the `.front` here to update display after adding this layer
// check how many layers
~fw.layers.size
(
// plot differen components with different colors
s.waitForBoot{
~audio = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav"));
~resynth = Buffer(s);
~n_components = 3;
~resynths = {Buffer(s)} ! ~n_components;
s.sync;
FluidBufNMF.processBlocking(s,~audio,resynth:~resynth,activations:~activations,components:~n_components);
~n_components.do{
arg i;
FluidBufCompose.processBlocking(s,~resynth,startChan:i,numChans:1,destination:~resynths[i]);
};
~colors = FluidViewer.createCatColors.collect{
arg color;
color.alpha_(0.4);
color;
};
s.sync;
~fw = FluidWaveform(bounds:Rect(0,0,1600,400));
~n_components.do{
arg i;
~fw.addAudioLayer(~resynths[i],~colors[i]);
};
~fw.front;
}
)
::

@ -1,4 +1,5 @@
(
// og test
s.waitForBoot{
Task{
@ -6,10 +7,248 @@ s.waitForBoot{
var slicepoints = Buffer(s);
FluidBufAmpSlice.process(s,buf,indices:slicepoints,fastRampUp:10,fastRampDown:2205,slowRampUp:4410,slowRampDown:4410,onThreshold:10,offThreshold:5,floor:-40,minSliceLength:4410,highPassFreq:20,action:{
FluidWaveform(buf,slicepoints,bounds:Rect(0,0,1600,400));
FluidWaveform(buf,slicepoints,bounds:Rect(0,0,1600,400)).front;
});
}.play(AppClock);
}
)
(
(
// embed it in a different window
s.waitForBoot{
var buf = Buffer.read(s,"/Users/macprocomputer/Desktop/_flucoma/code/flucoma-core-src/AudioFiles/Nicol-LoopE-M.wav");
var slicepoints = Buffer(s);
FluidBufAmpSlice.process(s,buf,indices:slicepoints,fastRampUp:10,fastRampDown:2205,slowRampUp:4410,slowRampDown:4410,onThreshold:10,offThreshold:5,floor:-40,minSliceLength:4410,highPassFreq:20,action:{
fork({
~win = Window("test",Rect(0,0,1600,400));
FluidWaveform(buf,slicepoints,bounds:Rect(100,100,1400,200),parent:~win).front;
},AppClock);
});
}
)
(
// lots of defaults
s.waitForBoot{
Window.closeAll;
~audio = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav"));
s.sync;
~mels = Buffer(s);
FluidBufMelBands.processBlocking(s,~audio,features:~mels);
~indices = Buffer(s);
FluidBufOnsetSlice.processBlocking(s,~audio,metric:9,indices:~indices);
~pitch_analysis = Buffer(s);
FluidBufPitch.processBlocking(s,~audio,features:~pitch_analysis);
s.sync;
~fw = FluidWaveform(bounds:Rect(100,100,1200,300));
~fw.addImageLayer(~mels,1,imageColorScaling:1);
~fw.addAudioLayer(~audio);
~fw.addIndicesLayer(~indices,~audio);
~fw.addFeaturesLayer(~pitch_analysis);
~fw.front;
}
)
(
// some more arguments
s.waitForBoot{
Window.closeAll;
~audio = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav"));
s.sync;
~mels = Buffer(s);
FluidBufMelBands.processBlocking(s,~audio,features:~mels);
~indices = Buffer(s);
FluidBufOnsetSlice.processBlocking(s,~audio,metric:9,indices:~indices);
~pitch_analysis = Buffer(s);
FluidBufPitch.processBlocking(s,~audio,features:~pitch_analysis);
~fw = FluidWaveform(bounds:Rect(100,100,1200,300));
~fw.addImageLayer(~mels,1,1);
~fw.addAudioLayer(~audio,Color.red);
~fw.addIndicesLayer(~indices,~audio,Color.yellow);
~fw.addFeaturesLayer(~pitch_analysis,Color.cyan,true);
~fw.front;
}
)
(
// another og test
s.waitForBoot{
Window.closeAll;
~audio = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav"));
s.sync;
~mels = Buffer(s);
FluidBufMelBands.processBlocking(s,~audio,features:~mels);
~indices = Buffer(s);
FluidBufOnsetSlice.processBlocking(s,~audio,metric:9,indices:~indices);
~pitch_analysis = Buffer(s);
FluidBufPitch.processBlocking(s,~audio,features:~pitch_analysis);
s.sync;
~fw = FluidWaveform(~audio,~indices,~pitch_analysis,imageBuffer:~mels,bounds:Rect(100,100,1200,300),imageColorScaling:1,imageColorScheme:1).front;
}
)
(
// another og test with gate info
s.waitForBoot{
Window.closeAll;
~audio = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav"));
s.sync;
~indices = Buffer(s);
FluidBufAmpGate.processBlocking(s,~audio,indices:~indices,onThreshold:-20,offThreshold:-30,minSliceLength:4410,minSilenceLength:441);
~pitch_analysis = Buffer(s);
FluidBufPitch.processBlocking(s,~audio,features:~pitch_analysis);
s.sync;
~fw = FluidWaveform(~audio,~indices,~pitch_analysis,bounds:Rect(100,100,1200,300),imageColorScaling:1,imageColorScheme:1).front;
}
)
(
s.waitForBoot{
// do some analysis
~audio = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav"));
~mels = Buffer(s);
FluidBufMelBands.processBlocking(s,~audio,features:~mels);
~indices = Buffer(s);
FluidBufOnsetSlice.processBlocking(s,~audio,metric:9,indices:~indices);
~pitch_analysis = Buffer(s);
FluidBufPitch.processBlocking(s,~audio,features:~pitch_analysis);
s.sync;
"analysis done".postln;
}
)
(
// plot it all
~fw = FluidWaveform(bounds:Rect(100,100,1200,300));
~fw.addImageLayer(~mels,2,imageColorScaling:1);
~fw.addAudioLayer(~audio,Color(1,1,1,0.5));
~fw.addIndicesLayer(~indices,~audio,Color.black);
~fw.addFeaturesLayer(~pitch_analysis,[Color.cyan,Color.magenta]);
~fw.front;
)
(
// plot most of it, but then...
~fw = FluidWaveform(bounds:Rect(100,100,1200,300));
~fw.addImageLayer(~mels,2,imageColorScaling:1);
~fw.addAudioLayer(~audio,Color(1,1,1,0.5));
~fw.addIndicesLayer(~indices,~audio,Color.black);
~fw.front;
)
// add one more
~fw.addFeaturesLayer(~pitch_analysis,[Color.cyan,Color.yellow]).front;
// check how many layers
~fw.layers
(
// plot differen components with different colors
s.waitForBoot{
~audio = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav"));
// ~audio = Buffer.read(s,FluidFilesPath("Tremblay-ASWINE-ScratchySynth-M.wav"));
~resynth = Buffer(s);
~n_components = 3;
~resynths = {Buffer(s)} ! ~n_components;
~mels = {Buffer(s)} ! ~n_components;
~activations = Buffer(s);
~harm = {Buffer(s)} ! 2;
~perc = {Buffer(s)} ! 2;
~percmels = {Buffer(s)} ! 2;
~harmmels = {Buffer(s)} ! 2;
~numBands = 400;
~windowSize = 4096;
~hopSize = 256;
s.sync;
FluidBufNMF.processBlocking(s,~audio,resynth:~resynth,activations:~activations,components:~n_components);
2.do{
arg i;
FluidBufHPSS.processBlocking(s,~audio,harmonic:~harm[i],percussive:~perc[i],maskingMode:i,harmFilterSize:51,percFilterSize:91);
};
~n_components.do{
arg i;
FluidBufCompose.processBlocking(s,~resynth,startChan:i,numChans:1,destination:~resynths[i]);
FluidBufMelBands.processBlocking(s,~resynths[i],features:~mels[i],numBands:~numBands,windowSize:~windowSize,hopSize:~hopSize);
};
~harm.do{
arg buf, i;
FluidBufMelBands.processBlocking(s,buf,features:~harmmels[i],numBands:~numBands,windowSize:~windowSize,hopSize:~hopSize);
};
~perc.do{
arg buf, i;
FluidBufMelBands.processBlocking(s,buf,features:~percmels[i],numBands:~numBands,windowSize:~windowSize,hopSize:~hopSize);
};
~colors = FluidViewer.createCatColors.collect{
arg color;
// color.alpha_(0.4);
color;
};
s.sync;
// ~resynths.postln;
~melsfw = FluidWaveform(bounds:Rect(0,0,1600,400));
// ~n_components.do{
// arg i;
// ~fw.addAudioLayer(~resynths[i],~colors[i]);
// };
~n_components.do{
arg i;
~melsfw.addImageLayer(~mels[i],~colors[i],1,1);
};
~melsfw.front;
~binaryfw = FluidWaveform(bounds:Rect(0,400,1600,400));
~binaryfw.addImageLayer(~percmels[1],~colors[0],1);
~binaryfw.addImageLayer(~harmmels[1],~colors[1],1);
~binaryfw.front;
~softfw = FluidWaveform(bounds:Rect(0,0,1600,400));
~softfw.addImageLayer(~percmels[0],~colors[0],1);
~softfw.addImageLayer(~harmmels[0],~colors[1],1);
~softfw.front;
}
)

Loading…
Cancel
Save