Playing a song with my oscillators

This is a big update, and it took me a long time. Here's what's going on.

I want this oscillator to be more than just playing a single note - after all, what is a synthesizer for, if not playing music? Okay, fine, making sound effects, too.

There is no graphical representation for a song yet. In the old-school tracker programs, they typically have a vertical set of tracks, with the song flow going from the top down (piano roll DAWs go from left to right). If I were to make a GUI, it would probably resemble a tracker.

Not very modern, but I used this interface a lot back in the day.

To make a song, I actually need some objects to define the song structure. So I'll start with those.

Song Classes

Song: The main object for a song. Holds instrument data and song metadata. For now, it holds just a single instrument and a bpm field, which isn't used yet.

Instrument: Contains data regarding an instrument. Effectively a single "voice" for the song. Contains data about how an instrument is to be played: an oscillator to produce sound, a decay value, and a single phrase (eventually multiple phrases). Contains functions that produce the actual PCM data for a song (I will eventually move this into a PCM class - see below).

Phrase: Contains instructions on what notes to play and when. Contains a length field (in beats), each beat has six positions. So the overall length of a phrase is beats * 6. Provides functions to insert a note into a phrase, and to get note data from a particular position. 

Note: Note instructions for a single position. POJO. Contains pitch, volume, and panning info.

PCM Classes

NoteSignalGenerator: This is the only PCM class for now. The purpose of these classes is to generate PCM data based on a certain set of inputs. That PCM data can then be sent to a SourceDataLine for playback. Eventually, I will have a PCM class for the song itself.

The NoteSignalGenerator accepts a Note, an Oscillator, and a decay value. It uses those to produce an array of the note as a ShortArray. It does not play any of the audio data, it just provides the values.

Player Classes

Responsible for actually playing the audio, but not producing it.

AudioPlayer: Now has two play methods, playSignal and playSong. playSignal (which I should probably rename to playSamples) accepts a ShortArray of PCM data and just plays the whole thing. This is for playing a single note. playSong accepts a Song object and iterates through the song, eventually playing the whole thing.

Oscillator Classes

My main change to the oscillators is they now keep track of their own position. The position value, which starts at zero, has a public get but a protected set. There is a public reset to reset the position to zero. The other major change is that oscillators now just return a double, and leaves the matter of applying amplification to whoever happens to be calling it. It shouldn't be the oscillator's job to apply amplitude - this could also make the oscillators potentially more useful if I ever want to use them for something other than generating PCM samples (LFO modulation, for example).

Runner

The runner currently has two options, specified at the command line. "1" plays a single note using the playSignal method. "2" plays a simple song using the playSong method. The song is just a C scale, with panning shifting from left to right.

In my next update, I'm going to start taking into account tempo.

Comments