Orinj version 4.0.0
Note: This page is not for the users of Orinj, but for developers who want to create digital signal processing effects for Orinj.
|oreffect.jar||June 9, 2019||The basis for all effects in Orinj. This JAR file contains the compiled interfaces that all Orinj effects should implement (6 KB).|
|oreffect.zip||June 9, 2019||The source code for oreffect.jar (12 KB).|
This Java JAR file resides in the "orinj" folder of the Orinj installation. Its most important role is to specify the two interfaces that an Orinj effect should implement in Java – EffectInterface and EffectPanelInterface.
- EffectInterface.java: This interface is the actual effect. For example, this interface specifies that an Orinj effect should implement a function "apply" with a specific syntax, so that Orinj can send buffers with audio data to this effect and this effect can be applied to these audio data.
- EffectPanelInterface.java: This interface must be implemented by the graphical user interface for the effect. The graphical user interface itself should contain the controls that the user can change to modify the effect. For example, this interface specifies that the graphical user interface for the Orinj effect should implement the function "updateData" so that Orinj can tell the effect when its user controls should be updated with the effect values.
When you design Orinj effects, you will at a minimum develop two Java classes – one of the effect and one for the graphical user interface of the effect. These two classes must implement the two interfaces above respectively.
The other classes in the JAR are as follows.
- EffectFont: This class contains static fonts of class java.awt.Font. For some Orinj skins, these fonts will be changed by Orinj to match the fonts used by the skin. You can use these fonts for the graphical user interface for your effect, so that the look and feel of your effect is the same as the look and feel of the rest of Orinj. You are not required to use these fonts.
- EffectGraph: This class contains colors used by Orinj graphs. You can use these colors for any effect graphs. You are not required to use these colors.
- EffectSkin: This class shows whether the current skin is dark (with light fonts and borders) or light (with dark fonts and borders). You can use this value accordingly. You are not required to use this value.
- Envelope: This is an interface that represents an amplitude envelope – a description of how some amplitude changes with time during playback. Some of the Orinj effects use two envelopes, both of which implement this interface. These are the dry mix envelope and the wet mix envelope of the effect (see below). Not all effects use envelopes.
- ReadInterface: This is an interface that should be used similarly to java.io.ObjectInputStream. It helps with the reading of effect parameters stored in saved sessions or loops or stored for future undo.
- Undo: All effects (the actual effect and not its graphical user interface) should extend this class. This class contains an array of event listeners and can be used to fire undo events, which Orinj uses to store undo information for your effect. With this, the Orinj user can undo changes made to the controls in your effect.
- UndoEvent: The event that should be fired so that Orinj stores undo information and displays an undo message in its menus.
- UndoListener: An interface that is implemented by the listener for undo events. This interface will be implemented by components of Orinj, who will listen for events fired by effects.
- WriteInterface: This is an interface that should be used similarly to java.io.ObjectOutputStream. It helps with the storing of effect parameters when saving sessions, loops, or undo information.
All classes in oreffect.jar are described below in alphabetical order.
This class contains four statically defined fonts – LARGEFONT, MEDIUMFONT, SMALLFONT, and SMALLERFONT – of the class java.awt.Font. You are not required to use these fonts in your effects.
- LARGEFONT: In the Orinj effects that are distributed with the Orinj installation, this font is used primarily for control labels.
- MEDIUMFONT: This font is not used in the Orinj effects that are distributed with the Orinj installation. In Orinj, a medium size font is used, for example, for the labels of the time line and time signature line.
- SMALLFONT: In the Orinj effects that are distributed with the Orinj installation, this font is used primarily for slider labels.
- SMALLERFONT: This font is not used in the Orinj effects that are distributed with the Orinj installation. In Orinj, this font is used, for example, for the MIDI roll keyboard and for envelopes.
This class contains four statically defined colors – BACKGROUND, GRID, FOREGROUND, and HIGHLIGHT – of the class java.awt.Color. You are not required to use these colors in your effects.
- BACKGROUND: This color is the background of Orinj effect graphs. For example, this is the background of the graph of the Orinj Compressor.
- GRID: This is the color of the grids, axis, and axis labels in the Orinj effect graphs. For example, this is the color of the grid of the Orinj Compressor graph.
- FOREGROUND: This color is the color of Orinj effect graphs. For example, this is the color of the compression line in the Orinj compressor graph.
- HIGHLIGHT: This is second foreground color in Orinj effect graphs. For example, this is the color of the selected point in the Orinj compressor graph.
All Orinj effects must implement this interface. The following is a description of the member functions of this interface.
- public abstract void apply(int  buffer, int  controlbuffer, int channels, float samplingRate, double time, float drymix, float wetmix, Envelope dryMixEnvelope, Envelope wetMixEnvelope): This function takes incoming audio data and modifies these data to apply the effect.
- buffer – this buffer contains the incoming audio data and is also used to store the effect output audio data. This is an array of 32-bit signed integers (that is, 32-bit signed integer PCM data) with values between Integer.MIN_VALUE and Integer.MAX_VALUE.
- This argument contains 32-bit signed integer PCM data for any sampling resolution that may be used in the rest of Orinj. This applies to both the incoming data and the outgoing data. It is up to Orinj and not up to the effect to convert audio data into a 32-bit integer buffer before sending the audio data to the effect and to convert the output of the effect back to whatever is used by sound devices.
- The audio data in buffer buffer may be for one or two channels as specified by the argument channels. With two channels, the first integer is the first sample for the left channel, the next integer is the first sample for the right channel, the integer after that is the second sample for the left channel and so on.
- buffer does not contain all audio data that will be sent to the effect. As playback progresses, audio data is sent to the effect in pieces. It is up to the effect to store, keep, and discard audio data as needed. The Orinj echo, for example, looks at previous audio data, as the current value of the echo repetitions depends on the past values of the signal. This echo stores previous audio data, uses it, and discards it when it is no longer necessary (i.e., when the audio data is too far in the past to be used by the echo).
- controlbuffer – the control buffer should be used by effects that allow side chaining, such as a side chained compressor. A side chained compressor changes the dynamics of one track based on the dynamics of a second track. The audio data contained in the second track would be sent to the effect in this argument.
- An effect that uses this argument should also return true in allowsSideChaining. If the effect does not allow side chaining, this argument may be null and should not be used.
- Orinj controls which track is to be used as a control track. There is no need to implement controls in the graphical user interface for the effect for this. Orinj will provide these controls, as long as the effect returns true in allowsSideChaining.
- If controlbuffer is not null, then the length of controlbuffer is the same as the length of buffer. The format of audio data in controlbuffer is the same as the format of audio data in buffer.
- channels – the number of audio channels in buffer.
- samplingRate – the sampling rate of the audio in buffer.
- time – the start time of the audio buffers (buffer and controlbuffer) in the session, wave, or loop that is being played, in seconds. For example, if playback starts at 10 seconds into the multitrack session, time will be equal to 10 in the first call to this function.
- This argument is used usually to compute the dry and wet mix envelope gains (see below). Most effects will not use this argument for any other purposes. An echo, for example, will produce signal repetitions with the same values for the delays and decays independently of the current playback time. Some effects may need to know the precise playback time. For example, a wah wah will sound better if its oscillation coincides with the rhythm of the song. The wah wah thus must know the time of playback. In general, effects that use low frequency oscillation – oscillation with frequency that can be created or perceived by humans – may need to use the playback time.
- drymix – the dry mix of the effect. This is a number between 0 and 1. It is the gain that must be applied to the original signal before that original signal is placed in the output buffer, but usually after the original signal is used for any computations in the effect (that is, the dry mix should not impact the repetitions of an echo, but only the original signal). Effects that do not use dry or wet mix should not use this argument.
- Not all effects have a dry and wet mix. Consider a simple delay that produces one delayed and decayed repetition of the original signal. Implementing dry and wet mix in this effect is simple. The dry mix gain is applied to the original signal and the wet mix gain is applied to the delayed and decayed repetition. Typically, the dry mix gain is only applied to the original signal after the delayed and decayed repetition is computed, so that this gain does not impact the wet portion of the resulting signal. Similarly, the wet mix gain should not impact the dry, original portion of the resulting signal.
- A compressor, on the other hand, typically will not have dry and wet mix. In a compressor, the arguments drymix, wetmix, dryMixEnvelope, and wetMixEnvelope should be ignored.
- wetmix – the wet mix of the effect. This is a number between 0 and 1. It is the gain applied to the processed signal after that processed signal is computed from the original signal. Effects that do not use dry or wet mix should not use this argument.
- dryMixEnvelope – a gain envelope for the dry mix of the effect. This envelope is an additional dry mix gain to be applied to the original signal before this original signal is placed in buffer and after this original signal is used to compute the wet signal. Effects that do not have dry and wet mix should not use this argument.
- The difference between this argument and drymix is that drymix is constant throughout the audio data in buffer and the dry mix envelope can change. A call to the member function of this argument Envelope::getValueAt will return the gain to be applied at a specific time. The gain is between 0 and 1 and should be used in the same way as drymix.
- wetMixEnvelope – a gain envelope for the wet mix of the effect. This envelope is an additional wet mix gain to be applied to the processed signal after this processed signal is computed from the original signal. Effects that do not have dry and wet mix should not use this argument.
- public abstract boolean allowsDryWetMix(): To implement this function, simply return true, if your effect allows dry and wet mix, and false, if it does not.
- public abstract boolean allowsSideChaining(): To implement this function, return true, if your effect allows side chaining, and false, if it does not.
- public abstract void startPlay(): Use this function to implement any preparations that must occur at the start of playback. The Orinj graphic equalizer, for example, implements this function to compute its frequency filters (although these filters may be recomputed again during playback, if the user changes the equalizer controls).
- public abstract void stopPlay(): Use this function to implement any cleanup actions that must take place at the stop of playback. You should be careful with implementing this function as playback may continue for a small amount of time even after this function is called, depending on the size of audio buffers in Orinj. You should not, for example, use this function to remove stored incoming audio data (rather, remove these data at the start of the next playback). Very few effects distributed with the Orinj installation use this function.
- public abstract boolean hasData(): This function should return true, if the effect has not finished processing and false otherwise. Imagine a simple delay (see Orinj Effect framework Example delay Delay.java). It creates a single repetition of the original signal. Even if the original signal has ended (e.g., at the end of the last wave in the track), the repetition of the signal created by the effect may continue shortly after that. Orinj will first check whether there is original signal. If not, it will ask the effect whether the effect itself will produce a signal. If yes, the effect will be processed. If not, the effect will not be processed. This is a way of saving computations in Orinj so that the effects can be processed faster. To create its repetition, the Example Delay stores a portion of the original signal. After the original signal is finished, Example Delay will return true with this function only if it still has a portion of the original signal stored. This means that it can still produce the end of the repeated signal. When the saved portion of the original signal is also finished in the effect itself, Example Delay will no longer produce the repeated signal and will return false here. Note that it is always safe to simply return false. If you so do, however, the wet (processed) signal from your effect may stop prematurely at the end of a wave. Most effects will, in fact, produce a delay in the signal that should be accounted for.
- public abstract boolean readObject(ReadInterface stream): This function and the function below allow Orinj to save the effect controls in the session files, loop files, or for undo. Orinj expects that each object (tracks, effects, volume envelopes) implements its own serialization methods (i.e., the standard Java serialization via Serializable is not used). The following is (a simplified version of) the readObject implementation for the Orinj delay.
- public boolean readObject(ReadInterface ar)
- m_leftDelay = ar.readFloat();
- m_rightDelay = ar.readFloat();
- m_leftPolarity = ar.readBoolean();
- m_rightPolarity = ar.readBoolean();
- m_leftDecay = ar.readFloat();
- m_rightDecay = ar.readFloat();
- m_lockChannels = ar.readBoolean();
- catch (IOException e)
- System.out.println("Exception in Delay::readObject: " + e);
- return false;
- return true;
- Note that, in addition to storing and reading effect controls, you can also store and read a version number for your effect that is independent of the Orinj version. In this way, you can implement some version control.
- public abstract boolean writeObject(WriteInterface stream): This is the function that Orinj uses to save effects in the Orinj session, loop, or for undo. This function should be implemented similarly to the one above, but values would be stored, rather than read.
- public abstract void setEqual(EffectInterface effect): This function should set the member data of the effect equal to the member data of the argument effect. You should check whether the two effects are of the same class. In the current version of Orinj, this function is not used. It may be used in future versions.
- public abstract void setLanguage(String languageCode): Implement this function if the graphical user interface for your effect supports different languages. This function changes the current language to the one specified by languageCode. The languageCode argument is the three letter ISO 639-2 language.
- In Orinj, strings for labels, tooltips, and other are stored in XML files (see, for example, the orinj/languages folder of your Orinj installation). However, you can implement languages differently and you do not have to implement languages at all.
The graphical user interface for Orinj effects should typically be an extension of the class javax.swing.JPanel and should implement this interface.
Orinj effects are processed during runtime (during playback) and the dialogs that Orinj uses so that the user can modify the effect controls are non-modal. That is, the user can work with the rest of Orinj while the effect dialog is opened. In Orinj, all effect dialogs are standard. The actual dialog (of class JDialog) is already implemented and you should not implement it. This dialog has:
- A Close button
- A bypass check box
- A text field to change the name of the effect
- A drop-down box to select presets
- Buttons to save or delete presets.
- A drop-down box to select a control track, if the effect allows side chaining.
These are already implemented and you should not implement them. You should only implement controls that are specific to the effect. For the Orinj delay, for example, these controls would be the left and right channel delays, decays, and polarity, as in the example above. When you implement the effect specific controls, place them in a class that extends javax.swing.JPanel. Orinj will take this pane and place it inside a dialog that already contains the Close button and the bypass checkbox.
The EffectPanelInterface interface contains a single function:
- public abstract void updateData(): This function should set the values of the effect panel controls to the values of the member data of the effect itself.
This class contains one statically defined boolean – DARK. You are not required to use it in your effects. Some Orinj effects have components with borders (see the text area of the Orinj Compressor) that are set based on this value. The component may set its border color to be brighter (see java.awt.Color.brighter()) or darker (see java.awt.Color.darker()) than the background depending on whether the current Orinj skin is dark or light.
An envelope allows for changes in audio controls over time, as playback or mixing progresses. For example, an amplitude envelope applied to a track in the session can allow the Orinj user to specify that the amplitude of the track should decrease gradually so that the track can fade out.
Orinj uses several types of envelopes, allowing for changes in volume, pan, the dry mix gain of an effect, and the wet mix gain of an effect. Only dry mix and wet mix gain envelopes are handled in the Orinj effect framework.
The Orinj envelopes are implemented in Orinj and should not be implemented in effects. This interface allows effects to access to the Orinj envelopes.
This interface contains only one function.
- getValueAt(double time): This function gets the value of the envelope at a specific point of playback or mixing time. The value depends on the type of envelope. For dry mix and wet mix gain envelopes, this will be a gain between 0 and 1. The argument time is in seconds.
- The appropriate way to use this function is within the implementation of the apply function of EffectInterface in an effect, The argument time of that apply function is the time at the beginning of the audio buffer (buffer). The time of a specific sample in buffer in the apply function depends on the sampling rate. For example, if the beginning time of buffer is 10 seconds, the sampling rate is 44.1 kHz, and we are looking at sample 100 in mono audio, then the time of the sample will be 10 + (100 / 44100) = 10.0022 seconds. getValueAt(10.0022) will provide the appropriate value of the envelope for that sample. That value can then be used in the effect computations.
- The value of an envelope can be obtained for every sample. However, since the distance between samples represents a very small amount of time and since envelopes do not usually change drastically over time, you can also consider obtaining the value of the envelope less often, every few samples.
There are two classes in Orinj that implement this interface. The first class is an extension of java.io.ObjectInputStream. Note that, in fact, most of the functions of ReadInterface are simply the functions of ObjectInputStream. You can use this class and its functions in the exact same way you would use ObjectInputStream and the functions that belong to that class (see the snippets of code above). The first class in Orinj that implements this interface and extends ObjectInputStream reads the member data of the effect as part of the serialization of the session or loop. The second class in Orinj that implements this interface reads the member data of the effect when the user undoes changes in the effect. This class is implemented very differently from ObjectInputStream. It does not read information from disk, for example, but reads it from memory. Having these two classes implement the same interface means that you would only need to implement one readObject function for each effect. This function will be called both when the effect is read as part of a session or loop and when this effect is read for the purposes of undo.
This class should be extended by all effects, even if these effects to do not actually implement undo (i.e., even if they do not fire undo events). This class essentially contains an array of event listeners, which are notified when an undo event is fired by the effect. The following are the member functions of the class.
- public void addUndoListener(UndoListener listener): This function adds a listener to the array of event listeners in the effect. This function is used by Orinj to add its components as event listeners.
- public void removeUndoListener(UndoListener listener): This function removes a listener from the array of event listeners in the effect. This function is also used by Orinj.
- protected void fireUndoEvent(UndoEvent event): This function notifies all listeners of the undo event. This function will be used by the effect to fire undo events when the effect controls change. The undo events will notify the listeners (the Orinj components) that the effect is changing and its data should be stored so that they can be brought back if the user wants to do so. The undo events will also provide Orinj with the message to be displayed in the undo menu.
This is the undo event that should be fired by effects, via the Undo class extended by the events, to notify Orinj that the effect has changed and to provide Orinj with the message that should be placed in the undo menu. This class simply contains one member data item – the message – and one member function besides the constructor – the function that provides access to the message.
This is the listener for undo events. This interface is used by Orinj.
Similarly to ReadInterface, there are two classes in Orinj that implement this interface. The first class is an extension of java.io.ObjectOutputStream and is used to save sessions and loops (and the effects as part of sessions and loops) for serialization. The second class saves effect information for undo.
Orinj Effect framework