feat(jdk8): move files to new folder to avoid resources compiled.

This commit is contained in:
2025-09-07 15:25:52 +08:00
parent 3f0047bf6f
commit 8c35cfb1c0
17415 changed files with 217 additions and 213 deletions

View File

@@ -0,0 +1,459 @@
/*
* Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Control;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineUnavailableException;
/**
* AbstractDataLine
*
* @author Kara Kytle
*/
abstract class AbstractDataLine extends AbstractLine implements DataLine {
// DEFAULTS
// default format
private final AudioFormat defaultFormat;
// default buffer size in bytes
private final int defaultBufferSize;
// the lock for synchronization
protected final Object lock = new Object();
// STATE
// current format
protected AudioFormat format;
// current buffer size in bytes
protected int bufferSize;
private volatile boolean running;
private volatile boolean started;
private volatile boolean active;
/**
* Constructs a new AbstractLine.
*/
protected AbstractDataLine(DataLine.Info info, AbstractMixer mixer, Control[] controls) {
this(info, mixer, controls, null, AudioSystem.NOT_SPECIFIED);
}
/**
* Constructs a new AbstractLine.
*/
protected AbstractDataLine(DataLine.Info info, AbstractMixer mixer, Control[] controls, AudioFormat format, int bufferSize) {
super(info, mixer, controls);
// record the default values
if (format != null) {
defaultFormat = format;
} else {
// default CD-quality
defaultFormat = new AudioFormat(44100.0f, 16, 2, true, Platform.isBigEndian());
}
if (bufferSize > 0) {
defaultBufferSize = bufferSize;
} else {
// 0.5 seconds buffer
defaultBufferSize = ((int) (defaultFormat.getFrameRate() / 2)) * defaultFormat.getFrameSize();
}
// set the initial values to the defaults
this.format = defaultFormat;
this.bufferSize = defaultBufferSize;
}
// DATA LINE METHODS
public final void open(AudioFormat format, int bufferSize) throws LineUnavailableException {
//$$fb 2001-10-09: Bug #4517739: avoiding deadlock by synchronizing to mixer !
synchronized (mixer) {
if (Printer.trace) Printer.trace("> AbstractDataLine.open(format, bufferSize) (class: "+getClass().getName());
// if the line is not currently open, try to open it with this format and buffer size
if (!isOpen()) {
// make sure that the format is specified correctly
// $$fb part of fix for 4679187: Clip.open() throws unexpected Exceptions
Toolkit.isFullySpecifiedAudioFormat(format);
if (Printer.debug) Printer.debug(" need to open the mixer...");
// reserve mixer resources for this line
//mixer.open(this, format, bufferSize);
mixer.open(this);
try {
// open the data line. may throw LineUnavailableException.
implOpen(format, bufferSize);
// if we succeeded, set the open state to true and send events
setOpen(true);
} catch (LineUnavailableException e) {
// release mixer resources for this line and then throw the exception
mixer.close(this);
throw e;
}
} else {
if (Printer.debug) Printer.debug(" dataline already open");
// if the line is already open and the requested format differs from the
// current settings, throw an IllegalStateException
//$$fb 2002-04-02: fix for 4661602: Buffersize is checked when re-opening line
if (!format.matches(getFormat())) {
throw new IllegalStateException("Line is already open with format " + getFormat() +
" and bufferSize " + getBufferSize());
}
//$$fb 2002-07-26: allow changing the buffersize of already open lines
if (bufferSize > 0) {
setBufferSize(bufferSize);
}
}
if (Printer.trace) Printer.trace("< AbstractDataLine.open(format, bufferSize) completed");
}
}
public final void open(AudioFormat format) throws LineUnavailableException {
open(format, AudioSystem.NOT_SPECIFIED);
}
/**
* This implementation always returns 0.
*/
public int available() {
return 0;
}
/**
* This implementation does nothing.
*/
public void drain() {
if (Printer.trace) Printer.trace("AbstractDataLine: drain");
}
/**
* This implementation does nothing.
*/
public void flush() {
if (Printer.trace) Printer.trace("AbstractDataLine: flush");
}
public final void start() {
//$$fb 2001-10-09: Bug #4517739: avoiding deadlock by synchronizing to mixer !
synchronized(mixer) {
if (Printer.trace) Printer.trace("> "+getClass().getName()+".start() - AbstractDataLine");
// $$kk: 06.06.99: if not open, this doesn't work....???
if (isOpen()) {
if (!isStartedRunning()) {
mixer.start(this);
implStart();
running = true;
}
}
}
synchronized(lock) {
lock.notifyAll();
}
if (Printer.trace) Printer.trace("< "+getClass().getName()+".start() - AbstractDataLine");
}
public final void stop() {
//$$fb 2001-10-09: Bug #4517739: avoiding deadlock by synchronizing to mixer !
synchronized(mixer) {
if (Printer.trace) Printer.trace("> "+getClass().getName()+".stop() - AbstractDataLine");
// $$kk: 06.06.99: if not open, this doesn't work.
if (isOpen()) {
if (isStartedRunning()) {
implStop();
mixer.stop(this);
running = false;
// $$kk: 11.10.99: this is not exactly correct, but will probably work
if (started && (!isActive())) {
setStarted(false);
}
}
}
}
synchronized(lock) {
lock.notifyAll();
}
if (Printer.trace) Printer.trace("< "+getClass().getName()+".stop() - AbstractDataLine");
}
// $$jb: 12.10.99: The official API for this is isRunning().
// Per the denied RFE 4297981,
// the change to isStarted() is technically an unapproved API change.
// The 'started' variable is false when playback of data stops.
// It is changed throughout the implementation with setStarted().
// This state is what should be returned by isRunning() in the API.
// Note that the 'running' variable is true between calls to
// start() and stop(). This state is accessed now through the
// isStartedRunning() method, defined below. I have not changed
// the variable names at this point, since 'running' is accessed
// in MixerSourceLine and MixerClip, and I want to touch as little
// code as possible to change isStarted() back to isRunning().
public final boolean isRunning() {
return started;
}
public final boolean isActive() {
return active;
}
public final long getMicrosecondPosition() {
long microseconds = getLongFramePosition();
if (microseconds != AudioSystem.NOT_SPECIFIED) {
microseconds = Toolkit.frames2micros(getFormat(), microseconds);
}
return microseconds;
}
public final AudioFormat getFormat() {
return format;
}
public final int getBufferSize() {
return bufferSize;
}
/**
* This implementation does NOT change the buffer size
*/
public final int setBufferSize(int newSize) {
return getBufferSize();
}
/**
* This implementation returns AudioSystem.NOT_SPECIFIED.
*/
public final float getLevel() {
return (float)AudioSystem.NOT_SPECIFIED;
}
// HELPER METHODS
/**
* running is true after start is called and before stop is called,
* regardless of whether data is actually being presented.
*/
// $$jb: 12.10.99: calling this method isRunning() conflicts with
// the official API that was once called isStarted(). Since we
// use this method throughout the implementation, I am renaming
// it to isStartedRunning(). This is part of backing out the
// change denied in RFE 4297981.
final boolean isStartedRunning() {
return running;
}
/**
* This method sets the active state and generates
* events if it changes.
*/
final void setActive(boolean active) {
if (Printer.trace) Printer.trace("> AbstractDataLine: setActive(" + active + ")");
//boolean sendEvents = false;
//long position = getLongFramePosition();
synchronized (this) {
//if (Printer.debug) Printer.debug(" AbstractDataLine: setActive: this.active: " + this.active);
//if (Printer.debug) Printer.debug(" active: " + active);
if (this.active != active) {
this.active = active;
//sendEvents = true;
}
}
//if (Printer.debug) Printer.debug(" this.active: " + this.active);
//if (Printer.debug) Printer.debug(" sendEvents: " + sendEvents);
// $$kk: 11.19.99: take ACTIVE / INACTIVE / EOM events out;
// putting them in is technically an API change.
// do not generate ACTIVE / INACTIVE events for now
// if (sendEvents) {
//
// if (active) {
// sendEvents(new LineEvent(this, LineEvent.Type.ACTIVE, position));
// } else {
// sendEvents(new LineEvent(this, LineEvent.Type.INACTIVE, position));
// }
//}
}
/**
* This method sets the started state and generates
* events if it changes.
*/
final void setStarted(boolean started) {
if (Printer.trace) Printer.trace("> AbstractDataLine: setStarted(" + started + ")");
boolean sendEvents = false;
long position = getLongFramePosition();
synchronized (this) {
//if (Printer.debug) Printer.debug(" AbstractDataLine: setStarted: this.started: " + this.started);
//if (Printer.debug) Printer.debug(" started: " + started);
if (this.started != started) {
this.started = started;
sendEvents = true;
}
}
//if (Printer.debug) Printer.debug(" this.started: " + this.started);
//if (Printer.debug) Printer.debug(" sendEvents: " + sendEvents);
if (sendEvents) {
if (started) {
sendEvents(new LineEvent(this, LineEvent.Type.START, position));
} else {
sendEvents(new LineEvent(this, LineEvent.Type.STOP, position));
}
}
if (Printer.trace) Printer.trace("< AbstractDataLine: setStarted completed");
}
/**
* This method generates a STOP event and sets the started state to false.
* It is here for historic reasons when an EOM event existed.
*/
final void setEOM() {
if (Printer.trace) Printer.trace("> AbstractDataLine: setEOM()");
//$$fb 2002-04-21: sometimes, 2 STOP events are generated.
// better use setStarted() to send STOP event.
setStarted(false);
if (Printer.trace) Printer.trace("< AbstractDataLine: setEOM() completed");
}
// OVERRIDES OF ABSTRACT LINE METHODS
/**
* Try to open the line with the current format and buffer size values.
* If the line is not open, these will be the defaults. If the
* line is open, this should return quietly because the values
* requested will match the current ones.
*/
public final void open() throws LineUnavailableException {
if (Printer.trace) Printer.trace("> "+getClass().getName()+".open() - AbstractDataLine");
// this may throw a LineUnavailableException.
open(format, bufferSize);
if (Printer.trace) Printer.trace("< "+getClass().getName()+".open() - AbstractDataLine");
}
/**
* This should also stop the line. The closed line should not be running or active.
* After we close the line, we reset the format and buffer size to the defaults.
*/
public final void close() {
//$$fb 2001-10-09: Bug #4517739: avoiding deadlock by synchronizing to mixer !
synchronized (mixer) {
if (Printer.trace) Printer.trace("> "+getClass().getName()+".close() - in AbstractDataLine.");
if (isOpen()) {
// stop
stop();
// set the open state to false and send events
setOpen(false);
// close resources for this line
implClose();
// release mixer resources for this line
mixer.close(this);
// reset format and buffer size to the defaults
format = defaultFormat;
bufferSize = defaultBufferSize;
}
}
if (Printer.trace) Printer.trace("< "+getClass().getName()+".close() - in AbstractDataLine");
}
// IMPLEMENTATIONS OF ABSTRACT LINE ABSTRACE METHODS
// ABSTRACT METHODS
abstract void implOpen(AudioFormat format, int bufferSize) throws LineUnavailableException;
abstract void implClose();
abstract void implStart();
abstract void implStop();
}

View File

@@ -0,0 +1,239 @@
/*
* Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.util.Map;
import java.util.Vector;
import java.util.WeakHashMap;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Control;
import javax.sound.sampled.Line;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.LineUnavailableException;
/**
* AbstractLine
*
* @author Kara Kytle
*/
abstract class AbstractLine implements Line {
protected final Line.Info info;
protected Control[] controls;
AbstractMixer mixer;
private volatile boolean open;
private final Vector listeners = new Vector();
/**
* Contains event dispatcher per thread group.
*/
private static final Map<ThreadGroup, EventDispatcher> dispatchers =
new WeakHashMap<>();
/**
* Constructs a new AbstractLine.
* @param mixer the mixer with which this line is associated
* @param controls set of supported controls
*/
protected AbstractLine(Line.Info info, AbstractMixer mixer, Control[] controls) {
if (controls == null) {
controls = new Control[0];
}
this.info = info;
this.mixer = mixer;
this.controls = controls;
}
// LINE METHODS
public final Line.Info getLineInfo() {
return info;
}
public final boolean isOpen() {
return open;
}
public final void addLineListener(LineListener listener) {
synchronized(listeners) {
if ( ! (listeners.contains(listener)) ) {
listeners.addElement(listener);
}
}
}
/**
* Removes an audio listener.
* @param listener listener to remove
*/
public final void removeLineListener(LineListener listener) {
listeners.removeElement(listener);
}
/**
* Obtains the set of controls supported by the
* line. If no controls are supported, returns an
* array of length 0.
* @return control set
*/
public final Control[] getControls() {
Control[] returnedArray = new Control[controls.length];
for (int i = 0; i < controls.length; i++) {
returnedArray[i] = controls[i];
}
return returnedArray;
}
public final boolean isControlSupported(Control.Type controlType) {
// protect against a NullPointerException
if (controlType == null) {
return false;
}
for (int i = 0; i < controls.length; i++) {
if (controlType == controls[i].getType()) {
return true;
}
}
return false;
}
public final Control getControl(Control.Type controlType) {
// protect against a NullPointerException
if (controlType != null) {
for (int i = 0; i < controls.length; i++) {
if (controlType == controls[i].getType()) {
return controls[i];
}
}
}
throw new IllegalArgumentException("Unsupported control type: " + controlType);
}
// HELPER METHODS
/**
* This method sets the open state and generates
* events if it changes.
*/
final void setOpen(boolean open) {
if (Printer.trace) Printer.trace("> "+getClass().getName()+" (AbstractLine): setOpen(" + open + ") this.open: " + this.open);
boolean sendEvents = false;
long position = getLongFramePosition();
synchronized (this) {
if (this.open != open) {
this.open = open;
sendEvents = true;
}
}
if (sendEvents) {
if (open) {
sendEvents(new LineEvent(this, LineEvent.Type.OPEN, position));
} else {
sendEvents(new LineEvent(this, LineEvent.Type.CLOSE, position));
}
}
if (Printer.trace) Printer.trace("< "+getClass().getName()+" (AbstractLine): setOpen(" + open + ") this.open: " + this.open);
}
/**
* Send line events.
*/
final void sendEvents(LineEvent event) {
getEventDispatcher().sendAudioEvents(event, listeners);
}
/**
* This is an error in the API: getFramePosition
* should return a long value. At CD quality,
* the int value wraps around after 13 hours.
*/
public final int getFramePosition() {
return (int) getLongFramePosition();
}
/**
* Return the frame position in a long value
* This implementation returns AudioSystem.NOT_SPECIFIED.
*/
public long getLongFramePosition() {
return AudioSystem.NOT_SPECIFIED;
}
// $$kk: 06.03.99: returns the mixer used in construction.
// this is a hold-over from when there was a public method like
// this on line and should be fixed!!
final AbstractMixer getMixer() {
return mixer;
}
final EventDispatcher getEventDispatcher() {
// create and start the global event thread
//TODO need a way to stop this thread when the engine is done
final ThreadGroup tg = Thread.currentThread().getThreadGroup();
synchronized (dispatchers) {
EventDispatcher eventDispatcher = dispatchers.get(tg);
if (eventDispatcher == null) {
eventDispatcher = new EventDispatcher();
dispatchers.put(tg, eventDispatcher);
eventDispatcher.start();
}
return eventDispatcher;
}
}
// ABSTRACT METHODS
public abstract void open() throws LineUnavailableException;
public abstract void close();
}

View File

@@ -0,0 +1,751 @@
/*
* Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
import javax.sound.midi.*;
/**
* Abstract AbstractMidiDevice class representing functionality shared by
* MidiInDevice and MidiOutDevice objects.
*
* @author David Rivas
* @author Kara Kytle
* @author Matthias Pfisterer
* @author Florian Bomers
*/
abstract class AbstractMidiDevice implements MidiDevice, ReferenceCountingDevice {
// STATIC VARIABLES
private static final boolean TRACE_TRANSMITTER = false;
// INSTANCE VARIABLES
private ArrayList<Receiver> receiverList;
private TransmitterList transmitterList;
// lock to protect receiverList and transmitterList
// from simultaneous creation and destruction
// reduces possibility of deadlock, compared to
// synchronizing to the class instance
private final Object traRecLock = new Object();
// DEVICE ATTRIBUTES
private final MidiDevice.Info info;
// DEVICE STATE
private volatile boolean open;
private int openRefCount;
/** List of Receivers and Transmitters that opened the device implicitely.
*/
private List openKeepingObjects;
/**
* This is the device handle returned from native code
*/
protected volatile long id;
// CONSTRUCTOR
/**
* Constructs an AbstractMidiDevice with the specified info object.
* @param info the description of the device
*/
/*
* The initial mode and and only supported mode default to OMNI_ON_POLY.
*/
protected AbstractMidiDevice(MidiDevice.Info info) {
if(Printer.trace) Printer.trace(">> AbstractMidiDevice CONSTRUCTOR");
this.info = info;
openRefCount = 0;
if(Printer.trace) Printer.trace("<< AbstractMidiDevice CONSTRUCTOR completed");
}
// MIDI DEVICE METHODS
public final MidiDevice.Info getDeviceInfo() {
return info;
}
/** Open the device from an application program.
* Setting the open reference count to -1 here prevents Transmitters and Receivers that
* opened the the device implicitly from closing it. The only way to close the device after
* this call is a call to close().
*/
public final void open() throws MidiUnavailableException {
if (Printer.trace) Printer.trace("> AbstractMidiDevice: open()");
synchronized(this) {
openRefCount = -1;
doOpen();
}
if (Printer.trace) Printer.trace("< AbstractMidiDevice: open() completed");
}
/** Open the device implicitly.
* This method is intended to be used by AbstractReceiver
* and BasicTransmitter. Actually, it is called by getReceiverReferenceCounting() and
* getTransmitterReferenceCounting(). These, in turn, are called by MidiSytem on calls to
* getReceiver() and getTransmitter(). The former methods should pass the Receiver or
* Transmitter just created as the object parameter to this method. Storing references to
* these objects is necessary to be able to decide later (when it comes to closing) if
* R/T's are ones that opened the device implicitly.
*
* @object The Receiver or Transmitter instance that triggered this implicit open.
*/
private void openInternal(Object object) throws MidiUnavailableException {
if (Printer.trace) Printer.trace("> AbstractMidiDevice: openInternal()");
synchronized(this) {
if (openRefCount != -1) {
openRefCount++;
getOpenKeepingObjects().add(object);
}
// double calls to doOpens() will be catched by the open flag.
doOpen();
}
if (Printer.trace) Printer.trace("< AbstractMidiDevice: openInternal() completed");
}
private void doOpen() throws MidiUnavailableException {
if (Printer.trace) Printer.trace("> AbstractMidiDevice: doOpen()");
synchronized(this) {
if (! isOpen()) {
implOpen();
open = true;
}
}
if (Printer.trace) Printer.trace("< AbstractMidiDevice: doOpen() completed");
}
public final void close() {
if (Printer.trace) Printer.trace("> AbstractMidiDevice: close()");
synchronized (this) {
doClose();
openRefCount = 0;
}
if (Printer.trace) Printer.trace("< AbstractMidiDevice: close() completed");
}
/** Close the device for an object that implicitely opened it.
* This method is intended to be used by Transmitter.close() and Receiver.close().
* Those methods should pass this for the object parameter. Since Transmitters or Receivers
* do not know if their device has been opened implicitely because of them, they call this
* method in any case. This method now is able to seperate Receivers/Transmitters that opened
* the device implicitely from those that didn't by looking up the R/T in the
* openKeepingObjects list. Only if the R/T is contained there, the reference count is
* reduced.
*
* @param object The object that might have been opening the device implicitely (for now,
* this may be a Transmitter or receiver).
*/
public final void closeInternal(Object object) {
if (Printer.trace) Printer.trace("> AbstractMidiDevice: closeInternal()");
synchronized(this) {
if (getOpenKeepingObjects().remove(object)) {
if (openRefCount > 0) {
openRefCount--;
if (openRefCount == 0) {
doClose();
}
}
}
}
if (Printer.trace) Printer.trace("< AbstractMidiDevice: closeInternal() completed");
}
public final void doClose() {
if (Printer.trace) Printer.trace("> AbstractMidiDevice: doClose()");
synchronized(this) {
if (isOpen()) {
implClose();
open = false;
}
}
if (Printer.trace) Printer.trace("< AbstractMidiDevice: doClose() completed");
}
public final boolean isOpen() {
return open;
}
protected void implClose() {
synchronized (traRecLock) {
if (receiverList != null) {
// close all receivers
for(int i = 0; i < receiverList.size(); i++) {
receiverList.get(i).close();
}
receiverList.clear();
}
if (transmitterList != null) {
// close all transmitters
transmitterList.close();
}
}
}
/**
* This implementation always returns -1.
* Devices that actually provide this should over-ride
* this method.
*/
public long getMicrosecondPosition() {
return -1;
}
/** Return the maximum number of Receivers supported by this device.
Depending on the return value of hasReceivers(), this method returns either 0 or -1.
Subclasses should rather override hasReceivers() than override this method.
*/
public final int getMaxReceivers() {
if (hasReceivers()) {
return -1;
} else {
return 0;
}
}
/** Return the maximum number of Transmitters supported by this device.
Depending on the return value of hasTransmitters(), this method returns either 0 or -1.
Subclasses should override hasTransmitters().
*/
public final int getMaxTransmitters() {
if (hasTransmitters()) {
return -1;
} else {
return 0;
}
}
/** Retrieve a Receiver for this device.
This method returns the value returned by createReceiver(), if it doesn't throw
an exception. Subclasses should rather override createReceiver() than override
this method.
If createReceiver returns a Receiver, it is added to the internal list
of Receivers (see getReceiversList)
*/
public final Receiver getReceiver() throws MidiUnavailableException {
Receiver receiver;
synchronized (traRecLock) {
receiver = createReceiver(); // may throw MidiUnavailableException
getReceiverList().add(receiver);
}
return receiver;
}
public final List<Receiver> getReceivers() {
List<Receiver> recs;
synchronized (traRecLock) {
if (receiverList == null) {
recs = Collections.unmodifiableList(new ArrayList<Receiver>(0));
} else {
recs = Collections.unmodifiableList
((List<Receiver>) (receiverList.clone()));
}
}
return recs;
}
/**
* This implementation uses createTransmitter, which may throw an exception.
* If a transmitter is returned in createTransmitter, it is added to the internal
* TransmitterList
*/
public final Transmitter getTransmitter() throws MidiUnavailableException {
Transmitter transmitter;
synchronized (traRecLock) {
transmitter = createTransmitter(); // may throw MidiUnavailableException
getTransmitterList().add(transmitter);
}
return transmitter;
}
public final List<Transmitter> getTransmitters() {
List<Transmitter> tras;
synchronized (traRecLock) {
if (transmitterList == null
|| transmitterList.transmitters.size() == 0) {
tras = Collections.unmodifiableList(new ArrayList<Transmitter>(0));
} else {
tras = Collections.unmodifiableList((List<Transmitter>) (transmitterList.transmitters.clone()));
}
}
return tras;
}
// HELPER METHODS
final long getId() {
return id;
}
// REFERENCE COUNTING
/** Retrieve a Receiver and open the device implicitly.
This method is called by MidiSystem.getReceiver().
*/
public final Receiver getReceiverReferenceCounting()
throws MidiUnavailableException {
/* Keep this order of commands! If getReceiver() throws an exception,
openInternal() should not be called!
*/
Receiver receiver;
synchronized (traRecLock) {
receiver = getReceiver();
AbstractMidiDevice.this.openInternal(receiver);
}
return receiver;
}
/** Retrieve a Transmitter and open the device implicitly.
This method is called by MidiSystem.getTransmitter().
*/
public final Transmitter getTransmitterReferenceCounting()
throws MidiUnavailableException {
/* Keep this order of commands! If getTransmitter() throws an exception,
openInternal() should not be called!
*/
Transmitter transmitter;
synchronized (traRecLock) {
transmitter = getTransmitter();
AbstractMidiDevice.this.openInternal(transmitter);
}
return transmitter;
}
/** Return the list of objects that have opened the device implicitely.
*/
private synchronized List getOpenKeepingObjects() {
if (openKeepingObjects == null) {
openKeepingObjects = new ArrayList();
}
return openKeepingObjects;
}
// RECEIVER HANDLING METHODS
/** Return the internal list of Receivers, possibly creating it first.
*/
private List<Receiver> getReceiverList() {
synchronized (traRecLock) {
if (receiverList == null) {
receiverList = new ArrayList<Receiver>();
}
}
return receiverList;
}
/** Returns if this device supports Receivers.
Subclasses that use Receivers should override this method to
return true. They also should override createReceiver().
@return true, if the device supports Receivers, false otherwise.
*/
protected boolean hasReceivers() {
return false;
}
/** Create a Receiver object.
throwing an exception here means that Receivers aren't enabled.
Subclasses that use Receivers should override this method with
one that returns objects implementing Receiver.
Classes overriding this method should also override hasReceivers()
to return true.
*/
protected Receiver createReceiver() throws MidiUnavailableException {
throw new MidiUnavailableException("MIDI IN receiver not available");
}
// TRANSMITTER HANDLING
/** Return the internal list of Transmitters, possibly creating it first.
*/
final TransmitterList getTransmitterList() {
synchronized (traRecLock) {
if (transmitterList == null) {
transmitterList = new TransmitterList();
}
}
return transmitterList;
}
/** Returns if this device supports Transmitters.
Subclasses that use Transmitters should override this method to
return true. They also should override createTransmitter().
@return true, if the device supports Transmitters, false otherwise.
*/
protected boolean hasTransmitters() {
return false;
}
/** Create a Transmitter object.
throwing an exception here means that Transmitters aren't enabled.
Subclasses that use Transmitters should override this method with
one that returns objects implementing Transmitters.
Classes overriding this method should also override hasTransmitters()
to return true.
*/
protected Transmitter createTransmitter() throws MidiUnavailableException {
throw new MidiUnavailableException("MIDI OUT transmitter not available");
}
// ABSTRACT METHODS
protected abstract void implOpen() throws MidiUnavailableException;
/**
* close this device if discarded by the garbage collector
*/
protected final void finalize() {
close();
}
// INNER CLASSES
/** Base class for Receivers.
Subclasses that use Receivers must use this base class, since it
contains magic necessary to manage implicit closing the device.
This is necessary for Receivers retrieved via MidiSystem.getReceiver()
(which opens the device implicitely).
*/
abstract class AbstractReceiver implements MidiDeviceReceiver {
private volatile boolean open = true;
/** Deliver a MidiMessage.
This method contains magic related to the closed state of a
Receiver. Therefore, subclasses should not override this method.
Instead, they should implement implSend().
*/
@Override
public final synchronized void send(final MidiMessage message,
final long timeStamp) {
if (!open) {
throw new IllegalStateException("Receiver is not open");
}
implSend(message, timeStamp);
}
abstract void implSend(MidiMessage message, long timeStamp);
/** Close the Receiver.
* Here, the call to the magic method closeInternal() takes place.
* Therefore, subclasses that override this method must call
* 'super.close()'.
*/
@Override
public final void close() {
open = false;
synchronized (AbstractMidiDevice.this.traRecLock) {
AbstractMidiDevice.this.getReceiverList().remove(this);
}
AbstractMidiDevice.this.closeInternal(this);
}
@Override
public final MidiDevice getMidiDevice() {
return AbstractMidiDevice.this;
}
final boolean isOpen() {
return open;
}
//$$fb is that a good idea?
//protected void finalize() {
// close();
//}
} // class AbstractReceiver
/**
* Transmitter base class.
* This class especially makes sure the device is closed if it
* has been opened implicitly by a call to MidiSystem.getTransmitter().
* The logic of doing so is actually in closeInternal().
*
* Also, it has some optimizations regarding sending to the Receivers,
* for known Receivers, and managing itself in the TransmitterList.
*/
class BasicTransmitter implements MidiDeviceTransmitter {
private Receiver receiver = null;
TransmitterList tlist = null;
protected BasicTransmitter() {
}
private void setTransmitterList(TransmitterList tlist) {
this.tlist = tlist;
}
public final void setReceiver(Receiver receiver) {
if (tlist != null && this.receiver != receiver) {
if (Printer.debug) Printer.debug("Transmitter "+toString()+": set receiver "+receiver);
tlist.receiverChanged(this, this.receiver, receiver);
this.receiver = receiver;
}
}
public final Receiver getReceiver() {
return receiver;
}
/** Close the Transmitter.
* Here, the call to the magic method closeInternal() takes place.
* Therefore, subclasses that override this method must call
* 'super.close()'.
*/
public final void close() {
AbstractMidiDevice.this.closeInternal(this);
if (tlist != null) {
tlist.receiverChanged(this, this.receiver, null);
tlist.remove(this);
tlist = null;
}
}
public final MidiDevice getMidiDevice() {
return AbstractMidiDevice.this;
}
} // class BasicTransmitter
/**
* a class to manage a list of transmitters
*/
final class TransmitterList {
private final ArrayList<Transmitter> transmitters = new ArrayList<Transmitter>();
private MidiOutDevice.MidiOutReceiver midiOutReceiver;
// how many transmitters must be present for optimized
// handling
private int optimizedReceiverCount = 0;
private void add(Transmitter t) {
synchronized(transmitters) {
transmitters.add(t);
}
if (t instanceof BasicTransmitter) {
((BasicTransmitter) t).setTransmitterList(this);
}
if (Printer.debug) Printer.debug("--added transmitter "+t);
}
private void remove(Transmitter t) {
synchronized(transmitters) {
int index = transmitters.indexOf(t);
if (index >= 0) {
transmitters.remove(index);
if (Printer.debug) Printer.debug("--removed transmitter "+t);
}
}
}
private void receiverChanged(BasicTransmitter t,
Receiver oldR,
Receiver newR) {
synchronized(transmitters) {
// some optimization
if (midiOutReceiver == oldR) {
midiOutReceiver = null;
}
if (newR != null) {
if ((newR instanceof MidiOutDevice.MidiOutReceiver)
&& (midiOutReceiver == null)) {
midiOutReceiver = ((MidiOutDevice.MidiOutReceiver) newR);
}
}
optimizedReceiverCount =
((midiOutReceiver!=null)?1:0);
}
// more potential for optimization here
}
/** closes all transmitters and empties the list */
void close() {
synchronized (transmitters) {
for(int i = 0; i < transmitters.size(); i++) {
transmitters.get(i).close();
}
transmitters.clear();
}
if (Printer.trace) Printer.trace("TransmitterList.close() succeeded");
}
/**
* Send this message to all receivers
* status = packedMessage & 0xFF
* data1 = (packedMessage & 0xFF00) >> 8;
* data1 = (packedMessage & 0xFF0000) >> 16;
*/
void sendMessage(int packedMessage, long timeStamp) {
try {
synchronized(transmitters) {
int size = transmitters.size();
if (optimizedReceiverCount == size) {
if (midiOutReceiver != null) {
if (TRACE_TRANSMITTER) Printer.println("Sending packed message to MidiOutReceiver");
midiOutReceiver.sendPackedMidiMessage(packedMessage, timeStamp);
}
} else {
if (TRACE_TRANSMITTER) Printer.println("Sending packed message to "+size+" transmitter's receivers");
for (int i = 0; i < size; i++) {
Receiver receiver = ((Transmitter)transmitters.get(i)).getReceiver();
if (receiver != null) {
if (optimizedReceiverCount > 0) {
if (receiver instanceof MidiOutDevice.MidiOutReceiver) {
((MidiOutDevice.MidiOutReceiver) receiver).sendPackedMidiMessage(packedMessage, timeStamp);
} else {
receiver.send(new FastShortMessage(packedMessage), timeStamp);
}
} else {
receiver.send(new FastShortMessage(packedMessage), timeStamp);
}
}
}
}
}
} catch (InvalidMidiDataException e) {
// this happens when invalid data comes over the wire. Ignore it.
}
}
void sendMessage(byte[] data, long timeStamp) {
try {
synchronized(transmitters) {
int size = transmitters.size();
if (TRACE_TRANSMITTER) Printer.println("Sending long message to "+size+" transmitter's receivers");
for (int i = 0; i < size; i++) {
Receiver receiver = ((Transmitter)transmitters.get(i)).getReceiver();
if (receiver != null) {
//$$fb 2002-04-02: SysexMessages are mutable, so
// an application could change the contents of this object,
// or try to use the object later. So we can't get around object creation
// But the array need not be unique for each FastSysexMessage object,
// because it cannot be modified.
receiver.send(new FastSysexMessage(data), timeStamp);
}
}
}
} catch (InvalidMidiDataException e) {
// this happens when invalid data comes over the wire. Ignore it.
return;
}
}
/**
* Send this message to all transmitters
*/
void sendMessage(MidiMessage message, long timeStamp) {
if (message instanceof FastShortMessage) {
sendMessage(((FastShortMessage) message).getPackedMsg(), timeStamp);
return;
}
synchronized(transmitters) {
int size = transmitters.size();
if (optimizedReceiverCount == size) {
if (midiOutReceiver != null) {
if (TRACE_TRANSMITTER) Printer.println("Sending MIDI message to MidiOutReceiver");
midiOutReceiver.send(message, timeStamp);
}
} else {
if (TRACE_TRANSMITTER) Printer.println("Sending MIDI message to "+size+" transmitter's receivers");
for (int i = 0; i < size; i++) {
Receiver receiver = ((Transmitter)transmitters.get(i)).getReceiver();
if (receiver != null) {
//$$fb 2002-04-02: ShortMessages are mutable, so
// an application could change the contents of this object,
// or try to use the object later.
// We violate this spec here, to avoid costly (and gc-intensive)
// object creation for potentially hundred of messages per second.
// The spec should be changed to allow Immutable MidiMessages
// (i.e. throws InvalidStateException or so in setMessage)
receiver.send(message, timeStamp);
}
}
}
}
}
} // TransmitterList
}

View File

@@ -0,0 +1,196 @@
/*
* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import javax.sound.midi.MidiDevice;
import javax.sound.midi.spi.MidiDeviceProvider;
/**
* Super class for MIDI input or output device provider.
*
* @author Florian Bomers
*/
public abstract class AbstractMidiDeviceProvider extends MidiDeviceProvider {
private static final boolean enabled;
/**
* Create objects representing all MIDI output devices on the system.
*/
static {
if (Printer.trace) Printer.trace("AbstractMidiDeviceProvider: static");
Platform.initialize();
enabled = Platform.isMidiIOEnabled();
if (Printer.trace) Printer.trace("AbstractMidiDeviceProvider: enabled: " + enabled);
// $$fb number of MIDI devices may change with time
// also for memory's sake, do not initialize the arrays here
}
final synchronized void readDeviceInfos() {
Info[] infos = getInfoCache();
MidiDevice[] devices = getDeviceCache();
if (!enabled) {
if (infos == null || infos.length != 0) {
setInfoCache(new Info[0]);
}
if (devices == null || devices.length != 0) {
setDeviceCache(new MidiDevice[0]);
}
return;
}
int oldNumDevices = (infos==null)?-1:infos.length;
int newNumDevices = getNumDevices();
if (oldNumDevices != newNumDevices) {
if (Printer.trace) Printer.trace(getClass().toString()
+": readDeviceInfos: old numDevices: "+oldNumDevices
+" newNumDevices: "+ newNumDevices);
// initialize the arrays
Info[] newInfos = new Info[newNumDevices];
MidiDevice[] newDevices = new MidiDevice[newNumDevices];
for (int i = 0; i < newNumDevices; i++) {
Info newInfo = createInfo(i);
// in case that we are re-reading devices, try to find
// the previous one and reuse it
if (infos != null) {
for (int ii = 0; ii < infos.length; ii++) {
Info info = infos[ii];
if (info != null && info.equalStrings(newInfo)) {
// new info matches the still existing info. Use old one
newInfos[i] = info;
info.setIndex(i);
infos[ii] = null; // prevent re-use
newDevices[i] = devices[ii];
devices[ii] = null;
break;
}
}
}
if (newInfos[i] == null) {
newInfos[i] = newInfo;
}
}
// the remaining MidiDevice.Info instances in the infos array
// have become obsolete.
if (infos != null) {
for (int i = 0; i < infos.length; i++) {
if (infos[i] != null) {
// disable this device info
infos[i].setIndex(-1);
}
// what to do with the MidiDevice instances that are left
// in the devices array ?? Close them ?
}
}
// commit new list of infos.
setInfoCache(newInfos);
setDeviceCache(newDevices);
}
}
public final MidiDevice.Info[] getDeviceInfo() {
readDeviceInfos();
Info[] infos = getInfoCache();
MidiDevice.Info[] localArray = new MidiDevice.Info[infos.length];
System.arraycopy(infos, 0, localArray, 0, infos.length);
return localArray;
}
public final MidiDevice getDevice(MidiDevice.Info info) {
if (info instanceof Info) {
readDeviceInfos();
MidiDevice[] devices = getDeviceCache();
Info[] infos = getInfoCache();
Info thisInfo = (Info) info;
int index = thisInfo.getIndex();
if (index >= 0 && index < devices.length && infos[index] == info) {
if (devices[index] == null) {
devices[index] = createDevice(thisInfo);
}
if (devices[index] != null) {
return devices[index];
}
}
}
throw new IllegalArgumentException("MidiDevice " + info.toString()
+ " not supported by this provider.");
}
// INNER CLASSES
/**
* Info class for MidiDevices. Adds an index value for
* making native references to a particular device.
*/
static class Info extends MidiDevice.Info {
private int index;
Info(String name, String vendor, String description, String version, int index) {
super(name, vendor, description, version);
this.index = index;
}
final boolean equalStrings(Info info) {
return (info != null
&& getName().equals(info.getName())
&& getVendor().equals(info.getVendor())
&& getDescription().equals(info.getDescription())
&& getVersion().equals(info.getVersion()));
}
final int getIndex() {
return index;
}
final void setIndex(int index) {
this.index = index;
}
} // class Info
// ABSTRACT METHODS
abstract int getNumDevices();
abstract MidiDevice[] getDeviceCache();
abstract void setDeviceCache(MidiDevice[] devices);
abstract Info[] getInfoCache();
abstract void setInfoCache(Info[] infos);
abstract Info createInfo(int index);
abstract MidiDevice createDevice(Info info);
}

View File

@@ -0,0 +1,556 @@
/*
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.util.Vector;
import javax.sound.sampled.Control;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.Line;
import javax.sound.sampled.LineUnavailableException;
/**
* Abstract Mixer. Implements Mixer (with abstract methods) and specifies
* some other common methods for use by our implementation.
*
* @author Kara Kytle
*/
//$$fb 2002-07-26: let AbstractMixer be an AbstractLine and NOT an AbstractDataLine!
abstract class AbstractMixer extends AbstractLine implements Mixer {
// STATIC VARIABLES
protected static final int PCM = 0;
protected static final int ULAW = 1;
protected static final int ALAW = 2;
// IMMUTABLE PROPERTIES
/**
* Info object describing this mixer.
*/
private final Mixer.Info mixerInfo;
/**
* source lines provided by this mixer
*/
protected Line.Info[] sourceLineInfo;
/**
* target lines provided by this mixer
*/
protected Line.Info[] targetLineInfo;
/**
* if any line of this mixer is started
*/
private boolean started = false;
/**
* if this mixer had been opened manually with open()
* If it was, then it won't be closed automatically,
* only when close() is called manually.
*/
private boolean manuallyOpened = false;
/**
* Supported formats for the mixer.
*/
//$$fb DELETE
//protected Vector formats = new Vector();
// STATE VARIABLES
/**
* Source lines (ports) currently open
*/
private final Vector sourceLines = new Vector();
/**
* Target lines currently open.
*/
private final Vector targetLines = new Vector();
/**
* Constructs a new AbstractMixer.
* @param mixer the mixer with which this line is associated
* @param controls set of supported controls
*/
protected AbstractMixer(Mixer.Info mixerInfo,
Control[] controls,
Line.Info[] sourceLineInfo,
Line.Info[] targetLineInfo) {
// Line.Info, AbstractMixer, Control[]
super(new Line.Info(Mixer.class), null, controls);
// setup the line part
this.mixer = this;
if (controls == null) {
controls = new Control[0];
}
// setup the mixer part
this.mixerInfo = mixerInfo;
this.sourceLineInfo = sourceLineInfo;
this.targetLineInfo = targetLineInfo;
}
// MIXER METHODS
public final Mixer.Info getMixerInfo() {
return mixerInfo;
}
public final Line.Info[] getSourceLineInfo() {
Line.Info[] localArray = new Line.Info[sourceLineInfo.length];
System.arraycopy(sourceLineInfo, 0, localArray, 0, sourceLineInfo.length);
return localArray;
}
public final Line.Info[] getTargetLineInfo() {
Line.Info[] localArray = new Line.Info[targetLineInfo.length];
System.arraycopy(targetLineInfo, 0, localArray, 0, targetLineInfo.length);
return localArray;
}
public final Line.Info[] getSourceLineInfo(Line.Info info) {
int i;
Vector vec = new Vector();
for (i = 0; i < sourceLineInfo.length; i++) {
if (info.matches(sourceLineInfo[i])) {
vec.addElement(sourceLineInfo[i]);
}
}
Line.Info[] returnedArray = new Line.Info[vec.size()];
for (i = 0; i < returnedArray.length; i++) {
returnedArray[i] = (Line.Info)vec.elementAt(i);
}
return returnedArray;
}
public final Line.Info[] getTargetLineInfo(Line.Info info) {
int i;
Vector vec = new Vector();
for (i = 0; i < targetLineInfo.length; i++) {
if (info.matches(targetLineInfo[i])) {
vec.addElement(targetLineInfo[i]);
}
}
Line.Info[] returnedArray = new Line.Info[vec.size()];
for (i = 0; i < returnedArray.length; i++) {
returnedArray[i] = (Line.Info)vec.elementAt(i);
}
return returnedArray;
}
public final boolean isLineSupported(Line.Info info) {
int i;
for (i = 0; i < sourceLineInfo.length; i++) {
if (info.matches(sourceLineInfo[i])) {
return true;
}
}
for (i = 0; i < targetLineInfo.length; i++) {
if (info.matches(targetLineInfo[i])) {
return true;
}
}
return false;
}
public abstract Line getLine(Line.Info info) throws LineUnavailableException;
public abstract int getMaxLines(Line.Info info);
protected abstract void implOpen() throws LineUnavailableException;
protected abstract void implStart();
protected abstract void implStop();
protected abstract void implClose();
public final Line[] getSourceLines() {
Line[] localLines;
synchronized(sourceLines) {
localLines = new Line[sourceLines.size()];
for (int i = 0; i < localLines.length; i++) {
localLines[i] = (Line)sourceLines.elementAt(i);
}
}
return localLines;
}
public final Line[] getTargetLines() {
Line[] localLines;
synchronized(targetLines) {
localLines = new Line[targetLines.size()];
for (int i = 0; i < localLines.length; i++) {
localLines[i] = (Line)targetLines.elementAt(i);
}
}
return localLines;
}
/**
* Default implementation always throws an exception.
*/
public final void synchronize(Line[] lines, boolean maintainSync) {
throw new IllegalArgumentException("Synchronization not supported by this mixer.");
}
/**
* Default implementation always throws an exception.
*/
public final void unsynchronize(Line[] lines) {
throw new IllegalArgumentException("Synchronization not supported by this mixer.");
}
/**
* Default implementation always returns false.
*/
public final boolean isSynchronizationSupported(Line[] lines,
boolean maintainSync) {
return false;
}
// OVERRIDES OF ABSTRACT DATA LINE METHODS
/**
* This implementation tries to open the mixer with its current format and buffer size settings.
*/
public final synchronized void open() throws LineUnavailableException {
open(true);
}
/**
* This implementation tries to open the mixer with its current format and buffer size settings.
*/
final synchronized void open(boolean manual) throws LineUnavailableException {
if (Printer.trace) Printer.trace(">> AbstractMixer: open()");
if (!isOpen()) {
implOpen();
// if the mixer is not currently open, set open to true and send event
setOpen(true);
if (manual) {
manuallyOpened = true;
}
}
if (Printer.trace) Printer.trace("<< AbstractMixer: open() succeeded");
}
// METHOD FOR INTERNAL IMPLEMENTATION USE
/**
* The default implementation of this method just determines whether
* this line is a source or target line, calls open(no-arg) on the
* mixer, and adds the line to the appropriate vector.
* The mixer may be opened at a format different than the line's
* format if it is a DataLine.
*/
final synchronized void open(Line line) throws LineUnavailableException {
if (Printer.trace) Printer.trace(">> AbstractMixer: open(line = " + line + ")");
// $$kk: 06.11.99: ignore ourselves for now
if (this.equals(line)) {
if (Printer.trace) Printer.trace("<< AbstractMixer: open(" + line + ") nothing done");
return;
}
// source line?
if (isSourceLine(line.getLineInfo())) {
if (! sourceLines.contains(line) ) {
// call the no-arg open method for the mixer; it should open at its
// default format if it is not open yet
open(false);
// we opened successfully! add the line to the list
sourceLines.addElement(line);
}
} else {
// target line?
if(isTargetLine(line.getLineInfo())) {
if (! targetLines.contains(line) ) {
// call the no-arg open method for the mixer; it should open at its
// default format if it is not open yet
open(false);
// we opened successfully! add the line to the list
targetLines.addElement(line);
}
} else {
if (Printer.err) Printer.err("Unknown line received for AbstractMixer.open(Line): " + line);
}
}
if (Printer.trace) Printer.trace("<< AbstractMixer: open(" + line + ") completed");
}
/**
* Removes this line from the list of open source lines and
* open target lines, if it exists in either.
* If the list is now empty, closes the mixer.
*/
final synchronized void close(Line line) {
if (Printer.trace) Printer.trace(">> AbstractMixer: close(" + line + ")");
// $$kk: 06.11.99: ignore ourselves for now
if (this.equals(line)) {
if (Printer.trace) Printer.trace("<< AbstractMixer: close(" + line + ") nothing done");
return;
}
sourceLines.removeElement(line);
targetLines.removeElement(line);
if (Printer.debug) Printer.debug("AbstractMixer: close(line): sourceLines.size() now: " + sourceLines.size());
if (Printer.debug) Printer.debug("AbstractMixer: close(line): targetLines.size() now: " + targetLines.size());
if (sourceLines.isEmpty() && targetLines.isEmpty() && !manuallyOpened) {
if (Printer.trace) Printer.trace("AbstractMixer: close(" + line + "): need to close the mixer");
close();
}
if (Printer.trace) Printer.trace("<< AbstractMixer: close(" + line + ") succeeded");
}
/**
* Close all lines and then close this mixer.
*/
public final synchronized void close() {
if (Printer.trace) Printer.trace(">> AbstractMixer: close()");
if (isOpen()) {
// close all source lines
Line[] localLines = getSourceLines();
for (int i = 0; i<localLines.length; i++) {
localLines[i].close();
}
// close all target lines
localLines = getTargetLines();
for (int i = 0; i<localLines.length; i++) {
localLines[i].close();
}
implClose();
// set the open state to false and send events
setOpen(false);
}
manuallyOpened = false;
if (Printer.trace) Printer.trace("<< AbstractMixer: close() succeeded");
}
/**
* Starts the mixer.
*/
final synchronized void start(Line line) {
if (Printer.trace) Printer.trace(">> AbstractMixer: start(" + line + ")");
// $$kk: 06.11.99: ignore ourselves for now
if (this.equals(line)) {
if (Printer.trace) Printer.trace("<< AbstractMixer: start(" + line + ") nothing done");
return;
}
// we just start the mixer regardless of anything else here.
if (!started) {
if (Printer.debug) Printer.debug("AbstractMixer: start(line): starting the mixer");
implStart();
started = true;
}
if (Printer.trace) Printer.trace("<< AbstractMixer: start(" + line + ") succeeded");
}
/**
* Stops the mixer if this was the last running line.
*/
final synchronized void stop(Line line) {
if (Printer.trace) Printer.trace(">> AbstractMixer: stop(" + line + ")");
// $$kk: 06.11.99: ignore ourselves for now
if (this.equals(line)) {
if (Printer.trace) Printer.trace("<< AbstractMixer: stop(" + line + ") nothing done");
return;
}
Vector localSourceLines = (Vector)sourceLines.clone();
for (int i = 0; i < localSourceLines.size(); i++) {
// if any other open line is running, return
// this covers clips and source data lines
if (localSourceLines.elementAt(i) instanceof AbstractDataLine) {
AbstractDataLine sourceLine = (AbstractDataLine)localSourceLines.elementAt(i);
if ( sourceLine.isStartedRunning() && (!sourceLine.equals(line)) ) {
if (Printer.trace) Printer.trace("<< AbstractMixer: stop(" + line + ") found running sourceLine: " + sourceLine);
return;
}
}
}
Vector localTargetLines = (Vector)targetLines.clone();
for (int i = 0; i < localTargetLines.size(); i++) {
// if any other open line is running, return
// this covers target data lines
if (localTargetLines.elementAt(i) instanceof AbstractDataLine) {
AbstractDataLine targetLine = (AbstractDataLine)localTargetLines.elementAt(i);
if ( targetLine.isStartedRunning() && (!targetLine.equals(line)) ) {
if (Printer.trace) Printer.trace("<< AbstractMixer: stop(" + line + ") found running targetLine: " + targetLine);
return;
}
}
}
// otherwise, stop
if (Printer.debug) Printer.debug("AbstractMixer: stop(line): stopping the mixer");
started = false;
implStop();
if (Printer.trace) Printer.trace("<< AbstractMixer: stop(" + line + ") succeeded");
}
/**
* Determines whether this is a source line for this mixer.
* Right now this just checks whether it's supported, but should
* check whether it actually belongs to this mixer....
*/
final boolean isSourceLine(Line.Info info) {
for (int i = 0; i < sourceLineInfo.length; i++) {
if (info.matches(sourceLineInfo[i])) {
return true;
}
}
return false;
}
/**
* Determines whether this is a target line for this mixer.
* Right now this just checks whether it's supported, but should
* check whether it actually belongs to this mixer....
*/
final boolean isTargetLine(Line.Info info) {
for (int i = 0; i < targetLineInfo.length; i++) {
if (info.matches(targetLineInfo[i])) {
return true;
}
}
return false;
}
/**
* Returns the first complete Line.Info object it finds that
* matches the one specified, or null if no matching Line.Info
* object is found.
*/
final Line.Info getLineInfo(Line.Info info) {
if (info == null) {
return null;
}
// $$kk: 05.31.99: need to change this so that
// the format and buffer size get set in the
// returned info object for data lines??
for (int i = 0; i < sourceLineInfo.length; i++) {
if (info.matches(sourceLineInfo[i])) {
return sourceLineInfo[i];
}
}
for (int i = 0; i < targetLineInfo.length; i++) {
if (info.matches(targetLineInfo[i])) {
return targetLineInfo[i];
}
}
return null;
}
}

View File

@@ -0,0 +1,97 @@
/*
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
/**
* AIFF file format.
*
* @author Jan Borgersen
*/
final class AiffFileFormat extends AudioFileFormat {
static final int AIFF_MAGIC = 1179603533;
// for writing AIFF
static final int AIFC_MAGIC = 0x41494643; // 'AIFC'
static final int AIFF_MAGIC2 = 0x41494646; // 'AIFF'
static final int FVER_MAGIC = 0x46564552; // 'FVER'
static final int FVER_TIMESTAMP = 0xA2805140; // timestamp of last AIFF-C update
static final int COMM_MAGIC = 0x434f4d4d; // 'COMM'
static final int SSND_MAGIC = 0x53534e44; // 'SSND'
// compression codes
static final int AIFC_PCM = 0x4e4f4e45; // 'NONE' PCM
static final int AIFC_ACE2 = 0x41434532; // 'ACE2' ACE 2:1 compression
static final int AIFC_ACE8 = 0x41434538; // 'ACE8' ACE 8:3 compression
static final int AIFC_MAC3 = 0x4d414333; // 'MAC3' MACE 3:1 compression
static final int AIFC_MAC6 = 0x4d414336; // 'MAC6' MACE 6:1 compression
static final int AIFC_ULAW = 0x756c6177; // 'ulaw' ITU G.711 u-Law
static final int AIFC_IMA4 = 0x696d6134; // 'ima4' IMA ADPCM
// $$fb static approach not good, but needed for estimation
static final int AIFF_HEADERSIZE = 54;
//$$fb 2001-07-13: added management of header size in this class
/** header size in bytes */
private final int headerSize=AIFF_HEADERSIZE;
/** comm chunk size in bytes, inclusive magic and length field */
private final int commChunkSize=26;
/** FVER chunk size in bytes, inclusive magic and length field */
private final int fverChunkSize=0;
AiffFileFormat( AudioFileFormat aff ) {
this( aff.getType(), aff.getByteLength(), aff.getFormat(), aff.getFrameLength() );
}
AiffFileFormat(Type type, int byteLength, AudioFormat format, int frameLength) {
super(type, byteLength, format, frameLength);
}
int getHeaderSize() {
return headerSize;
}
int getCommChunkSize() {
return commChunkSize;
}
int getFverChunkSize() {
return fverChunkSize;
}
int getSsndChunkOffset() {
return getHeaderSize()-16;
}
}

View File

@@ -0,0 +1,438 @@
/*
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
/**
* AIFF file reader and writer.
*
* @author Kara Kytle
* @author Jan Borgersen
* @author Florian Bomers
*/
public final class AiffFileReader extends SunFileReader {
private static final int MAX_READ_LENGTH = 8;
// METHODS TO IMPLEMENT AudioFileReader
/**
* Obtains the audio file format of the input stream provided. The stream must
* point to valid audio file data. In general, audio file providers may
* need to read some data from the stream before determining whether they
* support it. These parsers must
* be able to mark the stream, read enough data to determine whether they
* support the stream, and, if not, reset the stream's read pointer to its original
* position. If the input stream does not support this, this method may fail
* with an IOException.
* @param stream the input stream from which file format information should be
* extracted
* @return an <code>AudioFileFormat</code> object describing the audio file format
* @throws UnsupportedAudioFileException if the stream does not point to valid audio
* file data recognized by the system
* @throws IOException if an I/O exception occurs
* @see InputStream#markSupported
* @see InputStream#mark
*/
public AudioFileFormat getAudioFileFormat(InputStream stream) throws UnsupportedAudioFileException, IOException {
// fix for 4489272: AudioSystem.getAudioFileFormat() fails for InputStream, but works for URL
AudioFileFormat aff = getCOMM(stream, true);
// the following is not strictly necessary - but was implemented like that in 1.3.0 - 1.4.1
// so I leave it as it was. May remove this for 1.5.0
stream.reset();
return aff;
}
/**
* Obtains the audio file format of the URL provided. The URL must
* point to valid audio file data.
* @param url the URL from which file format information should be
* extracted
* @return an <code>AudioFileFormat</code> object describing the audio file format
* @throws UnsupportedAudioFileException if the URL does not point to valid audio
* file data recognized by the system
* @throws IOException if an I/O exception occurs
*/
public AudioFileFormat getAudioFileFormat(URL url) throws UnsupportedAudioFileException, IOException {
AudioFileFormat fileFormat = null;
InputStream urlStream = url.openStream(); // throws IOException
try {
fileFormat = getCOMM(urlStream, false);
} finally {
urlStream.close();
}
return fileFormat;
}
/**
* Obtains the audio file format of the File provided. The File must
* point to valid audio file data.
* @param file the File from which file format information should be
* extracted
* @return an <code>AudioFileFormat</code> object describing the audio file format
* @throws UnsupportedAudioFileException if the File does not point to valid audio
* file data recognized by the system
* @throws IOException if an I/O exception occurs
*/
public AudioFileFormat getAudioFileFormat(File file) throws UnsupportedAudioFileException, IOException {
AudioFileFormat fileFormat = null;
FileInputStream fis = new FileInputStream(file); // throws IOException
// part of fix for 4325421
try {
fileFormat = getCOMM(fis, false);
} finally {
fis.close();
}
return fileFormat;
}
/**
* Obtains an audio stream from the input stream provided. The stream must
* point to valid audio file data. In general, audio file providers may
* need to read some data from the stream before determining whether they
* support it. These parsers must
* be able to mark the stream, read enough data to determine whether they
* support the stream, and, if not, reset the stream's read pointer to its original
* position. If the input stream does not support this, this method may fail
* with an IOException.
* @param stream the input stream from which the <code>AudioInputStream</code> should be
* constructed
* @return an <code>AudioInputStream</code> object based on the audio file data contained
* in the input stream.
* @throws UnsupportedAudioFileException if the stream does not point to valid audio
* file data recognized by the system
* @throws IOException if an I/O exception occurs
* @see InputStream#markSupported
* @see InputStream#mark
*/
public AudioInputStream getAudioInputStream(InputStream stream) throws UnsupportedAudioFileException, IOException {
// getCOMM leaves the input stream at the beginning of the audio data
AudioFileFormat fileFormat = getCOMM(stream, true); // throws UnsupportedAudioFileException, IOException
// we've got everything, and the stream is at the
// beginning of the audio data, so return an AudioInputStream.
return new AudioInputStream(stream, fileFormat.getFormat(), fileFormat.getFrameLength());
}
/**
* Obtains an audio stream from the URL provided. The URL must
* point to valid audio file data.
* @param url the URL for which the <code>AudioInputStream</code> should be
* constructed
* @return an <code>AudioInputStream</code> object based on the audio file data pointed
* to by the URL
* @throws UnsupportedAudioFileException if the URL does not point to valid audio
* file data recognized by the system
* @throws IOException if an I/O exception occurs
*/
public AudioInputStream getAudioInputStream(URL url) throws UnsupportedAudioFileException, IOException {
InputStream urlStream = url.openStream(); // throws IOException
AudioFileFormat fileFormat = null;
try {
fileFormat = getCOMM(urlStream, false);
} finally {
if (fileFormat == null) {
urlStream.close();
}
}
return new AudioInputStream(urlStream, fileFormat.getFormat(), fileFormat.getFrameLength());
}
/**
* Obtains an audio stream from the File provided. The File must
* point to valid audio file data.
* @param file the File for which the <code>AudioInputStream</code> should be
* constructed
* @return an <code>AudioInputStream</code> object based on the audio file data pointed
* to by the File
* @throws UnsupportedAudioFileException if the File does not point to valid audio
* file data recognized by the system
* @throws IOException if an I/O exception occurs
*/
public AudioInputStream getAudioInputStream(File file)
throws UnsupportedAudioFileException, IOException {
FileInputStream fis = new FileInputStream(file); // throws IOException
AudioFileFormat fileFormat = null;
// part of fix for 4325421
try {
fileFormat = getCOMM(fis, false);
} finally {
if (fileFormat == null) {
fis.close();
}
}
return new AudioInputStream(fis, fileFormat.getFormat(), fileFormat.getFrameLength());
}
//--------------------------------------------------------------------
private AudioFileFormat getCOMM(InputStream is, boolean doReset)
throws UnsupportedAudioFileException, IOException {
DataInputStream dis = new DataInputStream(is);
if (doReset) {
dis.mark(MAX_READ_LENGTH);
}
// assumes a stream at the beginning of the file which has already
// passed the magic number test...
// leaves the input stream at the beginning of the audio data
int fileRead = 0;
int dataLength = 0;
AudioFormat format = null;
// Read the magic number
int magic = dis.readInt();
// $$fb: fix for 4369044: javax.sound.sampled.AudioSystem.getAudioInputStream() works wrong with Cp037
if (magic != AiffFileFormat.AIFF_MAGIC) {
// not AIFF, throw exception
if (doReset) {
dis.reset();
}
throw new UnsupportedAudioFileException("not an AIFF file");
}
int length = dis.readInt();
int iffType = dis.readInt();
fileRead += 12;
int totallength;
if(length <= 0 ) {
length = AudioSystem.NOT_SPECIFIED;
totallength = AudioSystem.NOT_SPECIFIED;
} else {
totallength = length + 8;
}
// Is this an AIFC or just plain AIFF file.
boolean aifc = false;
// $$fb: fix for 4369044: javax.sound.sampled.AudioSystem.getAudioInputStream() works wrong with Cp037
if (iffType == AiffFileFormat.AIFC_MAGIC) {
aifc = true;
}
// Loop through the AIFF chunks until
// we get to the SSND chunk.
boolean ssndFound = false;
while (!ssndFound) {
// Read the chunk name
int chunkName = dis.readInt();
int chunkLen = dis.readInt();
fileRead += 8;
int chunkRead = 0;
// Switch on the chunk name.
switch (chunkName) {
case AiffFileFormat.FVER_MAGIC:
// Ignore format version for now.
break;
case AiffFileFormat.COMM_MAGIC:
// AIFF vs. AIFC
// $$fb: fix for 4399551: Repost of bug candidate: cannot replay aif file (Review ID: 108108)
if ((!aifc && chunkLen < 18) || (aifc && chunkLen < 22)) {
throw new UnsupportedAudioFileException("Invalid AIFF/COMM chunksize");
}
// Read header info.
int channels = dis.readUnsignedShort();
if (channels <= 0) {
throw new UnsupportedAudioFileException("Invalid number of channels");
}
dis.readInt(); // numSampleFrames
int sampleSizeInBits = dis.readUnsignedShort();
if (sampleSizeInBits < 1 || sampleSizeInBits > 32) {
throw new UnsupportedAudioFileException("Invalid AIFF/COMM sampleSize");
}
float sampleRate = (float) read_ieee_extended(dis);
chunkRead += (2 + 4 + 2 + 10);
// If this is not AIFC then we assume it's
// a linearly encoded file.
AudioFormat.Encoding encoding = AudioFormat.Encoding.PCM_SIGNED;
if (aifc) {
int enc = dis.readInt(); chunkRead += 4;
switch (enc) {
case AiffFileFormat.AIFC_PCM:
encoding = AudioFormat.Encoding.PCM_SIGNED;
break;
case AiffFileFormat.AIFC_ULAW:
encoding = AudioFormat.Encoding.ULAW;
sampleSizeInBits = 8; // Java Sound convention
break;
default:
throw new UnsupportedAudioFileException("Invalid AIFF encoding");
}
}
int frameSize = calculatePCMFrameSize(sampleSizeInBits, channels);
//$fb what's that ??
//if (sampleSizeInBits == 8) {
// encoding = AudioFormat.Encoding.PCM_SIGNED;
//}
format = new AudioFormat(encoding, sampleRate,
sampleSizeInBits, channels,
frameSize, sampleRate, true);
break;
case AiffFileFormat.SSND_MAGIC:
// Data chunk.
// we are getting *weird* numbers for chunkLen sometimes;
// this really should be the size of the data chunk....
int dataOffset = dis.readInt();
int blocksize = dis.readInt();
chunkRead += 8;
// okay, now we are done reading the header. we need to set the size
// of the data segment. we know that sometimes the value we get for
// the chunksize is absurd. this is the best i can think of:if the
// value seems okay, use it. otherwise, we get our value of
// length by assuming that everything left is the data segment;
// its length should be our original length (for all AIFF data chunks)
// minus what we've read so far.
// $$kk: we should be able to get length for the data chunk right after
// we find "SSND." however, some aiff files give *weird* numbers. what
// is going on??
if (chunkLen < length) {
dataLength = chunkLen - chunkRead;
} else {
// $$kk: 11.03.98: this seems dangerous!
dataLength = length - (fileRead + chunkRead);
}
ssndFound = true;
break;
} // switch
fileRead += chunkRead;
// skip the remainder of this chunk
if (!ssndFound) {
int toSkip = chunkLen - chunkRead;
if (toSkip > 0) {
fileRead += dis.skipBytes(toSkip);
}
}
} // while
if (format == null) {
throw new UnsupportedAudioFileException("missing COMM chunk");
}
AudioFileFormat.Type type = aifc?AudioFileFormat.Type.AIFC:AudioFileFormat.Type.AIFF;
return new AiffFileFormat(type, totallength, format, dataLength / format.getFrameSize());
}
// HELPER METHODS
/** write_ieee_extended(DataOutputStream dos, double f) throws IOException {
* Extended precision IEEE floating-point conversion routine.
* @argument DataOutputStream
* @argument double
* @return void
* @exception IOException
*/
private void write_ieee_extended(DataOutputStream dos, double f) throws IOException {
int exponent = 16398;
double highMantissa = f;
// For now write the integer portion of f
// $$jb: 03.30.99: stay in synch with JMF on this!!!!
while (highMantissa < 44000) {
highMantissa *= 2;
exponent--;
}
dos.writeShort(exponent);
dos.writeInt( ((int) highMantissa) << 16);
dos.writeInt(0); // low Mantissa
}
/**
* read_ieee_extended
* Extended precision IEEE floating-point conversion routine.
* @argument DataInputStream
* @return double
* @exception IOException
*/
private double read_ieee_extended(DataInputStream dis) throws IOException {
double f = 0;
int expon = 0;
long hiMant = 0, loMant = 0;
long t1, t2;
double HUGE = ((double)3.40282346638528860e+38);
expon = dis.readUnsignedShort();
t1 = (long)dis.readUnsignedShort();
t2 = (long)dis.readUnsignedShort();
hiMant = t1 << 16 | t2;
t1 = (long)dis.readUnsignedShort();
t2 = (long)dis.readUnsignedShort();
loMant = t1 << 16 | t2;
if (expon == 0 && hiMant == 0 && loMant == 0) {
f = 0;
} else {
if (expon == 0x7FFF)
f = HUGE;
else {
expon -= 16383;
expon -= 31;
f = (hiMant * Math.pow(2, expon));
expon -= 32;
f += (loMant * Math.pow(2, expon));
}
}
return f;
}
}

View File

@@ -0,0 +1,449 @@
/*
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.RandomAccessFile;
import java.io.SequenceInputStream;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
//$$fb this class is buggy. Should be replaced in future.
/**
* AIFF file writer.
*
* @author Jan Borgersen
*/
public final class AiffFileWriter extends SunFileWriter {
/**
* Constructs a new AiffFileWriter object.
*/
public AiffFileWriter() {
super(new AudioFileFormat.Type[]{AudioFileFormat.Type.AIFF});
}
// METHODS TO IMPLEMENT AudioFileWriter
public AudioFileFormat.Type[] getAudioFileTypes(AudioInputStream stream) {
AudioFileFormat.Type[] filetypes = new AudioFileFormat.Type[types.length];
System.arraycopy(types, 0, filetypes, 0, types.length);
// make sure we can write this stream
AudioFormat format = stream.getFormat();
AudioFormat.Encoding encoding = format.getEncoding();
if( (AudioFormat.Encoding.ALAW.equals(encoding)) ||
(AudioFormat.Encoding.ULAW.equals(encoding)) ||
(AudioFormat.Encoding.PCM_SIGNED.equals(encoding)) ||
(AudioFormat.Encoding.PCM_UNSIGNED.equals(encoding)) ) {
return filetypes;
}
return new AudioFileFormat.Type[0];
}
public int write(AudioInputStream stream, AudioFileFormat.Type fileType, OutputStream out) throws IOException {
//$$fb the following check must come first ! Otherwise
// the next frame length check may throw an IOException and
// interrupt iterating File Writers. (see bug 4351296)
// throws IllegalArgumentException if not supported
AiffFileFormat aiffFileFormat = (AiffFileFormat)getAudioFileFormat(fileType, stream);
// we must know the total data length to calculate the file length
if( stream.getFrameLength() == AudioSystem.NOT_SPECIFIED ) {
throw new IOException("stream length not specified");
}
int bytesWritten = writeAiffFile(stream, aiffFileFormat, out);
return bytesWritten;
}
public int write(AudioInputStream stream, AudioFileFormat.Type fileType, File out) throws IOException {
// throws IllegalArgumentException if not supported
AiffFileFormat aiffFileFormat = (AiffFileFormat)getAudioFileFormat(fileType, stream);
// first write the file without worrying about length fields
FileOutputStream fos = new FileOutputStream( out ); // throws IOException
BufferedOutputStream bos = new BufferedOutputStream( fos, bisBufferSize );
int bytesWritten = writeAiffFile(stream, aiffFileFormat, bos );
bos.close();
// now, if length fields were not specified, calculate them,
// open as a random access file, write the appropriate fields,
// close again....
if( aiffFileFormat.getByteLength()== AudioSystem.NOT_SPECIFIED ) {
// $$kk: 10.22.99: jan: please either implement this or throw an exception!
// $$fb: 2001-07-13: done. Fixes Bug 4479981
int ssndBlockSize = (aiffFileFormat.getFormat().getChannels() * aiffFileFormat.getFormat().getSampleSizeInBits());
int aiffLength=bytesWritten;
int ssndChunkSize=aiffLength-aiffFileFormat.getHeaderSize()+16;
long dataSize=ssndChunkSize-16;
int numFrames=(int) (dataSize*8/ssndBlockSize);
RandomAccessFile raf=new RandomAccessFile(out, "rw");
// skip FORM magic
raf.skipBytes(4);
raf.writeInt(aiffLength-8);
// skip aiff2 magic, fver chunk, comm magic, comm size, channel count,
raf.skipBytes(4+aiffFileFormat.getFverChunkSize()+4+4+2);
// write frame count
raf.writeInt(numFrames);
// skip sample size, samplerate, SSND magic
raf.skipBytes(2+10+4);
raf.writeInt(ssndChunkSize-8);
// that's all
raf.close();
}
return bytesWritten;
}
// -----------------------------------------------------------------------
/**
* Returns the AudioFileFormat describing the file that will be written from this AudioInputStream.
* Throws IllegalArgumentException if not supported.
*/
private AudioFileFormat getAudioFileFormat(AudioFileFormat.Type type, AudioInputStream stream) {
AudioFormat format = null;
AiffFileFormat fileFormat = null;
AudioFormat.Encoding encoding = AudioFormat.Encoding.PCM_SIGNED;
AudioFormat streamFormat = stream.getFormat();
AudioFormat.Encoding streamEncoding = streamFormat.getEncoding();
float sampleRate;
int sampleSizeInBits;
int channels;
int frameSize;
float frameRate;
int fileSize;
boolean convert8to16 = false;
if( !types[0].equals(type) ) {
throw new IllegalArgumentException("File type " + type + " not supported.");
}
if( (AudioFormat.Encoding.ALAW.equals(streamEncoding)) ||
(AudioFormat.Encoding.ULAW.equals(streamEncoding)) ) {
if( streamFormat.getSampleSizeInBits()==8 ) {
encoding = AudioFormat.Encoding.PCM_SIGNED;
sampleSizeInBits=16;
convert8to16 = true;
} else {
// can't convert non-8-bit ALAW,ULAW
throw new IllegalArgumentException("Encoding " + streamEncoding + " supported only for 8-bit data.");
}
} else if ( streamFormat.getSampleSizeInBits()==8 ) {
encoding = AudioFormat.Encoding.PCM_UNSIGNED;
sampleSizeInBits=8;
} else {
encoding = AudioFormat.Encoding.PCM_SIGNED;
sampleSizeInBits=streamFormat.getSampleSizeInBits();
}
format = new AudioFormat( encoding,
streamFormat.getSampleRate(),
sampleSizeInBits,
streamFormat.getChannels(),
streamFormat.getFrameSize(),
streamFormat.getFrameRate(),
true); // AIFF is big endian
if( stream.getFrameLength()!=AudioSystem.NOT_SPECIFIED ) {
if( convert8to16 ) {
fileSize = (int)stream.getFrameLength()*streamFormat.getFrameSize()*2 + AiffFileFormat.AIFF_HEADERSIZE;
} else {
fileSize = (int)stream.getFrameLength()*streamFormat.getFrameSize() + AiffFileFormat.AIFF_HEADERSIZE;
}
} else {
fileSize = AudioSystem.NOT_SPECIFIED;
}
fileFormat = new AiffFileFormat( AudioFileFormat.Type.AIFF,
fileSize,
format,
(int)stream.getFrameLength() );
return fileFormat;
}
private int writeAiffFile(InputStream in, AiffFileFormat aiffFileFormat, OutputStream out) throws IOException {
int bytesRead = 0;
int bytesWritten = 0;
InputStream fileStream = getFileStream(aiffFileFormat, in);
byte buffer[] = new byte[bisBufferSize];
int maxLength = aiffFileFormat.getByteLength();
while( (bytesRead = fileStream.read( buffer )) >= 0 ) {
if (maxLength>0) {
if( bytesRead < maxLength ) {
out.write( buffer, 0, (int)bytesRead );
bytesWritten += bytesRead;
maxLength -= bytesRead;
} else {
out.write( buffer, 0, (int)maxLength );
bytesWritten += maxLength;
maxLength = 0;
break;
}
} else {
out.write( buffer, 0, (int)bytesRead );
bytesWritten += bytesRead;
}
}
return bytesWritten;
}
private InputStream getFileStream(AiffFileFormat aiffFileFormat, InputStream audioStream) throws IOException {
// private method ... assumes aiffFileFormat is a supported file format
AudioFormat format = aiffFileFormat.getFormat();
AudioFormat streamFormat = null;
AudioFormat.Encoding encoding = null;
//$$fb a little bit nicer handling of constants
//int headerSize = 54;
int headerSize = aiffFileFormat.getHeaderSize();
//int fverChunkSize = 0;
int fverChunkSize = aiffFileFormat.getFverChunkSize();
//int commChunkSize = 26;
int commChunkSize = aiffFileFormat.getCommChunkSize();
int aiffLength = -1;
int ssndChunkSize = -1;
//int ssndOffset = headerSize - 16;
int ssndOffset = aiffFileFormat.getSsndChunkOffset();
short channels = (short) format.getChannels();
short sampleSize = (short) format.getSampleSizeInBits();
int ssndBlockSize = (channels * sampleSize);
int numFrames = aiffFileFormat.getFrameLength();
long dataSize = -1;
if( numFrames != AudioSystem.NOT_SPECIFIED) {
dataSize = (long) numFrames * ssndBlockSize / 8;
ssndChunkSize = (int)dataSize + 16;
aiffLength = (int)dataSize+headerSize;
}
float sampleFramesPerSecond = format.getSampleRate();
int compCode = AiffFileFormat.AIFC_PCM;
byte header[] = null;
ByteArrayInputStream headerStream = null;
ByteArrayOutputStream baos = null;
DataOutputStream dos = null;
SequenceInputStream aiffStream = null;
InputStream codedAudioStream = audioStream;
// if we need to do any format conversion, do it here....
if( audioStream instanceof AudioInputStream ) {
streamFormat = ((AudioInputStream)audioStream).getFormat();
encoding = streamFormat.getEncoding();
// $$jb: Note that AIFF samples are ALWAYS signed
if( (AudioFormat.Encoding.PCM_UNSIGNED.equals(encoding)) ||
( (AudioFormat.Encoding.PCM_SIGNED.equals(encoding)) && !streamFormat.isBigEndian() ) ) {
// plug in the transcoder to convert to PCM_SIGNED. big endian
codedAudioStream = AudioSystem.getAudioInputStream( new AudioFormat (
AudioFormat.Encoding.PCM_SIGNED,
streamFormat.getSampleRate(),
streamFormat.getSampleSizeInBits(),
streamFormat.getChannels(),
streamFormat.getFrameSize(),
streamFormat.getFrameRate(),
true ),
(AudioInputStream)audioStream );
} else if( (AudioFormat.Encoding.ULAW.equals(encoding)) ||
(AudioFormat.Encoding.ALAW.equals(encoding)) ) {
if( streamFormat.getSampleSizeInBits() != 8 ) {
throw new IllegalArgumentException("unsupported encoding");
}
//$$fb 2001-07-13: this is probably not what we want:
// writing PCM when ULAW/ALAW is requested. AIFC is able to write ULAW !
// plug in the transcoder to convert to PCM_SIGNED_BIG_ENDIAN
codedAudioStream = AudioSystem.getAudioInputStream( new AudioFormat (
AudioFormat.Encoding.PCM_SIGNED,
streamFormat.getSampleRate(),
streamFormat.getSampleSizeInBits() * 2,
streamFormat.getChannels(),
streamFormat.getFrameSize() * 2,
streamFormat.getFrameRate(),
true ),
(AudioInputStream)audioStream );
}
}
// Now create an AIFF stream header...
baos = new ByteArrayOutputStream();
dos = new DataOutputStream(baos);
// Write the outer FORM chunk
dos.writeInt(AiffFileFormat.AIFF_MAGIC);
dos.writeInt( (aiffLength-8) );
dos.writeInt(AiffFileFormat.AIFF_MAGIC2);
// Write a FVER chunk - only for AIFC
//dos.writeInt(FVER_MAGIC);
//dos.writeInt( (fverChunkSize-8) );
//dos.writeInt(FVER_TIMESTAMP);
// Write a COMM chunk
dos.writeInt(AiffFileFormat.COMM_MAGIC);
dos.writeInt( (commChunkSize-8) );
dos.writeShort(channels);
dos.writeInt(numFrames);
dos.writeShort(sampleSize);
write_ieee_extended(dos, sampleFramesPerSecond); // 10 bytes
//Only for AIFC
//dos.writeInt(compCode);
//dos.writeInt(compCode);
//dos.writeShort(0);
// Write the SSND chunk header
dos.writeInt(AiffFileFormat.SSND_MAGIC);
dos.writeInt( (ssndChunkSize-8) );
// ssndOffset and ssndBlockSize set to 0 upon
// recommendation in "Sound Manager" chapter in
// "Inside Macintosh Sound", pp 2-87 (from Babu)
dos.writeInt(0); // ssndOffset
dos.writeInt(0); // ssndBlockSize
// Concat this with the audioStream and return it
dos.close();
header = baos.toByteArray();
headerStream = new ByteArrayInputStream( header );
aiffStream = new SequenceInputStream(headerStream,
new NoCloseInputStream(codedAudioStream));
return aiffStream;
}
// HELPER METHODS
private static final int DOUBLE_MANTISSA_LENGTH = 52;
private static final int DOUBLE_EXPONENT_LENGTH = 11;
private static final long DOUBLE_SIGN_MASK = 0x8000000000000000L;
private static final long DOUBLE_EXPONENT_MASK = 0x7FF0000000000000L;
private static final long DOUBLE_MANTISSA_MASK = 0x000FFFFFFFFFFFFFL;
private static final int DOUBLE_EXPONENT_OFFSET = 1023;
private static final int EXTENDED_EXPONENT_OFFSET = 16383;
private static final int EXTENDED_MANTISSA_LENGTH = 63;
private static final int EXTENDED_EXPONENT_LENGTH = 15;
private static final long EXTENDED_INTEGER_MASK = 0x8000000000000000L;
/**
* Extended precision IEEE floating-point conversion routine.
* @argument DataOutputStream
* @argument double
* @exception IOException
*/
private void write_ieee_extended(DataOutputStream dos, float f) throws IOException {
/* The special cases NaN, Infinity and Zero are ignored, since
they do not represent useful sample rates anyway.
Denormalized number aren't handled, too. Below, there is a cast
from float to double. We hope that in this conversion,
numbers are normalized. Numbers that cannot be normalized are
ignored, too, as they, too, do not represent useful sample rates. */
long doubleBits = Double.doubleToLongBits((double) f);
long sign = (doubleBits & DOUBLE_SIGN_MASK)
>> (DOUBLE_EXPONENT_LENGTH + DOUBLE_MANTISSA_LENGTH);
long doubleExponent = (doubleBits & DOUBLE_EXPONENT_MASK)
>> DOUBLE_MANTISSA_LENGTH;
long doubleMantissa = doubleBits & DOUBLE_MANTISSA_MASK;
long extendedExponent = doubleExponent - DOUBLE_EXPONENT_OFFSET
+ EXTENDED_EXPONENT_OFFSET;
long extendedMantissa = doubleMantissa
<< (EXTENDED_MANTISSA_LENGTH - DOUBLE_MANTISSA_LENGTH);
long extendedSign = sign << EXTENDED_EXPONENT_LENGTH;
short extendedBits79To64 = (short) (extendedSign | extendedExponent);
long extendedBits63To0 = EXTENDED_INTEGER_MASK | extendedMantissa;
dos.writeShort(extendedBits79To64);
dos.writeLong(extendedBits63To0);
}
}

View File

@@ -0,0 +1,448 @@
/*
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.io.IOException;
import java.util.Vector;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
/**
* A-law encodes linear data, and decodes a-law data to linear data.
*
* @author Kara Kytle
*/
public final class AlawCodec extends SunCodec {
/* Tables used for A-law decoding */
private static final byte[] ALAW_TABH = new byte[256];
private static final byte[] ALAW_TABL = new byte[256];
private static final AudioFormat.Encoding[] alawEncodings = { AudioFormat.Encoding.ALAW, AudioFormat.Encoding.PCM_SIGNED };
private static final short seg_end [] = {0xFF, 0x1FF, 0x3FF,
0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF};
/**
* Initializes the decode tables
*/
static {
for (int i=0;i<256;i++) {
int input = i ^ 0x55;
int mantissa = (input & 0xf ) << 4;
int segment = (input & 0x70) >> 4;
int value = mantissa+8;
if(segment>=1)
value+=0x100;
if(segment>1)
value <<= (segment -1);
if( (input & 0x80)==0 )
value = -value;
ALAW_TABL[i] = (byte)value;
ALAW_TABH[i] = (byte)(value>>8);
}
}
/**
* Constructs a new ALAW codec object.
*/
public AlawCodec() {
super(alawEncodings, alawEncodings);
}
// NEW CODE
/**
*/
public AudioFormat.Encoding[] getTargetEncodings(AudioFormat sourceFormat){
if( sourceFormat.getEncoding().equals( AudioFormat.Encoding.PCM_SIGNED )) {
if( sourceFormat.getSampleSizeInBits() == 16 ) {
AudioFormat.Encoding enc[] = new AudioFormat.Encoding[1];
enc[0] = AudioFormat.Encoding.ALAW;
return enc;
} else {
return new AudioFormat.Encoding[0];
}
} else if( sourceFormat.getEncoding().equals( AudioFormat.Encoding.ALAW ) ) {
if( sourceFormat.getSampleSizeInBits() == 8 ) {
AudioFormat.Encoding enc[] = new AudioFormat.Encoding[1];
enc[0] = AudioFormat.Encoding.PCM_SIGNED;
return enc;
} else {
return new AudioFormat.Encoding[0];
}
} else {
return new AudioFormat.Encoding[0];
}
}
/**
*/
public AudioFormat[] getTargetFormats(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat){
if( (targetEncoding.equals( AudioFormat.Encoding.PCM_SIGNED ) && sourceFormat.getEncoding().equals( AudioFormat.Encoding.ALAW)) ||
(targetEncoding.equals( AudioFormat.Encoding.ALAW) && sourceFormat.getEncoding().equals( AudioFormat.Encoding.PCM_SIGNED)) ) {
return getOutputFormats( sourceFormat );
} else {
return new AudioFormat[0];
}
}
/**
*/
public AudioInputStream getAudioInputStream(AudioFormat.Encoding targetEncoding, AudioInputStream sourceStream){
AudioFormat sourceFormat = sourceStream.getFormat();
AudioFormat.Encoding sourceEncoding = sourceFormat.getEncoding();
if( sourceEncoding.equals( targetEncoding ) ) {
return sourceStream;
} else {
AudioFormat targetFormat = null;
if( !isConversionSupported(targetEncoding,sourceStream.getFormat()) ) {
throw new IllegalArgumentException("Unsupported conversion: " + sourceStream.getFormat().toString() + " to " + targetEncoding.toString());
}
if( sourceEncoding.equals( AudioFormat.Encoding.ALAW ) &&
targetEncoding.equals( AudioFormat.Encoding.PCM_SIGNED ) ) {
targetFormat = new AudioFormat( targetEncoding,
sourceFormat.getSampleRate(),
16,
sourceFormat.getChannels(),
2*sourceFormat.getChannels(),
sourceFormat.getSampleRate(),
sourceFormat.isBigEndian());
} else if( sourceEncoding.equals( AudioFormat.Encoding.PCM_SIGNED ) &&
targetEncoding.equals( AudioFormat.Encoding.ALAW ) ) {
targetFormat = new AudioFormat( targetEncoding,
sourceFormat.getSampleRate(),
8,
sourceFormat.getChannels(),
sourceFormat.getChannels(),
sourceFormat.getSampleRate(),
false);
} else {
throw new IllegalArgumentException("Unsupported conversion: " + sourceStream.getFormat().toString() + " to " + targetEncoding.toString());
}
return getAudioInputStream( targetFormat, sourceStream );
}
}
/**
* use old code...
*/
public AudioInputStream getAudioInputStream(AudioFormat targetFormat, AudioInputStream sourceStream){
return getConvertedStream( targetFormat, sourceStream );
}
// OLD CODE
/**
* Opens the codec with the specified parameters.
* @param stream stream from which data to be processed should be read
* @param outputFormat desired data format of the stream after processing
* @return stream from which processed data may be read
* @throws IllegalArgumentException if the format combination supplied is
* not supported.
*/
/* public AudioInputStream getConvertedStream(AudioFormat outputFormat, AudioInputStream stream) { */
private AudioInputStream getConvertedStream(AudioFormat outputFormat, AudioInputStream stream) {
AudioInputStream cs = null;
AudioFormat inputFormat = stream.getFormat();
if( inputFormat.matches(outputFormat) ) {
cs = stream;
} else {
cs = (AudioInputStream) (new AlawCodecStream(stream, outputFormat));
}
return cs;
}
/**
* Obtains the set of output formats supported by the codec
* given a particular input format.
* If no output formats are supported for this input format,
* returns an array of length 0.
* @return array of supported output formats.
*/
/* public AudioFormat[] getOutputFormats(AudioFormat inputFormat) { */
private AudioFormat[] getOutputFormats(AudioFormat inputFormat) {
Vector formats = new Vector();
AudioFormat format;
if ( AudioFormat.Encoding.PCM_SIGNED.equals(inputFormat.getEncoding())) {
format = new AudioFormat(AudioFormat.Encoding.ALAW,
inputFormat.getSampleRate(),
8,
inputFormat.getChannels(),
inputFormat.getChannels(),
inputFormat.getSampleRate(),
false );
formats.addElement(format);
}
if (AudioFormat.Encoding.ALAW.equals(inputFormat.getEncoding())) {
format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
inputFormat.getSampleRate(),
16,
inputFormat.getChannels(),
inputFormat.getChannels()*2,
inputFormat.getSampleRate(),
false );
formats.addElement(format);
format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
inputFormat.getSampleRate(),
16,
inputFormat.getChannels(),
inputFormat.getChannels()*2,
inputFormat.getSampleRate(),
true );
formats.addElement(format);
}
AudioFormat[] formatArray = new AudioFormat[formats.size()];
for (int i = 0; i < formatArray.length; i++) {
formatArray[i] = (AudioFormat)(formats.elementAt(i));
}
return formatArray;
}
final class AlawCodecStream extends AudioInputStream {
// tempBuffer required only for encoding (when encode is true)
private static final int tempBufferSize = 64;
private byte tempBuffer [] = null;
/**
* True to encode to a-law, false to decode to linear
*/
boolean encode = false;
AudioFormat encodeFormat;
AudioFormat decodeFormat;
byte tabByte1[] = null;
byte tabByte2[] = null;
int highByte = 0;
int lowByte = 1;
AlawCodecStream(AudioInputStream stream, AudioFormat outputFormat) {
super(stream, outputFormat, -1);
AudioFormat inputFormat = stream.getFormat();
// throw an IllegalArgumentException if not ok
if ( ! (isConversionSupported(outputFormat, inputFormat)) ) {
throw new IllegalArgumentException("Unsupported conversion: " + inputFormat.toString() + " to " + outputFormat.toString());
}
//$$fb 2002-07-18: fix for 4714846: JavaSound ULAW (8-bit) encoder erroneously depends on endian-ness
boolean PCMIsBigEndian;
// determine whether we are encoding or decoding
if (AudioFormat.Encoding.ALAW.equals(inputFormat.getEncoding())) {
encode = false;
encodeFormat = inputFormat;
decodeFormat = outputFormat;
PCMIsBigEndian = outputFormat.isBigEndian();
} else {
encode = true;
encodeFormat = outputFormat;
decodeFormat = inputFormat;
PCMIsBigEndian = inputFormat.isBigEndian();
tempBuffer = new byte[tempBufferSize];
}
if (PCMIsBigEndian) {
tabByte1 = ALAW_TABH;
tabByte2 = ALAW_TABL;
highByte = 0;
lowByte = 1;
} else {
tabByte1 = ALAW_TABL;
tabByte2 = ALAW_TABH;
highByte = 1;
lowByte = 0;
}
// set the AudioInputStream length in frames if we know it
if (stream instanceof AudioInputStream) {
frameLength = ((AudioInputStream)stream).getFrameLength();
}
// set framePos to zero
framePos = 0;
frameSize = inputFormat.getFrameSize();
if( frameSize==AudioSystem.NOT_SPECIFIED ) {
frameSize=1;
}
}
/*
* $$jb 2/23/99
* Used to determine segment number in aLaw encoding
*/
private short search(short val, short table[], short size) {
for(short i = 0; i < size; i++) {
if (val <= table[i]) { return i; }
}
return size;
}
/**
* Note that this won't actually read anything; must read in
* two-byte units.
*/
public int read() throws IOException {
byte[] b = new byte[1];
return (int)read(b, 0, b.length);
}
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
public int read(byte[] b, int off, int len) throws IOException {
// don't read fractional frames
if( len%frameSize != 0 ) {
len -= (len%frameSize);
}
if (encode) {
short QUANT_MASK = 0xF;
short SEG_SHIFT = 4;
short mask;
short seg;
int adj;
int i;
short sample;
byte enc;
int readCount = 0;
int currentPos = off;
int readLeft = len*2;
int readLen = ( (readLeft>tempBufferSize) ? tempBufferSize : readLeft );
while ((readCount = super.read(tempBuffer,0,readLen))>0) {
for (i = 0; i < readCount; i+=2) {
/* Get the sample from the tempBuffer */
sample = (short)(( (tempBuffer[i + highByte]) << 8) & 0xFF00);
sample |= (short)( (tempBuffer[i + lowByte]) & 0xFF);
if(sample >= 0) {
mask = 0xD5;
} else {
mask = 0x55;
sample = (short)(-sample - 8);
}
/* Convert the scaled magnitude to segment number. */
seg = search(sample, seg_end, (short) 8);
/*
* Combine the sign, segment, quantization bits
*/
if (seg >= 8) { /* out of range, return maximum value. */
enc = (byte) (0x7F ^ mask);
} else {
enc = (byte) (seg << SEG_SHIFT);
if(seg < 2) {
enc |= (byte) ( (sample >> 4) & QUANT_MASK);
} else {
enc |= (byte) ( (sample >> (seg + 3)) & QUANT_MASK );
}
enc ^= mask;
}
/* Now put the encoded sample where it belongs */
b[currentPos] = enc;
currentPos++;
}
/* And update pointers and counters for next iteration */
readLeft -= readCount;
readLen = ( (readLeft>tempBufferSize) ? tempBufferSize : readLeft );
}
if( currentPos==off && readCount<0 ) { // EOF or error
return readCount;
}
return (currentPos - off); /* Number of bytes written to new buffer */
} else {
int i;
int readLen = len/2;
int readOffset = off + len/2;
int readCount = super.read(b, readOffset, readLen);
for (i = off; i < (off + (readCount*2)); i+=2) {
b[i] = (byte)tabByte1[b[readOffset] & 0xFF];
b[i+1] = (byte)tabByte2[b[readOffset] & 0xFF];
readOffset++;
}
if( readCount<0 ) { // EOF or error
return readCount;
}
return (i - off);
}
}
} // end class AlawCodecStream
} // end class ALAW

View File

@@ -0,0 +1,105 @@
/*
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
/**
* AU file format.
*
* @author Jan Borgersen
*/
final class AuFileFormat extends AudioFileFormat {
// magic numbers
static final int AU_SUN_MAGIC = 0x2e736e64;
static final int AU_SUN_INV_MAGIC = 0x646e732e;
static final int AU_DEC_MAGIC = 0x2e736400;
static final int AU_DEC_INV_MAGIC = 0x0064732e;
// encodings
static final int AU_ULAW_8 = 1; /* 8-bit ISDN u-law */
static final int AU_LINEAR_8 = 2; /* 8-bit linear PCM */
static final int AU_LINEAR_16 = 3; /* 16-bit linear PCM */
static final int AU_LINEAR_24 = 4; /* 24-bit linear PCM */
static final int AU_LINEAR_32 = 5; /* 32-bit linear PCM */
static final int AU_FLOAT = 6; /* 32-bit IEEE floating point */
static final int AU_DOUBLE = 7; /* 64-bit IEEE floating point */
static final int AU_ADPCM_G721 = 23; /* 4-bit CCITT g.721 ADPCM */
static final int AU_ADPCM_G722 = 24; /* CCITT g.722 ADPCM */
static final int AU_ADPCM_G723_3 = 25; /* CCITT g.723 3-bit ADPCM */
static final int AU_ADPCM_G723_5 = 26; /* CCITT g.723 5-bit ADPCM */
static final int AU_ALAW_8 = 27; /* 8-bit ISDN A-law */
static final int AU_HEADERSIZE = 24;
private int auType;
AuFileFormat( AudioFileFormat aff ) {
this( aff.getType(), aff.getByteLength(), aff.getFormat(), aff.getFrameLength() );
}
AuFileFormat(AudioFileFormat.Type type, int lengthInBytes, AudioFormat format, int lengthInFrames) {
super(type,lengthInBytes,format,lengthInFrames);
AudioFormat.Encoding encoding = format.getEncoding();
auType = -1;
if( AudioFormat.Encoding.ALAW.equals(encoding) ) {
if( format.getSampleSizeInBits()==8 ) {
auType = AU_ALAW_8;
}
} else if( AudioFormat.Encoding.ULAW.equals(encoding) ) {
if( format.getSampleSizeInBits()==8 ) {
auType = AU_ULAW_8;
}
} else if( AudioFormat.Encoding.PCM_SIGNED.equals(encoding) ) {
if( format.getSampleSizeInBits()==8 ) {
auType = AU_LINEAR_8;
} else if( format.getSampleSizeInBits()==16 ) {
auType = AU_LINEAR_16;
} else if( format.getSampleSizeInBits()==24 ) {
auType = AU_LINEAR_24;
} else if( format.getSampleSizeInBits()==32 ) {
auType = AU_LINEAR_32;
}
}
}
public int getAuType() {
return auType;
}
}

View File

@@ -0,0 +1,372 @@
/*
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
/**
* AU file reader.
*
* @author Kara Kytle
* @author Jan Borgersen
* @author Florian Bomers
*/
public final class AuFileReader extends SunFileReader {
// METHODS TO IMPLEMENT AudioFileReader
/**
* Obtains the audio file format of the input stream provided. The stream must
* point to valid audio file data. In general, audio file providers may
* need to read some data from the stream before determining whether they
* support it. These parsers must
* be able to mark the stream, read enough data to determine whether they
* support the stream, and, if not, reset the stream's read pointer to its original
* position. If the input stream does not support this, this method may fail
* with an IOException.
* @param stream the input stream from which file format information should be
* extracted
* @return an <code>AudioFileFormat</code> object describing the audio file format
* @throws UnsupportedAudioFileException if the stream does not point to valid audio
* file data recognized by the system
* @throws IOException if an I/O exception occurs
* @see InputStream#markSupported
* @see InputStream#mark
*/
public AudioFileFormat getAudioFileFormat(InputStream stream) throws UnsupportedAudioFileException, IOException {
AudioFormat format = null;
AuFileFormat fileFormat = null;
int maxReadLength = 28;
boolean bigendian = false;
int magic = -1;
int headerSize = -1;
int dataSize = -1;
int encoding_local = -1;
int sampleRate = -1;
int frameRate = -1;
int frameSize = -1;
int channels = -1;
final int sampleSizeInBits;
int length = 0;
int nread = 0;
AudioFormat.Encoding encoding = null;
DataInputStream dis = new DataInputStream( stream );
dis.mark(maxReadLength);
magic = dis.readInt();
if (! (magic == AuFileFormat.AU_SUN_MAGIC) || (magic == AuFileFormat.AU_DEC_MAGIC) ||
(magic == AuFileFormat.AU_SUN_INV_MAGIC) || (magic == AuFileFormat.AU_DEC_INV_MAGIC) ) {
// not AU, reset the stream, place into exception, throw exception
dis.reset();
throw new UnsupportedAudioFileException("not an AU file");
}
if ((magic == AuFileFormat.AU_SUN_MAGIC) || (magic == AuFileFormat.AU_DEC_MAGIC)) {
bigendian = true; // otherwise little-endian
}
headerSize = (bigendian==true ? dis.readInt() : rllong(dis) ); nread += 4;
dataSize = (bigendian==true ? dis.readInt() : rllong(dis) ); nread += 4;
encoding_local = (bigendian==true ? dis.readInt() : rllong(dis) ); nread += 4;
sampleRate = (bigendian==true ? dis.readInt() : rllong(dis) ); nread += 4;
channels = (bigendian==true ? dis.readInt() : rllong(dis) ); nread += 4;
if (channels <= 0) {
dis.reset();
throw new UnsupportedAudioFileException("Invalid number of channels");
}
frameRate = sampleRate;
switch (encoding_local) {
case AuFileFormat.AU_ULAW_8:
encoding = AudioFormat.Encoding.ULAW;
sampleSizeInBits = 8;
break;
case AuFileFormat.AU_ALAW_8:
encoding = AudioFormat.Encoding.ALAW;
sampleSizeInBits = 8;
break;
case AuFileFormat.AU_LINEAR_8:
// $$jb: 04.29.99: 8bit linear is *signed*, not *unsigned*
encoding = AudioFormat.Encoding.PCM_SIGNED;
sampleSizeInBits = 8;
break;
case AuFileFormat.AU_LINEAR_16:
encoding = AudioFormat.Encoding.PCM_SIGNED;
sampleSizeInBits = 16;
break;
case AuFileFormat.AU_LINEAR_24:
encoding = AudioFormat.Encoding.PCM_SIGNED;
sampleSizeInBits = 24;
break;
case AuFileFormat.AU_LINEAR_32:
encoding = AudioFormat.Encoding.PCM_SIGNED;
sampleSizeInBits = 32;
break;
// $jb: 03.19.99: we don't support these ...
/* case AuFileFormat.AU_FLOAT:
encoding = new AudioFormat.FLOAT;
sampleSizeInBits = 32;
break;
case AuFileFormat.AU_DOUBLE:
encoding = new AudioFormat.DOUBLE;
sampleSizeInBits = 8;
break;
case AuFileFormat.AU_ADPCM_G721:
encoding = new AudioFormat.G721_ADPCM;
sampleSizeInBits = 16;
break;
case AuFileFormat.AU_ADPCM_G723_3:
encoding = new AudioFormat.G723_3;
sampleSize = 24;
SamplePerUnit = 8;
break;
case AuFileFormat.AU_ADPCM_G723_5:
encoding = new AudioFormat.G723_5;
sampleSize = 40;
SamplePerUnit = 8;
break;
*/
default:
// unsupported filetype, throw exception
dis.reset();
throw new UnsupportedAudioFileException("not a valid AU file");
}
frameSize = calculatePCMFrameSize(sampleSizeInBits, channels);
//$$fb 2002-11-02: fix for 4629669: AU file reader: problems with empty files
if( dataSize < 0 ) {
length = AudioSystem.NOT_SPECIFIED;
} else {
//$$fb 2003-10-20: fix for 4940459: AudioInputStream.getFrameLength() returns 0 instead of NOT_SPECIFIED
length = dataSize / frameSize;
}
format = new AudioFormat( encoding, (float)sampleRate, sampleSizeInBits,
channels, frameSize, (float)frameRate, bigendian);
fileFormat = new AuFileFormat( AudioFileFormat.Type.AU, dataSize+headerSize,
format, length);
dis.reset(); // Throws IOException
return fileFormat;
}
/**
* Obtains the audio file format of the URL provided. The URL must
* point to valid audio file data.
* @param url the URL from which file format information should be
* extracted
* @return an <code>AudioFileFormat</code> object describing the audio file format
* @throws UnsupportedAudioFileException if the URL does not point to valid audio
* file data recognized by the system
* @throws IOException if an I/O exception occurs
*/
public AudioFileFormat getAudioFileFormat(URL url) throws UnsupportedAudioFileException, IOException {
InputStream urlStream = null;
BufferedInputStream bis = null;
AudioFileFormat fileFormat = null;
AudioFormat format = null;
urlStream = url.openStream(); // throws IOException
try {
bis = new BufferedInputStream( urlStream, bisBufferSize );
fileFormat = getAudioFileFormat( bis ); // throws UnsupportedAudioFileException
} finally {
urlStream.close();
}
return fileFormat;
}
/**
* Obtains the audio file format of the File provided. The File must
* point to valid audio file data.
* @param file the File from which file format information should be
* extracted
* @return an <code>AudioFileFormat</code> object describing the audio file format
* @throws UnsupportedAudioFileException if the File does not point to valid audio
* file data recognized by the system
* @throws IOException if an I/O exception occurs
*/
public AudioFileFormat getAudioFileFormat(File file) throws UnsupportedAudioFileException, IOException {
FileInputStream fis = null;
BufferedInputStream bis = null;
AudioFileFormat fileFormat = null;
AudioFormat format = null;
fis = new FileInputStream( file ); // throws IOException
// part of fix for 4325421
try {
bis = new BufferedInputStream( fis, bisBufferSize );
fileFormat = getAudioFileFormat( bis ); // throws UnsupportedAudioFileException
} finally {
fis.close();
}
return fileFormat;
}
/**
* Obtains an audio stream from the input stream provided. The stream must
* point to valid audio file data. In general, audio file providers may
* need to read some data from the stream before determining whether they
* support it. These parsers must
* be able to mark the stream, read enough data to determine whether they
* support the stream, and, if not, reset the stream's read pointer to its original
* position. If the input stream does not support this, this method may fail
* with an IOException.
* @param stream the input stream from which the <code>AudioInputStream</code> should be
* constructed
* @return an <code>AudioInputStream</code> object based on the audio file data contained
* in the input stream.
* @throws UnsupportedAudioFileException if the stream does not point to valid audio
* file data recognized by the system
* @throws IOException if an I/O exception occurs
* @see InputStream#markSupported
* @see InputStream#mark
*/
public AudioInputStream getAudioInputStream(InputStream stream) throws UnsupportedAudioFileException, IOException {
DataInputStream dis = null;
int headerSize;
AudioFileFormat fileFormat = null;
AudioFormat format = null;
fileFormat = getAudioFileFormat( stream ); // throws UnsupportedAudioFileException, IOException
// if we passed this call, we have an AU file.
format = fileFormat.getFormat();
dis = new DataInputStream(stream);
// now seek past the header
dis.readInt(); // magic
headerSize = (format.isBigEndian()==true ? dis.readInt() : rllong(dis) );
dis.skipBytes( headerSize - 8 );
// we've got everything, and the stream should be at the
// beginning of the data chunk, so return an AudioInputStream.
return new AudioInputStream(dis, format, fileFormat.getFrameLength());
}
/**
* Obtains an audio stream from the URL provided. The URL must
* point to valid audio file data.
* @param url the URL for which the <code>AudioInputStream</code> should be
* constructed
* @return an <code>AudioInputStream</code> object based on the audio file data pointed
* to by the URL
* @throws UnsupportedAudioFileException if the URL does not point to valid audio
* file data recognized by the system
* @throws IOException if an I/O exception occurs
*/
public AudioInputStream getAudioInputStream(URL url) throws UnsupportedAudioFileException, IOException {
InputStream urlStream = null;
BufferedInputStream bis = null;
AudioFileFormat fileFormat = null;
urlStream = url.openStream(); // throws IOException
AudioInputStream result = null;
try {
bis = new BufferedInputStream( urlStream, bisBufferSize );
result = getAudioInputStream( (InputStream)bis );
} finally {
if (result == null) {
urlStream.close();
}
}
return result;
}
/**
* Obtains an audio stream from the File provided. The File must
* point to valid audio file data.
* @param file the File for which the <code>AudioInputStream</code> should be
* constructed
* @return an <code>AudioInputStream</code> object based on the audio file data pointed
* to by the File
* @throws UnsupportedAudioFileException if the File does not point to valid audio
* file data recognized by the system
* @throws IOException if an I/O exception occurs
*/
public AudioInputStream getAudioInputStream(File file) throws UnsupportedAudioFileException, IOException {
FileInputStream fis = null;
BufferedInputStream bis = null;
AudioFileFormat fileFormat = null;
fis = new FileInputStream( file ); // throws IOException
AudioInputStream result = null;
// part of fix for 4325421
try {
bis = new BufferedInputStream( fis, bisBufferSize );
result = getAudioInputStream( (InputStream)bis );
} finally {
if (result == null) {
fis.close();
}
}
return result;
}
}

View File

@@ -0,0 +1,330 @@
/*
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.RandomAccessFile;
import java.io.SequenceInputStream;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
/**
* AU file writer.
*
* @author Jan Borgersen
*/
public final class AuFileWriter extends SunFileWriter {
//$$fb value for length field if length is not known
public final static int UNKNOWN_SIZE=-1;
/**
* Constructs a new AuFileWriter object.
*/
public AuFileWriter() {
super(new AudioFileFormat.Type[]{AudioFileFormat.Type.AU});
}
public AudioFileFormat.Type[] getAudioFileTypes(AudioInputStream stream) {
AudioFileFormat.Type[] filetypes = new AudioFileFormat.Type[types.length];
System.arraycopy(types, 0, filetypes, 0, types.length);
// make sure we can write this stream
AudioFormat format = stream.getFormat();
AudioFormat.Encoding encoding = format.getEncoding();
if( (AudioFormat.Encoding.ALAW.equals(encoding)) ||
(AudioFormat.Encoding.ULAW.equals(encoding)) ||
(AudioFormat.Encoding.PCM_SIGNED.equals(encoding)) ||
(AudioFormat.Encoding.PCM_UNSIGNED.equals(encoding)) ) {
return filetypes;
}
return new AudioFileFormat.Type[0];
}
public int write(AudioInputStream stream, AudioFileFormat.Type fileType, OutputStream out) throws IOException {
// we must know the total data length to calculate the file length
//$$fb 2001-07-13: fix for bug 4351296: do not throw an exception
//if( stream.getFrameLength() == AudioSystem.NOT_SPECIFIED ) {
// throw new IOException("stream length not specified");
//}
// throws IllegalArgumentException if not supported
AuFileFormat auFileFormat = (AuFileFormat)getAudioFileFormat(fileType, stream);
int bytesWritten = writeAuFile(stream, auFileFormat, out);
return bytesWritten;
}
public int write(AudioInputStream stream, AudioFileFormat.Type fileType, File out) throws IOException {
// throws IllegalArgumentException if not supported
AuFileFormat auFileFormat = (AuFileFormat)getAudioFileFormat(fileType, stream);
// first write the file without worrying about length fields
FileOutputStream fos = new FileOutputStream( out ); // throws IOException
BufferedOutputStream bos = new BufferedOutputStream( fos, bisBufferSize );
int bytesWritten = writeAuFile(stream, auFileFormat, bos );
bos.close();
// now, if length fields were not specified, calculate them,
// open as a random access file, write the appropriate fields,
// close again....
if( auFileFormat.getByteLength()== AudioSystem.NOT_SPECIFIED ) {
// $$kk: 10.22.99: jan: please either implement this or throw an exception!
// $$fb: 2001-07-13: done. Fixes Bug 4479981
RandomAccessFile raf=new RandomAccessFile(out, "rw");
if (raf.length()<=0x7FFFFFFFl) {
// skip AU magic and data offset field
raf.skipBytes(8);
raf.writeInt(bytesWritten-AuFileFormat.AU_HEADERSIZE);
// that's all
}
raf.close();
}
return bytesWritten;
}
// -------------------------------------------------------------
/**
* Returns the AudioFileFormat describing the file that will be written from this AudioInputStream.
* Throws IllegalArgumentException if not supported.
*/
private AudioFileFormat getAudioFileFormat(AudioFileFormat.Type type, AudioInputStream stream) {
AudioFormat format = null;
AuFileFormat fileFormat = null;
AudioFormat.Encoding encoding = AudioFormat.Encoding.PCM_SIGNED;
AudioFormat streamFormat = stream.getFormat();
AudioFormat.Encoding streamEncoding = streamFormat.getEncoding();
float sampleRate;
int sampleSizeInBits;
int channels;
int frameSize;
float frameRate;
int fileSize;
if( !types[0].equals(type) ) {
throw new IllegalArgumentException("File type " + type + " not supported.");
}
if( (AudioFormat.Encoding.ALAW.equals(streamEncoding)) ||
(AudioFormat.Encoding.ULAW.equals(streamEncoding)) ) {
encoding = streamEncoding;
sampleSizeInBits = streamFormat.getSampleSizeInBits();
} else if ( streamFormat.getSampleSizeInBits()==8 ) {
encoding = AudioFormat.Encoding.PCM_SIGNED;
sampleSizeInBits=8;
} else {
encoding = AudioFormat.Encoding.PCM_SIGNED;
sampleSizeInBits=streamFormat.getSampleSizeInBits();
}
format = new AudioFormat( encoding,
streamFormat.getSampleRate(),
sampleSizeInBits,
streamFormat.getChannels(),
streamFormat.getFrameSize(),
streamFormat.getFrameRate(),
true); // AU is always big endian
if( stream.getFrameLength()!=AudioSystem.NOT_SPECIFIED ) {
fileSize = (int)stream.getFrameLength()*streamFormat.getFrameSize() + AuFileFormat.AU_HEADERSIZE;
} else {
fileSize = AudioSystem.NOT_SPECIFIED;
}
fileFormat = new AuFileFormat( AudioFileFormat.Type.AU,
fileSize,
format,
(int)stream.getFrameLength() );
return fileFormat;
}
private InputStream getFileStream(AuFileFormat auFileFormat, InputStream audioStream) throws IOException {
// private method ... assumes auFileFormat is a supported file type
AudioFormat format = auFileFormat.getFormat();
int magic = AuFileFormat.AU_SUN_MAGIC;
int headerSize = AuFileFormat.AU_HEADERSIZE;
long dataSize = auFileFormat.getFrameLength();
//$$fb fix for Bug 4351296
//int dataSizeInBytes = dataSize * format.getFrameSize();
long dataSizeInBytes = (dataSize==AudioSystem.NOT_SPECIFIED)?UNKNOWN_SIZE:dataSize * format.getFrameSize();
if (dataSizeInBytes>0x7FFFFFFFl) {
dataSizeInBytes=UNKNOWN_SIZE;
}
int encoding_local = auFileFormat.getAuType();
int sampleRate = (int)format.getSampleRate();
int channels = format.getChannels();
//$$fb below is the fix for 4297100.
//boolean bigendian = format.isBigEndian();
boolean bigendian = true; // force bigendian
byte header[] = null;
ByteArrayInputStream headerStream = null;
ByteArrayOutputStream baos = null;
DataOutputStream dos = null;
SequenceInputStream auStream = null;
AudioFormat audioStreamFormat = null;
AudioFormat.Encoding encoding = null;
InputStream codedAudioStream = audioStream;
// if we need to do any format conversion, do it here.
codedAudioStream = audioStream;
if( audioStream instanceof AudioInputStream ) {
audioStreamFormat = ((AudioInputStream)audioStream).getFormat();
encoding = audioStreamFormat.getEncoding();
//$$ fb 2001-07-13: Bug 4391108
if( (AudioFormat.Encoding.PCM_UNSIGNED.equals(encoding)) ||
(AudioFormat.Encoding.PCM_SIGNED.equals(encoding)
&& bigendian != audioStreamFormat.isBigEndian()) ) {
// plug in the transcoder to convert to PCM_SIGNED, bigendian
// NOTE: little endian AU is not common, so we're always converting
// to big endian unless the passed in audioFileFormat is little.
// $$fb this NOTE is superseded. We always write big endian au files, this is by far the standard.
codedAudioStream = AudioSystem.getAudioInputStream( new AudioFormat (
AudioFormat.Encoding.PCM_SIGNED,
audioStreamFormat.getSampleRate(),
audioStreamFormat.getSampleSizeInBits(),
audioStreamFormat.getChannels(),
audioStreamFormat.getFrameSize(),
audioStreamFormat.getFrameRate(),
bigendian),
(AudioInputStream)audioStream );
}
}
baos = new ByteArrayOutputStream();
dos = new DataOutputStream(baos);
if (bigendian) {
dos.writeInt(AuFileFormat.AU_SUN_MAGIC);
dos.writeInt(headerSize);
dos.writeInt((int)dataSizeInBytes);
dos.writeInt(encoding_local);
dos.writeInt(sampleRate);
dos.writeInt(channels);
} else {
dos.writeInt(AuFileFormat.AU_SUN_INV_MAGIC);
dos.writeInt(big2little(headerSize));
dos.writeInt(big2little((int)dataSizeInBytes));
dos.writeInt(big2little(encoding_local));
dos.writeInt(big2little(sampleRate));
dos.writeInt(big2little(channels));
}
// Now create a new InputStream from headerStream and the InputStream
// in audioStream
dos.close();
header = baos.toByteArray();
headerStream = new ByteArrayInputStream( header );
auStream = new SequenceInputStream(headerStream,
new NoCloseInputStream(codedAudioStream));
return auStream;
}
private int writeAuFile(InputStream in, AuFileFormat auFileFormat, OutputStream out) throws IOException {
int bytesRead = 0;
int bytesWritten = 0;
InputStream fileStream = getFileStream(auFileFormat, in);
byte buffer[] = new byte[bisBufferSize];
int maxLength = auFileFormat.getByteLength();
while( (bytesRead = fileStream.read( buffer )) >= 0 ) {
if (maxLength>0) {
if( bytesRead < maxLength ) {
out.write( buffer, 0, (int)bytesRead );
bytesWritten += bytesRead;
maxLength -= bytesRead;
} else {
out.write( buffer, 0, (int)maxLength );
bytesWritten += maxLength;
maxLength = 0;
break;
}
} else {
out.write( buffer, 0, (int)bytesRead );
bytesWritten += bytesRead;
}
}
return bytesWritten;
}
}

View File

@@ -0,0 +1,144 @@
/*
* Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.Soundbank;
import javax.sound.midi.spi.SoundbankReader;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
/**
* Soundbank reader that uses audio files as soundbanks.
*
* @author Karl Helgason
*/
public final class AudioFileSoundbankReader extends SoundbankReader {
public Soundbank getSoundbank(URL url)
throws InvalidMidiDataException, IOException {
try {
AudioInputStream ais = AudioSystem.getAudioInputStream(url);
Soundbank sbk = getSoundbank(ais);
ais.close();
return sbk;
} catch (UnsupportedAudioFileException e) {
return null;
} catch (IOException e) {
return null;
}
}
public Soundbank getSoundbank(InputStream stream)
throws InvalidMidiDataException, IOException {
stream.mark(512);
try {
AudioInputStream ais = AudioSystem.getAudioInputStream(stream);
Soundbank sbk = getSoundbank(ais);
if (sbk != null)
return sbk;
} catch (UnsupportedAudioFileException e) {
} catch (IOException e) {
}
stream.reset();
return null;
}
public Soundbank getSoundbank(AudioInputStream ais)
throws InvalidMidiDataException, IOException {
int MEGABYTE = 1048576;
int DEFAULT_BUFFER_SIZE = 65536;
int MAX_FRAME_SIZE = 1024;
try {
byte[] buffer;
int frameSize = ais.getFormat().getFrameSize();
if (frameSize <= 0 || frameSize > MAX_FRAME_SIZE) {
throw new InvalidMidiDataException("Formats with frame size "
+ frameSize + " are not supported");
}
long totalSize = ais.getFrameLength() * frameSize;
if (totalSize >= Integer.MAX_VALUE - 2) {
throw new InvalidMidiDataException(
"Can not allocate enough memory to read audio data.");
}
if (ais.getFrameLength() == -1 || totalSize > MEGABYTE) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buff = new byte[DEFAULT_BUFFER_SIZE - (DEFAULT_BUFFER_SIZE % frameSize)];
int ret;
while ((ret = ais.read(buff)) != -1) {
baos.write(buff, 0, ret);
}
ais.close();
buffer = baos.toByteArray();
} else {
buffer = new byte[(int) totalSize];
new DataInputStream(ais).readFully(buffer);
}
ModelByteBufferWavetable osc = new ModelByteBufferWavetable(
new ModelByteBuffer(buffer), ais.getFormat(), -4800);
ModelPerformer performer = new ModelPerformer();
performer.getOscillators().add(osc);
SimpleSoundbank sbk = new SimpleSoundbank();
SimpleInstrument ins = new SimpleInstrument();
ins.add(performer);
sbk.addInstrument(ins);
return sbk;
} catch (Exception e) {
return null;
}
}
public Soundbank getSoundbank(File file)
throws InvalidMidiDataException, IOException {
try {
AudioInputStream ais = AudioSystem.getAudioInputStream(file);
ais.close();
ModelByteBufferWavetable osc = new ModelByteBufferWavetable(
new ModelByteBuffer(file, 0, file.length()), -4800);
ModelPerformer performer = new ModelPerformer();
performer.getOscillators().add(osc);
SimpleSoundbank sbk = new SimpleSoundbank();
SimpleInstrument ins = new SimpleInstrument();
ins.add(performer);
sbk.addInstrument(ins);
return sbk;
} catch (UnsupportedAudioFileException e1) {
return null;
} catch (IOException e) {
return null;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,617 @@
/*
* Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.AudioFormat.Encoding;
import javax.sound.sampled.spi.FormatConversionProvider;
/**
* This class is used to convert between 8,16,24,32 bit signed/unsigned
* big/litle endian fixed/floating stereo/mono/multi-channel audio streams and
* perform sample-rate conversion if needed.
*
* @author Karl Helgason
*/
public final class AudioFloatFormatConverter extends FormatConversionProvider {
private static class AudioFloatFormatConverterInputStream extends
InputStream {
private final AudioFloatConverter converter;
private final AudioFloatInputStream stream;
private float[] readfloatbuffer;
private final int fsize;
AudioFloatFormatConverterInputStream(AudioFormat targetFormat,
AudioFloatInputStream stream) {
this.stream = stream;
converter = AudioFloatConverter.getConverter(targetFormat);
fsize = ((targetFormat.getSampleSizeInBits() + 7) / 8);
}
public int read() throws IOException {
byte[] b = new byte[1];
int ret = read(b);
if (ret < 0)
return ret;
return b[0] & 0xFF;
}
public int read(byte[] b, int off, int len) throws IOException {
int flen = len / fsize;
if (readfloatbuffer == null || readfloatbuffer.length < flen)
readfloatbuffer = new float[flen];
int ret = stream.read(readfloatbuffer, 0, flen);
if (ret < 0)
return ret;
converter.toByteArray(readfloatbuffer, 0, ret, b, off);
return ret * fsize;
}
public int available() throws IOException {
int ret = stream.available();
if (ret < 0)
return ret;
return ret * fsize;
}
public void close() throws IOException {
stream.close();
}
public synchronized void mark(int readlimit) {
stream.mark(readlimit * fsize);
}
public boolean markSupported() {
return stream.markSupported();
}
public synchronized void reset() throws IOException {
stream.reset();
}
public long skip(long n) throws IOException {
long ret = stream.skip(n / fsize);
if (ret < 0)
return ret;
return ret * fsize;
}
}
private static class AudioFloatInputStreamChannelMixer extends
AudioFloatInputStream {
private final int targetChannels;
private final int sourceChannels;
private final AudioFloatInputStream ais;
private final AudioFormat targetFormat;
private float[] conversion_buffer;
AudioFloatInputStreamChannelMixer(AudioFloatInputStream ais,
int targetChannels) {
this.sourceChannels = ais.getFormat().getChannels();
this.targetChannels = targetChannels;
this.ais = ais;
AudioFormat format = ais.getFormat();
targetFormat = new AudioFormat(format.getEncoding(), format
.getSampleRate(), format.getSampleSizeInBits(),
targetChannels, (format.getFrameSize() / sourceChannels)
* targetChannels, format.getFrameRate(), format
.isBigEndian());
}
public int available() throws IOException {
return (ais.available() / sourceChannels) * targetChannels;
}
public void close() throws IOException {
ais.close();
}
public AudioFormat getFormat() {
return targetFormat;
}
public long getFrameLength() {
return ais.getFrameLength();
}
public void mark(int readlimit) {
ais.mark((readlimit / targetChannels) * sourceChannels);
}
public boolean markSupported() {
return ais.markSupported();
}
public int read(float[] b, int off, int len) throws IOException {
int len2 = (len / targetChannels) * sourceChannels;
if (conversion_buffer == null || conversion_buffer.length < len2)
conversion_buffer = new float[len2];
int ret = ais.read(conversion_buffer, 0, len2);
if (ret < 0)
return ret;
if (sourceChannels == 1) {
int cs = targetChannels;
for (int c = 0; c < targetChannels; c++) {
for (int i = 0, ix = off + c; i < len2; i++, ix += cs) {
b[ix] = conversion_buffer[i];
}
}
} else if (targetChannels == 1) {
int cs = sourceChannels;
for (int i = 0, ix = off; i < len2; i += cs, ix++) {
b[ix] = conversion_buffer[i];
}
for (int c = 1; c < sourceChannels; c++) {
for (int i = c, ix = off; i < len2; i += cs, ix++) {
b[ix] += conversion_buffer[i];
}
}
float vol = 1f / ((float) sourceChannels);
for (int i = 0, ix = off; i < len2; i += cs, ix++) {
b[ix] *= vol;
}
} else {
int minChannels = Math.min(sourceChannels, targetChannels);
int off_len = off + len;
int ct = targetChannels;
int cs = sourceChannels;
for (int c = 0; c < minChannels; c++) {
for (int i = off + c, ix = c; i < off_len; i += ct, ix += cs) {
b[i] = conversion_buffer[ix];
}
}
for (int c = minChannels; c < targetChannels; c++) {
for (int i = off + c; i < off_len; i += ct) {
b[i] = 0;
}
}
}
return (ret / sourceChannels) * targetChannels;
}
public void reset() throws IOException {
ais.reset();
}
public long skip(long len) throws IOException {
long ret = ais.skip((len / targetChannels) * sourceChannels);
if (ret < 0)
return ret;
return (ret / sourceChannels) * targetChannels;
}
}
private static class AudioFloatInputStreamResampler extends
AudioFloatInputStream {
private final AudioFloatInputStream ais;
private final AudioFormat targetFormat;
private float[] skipbuffer;
private SoftAbstractResampler resampler;
private final float[] pitch = new float[1];
private final float[] ibuffer2;
private final float[][] ibuffer;
private float ibuffer_index = 0;
private int ibuffer_len = 0;
private final int nrofchannels;
private float[][] cbuffer;
private final int buffer_len = 512;
private final int pad;
private final int pad2;
private final float[] ix = new float[1];
private final int[] ox = new int[1];
private float[][] mark_ibuffer = null;
private float mark_ibuffer_index = 0;
private int mark_ibuffer_len = 0;
AudioFloatInputStreamResampler(AudioFloatInputStream ais,
AudioFormat format) {
this.ais = ais;
AudioFormat sourceFormat = ais.getFormat();
targetFormat = new AudioFormat(sourceFormat.getEncoding(), format
.getSampleRate(), sourceFormat.getSampleSizeInBits(),
sourceFormat.getChannels(), sourceFormat.getFrameSize(),
format.getSampleRate(), sourceFormat.isBigEndian());
nrofchannels = targetFormat.getChannels();
Object interpolation = format.getProperty("interpolation");
if (interpolation != null && (interpolation instanceof String)) {
String resamplerType = (String) interpolation;
if (resamplerType.equalsIgnoreCase("point"))
this.resampler = new SoftPointResampler();
if (resamplerType.equalsIgnoreCase("linear"))
this.resampler = new SoftLinearResampler2();
if (resamplerType.equalsIgnoreCase("linear1"))
this.resampler = new SoftLinearResampler();
if (resamplerType.equalsIgnoreCase("linear2"))
this.resampler = new SoftLinearResampler2();
if (resamplerType.equalsIgnoreCase("cubic"))
this.resampler = new SoftCubicResampler();
if (resamplerType.equalsIgnoreCase("lanczos"))
this.resampler = new SoftLanczosResampler();
if (resamplerType.equalsIgnoreCase("sinc"))
this.resampler = new SoftSincResampler();
}
if (resampler == null)
resampler = new SoftLinearResampler2(); // new
// SoftLinearResampler2();
pitch[0] = sourceFormat.getSampleRate() / format.getSampleRate();
pad = resampler.getPadding();
pad2 = pad * 2;
ibuffer = new float[nrofchannels][buffer_len + pad2];
ibuffer2 = new float[nrofchannels * buffer_len];
ibuffer_index = buffer_len + pad;
ibuffer_len = buffer_len;
}
public int available() throws IOException {
return 0;
}
public void close() throws IOException {
ais.close();
}
public AudioFormat getFormat() {
return targetFormat;
}
public long getFrameLength() {
return AudioSystem.NOT_SPECIFIED; // ais.getFrameLength();
}
public void mark(int readlimit) {
ais.mark((int) (readlimit * pitch[0]));
mark_ibuffer_index = ibuffer_index;
mark_ibuffer_len = ibuffer_len;
if (mark_ibuffer == null) {
mark_ibuffer = new float[ibuffer.length][ibuffer[0].length];
}
for (int c = 0; c < ibuffer.length; c++) {
float[] from = ibuffer[c];
float[] to = mark_ibuffer[c];
for (int i = 0; i < to.length; i++) {
to[i] = from[i];
}
}
}
public boolean markSupported() {
return ais.markSupported();
}
private void readNextBuffer() throws IOException {
if (ibuffer_len == -1)
return;
for (int c = 0; c < nrofchannels; c++) {
float[] buff = ibuffer[c];
int buffer_len_pad = ibuffer_len + pad2;
for (int i = ibuffer_len, ix = 0; i < buffer_len_pad; i++, ix++) {
buff[ix] = buff[i];
}
}
ibuffer_index -= (ibuffer_len);
ibuffer_len = ais.read(ibuffer2);
if (ibuffer_len >= 0) {
while (ibuffer_len < ibuffer2.length) {
int ret = ais.read(ibuffer2, ibuffer_len, ibuffer2.length
- ibuffer_len);
if (ret == -1)
break;
ibuffer_len += ret;
}
Arrays.fill(ibuffer2, ibuffer_len, ibuffer2.length, 0);
ibuffer_len /= nrofchannels;
} else {
Arrays.fill(ibuffer2, 0, ibuffer2.length, 0);
}
int ibuffer2_len = ibuffer2.length;
for (int c = 0; c < nrofchannels; c++) {
float[] buff = ibuffer[c];
for (int i = c, ix = pad2; i < ibuffer2_len; i += nrofchannels, ix++) {
buff[ix] = ibuffer2[i];
}
}
}
public int read(float[] b, int off, int len) throws IOException {
if (cbuffer == null || cbuffer[0].length < len / nrofchannels) {
cbuffer = new float[nrofchannels][len / nrofchannels];
}
if (ibuffer_len == -1)
return -1;
if (len < 0)
return 0;
int offlen = off + len;
int remain = len / nrofchannels;
int destPos = 0;
int in_end = ibuffer_len;
while (remain > 0) {
if (ibuffer_len >= 0) {
if (ibuffer_index >= (ibuffer_len + pad))
readNextBuffer();
in_end = ibuffer_len + pad;
}
if (ibuffer_len < 0) {
in_end = pad2;
if (ibuffer_index >= in_end)
break;
}
if (ibuffer_index < 0)
break;
int preDestPos = destPos;
for (int c = 0; c < nrofchannels; c++) {
ix[0] = ibuffer_index;
ox[0] = destPos;
float[] buff = ibuffer[c];
resampler.interpolate(buff, ix, in_end, pitch, 0,
cbuffer[c], ox, len / nrofchannels);
}
ibuffer_index = ix[0];
destPos = ox[0];
remain -= destPos - preDestPos;
}
for (int c = 0; c < nrofchannels; c++) {
int ix = 0;
float[] buff = cbuffer[c];
for (int i = c + off; i < offlen; i += nrofchannels) {
b[i] = buff[ix++];
}
}
return len - remain * nrofchannels;
}
public void reset() throws IOException {
ais.reset();
if (mark_ibuffer == null)
return;
ibuffer_index = mark_ibuffer_index;
ibuffer_len = mark_ibuffer_len;
for (int c = 0; c < ibuffer.length; c++) {
float[] from = mark_ibuffer[c];
float[] to = ibuffer[c];
for (int i = 0; i < to.length; i++) {
to[i] = from[i];
}
}
}
public long skip(long len) throws IOException {
if (len < 0)
return 0;
if (skipbuffer == null)
skipbuffer = new float[1024 * targetFormat.getFrameSize()];
float[] l_skipbuffer = skipbuffer;
long remain = len;
while (remain > 0) {
int ret = read(l_skipbuffer, 0, (int) Math.min(remain,
skipbuffer.length));
if (ret < 0) {
if (remain == len)
return ret;
break;
}
remain -= ret;
}
return len - remain;
}
}
private final Encoding[] formats = {Encoding.PCM_SIGNED,
Encoding.PCM_UNSIGNED,
Encoding.PCM_FLOAT};
public AudioInputStream getAudioInputStream(Encoding targetEncoding,
AudioInputStream sourceStream) {
if (sourceStream.getFormat().getEncoding().equals(targetEncoding))
return sourceStream;
AudioFormat format = sourceStream.getFormat();
int channels = format.getChannels();
Encoding encoding = targetEncoding;
float samplerate = format.getSampleRate();
int bits = format.getSampleSizeInBits();
boolean bigendian = format.isBigEndian();
if (targetEncoding.equals(Encoding.PCM_FLOAT))
bits = 32;
AudioFormat targetFormat = new AudioFormat(encoding, samplerate, bits,
channels, channels * bits / 8, samplerate, bigendian);
return getAudioInputStream(targetFormat, sourceStream);
}
public AudioInputStream getAudioInputStream(AudioFormat targetFormat,
AudioInputStream sourceStream) {
if (!isConversionSupported(targetFormat, sourceStream.getFormat()))
throw new IllegalArgumentException("Unsupported conversion: "
+ sourceStream.getFormat().toString() + " to "
+ targetFormat.toString());
return getAudioInputStream(targetFormat, AudioFloatInputStream
.getInputStream(sourceStream));
}
public AudioInputStream getAudioInputStream(AudioFormat targetFormat,
AudioFloatInputStream sourceStream) {
if (!isConversionSupported(targetFormat, sourceStream.getFormat()))
throw new IllegalArgumentException("Unsupported conversion: "
+ sourceStream.getFormat().toString() + " to "
+ targetFormat.toString());
if (targetFormat.getChannels() != sourceStream.getFormat()
.getChannels())
sourceStream = new AudioFloatInputStreamChannelMixer(sourceStream,
targetFormat.getChannels());
if (Math.abs(targetFormat.getSampleRate()
- sourceStream.getFormat().getSampleRate()) > 0.000001)
sourceStream = new AudioFloatInputStreamResampler(sourceStream,
targetFormat);
return new AudioInputStream(new AudioFloatFormatConverterInputStream(
targetFormat, sourceStream), targetFormat, sourceStream
.getFrameLength());
}
public Encoding[] getSourceEncodings() {
return new Encoding[] { Encoding.PCM_SIGNED, Encoding.PCM_UNSIGNED,
Encoding.PCM_FLOAT };
}
public Encoding[] getTargetEncodings() {
return new Encoding[] { Encoding.PCM_SIGNED, Encoding.PCM_UNSIGNED,
Encoding.PCM_FLOAT };
}
public Encoding[] getTargetEncodings(AudioFormat sourceFormat) {
if (AudioFloatConverter.getConverter(sourceFormat) == null)
return new Encoding[0];
return new Encoding[] { Encoding.PCM_SIGNED, Encoding.PCM_UNSIGNED,
Encoding.PCM_FLOAT };
}
public AudioFormat[] getTargetFormats(Encoding targetEncoding,
AudioFormat sourceFormat) {
if (AudioFloatConverter.getConverter(sourceFormat) == null)
return new AudioFormat[0];
int channels = sourceFormat.getChannels();
ArrayList<AudioFormat> formats = new ArrayList<AudioFormat>();
if (targetEncoding.equals(Encoding.PCM_SIGNED))
formats.add(new AudioFormat(Encoding.PCM_SIGNED,
AudioSystem.NOT_SPECIFIED, 8, channels, channels,
AudioSystem.NOT_SPECIFIED, false));
if (targetEncoding.equals(Encoding.PCM_UNSIGNED))
formats.add(new AudioFormat(Encoding.PCM_UNSIGNED,
AudioSystem.NOT_SPECIFIED, 8, channels, channels,
AudioSystem.NOT_SPECIFIED, false));
for (int bits = 16; bits < 32; bits += 8) {
if (targetEncoding.equals(Encoding.PCM_SIGNED)) {
formats.add(new AudioFormat(Encoding.PCM_SIGNED,
AudioSystem.NOT_SPECIFIED, bits, channels, channels
* bits / 8, AudioSystem.NOT_SPECIFIED, false));
formats.add(new AudioFormat(Encoding.PCM_SIGNED,
AudioSystem.NOT_SPECIFIED, bits, channels, channels
* bits / 8, AudioSystem.NOT_SPECIFIED, true));
}
if (targetEncoding.equals(Encoding.PCM_UNSIGNED)) {
formats.add(new AudioFormat(Encoding.PCM_UNSIGNED,
AudioSystem.NOT_SPECIFIED, bits, channels, channels
* bits / 8, AudioSystem.NOT_SPECIFIED, true));
formats.add(new AudioFormat(Encoding.PCM_UNSIGNED,
AudioSystem.NOT_SPECIFIED, bits, channels, channels
* bits / 8, AudioSystem.NOT_SPECIFIED, false));
}
}
if (targetEncoding.equals(Encoding.PCM_FLOAT)) {
formats.add(new AudioFormat(Encoding.PCM_FLOAT,
AudioSystem.NOT_SPECIFIED, 32, channels, channels * 4,
AudioSystem.NOT_SPECIFIED, false));
formats.add(new AudioFormat(Encoding.PCM_FLOAT,
AudioSystem.NOT_SPECIFIED, 32, channels, channels * 4,
AudioSystem.NOT_SPECIFIED, true));
formats.add(new AudioFormat(Encoding.PCM_FLOAT,
AudioSystem.NOT_SPECIFIED, 64, channels, channels * 8,
AudioSystem.NOT_SPECIFIED, false));
formats.add(new AudioFormat(Encoding.PCM_FLOAT,
AudioSystem.NOT_SPECIFIED, 64, channels, channels * 8,
AudioSystem.NOT_SPECIFIED, true));
}
return formats.toArray(new AudioFormat[formats.size()]);
}
public boolean isConversionSupported(AudioFormat targetFormat,
AudioFormat sourceFormat) {
if (AudioFloatConverter.getConverter(sourceFormat) == null)
return false;
if (AudioFloatConverter.getConverter(targetFormat) == null)
return false;
if (sourceFormat.getChannels() <= 0)
return false;
if (targetFormat.getChannels() <= 0)
return false;
return true;
}
public boolean isConversionSupported(Encoding targetEncoding,
AudioFormat sourceFormat) {
if (AudioFloatConverter.getConverter(sourceFormat) == null)
return false;
for (int i = 0; i < formats.length; i++) {
if (targetEncoding.equals(formats[i]))
return true;
}
return false;
}
}

View File

@@ -0,0 +1,281 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
/**
* This class is used to create AudioFloatInputStream from AudioInputStream and
* byte buffers.
*
* @author Karl Helgason
*/
public abstract class AudioFloatInputStream {
private static class BytaArrayAudioFloatInputStream
extends AudioFloatInputStream {
private int pos = 0;
private int markpos = 0;
private final AudioFloatConverter converter;
private final AudioFormat format;
private final byte[] buffer;
private final int buffer_offset;
private final int buffer_len;
private final int framesize_pc;
BytaArrayAudioFloatInputStream(AudioFloatConverter converter,
byte[] buffer, int offset, int len) {
this.converter = converter;
this.format = converter.getFormat();
this.buffer = buffer;
this.buffer_offset = offset;
framesize_pc = format.getFrameSize() / format.getChannels();
this.buffer_len = len / framesize_pc;
}
public AudioFormat getFormat() {
return format;
}
public long getFrameLength() {
return buffer_len;// / format.getFrameSize();
}
public int read(float[] b, int off, int len) throws IOException {
if (b == null)
throw new NullPointerException();
if (off < 0 || len < 0 || len > b.length - off)
throw new IndexOutOfBoundsException();
if (pos >= buffer_len)
return -1;
if (len == 0)
return 0;
if (pos + len > buffer_len)
len = buffer_len - pos;
converter.toFloatArray(buffer, buffer_offset + pos * framesize_pc,
b, off, len);
pos += len;
return len;
}
public long skip(long len) throws IOException {
if (pos >= buffer_len)
return -1;
if (len <= 0)
return 0;
if (pos + len > buffer_len)
len = buffer_len - pos;
pos += len;
return len;
}
public int available() throws IOException {
return buffer_len - pos;
}
public void close() throws IOException {
}
public void mark(int readlimit) {
markpos = pos;
}
public boolean markSupported() {
return true;
}
public void reset() throws IOException {
pos = markpos;
}
}
private static class DirectAudioFloatInputStream
extends AudioFloatInputStream {
private final AudioInputStream stream;
private AudioFloatConverter converter;
private final int framesize_pc; // framesize / channels
private byte[] buffer;
DirectAudioFloatInputStream(AudioInputStream stream) {
converter = AudioFloatConverter.getConverter(stream.getFormat());
if (converter == null) {
AudioFormat format = stream.getFormat();
AudioFormat newformat;
AudioFormat[] formats = AudioSystem.getTargetFormats(
AudioFormat.Encoding.PCM_SIGNED, format);
if (formats.length != 0) {
newformat = formats[0];
} else {
float samplerate = format.getSampleRate();
int samplesizeinbits = format.getSampleSizeInBits();
int framesize = format.getFrameSize();
float framerate = format.getFrameRate();
samplesizeinbits = 16;
framesize = format.getChannels() * (samplesizeinbits / 8);
framerate = samplerate;
newformat = new AudioFormat(
AudioFormat.Encoding.PCM_SIGNED, samplerate,
samplesizeinbits, format.getChannels(), framesize,
framerate, false);
}
stream = AudioSystem.getAudioInputStream(newformat, stream);
converter = AudioFloatConverter.getConverter(stream.getFormat());
}
framesize_pc = stream.getFormat().getFrameSize()
/ stream.getFormat().getChannels();
this.stream = stream;
}
public AudioFormat getFormat() {
return stream.getFormat();
}
public long getFrameLength() {
return stream.getFrameLength();
}
public int read(float[] b, int off, int len) throws IOException {
int b_len = len * framesize_pc;
if (buffer == null || buffer.length < b_len)
buffer = new byte[b_len];
int ret = stream.read(buffer, 0, b_len);
if (ret == -1)
return -1;
converter.toFloatArray(buffer, b, off, ret / framesize_pc);
return ret / framesize_pc;
}
public long skip(long len) throws IOException {
long b_len = len * framesize_pc;
long ret = stream.skip(b_len);
if (ret == -1)
return -1;
return ret / framesize_pc;
}
public int available() throws IOException {
return stream.available() / framesize_pc;
}
public void close() throws IOException {
stream.close();
}
public void mark(int readlimit) {
stream.mark(readlimit * framesize_pc);
}
public boolean markSupported() {
return stream.markSupported();
}
public void reset() throws IOException {
stream.reset();
}
}
public static AudioFloatInputStream getInputStream(URL url)
throws UnsupportedAudioFileException, IOException {
return new DirectAudioFloatInputStream(AudioSystem
.getAudioInputStream(url));
}
public static AudioFloatInputStream getInputStream(File file)
throws UnsupportedAudioFileException, IOException {
return new DirectAudioFloatInputStream(AudioSystem
.getAudioInputStream(file));
}
public static AudioFloatInputStream getInputStream(InputStream stream)
throws UnsupportedAudioFileException, IOException {
return new DirectAudioFloatInputStream(AudioSystem
.getAudioInputStream(stream));
}
public static AudioFloatInputStream getInputStream(
AudioInputStream stream) {
return new DirectAudioFloatInputStream(stream);
}
public static AudioFloatInputStream getInputStream(AudioFormat format,
byte[] buffer, int offset, int len) {
AudioFloatConverter converter = AudioFloatConverter
.getConverter(format);
if (converter != null)
return new BytaArrayAudioFloatInputStream(converter, buffer,
offset, len);
InputStream stream = new ByteArrayInputStream(buffer, offset, len);
long aLen = format.getFrameSize() == AudioSystem.NOT_SPECIFIED
? AudioSystem.NOT_SPECIFIED : len / format.getFrameSize();
AudioInputStream astream = new AudioInputStream(stream, format, aLen);
return getInputStream(astream);
}
public abstract AudioFormat getFormat();
public abstract long getFrameLength();
public abstract int read(float[] b, int off, int len) throws IOException;
public final int read(float[] b) throws IOException {
return read(b, 0, b.length);
}
public final float read() throws IOException {
float[] b = new float[1];
int ret = read(b, 0, 1);
if (ret == -1 || ret == 0)
return 0;
return b[0];
}
public abstract long skip(long len) throws IOException;
public abstract int available() throws IOException;
public abstract void close() throws IOException;
public abstract void mark(int readlimit);
public abstract boolean markSupported();
public abstract void reset() throws IOException;
}

View File

@@ -0,0 +1,128 @@
/*
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.util.Map;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Synthesizer;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.SourceDataLine;
/**
* <code>AudioSynthesizer</code> is a <code>Synthesizer</code>
* which renders it's output audio into <code>SourceDataLine</code>
* or <code>AudioInputStream</code>.
*
* @see MidiSystem#getSynthesizer
* @see Synthesizer
*
* @author Karl Helgason
*/
public interface AudioSynthesizer extends Synthesizer {
/**
* Obtains the current format (encoding, sample rate, number of channels,
* etc.) of the synthesizer audio data.
*
* <p>If the synthesizer is not open and has never been opened, it returns
* the default format.
*
* @return current audio data format
* @see AudioFormat
*/
public AudioFormat getFormat();
/**
* Gets information about the possible properties for the synthesizer.
*
* @param info a proposed list of tag/value pairs that will be sent on open.
* @return an array of <code>AudioSynthesizerPropertyInfo</code> objects
* describing possible properties. This array may be an empty array if
* no properties are required.
*/
public AudioSynthesizerPropertyInfo[] getPropertyInfo(
Map<String, Object> info);
/**
* Opens the synthesizer and starts rendering audio into
* <code>SourceDataLine</code>.
*
* <p>An application opening a synthesizer explicitly with this call
* has to close the synthesizer by calling {@link #close}. This is
* necessary to release system resources and allow applications to
* exit cleanly.
*
* <p>Note that some synthesizers, once closed, cannot be reopened.
* Attempts to reopen such a synthesizer will always result in
* a <code>MidiUnavailableException</code>.
*
* @param line which <code>AudioSynthesizer</code> writes output audio into.
* If <code>line</code> is null, then line from system default mixer is used.
* @param info a <code>Map<String,Object></code> object containing
* properties for additional configuration supported by synthesizer.
* If <code>info</code> is null then default settings are used.
*
* @throws MidiUnavailableException thrown if the synthesizer cannot be
* opened due to resource restrictions.
* @throws SecurityException thrown if the synthesizer cannot be
* opened due to security restrictions.
*
* @see #close
* @see #isOpen
*/
public void open(SourceDataLine line, Map<String, Object> info)
throws MidiUnavailableException;
/**
* Opens the synthesizer and renders audio into returned
* <code>AudioInputStream</code>.
*
* <p>An application opening a synthesizer explicitly with this call
* has to close the synthesizer by calling {@link #close}. This is
* necessary to release system resources and allow applications to
* exit cleanly.
*
* <p>Note that some synthesizers, once closed, cannot be reopened.
* Attempts to reopen such a synthesizer will always result in
* a <code>MidiUnavailableException<code>.
*
* @param targetFormat specifies the <code>AudioFormat</code>
* used in returned <code>AudioInputStream</code>.
* @param info a <code>Map<String,Object></code> object containing
* properties for additional configuration supported by synthesizer.
* If <code>info</code> is null then default settings are used.
*
* @throws MidiUnavailableException thrown if the synthesizer cannot be
* opened due to resource restrictions.
* @throws SecurityException thrown if the synthesizer cannot be
* opened due to security restrictions.
*
* @see #close
* @see #isOpen
*/
public AudioInputStream openStream(AudioFormat targetFormat,
Map<String, Object> info) throws MidiUnavailableException;
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
/**
* Information about property used in opening <code>AudioSynthesizer</code>.
*
* @author Karl Helgason
*/
public final class AudioSynthesizerPropertyInfo {
/**
* Constructs a <code>AudioSynthesizerPropertyInfo</code> object with a given
* name and value. The <code>description</code> and <code>choices</code>
* are initialized by <code>null</code> values.
*
* @param name the name of the property
* @param value the current value or class used for values.
*
*/
public AudioSynthesizerPropertyInfo(String name, Object value) {
this.name = name;
if (value instanceof Class)
valueClass = (Class)value;
else
{
this.value = value;
if (value != null)
valueClass = value.getClass();
}
}
/**
* The name of the property.
*/
public String name;
/**
* A brief description of the property, which may be null.
*/
public String description = null;
/**
* The <code>value</code> field specifies the current value of
* the property.
*/
public Object value = null;
/**
* The <code>valueClass</code> field specifies class
* used in <code>value</code> field.
*/
public Class valueClass = null;
/**
* An array of possible values if the value for the field
* <code>AudioSynthesizerPropertyInfo.value</code> may be selected
* from a particular set of values; otherwise null.
*/
public Object[] choices = null;
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2002, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import javax.sound.sampled.Clip;
/**
* Interface for Clip objects that close themselves automatically
*
* @author Florian Bomers
*/
interface AutoClosingClip extends Clip {
/**
* Indicates whether this clip instance is auto closing.
* When the clip is auto closing, it calls the close()
* method automatically after a short period of inactivity.<br>
* <br>
*
* @return true if this clip is auto closing
*/
boolean isAutoClosing();
/**
* Sets whether this Clip instance is auto closing or not.
* If true, the close() method will be called automatically
* after a short period of inactivity.
*
* @param value - true to set this clip to auto closing
*/
void setAutoClosing(boolean value);
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import javax.sound.midi.Receiver;
/**
* Interface for Sequencers that are able to do the auto-connect
* as required by MidiSystem.getSequencer()
*
* @author Florian Bomers
*/
public interface AutoConnectSequencer {
/**
* Set the receiver that this device is
* auto-connected. If non-null, the device
* needs to re-connect itself to a suitable
* device in open().
*/
public void setAutoConnect(Receiver autoConnectReceiver);
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
/**
* This class is used to store information to describe soundbanks, instruments
* and samples. It is stored inside a "INFO" List Chunk inside DLS files.
*
* @author Karl Helgason
*/
public final class DLSInfo {
/**
* (INAM) Title or subject.
*/
public String name = "untitled";
/**
* (ICRD) Date of creation, the format is: YYYY-MM-DD.
* For example 2007-01-01 for 1. january of year 2007.
*/
public String creationDate = null;
/**
* (IENG) Name of engineer who created the object.
*/
public String engineers = null;
/**
* (IPRD) Name of the product which the object is intended for.
*/
public String product = null;
/**
* (ICOP) Copyright information.
*/
public String copyright = null;
/**
* (ICMT) General comments. Doesn't contain newline characters.
*/
public String comments = null;
/**
* (ISFT) Name of software package used to create the file.
*/
public String tools = null;
/**
* (IARL) Where content is archived.
*/
public String archival_location = null;
/**
* (IART) Artists of original content.
*/
public String artist = null;
/**
* (ICMS) Names of persons or orginizations who commissioned the file.
*/
public String commissioned = null;
/**
* (IGNR) Genre of the work.
* Example: jazz, classical, rock, etc.
*/
public String genre = null;
/**
* (IKEY) List of keyword that describe the content.
* Examples: FX, bird, piano, etc.
*/
public String keywords = null;
/**
* (IMED) Describes original medium of the data.
* For example: record, CD, etc.
*/
public String medium = null;
/**
* (ISBJ) Description of the content.
*/
public String subject = null;
/**
* (ISRC) Name of person or orginization who supplied
* orginal material for the file.
*/
public String source = null;
/**
* (ISRF) Source media for sample data is from.
* For example: CD, TV, etc.
*/
public String source_form = null;
/**
* (ITCH) Technician who sample the file/object.
*/
public String technician = null;
}

View File

@@ -0,0 +1,449 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sound.midi.Patch;
/**
* This class is used to store information to describe instrument.
* It contains list of regions and modulators.
* It is stored inside a "ins " List Chunk inside DLS files.
* In the DLS documentation a modulator is called articulator.
*
* @author Karl Helgason
*/
public final class DLSInstrument extends ModelInstrument {
int preset = 0;
int bank = 0;
boolean druminstrument = false;
byte[] guid = null;
DLSInfo info = new DLSInfo();
List<DLSRegion> regions = new ArrayList<DLSRegion>();
List<DLSModulator> modulators = new ArrayList<DLSModulator>();
public DLSInstrument() {
super(null, null, null, null);
}
public DLSInstrument(DLSSoundbank soundbank) {
super(soundbank, null, null, null);
}
public DLSInfo getInfo() {
return info;
}
public String getName() {
return info.name;
}
public void setName(String name) {
info.name = name;
}
public ModelPatch getPatch() {
return new ModelPatch(bank, preset, druminstrument);
}
public void setPatch(Patch patch) {
if (patch instanceof ModelPatch && ((ModelPatch)patch).isPercussion()) {
druminstrument = true;
bank = patch.getBank();
preset = patch.getProgram();
} else {
druminstrument = false;
bank = patch.getBank();
preset = patch.getProgram();
}
}
public Object getData() {
return null;
}
public List<DLSRegion> getRegions() {
return regions;
}
public List<DLSModulator> getModulators() {
return modulators;
}
public String toString() {
if (druminstrument)
return "Drumkit: " + info.name
+ " bank #" + bank + " preset #" + preset;
else
return "Instrument: " + info.name
+ " bank #" + bank + " preset #" + preset;
}
private ModelIdentifier convertToModelDest(int dest) {
if (dest == DLSModulator.CONN_DST_NONE)
return null;
if (dest == DLSModulator.CONN_DST_GAIN)
return ModelDestination.DESTINATION_GAIN;
if (dest == DLSModulator.CONN_DST_PITCH)
return ModelDestination.DESTINATION_PITCH;
if (dest == DLSModulator.CONN_DST_PAN)
return ModelDestination.DESTINATION_PAN;
if (dest == DLSModulator.CONN_DST_LFO_FREQUENCY)
return ModelDestination.DESTINATION_LFO1_FREQ;
if (dest == DLSModulator.CONN_DST_LFO_STARTDELAY)
return ModelDestination.DESTINATION_LFO1_DELAY;
if (dest == DLSModulator.CONN_DST_EG1_ATTACKTIME)
return ModelDestination.DESTINATION_EG1_ATTACK;
if (dest == DLSModulator.CONN_DST_EG1_DECAYTIME)
return ModelDestination.DESTINATION_EG1_DECAY;
if (dest == DLSModulator.CONN_DST_EG1_RELEASETIME)
return ModelDestination.DESTINATION_EG1_RELEASE;
if (dest == DLSModulator.CONN_DST_EG1_SUSTAINLEVEL)
return ModelDestination.DESTINATION_EG1_SUSTAIN;
if (dest == DLSModulator.CONN_DST_EG2_ATTACKTIME)
return ModelDestination.DESTINATION_EG2_ATTACK;
if (dest == DLSModulator.CONN_DST_EG2_DECAYTIME)
return ModelDestination.DESTINATION_EG2_DECAY;
if (dest == DLSModulator.CONN_DST_EG2_RELEASETIME)
return ModelDestination.DESTINATION_EG2_RELEASE;
if (dest == DLSModulator.CONN_DST_EG2_SUSTAINLEVEL)
return ModelDestination.DESTINATION_EG2_SUSTAIN;
// DLS2 Destinations
if (dest == DLSModulator.CONN_DST_KEYNUMBER)
return ModelDestination.DESTINATION_KEYNUMBER;
if (dest == DLSModulator.CONN_DST_CHORUS)
return ModelDestination.DESTINATION_CHORUS;
if (dest == DLSModulator.CONN_DST_REVERB)
return ModelDestination.DESTINATION_REVERB;
if (dest == DLSModulator.CONN_DST_VIB_FREQUENCY)
return ModelDestination.DESTINATION_LFO2_FREQ;
if (dest == DLSModulator.CONN_DST_VIB_STARTDELAY)
return ModelDestination.DESTINATION_LFO2_DELAY;
if (dest == DLSModulator.CONN_DST_EG1_DELAYTIME)
return ModelDestination.DESTINATION_EG1_DELAY;
if (dest == DLSModulator.CONN_DST_EG1_HOLDTIME)
return ModelDestination.DESTINATION_EG1_HOLD;
if (dest == DLSModulator.CONN_DST_EG1_SHUTDOWNTIME)
return ModelDestination.DESTINATION_EG1_SHUTDOWN;
if (dest == DLSModulator.CONN_DST_EG2_DELAYTIME)
return ModelDestination.DESTINATION_EG2_DELAY;
if (dest == DLSModulator.CONN_DST_EG2_HOLDTIME)
return ModelDestination.DESTINATION_EG2_HOLD;
if (dest == DLSModulator.CONN_DST_FILTER_CUTOFF)
return ModelDestination.DESTINATION_FILTER_FREQ;
if (dest == DLSModulator.CONN_DST_FILTER_Q)
return ModelDestination.DESTINATION_FILTER_Q;
return null;
}
private ModelIdentifier convertToModelSrc(int src) {
if (src == DLSModulator.CONN_SRC_NONE)
return null;
if (src == DLSModulator.CONN_SRC_LFO)
return ModelSource.SOURCE_LFO1;
if (src == DLSModulator.CONN_SRC_KEYONVELOCITY)
return ModelSource.SOURCE_NOTEON_VELOCITY;
if (src == DLSModulator.CONN_SRC_KEYNUMBER)
return ModelSource.SOURCE_NOTEON_KEYNUMBER;
if (src == DLSModulator.CONN_SRC_EG1)
return ModelSource.SOURCE_EG1;
if (src == DLSModulator.CONN_SRC_EG2)
return ModelSource.SOURCE_EG2;
if (src == DLSModulator.CONN_SRC_PITCHWHEEL)
return ModelSource.SOURCE_MIDI_PITCH;
if (src == DLSModulator.CONN_SRC_CC1)
return new ModelIdentifier("midi_cc", "1", 0);
if (src == DLSModulator.CONN_SRC_CC7)
return new ModelIdentifier("midi_cc", "7", 0);
if (src == DLSModulator.CONN_SRC_CC10)
return new ModelIdentifier("midi_cc", "10", 0);
if (src == DLSModulator.CONN_SRC_CC11)
return new ModelIdentifier("midi_cc", "11", 0);
if (src == DLSModulator.CONN_SRC_RPN0)
return new ModelIdentifier("midi_rpn", "0", 0);
if (src == DLSModulator.CONN_SRC_RPN1)
return new ModelIdentifier("midi_rpn", "1", 0);
if (src == DLSModulator.CONN_SRC_POLYPRESSURE)
return ModelSource.SOURCE_MIDI_POLY_PRESSURE;
if (src == DLSModulator.CONN_SRC_CHANNELPRESSURE)
return ModelSource.SOURCE_MIDI_CHANNEL_PRESSURE;
if (src == DLSModulator.CONN_SRC_VIBRATO)
return ModelSource.SOURCE_LFO2;
if (src == DLSModulator.CONN_SRC_MONOPRESSURE)
return ModelSource.SOURCE_MIDI_CHANNEL_PRESSURE;
if (src == DLSModulator.CONN_SRC_CC91)
return new ModelIdentifier("midi_cc", "91", 0);
if (src == DLSModulator.CONN_SRC_CC93)
return new ModelIdentifier("midi_cc", "93", 0);
return null;
}
private ModelConnectionBlock convertToModel(DLSModulator mod) {
ModelIdentifier source = convertToModelSrc(mod.getSource());
ModelIdentifier control = convertToModelSrc(mod.getControl());
ModelIdentifier destination_id =
convertToModelDest(mod.getDestination());
int scale = mod.getScale();
double f_scale;
if (scale == Integer.MIN_VALUE)
f_scale = Double.NEGATIVE_INFINITY;
else
f_scale = scale / 65536.0;
if (destination_id != null) {
ModelSource src = null;
ModelSource ctrl = null;
ModelConnectionBlock block = new ModelConnectionBlock();
if (control != null) {
ModelSource s = new ModelSource();
if (control == ModelSource.SOURCE_MIDI_PITCH) {
((ModelStandardTransform)s.getTransform()).setPolarity(
ModelStandardTransform.POLARITY_BIPOLAR);
} else if (control == ModelSource.SOURCE_LFO1
|| control == ModelSource.SOURCE_LFO2) {
((ModelStandardTransform)s.getTransform()).setPolarity(
ModelStandardTransform.POLARITY_BIPOLAR);
}
s.setIdentifier(control);
block.addSource(s);
ctrl = s;
}
if (source != null) {
ModelSource s = new ModelSource();
if (source == ModelSource.SOURCE_MIDI_PITCH) {
((ModelStandardTransform)s.getTransform()).setPolarity(
ModelStandardTransform.POLARITY_BIPOLAR);
} else if (source == ModelSource.SOURCE_LFO1
|| source == ModelSource.SOURCE_LFO2) {
((ModelStandardTransform)s.getTransform()).setPolarity(
ModelStandardTransform.POLARITY_BIPOLAR);
}
s.setIdentifier(source);
block.addSource(s);
src = s;
}
ModelDestination destination = new ModelDestination();
destination.setIdentifier(destination_id);
block.setDestination(destination);
if (mod.getVersion() == 1) {
//if (mod.getTransform() == DLSModulator.CONN_TRN_CONCAVE) {
// ((ModelStandardTransform)destination.getTransform())
// .setTransform(
// ModelStandardTransform.TRANSFORM_CONCAVE);
//}
if (mod.getTransform() == DLSModulator.CONN_TRN_CONCAVE) {
if (src != null) {
((ModelStandardTransform)src.getTransform())
.setTransform(
ModelStandardTransform.TRANSFORM_CONCAVE);
((ModelStandardTransform)src.getTransform())
.setDirection(
ModelStandardTransform.DIRECTION_MAX2MIN);
}
if (ctrl != null) {
((ModelStandardTransform)ctrl.getTransform())
.setTransform(
ModelStandardTransform.TRANSFORM_CONCAVE);
((ModelStandardTransform)ctrl.getTransform())
.setDirection(
ModelStandardTransform.DIRECTION_MAX2MIN);
}
}
} else if (mod.getVersion() == 2) {
int transform = mod.getTransform();
int src_transform_invert = (transform >> 15) & 1;
int src_transform_bipolar = (transform >> 14) & 1;
int src_transform = (transform >> 10) & 8;
int ctr_transform_invert = (transform >> 9) & 1;
int ctr_transform_bipolar = (transform >> 8) & 1;
int ctr_transform = (transform >> 4) & 8;
if (src != null) {
int trans = ModelStandardTransform.TRANSFORM_LINEAR;
if (src_transform == DLSModulator.CONN_TRN_SWITCH)
trans = ModelStandardTransform.TRANSFORM_SWITCH;
if (src_transform == DLSModulator.CONN_TRN_CONCAVE)
trans = ModelStandardTransform.TRANSFORM_CONCAVE;
if (src_transform == DLSModulator.CONN_TRN_CONVEX)
trans = ModelStandardTransform.TRANSFORM_CONVEX;
((ModelStandardTransform)src.getTransform())
.setTransform(trans);
((ModelStandardTransform)src.getTransform())
.setPolarity(src_transform_bipolar == 1);
((ModelStandardTransform)src.getTransform())
.setDirection(src_transform_invert == 1);
}
if (ctrl != null) {
int trans = ModelStandardTransform.TRANSFORM_LINEAR;
if (ctr_transform == DLSModulator.CONN_TRN_SWITCH)
trans = ModelStandardTransform.TRANSFORM_SWITCH;
if (ctr_transform == DLSModulator.CONN_TRN_CONCAVE)
trans = ModelStandardTransform.TRANSFORM_CONCAVE;
if (ctr_transform == DLSModulator.CONN_TRN_CONVEX)
trans = ModelStandardTransform.TRANSFORM_CONVEX;
((ModelStandardTransform)ctrl.getTransform())
.setTransform(trans);
((ModelStandardTransform)ctrl.getTransform())
.setPolarity(ctr_transform_bipolar == 1);
((ModelStandardTransform)ctrl.getTransform())
.setDirection(ctr_transform_invert == 1);
}
/* No output transforms are defined the DLS Level 2
int out_transform = transform % 8;
int trans = ModelStandardTransform.TRANSFORM_LINEAR;
if (out_transform == DLSModulator.CONN_TRN_SWITCH)
trans = ModelStandardTransform.TRANSFORM_SWITCH;
if (out_transform == DLSModulator.CONN_TRN_CONCAVE)
trans = ModelStandardTransform.TRANSFORM_CONCAVE;
if (out_transform == DLSModulator.CONN_TRN_CONVEX)
trans = ModelStandardTransform.TRANSFORM_CONVEX;
if (ctrl != null) {
((ModelStandardTransform)destination.getTransform())
.setTransform(trans);
}
*/
}
block.setScale(f_scale);
return block;
}
return null;
}
public ModelPerformer[] getPerformers() {
List<ModelPerformer> performers = new ArrayList<ModelPerformer>();
Map<String, DLSModulator> modmap = new HashMap<String, DLSModulator>();
for (DLSModulator mod: getModulators()) {
modmap.put(mod.getSource() + "x" + mod.getControl() + "=" +
mod.getDestination(), mod);
}
Map<String, DLSModulator> insmodmap =
new HashMap<String, DLSModulator>();
for (DLSRegion zone: regions) {
ModelPerformer performer = new ModelPerformer();
performer.setName(zone.getSample().getName());
performer.setSelfNonExclusive((zone.getFusoptions() &
DLSRegion.OPTION_SELFNONEXCLUSIVE) != 0);
performer.setExclusiveClass(zone.getExclusiveClass());
performer.setKeyFrom(zone.getKeyfrom());
performer.setKeyTo(zone.getKeyto());
performer.setVelFrom(zone.getVelfrom());
performer.setVelTo(zone.getVelto());
insmodmap.clear();
insmodmap.putAll(modmap);
for (DLSModulator mod: zone.getModulators()) {
insmodmap.put(mod.getSource() + "x" + mod.getControl() + "=" +
mod.getDestination(), mod);
}
List<ModelConnectionBlock> blocks = performer.getConnectionBlocks();
for (DLSModulator mod: insmodmap.values()) {
ModelConnectionBlock p = convertToModel(mod);
if (p != null)
blocks.add(p);
}
DLSSample sample = zone.getSample();
DLSSampleOptions sampleopt = zone.getSampleoptions();
if (sampleopt == null)
sampleopt = sample.getSampleoptions();
ModelByteBuffer buff = sample.getDataBuffer();
float pitchcorrection = (-sampleopt.unitynote * 100) +
sampleopt.finetune;
ModelByteBufferWavetable osc = new ModelByteBufferWavetable(buff,
sample.getFormat(), pitchcorrection);
osc.setAttenuation(osc.getAttenuation() / 65536f);
if (sampleopt.getLoops().size() != 0) {
DLSSampleLoop loop = sampleopt.getLoops().get(0);
osc.setLoopStart((int)loop.getStart());
osc.setLoopLength((int)loop.getLength());
if (loop.getType() == DLSSampleLoop.LOOP_TYPE_FORWARD)
osc.setLoopType(ModelWavetable.LOOP_TYPE_FORWARD);
if (loop.getType() == DLSSampleLoop.LOOP_TYPE_RELEASE)
osc.setLoopType(ModelWavetable.LOOP_TYPE_RELEASE);
else
osc.setLoopType(ModelWavetable.LOOP_TYPE_FORWARD);
}
performer.getConnectionBlocks().add(
new ModelConnectionBlock(SoftFilter.FILTERTYPE_LP12,
new ModelDestination(
new ModelIdentifier("filter", "type", 1))));
performer.getOscillators().add(osc);
performers.add(performer);
}
return performers.toArray(new ModelPerformer[performers.size()]);
}
public byte[] getGuid() {
return guid == null ? null : Arrays.copyOf(guid, guid.length);
}
public void setGuid(byte[] guid) {
this.guid = guid == null ? null : Arrays.copyOf(guid, guid.length);
}
}

View File

@@ -0,0 +1,351 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
/**
* This class is used to store modulator/artiuclation data.
* A modulator connects one synthesizer source to
* a destination. For example a note on velocity
* can be mapped to the gain of the synthesized voice.
* It is stored as a "art1" or "art2" chunk inside DLS files.
*
* @author Karl Helgason
*/
public final class DLSModulator {
// DLS1 Destinations
public static final int CONN_DST_NONE = 0x000; // 0
public static final int CONN_DST_GAIN = 0x001; // cB
public static final int CONN_DST_PITCH = 0x003; // cent
public static final int CONN_DST_PAN = 0x004; // 0.1%
public static final int CONN_DST_LFO_FREQUENCY = 0x104; // cent (default 5 Hz)
public static final int CONN_DST_LFO_STARTDELAY = 0x105; // timecent
public static final int CONN_DST_EG1_ATTACKTIME = 0x206; // timecent
public static final int CONN_DST_EG1_DECAYTIME = 0x207; // timecent
public static final int CONN_DST_EG1_RELEASETIME = 0x209; // timecent
public static final int CONN_DST_EG1_SUSTAINLEVEL = 0x20A; // 0.1%
public static final int CONN_DST_EG2_ATTACKTIME = 0x30A; // timecent
public static final int CONN_DST_EG2_DECAYTIME = 0x30B; // timecent
public static final int CONN_DST_EG2_RELEASETIME = 0x30D; // timecent
public static final int CONN_DST_EG2_SUSTAINLEVEL = 0x30E; // 0.1%
// DLS2 Destinations
public static final int CONN_DST_KEYNUMBER = 0x005;
public static final int CONN_DST_LEFT = 0x010; // 0.1%
public static final int CONN_DST_RIGHT = 0x011; // 0.1%
public static final int CONN_DST_CENTER = 0x012; // 0.1%
public static final int CONN_DST_LEFTREAR = 0x013; // 0.1%
public static final int CONN_DST_RIGHTREAR = 0x014; // 0.1%
public static final int CONN_DST_LFE_CHANNEL = 0x015; // 0.1%
public static final int CONN_DST_CHORUS = 0x080; // 0.1%
public static final int CONN_DST_REVERB = 0x081; // 0.1%
public static final int CONN_DST_VIB_FREQUENCY = 0x114; // cent
public static final int CONN_DST_VIB_STARTDELAY = 0x115; // dB
public static final int CONN_DST_EG1_DELAYTIME = 0x20B; // timecent
public static final int CONN_DST_EG1_HOLDTIME = 0x20C; // timecent
public static final int CONN_DST_EG1_SHUTDOWNTIME = 0x20D; // timecent
public static final int CONN_DST_EG2_DELAYTIME = 0x30F; // timecent
public static final int CONN_DST_EG2_HOLDTIME = 0x310; // timecent
public static final int CONN_DST_FILTER_CUTOFF = 0x500; // cent
public static final int CONN_DST_FILTER_Q = 0x501; // dB
// DLS1 Sources
public static final int CONN_SRC_NONE = 0x000; // 1
public static final int CONN_SRC_LFO = 0x001; // linear (sine wave)
public static final int CONN_SRC_KEYONVELOCITY = 0x002; // ??db or velocity??
public static final int CONN_SRC_KEYNUMBER = 0x003; // ??cent or keynumber??
public static final int CONN_SRC_EG1 = 0x004; // linear direct from eg
public static final int CONN_SRC_EG2 = 0x005; // linear direct from eg
public static final int CONN_SRC_PITCHWHEEL = 0x006; // linear -1..1
public static final int CONN_SRC_CC1 = 0x081; // linear 0..1
public static final int CONN_SRC_CC7 = 0x087; // linear 0..1
public static final int CONN_SRC_CC10 = 0x08A; // linear 0..1
public static final int CONN_SRC_CC11 = 0x08B; // linear 0..1
public static final int CONN_SRC_RPN0 = 0x100; // ?? // Pitch Bend Range
public static final int CONN_SRC_RPN1 = 0x101; // ?? // Fine Tune
public static final int CONN_SRC_RPN2 = 0x102; // ?? // Course Tune
// DLS2 Sources
public static final int CONN_SRC_POLYPRESSURE = 0x007; // linear 0..1
public static final int CONN_SRC_CHANNELPRESSURE = 0x008; // linear 0..1
public static final int CONN_SRC_VIBRATO = 0x009; // linear 0..1
public static final int CONN_SRC_MONOPRESSURE = 0x00A; // linear 0..1
public static final int CONN_SRC_CC91 = 0x0DB; // linear 0..1
public static final int CONN_SRC_CC93 = 0x0DD; // linear 0..1
// DLS1 Transforms
public static final int CONN_TRN_NONE = 0x000;
public static final int CONN_TRN_CONCAVE = 0x001;
// DLS2 Transforms
public static final int CONN_TRN_CONVEX = 0x002;
public static final int CONN_TRN_SWITCH = 0x003;
public static final int DST_FORMAT_CB = 1;
public static final int DST_FORMAT_CENT = 1;
public static final int DST_FORMAT_TIMECENT = 2;
public static final int DST_FORMAT_PERCENT = 3;
int source;
int control;
int destination;
int transform;
int scale;
int version = 1;
public int getControl() {
return control;
}
public void setControl(int control) {
this.control = control;
}
public static int getDestinationFormat(int destination) {
if (destination == CONN_DST_GAIN)
return DST_FORMAT_CB;
if (destination == CONN_DST_PITCH)
return DST_FORMAT_CENT;
if (destination == CONN_DST_PAN)
return DST_FORMAT_PERCENT;
if (destination == CONN_DST_LFO_FREQUENCY)
return DST_FORMAT_CENT;
if (destination == CONN_DST_LFO_STARTDELAY)
return DST_FORMAT_TIMECENT;
if (destination == CONN_DST_EG1_ATTACKTIME)
return DST_FORMAT_TIMECENT;
if (destination == CONN_DST_EG1_DECAYTIME)
return DST_FORMAT_TIMECENT;
if (destination == CONN_DST_EG1_RELEASETIME)
return DST_FORMAT_TIMECENT;
if (destination == CONN_DST_EG1_SUSTAINLEVEL)
return DST_FORMAT_PERCENT;
if (destination == CONN_DST_EG2_ATTACKTIME)
return DST_FORMAT_TIMECENT;
if (destination == CONN_DST_EG2_DECAYTIME)
return DST_FORMAT_TIMECENT;
if (destination == CONN_DST_EG2_RELEASETIME)
return DST_FORMAT_TIMECENT;
if (destination == CONN_DST_EG2_SUSTAINLEVEL)
return DST_FORMAT_PERCENT;
if (destination == CONN_DST_KEYNUMBER)
return DST_FORMAT_CENT; // NOT SURE WITHOUT DLS 2 SPEC
if (destination == CONN_DST_LEFT)
return DST_FORMAT_CB;
if (destination == CONN_DST_RIGHT)
return DST_FORMAT_CB;
if (destination == CONN_DST_CENTER)
return DST_FORMAT_CB;
if (destination == CONN_DST_LEFTREAR)
return DST_FORMAT_CB;
if (destination == CONN_DST_RIGHTREAR)
return DST_FORMAT_CB;
if (destination == CONN_DST_LFE_CHANNEL)
return DST_FORMAT_CB;
if (destination == CONN_DST_CHORUS)
return DST_FORMAT_PERCENT;
if (destination == CONN_DST_REVERB)
return DST_FORMAT_PERCENT;
if (destination == CONN_DST_VIB_FREQUENCY)
return DST_FORMAT_CENT;
if (destination == CONN_DST_VIB_STARTDELAY)
return DST_FORMAT_TIMECENT;
if (destination == CONN_DST_EG1_DELAYTIME)
return DST_FORMAT_TIMECENT;
if (destination == CONN_DST_EG1_HOLDTIME)
return DST_FORMAT_TIMECENT;
if (destination == CONN_DST_EG1_SHUTDOWNTIME)
return DST_FORMAT_TIMECENT;
if (destination == CONN_DST_EG2_DELAYTIME)
return DST_FORMAT_TIMECENT;
if (destination == CONN_DST_EG2_HOLDTIME)
return DST_FORMAT_TIMECENT;
if (destination == CONN_DST_FILTER_CUTOFF)
return DST_FORMAT_CENT;
if (destination == CONN_DST_FILTER_Q)
return DST_FORMAT_CB;
return -1;
}
public static String getDestinationName(int destination) {
if (destination == CONN_DST_GAIN)
return "gain";
if (destination == CONN_DST_PITCH)
return "pitch";
if (destination == CONN_DST_PAN)
return "pan";
if (destination == CONN_DST_LFO_FREQUENCY)
return "lfo1.freq";
if (destination == CONN_DST_LFO_STARTDELAY)
return "lfo1.delay";
if (destination == CONN_DST_EG1_ATTACKTIME)
return "eg1.attack";
if (destination == CONN_DST_EG1_DECAYTIME)
return "eg1.decay";
if (destination == CONN_DST_EG1_RELEASETIME)
return "eg1.release";
if (destination == CONN_DST_EG1_SUSTAINLEVEL)
return "eg1.sustain";
if (destination == CONN_DST_EG2_ATTACKTIME)
return "eg2.attack";
if (destination == CONN_DST_EG2_DECAYTIME)
return "eg2.decay";
if (destination == CONN_DST_EG2_RELEASETIME)
return "eg2.release";
if (destination == CONN_DST_EG2_SUSTAINLEVEL)
return "eg2.sustain";
if (destination == CONN_DST_KEYNUMBER)
return "keynumber";
if (destination == CONN_DST_LEFT)
return "left";
if (destination == CONN_DST_RIGHT)
return "right";
if (destination == CONN_DST_CENTER)
return "center";
if (destination == CONN_DST_LEFTREAR)
return "leftrear";
if (destination == CONN_DST_RIGHTREAR)
return "rightrear";
if (destination == CONN_DST_LFE_CHANNEL)
return "lfe_channel";
if (destination == CONN_DST_CHORUS)
return "chorus";
if (destination == CONN_DST_REVERB)
return "reverb";
if (destination == CONN_DST_VIB_FREQUENCY)
return "vib.freq";
if (destination == CONN_DST_VIB_STARTDELAY)
return "vib.delay";
if (destination == CONN_DST_EG1_DELAYTIME)
return "eg1.delay";
if (destination == CONN_DST_EG1_HOLDTIME)
return "eg1.hold";
if (destination == CONN_DST_EG1_SHUTDOWNTIME)
return "eg1.shutdown";
if (destination == CONN_DST_EG2_DELAYTIME)
return "eg2.delay";
if (destination == CONN_DST_EG2_HOLDTIME)
return "eg.2hold";
if (destination == CONN_DST_FILTER_CUTOFF)
return "filter.cutoff"; // NOT SURE WITHOUT DLS 2 SPEC
if (destination == CONN_DST_FILTER_Q)
return "filter.q"; // NOT SURE WITHOUT DLS 2 SPEC
return null;
}
public static String getSourceName(int source) {
if (source == CONN_SRC_NONE)
return "none";
if (source == CONN_SRC_LFO)
return "lfo";
if (source == CONN_SRC_KEYONVELOCITY)
return "keyonvelocity";
if (source == CONN_SRC_KEYNUMBER)
return "keynumber";
if (source == CONN_SRC_EG1)
return "eg1";
if (source == CONN_SRC_EG2)
return "eg2";
if (source == CONN_SRC_PITCHWHEEL)
return "pitchweel";
if (source == CONN_SRC_CC1)
return "cc1";
if (source == CONN_SRC_CC7)
return "cc7";
if (source == CONN_SRC_CC10)
return "c10";
if (source == CONN_SRC_CC11)
return "cc11";
if (source == CONN_SRC_POLYPRESSURE)
return "polypressure";
if (source == CONN_SRC_CHANNELPRESSURE)
return "channelpressure";
if (source == CONN_SRC_VIBRATO)
return "vibrato";
if (source == CONN_SRC_MONOPRESSURE)
return "monopressure";
if (source == CONN_SRC_CC91)
return "cc91";
if (source == CONN_SRC_CC93)
return "cc93";
return null;
}
public int getDestination() {
return destination;
}
public void setDestination(int destination) {
this.destination = destination;
}
public int getScale() {
return scale;
}
public void setScale(int scale) {
this.scale = scale;
}
public int getSource() {
return source;
}
public void setSource(int source) {
this.source = source;
}
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
public int getTransform() {
return transform;
}
public void setTransform(int transform) {
this.transform = transform;
}
}

View File

@@ -0,0 +1,150 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.util.ArrayList;
import java.util.List;
/**
* This class is used to store region parts for instrument.
* A region has a velocity and key range which it response to.
* And it has a list of modulators/articulators which
* is used how to synthesize a single voice.
* It is stored inside a "rgn " List Chunk inside DLS files.
*
* @author Karl Helgason
*/
public final class DLSRegion {
public final static int OPTION_SELFNONEXCLUSIVE = 0x0001;
List<DLSModulator> modulators = new ArrayList<DLSModulator>();
int keyfrom;
int keyto;
int velfrom;
int velto;
int options;
int exclusiveClass;
int fusoptions;
int phasegroup;
long channel;
DLSSample sample = null;
DLSSampleOptions sampleoptions;
public List<DLSModulator> getModulators() {
return modulators;
}
public long getChannel() {
return channel;
}
public void setChannel(long channel) {
this.channel = channel;
}
public int getExclusiveClass() {
return exclusiveClass;
}
public void setExclusiveClass(int exclusiveClass) {
this.exclusiveClass = exclusiveClass;
}
public int getFusoptions() {
return fusoptions;
}
public void setFusoptions(int fusoptions) {
this.fusoptions = fusoptions;
}
public int getKeyfrom() {
return keyfrom;
}
public void setKeyfrom(int keyfrom) {
this.keyfrom = keyfrom;
}
public int getKeyto() {
return keyto;
}
public void setKeyto(int keyto) {
this.keyto = keyto;
}
public int getOptions() {
return options;
}
public void setOptions(int options) {
this.options = options;
}
public int getPhasegroup() {
return phasegroup;
}
public void setPhasegroup(int phasegroup) {
this.phasegroup = phasegroup;
}
public DLSSample getSample() {
return sample;
}
public void setSample(DLSSample sample) {
this.sample = sample;
}
public int getVelfrom() {
return velfrom;
}
public void setVelfrom(int velfrom) {
this.velfrom = velfrom;
}
public int getVelto() {
return velto;
}
public void setVelto(int velto) {
this.velto = velto;
}
public void setModulators(List<DLSModulator> modulators) {
this.modulators = modulators;
}
public DLSSampleOptions getSampleoptions() {
return sampleoptions;
}
public void setSampleoptions(DLSSampleOptions sampleOptions) {
this.sampleoptions = sampleOptions;
}
}

View File

@@ -0,0 +1,123 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.io.InputStream;
import java.util.Arrays;
import javax.sound.midi.Soundbank;
import javax.sound.midi.SoundbankResource;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
/**
* This class is used to store the sample data itself.
* A sample is encoded as PCM audio stream
* and in DLS Level 1 files it is always a mono 8/16 bit stream.
* They are stored just like RIFF WAVE files are stored.
* It is stored inside a "wave" List Chunk inside DLS files.
*
* @author Karl Helgason
*/
public final class DLSSample extends SoundbankResource {
byte[] guid = null;
DLSInfo info = new DLSInfo();
DLSSampleOptions sampleoptions;
ModelByteBuffer data;
AudioFormat format;
public DLSSample(Soundbank soundBank) {
super(soundBank, null, AudioInputStream.class);
}
public DLSSample() {
super(null, null, AudioInputStream.class);
}
public DLSInfo getInfo() {
return info;
}
public Object getData() {
AudioFormat format = getFormat();
InputStream is = data.getInputStream();
if (is == null)
return null;
return new AudioInputStream(is, format, data.capacity());
}
public ModelByteBuffer getDataBuffer() {
return data;
}
public AudioFormat getFormat() {
return format;
}
public void setFormat(AudioFormat format) {
this.format = format;
}
public void setData(ModelByteBuffer data) {
this.data = data;
}
public void setData(byte[] data) {
this.data = new ModelByteBuffer(data);
}
public void setData(byte[] data, int offset, int length) {
this.data = new ModelByteBuffer(data, offset, length);
}
public String getName() {
return info.name;
}
public void setName(String name) {
info.name = name;
}
public DLSSampleOptions getSampleoptions() {
return sampleoptions;
}
public void setSampleoptions(DLSSampleOptions sampleOptions) {
this.sampleoptions = sampleOptions;
}
public String toString() {
return "Sample: " + info.name;
}
public byte[] getGuid() {
return guid == null ? null : Arrays.copyOf(guid, guid.length);
}
public void setGuid(byte[] guid) {
this.guid = guid == null ? null : Arrays.copyOf(guid, guid.length);
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
/**
* This class is used to store loop points inside DLSSampleOptions class.
*
* @author Karl Helgason
*/
public final class DLSSampleLoop {
public final static int LOOP_TYPE_FORWARD = 0;
public final static int LOOP_TYPE_RELEASE = 1;
long type;
long start;
long length;
public long getLength() {
return length;
}
public void setLength(long length) {
this.length = length;
}
public long getStart() {
return start;
}
public void setStart(long start) {
this.start = start;
}
public long getType() {
return type;
}
public void setType(long type) {
this.type = type;
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.util.ArrayList;
import java.util.List;
/**
* This class stores options how to playback sampled data like pitch/tuning,
* attenuation and loops.
* It is stored as a "wsmp" chunk inside DLS files.
*
* @author Karl Helgason
*/
public final class DLSSampleOptions {
int unitynote;
short finetune;
int attenuation;
long options;
List<DLSSampleLoop> loops = new ArrayList<DLSSampleLoop>();
public int getAttenuation() {
return attenuation;
}
public void setAttenuation(int attenuation) {
this.attenuation = attenuation;
}
public short getFinetune() {
return finetune;
}
public void setFinetune(short finetune) {
this.finetune = finetune;
}
public List<DLSSampleLoop> getLoops() {
return loops;
}
public long getOptions() {
return options;
}
public void setOptions(long options) {
this.options = options;
}
public int getUnitynote() {
return unitynote;
}
public void setUnitynote(int unitynote) {
this.unitynote = unitynote;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,74 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.Soundbank;
import javax.sound.midi.spi.SoundbankReader;
/**
* This class is used to connect the DLSSoundBank class
* to the SoundbankReader SPI interface.
*
* @author Karl Helgason
*/
public final class DLSSoundbankReader extends SoundbankReader {
public Soundbank getSoundbank(URL url)
throws InvalidMidiDataException, IOException {
try {
return new DLSSoundbank(url);
} catch (RIFFInvalidFormatException e) {
return null;
} catch(IOException ioe) {
return null;
}
}
public Soundbank getSoundbank(InputStream stream)
throws InvalidMidiDataException, IOException {
try {
stream.mark(512);
return new DLSSoundbank(stream);
} catch (RIFFInvalidFormatException e) {
stream.reset();
return null;
}
}
public Soundbank getSoundbank(File file)
throws InvalidMidiDataException, IOException {
try {
return new DLSSoundbank(file);
} catch (RIFFInvalidFormatException e) {
return null;
}
}
}

View File

@@ -0,0 +1,246 @@
/*
* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.util.Arrays;
import javax.sound.sampled.*;
/**
* Class to write an AudioInputStream to a SourceDataLine.
* Was previously an inner class in various classes like JavaSoundAudioClip
* and sun.audio.AudioDevice.
* It auto-opens and closes the SourceDataLine.
*
* @author Kara Kytle
* @author Florian Bomers
*/
public final class DataPusher implements Runnable {
private static final int AUTO_CLOSE_TIME = 5000;
private static final boolean DEBUG = false;
private final SourceDataLine source;
private final AudioFormat format;
// stream as source data
private final AudioInputStream ais;
// byte array as source data
private final byte[] audioData;
private final int audioDataByteLength;
private int pos;
private int newPos = -1;
private boolean looping;
private Thread pushThread = null;
private int wantedState;
private int threadState;
private final int STATE_NONE = 0;
private final int STATE_PLAYING = 1;
private final int STATE_WAITING = 2;
private final int STATE_STOPPING = 3;
private final int STATE_STOPPED = 4;
private final int BUFFER_SIZE = 16384;
public DataPusher(SourceDataLine sourceLine, AudioFormat format, byte[] audioData, int byteLength) {
this(sourceLine, format, null, audioData, byteLength);
}
public DataPusher(SourceDataLine sourceLine, AudioInputStream ais) {
this(sourceLine, ais.getFormat(), ais, null, 0);
}
private DataPusher(final SourceDataLine source, final AudioFormat format,
final AudioInputStream ais, final byte[] audioData,
final int audioDataByteLength) {
this.source = source;
this.format = format;
this.ais = ais;
this.audioDataByteLength = audioDataByteLength;
this.audioData = audioData == null ? null : Arrays.copyOf(audioData,
audioData.length);
}
public synchronized void start() {
start(false);
}
public synchronized void start(boolean loop) {
if (DEBUG || Printer.debug) Printer.debug("> DataPusher.start(loop="+loop+")");
try {
if (threadState == STATE_STOPPING) {
// wait that the thread has finished stopping
if (DEBUG || Printer.trace)Printer.trace("DataPusher.start(): calling stop()");
stop();
}
looping = loop;
newPos = 0;
wantedState = STATE_PLAYING;
if (!source.isOpen()) {
if (DEBUG || Printer.trace)Printer.trace("DataPusher: source.open()");
source.open(format);
}
if (DEBUG || Printer.trace)Printer.trace("DataPusher: source.flush()");
source.flush();
if (DEBUG || Printer.trace)Printer.trace("DataPusher: source.start()");
source.start();
if (pushThread == null) {
if (DEBUG || Printer.debug) Printer.debug("DataPusher.start(): Starting push");
pushThread = JSSecurityManager.createThread(this,
null, // name
false, // daemon
-1, // priority
true); // doStart
}
notifyAll();
} catch (Exception e) {
if (DEBUG || Printer.err) e.printStackTrace();
}
if (DEBUG || Printer.debug) Printer.debug("< DataPusher.start(loop="+loop+")");
}
public synchronized void stop() {
if (DEBUG || Printer.debug) Printer.debug("> DataPusher.stop()");
if (threadState == STATE_STOPPING
|| threadState == STATE_STOPPED
|| pushThread == null) {
if (DEBUG || Printer.debug) Printer.debug("DataPusher.stop(): nothing to do");
return;
}
if (DEBUG || Printer.debug) Printer.debug("DataPusher.stop(): Stopping push");
wantedState = STATE_WAITING;
if (source != null) {
if (DEBUG || Printer.trace)Printer.trace("DataPusher: source.flush()");
source.flush();
}
notifyAll();
int maxWaitCount = 50; // 5 seconds
while ((maxWaitCount-- >= 0) && (threadState == STATE_PLAYING)) {
try {
wait(100);
} catch (InterruptedException e) { }
}
if (DEBUG || Printer.debug) Printer.debug("< DataPusher.stop()");
}
synchronized void close() {
if (source != null) {
if (DEBUG || Printer.trace)Printer.trace("DataPusher.close(): source.close()");
source.close();
}
}
/**
* Write data to the source data line.
*/
public void run() {
byte[] buffer = null;
boolean useStream = (ais != null);
if (useStream) {
buffer = new byte[BUFFER_SIZE];
} else {
buffer = audioData;
}
while (wantedState != STATE_STOPPING) {
//try {
if (wantedState == STATE_WAITING) {
// wait for 5 seconds - maybe the clip is to be played again
if (DEBUG || Printer.debug)Printer.debug("DataPusher.run(): waiting 5 seconds");
try {
synchronized(this) {
threadState = STATE_WAITING;
wantedState = STATE_STOPPING;
wait(AUTO_CLOSE_TIME);
}
} catch (InterruptedException ie) {}
if (DEBUG || Printer.debug)Printer.debug("DataPusher.run(): waiting finished");
continue;
}
if (newPos >= 0) {
pos = newPos;
newPos = -1;
}
threadState = STATE_PLAYING;
int toWrite = BUFFER_SIZE;
if (useStream) {
try {
pos = 0; // always write from beginning of buffer
// don't use read(byte[]), because some streams
// may not override that method
toWrite = ais.read(buffer, 0, buffer.length);
} catch (java.io.IOException ioe) {
// end of stream
toWrite = -1;
}
} else {
if (toWrite > audioDataByteLength - pos) {
toWrite = audioDataByteLength - pos;
}
if (toWrite == 0) {
toWrite = -1; // end of "stream"
}
}
if (toWrite < 0) {
if (DEBUG || Printer.debug) Printer.debug("DataPusher.run(): Found end of stream");
if (!useStream && looping) {
if (DEBUG || Printer.debug)Printer.debug("DataPusher.run(): setting pos back to 0");
pos = 0;
continue;
}
if (DEBUG || Printer.debug)Printer.debug("DataPusher.run(): calling drain()");
wantedState = STATE_WAITING;
source.drain();
continue;
}
if (DEBUG || Printer.debug) Printer.debug("> DataPusher.run(): Writing " + toWrite + " bytes");
int bytesWritten = source.write(buffer, pos, toWrite);
pos += bytesWritten;
if (DEBUG || Printer.debug) Printer.debug("< DataPusher.run(): Wrote " + bytesWritten + " bytes");
}
threadState = STATE_STOPPING;
if (DEBUG || Printer.debug)Printer.debug("DataPusher: closing device");
if (Printer.trace)Printer.trace("DataPusher: source.flush()");
source.flush();
if (DEBUG || Printer.trace)Printer.trace("DataPusher: source.stop()");
source.stop();
if (DEBUG || Printer.trace)Printer.trace("DataPusher: source.flush()");
source.flush();
if (DEBUG || Printer.trace)Printer.trace("DataPusher: source.close()");
source.close();
threadState = STATE_STOPPED;
synchronized (this) {
pushThread = null;
notifyAll();
}
if (DEBUG || Printer.debug)Printer.debug("DataPusher:end of thread");
}
} // class DataPusher

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,177 @@
/*
* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.spi.MixerProvider;
/**
* DirectAudioDevice provider.
*
* @author Florian Bomers
*/
public final class DirectAudioDeviceProvider extends MixerProvider {
// STATIC VARIABLES
/**
* Set of info objects for all port input devices on the system.
*/
private static DirectAudioDeviceInfo[] infos;
/**
* Set of all port input devices on the system.
*/
private static DirectAudioDevice[] devices;
// STATIC
static {
// initialize
Platform.initialize();
}
// CONSTRUCTOR
/**
* Required public no-arg constructor.
*/
public DirectAudioDeviceProvider() {
synchronized (DirectAudioDeviceProvider.class) {
if (Platform.isDirectAudioEnabled()) {
init();
} else {
infos = new DirectAudioDeviceInfo[0];
devices = new DirectAudioDevice[0];
}
}
}
private static void init() {
// get the number of input devices
int numDevices = nGetNumDevices();
if (infos == null || infos.length != numDevices) {
if (Printer.trace) Printer.trace("DirectAudioDeviceProvider: init()");
// initialize the arrays
infos = new DirectAudioDeviceInfo[numDevices];
devices = new DirectAudioDevice[numDevices];
// fill in the info objects now.
for (int i = 0; i < infos.length; i++) {
infos[i] = nNewDirectAudioDeviceInfo(i);
}
if (Printer.trace) Printer.trace("DirectAudioDeviceProvider: init(): found numDevices: " + numDevices);
}
}
public Mixer.Info[] getMixerInfo() {
synchronized (DirectAudioDeviceProvider.class) {
Mixer.Info[] localArray = new Mixer.Info[infos.length];
System.arraycopy(infos, 0, localArray, 0, infos.length);
return localArray;
}
}
public Mixer getMixer(Mixer.Info info) {
synchronized (DirectAudioDeviceProvider.class) {
// if the default device is asked, we provide the mixer
// with SourceDataLine's
if (info == null) {
for (int i = 0; i < infos.length; i++) {
Mixer mixer = getDevice(infos[i]);
if (mixer.getSourceLineInfo().length > 0) {
return mixer;
}
}
}
// otherwise get the first mixer that matches
// the requested info object
for (int i = 0; i < infos.length; i++) {
if (infos[i].equals(info)) {
return getDevice(infos[i]);
}
}
}
throw new IllegalArgumentException("Mixer " + info.toString() + " not supported by this provider.");
}
private static Mixer getDevice(DirectAudioDeviceInfo info) {
int index = info.getIndex();
if (devices[index] == null) {
devices[index] = new DirectAudioDevice(info);
}
return devices[index];
}
// INNER CLASSES
/**
* Info class for DirectAudioDevices. Adds an index value and a string for
* making native references to a particular device.
* This constructor is called from native.
*/
static final class DirectAudioDeviceInfo extends Mixer.Info {
private final int index;
private final int maxSimulLines;
// For ALSA, the deviceID contains the encoded card index, device index, and sub-device-index
private final int deviceID;
private DirectAudioDeviceInfo(int index, int deviceID, int maxSimulLines,
String name, String vendor,
String description, String version) {
super(name, vendor, "Direct Audio Device: "+description, version);
this.index = index;
this.maxSimulLines = maxSimulLines;
this.deviceID = deviceID;
}
int getIndex() {
return index;
}
int getMaxSimulLines() {
return maxSimulLines;
}
int getDeviceID() {
return deviceID;
}
} // class DirectAudioDeviceInfo
// NATIVE METHODS
private static native int nGetNumDevices();
// index: [0..nGetNumDevices()-1]
private static native DirectAudioDeviceInfo nNewDirectAudioDeviceInfo(int deviceIndex);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,457 @@
/*
* Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.util.ArrayList;
import java.util.List;
import javax.sound.midi.ControllerEventListener;
import javax.sound.midi.MetaEventListener;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.ShortMessage;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
/**
* EventDispatcher. Used by various classes in the Java Sound implementation
* to send events.
*
* @author David Rivas
* @author Kara Kytle
* @author Florian Bomers
*/
final class EventDispatcher implements Runnable {
/**
* time of inactivity until the auto closing clips
* are closed
*/
private static final int AUTO_CLOSE_TIME = 5000;
/**
* List of events
*/
private final ArrayList eventQueue = new ArrayList();
/**
* Thread object for this EventDispatcher instance
*/
private Thread thread = null;
/*
* support for auto-closing Clips
*/
private final ArrayList<ClipInfo> autoClosingClips = new ArrayList<ClipInfo>();
/*
* support for monitoring data lines
*/
private final ArrayList<LineMonitor> lineMonitors = new ArrayList<LineMonitor>();
/**
* Approximate interval between calls to LineMonitor.checkLine
*/
static final int LINE_MONITOR_TIME = 400;
/**
* This start() method starts an event thread if one is not already active.
*/
synchronized void start() {
if(thread == null) {
thread = JSSecurityManager.createThread(this,
"Java Sound Event Dispatcher", // name
true, // daemon
-1, // priority
true); // doStart
}
}
/**
* Invoked when there is at least one event in the queue.
* Implement this as a callback to process one event.
*/
void processEvent(EventInfo eventInfo) {
int count = eventInfo.getListenerCount();
// process an LineEvent
if (eventInfo.getEvent() instanceof LineEvent) {
LineEvent event = (LineEvent) eventInfo.getEvent();
if (Printer.debug) Printer.debug("Sending "+event+" to "+count+" listeners");
for (int i = 0; i < count; i++) {
try {
((LineListener) eventInfo.getListener(i)).update(event);
} catch (Throwable t) {
if (Printer.err) t.printStackTrace();
}
}
return;
}
// process a MetaMessage
if (eventInfo.getEvent() instanceof MetaMessage) {
MetaMessage event = (MetaMessage)eventInfo.getEvent();
for (int i = 0; i < count; i++) {
try {
((MetaEventListener) eventInfo.getListener(i)).meta(event);
} catch (Throwable t) {
if (Printer.err) t.printStackTrace();
}
}
return;
}
// process a Controller or Mode Event
if (eventInfo.getEvent() instanceof ShortMessage) {
ShortMessage event = (ShortMessage)eventInfo.getEvent();
int status = event.getStatus();
// Controller and Mode events have status byte 0xBc, where
// c is the channel they are sent on.
if ((status & 0xF0) == 0xB0) {
for (int i = 0; i < count; i++) {
try {
((ControllerEventListener) eventInfo.getListener(i)).controlChange(event);
} catch (Throwable t) {
if (Printer.err) t.printStackTrace();
}
}
}
return;
}
Printer.err("Unknown event type: " + eventInfo.getEvent());
}
/**
* Wait until there is something in the event queue to process. Then
* dispatch the event to the listeners.The entire method does not
* need to be synchronized since this includes taking the event out
* from the queue and processing the event. We only need to provide
* exclusive access over the code where an event is removed from the
*queue.
*/
void dispatchEvents() {
EventInfo eventInfo = null;
synchronized (this) {
// Wait till there is an event in the event queue.
try {
if (eventQueue.size() == 0) {
if (autoClosingClips.size() > 0 || lineMonitors.size() > 0) {
int waitTime = AUTO_CLOSE_TIME;
if (lineMonitors.size() > 0) {
waitTime = LINE_MONITOR_TIME;
}
wait(waitTime);
} else {
wait();
}
}
} catch (InterruptedException e) {
}
if (eventQueue.size() > 0) {
// Remove the event from the queue and dispatch it to the listeners.
eventInfo = (EventInfo) eventQueue.remove(0);
}
} // end of synchronized
if (eventInfo != null) {
processEvent(eventInfo);
} else {
if (autoClosingClips.size() > 0) {
closeAutoClosingClips();
}
if (lineMonitors.size() > 0) {
monitorLines();
}
}
}
/**
* Queue the given event in the event queue.
*/
private synchronized void postEvent(EventInfo eventInfo) {
eventQueue.add(eventInfo);
notifyAll();
}
/**
* A loop to dispatch events.
*/
public void run() {
while (true) {
try {
dispatchEvents();
} catch (Throwable t) {
if (Printer.err) t.printStackTrace();
}
}
}
/**
* Send audio and MIDI events.
*/
void sendAudioEvents(Object event, List listeners) {
if ((listeners == null)
|| (listeners.size() == 0)) {
// nothing to do
return;
}
start();
EventInfo eventInfo = new EventInfo(event, listeners);
postEvent(eventInfo);
}
/*
* go through the list of registered auto-closing
* Clip instances and close them, if appropriate
*
* This method is called in regular intervals
*/
private void closeAutoClosingClips() {
synchronized(autoClosingClips) {
if (Printer.debug)Printer.debug("> EventDispatcher.closeAutoClosingClips ("+autoClosingClips.size()+" clips)");
long currTime = System.currentTimeMillis();
for (int i = autoClosingClips.size()-1; i >= 0 ; i--) {
ClipInfo info = autoClosingClips.get(i);
if (info.isExpired(currTime)) {
AutoClosingClip clip = info.getClip();
// sanity check
if (!clip.isOpen() || !clip.isAutoClosing()) {
if (Printer.debug)Printer.debug("EventDispatcher: removing clip "+clip+" isOpen:"+clip.isOpen());
autoClosingClips.remove(i);
}
else if (!clip.isRunning() && !clip.isActive() && clip.isAutoClosing()) {
if (Printer.debug)Printer.debug("EventDispatcher: closing clip "+clip);
clip.close();
} else {
if (Printer.debug)Printer.debug("Doing nothing with clip "+clip+":");
if (Printer.debug)Printer.debug(" open="+clip.isOpen()+", autoclosing="+clip.isAutoClosing());
if (Printer.debug)Printer.debug(" isRunning="+clip.isRunning()+", isActive="+clip.isActive());
}
} else {
if (Printer.debug)Printer.debug("EventDispatcher: clip "+info.getClip()+" not yet expired");
}
}
}
if (Printer.debug)Printer.debug("< EventDispatcher.closeAutoClosingClips ("+autoClosingClips.size()+" clips)");
}
private int getAutoClosingClipIndex(AutoClosingClip clip) {
synchronized(autoClosingClips) {
for (int i = autoClosingClips.size()-1; i >= 0; i--) {
if (clip.equals(autoClosingClips.get(i).getClip())) {
return i;
}
}
}
return -1;
}
/**
* called from auto-closing clips when one of their open() method is called
*/
void autoClosingClipOpened(AutoClosingClip clip) {
if (Printer.debug)Printer.debug("> EventDispatcher.autoClosingClipOpened ");
int index = 0;
synchronized(autoClosingClips) {
index = getAutoClosingClipIndex(clip);
if (index == -1) {
if (Printer.debug)Printer.debug("EventDispatcher: adding auto-closing clip "+clip);
autoClosingClips.add(new ClipInfo(clip));
}
}
if (index == -1) {
synchronized (this) {
// this is only for the case that the first clip is set to autoclosing,
// and it is already open, and nothing is done with it.
// EventDispatcher.process() method would block in wait() and
// never close this first clip, keeping the device open.
notifyAll();
}
}
if (Printer.debug)Printer.debug("< EventDispatcher.autoClosingClipOpened finished("+autoClosingClips.size()+" clips)");
}
/**
* called from auto-closing clips when their closed() method is called
*/
void autoClosingClipClosed(AutoClosingClip clip) {
synchronized(autoClosingClips) {
int index = getAutoClosingClipIndex(clip);
if (index != -1) {
autoClosingClips.remove(index);
}
}
}
// ////////////////////////// Line Monitoring Support /////////////////// //
/*
* go through the list of registered line monitors
* and call their checkLine method
*
* This method is called in regular intervals
*/
private void monitorLines() {
synchronized(lineMonitors) {
if (Printer.debug)Printer.debug("> EventDispatcher.monitorLines ("+lineMonitors.size()+" monitors)");
for (int i = 0; i < lineMonitors.size(); i++) {
lineMonitors.get(i).checkLine();
}
}
if (Printer.debug)Printer.debug("< EventDispatcher.monitorLines("+lineMonitors.size()+" monitors)");
}
/**
* Add this LineMonitor instance to the list of monitors
*/
void addLineMonitor(LineMonitor lm) {
if (Printer.trace)Printer.trace("> EventDispatcher.addLineMonitor("+lm+")");
synchronized(lineMonitors) {
if (lineMonitors.indexOf(lm) >= 0) {
if (Printer.trace)Printer.trace("< EventDispatcher.addLineMonitor finished -- this monitor already exists!");
return;
}
if (Printer.debug)Printer.debug("EventDispatcher: adding line monitor "+lm);
lineMonitors.add(lm);
}
synchronized (this) {
// need to interrupt the infinite wait()
notifyAll();
}
if (Printer.debug)Printer.debug("< EventDispatcher.addLineMonitor finished -- now ("+lineMonitors.size()+" monitors)");
}
/**
* Remove this LineMonitor instance from the list of monitors
*/
void removeLineMonitor(LineMonitor lm) {
if (Printer.trace)Printer.trace("> EventDispatcher.removeLineMonitor("+lm+")");
synchronized(lineMonitors) {
if (lineMonitors.indexOf(lm) < 0) {
if (Printer.trace)Printer.trace("< EventDispatcher.removeLineMonitor finished -- this monitor does not exist!");
return;
}
if (Printer.debug)Printer.debug("EventDispatcher: removing line monitor "+lm);
lineMonitors.remove(lm);
}
if (Printer.debug)Printer.debug("< EventDispatcher.removeLineMonitor finished -- now ("+lineMonitors.size()+" monitors)");
}
// /////////////////////////////////// INNER CLASSES ////////////////////////////////////////// //
/**
* Container for an event and a set of listeners to deliver it to.
*/
private class EventInfo {
private final Object event;
private final Object[] listeners;
/**
* Create a new instance of this event Info class
* @param event the event to be dispatched
* @param listeners listener list; will be copied
*/
EventInfo(Object event, List listeners) {
this.event = event;
this.listeners = listeners.toArray();
}
Object getEvent() {
return event;
}
int getListenerCount() {
return listeners.length;
}
Object getListener(int index) {
return listeners[index];
}
} // class EventInfo
/**
* Container for a clip with its expiration time
*/
private class ClipInfo {
private final AutoClosingClip clip;
private final long expiration;
/**
* Create a new instance of this clip Info class
*/
ClipInfo(AutoClosingClip clip) {
this.clip = clip;
this.expiration = System.currentTimeMillis() + AUTO_CLOSE_TIME;
}
AutoClosingClip getClip() {
return clip;
}
boolean isExpired(long currTime) {
return currTime > expiration;
}
} // class ClipInfo
/**
* Interface that a class that wants to get regular
* line monitor events implements
*/
interface LineMonitor {
/**
* Called by event dispatcher in regular intervals
*/
public void checkLine();
}
} // class EventDispatcher

View File

@@ -0,0 +1,748 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
/**
* Fast Fourier Transformer.
*
* @author Karl Helgason
*/
public final class FFT {
private final double[] w;
private final int fftFrameSize;
private final int sign;
private final int[] bitm_array;
private final int fftFrameSize2;
// Sign = -1 is FFT, 1 is IFFT (inverse FFT)
// Data = Interlaced double array to be transformed.
// The order is: real (sin), complex (cos)
// Framesize must be power of 2
public FFT(int fftFrameSize, int sign) {
w = computeTwiddleFactors(fftFrameSize, sign);
this.fftFrameSize = fftFrameSize;
this.sign = sign;
fftFrameSize2 = fftFrameSize << 1;
// Pre-process Bit-Reversal
bitm_array = new int[fftFrameSize2];
for (int i = 2; i < fftFrameSize2; i += 2) {
int j;
int bitm;
for (bitm = 2, j = 0; bitm < fftFrameSize2; bitm <<= 1) {
if ((i & bitm) != 0)
j++;
j <<= 1;
}
bitm_array[i] = j;
}
}
public void transform(double[] data) {
bitreversal(data);
calc(fftFrameSize, data, sign, w);
}
private final static double[] computeTwiddleFactors(int fftFrameSize,
int sign) {
int imax = (int) (Math.log(fftFrameSize) / Math.log(2.));
double[] warray = new double[(fftFrameSize - 1) * 4];
int w_index = 0;
for (int i = 0, nstep = 2; i < imax; i++) {
int jmax = nstep;
nstep <<= 1;
double wr = 1.0;
double wi = 0.0;
double arg = Math.PI / (jmax >> 1);
double wfr = Math.cos(arg);
double wfi = sign * Math.sin(arg);
for (int j = 0; j < jmax; j += 2) {
warray[w_index++] = wr;
warray[w_index++] = wi;
double tempr = wr;
wr = tempr * wfr - wi * wfi;
wi = tempr * wfi + wi * wfr;
}
}
// PRECOMPUTATION of wwr1, wwi1 for factor 4 Decomposition (3 * complex
// operators and 8 +/- complex operators)
{
w_index = 0;
int w_index2 = warray.length >> 1;
for (int i = 0, nstep = 2; i < (imax - 1); i++) {
int jmax = nstep;
nstep *= 2;
int ii = w_index + jmax;
for (int j = 0; j < jmax; j += 2) {
double wr = warray[w_index++];
double wi = warray[w_index++];
double wr1 = warray[ii++];
double wi1 = warray[ii++];
warray[w_index2++] = wr * wr1 - wi * wi1;
warray[w_index2++] = wr * wi1 + wi * wr1;
}
}
}
return warray;
}
private final static void calc(int fftFrameSize, double[] data, int sign,
double[] w) {
final int fftFrameSize2 = fftFrameSize << 1;
int nstep = 2;
if (nstep >= fftFrameSize2)
return;
int i = nstep - 2;
if (sign == -1)
calcF4F(fftFrameSize, data, i, nstep, w);
else
calcF4I(fftFrameSize, data, i, nstep, w);
}
private final static void calcF2E(int fftFrameSize, double[] data, int i,
int nstep, double[] w) {
int jmax = nstep;
for (int n = 0; n < jmax; n += 2) {
double wr = w[i++];
double wi = w[i++];
int m = n + jmax;
double datam_r = data[m];
double datam_i = data[m + 1];
double datan_r = data[n];
double datan_i = data[n + 1];
double tempr = datam_r * wr - datam_i * wi;
double tempi = datam_r * wi + datam_i * wr;
data[m] = datan_r - tempr;
data[m + 1] = datan_i - tempi;
data[n] = datan_r + tempr;
data[n + 1] = datan_i + tempi;
}
return;
}
// Perform Factor-4 Decomposition with 3 * complex operators and 8 +/-
// complex operators
private final static void calcF4F(int fftFrameSize, double[] data, int i,
int nstep, double[] w) {
final int fftFrameSize2 = fftFrameSize << 1; // 2*fftFrameSize;
// Factor-4 Decomposition
int w_len = w.length >> 1;
while (nstep < fftFrameSize2) {
if (nstep << 2 == fftFrameSize2) {
// Goto Factor-4 Final Decomposition
// calcF4E(data, i, nstep, -1, w);
calcF4FE(fftFrameSize, data, i, nstep, w);
return;
}
int jmax = nstep;
int nnstep = nstep << 1;
if (nnstep == fftFrameSize2) {
// Factor-4 Decomposition not possible
calcF2E(fftFrameSize, data, i, nstep, w);
return;
}
nstep <<= 2;
int ii = i + jmax;
int iii = i + w_len;
{
i += 2;
ii += 2;
iii += 2;
for (int n = 0; n < fftFrameSize2; n += nstep) {
int m = n + jmax;
double datam1_r = data[m];
double datam1_i = data[m + 1];
double datan1_r = data[n];
double datan1_i = data[n + 1];
n += nnstep;
m += nnstep;
double datam2_r = data[m];
double datam2_i = data[m + 1];
double datan2_r = data[n];
double datan2_i = data[n + 1];
double tempr = datam1_r;
double tempi = datam1_i;
datam1_r = datan1_r - tempr;
datam1_i = datan1_i - tempi;
datan1_r = datan1_r + tempr;
datan1_i = datan1_i + tempi;
double n2w1r = datan2_r;
double n2w1i = datan2_i;
double m2ww1r = datam2_r;
double m2ww1i = datam2_i;
tempr = m2ww1r - n2w1r;
tempi = m2ww1i - n2w1i;
datam2_r = datam1_r + tempi;
datam2_i = datam1_i - tempr;
datam1_r = datam1_r - tempi;
datam1_i = datam1_i + tempr;
tempr = n2w1r + m2ww1r;
tempi = n2w1i + m2ww1i;
datan2_r = datan1_r - tempr;
datan2_i = datan1_i - tempi;
datan1_r = datan1_r + tempr;
datan1_i = datan1_i + tempi;
data[m] = datam2_r;
data[m + 1] = datam2_i;
data[n] = datan2_r;
data[n + 1] = datan2_i;
n -= nnstep;
m -= nnstep;
data[m] = datam1_r;
data[m + 1] = datam1_i;
data[n] = datan1_r;
data[n + 1] = datan1_i;
}
}
for (int j = 2; j < jmax; j += 2) {
double wr = w[i++];
double wi = w[i++];
double wr1 = w[ii++];
double wi1 = w[ii++];
double wwr1 = w[iii++];
double wwi1 = w[iii++];
// double wwr1 = wr * wr1 - wi * wi1; // these numbers can be
// precomputed!!!
// double wwi1 = wr * wi1 + wi * wr1;
for (int n = j; n < fftFrameSize2; n += nstep) {
int m = n + jmax;
double datam1_r = data[m];
double datam1_i = data[m + 1];
double datan1_r = data[n];
double datan1_i = data[n + 1];
n += nnstep;
m += nnstep;
double datam2_r = data[m];
double datam2_i = data[m + 1];
double datan2_r = data[n];
double datan2_i = data[n + 1];
double tempr = datam1_r * wr - datam1_i * wi;
double tempi = datam1_r * wi + datam1_i * wr;
datam1_r = datan1_r - tempr;
datam1_i = datan1_i - tempi;
datan1_r = datan1_r + tempr;
datan1_i = datan1_i + tempi;
double n2w1r = datan2_r * wr1 - datan2_i * wi1;
double n2w1i = datan2_r * wi1 + datan2_i * wr1;
double m2ww1r = datam2_r * wwr1 - datam2_i * wwi1;
double m2ww1i = datam2_r * wwi1 + datam2_i * wwr1;
tempr = m2ww1r - n2w1r;
tempi = m2ww1i - n2w1i;
datam2_r = datam1_r + tempi;
datam2_i = datam1_i - tempr;
datam1_r = datam1_r - tempi;
datam1_i = datam1_i + tempr;
tempr = n2w1r + m2ww1r;
tempi = n2w1i + m2ww1i;
datan2_r = datan1_r - tempr;
datan2_i = datan1_i - tempi;
datan1_r = datan1_r + tempr;
datan1_i = datan1_i + tempi;
data[m] = datam2_r;
data[m + 1] = datam2_i;
data[n] = datan2_r;
data[n + 1] = datan2_i;
n -= nnstep;
m -= nnstep;
data[m] = datam1_r;
data[m + 1] = datam1_i;
data[n] = datan1_r;
data[n + 1] = datan1_i;
}
}
i += jmax << 1;
}
calcF2E(fftFrameSize, data, i, nstep, w);
}
// Perform Factor-4 Decomposition with 3 * complex operators and 8 +/-
// complex operators
private final static void calcF4I(int fftFrameSize, double[] data, int i,
int nstep, double[] w) {
final int fftFrameSize2 = fftFrameSize << 1; // 2*fftFrameSize;
// Factor-4 Decomposition
int w_len = w.length >> 1;
while (nstep < fftFrameSize2) {
if (nstep << 2 == fftFrameSize2) {
// Goto Factor-4 Final Decomposition
// calcF4E(data, i, nstep, 1, w);
calcF4IE(fftFrameSize, data, i, nstep, w);
return;
}
int jmax = nstep;
int nnstep = nstep << 1;
if (nnstep == fftFrameSize2) {
// Factor-4 Decomposition not possible
calcF2E(fftFrameSize, data, i, nstep, w);
return;
}
nstep <<= 2;
int ii = i + jmax;
int iii = i + w_len;
{
i += 2;
ii += 2;
iii += 2;
for (int n = 0; n < fftFrameSize2; n += nstep) {
int m = n + jmax;
double datam1_r = data[m];
double datam1_i = data[m + 1];
double datan1_r = data[n];
double datan1_i = data[n + 1];
n += nnstep;
m += nnstep;
double datam2_r = data[m];
double datam2_i = data[m + 1];
double datan2_r = data[n];
double datan2_i = data[n + 1];
double tempr = datam1_r;
double tempi = datam1_i;
datam1_r = datan1_r - tempr;
datam1_i = datan1_i - tempi;
datan1_r = datan1_r + tempr;
datan1_i = datan1_i + tempi;
double n2w1r = datan2_r;
double n2w1i = datan2_i;
double m2ww1r = datam2_r;
double m2ww1i = datam2_i;
tempr = n2w1r - m2ww1r;
tempi = n2w1i - m2ww1i;
datam2_r = datam1_r + tempi;
datam2_i = datam1_i - tempr;
datam1_r = datam1_r - tempi;
datam1_i = datam1_i + tempr;
tempr = n2w1r + m2ww1r;
tempi = n2w1i + m2ww1i;
datan2_r = datan1_r - tempr;
datan2_i = datan1_i - tempi;
datan1_r = datan1_r + tempr;
datan1_i = datan1_i + tempi;
data[m] = datam2_r;
data[m + 1] = datam2_i;
data[n] = datan2_r;
data[n + 1] = datan2_i;
n -= nnstep;
m -= nnstep;
data[m] = datam1_r;
data[m + 1] = datam1_i;
data[n] = datan1_r;
data[n + 1] = datan1_i;
}
}
for (int j = 2; j < jmax; j += 2) {
double wr = w[i++];
double wi = w[i++];
double wr1 = w[ii++];
double wi1 = w[ii++];
double wwr1 = w[iii++];
double wwi1 = w[iii++];
// double wwr1 = wr * wr1 - wi * wi1; // these numbers can be
// precomputed!!!
// double wwi1 = wr * wi1 + wi * wr1;
for (int n = j; n < fftFrameSize2; n += nstep) {
int m = n + jmax;
double datam1_r = data[m];
double datam1_i = data[m + 1];
double datan1_r = data[n];
double datan1_i = data[n + 1];
n += nnstep;
m += nnstep;
double datam2_r = data[m];
double datam2_i = data[m + 1];
double datan2_r = data[n];
double datan2_i = data[n + 1];
double tempr = datam1_r * wr - datam1_i * wi;
double tempi = datam1_r * wi + datam1_i * wr;
datam1_r = datan1_r - tempr;
datam1_i = datan1_i - tempi;
datan1_r = datan1_r + tempr;
datan1_i = datan1_i + tempi;
double n2w1r = datan2_r * wr1 - datan2_i * wi1;
double n2w1i = datan2_r * wi1 + datan2_i * wr1;
double m2ww1r = datam2_r * wwr1 - datam2_i * wwi1;
double m2ww1i = datam2_r * wwi1 + datam2_i * wwr1;
tempr = n2w1r - m2ww1r;
tempi = n2w1i - m2ww1i;
datam2_r = datam1_r + tempi;
datam2_i = datam1_i - tempr;
datam1_r = datam1_r - tempi;
datam1_i = datam1_i + tempr;
tempr = n2w1r + m2ww1r;
tempi = n2w1i + m2ww1i;
datan2_r = datan1_r - tempr;
datan2_i = datan1_i - tempi;
datan1_r = datan1_r + tempr;
datan1_i = datan1_i + tempi;
data[m] = datam2_r;
data[m + 1] = datam2_i;
data[n] = datan2_r;
data[n + 1] = datan2_i;
n -= nnstep;
m -= nnstep;
data[m] = datam1_r;
data[m + 1] = datam1_i;
data[n] = datan1_r;
data[n + 1] = datan1_i;
}
}
i += jmax << 1;
}
calcF2E(fftFrameSize, data, i, nstep, w);
}
// Perform Factor-4 Decomposition with 3 * complex operators and 8 +/-
// complex operators
private final static void calcF4FE(int fftFrameSize, double[] data, int i,
int nstep, double[] w) {
final int fftFrameSize2 = fftFrameSize << 1; // 2*fftFrameSize;
// Factor-4 Decomposition
int w_len = w.length >> 1;
while (nstep < fftFrameSize2) {
int jmax = nstep;
int nnstep = nstep << 1;
if (nnstep == fftFrameSize2) {
// Factor-4 Decomposition not possible
calcF2E(fftFrameSize, data, i, nstep, w);
return;
}
nstep <<= 2;
int ii = i + jmax;
int iii = i + w_len;
for (int n = 0; n < jmax; n += 2) {
double wr = w[i++];
double wi = w[i++];
double wr1 = w[ii++];
double wi1 = w[ii++];
double wwr1 = w[iii++];
double wwi1 = w[iii++];
// double wwr1 = wr * wr1 - wi * wi1; // these numbers can be
// precomputed!!!
// double wwi1 = wr * wi1 + wi * wr1;
int m = n + jmax;
double datam1_r = data[m];
double datam1_i = data[m + 1];
double datan1_r = data[n];
double datan1_i = data[n + 1];
n += nnstep;
m += nnstep;
double datam2_r = data[m];
double datam2_i = data[m + 1];
double datan2_r = data[n];
double datan2_i = data[n + 1];
double tempr = datam1_r * wr - datam1_i * wi;
double tempi = datam1_r * wi + datam1_i * wr;
datam1_r = datan1_r - tempr;
datam1_i = datan1_i - tempi;
datan1_r = datan1_r + tempr;
datan1_i = datan1_i + tempi;
double n2w1r = datan2_r * wr1 - datan2_i * wi1;
double n2w1i = datan2_r * wi1 + datan2_i * wr1;
double m2ww1r = datam2_r * wwr1 - datam2_i * wwi1;
double m2ww1i = datam2_r * wwi1 + datam2_i * wwr1;
tempr = m2ww1r - n2w1r;
tempi = m2ww1i - n2w1i;
datam2_r = datam1_r + tempi;
datam2_i = datam1_i - tempr;
datam1_r = datam1_r - tempi;
datam1_i = datam1_i + tempr;
tempr = n2w1r + m2ww1r;
tempi = n2w1i + m2ww1i;
datan2_r = datan1_r - tempr;
datan2_i = datan1_i - tempi;
datan1_r = datan1_r + tempr;
datan1_i = datan1_i + tempi;
data[m] = datam2_r;
data[m + 1] = datam2_i;
data[n] = datan2_r;
data[n + 1] = datan2_i;
n -= nnstep;
m -= nnstep;
data[m] = datam1_r;
data[m + 1] = datam1_i;
data[n] = datan1_r;
data[n + 1] = datan1_i;
}
i += jmax << 1;
}
}
// Perform Factor-4 Decomposition with 3 * complex operators and 8 +/-
// complex operators
private final static void calcF4IE(int fftFrameSize, double[] data, int i,
int nstep, double[] w) {
final int fftFrameSize2 = fftFrameSize << 1; // 2*fftFrameSize;
// Factor-4 Decomposition
int w_len = w.length >> 1;
while (nstep < fftFrameSize2) {
int jmax = nstep;
int nnstep = nstep << 1;
if (nnstep == fftFrameSize2) {
// Factor-4 Decomposition not possible
calcF2E(fftFrameSize, data, i, nstep, w);
return;
}
nstep <<= 2;
int ii = i + jmax;
int iii = i + w_len;
for (int n = 0; n < jmax; n += 2) {
double wr = w[i++];
double wi = w[i++];
double wr1 = w[ii++];
double wi1 = w[ii++];
double wwr1 = w[iii++];
double wwi1 = w[iii++];
// double wwr1 = wr * wr1 - wi * wi1; // these numbers can be
// precomputed!!!
// double wwi1 = wr * wi1 + wi * wr1;
int m = n + jmax;
double datam1_r = data[m];
double datam1_i = data[m + 1];
double datan1_r = data[n];
double datan1_i = data[n + 1];
n += nnstep;
m += nnstep;
double datam2_r = data[m];
double datam2_i = data[m + 1];
double datan2_r = data[n];
double datan2_i = data[n + 1];
double tempr = datam1_r * wr - datam1_i * wi;
double tempi = datam1_r * wi + datam1_i * wr;
datam1_r = datan1_r - tempr;
datam1_i = datan1_i - tempi;
datan1_r = datan1_r + tempr;
datan1_i = datan1_i + tempi;
double n2w1r = datan2_r * wr1 - datan2_i * wi1;
double n2w1i = datan2_r * wi1 + datan2_i * wr1;
double m2ww1r = datam2_r * wwr1 - datam2_i * wwi1;
double m2ww1i = datam2_r * wwi1 + datam2_i * wwr1;
tempr = n2w1r - m2ww1r;
tempi = n2w1i - m2ww1i;
datam2_r = datam1_r + tempi;
datam2_i = datam1_i - tempr;
datam1_r = datam1_r - tempi;
datam1_i = datam1_i + tempr;
tempr = n2w1r + m2ww1r;
tempi = n2w1i + m2ww1i;
datan2_r = datan1_r - tempr;
datan2_i = datan1_i - tempi;
datan1_r = datan1_r + tempr;
datan1_i = datan1_i + tempi;
data[m] = datam2_r;
data[m + 1] = datam2_i;
data[n] = datan2_r;
data[n + 1] = datan2_i;
n -= nnstep;
m -= nnstep;
data[m] = datam1_r;
data[m + 1] = datam1_i;
data[n] = datan1_r;
data[n + 1] = datan1_i;
}
i += jmax << 1;
}
}
private final void bitreversal(double[] data) {
if (fftFrameSize < 4)
return;
int inverse = fftFrameSize2 - 2;
for (int i = 0; i < fftFrameSize; i += 4) {
int j = bitm_array[i];
// Performing Bit-Reversal, even v.s. even, O(2N)
if (i < j) {
int n = i;
int m = j;
// COMPLEX: SWAP(data[n], data[m])
// Real Part
double tempr = data[n];
data[n] = data[m];
data[m] = tempr;
// Imagery Part
n++;
m++;
double tempi = data[n];
data[n] = data[m];
data[m] = tempi;
n = inverse - i;
m = inverse - j;
// COMPLEX: SWAP(data[n], data[m])
// Real Part
tempr = data[n];
data[n] = data[m];
data[m] = tempr;
// Imagery Part
n++;
m++;
tempi = data[n];
data[n] = data[m];
data[m] = tempi;
}
// Performing Bit-Reversal, odd v.s. even, O(N)
int m = j + fftFrameSize; // bitm_array[i+2];
// COMPLEX: SWAP(data[n], data[m])
// Real Part
int n = i + 2;
double tempr = data[n];
data[n] = data[m];
data[m] = tempr;
// Imagery Part
n++;
m++;
double tempi = data[n];
data[n] = data[m];
data[m] = tempi;
}
}
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import javax.sound.midi.*;
/**
* an optimized ShortMessage that does not need an array
*
* @author Florian Bomers
*/
final class FastShortMessage extends ShortMessage {
private int packedMsg;
FastShortMessage(int packedMsg) throws InvalidMidiDataException {
this.packedMsg = packedMsg;
getDataLength(packedMsg & 0xFF); // to check for validity
}
/** Creates a FastShortMessage from this ShortMessage */
FastShortMessage(ShortMessage msg) {
this.packedMsg = msg.getStatus()
| (msg.getData1() << 8)
| (msg.getData2() << 16);
}
int getPackedMsg() {
return packedMsg;
}
public byte[] getMessage() {
int length = 0;
try {
// fix for bug 4851018: MidiMessage.getLength and .getData return wrong values
// fix for bug 4890405: Reading MidiMessage byte array fails in 1.4.2
length = getDataLength(packedMsg & 0xFF) + 1;
} catch (InvalidMidiDataException imde) {
// should never happen
}
byte[] returnedArray = new byte[length];
if (length>0) {
returnedArray[0] = (byte) (packedMsg & 0xFF);
if (length>1) {
returnedArray[1] = (byte) ((packedMsg & 0xFF00) >> 8);
if (length>2) {
returnedArray[2] = (byte) ((packedMsg & 0xFF0000) >> 16);
}
}
}
return returnedArray;
}
public int getLength() {
try {
return getDataLength(packedMsg & 0xFF) + 1;
} catch (InvalidMidiDataException imde) {
// should never happen
}
return 0;
}
public void setMessage(int status) throws InvalidMidiDataException {
// check for valid values
int dataLength = getDataLength(status); // can throw InvalidMidiDataException
if (dataLength != 0) {
super.setMessage(status); // throws Exception
}
packedMsg = (packedMsg & 0xFFFF00) | (status & 0xFF);
}
public void setMessage(int status, int data1, int data2) throws InvalidMidiDataException {
getDataLength(status); // can throw InvalidMidiDataException
packedMsg = (status & 0xFF) | ((data1 & 0xFF) << 8) | ((data2 & 0xFF) << 16);
}
public void setMessage(int command, int channel, int data1, int data2) throws InvalidMidiDataException {
getDataLength(command); // can throw InvalidMidiDataException
packedMsg = (command & 0xF0) | (channel & 0x0F) | ((data1 & 0xFF) << 8) | ((data2 & 0xFF) << 16);
}
public int getChannel() {
return packedMsg & 0x0F;
}
public int getCommand() {
return packedMsg & 0xF0;
}
public int getData1() {
return (packedMsg & 0xFF00) >> 8;
}
public int getData2() {
return (packedMsg & 0xFF0000) >> 16;
}
public int getStatus() {
return packedMsg & 0xFF;
}
/**
* Creates a new object of the same class and with the same contents
* as this object.
* @return a clone of this instance.
*/
public Object clone() {
try {
return new FastShortMessage(packedMsg);
} catch (InvalidMidiDataException imde) {
// should never happen
}
return null;
}
} // class FastShortMsg

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import javax.sound.midi.*;
/**
* optimized FastSysexMessage that doesn't copy the array upon instantiation
*
* @author Florian Bomers
*/
final class FastSysexMessage extends SysexMessage {
FastSysexMessage(byte[] data) throws InvalidMidiDataException {
super(data);
if (data.length==0 || (((data[0] & 0xFF) != 0xF0) && ((data[0] & 0xFF) != 0xF7))) {
super.setMessage(data, data.length); // will throw Exception
}
}
/**
* The returned array may be larger than this message is.
* Use getLength() to get the real length of the message.
*/
byte[] getReadOnlyMessage() {
return data;
}
// overwrite this method so that the original data array,
// which is shared among all transmitters, cannot be modified
public void setMessage(byte[] data, int length) throws InvalidMidiDataException {
if ((data.length == 0) || (((data[0] & 0xFF) != 0xF0) && ((data[0] & 0xFF) != 0xF7))) {
super.setMessage(data, data.length); // will throw Exception
}
this.length = length;
this.data = new byte[this.length];
System.arraycopy(data, 0, this.data, 0, length);
}
} // class FastSysexMessage

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.io.IOException;
/**
* This exception is used when a file contains illegal or unexpected data.
*
* @author Karl Helgason
*/
public class InvalidDataException extends IOException {
private static final long serialVersionUID = 1L;
public InvalidDataException() {
super("Invalid Data!");
}
public InvalidDataException(String s) {
super(s);
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
/**
* This exception is used when a reader is used to read file of a format
* it doesn't unterstand or support.
*
* @author Karl Helgason
*/
public class InvalidFormatException extends InvalidDataException {
private static final long serialVersionUID = 1L;
public InvalidFormatException() {
super("Invalid format!");
}
public InvalidFormatException(String s) {
super(s);
}
}

View File

@@ -0,0 +1,138 @@
/*
* Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.AccessController;
import java.util.ArrayList;
import java.util.Objects;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.Soundbank;
import javax.sound.midi.spi.SoundbankReader;
import sun.reflect.misc.ReflectUtil;
import sun.security.action.GetBooleanAction;
/**
* JarSoundbankReader is used to read soundbank object from jar files.
*
* @author Karl Helgason
*/
public final class JARSoundbankReader extends SoundbankReader {
/**
* Value of the system property that enables the Jar soundbank loading
* {@code true} if jar sound bank is allowed to be loaded default is
* {@code false}.
*/
@SuppressWarnings("removal")
private static final boolean JAR_SOUNDBANK_ENABLED =
AccessController.doPrivileged(
new GetBooleanAction("jdk.sound.jarsoundbank"));
private static boolean isZIP(URL url) {
boolean ok = false;
try {
InputStream stream = url.openStream();
try {
byte[] buff = new byte[4];
ok = stream.read(buff) == 4;
if (ok) {
ok = (buff[0] == 0x50
&& buff[1] == 0x4b
&& buff[2] == 0x03
&& buff[3] == 0x04);
}
} finally {
stream.close();
}
} catch (IOException e) {
}
return ok;
}
public Soundbank getSoundbank(URL url)
throws InvalidMidiDataException, IOException {
Objects.requireNonNull(url);
if (!JAR_SOUNDBANK_ENABLED || !isZIP(url))
return null;
ArrayList<Soundbank> soundbanks = new ArrayList<Soundbank>();
URLClassLoader ucl = URLClassLoader.newInstance(new URL[]{url});
InputStream stream = ucl.getResourceAsStream(
"META-INF/services/javax.sound.midi.Soundbank");
if (stream == null)
return null;
try
{
BufferedReader r = new BufferedReader(new InputStreamReader(stream));
String line = r.readLine();
while (line != null) {
if (!line.startsWith("#")) {
try {
Class<?> c = Class.forName(line.trim(), false, ucl);
if (Soundbank.class.isAssignableFrom(c)) {
Object o = ReflectUtil.newInstance(c);
soundbanks.add((Soundbank) o);
}
} catch (ClassNotFoundException ignored) {
} catch (InstantiationException ignored) {
} catch (IllegalAccessException ignored) {
}
}
line = r.readLine();
}
}
finally
{
stream.close();
}
if (soundbanks.size() == 0)
return null;
if (soundbanks.size() == 1)
return soundbanks.get(0);
SimpleSoundbank sbk = new SimpleSoundbank();
for (Soundbank soundbank : soundbanks)
sbk.addAllInstruments(soundbank);
return sbk;
}
public Soundbank getSoundbank(InputStream stream)
throws InvalidMidiDataException, IOException {
return null;
}
public Soundbank getSoundbank(File file)
throws InvalidMidiDataException, IOException {
Objects.requireNonNull(file);
return getSoundbank(file.toURI().toURL());
}
}

View File

@@ -0,0 +1,205 @@
/*
* Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import javax.sound.midi.Receiver;
import javax.sound.midi.Sequencer;
import javax.sound.midi.Synthesizer;
import javax.sound.midi.Transmitter;
import javax.sound.midi.spi.MidiDeviceProvider;
import javax.sound.midi.spi.MidiFileReader;
import javax.sound.midi.spi.MidiFileWriter;
import javax.sound.midi.spi.SoundbankReader;
import javax.sound.sampled.Clip;
import javax.sound.sampled.Port;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.TargetDataLine;
import javax.sound.sampled.spi.AudioFileReader;
import javax.sound.sampled.spi.AudioFileWriter;
import javax.sound.sampled.spi.FormatConversionProvider;
import javax.sound.sampled.spi.MixerProvider;
/**
* JDK13Services uses the Service class in JDK 1.3 to discover a list of service
* providers installed in the system.
* <p>
* This class is public because it is called from javax.sound.midi.MidiSystem
* and javax.sound.sampled.AudioSystem. The alternative would be to make
* JSSecurityManager public, which is considered worse.
*
* @author Matthias Pfisterer
*/
public final class JDK13Services {
/**
* Filename of the properties file for default provider properties. This
* file is searched in the subdirectory "lib" of the JRE directory (this
* behaviour is hardcoded).
*/
private static final String PROPERTIES_FILENAME = "sound.properties";
/**
* Properties loaded from the properties file for default provider
* properties.
*/
private static Properties properties;
/**
* Private, no-args constructor to ensure against instantiation.
*/
private JDK13Services() {
}
/**
* Obtains a List containing installed instances of the providers for the
* requested service. The returned List is immutable.
*
* @param serviceClass The type of providers requested. This should be one
* of AudioFileReader.class, AudioFileWriter.class,
* FormatConversionProvider.class, MixerProvider.class,
* MidiDeviceProvider.class, MidiFileReader.class,
* MidiFileWriter.class or SoundbankReader.class.
*
* @return A List of providers of the requested type. This List is
* immutable.
*/
public static List<?> getProviders(final Class<?> serviceClass) {
final List<?> providers;
if (!MixerProvider.class.equals(serviceClass)
&& !FormatConversionProvider.class.equals(serviceClass)
&& !AudioFileReader.class.equals(serviceClass)
&& !AudioFileWriter.class.equals(serviceClass)
&& !MidiDeviceProvider.class.equals(serviceClass)
&& !SoundbankReader.class.equals(serviceClass)
&& !MidiFileWriter.class.equals(serviceClass)
&& !MidiFileReader.class.equals(serviceClass)) {
providers = new ArrayList<>(0);
} else {
providers = JSSecurityManager.getProviders(serviceClass);
}
return Collections.unmodifiableList(providers);
}
/** Obtain the provider class name part of a default provider property.
@param typeClass The type of the default provider property. This
should be one of Receiver.class, Transmitter.class, Sequencer.class,
Synthesizer.class, SourceDataLine.class, TargetDataLine.class,
Clip.class or Port.class.
@return The value of the provider class name part of the property
(the part before the hash sign), if available. If the property is
not set or the value has no provider class name part, null is returned.
*/
public static synchronized String getDefaultProviderClassName(Class typeClass) {
String value = null;
String defaultProviderSpec = getDefaultProvider(typeClass);
if (defaultProviderSpec != null) {
int hashpos = defaultProviderSpec.indexOf('#');
if (hashpos == 0) {
// instance name only; leave value as null
} else if (hashpos > 0) {
value = defaultProviderSpec.substring(0, hashpos);
} else {
value = defaultProviderSpec;
}
}
return value;
}
/** Obtain the instance name part of a default provider property.
@param typeClass The type of the default provider property. This
should be one of Receiver.class, Transmitter.class, Sequencer.class,
Synthesizer.class, SourceDataLine.class, TargetDataLine.class,
Clip.class or Port.class.
@return The value of the instance name part of the property (the
part after the hash sign), if available. If the property is not set
or the value has no instance name part, null is returned.
*/
public static synchronized String getDefaultInstanceName(Class typeClass) {
String value = null;
String defaultProviderSpec = getDefaultProvider(typeClass);
if (defaultProviderSpec != null) {
int hashpos = defaultProviderSpec.indexOf('#');
if (hashpos >= 0 && hashpos < defaultProviderSpec.length() - 1) {
value = defaultProviderSpec.substring(hashpos + 1);
}
}
return value;
}
/** Obtain the value of a default provider property.
@param typeClass The type of the default provider property. This
should be one of Receiver.class, Transmitter.class, Sequencer.class,
Synthesizer.class, SourceDataLine.class, TargetDataLine.class,
Clip.class or Port.class.
@return The complete value of the property, if available.
If the property is not set, null is returned.
*/
private static synchronized String getDefaultProvider(Class typeClass) {
if (!SourceDataLine.class.equals(typeClass)
&& !TargetDataLine.class.equals(typeClass)
&& !Clip.class.equals(typeClass)
&& !Port.class.equals(typeClass)
&& !Receiver.class.equals(typeClass)
&& !Transmitter.class.equals(typeClass)
&& !Synthesizer.class.equals(typeClass)
&& !Sequencer.class.equals(typeClass)) {
return null;
}
String name = typeClass.getName();
String value = AccessController.doPrivileged(
(PrivilegedAction<String>) () -> System.getProperty(name));
if (value == null) {
value = getProperties().getProperty(name);
}
if ("".equals(value)) {
value = null;
}
return value;
}
/** Obtain a properties bundle containing property values from the
properties file. If the properties file could not be loaded,
the properties bundle is empty.
*/
private static synchronized Properties getProperties() {
if (properties == null) {
properties = new Properties();
JSSecurityManager.loadProperties(properties, PROPERTIES_FILENAME);
}
return properties;
}
}

View File

@@ -0,0 +1,204 @@
/*
* Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.ServiceLoader;
import java.security.AccessController;
import java.security.PrivilegedAction;
import javax.sound.sampled.AudioPermission;
/** Managing security in the Java Sound implementation.
* This class contains all code that uses and is used by
* SecurityManager.doPrivileged().
*
* @author Matthias Pfisterer
*/
final class JSSecurityManager {
/** Prevent instantiation.
*/
private JSSecurityManager() {
}
/** Checks if the VM currently has a SecurityManager installed.
* Note that this may change over time. So the result of this method
* should not be cached.
*
* @return true if a SecurityManger is installed, false otherwise.
*/
private static boolean hasSecurityManager() {
return (System.getSecurityManager() != null);
}
static void checkRecordPermission() throws SecurityException {
if(Printer.trace) Printer.trace("JSSecurityManager.checkRecordPermission()");
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new AudioPermission("record"));
}
}
/** Load properties from a file.
This method tries to load properties from the filename give into
the passed properties object.
If the file cannot be found or something else goes wrong,
the method silently fails.
@param properties The properties bundle to store the values of the
properties file.
@param filename The filename of the properties file to load. This
filename is interpreted as relative to the subdirectory "lib" in
the JRE directory.
*/
static void loadProperties(final Properties properties,
final String filename) {
if(hasSecurityManager()) {
try {
// invoke the privileged action using 1.2 security
PrivilegedAction<Void> action = new PrivilegedAction<Void>() {
public Void run() {
loadPropertiesImpl(properties, filename);
return null;
}
};
AccessController.doPrivileged(action);
if(Printer.debug)Printer.debug("Loaded properties with JDK 1.2 security");
} catch (Exception e) {
if(Printer.debug)Printer.debug("Exception loading properties with JDK 1.2 security");
// try without using JDK 1.2 security
loadPropertiesImpl(properties, filename);
}
} else {
// not JDK 1.2 security, assume we already have permission
loadPropertiesImpl(properties, filename);
}
}
private static void loadPropertiesImpl(Properties properties,
String filename) {
if(Printer.trace)Printer.trace(">> JSSecurityManager: loadPropertiesImpl()");
String fname = System.getProperty("java.home");
try {
if (fname == null) {
throw new Error("Can't find java.home ??");
}
File f = new File(fname, "lib");
f = new File(f, filename);
fname = f.getCanonicalPath();
InputStream in = new FileInputStream(fname);
BufferedInputStream bin = new BufferedInputStream(in);
try {
properties.load(bin);
} finally {
if (in != null) {
in.close();
}
}
} catch (Throwable t) {
if (Printer.trace) {
System.err.println("Could not load properties file \"" + fname + "\"");
t.printStackTrace();
}
}
if(Printer.trace)Printer.trace("<< JSSecurityManager: loadPropertiesImpl() completed");
}
/** Create a Thread in the current ThreadGroup.
*/
static Thread createThread(final Runnable runnable,
final String threadName,
final boolean isDaemon, final int priority,
final boolean doStart) {
Thread thread = new Thread(runnable);
if (threadName != null) {
thread.setName(threadName);
}
thread.setDaemon(isDaemon);
if (priority >= 0) {
thread.setPriority(priority);
}
if (doStart) {
thread.start();
}
return thread;
}
static synchronized <T> List<T> getProviders(final Class<T> providerClass) {
List<T> p = new ArrayList<>(7);
// ServiceLoader creates "lazy" iterator instance, but it ensures that
// next/hasNext run with permissions that are restricted by whatever
// creates the ServiceLoader instance, so it requires to be called from
// privileged section
final PrivilegedAction<Iterator<T>> psAction =
new PrivilegedAction<Iterator<T>>() {
@Override
public Iterator<T> run() {
return ServiceLoader.load(providerClass).iterator();
}
};
final Iterator<T> ps = AccessController.doPrivileged(psAction);
// the iterator's hasNext() method looks through classpath for
// the provider class names, so it requires read permissions
PrivilegedAction<Boolean> hasNextAction = new PrivilegedAction<Boolean>() {
public Boolean run() {
return ps.hasNext();
}
};
while (AccessController.doPrivileged(hasNextAction)) {
try {
// the iterator's next() method creates instances of the
// providers and it should be called in the current security
// context
T provider = ps.next();
if (providerClass.isInstance(provider)) {
// $$mp 2003-08-22
// Always adding at the beginning reverses the
// order of the providers. So we no longer have
// to do this in AudioSystem and MidiSystem.
p.add(0, provider);
}
} catch (Throwable t) {
//$$fb 2002-11-07: do not fail on SPI not found
if (Printer.err) t.printStackTrace();
}
}
return p;
}
}

View File

@@ -0,0 +1,485 @@
/*
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.io.IOException;
import java.io.InputStream;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.applet.AudioClip;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiFileFormat;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.MetaEventListener;
/**
* Java Sound audio clip;
*
* @author Arthur van Hoff, Kara Kytle, Jan Borgersen
* @author Florian Bomers
*/
public final class JavaSoundAudioClip implements AudioClip, MetaEventListener, LineListener {
private static final boolean DEBUG = false;
private static final int BUFFER_SIZE = 16384; // number of bytes written each time to the source data line
private long lastPlayCall = 0;
private static final int MINIMUM_PLAY_DELAY = 30;
private byte loadedAudio[] = null;
private int loadedAudioByteLength = 0;
private AudioFormat loadedAudioFormat = null;
private AutoClosingClip clip = null;
private boolean clipLooping = false;
private DataPusher datapusher = null;
private Sequencer sequencer = null;
private Sequence sequence = null;
private boolean sequencerloop = false;
/**
* used for determining how many samples is the
* threshhold between playing as a Clip and streaming
* from the file.
*
* $$jb: 11.07.99: the engine has a limit of 1M
* samples to play as a Clip, so compare this number
* with the number of samples in the stream.
*
*/
private final static long CLIP_THRESHOLD = 1048576;
//private final static long CLIP_THRESHOLD = 1;
private final static int STREAM_BUFFER_SIZE = 1024;
public JavaSoundAudioClip(InputStream in) throws IOException {
if (DEBUG || Printer.debug)Printer.debug("JavaSoundAudioClip.<init>");
BufferedInputStream bis = new BufferedInputStream(in, STREAM_BUFFER_SIZE);
bis.mark(STREAM_BUFFER_SIZE);
boolean success = false;
try {
AudioInputStream as = AudioSystem.getAudioInputStream(bis);
// load the stream data into memory
success = loadAudioData(as);
if (success) {
success = false;
if (loadedAudioByteLength < CLIP_THRESHOLD) {
success = createClip();
}
if (!success) {
success = createSourceDataLine();
}
}
} catch (UnsupportedAudioFileException e) {
// not an audio file
try {
MidiFileFormat mff = MidiSystem.getMidiFileFormat(bis);
success = createSequencer(bis);
} catch (InvalidMidiDataException e1) {
success = false;
}
}
if (!success) {
throw new IOException("Unable to create AudioClip from input stream");
}
}
public synchronized void play() {
startImpl(false);
}
public synchronized void loop() {
startImpl(true);
}
private synchronized void startImpl(boolean loop) {
// hack for some applets that call the start method very rapidly...
long currentTime = System.currentTimeMillis();
long diff = currentTime - lastPlayCall;
if (diff < MINIMUM_PLAY_DELAY) {
if (DEBUG || Printer.debug) Printer.debug("JavaSoundAudioClip.startImpl(loop="+loop+"): abort - too rapdly");
return;
}
lastPlayCall = currentTime;
if (DEBUG || Printer.debug) Printer.debug("JavaSoundAudioClip.startImpl(loop="+loop+")");
try {
if (clip != null) {
// We need to disable autoclosing mechanism otherwise the clip
// can be closed after "!clip.isOpen()" check, because of
// previous inactivity.
clip.setAutoClosing(false);
try {
if (!clip.isOpen()) {
clip.open(loadedAudioFormat, loadedAudio, 0,
loadedAudioByteLength);
} else {
clip.flush();
if (loop != clipLooping) {
// need to stop in case the looped status changed
clip.stop();
}
}
clip.setFramePosition(0);
if (loop) {
clip.loop(Clip.LOOP_CONTINUOUSLY);
} else {
clip.start();
}
clipLooping = loop;
} finally {
clip.setAutoClosing(true);
}
} else if (datapusher != null ) {
datapusher.start(loop);
if (DEBUG || Printer.debug)Printer.debug("Stream should be playing/looping");
} else if (sequencer != null) {
sequencerloop = loop;
if (sequencer.isRunning()) {
sequencer.setMicrosecondPosition(0);
}
if (!sequencer.isOpen()) {
try {
sequencer.open();
sequencer.setSequence(sequence);
} catch (InvalidMidiDataException e1) {
if (DEBUG || Printer.err)e1.printStackTrace();
} catch (MidiUnavailableException e2) {
if (DEBUG || Printer.err)e2.printStackTrace();
}
}
sequencer.addMetaEventListener(this);
try {
sequencer.start();
} catch (Exception e) {
if (DEBUG || Printer.err) e.printStackTrace();
}
if (DEBUG || Printer.debug)Printer.debug("Sequencer should be playing/looping");
}
} catch (Exception e) {
if (DEBUG || Printer.err)e.printStackTrace();
}
}
public synchronized void stop() {
if (DEBUG || Printer.debug)Printer.debug("JavaSoundAudioClip->stop()");
lastPlayCall = 0;
if (clip != null) {
try {
if (DEBUG || Printer.trace)Printer.trace("JavaSoundAudioClip: clip.flush()");
clip.flush();
} catch (Exception e1) {
if (Printer.err) e1.printStackTrace();
}
try {
if (DEBUG || Printer.trace)Printer.trace("JavaSoundAudioClip: clip.stop()");
clip.stop();
} catch (Exception e2) {
if (Printer.err) e2.printStackTrace();
}
if (DEBUG || Printer.debug)Printer.debug("Clip should be stopped");
} else if (datapusher != null) {
datapusher.stop();
if (DEBUG || Printer.debug)Printer.debug("Stream should be stopped");
} else if (sequencer != null) {
try {
sequencerloop = false;
sequencer.addMetaEventListener(this);
sequencer.stop();
} catch (Exception e3) {
if (Printer.err) e3.printStackTrace();
}
try {
sequencer.close();
} catch (Exception e4) {
if (Printer.err) e4.printStackTrace();
}
if (DEBUG || Printer.debug)Printer.debug("Sequencer should be stopped");
}
}
// Event handlers (for debugging)
public synchronized void update(LineEvent event) {
if (DEBUG || Printer.debug) Printer.debug("line event received: "+event);
}
// handle MIDI track end meta events for looping
public synchronized void meta( MetaMessage message ) {
if (DEBUG || Printer.debug)Printer.debug("META EVENT RECEIVED!!!!! ");
if( message.getType() == 47 ) {
if (sequencerloop){
//notifyAll();
sequencer.setMicrosecondPosition(0);
loop();
} else {
stop();
}
}
}
public String toString() {
return getClass().toString();
}
protected void finalize() {
if (clip != null) {
if (DEBUG || Printer.trace)Printer.trace("JavaSoundAudioClip.finalize: clip.close()");
clip.close();
}
//$$fb 2001-09-26: may improve situation related to bug #4302884
if (datapusher != null) {
datapusher.close();
}
if (sequencer != null) {
sequencer.close();
}
}
// FILE LOADING METHODS
private boolean loadAudioData(AudioInputStream as) throws IOException, UnsupportedAudioFileException {
if (DEBUG || Printer.debug)Printer.debug("JavaSoundAudioClip->openAsClip()");
// first possibly convert this stream to PCM
as = Toolkit.getPCMConvertedAudioInputStream(as);
if (as == null) {
return false;
}
loadedAudioFormat = as.getFormat();
long frameLen = as.getFrameLength();
int frameSize = loadedAudioFormat.getFrameSize();
long byteLen = AudioSystem.NOT_SPECIFIED;
if (frameLen != AudioSystem.NOT_SPECIFIED
&& frameLen > 0
&& frameSize != AudioSystem.NOT_SPECIFIED
&& frameSize > 0) {
byteLen = frameLen * frameSize;
}
if (byteLen != AudioSystem.NOT_SPECIFIED) {
// if the stream length is known, it can be efficiently loaded into memory
readStream(as, byteLen);
} else {
// otherwise we use a ByteArrayOutputStream to load it into memory
readStream(as);
}
// if everything went fine, we have now the audio data in
// loadedAudio, and the byte length in loadedAudioByteLength
return true;
}
private void readStream(AudioInputStream as, long byteLen) throws IOException {
// arrays "only" max. 2GB
int intLen;
if (byteLen > 2147483647) {
intLen = 2147483647;
} else {
intLen = (int) byteLen;
}
loadedAudio = new byte[intLen];
loadedAudioByteLength = 0;
// this loop may throw an IOException
while (true) {
int bytesRead = as.read(loadedAudio, loadedAudioByteLength, intLen - loadedAudioByteLength);
if (bytesRead <= 0) {
as.close();
break;
}
loadedAudioByteLength += bytesRead;
}
}
private void readStream(AudioInputStream as) throws IOException {
DirectBAOS baos = new DirectBAOS();
byte buffer[] = new byte[16384];
int bytesRead = 0;
int totalBytesRead = 0;
// this loop may throw an IOException
while( true ) {
bytesRead = as.read(buffer, 0, buffer.length);
if (bytesRead <= 0) {
as.close();
break;
}
totalBytesRead += bytesRead;
baos.write(buffer, 0, bytesRead);
}
loadedAudio = baos.getInternalBuffer();
loadedAudioByteLength = totalBytesRead;
}
// METHODS FOR CREATING THE DEVICE
private boolean createClip() {
if (DEBUG || Printer.debug)Printer.debug("JavaSoundAudioClip.createClip()");
try {
DataLine.Info info = new DataLine.Info(Clip.class, loadedAudioFormat);
if (!(AudioSystem.isLineSupported(info)) ) {
if (DEBUG || Printer.err)Printer.err("Clip not supported: "+loadedAudioFormat);
// fail silently
return false;
}
Object line = AudioSystem.getLine(info);
if (!(line instanceof AutoClosingClip)) {
if (DEBUG || Printer.err)Printer.err("Clip is not auto closing!"+clip);
// fail -> will try with SourceDataLine
return false;
}
clip = (AutoClosingClip) line;
clip.setAutoClosing(true);
if (DEBUG || Printer.debug) clip.addLineListener(this);
} catch (Exception e) {
if (DEBUG || Printer.err)e.printStackTrace();
// fail silently
return false;
}
if (clip==null) {
// fail silently
return false;
}
if (DEBUG || Printer.debug)Printer.debug("Loaded clip.");
return true;
}
private boolean createSourceDataLine() {
if (DEBUG || Printer.debug)Printer.debug("JavaSoundAudioClip.createSourceDataLine()");
try {
DataLine.Info info = new DataLine.Info(SourceDataLine.class, loadedAudioFormat);
if (!(AudioSystem.isLineSupported(info)) ) {
if (DEBUG || Printer.err)Printer.err("Line not supported: "+loadedAudioFormat);
// fail silently
return false;
}
SourceDataLine source = (SourceDataLine) AudioSystem.getLine(info);
datapusher = new DataPusher(source, loadedAudioFormat, loadedAudio, loadedAudioByteLength);
} catch (Exception e) {
if (DEBUG || Printer.err)e.printStackTrace();
// fail silently
return false;
}
if (datapusher==null) {
// fail silently
return false;
}
if (DEBUG || Printer.debug)Printer.debug("Created SourceDataLine.");
return true;
}
private boolean createSequencer(BufferedInputStream in) throws IOException {
if (DEBUG || Printer.debug)Printer.debug("JavaSoundAudioClip.createSequencer()");
// get the sequencer
try {
sequencer = MidiSystem.getSequencer( );
} catch(MidiUnavailableException me) {
if (DEBUG || Printer.err)me.printStackTrace();
return false;
}
if (sequencer==null) {
return false;
}
try {
sequence = MidiSystem.getSequence(in);
if (sequence == null) {
return false;
}
} catch (InvalidMidiDataException e) {
if (DEBUG || Printer.err)e.printStackTrace();
return false;
}
if (DEBUG || Printer.debug)Printer.debug("Created Sequencer.");
return true;
}
/*
* private inner class representing a ByteArrayOutputStream
* which allows retrieval of the internal array
*/
private static class DirectBAOS extends ByteArrayOutputStream {
DirectBAOS() {
super();
}
public byte[] getInternalBuffer() {
return buf;
}
} // class DirectBAOS
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import javax.sound.midi.*;
/**
* Helper class which allows to convert {@code Receiver}
* to {@code MidiDeviceReceiver}.
*
* @author Alex Menkov
*/
public final class MidiDeviceReceiverEnvelope implements MidiDeviceReceiver {
private final MidiDevice device;
private final Receiver receiver;
/**
* Creates a new {@code MidiDeviceReceiverEnvelope} object which
* envelops the specified {@code Receiver}
* and is owned by the specified {@code MidiDevice}.
*
* @param device the owner {@code MidiDevice}
* @param receiver the {@code Receiver} to be enveloped
*/
public MidiDeviceReceiverEnvelope(MidiDevice device, Receiver receiver) {
if (device == null || receiver == null) {
throw new NullPointerException();
}
this.device = device;
this.receiver = receiver;
}
// Receiver implementation
public void close() {
receiver.close();
}
public void send(MidiMessage message, long timeStamp) {
receiver.send(message, timeStamp);
}
// MidiDeviceReceiver implementation
public MidiDevice getMidiDevice() {
return device;
}
/**
* Obtains the receiver enveloped
* by this {@code MidiDeviceReceiverEnvelope} object.
*
* @return the enveloped receiver
*/
public Receiver getReceiver() {
return receiver;
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import javax.sound.midi.*;
/**
* Helper class which allows to convert {@code Transmitter}
* to {@code MidiDeviceTransmitter}.
*
* @author Alex Menkov
*/
public final class MidiDeviceTransmitterEnvelope implements MidiDeviceTransmitter {
private final MidiDevice device;
private final Transmitter transmitter;
/**
* Creates a new {@code MidiDeviceTransmitterEnvelope} object which
* envelops the specified {@code Transmitter}
* and is owned by the specified {@code MidiDevice}.
*
* @param device the owner {@code MidiDevice}
* @param transmitter the {@code Transmitter} to be enveloped
*/
public MidiDeviceTransmitterEnvelope(MidiDevice device, Transmitter transmitter) {
if (device == null || transmitter == null) {
throw new NullPointerException();
}
this.device = device;
this.transmitter = transmitter;
}
// Transmitter implementation
public void setReceiver(Receiver receiver) {
transmitter.setReceiver(receiver);
}
public Receiver getReceiver() {
return transmitter.getReceiver();
}
public void close() {
transmitter.close();
}
// MidiDeviceReceiver implementation
public MidiDevice getMidiDevice() {
return device;
}
/**
* Obtains the transmitter enveloped
* by this {@code MidiDeviceTransmitterEnvelope} object.
*
* @return the enveloped transmitter
*/
public Transmitter getTransmitter() {
return transmitter;
}
}

View File

@@ -0,0 +1,195 @@
/*
* Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import javax.sound.midi.*;
/**
* MidiInDevice class representing functionality of MidiIn devices.
*
* @author David Rivas
* @author Kara Kytle
* @author Florian Bomers
*/
final class MidiInDevice extends AbstractMidiDevice implements Runnable {
private volatile Thread midiInThread;
// CONSTRUCTOR
MidiInDevice(AbstractMidiDeviceProvider.Info info) {
super(info);
if(Printer.trace) Printer.trace("MidiInDevice CONSTRUCTOR");
}
// IMPLEMENTATION OF ABSTRACT MIDI DEVICE METHODS
// $$kk: 06.24.99: i have this both opening and starting the midi in device.
// may want to separate these??
protected synchronized void implOpen() throws MidiUnavailableException {
if (Printer.trace) Printer.trace("> MidiInDevice: implOpen()");
int index = ((MidiInDeviceProvider.MidiInDeviceInfo)getDeviceInfo()).getIndex();
id = nOpen(index); // can throw MidiUnavailableException
if (id == 0) {
throw new MidiUnavailableException("Unable to open native device");
}
// create / start a thread to get messages
if (midiInThread == null) {
midiInThread = JSSecurityManager.createThread(this,
"Java Sound MidiInDevice Thread", // name
false, // daemon
-1, // priority
true); // doStart
}
nStart(id); // can throw MidiUnavailableException
if (Printer.trace) Printer.trace("< MidiInDevice: implOpen() completed");
}
// $$kk: 06.24.99: i have this both stopping and closing the midi in device.
// may want to separate these??
protected synchronized void implClose() {
if (Printer.trace) Printer.trace("> MidiInDevice: implClose()");
long oldId = id;
id = 0;
super.implClose();
// close the device
nStop(oldId);
if (midiInThread != null) {
try {
midiInThread.join(1000);
} catch (InterruptedException e) {
// IGNORE EXCEPTION
}
}
nClose(oldId);
if (Printer.trace) Printer.trace("< MidiInDevice: implClose() completed");
}
public long getMicrosecondPosition() {
long timestamp = -1;
if (isOpen()) {
timestamp = nGetTimeStamp(id);
}
return timestamp;
}
// OVERRIDES OF ABSTRACT MIDI DEVICE METHODS
protected boolean hasTransmitters() {
return true;
}
protected Transmitter createTransmitter() {
return new MidiInTransmitter();
}
/**
* An own class to distinguish the class name from
* the transmitter of other devices
*/
private final class MidiInTransmitter extends BasicTransmitter {
private MidiInTransmitter() {
super();
}
}
// RUNNABLE METHOD
public void run() {
// while the device is started, keep trying to get messages.
// this thread returns from native code whenever stop() or close() is called
while (id!=0) {
// go into native code and retrieve messages
nGetMessages(id);
if (id!=0) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {}
}
}
if(Printer.verbose) Printer.verbose("MidiInDevice Thread exit");
// let the thread exit
midiInThread = null;
}
// CALLBACKS FROM NATIVE
/**
* Callback from native code when a short MIDI event is received from hardware.
* @param packedMsg: status | data1 << 8 | data2 << 8
* @param timeStamp time-stamp in microseconds
*/
void callbackShortMessage(int packedMsg, long timeStamp) {
if (packedMsg == 0 || id == 0) {
return;
}
/*if(Printer.verbose) {
int status = packedMsg & 0xFF;
int data1 = (packedMsg & 0xFF00)>>8;
int data2 = (packedMsg & 0xFF0000)>>16;
Printer.verbose(">> MidiInDevice callbackShortMessage: status: " + status + " data1: " + data1 + " data2: " + data2 + " timeStamp: " + timeStamp);
}*/
getTransmitterList().sendMessage(packedMsg, timeStamp);
}
void callbackLongMessage(byte[] data, long timeStamp) {
if (id == 0 || data == null) {
return;
}
getTransmitterList().sendMessage(data, timeStamp);
}
// NATIVE METHODS
private native long nOpen(int index) throws MidiUnavailableException;
private native void nClose(long id);
private native void nStart(long id) throws MidiUnavailableException;
private native void nStop(long id);
private native long nGetTimeStamp(long id);
// go into native code and get messages. May be blocking
private native void nGetMessages(long id);
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import javax.sound.midi.MidiDevice;
/**
* MIDI input device provider.
*
* @author Kara Kytle
* @author Florian Bomers
*/
public final class MidiInDeviceProvider extends AbstractMidiDeviceProvider {
/** Cache of info objects for all MIDI output devices on the system. */
private static Info[] infos = null;
/** Cache of open MIDI input devices on the system. */
private static MidiDevice[] devices = null;
private static final boolean enabled;
// STATIC
static {
// initialize
Platform.initialize();
enabled = Platform.isMidiIOEnabled();
}
// CONSTRUCTOR
/**
* Required public no-arg constructor.
*/
public MidiInDeviceProvider() {
if (Printer.trace) Printer.trace("MidiInDeviceProvider: constructor");
}
// implementation of abstract methods in AbstractMidiDeviceProvider
AbstractMidiDeviceProvider.Info createInfo(int index) {
if (!enabled) {
return null;
}
return new MidiInDeviceInfo(index, MidiInDeviceProvider.class);
}
MidiDevice createDevice(AbstractMidiDeviceProvider.Info info) {
if (enabled && (info instanceof MidiInDeviceInfo)) {
return new MidiInDevice(info);
}
return null;
}
int getNumDevices() {
if (!enabled) {
if (Printer.debug)Printer.debug("MidiInDevice not enabled, returning 0 devices");
return 0;
}
int numDevices = nGetNumDevices();
if (Printer.debug)Printer.debug("MidiInDeviceProvider.getNumDevices(): devices: " + numDevices);
return numDevices;
}
MidiDevice[] getDeviceCache() { return devices; }
void setDeviceCache(MidiDevice[] devices) { this.devices = devices; }
Info[] getInfoCache() { return infos; }
void setInfoCache(Info[] infos) { this.infos = infos; }
// INNER CLASSES
/**
* Info class for MidiInDevices. Adds the
* provider's Class to keep the provider class from being
* unloaded. Otherwise, at least on JDK1.1.7 and 1.1.8,
* the provider class can be unloaded. Then, then the provider
* is next invoked, the static block is executed again and a new
* instance of the device object is created. Even though the
* previous instance may still exist and be open / in use / etc.,
* the new instance will not reflect that state...
*/
static final class MidiInDeviceInfo extends AbstractMidiDeviceProvider.Info {
private final Class providerClass;
private MidiInDeviceInfo(int index, Class providerClass) {
super(nGetName(index), nGetVendor(index), nGetDescription(index), nGetVersion(index), index);
this.providerClass = providerClass;
}
} // class MidiInDeviceInfo
// NATIVE METHODS
private static native int nGetNumDevices();
private static native String nGetName(int index);
private static native String nGetVendor(int index);
private static native String nGetDescription(int index);
private static native String nGetVersion(int index);
}

View File

@@ -0,0 +1,176 @@
/*
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import javax.sound.midi.*;
/**
* MidiOutDevice class representing functionality of MidiOut devices.
*
* @author David Rivas
* @author Kara Kytle
* @author Florian Bomers
*/
final class MidiOutDevice extends AbstractMidiDevice {
// CONSTRUCTOR
MidiOutDevice(AbstractMidiDeviceProvider.Info info) {
super(info);
if(Printer.trace) Printer.trace("MidiOutDevice CONSTRUCTOR");
}
// IMPLEMENTATION OF ABSTRACT MIDI DEVICE METHODS
protected synchronized void implOpen() throws MidiUnavailableException {
if (Printer.trace) Printer.trace("> MidiOutDevice: implOpen()");
int index = ((AbstractMidiDeviceProvider.Info)getDeviceInfo()).getIndex();
id = nOpen(index); // can throw MidiUnavailableException
if (id == 0) {
throw new MidiUnavailableException("Unable to open native device");
}
if (Printer.trace) Printer.trace("< MidiOutDevice: implOpen(): completed.");
}
protected synchronized void implClose() {
if (Printer.trace) Printer.trace("> MidiOutDevice: implClose()");
// prevent further action
long oldId = id;
id = 0;
super.implClose();
// close the device
nClose(oldId);
if (Printer.trace) Printer.trace("< MidiOutDevice: implClose(): completed");
}
public long getMicrosecondPosition() {
long timestamp = -1;
if (isOpen()) {
timestamp = nGetTimeStamp(id);
}
return timestamp;
}
// OVERRIDES OF ABSTRACT MIDI DEVICE METHODS
/** Returns if this device supports Receivers.
This implementation always returns true.
@return true, if the device supports Receivers, false otherwise.
*/
protected boolean hasReceivers() {
return true;
}
protected Receiver createReceiver() {
return new MidiOutReceiver();
}
// INNER CLASSES
final class MidiOutReceiver extends AbstractReceiver {
void implSend(final MidiMessage message, final long timeStamp) {
final int length = message.getLength();
final int status = message.getStatus();
if (length <= 3 && status != 0xF0 && status != 0xF7) {
int packedMsg;
if (message instanceof ShortMessage) {
if (message instanceof FastShortMessage) {
packedMsg = ((FastShortMessage) message).getPackedMsg();
} else {
ShortMessage msg = (ShortMessage) message;
packedMsg = (status & 0xFF)
| ((msg.getData1() & 0xFF) << 8)
| ((msg.getData2() & 0xFF) << 16);
}
} else {
packedMsg = 0;
byte[] data = message.getMessage();
if (length>0) {
packedMsg = data[0] & 0xFF;
if (length>1) {
/* We handle meta messages here. The message
system reset (FF) doesn't get until here,
because it's length is only 1. So if we see
a status byte of FF, it's sure that we
have a Meta message. */
if (status == 0xFF) {
return;
}
packedMsg |= (data[1] & 0xFF) << 8;
if (length>2) {
packedMsg |= (data[2] & 0xFF) << 16;
}
}
}
}
nSendShortMessage(id, packedMsg, timeStamp);
} else {
final byte[] data;
if (message instanceof FastSysexMessage) {
data = ((FastSysexMessage) message).getReadOnlyMessage();
} else {
data = message.getMessage();
}
final int dataLength = Math.min(length, data.length);
if (dataLength > 0) {
nSendLongMessage(id, data, dataLength, timeStamp);
}
}
}
/** shortcut for the Sun implementation */
synchronized void sendPackedMidiMessage(int packedMsg, long timeStamp) {
if (isOpen() && id != 0) {
nSendShortMessage(id, packedMsg, timeStamp);
}
}
} // class MidiOutReceiver
// NATIVE METHODS
private native long nOpen(int index) throws MidiUnavailableException;
private native void nClose(long id);
private native void nSendShortMessage(long id, int packedMsg, long timeStamp);
private native void nSendLongMessage(long id, byte[] data, int size, long timeStamp);
private native long nGetTimeStamp(long id);
} // class MidiOutDevice

View File

@@ -0,0 +1,124 @@
/*
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import javax.sound.midi.MidiDevice;
/**
* MIDI output device provider.
*
* @author Kara Kytle
* @author Florian Bomers
*/
public final class MidiOutDeviceProvider extends AbstractMidiDeviceProvider {
/** Cache of info objects for all MIDI output devices on the system. */
private static Info[] infos = null;
/** Cache of open MIDI output devices on the system. */
private static MidiDevice[] devices = null;
private final static boolean enabled;
// STATIC
static {
// initialize
Platform.initialize();
enabled = Platform.isMidiIOEnabled();
}
// CONSTRUCTOR
/**
* Required public no-arg constructor.
*/
public MidiOutDeviceProvider() {
if (Printer.trace) Printer.trace("MidiOutDeviceProvider: constructor");
}
// implementation of abstract methods in AbstractMidiDeviceProvider
AbstractMidiDeviceProvider.Info createInfo(int index) {
if (!enabled) {
return null;
}
return new MidiOutDeviceInfo(index, MidiOutDeviceProvider.class);
}
MidiDevice createDevice(AbstractMidiDeviceProvider.Info info) {
if (enabled && (info instanceof MidiOutDeviceInfo)) {
return new MidiOutDevice(info);
}
return null;
}
int getNumDevices() {
if (!enabled) {
if (Printer.debug)Printer.debug("MidiOutDevice not enabled, returning 0 devices");
return 0;
}
return nGetNumDevices();
}
MidiDevice[] getDeviceCache() { return devices; }
void setDeviceCache(MidiDevice[] devices) { this.devices = devices; }
Info[] getInfoCache() { return infos; }
void setInfoCache(Info[] infos) { this.infos = infos; }
// INNER CLASSES
/**
* Info class for MidiOutDevices. Adds the
* provider's Class to keep the provider class from being
* unloaded. Otherwise, at least on JDK1.1.7 and 1.1.8,
* the provider class can be unloaded. Then, then the provider
* is next invoked, the static block is executed again and a new
* instance of the device object is created. Even though the
* previous instance may still exist and be open / in use / etc.,
* the new instance will not reflect that state...
*/
static final class MidiOutDeviceInfo extends AbstractMidiDeviceProvider.Info {
private final Class providerClass;
private MidiOutDeviceInfo(int index, Class providerClass) {
super(nGetName(index), nGetVendor(index), nGetDescription(index), nGetVersion(index), index);
this.providerClass = providerClass;
}
} // class MidiOutDeviceInfo
// NATIVE METHODS
private static native int nGetNumDevices();
private static native String nGetName(int index);
private static native String nGetVendor(int index);
private static native String nGetDescription(int index);
private static native String nGetVersion(int index);
}

View File

@@ -0,0 +1,361 @@
/*
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import javax.sound.midi.*;
import java.util.ArrayList;
// TODO:
// - define and use a global symbolic constant for 60000000 (see convertTempo)
/**
* Some utilities for MIDI (some stuff is used from javax.sound.midi)
*
* @author Florian Bomers
*/
public final class MidiUtils {
public final static int DEFAULT_TEMPO_MPQ = 500000; // 120bpm
public final static int META_END_OF_TRACK_TYPE = 0x2F;
public final static int META_TEMPO_TYPE = 0x51;
/**
* Suppresses default constructor, ensuring non-instantiability.
*/
private MidiUtils() {
}
/** return true if the passed message is Meta End Of Track */
public static boolean isMetaEndOfTrack(MidiMessage midiMsg) {
// first check if it is a META message at all
if (midiMsg.getLength() != 3
|| midiMsg.getStatus() != MetaMessage.META) {
return false;
}
// now get message and check for end of track
byte[] msg = midiMsg.getMessage();
return ((msg[1] & 0xFF) == META_END_OF_TRACK_TYPE) && (msg[2] == 0);
}
/** return if the given message is a meta tempo message */
public static boolean isMetaTempo(MidiMessage midiMsg) {
// first check if it is a META message at all
if (midiMsg.getLength() != 6
|| midiMsg.getStatus() != MetaMessage.META) {
return false;
}
// now get message and check for tempo
byte[] msg = midiMsg.getMessage();
// meta type must be 0x51, and data length must be 3
return ((msg[1] & 0xFF) == META_TEMPO_TYPE) && (msg[2] == 3);
}
/** parses this message for a META tempo message and returns
* the tempo in MPQ, or -1 if this isn't a tempo message
*/
public static int getTempoMPQ(MidiMessage midiMsg) {
// first check if it is a META message at all
if (midiMsg.getLength() != 6
|| midiMsg.getStatus() != MetaMessage.META) {
return -1;
}
byte[] msg = midiMsg.getMessage();
if (((msg[1] & 0xFF) != META_TEMPO_TYPE) || (msg[2] != 3)) {
return -1;
}
int tempo = (msg[5] & 0xFF)
| ((msg[4] & 0xFF) << 8)
| ((msg[3] & 0xFF) << 16);
return tempo;
}
/**
* converts<br>
* 1 - MPQ-Tempo to BPM tempo<br>
* 2 - BPM tempo to MPQ tempo<br>
*/
public static double convertTempo(double tempo) {
if (tempo <= 0) {
tempo = 1;
}
return ((double) 60000000l) / tempo;
}
/**
* convert tick to microsecond with given tempo.
* Does not take tempo changes into account.
* Does not work for SMPTE timing!
*/
public static long ticks2microsec(long tick, double tempoMPQ, int resolution) {
return (long) (((double) tick) * tempoMPQ / resolution);
}
/**
* convert tempo to microsecond with given tempo
* Does not take tempo changes into account.
* Does not work for SMPTE timing!
*/
public static long microsec2ticks(long us, double tempoMPQ, int resolution) {
// do not round to nearest tick
//return (long) Math.round((((double)us) * resolution) / tempoMPQ);
return (long) ((((double)us) * resolution) / tempoMPQ);
}
/**
* Given a tick, convert to microsecond
* @param cache tempo info and current tempo
*/
public static long tick2microsecond(Sequence seq, long tick, TempoCache cache) {
if (seq.getDivisionType() != Sequence.PPQ ) {
double seconds = ((double)tick / (double)(seq.getDivisionType() * seq.getResolution()));
return (long) (1000000 * seconds);
}
if (cache == null) {
cache = new TempoCache(seq);
}
int resolution = seq.getResolution();
long[] ticks = cache.ticks;
int[] tempos = cache.tempos; // in MPQ
int cacheCount = tempos.length;
// optimization to not always go through entire list of tempo events
int snapshotIndex = cache.snapshotIndex;
int snapshotMicro = cache.snapshotMicro;
// walk through all tempo changes and add time for the respective blocks
long us = 0; // microsecond
if (snapshotIndex <= 0
|| snapshotIndex >= cacheCount
|| ticks[snapshotIndex] > tick) {
snapshotMicro = 0;
snapshotIndex = 0;
}
if (cacheCount > 0) {
// this implementation needs a tempo event at tick 0!
int i = snapshotIndex + 1;
while (i < cacheCount && ticks[i] <= tick) {
snapshotMicro += ticks2microsec(ticks[i] - ticks[i - 1], tempos[i - 1], resolution);
snapshotIndex = i;
i++;
}
us = snapshotMicro
+ ticks2microsec(tick - ticks[snapshotIndex],
tempos[snapshotIndex],
resolution);
}
cache.snapshotIndex = snapshotIndex;
cache.snapshotMicro = snapshotMicro;
return us;
}
/**
* Given a microsecond time, convert to tick.
* returns tempo at the given time in cache.getCurrTempoMPQ
*/
public static long microsecond2tick(Sequence seq, long micros, TempoCache cache) {
if (seq.getDivisionType() != Sequence.PPQ ) {
double dTick = ( ((double) micros)
* ((double) seq.getDivisionType())
* ((double) seq.getResolution()))
/ ((double) 1000000);
long tick = (long) dTick;
if (cache != null) {
cache.currTempo = (int) cache.getTempoMPQAt(tick);
}
return tick;
}
if (cache == null) {
cache = new TempoCache(seq);
}
long[] ticks = cache.ticks;
int[] tempos = cache.tempos; // in MPQ
int cacheCount = tempos.length;
int resolution = seq.getResolution();
long us = 0; long tick = 0; int newReadPos = 0; int i = 1;
// walk through all tempo changes and add time for the respective blocks
// to find the right tick
if (micros > 0 && cacheCount > 0) {
// this loop requires that the first tempo Event is at time 0
while (i < cacheCount) {
long nextTime = us + ticks2microsec(ticks[i] - ticks[i - 1],
tempos[i - 1], resolution);
if (nextTime > micros) {
break;
}
us = nextTime;
i++;
}
tick = ticks[i - 1] + microsec2ticks(micros - us, tempos[i - 1], resolution);
if (Printer.debug) Printer.debug("microsecond2tick(" + (micros / 1000)+") = "+tick+" ticks.");
//if (Printer.debug) Printer.debug(" -> convert back = " + (tick2microsecond(seq, tick, null) / 1000)+" microseconds");
}
cache.currTempo = tempos[i - 1];
return tick;
}
/**
* Binary search for the event indexes of the track
*
* @param tick - tick number of index to be found in array
* @return index in track which is on or after "tick".
* if no entries are found that follow after tick, track.size() is returned
*/
public static int tick2index(Track track, long tick) {
int ret = 0;
if (tick > 0) {
int low = 0;
int high = track.size() - 1;
while (low < high) {
// take the middle event as estimate
ret = (low + high) >> 1;
// tick of estimate
long t = track.get(ret).getTick();
if (t == tick) {
break;
} else if (t < tick) {
// estimate too low
if (low == high - 1) {
// "or after tick"
ret++;
break;
}
low = ret;
} else { // if (t>tick)
// estimate too high
high = ret;
}
}
}
return ret;
}
public static final class TempoCache {
long[] ticks;
int[] tempos; // in MPQ
// index in ticks/tempos at the snapshot
int snapshotIndex = 0;
// microsecond at the snapshot
int snapshotMicro = 0;
int currTempo; // MPQ, used as return value for microsecond2tick
private boolean firstTempoIsFake = false;
public TempoCache() {
// just some defaults, to prevents weird stuff
ticks = new long[1];
tempos = new int[1];
tempos[0] = DEFAULT_TEMPO_MPQ;
snapshotIndex = 0;
snapshotMicro = 0;
}
public TempoCache(Sequence seq) {
this();
refresh(seq);
}
public synchronized void refresh(Sequence seq) {
ArrayList list = new ArrayList();
Track[] tracks = seq.getTracks();
if (tracks.length > 0) {
// tempo events only occur in track 0
Track track = tracks[0];
int c = track.size();
for (int i = 0; i < c; i++) {
MidiEvent ev = track.get(i);
MidiMessage msg = ev.getMessage();
if (isMetaTempo(msg)) {
// found a tempo event. Add it to the list
list.add(ev);
}
}
}
int size = list.size() + 1;
firstTempoIsFake = true;
if ((size > 1)
&& (((MidiEvent) list.get(0)).getTick() == 0)) {
// do not need to add an initial tempo event at the beginning
size--;
firstTempoIsFake = false;
}
ticks = new long[size];
tempos = new int[size];
int e = 0;
if (firstTempoIsFake) {
// add tempo 120 at beginning
ticks[0] = 0;
tempos[0] = DEFAULT_TEMPO_MPQ;
e++;
}
for (int i = 0; i < list.size(); i++, e++) {
MidiEvent evt = (MidiEvent) list.get(i);
ticks[e] = evt.getTick();
tempos[e] = getTempoMPQ(evt.getMessage());
}
snapshotIndex = 0;
snapshotMicro = 0;
}
public int getCurrTempoMPQ() {
return currTempo;
}
float getTempoMPQAt(long tick) {
return getTempoMPQAt(tick, -1.0f);
}
synchronized float getTempoMPQAt(long tick, float startTempoMPQ) {
for (int i = 0; i < ticks.length; i++) {
if (ticks[i] > tick) {
if (i > 0) i--;
if (startTempoMPQ > 0 && i == 0 && firstTempoIsFake) {
return startTempoMPQ;
}
return (float) tempos[i];
}
}
return tempos[tempos.length - 1];
}
}
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
/**
* ModelAbstractChannelMixer is ready for use class to implement
* ModelChannelMixer interface.
*
* @author Karl Helgason
*/
public abstract class ModelAbstractChannelMixer implements ModelChannelMixer {
public abstract boolean process(float[][] buffer, int offset, int len);
public abstract void stop();
public void allNotesOff() {
}
public void allSoundOff() {
}
public void controlChange(int controller, int value) {
}
public int getChannelPressure() {
return 0;
}
public int getController(int controller) {
return 0;
}
public boolean getMono() {
return false;
}
public boolean getMute() {
return false;
}
public boolean getOmni() {
return false;
}
public int getPitchBend() {
return 0;
}
public int getPolyPressure(int noteNumber) {
return 0;
}
public int getProgram() {
return 0;
}
public boolean getSolo() {
return false;
}
public boolean localControl(boolean on) {
return false;
}
public void noteOff(int noteNumber) {
}
public void noteOff(int noteNumber, int velocity) {
}
public void noteOn(int noteNumber, int velocity) {
}
public void programChange(int program) {
}
public void programChange(int bank, int program) {
}
public void resetAllControllers() {
}
public void setChannelPressure(int pressure) {
}
public void setMono(boolean on) {
}
public void setMute(boolean mute) {
}
public void setOmni(boolean on) {
}
public void setPitchBend(int bend) {
}
public void setPolyPressure(int noteNumber, int pressure) {
}
public void setSolo(boolean soloState) {
}
}

View File

@@ -0,0 +1,200 @@
/*
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.io.IOException;
import javax.sound.midi.Instrument;
import javax.sound.midi.MidiChannel;
import javax.sound.midi.Patch;
import javax.sound.midi.Soundbank;
import javax.sound.midi.SoundbankResource;
import javax.sound.midi.VoiceStatus;
/**
* A abstract class used to simplify creating custom ModelOscillator.
*
* @author Karl Helgason
*/
public abstract class ModelAbstractOscillator
implements ModelOscillator, ModelOscillatorStream, Soundbank {
protected float pitch = 6000;
protected float samplerate;
protected MidiChannel channel;
protected VoiceStatus voice;
protected int noteNumber;
protected int velocity;
protected boolean on = false;
public void init() {
}
public void close() throws IOException {
}
public void noteOff(int velocity) {
on = false;
}
public void noteOn(MidiChannel channel, VoiceStatus voice, int noteNumber,
int velocity) {
this.channel = channel;
this.voice = voice;
this.noteNumber = noteNumber;
this.velocity = velocity;
on = true;
}
public int read(float[][] buffer, int offset, int len) throws IOException {
return -1;
}
public MidiChannel getChannel() {
return channel;
}
public VoiceStatus getVoice() {
return voice;
}
public int getNoteNumber() {
return noteNumber;
}
public int getVelocity() {
return velocity;
}
public boolean isOn() {
return on;
}
public void setPitch(float pitch) {
this.pitch = pitch;
}
public float getPitch() {
return pitch;
}
public void setSampleRate(float samplerate) {
this.samplerate = samplerate;
}
public float getSampleRate() {
return samplerate;
}
public float getAttenuation() {
return 0;
}
public int getChannels() {
return 1;
}
public String getName() {
return getClass().getName();
}
public Patch getPatch() {
return new Patch(0, 0);
}
public ModelOscillatorStream open(float samplerate) {
ModelAbstractOscillator oscs;
try {
oscs = this.getClass().newInstance();
} catch (InstantiationException e) {
throw new IllegalArgumentException(e);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException(e);
}
oscs.setSampleRate(samplerate);
oscs.init();
return oscs;
}
public ModelPerformer getPerformer() {
// Create performer for my custom oscillirator
ModelPerformer performer = new ModelPerformer();
performer.getOscillators().add(this);
return performer;
}
public ModelInstrument getInstrument() {
// Create Instrument object around my performer
SimpleInstrument ins = new SimpleInstrument();
ins.setName(getName());
ins.add(getPerformer());
ins.setPatch(getPatch());
return ins;
}
public Soundbank getSoundBank() {
// Create Soundbank object around the instrument
SimpleSoundbank sbk = new SimpleSoundbank();
sbk.addInstrument(getInstrument());
return sbk;
}
public String getDescription() {
return getName();
}
public Instrument getInstrument(Patch patch) {
Instrument ins = getInstrument();
Patch p = ins.getPatch();
if (p.getBank() != patch.getBank())
return null;
if (p.getProgram() != patch.getProgram())
return null;
if (p instanceof ModelPatch && patch instanceof ModelPatch) {
if (((ModelPatch)p).isPercussion()
!= ((ModelPatch)patch).isPercussion()) {
return null;
}
}
return ins;
}
public Instrument[] getInstruments() {
return new Instrument[]{getInstrument()};
}
public SoundbankResource[] getResources() {
return new SoundbankResource[0];
}
public String getVendor() {
return null;
}
public String getVersion() {
return null;
}
}

View File

@@ -0,0 +1,331 @@
/*
* Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.Collection;
/**
* This class is a pointer to a binary array either in memory or on disk.
*
* @author Karl Helgason
*/
public final class ModelByteBuffer {
private ModelByteBuffer root = this;
private File file;
private long fileoffset;
private byte[] buffer;
private long offset;
private final long len;
private class RandomFileInputStream extends InputStream {
private final RandomAccessFile raf;
private long left;
private long mark = 0;
private long markleft = 0;
RandomFileInputStream() throws IOException {
raf = new RandomAccessFile(root.file, "r");
raf.seek(root.fileoffset + arrayOffset());
left = capacity();
}
public int available() throws IOException {
if (left > Integer.MAX_VALUE)
return Integer.MAX_VALUE;
return (int)left;
}
public synchronized void mark(int readlimit) {
try {
mark = raf.getFilePointer();
markleft = left;
} catch (IOException e) {
//e.printStackTrace();
}
}
public boolean markSupported() {
return true;
}
public synchronized void reset() throws IOException {
raf.seek(mark);
left = markleft;
}
public long skip(long n) throws IOException {
if( n < 0)
return 0;
if (n > left)
n = left;
long p = raf.getFilePointer();
raf.seek(p + n);
left -= n;
return n;
}
public int read(byte b[], int off, int len) throws IOException {
if (len > left)
len = (int)left;
if (left == 0)
return -1;
len = raf.read(b, off, len);
if (len == -1)
return -1;
left -= len;
return len;
}
public int read(byte[] b) throws IOException {
int len = b.length;
if (len > left)
len = (int)left;
if (left == 0)
return -1;
len = raf.read(b, 0, len);
if (len == -1)
return -1;
left -= len;
return len;
}
public int read() throws IOException {
if (left == 0)
return -1;
int b = raf.read();
if (b == -1)
return -1;
left--;
return b;
}
public void close() throws IOException {
raf.close();
}
}
private ModelByteBuffer(ModelByteBuffer parent,
long beginIndex, long endIndex, boolean independent) {
this.root = parent.root;
this.offset = 0;
long parent_len = parent.len;
if (beginIndex < 0)
beginIndex = 0;
if (beginIndex > parent_len)
beginIndex = parent_len;
if (endIndex < 0)
endIndex = 0;
if (endIndex > parent_len)
endIndex = parent_len;
if (beginIndex > endIndex)
beginIndex = endIndex;
offset = beginIndex;
len = endIndex - beginIndex;
if (independent) {
buffer = root.buffer;
if (root.file != null) {
file = root.file;
fileoffset = root.fileoffset + arrayOffset();
offset = 0;
} else
offset = arrayOffset();
root = this;
}
}
public ModelByteBuffer(byte[] buffer) {
this.buffer = buffer;
this.offset = 0;
this.len = buffer.length;
}
public ModelByteBuffer(byte[] buffer, int offset, int len) {
this.buffer = buffer;
this.offset = offset;
this.len = len;
}
public ModelByteBuffer(File file) {
this.file = file;
this.fileoffset = 0;
this.len = file.length();
}
public ModelByteBuffer(File file, long offset, long len) {
this.file = file;
this.fileoffset = offset;
this.len = len;
}
public void writeTo(OutputStream out) throws IOException {
if (root.file != null && root.buffer == null) {
try (InputStream is = getInputStream()) {
byte[] buff = new byte[1024];
int ret;
while ((ret = is.read(buff)) != -1) {
out.write(buff, 0, ret);
}
}
} else
out.write(array(), (int) arrayOffset(), (int) capacity());
}
public InputStream getInputStream() {
if (root.file != null && root.buffer == null) {
try {
return new RandomFileInputStream();
} catch (IOException e) {
//e.printStackTrace();
return null;
}
}
return new ByteArrayInputStream(array(),
(int)arrayOffset(), (int)capacity());
}
public ModelByteBuffer subbuffer(long beginIndex) {
return subbuffer(beginIndex, capacity());
}
public ModelByteBuffer subbuffer(long beginIndex, long endIndex) {
return subbuffer(beginIndex, endIndex, false);
}
public ModelByteBuffer subbuffer(long beginIndex, long endIndex,
boolean independent) {
return new ModelByteBuffer(this, beginIndex, endIndex, independent);
}
public byte[] array() {
return root.buffer;
}
public long arrayOffset() {
if (root != this)
return root.arrayOffset() + offset;
return offset;
}
public long capacity() {
return len;
}
public ModelByteBuffer getRoot() {
return root;
}
public File getFile() {
return file;
}
public long getFilePointer() {
return fileoffset;
}
public static void loadAll(Collection<ModelByteBuffer> col)
throws IOException {
File selfile = null;
RandomAccessFile raf = null;
try {
for (ModelByteBuffer mbuff : col) {
mbuff = mbuff.root;
if (mbuff.file == null)
continue;
if (mbuff.buffer != null)
continue;
if (selfile == null || !selfile.equals(mbuff.file)) {
if (raf != null) {
raf.close();
raf = null;
}
selfile = mbuff.file;
raf = new RandomAccessFile(mbuff.file, "r");
}
raf.seek(mbuff.fileoffset);
byte[] buffer = new byte[(int) mbuff.capacity()];
int read = 0;
int avail = buffer.length;
while (read != avail) {
if (avail - read > 65536) {
raf.readFully(buffer, read, 65536);
read += 65536;
} else {
raf.readFully(buffer, read, avail - read);
read = avail;
}
}
mbuff.buffer = buffer;
mbuff.offset = 0;
}
} finally {
if (raf != null)
raf.close();
}
}
public void load() throws IOException {
if (root != this) {
root.load();
return;
}
if (buffer != null)
return;
if (file == null) {
throw new IllegalStateException(
"No file associated with this ByteBuffer!");
}
DataInputStream is = new DataInputStream(getInputStream());
buffer = new byte[(int) capacity()];
offset = 0;
is.readFully(buffer);
is.close();
}
public void unload() {
if (root != this) {
root.unload();
return;
}
if (file == null) {
throw new IllegalStateException(
"No file associated with this ByteBuffer!");
}
root.buffer = null;
}
}

View File

@@ -0,0 +1,282 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.io.IOException;
import java.io.InputStream;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.AudioFormat.Encoding;
/**
* Wavetable oscillator for pre-loaded data.
*
* @author Karl Helgason
*/
public final class ModelByteBufferWavetable implements ModelWavetable {
private class Buffer8PlusInputStream extends InputStream {
private final boolean bigendian;
private final int framesize_pc;
int pos = 0;
int pos2 = 0;
int markpos = 0;
int markpos2 = 0;
Buffer8PlusInputStream() {
framesize_pc = format.getFrameSize() / format.getChannels();
bigendian = format.isBigEndian();
}
public int read(byte[] b, int off, int len) throws IOException {
int avail = available();
if (avail <= 0)
return -1;
if (len > avail)
len = avail;
byte[] buff1 = buffer.array();
byte[] buff2 = buffer8.array();
pos += buffer.arrayOffset();
pos2 += buffer8.arrayOffset();
if (bigendian) {
for (int i = 0; i < len; i += (framesize_pc + 1)) {
System.arraycopy(buff1, pos, b, i, framesize_pc);
System.arraycopy(buff2, pos2, b, i + framesize_pc, 1);
pos += framesize_pc;
pos2 += 1;
}
} else {
for (int i = 0; i < len; i += (framesize_pc + 1)) {
System.arraycopy(buff2, pos2, b, i, 1);
System.arraycopy(buff1, pos, b, i + 1, framesize_pc);
pos += framesize_pc;
pos2 += 1;
}
}
pos -= buffer.arrayOffset();
pos2 -= buffer8.arrayOffset();
return len;
}
public long skip(long n) throws IOException {
int avail = available();
if (avail <= 0)
return -1;
if (n > avail)
n = avail;
pos += (n / (framesize_pc + 1)) * (framesize_pc);
pos2 += n / (framesize_pc + 1);
return super.skip(n);
}
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
public int read() throws IOException {
byte[] b = new byte[1];
int ret = read(b, 0, 1);
if (ret == -1)
return -1;
return 0 & 0xFF;
}
public boolean markSupported() {
return true;
}
public int available() throws IOException {
return (int)buffer.capacity() + (int)buffer8.capacity() - pos - pos2;
}
public synchronized void mark(int readlimit) {
markpos = pos;
markpos2 = pos2;
}
public synchronized void reset() throws IOException {
pos = markpos;
pos2 = markpos2;
}
}
private float loopStart = -1;
private float loopLength = -1;
private final ModelByteBuffer buffer;
private ModelByteBuffer buffer8 = null;
private AudioFormat format = null;
private float pitchcorrection = 0;
private float attenuation = 0;
private int loopType = LOOP_TYPE_OFF;
public ModelByteBufferWavetable(ModelByteBuffer buffer) {
this.buffer = buffer;
}
public ModelByteBufferWavetable(ModelByteBuffer buffer,
float pitchcorrection) {
this.buffer = buffer;
this.pitchcorrection = pitchcorrection;
}
public ModelByteBufferWavetable(ModelByteBuffer buffer, AudioFormat format) {
this.format = format;
this.buffer = buffer;
}
public ModelByteBufferWavetable(ModelByteBuffer buffer, AudioFormat format,
float pitchcorrection) {
this.format = format;
this.buffer = buffer;
this.pitchcorrection = pitchcorrection;
}
public void set8BitExtensionBuffer(ModelByteBuffer buffer) {
buffer8 = buffer;
}
public ModelByteBuffer get8BitExtensionBuffer() {
return buffer8;
}
public ModelByteBuffer getBuffer() {
return buffer;
}
public AudioFormat getFormat() {
if (format == null) {
if (buffer == null)
return null;
InputStream is = buffer.getInputStream();
AudioFormat format = null;
try {
format = AudioSystem.getAudioFileFormat(is).getFormat();
} catch (Exception e) {
//e.printStackTrace();
}
try {
is.close();
} catch (IOException e) {
//e.printStackTrace();
}
return format;
}
return format;
}
public AudioFloatInputStream openStream() {
if (buffer == null)
return null;
if (format == null) {
InputStream is = buffer.getInputStream();
AudioInputStream ais = null;
try {
ais = AudioSystem.getAudioInputStream(is);
} catch (Exception e) {
//e.printStackTrace();
return null;
}
return AudioFloatInputStream.getInputStream(ais);
}
if (buffer.array() == null) {
return AudioFloatInputStream.getInputStream(new AudioInputStream(
buffer.getInputStream(), format,
buffer.capacity() / format.getFrameSize()));
}
if (buffer8 != null) {
if (format.getEncoding().equals(Encoding.PCM_SIGNED)
|| format.getEncoding().equals(Encoding.PCM_UNSIGNED)) {
InputStream is = new Buffer8PlusInputStream();
AudioFormat format2 = new AudioFormat(
format.getEncoding(),
format.getSampleRate(),
format.getSampleSizeInBits() + 8,
format.getChannels(),
format.getFrameSize() + (1 * format.getChannels()),
format.getFrameRate(),
format.isBigEndian());
AudioInputStream ais = new AudioInputStream(is, format2,
buffer.capacity() / format.getFrameSize());
return AudioFloatInputStream.getInputStream(ais);
}
}
return AudioFloatInputStream.getInputStream(format, buffer.array(),
(int)buffer.arrayOffset(), (int)buffer.capacity());
}
public int getChannels() {
return getFormat().getChannels();
}
public ModelOscillatorStream open(float samplerate) {
// ModelWavetableOscillator doesn't support ModelOscillatorStream
return null;
}
// attenuation is in cB
public float getAttenuation() {
return attenuation;
}
// attenuation is in cB
public void setAttenuation(float attenuation) {
this.attenuation = attenuation;
}
public float getLoopLength() {
return loopLength;
}
public void setLoopLength(float loopLength) {
this.loopLength = loopLength;
}
public float getLoopStart() {
return loopStart;
}
public void setLoopStart(float loopStart) {
this.loopStart = loopStart;
}
public void setLoopType(int loopType) {
this.loopType = loopType;
}
public int getLoopType() {
return loopType;
}
public float getPitchcorrection() {
return pitchcorrection;
}
public void setPitchcorrection(float pitchcorrection) {
this.pitchcorrection = pitchcorrection;
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import javax.sound.midi.MidiChannel;
/**
* ModelChannelMixer is used to process channel voice mix output before going
* to master output.<br>
* It can be used to:<br>
* <ul>
* <li>Implement non-voice oriented instruments.</li>
* <li>Add insert effect to instruments; for example distortion effect.</li>
* </ui>
* <p>
* <b>Warning! Classes that implements ModelChannelMixer must be thread-safe.</b>
*
* @author Karl Helgason
*/
public interface ModelChannelMixer extends MidiChannel {
// Used to process input audio from voices mix.
public boolean process(float[][] buffer, int offset, int len);
// Is used to trigger that this mixer is not be used
// and it should fade out.
public void stop();
}

View File

@@ -0,0 +1,135 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.util.Arrays;
/**
* Connection blocks are used to connect source variable
* to a destination variable.
* For example Note On velocity can be connected to output gain.
* In DLS this is called articulator and in SoundFonts (SF2) a modulator.
*
* @author Karl Helgason
*/
public final class ModelConnectionBlock {
//
// source1 * source2 * scale -> destination
//
private final static ModelSource[] no_sources = new ModelSource[0];
private ModelSource[] sources = no_sources;
private double scale = 1;
private ModelDestination destination;
public ModelConnectionBlock() {
}
public ModelConnectionBlock(double scale, ModelDestination destination) {
this.scale = scale;
this.destination = destination;
}
public ModelConnectionBlock(ModelSource source,
ModelDestination destination) {
if (source != null) {
this.sources = new ModelSource[1];
this.sources[0] = source;
}
this.destination = destination;
}
public ModelConnectionBlock(ModelSource source, double scale,
ModelDestination destination) {
if (source != null) {
this.sources = new ModelSource[1];
this.sources[0] = source;
}
this.scale = scale;
this.destination = destination;
}
public ModelConnectionBlock(ModelSource source, ModelSource control,
ModelDestination destination) {
if (source != null) {
if (control == null) {
this.sources = new ModelSource[1];
this.sources[0] = source;
} else {
this.sources = new ModelSource[2];
this.sources[0] = source;
this.sources[1] = control;
}
}
this.destination = destination;
}
public ModelConnectionBlock(ModelSource source, ModelSource control,
double scale, ModelDestination destination) {
if (source != null) {
if (control == null) {
this.sources = new ModelSource[1];
this.sources[0] = source;
} else {
this.sources = new ModelSource[2];
this.sources[0] = source;
this.sources[1] = control;
}
}
this.scale = scale;
this.destination = destination;
}
public ModelDestination getDestination() {
return destination;
}
public void setDestination(ModelDestination destination) {
this.destination = destination;
}
public double getScale() {
return scale;
}
public void setScale(double scale) {
this.scale = scale;
}
public ModelSource[] getSources() {
return Arrays.copyOf(sources, sources.length);
}
public void setSources(ModelSource[] source) {
this.sources = source == null ? no_sources : Arrays.copyOf(source, source.length);
}
public void addSource(ModelSource source) {
ModelSource[] oldsources = sources;
sources = new ModelSource[oldsources.length + 1];
System.arraycopy(oldsources, 0, sources, 0, oldsources.length);
sources[sources.length - 1] = source;
}
}

View File

@@ -0,0 +1,117 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
/**
* This class is used to identify destinations in connection blocks,
* see ModelConnectionBlock.
*
* @author Karl Helgason
*/
public final class ModelDestination {
public static final ModelIdentifier DESTINATION_NONE = null;
public static final ModelIdentifier DESTINATION_KEYNUMBER
= new ModelIdentifier("noteon", "keynumber");
public static final ModelIdentifier DESTINATION_VELOCITY
= new ModelIdentifier("noteon", "velocity");
public static final ModelIdentifier DESTINATION_PITCH
= new ModelIdentifier("osc", "pitch"); // cent
public static final ModelIdentifier DESTINATION_GAIN
= new ModelIdentifier("mixer", "gain"); // cB
public static final ModelIdentifier DESTINATION_PAN
= new ModelIdentifier("mixer", "pan"); // 0.1 %
public static final ModelIdentifier DESTINATION_REVERB
= new ModelIdentifier("mixer", "reverb"); // 0.1 %
public static final ModelIdentifier DESTINATION_CHORUS
= new ModelIdentifier("mixer", "chorus"); // 0.1 %
public static final ModelIdentifier DESTINATION_LFO1_DELAY
= new ModelIdentifier("lfo", "delay", 0); // timecent
public static final ModelIdentifier DESTINATION_LFO1_FREQ
= new ModelIdentifier("lfo", "freq", 0); // cent
public static final ModelIdentifier DESTINATION_LFO2_DELAY
= new ModelIdentifier("lfo", "delay", 1); // timecent
public static final ModelIdentifier DESTINATION_LFO2_FREQ
= new ModelIdentifier("lfo", "freq", 1); // cent
public static final ModelIdentifier DESTINATION_EG1_DELAY
= new ModelIdentifier("eg", "delay", 0); // timecent
public static final ModelIdentifier DESTINATION_EG1_ATTACK
= new ModelIdentifier("eg", "attack", 0); // timecent
public static final ModelIdentifier DESTINATION_EG1_HOLD
= new ModelIdentifier("eg", "hold", 0); // timecent
public static final ModelIdentifier DESTINATION_EG1_DECAY
= new ModelIdentifier("eg", "decay", 0); // timecent
public static final ModelIdentifier DESTINATION_EG1_SUSTAIN
= new ModelIdentifier("eg", "sustain", 0);
// 0.1 % (I want this to be value not %)
public static final ModelIdentifier DESTINATION_EG1_RELEASE
= new ModelIdentifier("eg", "release", 0); // timecent
public static final ModelIdentifier DESTINATION_EG1_SHUTDOWN
= new ModelIdentifier("eg", "shutdown", 0); // timecent
public static final ModelIdentifier DESTINATION_EG2_DELAY
= new ModelIdentifier("eg", "delay", 1); // timecent
public static final ModelIdentifier DESTINATION_EG2_ATTACK
= new ModelIdentifier("eg", "attack", 1); // timecent
public static final ModelIdentifier DESTINATION_EG2_HOLD
= new ModelIdentifier("eg", "hold", 1); // 0.1 %
public static final ModelIdentifier DESTINATION_EG2_DECAY
= new ModelIdentifier("eg", "decay", 1); // timecent
public static final ModelIdentifier DESTINATION_EG2_SUSTAIN
= new ModelIdentifier("eg", "sustain", 1);
// 0.1 % ( I want this to be value not %)
public static final ModelIdentifier DESTINATION_EG2_RELEASE
= new ModelIdentifier("eg", "release", 1); // timecent
public static final ModelIdentifier DESTINATION_EG2_SHUTDOWN
= new ModelIdentifier("eg", "shutdown", 1); // timecent
public static final ModelIdentifier DESTINATION_FILTER_FREQ
= new ModelIdentifier("filter", "freq", 0); // cent
public static final ModelIdentifier DESTINATION_FILTER_Q
= new ModelIdentifier("filter", "q", 0); // cB
private ModelIdentifier destination = DESTINATION_NONE;
private ModelTransform transform = new ModelStandardTransform();
public ModelDestination() {
}
public ModelDestination(ModelIdentifier id) {
destination = id;
}
public ModelIdentifier getIdentifier() {
return destination;
}
public void setIdentifier(ModelIdentifier destination) {
this.destination = destination;
}
public ModelTransform getTransform() {
return transform;
}
public void setTransform(ModelTransform transform) {
this.transform = transform;
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
/**
* ModelDirectedPlayer is the one who is directed by ModelDirector
* to play ModelPerformer objects.
*
* @author Karl Helgason
*/
public interface ModelDirectedPlayer {
public void play(int performerIndex, ModelConnectionBlock[] connectionBlocks);
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
/**
* A director chooses what performers should be played for each note on
* and note off events.
*
* ModelInstrument can implement custom performer who chooses what performers
* to play for example by sustain pedal is off or on.
*
* The default director (ModelStandardDirector) chooses performers
* by there keyfrom,keyto,velfrom,velto properties.
*
* @author Karl Helgason
*/
public interface ModelDirector {
public void noteOn(int noteNumber, int velocity);
public void noteOff(int noteNumber, int velocity);
public void close();
}

View File

@@ -0,0 +1,169 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
/**
* This class stores the identity of source and destinations in connection
* blocks, see ModelConnectionBlock.
*
* @author Karl Helgason
*/
public final class ModelIdentifier {
/*
* Object Variable
* ------ --------
*
* // INPUT parameters
* noteon keynumber 7 bit midi value
* velocity 7 bit midi vale
* on 1 or 0
*
* midi pitch 14 bit midi value
* channel_pressure 7 bit midi value
* poly_pressure 7 bit midi value
*
* midi_cc 0 (midi control #0 7 bit midi value
* 1 (midi control #1 7 bit midi value
* ...
* 127 (midi control #127 7 bit midi value
*
* midi_rpn 0 (midi rpn control #0) 14 bit midi value
* 1 (midi rpn control #1) 14 bit midi value
* ....
*
* // DAHDSR envelope generator
* eg (null)
* delay timecent
* attack timecent
* hold timecent
* decay timecent
* sustain 0.1 %
* release timecent
*
* // Low frequency oscillirator (sine wave)
* lfo (null)
* delay timcent
* freq cent
*
* // Resonance LowPass Filter 6dB slope
* filter (null) (output/input)
* freq cent
* q cB
*
* // The oscillator with preloaded wavetable data
* osc (null)
* pitch cent
*
* // Output mixer pins
* mixer gain cB
* pan 0.1 %
* reverb 0.1 %
* chorus 0.1 %
*
*/
private String object = null;
private String variable = null;
private int instance = 0;
public ModelIdentifier(String object) {
this.object = object;
}
public ModelIdentifier(String object, int instance) {
this.object = object;
this.instance = instance;
}
public ModelIdentifier(String object, String variable) {
this.object = object;
this.variable = variable;
}
public ModelIdentifier(String object, String variable, int instance) {
this.object = object;
this.variable = variable;
this.instance = instance;
}
public int getInstance() {
return instance;
}
public void setInstance(int instance) {
this.instance = instance;
}
public String getObject() {
return object;
}
public void setObject(String object) {
this.object = object;
}
public String getVariable() {
return variable;
}
public void setVariable(String variable) {
this.variable = variable;
}
public int hashCode() {
int hashcode = instance;
if(object != null) hashcode |= object.hashCode();
if(variable != null) hashcode |= variable.hashCode();
return hashcode;
}
public boolean equals(Object obj) {
if (!(obj instanceof ModelIdentifier))
return false;
ModelIdentifier mobj = (ModelIdentifier)obj;
if ((object == null) != (mobj.object == null))
return false;
if ((variable == null) != (mobj.variable == null))
return false;
if (mobj.getInstance() != getInstance())
return false;
if (!(object == null || object.equals(mobj.object)))
return false;
if (!(variable == null || variable.equals(mobj.variable)))
return false;
return true;
}
public String toString() {
if (variable == null) {
return object + "[" + instance + "]";
} else {
return object + "[" + instance + "]" + "." + variable;
}
}
}

View File

@@ -0,0 +1,136 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import javax.sound.midi.Instrument;
import javax.sound.midi.MidiChannel;
import javax.sound.midi.Patch;
import javax.sound.midi.Soundbank;
import javax.sound.sampled.AudioFormat;
/**
* The model instrument class.
*
* <p>The main methods to override are:<br>
* getPerformer, getDirector, getChannelMixer.
*
* <p>Performers are used to define what voices which will
* playback when using the instrument.<br>
*
* ChannelMixer is used to add channel-wide processing
* on voices output or to define non-voice oriented instruments.<br>
*
* Director is used to change how the synthesizer
* chooses what performers to play on midi events.
*
* @author Karl Helgason
*/
public abstract class ModelInstrument extends Instrument {
protected ModelInstrument(Soundbank soundbank, Patch patch, String name,
Class<?> dataClass) {
super(soundbank, patch, name, dataClass);
}
public ModelDirector getDirector(ModelPerformer[] performers,
MidiChannel channel, ModelDirectedPlayer player) {
return new ModelStandardIndexedDirector(performers, player);
}
public ModelPerformer[] getPerformers() {
return new ModelPerformer[0];
}
public ModelChannelMixer getChannelMixer(MidiChannel channel,
AudioFormat format) {
return null;
}
// Get General MIDI 2 Alias patch for this instrument.
public final Patch getPatchAlias() {
Patch patch = getPatch();
int program = patch.getProgram();
int bank = patch.getBank();
if (bank != 0)
return patch;
boolean percussion = false;
if (getPatch() instanceof ModelPatch)
percussion = ((ModelPatch)getPatch()).isPercussion();
if (percussion)
return new Patch(0x78 << 7, program);
else
return new Patch(0x79 << 7, program);
}
// Return name of all the keys.
// This information is generated from ModelPerformer.getName()
// returned from getPerformers().
public final String[] getKeys() {
String[] keys = new String[128];
for (ModelPerformer performer : getPerformers()) {
for (int k = performer.getKeyFrom(); k <= performer.getKeyTo(); k++) {
if (k >= 0 && k < 128 && keys[k] == null) {
String name = performer.getName();
if (name == null)
name = "untitled";
keys[k] = name;
}
}
}
return keys;
}
// Return what channels this instrument will probably response
// on General MIDI synthesizer.
public final boolean[] getChannels() {
boolean percussion = false;
if (getPatch() instanceof ModelPatch)
percussion = ((ModelPatch)getPatch()).isPercussion();
// Check if instrument is percussion.
if (percussion) {
boolean[] ch = new boolean[16];
for (int i = 0; i < ch.length; i++)
ch[i] = false;
ch[9] = true;
return ch;
}
// Check if instrument uses General MIDI 2 default banks.
int bank = getPatch().getBank();
if (bank >> 7 == 0x78 || bank >> 7 == 0x79) {
boolean[] ch = new boolean[16];
for (int i = 0; i < ch.length; i++)
ch[i] = true;
return ch;
}
boolean[] ch = new boolean[16];
for (int i = 0; i < ch.length; i++)
ch[i] = true;
ch[9] = false;
return ch;
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.util.Comparator;
import javax.sound.midi.Instrument;
import javax.sound.midi.Patch;
/**
* Instrument comparator class.
* Used to order instrument by program, bank, percussion.
*
* @author Karl Helgason
*/
public final class ModelInstrumentComparator implements Comparator<Instrument> {
public int compare(Instrument arg0, Instrument arg1) {
Patch p0 = arg0.getPatch();
Patch p1 = arg1.getPatch();
int a = p0.getBank() * 128 + p0.getProgram();
int b = p1.getBank() * 128 + p1.getProgram();
if (p0 instanceof ModelPatch) {
a += ((ModelPatch)p0).isPercussion() ? 2097152 : 0;
}
if (p1 instanceof ModelPatch) {
b += ((ModelPatch)p1).isPercussion() ? 2097152 : 0;
}
return a - b;
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import javax.sound.midi.MidiChannel;
import javax.sound.midi.Patch;
import javax.sound.sampled.AudioFormat;
/**
* This class is used to map instrument to another patch.
*
* @author Karl Helgason
*/
public final class ModelMappedInstrument extends ModelInstrument {
private final ModelInstrument ins;
public ModelMappedInstrument(ModelInstrument ins, Patch patch) {
super(ins.getSoundbank(), patch, ins.getName(), ins.getDataClass());
this.ins = ins;
}
public Object getData() {
return ins.getData();
}
public ModelPerformer[] getPerformers() {
return ins.getPerformers();
}
public ModelDirector getDirector(ModelPerformer[] performers,
MidiChannel channel, ModelDirectedPlayer player) {
return ins.getDirector(performers, channel, player);
}
public ModelChannelMixer getChannelMixer(MidiChannel channel,
AudioFormat format) {
return ins.getChannelMixer(channel, format);
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
/**
* This interface is used for oscillators.
* See example in ModelDefaultOscillator which is a wavetable oscillator.
*
* @author Karl Helgason
*/
public interface ModelOscillator {
public int getChannels();
/**
* Attenuation is in cB.
* @return
*/
public float getAttenuation();
public ModelOscillatorStream open(float samplerate);
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.io.IOException;
import javax.sound.midi.MidiChannel;
import javax.sound.midi.VoiceStatus;
/**
* This interface is used for audio streams from ModelOscillator.
*
* @author Karl Helgason
*/
public interface ModelOscillatorStream {
public void setPitch(float pitch); // Pitch is in cents!
public void noteOn(MidiChannel channel, VoiceStatus voice, int noteNumber,
int velocity);
public void noteOff(int velocity);
public int read(float[][] buffer, int offset, int len) throws IOException;
public void close() throws IOException;
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import javax.sound.midi.Patch;
/**
* A extended patch object that has isPercussion function.
* Which is necessary to identify percussion instruments
* from melodic instruments.
*
* @author Karl Helgason
*/
public final class ModelPatch extends Patch {
private boolean percussion = false;
public ModelPatch(int bank, int program) {
super(bank, program);
}
public ModelPatch(int bank, int program, boolean percussion) {
super(bank, program);
this.percussion = percussion;
}
public boolean isPercussion() {
return percussion;
}
}

View File

@@ -0,0 +1,143 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.util.ArrayList;
import java.util.List;
/**
* This class is used to define how to synthesize audio in universal maner
* for both SF2 and DLS instruments.
*
* @author Karl Helgason
*/
public final class ModelPerformer {
private final List<ModelOscillator> oscillators = new ArrayList<ModelOscillator>();
private List<ModelConnectionBlock> connectionBlocks
= new ArrayList<ModelConnectionBlock>();
private int keyFrom = 0;
private int keyTo = 127;
private int velFrom = 0;
private int velTo = 127;
private int exclusiveClass = 0;
private boolean releaseTrigger = false;
private boolean selfNonExclusive = false;
private Object userObject = null;
private boolean addDefaultConnections = true;
private String name = null;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<ModelConnectionBlock> getConnectionBlocks() {
return connectionBlocks;
}
public void setConnectionBlocks(List<ModelConnectionBlock> connectionBlocks) {
this.connectionBlocks = connectionBlocks;
}
public List<ModelOscillator> getOscillators() {
return oscillators;
}
public int getExclusiveClass() {
return exclusiveClass;
}
public void setExclusiveClass(int exclusiveClass) {
this.exclusiveClass = exclusiveClass;
}
public boolean isSelfNonExclusive() {
return selfNonExclusive;
}
public void setSelfNonExclusive(boolean selfNonExclusive) {
this.selfNonExclusive = selfNonExclusive;
}
public int getKeyFrom() {
return keyFrom;
}
public void setKeyFrom(int keyFrom) {
this.keyFrom = keyFrom;
}
public int getKeyTo() {
return keyTo;
}
public void setKeyTo(int keyTo) {
this.keyTo = keyTo;
}
public int getVelFrom() {
return velFrom;
}
public void setVelFrom(int velFrom) {
this.velFrom = velFrom;
}
public int getVelTo() {
return velTo;
}
public void setVelTo(int velTo) {
this.velTo = velTo;
}
public boolean isReleaseTriggered() {
return releaseTrigger;
}
public void setReleaseTriggered(boolean value) {
this.releaseTrigger = value;
}
public Object getUserObject() {
return userObject;
}
public void setUserObject(Object object) {
userObject = object;
}
public boolean isDefaultConnectionsEnabled() {
return addDefaultConnections;
}
public void setDefaultConnectionsEnabled(boolean addDefaultConnections) {
this.addDefaultConnections = addDefaultConnections;
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
/**
* This class is used to identify sources in connection blocks,
* see ModelConnectionBlock.
*
* @author Karl Helgason
*/
public final class ModelSource {
public static final ModelIdentifier SOURCE_NONE = null;
public static final ModelIdentifier SOURCE_NOTEON_KEYNUMBER =
new ModelIdentifier("noteon", "keynumber"); // midi keynumber
public static final ModelIdentifier SOURCE_NOTEON_VELOCITY =
new ModelIdentifier("noteon", "velocity"); // midi velocity
public static final ModelIdentifier SOURCE_EG1 =
new ModelIdentifier("eg", null, 0);
public static final ModelIdentifier SOURCE_EG2 =
new ModelIdentifier("eg", null, 1);
public static final ModelIdentifier SOURCE_LFO1 =
new ModelIdentifier("lfo", null, 0);
public static final ModelIdentifier SOURCE_LFO2 =
new ModelIdentifier("lfo", null, 1);
public static final ModelIdentifier SOURCE_MIDI_PITCH =
new ModelIdentifier("midi", "pitch", 0); // (0..16383)
public static final ModelIdentifier SOURCE_MIDI_CHANNEL_PRESSURE =
new ModelIdentifier("midi", "channel_pressure", 0); // (0..127)
// public static final ModelIdentifier SOURCE_MIDI_MONO_PRESSURE =
// new ModelIdentifier("midi","mono_pressure",0); // (0..127)
public static final ModelIdentifier SOURCE_MIDI_POLY_PRESSURE =
new ModelIdentifier("midi", "poly_pressure", 0); // (0..127)
public static final ModelIdentifier SOURCE_MIDI_CC_0 =
new ModelIdentifier("midi_cc", "0", 0); // (0..127)
public static final ModelIdentifier SOURCE_MIDI_RPN_0 =
new ModelIdentifier("midi_rpn", "0", 0); // (0..16383)
private ModelIdentifier source = SOURCE_NONE;
private ModelTransform transform;
public ModelSource() {
this.transform = new ModelStandardTransform();
}
public ModelSource(ModelIdentifier id) {
source = id;
this.transform = new ModelStandardTransform();
}
public ModelSource(ModelIdentifier id, boolean direction) {
source = id;
this.transform = new ModelStandardTransform(direction);
}
public ModelSource(ModelIdentifier id, boolean direction, boolean polarity) {
source = id;
this.transform = new ModelStandardTransform(direction, polarity);
}
public ModelSource(ModelIdentifier id, boolean direction, boolean polarity,
int transform) {
source = id;
this.transform =
new ModelStandardTransform(direction, polarity, transform);
}
public ModelSource(ModelIdentifier id, ModelTransform transform) {
source = id;
this.transform = transform;
}
public ModelIdentifier getIdentifier() {
return source;
}
public void setIdentifier(ModelIdentifier source) {
this.source = source;
}
public ModelTransform getTransform() {
return transform;
}
public void setTransform(ModelTransform transform) {
this.transform = transform;
}
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.util.Arrays;
/**
* A standard director who chooses performers
* by there keyfrom,keyto,velfrom,velto properties.
*
* @author Karl Helgason
*/
public final class ModelStandardDirector implements ModelDirector {
private final ModelPerformer[] performers;
private final ModelDirectedPlayer player;
private boolean noteOnUsed = false;
private boolean noteOffUsed = false;
public ModelStandardDirector(final ModelPerformer[] performers,
final ModelDirectedPlayer player) {
this.performers = Arrays.copyOf(performers, performers.length);
this.player = player;
for (final ModelPerformer p : this.performers) {
if (p.isReleaseTriggered()) {
noteOffUsed = true;
} else {
noteOnUsed = true;
}
}
}
public void close() {
}
public void noteOff(int noteNumber, int velocity) {
if (!noteOffUsed)
return;
for (int i = 0; i < performers.length; i++) {
ModelPerformer p = performers[i];
if (p.getKeyFrom() <= noteNumber && p.getKeyTo() >= noteNumber) {
if (p.getVelFrom() <= velocity && p.getVelTo() >= velocity) {
if (p.isReleaseTriggered()) {
player.play(i, null);
}
}
}
}
}
public void noteOn(int noteNumber, int velocity) {
if (!noteOnUsed)
return;
for (int i = 0; i < performers.length; i++) {
ModelPerformer p = performers[i];
if (p.getKeyFrom() <= noteNumber && p.getKeyTo() >= noteNumber) {
if (p.getVelFrom() <= velocity && p.getVelTo() >= velocity) {
if (!p.isReleaseTriggered()) {
player.play(i, null);
}
}
}
}
}
}

View File

@@ -0,0 +1,187 @@
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.util.Arrays;
/**
* A standard indexed director who chooses performers
* by there keyfrom,keyto,velfrom,velto properties.
*
* @author Karl Helgason
*/
public final class ModelStandardIndexedDirector implements ModelDirector {
private final ModelPerformer[] performers;
private final ModelDirectedPlayer player;
private boolean noteOnUsed = false;
private boolean noteOffUsed = false;
// Variables needed for index
private byte[][] trantables;
private int[] counters;
private int[][] mat;
public ModelStandardIndexedDirector(final ModelPerformer[] performers,
final ModelDirectedPlayer player) {
this.performers = Arrays.copyOf(performers, performers.length);
this.player = player;
for (final ModelPerformer p : this.performers) {
if (p.isReleaseTriggered()) {
noteOffUsed = true;
} else {
noteOnUsed = true;
}
}
buildindex();
}
private int[] lookupIndex(int x, int y) {
if ((x >= 0) && (x < 128) && (y >= 0) && (y < 128)) {
int xt = trantables[0][x];
int yt = trantables[1][y];
if (xt != -1 && yt != -1) {
return mat[xt + yt * counters[0]];
}
}
return null;
}
private int restrict(int value) {
if(value < 0) return 0;
if(value > 127) return 127;
return value;
}
private void buildindex() {
trantables = new byte[2][129];
counters = new int[trantables.length];
for (ModelPerformer performer : performers) {
int keyFrom = performer.getKeyFrom();
int keyTo = performer.getKeyTo();
int velFrom = performer.getVelFrom();
int velTo = performer.getVelTo();
if (keyFrom > keyTo) continue;
if (velFrom > velTo) continue;
keyFrom = restrict(keyFrom);
keyTo = restrict(keyTo);
velFrom = restrict(velFrom);
velTo = restrict(velTo);
trantables[0][keyFrom] = 1;
trantables[0][keyTo + 1] = 1;
trantables[1][velFrom] = 1;
trantables[1][velTo + 1] = 1;
}
for (int d = 0; d < trantables.length; d++) {
byte[] trantable = trantables[d];
int transize = trantable.length;
for (int i = transize - 1; i >= 0; i--) {
if (trantable[i] == 1) {
trantable[i] = -1;
break;
}
trantable[i] = -1;
}
int counter = -1;
for (int i = 0; i < transize; i++) {
if (trantable[i] != 0) {
counter++;
if (trantable[i] == -1)
break;
}
trantable[i] = (byte) counter;
}
counters[d] = counter;
}
mat = new int[counters[0] * counters[1]][];
int ix = 0;
for (ModelPerformer performer : performers) {
int keyFrom = performer.getKeyFrom();
int keyTo = performer.getKeyTo();
int velFrom = performer.getVelFrom();
int velTo = performer.getVelTo();
if (keyFrom > keyTo) continue;
if (velFrom > velTo) continue;
keyFrom = restrict(keyFrom);
keyTo = restrict(keyTo);
velFrom = restrict(velFrom);
velTo = restrict(velTo);
int x_from = trantables[0][keyFrom];
int x_to = trantables[0][keyTo + 1];
int y_from = trantables[1][velFrom];
int y_to = trantables[1][velTo + 1];
if (x_to == -1)
x_to = counters[0];
if (y_to == -1)
y_to = counters[1];
for (int y = y_from; y < y_to; y++) {
int i = x_from + y * counters[0];
for (int x = x_from; x < x_to; x++) {
int[] mprev = mat[i];
if (mprev == null) {
mat[i] = new int[] { ix };
} else {
int[] mnew = new int[mprev.length + 1];
mnew[mnew.length - 1] = ix;
for (int k = 0; k < mprev.length; k++)
mnew[k] = mprev[k];
mat[i] = mnew;
}
i++;
}
}
ix++;
}
}
public void close() {
}
public void noteOff(int noteNumber, int velocity) {
if (!noteOffUsed)
return;
int[] plist = lookupIndex(noteNumber, velocity);
if(plist == null) return;
for (int i : plist) {
ModelPerformer p = performers[i];
if (p.isReleaseTriggered()) {
player.play(i, null);
}
}
}
public void noteOn(int noteNumber, int velocity) {
if (!noteOnUsed)
return;
int[] plist = lookupIndex(noteNumber, velocity);
if(plist == null) return;
for (int i : plist) {
ModelPerformer p = performers[i];
if (!p.isReleaseTriggered()) {
player.play(i, null);
}
}
}
}

View File

@@ -0,0 +1,139 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
/**
* A standard transformer used in connection blocks.
* It expects input values to be between 0 and 1.
*
* The result of the transform is
* between 0 and 1 if polarity = unipolar and
* between -1 and 1 if polarity = bipolar.
*
* These constraints only applies to Concave, Convex and Switch transforms.
*
* @author Karl Helgason
*/
public final class ModelStandardTransform implements ModelTransform {
public static final boolean DIRECTION_MIN2MAX = false;
public static final boolean DIRECTION_MAX2MIN = true;
public static final boolean POLARITY_UNIPOLAR = false;
public static final boolean POLARITY_BIPOLAR = true;
public static final int TRANSFORM_LINEAR = 0;
// concave: output = (20*log10(127^2/value^2)) / 96
public static final int TRANSFORM_CONCAVE = 1;
// convex: same as concave except that start and end point are reversed.
public static final int TRANSFORM_CONVEX = 2;
// switch: if value > avg(max,min) then max else min
public static final int TRANSFORM_SWITCH = 3;
public static final int TRANSFORM_ABSOLUTE = 4;
private boolean direction = DIRECTION_MIN2MAX;
private boolean polarity = POLARITY_UNIPOLAR;
private int transform = TRANSFORM_LINEAR;
public ModelStandardTransform() {
}
public ModelStandardTransform(boolean direction) {
this.direction = direction;
}
public ModelStandardTransform(boolean direction, boolean polarity) {
this.direction = direction;
this.polarity = polarity;
}
public ModelStandardTransform(boolean direction, boolean polarity,
int transform) {
this.direction = direction;
this.polarity = polarity;
this.transform = transform;
}
public double transform(double value) {
double s;
double a;
if (direction == DIRECTION_MAX2MIN)
value = 1.0 - value;
if (polarity == POLARITY_BIPOLAR)
value = value * 2.0 - 1.0;
switch (transform) {
case TRANSFORM_CONCAVE:
s = Math.signum(value);
a = Math.abs(value);
a = -((5.0 / 12.0) / Math.log(10)) * Math.log(1.0 - a);
if (a < 0)
a = 0;
else if (a > 1)
a = 1;
return s * a;
case TRANSFORM_CONVEX:
s = Math.signum(value);
a = Math.abs(value);
a = 1.0 + ((5.0 / 12.0) / Math.log(10)) * Math.log(a);
if (a < 0)
a = 0;
else if (a > 1)
a = 1;
return s * a;
case TRANSFORM_SWITCH:
if (polarity == POLARITY_BIPOLAR)
return (value > 0) ? 1 : -1;
else
return (value > 0.5) ? 1 : 0;
case TRANSFORM_ABSOLUTE:
return Math.abs(value);
default:
break;
}
return value;
}
public boolean getDirection() {
return direction;
}
public void setDirection(boolean direction) {
this.direction = direction;
}
public boolean getPolarity() {
return polarity;
}
public void setPolarity(boolean polarity) {
this.polarity = polarity;
}
public int getTransform() {
return transform;
}
public void setTransform(int transform) {
this.transform = transform;
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
/**
* Model transform interface.
*
* @author Karl Helgason
*/
public interface ModelTransform {
abstract public double transform(double value);
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
/**
* This is a wavetable oscillator interface.
*
* @author Karl Helgason
*/
public interface ModelWavetable extends ModelOscillator {
public static final int LOOP_TYPE_OFF = 0;
public static final int LOOP_TYPE_FORWARD = 1;
public static final int LOOP_TYPE_RELEASE = 2;
public static final int LOOP_TYPE_PINGPONG = 4;
public static final int LOOP_TYPE_REVERSE = 8;
public AudioFloatInputStream openStream();
public float getLoopLength();
public float getLoopStart();
public int getLoopType();
public float getPitchcorrection();
}

View File

@@ -0,0 +1,589 @@
/*
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.io.IOException;
import java.util.Vector;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
/**
* Converts among signed/unsigned and little/big endianness of sampled.
*
* @author Jan Borgersen
*/
public final class PCMtoPCMCodec extends SunCodec {
private static final AudioFormat.Encoding[] inputEncodings = {
AudioFormat.Encoding.PCM_SIGNED,
AudioFormat.Encoding.PCM_UNSIGNED,
};
private static final AudioFormat.Encoding[] outputEncodings = {
AudioFormat.Encoding.PCM_SIGNED,
AudioFormat.Encoding.PCM_UNSIGNED,
};
private static final int tempBufferSize = 64;
private byte tempBuffer [] = null;
/**
* Constructs a new PCMtoPCM codec object.
*/
public PCMtoPCMCodec() {
super( inputEncodings, outputEncodings);
}
// NEW CODE
/**
*/
public AudioFormat.Encoding[] getTargetEncodings(AudioFormat sourceFormat){
if( sourceFormat.getEncoding().equals( AudioFormat.Encoding.PCM_SIGNED ) ||
sourceFormat.getEncoding().equals( AudioFormat.Encoding.PCM_UNSIGNED ) ) {
AudioFormat.Encoding encs[] = new AudioFormat.Encoding[2];
encs[0] = AudioFormat.Encoding.PCM_SIGNED;
encs[1] = AudioFormat.Encoding.PCM_UNSIGNED;
return encs;
} else {
return new AudioFormat.Encoding[0];
}
}
/**
*/
public AudioFormat[] getTargetFormats(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat){
// filter out targetEncoding from the old getOutputFormats( sourceFormat ) method
AudioFormat[] formats = getOutputFormats( sourceFormat );
Vector newFormats = new Vector();
for(int i=0; i<formats.length; i++ ) {
if( formats[i].getEncoding().equals( targetEncoding ) ) {
newFormats.addElement( formats[i] );
}
}
AudioFormat[] formatArray = new AudioFormat[newFormats.size()];
for (int i = 0; i < formatArray.length; i++) {
formatArray[i] = (AudioFormat)(newFormats.elementAt(i));
}
return formatArray;
}
/**
*/
public AudioInputStream getAudioInputStream(AudioFormat.Encoding targetEncoding, AudioInputStream sourceStream) {
if( isConversionSupported(targetEncoding, sourceStream.getFormat()) ) {
AudioFormat sourceFormat = sourceStream.getFormat();
AudioFormat targetFormat = new AudioFormat( targetEncoding,
sourceFormat.getSampleRate(),
sourceFormat.getSampleSizeInBits(),
sourceFormat.getChannels(),
sourceFormat.getFrameSize(),
sourceFormat.getFrameRate(),
sourceFormat.isBigEndian() );
return getAudioInputStream( targetFormat, sourceStream );
} else {
throw new IllegalArgumentException("Unsupported conversion: " + sourceStream.getFormat().toString() + " to " + targetEncoding.toString() );
}
}
/**
* use old code
*/
public AudioInputStream getAudioInputStream(AudioFormat targetFormat, AudioInputStream sourceStream){
return getConvertedStream( targetFormat, sourceStream );
}
// OLD CODE
/**
* Opens the codec with the specified parameters.
* @param stream stream from which data to be processed should be read
* @param outputFormat desired data format of the stream after processing
* @return stream from which processed data may be read
* @throws IllegalArgumentException if the format combination supplied is
* not supported.
*/
/* public AudioInputStream getConvertedStream(AudioFormat outputFormat, AudioInputStream stream) {*/
private AudioInputStream getConvertedStream(AudioFormat outputFormat, AudioInputStream stream) {
AudioInputStream cs = null;
AudioFormat inputFormat = stream.getFormat();
if( inputFormat.matches(outputFormat) ) {
cs = stream;
} else {
cs = (AudioInputStream) (new PCMtoPCMCodecStream(stream, outputFormat));
tempBuffer = new byte[tempBufferSize];
}
return cs;
}
/**
* Obtains the set of output formats supported by the codec
* given a particular input format.
* If no output formats are supported for this input format,
* returns an array of length 0.
* @return array of supported output formats.
*/
/* public AudioFormat[] getOutputFormats(AudioFormat inputFormat) { */
private AudioFormat[] getOutputFormats(AudioFormat inputFormat) {
Vector formats = new Vector();
AudioFormat format;
int sampleSize = inputFormat.getSampleSizeInBits();
boolean isBigEndian = inputFormat.isBigEndian();
if ( sampleSize==8 ) {
if ( AudioFormat.Encoding.PCM_SIGNED.equals(inputFormat.getEncoding()) ) {
format = new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED,
inputFormat.getSampleRate(),
inputFormat.getSampleSizeInBits(),
inputFormat.getChannels(),
inputFormat.getFrameSize(),
inputFormat.getFrameRate(),
false );
formats.addElement(format);
}
if ( AudioFormat.Encoding.PCM_UNSIGNED.equals(inputFormat.getEncoding()) ) {
format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
inputFormat.getSampleRate(),
inputFormat.getSampleSizeInBits(),
inputFormat.getChannels(),
inputFormat.getFrameSize(),
inputFormat.getFrameRate(),
false );
formats.addElement(format);
}
} else if ( sampleSize==16 ) {
if ( AudioFormat.Encoding.PCM_SIGNED.equals(inputFormat.getEncoding()) && isBigEndian ) {
format = new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED,
inputFormat.getSampleRate(),
inputFormat.getSampleSizeInBits(),
inputFormat.getChannels(),
inputFormat.getFrameSize(),
inputFormat.getFrameRate(),
true );
formats.addElement(format);
format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
inputFormat.getSampleRate(),
inputFormat.getSampleSizeInBits(),
inputFormat.getChannels(),
inputFormat.getFrameSize(),
inputFormat.getFrameRate(),
false );
formats.addElement(format);
format = new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED,
inputFormat.getSampleRate(),
inputFormat.getSampleSizeInBits(),
inputFormat.getChannels(),
inputFormat.getFrameSize(),
inputFormat.getFrameRate(),
false );
formats.addElement(format);
}
if ( AudioFormat.Encoding.PCM_UNSIGNED.equals(inputFormat.getEncoding()) && isBigEndian ) {
format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
inputFormat.getSampleRate(),
inputFormat.getSampleSizeInBits(),
inputFormat.getChannels(),
inputFormat.getFrameSize(),
inputFormat.getFrameRate(),
true );
formats.addElement(format);
format = new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED,
inputFormat.getSampleRate(),
inputFormat.getSampleSizeInBits(),
inputFormat.getChannels(),
inputFormat.getFrameSize(),
inputFormat.getFrameRate(),
false );
formats.addElement(format);
format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
inputFormat.getSampleRate(),
inputFormat.getSampleSizeInBits(),
inputFormat.getChannels(),
inputFormat.getFrameSize(),
inputFormat.getFrameRate(),
false );
formats.addElement(format);
}
if ( AudioFormat.Encoding.PCM_SIGNED.equals(inputFormat.getEncoding()) && !isBigEndian ) {
format = new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED,
inputFormat.getSampleRate(),
inputFormat.getSampleSizeInBits(),
inputFormat.getChannels(),
inputFormat.getFrameSize(),
inputFormat.getFrameRate(),
false );
formats.addElement(format);
format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
inputFormat.getSampleRate(),
inputFormat.getSampleSizeInBits(),
inputFormat.getChannels(),
inputFormat.getFrameSize(),
inputFormat.getFrameRate(),
true );
formats.addElement(format);
format = new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED,
inputFormat.getSampleRate(),
inputFormat.getSampleSizeInBits(),
inputFormat.getChannels(),
inputFormat.getFrameSize(),
inputFormat.getFrameRate(),
true );
formats.addElement(format);
}
if ( AudioFormat.Encoding.PCM_UNSIGNED.equals(inputFormat.getEncoding()) && !isBigEndian ) {
format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
inputFormat.getSampleRate(),
inputFormat.getSampleSizeInBits(),
inputFormat.getChannels(),
inputFormat.getFrameSize(),
inputFormat.getFrameRate(),
false );
formats.addElement(format);
format = new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED,
inputFormat.getSampleRate(),
inputFormat.getSampleSizeInBits(),
inputFormat.getChannels(),
inputFormat.getFrameSize(),
inputFormat.getFrameRate(),
true );
formats.addElement(format);
format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
inputFormat.getSampleRate(),
inputFormat.getSampleSizeInBits(),
inputFormat.getChannels(),
inputFormat.getFrameSize(),
inputFormat.getFrameRate(),
true );
formats.addElement(format);
}
}
AudioFormat[] formatArray;
synchronized(formats) {
formatArray = new AudioFormat[formats.size()];
for (int i = 0; i < formatArray.length; i++) {
formatArray[i] = (AudioFormat)(formats.elementAt(i));
}
}
return formatArray;
}
class PCMtoPCMCodecStream extends AudioInputStream {
private final int PCM_SWITCH_SIGNED_8BIT = 1;
private final int PCM_SWITCH_ENDIAN = 2;
private final int PCM_SWITCH_SIGNED_LE = 3;
private final int PCM_SWITCH_SIGNED_BE = 4;
private final int PCM_UNSIGNED_LE2SIGNED_BE = 5;
private final int PCM_SIGNED_LE2UNSIGNED_BE = 6;
private final int PCM_UNSIGNED_BE2SIGNED_LE = 7;
private final int PCM_SIGNED_BE2UNSIGNED_LE = 8;
private final int sampleSizeInBytes;
private int conversionType = 0;
PCMtoPCMCodecStream(AudioInputStream stream, AudioFormat outputFormat) {
super(stream, outputFormat, -1);
int sampleSizeInBits = 0;
AudioFormat.Encoding inputEncoding = null;
AudioFormat.Encoding outputEncoding = null;
boolean inputIsBigEndian;
boolean outputIsBigEndian;
AudioFormat inputFormat = stream.getFormat();
// throw an IllegalArgumentException if not ok
if ( ! (isConversionSupported(inputFormat, outputFormat)) ) {
throw new IllegalArgumentException("Unsupported conversion: " + inputFormat.toString() + " to " + outputFormat.toString());
}
inputEncoding = inputFormat.getEncoding();
outputEncoding = outputFormat.getEncoding();
inputIsBigEndian = inputFormat.isBigEndian();
outputIsBigEndian = outputFormat.isBigEndian();
sampleSizeInBits = inputFormat.getSampleSizeInBits();
sampleSizeInBytes = sampleSizeInBits/8;
// determine conversion to perform
if( sampleSizeInBits==8 ) {
if( AudioFormat.Encoding.PCM_UNSIGNED.equals(inputEncoding) &&
AudioFormat.Encoding.PCM_SIGNED.equals(outputEncoding) ) {
conversionType = PCM_SWITCH_SIGNED_8BIT;
if(Printer.debug) Printer.debug("PCMtoPCMCodecStream: conversionType = PCM_SWITCH_SIGNED_8BIT");
} else if( AudioFormat.Encoding.PCM_SIGNED.equals(inputEncoding) &&
AudioFormat.Encoding.PCM_UNSIGNED.equals(outputEncoding) ) {
conversionType = PCM_SWITCH_SIGNED_8BIT;
if(Printer.debug) Printer.debug("PCMtoPCMCodecStream: conversionType = PCM_SWITCH_SIGNED_8BIT");
}
} else {
if( inputEncoding.equals(outputEncoding) && (inputIsBigEndian != outputIsBigEndian) ) {
conversionType = PCM_SWITCH_ENDIAN;
if(Printer.debug) Printer.debug("PCMtoPCMCodecStream: conversionType = PCM_SWITCH_ENDIAN");
} else if (AudioFormat.Encoding.PCM_UNSIGNED.equals(inputEncoding) && !inputIsBigEndian &&
AudioFormat.Encoding.PCM_SIGNED.equals(outputEncoding) && outputIsBigEndian) {
conversionType = PCM_UNSIGNED_LE2SIGNED_BE;
if(Printer.debug) Printer.debug("PCMtoPCMCodecStream: conversionType = PCM_UNSIGNED_LE2SIGNED_BE");
} else if (AudioFormat.Encoding.PCM_SIGNED.equals(inputEncoding) && !inputIsBigEndian &&
AudioFormat.Encoding.PCM_UNSIGNED.equals(outputEncoding) && outputIsBigEndian) {
conversionType = PCM_SIGNED_LE2UNSIGNED_BE;
if(Printer.debug) Printer.debug("PCMtoPCMCodecStream: conversionType = PCM_SIGNED_LE2UNSIGNED_BE");
} else if (AudioFormat.Encoding.PCM_UNSIGNED.equals(inputEncoding) && inputIsBigEndian &&
AudioFormat.Encoding.PCM_SIGNED.equals(outputEncoding) && !outputIsBigEndian) {
conversionType = PCM_UNSIGNED_BE2SIGNED_LE;
if(Printer.debug) Printer.debug("PCMtoPCMCodecStream: conversionType = PCM_UNSIGNED_BE2SIGNED_LE");
} else if (AudioFormat.Encoding.PCM_SIGNED.equals(inputEncoding) && inputIsBigEndian &&
AudioFormat.Encoding.PCM_UNSIGNED.equals(outputEncoding) && !outputIsBigEndian) {
conversionType = PCM_SIGNED_BE2UNSIGNED_LE;
if(Printer.debug) Printer.debug("PCMtoPCMCodecStream: conversionType = PCM_SIGNED_BE2UNSIGNED_LE");
}
}
// set the audio stream length in frames if we know it
frameSize = inputFormat.getFrameSize();
if( frameSize == AudioSystem.NOT_SPECIFIED ) {
frameSize=1;
}
if( stream instanceof AudioInputStream ) {
frameLength = stream.getFrameLength();
} else {
frameLength = AudioSystem.NOT_SPECIFIED;
}
// set framePos to zero
framePos = 0;
}
/**
* Note that this only works for sign conversions.
* Other conversions require a read of at least 2 bytes.
*/
public int read() throws IOException {
// $$jb: do we want to implement this function?
int temp;
byte tempbyte;
if( frameSize==1 ) {
if( conversionType == PCM_SWITCH_SIGNED_8BIT ) {
temp = super.read();
if( temp < 0 ) return temp; // EOF or error
tempbyte = (byte) (temp & 0xf);
tempbyte = (tempbyte >= 0) ? (byte)(0x80 | tempbyte) : (byte)(0x7F & tempbyte);
temp = (int) tempbyte & 0xf;
return temp;
} else {
// $$jb: what to return here?
throw new IOException("cannot read a single byte if frame size > 1");
}
} else {
throw new IOException("cannot read a single byte if frame size > 1");
}
}
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
public int read(byte[] b, int off, int len) throws IOException {
int i;
// don't read fractional frames
if ( len%frameSize != 0 ) {
len -= (len%frameSize);
}
// don't read past our own set length
if( (frameLength!=AudioSystem.NOT_SPECIFIED) && ( (len/frameSize) >(frameLength-framePos)) ) {
len = (int)(frameLength-framePos) * frameSize;
}
int readCount = super.read(b, off, len);
byte tempByte;
if(readCount<0) { // EOF or error
return readCount;
}
// now do the conversions
switch( conversionType ) {
case PCM_SWITCH_SIGNED_8BIT:
switchSigned8bit(b,off,len,readCount);
break;
case PCM_SWITCH_ENDIAN:
switchEndian(b,off,len,readCount);
break;
case PCM_SWITCH_SIGNED_LE:
switchSignedLE(b,off,len,readCount);
break;
case PCM_SWITCH_SIGNED_BE:
switchSignedBE(b,off,len,readCount);
break;
case PCM_UNSIGNED_LE2SIGNED_BE:
case PCM_SIGNED_LE2UNSIGNED_BE:
switchSignedLE(b,off,len,readCount);
switchEndian(b,off,len,readCount);
break;
case PCM_UNSIGNED_BE2SIGNED_LE:
case PCM_SIGNED_BE2UNSIGNED_LE:
switchSignedBE(b,off,len,readCount);
switchEndian(b,off,len,readCount);
break;
default:
// do nothing
}
// we've done the conversion, just return the readCount
return readCount;
}
private void switchSigned8bit(byte[] b, int off, int len, int readCount) {
for(int i=off; i < (off+readCount); i++) {
b[i] = (b[i] >= 0) ? (byte)(0x80 | b[i]) : (byte)(0x7F & b[i]);
}
}
private void switchSignedBE(byte[] b, int off, int len, int readCount) {
for(int i=off; i < (off+readCount); i+= sampleSizeInBytes ) {
b[i] = (b[i] >= 0) ? (byte)(0x80 | b[i]) : (byte)(0x7F & b[i]);
}
}
private void switchSignedLE(byte[] b, int off, int len, int readCount) {
for(int i=(off+sampleSizeInBytes-1); i < (off+readCount); i+= sampleSizeInBytes ) {
b[i] = (b[i] >= 0) ? (byte)(0x80 | b[i]) : (byte)(0x7F & b[i]);
}
}
private void switchEndian(byte[] b, int off, int len, int readCount) {
if(sampleSizeInBytes == 2) {
for(int i=off; i < (off+readCount); i += sampleSizeInBytes ) {
byte temp;
temp = b[i];
b[i] = b[i+1];
b[i+1] = temp;
}
}
}
} // end class PCMtoPCMCodecStream
} // end class PCMtoPCMCodec

View File

@@ -0,0 +1,201 @@
/*
* Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.StringTokenizer;
/**
* Audio configuration class for exposing attributes specific to the platform or system.
*
* @author Kara Kytle
* @author Florian Bomers
*/
final class Platform {
// STATIC FINAL CHARACTERISTICS
// native library we need to load
private static final String libNameMain = "jsound";
private static final String libNameALSA = "jsoundalsa";
private static final String libNameDSound = "jsoundds";
// extra libs handling: bit flags for each different library
public static final int LIB_MAIN = 1;
public static final int LIB_ALSA = 2;
public static final int LIB_DSOUND = 4;
// bit field of the constants above. Willbe set in loadLibraries
private static int loadedLibs = 0;
// features: the main native library jsound reports which feature is
// contained in which lib
public static final int FEATURE_MIDIIO = 1;
public static final int FEATURE_PORTS = 2;
public static final int FEATURE_DIRECT_AUDIO = 3;
// SYSTEM CHARACTERISTICS
// vary according to hardware architecture
// signed8 (use signed 8-bit values) is true for everything we support except for
// the solaris sbpro card.
// we'll leave it here as a variable; in the future we may need this in java.
// wait, is that true? i'm not sure. i think solaris takes unsigned data?
// $$kk: 03.11.99: i think solaris takes unsigned 8-bit or signed 16-bit data....
private static boolean signed8;
// intel is little-endian. sparc is big-endian.
private static boolean bigEndian;
static {
if(Printer.trace)Printer.trace(">> Platform.java: static");
loadLibraries();
readProperties();
}
/**
* Private constructor.
*/
private Platform() {
}
// METHODS FOR INTERNAL IMPLEMENTATION USE
/**
* Dummy method for forcing initialization.
*/
static void initialize() {
if(Printer.trace)Printer.trace("Platform: initialize()");
}
/**
* Determine whether the system is big-endian.
*/
static boolean isBigEndian() {
return bigEndian;
}
/**
* Determine whether the system takes signed 8-bit data.
*/
static boolean isSigned8() {
return signed8;
}
// PRIVATE METHODS
/**
* Load the native library or libraries.
*/
private static void loadLibraries() {
if(Printer.trace)Printer.trace(">>Platform.loadLibraries");
// load the main library
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
System.loadLibrary(libNameMain);
return null;
});
// just for the heck of it...
loadedLibs |= LIB_MAIN;
// now try to load extra libs. They are defined at compile time in the Makefile
// with the define EXTRA_SOUND_JNI_LIBS
String extraLibs = nGetExtraLibraries();
// the string is the libraries, separated by white space
StringTokenizer st = new StringTokenizer(extraLibs);
while (st.hasMoreTokens()) {
final String lib = st.nextToken();
try {
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
System.loadLibrary(lib);
return null;
});
if (lib.equals(libNameALSA)) {
loadedLibs |= LIB_ALSA;
if (Printer.debug) Printer.debug("Loaded ALSA lib successfully.");
} else if (lib.equals(libNameDSound)) {
loadedLibs |= LIB_DSOUND;
if (Printer.debug) Printer.debug("Loaded DirectSound lib successfully.");
} else {
if (Printer.err) Printer.err("Loaded unknown lib '"+lib+"' successfully.");
}
} catch (Throwable t) {
if (Printer.err) Printer.err("Couldn't load library "+lib+": "+t.toString());
}
}
}
static boolean isMidiIOEnabled() {
return isFeatureLibLoaded(FEATURE_MIDIIO);
}
static boolean isPortsEnabled() {
return isFeatureLibLoaded(FEATURE_PORTS);
}
static boolean isDirectAudioEnabled() {
return isFeatureLibLoaded(FEATURE_DIRECT_AUDIO);
}
private static boolean isFeatureLibLoaded(int feature) {
if (Printer.debug) Printer.debug("Platform: Checking for feature "+feature+"...");
int requiredLib = nGetLibraryForFeature(feature);
boolean isLoaded = (requiredLib != 0) && ((loadedLibs & requiredLib) == requiredLib);
if (Printer.debug) Printer.debug(" ...needs library "+requiredLib+". Result is loaded="+isLoaded);
return isLoaded;
}
// the following native methods are implemented in Platform.c
private native static boolean nIsBigEndian();
private native static boolean nIsSigned8();
private native static String nGetExtraLibraries();
private native static int nGetLibraryForFeature(int feature);
/**
* Read the required system properties.
*/
private static void readProperties() {
// $$fb 2002-03-06: implement check for endianness in native. Facilitates porting !
bigEndian = nIsBigEndian();
signed8 = nIsSigned8(); // Solaris on Sparc: signed, all others unsigned
}
}

View File

@@ -0,0 +1,505 @@
/*
* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.util.Vector;
import javax.sound.sampled.Control;
import javax.sound.sampled.Line;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Port;
import javax.sound.sampled.BooleanControl;
import javax.sound.sampled.CompoundControl;
import javax.sound.sampled.FloatControl;
/**
* A Mixer which only provides Ports.
*
* @author Florian Bomers
*/
final class PortMixer extends AbstractMixer {
// CONSTANTS
private static final int SRC_UNKNOWN = 0x01;
private static final int SRC_MICROPHONE = 0x02;
private static final int SRC_LINE_IN = 0x03;
private static final int SRC_COMPACT_DISC = 0x04;
private static final int SRC_MASK = 0xFF;
private static final int DST_UNKNOWN = 0x0100;
private static final int DST_SPEAKER = 0x0200;
private static final int DST_HEADPHONE = 0x0300;
private static final int DST_LINE_OUT = 0x0400;
private static final int DST_MASK = 0xFF00;
// INSTANCE VARIABLES
private Port.Info[] portInfos;
// cache of instantiated ports
private PortMixerPort[] ports;
// instance ID of the native implementation
private long id = 0;
// CONSTRUCTOR
PortMixer(PortMixerProvider.PortMixerInfo portMixerInfo) {
// pass in Line.Info, mixer, controls
super(portMixerInfo, // Mixer.Info
null, // Control[]
null, // Line.Info[] sourceLineInfo
null); // Line.Info[] targetLineInfo
if (Printer.trace) Printer.trace(">> PortMixer: constructor");
int count = 0;
int srcLineCount = 0;
int dstLineCount = 0;
try {
try {
id = nOpen(getMixerIndex());
if (id != 0) {
count = nGetPortCount(id);
if (count < 0) {
if (Printer.trace) Printer.trace("nGetPortCount() returned error code: " + count);
count = 0;
}
}
} catch (Exception e) {}
portInfos = new Port.Info[count];
for (int i = 0; i < count; i++) {
int type = nGetPortType(id, i);
srcLineCount += ((type & SRC_MASK) != 0)?1:0;
dstLineCount += ((type & DST_MASK) != 0)?1:0;
portInfos[i] = getPortInfo(i, type);
}
} finally {
if (id != 0) {
nClose(id);
}
id = 0;
}
// fill sourceLineInfo and targetLineInfos with copies of the ones in portInfos
sourceLineInfo = new Port.Info[srcLineCount];
targetLineInfo = new Port.Info[dstLineCount];
srcLineCount = 0; dstLineCount = 0;
for (int i = 0; i < count; i++) {
if (portInfos[i].isSource()) {
sourceLineInfo[srcLineCount++] = portInfos[i];
} else {
targetLineInfo[dstLineCount++] = portInfos[i];
}
}
if (Printer.trace) Printer.trace("<< PortMixer: constructor completed");
}
// ABSTRACT MIXER: ABSTRACT METHOD IMPLEMENTATIONS
public Line getLine(Line.Info info) throws LineUnavailableException {
Line.Info fullInfo = getLineInfo(info);
if ((fullInfo != null) && (fullInfo instanceof Port.Info)) {
for (int i = 0; i < portInfos.length; i++) {
if (fullInfo.equals(portInfos[i])) {
return getPort(i);
}
}
}
throw new IllegalArgumentException("Line unsupported: " + info);
}
public int getMaxLines(Line.Info info) {
Line.Info fullInfo = getLineInfo(info);
// if it's not supported at all, return 0.
if (fullInfo == null) {
return 0;
}
if (fullInfo instanceof Port.Info) {
//return AudioSystem.NOT_SPECIFIED; // if several instances of PortMixerPort
return 1;
}
return 0;
}
protected void implOpen() throws LineUnavailableException {
if (Printer.trace) Printer.trace(">> PortMixer: implOpen (id="+id+")");
// open the mixer device
id = nOpen(getMixerIndex());
if (Printer.trace) Printer.trace("<< PortMixer: implOpen succeeded.");
}
protected void implClose() {
if (Printer.trace) Printer.trace(">> PortMixer: implClose");
// close the mixer device
long thisID = id;
id = 0;
nClose(thisID);
if (ports != null) {
for (int i = 0; i < ports.length; i++) {
if (ports[i] != null) {
ports[i].disposeControls();
}
}
}
if (Printer.trace) Printer.trace("<< PortMixer: implClose succeeded");
}
protected void implStart() {}
protected void implStop() {}
// IMPLEMENTATION HELPERS
private Port.Info getPortInfo(int portIndex, int type) {
switch (type) {
case SRC_UNKNOWN: return new PortInfo(nGetPortName(getID(), portIndex), true);
case SRC_MICROPHONE: return Port.Info.MICROPHONE;
case SRC_LINE_IN: return Port.Info.LINE_IN;
case SRC_COMPACT_DISC: return Port.Info.COMPACT_DISC;
case DST_UNKNOWN: return new PortInfo(nGetPortName(getID(), portIndex), false);
case DST_SPEAKER: return Port.Info.SPEAKER;
case DST_HEADPHONE: return Port.Info.HEADPHONE;
case DST_LINE_OUT: return Port.Info.LINE_OUT;
}
// should never happen...
if (Printer.debug) Printer.debug("unknown port type: "+type);
return null;
}
int getMixerIndex() {
return ((PortMixerProvider.PortMixerInfo) getMixerInfo()).getIndex();
}
Port getPort(int index) {
if (ports == null) {
ports = new PortMixerPort[portInfos.length];
}
if (ports[index] == null) {
ports[index] = new PortMixerPort((Port.Info)portInfos[index], this, index);
return ports[index];
}
// $$fb TODO: return (Port) (ports[index].clone());
return ports[index];
}
long getID() {
return id;
}
// INNER CLASSES
/**
* Private inner class representing a Port for the PortMixer.
*/
private static final class PortMixerPort extends AbstractLine
implements Port {
private final int portIndex;
private long id;
// CONSTRUCTOR
private PortMixerPort(Port.Info info,
PortMixer mixer,
int portIndex) {
super(info, mixer, null);
if (Printer.trace) Printer.trace("PortMixerPort CONSTRUCTOR: info: " + info);
this.portIndex = portIndex;
}
// ABSTRACT METHOD IMPLEMENTATIONS
// ABSTRACT LINE
void implOpen() throws LineUnavailableException {
if (Printer.trace) Printer.trace(">> PortMixerPort: implOpen().");
long newID = ((PortMixer) mixer).getID();
if ((id == 0) || (newID != id) || (controls.length == 0)) {
id = newID;
Vector vector = new Vector();
synchronized (vector) {
nGetControls(id, portIndex, vector);
controls = new Control[vector.size()];
for (int i = 0; i < controls.length; i++) {
controls[i] = (Control) vector.elementAt(i);
}
}
} else {
enableControls(controls, true);
}
if (Printer.trace) Printer.trace("<< PortMixerPort: implOpen() succeeded");
}
private void enableControls(Control[] controls, boolean enable) {
for (int i = 0; i < controls.length; i++) {
if (controls[i] instanceof BoolCtrl) {
((BoolCtrl) controls[i]).closed = !enable;
}
else if (controls[i] instanceof FloatCtrl) {
((FloatCtrl) controls[i]).closed = !enable;
}
else if (controls[i] instanceof CompoundControl) {
enableControls(((CompoundControl) controls[i]).getMemberControls(), enable);
}
}
}
private void disposeControls() {
enableControls(controls, false);
controls = new Control[0];
}
void implClose() {
if (Printer.trace) Printer.trace(">> PortMixerPort: implClose()");
// get rid of controls
enableControls(controls, false);
if (Printer.trace) Printer.trace("<< PortMixerPort: implClose() succeeded");
}
// METHOD OVERRIDES
// this is very similar to open(AudioFormat, int) in AbstractDataLine...
public void open() throws LineUnavailableException {
synchronized (mixer) {
// if the line is not currently open, try to open it with this format and buffer size
if (!isOpen()) {
if (Printer.trace) Printer.trace("> PortMixerPort: open");
// reserve mixer resources for this line
mixer.open(this);
try {
// open the line. may throw LineUnavailableException.
implOpen();
// if we succeeded, set the open state to true and send events
setOpen(true);
} catch (LineUnavailableException e) {
// release mixer resources for this line and then throw the exception
mixer.close(this);
throw e;
}
if (Printer.trace) Printer.trace("< PortMixerPort: open succeeded");
}
}
}
// this is very similar to close() in AbstractDataLine...
public void close() {
synchronized (mixer) {
if (isOpen()) {
if (Printer.trace) Printer.trace("> PortMixerPort.close()");
// set the open state to false and send events
setOpen(false);
// close resources for this line
implClose();
// release mixer resources for this line
mixer.close(this);
if (Printer.trace) Printer.trace("< PortMixerPort.close() succeeded");
}
}
}
} // class PortMixerPort
/**
* Private inner class representing a BooleanControl for PortMixerPort
*/
private static final class BoolCtrl extends BooleanControl {
// the handle to the native control function
private final long controlID;
private boolean closed = false;
private static BooleanControl.Type createType(String name) {
if (name.equals("Mute")) {
return BooleanControl.Type.MUTE;
}
else if (name.equals("Select")) {
// $$fb add as new static type?
//return BooleanControl.Type.SELECT;
}
return new BCT(name);
}
private BoolCtrl(long controlID, String name) {
this(controlID, createType(name));
}
private BoolCtrl(long controlID, BooleanControl.Type typ) {
super(typ, false);
this.controlID = controlID;
}
public void setValue(boolean value) {
if (!closed) {
nControlSetIntValue(controlID, value?1:0);
}
}
public boolean getValue() {
if (!closed) {
// never use any cached values
return (nControlGetIntValue(controlID)!=0)?true:false;
}
// ??
return false;
}
/**
* inner class for custom types
*/
private static final class BCT extends BooleanControl.Type {
private BCT(String name) {
super(name);
}
}
}
/**
* Private inner class representing a CompoundControl for PortMixerPort
*/
private static final class CompCtrl extends CompoundControl {
private CompCtrl(String name, Control[] controls) {
super(new CCT(name), controls);
}
/**
* inner class for custom compound control types
*/
private static final class CCT extends CompoundControl.Type {
private CCT(String name) {
super(name);
}
}
}
/**
* Private inner class representing a BooleanControl for PortMixerPort
*/
private static final class FloatCtrl extends FloatControl {
// the handle to the native control function
private final long controlID;
private boolean closed = false;
// predefined float control types. See also Ports.h
private final static FloatControl.Type[] FLOAT_CONTROL_TYPES = {
null,
FloatControl.Type.BALANCE,
FloatControl.Type.MASTER_GAIN,
FloatControl.Type.PAN,
FloatControl.Type.VOLUME
};
private FloatCtrl(long controlID, String name,
float min, float max, float precision, String units) {
this(controlID, new FCT(name), min, max, precision, units);
}
private FloatCtrl(long controlID, int type,
float min, float max, float precision, String units) {
this(controlID, FLOAT_CONTROL_TYPES[type], min, max, precision, units);
}
private FloatCtrl(long controlID, FloatControl.Type typ,
float min, float max, float precision, String units) {
super(typ, min, max, precision, 1000, min, units);
this.controlID = controlID;
}
public void setValue(float value) {
if (!closed) {
nControlSetFloatValue(controlID, value);
}
}
public float getValue() {
if (!closed) {
// never use any cached values
return nControlGetFloatValue(controlID);
}
// ??
return getMinimum();
}
/**
* inner class for custom types
*/
private static final class FCT extends FloatControl.Type {
private FCT(String name) {
super(name);
}
}
}
/**
* Private inner class representing a port info
*/
private static final class PortInfo extends Port.Info {
private PortInfo(String name, boolean isSource) {
super(Port.class, name, isSource);
}
}
// open the mixer with the given index. Returns a handle ID
private static native long nOpen(int mixerIndex) throws LineUnavailableException;
private static native void nClose(long id);
// gets the number of ports for this mixer
private static native int nGetPortCount(long id);
// gets the type of the port with this index
private static native int nGetPortType(long id, int portIndex);
// gets the name of the port with this index
private static native String nGetPortName(long id, int portIndex);
// fills the vector with the controls for this port
private static native void nGetControls(long id, int portIndex, Vector vector);
// getters/setters for controls
private static native void nControlSetIntValue(long controlID, int value);
private static native int nControlGetIntValue(long controlID);
private static native void nControlSetFloatValue(long controlID, float value);
private static native float nControlGetFloatValue(long controlID);
}

View File

@@ -0,0 +1,151 @@
/*
* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.spi.MixerProvider;
/**
* Port provider.
*
* @author Florian Bomers
*/
public final class PortMixerProvider extends MixerProvider {
// STATIC VARIABLES
/**
* Set of info objects for all port input devices on the system.
*/
private static PortMixerInfo[] infos;
/**
* Set of all port input devices on the system.
*/
private static PortMixer[] devices;
// STATIC
static {
// initialize
Platform.initialize();
}
// CONSTRUCTOR
/**
* Required public no-arg constructor.
*/
public PortMixerProvider() {
synchronized (PortMixerProvider.class) {
if (Platform.isPortsEnabled()) {
init();
} else {
infos = new PortMixerInfo[0];
devices = new PortMixer[0];
}
}
}
private static void init() {
// get the number of input devices
int numDevices = nGetNumDevices();
if (infos == null || infos.length != numDevices) {
if (Printer.trace) Printer.trace("PortMixerProvider: init()");
// initialize the arrays
infos = new PortMixerInfo[numDevices];
devices = new PortMixer[numDevices];
// fill in the info objects now.
// we'll fill in the device objects as they're requested.
for (int i = 0; i < infos.length; i++) {
infos[i] = nNewPortMixerInfo(i);
}
if (Printer.trace) Printer.trace("PortMixerProvider: init(): found numDevices: " + numDevices);
}
}
public Mixer.Info[] getMixerInfo() {
synchronized (PortMixerProvider.class) {
Mixer.Info[] localArray = new Mixer.Info[infos.length];
System.arraycopy(infos, 0, localArray, 0, infos.length);
return localArray;
}
}
public Mixer getMixer(Mixer.Info info) {
synchronized (PortMixerProvider.class) {
for (int i = 0; i < infos.length; i++) {
if (infos[i].equals(info)) {
return getDevice(infos[i]);
}
}
}
throw new IllegalArgumentException("Mixer " + info.toString()
+ " not supported by this provider.");
}
private static Mixer getDevice(PortMixerInfo info) {
int index = info.getIndex();
if (devices[index] == null) {
devices[index] = new PortMixer(info);
}
return devices[index];
}
// INNER CLASSES
/**
* Info class for PortMixers. Adds an index value for
* making native references to a particular device.
* This constructor is called from native.
*/
static final class PortMixerInfo extends Mixer.Info {
private final int index;
private PortMixerInfo(int index, String name, String vendor, String description, String version) {
super("Port " + name, vendor, description, version);
this.index = index;
}
int getIndex() {
return index;
}
} // class PortMixerInfo
// NATIVE METHODS
private static native int nGetNumDevices();
private static native PortMixerInfo nNewPortMixerInfo(int mixerIndex);
}

View File

@@ -0,0 +1,127 @@
/*
* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
/**
* Printer allows you to set up global debugging status and print
* messages accordingly.
*
* @author David Rivas
* @author Kara Kytle
*/
final class Printer {
static final boolean err = false;
static final boolean debug = false;
static final boolean trace = false;
static final boolean verbose = false;
static final boolean release = false;
static final boolean SHOW_THREADID = false;
static final boolean SHOW_TIMESTAMP = false;
/*static void setErrorPrint(boolean on) {
err = on;
}
static void setDebugPrint(boolean on) {
debug = on;
}
static void setTracePrint(boolean on) {
trace = on;
}
static void setVerbosePrint(boolean on) {
verbose = on;
}
static void setReleasePrint(boolean on) {
release = on;
}*/
/**
* Suppresses default constructor, ensuring non-instantiability.
*/
private Printer() {
}
public static void err(String str) {
if (err)
println(str);
}
public static void debug(String str) {
if (debug)
println(str);
}
public static void trace(String str) {
if (trace)
println(str);
}
public static void verbose(String str) {
if (verbose)
println(str);
}
public static void release(String str) {
if (release)
println(str);
}
private static long startTime = 0;
public static void println(String s) {
String prepend = "";
if (SHOW_THREADID) {
prepend = "thread " + Thread.currentThread().getId() + " " + prepend;
}
if (SHOW_TIMESTAMP) {
if (startTime == 0) {
startTime = System.nanoTime() / 1000000l;
}
prepend = prepend + ((System.nanoTime()/1000000l) - startTime) + "millis: ";
}
System.out.println(prepend + s);
}
public static void println() {
System.out.println();
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
/**
* This exception is used when a RIFF file contains illegal or unexpected data.
*
* @author Karl Helgason
*/
public final class RIFFInvalidDataException extends InvalidDataException {
private static final long serialVersionUID = 1L;
public RIFFInvalidDataException() {
super("Invalid Data!");
}
public RIFFInvalidDataException(String s) {
super(s);
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
/**
* This exception is used when a reader is used to read RIFF file of a format it
* doesn't unterstand or support.
*
* @author Karl Helgason
*/
public final class RIFFInvalidFormatException extends InvalidFormatException {
private static final long serialVersionUID = 1L;
public RIFFInvalidFormatException() {
super("Invalid format!");
}
public RIFFInvalidFormatException(String s) {
super(s);
}
}

View File

@@ -0,0 +1,339 @@
/*
* Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
/**
* Resource Interchange File Format (RIFF) stream decoder.
*
* @author Karl Helgason
*/
public final class RIFFReader extends InputStream {
private final RIFFReader root;
private long filepointer = 0;
private final String fourcc;
private String riff_type = null;
private long ckSize = Integer.MAX_VALUE;
private InputStream stream;
private long avail = Integer.MAX_VALUE;
private RIFFReader lastiterator = null;
public RIFFReader(InputStream stream) throws IOException {
if (stream instanceof RIFFReader) {
root = ((RIFFReader) stream).root;
} else {
root = this;
}
this.stream = stream;
// Check for RIFF null paddings,
int b;
while (true) {
b = read();
if (b == -1) {
fourcc = ""; // don't put null value into fourcc,
// because it is expected to
// always contain a string value
riff_type = null;
avail = 0;
return;
}
if (b != 0)
break;
}
byte[] fourcc = new byte[4];
fourcc[0] = (byte) b;
readFully(fourcc, 1, 3);
this.fourcc = new String(fourcc, "ascii");
ckSize = readUnsignedInt();
avail = ckSize;
if (getFormat().equals("RIFF") || getFormat().equals("LIST")) {
if (avail > Integer.MAX_VALUE) {
throw new RIFFInvalidDataException("Chunk size too big");
}
byte[] format = new byte[4];
readFully(format);
this.riff_type = new String(format, "ascii");
}
}
public long getFilePointer() throws IOException {
return root.filepointer;
}
public boolean hasNextChunk() throws IOException {
if (lastiterator != null)
lastiterator.finish();
return avail != 0;
}
public RIFFReader nextChunk() throws IOException {
if (lastiterator != null)
lastiterator.finish();
if (avail == 0)
return null;
lastiterator = new RIFFReader(this);
return lastiterator;
}
public String getFormat() {
return fourcc;
}
public String getType() {
return riff_type;
}
public long getSize() {
return ckSize;
}
public int read() throws IOException {
if (avail == 0) {
return -1;
}
int b = stream.read();
if (b == -1) {
avail = 0;
return -1;
}
avail--;
filepointer++;
return b;
}
public int read(byte[] b, int offset, int len) throws IOException {
if (avail == 0) {
return -1;
}
if (len > avail) {
int rlen = stream.read(b, offset, (int)avail);
if (rlen != -1)
filepointer += rlen;
avail = 0;
return rlen;
} else {
int ret = stream.read(b, offset, len);
if (ret == -1) {
avail = 0;
return -1;
}
avail -= ret;
filepointer += ret;
return ret;
}
}
public final void readFully(byte b[]) throws IOException {
readFully(b, 0, b.length);
}
public final void readFully(byte b[], int off, int len) throws IOException {
if (len < 0)
throw new IndexOutOfBoundsException();
while (len > 0) {
int s = read(b, off, len);
if (s < 0)
throw new EOFException();
if (s == 0)
Thread.yield();
off += s;
len -= s;
}
}
@Override
public long skip(final long n) throws IOException {
if (n <= 0 || avail == 0) {
return 0;
}
// will not skip more than
long remaining = Math.min(n, avail);
while (remaining > 0) {
// Some input streams like FileInputStream can return more bytes,
// when EOF is reached.
long ret = Math.min(stream.skip(remaining), remaining);
if (ret == 0) {
// EOF or not? we need to check.
Thread.yield();
if (stream.read() == -1) {
avail = 0;
break;
}
ret = 1;
}
remaining -= ret;
avail -= ret;
filepointer += ret;
}
return n - remaining;
}
@Override
public int available() {
return (int)avail;
}
public void finish() throws IOException {
if (avail != 0) {
skip(avail);
}
}
// Read ASCII chars from stream
public String readString(final int len) throws IOException {
final byte[] buff;
try {
buff = new byte[len];
} catch (final OutOfMemoryError oom) {
throw new IOException("Length too big", oom);
}
readFully(buff);
for (int i = 0; i < buff.length; i++) {
if (buff[i] == 0) {
return new String(buff, 0, i, "ascii");
}
}
return new String(buff, "ascii");
}
// Read 8 bit signed integer from stream
public byte readByte() throws IOException {
int ch = read();
if (ch < 0)
throw new EOFException();
return (byte) ch;
}
// Read 16 bit signed integer from stream
public short readShort() throws IOException {
int ch1 = read();
int ch2 = read();
if (ch1 < 0)
throw new EOFException();
if (ch2 < 0)
throw new EOFException();
return (short)(ch1 | (ch2 << 8));
}
// Read 32 bit signed integer from stream
public int readInt() throws IOException {
int ch1 = read();
int ch2 = read();
int ch3 = read();
int ch4 = read();
if (ch1 < 0)
throw new EOFException();
if (ch2 < 0)
throw new EOFException();
if (ch3 < 0)
throw new EOFException();
if (ch4 < 0)
throw new EOFException();
return ch1 + (ch2 << 8) | (ch3 << 16) | (ch4 << 24);
}
// Read 64 bit signed integer from stream
public long readLong() throws IOException {
long ch1 = read();
long ch2 = read();
long ch3 = read();
long ch4 = read();
long ch5 = read();
long ch6 = read();
long ch7 = read();
long ch8 = read();
if (ch1 < 0)
throw new EOFException();
if (ch2 < 0)
throw new EOFException();
if (ch3 < 0)
throw new EOFException();
if (ch4 < 0)
throw new EOFException();
if (ch5 < 0)
throw new EOFException();
if (ch6 < 0)
throw new EOFException();
if (ch7 < 0)
throw new EOFException();
if (ch8 < 0)
throw new EOFException();
return ch1 | (ch2 << 8) | (ch3 << 16) | (ch4 << 24)
| (ch5 << 32) | (ch6 << 40) | (ch7 << 48) | (ch8 << 56);
}
// Read 8 bit unsigned integer from stream
public int readUnsignedByte() throws IOException {
int ch = read();
if (ch < 0)
throw new EOFException();
return ch;
}
// Read 16 bit unsigned integer from stream
public int readUnsignedShort() throws IOException {
int ch1 = read();
int ch2 = read();
if (ch1 < 0)
throw new EOFException();
if (ch2 < 0)
throw new EOFException();
return ch1 | (ch2 << 8);
}
// Read 32 bit unsigned integer from stream
public long readUnsignedInt() throws IOException {
long ch1 = read();
long ch2 = read();
long ch3 = read();
long ch4 = read();
if (ch1 < 0)
throw new EOFException();
if (ch2 < 0)
throw new EOFException();
if (ch3 < 0)
throw new EOFException();
if (ch4 < 0)
throw new EOFException();
return ch1 + (ch2 << 8) | (ch3 << 16) | (ch4 << 24);
}
@Override
public void close() throws IOException {
finish();
if (this == root)
stream.close();
stream = null;
}
}

View File

@@ -0,0 +1,365 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
/**
* Resource Interchange File Format (RIFF) stream encoder.
*
* @author Karl Helgason
*/
public final class RIFFWriter extends OutputStream {
private interface RandomAccessWriter {
public void seek(long chunksizepointer) throws IOException;
public long getPointer() throws IOException;
public void close() throws IOException;
public void write(int b) throws IOException;
public void write(byte[] b, int off, int len) throws IOException;
public void write(byte[] bytes) throws IOException;
public long length() throws IOException;
public void setLength(long i) throws IOException;
}
private static class RandomAccessFileWriter implements RandomAccessWriter {
RandomAccessFile raf;
RandomAccessFileWriter(File file) throws FileNotFoundException {
this.raf = new RandomAccessFile(file, "rw");
}
RandomAccessFileWriter(String name) throws FileNotFoundException {
this.raf = new RandomAccessFile(name, "rw");
}
public void seek(long chunksizepointer) throws IOException {
raf.seek(chunksizepointer);
}
public long getPointer() throws IOException {
return raf.getFilePointer();
}
public void close() throws IOException {
raf.close();
}
public void write(int b) throws IOException {
raf.write(b);
}
public void write(byte[] b, int off, int len) throws IOException {
raf.write(b, off, len);
}
public void write(byte[] bytes) throws IOException {
raf.write(bytes);
}
public long length() throws IOException {
return raf.length();
}
public void setLength(long i) throws IOException {
raf.setLength(i);
}
}
private static class RandomAccessByteWriter implements RandomAccessWriter {
byte[] buff = new byte[32];
int length = 0;
int pos = 0;
byte[] s;
final OutputStream stream;
RandomAccessByteWriter(OutputStream stream) {
this.stream = stream;
}
public void seek(long chunksizepointer) throws IOException {
pos = (int) chunksizepointer;
}
public long getPointer() throws IOException {
return pos;
}
public void close() throws IOException {
stream.write(buff, 0, length);
stream.close();
}
public void write(int b) throws IOException {
if (s == null)
s = new byte[1];
s[0] = (byte)b;
write(s, 0, 1);
}
public void write(byte[] b, int off, int len) throws IOException {
int newsize = pos + len;
if (newsize > length)
setLength(newsize);
int end = off + len;
for (int i = off; i < end; i++) {
buff[pos++] = b[i];
}
}
public void write(byte[] bytes) throws IOException {
write(bytes, 0, bytes.length);
}
public long length() throws IOException {
return length;
}
public void setLength(long i) throws IOException {
length = (int) i;
if (length > buff.length) {
int newlen = Math.max(buff.length << 1, length);
byte[] newbuff = new byte[newlen];
System.arraycopy(buff, 0, newbuff, 0, buff.length);
buff = newbuff;
}
}
}
private int chunktype = 0; // 0=RIFF, 1=LIST; 2=CHUNK
private RandomAccessWriter raf;
private final long chunksizepointer;
private final long startpointer;
private RIFFWriter childchunk = null;
private boolean open = true;
private boolean writeoverride = false;
public RIFFWriter(String name, String format) throws IOException {
this(new RandomAccessFileWriter(name), format, 0);
}
public RIFFWriter(File file, String format) throws IOException {
this(new RandomAccessFileWriter(file), format, 0);
}
public RIFFWriter(OutputStream stream, String format) throws IOException {
this(new RandomAccessByteWriter(stream), format, 0);
}
private RIFFWriter(RandomAccessWriter raf, String format, int chunktype)
throws IOException {
if (chunktype == 0)
if (raf.length() != 0)
raf.setLength(0);
this.raf = raf;
if (raf.getPointer() % 2 != 0)
raf.write(0);
if (chunktype == 0)
raf.write("RIFF".getBytes("ascii"));
else if (chunktype == 1)
raf.write("LIST".getBytes("ascii"));
else
raf.write((format + " ").substring(0, 4).getBytes("ascii"));
chunksizepointer = raf.getPointer();
this.chunktype = 2;
writeUnsignedInt(0);
this.chunktype = chunktype;
startpointer = raf.getPointer();
if (chunktype != 2)
raf.write((format + " ").substring(0, 4).getBytes("ascii"));
}
public void seek(long pos) throws IOException {
raf.seek(pos);
}
public long getFilePointer() throws IOException {
return raf.getPointer();
}
public void setWriteOverride(boolean writeoverride) {
this.writeoverride = writeoverride;
}
public boolean getWriteOverride() {
return writeoverride;
}
public void close() throws IOException {
if (!open)
return;
if (childchunk != null) {
childchunk.close();
childchunk = null;
}
int bakchunktype = chunktype;
long fpointer = raf.getPointer();
raf.seek(chunksizepointer);
chunktype = 2;
writeUnsignedInt(fpointer - startpointer);
if (bakchunktype == 0)
raf.close();
else
raf.seek(fpointer);
open = false;
raf = null;
}
public void write(int b) throws IOException {
if (!writeoverride) {
if (chunktype != 2) {
throw new IllegalArgumentException(
"Only chunks can write bytes!");
}
if (childchunk != null) {
childchunk.close();
childchunk = null;
}
}
raf.write(b);
}
public void write(byte b[], int off, int len) throws IOException {
if (!writeoverride) {
if (chunktype != 2) {
throw new IllegalArgumentException(
"Only chunks can write bytes!");
}
if (childchunk != null) {
childchunk.close();
childchunk = null;
}
}
raf.write(b, off, len);
}
public RIFFWriter writeList(String format) throws IOException {
if (chunktype == 2) {
throw new IllegalArgumentException(
"Only LIST and RIFF can write lists!");
}
if (childchunk != null) {
childchunk.close();
childchunk = null;
}
childchunk = new RIFFWriter(this.raf, format, 1);
return childchunk;
}
public RIFFWriter writeChunk(String format) throws IOException {
if (chunktype == 2) {
throw new IllegalArgumentException(
"Only LIST and RIFF can write chunks!");
}
if (childchunk != null) {
childchunk.close();
childchunk = null;
}
childchunk = new RIFFWriter(this.raf, format, 2);
return childchunk;
}
// Write ASCII chars to stream
public void writeString(String string) throws IOException {
byte[] buff = string.getBytes();
write(buff);
}
// Write ASCII chars to stream
public void writeString(String string, int len) throws IOException {
byte[] buff = string.getBytes();
if (buff.length > len)
write(buff, 0, len);
else {
write(buff);
for (int i = buff.length; i < len; i++)
write(0);
}
}
// Write 8 bit signed integer to stream
public void writeByte(int b) throws IOException {
write(b);
}
// Write 16 bit signed integer to stream
public void writeShort(short b) throws IOException {
write((b >>> 0) & 0xFF);
write((b >>> 8) & 0xFF);
}
// Write 32 bit signed integer to stream
public void writeInt(int b) throws IOException {
write((b >>> 0) & 0xFF);
write((b >>> 8) & 0xFF);
write((b >>> 16) & 0xFF);
write((b >>> 24) & 0xFF);
}
// Write 64 bit signed integer to stream
public void writeLong(long b) throws IOException {
write((int) (b >>> 0) & 0xFF);
write((int) (b >>> 8) & 0xFF);
write((int) (b >>> 16) & 0xFF);
write((int) (b >>> 24) & 0xFF);
write((int) (b >>> 32) & 0xFF);
write((int) (b >>> 40) & 0xFF);
write((int) (b >>> 48) & 0xFF);
write((int) (b >>> 56) & 0xFF);
}
// Write 8 bit unsigned integer to stream
public void writeUnsignedByte(int b) throws IOException {
writeByte((byte) b);
}
// Write 16 bit unsigned integer to stream
public void writeUnsignedShort(int b) throws IOException {
writeShort((short) b);
}
// Write 32 bit unsigned integer to stream
public void writeUnsignedInt(long b) throws IOException {
writeInt((int) b);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.spi.MidiDeviceProvider;
/**
* Provider for RealTimeSequencer objects.
*
* @author Florian Bomers
*/
public final class RealTimeSequencerProvider extends MidiDeviceProvider {
public MidiDevice.Info[] getDeviceInfo() {
MidiDevice.Info[] localArray = { RealTimeSequencer.info };
return localArray;
}
public MidiDevice getDevice(MidiDevice.Info info) {
if ((info != null) && (!info.equals(RealTimeSequencer.info))) {
return null;
}
try {
return new RealTimeSequencer();
} catch (MidiUnavailableException e) {
return null;
}
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;
import javax.sound.midi.Transmitter;
/** MidiDevice that can use reference counting for open/close.
* This interface is intended to be used by MidiSystem.getTransmitter() and
* MidiSystem.getReceiver().
*
* @author Matthias Pfisterer
*/
public interface ReferenceCountingDevice {
/** Retrieve a Receiver that opens the device implicitly.
* This method is similar to MidiDevice.getReceiver(). However, by calling this one,
* the device is opened implicitly. This is needed by MidiSystem.getReceiver().
*/
public Receiver getReceiverReferenceCounting() throws MidiUnavailableException;
/** Retrieve a Transmitter that opens the device implicitly.
* This method is similar to MidiDevice.getTransmitter(). However, by calling this one,
* the device is opened implicitly. This is needed by MidiSystem.getTransmitter().
*/
public Transmitter getTransmitterReferenceCounting() throws MidiUnavailableException;
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
/**
* Soundfont global region.
*
* @author Karl Helgason
*/
public final class SF2GlobalRegion extends SF2Region {
}

View File

@@ -0,0 +1,911 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sound.midi.Patch;
/**
* Soundfont instrument.
*
* @author Karl Helgason
*/
public final class SF2Instrument extends ModelInstrument {
String name = "";
int preset = 0;
int bank = 0;
long library = 0;
long genre = 0;
long morphology = 0;
SF2GlobalRegion globalregion = null;
List<SF2InstrumentRegion> regions
= new ArrayList<SF2InstrumentRegion>();
public SF2Instrument() {
super(null, null, null, null);
}
public SF2Instrument(SF2Soundbank soundbank) {
super(soundbank, null, null, null);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Patch getPatch() {
if (bank == 128)
return new ModelPatch(0, preset, true);
else
return new ModelPatch(bank << 7, preset, false);
}
public void setPatch(Patch patch) {
if (patch instanceof ModelPatch && ((ModelPatch) patch).isPercussion()) {
bank = 128;
preset = patch.getProgram();
} else {
bank = patch.getBank() >> 7;
preset = patch.getProgram();
}
}
public Object getData() {
return null;
}
public long getGenre() {
return genre;
}
public void setGenre(long genre) {
this.genre = genre;
}
public long getLibrary() {
return library;
}
public void setLibrary(long library) {
this.library = library;
}
public long getMorphology() {
return morphology;
}
public void setMorphology(long morphology) {
this.morphology = morphology;
}
public List<SF2InstrumentRegion> getRegions() {
return regions;
}
public SF2GlobalRegion getGlobalRegion() {
return globalregion;
}
public void setGlobalZone(SF2GlobalRegion zone) {
globalregion = zone;
}
public String toString() {
if (bank == 128)
return "Drumkit: " + name + " preset #" + preset;
else
return "Instrument: " + name + " bank #" + bank
+ " preset #" + preset;
}
public ModelPerformer[] getPerformers() {
int performercount = 0;
for (SF2InstrumentRegion presetzone : regions)
performercount += presetzone.getLayer().getRegions().size();
ModelPerformer[] performers = new ModelPerformer[performercount];
int pi = 0;
SF2GlobalRegion presetglobal = globalregion;
for (SF2InstrumentRegion presetzone : regions) {
Map<Integer, Short> pgenerators = new HashMap<Integer, Short>();
pgenerators.putAll(presetzone.getGenerators());
if (presetglobal != null)
pgenerators.putAll(presetglobal.getGenerators());
SF2Layer layer = presetzone.getLayer();
SF2GlobalRegion layerglobal = layer.getGlobalRegion();
for (SF2LayerRegion layerzone : layer.getRegions()) {
ModelPerformer performer = new ModelPerformer();
if (layerzone.getSample() != null)
performer.setName(layerzone.getSample().getName());
else
performer.setName(layer.getName());
performers[pi++] = performer;
int keyfrom = 0;
int keyto = 127;
int velfrom = 0;
int velto = 127;
if (layerzone.contains(SF2Region.GENERATOR_EXCLUSIVECLASS)) {
performer.setExclusiveClass(layerzone.getInteger(
SF2Region.GENERATOR_EXCLUSIVECLASS));
}
if (layerzone.contains(SF2Region.GENERATOR_KEYRANGE)) {
byte[] bytes = layerzone.getBytes(
SF2Region.GENERATOR_KEYRANGE);
if (bytes[0] >= 0)
if (bytes[0] > keyfrom)
keyfrom = bytes[0];
if (bytes[1] >= 0)
if (bytes[1] < keyto)
keyto = bytes[1];
}
if (layerzone.contains(SF2Region.GENERATOR_VELRANGE)) {
byte[] bytes = layerzone.getBytes(
SF2Region.GENERATOR_VELRANGE);
if (bytes[0] >= 0)
if (bytes[0] > velfrom)
velfrom = bytes[0];
if (bytes[1] >= 0)
if (bytes[1] < velto)
velto = bytes[1];
}
if (presetzone.contains(SF2Region.GENERATOR_KEYRANGE)) {
byte[] bytes = presetzone.getBytes(
SF2Region.GENERATOR_KEYRANGE);
if (bytes[0] > keyfrom)
keyfrom = bytes[0];
if (bytes[1] < keyto)
keyto = bytes[1];
}
if (presetzone.contains(SF2Region.GENERATOR_VELRANGE)) {
byte[] bytes = presetzone.getBytes(
SF2Region.GENERATOR_VELRANGE);
if (bytes[0] > velfrom)
velfrom = bytes[0];
if (bytes[1] < velto)
velto = bytes[1];
}
performer.setKeyFrom(keyfrom);
performer.setKeyTo(keyto);
performer.setVelFrom(velfrom);
performer.setVelTo(velto);
int startAddrsOffset = layerzone.getShort(
SF2Region.GENERATOR_STARTADDRSOFFSET);
int endAddrsOffset = layerzone.getShort(
SF2Region.GENERATOR_ENDADDRSOFFSET);
int startloopAddrsOffset = layerzone.getShort(
SF2Region.GENERATOR_STARTLOOPADDRSOFFSET);
int endloopAddrsOffset = layerzone.getShort(
SF2Region.GENERATOR_ENDLOOPADDRSOFFSET);
startAddrsOffset += layerzone.getShort(
SF2Region.GENERATOR_STARTADDRSCOARSEOFFSET) * 32768;
endAddrsOffset += layerzone.getShort(
SF2Region.GENERATOR_ENDADDRSCOARSEOFFSET) * 32768;
startloopAddrsOffset += layerzone.getShort(
SF2Region.GENERATOR_STARTLOOPADDRSCOARSEOFFSET) * 32768;
endloopAddrsOffset += layerzone.getShort(
SF2Region.GENERATOR_ENDLOOPADDRSCOARSEOFFSET) * 32768;
startloopAddrsOffset -= startAddrsOffset;
endloopAddrsOffset -= startAddrsOffset;
SF2Sample sample = layerzone.getSample();
int rootkey = sample.originalPitch;
if (layerzone.getShort(SF2Region.GENERATOR_OVERRIDINGROOTKEY) != -1) {
rootkey = layerzone.getShort(
SF2Region.GENERATOR_OVERRIDINGROOTKEY);
}
float pitchcorrection = (-rootkey * 100) + sample.pitchCorrection;
ModelByteBuffer buff = sample.getDataBuffer();
ModelByteBuffer buff24 = sample.getData24Buffer();
if (startAddrsOffset != 0 || endAddrsOffset != 0) {
buff = buff.subbuffer(startAddrsOffset * 2,
buff.capacity() + endAddrsOffset * 2);
if (buff24 != null) {
buff24 = buff24.subbuffer(startAddrsOffset,
buff24.capacity() + endAddrsOffset);
}
/*
if (startAddrsOffset < 0)
startAddrsOffset = 0;
if (endAddrsOffset > (buff.capacity()/2-startAddrsOffset))
startAddrsOffset = (int)buff.capacity()/2-startAddrsOffset;
byte[] data = buff.array();
int off = (int)buff.arrayOffset() + startAddrsOffset*2;
int len = (int)buff.capacity() + endAddrsOffset*2;
if (off+len > data.length)
len = data.length - off;
buff = new ModelByteBuffer(data, off, len);
if(buff24 != null) {
data = buff.array();
off = (int)buff.arrayOffset() + startAddrsOffset;
len = (int)buff.capacity() + endAddrsOffset;
buff24 = new ModelByteBuffer(data, off, len);
}
*/
}
ModelByteBufferWavetable osc = new ModelByteBufferWavetable(
buff, sample.getFormat(), pitchcorrection);
if (buff24 != null)
osc.set8BitExtensionBuffer(buff24);
Map<Integer, Short> generators = new HashMap<Integer, Short>();
if (layerglobal != null)
generators.putAll(layerglobal.getGenerators());
generators.putAll(layerzone.getGenerators());
for (Map.Entry<Integer, Short> gen : pgenerators.entrySet()) {
short val;
if (!generators.containsKey(gen.getKey()))
val = layerzone.getShort(gen.getKey());
else
val = generators.get(gen.getKey());
val += gen.getValue();
generators.put(gen.getKey(), val);
}
// SampleMode:
// 0 indicates a sound reproduced with no loop
// 1 indicates a sound which loops continuously
// 2 is unused but should be interpreted as indicating no loop
// 3 indicates a sound which loops for the duration of key
// depression then proceeds to play the remainder of the sample.
int sampleMode = getGeneratorValue(generators,
SF2Region.GENERATOR_SAMPLEMODES);
if ((sampleMode == 1) || (sampleMode == 3)) {
if (sample.startLoop >= 0 && sample.endLoop > 0) {
osc.setLoopStart((int)(sample.startLoop
+ startloopAddrsOffset));
osc.setLoopLength((int)(sample.endLoop - sample.startLoop
+ endloopAddrsOffset - startloopAddrsOffset));
if (sampleMode == 1)
osc.setLoopType(ModelWavetable.LOOP_TYPE_FORWARD);
if (sampleMode == 3)
osc.setLoopType(ModelWavetable.LOOP_TYPE_RELEASE);
}
}
performer.getOscillators().add(osc);
short volDelay = getGeneratorValue(generators,
SF2Region.GENERATOR_DELAYVOLENV);
short volAttack = getGeneratorValue(generators,
SF2Region.GENERATOR_ATTACKVOLENV);
short volHold = getGeneratorValue(generators,
SF2Region.GENERATOR_HOLDVOLENV);
short volDecay = getGeneratorValue(generators,
SF2Region.GENERATOR_DECAYVOLENV);
short volSustain = getGeneratorValue(generators,
SF2Region.GENERATOR_SUSTAINVOLENV);
short volRelease = getGeneratorValue(generators,
SF2Region.GENERATOR_RELEASEVOLENV);
if (volHold != -12000) {
short volKeyNumToHold = getGeneratorValue(generators,
SF2Region.GENERATOR_KEYNUMTOVOLENVHOLD);
volHold += 60 * volKeyNumToHold;
float fvalue = -volKeyNumToHold * 128;
ModelIdentifier src = ModelSource.SOURCE_NOTEON_KEYNUMBER;
ModelIdentifier dest = ModelDestination.DESTINATION_EG1_HOLD;
performer.getConnectionBlocks().add(
new ModelConnectionBlock(new ModelSource(src), fvalue,
new ModelDestination(dest)));
}
if (volDecay != -12000) {
short volKeyNumToDecay = getGeneratorValue(generators,
SF2Region.GENERATOR_KEYNUMTOVOLENVDECAY);
volDecay += 60 * volKeyNumToDecay;
float fvalue = -volKeyNumToDecay * 128;
ModelIdentifier src = ModelSource.SOURCE_NOTEON_KEYNUMBER;
ModelIdentifier dest = ModelDestination.DESTINATION_EG1_DECAY;
performer.getConnectionBlocks().add(
new ModelConnectionBlock(new ModelSource(src), fvalue,
new ModelDestination(dest)));
}
addTimecentValue(performer,
ModelDestination.DESTINATION_EG1_DELAY, volDelay);
addTimecentValue(performer,
ModelDestination.DESTINATION_EG1_ATTACK, volAttack);
addTimecentValue(performer,
ModelDestination.DESTINATION_EG1_HOLD, volHold);
addTimecentValue(performer,
ModelDestination.DESTINATION_EG1_DECAY, volDecay);
//float fvolsustain = (960-volSustain)*(1000.0f/960.0f);
volSustain = (short)(1000 - volSustain);
if (volSustain < 0)
volSustain = 0;
if (volSustain > 1000)
volSustain = 1000;
addValue(performer,
ModelDestination.DESTINATION_EG1_SUSTAIN, volSustain);
addTimecentValue(performer,
ModelDestination.DESTINATION_EG1_RELEASE, volRelease);
if (getGeneratorValue(generators,
SF2Region.GENERATOR_MODENVTOFILTERFC) != 0
|| getGeneratorValue(generators,
SF2Region.GENERATOR_MODENVTOPITCH) != 0) {
short modDelay = getGeneratorValue(generators,
SF2Region.GENERATOR_DELAYMODENV);
short modAttack = getGeneratorValue(generators,
SF2Region.GENERATOR_ATTACKMODENV);
short modHold = getGeneratorValue(generators,
SF2Region.GENERATOR_HOLDMODENV);
short modDecay = getGeneratorValue(generators,
SF2Region.GENERATOR_DECAYMODENV);
short modSustain = getGeneratorValue(generators,
SF2Region.GENERATOR_SUSTAINMODENV);
short modRelease = getGeneratorValue(generators,
SF2Region.GENERATOR_RELEASEMODENV);
if (modHold != -12000) {
short modKeyNumToHold = getGeneratorValue(generators,
SF2Region.GENERATOR_KEYNUMTOMODENVHOLD);
modHold += 60 * modKeyNumToHold;
float fvalue = -modKeyNumToHold * 128;
ModelIdentifier src = ModelSource.SOURCE_NOTEON_KEYNUMBER;
ModelIdentifier dest = ModelDestination.DESTINATION_EG2_HOLD;
performer.getConnectionBlocks().add(
new ModelConnectionBlock(new ModelSource(src),
fvalue, new ModelDestination(dest)));
}
if (modDecay != -12000) {
short modKeyNumToDecay = getGeneratorValue(generators,
SF2Region.GENERATOR_KEYNUMTOMODENVDECAY);
modDecay += 60 * modKeyNumToDecay;
float fvalue = -modKeyNumToDecay * 128;
ModelIdentifier src = ModelSource.SOURCE_NOTEON_KEYNUMBER;
ModelIdentifier dest = ModelDestination.DESTINATION_EG2_DECAY;
performer.getConnectionBlocks().add(
new ModelConnectionBlock(new ModelSource(src),
fvalue, new ModelDestination(dest)));
}
addTimecentValue(performer,
ModelDestination.DESTINATION_EG2_DELAY, modDelay);
addTimecentValue(performer,
ModelDestination.DESTINATION_EG2_ATTACK, modAttack);
addTimecentValue(performer,
ModelDestination.DESTINATION_EG2_HOLD, modHold);
addTimecentValue(performer,
ModelDestination.DESTINATION_EG2_DECAY, modDecay);
if (modSustain < 0)
modSustain = 0;
if (modSustain > 1000)
modSustain = 1000;
addValue(performer, ModelDestination.DESTINATION_EG2_SUSTAIN,
1000 - modSustain);
addTimecentValue(performer,
ModelDestination.DESTINATION_EG2_RELEASE, modRelease);
if (getGeneratorValue(generators,
SF2Region.GENERATOR_MODENVTOFILTERFC) != 0) {
double fvalue = getGeneratorValue(generators,
SF2Region.GENERATOR_MODENVTOFILTERFC);
ModelIdentifier src = ModelSource.SOURCE_EG2;
ModelIdentifier dest
= ModelDestination.DESTINATION_FILTER_FREQ;
performer.getConnectionBlocks().add(
new ModelConnectionBlock(new ModelSource(src),
fvalue, new ModelDestination(dest)));
}
if (getGeneratorValue(generators,
SF2Region.GENERATOR_MODENVTOPITCH) != 0) {
double fvalue = getGeneratorValue(generators,
SF2Region.GENERATOR_MODENVTOPITCH);
ModelIdentifier src = ModelSource.SOURCE_EG2;
ModelIdentifier dest = ModelDestination.DESTINATION_PITCH;
performer.getConnectionBlocks().add(
new ModelConnectionBlock(new ModelSource(src),
fvalue, new ModelDestination(dest)));
}
}
if (getGeneratorValue(generators,
SF2Region.GENERATOR_MODLFOTOFILTERFC) != 0
|| getGeneratorValue(generators,
SF2Region.GENERATOR_MODLFOTOPITCH) != 0
|| getGeneratorValue(generators,
SF2Region.GENERATOR_MODLFOTOVOLUME) != 0) {
short lfo_freq = getGeneratorValue(generators,
SF2Region.GENERATOR_FREQMODLFO);
short lfo_delay = getGeneratorValue(generators,
SF2Region.GENERATOR_DELAYMODLFO);
addTimecentValue(performer,
ModelDestination.DESTINATION_LFO1_DELAY, lfo_delay);
addValue(performer,
ModelDestination.DESTINATION_LFO1_FREQ, lfo_freq);
}
short vib_freq = getGeneratorValue(generators,
SF2Region.GENERATOR_FREQVIBLFO);
short vib_delay = getGeneratorValue(generators,
SF2Region.GENERATOR_DELAYVIBLFO);
addTimecentValue(performer,
ModelDestination.DESTINATION_LFO2_DELAY, vib_delay);
addValue(performer,
ModelDestination.DESTINATION_LFO2_FREQ, vib_freq);
if (getGeneratorValue(generators,
SF2Region.GENERATOR_VIBLFOTOPITCH) != 0) {
double fvalue = getGeneratorValue(generators,
SF2Region.GENERATOR_VIBLFOTOPITCH);
ModelIdentifier src = ModelSource.SOURCE_LFO2;
ModelIdentifier dest = ModelDestination.DESTINATION_PITCH;
performer.getConnectionBlocks().add(
new ModelConnectionBlock(
new ModelSource(src,
ModelStandardTransform.DIRECTION_MIN2MAX,
ModelStandardTransform.POLARITY_BIPOLAR),
fvalue, new ModelDestination(dest)));
}
if (getGeneratorValue(generators,
SF2Region.GENERATOR_MODLFOTOFILTERFC) != 0) {
double fvalue = getGeneratorValue(generators,
SF2Region.GENERATOR_MODLFOTOFILTERFC);
ModelIdentifier src = ModelSource.SOURCE_LFO1;
ModelIdentifier dest = ModelDestination.DESTINATION_FILTER_FREQ;
performer.getConnectionBlocks().add(
new ModelConnectionBlock(
new ModelSource(src,
ModelStandardTransform.DIRECTION_MIN2MAX,
ModelStandardTransform.POLARITY_BIPOLAR),
fvalue, new ModelDestination(dest)));
}
if (getGeneratorValue(generators,
SF2Region.GENERATOR_MODLFOTOPITCH) != 0) {
double fvalue = getGeneratorValue(generators,
SF2Region.GENERATOR_MODLFOTOPITCH);
ModelIdentifier src = ModelSource.SOURCE_LFO1;
ModelIdentifier dest = ModelDestination.DESTINATION_PITCH;
performer.getConnectionBlocks().add(
new ModelConnectionBlock(
new ModelSource(src,
ModelStandardTransform.DIRECTION_MIN2MAX,
ModelStandardTransform.POLARITY_BIPOLAR),
fvalue, new ModelDestination(dest)));
}
if (getGeneratorValue(generators,
SF2Region.GENERATOR_MODLFOTOVOLUME) != 0) {
double fvalue = getGeneratorValue(generators,
SF2Region.GENERATOR_MODLFOTOVOLUME);
ModelIdentifier src = ModelSource.SOURCE_LFO1;
ModelIdentifier dest = ModelDestination.DESTINATION_GAIN;
performer.getConnectionBlocks().add(
new ModelConnectionBlock(
new ModelSource(src,
ModelStandardTransform.DIRECTION_MIN2MAX,
ModelStandardTransform.POLARITY_BIPOLAR),
fvalue, new ModelDestination(dest)));
}
if (layerzone.getShort(SF2Region.GENERATOR_KEYNUM) != -1) {
double val = layerzone.getShort(SF2Region.GENERATOR_KEYNUM)/128.0;
addValue(performer, ModelDestination.DESTINATION_KEYNUMBER, val);
}
if (layerzone.getShort(SF2Region.GENERATOR_VELOCITY) != -1) {
double val = layerzone.getShort(SF2Region.GENERATOR_VELOCITY)
/ 128.0;
addValue(performer, ModelDestination.DESTINATION_VELOCITY, val);
}
if (getGeneratorValue(generators,
SF2Region.GENERATOR_INITIALFILTERFC) < 13500) {
short filter_freq = getGeneratorValue(generators,
SF2Region.GENERATOR_INITIALFILTERFC);
short filter_q = getGeneratorValue(generators,
SF2Region.GENERATOR_INITIALFILTERQ);
addValue(performer,
ModelDestination.DESTINATION_FILTER_FREQ, filter_freq);
addValue(performer,
ModelDestination.DESTINATION_FILTER_Q, filter_q);
}
int tune = 100 * getGeneratorValue(generators,
SF2Region.GENERATOR_COARSETUNE);
tune += getGeneratorValue(generators,
SF2Region.GENERATOR_FINETUNE);
if (tune != 0) {
addValue(performer,
ModelDestination.DESTINATION_PITCH, (short) tune);
}
if (getGeneratorValue(generators, SF2Region.GENERATOR_PAN) != 0) {
short val = getGeneratorValue(generators,
SF2Region.GENERATOR_PAN);
addValue(performer, ModelDestination.DESTINATION_PAN, val);
}
if (getGeneratorValue(generators, SF2Region.GENERATOR_INITIALATTENUATION) != 0) {
short val = getGeneratorValue(generators,
SF2Region.GENERATOR_INITIALATTENUATION);
addValue(performer,
ModelDestination.DESTINATION_GAIN, -0.376287f * val);
}
if (getGeneratorValue(generators,
SF2Region.GENERATOR_CHORUSEFFECTSSEND) != 0) {
short val = getGeneratorValue(generators,
SF2Region.GENERATOR_CHORUSEFFECTSSEND);
addValue(performer, ModelDestination.DESTINATION_CHORUS, val);
}
if (getGeneratorValue(generators,
SF2Region.GENERATOR_REVERBEFFECTSSEND) != 0) {
short val = getGeneratorValue(generators,
SF2Region.GENERATOR_REVERBEFFECTSSEND);
addValue(performer, ModelDestination.DESTINATION_REVERB, val);
}
if (getGeneratorValue(generators,
SF2Region.GENERATOR_SCALETUNING) != 100) {
short fvalue = getGeneratorValue(generators,
SF2Region.GENERATOR_SCALETUNING);
if (fvalue == 0) {
ModelIdentifier dest = ModelDestination.DESTINATION_PITCH;
performer.getConnectionBlocks().add(
new ModelConnectionBlock(null, rootkey * 100,
new ModelDestination(dest)));
} else {
ModelIdentifier dest = ModelDestination.DESTINATION_PITCH;
performer.getConnectionBlocks().add(
new ModelConnectionBlock(null, rootkey * (100 - fvalue),
new ModelDestination(dest)));
}
ModelIdentifier src = ModelSource.SOURCE_NOTEON_KEYNUMBER;
ModelIdentifier dest = ModelDestination.DESTINATION_PITCH;
performer.getConnectionBlocks().add(
new ModelConnectionBlock(new ModelSource(src),
128 * fvalue, new ModelDestination(dest)));
}
performer.getConnectionBlocks().add(
new ModelConnectionBlock(
new ModelSource(ModelSource.SOURCE_NOTEON_VELOCITY,
new ModelTransform() {
public double transform(double value) {
if (value < 0.5)
return 1 - value * 2;
else
return 0;
}
}),
-2400,
new ModelDestination(
ModelDestination.DESTINATION_FILTER_FREQ)));
performer.getConnectionBlocks().add(
new ModelConnectionBlock(
new ModelSource(ModelSource.SOURCE_LFO2,
ModelStandardTransform.DIRECTION_MIN2MAX,
ModelStandardTransform.POLARITY_BIPOLAR,
ModelStandardTransform.TRANSFORM_LINEAR),
new ModelSource(new ModelIdentifier("midi_cc", "1", 0),
ModelStandardTransform.DIRECTION_MIN2MAX,
ModelStandardTransform.POLARITY_UNIPOLAR,
ModelStandardTransform.TRANSFORM_LINEAR),
50, new ModelDestination(
ModelDestination.DESTINATION_PITCH)));
if (layer.getGlobalRegion() != null) {
for (SF2Modulator modulator
: layer.getGlobalRegion().getModulators()) {
convertModulator(performer, modulator);
}
}
for (SF2Modulator modulator : layerzone.getModulators())
convertModulator(performer, modulator);
if (presetglobal != null) {
for (SF2Modulator modulator : presetglobal.getModulators())
convertModulator(performer, modulator);
}
for (SF2Modulator modulator : presetzone.getModulators())
convertModulator(performer, modulator);
}
}
return performers;
}
private void convertModulator(ModelPerformer performer,
SF2Modulator modulator) {
ModelSource src1 = convertSource(modulator.getSourceOperator());
ModelSource src2 = convertSource(modulator.getAmountSourceOperator());
if (src1 == null && modulator.getSourceOperator() != 0)
return;
if (src2 == null && modulator.getAmountSourceOperator() != 0)
return;
double amount = modulator.getAmount();
double[] amountcorrection = new double[1];
ModelSource[] extrasrc = new ModelSource[1];
amountcorrection[0] = 1;
ModelDestination dst = convertDestination(
modulator.getDestinationOperator(), amountcorrection, extrasrc);
amount *= amountcorrection[0];
if (dst == null)
return;
if (modulator.getTransportOperator() == SF2Modulator.TRANSFORM_ABSOLUTE) {
((ModelStandardTransform)dst.getTransform()).setTransform(
ModelStandardTransform.TRANSFORM_ABSOLUTE);
}
ModelConnectionBlock conn = new ModelConnectionBlock(src1, src2, amount, dst);
if (extrasrc[0] != null)
conn.addSource(extrasrc[0]);
performer.getConnectionBlocks().add(conn);
}
private static ModelSource convertSource(int src) {
if (src == 0)
return null;
ModelIdentifier id = null;
int idsrc = src & 0x7F;
if ((src & SF2Modulator.SOURCE_MIDI_CONTROL) != 0) {
id = new ModelIdentifier("midi_cc", Integer.toString(idsrc));
} else {
if (idsrc == SF2Modulator.SOURCE_NOTE_ON_VELOCITY)
id = ModelSource.SOURCE_NOTEON_VELOCITY;
if (idsrc == SF2Modulator.SOURCE_NOTE_ON_KEYNUMBER)
id = ModelSource.SOURCE_NOTEON_KEYNUMBER;
if (idsrc == SF2Modulator.SOURCE_POLY_PRESSURE)
id = ModelSource.SOURCE_MIDI_POLY_PRESSURE;
if (idsrc == SF2Modulator.SOURCE_CHANNEL_PRESSURE)
id = ModelSource.SOURCE_MIDI_CHANNEL_PRESSURE;
if (idsrc == SF2Modulator.SOURCE_PITCH_WHEEL)
id = ModelSource.SOURCE_MIDI_PITCH;
if (idsrc == SF2Modulator.SOURCE_PITCH_SENSITIVITY)
id = new ModelIdentifier("midi_rpn", "0");
}
if (id == null)
return null;
ModelSource msrc = new ModelSource(id);
ModelStandardTransform transform
= (ModelStandardTransform) msrc.getTransform();
if ((SF2Modulator.SOURCE_DIRECTION_MAX_MIN & src) != 0)
transform.setDirection(ModelStandardTransform.DIRECTION_MAX2MIN);
else
transform.setDirection(ModelStandardTransform.DIRECTION_MIN2MAX);
if ((SF2Modulator.SOURCE_POLARITY_BIPOLAR & src) != 0)
transform.setPolarity(ModelStandardTransform.POLARITY_BIPOLAR);
else
transform.setPolarity(ModelStandardTransform.POLARITY_UNIPOLAR);
if ((SF2Modulator.SOURCE_TYPE_CONCAVE & src) != 0)
transform.setTransform(ModelStandardTransform.TRANSFORM_CONCAVE);
if ((SF2Modulator.SOURCE_TYPE_CONVEX & src) != 0)
transform.setTransform(ModelStandardTransform.TRANSFORM_CONVEX);
if ((SF2Modulator.SOURCE_TYPE_SWITCH & src) != 0)
transform.setTransform(ModelStandardTransform.TRANSFORM_SWITCH);
return msrc;
}
static ModelDestination convertDestination(int dst,
double[] amountcorrection, ModelSource[] extrasrc) {
ModelIdentifier id = null;
switch (dst) {
case SF2Region.GENERATOR_INITIALFILTERFC:
id = ModelDestination.DESTINATION_FILTER_FREQ;
break;
case SF2Region.GENERATOR_INITIALFILTERQ:
id = ModelDestination.DESTINATION_FILTER_Q;
break;
case SF2Region.GENERATOR_CHORUSEFFECTSSEND:
id = ModelDestination.DESTINATION_CHORUS;
break;
case SF2Region.GENERATOR_REVERBEFFECTSSEND:
id = ModelDestination.DESTINATION_REVERB;
break;
case SF2Region.GENERATOR_PAN:
id = ModelDestination.DESTINATION_PAN;
break;
case SF2Region.GENERATOR_DELAYMODLFO:
id = ModelDestination.DESTINATION_LFO1_DELAY;
break;
case SF2Region.GENERATOR_FREQMODLFO:
id = ModelDestination.DESTINATION_LFO1_FREQ;
break;
case SF2Region.GENERATOR_DELAYVIBLFO:
id = ModelDestination.DESTINATION_LFO2_DELAY;
break;
case SF2Region.GENERATOR_FREQVIBLFO:
id = ModelDestination.DESTINATION_LFO2_FREQ;
break;
case SF2Region.GENERATOR_DELAYMODENV:
id = ModelDestination.DESTINATION_EG2_DELAY;
break;
case SF2Region.GENERATOR_ATTACKMODENV:
id = ModelDestination.DESTINATION_EG2_ATTACK;
break;
case SF2Region.GENERATOR_HOLDMODENV:
id = ModelDestination.DESTINATION_EG2_HOLD;
break;
case SF2Region.GENERATOR_DECAYMODENV:
id = ModelDestination.DESTINATION_EG2_DECAY;
break;
case SF2Region.GENERATOR_SUSTAINMODENV:
id = ModelDestination.DESTINATION_EG2_SUSTAIN;
amountcorrection[0] = -1;
break;
case SF2Region.GENERATOR_RELEASEMODENV:
id = ModelDestination.DESTINATION_EG2_RELEASE;
break;
case SF2Region.GENERATOR_DELAYVOLENV:
id = ModelDestination.DESTINATION_EG1_DELAY;
break;
case SF2Region.GENERATOR_ATTACKVOLENV:
id = ModelDestination.DESTINATION_EG1_ATTACK;
break;
case SF2Region.GENERATOR_HOLDVOLENV:
id = ModelDestination.DESTINATION_EG1_HOLD;
break;
case SF2Region.GENERATOR_DECAYVOLENV:
id = ModelDestination.DESTINATION_EG1_DECAY;
break;
case SF2Region.GENERATOR_SUSTAINVOLENV:
id = ModelDestination.DESTINATION_EG1_SUSTAIN;
amountcorrection[0] = -1;
break;
case SF2Region.GENERATOR_RELEASEVOLENV:
id = ModelDestination.DESTINATION_EG1_RELEASE;
break;
case SF2Region.GENERATOR_KEYNUM:
id = ModelDestination.DESTINATION_KEYNUMBER;
break;
case SF2Region.GENERATOR_VELOCITY:
id = ModelDestination.DESTINATION_VELOCITY;
break;
case SF2Region.GENERATOR_COARSETUNE:
amountcorrection[0] = 100;
id = ModelDestination.DESTINATION_PITCH;
break;
case SF2Region.GENERATOR_FINETUNE:
id = ModelDestination.DESTINATION_PITCH;
break;
case SF2Region.GENERATOR_INITIALATTENUATION:
id = ModelDestination.DESTINATION_GAIN;
amountcorrection[0] = -0.376287f;
break;
case SF2Region.GENERATOR_VIBLFOTOPITCH:
id = ModelDestination.DESTINATION_PITCH;
extrasrc[0] = new ModelSource(
ModelSource.SOURCE_LFO2,
ModelStandardTransform.DIRECTION_MIN2MAX,
ModelStandardTransform.POLARITY_BIPOLAR);
break;
case SF2Region.GENERATOR_MODLFOTOPITCH:
id = ModelDestination.DESTINATION_PITCH;
extrasrc[0] = new ModelSource(
ModelSource.SOURCE_LFO1,
ModelStandardTransform.DIRECTION_MIN2MAX,
ModelStandardTransform.POLARITY_BIPOLAR);
break;
case SF2Region.GENERATOR_MODLFOTOFILTERFC:
id = ModelDestination.DESTINATION_FILTER_FREQ;
extrasrc[0] = new ModelSource(
ModelSource.SOURCE_LFO1,
ModelStandardTransform.DIRECTION_MIN2MAX,
ModelStandardTransform.POLARITY_BIPOLAR);
break;
case SF2Region.GENERATOR_MODLFOTOVOLUME:
id = ModelDestination.DESTINATION_GAIN;
amountcorrection[0] = -0.376287f;
extrasrc[0] = new ModelSource(
ModelSource.SOURCE_LFO1,
ModelStandardTransform.DIRECTION_MIN2MAX,
ModelStandardTransform.POLARITY_BIPOLAR);
break;
case SF2Region.GENERATOR_MODENVTOPITCH:
id = ModelDestination.DESTINATION_PITCH;
extrasrc[0] = new ModelSource(
ModelSource.SOURCE_EG2,
ModelStandardTransform.DIRECTION_MIN2MAX,
ModelStandardTransform.POLARITY_BIPOLAR);
break;
case SF2Region.GENERATOR_MODENVTOFILTERFC:
id = ModelDestination.DESTINATION_FILTER_FREQ;
extrasrc[0] = new ModelSource(
ModelSource.SOURCE_EG2,
ModelStandardTransform.DIRECTION_MIN2MAX,
ModelStandardTransform.POLARITY_BIPOLAR);
break;
default:
break;
}
if (id != null)
return new ModelDestination(id);
return null;
}
private void addTimecentValue(ModelPerformer performer,
ModelIdentifier dest, short value) {
double fvalue;
if (value == -12000)
fvalue = Double.NEGATIVE_INFINITY;
else
fvalue = value;
performer.getConnectionBlocks().add(
new ModelConnectionBlock(fvalue, new ModelDestination(dest)));
}
private void addValue(ModelPerformer performer,
ModelIdentifier dest, short value) {
double fvalue = value;
performer.getConnectionBlocks().add(
new ModelConnectionBlock(fvalue, new ModelDestination(dest)));
}
private void addValue(ModelPerformer performer,
ModelIdentifier dest, double value) {
double fvalue = value;
performer.getConnectionBlocks().add(
new ModelConnectionBlock(fvalue, new ModelDestination(dest)));
}
private short getGeneratorValue(Map<Integer, Short> generators, int gen) {
if (generators.containsKey(gen))
return generators.get(gen);
return SF2Region.getDefaultValue(gen);
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
/**
* Soundfont instrument region.
*
* @author Karl Helgason
*/
public final class SF2InstrumentRegion extends SF2Region {
SF2Layer layer;
public SF2Layer getLayer() {
return layer;
}
public void setLayer(SF2Layer layer) {
this.layer = layer;
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.util.ArrayList;
import java.util.List;
import javax.sound.midi.SoundbankResource;
/**
* Soundfont layer.
*
* @author Karl Helgason
*/
public final class SF2Layer extends SoundbankResource {
String name = "";
SF2GlobalRegion globalregion = null;
List<SF2LayerRegion> regions = new ArrayList<SF2LayerRegion>();
public SF2Layer(SF2Soundbank soundBank) {
super(soundBank, null, null);
}
public SF2Layer() {
super(null, null, null);
}
public Object getData() {
return null;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<SF2LayerRegion> getRegions() {
return regions;
}
public SF2GlobalRegion getGlobalRegion() {
return globalregion;
}
public void setGlobalZone(SF2GlobalRegion zone) {
globalregion = zone;
}
public String toString() {
return "Layer: " + name;
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
/**
* Soundfont layer region.
*
* @author Karl Helgason
*/
public final class SF2LayerRegion extends SF2Region {
SF2Sample sample;
public SF2Sample getSample() {
return sample;
}
public void setSample(SF2Sample sample) {
this.sample = sample;
}
}

View File

@@ -0,0 +1,97 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
/**
* Soundfont modulator container.
*
* @author Karl Helgason
*/
public final class SF2Modulator {
public final static int SOURCE_NONE = 0;
public final static int SOURCE_NOTE_ON_VELOCITY = 2;
public final static int SOURCE_NOTE_ON_KEYNUMBER = 3;
public final static int SOURCE_POLY_PRESSURE = 10;
public final static int SOURCE_CHANNEL_PRESSURE = 13;
public final static int SOURCE_PITCH_WHEEL = 14;
public final static int SOURCE_PITCH_SENSITIVITY = 16;
public final static int SOURCE_MIDI_CONTROL = 128 * 1;
public final static int SOURCE_DIRECTION_MIN_MAX = 256 * 0;
public final static int SOURCE_DIRECTION_MAX_MIN = 256 * 1;
public final static int SOURCE_POLARITY_UNIPOLAR = 512 * 0;
public final static int SOURCE_POLARITY_BIPOLAR = 512 * 1;
public final static int SOURCE_TYPE_LINEAR = 1024 * 0;
public final static int SOURCE_TYPE_CONCAVE = 1024 * 1;
public final static int SOURCE_TYPE_CONVEX = 1024 * 2;
public final static int SOURCE_TYPE_SWITCH = 1024 * 3;
public final static int TRANSFORM_LINEAR = 0;
public final static int TRANSFORM_ABSOLUTE = 2;
int sourceOperator;
int destinationOperator;
short amount;
int amountSourceOperator;
int transportOperator;
public short getAmount() {
return amount;
}
public void setAmount(short amount) {
this.amount = amount;
}
public int getAmountSourceOperator() {
return amountSourceOperator;
}
public void setAmountSourceOperator(int amountSourceOperator) {
this.amountSourceOperator = amountSourceOperator;
}
public int getTransportOperator() {
return transportOperator;
}
public void setTransportOperator(int transportOperator) {
this.transportOperator = transportOperator;
}
public int getDestinationOperator() {
return destinationOperator;
}
public void setDestinationOperator(int destinationOperator) {
this.destinationOperator = destinationOperator;
}
public int getSourceOperator() {
return sourceOperator;
}
public void setSourceOperator(int sourceOperator) {
this.sourceOperator = sourceOperator;
}
}

View File

@@ -0,0 +1,167 @@
/*
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Soundfont general region.
*
* @author Karl Helgason
*/
public class SF2Region {
public final static int GENERATOR_STARTADDRSOFFSET = 0;
public final static int GENERATOR_ENDADDRSOFFSET = 1;
public final static int GENERATOR_STARTLOOPADDRSOFFSET = 2;
public final static int GENERATOR_ENDLOOPADDRSOFFSET = 3;
public final static int GENERATOR_STARTADDRSCOARSEOFFSET = 4;
public final static int GENERATOR_MODLFOTOPITCH = 5;
public final static int GENERATOR_VIBLFOTOPITCH = 6;
public final static int GENERATOR_MODENVTOPITCH = 7;
public final static int GENERATOR_INITIALFILTERFC = 8;
public final static int GENERATOR_INITIALFILTERQ = 9;
public final static int GENERATOR_MODLFOTOFILTERFC = 10;
public final static int GENERATOR_MODENVTOFILTERFC = 11;
public final static int GENERATOR_ENDADDRSCOARSEOFFSET = 12;
public final static int GENERATOR_MODLFOTOVOLUME = 13;
public final static int GENERATOR_UNUSED1 = 14;
public final static int GENERATOR_CHORUSEFFECTSSEND = 15;
public final static int GENERATOR_REVERBEFFECTSSEND = 16;
public final static int GENERATOR_PAN = 17;
public final static int GENERATOR_UNUSED2 = 18;
public final static int GENERATOR_UNUSED3 = 19;
public final static int GENERATOR_UNUSED4 = 20;
public final static int GENERATOR_DELAYMODLFO = 21;
public final static int GENERATOR_FREQMODLFO = 22;
public final static int GENERATOR_DELAYVIBLFO = 23;
public final static int GENERATOR_FREQVIBLFO = 24;
public final static int GENERATOR_DELAYMODENV = 25;
public final static int GENERATOR_ATTACKMODENV = 26;
public final static int GENERATOR_HOLDMODENV = 27;
public final static int GENERATOR_DECAYMODENV = 28;
public final static int GENERATOR_SUSTAINMODENV = 29;
public final static int GENERATOR_RELEASEMODENV = 30;
public final static int GENERATOR_KEYNUMTOMODENVHOLD = 31;
public final static int GENERATOR_KEYNUMTOMODENVDECAY = 32;
public final static int GENERATOR_DELAYVOLENV = 33;
public final static int GENERATOR_ATTACKVOLENV = 34;
public final static int GENERATOR_HOLDVOLENV = 35;
public final static int GENERATOR_DECAYVOLENV = 36;
public final static int GENERATOR_SUSTAINVOLENV = 37;
public final static int GENERATOR_RELEASEVOLENV = 38;
public final static int GENERATOR_KEYNUMTOVOLENVHOLD = 39;
public final static int GENERATOR_KEYNUMTOVOLENVDECAY = 40;
public final static int GENERATOR_INSTRUMENT = 41;
public final static int GENERATOR_RESERVED1 = 42;
public final static int GENERATOR_KEYRANGE = 43;
public final static int GENERATOR_VELRANGE = 44;
public final static int GENERATOR_STARTLOOPADDRSCOARSEOFFSET = 45;
public final static int GENERATOR_KEYNUM = 46;
public final static int GENERATOR_VELOCITY = 47;
public final static int GENERATOR_INITIALATTENUATION = 48;
public final static int GENERATOR_RESERVED2 = 49;
public final static int GENERATOR_ENDLOOPADDRSCOARSEOFFSET = 50;
public final static int GENERATOR_COARSETUNE = 51;
public final static int GENERATOR_FINETUNE = 52;
public final static int GENERATOR_SAMPLEID = 53;
public final static int GENERATOR_SAMPLEMODES = 54;
public final static int GENERATOR_RESERVED3 = 55;
public final static int GENERATOR_SCALETUNING = 56;
public final static int GENERATOR_EXCLUSIVECLASS = 57;
public final static int GENERATOR_OVERRIDINGROOTKEY = 58;
public final static int GENERATOR_UNUSED5 = 59;
public final static int GENERATOR_ENDOPR = 60;
protected Map<Integer, Short> generators = new HashMap<Integer, Short>();
protected List<SF2Modulator> modulators = new ArrayList<SF2Modulator>();
public Map<Integer, Short> getGenerators() {
return generators;
}
public boolean contains(int generator) {
return generators.containsKey(generator);
}
static public short getDefaultValue(int generator) {
if (generator == 8) return (short)13500;
if (generator == 21) return (short)-12000;
if (generator == 23) return (short)-12000;
if (generator == 25) return (short)-12000;
if (generator == 26) return (short)-12000;
if (generator == 27) return (short)-12000;
if (generator == 28) return (short)-12000;
if (generator == 30) return (short)-12000;
if (generator == 33) return (short)-12000;
if (generator == 34) return (short)-12000;
if (generator == 35) return (short)-12000;
if (generator == 36) return (short)-12000;
if (generator == 38) return (short)-12000;
if (generator == 43) return (short)0x7F00;
if (generator == 44) return (short)0x7F00;
if (generator == 46) return (short)-1;
if (generator == 47) return (short)-1;
if (generator == 56) return (short)100;
if (generator == 58) return (short)-1;
return 0;
}
public short getShort(int generator) {
if (!contains(generator))
return getDefaultValue(generator);
return generators.get(generator);
}
public void putShort(int generator, short value) {
generators.put(generator, value);
}
public byte[] getBytes(int generator) {
int val = getInteger(generator);
byte[] bytes = new byte[2];
bytes[0] = (byte) (0xFF & val);
bytes[1] = (byte) ((0xFF00 & val) >> 8);
return bytes;
}
public void putBytes(int generator, byte[] bytes) {
generators.put(generator, (short) (bytes[0] + (bytes[1] << 8)));
}
public int getInteger(int generator) {
return 0xFFFF & getShort(generator);
}
public void putInteger(int generator, int value) {
generators.put(generator, (short) value);
}
public List<SF2Modulator> getModulators() {
return modulators;
}
}

View File

@@ -0,0 +1,216 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.io.InputStream;
import javax.sound.midi.Soundbank;
import javax.sound.midi.SoundbankResource;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
/**
* Soundfont sample storage.
*
* @author Karl Helgason
*/
public final class SF2Sample extends SoundbankResource {
String name = "";
long startLoop = 0;
long endLoop = 0;
long sampleRate = 44100;
int originalPitch = 60;
byte pitchCorrection = 0;
int sampleLink = 0;
int sampleType = 0;
ModelByteBuffer data;
ModelByteBuffer data24;
public SF2Sample(Soundbank soundBank) {
super(soundBank, null, AudioInputStream.class);
}
public SF2Sample() {
super(null, null, AudioInputStream.class);
}
public Object getData() {
AudioFormat format = getFormat();
/*
if (sampleFile != null) {
FileInputStream fis;
try {
fis = new FileInputStream(sampleFile);
RIFFReader riff = new RIFFReader(fis);
if (!riff.getFormat().equals("RIFF")) {
throw new RIFFInvalidDataException(
"Input stream is not a valid RIFF stream!");
}
if (!riff.getType().equals("sfbk")) {
throw new RIFFInvalidDataException(
"Input stream is not a valid SoundFont!");
}
while (riff.hasNextChunk()) {
RIFFReader chunk = riff.nextChunk();
if (chunk.getFormat().equals("LIST")) {
if (chunk.getType().equals("sdta")) {
while(chunk.hasNextChunk()) {
RIFFReader chunkchunk = chunk.nextChunk();
if(chunkchunk.getFormat().equals("smpl")) {
chunkchunk.skip(sampleOffset);
return new AudioInputStream(chunkchunk,
format, sampleLen);
}
}
}
}
}
return null;
} catch (Exception e) {
return new Throwable(e.toString());
}
}
*/
InputStream is = data.getInputStream();
if (is == null)
return null;
return new AudioInputStream(is, format, data.capacity());
}
public ModelByteBuffer getDataBuffer() {
return data;
}
public ModelByteBuffer getData24Buffer() {
return data24;
}
public AudioFormat getFormat() {
return new AudioFormat(sampleRate, 16, 1, true, false);
}
public void setData(ModelByteBuffer data) {
this.data = data;
}
public void setData(byte[] data) {
this.data = new ModelByteBuffer(data);
}
public void setData(byte[] data, int offset, int length) {
this.data = new ModelByteBuffer(data, offset, length);
}
public void setData24(ModelByteBuffer data24) {
this.data24 = data24;
}
public void setData24(byte[] data24) {
this.data24 = new ModelByteBuffer(data24);
}
public void setData24(byte[] data24, int offset, int length) {
this.data24 = new ModelByteBuffer(data24, offset, length);
}
/*
public void setData(File file, int offset, int length) {
this.data = null;
this.sampleFile = file;
this.sampleOffset = offset;
this.sampleLen = length;
}
*/
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getEndLoop() {
return endLoop;
}
public void setEndLoop(long endLoop) {
this.endLoop = endLoop;
}
public int getOriginalPitch() {
return originalPitch;
}
public void setOriginalPitch(int originalPitch) {
this.originalPitch = originalPitch;
}
public byte getPitchCorrection() {
return pitchCorrection;
}
public void setPitchCorrection(byte pitchCorrection) {
this.pitchCorrection = pitchCorrection;
}
public int getSampleLink() {
return sampleLink;
}
public void setSampleLink(int sampleLink) {
this.sampleLink = sampleLink;
}
public long getSampleRate() {
return sampleRate;
}
public void setSampleRate(long sampleRate) {
this.sampleRate = sampleRate;
}
public int getSampleType() {
return sampleType;
}
public void setSampleType(int sampleType) {
this.sampleType = sampleType;
}
public long getStartLoop() {
return startLoop;
}
public void setStartLoop(long startLoop) {
this.startLoop = startLoop;
}
public String toString() {
return "Sample: " + name;
}
}

View File

@@ -0,0 +1,989 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.sound.midi.Instrument;
import javax.sound.midi.Patch;
import javax.sound.midi.Soundbank;
import javax.sound.midi.SoundbankResource;
/**
* A SoundFont 2.04 soundbank reader.
*
* Based on SoundFont 2.04 specification from:
* <p> http://developer.creative.com <br>
* http://www.soundfont.com/ ;
*
* @author Karl Helgason
*/
public final class SF2Soundbank implements Soundbank {
// version of the Sound Font RIFF file
int major = 2;
int minor = 1;
// target Sound Engine
String targetEngine = "EMU8000";
// Sound Font Bank Name
String name = "untitled";
// Sound ROM Name
String romName = null;
// Sound ROM Version
int romVersionMajor = -1;
int romVersionMinor = -1;
// Date of Creation of the Bank
String creationDate = null;
// Sound Designers and Engineers for the Bank
String engineers = null;
// Product for which the Bank was intended
String product = null;
// Copyright message
String copyright = null;
// Comments
String comments = null;
// The SoundFont tools used to create and alter the bank
String tools = null;
// The Sample Data loaded from the SoundFont
private ModelByteBuffer sampleData = null;
private ModelByteBuffer sampleData24 = null;
private File sampleFile = null;
private boolean largeFormat = false;
private final List<SF2Instrument> instruments = new ArrayList<SF2Instrument>();
private final List<SF2Layer> layers = new ArrayList<SF2Layer>();
private final List<SF2Sample> samples = new ArrayList<SF2Sample>();
public SF2Soundbank() {
}
public SF2Soundbank(URL url) throws IOException {
InputStream is = url.openStream();
try {
readSoundbank(is);
} finally {
is.close();
}
}
public SF2Soundbank(File file) throws IOException {
largeFormat = true;
sampleFile = file;
InputStream is = new FileInputStream(file);
try {
readSoundbank(is);
} finally {
is.close();
}
}
public SF2Soundbank(InputStream inputstream) throws IOException {
readSoundbank(inputstream);
}
private void readSoundbank(InputStream inputstream) throws IOException {
RIFFReader riff = new RIFFReader(inputstream);
if (!riff.getFormat().equals("RIFF")) {
throw new RIFFInvalidFormatException(
"Input stream is not a valid RIFF stream!");
}
if (!riff.getType().equals("sfbk")) {
throw new RIFFInvalidFormatException(
"Input stream is not a valid SoundFont!");
}
while (riff.hasNextChunk()) {
RIFFReader chunk = riff.nextChunk();
if (chunk.getFormat().equals("LIST")) {
if (chunk.getType().equals("INFO"))
readInfoChunk(chunk);
if (chunk.getType().equals("sdta"))
readSdtaChunk(chunk);
if (chunk.getType().equals("pdta"))
readPdtaChunk(chunk);
}
}
}
private void readInfoChunk(RIFFReader riff) throws IOException {
while (riff.hasNextChunk()) {
RIFFReader chunk = riff.nextChunk();
String format = chunk.getFormat();
if (format.equals("ifil")) {
major = chunk.readUnsignedShort();
minor = chunk.readUnsignedShort();
} else if (format.equals("isng")) {
this.targetEngine = chunk.readString(chunk.available());
} else if (format.equals("INAM")) {
this.name = chunk.readString(chunk.available());
} else if (format.equals("irom")) {
this.romName = chunk.readString(chunk.available());
} else if (format.equals("iver")) {
romVersionMajor = chunk.readUnsignedShort();
romVersionMinor = chunk.readUnsignedShort();
} else if (format.equals("ICRD")) {
this.creationDate = chunk.readString(chunk.available());
} else if (format.equals("IENG")) {
this.engineers = chunk.readString(chunk.available());
} else if (format.equals("IPRD")) {
this.product = chunk.readString(chunk.available());
} else if (format.equals("ICOP")) {
this.copyright = chunk.readString(chunk.available());
} else if (format.equals("ICMT")) {
this.comments = chunk.readString(chunk.available());
} else if (format.equals("ISFT")) {
this.tools = chunk.readString(chunk.available());
}
}
}
private void readSdtaChunk(RIFFReader riff) throws IOException {
while (riff.hasNextChunk()) {
RIFFReader chunk = riff.nextChunk();
if (chunk.getFormat().equals("smpl")) {
if (!largeFormat) {
byte[] sampleData = new byte[chunk.available()];
int read = 0;
int avail = chunk.available();
while (read != avail) {
if (avail - read > 65536) {
chunk.readFully(sampleData, read, 65536);
read += 65536;
} else {
chunk.readFully(sampleData, read, avail - read);
read = avail;
}
}
this.sampleData = new ModelByteBuffer(sampleData);
//chunk.read(sampleData);
} else {
this.sampleData = new ModelByteBuffer(sampleFile,
chunk.getFilePointer(), chunk.available());
}
}
if (chunk.getFormat().equals("sm24")) {
if (!largeFormat) {
byte[] sampleData24 = new byte[chunk.available()];
//chunk.read(sampleData24);
int read = 0;
int avail = chunk.available();
while (read != avail) {
if (avail - read > 65536) {
chunk.readFully(sampleData24, read, 65536);
read += 65536;
} else {
chunk.readFully(sampleData24, read, avail - read);
read = avail;
}
}
this.sampleData24 = new ModelByteBuffer(sampleData24);
} else {
this.sampleData24 = new ModelByteBuffer(sampleFile,
chunk.getFilePointer(), chunk.available());
}
}
}
}
private void readPdtaChunk(RIFFReader riff) throws IOException {
List<SF2Instrument> presets = new ArrayList<SF2Instrument>();
List<Integer> presets_bagNdx = new ArrayList<Integer>();
List<SF2InstrumentRegion> presets_splits_gen
= new ArrayList<SF2InstrumentRegion>();
List<SF2InstrumentRegion> presets_splits_mod
= new ArrayList<SF2InstrumentRegion>();
List<SF2Layer> instruments = new ArrayList<SF2Layer>();
List<Integer> instruments_bagNdx = new ArrayList<Integer>();
List<SF2LayerRegion> instruments_splits_gen
= new ArrayList<SF2LayerRegion>();
List<SF2LayerRegion> instruments_splits_mod
= new ArrayList<SF2LayerRegion>();
while (riff.hasNextChunk()) {
RIFFReader chunk = riff.nextChunk();
String format = chunk.getFormat();
if (format.equals("phdr")) {
// Preset Header / Instrument
if (chunk.available() % 38 != 0)
throw new RIFFInvalidDataException();
int count = chunk.available() / 38;
for (int i = 0; i < count; i++) {
SF2Instrument preset = new SF2Instrument(this);
preset.name = chunk.readString(20);
preset.preset = chunk.readUnsignedShort();
preset.bank = chunk.readUnsignedShort();
presets_bagNdx.add(chunk.readUnsignedShort());
preset.library = chunk.readUnsignedInt();
preset.genre = chunk.readUnsignedInt();
preset.morphology = chunk.readUnsignedInt();
presets.add(preset);
if (i != count - 1)
this.instruments.add(preset);
}
} else if (format.equals("pbag")) {
// Preset Zones / Instruments splits
if (chunk.available() % 4 != 0)
throw new RIFFInvalidDataException();
int count = chunk.available() / 4;
// Skip first record
{
int gencount = chunk.readUnsignedShort();
int modcount = chunk.readUnsignedShort();
while (presets_splits_gen.size() < gencount)
presets_splits_gen.add(null);
while (presets_splits_mod.size() < modcount)
presets_splits_mod.add(null);
count--;
}
if (presets_bagNdx.isEmpty()) {
throw new RIFFInvalidDataException();
}
int offset = presets_bagNdx.get(0);
// Offset should be 0 (but just case)
for (int i = 0; i < offset; i++) {
if (count == 0)
throw new RIFFInvalidDataException();
int gencount = chunk.readUnsignedShort();
int modcount = chunk.readUnsignedShort();
while (presets_splits_gen.size() < gencount)
presets_splits_gen.add(null);
while (presets_splits_mod.size() < modcount)
presets_splits_mod.add(null);
count--;
}
for (int i = 0; i < presets_bagNdx.size() - 1; i++) {
int zone_count = presets_bagNdx.get(i + 1)
- presets_bagNdx.get(i);
SF2Instrument preset = presets.get(i);
for (int ii = 0; ii < zone_count; ii++) {
if (count == 0)
throw new RIFFInvalidDataException();
int gencount = chunk.readUnsignedShort();
int modcount = chunk.readUnsignedShort();
SF2InstrumentRegion split = new SF2InstrumentRegion();
preset.regions.add(split);
while (presets_splits_gen.size() < gencount)
presets_splits_gen.add(split);
while (presets_splits_mod.size() < modcount)
presets_splits_mod.add(split);
count--;
}
}
} else if (format.equals("pmod")) {
// Preset Modulators / Split Modulators
for (int i = 0; i < presets_splits_mod.size(); i++) {
SF2Modulator modulator = new SF2Modulator();
modulator.sourceOperator = chunk.readUnsignedShort();
modulator.destinationOperator = chunk.readUnsignedShort();
modulator.amount = chunk.readShort();
modulator.amountSourceOperator = chunk.readUnsignedShort();
modulator.transportOperator = chunk.readUnsignedShort();
SF2InstrumentRegion split = presets_splits_mod.get(i);
if (split != null)
split.modulators.add(modulator);
}
} else if (format.equals("pgen")) {
// Preset Generators / Split Generators
for (int i = 0; i < presets_splits_gen.size(); i++) {
int operator = chunk.readUnsignedShort();
short amount = chunk.readShort();
SF2InstrumentRegion split = presets_splits_gen.get(i);
if (split != null)
split.generators.put(operator, amount);
}
} else if (format.equals("inst")) {
// Instrument Header / Layers
if (chunk.available() % 22 != 0)
throw new RIFFInvalidDataException();
int count = chunk.available() / 22;
for (int i = 0; i < count; i++) {
SF2Layer layer = new SF2Layer(this);
layer.name = chunk.readString(20);
instruments_bagNdx.add(chunk.readUnsignedShort());
instruments.add(layer);
if (i != count - 1)
this.layers.add(layer);
}
} else if (format.equals("ibag")) {
// Instrument Zones / Layer splits
if (chunk.available() % 4 != 0)
throw new RIFFInvalidDataException();
int count = chunk.available() / 4;
// Skip first record
{
int gencount = chunk.readUnsignedShort();
int modcount = chunk.readUnsignedShort();
while (instruments_splits_gen.size() < gencount)
instruments_splits_gen.add(null);
while (instruments_splits_mod.size() < modcount)
instruments_splits_mod.add(null);
count--;
}
if (instruments_bagNdx.isEmpty()) {
throw new RIFFInvalidDataException();
}
int offset = instruments_bagNdx.get(0);
// Offset should be 0 (but just case)
for (int i = 0; i < offset; i++) {
if (count == 0)
throw new RIFFInvalidDataException();
int gencount = chunk.readUnsignedShort();
int modcount = chunk.readUnsignedShort();
while (instruments_splits_gen.size() < gencount)
instruments_splits_gen.add(null);
while (instruments_splits_mod.size() < modcount)
instruments_splits_mod.add(null);
count--;
}
for (int i = 0; i < instruments_bagNdx.size() - 1; i++) {
int zone_count = instruments_bagNdx.get(i + 1) - instruments_bagNdx.get(i);
SF2Layer layer = layers.get(i);
for (int ii = 0; ii < zone_count; ii++) {
if (count == 0)
throw new RIFFInvalidDataException();
int gencount = chunk.readUnsignedShort();
int modcount = chunk.readUnsignedShort();
SF2LayerRegion split = new SF2LayerRegion();
layer.regions.add(split);
while (instruments_splits_gen.size() < gencount)
instruments_splits_gen.add(split);
while (instruments_splits_mod.size() < modcount)
instruments_splits_mod.add(split);
count--;
}
}
} else if (format.equals("imod")) {
// Instrument Modulators / Split Modulators
for (int i = 0; i < instruments_splits_mod.size(); i++) {
SF2Modulator modulator = new SF2Modulator();
modulator.sourceOperator = chunk.readUnsignedShort();
modulator.destinationOperator = chunk.readUnsignedShort();
modulator.amount = chunk.readShort();
modulator.amountSourceOperator = chunk.readUnsignedShort();
modulator.transportOperator = chunk.readUnsignedShort();
if (i < 0 || i >= instruments_splits_gen.size()) {
throw new RIFFInvalidDataException();
}
SF2LayerRegion split = instruments_splits_gen.get(i);
if (split != null)
split.modulators.add(modulator);
}
} else if (format.equals("igen")) {
// Instrument Generators / Split Generators
for (int i = 0; i < instruments_splits_gen.size(); i++) {
int operator = chunk.readUnsignedShort();
short amount = chunk.readShort();
SF2LayerRegion split = instruments_splits_gen.get(i);
if (split != null)
split.generators.put(operator, amount);
}
} else if (format.equals("shdr")) {
// Sample Headers
if (chunk.available() % 46 != 0)
throw new RIFFInvalidDataException();
int count = chunk.available() / 46;
for (int i = 0; i < count; i++) {
SF2Sample sample = new SF2Sample(this);
sample.name = chunk.readString(20);
long start = chunk.readUnsignedInt();
long end = chunk.readUnsignedInt();
if (sampleData != null)
sample.data = sampleData.subbuffer(start * 2, end * 2, true);
if (sampleData24 != null)
sample.data24 = sampleData24.subbuffer(start, end, true);
/*
sample.data = new ModelByteBuffer(sampleData, (int)(start*2),
(int)((end - start)*2));
if (sampleData24 != null)
sample.data24 = new ModelByteBuffer(sampleData24,
(int)start, (int)(end - start));
*/
sample.startLoop = chunk.readUnsignedInt() - start;
sample.endLoop = chunk.readUnsignedInt() - start;
if (sample.startLoop < 0)
sample.startLoop = -1;
if (sample.endLoop < 0)
sample.endLoop = -1;
sample.sampleRate = chunk.readUnsignedInt();
sample.originalPitch = chunk.readUnsignedByte();
sample.pitchCorrection = chunk.readByte();
sample.sampleLink = chunk.readUnsignedShort();
sample.sampleType = chunk.readUnsignedShort();
if (i != count - 1)
this.samples.add(sample);
}
}
}
Iterator<SF2Layer> liter = this.layers.iterator();
while (liter.hasNext()) {
SF2Layer layer = liter.next();
Iterator<SF2LayerRegion> siter = layer.regions.iterator();
SF2Region globalsplit = null;
while (siter.hasNext()) {
SF2LayerRegion split = siter.next();
if (split.generators.get(SF2LayerRegion.GENERATOR_SAMPLEID) != null) {
int sampleid = split.generators.get(
SF2LayerRegion.GENERATOR_SAMPLEID);
split.generators.remove(SF2LayerRegion.GENERATOR_SAMPLEID);
if (sampleid < 0 || sampleid >= samples.size()) {
throw new RIFFInvalidDataException();
}
split.sample = samples.get(sampleid);
} else {
globalsplit = split;
}
}
if (globalsplit != null) {
layer.getRegions().remove(globalsplit);
SF2GlobalRegion gsplit = new SF2GlobalRegion();
gsplit.generators = globalsplit.generators;
gsplit.modulators = globalsplit.modulators;
layer.setGlobalZone(gsplit);
}
}
Iterator<SF2Instrument> iiter = this.instruments.iterator();
while (iiter.hasNext()) {
SF2Instrument instrument = iiter.next();
Iterator<SF2InstrumentRegion> siter = instrument.regions.iterator();
SF2Region globalsplit = null;
while (siter.hasNext()) {
SF2InstrumentRegion split = siter.next();
if (split.generators.get(SF2LayerRegion.GENERATOR_INSTRUMENT) != null) {
int instrumentid = split.generators.get(
SF2InstrumentRegion.GENERATOR_INSTRUMENT);
split.generators.remove(SF2LayerRegion.GENERATOR_INSTRUMENT);
if (instrumentid < 0 || instrumentid >= layers.size()) {
throw new RIFFInvalidDataException();
}
split.layer = layers.get(instrumentid);
} else {
globalsplit = split;
}
}
if (globalsplit != null) {
instrument.getRegions().remove(globalsplit);
SF2GlobalRegion gsplit = new SF2GlobalRegion();
gsplit.generators = globalsplit.generators;
gsplit.modulators = globalsplit.modulators;
instrument.setGlobalZone(gsplit);
}
}
}
public void save(String name) throws IOException {
writeSoundbank(new RIFFWriter(name, "sfbk"));
}
public void save(File file) throws IOException {
writeSoundbank(new RIFFWriter(file, "sfbk"));
}
public void save(OutputStream out) throws IOException {
writeSoundbank(new RIFFWriter(out, "sfbk"));
}
private void writeSoundbank(RIFFWriter writer) throws IOException {
writeInfo(writer.writeList("INFO"));
writeSdtaChunk(writer.writeList("sdta"));
writePdtaChunk(writer.writeList("pdta"));
writer.close();
}
private void writeInfoStringChunk(RIFFWriter writer, String name,
String value) throws IOException {
if (value == null)
return;
RIFFWriter chunk = writer.writeChunk(name);
chunk.writeString(value);
int len = value.getBytes("ascii").length;
chunk.write(0);
len++;
if (len % 2 != 0)
chunk.write(0);
}
private void writeInfo(RIFFWriter writer) throws IOException {
if (this.targetEngine == null)
this.targetEngine = "EMU8000";
if (this.name == null)
this.name = "";
RIFFWriter ifil_chunk = writer.writeChunk("ifil");
ifil_chunk.writeUnsignedShort(this.major);
ifil_chunk.writeUnsignedShort(this.minor);
writeInfoStringChunk(writer, "isng", this.targetEngine);
writeInfoStringChunk(writer, "INAM", this.name);
writeInfoStringChunk(writer, "irom", this.romName);
if (romVersionMajor != -1) {
RIFFWriter iver_chunk = writer.writeChunk("iver");
iver_chunk.writeUnsignedShort(this.romVersionMajor);
iver_chunk.writeUnsignedShort(this.romVersionMinor);
}
writeInfoStringChunk(writer, "ICRD", this.creationDate);
writeInfoStringChunk(writer, "IENG", this.engineers);
writeInfoStringChunk(writer, "IPRD", this.product);
writeInfoStringChunk(writer, "ICOP", this.copyright);
writeInfoStringChunk(writer, "ICMT", this.comments);
writeInfoStringChunk(writer, "ISFT", this.tools);
writer.close();
}
private void writeSdtaChunk(RIFFWriter writer) throws IOException {
byte[] pad = new byte[32];
RIFFWriter smpl_chunk = writer.writeChunk("smpl");
for (SF2Sample sample : samples) {
ModelByteBuffer data = sample.getDataBuffer();
data.writeTo(smpl_chunk);
/*
smpl_chunk.write(data.array(),
data.arrayOffset(),
data.capacity());
*/
smpl_chunk.write(pad);
smpl_chunk.write(pad);
}
if (major < 2)
return;
if (major == 2 && minor < 4)
return;
for (SF2Sample sample : samples) {
ModelByteBuffer data24 = sample.getData24Buffer();
if (data24 == null)
return;
}
RIFFWriter sm24_chunk = writer.writeChunk("sm24");
for (SF2Sample sample : samples) {
ModelByteBuffer data = sample.getData24Buffer();
data.writeTo(sm24_chunk);
/*
sm24_chunk.write(data.array(),
data.arrayOffset(),
data.capacity());*/
smpl_chunk.write(pad);
}
}
private void writeModulators(RIFFWriter writer, List<SF2Modulator> modulators)
throws IOException {
for (SF2Modulator modulator : modulators) {
writer.writeUnsignedShort(modulator.sourceOperator);
writer.writeUnsignedShort(modulator.destinationOperator);
writer.writeShort(modulator.amount);
writer.writeUnsignedShort(modulator.amountSourceOperator);
writer.writeUnsignedShort(modulator.transportOperator);
}
}
private void writeGenerators(RIFFWriter writer, Map<Integer, Short> generators)
throws IOException {
Short keyrange = (Short) generators.get(SF2Region.GENERATOR_KEYRANGE);
Short velrange = (Short) generators.get(SF2Region.GENERATOR_VELRANGE);
if (keyrange != null) {
writer.writeUnsignedShort(SF2Region.GENERATOR_KEYRANGE);
writer.writeShort(keyrange);
}
if (velrange != null) {
writer.writeUnsignedShort(SF2Region.GENERATOR_VELRANGE);
writer.writeShort(velrange);
}
for (Map.Entry<Integer, Short> generator : generators.entrySet()) {
if (generator.getKey() == SF2Region.GENERATOR_KEYRANGE)
continue;
if (generator.getKey() == SF2Region.GENERATOR_VELRANGE)
continue;
writer.writeUnsignedShort(generator.getKey());
writer.writeShort(generator.getValue());
}
}
private void writePdtaChunk(RIFFWriter writer) throws IOException {
RIFFWriter phdr_chunk = writer.writeChunk("phdr");
int phdr_zone_count = 0;
for (SF2Instrument preset : this.instruments) {
phdr_chunk.writeString(preset.name, 20);
phdr_chunk.writeUnsignedShort(preset.preset);
phdr_chunk.writeUnsignedShort(preset.bank);
phdr_chunk.writeUnsignedShort(phdr_zone_count);
if (preset.getGlobalRegion() != null)
phdr_zone_count += 1;
phdr_zone_count += preset.getRegions().size();
phdr_chunk.writeUnsignedInt(preset.library);
phdr_chunk.writeUnsignedInt(preset.genre);
phdr_chunk.writeUnsignedInt(preset.morphology);
}
phdr_chunk.writeString("EOP", 20);
phdr_chunk.writeUnsignedShort(0);
phdr_chunk.writeUnsignedShort(0);
phdr_chunk.writeUnsignedShort(phdr_zone_count);
phdr_chunk.writeUnsignedInt(0);
phdr_chunk.writeUnsignedInt(0);
phdr_chunk.writeUnsignedInt(0);
RIFFWriter pbag_chunk = writer.writeChunk("pbag");
int pbag_gencount = 0;
int pbag_modcount = 0;
for (SF2Instrument preset : this.instruments) {
if (preset.getGlobalRegion() != null) {
pbag_chunk.writeUnsignedShort(pbag_gencount);
pbag_chunk.writeUnsignedShort(pbag_modcount);
pbag_gencount += preset.getGlobalRegion().getGenerators().size();
pbag_modcount += preset.getGlobalRegion().getModulators().size();
}
for (SF2InstrumentRegion region : preset.getRegions()) {
pbag_chunk.writeUnsignedShort(pbag_gencount);
pbag_chunk.writeUnsignedShort(pbag_modcount);
if (layers.indexOf(region.layer) != -1) {
// One generator is used to reference to instrument record
pbag_gencount += 1;
}
pbag_gencount += region.getGenerators().size();
pbag_modcount += region.getModulators().size();
}
}
pbag_chunk.writeUnsignedShort(pbag_gencount);
pbag_chunk.writeUnsignedShort(pbag_modcount);
RIFFWriter pmod_chunk = writer.writeChunk("pmod");
for (SF2Instrument preset : this.instruments) {
if (preset.getGlobalRegion() != null) {
writeModulators(pmod_chunk,
preset.getGlobalRegion().getModulators());
}
for (SF2InstrumentRegion region : preset.getRegions())
writeModulators(pmod_chunk, region.getModulators());
}
pmod_chunk.write(new byte[10]);
RIFFWriter pgen_chunk = writer.writeChunk("pgen");
for (SF2Instrument preset : this.instruments) {
if (preset.getGlobalRegion() != null) {
writeGenerators(pgen_chunk,
preset.getGlobalRegion().getGenerators());
}
for (SF2InstrumentRegion region : preset.getRegions()) {
writeGenerators(pgen_chunk, region.getGenerators());
int ix = (int) layers.indexOf(region.layer);
if (ix != -1) {
pgen_chunk.writeUnsignedShort(SF2Region.GENERATOR_INSTRUMENT);
pgen_chunk.writeShort((short) ix);
}
}
}
pgen_chunk.write(new byte[4]);
RIFFWriter inst_chunk = writer.writeChunk("inst");
int inst_zone_count = 0;
for (SF2Layer instrument : this.layers) {
inst_chunk.writeString(instrument.name, 20);
inst_chunk.writeUnsignedShort(inst_zone_count);
if (instrument.getGlobalRegion() != null)
inst_zone_count += 1;
inst_zone_count += instrument.getRegions().size();
}
inst_chunk.writeString("EOI", 20);
inst_chunk.writeUnsignedShort(inst_zone_count);
RIFFWriter ibag_chunk = writer.writeChunk("ibag");
int ibag_gencount = 0;
int ibag_modcount = 0;
for (SF2Layer instrument : this.layers) {
if (instrument.getGlobalRegion() != null) {
ibag_chunk.writeUnsignedShort(ibag_gencount);
ibag_chunk.writeUnsignedShort(ibag_modcount);
ibag_gencount
+= instrument.getGlobalRegion().getGenerators().size();
ibag_modcount
+= instrument.getGlobalRegion().getModulators().size();
}
for (SF2LayerRegion region : instrument.getRegions()) {
ibag_chunk.writeUnsignedShort(ibag_gencount);
ibag_chunk.writeUnsignedShort(ibag_modcount);
if (samples.indexOf(region.sample) != -1) {
// One generator is used to reference to instrument record
ibag_gencount += 1;
}
ibag_gencount += region.getGenerators().size();
ibag_modcount += region.getModulators().size();
}
}
ibag_chunk.writeUnsignedShort(ibag_gencount);
ibag_chunk.writeUnsignedShort(ibag_modcount);
RIFFWriter imod_chunk = writer.writeChunk("imod");
for (SF2Layer instrument : this.layers) {
if (instrument.getGlobalRegion() != null) {
writeModulators(imod_chunk,
instrument.getGlobalRegion().getModulators());
}
for (SF2LayerRegion region : instrument.getRegions())
writeModulators(imod_chunk, region.getModulators());
}
imod_chunk.write(new byte[10]);
RIFFWriter igen_chunk = writer.writeChunk("igen");
for (SF2Layer instrument : this.layers) {
if (instrument.getGlobalRegion() != null) {
writeGenerators(igen_chunk,
instrument.getGlobalRegion().getGenerators());
}
for (SF2LayerRegion region : instrument.getRegions()) {
writeGenerators(igen_chunk, region.getGenerators());
int ix = samples.indexOf(region.sample);
if (ix != -1) {
igen_chunk.writeUnsignedShort(SF2Region.GENERATOR_SAMPLEID);
igen_chunk.writeShort((short) ix);
}
}
}
igen_chunk.write(new byte[4]);
RIFFWriter shdr_chunk = writer.writeChunk("shdr");
long sample_pos = 0;
for (SF2Sample sample : samples) {
shdr_chunk.writeString(sample.name, 20);
long start = sample_pos;
sample_pos += sample.data.capacity() / 2;
long end = sample_pos;
long startLoop = sample.startLoop + start;
long endLoop = sample.endLoop + start;
if (startLoop < start)
startLoop = start;
if (endLoop > end)
endLoop = end;
shdr_chunk.writeUnsignedInt(start);
shdr_chunk.writeUnsignedInt(end);
shdr_chunk.writeUnsignedInt(startLoop);
shdr_chunk.writeUnsignedInt(endLoop);
shdr_chunk.writeUnsignedInt(sample.sampleRate);
shdr_chunk.writeUnsignedByte(sample.originalPitch);
shdr_chunk.writeByte(sample.pitchCorrection);
shdr_chunk.writeUnsignedShort(sample.sampleLink);
shdr_chunk.writeUnsignedShort(sample.sampleType);
sample_pos += 32;
}
shdr_chunk.writeString("EOS", 20);
shdr_chunk.write(new byte[26]);
}
public String getName() {
return name;
}
public String getVersion() {
return major + "." + minor;
}
public String getVendor() {
return engineers;
}
public String getDescription() {
return comments;
}
public void setName(String s) {
name = s;
}
public void setVendor(String s) {
engineers = s;
}
public void setDescription(String s) {
comments = s;
}
public SoundbankResource[] getResources() {
SoundbankResource[] resources
= new SoundbankResource[layers.size() + samples.size()];
int j = 0;
for (int i = 0; i < layers.size(); i++)
resources[j++] = layers.get(i);
for (int i = 0; i < samples.size(); i++)
resources[j++] = samples.get(i);
return resources;
}
public SF2Instrument[] getInstruments() {
SF2Instrument[] inslist_array
= instruments.toArray(new SF2Instrument[instruments.size()]);
Arrays.sort(inslist_array, new ModelInstrumentComparator());
return inslist_array;
}
public SF2Layer[] getLayers() {
return layers.toArray(new SF2Layer[layers.size()]);
}
public SF2Sample[] getSamples() {
return samples.toArray(new SF2Sample[samples.size()]);
}
public Instrument getInstrument(Patch patch) {
int program = patch.getProgram();
int bank = patch.getBank();
boolean percussion = false;
if (patch instanceof ModelPatch)
percussion = ((ModelPatch)patch).isPercussion();
for (Instrument instrument : instruments) {
Patch patch2 = instrument.getPatch();
int program2 = patch2.getProgram();
int bank2 = patch2.getBank();
if (program == program2 && bank == bank2) {
boolean percussion2 = false;
if (patch2 instanceof ModelPatch)
percussion2 = ((ModelPatch) patch2).isPercussion();
if (percussion == percussion2)
return instrument;
}
}
return null;
}
public String getCreationDate() {
return creationDate;
}
public void setCreationDate(String creationDate) {
this.creationDate = creationDate;
}
public String getProduct() {
return product;
}
public void setProduct(String product) {
this.product = product;
}
public String getRomName() {
return romName;
}
public void setRomName(String romName) {
this.romName = romName;
}
public int getRomVersionMajor() {
return romVersionMajor;
}
public void setRomVersionMajor(int romVersionMajor) {
this.romVersionMajor = romVersionMajor;
}
public int getRomVersionMinor() {
return romVersionMinor;
}
public void setRomVersionMinor(int romVersionMinor) {
this.romVersionMinor = romVersionMinor;
}
public String getTargetEngine() {
return targetEngine;
}
public void setTargetEngine(String targetEngine) {
this.targetEngine = targetEngine;
}
public String getTools() {
return tools;
}
public void setTools(String tools) {
this.tools = tools;
}
public void addResource(SoundbankResource resource) {
if (resource instanceof SF2Instrument)
instruments.add((SF2Instrument)resource);
if (resource instanceof SF2Layer)
layers.add((SF2Layer)resource);
if (resource instanceof SF2Sample)
samples.add((SF2Sample)resource);
}
public void removeResource(SoundbankResource resource) {
if (resource instanceof SF2Instrument)
instruments.remove((SF2Instrument)resource);
if (resource instanceof SF2Layer)
layers.remove((SF2Layer)resource);
if (resource instanceof SF2Sample)
samples.remove((SF2Sample)resource);
}
public void addInstrument(SF2Instrument resource) {
instruments.add(resource);
}
public void removeInstrument(SF2Instrument resource) {
instruments.remove(resource);
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.Soundbank;
import javax.sound.midi.spi.SoundbankReader;
/**
* This class is used to connect the SF2SoundBank class
* to the SoundbankReader SPI interface.
*
* @author Karl Helgason
*/
public final class SF2SoundbankReader extends SoundbankReader {
public Soundbank getSoundbank(URL url)
throws InvalidMidiDataException, IOException {
try {
return new SF2Soundbank(url);
} catch (RIFFInvalidFormatException e) {
return null;
} catch(IOException ioe) {
return null;
}
}
public Soundbank getSoundbank(InputStream stream)
throws InvalidMidiDataException, IOException {
try {
stream.mark(512);
return new SF2Soundbank(stream);
} catch (RIFFInvalidFormatException e) {
stream.reset();
return null;
}
}
public Soundbank getSoundbank(File file)
throws InvalidMidiDataException, IOException {
try {
return new SF2Soundbank(file);
} catch (RIFFInvalidFormatException e) {
return null;
}
}
}

View File

@@ -0,0 +1,196 @@
/*
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.util.ArrayList;
import java.util.List;
import javax.sound.midi.Patch;
/**
* A simple instrument that is made of other ModelInstrument, ModelPerformer
* objects.
*
* @author Karl Helgason
*/
public class SimpleInstrument extends ModelInstrument {
private static class SimpleInstrumentPart {
ModelPerformer[] performers;
int keyFrom;
int keyTo;
int velFrom;
int velTo;
int exclusiveClass;
}
protected int preset = 0;
protected int bank = 0;
protected boolean percussion = false;
protected String name = "";
protected List<SimpleInstrumentPart> parts
= new ArrayList<SimpleInstrumentPart>();
public SimpleInstrument() {
super(null, null, null, null);
}
public void clear() {
parts.clear();
}
public void add(ModelPerformer[] performers, int keyFrom, int keyTo,
int velFrom, int velTo, int exclusiveClass) {
SimpleInstrumentPart part = new SimpleInstrumentPart();
part.performers = performers;
part.keyFrom = keyFrom;
part.keyTo = keyTo;
part.velFrom = velFrom;
part.velTo = velTo;
part.exclusiveClass = exclusiveClass;
parts.add(part);
}
public void add(ModelPerformer[] performers, int keyFrom, int keyTo,
int velFrom, int velTo) {
add(performers, keyFrom, keyTo, velFrom, velTo, -1);
}
public void add(ModelPerformer[] performers, int keyFrom, int keyTo) {
add(performers, keyFrom, keyTo, 0, 127, -1);
}
public void add(ModelPerformer[] performers) {
add(performers, 0, 127, 0, 127, -1);
}
public void add(ModelPerformer performer, int keyFrom, int keyTo,
int velFrom, int velTo, int exclusiveClass) {
add(new ModelPerformer[]{performer}, keyFrom, keyTo, velFrom, velTo,
exclusiveClass);
}
public void add(ModelPerformer performer, int keyFrom, int keyTo,
int velFrom, int velTo) {
add(new ModelPerformer[]{performer}, keyFrom, keyTo, velFrom, velTo);
}
public void add(ModelPerformer performer, int keyFrom, int keyTo) {
add(new ModelPerformer[]{performer}, keyFrom, keyTo);
}
public void add(ModelPerformer performer) {
add(new ModelPerformer[]{performer});
}
public void add(ModelInstrument ins, int keyFrom, int keyTo, int velFrom,
int velTo, int exclusiveClass) {
add(ins.getPerformers(), keyFrom, keyTo, velFrom, velTo, exclusiveClass);
}
public void add(ModelInstrument ins, int keyFrom, int keyTo, int velFrom,
int velTo) {
add(ins.getPerformers(), keyFrom, keyTo, velFrom, velTo);
}
public void add(ModelInstrument ins, int keyFrom, int keyTo) {
add(ins.getPerformers(), keyFrom, keyTo);
}
public void add(ModelInstrument ins) {
add(ins.getPerformers());
}
public ModelPerformer[] getPerformers() {
int percount = 0;
for (SimpleInstrumentPart part : parts)
if (part.performers != null)
percount += part.performers.length;
ModelPerformer[] performers = new ModelPerformer[percount];
int px = 0;
for (SimpleInstrumentPart part : parts) {
if (part.performers != null) {
for (ModelPerformer mperfm : part.performers) {
ModelPerformer performer = new ModelPerformer();
performer.setName(getName());
performers[px++] = performer;
performer.setDefaultConnectionsEnabled(
mperfm.isDefaultConnectionsEnabled());
performer.setKeyFrom(mperfm.getKeyFrom());
performer.setKeyTo(mperfm.getKeyTo());
performer.setVelFrom(mperfm.getVelFrom());
performer.setVelTo(mperfm.getVelTo());
performer.setExclusiveClass(mperfm.getExclusiveClass());
performer.setSelfNonExclusive(mperfm.isSelfNonExclusive());
performer.setReleaseTriggered(mperfm.isReleaseTriggered());
if (part.exclusiveClass != -1)
performer.setExclusiveClass(part.exclusiveClass);
if (part.keyFrom > performer.getKeyFrom())
performer.setKeyFrom(part.keyFrom);
if (part.keyTo < performer.getKeyTo())
performer.setKeyTo(part.keyTo);
if (part.velFrom > performer.getVelFrom())
performer.setVelFrom(part.velFrom);
if (part.velTo < performer.getVelTo())
performer.setVelTo(part.velTo);
performer.getOscillators().addAll(mperfm.getOscillators());
performer.getConnectionBlocks().addAll(
mperfm.getConnectionBlocks());
}
}
}
return performers;
}
public Object getData() {
return null;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public ModelPatch getPatch() {
return new ModelPatch(bank, preset, percussion);
}
public void setPatch(Patch patch) {
if (patch instanceof ModelPatch && ((ModelPatch)patch).isPercussion()) {
percussion = true;
bank = patch.getBank();
preset = patch.getProgram();
} else {
percussion = false;
bank = patch.getBank();
preset = patch.getProgram();
}
}
}

View File

@@ -0,0 +1,145 @@
/*
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.sound.midi.Instrument;
import javax.sound.midi.Patch;
import javax.sound.midi.Soundbank;
import javax.sound.midi.SoundbankResource;
/**
* A simple soundbank that contains instruments and soundbankresources.
*
* @author Karl Helgason
*/
public class SimpleSoundbank implements Soundbank {
String name = "";
String version = "";
String vendor = "";
String description = "";
List<SoundbankResource> resources = new ArrayList<SoundbankResource>();
List<Instrument> instruments = new ArrayList<Instrument>();
public String getName() {
return name;
}
public String getVersion() {
return version;
}
public String getVendor() {
return vendor;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public void setName(String name) {
this.name = name;
}
public void setVendor(String vendor) {
this.vendor = vendor;
}
public void setVersion(String version) {
this.version = version;
}
public SoundbankResource[] getResources() {
return resources.toArray(new SoundbankResource[resources.size()]);
}
public Instrument[] getInstruments() {
Instrument[] inslist_array
= instruments.toArray(new Instrument[resources.size()]);
Arrays.sort(inslist_array, new ModelInstrumentComparator());
return inslist_array;
}
public Instrument getInstrument(Patch patch) {
int program = patch.getProgram();
int bank = patch.getBank();
boolean percussion = false;
if (patch instanceof ModelPatch)
percussion = ((ModelPatch)patch).isPercussion();
for (Instrument instrument : instruments) {
Patch patch2 = instrument.getPatch();
int program2 = patch2.getProgram();
int bank2 = patch2.getBank();
if (program == program2 && bank == bank2) {
boolean percussion2 = false;
if (patch2 instanceof ModelPatch)
percussion2 = ((ModelPatch)patch2).isPercussion();
if (percussion == percussion2)
return instrument;
}
}
return null;
}
public void addResource(SoundbankResource resource) {
if (resource instanceof Instrument)
instruments.add((Instrument) resource);
else
resources.add(resource);
}
public void removeResource(SoundbankResource resource) {
if (resource instanceof Instrument)
instruments.remove((Instrument) resource);
else
resources.remove(resource);
}
public void addInstrument(Instrument resource) {
instruments.add(resource);
}
public void removeInstrument(Instrument resource) {
instruments.remove(resource);
}
public void addAllInstruments(Soundbank soundbank) {
for (Instrument ins : soundbank.getInstruments())
addInstrument(ins);
}
public void removeAllInstruments(Soundbank soundbank) {
for (Instrument ins : soundbank.getInstruments())
removeInstrument(ins);
}
}

View File

@@ -0,0 +1,390 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.io.IOException;
import java.util.Arrays;
import javax.sound.midi.MidiChannel;
import javax.sound.midi.VoiceStatus;
/**
* Abstract resampler class.
*
* @author Karl Helgason
*/
public abstract class SoftAbstractResampler implements SoftResampler {
private class ModelAbstractResamplerStream implements SoftResamplerStreamer {
AudioFloatInputStream stream;
boolean stream_eof = false;
int loopmode;
boolean loopdirection = true; // true = forward
float loopstart;
float looplen;
float target_pitch;
float[] current_pitch = new float[1];
boolean started;
boolean eof;
int sector_pos = 0;
int sector_size = 400;
int sector_loopstart = -1;
boolean markset = false;
int marklimit = 0;
int streampos = 0;
int nrofchannels = 2;
boolean noteOff_flag = false;
float[][] ibuffer;
boolean ibuffer_order = true;
float[] sbuffer;
int pad;
int pad2;
float[] ix = new float[1];
int[] ox = new int[1];
float samplerateconv = 1;
float pitchcorrection = 0;
ModelAbstractResamplerStream() {
pad = getPadding();
pad2 = getPadding() * 2;
ibuffer = new float[2][sector_size + pad2];
ibuffer_order = true;
}
public void noteOn(MidiChannel channel, VoiceStatus voice,
int noteNumber, int velocity) {
}
public void noteOff(int velocity) {
noteOff_flag = true;
}
public void open(ModelWavetable osc, float outputsamplerate)
throws IOException {
eof = false;
nrofchannels = osc.getChannels();
if (ibuffer.length < nrofchannels) {
ibuffer = new float[nrofchannels][sector_size + pad2];
}
stream = osc.openStream();
streampos = 0;
stream_eof = false;
pitchcorrection = osc.getPitchcorrection();
samplerateconv
= stream.getFormat().getSampleRate() / outputsamplerate;
looplen = osc.getLoopLength();
loopstart = osc.getLoopStart();
sector_loopstart = (int) (loopstart / sector_size);
sector_loopstart = sector_loopstart - 1;
sector_pos = 0;
if (sector_loopstart < 0)
sector_loopstart = 0;
started = false;
loopmode = osc.getLoopType();
if (loopmode != 0) {
markset = false;
marklimit = nrofchannels * (int) (looplen + pad2 + 1);
} else
markset = true;
// loopmode = 0;
target_pitch = samplerateconv;
current_pitch[0] = samplerateconv;
ibuffer_order = true;
loopdirection = true;
noteOff_flag = false;
for (int i = 0; i < nrofchannels; i++)
Arrays.fill(ibuffer[i], sector_size, sector_size + pad2, 0);
ix[0] = pad;
eof = false;
ix[0] = sector_size + pad;
sector_pos = -1;
streampos = -sector_size;
nextBuffer();
}
public void setPitch(float pitch) {
/*
this.pitch = (float) Math.pow(2f,
(pitchcorrection + pitch) / 1200.0f)
* samplerateconv;
*/
this.target_pitch = (float)Math.exp(
(pitchcorrection + pitch) * (Math.log(2.0) / 1200.0))
* samplerateconv;
if (!started)
current_pitch[0] = this.target_pitch;
}
public void nextBuffer() throws IOException {
if (ix[0] < pad) {
if (markset) {
// reset to target sector
stream.reset();
ix[0] += streampos - (sector_loopstart * sector_size);
sector_pos = sector_loopstart;
streampos = sector_pos * sector_size;
// and go one sector backward
ix[0] += sector_size;
sector_pos -= 1;
streampos -= sector_size;
stream_eof = false;
}
}
if (ix[0] >= sector_size + pad) {
if (stream_eof) {
eof = true;
return;
}
}
if (ix[0] >= sector_size * 4 + pad) {
int skips = (int)((ix[0] - sector_size * 4 + pad) / sector_size);
ix[0] -= sector_size * skips;
sector_pos += skips;
streampos += sector_size * skips;
stream.skip(sector_size * skips);
}
while (ix[0] >= sector_size + pad) {
if (!markset) {
if (sector_pos + 1 == sector_loopstart) {
stream.mark(marklimit);
markset = true;
}
}
ix[0] -= sector_size;
sector_pos++;
streampos += sector_size;
for (int c = 0; c < nrofchannels; c++) {
float[] cbuffer = ibuffer[c];
for (int i = 0; i < pad2; i++)
cbuffer[i] = cbuffer[i + sector_size];
}
int ret;
if (nrofchannels == 1)
ret = stream.read(ibuffer[0], pad2, sector_size);
else {
int slen = sector_size * nrofchannels;
if (sbuffer == null || sbuffer.length < slen)
sbuffer = new float[slen];
int sret = stream.read(sbuffer, 0, slen);
if (sret == -1)
ret = -1;
else {
ret = sret / nrofchannels;
for (int i = 0; i < nrofchannels; i++) {
float[] buff = ibuffer[i];
int ix = i;
int ix_step = nrofchannels;
int ox = pad2;
for (int j = 0; j < ret; j++, ix += ix_step, ox++)
buff[ox] = sbuffer[ix];
}
}
}
if (ret == -1) {
ret = 0;
stream_eof = true;
for (int i = 0; i < nrofchannels; i++)
Arrays.fill(ibuffer[i], pad2, pad2 + sector_size, 0f);
return;
}
if (ret != sector_size) {
for (int i = 0; i < nrofchannels; i++)
Arrays.fill(ibuffer[i], pad2 + ret, pad2 + sector_size, 0f);
}
ibuffer_order = true;
}
}
public void reverseBuffers() {
ibuffer_order = !ibuffer_order;
for (int c = 0; c < nrofchannels; c++) {
float[] cbuff = ibuffer[c];
int len = cbuff.length - 1;
int len2 = cbuff.length / 2;
for (int i = 0; i < len2; i++) {
float x = cbuff[i];
cbuff[i] = cbuff[len - i];
cbuff[len - i] = x;
}
}
}
public int read(float[][] buffer, int offset, int len)
throws IOException {
if (eof)
return -1;
if (noteOff_flag)
if ((loopmode & 2) != 0)
if (loopdirection)
loopmode = 0;
float pitchstep = (target_pitch - current_pitch[0]) / len;
float[] current_pitch = this.current_pitch;
started = true;
int[] ox = this.ox;
ox[0] = offset;
int ox_end = len + offset;
float ixend = sector_size + pad;
if (!loopdirection)
ixend = pad;
while (ox[0] != ox_end) {
nextBuffer();
if (!loopdirection) {
// If we are in backward playing part of pingpong
// or reverse loop
if (streampos < (loopstart + pad)) {
ixend = loopstart - streampos + pad2;
if (ix[0] <= ixend) {
if ((loopmode & 4) != 0) {
// Ping pong loop, change loopdirection
loopdirection = true;
ixend = sector_size + pad;
continue;
}
ix[0] += looplen;
ixend = pad;
continue;
}
}
if (ibuffer_order != loopdirection)
reverseBuffers();
ix[0] = (sector_size + pad2) - ix[0];
ixend = (sector_size + pad2) - ixend;
ixend++;
float bak_ix = ix[0];
int bak_ox = ox[0];
float bak_pitch = current_pitch[0];
for (int i = 0; i < nrofchannels; i++) {
if (buffer[i] != null) {
ix[0] = bak_ix;
ox[0] = bak_ox;
current_pitch[0] = bak_pitch;
interpolate(ibuffer[i], ix, ixend, current_pitch,
pitchstep, buffer[i], ox, ox_end);
}
}
ix[0] = (sector_size + pad2) - ix[0];
ixend--;
ixend = (sector_size + pad2) - ixend;
if (eof) {
current_pitch[0] = this.target_pitch;
return ox[0] - offset;
}
continue;
}
if (loopmode != 0) {
if (streampos + sector_size > (looplen + loopstart + pad)) {
ixend = loopstart + looplen - streampos + pad2;
if (ix[0] >= ixend) {
if ((loopmode & 4) != 0 || (loopmode & 8) != 0) {
// Ping pong or revese loop, change loopdirection
loopdirection = false;
ixend = pad;
continue;
}
ixend = sector_size + pad;
ix[0] -= looplen;
continue;
}
}
}
if (ibuffer_order != loopdirection)
reverseBuffers();
float bak_ix = ix[0];
int bak_ox = ox[0];
float bak_pitch = current_pitch[0];
for (int i = 0; i < nrofchannels; i++) {
if (buffer[i] != null) {
ix[0] = bak_ix;
ox[0] = bak_ox;
current_pitch[0] = bak_pitch;
interpolate(ibuffer[i], ix, ixend, current_pitch,
pitchstep, buffer[i], ox, ox_end);
}
}
if (eof) {
current_pitch[0] = this.target_pitch;
return ox[0] - offset;
}
}
current_pitch[0] = this.target_pitch;
return len;
}
public void close() throws IOException {
stream.close();
}
}
public abstract int getPadding();
public abstract void interpolate(float[] in, float[] in_offset,
float in_end, float[] pitch, float pitchstep, float[] out,
int[] out_offset, int out_end);
public final SoftResamplerStreamer openStreamer() {
return new ModelAbstractResamplerStream();
}
}

View File

@@ -0,0 +1,128 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
import java.util.Arrays;
import javax.sound.sampled.AudioFormat;
/**
* This class is used to store audio buffer.
*
* @author Karl Helgason
*/
public final class SoftAudioBuffer {
private int size;
private float[] buffer;
private boolean empty = true;
private AudioFormat format;
private AudioFloatConverter converter;
private byte[] converter_buffer;
public SoftAudioBuffer(int size, AudioFormat format) {
this.size = size;
this.format = format;
converter = AudioFloatConverter.getConverter(format);
}
public void swap(SoftAudioBuffer swap)
{
int bak_size = size;
float[] bak_buffer = buffer;
boolean bak_empty = empty;
AudioFormat bak_format = format;
AudioFloatConverter bak_converter = converter;
byte[] bak_converter_buffer = converter_buffer;
size = swap.size;
buffer = swap.buffer;
empty = swap.empty;
format = swap.format;
converter = swap.converter;
converter_buffer = swap.converter_buffer;
swap.size = bak_size;
swap.buffer = bak_buffer;
swap.empty = bak_empty;
swap.format = bak_format;
swap.converter = bak_converter;
swap.converter_buffer = bak_converter_buffer;
}
public AudioFormat getFormat() {
return format;
}
public int getSize() {
return size;
}
public void clear() {
if (!empty) {
Arrays.fill(buffer, 0);
empty = true;
}
}
public boolean isSilent() {
return empty;
}
public float[] array() {
empty = false;
if (buffer == null)
buffer = new float[size];
return buffer;
}
public void get(byte[] buffer, int channel) {
int framesize_pc = (format.getFrameSize() / format.getChannels());
int c_len = size * framesize_pc;
if (converter_buffer == null || converter_buffer.length < c_len)
converter_buffer = new byte[c_len];
if (format.getChannels() == 1) {
converter.toByteArray(array(), size, buffer);
} else {
converter.toByteArray(array(), size, converter_buffer);
if (channel >= format.getChannels())
return;
int z_stepover = format.getChannels() * framesize_pc;
int k_stepover = framesize_pc;
for (int j = 0; j < framesize_pc; j++) {
int k = j;
int z = channel * framesize_pc + j;
for (int i = 0; i < size; i++) {
buffer[z] = converter_buffer[k];
z += z_stepover;
k += k_stepover;
}
}
}
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.media.sound;
/**
* Audio processor interface.
*
* @author Karl Helgason
*/
public interface SoftAudioProcessor {
public void globalParameterControlChange(int[] slothpath, long param,
long value);
public void init(float samplerate, float controlrate);
public void setInput(int pin, SoftAudioBuffer input);
public void setOutput(int pin, SoftAudioBuffer output);
public void setMixMode(boolean mix);
public void processAudio();
public void processControlLogic();
}

Some files were not shown because too many files have changed in this diff Show More