Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 13 additions & 16 deletions doc/BufFlatten.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,59 +2,56 @@
:species: buffer-proc
:sc-categories: FluidCorpusManipulation
:sc-related: Classes/Buffer
:see-also:
:see-also: BufCompose, BufStats
:description:
Flatten a multichannel |buffer| to a single channel. This can be useful for constructing n-dimensional data points for use with :fluid-obj:`DataSet`
Flatten a multichannel |buffer| to a single channel. This can be useful to structure a buffer such that it can be added to a :fluid-obj:`DataSet`

The ``axis`` determines how the flattening is arranged. The default value, 1, flattens channel-wise, such that (if we imagine channels are rows, time positions are columns):
The ``axis`` argument determines how the flattening is arranged. The default value, 1, flattens channel-wise (similar to how audio files are stored), such that (if we imagine channels are rows, time positions are columns):

=== === ===
a 1 a 2 a 3
b 1 b 2 b 3
c 1 c 2 c 3
=== === ===


becomes

=== === === === === === === === ===
a 1 b 1 c 1 a 2 b 2 c 2 a 3 b 3 c 3
=== === === === === === === === ===


whereas with ``axis = 0`` we get
whereas with ``axis`` = 0, the buffer is flattened frame-wise, resulting in:

=== === === === === === === === ===
a 1 a 2 a 3 b 1 b 2 b 3 c 1 c 2 c 3
=== === === === === === === === ===


To learn more visit https://learn.flucoma.org/reference/bufflatten/.

:control source:

The |buffer| to flatten
The |buffer| to flatten.

:control startFrame:

Where in the source should the flattening process start, in samples.
Where in ``source`` should the flattening process start, in samples. The default is 0.

:control numFrames:

How many frames should be processed.
How many frames should be processed. The default of -1 indicates to process through the end of the buffer.

:control startChan:

For multichannel source buffers, which channel to start processing at.
For multichannel ``source`` buffers, which which channel to begin the processing. The default is 0.

:control numChans:

For multichannel source buffers, how many channels should be processed.
For multichannel ``source`` buffers, how many channels should be processed. The default of -1 indicates to process up through the last channel in ``source``.

:control destination:

The |buffer| to write the flattened data to
The |buffer| to write the flattened data to.

:control axis:

Whether to group points channel-wise or frame-wise

Whether to flatten the buffer channel-wise (1) or frame-wise (0). The default is 1 (channel-wise).
80 changes: 69 additions & 11 deletions example-code/sc/BufFlatten.scd
Original file line number Diff line number Diff line change
@@ -1,22 +1,74 @@

strong::Using FluidBufFlatten for adding points to a FluidDataSet::
code::
//FluidBufPitch is useful to illustrate the effect of this, because the pitch and confidence values are easily distinguishable

(
~path = FluidFilesPath();
~randomsoundfile = SoundFile.collect(~path +/+ '*').choose;
b = Buffer.read(s,~randomsoundfile.path,action:{"Sound Loaded".postln});
~pitchdata = Buffer.new;
~flatdata = Buffer.new;
~buf = Buffer.readChannel(s,FluidFilesPath("Tremblay-CEL-GlitchyMusicBoxMelo.wav"),channels:[0]);
~chromabuf = Buffer(s); // buffer for storing chroma analysis
~statsbuf = Buffer(s); // buffer for storing statistical analysis of chroma analysis
~flatbuf = Buffer(s); // buffer for storing the flattened data of the mean chroma values
~chroma_ds = FluidDataSet(s); // dataset for writing points into
)

(
fork{
~n_analysis_frames = ~buf.sampleRate * 0.1; // analyze in 0.1 second windows
~current_analysis_frame = 0; // start at 0
i = 0;
while({
// while the next frame that we would analyze doesn't go past the end of the buffer
(~current_analysis_frame + ~n_analysis_frames) < ~buf.numFrames;
},{
// do the chroma analysis on this section of the buffer. this will return a buffer of 12 channels (one for each chroma)
// with as many frames as there are FFT windows in the 0.1 second slice we're analysing
FluidBufChroma.processBlocking(s,~buf,~current_analysis_frame,~n_analysis_frames,features:~chromabuf);

// next, perform statistical analysis on each channel in chromabuf. this will return a buffer with however many
// channels are in chromabuf (12) and 7 frames, one for each statistical analysis
FluidBufStats.processBlocking(s,~chromabuf,stats:~statsbuf);

// finally, since we want to have the mean value for each of the chroma we can ask FluidBufFlatten to just
// process the 0th frame (that has the mean values) of all the channels (so just the 0th column) into a flat
// buffer so it can be used in FluidDataSet.addPoint
FluidBufFlatten.processBlocking(s,~statsbuf,numFrames:1,destination:~flatbuf);
~chroma_ds.addPoint("point-%".format(i),~flatbuf);

~current_analysis_frame = ~current_analysis_frame + ~n_analysis_frames;
i = i + 1;
});
s.sync;
" \t\tnumFrames\tnumChannels".postln;
"shape of chromabuf:\t\t%\t\t\t%".format(~chromabuf.numFrames,~chromabuf.numChannels).postln;
"shape of statsbuf: \t\t%\t\t\t%".format(~statsbuf.numFrames,~statsbuf.numChannels).postln;
"shape of flatbuf: \t\t%\t\t\t%\n".format(~flatbuf.numFrames,~flatbuf.numChannels).postln;
~chroma_ds.print;
}
)
::
strong::FluidBufPitch is useful to illustrate the effect of this, because the pitch and confidence values are easily distinguishable::
code::
(
~buf = Buffer.readChannel(s,FluidFilesPath("Tremblay-CEL-GlitchyMusicBoxMelo.wav"),channels:[0]);
~pitchdata = Buffer(s);
~flatdata = Buffer(s);
)

//Pitch analysis, writes pitches as frequencies to chan 0, confidences [0-1] to chan 1
FluidBufPitch.process(s,b,numFrames:512 * 10,numChans:1,features:~pitchdata,action:{"Pitch Analysis Done".postln});
// we'll only analyze the first 10 frames here so we can visually inspect the data more easily
FluidBufPitch.processBlocking(s,~buf,numFrames:512 * 9,numChans:1,features:~pitchdata,action:{"Pitch Analysis Done".postln});

//plot the original buffer to get a sense of what is in it:
(
"numFrames: %".format(~pitchdata.numFrames).postln;
"numChannels: %".format(~pitchdata.numChannels).postln;
~pitchdata.plot(separately:true);
)

// Flatten and print the flat buffer. We expect to see larger numbers (20-2000) interleaved with smaller (0-1)
(
FluidBufFlatten.process(s,~pitchdata, destination: ~flatdata, axis:1, action:{
~flatdata.loadToFloatArray(action:{ |a|
"numFrames: %".format(~flatdata.numFrames).postln;
"numChannels: %".format(~flatdata.numChannels).postln;
a.postln;
})
})
Expand All @@ -26,27 +78,33 @@ FluidBufFlatten.process(s,~pitchdata, destination: ~flatdata, axis:1, action:{
(
FluidBufFlatten.process(s,~pitchdata, destination:~flatdata, axis:0, action:{
~flatdata.loadToFloatArray(action:{ |a|
"numFrames: %".format(~flatdata.numFrames).postln;
"numChannels: %".format(~flatdata.numChannels).postln;
a.postln;
})
})
)

//adding the source range make this processor very powerful, but can be quite confusing
//adding the source range (startFrame and numFrames) make this processor very powerful
//here we take only one frame starting at the second one (0-counting)
(
FluidBufFlatten.process(s,~pitchdata,startFrame: 1,numFrames: 1, destination:~flatdata, action:{
~flatdata.loadToFloatArray(action:{ |a|
"numFrames: %".format(~flatdata.numFrames).postln;
"numChannels: %".format(~flatdata.numChannels).postln;
a.postln;
})
})
)

//and here we take only the confidences
(
FluidBufFlatten.process(s,~pitchdata, startChan: 1, destination:~flatdata, action:{
~flatdata.loadToFloatArray(action:{ |a|
"numFrames: %".format(~flatdata.numFrames).postln;
"numChannels: %".format(~flatdata.numChannels).postln;
a.postln;
})
})
)

::
::