Render Notes 5 Readme by Peter C. Capasso Render_notes_5 is a command-line monophonic software synth, with one lfo, one oscillator, two unison oscillators, a sample playback unit, phase distortion, resonant low pass filter, high pass filter, VCA, and a delay (echo). Synthesis is non real time and is output to a .sam[ple] file. Compiling ~~~~~~~~~ gcc -lm -o render_notes_5 render_notes_5.c if you are feeling rather saucy you can optimize for your CPU (for example): gcc -march=athlon-xp -lm -o render_notes_5 render_notes_5.c Usage ~~~~~ render_notes_5 infile.txt outfile.sample BPM Specify an input file name of "-" to read from stdin (so you can read from a pipe). Specify an output file name of "-" to write to stdout (so you can write to a pipe). BPM is a floating point number in the range 40.0 to 240.0. Those parameters must occur in that order. There are no other parameters. Everything else is controlled from the commands in the input text file/stream. Syntax ~~~~~~ All lines that follow can be one of the following commands: note ---- This command renders a note. The command is followed by three parameters. The first parameter is the pitch, expressed in the form of a note letter (with or without a sharp) and an octave number. The second parameter is the total note length, as a ratio of a quarter note. The third parameter is how long the note is played: articulation. This number cannot be longer than the total note length. Some examples: note c2 1 0.8 note d#3 0.5 0.3 note f3 0.25 0.12 note g3 0.25 0.1 The first line plays a quarter note, with an articulation of 0.8 of a quarter note. The second line plays an eighth note. The last two lines play 16th notes at different articulations. anote ----- This command is an easier version of the note command. It differs from the note command in that the articulation is not specified, it is automated. Example (plays one eighth note with a pitch of d# in octave four): anote d#4 0.5 rest ---- This command renders out a rest. The command is followed by a number. This number is the length of the rest, specified as in the note command. If a previous note has not finished its release stage, the release will continue during the rest (the note will continue to "ring"). wave ---- This command specifies the waveform to be used by the oscillator. The command is followed by one of six named waveforms: wave sawtooth wave square wave triangle wave sine wave circle wave line The line wave is a special case. It differs from the others in that it renders a user-specified list of line segments into a waveform. More on this in the line wave section. The add24 command can be used instead to build a custom waveform using 24-harmonic additive synthesis. Also note that the bit_wave command provides a second alternative way of building up a custom waveform from a bit pattern. The line wave commands ---------------------- This renders a user specified list of line segments into a wave form. The user must set up this list before hand. The two commands used for building lists of lines are add_line and init_line. init_line clears the current list. Attempting to "wave line" at this point will not work. add_line adds one line to the current list. It is followed by three parameters: the starting level of the line (-1.0 to 1.0) the ending level (-1.0 to 1.0) and the relative duration of the line. This relative duration is relative to the lengths of the other lines in the list. The lengths will be adjusted when the wave is rendered so that the total length of the lines fits in the waveform buffer. This is confusing to explain but simple in concept. let's have some examples. init_line add_line 1 -1 1 wave line The above three lines of code generates a sawtooth wave. It starts at a level of 1, then goes down to a level of -1. The length was specified as 1 and that was scaled to fit the waveform buffer. init_line add_line 1 -1 7.5 wave line The above makes the exact same sawtooth wave as the previous example. The length of 7.5 was scaled to the waveform buffer. With only one line in a wave, the relative length parameter is irrelevant (but must still be included). init_line add_line 1 0 1 add_line 0 0 1 add_line 0 -1 1 wave line The above example renders a wave out of three lines. Since all three lines have the same length/duration, each line occupies one third of the wave buffer. The wave starts at 1 (like a sawtooth), then goes down to zero a third into the waveform buffer. Then it holds at that zero until two-thirds along the waveform buffer, Then from there it drops to -1. init_line add_line 1 1 0.05 add_line -1 -1 0.95 wave line The above example renders a rectangle wave with a duty cycle set to around 5%. This illustrates that discontinuities (jumps in level) between lines are possible. It might be best to just experiment with these commands to get a better feel for how it all works. add24 ----- This command allows the user to create custom waveforms by layering 24 sine waves (tuned to the fundamental and 23 harmonics). The relative amplitude of each harmonic is specified as a number from 0.0 to 1.0. The "add24" command is followed by 24 numbers, all on the same line, separated by spaces. There must always be 24 numbers. The resulting waveform is normalized to "fit" so clipping is not a concern. The following example builds up a the first five partials of a sawtooth wave: add24 1 0.5 0.33 0.25 0.2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Add24 combined with use of unison and/or phase distortion can result in some "digital FM" style sounds. bit_wave -------- Like add24, this allows the user to create custom waveforms. This uses a user-specified pattern of bits to build up a complex rectilinear waveform, similar to the way that certain tracker "chip mods" or the old Casio VL-1 synth & calculator used to do it. "1" corresponds to a maximum positive voltage and "0" corresponds to a maximum negative voltage. The following three lines all have the identical result of building a square wave: bit_wave 10 bit_wave 1111100000 bit_wave 1100 A square wave is a rectilinear wave whose duty cycle is 50%. The following example creates a wave with a different duty cycle: bit_wave 10000 A more complex example: bit_wave 011010001000 Most of the time a DC-offset is introduced by this sort of waveform so it is a good idea to use some high pass filter. bitwave ------- Same as bit_wave. alias_xv -------- Called after selecting or building a waveform, this command goes through the wave table buffer and down-samples to a wave table of only 128 samples in length, containing samples of 12-bit resolution. This enables the oscillators to get a sort of trashy aliasing sound at higher pitches, similar to the digital oscillators in the DSI Evolver. The effects of this command last until the next waveform change. alias_8bit ---------- Called after selecting or building a waveform, this command goes through the wave table buffer and down-samples to a wave table of only 64 samples in length, containing samples of 8-bit resolution. This enables the oscillators to get that crappy "1980s 8-bit" or "chip mod" sound. The effects of this command last until the next waveform change. delay_regen ----------- This command controls the amount of regeneration (feedback) of the delayed signal. The command is followed by one number. A setting of 0.0 indicates no regeneration. A setting of 1.0 indicates maximum regeneration, which for this program is kind of pointless. delay_level ----------- This command controls the mix of the delayed signal with the dry signal. the command is followed by one number. The values range from 0.0 (no delay) to 1.0 (delay and dry at a 50:50 mix). delay_time ---------- This command sets the delay time. The command is followed by one number, which is the delay time in seconds. The maximum delay time is not that long. env_attack_rate --------------- This command sets the attack rate of the ADSR envelope generator. The command is followed by one number. This value is not a time in seconds. It is a fraction of the total volume range that the sample will increase per sample. The total volume range is 1.0, so valid settings for the attack rate are > 0.0 to 1.0. A setting of 1.0 jumps immediately from zero to full volume. This is the fastest/quickest attack but will often result in clicks at the beginning of notes. A setting of 0.5 will raise the volume by 0.5 every sample; so after two samples the attack phase will have completed. A setting of 0.1 will raise the volume by 0.1 each sample; so after ten samples the attack phase will have completed. A setting of 0.01 will raise the volume by 0.01 every sample; so after 100 samples the attack phase will have completed. In the above calculations, remember that render_notes always operates at a sample rate of 44100 Hz. env_attack_time --------------- This is command is an alternative to the env_attack_rate command. This command sets the attack time of the ADSR envelope generator. The command is followed by one number. This value is the time in seconds that it will take the envelope generator to rise from the initial note on to the full volume. A setting of 0.0 jumps immediately from zero to full volume. This is the fastest/quickest attack but will often result in clicks at the beginning of notes. A setting of 1.0 will raise the volume over a period of one second. env_punch_time -------------- This command sets the punch time of the ADSR envelope generator. The command is followed by one number. This is the time (in seconds) that the envelope pauses after completing the attack phase and before beginning the decay phase. The output volume of the envelope generator will be 1.0 (full) during this time. This command allows one to add "punch" to the sound, as if it had already been partially "squashed" by an (audio level) compressor. Default punch is 0 seconds (no punch at all). env_decay_time --------------- This command sets the decay time of the ADSR envelope generator. The command is followed by one number. This is the time (in seconds) that the envelope takes to decay, in seconds, after finishing the attack portion and the punch portion. At the end of the decay time, the output volume of the envelope generator will be at the sustain level. env_sustain_level ----------------- This command sets the sustain level of the ADSR envelope generator. The command is followed by one number. This is the volume that long/held notes sustain at after the decay time has passed. The number must be in the range 0.0 (silence) to 1.0 (maximum level). Default is 0.5. env_release_time ---------------- This command sets the release time of the ADSR envelope generator. This is the time (in seconds) that the note will "ring" on after the note has been turned off or "released" ("key up" or "note off"). The command is followed by one number. The time is specified in seconds. For example, to set the release time to be one quarter of a second, the command would be: env_release_time 0.25 Note: the release of a note can continue beyond the note length if a rest follows the note. phase_distort ------------- This command sets the strength of the phase distortion effect. The command is followed by one number. A setting of 0.0 indicates no phase distortion and a setting of 1.0 indicates the maximum phase distortion. On a square wave, phase distortion is similar to a duty cycle setting. Most of the time a DC-offset is introduced by this. A mild fixed high pass filter is part of the signal chain of this program so this should not be a problem. env_to_phase_distort -------------------- This command controls how much the envelope generator modulates the phase distortion effect. The command is followed by one number in the range 0.0 (no modulation) to 1.0 (maximum modulation). filter_cutoff ------------- This command controls the low-pass filter setting. The command is followed by one number. A setting of 0.0 filters the sounds almost to silence whereas a filter setting of 1.0 is equivalent to no filtering at all. env_to_filter_cutoff -------------------- This command controls how much the envelope generator modulates the filter cutoff. The command is followed by one number in the range -1.0 (maximum inverted modulation) to 0.0 (no modulation) to 1.0 (maximum modulation). filter_feedback --------------- This command controls the amount of feedback (aka "q" or "resonance" or "peak") in the low pass filter. A setting of 0.0 is no resonance and higher settings increase the resonance. This setting will often cause clipping so you will probably want to use the "filter_volume" command to compensate. filter_volume ------------- This command adjusts the volume of the output of the low pass filter. A setting of 1 has no effect. A setting of zero results in silence. In between values work as one would expect. Unlike the volume command, this does not affect the sample volume. Mostly this is used to correct for the increased volume that happens when filter resonance is used. Valid values are 0.0 through 1.0. This precedes the effects of the volume command in the signal chain. For example, if the volume is set to 0.5 and the filter_volume is set to 0.5, the effective volume of the oscillator part is 0.25. high_pass_pitch --------------- This commands sets the cutoff frequency of the high pass filter. The frequency is specified in Hz and has a range of 0.0001 (pretty much no filter at all) to 22050.0 (just about everything filtered out). The default value is 20.0 Hz. The high pass filter has no resonance and is not modulated by anything. volume ------ This command sets the overall volume of the sound. The range is 0.0 to 2.0. 0.0 would yield silence. A volume of 1.0 is a sensible and obvious setting. Settings from 1.0 to 2.0 can be used to cause a simple "clipping" distortion. Note that this distortion follows the delay in the effects chain. This command is not to be confused with the filter_volume command. lfo_pitch --------- This command controls the rate at which the lfo oscillates. The command is followed by one number indicating the pitch in Hz. Numbers less than 1.0 yet more than 0.0 (thus indicating a wavelength greater than one second) are allowed and are rather useful for chorusing effects when modulating phase distortion or sync. lfo_phase --------- This command changes the phase of the lfo. This command expects one parameter, a number between 0 and 1 inclusive. 0 corresponds to 0 degrees, 0.25 corresponds to 90 degrees, 0.5 corresponds to 180 degrees, 1.0 corresponds to 360 degrees (which is the same as 0 degrees). lfo_to_pitch ------------ This command controls how much the lfo modulates the pitch of the oscillator. Good for making cheesy organs. The command is followed by one number, in the range 0.0 (no effect) to 1.0 (mega wobbly). Negative values are allowed and reverse the phase of the output of the modulating oscillator. 0.002 or 0.003 is a reasonable amount. 0.0 is the default. lfo_to_phase_distort -------------------- This command controls how much the lfo modulates the phase distortion of the oscillator. The command is followed by one number, in the range 0.0 (no effect) to 1.0 (maximum effect). Negative values are allowed (down to -1.0) and reverse the phase of the output of the modulating oscillator. lfo_to_sync ----------- This command controls how much the lfo modulates the sync of the oscillator. The command is followed by one number, in the range 0.0 (no effect) to 1.0 (maximum effect). Negative values are allowed (down to -1.0) and reverse the phase of the output of the modulating oscillator. fine_tune --------- This command adjusts the fine tune of the sample (not of the oscillator). It is followed by one number which must be -1 to 1. -1 should correspond to a full half step down and 1 should correspond to full half step up ... but I am not sure I've got that right. Avoid using this command if possible at this time since it will most likely change in future versions. transpose --------- This command sets the amount of transposition for all notes to follow. This transpose setting will persist until the end of the song or until the next transpose command. It is followed by an integer that indicates the number of half-steps of the transposition. Negative values are allowed and indicate a transpose down in pitch. An example command would be: transpose 3 Successive transpose statements are not cumulative. In other words, a "transpose 2" followed by a second "transpose 2" will not result in a shift of 4 half-steps, just 2. macro ----- This command begins definition of a new macro. Macros can contain notes, rests, anotes, and calls (other commands will not work within a macro). This command is followed by a number which will later be used to call the macro. Each macro must have a unique number. Macros can call other macros. Macros cannot be recursive. An example line would be: macro 2 The macro is followed by the commands that are to be put into the macro end_macro --------- This command finishes definition of the macro that is currently being defined. There are no parameters. call ---- This command calls an already-defined macro. This command is followed by a number that corresponds to the number of an existing macro. An example line would be: call 2 sample ------ This command allows a .sample file (assumed to contain a pitched tone in .sample format) to be loaded as a sample to layer along with the oscillator. The command is followed by a quoted string containing the filename of the sample to load. The syntax of the path is operating-system dependent. An example line for the dos (windows console) version would be: sample "c:\sounds\tones\blah.sam" The default sample volume is zero. best to follow this command with a sample_volume command because loading a new sample does not change the current sample_volume setting or sample_note setting. The sample is not routed through the filter and VCA but is run through the delay and the final volume. If the sample is more than two seconds in length, only the first two seconds will be read. sample_note ----------- This command is used to specify the original pitch of the sample. The command is followed by one note in the usual render_notes syntax. For example: to specify that the input sample is an A sharp in the third octave, the line would be: sample_note a#3 Loading a new sample later on will not change this. sample_volume ------------- This command sets the mix ratio between the oscillator and the sample. This command is followed by one number. At a setting of 1.0, only the sample is heard. At a setting of 0.0, only the oscillator is heard. When a new sample is loaded, the current sample_volume is not changed. 0.0 is the initial setting when the program starts. unison_detune ------------- The command specifies the amount of pitch deviation of the two (or four) unison oscillators, sort of like in the MonoMachine synth. The "unison_detune" command is followed by one number. A setting of 0 indicates no detuning at all. A setting of 1.0 is the maximum detune effect. Default setting is 0. unison_mix ---------- This command specifies the relative volume of the unison detuned waves with the main wave. It is followed by one number. A setting of 0 indicates no unison wave at all, a setting of one is the maximum mix, in which the two unison waves are each mixed at one half the volume of the main oscillator. Default setting is 0.0. unison_spread ------------- This command specifies the relative volume of two additional unison spread oscillators, making the total number of unison oscillators four. It is followed by one number in the 0.0 to 1.0 range. This value is relative to the unison_mix setting. A setting of 0 indicates no unison spread to the two extra oscillators at all. A setting of 1.0 makes the spread oscillators equivalent in volume to the unison oscillators. Default setting is 0.0. sub_osc_level ------------- This command sets the volume of the sub-oscillator, a sine wave that is pitched one octave below the main oscillator. The sub-oscillator is not affected by sync and phase_distort, but does follow portamento, updown, and lfo_to_pitch. Default setting is 0.0. copy_osc_sub ------------ This command copies the waveform of the oscillator into the sub-oscillator. swap_osc_sub ------------ This command swaps the waveform of the oscillator with that of the sub-oscillator. include ------- This command allows a file to be included, similar to the functionality of most programming languages. The command is followed by one file name in quotes as in the sample command. Files can in turn include other files. The maximum include depth is 12. No check is done for a file including itself or similar recursive techniques: the user is assumed to know enough not to do it. ;) An example include line: include "verse_1.txt" osc_phase --------- This command changes the current phase of the main oscillator. This command expects one parameter, a number between 0 and 1 inclusive. 0 corresponds to 0 degrees, 0.5 corresponds to 180 degrees, 1.0 corresponds to 360 degrees. up_osc_phase ------------ This command changes the current phase of the up unison oscillator. This command expects one parameter, a number between 0 and 1 inclusive. 0 corresponds to 0 degrees, 0.5 corresponds to 180 degrees, 1.0 corresponds to 360 degrees. down_osc_phase -------------- This command changes the current phase of the down unison oscillator. This command expects one parameter, a number between 0 and 1 inclusive. 0 corresponds to 0 degrees, 0.5 corresponds to 180 degrees, 1.0 corresponds to 360 degrees. upmore_osc_phase ---------------- This command changes the current phase of the upmore spread unison oscillator. The parameter syntax is identical to the up_osc_phase command. downmore_osc_phase ------------------ This command changes the current phase of the downmore spread unison oscillator. The parameter syntax is identical to the down_osc_phase command. sync ---- Creates a "hard sync" effect, the sound of an analog synth in which one oscillator is re-triggered (reset to its starting phase) whenever a second oscillator has a positive zero crossing. This command does not work exactly like that (since I did not want to add more oscillators just for sync effects) but sounds similar. Valid values are 0.5 through 2.0. 1.0 (the default setting) results in no sync effect. noise_volume ------------ Sets the amount of white noise to mix in with the oscillator. This occurs just before the low pass filter in the signal chain. A value of 1.0 is all noise and no oscillator and a value of 0.0 (which is the default) is no noise and all oscillator. An example: noise_volume 0.1 The noise generator is generated via the built in c language random number generator and is not very sophisticated nor truly random. Also, the initial seed for this random number generator is always set to the same value for every run of the program. ext_in ------ This command specifies an existing .sample file that will be read in, mixed with the mixed noise & oscillator signal and then routed through the filters and gated by the envelope generator. An example: ext_in "stuff_to_gate.sam" If the ext_in_file ends before your composition ends, silence will be generated for the ext_in signal instead. ext_in_volume ------------- This command sets the mix of the ext_in signal with the mixed noise & oscillator signal. A setting of 1.0 is only the ext_in signal and a setting of 0.0 is no ext_in_signal. A setting of -1.0 is similar to the 1.0 setting but the phase of the ext_in signal will be reversed. The default setting is 0.0. If the ext_in_volume is raised yet there is no ext_in_file, silence will be generated for the ext_in signal. updown ------ Get some wine to have with this cheese. This effect mimics an old casio sound and also a chipmod type sound where the oscillator switches between the current pitch and an octave down pitch. The updown parameter is followed by one number which is a time relative to the BPM. A time of 1.0 is one quarter note. A time of 0.5 is one eighth note, etc. A time of zero or close to zero will turn the effect off. Default value is 0 (which is off). Some examples: updown 0.015625 updown 0.5 updown 0 The first line sets updown to be one 64th note. The second line sets updown to be an 8th note. The last line turns the updown effect off. disable_volume_envelope ----------------------- This disables the envelope control of the volume. This could be used to make drones but the real intent is to pipe one render_notes_5 process into another to double-filter the signal. This does not affect the sample playback part. enable_volume_envelope ----------------------- This (re)enables the envelope control of the volume. Issues ~~~~~~ Since I am not a publisher of periodicals, there are no issues. An issue is something you read. It usually comes out weekly or monthly. Your software might have a bug, you might express a concern, you may have a problem, but the only one who "has issues" is the publisher (or collector) of periodicals. Bugs ~~~~ The tempo/timing of the program will slowly drift. After about six minutes or so (depending on the number of notes played and the BPM), the timing drift may become noticeable when mixed alongside another file at the same BPM. The parsing is really crude. Syntax errors can sometimes go unnoticed and can be difficult to trace. Filter feedback tends to make the sound much louder. Whenever using feedback, reduce the filter_volume. A rough rule of thumb is to cut the volume in half and then see if it still distorts. For a feedback of 3, 0.3 or 0.25 seems to be a "safe" filter_volume.