////////////////////////////////////////////////////////////////////////////////
// Copyright 2012 Michael Schmalle - Teoti Graphix, LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License
//
// Author: Michael Schmalle, Principal Architect
// mschmalle at teotigraphix dot com
////////////////////////////////////////////////////////////////////////////////
package com.teotigraphix.caustk.sequencer;
import java.io.File;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.androidtransfuse.event.EventObserver;
import com.teotigraphix.caustk.controller.ICaustkController;
import com.teotigraphix.caustk.core.CausticException;
import com.teotigraphix.caustk.library.EffectMixerState;
import com.teotigraphix.caustk.library.SoundMixerState;
import com.teotigraphix.caustk.library.SoundSourceState;
import com.teotigraphix.caustk.sequencer.Track.OnTrackPhraseAdd;
import com.teotigraphix.caustk.sequencer.Track.OnTrackPhraseRemove;
import com.teotigraphix.caustk.service.ISerialize;
import com.teotigraphix.caustk.tone.Tone;
/**
* @author Michael Schmalle
* @copyright Teoti Graphix, LLC
* @since 1.0
*/
public class TrackSong extends Song implements ISerialize {
private transient ICaustkController controller;
public final ICaustkController getController() {
return controller;
}
//--------------------------------------------------------------------------
//
// ITrackSong API :: Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// soundSourceState
//----------------------------------
private SoundSourceState soundSourceState;
public SoundSourceState getSoundSourceState() {
return soundSourceState;
}
public void setSoundSourceState(SoundSourceState value) {
soundSourceState = value;
}
//----------------------------------
// soundMixerState
//----------------------------------
private SoundMixerState soundMixerState;
public SoundMixerState getSoundMixerState() {
return soundMixerState;
}
public void setSoundMixerState(SoundMixerState value) {
soundMixerState = value;
}
//----------------------------------
// effectRackInfo
//----------------------------------
private EffectMixerState effectMixerState;
public EffectMixerState getEffectMixerState() {
return effectMixerState;
}
public void setEffectMixerState(EffectMixerState value) {
effectMixerState = value;
}
//----------------------------------
// file
//----------------------------------
private File file;
/**
* Returns the relative song file path from the base <code>songs</code>
* directory.
* <p>
* Use the {@link ISongManager} to create an absolute file path for this
* song.
*/
public final File getFile() {
return file;
}
public final void setFile(File value) {
file = value;
}
//----------------------------------
// tracks
//----------------------------------
private int numTracks = -1;
public int getNumTracks() {
return numTracks;
}
/**
* Resets the number of {@link Track}s int he song.
* <p>
* Setting this property will reset all data and tracks.
*
* @param value The number of tracks in the song.
*/
public void setNumTracks(int value) {
numTracks = value;
createTracks();
initializeTracks();
}
private void createTracks() {
tracks.clear();
for (int i = 0; i < numTracks; i++) {
Track track = new Track();
track.setIndex(i);
tracks.put(i, track);
}
}
/**
* Initializes the {@link Track} instance.
* <p>
* Must be called after a {@link TrackSong} deserialization.
*/
public void initializeTracks() {
for (Track track : tracks.values()) {
track.setDispatcher(getDispatcher());
getDispatcher().register(OnTrackPhraseAdd.class, onTrackPhraseHandler);
getDispatcher().register(OnTrackPhraseRemove.class, onTrackPhraseRemoveHandler);
}
}
private transient EventObserver<OnTrackPhraseAdd> onTrackPhraseHandler = new EventObserver<OnTrackPhraseAdd>() {
@Override
public void trigger(OnTrackPhraseAdd object) {
Track track = object.getTrack();
TrackItem trackItem = object.getItem();
Tone tone = controller.getSoundSource().getTone(track.getIndex());
int bank = trackItem.getBankIndex();
int pattern = trackItem.getPatternIndex();
int start = trackItem.getStartMeasure();
int end = trackItem.getEndMeasure();
// add the track to the song sequencer
try {
controller.getSongSequencer().addPattern(tone, bank, pattern, start, end);
} catch (CausticException e) {
e.printStackTrace();
}
}
};
private transient EventObserver<OnTrackPhraseRemove> onTrackPhraseRemoveHandler = new EventObserver<OnTrackPhraseRemove>() {
@Override
public void trigger(OnTrackPhraseRemove object) {
Track track = object.getTrack();
TrackItem trackItem = object.getItem();
Tone tone = controller.getSoundSource().getTone(track.getIndex());
int start = trackItem.getStartMeasure();
int end = trackItem.getEndMeasure();
// remove the track to the song sequencer
try {
controller.getSongSequencer().removePattern(tone, start, end);
} catch (CausticException e) {
e.printStackTrace();
}
}
};
//----------------------------------
// tracks
//----------------------------------
private Map<Integer, Track> tracks = new HashMap<Integer, Track>();
/*
* The last pattern in the song in All tracks. This is used to easily
* calculate the measure length of the song.
*/
private TrackItem lastPatternInTracks;
public Collection<Track> getTracks() {
return Collections.unmodifiableCollection(tracks.values());
}
@Override
public int getNumBeats() {
if (lastPatternInTracks == null)
return 0;
int measures = lastPatternInTracks.getEndMeasure();
return measures * 4;
}
@Override
public int getNumMeasures() {
if (lastPatternInTracks == null)
return 0;
// 0 index, we need to use the end measure that is measures + 1
int measures = lastPatternInTracks.getEndMeasure();
return measures;
}
@Override
public int getTotalTime() {
float bpm = getBPM();
float timeInSec = 60 / bpm;
float totalNumBeats = getNumBeats() + getMeasureBeat();
float total = timeInSec * totalNumBeats;
return (int)total;
}
@Override
public int getCurrentTime() {
float bpm = getBPM();
float timeInSec = 60 / bpm;
float totalNumBeats = (getCurrentMeasure() * 4) + getMeasureBeat();
float total = timeInSec * totalNumBeats;
return (int)total;
}
public TrackSong() {
super();
}
/**
* Returns the {@link Track} at the specified index.
*
* @param index The tack index.
*/
public Track getTrack(int index) {
return tracks.get(index);
}
/**
* Clears all the tracks {@link TrackPhrase}s.
*
* @throws CausticException
*/
public void clearTracks() throws CausticException {
for (Track track : tracks.values()) {
track.clearPhrases();
}
}
@Override
public void sleep() {
// TrackUtils.refreshTrackSongInfos(controller, this);
for (Track track : tracks.values()) {
track.sleep();
}
}
@Override
public void wakeup(ICaustkController controller) {
this.controller = controller;
initializeTracks();
for (Track track : tracks.values()) {
track.wakeup(controller);
}
}
}