FIG. 1: Inventors J. Presper Eckert and John W. Mauchly operate the Electronic Numerical Integrator and Computer (ENIAC) with lab assistants. This was the first general-purpose computer, used by the U.S. government from 1946 through 1952 for work in ballistics, weather prediction, cosmic-ray studies, thermal ignition, random number studies, and wind-tunnel design.
Have you ever wondered what's under the hood of your digital audio sequencer? Your favorite program more than likely has hundreds of thousands of lines of code. That code describes a vast network of interactions that make up your sequencer, audio editor, and any other modern, mature program. When you launch Digital Performer or WaveLab, you are executing a “stored program” (to put it in geek speak); that is, you are loading a library of functions into your computer that lurk in the background, waiting to be activated.
Remember Your Numbers
Like a post-office box, a computer's memory cells have their own unique address and contents. The contents are in the form of binary numbers (the number 0 means “off” and 1 means “on”) and represent either data or instructions, depending on their value and context. Enter the right instructions, and you can retrieve a value, work with it, and re-store it.
Instructions tell the computer how to access and manipulate the contents of the computer's memory. A sample instruction, such as “Retrieve the data from two locations and add them together, then store the result in a third memory location,” would consist of four binary numbers: one number that commands the computer to add; a second number that has the address of the first value; a third number, which has the address of the second value; and a fourth number, which identifies the location for the result.
When people communicate with a computer using binary numbers, they are using a machine language. Computer operators in the 1940s and the 1950s used machine languages by moving physical switches up and down, creating the “off” and “on” combinations needed to retrieve, generate, or store values (see Fig. 1). Switches were abandoned in favor of punch cards that contained the desired commands. Punch cards use rows of chads, either punched or unpunched, and those two states represent the values 1 and 0.
FIG.2: This algorithm shows the process of a simple lowpass filter.
Using sets of punch cards, entire libraries of familiar instructions could be created. The instructions could be loaded into the machine by feeding it cards one by one, giving it functions that could be carried out when the right command was entered. Thus, the stored program was born. With stored programs, computers evolved from being fast calculators to becoming flexible problem-solving machines, equipped with tools to address a variety of situations. To more easily create stored programs, programming languages were invented starting in the 1950s. Punch cards were eventually replaced by other forms of media on which to store programs — first magnetic tape and magnetic drums, then magnetic discs, and finally optical discs, which is the medium in use today.
Higher and Higher
Nowadays, the only people who type binary instructions are the folks in hardware design. High-level programming languages such as Pascal, Fortran, and Basic are used to write software programs and have superseded binary machine languages. Programming languages allow a developer to create software using English-like statements (called source code). A common language for music software is C, which is a midlevel language; it operates close to machine level, addressing memory directly, making it fast and flexible. C's structure allows sets of statements (called functions or subroutines) to be named, which enables a single action (such as clicking on a button or selecting an item from a menu) to call the function and trigger a series of steps. For example, a single function call could invoke a series of calculations to derive the value of the sine of x.
Modern programmers implement algorithms, which are reusable sequences of functions. An algorithm to create a wavetable might use one function that generates a sequence of values, another function to perform trigonometric calculations on each value, and bookkeeping functions to ensure that the sample values are stored properly (see Fig. 2).
Source code for any program is written using a text editor. Once written, the program can be either run immediately by an interpreter or compiled into an executable program. (Programmers typically use an Integrated Development Environment (IDE), such as Metroworks CodeWarrior, to facilitate the creation, testing, and compilation of program code. An IDE most often contains a text editor, compiler, and debugger.) An interpreter executes program instructions one statement at a time and executes the source code while it is reading it. A compiler translates the code into machine language for a specific platform, such as Macintosh or Windows, and creates the executable program.
Java, a programming language written at Sun Microsystems in the early 1990s, can be used to create a single version of an application that will run on any platform. Originally designed for creating software for appliances such as microwave ovens and TV remote controls, Java now dominates Internet-based applications.
Go to the Head of the Class
A revolutionary step in program organization was object-oriented programming (OOP), started by the Norwegian Computing Center in the mid-1960s. An OOP version of C, called C++, was developed at Bell Labs in the early 1980s. An object is a self-contained unit of code that carries out a specific task, like a worker bee. It understands certain types of messages, such as commands entered from the keyboard or the mouse or values generated by another object, and outputs information by storing or displaying new values. That information may in turn be ingested and processed by another worker bee. An example is Cycling '74's Max/MSP, an object-oriented programming language for MIDI and audio written in C++. Max/MSP uses a graphical user interface to show the OOP idea: select object types to use, connect their inlets and outlets, and create customized signal flow.
An OOP lets you create hierarchies of objects based on a “parent” design, called a class. (Think of a class as a template or type of object.) For example, James McCartney's object-oriented synthesis language SuperCollider for Mac and Linux has a general class description for unit generator, which is a template for algorithms that create or process audio. The unit generator class has some overall guiding parameters, such as whether audio is being produced at audio rate (44,100 samples per second) or control rate (a slower processing rate, about 689 samples per second). Real work, though, is done by subclasses of the unit generator class.
Subclasses of unit generators are all related. They inherit all properties of the unit generator class, such as an understanding of sampling rates, but have specific functions that equip them for their particular tasks. Subclasses make it easy to create specialized versions of a generalized parent class. Subclasses can, in turn, be subclassed.
In SuperCollider, two subclasses of unit generators are oscillators and filters. The oscillator class can receive messages regarding the frequency and amplitude of a waveform. Subclasses of oscillators inherit those capabilities, but each outputs a different wave shape (for example, sine, sawtooth, or triangle). The filter class can receive messages in the form of an input signal and a reference frequency. Subclasses of filters inherit an expectation of those messages and behave as lowpass, highpass, bandpass, or other filter types.
Plug It In
Programmers don't always create entire applications from scratch. Plug-ins are miniprograms that are linked to a host program. Cakewalk, for example, supports plug-ins created in DirectX format. The C++ library is used to create a DirectX plug-in and provides a starting framework for such programs. In OS X, Apple introduced Audio Units, which are generic plug-ins written in Objective C (another object-oriented version of C) for any Macintosh audio software. In Max/MSP, new objects can also be created with C++; Max/MSP, in turn, can be used to build plug-ins in Steinberg's VST format, which many applications are able to host.
Programming opens up a world in which you can make your own tools and give yourself options that exceed the capabilities of off-the-shelf software. But watch out, it can become delightfully habit-forming.
Mark Ballora teaches music technology at Penn State University. He can be reached firstname.lastname@example.org.
For more information about programming, check out these resources:
Computer Music: Synthesis, Composition, and Performance (2nd edition), by Charles Dodge and Thomas A. Jerse (Schirmer Books, 1997)
The C Programming Language (2nd Edition), by Brian W. Kernighan and Dennis M. Ritchie (Prentice Hall, 1988)
C++ Algorithms for Digital Signal Processing, by Paul M. Embree and Damon Danieli (Prentice Hall, 1998)
The C++ Programming Language: Special Edition, by Bjarne Stroustrup (Pearson Education, 2000)
SmallTalk: Best Practice Patterns, by Kent Beck (Prentice Hall, 1997)
MUSIC-DSP Mailing List and Web Site:http://shoko.calarts.edu/~glmrboy/musicdsp/music-dsp.htm