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.
|exampledelaytest.zip||January 11, 2019||The source code and executable JAR for the effect testing application (18 KB)|
This zip file contains:
- The source code for the application that tests effects. Although the zip file is called "ExampleDelayTest" it can be used to test not only the Orinj Example Delay, but also other effects.
- oreffect.jar, which is need for the compilation and execution (see Orinj Effect framework oreffect JAR; this topic also includes the source code for that JAR file).
- exampledelay.jar, which is an implementation of an example delay (see Orinj Effect framework Example delay Package).
- exampledelaytest.jar, which is the compiled executable JAR for this source code.
This source code can be used to test effects for Orinj – effects that are created with the Orinj effect framework. This source code replicates closely the way Orinj searches for effect JAR files, extracts information about the effects included in these JAR files and creates the effect and its graphical user interface. The code also allows the playing of a single wave file and applying one of the available effects to the playback.
The source code itself can be compiled with the following.
javac "ExampleDelayTest\src\com\recordingblogs\AudioBuffer.java" -d "ExampleDelayTest\class" -classpath "ExampleDelayTest\class;Orange\oreffect.jar"
javac "ExampleDelayTest\src\com\recordingblogs\WaveFile.java" -d "ExampleDelayTest\class" -classpath "ExampleDelayTest\class;Orange\oreffect.jar"
javac "ExampleDelayTest\src\com\recordingblogs\SelectDialog.java" -d "ExampleDelayTest\class" -classpath "ExampleDelayTest\class;Orange\oreffect.jar"
javac "ExampleDelayTest\src\com\recordingblogs\WaveFileDialog.java" -d "ExampleDelayTest\class" -classpath "ExampleDelayTest\class;Orange\oreffect.jar"
javac "ExampleDelayTest\src\com\recordingblogs\EffectStoreItem.java" -d "ExampleDelayTest\class" -classpath "ExampleDelayTest\class;Orange\oreffect.jar"
javac "ExampleDelayTest\src\com\recordingblogs\EffectStore.java" -d "ExampleDelayTest\class" -classpath "ExampleDelayTest\class;Orange\oreffect.jar"
javac "ExampleDelayTest\src\com\recordingblogs\Mixer.java" -d "ExampleDelayTest\class" -classpath "ExampleDelayTest\class;Orange\oreffect.jar"
javac "ExampleDelayTest\src\com\recordingblogs\EffectDialog.java" -d "ExampleDelayTest\class" -classpath "ExampleDelayTest\class;Orange\oreffect.jar"
javac "ExampleDelayTest\src\com\recordingblogs\MainFrame.java" -d "ExampleDelayTest\class" -classpath "ExampleDelayTest\class;Orange\oreffect.jar"
javac "ExampleDelayTest\src\com\recordingblogs\ExampleDelayTest.java" -d "ExampleDelayTest\class" -classpath "ExampleDelayTest\class;Orange\oreffect.jar"
jar cfm ExampleDelayTest\exampledelaytest.jar ExampleDelayTest\manifest.txt -C ExampleDelayTest\class .
The following describes the classes included in this code in alphabetical order.
This class is a simple array of bytes that is used as a buffer to store audio data that will be read from the wave file and sent to the output audio device. Since this buffer will not always be full (e.g., when reading audio data from the end of the wave file), this class also keeps track of the number of bytes that were read and should be used during playback.
This is an extension of JDialog that encompasses the graphical user interface for the effect. In this code, this class serves two purposes.
- First, the dialog shows up during playback, so that you can change the controls of the effect and thus test the effect (the ExampleDelay effect, for example, starts with delay of zero and decay of 100%, which is not very interesting. You will need to change these controls to actually hear the effect).
- Second, closing the dialog will cause playback to stop, so that you do not have to always listen to the whole wave file.
The actual controls for each effect are contained in a JPanel (see Orinj Effect Framework Example delay DelayPanel Java). This dialog simply adds the panel and a Close button.
This class is an array of the available effects. It replicates the way Orinj keeps track of available effects. As effects are read from the existing effect package JAR files, they are added to this array to be used later.
This class contains information about a single effect, including name, type, and the constructors for the effect and its graphical user interface. These items are created as effects are read from the existing effect package JAR files.
This is the main function for this source code. This function does the following.
- It loads all available effects by looking through the available effect packages.
- It shows all available effects to the user and allows the user to select an effect. The selected effect is the one that will be applied during playback. If the user clicks on Cancel in the dialog for selecting an effect, the execution will stop.
- It allows the user to select a wave file to be played. If the user clicks on Cancel in the dialog for choosing a wave, the execution will stop.
- It opens the chosen wave and tests it to make sure that it is a valid wave file with the appropriate format. Note that this source code only works with wave files that are PCM wave files with compression code 1 (see Format chunk (of a Wave file)), and have the sampling rate 44100 Hz and the sampling resolution 16 bits per sample.
- It creates a mixer. The mixer applies the effect to the audio data in the wave file and plays the resulting audio.
- It creates the effect and the effect dialog and begins playback.
This class loads available effects. An extension of the class JFrame was chosen, as this is done similarly in Orinj and as we need the class loader of an instantiated object in order to load the constructors for the effect and its graphical user interface. This class does the following.
- It looks through the "effects" folder for JAR files.
- It unzips JAR files and looks for the file "effect.xml" in each JAR file. The effect.xml file should reside at the top of each effect package JAR file and should contain information on the effects in the specific JAR file (see Orinj Effect framework oreffect JAR).
- It loads all effects according to the information in the effect.xml files and places them in the effect store (see EffectStore.java and EffectStoreItem.java above).
This class applies the chosen effect to the chosen wave file and plays the resulting audio. Note that a good amount of preparation for playback is done inside this class, including aligning the wave file pointer to the start of its data chunk, filling the audio buffers with silence, reading a few audio buffers from the wave file, and sending these buffers to the output audio device. This means that the output audio device's own buffer will be filled with some amount of audio, which will be played and continuously filled while the mixer reads and sends additional buffers.
The continuation of playback, after the initial preparation, is done in a separate thread. This is so to allow the user to change controls in the effect dialog (see EffectDialog.java above) or to press the Close button of the effect dialog and stop playback.
In principle, it is not necessary to send several buffers to the output audio device at the start of playback. If at least one buffer is sent and if the remaining code is executed sufficiently quickly, then the additional buffers will come from the separate playback thread while the first buffer is still playing, thus ensuring that there are no gaps in the playback.
Note that the mixer treats the effect as an object of class EffectInterface (see Orinj Effect framework oreffect JAR). This means that the mixer will only have access to the functions of EffectInterface (in this case, the mixer only uses "apply" and "allowsDryWetMix").
For effects that allow dry and wet mix (see Orinj Effect framework oreffect JAR), this mixer must mix the incoming dry buffer with the wet buffer from the effect (at which point, in Orinj, the user defined dry and wet mix are applied, even though there is no actual dry and wet mix here). The mixing implemented in this code is only for 16-bit audio data.
This dialog simply lists the names of all available effects and lets the user choose one effect to be applied during playback.
This is an implementation of a wave file. The wave file is implemented as a RandomAccessFile. This is a good idea, as wave files consists of chunks, some of which may or may not be used. For example, in this code, we use the format and data chunks of the wave file, but a wave file may contain other chunks, such as ones that contain markers, text information about the file, or other. We may want to skip these chunks when working with the wave file.
A wave file may also contain a wave list chunk with alternating data and silence chunks. In such a file, the data chunks will not be at the top of the wave file. For such a file, the implementation of the wave file in this class is not the best implementation. A better way is to implement the wave file as a stack of chunks. For simplicity, this implementation assumes that the data and format chunks of the wave file are at the top of the file and not sub chunks of some other chunk. Thus, this implementation will not be able to handle all wave files.
The following are the member data of the class.
- m_datapointer – this is the position of the data chunk in the wave file (after the name of the chunk "data" and the four byte size of the chunk). We keep this information so that we can quickly go to the audio data portion of the wave to begin playback.
- m_format – the audio format of the wave. In many audio applications, quick access to the format will be necessary so that the wave can be appropriately mixed. For example, one should know whether the wave contains one or two channels so that effects that use different parameters for the different channels can be applied.
The following are the more interesting functions in the class.
- readRIFFString(), readRIFFUnsignedShort(), readRIFFUnsignedInt() – these functions simply read strings, unsigned shorts, and unsigned integers from the random access file (the wave file). The names of chunks are strings. Unsigned shorts and unsigned integers are contained in the format chunk (e.g., for the compression code / format type, the number of channels, sampling rate, etc.). Unsigned integers are also used for the size of chunks.
- openRead() – this function checks the format of the file (for the right headers, subheaders, and chunks) and prepares the file for use (by aligning m_datapointer). Since this code only handles 16-bit PCM waves with the 44100 Hz sampling rate, this function also checks for the appropriate compression code, sampling resolution, and sampling rate.
- startPlay() – this function moves the file pointer to the beginning of the audio data.
- getData() – this function reads a buffer of audio data. This function is simple in this implementation, but could become a lot more complex if the application is to allow fast forwarding, rewinding, and looped playback.
This dialog simply allows the user to choose a wave file for playback.
Testing effect packages in Orinj and creating presets
You can also test your effects in Orinj. Place your effect package JAR file in the "orange/effects" folder and start Orinj. Orinj will automatically recognize the JAR file, recognize the effects, and create menus so that these effects can be used in tracks.
Almost all default Orinj effects are each distributed with a set of presets (see Orinj Effects). Each preset contains specific values for the effect controls. For example, the preset "bounce (quick)" for the Orinj Delay specifies that the delay in the left channel is 400 ms, the delay in the right channel is 800 ms, the decay in the left channel is 80%, and the decay in the right channel is 60%. Presets are simply a way of allowing the users to quickly find good settings for each effect, without having to try many options by themselves.
It is not required that you create presets for your effects. If you want to do so, however, you can. In Orinj, add the effect to a track. In the dialog for your effect, set the controls of the effect to the values that should be in the preset. Click on the save button next to the drop down box for the presets (the preset drop-down box and the save button will be added by Orinj independent of how you organize your effect dialog). Save the preset.
You can save as many presets as you wish. These presets should be saved in the Orinj "orange/presets" folder with the name of the preset that you choose, followed by the name of your effect Java class. If you create an installation for your effect package, you should make sure that the installation places the effect package in the "orange/effect" folder of the Orinj installation and the effect presets in the "orange/presets" folder of the installation.
In Orinj versions prior to 3.0.2, if you do not have presets and add one of your effects to a track, you will get the warning: "Cannot find presets". Your effect will still work. With version 3.0.2, this warning was removed, as there may be effects in Orinj that do not have parameters and thus do not have presets. The Orinj phase oscilloscope and spectrum monitor do not have parameters and do not have presets.
Orinj Effect framework