use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use crate::Result;
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Vpr {
pub version: Version,
#[serde(default = "vpr_vender")]
pub vender: String, pub title: String,
pub master_track: MasterTrack,
pub voices: Vec<Voice>,
pub tracks: Vec<Track>,
}
impl From<super::vsqx4::Vsqx4> for Vpr {
fn from(v: super::vsqx4::Vsqx4) -> Self {
super::v4to5::convert_vsqx4_to_vpr(&v)
}
}
impl From<super::vsqx3::Vsqx3> for Vpr {
fn from(v: super::vsqx3::Vsqx3) -> Self {
super::v4to5::convert_vsqx4_to_vpr(&v.into())
}
}
impl Vpr {
pub fn open<P: AsRef<std::path::Path>>(path: P) -> Result<Vpr> {
use std::fs::File;
Self::from_reader(File::open(path)?)
}
pub fn from_bytes(bytes: &[u8]) -> Result<Vpr> {
use std::io::Cursor;
Self::from_reader(Cursor::new(bytes))
}
pub fn from_reader<R: std::io::Read + std::io::Seek>(reader: R) -> Result<Vpr> {
use zip::ZipArchive;
let mut z = ZipArchive::new(reader)?;
let seq = z.by_name("Project\\sequence.json");
let seq = if seq.is_ok() {
seq?
} else {
drop(seq);
z.by_name("Project/sequence.json")?
};
Ok(serde_json::from_reader(seq)?)
}
pub fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(self)?)
}
pub fn to_json_binary(&self) -> Result<Vec<u8>> {
Ok(serde_json::to_vec(self)?)
}
pub fn write_vpr<W: std::io::Write + std::io::Seek>(&self, writer: W) -> Result<()> {
use std::io::Write;
use zip::{write::FileOptions, ZipWriter};
let mut w = ZipWriter::new(writer);
w.start_file("Project\\sequence.json", FileOptions::default())?;
w.write_all(&self.to_json_binary()?)?;
w.finish()?;
Ok(())
}
}
pub(crate) fn vpr_vender() -> String {
"Yamaha Corporation".into()
}
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq)]
pub struct Version {
pub major: u64,
pub minor: u64,
pub revision: u64,
}
impl Version {
pub fn new(major: u64, minor: u64, revision: u64) -> Self {
Self {
major,
minor,
revision,
}
}
}
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct MasterTrack {
pub sampling_rate: u64,
#[serde(rename = "loop")]
pub loop_info: Loop,
pub tempo: Tempo,
pub time_sig: TimeSignature,
pub volume: Volume,
}
#[derive(Default, Clone, Deserialize, Serialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Loop {
pub is_enabled: bool,
pub begin: i64,
pub end: i64,
}
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Tempo {
pub is_folded: bool,
pub height: f64,
pub global: GlobalTempo,
pub events: Vec<ControlChange>,
}
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct GlobalTempo {
pub is_enabled: bool,
pub value: u64,
}
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct TimeSignature {
pub is_folded: bool,
pub events: Vec<TimeSignatureEvent>,
}
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq)]
pub struct TimeSignatureEvent {
pub bar: i64,
#[serde(rename = "numer")]
pub numerator: i64,
#[serde(rename = "denom")]
pub denominator: i64,
}
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Volume {
pub is_folded: bool,
pub height: f64,
pub events: Vec<ControlChange>,
}
impl Default for Volume {
fn default() -> Self {
Self {
is_folded: true,
height: 0.0,
events: vec![ControlChange { pos: 0, value: 0 }],
}
}
}
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Panpot {
pub is_folded: bool,
pub height: f64,
pub events: Vec<ControlChange>,
}
impl Default for Panpot {
fn default() -> Self {
Self {
is_folded: true,
height: 0.0,
events: vec![ControlChange { pos: 0, value: 0 }],
}
}
}
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Voice {
#[serde(rename = "compID")]
pub comp_id: String,
#[serde(rename = "langID")]
#[serde(skip_serializing_if = "Option::is_none")]
pub lang_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
}
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Track {
#[serde(rename = "type")]
pub track_type: i64,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
pub color: i64,
pub bus_no: i64,
pub is_folded: bool,
pub height: f64,
pub volume: Volume,
pub panpot: Panpot,
pub is_muted: bool,
pub is_solo_mode: bool,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub parts: Vec<Part>,
}
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Part {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
pub pos: u64,
pub duration: u64,
pub style_name: String,
pub voice: Voice,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub midi_effects: Vec<MidiEffect>,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub notes: Vec<Note>,
}
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct MidiEffect {
pub id: String,
pub is_bypassed: bool,
pub is_folded: bool,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub parameters: Vec<Parameter>,
}
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Parameter {
pub name: String,
pub value: serde_json::Value,
}
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Note {
pub lyric: String,
pub phoneme: String,
pub is_protected: bool,
pub pos: i64,
pub duration: u64,
pub number: i64,
pub velocity: u8,
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub exp: HashMap<String, i64>,
pub singing_skill: Option<SingingSkill>,
pub vibrato: Vibrato,
}
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct SingingSkill {
pub duration: i64,
pub weight: SkillWeight,
}
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct SkillWeight {
pub pre: i64,
pub post: i64,
}
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Vibrato {
#[serde(rename = "type")]
pub vibrato_type: i64,
pub duration: i64,
}
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq)]
pub struct ControlChange<T = i64> {
pub pos: i64,
pub value: T,
}
#[test]
#[cfg(test)]
fn test_vpr_open() {
Vpr::from_bytes(include_bytes!("test/v5.vpr")).unwrap();
}
#[test]
#[cfg(test)]
fn test_vpr_to_vsqx4() {
use super::vsqx4::Vsqx4;
let vpr = Vpr::from_bytes(include_bytes!("test/v5.vpr")).unwrap();
let v4 = Vsqx4::from(vpr);
v4.write("./test_vocaloid.vsqx").unwrap();
}
#[test]
#[cfg(test)]
fn test_vpr_serde() {
use serde_json;
let vpr: Vpr = serde_json::from_str(include_str!("test/vpr.json")).unwrap();
let vpr2 = serde_json::to_string(&vpr).unwrap();
let vpr2: Vpr = serde_json::from_str(&vpr2).unwrap();
assert_eq!(vpr, vpr2);
}
#[test]
#[ignore] #[cfg(test)]
fn test_vpr_serde_full() {
use serde_json;
let vpr: Vpr = serde_json::from_str(include_str!("test/vpr.json")).unwrap();
let vpr = serde_json::to_string(&vpr).unwrap();
let vpr_orig: serde_json::Value = serde_json::from_str(include_str!("test/vpr.json")).unwrap();
let vpr_json: serde_json::Value = serde_json::from_str(&vpr).unwrap();
assert_eq!(vpr_orig, vpr_json);
}