mirror of
https://git.femboyfinancial.jp/james/lipsync.git
synced 2024-11-25 12:02:00 -08:00
414 lines
14 KiB
C#
414 lines
14 KiB
C#
|
/*
|
|||
|
* VsqTrack.cs
|
|||
|
* Copyright (c) 2008-2009 kbinani
|
|||
|
*
|
|||
|
* This file is part of Boare.Lib.Vsq.
|
|||
|
*
|
|||
|
* Boare.Lib.Vsq is free software; you can redistribute it and/or
|
|||
|
* modify it under the terms of the BSD License.
|
|||
|
*
|
|||
|
* Boare.Lib.Vsq 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.
|
|||
|
*/
|
|||
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.IO;
|
|||
|
|
|||
|
namespace Boare.Lib.Vsq {
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Stores the data of a vsq track.
|
|||
|
/// </summary>
|
|||
|
[Serializable]
|
|||
|
public partial class VsqTrack : ICloneable {
|
|||
|
public object Tag;
|
|||
|
/// <summary>
|
|||
|
/// トラックの名前。
|
|||
|
/// </summary>
|
|||
|
public string Name;
|
|||
|
private VsqMetaText m_meta_text;
|
|||
|
private List<Boare.Lib.Vsq.MidiEvent> m_midi_event;
|
|||
|
private int m_edited_start = int.MaxValue;
|
|||
|
private int m_edited_end = int.MinValue;
|
|||
|
public string Renderer = "VOCALOID2";
|
|||
|
|
|||
|
private class SingerEventIterator : Iterator {
|
|||
|
VsqEventList m_list;
|
|||
|
int m_pos;
|
|||
|
|
|||
|
public SingerEventIterator( VsqEventList list ) {
|
|||
|
m_list = list;
|
|||
|
m_pos = -1;
|
|||
|
}
|
|||
|
|
|||
|
public bool hasNext() {
|
|||
|
for ( int i = m_pos + 1; i < m_list.getCount(); i++ ) {
|
|||
|
if ( m_list.getElement( i ).ID.type == VsqIDType.Singer ) {
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
public object next() {
|
|||
|
for ( int i = m_pos + 1; i < m_list.getCount(); i++ ) {
|
|||
|
VsqEvent item = m_list.getElement( i );
|
|||
|
if ( item.ID.type == VsqIDType.Singer ) {
|
|||
|
m_pos = i;
|
|||
|
return item;
|
|||
|
}
|
|||
|
}
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
public void remove() {
|
|||
|
if ( 0 <= m_pos && m_pos < m_list.getCount() ) {
|
|||
|
m_list.removeAt( m_pos );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private class NoteEventIterator : Iterator {
|
|||
|
VsqEventList m_list;
|
|||
|
int m_pos;
|
|||
|
|
|||
|
public NoteEventIterator( VsqEventList list ) {
|
|||
|
m_list = list;
|
|||
|
m_pos = -1;
|
|||
|
}
|
|||
|
|
|||
|
public bool hasNext() {
|
|||
|
for ( int i = m_pos + 1; i < m_list.getCount(); i++ ) {
|
|||
|
if ( m_list.getElement( i ).ID.type == VsqIDType.Anote ) {
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
public object next() {
|
|||
|
for ( int i = m_pos + 1; i < m_list.getCount(); i++ ) {
|
|||
|
VsqEvent item = m_list.getElement( i );
|
|||
|
if ( item.ID.type == VsqIDType.Anote ) {
|
|||
|
m_pos = i;
|
|||
|
return item;
|
|||
|
}
|
|||
|
}
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
public void remove() {
|
|||
|
if ( 0 <= m_pos && m_pos < m_list.getCount() ) {
|
|||
|
m_list.removeAt( m_pos );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private class EventIterator : Iterator{
|
|||
|
private VsqEventList m_list;
|
|||
|
private int m_pos;
|
|||
|
|
|||
|
public EventIterator( VsqEventList list ) {
|
|||
|
m_list = list;
|
|||
|
m_pos = -1;
|
|||
|
}
|
|||
|
|
|||
|
public Boolean hasNext() {
|
|||
|
if ( 0 <= m_pos + 1 && m_pos + 1 < m_list.getCount() ) {
|
|||
|
return true;
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
public Object next() {
|
|||
|
m_pos++;
|
|||
|
return m_list.getElement( m_pos );
|
|||
|
}
|
|||
|
|
|||
|
public void remove() {
|
|||
|
if ( 0 <= m_pos && m_pos < m_list.getCount() ) {
|
|||
|
m_list.removeAt( m_pos );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 歌手変更イベントを,曲の先頭から順に返すIteratorを取得します
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
public Iterator getSingerEventIterator() {
|
|||
|
return new SingerEventIterator( m_meta_text.getEventList() );
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 音符イベントを,曲の先頭から順に返すIteratorを取得します
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
public Iterator getNoteEventIterator() {
|
|||
|
return new NoteEventIterator( m_meta_text.getEventList() );
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// メタテキストを,メモリー上のストリームに出力します
|
|||
|
/// </summary>
|
|||
|
/// <param name="sw"></param>
|
|||
|
/// <param name="encode"></param>
|
|||
|
/// <param name="eos"></param>
|
|||
|
/// <param name="start"></param>
|
|||
|
public void printMetaText( TextMemoryStream sw, int eos, int start ) {
|
|||
|
m_meta_text.print( sw, false, eos, start );
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// メタテキストを,指定されたファイルに出力します
|
|||
|
/// </summary>
|
|||
|
/// <param name="file"></param>
|
|||
|
public void printMetaText( string file ) {
|
|||
|
TextMemoryStream tms = new TextMemoryStream( FileAccess.ReadWrite );
|
|||
|
int count = m_meta_text.getEventList().getCount();
|
|||
|
int clLast = m_meta_text.getEventList().getElement( count - 1 ).Clock + 480;
|
|||
|
m_meta_text.print( tms, true, clLast, 0 );
|
|||
|
using ( StreamWriter sw = new StreamWriter( file ) ) {
|
|||
|
tms.rewind();
|
|||
|
while ( tms.peek() >= 0 ) {
|
|||
|
string line = tms.readLine();
|
|||
|
sw.WriteLine( line );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Masterを取得します
|
|||
|
/// </summary>
|
|||
|
public VsqMaster getMaster() {
|
|||
|
return m_meta_text.master;
|
|||
|
}
|
|||
|
|
|||
|
internal void setMaster( VsqMaster value ) {
|
|||
|
m_meta_text.master = value;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Mixerを取得します
|
|||
|
/// </summary>
|
|||
|
public VsqMixer getMixer() {
|
|||
|
return m_meta_text.mixer;
|
|||
|
}
|
|||
|
|
|||
|
internal void setMixer( VsqMixer value ) {
|
|||
|
m_meta_text.mixer = value;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// このトラックが保持している,指定されたカーブのBPListを取得します
|
|||
|
/// </summary>
|
|||
|
/// <param name="curve"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public VsqBPList getCurve( string curve ) {
|
|||
|
return m_meta_text.getElement( curve );
|
|||
|
}
|
|||
|
|
|||
|
public void setCurve( string curve, VsqBPList value ) {
|
|||
|
m_meta_text.setElement( curve, value );
|
|||
|
}
|
|||
|
|
|||
|
public int getEventCount() {
|
|||
|
return m_meta_text.getEventList().getCount();
|
|||
|
}
|
|||
|
|
|||
|
public VsqEvent getEvent( int index ) {
|
|||
|
return m_meta_text.getEventList().getElement( index );
|
|||
|
}
|
|||
|
|
|||
|
public void setEvent( int index, VsqEvent item ) {
|
|||
|
m_meta_text.getEventList().setElement( index, item );
|
|||
|
}
|
|||
|
|
|||
|
public void addEvent( VsqEvent item ) {
|
|||
|
m_meta_text.getEventList().add( item );
|
|||
|
}
|
|||
|
|
|||
|
public Iterator getEventIterator() {
|
|||
|
return new EventIterator( m_meta_text.getEventList() );
|
|||
|
}
|
|||
|
|
|||
|
public void removeEvent( int index ) {
|
|||
|
m_meta_text.getEventList().removeAt( index );
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// このトラックの,最後に編集が加えられた範囲の,開始位置(クロック)を取得します.
|
|||
|
/// このインスタンスを保持しているVsqFileインスタンスのExecuteメソッドによって自動的に更新されます
|
|||
|
/// </summary>
|
|||
|
public int getEditedStart() {
|
|||
|
return m_edited_start;
|
|||
|
}
|
|||
|
|
|||
|
internal void setEditedStart( int value ) {
|
|||
|
if ( value < m_edited_start ) {
|
|||
|
m_edited_start = value;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// このトラックの,最後に編集が加えられた範囲の,終了位置(クロック)を取得します.
|
|||
|
/// このインスタンスを保持しているVsqFileインスタンスのExecuteメソッドによって自動的に更新されます
|
|||
|
/// </summary>
|
|||
|
public int getEditedEnd() {
|
|||
|
return m_edited_end;
|
|||
|
}
|
|||
|
|
|||
|
internal void setEditedEnd( int value ) {
|
|||
|
if ( m_edited_end < value ) {
|
|||
|
m_edited_end = value;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// このトラックの,編集範囲(EditedStart, EditedEnd)をリセットします.
|
|||
|
/// </summary>
|
|||
|
public void resetEditedArea() {
|
|||
|
m_edited_start = int.MaxValue;
|
|||
|
m_edited_end = int.MinValue;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// このインスタンスのコピーを作成します
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
public object Clone() {
|
|||
|
VsqTrack res = new VsqTrack();
|
|||
|
res.Name = Name;
|
|||
|
if ( m_meta_text != null ) {
|
|||
|
res.m_meta_text = (VsqMetaText)m_meta_text.Clone();
|
|||
|
}
|
|||
|
if ( m_midi_event != null ) {
|
|||
|
res.m_midi_event = new List<MidiEvent>( m_midi_event );
|
|||
|
}
|
|||
|
res.m_edited_start = m_edited_start;
|
|||
|
res.m_edited_end = m_edited_end;
|
|||
|
res.Renderer = Renderer;
|
|||
|
return res;
|
|||
|
}
|
|||
|
|
|||
|
private VsqTrack() {
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Master Trackを構築
|
|||
|
/// </summary>
|
|||
|
/// <param name="tempo"></param>
|
|||
|
/// <param name="numerator"></param>
|
|||
|
/// <param name="denominator"></param>
|
|||
|
public VsqTrack( int tempo, int numerator, int denominator ) {
|
|||
|
this.Name = "Master Track";
|
|||
|
this.m_meta_text = null;
|
|||
|
m_midi_event = new List<MidiEvent>();
|
|||
|
|
|||
|
// テンポを設定
|
|||
|
MidiEvent mi_tempo = new MidiEvent();
|
|||
|
mi_tempo.Clock = 0;
|
|||
|
mi_tempo.FirstByte = 0xff;
|
|||
|
byte b1 = (byte)(tempo & 0xff);
|
|||
|
tempo = tempo >> 8;
|
|||
|
byte b2 = (byte)(tempo & 0xff);
|
|||
|
tempo = tempo >> 8;
|
|||
|
byte b3 = (byte)(tempo & 0xff);
|
|||
|
mi_tempo.Data = new byte[5] { 0x51, 0x03, b3, b2, b1 };
|
|||
|
m_midi_event.Add( mi_tempo );
|
|||
|
|
|||
|
// 拍子を設定
|
|||
|
MidiEvent mi_timesig = MidiEvent.generateTimeSigEvent( 0, numerator, denominator );
|
|||
|
m_midi_event.Add( mi_timesig );
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Master Trackでないトラックを構築。
|
|||
|
/// </summary>
|
|||
|
/// <param name="name"></param>
|
|||
|
/// <param name="singer"></param>
|
|||
|
public VsqTrack( string name, string singer ) {
|
|||
|
Name = name;
|
|||
|
m_meta_text = new VsqMetaText( name, singer );
|
|||
|
m_midi_event = new List<MidiEvent>();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 歌詞の文字数を調べます
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
public int getLyricLength() {
|
|||
|
int counter = 0;
|
|||
|
for ( int i = 0; i < m_meta_text.getEventList().getCount(); i++ ) {
|
|||
|
if ( m_meta_text.getEventList().getElement( i ).ID.type == VsqIDType.Anote ) {
|
|||
|
counter++;
|
|||
|
}
|
|||
|
}
|
|||
|
return counter;
|
|||
|
}
|
|||
|
|
|||
|
public VsqTrack( List<Boare.Lib.Vsq.MidiEvent> midi_events ) {
|
|||
|
m_midi_event = new List<MidiEvent>( midi_events );
|
|||
|
Name = "";
|
|||
|
#if DEBUG
|
|||
|
bocoree.debug.push_log( "VsqTrack..ctor" );
|
|||
|
#endif
|
|||
|
using ( TextMemoryStream sw = new TextMemoryStream() ) {
|
|||
|
for ( int i = 0; i < m_midi_event.Count; i++ ) {
|
|||
|
if ( m_midi_event[i].FirstByte == 0xff && m_midi_event[i].Data.Length > 0 ) {
|
|||
|
// meta textを抽出
|
|||
|
byte type = m_midi_event[i].Data[0];
|
|||
|
if ( type == 0x01 || type == 0x03 ) {
|
|||
|
char[] ch = new char[m_midi_event[i].Data.Length - 1];
|
|||
|
for ( int j = 1; j < midi_events[i].Data.Length; j++ ) {
|
|||
|
ch[j - 1] = (char)midi_events[i].Data[j];
|
|||
|
}
|
|||
|
string line = new string( ch );
|
|||
|
if ( type == 0x01 ) {
|
|||
|
int second_colon = line.IndexOf( ':', 3 );
|
|||
|
line = line.Substring( second_colon + 1 );
|
|||
|
line = line.Replace( "\\n", Environment.NewLine );
|
|||
|
line = line.Replace( "\n", Environment.NewLine );
|
|||
|
sw.write( line );
|
|||
|
} else {
|
|||
|
Name = line;
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
continue;
|
|||
|
}
|
|||
|
}
|
|||
|
sw.rewind();
|
|||
|
m_meta_text = new VsqMetaText( sw );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public List<MidiEvent> getTempoList() {
|
|||
|
List<MidiEvent> list = new List<MidiEvent>();
|
|||
|
for ( int i = 0; i < m_midi_event.Count; i++ ) {
|
|||
|
if ( m_midi_event[i].FirstByte == 0xff && m_midi_event[i].Data.Length >= 4 && m_midi_event[i].Data[0] == 0x51 ) {
|
|||
|
list.Add( m_midi_event[i] );
|
|||
|
}
|
|||
|
}
|
|||
|
return list;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// MidiEventの中から拍子情報を抽出します
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
public List<MidiEvent> getTimeSigList() {
|
|||
|
List<MidiEvent> list = new List<MidiEvent>();
|
|||
|
for ( int i = 0; i < m_midi_event.Count; i++ ) {
|
|||
|
if ( m_midi_event[i].FirstByte == 0xff && m_midi_event[i].Data.Length >= 5 && m_midi_event[i].Data[0] == 0x58 ) {
|
|||
|
list.Add( m_midi_event[i] );
|
|||
|
}
|
|||
|
}
|
|||
|
return list;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
}
|