lipsync/Boare.Lib.Vsq/VsqTrack.cs
2024-05-20 00:17:44 +00:00

603 lines
20 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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.
*/
#if JAVA
package org.kbinani.vsq;
import java.io.*;
import java.util.*;
import org.kbinani.*;
#else
using System;
using bocoree;
using bocoree.java.util;
using bocoree.java.io;
namespace Boare.Lib.Vsq {
using boolean = System.Boolean;
#endif
/// <summary>
/// Stores the data of a vsq track.
/// </summary>
#if JAVA
public class VsqTrack implements Cloneable, Serializable {
#else
[Serializable]
public class VsqTrack : ICloneable {
#endif
public String Tag;
/// <summary>
/// トラックの名前。
/// </summary>
//public String Name;
public VsqMetaText MetaText;
private int m_edited_start = int.MaxValue;
private int m_edited_end = int.MinValue;
#if JAVA
private class SingerEventIterator implements Iterator{
#else
private class SingerEventIterator : Iterator {
#endif
VsqEventList m_list;
int m_pos;
public SingerEventIterator( VsqEventList list ) {
m_list = list;
m_pos = -1;
}
public boolean 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 );
}
}
}
#if JAVA
private class NoteEventIterator implements Iterator{
#else
private class NoteEventIterator : Iterator {
#endif
VsqEventList m_list;
int m_pos;
public NoteEventIterator( VsqEventList list ) {
m_list = list;
m_pos = -1;
}
public boolean hasNext() {
int count = m_list.getCount();
for ( int i = m_pos + 1; i < count; i++ ) {
if ( m_list.getElement( i ).ID.type == VsqIDType.Anote ) {
return true;
}
}
return false;
}
public Object next() {
int count = m_list.getCount();
for ( int i = m_pos + 1; i < count; 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 );
}
}
}
#if JAVA
private class EventIterator implements Iterator{
#else
private class EventIterator : Iterator {
#endif
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 );
}
}
}
public String getName() {
if ( MetaText == null || (MetaText != null && MetaText.Common == null) ) {
return "Master Track";
} else {
return MetaText.Common.Name;
}
}
public void setName( String value ) {
if ( MetaText != null ) {
if ( MetaText.Common == null ) {
MetaText.Common = new VsqCommon();
}
MetaText.Common.Name = value;
}
}
#if !JAVA
[Obsolete]
public String Name {
get {
return getName();
}
set {
setName( value );
}
}
#endif
/// <summary>
/// ピッチベンド。Cent単位
/// </summary>
/// <param name="clock"></param>
/// <returns></returns>
public double getPitchAt( int clock ) {
double inv2_13 = 1.0 / 8192.0;
int pit = MetaText.PIT.getValue( clock );
int pbs = MetaText.PBS.getValue( clock );
return (double)pit * (double)pbs * inv2_13 * 100.0;
}
/// <summary>
/// 指定したクロック位置において、歌唱を担当している歌手のVsqEventを返します。
/// </summary>
/// <param name="clock"></param>
/// <returns></returns>
public VsqEvent getSingerEventAt( int clock ) {
VsqEvent last = null;
for ( Iterator itr = getSingerEventIterator(); itr.hasNext(); ) {
VsqEvent item = (VsqEvent)itr.next();
if ( clock < item.Clock ) {
return last;
}
last = item;
}
return last;
}
public void sortEvent() {
MetaText.Events.sort();
}
/// <summary>
/// 歌手変更イベントを曲の先頭から順に返すIteratorを取得します
/// </summary>
/// <returns></returns>
public Iterator getSingerEventIterator() {
return new SingerEventIterator( MetaText.getEventList() );
}
/// <summary>
/// 音符イベントを曲の先頭から順に返すIteratorを取得します
/// </summary>
/// <returns></returns>
public Iterator getNoteEventIterator() {
if ( MetaText == null ) {
return new NoteEventIterator( new VsqEventList() );
} else {
return new NoteEventIterator( MetaText.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 )
#if JAVA
throws IOException
#endif
{
MetaText.print( sw, eos, start );
}
/// <summary>
/// メタテキストを,指定されたファイルに出力します
/// </summary>
/// <param name="file"></param>
public void printMetaText( String file )
#if JAVA
throws IOException
#endif
{
TextMemoryStream tms = new TextMemoryStream();
int count = MetaText.getEventList().getCount();
int clLast = MetaText.getEventList().getElement( count - 1 ).Clock + 480;
MetaText.print( tms, clLast, 0 );
BufferedWriter sw = null;
try {
sw = new BufferedWriter( new FileWriter( file ) );
tms.rewind();
while ( tms.peek() >= 0 ) {
String line = tms.readLine();
sw.write( line );
sw.newLine();
}
} catch ( Exception ex ) {
} finally {
if ( sw != null ) {
try {
sw.close();
} catch ( Exception ex2 ) {
}
}
}
}
/// <summary>
/// Masterを取得します
/// </summary>
public VsqMaster getMaster() {
return MetaText.master;
}
public void setMaster( VsqMaster value ) {
MetaText.master = value;
}
/// <summary>
/// Mixerを取得します
/// </summary>
public VsqMixer getMixer() {
return MetaText.mixer;
}
public void setMixer( VsqMixer value ) {
MetaText.mixer = value;
}
/// <summary>
/// Commonを取得します
/// </summary>
/// <returns></returns>
public VsqCommon getCommon() {
return MetaText.Common;
}
/// <summary>
/// 指定したトラックのレンダラーを変更します
/// </summary>
/// <param name="track"></param>
/// <param name="new_renderer"></param>
/// <param name="singers"></param>
public void changeRenderer( String new_renderer, Vector<VsqID> singers ) {
VsqID default_id = null;
int singers_size = singers.size();
if ( singers_size <= 0 ) {
default_id = new VsqID();
default_id.type = VsqIDType.Singer;
default_id.IconHandle = new IconHandle();
default_id.IconHandle.IconID = "$0701" + PortUtil.toHexString( 0, 4 );
default_id.IconHandle.IDS = "Unknown";
default_id.IconHandle.Index = 0;
default_id.IconHandle.Language = 0;
default_id.IconHandle.setLength( 1 );
default_id.IconHandle.Original = 0;
default_id.IconHandle.Program = 0;
default_id.IconHandle.Caption = "";
} else {
default_id = singers.get( 0 );
}
for ( Iterator itr = getSingerEventIterator(); itr.hasNext(); ) {
VsqEvent ve = (VsqEvent)itr.next();
int program = ve.ID.IconHandle.Program;
boolean found = false;
for ( int i = 0; i < singers_size; i++ ) {
VsqID id = singers.get( i );
if ( program == id.IconHandle.Program ) {
ve.ID = (VsqID)id.clone();
found = true;
break;
}
}
if ( !found ) {
VsqID add = (VsqID)default_id.clone();
add.IconHandle.Program = program;
ve.ID = add;
}
}
MetaText.Common.Version = new_renderer;
}
/// <summary>
/// このトラックが保持している指定されたカーブのBPListを取得します
/// </summary>
/// <param name="curve"></param>
/// <returns></returns>
public VsqBPList getCurve( String curve ) {
return MetaText.getElement( curve );
}
public void setCurve( String curve, VsqBPList value ) {
MetaText.setElement( curve, value );
}
public int getEventCount() {
return MetaText.getEventList().getCount();
}
public VsqEvent getEvent( int index ) {
return MetaText.getEventList().getElement( index );
}
public VsqEvent findEventFromID( int internal_id ) {
return MetaText.getEventList().findFromID( internal_id );
}
public void setEvent( int index, VsqEvent item ) {
MetaText.getEventList().setElement( index, item );
}
public void addEvent( VsqEvent item ) {
MetaText.getEventList().add( item );
}
public Iterator getEventIterator() {
return new EventIterator( MetaText.getEventList() );
}
public void removeEvent( int index ) {
MetaText.getEventList().removeAt( index );
}
/// <summary>
/// このトラックの,最後に編集が加えられた範囲の,開始位置(クロック)を取得します.
/// </summary>
public int getEditedStart() {
return m_edited_start;
}
public void setEditedStart( int value ) {
if ( value < m_edited_start ) {
m_edited_start = value;
}
}
/// <summary>
/// このトラックの,最後に編集が加えられた範囲の,終了位置(クロック)を取得します.
/// </summary>
public int getEditedEnd() {
return m_edited_end;
}
public 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.setName( getName() );
if ( MetaText != null ) {
res.MetaText = (VsqMetaText)MetaText.clone();
}
res.m_edited_start = m_edited_start;
res.m_edited_end = m_edited_end;
res.Tag = Tag;
return res;
}
#if !JAVA
public object Clone() {
return clone();
}
#endif
/// <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";
// metatextがnullのときトラック名はMaster Track
this.MetaText = null;
}
/// <summary>
/// Master Trackでないトラックを構築。
/// </summary>
/// <param name="name"></param>
/// <param name="singer"></param>
public VsqTrack( String name, String singer ) {
MetaText = new VsqMetaText( name, singer );
}
#if JAVA
public VsqTrack(){
this( "Voice1", "Miku" );
#else
public VsqTrack()
: this( "Voice1", "Miku" ) {
#endif
}
/// <summary>
/// 歌詞の文字数を調べます
/// </summary>
/// <returns></returns>
public int getLyricLength() {
int counter = 0;
for ( int i = 0; i < MetaText.getEventList().getCount(); i++ ) {
if ( MetaText.getEventList().getElement( i ).ID.type == VsqIDType.Anote ) {
counter++;
}
}
return counter;
}
public VsqTrack( Vector<MidiEvent> midi_event, String encoding ) {
#if DEBUG
bocoree.debug.push_log( "VsqTrack..ctor" );
#endif
String track_name = "";
TextMemoryStream sw = null;
try {
sw = new TextMemoryStream();
int count = midi_event.size();
Vector<Byte> buffer = new Vector<Byte>();
for ( int i = 0; i < count; i++ ) {
MidiEvent item = midi_event.get( i );
if ( item.firstByte == 0xff && item.data.Length > 0 ) {
// meta textを抽出
byte type = item.data[0];
if ( type == 0x01 || type == 0x03 ) {
//String line = PortUtil.getDecodedString( encoding, dat, 1, dat.Length - 1 );
if ( type == 0x01 ) {
int colon_count = 0;
for ( int j = 0; j < item.data.Length - 1; j++ ) {
byte d = item.data[j + 1];
if ( d == 0x3a ) {
colon_count++;
if ( colon_count <= 2 ) {
continue;
}
}
if ( colon_count < 2 ) {
continue;
}
buffer.add( d );
}
int index_0x0a = buffer.indexOf( 0x0a );
while ( index_0x0a >= 0 ) {
byte[] cpy = new byte[index_0x0a];
for ( int j = 0; j < index_0x0a; j++ ) {
cpy[j] = buffer.get( 0 );
buffer.removeElementAt( 0 );
}
String line = PortUtil.getDecodedString( encoding, cpy );
sw.writeLine( line );
buffer.removeElementAt( 0 );
index_0x0a = buffer.indexOf( 0x0a );
}
/*int second_colon = line.IndexOf( ':', 3 );
line = line.Substring( second_colon + 1 );
line = line.Replace( "\\n", "\n" );
String[] lines = PortUtil.splitString( line, '\n' );
int c = lines.Length;
for ( int j = 0; j < c; j++ ) {
if ( j < c - 1 ) {
sw.writeLine( lines[j] );
} else {
sw.write( lines[j] );
}
}*/
} else {
for ( int j = 0; j < item.data.Length - 1; j++ ) {
buffer.add( item.data[j + 1] );
}
track_name = PortUtil.getDecodedString( encoding,
PortUtil.convertByteArray( buffer.toArray( new Byte[] { } ) ) );
#if DEBUG
Console.WriteLine( "VsqTrack#.ctor; track_name=" + track_name );
#endif
buffer.clear();
}
}
} else {
continue;
}
}
sw.rewind();
MetaText = new VsqMetaText( sw );
setName( track_name );
} catch ( Exception ex ) {
PortUtil.println( "com.boare.vsq.VsqTrack#.ctor; ex=" + ex );
} finally {
if ( sw != null ) {
try {
sw.close();
} catch ( Exception ex2 ) {
}
}
}
}
}
#if !JAVA
}
#endif