/*
* @(#)ANIMAudioCommand.java 1.2.1 2010-12-25
*
* Copyright (c) 2003-2005 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media.anim;
import org.monte.media.eightsvx.*;
/**
* An ANIMAudioCommand handles an audio command that is associated to
* a single ANIMFrame of a ANIMMovieTrack. An ANIMFrame may be associated
* to multiple ANIMAudioCommands.
* <p>
* This version of ANIMAudioCommand is designed to handle audio commands
* as specified by the ANIM+SLA Sound Control collection chunk (ILBM SCTL).
* <p>
* Here's the specification of the SCTL collection chunk:
* <pre>
* typedef UBYTE Command; // Choice of commands
* #define cmdPlaySound 1 // Start playing a sound
* #define cmdStopSound 2 // Stop the sound in a given channelMask
* #define cmdSetFreqvol 3 // Change frequency/volume for a channelMask
*
* typedef USHORT Flags; // Choice of flags
* #define flagNoInterrupt 1 // Play the sound, but only if
* // the channelMask isn't in use
*
* typedef struct {
* Command command; // What to do, see above
* UBYTE volume; // Volume 0..64
* UWORD sound, // Sound number (one based)
* repeats, // Number of times to play the sound
* channelMask, // Channel(s) to use for playing (bit mask)
* frequency; // If non-zero, overrides the VHDR value
* Flags flags; // Flags, see above
* UBYTE pad[4]; // For future use
*
* @author Werner Randelshofer, Hausmatt 10, CH-6405 Goldau, Switzerland
* @version 1.2.1 2010-12-25 Minor fixes for J2SE 5.
* <br>1.2 2005-09-16 Support for swapping left and right speakers added.
* <br>1.1 2003-04-25 Revised.
* <br>1.0 April 3, 2003 Created.
*/
public class ANIMAudioCommand {
/** Start playing a sound. */
public final static int COMMAND_PLAY_SOUND = 1;
/** Stop the sound in a given channelMask. */
public final static int COMMAND_STOP_SOUND = 2;
/** Change frequency/volume for a channelMask. */
public final static int COMMAND_SET_FREQVOL = 3;
/** Play the sound, but only if
* the channelMask isn't in use. */
public final static int FLAG_NO_INTERRUPT = 1;
/** What to do. */
private int command;
/** Volume 0..64 */
private int volume;
/** Sound number (one based). */
private int sound;
/** Number of times to play the sound. */
private int repeats;
/** Channel(s) to use for playing (bit mask).
* The channel mask tells which channel(s) we want.
* The code is 1=channel0 (left), 2=channel1 (right), 4=channel2 (left),
* 8=channel3 (right). If you want more than one channel, add the codes up.
*/
private int channelMask;
private final static int CHANNEL0_MASK = 1, CHANNEL1_MASK = 2, CHANNEL2_MASK = 4,
CHANNEL3_MASK = 8;
private final static int CHANNEL_LEFT_MASK = CHANNEL0_MASK | CHANNEL2_MASK;
private final static int CHANNEL_RIGHT_MASK = CHANNEL1_MASK | CHANNEL3_MASK;
/** If non-zero, overrides the VHDR value. */
private int frequency;
/** Flags, see above. */
private int flags;
/** Channel(s) that are in use now for playing (bit mask).
* If this mask is != zero, then this audio command is playing sound.
*/
private int activeChannelMask;
/** The prepared audio data. */
private LoopableAudioClip audioClip;
/** Creates a new instance. */
public ANIMAudioCommand(int command, int volume, int sound, int repeats, int channelMask, int frequency, int flags) {
this.command = command;
this.volume = volume;
this.sound = sound;
this.repeats = repeats;
this.channelMask = channelMask;
this.frequency = frequency;
this.flags = flags;
}
public int getChannelMask() {
return channelMask;
}
public int getFrequency() {
return frequency;
}
public int getSound() {
return sound;
}
public int getVolume() {
return volume;
}
public int getCommand() {
return command;
}
public void prepare(ANIMMovieTrack track) {
if (command == COMMAND_PLAY_SOUND && audioClip == null) {
float pan;
if ((channelMask & CHANNEL_LEFT_MASK) != 0
&& (channelMask & CHANNEL_RIGHT_MASK) == 0) {
pan = -1f; // left speakers only
} else if ((channelMask & CHANNEL_RIGHT_MASK) != 0
&& (channelMask & CHANNEL_LEFT_MASK) == 0) {
pan = 1f; // right speakers only
} else {
pan = 0f; // both speakers
}
EightSVXAudioClip eightSVXAudioClip = (EightSVXAudioClip) track.getAudioClip(sound - 1);
audioClip = eightSVXAudioClip.createAudioClip(
(frequency == 0) ? eightSVXAudioClip.getSampleRate() : frequency,
volume,
track.isSwapSpeakers() ? -pan : pan
);
}
}
public void play(ANIMMovieTrack track) {
prepare(track);
if (audioClip != null) {
if (repeats < 2) {
audioClip.play();
} else {
audioClip.loop(repeats);
}
}
activeChannelMask = channelMask;
}
public void stop(ANIMMovieTrack track) {
activeChannelMask = 0;
if (audioClip != null) {
audioClip.stop();
}
}
/**
* Stops playback of this audio command on the specified channels.
*
*/
public void stop(ANIMMovieTrack track, int channelMask) {
activeChannelMask &= ~channelMask;
if (activeChannelMask == 0) {
audioClip.stop();
}
}
public void doCommand(ANIMMovieTrack track, ANIMAudioCommand[] runningCommands) {
// long start = System.currentTimeMillis();
switch (command) {
case COMMAND_PLAY_SOUND : {
boolean isPlayingOnOneChannel = false;
for (int j=0; j < 4; j++) {
if ((channelMask & (1 << j)) != 0) {
// We stop all audio commands that are playing on
// the channels specified by the channel mask.
if (runningCommands[j] != null) {
runningCommands[j].stop(track, 1 << j);
}
if (! isPlayingOnOneChannel) {
// We only play an audio command on the first
// channel defined by the channel mask.
// This is hopefully suff�cient for all cases we
// encounter.
isPlayingOnOneChannel = true;
play(track);
}
runningCommands[j] = this;
}
}
}
break;
case COMMAND_STOP_SOUND : {
for (int j=0; j < 4; j++) {
if ((channelMask & (1 << j)) != 0) {
// We stop all audio commands that are playing on
// the channels specified by the channel mask.
if (runningCommands[j] != null) {
runningCommands[j].stop(track, 1 << j);
runningCommands[j] = null;
}
}
}
}
break;
case COMMAND_SET_FREQVOL :
break;
}
}
public void dispose() {
if (audioClip != null) {
audioClip = null;
}
}
}