When doing audio processing in Forth systems we were dependent on a working audio handler made available by the Forth vendor or had a lot of work to do.
Modern sound systems regularly make use of a callback driven model, ALSA for linux is just one example. But ALSA is not portable and doesn't allow connecting applications together in a sound-flow environment. One popular system allowing this is the 'jack-audio-connection-kit' JACK. It is available for all major platforms, Linux, OSX. Solaris and the Windows world. Jack makes use of the available hardware driver in a real time process. There are many audio applications around using the JACK interface and Forth can now connect to all of them.
fJACK is written in Forth and uses some of the advanced techniques like threading, binding of external libraries and callbacks. So it is not ANS Forth. The good news: it's available for iForth (Linux32/64, Windows, OSX32/64) and VFX Forth (Windows and Linux).
First you must install a JACK system and get it running. Read the HOWTO in the net and specialized audio groups to find out how you can get the most out of it. Getting JACK running with low latency and no drops/xruns is sometimes a bit difficult. Look out for hardware that is well supported for your environment. If available use a real-time-kernel, even with a modern general kernel and optimized settings in security.conf you will have much larger latencies or dropouts.
This is a good place to find jack: http://jackaudio.org/download or go for compiled packages for your system.
Currently there is a change in the jack system, either the 'old' jackd which has proven to be stable and should be available for all linux distributions. Please get at least the 0.116.1 version - the jackdmp based jack2 like 1.9.4 is also fine.
For windows and OSX systems there is the new jackdmp 1.9.4 available. Setting this up including the portaudio or coreaudio driver is not covered here but all compiled binaries come with a setup utility.
fJACK works fine with both systems. You should update to either jack >=0.116.1 or jackmp 1.9.4. Earlier versions like jack 0.109 or jackdmp 0.7.1 are much more likely to leave you worried especially when you work in a mixed 64/32bit environment.. All suggested versions have the ability to auto-start a jack demon when a client wants to register and no running demon is found. As the library knows much better how to start a default demon this feature is used in fJACK - the 'KICKSTART' method in ijack is not used as default any more. I guess this is more flexible as the user can choose between three options:
All recent fJACK versions use the fftw3 library to define the filter response of FIR filters so you really should install it even when you don't need the signal analysis fft plugin. BAND-PASS BAND-STOP and USER-FIR filters with a user-specified frequency response and the EQUALIZER will not work without it. Calculating the filter parameters is not a real performance problem. But doing signal analysis on large amount of data or with sizes <> 2^n depends on a 'calibrated' fftw3 system. Currently creating fftw3 plans uses #FFTW_ESTIMATE because in fjack only 2^n sizes are used and then there not much of a difference. See fftw3lib.frt and fftw3.frt
fJACK 0.122 or later uses optimized sse2 code filter kernels on all platforms. This is done by including the code generated on iForth systems via very simple binary code snippets. These are included at compile time depending on the used architecture and are also used in turnkeys. So for vfx this means the same performance as iforth systems.
For iForth this also means the assembler is only needed when you want to change the optimized code snippets.
The fjack client tests the running cpu, if your machine doesn't offer sse2 code there is a fallback to forth code which gives roughly half performance in all FIR filters. This test just uses the cpuid instruction, all supported operating systems make sure, the xmm registers are safe with threading.
The jack2 future will probably bring even better support for multi-core cpus, fjack is ready for that.
Feel free to ask for help at hanno@schwalm-bremen.de
Whenever you plan to use fJACK in commercial applications read the Licence.txt and email me.
History
The fJACK system sources are the fjack subdirectory containing the client and extension modules sources plus the 'loader' fjack.frt. This directory also holds the default connection files in the connections directory and some binary files in the sysfiles directory. These subdirectories should be made writable.
All iForth versions (exept Windows64) are supported. The fJACK sources should be in - or go to - the include directory. IN fjack will do. The iForth fJACK system uses an environment variable FJACKCONNECTIONS, it can be set to a directory of your choice.
The fJACK sources should be in - or go to - the Examples directory. You should cd to this directory, then INCLUDE fjack.frt will do.
loading as above starts the jack sound demon when it is not running already, compiles the basic Forth interface to jack, defines a JACK client with some ports and connects these ports.
In the shipped setting there is also a little demo also loaded. Later you probably won't use it, switch it off in
PLAY-SNIPPETS starts playing snippets, sample-rate is changed by random- press key to stop.
ON-SINUS OFF-SINUS test the sinus oscillator.
ON-FIR OFF-FIR test an example highpass FIR filter.
ON-ADP OFF-ADP test an example adaptive FIR filter.
ON-IIR OFF-IIR test an example IIR lowpass filter.
ON-DELAY OFF-DELAY test an example IIR lowpass filter.
ON-NOTCHES OFF-NOTCHES test a bunch of IIR notch filters.
ON-VIB OFF-VIB tests a vibrato effect.
ON-MIXER OFF-MIXER tests the mixing to channel 2.
ON-PHASER OFF-PHASER tests a phaser plugin
Hurray and OOOOH give some sound.
fJACK can also be used in turnkey applications, all plugin object structs and the client data are in permanent memory space. When compiling your fJACK based application the client is initialised and started.
Turnkey applications all start the client on their own, you just have to put your application 'xt' in the boot chain.
' application AtCold
Stopping the client and system housekeeping is done in the AtExit chain.
\ MARKER -fjack
The ANS MARKER -fjack can be safely used to remove fJACK from your forth system.
It's supported on all platforms.
VOCABULARY JACKAUDIO
The JACKAUDIO vocabulary holds internals of the fJACK system, it's words could be changed
in next releases and should be used in end-user applications with great care.
: .JACK-RELEASE ." v1.01 (2010-05-13)" ;
First a very short processing overview
fJACK does real-time processing on a user defined number of audio channels (ports) JACK-CHANNELS, it controls and is controlled by a jack demon. You can connect any other jack-ready application via these ports with fJACK. To allow audio data processing you can define audio plugins and put them in any of the slots. So the basic overview is like this
Please recognize that the processing callback has changed in version 0.115, now every processing plugin has it's history buffer. Later you will find out, that every plugin can indeed read data from any history buffer. This new sheme allows a lot more features for sound effects and mixing.
Following are some system wide parameters defined in fjack/config.frt that could be changed by You!
TRUE VALUE ALSO-EXAMPLES?
Include the examples code when loading fjack?
4 CONSTANT JACK-CHANNELS
It can be set to any number >= 1 you like.
The default is 4, when you have a powerful computer and wish to use more audio channels,
just set this to 8 or whatever you need. Each channel has one input and one output port defined.
These ports are used for the audio processing plugins described later.
There is also a programmers interface to be used by you to define your own sound processing tools.
Also think about using other jack plugins available in the net to be used by your forth application.
Just one hint - more channels than needed will use a lot of memory and slow down the system a bit.
#32 CONSTANT JACK-IO-SLOTS
Defines the maximum number of processing plugins that can be placed in the Input-Port->Output-Port
processing chain for a channel. Also see #PLUGINS
#32 CONSTANT #PLUGINS
The maximum number of plugins used at the same time in the input->output processing chain.
FALSE VALUE #JACK-VERBOSE
jack demon information and error messages are redirected to the fjack client, you may switch off the information writing.
to the console by setting this to false.
DEFER KICKSTART-HOOK ( -- ) ' NOOP IS KICKSTART-HOOK
Sometimes - in probably not well configured or old jack systems - the auto-starting feature
of the libjack function jack_client_open won't work. This hook is executed when
no running JACK server is found just before starting th client.
There is one example how to do it added here.
I really strongly suggest not using this feature, all supported platforms offer a nice jack control software that can be started automatically and offers nice configure options.
#2048 CONSTANT #FIR_BUFF
This defines the maximum number of taps in adaptive and fir filters; every defined FIR filter object uses 5*#FIR_BUFF DFLOATS
dynamic memory. Must be a power of 2 and at least 4. This is quite a good default as normal fir filters won't need larger buffers.
When using special effect plugins using the generic fir filter routines you might need larger buffers.
\ %0000 CONSTANT PLAY-CLICK \ 0: start-of-wav-block 1: end-of-wav block 2: start-of-bunch 3:end-of-bunch
This is an optional compile-time bitmask CONSTANT used for debugging , when defined and one of the lowest bits 0-3 is set 'clicks'
are added to the played audio signal, these clicks can be analysed - and heard - on a recorded file.
\ %1111 CONSTANT READ-CLICK \ 0: start-of-wav-block 1: end-of-wav block 2: start-of-bunch 3:end-of-bunch
This is an another optional compile-time bitmask CONSTANT, when defined and one of the lowest bits 0-3 is set 'clicks'
are added to the recorded audio signal.
#32 CONSTANT #jack-fifos \ MUST be a power of 2
Gapless wav playing and writing is controlled via a FIFOs with #jack-fifos elements.
Only in very rare occasions there is a need to change this. If
an applications writes small chunks of wav data at very high speed you could increase this.
More information is available in the wav handling chapter.
$800 CONSTANT #max-frame-size \ MUST be a power of 2
The maximum frame size suported. Only with very slow hardware you will want larger buffers as
the latency gets higher. #1024 is the default for the current jack versions so it must be at least
$400.
$20000 CONSTANT #HISTORY-SIZE \ must be a power of 2 and at least 4 * #max-frame-size
fJACK may want access to data played some time before for delaying or data analysis so data
are saved in this circular buffer. This history is available in every plugin.
FALSE VALUE AUTO-HISTORY-CLEAR?
Whenever a plugin object is put into the processing chain the history might be cleared automatically.
I don't think it's worth to set this to TRUE but it's your decision.
0 CONSTANT DEFAULT-FIR-WINDOW
The coefficients of FIR filters are calculated by a windowed inverse FFT, this windowing function can 0,1 or 2.
See SET-FIR-WINDOWING for more information.
FALSE CONSTANT EXTENDED-JACK-INFO IMMEDIATE
For debugging sessions you might get more information with setting this flag to TRUE.
TRUE VALUE OPTIMIZED-FILTER-CODE?
You might prohibit usage of optimized sse2 filter kernels by setting this to false; mainly used for debugging.
TRUE CONSTANT FJACK-LOGIN-AUDIO IMMEDIATE
You can disable the start/stop sound by setting this flag to FALSE
Datatype definitions might change in future releases, the way these definitions are used will not. Currently the object-makers all return object-pointers or are defined as CONSTANTs.
: -->DSP ( object -- )
makes the plugin object the 'current object', this is done in a USER variable so that the object
is local to a thread. After doing so all the elements of this object can be used with the TO +TO 0TO methods.
: @DSP ( -- object )
This is used to get the currently selected plugin object.
: -->FIFO ( object -- )
make the fifo object the current.
: @FIFO ( -- object )
Get the current fifo object.
: CHAN-VALUE ( addr -- addr') \ name
Define a VALUE 'name' per channel; the runtime stack is (channel -- ?? )
: CHAN-FVALUE ( addr -- addr') \ name
Define a FVALUE 'name' per channel; the runtime stack is (channel -- ?? )
: DSP-VALUE ( n -- n' ) \ name
Define a VALUE 'name' the for a plugin object. When using 'name' the
object must be made valie before with -->DSP.
: DSP-DVALUE ( n -- n' ) \ name
Define a DVALUE 'name' the for a plugin object. When using 'name' the
object must be made valie before with -->DSP.
: DSP-FVALUE ( n -- n' ) \ name
Define a FVALUE 'name' for a plugin object. When using 'name' the
object must have been made valid before with -->DSP.
: FIFO-VALUE ( n -- n' )
The wav playing and reading is done via fifo structs, these structs have named elements.
When using 'name' the
object must have been made valid before with -->FIFO.
: IIR-FVALUE ( n -- n' ) \ name
Only used by the iir plugin;
1e0 FVALUE PLAY-WAV-LEVEL
The level of the WAV data played common for all channels. The default of 1.0 is ok in most situations, when you
have wav files that are a) 8bit-coded b) on almost full level and c) need resampling you might set this to a smaller value
like 0.9 to avoid distortions.
1e0 FVALUE READ-WAV-LEVEL
The level of the recorded=read WAV data common for all channels.
1e0 FVALUE JACK-IO-LEVEL
This level is applied to data read at the InputPort before the Input->Output processing is done,
it's common for all channels. The leveler plugin uses it as an example.
#10000 VALUE JACK-SAMPLE-RATE ( -- n )
The sample rate the JACK demon is running. It can be changed by other applications and the fJACK client is
told about these changes via a callback.
#1024 VALUE JACK-CHKSIZE ( -- n )
Number of samples in a process frames callback as returned by the JACK demon.
0 VALUE JACK-XRUNS ( -- n )
Counter for the xrun callbacks
0 VALUE fJACK-ID ( -- )
The client id returned by the JACK library call when registering a client is kept here.
DEFER MIDI-PROCESS-HOOK ( -- ) ' NOOP IS MIDI-PROCESS-HOOK
Done by the midi handler once per PROCESS-FRAMES callback.
DEFER GRAPH-REORDER-HOOK ( -- ) ' NOOP IS GRAPH-REORDER-HOOK
Called by the reorder callback; you can define a function watching the jack environment, for example
you could make sure about connections.
DEFER PORT-CONNECT-HOOK ( a b flag -- ) ' 3DROP IS PORT-CONNECT-HOOK
Called be the port-connect callback; you can define a function that looks for connections.
This function must take the flag and both port id's from the stack
0 VALUE #FJACK-SAMPLE
In the input->output processing chain you can use this value as the sample number
: OBJECT-HISTORY ( plugin-object|channel -- addr )
To use @HISTORY-DATA, MOVE-HISTORY-DATA, @IDX-HISTORY-DATA, RECONNECT-PLUGIN
or @INTERPOLATED-HISTORY-DATA
you need the history buffers base address, this calculates it for you.
The parameter can be either the channel getting the inport history buffer or the plugin object, then
the plugins private history buffers is returned.
As the history holds sfloat data there is a very small precision loss compared to pre-0.115 versions.
There are always JACK-CHANNELS history buffers for the input ports and #PLUGINS buffers for the active plugin objects allocated. The size of each history buffers is #HISTORY-SIZE SFLOATS. See for the config.frt file to change the default values. Each history holds several groups of JACK-CHKSIZE SFLOATS segments
: *N-HISTORY ( history idx -- addr ) #HISTORY-SIZE 1- AND SFLOATS + ;
Calculates the address inthe history buffer
: @IDX-HISTORY-DATA ( history idx -- ) ( F -- data ) *N-HISTORY SF@ ;
Reads the audio data in the history buffer at position idx.
: @HISTORY-DATA ( history offset -- ) ( F -- data ) #FJACK-SAMPLE SWAP - *N-HISTORY SF@ ;
Reads the audio data in the history buffer relative to the current position in the audio stream.
: MOVE-HISTORY-DATA ( historybuffer startoffset size target-addr -- )
Copies size samples of audio data from the history buffer to the target buffer. The offset marks the end of data.
: @INTERPOLATED-HISTORY-DATA \ ( historybuffer -- ) (F time-offset -- data )
You may read the interpolated audio data from the history buffer by this,
the time-offset is measured in seconds. a zero time offset always references
to the currently processed sample in the input->output processing chain
so it is only valid there.
: JACK-TIME? ( -- d )
The time of the JACK demon is always 64bit, so for portability it's a double in Forth.
The time is derived from the high resolution timer.
: JACK-UNIQUE ( -- d )
This is a unique double telling about the position in JACKs processing queue. See for more details
in the libjack sources or API documentation, not much use for this in most situations.
: JACK-POSITION ( -- u32 )
The position in the audio data stream, it's an unsigned 32bit integer.
: JACK-LOAD ( -- n )
The JACK demon measures the time spent in the clients callback by using the high-resolution-timer
and calculates the 'load' as it also knows the time between callbacks. The value n is the load in percent.
: JACK-RUNNING? ( -- flag )
To be used by the user application testing both the client id and the state given by
the callback.
: START-JACK-TRANSPORT ( -- )
As the name says the JACK demon transport is started.
: STOP-JACK-TRANSPORT ( -- )
As the name says the JACK demon transport is stopped.
0 VALUE WAV-PLAY-MODE
When playing wav data the audio samples are inserted in the input->output procesing stream.
This can be before or after the signal processing is done. WAV-PLAY-MODE is used to switch
between these modes , n=0 means before processing.
1 VALUE WAV-READ-MODE
When recording wav data from the input->output processing stream the samples can be fetched from
before or after are signal processing.
WAV-READ-MODE is used to switch between these modes , n=0 means before processing.
So wav-playing and recording uses history buffers selected by the values of WAV-PLAY-MODE and WAV-READ-MODE. In this graphics using a value of 0 uses the green buffers, red buffers otherwise.
: CALC-WAV-RATE ( srate -- true-sample-rate )
The sample rate conversion when playing or recording wav data uses a rather simple linear
approximation algorithm, the used 'real' sample rates can be any truerate = n * samplerate / JACK-CHKSIZE.
This ensures a small distortion and no glitches are in recorded or played data.
Another point is: there are better buffersizes than others!
When you want to use resampling, CALC-WAV-RATE tells the true sample rate
calculated from the desired one.
: CALC-WAV-BLOCK ( srate channels mode -- n )
As told at CALC-WAV-RATE there are 'better buffersizes than others'.
CALC-WAV-BLOCK gives a good suggestion what the block size should be
or you better take a multiple of this size.
: PLAY-WAV ( addr size rate channels mode -- id ?error )
Plays WAV data available at an addr and a size. These data are not played immediately
but are inserted into a FIFO 'slot' and will be played after all before playing commands have
finished.
When size is negative the FIFO is cleared before inserting this block into the FIFO so playing
will start at once. More precise - it will start when the next PROCESS-FRAMES callback is taken.
A negative channel number results in reversed channels when listening, playing wav files with
only one channel will be played as stereo.
Thew rate can be freely choosen, the mode can be any of the 8bit/16bit/32bit/32bit-float
modes supported here. Named constants for these are defined in JACKAUDIO
these are defined STEREO MONO 8BIT-AUDIO 16BIT-AUDIO 32BIT-AUDIO SFLOAT-AUDIO.
The 'id' can be used to test a block of wav data in the FIFO. See: WAV-PLAYING? STOP-PLAY-WAV, ?error tells about the status
: READ-WAV ( addr size rate channels mode -- id ?error )
READ-WAV works as PLAY-WAV exept it reads the data from the audio stream and
puts them into the buffer at addr. More info at PLAY-WAV
: STOP-PLAY-WAV ( ms id -- )
The data that had earlier been put into the play FIFO and had returned 'id' will stop
: STOP-READ-WAV ( ms id -- )
The data that had earlier been put into the read FIFO and had returned 'id' will stop
: WAV-PLAYING? ( id -- val )
Checks the status of the played wav block 'id'.
'val' is the remaining time until this block is played completely or zero when the fifo block is unused.
A typical piece of code code be
( id ) BEGIN DUP WAV-PLAYING? DUP MS 0= UNTIL DROP
: WAV-READING? ( id -- val )
Checks the status of the recorded wav block 'id'.
'val' is the remaining time until this block is recorded completely or zero when the fifo block is unused. See WAV-PLAYING?
These functions are not to be used in user applications. They are functions to be called by the callbacks defined for this client. They could be used by testing tools to measure the exact performance of the io system but so far no such tools exist.
: XT-PROCESS-FRAMES ( frames parameter -- 0 )
The main processing callback; must return 0.
When initialising the client the jack demon starts a realtime thread, this thread later calls this callback and
MUST NEVER use any forth word that might add some sort of delay longer than a few ms. Otherwise the client might be terminated
or lots of xruns are signalled. So No semaphores! No disk-io! No terminal-io!
The XT-PROCESS-FRAMES does all the signal processing defined by you in the input->output processing slots. When you make your own plugins remember the above!
: XT-GRAPH-REORDER ( 0 -- 0 )
Whenever the jack demons processing graph is changed this callback is executed. This happens when ports are disconnected
or a new jack application is starting. The user-defined GRAPH-REORDER-HOOK ( -- ) is executed in this callback to allow you
to watch the surrounding.
: XT-PORT-CONNECT ( a b parameter flag 0 -- 0 )
Whenever a port is connected or reconnected this callback is executed. You can find the fJACK ports with
>inport ( idx -- id ) and >outport ( idx -- id), both words are in JACKAUDIO.
idx is the channel, the returned id is unique and given by the jack demon. I won't give any details here
how to use these id's or how to use the PORT-CONNECT-HOOK - go and read the jack manual, you will
need deeper informations to make use of it.
Prototype for the client supplied function that is called whenever a client is registered or unregistered.
a: one of two ports connected or disconnected, b: one of two ports connected or disconnected
flag non-zero if ports were connected zero if ports were disconnected
The user defined function is executed via the PORT-CONNECT-HOOK ( a b flag -- )
: XT-NEW-FRAMES ( fr parameter -- 0 )
When some other client changes the chunksize this is told here. So far i don't know ANY client that does so
: XT-NEW-SAMPLERATE ( sr parameter -- FALSE )
The callback when other clients change the sampling rate for the jackd. See JACK-SAMPLE-RATE
: XT-CLIENT-STOPPED ( parameter -- 0 )
A client that is forced by some other client to shut down tells all it's registered clients
about the new situation.
: XT-COUNT-XRUNS ( parameter -- 0 )
When the JACK demon gets an interrupt by the audio master clock to do new processing and
the queue of callbacks of the last interrupt has not been fully processed, this condition
is noted to all clients - it's called a 'XRUN'. See JACK-XRUNS
: XT-NEW-TRANSPORT ( s p parameter -- f )
A Repositioning callback, so far the position is not used.
: XT-SERVER-ERROR ( str -- str )
The callback function to print a jackd error message.
: XT-SERVER-INFO ( str -- str )
The callback to print a jackd information message.
There are some graphical tools around to connect ports between the different JACK clients or the playback or capture ports. You may use these nice tools to connect any ports but how can you save and restore your configuration?
: SAVE-CONNECTIONS ( addr cnt -- )
writes a file specified by the name 'addr cnt' holding information about all ports connected to your fJACK client.
This file can later be used with LOAD-CONNECTIONS and CLEAR-CONNECTIONS.
Actually the file is a usual forth source.
Please make sure the directory fjack/connections has writing enabled for you.
: CONNECT-JACKPORTS ( -- ) \ source destination
Connects two ports, the portname are read from the input stream.
: UNCONNECT-JACKPORTS ( -- ) \ source destination
Disconnects two ports, the portname are read from the input stream.
: CLEAR-CONNECTIONS ( -- )
Unconnects all ports of the fJACK client.
: LOAD-CONNECTIONS ( addr len -- )
Connects all connections specified in the file.
: UNLOAD-CONNECTIONS ( addr len -- )
Disconnects all connections specified in the file.
: JACK-INFO ( -- )
Print information about the fJACK client. You may extend the display information by configuring
EXTENDED-JACK-INFO to true. This is probably only usefull in debugging sessions.
: .JACK-VERSION ( -- )
prints the current fJACK description
: JACK-PORTS ( -- )
Prints information about all connections to the fJACK client.
: JACK-SERVER-RUNNING? ( -- flag )
flag is TRUE when a running jack server is available
: START-JACK-CLIENT ( -- )
Starts the fJACK client - this is done automatically when fjack is compiled and also via the AtCold hook.
If the clients name is not unique - there could be
another fJACK client running already - the jack demon might choose another name like client-01.
This is totally OK as all client handling is done via the client structure.
The port connections also take care about this. There is one good reason to do so:
Different applications can all use the default client fJACK and don't have to care about other names.
So you may start several Forth terminals and start a fJACK client, all these clients
can share the connection files - the true client name is just hidden.
: STOP-JACK-CLIENT ( -- )
Stops the fJACK client running.
: STREAM-EXTERN-FRAMES ( rate channels #frames in-xt out-xt -- left-#frames )
Whe the fJACK client is not running you may stream audio data through the input->output processing chain.
You have to tell the sample-rate, the number of channels and number of samples to be processed.
Also there is are no defined input and output mechanisms -- instead of using the data delivered and used by the jack demon you must define functions to deliver input data and do with the output data whatever you like.
The in-xt ( addr channel #frames -- ) must fill the buffer at addr with your audio data for the specified channel, it must deliver #frames SFLOATS.
The out-xt ( addr channel #frames -- ) must take the buffer at addr with the filtered audio data for the specified channel, it must use #frames SFLOATS.
STREAM-EXTERN-FRAMES returns the number of non-processed frames.
What can this be used for?
Let me give two examples - not implemented - for this. You could write an application that reads data from a wav file, does the desired processing and writes a new wav file. You could also use this mechanism to use the existing fjack framework for other hardware drivers.
Please note that this framework is in alpha state, i did only a few tests and it works, if you use it and and run into trouble, let me know and i'll fix it.
Some constants - in JACKAUDIO - for better readability
2 CONSTANT STEREO
1 CONSTANT MONO
8 CONSTANT 8BIT-AUDIO
#16 CONSTANT 16BIT-AUDIO
#32 CONSTANT 32BIT-AUDIO
#-32 CONSTANT SFLOAT-AUDIO
1e0 FVALUE SPEEDUP \ Allow the caller to define pitching using fJACK resampling
Allow the caller to define pitching in playing snippets and wav filesusing fJACK resampling.
As playing and recording of complete .wav files is done in a background thread is it sometimes conveniant to have a hook when the recording/playing is actually started or stopped in the thread. The windows vfx RichEdit device is not very stable in callbacks or threads, i suggest you should not write anything to the console. Especially $0A EMIT and CR make severe problems.
DEFER BEGIN-OF-PLAYING ' NOOP IS BEGIN-OF-PLAYING DEFER END-OF-PLAYING ' NOOP IS END-OF-PLAYING DEFER BEGIN-OF-RECORDING ' NOOP IS BEGIN-OF-RECORDING DEFER END-OF-RECORDING ' NOOP IS END-OF-RECORDING
: .WAV ( c-addr u -- )
Plays the complete file defined by the parameters in a background thread.
: .SILENTWAV ( c-addr u -- )
Plays the complete file defined by the parameters in a background thread, if problems are
detected no messages are printed.
: .SOUND ( samplerate channels 8/16/32/-32 c-addr u -- )
Play any file with specified parameters.
: STOP-PLAYING ( -- )
Any file already playing can be stopped by this command.
: REPOSITION-PLAY-FILE ( position relative-flag -- )
The played wav file may be repositioned during playing. If relative-flag is TRUE
the position is relative to the current position, otherwise it's the absolute position.
The 'position' always means a sample position, so you don't have to worry about channels and mode.
: PLAY-FILE-POSITION ( -- position|-1 )
This tells you the current position of the played wav file, it's also a sample postion
so don't care about channels and mode. If the position is negative, there is no file played at the moment.
: START-RECORDING ( time samplerate channels mode name namelen -- )
Starts recording a .wav file with a given file name. Also the audio
parameters must be specified. time is in seconds.
: STOP-RECORDING ( -- )
Stops .wav file recording.
: RECORD-WAV ( time name len -- )
Records a .wav file with the duration of time seconds and the given file name.
audio parameters are JACK-SAMPLE-RATE STEREO 16BIT-AUDIO
: RECORD-SIMPLE ( time name len -- )
Records a .wav file with the duration of time seconds and the given file name.
audio parameters are 16000 MONO 8BIT-AUDIO
: RECORD-FLOATS ( time name len -- )
Records a .wav file with the duration of time seconds and the given file name.
audio parameters are JACK-SAMPLE-RATE STEREO SFLOAT-AUDIO
: WAV-PLAYING-ON? ( -- flag )
flag is true when fJACK is already playing a .wav file.
: WAV-RECORDING-ON? ( -- flag )
flag is true when fJACK is already recording a .wav file.
: SNIPPET ( c-addr u -- ) \ name
Expects a filename on stack and creates a word 'name'. The file must be a wavfile, it's
read into allocated memory.
The runtime behaviour of the defined word is: play the audio data immediatly via the fJACK client.
Snippets are defined in a linked list; audio data are also available in turnkey applications as they are reloaded in the cold-chain.
This library can be tuned for optimized performance, this implementation currently supports the import of system wisdom files - see the fftw3 docs how to generate such a wisdom file.
Without such wisdom the best algorithm is just estimated, you can change this to one of the other methods in fftw3lib.frt.
: CHECK-FFTW-SIZE ( n -- ok-mask ) \ sizes in fftw may be multiples of primes 2-13
Checks the desired size of a fftw3 plan. Principally all sizes are suported but many ready-compiled
libraries only support a limited number range. For all
n = 2*a * 3^b * 5^c * 7^d * 11^e * 13^f
the fftw3 library has algorithms available. The 'ok' is a bit-mask about a-f being >= 1, for probably impossible sizes %000000 is returned
%000001-a, %000010-b, %000100-c, %001000-d, %010000-e, %100000-f
%000001 has best algorithms
%001110 still is good
%110000 has existing algorithms in probably all compiled libraries but will be slower
: WINDOWING-OFF ( struct )
The input parameters for the signal analysis may be 'windowed', this switches windowing off.
: HAMMING-WINDOW ( struct )
The input parameters for the signal analysis may be 'windowed', this switches to 'hamming'
: HANN-WINDOW ( struct )
The input parameters for the signal analysis may be 'windowed', this switches to 'hann'
: FFT-STRUCT ( n -- struct )
When you want to to signal analysis using the fftw3 library you must define a forth struct that
defines the interface to the fftw3 library. 'n' is the number of samples to be analysed, FFT-STRUCT
defines the struct and returns a pointer to it. This struct pointer will be used by the next words.
Internally there are arrays for real parameter data and complex results at place, a fftw-plan is calculated. See CHECK-FFTW-SIZE for a discussion about possible sizes.
: INVERSE-FFT-STRUCT ( n -- struct )
When you want to to signal analysis using the fftw3 library you must define a forth struct that
defines the interface to the fftw3 library. 'n' is the number of samples to be analysed, INVERSE-FFT-STRUCT
defines the struct and returns a pointer to it. This struct pointer will be used by the next words.
Internally there are arrays for real parameter data and complex results at place, a fftw-plan is calculated. See CHECK-FFTW-SIZE for a discussion about possible sizes.
: #FFT-SIZE ( struct -- #elements )
tells the number of samples for this struct.
: >FFT-COMPLEXDATA ( struct idx -- addr )
Calculates the address in a FFT-STRUCT result complex datastructure or INVERSE-FFT-STRUCT
input parameter complex datastructure.
All FFT data are of DFLOAT type and so must be accessed with DF@ DF! and friends. In complex number arrays the real-part is always at the lower address.
: >FFT-DATA ( struct idx -- addr )
Calculates the address in a FFT-STRUCT input parameter datastructure or INVERSE-FFT-STRUCT
result datastructure.
: CALCULATE-FFT ( struct )
After storing the input data into the FFT-STRUCT parameter array you can do the signal analysis with CALCULATE-FFT.
: CALCULATE-INVERSE-FFT ( struct )
After storing the input data into the INVERSE-FFT-STRUCT complex parameter array you can do the signal
You know that you can put audio processing plugin objects in any one of the slots, this module takes care of it. The order of processing in the jack-callback is like this:
In effect all plugins can assume, that data in a lower slot number have been completely processed already.
A plugin object is a data struct, some parts of this struct are the same in all plugin objects - see the client.frt source for details. These public parts are used by the plugin manager to take care about safety, setting parameters like frequency, quality and more.
Also the structs are 'connected' to other plugin objects or the input/output jack ports via plug_input and plug_output. basically a plugin object reads samples from the plug_input history buffer, does it's specific processing and writes data to the plug_input history buffer - in the graphics it's the red arrows. Also you may read data from other history buffers by words like @HISTORY-DATA and friends, see the green arrow and docs in the client manual.
The fjack system knows some predefined plugin object types, when you write other plugins use numbers above #256 for your private applications or let me know to include them in here.
$01 CONSTANT LOW-PASS $02 CONSTANT HIGH-PASS $03 CONSTANT BAND-PASS $04 CONSTANT BAND-STOP $05 CONSTANT ADAPTIVE $06 CONSTANT DECODER $07 CONSTANT SIN-TONE $08 CONSTANT DELAYER $09 CONSTANT AUTOLEVEL $0A CONSTANT USER-FIR $0B CONSTANT MIXER-PLUGIN $0C CONSTANT LESLIE-PLUGIN $0D CONSTANT PHASER-PLUGIN $0E CONSTANT ECHO&HALL-PLUGIN $0F CONSTANT VIBRATO-PLUGIN $10 CONSTANT EQUALIZER $11 CONSTANT EFFECTS-FIR
\ CONSTANT #PRIVATE-DSP ( -- n )
All plugin objects have some data in common, the public part of the objects struct is used
by the plugin handler.
The common part of a plugin object holds predefined DSP-VALUEs, so when you are making a plugin object definer you must leave this public part in peace. So when defining the struct you should 'start with #PRIVATE-DSP.
: REORDER-PLUGINS ( -- )
It compresses the execution token list to a zero-terminated list for faster processing
in the input->output processing chain.
: LIST-PLUGINS ( -- )
prints all objects plugged into the process chain.
: LIST-ALL-PLUGINS
prints information about all defined plugins. You may extend the display information by configuring
EXTENDED-JACK-INFO to true. This is probably only usefull in debugging sessions.
: REMOVE-JACK-PLUGIN ( filterobject -- )
revoves the object from the process chain.
: MAKE&ALLOCATE-PLUGIN-OBJECT ( initialize-xt dynmemsize objectsize -- object )
Whenever you define a plugin object you MUST use this function to initialize it.
'size' is the objects size in bytes, the XT is the function to initialize the object.
Dynamic memory size is allocated and plug_dynmem is set.
This is done at compile time - so the object can be used at once - also this allows turnkey applications, as when booting all defined objects are initialized.
When developing your own plugins you can and definitly should use the automatic memory allocation, this is done before the initialize-xt is called.
: SET-PLUGIN-BYPASS ( flag filterobject -- )
You may set the objects bypass status. When the bypass flag is set - flag is TRUE, the plugin just passed the
audio data to it's history buffer but does no processing on the data. See comments in SET-JACK-PLUGIN.
: SET-JACK-PLUGIN ( filterobject slot channel -- )
Puts the object in the slot/channel. channel must be between 0 and JACK-CHANNELS-1,
slot must be between 0 and JACK-IO-SLOTS. If there are wrong parameters an abort
message is displayed.
If an object is already in use at that slot it is removed.
Please remember two things, at the moment you may only use #PLUGINS at the same time, the history buffers are allocated when starting fjack. SET-JACK-PLUGIN and REMOVE-JACK-PLUGIN change the processing order and clear the history buffers used. On slow machines the clearing might take some time resulting in audio drops when changing the plugin objects in use. To overcome this problems you could keep a plugin object in the processing chain and just toggle it's bypass status.
: RECONNECT-PLUGIN ( history-buffer object/output-port -- )
Due to the new input->output sheduler every plugin object or output port can get it's data
from another history buffer. This function can be used to connect at runtime.
See the example where the two dotted red lines show reconnected plugins, the green line shows
a reconnected output port. You can get the address of the history buffer by OBJECT-HISTORY.
: LOCATE-JACK-PLUGIN ( object -- slot channel true | false )
Gives information about the slot/channel for a specified object and a TRUE flag
when the object was installed or FALSE when not found.
: GET-PLUGIN-OBJECT ( slot channel -- 0|filterobject )
Gives the object found in slot/channel, when no object has been placed there with
SET-JACK-PLUGIN a FALSE flag is returned.
: SET-OBJ-FREQUENCY ( frequency object -- )
Many plugins have a specified tune-to-frequency function available, you can use this to do so.
This sets the frequency of the specified object, the object itself known what do do,
f.e. filters will be tuned to the specified frequency. A few object types have to frequencies,
BAND-PASS BAND-STOP filters expect two integers as a special case.
: SET-OBJ-FLOAT-FREQUENCY ( object -- ) ( F: frequency -- )
In many situations the precision of SET-OBJ-FREQUENCY is not good enough, then you can set
the frequency by this. BEWARE: ONLY ONE parameter possible as float!
: SET-OBJ-QUALITY ( quality object -- )
Many plugins have a quality - in notch filters this would be the sharpness in FIR and adaptive filters this will be the number
of taps. This functions handles the objects quality in a general way so look for the special plugin docs.
The effect of setting the quality can be viewed in the fft display plugin, here is a band filter
with a lowpass set to ~1.5kHz with 41 and 125 poles
: SET-OBJ-LEVEL \ ( object -- ) (F level -- )
All plugin objects have a specific level value this word sets it.
: SLOT/CHANNEL-HISTORY ( slot channel -- history )
Finds the history address of a slot/channel pair, if the slot is less than 0 it always means
the input port. If a slots object is undefined the result is 0.
: CALCULATE-FILTER-DELAY ( slot1 slot2 channel -- n )
Calculates the phase delay in the processing chain between 2 slots for a given channel,
the result is the phase shift measured in samples.
: OBJ-BENCHMARK ( object -- )
You can measure the performance of any filter plugin with this, define any sort of filter object and
pass the object to this tool.
: MAKE-FIR-FILTER ( filtype size -- object )
Constructs a linear-phase FIR filter object and leave it's address. 'type' can be LOW-PASS HIGH-PASS BAND-PASS BAND-STOP USER-FIR, the 'size'
is the number of taps corrected to odd.
FIR filters add a signal delay by half the number of taps.
You may select a different number of taps later via SET-OBJ-QUALITY ( taps object -- ), also you might change the frequency response of this filter by SET-FIR-TUNING.
BAND-PASS BAND-STOP USER-FIR filters and the SET-FIR-TUNING SET-FIR-WINDOWING SET-FIR-COEFF are only possible when the fftw3 library is installed.
: FIR-FILTER: ( type size -- ) \ name
Defines a named MAKE-FIR-FILTER plugin object, calling 'name' returns the object address.
: MAKE-EQUALIZER ( size -- object )
Define an linear phase equalizer object with size taps corrected to odd, it's a special type of the generic FIR filter.
It offers 256 frequency bands in the
0-JACK-SAMPLE-RATE/2 range, each band has it's own defined level. You can set these levels by
SET-EQUALIZER-BAND and SET-EQUALIZER-RANGE.
You may also select a different number of taps later via SET-OBJ-QUALITY ( taps object -- ).
For iForth users only: the showfft module now can directly control a bound equalizer filter, see SET-FFT-EQUALIZER in the gui/showfft module.
: EQUALIZER: ( size -- )
Defines a named MAKE-EQUALIZER plugin object, calling 'name' returns the object address.
: SET-EQUALIZER-BAND \ ( object -- ) (F dBlevel frequency -- )
Sets the level for the specified frequency in the equalizer object and calculates the filter coefficients.
: SET-EQUALIZER-RANGE \ ( object -- ) (F dBlevel1 frequency1 dBlevel2 frequency2 -- )
Sets a linear level curve between the level/frequency pairs in for the equalizer objects levels
and calculates the filter coefficients. At least one point is set.
: SET-EQUALIZER-MODE ( mode object -- )
Sets the approximation mode for SET-EQUALIZER-RANGE, it can be either 0 for linear or 1 for sinoid mode.
: EXPORT-EQUALIZER ( name len object -- )
Exports the equalizer setings to a file
: IMPORT-EQUALIZER ( name len object -- )
Imports the equalizer setings from a file
: SET-FIR-WINDOWING ( windowmode object -- )
Calculating the filter coefficents is done with a IFFT algorithm using a windowing function. This windowing
function can be set for each filter with SET-FIR-WINDOWING.
See the literature for descriptions of these windowing functions.
The default is set by DEFAULT-FIR-WINDOW.
The blackmann window 0 gives the deepest out-of-band suppression with a modest steepness.
The hamming window 1 has a steeper flank with less out of band suppression
The rectangle window 2 has the steepest flank, highest ripple and lowest out-of-band suppression.
: SET-FIR-TUNING ( 0|tuning-xt object -- )
The MAKE-FIR-FILTERs frequency response curve of each filter can be tuned to your needs by
adding a tuning-function-xt describing the desired freuency response. The function must have this stack effect:
(F level frequency -- your-level )
It must be able to return values for frequencies from 0 to sample-rate/2.
: SET-FIR-COEFF ( 0|set-coeff-xt object -- )
The USER-FIR MAKE-FIR-FILTER is a very special type of FIR filter, the dsp algorithm is the same but
there are many situations where you don't want to specify a frequency response of the filter but want to add effects
like hall, echo, delay, averagers and more.
To use this sort of filter you will need to think about the way the filter coefficients are calculated. Whenever the filter is tuned, resized or defined there is the default function that sets x[0] to 1.0e0, all others are zero. To define a filter according to your needs you will have to tell the coefficient-calculating-function execution token xt. The calculating function must take two integer parameters ( number-of-filtertaps index) and must return the float (F -- coefficient) for that tap. This automatically set the filter size, so don't use SET-OBJ-QUALITY after calculating. The maximum number of taps is #FIR_BUFF so when you need a larger range think of the generic effects plugin.
The graphics shows the signal flow, the number of taps is the number of multipliers.
: MAKE-ADAPTIVE-FILTER ( size -- object )
Constructs an adaptive filter object and leave it's address, the 'size'
is the number of taps corrected to even.
Adaptive filters add a signal delay by half the number of taps.
This filter uses a simple 'least mean square' algorithm.
You may select a different number of taps later via SET-OBJ-QUALITY ( taps object -- ).
: ADAPTIVE-FILTER: ( n -- ) \ name
Defines a named adaptive plugin object, calling 'name' returns the object address.
: SET-FREEZE-ADAPTIVE ( flag object -- )
You can freeze the adaptive filter coefficients - this is usefull when you have 'tuned' to a noise signal
and want to stay with this filter. FALSE will continue adapting otherwise it will be frozen.
As changing the filter qaulity means other coefficients SET-OBJ-QUALITY for an adaptive filter
always unfreezes it.
: SET-ADAPTIVE-SPEED ( n object -- )
Sets the speed of the adapting algorithm, 0-100 are accepted.
: MAKE-IIR-FILTER ( type -- filter)
Constructs a 10pole IIR-FILTER (Butterworth) plugin object and leaves the address of the object.
The freqency of this filter can be set as usual with SET-OBJ-FREQUENCY
the quality (or sharpness) of the filter with SET-OBJ-QUALITY
: IIR-FILTER: ( type -- ) \ name
Defines a named iir-filter plugin object, calling 'name' returns the object address.
Quite often you will have to do a signal analysis of your audio signals somewhere in the input->output processing chain. You can define such an analyser plugin with MAKE-FFT-PORT and can always do an ad-hoc real time analysis of the signal.
This plugin does an fft analysis, avaraging of the spectrum, finds maxima and the average level in a defined frequency range. In the background this uses the fftw3 library.
: FFT-PORT-SIZE ( fftport -- size )
Tells the size of the port.
: FFT-HAMMING ( mode fftport )
Changes the 'windowing' function of the analyser port; 0 means no windowing, 1 is hamming and 2 hann.
: FFT-ANARANGE ( low-frequency high-frequency fftport -- )
Sets the frequency range in which the average signal level is calculated.
: FFT-GET-ANARANGE ( fftport -- low-frequency-index high-frequency-index )
Gets the frequency range for average analysis.
: MAKE-FFT-PORT ( size -- fftport )
Define a fft-port, this can be used as a plugin in the processing chain. For the iForth system
there is a fft viewing module available FFT-VIEWPORT: that works perfectly with fft-ports.
: FFT-PORT: \ ( size -- ) name
Defines a named fft-port.
: FFT-ANALYSIS ( fftport -- )
Does an fft analysis of the last audio data in the stream plus avaraging and maxima finding.
: @FFT-dB-LAST \ ( fftport idx -- ) (F -- level-dB )
gets the average level for the last 8 FFT-ANALYSIS for this port.
: @FFT-dB-AV \ ( fftport idx -- ) (F -- average-dB )
gets the average level for the last 8 FFT-ANALYSIS for this port.
: FFT-MAXIMUM? \ ( fftport idx -- flag )
returns TRUE when a maximum is detected at this point.
: FFT-GREATEST-MAXIMUM \ ( fftport -- index ) (F -- level )
Finds the greatest maximum for the port and returns the index.
: FIND-AVERAGE-LEVEL \ ( fftport -- ) (F -- avlevel )
returns the average signal level in the specified frequency range measured in dB
: FFT-IDX>FREQU \ ( index fftport -- frequency )
Converts index for this port to frequency.
: FFT-FREQU>IDX \ ( frequency fftport -- idx fftport )
Converts frequency for this port to index, the port kept on the stack as this is most often used later.
: SET-FFT-DELAY ( delay fftport -- )
The fft analyser allows a delay in the history to compensate in fft displays.
Allows mixing and processing of audio data.
: MAKE-MIXER ( -- object )
Construct a mixer plugin object and leave it's address.
The value in the processing stream is calculated by the presented value plus
all other slots/channels that have been added to the mixing by SET-MIXER-LEVEL.
SET-OBJ-LEVEL sets the level of the original source of this plugin.
The standard way to mix the audio data is just adding all values. There is a special feature here to do some more tricks on the data, you may replace the 'adding' function by some other word with SET-MIXER-ACTION.
: MIXER:
Creates a named mixer plugin.
: SET-MIXER-INPUT \ ( slot channel mixer -- ) (F level delay -- )
Sets the level and delaytime (seconds) in the mixer.
A slot-number <0 means the input port. You can only mix data from smaller slots
than the mixer objects slot. The graph shows the mixer inputs
: SET-MIXER-ACTION ( action-xt object -- )
This replaces the default 'adding' function of a mixer plugin object by another one.
The stack effect of the new function must be
: my-function ( array cnt -- ) (F -- value )
The array holds cnt sfloats of input data read from all the activated mixer channels, you can do whatever you want to do with these data but you MUST take the parameters and MUST leave the floating result. All data have already been changed to the desired level, the first position in the array holds the original input stream to this slot.
: .MIXER-INFO ( object -- )
Show the status of the mixer object; level is absolute; delay measured in seconds.
Allows the user defined definitions of effects. The generic effect plugin is just a special type of FIR filter, the dsp algorithm is the same but the coefficients are not calculated from a defined frequency response. So the SET-OBJECT-FREQUENCY tool is undefined, instead it uses a callback function defining the coefficient for each tap number.
: DEFINE-EFFECT-FILTER ( calc-xt|0 object -- )
calculates the effect filter coefficients for a given plugin object with the calculating function xt.
The calculating function must take two integer parameters ( number-of-filtertaps index -- )
and must return the float (F -- coefficient) value for the index tap.
When DEFINE-EFFECT-FILTER is executed, it calls the function for every tap from 0 to {#HISTORY-SIZE},
so the default setting will allow processing in the ~3 seconds range for CD sampling rate.
When the xt is FALSE the effects filter is 'just-do-nothing'.
: MAKE-EFFECT-FILTER ( -- object )
Constructs an effects plugin object leaves it's address. Calculation of the filter coefficients is done
with DEFINE-EFFECT-FILTER.
This word is meant for user extensions like hall and echo effects, combs ... let's see what we will make out of it.
Some working plugins found in fjack/examples are here for you to start. All the plugins defined here and the test suite are NOT included in the default fjack module.
When writing more plugins read this and the types.frt carefully, also you can use the prototype.frt as a starting point.
Words that are used inside the object should be defined in the JACKAUDIO vocabulary.
ALSO JACKAUDIO ALSO DEFINITIONS
Now a pointer to a counted text should be defined, later this is set to the plug_type object-element; it's there for a) making the plug_type defined and b) the LIST-PLUGINS and LIST-ALL-PLUGINS tools gives more detailed information.
HERE ," SINUS oscillator" CONSTANT #sin-id
#PRIVATE-DSP
DSP-FVALUE sin_phase
DSP-FVALUE sin_step
CONSTANT #sinstruct
Then the object elements are defined and the size of the whole object struct is made a CONSTANT
: CHK-SIN #sin-id plug_type <> ABORT" Invalid SINUS-TONE object" ;
Check for a correct plug_type and aborts when this not ok
Each object type MUST have a specified signal processing funtion; the execution token of this function is stored in the plug_xt element. The funcion MUST take one FLOAT as a parameter and MUST leave one FLOAT as a result. So the depth of the floating stack must never be changed.
: calc_sin_val \ ( -- ) (F val -- val' )
sin_step sin_phase F+ REDUCE.2PI FDUP TO sin_phase FSIN plug_level F* F+ ;
Before you start writing your own audio-data-processing code remember that
Whenever you want to do things like that: FORGET ABOUT THEM in the process function. What you could and should do: Write thread-safe buffering functions!
You may watch the jack performance with two included tools, JACK-LOAD and JACK-XRUNS.
Many - as this example plugin - can be tuned to a frequency; the io_procs module implements a generic tuning word SET-PLUGIN-FREQUENCY which calls a function defined in the object making phase.
: (tune-sine-object) ( frequency -- )
FDUP TO plug_frequency PI F* F2* JACK-SAMPLE-RATE S>F F/ TO sin_step ;
Every object definer needs a function that initialises the plugin object when it is defined and also in the turnkey bootstrap word INIT-ALL-JACK-OBJECTS. If - in some cases - there is no such function needed MAKE&ALLOCATE-PLUGIN-OBJECT must be given 0.
: (init-sine-object)
; \ nothing to do; no allocation
Now you can make an object-definer, as this should be public you should switch to FORTH before.
FORTH DEFINITIONS
: MAKE-SINE-OSCILLATOR ( -- addr ) \ Create a sinus oszillator struct
\ Make an object and make it the 'current' object
['] (init-sine-object) 0 #sinstruct MAKE&ALLOCATE-PLUGIN-OBJECT
\ Set the correct plug_type and set a plug_filtype mode
#sin-id TO plug_type SIN-TONE TO plug_filtype
\ set the execution token of the signal processing function
['] calc_sin_val TO plug_xt
\ set the tuning function
['] (tune-sine-object) TO plug_xt_tune
\ these data can be permanent
0e0 TO sin_step 0e0 TO sin_phase
\ leave the 'current' object on the stack
@DSP ;
\ A more simple definer makes the object and defines it as a constant
: SINE-OSCILLATOR: ( -- ) \ name
MAKE-SINE-OSCILLATOR CONSTANT ;
PREVIOUS PREVIOUS DEFINITIONS
Implements a bank of notch IIR butterworth filters, the slots 15-22 in a channel are used
8 CONSTANT #NOTCHES
The number of notch filters available
: GET-FREE-NOTCH ( -- object flag )
Try to get a notch filter object from a bunch of 8, if all notches are already in use
flag is FALSE.
: NOTCH-THERE? ( frequ channel -- object flag )
checks for an already used notch filter with frequency - or very close to.
If such a notch is plugged in, the object and a TRUE flag are returned.
: SET-NOTCH-FILTER \ ( frequ channel -- )
Sets a notch filter from a bunch of 8. If there is a notch already plugged in it is switched off.
: CLEAR-ALL-NOTCHES ( -- )
removes all notches from the processing queue.
This can add an echo to the current signal, the level of the echo and the original signal can be set.
: MAKE-DELAYER ( -- object )
Construct a delayer object and leave it's address.
: DELAYER: ( -- ) \ name
Define a named delayer object
: SET-DELAY-TIME ( useconds object -- )
Sets the dapay time for the delayer object. Time in usec.
: SET-DELAY-LEVELS \ ( object -- ) (F delayed-level now-level -- )
Sets the signal levels for a delayer object; both level must be floats.
: MAKE-VIBRATO ( -- addr )
Make a vibrato effect plugin object and leave a pointer to it.
When setting the frequency by SET-OBJ-FREQUENCY the argument is the frequency*1000 to allow
more precise settings.
The vibrato strength is set as usual by the SET-OBJ-LEVEL method.
: VIBRATO: ( -- ) \ name
make a named vibrato effect plugin
This set the JACK-IO-LEVEL to a moderate level.
: MAKE-AUTOLEVELER ( -- object )
Construct a autolevel object and leave it's address.
: AUTOLEVELER: \ ( -- ) name
Define a named autolevel object
The phaser plugin emulates a phaser/flanger. You can control the timing of the shifted signal, the timing is also modulated by an oszillator.
: MAKE-PHASER ( -- object )
Construct a phaser object and leave it's address. The default settings are a timeshift of 10msec,
a strength of 0.2 and an oszillating speed of 5Hz.
: PHASER: ( -- ) \ name
Define a named delayer object
: SET-PHASER-PARAMETERS \ ( object -- ) (F frequ strength timeshift -- )
Sets parameters for the phaser object.
The strength may be choosen from 0 to 1, this controls the oszillating amount, the default of 0.2 is already pretty strong.
Timeshift in milliseconds, the maximum depends on the history size, probably up to 100 msecs are usefull.
frequency controls the shift oszillator in Hz.
: JACK-BENCHMARK ( -- )
A little fjack processing benchmark