The Core of CBPSC: CorpusDB
Creating a Corpus
At its core, CBPSC is a collection of classes for SuperCollider. The main class that sets up and maintains a database of sound samples and associated metadata is called CorpusDB. To set up a new Corpus, simply call
CorpusDB.new and assign the result to a variable. The variable should be an environmental variable (which has more or less global scope).
~myCorpusName = CorpusDB.new("name", s);
s is a variable pointing to the current running server, and name is some string identifying the server. Let’s have a look at the code for the CorpusDB class…
[img=images/CorpusDB-init.png (popup=false) (float=left)]
There are a few things to point out:
- The class is a subclass of the Dictionary class. This means that all of Dictionary’s methods are automatically incorporated. In practice, it means that we can access the main parts of a corpus by using simple key-value lookups rather than methods or having to compose a top-level dictionary as a member of the class.
- Class variables, nothing special.
- An example of the simplified Dictionary-style calls. In a SuperCollider class, this refers to the class (like
selfin other languages). Here we are creating second-level tables to store various types of data references and metadata.
\dTableis a “descriptors table”. It is important in that it defines the descriptors we will use to categorize our corpus metadata. The index numbers mapped to descriptors map to the columns that define the descriptors, i.e. the unitID will always appear in the first (0th) column.
- One of the last things we do is call a function that builds a SynthDef to be used for all analysis. In future versions of CBPSC, this SynthDef will be reprogrammable in an integrated way that fits with the rest of the class functions.
- Finally, we return this so that the caller can assign the returned reference to the new class to a variable.
The rest of the class is comprised of a series of methods that provide all the access to the data/metadata, and all the functionality to manipulate, assess, manage, etc. that data.
Methods by Functionality
importSoundFileToBuffer add references to the sound file (namely, its path) and import the file to a Buffer on the server, respectively. The former must always happen before the latter.
removeSoundFile flushes any reference to a sound file from the database (but not metadata; that must happen separately).
mapIDToSF is a helper function that maps an integer ID to the sound file string, a step necessary to maintaining necessary internal mappings. References to sound files are stored in the sound files table, or
analyzeSoundFile is a huge function that analyzes a sound file in NRT (non real time) mode and dumps the resulting raw metadata into the appropriate tables. Raw analysis metadata is stored in the sound files table.
addSoundFileUnit defines the temporal boundaries and indexing of a unit. A unit is a segment of a sound file to which metadata will be mapped.
addRawMetadata is the method that maps raw metadata to the sound file before being divided into units. segmentUnits is the lynchpin that takes raw metadata and unit boundaries and maps the appropriate raw metadata into averaged values for each particular unit.
clearSoundFileUnit do what their names suggest, providing methods to update or delete sound file units as needed.
Important: First, the table that stores the units’ metadata is called the sound file units table or
this[\sfutable]. Next, note that the sound file units table maps sound files to their segmented metadata/units using indexes relative to that sound file. The method that coordinates this is called [b]mapBySFRelID[/b] and must be called at certain points when constructing a corpus.
mapSoundFileUnitsToCorpusUnits is the function that takes the sound-file-mapped units and remaps each unit to an index unique within the current corpus. This mapping preserves the sound file mapping and sound-file-relative indexing, adding a more-or-less global unique indexing scheme. If corpora are combined into a super-corpus, the combining algorithms (either during import or during “add-” methods following an import) will never duplicate a corpus index. These corpus-level indexed units are put into a table called
clearCorpusUnit, allow direct manipulation of the corpus unit table. They are usually called only within the class.
Now that we have identified three main unit tables, note how easy it is to access data from any of them. If you know the path of the sound file with which you are concerned or its index, you can access sound file metadata, or the actual Buffer. Likewise, you have access to that sound file’s unit metadata. If you have called
mapSoundFileUnitsToCorpusUnits, you are now able to access metadata by corpus-level indices. Furthermore, every unit row that you pull out of a
\cutable will contain metadata describing that data, as well as the index, onset time, and duration of the segment that unit defines. This bidirectional mapping completes the general sound data → metadata → sound data loop.
Finally, we want to be able to store our metadata to a file so that we do not have to reanalyze or re-segment our data.
exportCorpusToXML are the functions that export to XML and import back from an XML file.
CorpusDB is the core class for a corpus and contains much in the way of useful functionality. You can look through the class file to see the details. Next: a short digression on how to make a SynthDef perfect for recording samples to add to a database...