|
@@ -1,19 +1,18 @@
|
|
|
use std::path::{Path, PathBuf};
|
|
|
-// use std::io::BufReader;
|
|
|
use std::thread;
|
|
|
use std::time::Duration;
|
|
|
use std::fs::File;
|
|
|
use std::io::{Cursor, Read};
|
|
|
-use std::sync::Arc;
|
|
|
+use std::sync::{Arc, Mutex};
|
|
|
+use std::io::{BufWriter, BufReader};
|
|
|
+use std::collections::HashMap;
|
|
|
use rodio::Decoder;
|
|
|
use crate::rodio::Source;
|
|
|
-
|
|
|
-use std::io::BufWriter;
|
|
|
-use std::io::BufReader;
|
|
|
-use std::collections::HashMap;
|
|
|
use serde::{Serializer};
|
|
|
use crate::serde::ser::SerializeMap;
|
|
|
+use std::sync::MutexGuard;
|
|
|
// pub fn collect
|
|
|
+use log::*;
|
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Default, Debug)]
|
|
@@ -28,7 +27,10 @@ pub struct Sound {
|
|
|
pub roll: i32,
|
|
|
pub rollrate: i32,
|
|
|
pub trim: i32,
|
|
|
- pub speed: f32,
|
|
|
+ #[serde(skip)]
|
|
|
+ // pub active: bool,
|
|
|
+ pub active: Arc<Mutex<bool>>,
|
|
|
+ pub speed: f32,
|
|
|
#[serde(default)]
|
|
|
pub reverse: bool
|
|
|
}
|
|
@@ -113,6 +115,8 @@ impl Sound {
|
|
|
_ => self.rollrate
|
|
|
};
|
|
|
|
|
|
+ *self.active.lock().unwrap() = true;
|
|
|
+ // *self.active = true;
|
|
|
if self.roll == 0 {
|
|
|
match Decoder::new(self.cursor()) {
|
|
|
Ok(ok_decoder) => {
|
|
@@ -130,11 +134,17 @@ impl Sound {
|
|
|
.speed(self.speed)
|
|
|
.amplify(self.volume)
|
|
|
.convert_samples());
|
|
|
- }
|
|
|
|
|
|
+ // match rodio::play_once(&device, ok_decoder) {
|
|
|
+ // Ok(s) => {},
|
|
|
+ // Err(_) => {}
|
|
|
+ // }
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+ thread::sleep(Duration::from_millis(delay));
|
|
|
|
|
|
|
|
|
-
|
|
|
}
|
|
|
Err(e) => {
|
|
|
dbg!(e);
|
|
@@ -142,6 +152,7 @@ impl Sound {
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
+ // Roll the sound
|
|
|
for _ in 0..self.roll+1 {
|
|
|
|
|
|
match Decoder::new(self.cursor()) {
|
|
@@ -150,7 +161,7 @@ impl Sound {
|
|
|
rodio::play_raw(&device, ok_decoder
|
|
|
.speed(self.speed)
|
|
|
.amplify(self.volume)
|
|
|
- .take_duration(Duration::from_millis(delay / rollrate as u64))
|
|
|
+ // .take_duration(Duration::from_millis(delay / rollrate as u64))
|
|
|
.convert_samples());
|
|
|
|
|
|
}
|
|
@@ -159,10 +170,9 @@ impl Sound {
|
|
|
}
|
|
|
}
|
|
|
thread::sleep(Duration::from_millis(delay / rollrate as u64 ));
|
|
|
-
|
|
|
-
|
|
|
}
|
|
|
}
|
|
|
+ *self.active.lock().unwrap() = false;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -214,12 +224,14 @@ D: serde::de::Deserializer<'de>,
|
|
|
pub struct Pattern {
|
|
|
pub name: String,
|
|
|
pub repeat: i32,
|
|
|
+ #[serde(default)]
|
|
|
+ pub resolution: i32,
|
|
|
pub xsize: usize,
|
|
|
pub ysize: usize,
|
|
|
#[serde(serialize_with="ser_snds", deserialize_with="de_snds")]
|
|
|
pub sounds: HashMap<(usize, usize), Sound>,
|
|
|
#[serde(skip)]
|
|
|
- pub progress: Option<(usize, usize)>
|
|
|
+ pub snds: Arc<Mutex<HashMap<(usize, usize), Sound>>>
|
|
|
}
|
|
|
|
|
|
/// Some basic defaults for a pattern.
|
|
@@ -228,48 +240,74 @@ impl Default for Pattern {
|
|
|
Pattern {
|
|
|
name: "Unnamed".to_string(),
|
|
|
repeat: 0,
|
|
|
+ resolution: 8,
|
|
|
xsize: 8,
|
|
|
ysize: 1,
|
|
|
sounds: HashMap::new(),
|
|
|
- progress: None
|
|
|
+ snds: Arc::new(Mutex::new(HashMap::new()))
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
impl Pattern {
|
|
|
- pub fn play(&self, device: &Arc<rodio::Device>, bpm: i32) {
|
|
|
|
|
|
- let pat_clone = self.clone();
|
|
|
+ // pub fn play(&self, device: &Arc<rodio::Device>, bpm: i32) {
|
|
|
+
|
|
|
+ // let pat_clone = Arc::new(self.clone());
|
|
|
+ // // let pat_clone = self.clone();
|
|
|
+ // let device = device.clone();
|
|
|
+ // let delay = ((60.0 / bpm as f32)*1000.0) as u64;
|
|
|
+
|
|
|
+ // thread::spawn(move || {
|
|
|
+
|
|
|
+ // // loop for repeats
|
|
|
+ // for _ in 0..pat_clone.repeat + 1 {
|
|
|
+ // for x in 0..pat_clone.xsize {
|
|
|
+ // for y in 0..pat_clone.ysize {
|
|
|
+ // let device = device.clone();
|
|
|
+ // if let Some(s) = pat_clone.sounds.get(&(x, y)) {
|
|
|
+ // let s = s.clone();
|
|
|
+ // thread::spawn(move || {
|
|
|
+ // s.play(&device, bpm);
|
|
|
+ // });
|
|
|
+
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ // thread::sleep(Duration::from_millis(delay));
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ // });
|
|
|
+ // }
|
|
|
|
|
|
+ /// Compute the duration in ms between samples in a pattern
|
|
|
+ pub fn get_sample_spacing(&self, bpm: i32) -> u64 {
|
|
|
+ // dbg!(&self.resolution);
|
|
|
+ let res = self.resolution/2;
|
|
|
+ //calc ms delay - this is for 4/4
|
|
|
+ ((60000.0 / bpm as f32) / res as f32) as u64
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn play_ref(&self, device: &Arc<rodio::Device>, bpm: i32) {
|
|
|
+
|
|
|
+ // let pat_clone = Arc::new(self.clone());
|
|
|
+ let pat_clone = self.clone();
|
|
|
let device = device.clone();
|
|
|
- // let device = device.clone();
|
|
|
- let delay = ((60.0 / bpm as f32)*1000.0) as u64;
|
|
|
+ let delay = self.get_sample_spacing(bpm);
|
|
|
|
|
|
+ let snds = self.snds.clone();
|
|
|
thread::spawn(move || {
|
|
|
|
|
|
// loop for repeats
|
|
|
-
|
|
|
for _ in 0..pat_clone.repeat + 1 {
|
|
|
for x in 0..pat_clone.xsize {
|
|
|
for y in 0..pat_clone.ysize {
|
|
|
let device = device.clone();
|
|
|
- if let Some(s) = pat_clone.sounds.get(&(x, y)) {
|
|
|
- // s.play(&device);
|
|
|
-
|
|
|
+ if let Some(s) = snds.lock().unwrap().get(&(x, y)) {
|
|
|
+ // if let Some(s) = pat_clone.sounds.get(&(x, y)) {
|
|
|
let s = s.clone();
|
|
|
thread::spawn(move || {
|
|
|
-
|
|
|
s.play(&device, bpm);
|
|
|
-
|
|
|
- // if s.roll == 0 {
|
|
|
- // s.play(&device, bpm);
|
|
|
- // } else {
|
|
|
- // // let bpm = self.bpm;
|
|
|
- // s.play_rolled(&device, bpm);
|
|
|
- // }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
});
|
|
|
|
|
|
}
|
|
@@ -278,25 +316,13 @@ impl Pattern {
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- // for _ in 0..self.repeat + 1 {
|
|
|
- // for x in 0..self.xsize {
|
|
|
- // for y in 0..self.ysize {
|
|
|
- // if let Some(s) = self.sounds.get(&(x, y)) {
|
|
|
- // s.play(device);
|
|
|
- // }
|
|
|
- // }
|
|
|
- // thread::sleep(Duration::from_millis(delay));
|
|
|
- // }
|
|
|
- // }
|
|
|
-
|
|
|
}
|
|
|
|
|
|
- pub fn duration(&self, bpm: i32) -> u64 {
|
|
|
- ((60.0 / bpm as f32)*1000.0) as u64 * self.xsize as u64
|
|
|
|
|
|
+ /// Get the pattern duration in ms
|
|
|
+ pub fn duration(&self, bpm: i32) -> u64 {
|
|
|
+ self.get_sample_spacing(bpm) * self.xsize as u64
|
|
|
+ // ((60.0 / bpm as f32)*1000.0) as u64 * self.xsize as u64
|
|
|
}
|
|
|
|
|
|
/// Deserialize a pattern from a file.
|
|
@@ -304,8 +330,14 @@ impl Pattern {
|
|
|
match File::open(file) {
|
|
|
Ok(f) => {
|
|
|
serde_json::from_reader(BufReader::new(f))
|
|
|
- .map_err(|e| e.to_string())
|
|
|
- .map(|mut p: Pattern| p.reload_sounds( ))
|
|
|
+ .map_err(|e| {
|
|
|
+ dbg!(&e);
|
|
|
+ e.to_string()
|
|
|
+ })
|
|
|
+ .map(|mut p: Pattern| p.reload_sounds())
|
|
|
+ .map(|p| {
|
|
|
+ println!("LD {} TMG {}", p.name, p.resolution);
|
|
|
+ p})
|
|
|
}
|
|
|
Err(e) => Err(e.to_string())
|
|
|
}
|
|
@@ -315,7 +347,8 @@ impl Pattern {
|
|
|
/// This only replaces the source, leaving other settings intact.
|
|
|
pub fn replace_sound_sources(&mut self, new_sound: &Sound, row: usize) {
|
|
|
for x in 0..self.xsize {
|
|
|
- if let Some(snd) = self.sounds.get_mut(&(x, row)) {
|
|
|
+ if let Some(snd) = self.snds.lock().unwrap().get_mut(&(x, row)) {
|
|
|
+ info!("replacing {} with {}", snd.name, new_sound.name);
|
|
|
snd.location = new_sound.location.clone();
|
|
|
snd.data = new_sound.data.clone();
|
|
|
}
|
|
@@ -323,6 +356,34 @@ impl Pattern {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// Insert a sound
|
|
|
+ pub fn insert_sound(&mut self, pos: &(usize, usize), snd: &Sound) {
|
|
|
+ self.snds.lock().unwrap().insert(*pos, snd.clone());
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Remove a sound
|
|
|
+ pub fn remove_sound(&mut self, pos: &(usize, usize)) {
|
|
|
+ self.snds.lock().unwrap().remove(pos);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Check if a pattern contains a sound
|
|
|
+ pub fn contains_sound(&self, pos: &(usize, usize)) -> bool {
|
|
|
+ self.snds.lock().unwrap().contains_key(pos)
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Return a MutexGuard to the sounds
|
|
|
+ pub fn sounds(&self) -> MutexGuard<HashMap<(usize, usize), Sound>> {
|
|
|
+ self.snds.lock().unwrap()
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Get a sound from a pattern
|
|
|
+ pub fn get_sound(&self, pos: &(usize, usize)) -> Option<Sound> {
|
|
|
+ self.snds.lock().unwrap()
|
|
|
+ .get(pos)
|
|
|
+ .map(|s| s.clone())
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
/// Replace all souds in a row by a new sound.
|
|
|
/// This only replaces the source, leaving other settings intact.
|
|
|
pub fn replace_sounds(&mut self, new_sound: &Sound, row: usize) {
|
|
@@ -339,13 +400,12 @@ impl Pattern {
|
|
|
let mut names: HashMap<String, usize> = HashMap::new();
|
|
|
|
|
|
for x in 0..self.xsize {
|
|
|
- if let Some(snd) = self.sounds.get(&(x, row)) {
|
|
|
+ if let Some(snd) = self.snds.lock().unwrap().get(&(x, row)) {
|
|
|
match names.get_mut(&snd.name) {
|
|
|
Some(v) => *v += 1,
|
|
|
None => {names.insert(snd.name.clone(), 1);},
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
+ }
|
|
|
}
|
|
|
let mut count_vec: Vec<_> = names.iter().collect();
|
|
|
count_vec.sort_by(|a, b| b.1.cmp(a.1));
|
|
@@ -363,17 +423,15 @@ impl Pattern {
|
|
|
}
|
|
|
|
|
|
/// Clear all sound in a pattern row
|
|
|
- pub fn clear_sounds(&mut self, row: usize) {
|
|
|
+ pub fn clear_row(&mut self, row: usize) {
|
|
|
for x in 0..self.xsize {
|
|
|
- if let Some(_) = self.sounds.get(&(x, row)) {
|
|
|
- self.sounds.remove(&(x, row));
|
|
|
- }
|
|
|
+ self.remove_sound(&(x, row));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// Clear all sounds in a pattern.
|
|
|
- pub fn clear_all_sounds(&mut self) {
|
|
|
- self.sounds.clear();
|
|
|
+ pub fn clear_all(&mut self) {
|
|
|
+ self.snds.lock().unwrap().clear();
|
|
|
}
|
|
|
|
|
|
/// Reload all sounds in a pattern
|
|
@@ -389,6 +447,7 @@ impl Pattern {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ self.snds = Arc::new(Mutex::new(self.sounds.clone()));
|
|
|
self.clone()
|
|
|
}
|
|
|
|
|
@@ -407,20 +466,24 @@ impl Pattern {
|
|
|
self.ysize = self.ysize+rows;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
}
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
pub struct Timeline {
|
|
|
- pub patterns: Vec<Pattern>,
|
|
|
- pub bpm: i32
|
|
|
+ pub bpm: i32,
|
|
|
+ pub xsize: usize,
|
|
|
+ pub ysize: usize,
|
|
|
+ pub patterns: HashMap<(usize, usize), Pattern>,
|
|
|
+
|
|
|
}
|
|
|
|
|
|
impl Default for Timeline {
|
|
|
fn default() -> Timeline {
|
|
|
Timeline {
|
|
|
- patterns: vec![Pattern::default()],
|
|
|
- bpm: 120
|
|
|
+ bpm: 120,
|
|
|
+ xsize: 8,
|
|
|
+ ysize: 1,
|
|
|
+ patterns: HashMap::new()
|
|
|
}
|
|
|
}
|
|
|
}
|