/// <summary> /// 指定したVSQの指定した位置に,テンポの挿入を試みます. /// 既存のテンポがある場合,値が上書きされます /// </summary> /// <param name="vsq">挿入対象のVSQ</param> /// <param name="clock">挿入位置</param> /// <param name="tempo">楽譜表記上のテンポ.BPS</param> private static void insertTempoInto(VsqFileEx vsq, int clock, float t) { // clockの位置にテンポ変更があるかどうか? int num_tempo = vsq.TempoTable.size(); int index = -1; for (int j = 0; j < num_tempo; j++) { TempoTableEntry itemj = vsq.TempoTable[j]; if (itemj.Clock == clock) { index = j; break; } } int tempo = (int)(60e6 / t); if (index >= 0) { // clock位置に既存のテンポ変更がある場合,テンポ値を変更 TempoTableEntry itemj = vsq.TempoTable[index]; itemj.Tempo = tempo; } else { // 既存のものはないので新規に追加 vsq.TempoTable.Add(new TempoTableEntry(clock, tempo, 0.0)); } // テンポテーブルを更新 vsq.TempoTable.updateTempoInfo(); }
private decimal getTempo(TempoTableEntry tempo) { return(Math.Round((decimal)(60e6 / tempo.Tempo), 2)); }
/// <summary> /// このWAVE描画コンテキストが保持しているWAVEデータを、ゲートタイム基準でグラフィクスに描画します。 /// </summary> /// <param name="g">描画に使用するグラフィクスオブジェクト</param> /// <param name="pen">描画に使用するペン</param> /// <param name="rect">描画範囲</param> /// <param name="clock_start">描画開始位置のゲートタイム</param> /// <param name="clock_end">描画終了位置のゲートタイム</param> /// <param name="tempo_table">ゲートタイムから秒数を調べる際使用するテンポ・テーブル</param> /// <param name="pixel_per_clock">ゲートタイムあたりの秒数</param> /// <param name="scale_y">Y軸方向の描画スケール。デフォルトは1.0</param> /// <param name="auto_maximize">自動で最大化するかどうか</param> private void drawCore( Graphics2D g, Color pen, Rectangle rect, int clock_start, int clock_end, TempoVector tempo_table, float pixel_per_clock, float scale_y, bool auto_maximize) { if (mWave.Length == 0) { return; } #if DEBUG double startedTime = PortUtil.getCurrentTime(); #endif mDrawer.setGraphics(g); mDrawer.clear(); double secStart = tempo_table.getSecFromClock(clock_start); double secEnd = tempo_table.getSecFromClock(clock_end); int sStart0 = (int)(secStart * mSampleRate) - 1; int sEnd0 = (int)(secEnd * mSampleRate) + 1; int count = tempo_table.Count; int sStart = 0; double cStart = 0.0; float order_y = 1.0f; if (auto_maximize) { order_y = rect.height / 2.0f / 127.0f * mMaxAmplitude / mActualMaxAmplitude; } else { order_y = rect.height / 127.0f * scale_y * mMaxAmplitude; } int ox = rect.x; int oy = rect.height / 2; int last = mWave[0]; int lastx = ox; int lastYMax = oy - (int)(last * order_y); int lastYMin = lastYMax; int lasty = lastYMin; int lasty2 = lastYMin; bool skipped = false; mDrawer.append(ox, lasty); int xmax = rect.x + rect.width; int lastTempo = 500000; for (int i = 0; i <= count; i++) { double time = 0.0; int tempo = 500000; int cEnd = 0; if (i < count) { TempoTableEntry entry = tempo_table[i]; time = entry.Time; tempo = entry.Tempo; cEnd = entry.Clock; } else { time = tempo_table.getSecFromClock(clock_end); tempo = tempo_table[i - 1].Tempo; cEnd = clock_end; } int sEnd = (int)(time * mSampleRate); // sStartサンプルからsThisEndサンプルまでを描画する(必要なら!) if (sEnd < sStart0) { sStart = sEnd; cStart = cEnd; lastTempo = tempo; continue; } if (sEnd0 < sStart) { break; } // int xoffset = (int)(cStart * pixel_per_clock) - AppManager.mMainWindowController.getStartToDrawX() + AppManager.keyOffset; double sec_per_clock = lastTempo * 1e-6 / 480.0; lastTempo = tempo; double pixel_per_sample = 1.0 / mSampleRate / sec_per_clock * pixel_per_clock; int j0 = sStart; if (j0 < 0) { j0 = 0; } int j1 = sEnd; if (mWave.Length < j1) { j1 = mWave.Length; } // 第j0サンプルのデータを画面に描画したときのx座標がいくらになるか? int draftStartX = xoffset + (int)((j0 - sStart) * pixel_per_sample); if (draftStartX < rect.x) { j0 = (int)((rect.x - xoffset) / pixel_per_sample) + sStart; } // 第j1サンプルのデータを画面に描画した時のx座標がいくらになるか? int draftEndX = xoffset + (int)((j1 - sStart) * pixel_per_sample); if (rect.x + rect.width < draftEndX) { j1 = (int)((rect.x + rect.width - xoffset) / pixel_per_sample) + sStart; } bool breakRequired = false; for (int j = j0; j < j1; j++) { int v = mWave[j]; if (v == last) { skipped = true; continue; } int x = xoffset + (int)((j - sStart) * pixel_per_sample); if (xmax < x) { breakRequired = true; break; } if (x < rect.x) { continue; } int y = oy - (int)(v * order_y); if (lastx == x) { lastYMax = Math.Max(lastYMax, y); lastYMin = Math.Min(lastYMin, y); continue; } if (skipped) { mDrawer.append(x - 1, lasty); lastx = x - 1; } if (lastYMax == lastYMin) { mDrawer.append(x, y); } else { if (lasty2 != lastYMin) { mDrawer.append(lastx, lastYMin); } mDrawer.append(lastx, lastYMax); if (lastYMax != lasty) { mDrawer.append(lastx, lasty); } mDrawer.append(x, y); } lasty2 = lasty; lastx = x; lastYMin = y; lastYMax = y; lasty = y; last = v; skipped = false; } sStart = sEnd; cStart = cEnd; if (breakRequired) { break; } } mDrawer.append(rect.x + rect.width, lasty); mDrawer.flush(); }
/// <summary> /// vsqxファイルを読み込み,新しいシーケンスオブジェクトを生成する /// </summary> /// <param name="filePath">ファイルパス</param> /// <exception cref="System.Exception">読み込みに失敗した時スローされる</exception> /// <returns>生成したシーケンスオブジェクト</returns> public static VsqFile readFromVsqx(string filePath) { if (filePath == null) { throw new ArgumentNullException("filePath"); } if (false == File.Exists(filePath)) { throw new Exception("file not found"); } var xml = new XmlDocument(); xml.Load(filePath); // 音源テーブルを解釈 var voiceTable = getVoiceTable(xml); // マスタートラックを解釈 XmlElement masterTrack = xml.DocumentElement["masterTrack"]; int preMeasure = int.Parse(masterTrack["preMeasure"].InnerText); VsqFile result = new VsqFile("", preMeasure, 4, 4, 500000); // テンポ変更を読み取る result.TempoTable.Clear(); foreach (XmlNode node in masterTrack.GetElementsByTagName("tempo")) { int posTick = int.Parse(node["posTick"].InnerText); int bpm = int.Parse(node["bpm"].InnerText); int tempo = (int)(6000000000L / bpm); TempoTableEntry tempoEntry = new TempoTableEntry(posTick, tempo, 0.0); result.TempoTable.Add(tempoEntry); } result.TempoTable.updateTempoInfo(); // 拍子変更を読み取る result.TimesigTable.Clear(); foreach (XmlNode node in masterTrack.GetElementsByTagName("timeSig")) { int posMes = int.Parse(node["posMes"].InnerText); int numerator = int.Parse(node["nume"].InnerText); int denominator = int.Parse(node["denomi"].InnerText); TimeSigTableEntry timesigEntry = new TimeSigTableEntry(0, numerator, denominator, posMes); result.TimesigTable.Add(timesigEntry); } result.TimesigTable.updateTimesigInfo(); // マスター以外のトラックを解釈 foreach (XmlNode node in xml.DocumentElement.GetElementsByTagName("vsTrack")) { int trackIndex = int.Parse(node["vsTrackNo"].InnerText) + 1; VsqTrack track = null; if (result.Track.Count <= trackIndex) { int amount = trackIndex + 1 - result.Track.Count; for (int i = 0; i < amount; i++) { result.Track.Add(new VsqTrack("", "")); } } track = result.Track[trackIndex]; track.setName(node["trackName"].InnerText); foreach (XmlNode child in node.ChildNodes) { if (child.Name == "musicalPart") { parseMusicalPart(voiceTable, track, child); } } } // MasterMixerをパース var mixer = xml.DocumentElement["mixer"]; var masterUnit = mixer["masterUnit"]; result.Mixer.MasterFeder = int.Parse(masterUnit["vol"].InnerText); result.Mixer.MasterMute = 0; result.Mixer.MasterPanpot = 0; // SlaveMixerをパース result.Mixer.Slave.Clear(); for (int i = 1; i < result.Track.Count; i++) { result.Mixer.Slave.Add(null); } foreach (XmlNode vsUnit in mixer.GetElementsByTagName("vsUnit")) { int vsTrackNo = int.Parse(vsUnit["vsTrackNo"].InnerText); int mute = int.Parse(vsUnit["mute"].InnerText); int solo = int.Parse(vsUnit["solo"].InnerText); int pan = int.Parse(vsUnit["pan"].InnerText); int vol = int.Parse(vsUnit["vol"].InnerText); var slave = new VsqMixerEntry(vol, pan, mute, solo); result.Mixer.Slave[vsTrackNo] = slave; } return(result); }
public SelectedTempoEntry(TempoTableEntry original_, TempoTableEntry editing_) { original = original_; editing = editing_; }