/*
Copyright (c) 2009, hkrn All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer. Redistributions in binary
form must reproduce the above copyright notice, this list of conditions and
the following disclaimer in the documentation and/or other materials
provided with the distribution. Neither the name of the hkrn nor
the names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
*/
//
// $Id$
//
using System;
using System.Collections.Generic;
using System.Windows;
namespace SlMML
{
public class Track
{
#region 定数の定義
public const int TEMPO_TRACK = 0;
public const int FIRST_TRACK = 1;
#endregion
#region コンストラクタおよびデストラクタの定義
///
/// トラックオブジェクトを生成します
///
public Track()
{
End = false;
m_channel = new Channel();
m_events = new List(32);
m_index = 0;
m_delta = 0;
GlobalTick = 0;
m_needle = 0.0;
Duration = new Duration(TimeSpan.FromMilliseconds(0));
BPM = DEFAULT_BPM;
RecordGate(15.0 / 16.0);
RecordGate(0);
}
#endregion
#region 公開メソッドの定義
///
/// 指定した位置からサンプルを取得します
///
/// サンプルバッファ
/// サンプルの取得開始位置
/// サンプルの取得終了位置
/// サンプルを実際に取得するかどうか
public void GetSamples(ref double[] samples, int start, int end, bool update)
{
if (End)
return;
int eventCount = m_events.Count, i = start;
while (i < end)
{
bool loop = false;
double delta = 0;
do
{
loop = false;
if (m_index < eventCount)
{
Events.Event e = m_events[m_index];
delta = e.Delta * m_spt;
if (m_needle >= delta)
{
loop = true;
if (e is Events.NoteOn)
m_channel.EnableNote((Events.NoteOn)e);
else if (e is Events.NoteOff)
m_channel.DisableNote();
else if (e is Events.Note)
m_channel.NoteIndex = ((Events.Note)(e)).Index;
else if (e is Events.Tempo)
BPM = ((Events.Tempo)e).Value;
else if (e is Events.Form)
m_channel.Form = (Events.Form)e;
else if (e is Events.VCO)
m_channel.ADSRForVCO = (Events.VCO)e;
else if (e is Events.VCF)
m_channel.ADSRForVCF = (Events.VCF)e;
else if (e is Events.NoiseFrequency)
m_channel.NoiseFrequency = ((Events.NoiseFrequency)e).Value;
else if (e is Events.PWM)
m_channel.PWM = ((Events.PWM)e).Value;
else if (e is Events.Pan)
m_channel.Pan = ((Events.Pan)e).Value;
else if (e is Events.Vowel)
m_channel.FormantVowel = ((Events.Vowel)e).Value;
else if (e is Events.Detune)
m_channel.Detune = ((Events.Detune)e).Value;
else if (e is Events.LFO)
{
Events.LFO lfo = (Events.LFO)e;
double width = lfo.Width * m_spt;
lfo.Delay = (int)(lfo.Delay * m_spt);
lfo.Time = (int)(lfo.Time * width);
m_channel.SetLFO(lfo, Sample.RATE / width);
}
else if (e is Events.LPF)
m_channel.LPF = (Events.LPF)e;
else if (e is Events.VolumeMode)
m_channel.VolumeMode = ((Events.VolumeMode)e).Value;
else if (e is Events.Input)
m_channel.Input = (Events.Input)e;
else if (e is Events.Output)
m_channel.Output = (Events.Output)e;
else if (e is Events.Expression)
m_channel.Expression = ((Events.Expression)e).Value;
else if (e is Events.Ring)
m_channel.Ring = (Events.Ring)e;
else if (e is Events.Sync)
m_channel.Sync = (Events.Sync)e;
else if (e is Events.Close)
m_channel.Close();
else if (e is Events.Eot)
End = true;
m_needle -= delta;
m_index++;
}
}
}
while (loop);
int di = 0;
if (m_index < eventCount)
{
Events.Event e = m_events[m_index];
delta = e.Delta * m_spt;
di = (int)Math.Ceiling(delta - m_needle);
if (i + di >= end)
di = end - i;
m_needle += di;
if (update)
m_channel.GetSamples(ref samples, i, di, end);
i += di;
}
else
break;
}
}
public void Seek()
{
GlobalTick = 0;
}
public void Seek(int delta)
{
m_delta += delta;
GlobalTick += (uint)delta;
}
///
/// ノートを記録する
///
/// ノート番号
/// ノートの長さ
/// ノートの音の強さ
/// キーを有効にするか
/// キーを無効にするか
public void RecordNote(int index, int length, int velocity, bool keyOn, bool keyOff)
{
Events.Event e;
if (keyOn)
e = new Events.NoteOn() { Index = index, Velocity = velocity };
else
e = new Events.Note() { Index = index };
SetDeltaAndAddEvent(e);
if (keyOff)
{
int gate = Math.Max((int)(length * m_gate - m_gate2), 0);
Seek(gate);
e = new Events.NoteOff() { Index = index, Velocity = velocity };
SetDeltaAndAddEvent(e);
Seek(length - gate);
}
else
Seek(length);
}
///
/// 休符を記録します
///
/// 長さ
public void RecordRest(int length)
{
Seek(length);
}
///
/// 休符を記録します
///
/// 長さ(ミリ秒単位)
public void RecordRest(uint msec)
{
int length = (int)(msec * Sample.RATE / (m_spt * 1000));
Seek(length);
}
///
/// ボリュームを記録します
///
/// ボリューム値
public void RecordVolume(int volume)
{
Events.Volume e = new Events.Volume() { Value = volume };
SetDeltaAndAddEvent(e);
}
///
/// BPMを記録します
///
/// BPM値
/// 曲全体の長さ
public void RecordTempo(int tempo, uint globalTick)
{
Events.Tempo e = new Events.Tempo() { Value = tempo };
SetDelta(e);
RecordGlobalTick(globalTick, e);
}
///
/// 曲の演奏完了を記録します
///
public void RecordEOT()
{
Events.Eot e = new Events.Eot() { Delta = m_delta };
SetDeltaAndAddEvent(e);
}
public void RecordGate(double gate)
{
m_gate = gate;
}
public void RecordGate(int gate)
{
m_gate2 = Math.Max(gate, 0);
}
///
/// モジュレータを設定します
///
/// モジュレータの種類
/// モジュレータに与える値
public void RecordForm(OscillatorForm form, OscillatorForm subform)
{
Events.Form e = new Events.Form() { Main = form, Sub = subform };
SetDeltaAndAddEvent(e);
}
public void RecordNoiseFrequency(int frequency)
{
Events.NoiseFrequency e = new Events.NoiseFrequency() { Value = frequency };
SetDeltaAndAddEvent(e);
}
///
/// PWM (Pulse Width Modulation)を記録します
///
/// PWMの値
public void RecordPWM(int pwm)
{
Events.PWM e = new Events.PWM() { Value = pwm };
SetDeltaAndAddEvent(e);
}
///
/// パンを記録します
///
/// パンの値
public void RecordPan(int pan)
{
Events.Pan e = new Events.Pan() { Value = pan };
SetDeltaAndAddEvent(e);
}
///
/// 声を記録します
///
/// 声の種類
public void RecordFormantVowel(FormantVowel vowel)
{
Events.Vowel e = new Events.Vowel() { Value = vowel };
SetDeltaAndAddEvent(e);
}
///
/// デチューンを記録します
///
/// デチューンの値
public void RecordDetune(int detune)
{
Events.Detune e = new Events.Detune() { Value = detune };
SetDeltaAndAddEvent(e);
}
///
/// LFO (Low Frequency Oscillator)を記録します
///
/// モジュレータの種類
/// モジュレータに与える値
/// 深さ
/// 長さ
/// 遅延値
/// 時間
/// オシレータの種類番号を反転するかどうか
public void RecordLFO(OscillatorForm form, OscillatorForm subform, int depth, int width, int delay, int time, bool reverse)
{
Events.LFO e = new Events.LFO()
{
Main = form,
Sub = subform,
Depth = depth,
Width = width,
Delay = delay,
Time = time,
Reverse = reverse
};
SetDeltaAndAddEvent(e);
}
///
/// LPF (Low Pass Filter)を記録します
///
/// スイッチ
/// 量
/// 周波数
/// 共鳴値
public void RecordLPF(FilterType sw, int amount, int frequency, int resonance)
{
Events.LPF e = new Events.LPF()
{
Switch = sw,
Amount = amount,
Frequency = frequency,
Resonance = resonance
};
SetDeltaAndAddEvent(e);
}
///
/// ボリュームモードを記録します
///
/// ボリュームモード
public void RecordVolumeMode(int mode)
{
Events.VolumeMode e = new Events.VolumeMode() { Value = mode };
SetDeltaAndAddEvent(e);
}
///
/// 入力を記録します
///
///
///
public void RecordInput(int inSens, int pipe)
{
Events.Input e = new Events.Input() { Sens = inSens, Pipe = pipe };
SetDeltaAndAddEvent(e);
}
///
/// 出力を記録します
///
///
///
public void RecordOutput(ChannelOutputMode mode, int pipe)
{
Events.Output e = new Events.Output() { Mode = mode, Pipe = pipe };
SetDeltaAndAddEvent(e);
}
///
/// エクスプレッションを記録します
///
/// エクスプレッションの値
public void RecordExpression(int expression)
{
Events.Expression e = new Events.Expression() { Value = expression };
SetDeltaAndAddEvent(e);
}
///
/// リングを記録します
///
///
///
public void RecordRing(int inSens, int pipe)
{
Events.Ring e = new Events.Ring() { Sens = inSens, Pipe = pipe };
SetDeltaAndAddEvent(e);
}
///
/// 同期を記録します
///
///
///
public void RecordSync(ChannelOutputMode mode, int pipe)
{
Events.Sync e = new Events.Sync() { Mode = mode, Pipe = pipe };
SetDeltaAndAddEvent(e);
}
///
/// 終了マークを記録します
///
public void RecordClose()
{
Events.Close e = new Events.Close();
SetDeltaAndAddEvent(e);
}
///
/// ADSR (Attack, Decay, Sustain, Release)を記録します
///
/// 立ち上がりの値
/// 減衰の値
/// 減衰後の保持する値
/// 余韻の値
/// VCO (Voltage Control Oscillator)であるかどうか
public void RecordEnvelopeADSR(int attack, int decay, int sustain, int release, bool isVCO)
{
Events.Event e;
if (isVCO)
e = new Events.VCO()
{
Delta = m_delta,
Attack = attack,
Decay = decay,
Sustain = sustain,
Release = release
};
else
e = new Events.VCF()
{
Delta = m_delta,
Attack = attack,
Decay = decay,
Sustain = sustain,
Release = release
};
SetDeltaAndAddEvent(e);
}
///
/// トラックリストを集めて演奏時間を求めます
///
///
/// バッファを完全に吐き出すため、実際の時間に3秒加算されます
///
/// トラックリスト
public void ConductTracks(IList