The original was: complexarray = ((((Complex( Signal.newFrom( magarray[0] ), Signal.newFrom( magarray[1] ) ).magnitude.reverse)).log10) * 80).clip(0, 255);
This resulted in magnitude values below 1 producing negative values after log10, which were then clipped away.
this clipped the higher frequencies away at fft buffer size 2048 or more, but the same was for the original algorithm
complexarray = ( (Complex(Signal.newFrom(magarray[0]), Signal.newFrom(magarray[1])).magnitude.reverse + 1).log10
- 80
).clip(0, 255);
complexarray = ((((Complex( Signal.newFrom( magarray[0] ), Signal.newFrom( magarray[1] ) ).magnitude.reverse) + 1).log10) * 80).clip(0, 255);complexarray = Complex( Signal.newFrom( magarray[0] ), Signal.newFrom( magarray[1] ) ).magnitude.reverse + 1; // prevent items < 0 producing negative values at log10
[“min, max before log10”, complexarray.minItem, complexarray.maxItem].postln; complexarray = complexarray.log10; [“min, max after log10”, complexarray.minItem, complexarray.maxItem].postln; complexarray = complexarray * 160; [“max after * 160”, complexarray.maxItem].postln;
complexarray = complexarray.clip(0, 255); [“max after clip”, complexarray.maxItem].postln;
/* complexarray = (80 * Complex( Signal.newFrom( magarray[0] ), Signal.newFrom( magarray[1] ) ).magnitude.reverse.log10).clip(0, 255); */
use: classvar <>defaultFFTBufSize = 1024, <>colorSize = 64;
recalcGradient { var colors;
// colors = (0..16).collect({ | val | blend(background, color, val / 16)}); // THIS: colors = (0..colorSize).collect({ | val | blend(background, color, val / colorSize)}); // colors = (1..64).pow(0.01).normalize.collect(blend(background, color, _)); colints = colors.collect({ | col | Image colorToPixel: col }); }
futhermore:
colors = (1..colorSize).pow(0.01).normalize.collect(blend(background, color, _));
- Note taken on [2011-04-19 Tue 18:26]
The power exponent in calcGradient could be reduced to 0.1 or other values
recalcGradient { var colors; colors = (1..colorSize).pow(0.01).normalize.collect(blend(background, color, _)); colints = colors.collect({ | col | Image colorToPixel: col }); }
complexarray = log10( 1 + Complex( Signal.newFrom(magarray[0]), Signal.newFrom(magarray[1]) ).magnitude.reverse; ).clip(0, 1) * intensity;
complexarray.do({ | val, i | fftDataArray[i] = colints.clipAt((val * colorSize).round); });
Default: 0.5. Tested between: 0.01 and 1 Higher values: peaks show clearer, lower magnitudes may disappear Lower values: peaks smear more, lower magnitudes are displayed
Define vars in Spectrogram2:
- imageObjects: objects that draw on the image (before it is sent to pen)
- penObjects: objects that draw with pen (after image is sent to pen)
An object that wants to draw, must add itself to one of those two lists. Spectrogram2 will send all objects stored in imageObects the message drawImage, and all the objects stored in penObjects the image drawPen.
An object can add itself to start drawing or remove itself to stop drawing. This is simpler than going via NotificationCenter.
This is because buf.getn can only fetch up to 1024 values (OSC limit?).
removed frombin - tobin as I find little use for zooming in to part of a spectrum and it complicates the code
This is important when installing lilt2 to a new computer that does not have any sessions stored.
Function registers itself to notifier and does not reevaluate.
Important for booting servers / scopes only once.
! marked items in DocListWindow type files can use this automatically. Something like:
{ codeString.interpret }.doOnce instead of: { codeString.interpret }.value
At some point the older items remain on the pane together with the newly selecte ones.
Could not remedy that. Suggestion: Try using EZListView instead.
This is because, for example:
{ WhiteNoise.ar(0.2) } and { WhiteNoise.ar(0.1) }
have the same code:
Int8Array[ 0, 0, 64, -80, -62, 52, -14 ]
So now, in order for a function to be treated as “unique” by UniqueFunction, it must have exactly the same source code string.
Note: function.hashKey is used by UniquePlay.
BUFFER: *new { arg server, numFrames, numChannels, bufnum;} *alloc(server, numFrames, numChannels, completionMessage, bufnum) *read { arg server, path, startFrame = 0, numFrames = -1, action, bufnum; } *read(server, path, startFrame, numFrames, action, bufnum)
UNIQUESYNTH: *new { | key, defName, args, target, addAction=\addToHead | ^super.new(key, defName ?? { key.asSymbol }, args, target, addAction);
} init { | what, args, target, addAction … moreArgs | }
MODIFIED, works like Buffer:alloc *new { | key, server, numFrames, numChannels, action | } like Buffer:alloc, with action executed as soon as info is received, no bufnum allocates immediately, boots buffer if needed
*read { | key, server, path, startFrame = 0, numFrames = -1, action | } read from file. Boot buffer if needed. Store in library at [buffers, server:key]
NOT USED: *alloc( | key, server, numFrames, numChannels, completionMessage, bufnum | ) allocate immediately. Boot server if needed.
*play { | func, args | } play last selected buffer or default buffer if none selected
*load { | func, args | } load with file open dialog window, play if func is given
*select { | func, args | } open list of buffers, select one and execute func if provided. selected buffer stays for next calls of “play” or spectrogram.
*spectrogram { | func, args | }
*clear { | server | } clear all UniqueBuffers on server
*play { | func, args | } play last selected buffer or default buffer if none selected
*load { | func, args | } load from file and play if given func
*select { | func, args | }
*spectrogram { | func, args | }
*clear { | server | } clear all UniqueBuffers on server
String:ubuf(func) -> get / load the UniqueBuffer corresponding to this path, on the default server if func is provided, it is used to play the buffer
Symbol:ubuf(func) -> get the UniqueBuffer registered at the default buffer under this string if func is provided, it is used to play the buffer
*new { | key, server, numFrames, numChannels, bufnum | ^super.new(key, defName ?? { key.asSymbol }, args, target, addAction); }
*alloc {
}
*/
UniqueBuffer:play therefore now uses Function:mplay. This is not a major handicap.
A minor glitch, but should be looked into.
Extend UniqueSynth class with subclasses:
PrivateBusSynth: adds a private Bus and redirects the output of the synth to it. For analysis synths, effects etc. The bus gets freed as soon as the UniqueSynth stops.
SignalPoller: Adds a synchronized routine (rsynca) that polls the output of the synth to the private bus, or an anasysis buffer, and shares the data via a list of functions with other processes.
ObjectService? ObjectRegistry? ObjectServer?
PersistentObject? LibObject?
If Code class variable autoBoot is set to true, then evaluating a code snippet with Cmd-shift-x will call WaitForServer.new, thereby ensuring that any calls to ‘wait’ will be in sync with the server booted.
doWhen { | condition, action | // Some objects created by makeFunc may need time to initialize { while { condition.(this).not } { 0.1.wait }; action.(this); }.fork(AppClock) }
Elemenbí (L. M.: N. B!)
Added Pattern support: EventStream, UniqueStream, Function:sched. Also SynthPrep uses OSCpathResponder for exact boot timing
- Obviate the need to boot the server when starting synths
- Ensure that Buffers and SynthDefs are allocated / sent to the server before starting synths, efficiently.
- Provide a safe way for starting synth and routine processes when the server boots or when the tree is inited, ensuring that SynthDefs and Buffers will be loaded first
Classes involved:
- ServerPrep
- ServerActionLoader
- SynthLoader
- DefLoader
- BufLoader
- RoutineLoader
- UniqueBuffer
- Udef
- Simplify the creation and control of Synths by storing them in a dictionary for later access, and by providing utility methods for controlling the duration and release time, for synchronizing the execution and life time of routines pertaining to a synth, and for attaching other objects that react to the start and end of a synth.
Example of how UniqueSynth can simplify the code required:
Without Symbol:mplay
(
{
loop {
{ var synth;
synth = Synth(\default, [\freq, (25..50).choose.midicps]);
0.1.wait;
synth.release(exprand(0.01, 1.0));
}.fork;
[0.1, 0.2].choose.wait;
};
}.fork;
)
Using Symbol:mplay
(
{
loop {
\default.mplay([\freq, (25..50).choose.midicps]).dur(0.1, exprand(0.01, 1.0));
[0.1, 0.2].choose.wait;
};
}.fork;
)
Simplify the creation and access of Streams from Patterns and their use with Routines and Functions scheduled for repeated execution.
Example: Simplify the above code even further, while enabling control of dtime (and any other parameters) via patterns:
(
{ // Symbol:stream creates and / or accesses the stream as appropriate:
\default.mplay([\freq, \freq.prand((25..50), inf).midicps]).dur(0.1, exprand(0.01, 1.0));
\duration.stream(Prand([0.1, 0.2], 20)); // play 20 events only
}.stream;
)
Note: symbol.stream(Prand(…)) is equivalent to \symbol.prand(…)
Simplify the connection of objects for sending messages to each other via NotificationCenter. Automate the creation of mutual NotificationCenter registrations to messages, and their removal when an object receives the message objectClosed.
One beneficial effect of this is that it is no longer needed to check whether an object stored in a variable is nil in order to decide whether to send it a message. One can create messaging interconnections between objects without storing one in a variable of the other, and one can safely send a message to an object before it is created or after it is no longer a valid receiver of that message.
Enable the selection of parts of a SuperCollider document separated by comments followed by :, the movement between such parts, and the execution of those parts through keyboard shortcuts. Additionally, wrap these code parts in a routine so that number.wait messages can be written straight in the code, without wrapping them in { }.fork or Routine({ }).
Also ensure that the code will run after the default server is booted and the Buffers and SynthDefs defined as Udefs in a Session have been loaded.
Shortcuts provided are:
Command-shift-x: Evaluate the code in an AppClock routine, after booting the default server if needed Command-shift-alt-x: Evaluate the code in a SystemClock routine, after booting the default server if needed Command-shift-v: Evaluate and post the results of the code, without routine or server booting Command-shift-j: Select the next code part Command-shift-k: Select the previous code part
Arrange Document windows on the screen conveniently for maximum view area on the screen. Provide 2 layouts: single pane and 2 panes side by side, with keyboard shortcuts for switching between them. Provide an auto-updating document list palette for selecting documents by mouse or by string search. Provide a way for switching between a dark colored document theme and the default document theme via keyboard shortcuts, with automatic updating of the coloring of all relevant documents.
Provide some useful shortcuts for common tasks: browseUserClasses : Open a list of all classes defined in the user’s Application Support directory. Typing return on a selected item opens the code file with the definition of this class.
insertClassHelpTemplate : Insert a template for documenting a class named after the name of the document. Inserts listings of superclasses, class and instance variables and methods.
openCreateHelpFile : Open a help file for a selected user class. Automatic creation of the file is reserved to code residing outside the distribution files of this library.
showDocListWindow : An auto-updating window listing all open Documents, with selection by mouse click or by text search.
closeDocListWindow : Close the document list window
An example application showing some of the features of this library. Creates a window showing a live running spectrogram of one of the audio channels. The fft polling process for the spectrogram is persistent, that is, it starts as soon as the server boots and re-starts if the server’s processes are killed by Command-. It (optionally) stops when the Spectrograph window is closed.
(With Aris Bezas)
Added ./Extensions/Examples directory to master branch and attached it here to test orgmode link integration.
So you can write in a document this: //:Example 1
{ WhiteNoise.ar(0.1) }.play;
//: End of above example
Some text here. The part here will not be listed as a code snippet in the Code list window. (Cmd-})
//:Next example
\window.window;
/*
Reset ServerReady if an Exception occurs, to enable restarting the next time that ServerReady is needed.
*/
- Exception {
*new { arg what; var backtrace; // permit to use Cmd-Shift-X for evaluating code again. See classes Code and ServerPrep ServerPrep.initClass; if (debug) { backtrace = this.getBackTrace.caller }; ^super.newCopyArgs(what ? this.name, backtrace) }
}
Cmd-Ctl-C: Class list Cmd-Ctl-S: Synthdef List Cmd-Ctl-B: Buffer list
Should be simplified. Try to follow one principle: the symbol that gets the message is the one that stores the resulting new resource.
\symbol.map(\param, optional: symbol) ->
symbol.synth.map(param, symbol ?? (symbol ++ _ ++ param).control.index )
\symbol.mapDef(\param, defname, args (target???) ) ->
Like above, but start a synth with the defname and args and send its output to the bus.
\symbol.mapFunc(\param, function, args) ->
Like above, but start a synth with the function and args and send its output to the bus.
Onsets.kr(chain, threshold, odftype)
An onset detector for musical audio signals - detects the beginning of notes/drumbeats/etc. Outputs a control-rate trigger signal which is 1 when an onset is detected, and 0 otherwise.
chain - an FFT chain threshold - the detection threshold, typically between 0 and 1, although in rare cases you may find values outside this range useful odftype - the function used to analyse the signal (options described below; OK to leave this at its default value)
FFT onset detector based on work described in
Hainsworth, S. (2003) Techniques for the Automated Analysis of Musical Audio. PhD, University of Cambridge engineering dept. See especially p128. The Hainsworth metric is a modification of the Kullback Liebler distance.
The onset detector has general ability to spot spectral change, so may have some ability to track chord changes aside from obvious transient jolts, but there’s no guarantee it won’t be confused by frequency modulation artifacts.
Hainsworth metric on it’s own gives good results but Foote might be useful in some situations: experimental.
*ar(buffer, proph=0.0, propf=0.0, threshold=1.0, waittime=0.04)
FFT feature detector for onset detection based on work described in
Jensen,K. & Andersen, T. H. (2003). Real-time Beat Estimation Using Feature Extraction. In Proceedings of the Computer Music Modeling and Retrieval Symposium, Lecture Notes in Computer Science. Springer Verlag.
First order derivatives of the features are taken. Threshold may need to be set low to pick up on changes.
*ar(buffer, propsc=0.25, prophfe=0.25, prophfc=0.25, propsf=0.25, threshold=1.0, waittime=0.04)
buffer- FFT buffer to read from.
propsc- Proportion of spectral centroid feature.
prophfe- Proportion of high frequency energy feature.
prophfc- Proportion of high frequency content feature.
propsf- Proportion of spectral flux feature.
threshold- Threshold level for allowing a detection
waittime- If triggered, minimum wait until a further frame can cause another spot (useful to stop multiple detects on heavy signals)
chain [fft] - Audio input to track, which has been pre-analysed by the FFT UGen; see examples below for the expected FFT size smask [sk] - Spectral masking param: lower bins mask higher bin power within ERB bands, with a power falloff (leaky integration multiplier) of smask per bin tmask [sk] - Temporal masking param: the phon level let through in an ERB band is the maximum of the new measurement, and the previous minus tmask phons
Outputs the peak amplitude of the signal received at the input.
Outputs the peak amplitude of the signal received at the input. if level is below maximum, the level decreases by the factor given in decay.
#freq, hasFreq = Pitch.kr(in, initFreq, minFreq, maxFreq, execFreq, maxBinsPerOctave, median, ampThreshold, peakThreshold, downSample, clar)
ZeroCrossing.ar(in)
#coeff1, coeff2, … = MFCC.kr(chain, numcoeff=13) // coeffn -> kr signal. (OutputProxy).
Slope.ar(in, mul, add) Slope.kr(in, mul, add)
Measures the rate of change per second of a signal. Formula implemented is:
out[i] = (in[i] - in[i-1]) * sampling_rate
SpecCentroid.kr(chain)
Given an FFT chain, this measures the spectral centroid, which is the weighted mean frequency, or the “centre of mass” of the spectrum. (DC is ignored.)
This can be a useful indicator of the perceptual brightness of a signal.
SpecFlatness.kr(chain)
Given an FFT chain this calculates the Spectral Flatness measure, defined as a power spectrum’s geometric mean divided by its arithmetic mean. This gives a measure which ranges from approx 0 for a pure sinusoid, to approx 1 for white noise.
SpecPcile.kr(chain, fraction)
Given an FFT chain this calculates the cumulative distribution of the frequency spectrum, and outputs the frequency value which corresponds to the desired percentile.
Local Quark folders to use:
quarks.local.core : Core library parts quarks.local.projects : Project code
GitQuarks should copy installed version back to uninstalled and restore timestamps for git transparency
Added + tweaked local Quark mechanism by Martin Carle. Documented Quark installation procedure and added instructions in README files. Quarks are installed as symlinks in the Extension library (not as copies).
When switching from one Git branch to another, links to some quark folders which were installed may be missing. How to restore the quark configuration that belongs to a particular branch of a Git repository?
Note: What happens when the folder of an installed quark is missing because the branch does not contain it? Testing this with SC 3.4.4 on MacOS X 10.7.2: The missing quark is simply not included in the library, but SC compiles OK. When the folder is recreated by switching to another branch, then the link to that folder works again and the quark is included in the current SC compile configuration.
Each branch should remember the quark configuration that was last installed by the user. This configuration should be installed when the user re-compiles.
SC should be able to recognize what git branch is currently checked out. Solution: Write the name of the branch in a file that is saved in the repository.
The user should recall which configuration of quarks was last installed for each branch. This configuration can be different for each user. Therefore, the configuration is saved in the user application support folder, not in the git repository.
The configurations are therefore saved as a multi-level dictionary in an archive in the user application support folder. The structure of the dictionary is:
- level 1: name of repository
- level 2: name of branch For each branch in each repository: List of names of quarks installed.
This configuration should be saved at each time that any quark is installed or deinstalled using the quarks GUI.
Additionally, it is proposed to save a default quark configuration for each branch, inside the branch, possibly in the same file that saves the name of the branch. If a user-specific configuration for this branch is not found, then a dialog pops up and prompts the user to install the default configuration.
Merge procedure and setup to include two remotes in one local repository:
The assembla remote repository is protected and is used by mc and iz for pushing code collaboratively.
The github repository is public and is used for sharing code with everyone.
The assembla repository is linked to the master branch. The github respository is linked to the LogIn branch.
UserPrefs may use the global Archive, which is the standard method for saving objects to disc for the user.
Simplify Panes:
- Where should menu items such as Boot/Quit default server go? New class …
- Review and simplify pane placement scheme, especially:
- *defaults
- protoPanePos
- *arrange1Pane, *arrangeMultiPanes
- review role of *multiPaneWidth
- *nextPane