![JAR search and dependency download from the Maven repository](/logo.png)
jm.audio.Instrument Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jmusic Show documentation
Show all versions of jmusic Show documentation
JMusic - Java Music Library
The newest version!
/*
Copyright (C) 2000 Andrew Sorensen & Andrew Brown
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or any
later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package jm.audio;
import jm.JMC;
import jm.music.data.Note;
import jm.music.rt.RTLine;
import java.util.Enumeration;
import java.util.Vector;
/**
* An Instrument in jMusic is a chain of AudioObjects which are strung
* together to form a signal processing chain. Instruments chains are each
* run in their own thread.
*
* @author Andrew Sorensen
* @version 1.0, Sun Feb 25 18:42:44 2001
*/
public abstract class Instrument extends Thread implements Runnable, jm.JMC {
//----------------------------------------------
// Attributes
//----------------------------------------------
/**
* Output from instrument to go to a file of the hard drive.
* This is normally associated with using the SampleOut audio object
*/
public final static int RENDER = 0;
/**
* Which output type is the instrument for? Rendering, realtime, or other.
* This flag must be handled by each instrument, as it is not static (global).
*/
protected int output = RENDER;
/**
* Output from instrument should go to RTLine and RTMixer for realtime playback.
* This normally means the SampleOut audio object is NOT part of the audio chain.
*/
public final static int REALTIME = 1;
/**
* the number of samples processed by this instrument for this note
*/
public int iterations;
public boolean finishedNewData = false;
/**
* the primary audio chain objects (ie the first in each chain)
*/
protected Vector primaryAO;
/**
* the final audio chain object
*/
protected AudioObject finalAO = null;
/**
* the number of samples which this Instrument needs to write
*/
protected int numOfSamples = 0;
/**
* the number of channels which this Instrument must supply
*/
protected int numOfChannels = 0;
/**
* buffer size for passing samples between work methods
*/
protected int bufsize = 4096;
/**
* Real time export Buffer which is ALWAYS bufsize (i.e., not
* changed to reflect a change in the notes duration which is why
* we need an rtBufferIndex).
*/
private float[] rtBuffer = new float[bufsize];
/**
* Vector for holding AudioChainListeners
*/
protected volatile Vector listeners = new Vector();
/**
* number of samples processed by this instrument in its lifetime
*/
protected long samplesProcessed = 0;
/**
* This shows whether we can just substitue blank buffers (all zeros)
*/
private boolean restNote = false;
/**
* Signifies whether this Instruments audio chain is finished processing
* NOTE: This may be longer than both the note's duration and the
* note's rhythmvalue !
*/
private boolean finished = true;
/**
* if clear is true the threads wait call will be skipped
*/
private boolean clear = false;
/**
* should the inst block or not (RT should block, non-RT should not
*/
private boolean block = true;
/**
* This is the rtline that this instrument is associated with (RT Only)
*/
private RTLine rtline;
/**
* Index value for rtBuffer
*/
private int index = 0;
/**
* Has the creatChain method been called yet?
*/
private boolean initialised = false;
/**
* The instrument is free to render or play
*/
private boolean okToRun = true;
/* The sample buffer counter */
private int returned;
/* tempory sample bufer */
private float[] buffer;
//----------------------------------------------
// Constructors
//----------------------------------------------
/**
* Simple default constructor
*/
protected Instrument() {
primaryAO = new Vector();
}
//----------------------------------------------
// Class Methods
//----------------------------------------------
/**
* The renderNote method is called whenever the instrument is asked to
* render a new note. This method is responsible for caclulating the
* number of samples required for the note and calls any AudioObjects
* build methods prior to starting the chain.
*
* @param note The note to render
* @param startTime The startTime of the note
* @return true if available for a new note and false if still rendering
*/
public void renderNote(Note note, double startTime) {
this.finalAO = null;
Enumeration enum1 = primaryAO.elements();
//set the numOfSamples variable
AudioObject pao = (AudioObject) primaryAO.elementAt(0);
// continue onward
this.numOfSamples = (int) ((float) pao.getSampleRate() * (float) ((float) note.getDuration()));
this.numOfChannels = pao.channels;
//System.out.println("Instrument: NumOfSamples = " + numOfSamples + " duration = " + note.getDuration());
//do any note specific initialization on the audio chain headers
//which will propogate the build(note) method through the chain
if (note.getFrequency() == (double) REST) {
restNote = true;
} else {
// calc frequency
double frequency = 0.0;
if (note.getPitchType() == Note.MIDI_PITCH && note.getPitch() != JMC.REST &&
note.getPitch() <= Note.MIN_PITCH && note.getPitch() >= Note.MAX_PITCH) {
frequency = jm.JMC.FRQ[note.getPitch()];
} else {
frequency = note.getFrequency();
}
//System.out.println("Instrument class: freq = " + frequency);
if ((double) pao.getSampleRate() * 0.5 < frequency) {
System.out.println("jMusic Instrument error: Sorry, can't render " +
"a note above the Nyquist frequency.");
System.out.println("Sample rate = " + pao.getSampleRate() +
" Pitch frequency = " + note.getFrequency());
System.exit(1);
}
// process
restNote = false;
while (enum1.hasMoreElements()) {
AudioObject ao = (AudioObject) enum1.nextElement();
ao.newNote(note, (startTime - note.getOffset()),
this.numOfSamples);
}
}
}
/**
* Add primary Audio Objects to the primaryAO vector
*/
public void addPrimaryAO(AudioObject ao) {
primaryAO.addElement(ao);
}
/**
* Sets the finalAO for this instrument
*/
public void setFinalAO(AudioObject ao) throws AOException {
if (finalAO == null || finalAO == ao) {
this.finalAO = ao;
} else {
throw new AOException("jMusic Instrument error: " + ao.name, finalAO.name +
" is already set as finalAO.\n" +
" There can only be one finalAO.");
}
}
/**
* Return the finished state of this note
*/
public boolean getFinished() {
return this.finished;
}
/**
* Is the instrument still processing a note?
* If set to false during playback the note will end abruptly.
*/
public void setFinished(boolean state) {
if (this.finished == false) return;
this.finished = state;
}
/**
* Returns the buffer size being used by this instrument.
*
* @return bufsize The size of the buffer being used by this instrument
*/
public int getBufSize() {
return this.bufsize;
}
/**
* Sets the buffer size which is used to pass
* sample data between work methods.
*
* @param bufsize The buffer size to set for this instrument
*/
public void setBufSize(int bufsize) {
this.bufsize = bufsize;
rtBuffer = new float[bufsize];
}
/**
* Returns the number of channels being used by this instrument.
*
* @return numOfChannels The number of channels being used by this instrument
*/
public int getChannels() {
try {
if (this.getInitialised() == false) {
this.createChain();
this.setInitialised(true);
}
} catch (AOException e) {
e.printStackTrace();
}
return ((AudioObject) primaryAO.firstElement()).getChannels();
}
/**
* Returns the sample rate being used by this instrument.
*
* @return sampleRate The sample rate being used by this instrument
*/
public int getSampleRate() {
try {
if (this.getInitialised() == false) {
this.createChain();
this.setInitialised(true);
}
} catch (AOException e) {
e.printStackTrace();
}
return ((AudioObject) primaryAO.firstElement()).getSampleRate();
}
/**
* Returns theinitialsed status of the instrument.
*
* @return initialised
*/
public boolean getInitialised() {
return this.initialised;
}
/**
* Tells if the instrument's creatchain method has been called or not.
*/
public void setInitialised(boolean val) {
this.initialised = val;
}
/**
* Associates an RTLine with this Instrument
*/
public void addRTLine(RTLine rtline) {
this.rtline = rtline;
}
/**
* Return this instruments audio chain listeners
*
* @return listeners return all audio chain listeners
*/
public Enumeration getListeners() {
return this.listeners.elements();
}
/**
* Attaches an AudioChainListener to this instrument
*
* @param listener the AudioChainListener to add
*/
public void addAudioChainListener(AudioChainListener listener) {
this.listeners.addElement(listener);
}
/**
* An array of double values which can be used as controller messages.
* Instruments that wish to implement controllers should override this
* method. Controller values which are not doubles will need to be
* cast to their appropriate types in the overriding method.
*
* @param controlValues
*/
public void setController(double[] controlValues) {
}
/**
* This thread's run method
*/
public void run() {
while (true) {
if (this.okToRun) { // (true)
//??? called in real time only when idle??
//Start pulling samples through the audio chain.
this.finished = false;
rtline.instNote(this, samplesProcessed);
//System.out.println("Instrument: run() : samplesProcessed = "
//+ samplesProcessed + " finished = " + this.finished);
this.iterateChain();
} else {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
}
}
}
/*
* Halts the playback thread.
*/
public void pause() {
this.okToRun = false;
}
/*
* Continues the playback thread.
*/
public void unPause() {
this.okToRun = true;
}
/**
* Set whether the instrument should block or not. This setting should be
* set to true if working in real-time and false if not working in real
* time.
*/
public void setBlock(boolean block) {
this.block = block;
}
/**
* If clear is set to true the iterate method will not block after calling
* controlChange on all its listeners.
*/
public void setClear(boolean clear) {
this.clear = clear;
}
/**
* Check on the current output flag status.
* Normally 0 = render to file, 1 = play via realtime output.
* However, these flags must be handled by each instrument, as they are not global.
*/
public int getOutput() {
return this.output;
}
/**
* Select the output flage value. This is normally used to specify if
* the SampleOut object will be included in the audio chain.
* There are constats in this class for common values,
* RENDER = 0 and REALTIME = 1.
*/
public void setOutput(int out) {
this.output = out;
}
/**
* release is called by RTAudio when the RTLine is ready to start
* processing data again. This is required so that all instruments
* in turn provide the buffer they are currently processing to the
* real time audio stream
*/
public synchronized void release() {
this.notify();
this.clear = true;
}
/**
* block is called to hold any further processing untill all instruments
* are in the same place (in time that is). Block calls wait on this
* instrument and is released by a call to release. The RTAudio class is
* responsible for handling all block and release calls.
*/
public synchronized void block() {
if (!this.clear && this.block) {
try {
this.wait();
} catch (InterruptedException ie) {
//Do anything ?
}
}
this.clear = false;
}
//----------------------------------------------
// Abstract Methods
//----------------------------------------------
/**
* This method is automatically called on startup to initialise
* any AudioObjects used by this instrument
*/
public abstract void createChain() throws AOException;
//----------------------------------------------
// Private Methods
//----------------------------------------------
/**
* The iterate chain method is resposible for pulling the appropriate
* number of samples through the audio chain.
*/
public void iterateChain() {
iterations = 0;
if (this.numOfSamples > 0)
iterations = this.numOfSamples * this.numOfChannels;
returned = 0;
int i;
while (!finished) {
finished = true; //finish unless proved otherwise
//finished = false;
buffer = null;
if (iterations > bufsize || iterations <= 0) {
//System.out.println("BufferSizeA = " + bufsize);
buffer = new float[bufsize];
/*
for(i=0; i 0) {
finished = false;
}
samplesProcessed += returned;
//put any new samples into rtBuffer and pass rtBuffer onto any
//listeners if index has reached bufsize
for (i = 0; i < returned; i++) {
rtBuffer[index++] = buffer[i];
if (index == bufsize) {
index = 0;
Enumeration enum1 = listeners.elements();
while (enum1.hasMoreElements()) {
AudioChainListener acl = (AudioChainListener) enum1.nextElement();
acl.controlChange(rtBuffer, returned, finished);
}
this.block();
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy