mirror of
https://github.com/G2-Games/minidisc-cli.git
synced 2025-04-19 11:42:53 -05:00
Updated some interface functions to retrieve multiple tracks, added saveTrackToArray
This commit is contained in:
parent
6e100399d8
commit
1cb19a51d3
3 changed files with 99 additions and 74 deletions
|
@ -2,7 +2,7 @@ use crate::netmd::base;
|
||||||
use crate::netmd::query_utils::{format_query, scan_query, QueryValue};
|
use crate::netmd::query_utils::{format_query, scan_query, QueryValue};
|
||||||
use crate::netmd::utils::{
|
use crate::netmd::utils::{
|
||||||
half_width_to_full_width_range, length_after_encoding_to_jis,
|
half_width_to_full_width_range, length_after_encoding_to_jis,
|
||||||
sanitize_full_width_title, sanitize_half_width_title, time_to_frames
|
sanitize_full_width_title, sanitize_half_width_title, time_to_duration
|
||||||
};
|
};
|
||||||
use encoding_rs::*;
|
use encoding_rs::*;
|
||||||
use rusb;
|
use rusb;
|
||||||
|
@ -23,7 +23,8 @@ enum Track {
|
||||||
Restart = 0x0001,
|
Restart = 0x0001,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum DiscFormat {
|
#[derive(Debug)]
|
||||||
|
pub enum DiscFormat {
|
||||||
LP4 = 0,
|
LP4 = 0,
|
||||||
LP2 = 2,
|
LP2 = 2,
|
||||||
SPMono = 4,
|
SPMono = 4,
|
||||||
|
@ -706,7 +707,7 @@ impl NetMDInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The number of tracks on the disc
|
/// The number of tracks on the disc
|
||||||
pub fn track_count(&self) -> Result<u8, Box<dyn Error>> {
|
pub fn track_count(&self) -> Result<u16, Box<dyn Error>> {
|
||||||
self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead);
|
self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead);
|
||||||
|
|
||||||
let mut query = format_query(
|
let mut query = format_query(
|
||||||
|
@ -725,7 +726,7 @@ impl NetMDInterface {
|
||||||
|
|
||||||
self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::Close);
|
self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::Close);
|
||||||
|
|
||||||
Ok(res[0].to_i64().unwrap() as u8)
|
Ok(res[0].to_i64().unwrap() as u16)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _disc_title(&self, wchar: bool) -> Result<String, Box<dyn Error>> {
|
fn _disc_title(&self, wchar: bool) -> Result<String, Box<dyn Error>> {
|
||||||
|
@ -902,43 +903,8 @@ impl NetMDInterface {
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the title of a track at a specified index
|
/// Gets a list of track titles from a set
|
||||||
pub fn track_title(&self, track: u16, wchar: bool) -> Result<String, Box<dyn Error>> {
|
pub fn track_titles(&self, tracks: Vec<u16>, wchar: bool) -> Result<Vec<String>, Box<dyn Error>> {
|
||||||
let wchar_value = match wchar {
|
|
||||||
true => 3,
|
|
||||||
false => 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut query = format_query(
|
|
||||||
"1806 022018%b %w 3000 0a00 ff00 00000000".to_string(),
|
|
||||||
vec![QueryValue::Number(wchar_value), QueryValue::Number(track as i64)],
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let descriptor_type = match wchar {
|
|
||||||
true => Descriptor::AudioUTOC4TD,
|
|
||||||
false => Descriptor::AudioUTOC1TD,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.change_descriptor_state(&descriptor_type, &DescriptorAction::OpenRead);
|
|
||||||
|
|
||||||
let reply = self.send_query(&mut query, false, false)?;
|
|
||||||
|
|
||||||
let res = scan_query(
|
|
||||||
reply,
|
|
||||||
"1806 022018%? %?%? %?%? %?%? 1000 00%?0000 00%?000a %x".to_string(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
self.change_descriptor_state(&descriptor_type, &DescriptorAction::Close);
|
|
||||||
|
|
||||||
Ok(encoding_rs::SHIFT_JIS
|
|
||||||
.decode(&res[0].to_vec().unwrap())
|
|
||||||
.0
|
|
||||||
.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn all_track_titles(&self, wchar: bool) -> Result<Vec<String>, Box<dyn Error>> {
|
|
||||||
let wchar_value = match wchar {
|
let wchar_value = match wchar {
|
||||||
true => 3,
|
true => 3,
|
||||||
false => 2,
|
false => 2,
|
||||||
|
@ -952,8 +918,7 @@ impl NetMDInterface {
|
||||||
self.change_descriptor_state(&descriptor_type, &DescriptorAction::OpenRead);
|
self.change_descriptor_state(&descriptor_type, &DescriptorAction::OpenRead);
|
||||||
|
|
||||||
let mut track_titles: Vec<String> = vec![];
|
let mut track_titles: Vec<String> = vec![];
|
||||||
for i in 0..self.track_count().unwrap() {
|
for i in tracks {
|
||||||
|
|
||||||
let mut query = format_query(
|
let mut query = format_query(
|
||||||
"1806 022018%b %w 3000 0a00 ff00 00000000".to_string(),
|
"1806 022018%b %w 3000 0a00 ff00 00000000".to_string(),
|
||||||
vec![QueryValue::Number(wchar_value), QueryValue::Number(i as i64)],
|
vec![QueryValue::Number(wchar_value), QueryValue::Number(i as i64)],
|
||||||
|
@ -979,6 +944,12 @@ impl NetMDInterface {
|
||||||
Ok(track_titles)
|
Ok(track_titles)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the title of a track at an index
|
||||||
|
pub fn track_title(&self, track: u16, wchar: bool) -> Result<String, Box<dyn Error>> {
|
||||||
|
let title = self.track_titles([track].into(), wchar).unwrap()[0].clone();
|
||||||
|
Ok(title)
|
||||||
|
}
|
||||||
|
|
||||||
// Sets the title of the disc
|
// Sets the title of the disc
|
||||||
pub fn set_disc_title(&self, title: String, wchar: bool) -> Result<(), Box<dyn Error>> {
|
pub fn set_disc_title(&self, title: String, wchar: bool) -> Result<(), Box<dyn Error>> {
|
||||||
let current_title = self._disc_title(wchar)?;
|
let current_title = self._disc_title(wchar)?;
|
||||||
|
@ -1112,7 +1083,7 @@ impl NetMDInterface {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _get_track_info(&self, track: u16, p1: i32, p2: i32) -> Result<Vec<u8>, Box<dyn Error>> {
|
fn _track_info(&self, track: u16, p1: i32, p2: i32) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead);
|
self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead);
|
||||||
|
|
||||||
let mut query = format_query(
|
let mut query = format_query(
|
||||||
|
@ -1132,11 +1103,12 @@ impl NetMDInterface {
|
||||||
return Ok(res[0].to_vec().unwrap());
|
return Ok(res[0].to_vec().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn all_track_length(&self) -> Result<Vec<std::time::Duration>, Box<dyn Error>> {
|
/// Gets the length of tracks as a `std::time::Duration` from a set
|
||||||
|
pub fn track_lengths(&self, tracks: Vec<u16>) -> Result<Vec<std::time::Duration>, Box<dyn Error>> {
|
||||||
let mut times: Vec<std::time::Duration> = vec![];
|
let mut times: Vec<std::time::Duration> = vec![];
|
||||||
|
|
||||||
self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead);
|
self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead);
|
||||||
for track in 0..self.track_count()? {
|
for track in tracks {
|
||||||
let mut query = format_query(
|
let mut query = format_query(
|
||||||
"1806 02201001 %w %w %w ff00 00000000".to_string(),
|
"1806 02201001 %w %w %w ff00 00000000".to_string(),
|
||||||
vec![
|
vec![
|
||||||
|
@ -1154,8 +1126,8 @@ impl NetMDInterface {
|
||||||
|
|
||||||
let times_num: Vec<u64> = result.into_iter().map(|v| v.to_i64().unwrap() as u64).collect();
|
let times_num: Vec<u64> = result.into_iter().map(|v| v.to_i64().unwrap() as u64).collect();
|
||||||
|
|
||||||
let time_micros = (times_num[0] * 3600000000) + (times_num[1] * 60000000) + (times_num[2] * 1000000) + (times_num[3] * 11600);
|
let length = time_to_duration(×_num);
|
||||||
times.push(std::time::Duration::from_micros(time_micros));
|
times.push(length);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::Close);
|
self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::Close);
|
||||||
|
@ -1164,23 +1136,13 @@ impl NetMDInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the length of a track as a `std::time::Duration`
|
/// Gets the length of a track as a `std::time::Duration`
|
||||||
pub fn track_length(&self, track_number: u16) -> Result<std::time::Duration, Box<dyn Error>> {
|
pub fn track_length(&self, track: u16) -> Result<std::time::Duration, Box<dyn Error>> {
|
||||||
let raw_value = self._get_track_info(track_number, 0x3000, 0x0100)?;
|
Ok(self.track_lengths([track].into())?[0])
|
||||||
let result = scan_query(raw_value, "01 0006 0000 %B %B %B %B".to_string())?;
|
|
||||||
|
|
||||||
let hours = result[0].to_i64().unwrap();
|
|
||||||
let minutes = result[1].to_i64().unwrap();
|
|
||||||
let seconds = result[2].to_i64().unwrap();
|
|
||||||
let frames = result[3].to_i64().unwrap();
|
|
||||||
|
|
||||||
let time_ms = (hours * 3600000000) + (minutes * 60000000) + (seconds * 1000000) + (frames * 11600);
|
|
||||||
|
|
||||||
Ok(std::time::Duration::from_micros(time_ms as u64))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the encoding of a track (SP, LP2, LP4)
|
/// Gets the encoding of a track (SP, LP2, LP4)
|
||||||
pub fn track_encoding(&self, track_number: u16) -> Result<Encoding, Box<dyn Error>> {
|
pub fn track_encoding(&self, track_number: u16) -> Result<Encoding, Box<dyn Error>> {
|
||||||
let raw_value = self._get_track_info(track_number, 0x3080, 0x0700)?;
|
let raw_value = self._track_info(track_number, 0x3080, 0x0700)?;
|
||||||
let result = scan_query(raw_value, "07 0004 0110 %b %b".to_string())?;
|
let result = scan_query(raw_value, "07 0004 0110 %b %b".to_string())?;
|
||||||
|
|
||||||
let final_encoding = match result[0].to_i64() {
|
let final_encoding = match result[0].to_i64() {
|
||||||
|
@ -1212,6 +1174,7 @@ impl NetMDInterface {
|
||||||
Ok(res[0].to_i64().unwrap() as u8)
|
Ok(res[0].to_i64().unwrap() as u8)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the disc capacity as a `std::time::Duration`
|
||||||
pub fn disc_capacity(&self) -> Result<[std::time::Duration; 3], Box<dyn Error>> {
|
pub fn disc_capacity(&self) -> Result<[std::time::Duration; 3], Box<dyn Error>> {
|
||||||
self.change_descriptor_state(&Descriptor::RootTD, &DescriptorAction::OpenRead);
|
self.change_descriptor_state(&Descriptor::RootTD, &DescriptorAction::OpenRead);
|
||||||
let mut query = format_query(
|
let mut query = format_query(
|
||||||
|
@ -1221,8 +1184,6 @@ impl NetMDInterface {
|
||||||
let reply = self.send_query(&mut query, false, false)?;
|
let reply = self.send_query(&mut query, false, false)?;
|
||||||
let mut result: [std::time::Duration; 3] = [std::time::Duration::from_secs(0); 3];
|
let mut result: [std::time::Duration; 3] = [std::time::Duration::from_secs(0); 3];
|
||||||
|
|
||||||
println!("{:?}", reply);
|
|
||||||
|
|
||||||
// 8003 changed to %?03 - Panasonic returns 0803 instead. This byte's meaning is unknown
|
// 8003 changed to %?03 - Panasonic returns 0803 instead. This byte's meaning is unknown
|
||||||
let res = scan_query(
|
let res = scan_query(
|
||||||
reply,
|
reply,
|
||||||
|
@ -1230,11 +1191,9 @@ impl NetMDInterface {
|
||||||
)?; //25^
|
)?; //25^
|
||||||
let res_num: Vec<u64> = res.into_iter().map(|v| v.to_i64().unwrap() as u64).collect();
|
let res_num: Vec<u64> = res.into_iter().map(|v| v.to_i64().unwrap() as u64).collect();
|
||||||
|
|
||||||
println!("{:?}", res_num);
|
|
||||||
// Create 3 values, `Frames Used`, `Frames Total`, and `Frames Left`
|
// Create 3 values, `Frames Used`, `Frames Total`, and `Frames Left`
|
||||||
for i in 0..3 {
|
for i in 0..3 {
|
||||||
let tmp = &res_num[(4 * i)..=(4 * i) + 3];
|
let tmp = &res_num[(4 * i)..=(4 * i) + 3];
|
||||||
println!("{:?}", tmp);
|
|
||||||
let time_micros = (tmp[0] * 3600000000) + (tmp[1] * 60000000) + (tmp[2] * 1000000) + (tmp[3] * 11600);
|
let time_micros = (tmp[0] * 3600000000) + (tmp[1] * 60000000) + (tmp[2] * 1000000) + (tmp[3] * 11600);
|
||||||
result[i] = std::time::Duration::from_micros(time_micros);
|
result[i] = std::time::Duration::from_micros(time_micros);
|
||||||
}
|
}
|
||||||
|
@ -1243,4 +1202,56 @@ impl NetMDInterface {
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn recording_parameters(&self) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
|
self.change_descriptor_state(&Descriptor::OperatingStatusBlock, &DescriptorAction::OpenRead);
|
||||||
|
let mut query = format_query(
|
||||||
|
"1809 8001 0330 8801 0030 8805 0030 8807 00 ff00 00000000".to_string(),
|
||||||
|
vec![],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let reply = self.send_query(&mut query, false, false)?;
|
||||||
|
|
||||||
|
let res = scan_query(reply, "1809 8001 0330 8801 0030 8805 0030 8807 00 1000 000e0000 000c 8805 0008 80e0 0110 %b %b 4000".to_string())?;
|
||||||
|
|
||||||
|
self.change_descriptor_state(&Descriptor::OperatingStatusBlock, &DescriptorAction::Close);
|
||||||
|
|
||||||
|
Ok(res.into_iter().map(|x| x.to_i64().unwrap() as u8).collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the bytes of a track
|
||||||
|
///
|
||||||
|
/// This can only be executed on an MZ-RH1 / M200
|
||||||
|
pub fn save_track_to_array(&self, track: u16) -> Result<(DiscFormat, u16, Vec<u8>), Box<dyn Error>> {
|
||||||
|
let mut query = format_query(
|
||||||
|
"1800 080046 f003010330 ff00 1001 %w".to_string(),
|
||||||
|
vec![
|
||||||
|
QueryValue::Number((track + 1) as i64)
|
||||||
|
],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let reply = self.send_query(&mut query, false, false)?;
|
||||||
|
|
||||||
|
let res = scan_query(reply, "1800 080046 f0030103 300000 1001 %w %b %d".to_string())?;
|
||||||
|
|
||||||
|
let frames = res[0].to_i64().unwrap() as u16;
|
||||||
|
let codec = res[1].to_i64().unwrap() as u8;
|
||||||
|
let length = res[2].to_i64().unwrap() as u32;
|
||||||
|
|
||||||
|
let result = self.net_md_device.read_bulk(length, 0x10000)?;
|
||||||
|
|
||||||
|
scan_query(self.read_reply(false)?, "1800 080046 f003010330 0000 1001 %?%? %?%?".to_string())?;
|
||||||
|
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||||
|
|
||||||
|
let format: DiscFormat = match codec & 0x06 {
|
||||||
|
0 => DiscFormat::LP4,
|
||||||
|
2 => DiscFormat::LP2,
|
||||||
|
4 => DiscFormat::SPMono,
|
||||||
|
6 => DiscFormat::SPStereo,
|
||||||
|
_ => return Err("DiscFormat out of range 0..6".into())
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((format, frames, result))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -178,7 +178,7 @@ pub fn agressive_sanitize_title(title: &String) -> String {
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn time_to_frames(time: &[i64]) -> i64 {
|
pub fn time_to_duration(time: &Vec<u64>) -> std::time::Duration {
|
||||||
assert_eq!(time.len(), 4);
|
assert_eq!(time.len(), 4);
|
||||||
return (((time[0] as f64 * 60.0 + time[1] as f64) * 60.0 + time[2] as f64) * 424.0 + time[3] as f64) as i64
|
std::time::Duration::from_micros((time[0] * 3600000000) + (time[1] * 60000000) + (time[2] * 1000000) + (time[3] * 11600))
|
||||||
}
|
}
|
||||||
|
|
28
src/main.rs
28
src/main.rs
|
@ -1,4 +1,5 @@
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
use minidisc_rs::netmd::interface;
|
use minidisc_rs::netmd::interface;
|
||||||
use rusb;
|
use rusb;
|
||||||
|
@ -34,10 +35,12 @@ fn main() {
|
||||||
);
|
);
|
||||||
//println!("TEST CASE: {:?}", player_controller.set_track_title(3, "初音ミクの消失".to_string(), false).unwrap());
|
//println!("TEST CASE: {:?}", player_controller.set_track_title(3, "初音ミクの消失".to_string(), false).unwrap());
|
||||||
|
|
||||||
|
let track_count = player_controller.track_count().unwrap();
|
||||||
|
|
||||||
let now = std::time::SystemTime::now();
|
let now = std::time::SystemTime::now();
|
||||||
let times = player_controller.all_track_length().unwrap();
|
let times = player_controller.track_lengths((0u16..track_count).collect()).unwrap();
|
||||||
let titles_hw = player_controller.all_track_titles(false).unwrap();
|
let titles_hw = player_controller.track_titles((0u16..track_count).collect(), false).unwrap();
|
||||||
let titles_fw = player_controller.all_track_titles(true).unwrap();
|
let titles_fw = player_controller.track_titles((0u16..track_count).collect(), true).unwrap();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
let now = std::time::SystemTime::now();
|
let now = std::time::SystemTime::now();
|
||||||
|
@ -50,16 +53,21 @@ fn main() {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━
|
"
|
||||||
Tracks: │ {: <21} │
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
Tracks: {:0>2}, Length: {:0>2} minutes
|
||||||
|
───────────────┬┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┬┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
|
||||||
|
│ Half Width │ Full Width
|
||||||
|
━━━━━━━━━━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
Disc Title: │ {: >21} │ {}
|
Disc Title: │ {: >21} │ {}
|
||||||
────┬──────────┼───────────────────────┼─────────────────────────",
|
────┬──────────┼───────────────────────┼─────────────────────────",
|
||||||
player_controller.track_count().unwrap(),
|
track_count,
|
||||||
|
player_controller.disc_capacity().unwrap()[0].as_secs() / 60,
|
||||||
player_controller.disc_title(false).unwrap(),
|
player_controller.disc_title(false).unwrap(),
|
||||||
player_controller.disc_title(true).unwrap()
|
player_controller.disc_title(true).unwrap()
|
||||||
);
|
);
|
||||||
|
|
||||||
for i in 0..player_controller.track_count().unwrap() {
|
for i in 0..track_count {
|
||||||
println!(
|
println!(
|
||||||
" {: >2} │ {:0>2}:{:0>2}:{:0>2} │ {: >21} │ {}",
|
" {: >2} │ {:0>2}:{:0>2}:{:0>2} │ {: >21} │ {}",
|
||||||
i + 1,
|
i + 1,
|
||||||
|
@ -71,5 +79,11 @@ fn main() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
println!("────┴──────────┴───────────────────────┴─────────────────────────\nFinished in: [{}ms]", now.elapsed().unwrap().as_millis());
|
println!("────┴──────────┴───────────────────────┴─────────────────────────\nFinished in: [{}ms]", now.elapsed().unwrap().as_millis());
|
||||||
|
|
||||||
|
|
||||||
|
println!("\n\n\nAttempting to get track 1");
|
||||||
|
let returned_bytes = player_controller.save_track_to_array(0);
|
||||||
|
|
||||||
|
println!("\n{:?}", returned_bytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue