Book Image

Mapping and Visualization with SuperCollider

By : Marinos Koutsomichalis
Book Image

Mapping and Visualization with SuperCollider

By: Marinos Koutsomichalis

Overview of this book

SuperCollider is an environment and programming language used by musicians, scientists, and artists who work with audio-files SuperCollider has built-in graphical features which are used in conjunction with the sound synthesis server to create audio-visual mapping and sound visualization. If you wish to create data visualizations by acquiring data from audio and visual sources, then this book is for you.Digital sound artists need to analyze, manipulate, map, and visualize data when working on a scientific or an artistic project. As an artist, this book, by means of its numerous code examples will provide you with the necessary knowledge of SuperCollider's practical applications, so that you can extract meaningful information from audio-files and master its visualization techniques. This book will help you to prototype and implement sophisticated visualizers, sonifiers, and complex mappings of your data.This book takes a closer look at SuperCollider features such as plotting and metering functionality to dispel the mysterious aura surrounding the more advanced mappings and animation strategies. This book also takes you through a number of examples that help you to create intelligent mapping and visualization systems. Throughout the course of the book, you will synthesize and optimize waveforms and spectra for scoping as well as extract information from an audio signal. The later sections of the book focus on advanced topics such as emulating physical forces, designing kinematic structures, and using neural networks to enable you to develop a visualization that has a natural motion with structures that respect anatomy and which come with an intelligent encoding mechanism. This book will teach you everything you need to work with intelligent audio-visual systems to extract and visualize audio-visual data.
Table of Contents (16 chapters)

Metering levels


Besides plotting the actual signal or dataset, there are situations where we merely want to monitor changes in some magnitude. The most typical scenario is metering the amplitude of some signal, but we could meter anything really, as long as it is represented by some numerical value.

Monitoring signals

Generic metering in SuperCollider is primarily addressed by the LevelIndicatorclass. To monitor some magnitude specific to a signal, we first need to track it, write the resulting values to some control-rate instance of Bus or to some instance of Buffer, and later use an instance of Routine to manually update the value of LevelIndicator as appropriate. For now, we will limit ourselves to using the Amplitude UGen to only track the amplitude; in Chapter 6, Data Acquisition and Mapping, we will discuss how to track other kinds of magnitudes and how to extract information out of audio signals. Note also that a convenient meter method does exist, yet it is only limited to instances of Server and to monitoring the global I/O streams of all its default channels (for example, Server.default.meter).

(  // Simple Level Metering
Server.default.waitForBoot({

  // create the parent window
  var window = Window.new("Level Metering", Rect(200,400,60,220)).front
  .onClose_({   // stop routine when the window is closed
    updateIndicator.stop;
    sound.free;
  });

  var bus = Bus.control();  	// create a Bus to store amplitude data

  // an audio signal
  var sound = { 
    var sound = WhiteNoise.ar(Demand.kr(Dust.kr(20),0,Dbrown(0,1,0.3)));
    var amp = Amplitude.kr(sound);  // track the signal's amplitude
    Out.kr(bus, amp);  // write amplitude data to control bus
    Out.ar(0,sound);   // write sound to output bus
  }.play;

  // create and customize Indicator
  var indicator = LevelIndicator(window,Rect(10,10,40,200))
  .warning_(0.5)           // set warning level
  .critical_(0.7)          // set critical level
  .background_(Color.cyan) // set Color
  .numTicks_(12)           // set number of measurement lines
  .numMajorTicks_(3)       // set number of major measurement lines
  .drawsPeak_(true);       // draw Peak Values

  // update the Indicator's value with a routine
  var updateIndicator = fork{loop{
    bus.get({   // get current value from the bus
      arg value;
      {indicator.value_(value);     // set Indicator's value
        indicator.peakLevel_(value); // set Indicator's peak value
      }.defer(); // schedule in the AppClock
    });
    0.01.wait; // indicator will be updated every 0.01 seconds
  }};
});
)

Again, note that we use a defer block to schedule anything that is GUI-related to the AppClock subclass.

Monitoring numerical data

Apart from a signal's magnitude, LevelIndicator can be used to monitor any kind of data we may be interested in. In the following code, we loop through an eight-channel multidimensional dataset:

(  // Monitoring a complex numerical Dataset
var indicators, updateIndicators; 
var index = 0;  // a global index used to iterate through the dataset
var dataset = Array.fill(8,{Array.fill(1000,{rrand(0,1.0)})}); 
// a multi-dimensional dataset

// create window
var window = Window.new("Monitoring a complex numerical dataset", 360@210).front.onClose_({ updateIndicators.stop });
window.addFlowLayout; // add flowLayout

// create and customize 8 Level indicators
indicators = Array.fill(8, {LevelIndicator(window,40@200)});
indicators.do { arg item;
  item.warning_(0.8).critical_(0.9).background_(Color.cyan).drawsPeak_(true);
};

// update the indicators with a routine
updateIndicators = fork{loop{
  indicators.do{ arg item, i; {
    var value = dataset[i][index];  // read value from the dataset
    item.value_(value);             // set each Indicator's value
    item.peakLevel_(value);         // set each Indicator's peak value
  }.defer();  // schedule in the AppClock
  };
  // increment index or set to 0 if it has exceeded dataset's size
  if ( index < 1000) {index = index + 1;} {index = 0; };
  0.1.wait; // indicators will be updated every 0.1 seconds
}};
)

This time an array of LevelIndicator objects is used instead of a singleton element, and of course, there is no need for some specialized tracking UGen. We merely use an instance of Routine to access the dataset by means of a global index, which is accordingly incremented once some datum is read. This is so that it always reflects the position of the next object. We will also need an if construct to zero out the index once it has exceeded our dataset's size in order to reiterate from the beginning.