Browse Source

song support

Johann Woelper 4 years ago
parent
commit
28e2c4fd9d
2 changed files with 332 additions and 307 deletions
  1. 135 111
      src/base.rs
  2. 197 196
      src/main.rs

+ 135 - 111
src/base.rs

@@ -16,6 +16,73 @@ use log::*;
 use std::collections::hash_map::DefaultHasher;
 use std::hash::{Hash, Hasher};
 
+fn hm_key_to_string<T>(snds: &HashMap<(usize, usize), T>)  ->  HashMap<String, &T> {
+    snds.into_iter()
+        .map(|(k, v)| (format!("{},{}", k.0, k.1), v.clone()))
+        .collect()
+}
+
+fn hm_key_from_string<T>(snds: &HashMap<String, T>)  ->  HashMap<(usize, usize), &T> {
+    snds.into_iter()
+        .map(|(k, v)| {
+            //TODO: remove var and collect directly into tuple
+            let idx: Vec<usize> = k.split(",").map(|s| s.parse::<usize>().unwrap_or_default()).collect();
+            ((idx[0],idx[1]), v.to_owned())
+        
+        })
+        .collect()
+}
+
+
+fn ser_snds<S>(h: &Arc<Mutex<HashMap<(usize, usize), Sound>>>, s: S) -> Result<S::Ok, S::Error>
+where
+    S: Serializer,
+{
+    let um = h.lock().unwrap();
+    let mut map = s.serialize_map(Some(um.len()))?;
+    for (k, v) in hm_key_to_string(&um) {
+        map.serialize_entry(&k, &v)?;
+    }
+    map.end()
+}
+
+
+
+fn de_snds<'de, D>(deserializer: D) -> Result<Arc<Mutex<HashMap<(usize, usize), Sound>>>, D::Error>
+where
+D: serde::de::Deserializer<'de>,
+{
+    // TODO err checking
+    let s: HashMap<String, Sound> = serde::de::Deserialize::deserialize(deserializer)?;
+    // serde_json::from_str(s).map_err(serde::de::Error::custom)
+    let x = hm_key_from_string(&s).iter().map(|(k, v)| (k.clone(), v.to_owned().clone()) ).collect::<_>();
+    Ok(Arc::new(Mutex::new(x)))
+}
+
+fn de_song<'de, D>(deserializer: D) -> Result<HashMap<(usize, usize), Pattern>, D::Error>
+where
+D: serde::de::Deserializer<'de>,
+{
+    // TODO err checking
+    let s: HashMap<String, Pattern> = serde::de::Deserialize::deserialize(deserializer)?;
+    let x = hm_key_from_string(&s).iter().map(|(k, v)| (k.clone(), v.to_owned().clone()) ).collect::<_>();
+    // Ok(Arc::new(x))
+    Ok(x)
+}
+
+fn ser_song<S>(h: &HashMap<(usize, usize), Pattern>, s: S) -> Result<S::Ok, S::Error>
+where
+    S: Serializer,
+{
+    let um = h;
+    let mut map = s.serialize_map(Some(um.len()))?;
+    for (k, v) in hm_key_to_string(&um) {
+        map.serialize_entry(&k, &v)?;
+    }
+    map.end()
+}
+
+
 #[derive(Serialize, Deserialize, Clone, Default, Debug)]
 pub struct Sound {
     pub name: String,
@@ -46,12 +113,6 @@ impl PartialEq for Sound {
         && self.trim == other.trim
     }
 }
-// impl Hash for Sound {
-//     fn hash<H: Hasher>(&self, state: &mut H) {
-//         self.name.hash(state);
-//         self.volume.hash(state);
-//     }
-// }
 
 impl AsRef<[u8]> for Sound {
     fn as_ref(&self) -> &[u8] {
@@ -174,8 +235,6 @@ impl Sound {
                     
                         }
                     thread::sleep(Duration::from_millis(delay));
-
-
                 }
                 Err(e) => {
                     dbg!(e);
@@ -207,84 +266,15 @@ impl Sound {
     }
     
     pub fn play_detached(&self, device: &Arc<rodio::Device>, bpm: i32) {
-    
         let s = self.clone();
         let device = device.clone();
-        
         thread::spawn( move || {
             s.play(&device, bpm);
         });
-
-
     }
 }
 
 
-fn hm_key_to_string(snds: &HashMap<(usize, usize), Sound>)  ->  HashMap<String, Sound> {
-    snds.into_iter()
-        .map(|(k, v)| (format!("{},{}", k.0, k.1), v.clone()))
-        .collect()
-}
-
-fn hm_key_from_string(snds: &HashMap<String, Sound>)  ->  HashMap<(usize, usize), Sound> {
-    snds.into_iter()
-        .map(|(k, v)| {
-            //TODO: remove var and collect directly into tuple
-            let idx: Vec<usize> = k.split(",").map(|s| s.parse::<usize>().unwrap_or_default()).collect();
-                
-            ((idx[0],idx[1]), v.clone() )
-        
-        })
-        .collect()
-}
-
-fn ser_sounds<S>(h: &HashMap<(usize, usize), Sound>, s: S) -> Result<S::Ok, S::Error>
-where
-    S: Serializer,
-{
-    // s.serialize_map(hm_key_to_string(h))
-    let mut map = s.serialize_map(Some(h.len()))?;
-    for (k, v) in hm_key_to_string(h) {
-        map.serialize_entry(&k, &v)?;
-    }
-    map.end()
-}
-
-
-fn ser_snds<S>(h: &Arc<Mutex<HashMap<(usize, usize), Sound>>>, s: S) -> Result<S::Ok, S::Error>
-where
-    S: Serializer,
-{
-    let um = h.lock().unwrap();
-    let mut map = s.serialize_map(Some(um.len()))?;
-    for (k, v) in hm_key_to_string(&um) {
-        map.serialize_entry(&k, &v)?;
-    }
-    map.end()
-}
-
-fn de_snds<'de, D>(deserializer: D) -> Result<Arc<Mutex<HashMap<(usize, usize), Sound>>>, D::Error>
-where
-D: serde::de::Deserializer<'de>,
-{
-    // TODO err checking
-    let s: HashMap<String, Sound> = serde::de::Deserialize::deserialize(deserializer)?;
-    // serde_json::from_str(s).map_err(serde::de::Error::custom)
-    let x = hm_key_from_string(&s);
-    Ok(Arc::new(Mutex::new(x)))
-}
-
-
-fn de_sounds<'de, D>(deserializer: D) -> Result<HashMap<(usize, usize), Sound>, D::Error>
-where
-D: serde::de::Deserializer<'de>,
-{
-    // TODO err checking
-    let s: HashMap<String, Sound> = serde::de::Deserialize::deserialize(deserializer)?;
-    // serde_json::from_str(s).map_err(serde::de::Error::custom)
-    let x = hm_key_from_string(&s);
-    Ok(x)
-}
 
 
 #[derive(Serialize, Deserialize, Clone, Debug)]
@@ -296,8 +286,6 @@ pub struct Pattern {
     pub resolution: i32,
     pub xsize: usize,
     pub ysize: usize,
-    // #[serde(serialize_with="ser_sounds", deserialize_with="de_sounds")]
-    // pub sounds: HashMap<(usize, usize), Sound>,
     #[serde(serialize_with="ser_snds", deserialize_with="de_snds")]
     pub sounds: Arc<Mutex<HashMap<(usize, usize), Sound>>>
 }
@@ -319,34 +307,6 @@ impl Default for Pattern {
 
 impl Pattern {
 
-    // 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);
@@ -357,8 +317,6 @@ impl Pattern {
     }
 
     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 delay = self.get_sample_spacing(bpm);
@@ -389,7 +347,7 @@ impl Pattern {
 
     /// Get the pattern duration in ms
     pub fn duration(&self, bpm: i32) -> u64 {
-        self.get_sample_spacing(bpm) * self.xsize as u64
+        self.get_sample_spacing(bpm) * self.xsize as u64 * (self.repeat+1) as u64
         // ((60.0 / bpm as f32)*1000.0) as u64 * self.xsize as u64
     }
 
@@ -557,7 +515,6 @@ pub struct Timeline {
     pub xsize: usize,
     pub ysize: usize,
     pub patterns: HashMap<(usize, usize), Pattern>,
-
 }
 
 impl Default for Timeline {
@@ -569,4 +526,71 @@ impl Default for Timeline {
             patterns: HashMap::new()
         }
     }
+}
+
+
+#[derive(Serialize, Deserialize, Clone, Debug)]
+/// A pattern. This holds a matrix of sounds - defined by a length and several tracks.
+pub struct Song {
+    pub name: String,
+    #[serde(default)]
+    pub xsize: usize,
+    pub ysize: usize,
+    #[serde(serialize_with="ser_song", deserialize_with="de_song")]
+    // pub patterns: Arc<HashMap<(usize, usize), Pattern>>
+    pub patterns: HashMap<(usize, usize), Pattern>
+}
+
+impl Default for Song {
+    fn default() -> Song {
+        Song {
+            name: "Untitled".to_string(),
+            xsize: 8,
+            ysize: 2,
+            // patterns: Arc::new(HashMap::new())
+            patterns: HashMap::new()
+        }
+    }
+}
+
+
+impl Song {
+
+
+    /// Get a pattern
+    pub fn get_pattern(&self, pos: &(usize, usize)) -> Option<Pattern> {
+        self.patterns
+            .get(pos)
+            .map(|s| s.clone())
+    }
+
+    /// Remove a pattern
+    pub fn remove_pattern(&mut self, pos: &(usize, usize)) {
+        self.patterns.remove(pos);
+    }
+
+    /// Insert a pattern
+    pub fn insert_pattern(&mut self, pos: &(usize, usize), pattern: &Pattern) {
+        self.patterns.insert(pos.clone(), pattern.clone());
+    }
+
+    /// Insert a pattern
+    pub fn play(&mut self, device: &Arc<rodio::Device>, bpm: i32) {
+        for track in 0..self.ysize {
+
+            for x in 0..self.xsize {
+                if let Some(p) = self.get_pattern(&(x, track)) {
+                    info!("start playing {}", p.name);
+                    p.play_ref(device, bpm);
+                    thread::sleep(Duration::from_millis(p.duration(bpm)));
+                    info!("done playing {}", p.name);
+                }
+            }
+        }
+    }
+
+    /// Increase the length
+    pub fn extend_length(&mut self, slots: usize) {
+        self.xsize = self.xsize+slots;
+    }
 }

+ 197 - 196
src/main.rs

@@ -1,4 +1,3 @@
-
 #[macro_use]
 extern crate serde_derive;
 extern crate serde;
@@ -13,71 +12,72 @@ mod support_ogl;
 // mod support_glium;
 use env_logger;
 use env_logger::Env;
-use std::sync::Arc;
-use std::sync::Mutex;
 use log::*;
 use std::collections::HashMap;
 use std::fs;
 use std::path::Path;
-
+use std::sync::Arc;
+use std::sync::Mutex;
 
 enum FsType {
     Dir,
-    File
+    File,
 }
 
 struct FsItem {
     fstype: FsType,
     parent: Option<Box<FsItem>>,
     children: Vec<FsItem>,
-
 }
 
-
-
-fn collect_sounds (root: &str) -> Vec<Sound> {
+fn collect_sounds(root: &str) -> Vec<Sound> {
     WalkDir::new(root)
-    .into_iter()
-    .filter_map(|e| e.ok())
-    .filter(|e| e.path().is_file())
-    .map(|f| Sound::new(&f.path().to_path_buf()))
-    .filter_map(|e| e.ok())
-    .collect()
+        .into_iter()
+        .filter_map(|e| e.ok())
+        .filter(|e| e.path().is_file())
+        .map(|f| Sound::new(&f.path().to_path_buf()))
+        .filter_map(|e| e.ok())
+        .collect()
 }
 
-
 fn sound_lib(root: &str) -> HashMap<String, Vec<Sound>> {
     let mut hm: HashMap<String, Vec<Sound>> = HashMap::new();
 
     for s in collect_sounds(root) {
-        let e = hm.entry(
-            s.location.clone().parent().unwrap().to_string_lossy().into()
-        ).or_default().push(s.clone());
-
+        let e = hm
+            .entry(
+                s.location
+                    .clone()
+                    .parent()
+                    .unwrap()
+                    .to_string_lossy()
+                    .into(),
+            )
+            .or_default()
+            .push(s.clone());
     }
 
     hm
 }
 
-fn collect_patterns (root: &str) -> Vec<Pattern> {
+fn collect_patterns(root: &str) -> Vec<Pattern> {
     WalkDir::new(root)
-    .into_iter()
-    .filter_map(|e| e.ok())
-    .filter(|e| e.path().is_file())
-    .map(|f| Pattern::load(f.path()))
-    // .map(|p| dbg!(p))
-    .filter_map(|e| e.ok())
-    // .map(|p| Arc::new(p))
-    .collect()
+        .into_iter()
+        .filter_map(|e| e.ok())
+        .filter(|e| e.path().is_file())
+        .map(|f| Pattern::load(f.path()))
+        // .map(|p| dbg!(p))
+        .filter_map(|e| e.ok())
+        // .map(|p| Arc::new(p))
+        .collect()
 }
 
-fn brighten(col: [f32; 4] ) -> [f32; 4] {
+fn brighten(col: [f32; 4]) -> [f32; 4] {
     let fac = 2.2;
-    [col[0]*fac, col[1]*fac, col[2]*fac, col[3]*fac]
+    [col[0] * fac, col[1] * fac, col[2] * fac, col[3] * fac]
 }
 
 fn main() {
-
     // env_logger::init();
     env_logger::from_env(Env::default().default_filter_or("info")).init();
     info!("Starting");
@@ -86,7 +86,7 @@ fn main() {
     // .init();
     // let dev = rodio::default_output_device().unwrap();
     let dev = Arc::new(rodio::default_output_device().unwrap());
-    
+
     let mut sounds = collect_sounds("media");
     let soundlib = sound_lib("media");
     // for (k, v) in &soundlib {
@@ -96,11 +96,11 @@ fn main() {
     let mut patterns = collect_patterns("patterns");
     let mut bpm = 120;
 
-    let mut timeline = Timeline::default();
-    
+    let mut song = Song::default();
+
     let mut pattern_col: usize = 0;
     let mut pattern_row: usize = 0;
-    
+
     let mut active_pattern = Arc::new(Mutex::new(Pattern::default()));
     let mut active_pattern = active_pattern.lock().unwrap();
     // let mut active_pattern = Arc::new(Pattern::default());
@@ -112,11 +112,11 @@ fn main() {
     style.window_rounding = 0.0;
     // style.anti_aliased_lines = false;
     // style.anti_aliased_fill = false;
-    
+    let button_size: f32 = 28.0;
+
     const PURPLE: [f32; 4] = [0.07, 0.05, 0.27, 1.00];
     const YELLOW: [f32; 4] = [0.60, 0.25, 0.0, 1.00];
     system.main_loop(move |_, ui| {
-
         let colors = ui.push_style_colors(&[
             (StyleColor::FrameBg, PURPLE),
             (StyleColor::FrameBgHovered, YELLOW),
@@ -130,92 +130,54 @@ fn main() {
             (StyleColor::TabHovered, YELLOW),
         ]);
 
-
-        // SOUND SOURCES WINDOW
-        // Window::new(im_str!("samples"))
-        //     .position([800.0, 0.0], Condition::Appearing)
-        //     .size([200.0, 500.0], Condition::FirstUseEver)
-        //     .build(ui, || {
-
-        //         ui.tree_node(im_str!("snds"))
-        //         .opened(true, Condition::Appearing)
-        //         .build(|| {
-        //             for s in &mut sounds {
-        //                 ui.tree_node(&im_str!("{}", s.name)).build(|| {
-        //                     // ui.same_line(0.0);
-        //                     // if ui.small_button(im_str!("add to pattern")) {
-        //                     //     active_pattern.sounds[pattern_row][pattern_col] = Some(s.clone());
-        //                     // }
-        //                     if ui.small_button(im_str!("load")) {
-        //                         active_sound = Some(s.clone());
-        //                     }
-        //                     ui.same_line(0.0);
-        //                     if ui.small_button(im_str!("play")) {
-        //                         s.play(&dev, bpm);
-        //                     }
-        //                 });
-        //             }
-        //         });
-        //     });
-
-
-            
-            Window::new(im_str!("samples2"))
+        Window::new(im_str!("samples"))
             .position([800.0, 0.0], Condition::Appearing)
             .size([200.0, 500.0], Condition::FirstUseEver)
             .build(ui, || {
-                
-                
                 for (k, snds) in &soundlib {
-
                     ui.tree_node(&im_str!("{}", k))
+                        .opened(true, Condition::Appearing)
+                        .build(|| {
+                            for s in snds {
+                                ui.tree_node(&im_str!("{}", s.name)).build(|| {
+                                    // ui.same_line(0.0);
+                                    // if ui.small_button(im_str!("add to pattern")) {
+                                    //     active_pattern.sounds[pattern_row][pattern_col] = Some(s.clone());
+                                    // }
+                                    if ui.small_button(im_str!("load")) {
+                                        active_sound = Some(s.clone());
+                                    }
+                                    ui.same_line(0.0);
+                                    if ui.small_button(im_str!("play")) {
+                                        s.play(&dev, bpm);
+                                    }
+                                });
+                            }
+                        });
+                }
+            });
+
+        // PATTERN LOAD WINDOW
+        Window::new(im_str!("patterns"))
+            .position([800.0, 500.0], Condition::Appearing)
+            .size([200.0, 300.0], Condition::FirstUseEver)
+            .build(ui, || {
+                ui.tree_node(im_str!("patterns"))
                     .opened(true, Condition::Appearing)
                     .build(|| {
-                        for s in snds {
-                            ui.tree_node(&im_str!("{}", s.name)).build(|| {
+                        for p in &mut patterns {
+                            ui.tree_node(&im_str!("{}", p.name)).build(|| {
                                 // ui.same_line(0.0);
-                                // if ui.small_button(im_str!("add to pattern")) {
-                                //     active_pattern.sounds[pattern_row][pattern_col] = Some(s.clone());
-                                // }
                                 if ui.small_button(im_str!("load")) {
-                                    active_sound = Some(s.clone());
+                                    *active_pattern = p.clone();
                                 }
                                 ui.same_line(0.0);
                                 if ui.small_button(im_str!("play")) {
-                                    s.play(&dev, bpm);
+                                    p.play_ref(&dev, bpm);
                                 }
                             });
                         }
                     });
-
-
-                }
-
-
-
-                });
-
-        // PATTERN LOAD WINDOW
-        Window::new(im_str!("patterns"))
-            .position([800.0, 500.0], Condition::Appearing)
-            .size([200.0, 300.0], Condition::FirstUseEver)
-            .build(ui, || {
-                ui.tree_node(im_str!("patterns"))
-                .opened(true, Condition::Appearing)
-                .build(|| {
-                    for p in &mut patterns {
-                        ui.tree_node(&im_str!("{}", p.name)).build(|| {
-                            // ui.same_line(0.0);
-                            if ui.small_button(im_str!("load")) {
-                                *active_pattern = p.clone();
-                            }
-                            ui.same_line(0.0);
-                            if ui.small_button(im_str!("play")) {
-                                p.play_ref(&dev, bpm);
-                            }
-                        });
-                    }
-                });
             });
 
         // PATTERN EDIT WINDOW ====================================
@@ -223,28 +185,37 @@ fn main() {
             .size([800.0, 400.0], Condition::FirstUseEver)
             .position([0.0, 255.0], Condition::Appearing)
             .build(ui, || {
-                let button_size: f32 = 28.0;
-
-                if ui.small_button(&im_str!("length + 1")) {active_pattern.extend_length(1);}
+                if ui.small_button(&im_str!("length + 1")) {
+                    active_pattern.extend_length(1);
+                }
                 ui.same_line(0.0);
-                if ui.small_button(&im_str!("length + 4")) {active_pattern.extend_length(4);}
+                if ui.small_button(&im_str!("length + 4")) {
+                    active_pattern.extend_length(4);
+                }
                 ui.same_line(0.0);
-                if ui.small_button(&im_str!("length + 8")) {active_pattern.extend_length(8);}
-                
-                if ui.small_button(&im_str!("add trk")) {active_pattern.extend_row(1);}
+                if ui.small_button(&im_str!("length + 8")) {
+                    active_pattern.extend_length(8);
+                }
+
+                if ui.small_button(&im_str!("add trk")) {
+                    active_pattern.extend_row(1);
+                }
                 ui.same_line(0.0);
-                if ui.small_button(&im_str!("save pattern")) {active_pattern.save();}
-     
+                if ui.small_button(&im_str!("save pattern")) {
+                    active_pattern.save();
+                }
 
                 // The pattern name
                 let mut imstr_name = ImString::from(active_pattern.name.clone());
-                if ui.input_text(&im_str!("name"), &mut imstr_name)
+                if ui
+                    .input_text(&im_str!("name"), &mut imstr_name)
                     .resize_buffer(true)
                     .auto_select_all(true)
-                    .build() {
+                    .build()
+                {
                     active_pattern.name = imstr_name.to_string();
                 };
-                
+
                 ui.drag_int(im_str!("beats/bar"), &mut active_pattern.resolution)
                     .max(64)
                     .speed(0.1)
@@ -256,18 +227,16 @@ fn main() {
                     .build();
 
                 // The grid header labels
-                
+
                 ui.text(im_str!("loc     "));
                 for x in 0..active_pattern.xsize {
                     ui.same_line(0.0);
-                    if x%4 == 0 {
+                    if x % 4 == 0 {
                         let s = ui.push_style_color(StyleColor::Button, brighten(PURPLE));
-                        ui.button(&im_str!("{}", x+1), [button_size, button_size]);
-                        s.pop(&ui);                
-
+                        ui.button(&im_str!("{}", x + 1), [button_size, button_size]);
+                        s.pop(&ui);
                     } else {
-
-                        ui.button(&im_str!("{}", x+1), [button_size, button_size]);
+                        ui.button(&im_str!("{}", x + 1), [button_size, button_size]);
                     }
                 }
 
@@ -275,41 +244,44 @@ fn main() {
                 for y in 0..active_pattern.ysize {
                     ui.text(im_str!("{}", active_pattern.name_from_row(y)));
                     for x in 0..active_pattern.xsize {
-                        let snd = active_pattern.get_sound(&(x,y));
-                        
+                        let snd = active_pattern.get_sound(&(x, y));
+
                         let mut label = " ";
-                        
+
                         match snd {
                             Some(s) => {
                                 label = "X";
                                 if *s.active.lock().unwrap() {
                                     label = "-"
                                 }
-                            },
+                            }
                             None => {}
                         }
-                        
+
                         ui.same_line(0.0);
-                        if ui.button(&im_str!("{}##{}{}", label, y, x), [button_size, button_size]) {
+                        if ui.button(
+                            &im_str!("{}##{}{}", label, y, x),
+                            [button_size, button_size],
+                        ) {
                             pattern_col = x;
                             pattern_row = y;
                             match &active_sound {
                                 Some(snd) => {
-                                    snd.play_detached(&dev, bpm);    
+                                    snd.play_detached(&dev, bpm);
                                     // active_pattern.insert_sound(&(x,y),  &snd.updated_from_source().unwrap())
-                                    active_pattern.insert_sound(&(x,y),  &snd.cloned())
-                                },
-                                None => active_pattern.remove_sound(&(x,y))
+                                    active_pattern.insert_sound(&(x, y), &snd.cloned())
+                                }
+                                None => active_pattern.remove_sound(&(x, y)),
                             }
                         }
                         // Convenience: remove items on right click
                         if ui.is_item_clicked(MouseButton::Right) {
-                            active_pattern.remove_sound(&(x,y));
+                            active_pattern.remove_sound(&(x, y));
                         }
                     }
-                    
+
                     ui.same_line(0.0);
-                    if ui.small_button(&im_str!("replace src##{}",y) ){
+                    if ui.small_button(&im_str!("replace src##{}", y)) {
                         if let Some(acs) = &active_sound {
                             info!("{:?} {}", acs.location, acs.name);
                             active_pattern.replace_sound_sources(acs, y);
@@ -317,12 +289,11 @@ fn main() {
                     }
 
                     ui.same_line(0.0);
-                    if ui.small_button(&im_str!("clr##{}", y) ){
+                    if ui.small_button(&im_str!("clr##{}", y)) {
                         active_pattern.clear_row(y);
                     }
                 }
 
-
                 for used_snd in active_pattern.get_all_sounds() {
                     if ui.small_button(&im_str!("{}", used_snd.extended_display())) {
                         active_sound = Some(used_snd.clone());
@@ -331,23 +302,20 @@ fn main() {
                     }
                 }
 
-
                 if ui.small_button(&im_str!("play")) {
                     active_pattern.play_ref(&dev, bpm);
                     //active_pattern.play(&dev, bpm);
                 }
             });
-    
-            Window::new(im_str!("sound"))
+
+        Window::new(im_str!("sound"))
             .size([500.0, 255.0], Condition::FirstUseEver)
             .position([0.0, 0.0], Condition::Appearing)
             .build(ui, || {
-
                 // let w = ui.current_column_width()*2.0/3.0 + 2.0;
                 let w = -1.0;
                 // check if active sounds is some
                 if let Some(s) = &mut active_sound {
-
                     ui.text(im_str!("name   {}", s.name));
 
                     ui.drag_float(im_str!("volume"), &mut s.volume)
@@ -360,8 +328,8 @@ fn main() {
                         .min(0.01)
                         .max(10.0)
                         .speed(0.01)
-                        .build();    
-                        
+                        .build();
+
                     ui.drag_int(im_str!("trim ms"), &mut s.trim)
                         .min(0)
                         .max(4000)
@@ -380,7 +348,7 @@ fn main() {
                         .speed(0.04)
                         .build();
                     ui.checkbox(im_str!("reverse"), &mut s.reverse);
-                        
+
                     if ui.button(&im_str!("adjust speed to pattern"), [w, 0.0]) {
                         dbg!(s.duration());
                         if let Some(sd) = s.duration() {
@@ -393,65 +361,98 @@ fn main() {
                         // dbg!(&l);
                         // let bpm = active_pattern.bpm;
                     }
-                
-                    if ui.button(&im_str!("play"), [w, 0.0]) {s.play_detached(&dev, bpm)}
-                
+
+                    if ui.button(&im_str!("play"), [w, 0.0]) {
+                        s.play_detached(&dev, bpm)
+                    }
                 }
 
-                if ui.button(&im_str!("clear"), [w, 0.0]) {active_sound = None}
+                if ui.button(&im_str!("clear"), [w, 0.0]) {
+                    active_sound = None
+                }
             });
-    
 
-
-
-            Window::new(im_str!("tracks"))
+        Window::new(im_str!("song"))
             .size([300.0, 100.0], Condition::FirstUseEver)
             .position([0.0, 650.0], Condition::Appearing)
-
             .build(ui, || {
-
                 ui.drag_int(im_str!("BPM"), &mut bpm)
-                .min(1)
-                .max(1000)
-                .speed(0.01)
-                .build();
-               
-
-                if ui.small_button(&im_str!("add current pattern")) {
-                    // active_pattern.play(&dev, bpm);
-                }
+                    .min(1)
+                    .max(1000)
+                    .speed(0.01)
+                    .build();
 
-                if ui.small_button(&im_str!("play")) {
-                    active_pattern.play_ref(&dev, bpm);
+                ui.dummy([0.0, 0.0]);
+                // Loop over the length of the pattern
+                
+                
+                if ui.small_button(&im_str!("length + 1")) {
+                    song.extend_length(1);
                 }
 
-                // let k = Key::Space;
-                // if ui.is_key_released(k as u32) {
-                //     dbg!("GO");
-                // }
-            });
 
+                for y in 0..song.ysize {
+                    ui.dummy([0.0, 0.0]);
+
+                    for x in 0..song.xsize {
+                        let snd = song.get_pattern(&(x, y));
+
+                        match snd {
+                            Some(p) => {
+                            
+                                ui.same_line(0.0);
+                                if ui.button(
+                                    &im_str!("{}##{}{}", p.name, y, x),
+                                    [(button_size/2.0)*p.xsize as f32 * p.repeat as f32, button_size],
+                                ) {
+                                    song.insert_pattern(&(x, y), &active_pattern);
+                                }
+                            }
+                            None => {
+                                ui.same_line(0.0);
+                                if ui.button(
+                                    &im_str!(" ##{}{}", y, x),
+                                    [button_size, button_size],
+                                ) {
+        
+                                    song.insert_pattern(&(x, y), &active_pattern);
+                                }
+                            }
+                        }
+
+           
+                    
+                    
+                    
+                    }
 
+                    // ui.same_line(0.0);
+                    // if ui.small_button(&im_str!("clr##{}", y) ){
+                    //     active_pattern.clear_row(y);
+                    // }
+                }
 
+                if ui.small_button(&im_str!("play")) {
+                    song.play(&dev, bpm);
+                }
+            });
 
-    
-            // Window::new(im_str!("settings"))
-            // .size([300.0, 100.0], Condition::FirstUseEver)
-            // .build(ui, || {
-               
-            //     let mut scale = 1.0;
-            //     if ui.drag_float(im_str!("size"), &mut scale)
-            //     .min(0.0)
-            //     .max(3.0)
-            //     .speed(0.01)
-            //     .build() {
-            //         // let s = system.imgui.style_mut();
-            //         // s.scale_all_sizes(scale);
-            //     }
-            // });
-
-            // Discard color token
-            colors.pop(&ui);
-    
-        });
+        // Window::new(im_str!("settings"))
+        // .size([300.0, 100.0], Condition::FirstUseEver)
+        // .build(ui, || {
+
+        //     let mut scale = 1.0;
+        //     if ui.drag_float(im_str!("size"), &mut scale)
+        //     .min(0.0)
+        //     .max(3.0)
+        //     .speed(0.01)
+        //     .build() {
+        //         // let s = system.imgui.style_mut();
+        //         // s.scale_all_sizes(scale);
+        //     }
+        // });
+
+        // Discard color token
+        colors.pop(&ui);
+    });
 }