Orinj version 3.0.2
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 21, 2016||The basis for all effects that Orinj can use. This JAR file contains the compiled interfaces that all Orinj effects should implement (6 KB).|
|oreffect.zip||June 21, 2016||The source code for oreffect.jar (10 KB).|
This Java JAR file resides in the "Orinj/orange" 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 is the graphical user interface for the effect. It 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 two static fonts of class java.awt.Font. 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.
- 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.
All classes in oreffect.jar are described below in alphabetical order.
This class simply contains two statically defined fonts – LARGEFONT and SMALLFONT – 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.
- SMALLFONT: In the Orinj effects that are distributed with the Orinj installation, this font is used primarily for slider labels.
All Orinj effects must implement this class. The following is a description of the member functions of this interface.
- public abstract void apply(byte  drybuffer, byte  wetbuffer, byte  controlbuffer, AudioFormat format):
- drybuffer: This buffer contains the incoming audio data. Note that this is an array of bytes and hence must be translated into sampled audio data depending on the argument format. If, for example, the format specifies that this is 16-bit data, then the sampled value of the signal represented by the first two bytes is
- (short)(((drybuffer & 0xff) << 8) + (drybuffer & 0xff));
- drybuffer may contain: unsigned 8-bit integer data, signed 16-bit integer data, signed 24-bit integer data, or signed 32-bit floating point data. These data will have the format of the wave files that are used by Orinj and will be littleendian as in the example above (i.e., in the example above, the first byte is the lower byte of the two-byte value and the second byte is the higher one; thus, the second byte is shifted to the left by 8 bits).
- drybuffer may contain one or two channels as specified by the argument format. With two channels and 16-bit data for example, the first two bytes will be the first sample for the left channel, the next two bytes will be the first sample for the right channel, the two bytes after that will be the second sample for the left channel and so on.
- Note that drybuffer 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 previous audio data as needed. The Orinj echo, for example, has to look 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).
- wetbuffer: Treat this buffer as an empty one, but place the output of your effect in this buffer. If your effect has a dry and wet mix that Orinj can change, then place the wet data into this buffer. If your effect does not have a dry and wet mix, place the output of your effect in this buffer.
- Consider the Orinj Delay effect. This effect takes the original signal and creates one repetition of that signal. The dry audio data is the original signal. The wet audio data is the repetition. The simple delay effect in Orinj leaves drybuffer unchanged, but places the repetition in the wetbuffer. Since Orinj knows that this effect has a dry and wet mix (see allowsDryWetMix below), Orinj will use both the dry and wet buffers in further processing.
- Consider the Orinj Compressor. This effect does not have a dry and wet mix. It leaves drybuffer unchanged, but places the compressed signal in wetbuffer. Since Orinj knows that this effect does not have a dry and wet mix (see allowsDryWetMix below), it will ignore drybuffer coming out of this effect in further processing.
- The length of wetbuffer is the same as the length of drybuffer. The format of audio data in wetbuffer should be the same as the format of audio data in drybuffer. This format is defined by the argument format.
- controlbuffer: This buffer should be used by the effects in Orinj that allow side chaining (see allowsSideChaining below). The Orinj side chained compressor, for example, adjusts the levels of a signal depending on the levels of another signal (i.e., the amplitudes in one track depending on the amplitudes of another in the multitrack session view). In this effect, controlbuffer contains the audio data of the second track – the one whose amplitudes are considered in adjusting the amplitudes of the first track.
- The length of controlbuffer is the same as the length of drybuffer. The format of audio data in controlbuffer is the same as the format of audio data in drybuffer. controlbuffer may be null or empty for effects that do not specify that they can use side chaining.
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.
- format: This is the format of drybuffer, wetbuffer, and controlbuffer. It is of class javax.sound.sampled.AudioFormat.
- time: This is the start time in seconds of the audio buffers (e.g., drybuffer) in the session or wave that is being played. 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. Most effects will not use this argument. An echo, for example, will produce repetitions with the same values for the delays and decays independently of the current playback time. Other 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.
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.
- 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.
- In Orinj, strings for labels, tooltips, and other are stored in XML files (see, for example, the Orinj/orange/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 class 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.
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