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.
The class Delay.java is a part of the example Orinj effect Example Delay.
The member data of the class is as follows.
MINDELAY – a final static variable that defines the minimum delay in seconds
MAXDELAY – a final static variable that defines the maximum delay in seconds
MINDECAY – a final static variable that defines the minimum decay ratio
MAXDECAY – a final static variable that defines the maximum decay ratio
m_delay – the actual delay specified by the user, in seconds
m_decay – the actual decay specified by the user, ratio of the amplitude of the delayed signal repetition to the amplitude of the original signal
m_storeBuffers – an array of byte buffers necessary to store past audio data
m_delayOriginal – the amount of delay that will be stored for undo, needed to limit the number of undo operations as described below
m_decayOriginal – the amount of decay that will be stored for undo, needed to limit the number of undo operations as described below
This class extends Undo and implements EffectInterface (see Orinj Effect framework oreffect JAR). Both are required so that Orinj can create the effect. You should extend Undo even if you do not actually implement undo (do not fire undo events).
The static declarations for the minimum and maximum delay and decay are not necessary, but are a good idea if the code is to consistently check for potential errors in delay and decay amounts. These are used both in the Delay.java class, to check for incoming data when setting m_delay and m_decay, as well as in the DelayPanel.java class, to check the user input.
m_storeBuffers is necessary in this effect (and in most other effects), as this effect must look in the past and as the audio data is sent to the effect in chunks. The current values of the repetition – the delayed and decayed signal – is equal to some past value (with decay) of the original signal, which may be in some chunk that was sent to the effect in the past. The effect uses m_storeBuffers to save this past audio chunks, so that they can be used in current computations. This variable is further discussed below.
The functions in this class are as follows.
- Delay() – the one and only constructor. This constructor initializes the delay and decay amounts and the array of byte buffers.
- float getDelay() – this function returns m_delay, the amount of user defined delay
- void setDelay(float delay) – this function sets m_delay, the amount of user defined delay, to the value of its argument delay. The code for this function is as follows.
- public void setDelay(float delay, boolean storeUndo)
- if (delay < MINDELAY || delay > MAXDELAY)
- System.out.println("Error in Delay::setDelay: Error in delay");
- delay = Math.min(MAXDELAY, Math.max(MINDELAY, delay));
- m_delay = delay;
- if (m_delayOriginal != delay && storeUndo)
- m_delay = m_delayOriginal;
- fireEffectUndoEvent(new EffectUndoEvent(this, "Undo Delay"));
- m_delay = delay;
- m_delayOriginal = m_delay;
- Note first that this function takes two arguments.
- delay – the amount of (user defined) delay.
- storeUndo – true, if the effect should store its data for future undo; false, otherwise
- In the first two lines above, the function checks whether the incoming delay is between the allowed limits. The function then limits the amount of delay to be within the allowed limits and sets m_delay in the effect.
- The following lines store undo information. Note that the amount of delay in the effect is set by the graphical user interface and may be set either by a text field or by a slider (as we designed the graphical user interface to have both a text field and a slider). The graphical user interface handles changes in the text fields and sliders in various ways (e.g., actionPerformed, focusLost), but a choice was made in the design of this effect to store undo information only when the user moves the focus away from the specific control. Thus, the storing of undo is only done occasionally. For example, the user can move the slider up and down several times, which will change the amount of delay in the effect and the results will be heard during playback, but undo will only be stored when the user moves away from the slider.
- In the first instance, the function checks whether the current amount of delay is different than m_delayOriginal. m_delayOriginal is only set when a previous storing of undo information took place. Thus, the value right after the previous undo storage will be stored for undo, rather than the current value of m_delay. The function then fires an undo event, which prompts Orinj to store undo information. Finally, the function sets the value of m_delay at the appropriate level.
- Handling undo is not required. Using sliders or multiple controls for the same effect value is also not required. Thus, the implementation of this function could be much simpler, depending on the design.
- float getDecay() – this function returns m_decay, the amount of user defined decay
- void setDecay(float decay) – this function sets m_decay, the amount of user defined decay, to the value of its argument decay. This function is implemented similarly to setDelay above.
- void allowsDryWetMix() – this function should simply return true, if the effect allows dry and wet mix, and false, if the effect does not allow dry and wet mix (see Orinj Effect framework oreffect JAR). Orinj will separately allow the user the specify the amounts of dry and wet mix (between 0% and 100% of the signal amplitude).
- void allowsSideChaining() – this function should simply return true, if the effect allows side chaining, and false, if the effect does not allow side chaining (see Orinj Effect framework oreffect JAR).
- void startPlay() – this function should implement any processing that must be performed at the beginning of playback. For this effect, this function simply empties m_storeBuffer to remove any audio data from previous playback, so that these data are not heard during the current playback.
- void startPlay() – this function should implement any processing that must be performed at the end of playback. This should be rare. Most Orinj effects leave this function empty.
- void setLanguage(String language) – this function sets the language of strings that are used in the graphical user interface for the effect and for error messages that may be shown to the user. The argument language is the three-letter ISO 693-2 language code of the language that should be chosen. In this example, this function is empty and the user will always see all text in English. Orinj effects and Orinj itself, on the other hand, contain XML files that translate each character string in Orinj in the languages that Orinj supports. This link contains the XML schema for the XML language files. It is not required that you implement multiple languages. If you implement languages, it is not necessary to implement the languages that Orinj supports, but you should choose a default language, in case Orinj chooses a language that your effect does not support.
- boolean writeObject(WriteInterface ar) – Orinj does not rely on the Java Serializable interfaces, but implements its own serialization. Thus, this function is necessary so that effect data can be saved as part of the session or loop and as presets. This function is also used to save effect information for undo. This function should return true if writing did not encounter errors and false if there was an error.
- boolean readObject(ReadInterface ar) – similarly to the previous function, this function must be implemented so that effect data can be read from sessions, loops, presets, and undo. This function should return true if reading did not encounter errors and false if there was an error.
- void setEqual(EffectInterface effect) – this function is not currently used by Orinj, but should be implemented in case Orinj uses it in the future. This function sets the data of the effect equal to the data of another effect, the one represent by the argument effect. This function must check whether the argument effect is of the same class as the current effect.
- void apply(byte  dry, byte  wet, byte  control, AudioFormat format, double time) – this function computes the effect. It takes an original signal (the dry buffer below) and returns a delayed and decayed signal (the wet buffer below). The arguments of this function are as follows.
- dry – this array of bytes contains the original signal, to which the effect should be applied. Note that this buffer is a byte array and the values of the sampled audio data must be computed from these bytes. The format of audio data is specified by the argument format below. In addition, this buffer does not contain all incoming audio data, as these data will be sent to this function in pieces. This necessitates some of the audio data to be stored and processed the next time the function is called. You should expect that the length of this buffer is divisible by the frame rate of the incoming audio recording (the number of bytes for one sample in one channel multiplied by the number of channels).
- wet – this array of bytes will contain the resulting outgoing wet buffer with the effect applied. There are two types of effects in Orinj – effects that allow dry and wet mix (such as the Orinj delay or echo) and effects that do not allow dry and wet mix (such as the Orinj compressor). If the delay allows dry and wet mix, then Orinj will take both the dry and wet signals (the original signal in the dry buffer and the computed signal in the wet buffer) and will mix those according to the dry / wet mix requested by the user. If the effect does not allow dry / wet mix, Orinj will take the resulting wet buffer and ignore the dry buffer in further processing after the effect. This buffer has the same length as the dry buffer and the same format, as specified in the argument format.
- control – The control buffer should be used by effects that allow side chaining, such as a side chained compressor. A side chained compressor changes the amplitude of one track based on the amplitude 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 and selecting a control track should not be implemented by the effect's graphical user interface. This buffer has the same length as the dry buffer and the same format, as specified in the argument format.
- format – the audio format of the buffers. In the Orinj multitrack session view, this will be the format of the session. In the Orinj single wave view, this will be the format of the current wave file. In the Orinj loop building view, this will be the format of the loop.
- time – the time of the start of the audio buffers. This argument is not used, as the delay is not concerned with the exact playback time.
- This function, as implemented here, is only an example and only works on 16-bit audio data (see Sampling resolution). Additional work is needed to make the function work on 8, 24, and 32 bit data.
- Note the way this apply function stores audio data with the following code.
- byte  storeBuffer = new byte [dry.length];
- System.arraycopy(dry, 0, storeBuffer, 0, dry.length);
- The function creates a new buffer and copies the audio data into the new buffer. The dry buffer (the variable dry) is passed to this function by reference and, after this function is executed, will be reused by Orinj. Its audio data will change. Thus, the audio data must be copied into a new buffer.
- Note that stored buffers are removed when they are no longer needed. This is necessary as audio data can be large and should not be all kept in memory. The function computes the first buffer that will be used in the array m_storeBuffers (the index curBuffer). As long as curBuffer is greater than zero, the first buffer in the array is not needed and should be removed. The following is the relevant code.
- while (curBuffer > 0)
- Finally, note that all computed data is placed in the wet buffer. Note also that, at the beginning of playback, when there is not enough past audio data stored, the wet buffer is populated with silence.
Orinj Effect framework