Skip to content

Commit

Permalink
refactor: printing colours and errors; misc: MVP release, version v0.…
Browse files Browse the repository at this point in the history
…1.0;
  • Loading branch information
codybloemhard committed Sep 12, 2021
1 parent 6aad38f commit 9b4aeb4
Show file tree
Hide file tree
Showing 9 changed files with 189 additions and 71 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ skim = "0.9.4"
sdl2 = "0.34.5"
lv2-host-minimal = "0.1.2"
apres = "0.3.0"
term-basics-linux = "0.5.6"
term-basics-linux = "0.5.7"
fnrs = "0.1.6"
sampsyn = "0.1.2"
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Termdaw is a (or should become a) terminal, graph based programmable pipeline di
- [x] Controls: play, pause, stop
- [x] Controls: set/get time, dash in time
- [x] Controls: refresh, render
- [ ] View: Terminal logging, warnings, errors, colors
- [x] View: Terminal logging, warnings, errors, colors
- [x] Toml configuration
- Structure
- [x] Sample Bank
Expand Down
61 changes: 61 additions & 0 deletions examples/sample-project.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
set_length(40.0);
set_render_samplerate(48000);
set_render_bitdepth(16);
set_output_file("outp.wav");

load_sample("snare", "/home/cody/doc/samples/drumnbass/snare-1/snare-1-v-9.wav");
load_sample("kick", "/home/cody/doc/samples/drumnbass/kick/kick-v-9.wav");
load_sample("hihat", "/home/cody/doc/samples/drumnbass/hi-hats/closed.wav");
load_sample("ride", "/home/cody/doc/samples/drumnbass/ride/standard.wav");

load_midi_floww("bassd", "/home/cody/git/music-gen/bassd.midi");
load_midi_floww("snare", "/home/cody/git/music-gen/snare.midi");
load_midi_floww("comping", "/home/cody/git/music-gen/comping0.midi");
load_midi_floww("bass", "/home/cody/git/music-gen/bass.midi");
load_midi_floww("hihat", "/home/cody/git/music-gen/hihat.midi");
load_midi_floww("ride", "/home/cody/git/music-gen/ride.midi");

load_lv2("reverb", "http://calf.sourceforge.net/plugins/Reverb");
load_lv2("chorus", "http://calf.sourceforge.net/plugins/MultiChorus");
load_lv2("compressor", "http://calf.sourceforge.net/plugins/Compressor");
load_lv2("tape", "http://calf.sourceforge.net/plugins/TapeSimulator");

parameter("compressor", "Attack", 40.0);
parameter("compressor", "Release", 100.0);
parameter("compressor", "Knee", 4.0);
parameter("compressor", "Ratio", 2.0);

load_resource("testtable", "/home/cody/git/sampsyn/table");

add_sample_lerp("kick", 2.0, 0.0, "kick", "bassd", -1, 40);
add_sample_lerp("snare", 1.0, 0.0, "snare", "snare", -1, 40);
add_sample_lerp("hihat", 0.8, 50.0, "hihat", "hihat", -1, 40);
add_sample_lerp("ride", 0.8, -50.0, "ride", "ride", -1, 40);

hit_adsr = { 0.001, 0.02, 0.0, 0.0, 0.0, 0.0 };
note_adsr = { 0.01, 0.1, 0.8, 5.0, 0.2, 0.5 };
std_adsr = { 0.01, 1.0, 1.0, 1.0, 1.0, 0.4 };
add_synth("bass", 0.5, 0.0, "bass", 0.4, 0.3, hit_adsr, 1.0, 0.8, note_adsr, 0.0, {});
--add_synth("comp", 0.5, 0.0, "comping", 0.5, 0.2, hit_adsr, 1.0, 0.7, note_adsr, 0.0, {});
add_sampsyn("comp", 0.5, 0.0, "comping", std_adsr, "testtable");

add_lv2fx("chorus", 1.0, 0.0, 1.0, "chorus");
add_lv2fx("reverb", 1.0, 0.0, 0.9, "reverb");
add_lv2fx("compress", 1.0, 0.0, 1.0, "compressor");
add_lv2fx("tape", 1.0, 0.0, 1.0, "tape");

add_normalize("sum", 1.0, 0.0);

connect("kick", "compress");
connect("snare", "compress");
connect("hihat", "reverb");
connect("ride", "reverb");
connect("bass", "chorus");
connect("comp", "chorus");

connect("chorus", "reverb");
connect("reverb", "compress");
connect("compress", "tape");
connect("tape", "sum");

set_output("sum");
11 changes: 8 additions & 3 deletions src/bufferbank.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use term_basics_linux::UC;

use std::collections::{ HashMap, HashSet };
use std::io::prelude::*;
use std::fs::File;
Expand All @@ -21,16 +23,19 @@ impl BufferBank{

pub fn add(&mut self, name: String, file_path: &str) -> Result<(), String>{
if self.names.get(&name).is_some() {
return Err(format!("TermDaw: BlobBank: there is already a blob with name \"{}\" present.", name));
return Err(format!("{}TermDaw: BufferBank: there is already a blob with name {}\"{}\"{} present.",
UC::Red, UC::Blue, name, UC::Red));
}

let mut buffer = Vec::new();
let mut file = if let Ok(file) = File::open(file_path) { file }
else {
return Err(format!("TermDaw: BlobBank: could open read file \"{}\"", file_path));
return Err(format!("{}TermDaw: BufferBank: could open read file {}\"{}\"{}.",
UC::Red, UC::Blue, file_path, UC::Red));
};
if file.read_to_end(&mut buffer).is_err() {
return Err(format!("TermDaw: BlobBank: could not read file \"{}\"", file_path));
return Err(format!("{}TermDaw: BufferBank: could not read file {}\"{}\"{}.",
UC::Red, UC::Blue, file_path, UC::Red));
}

self.buffers.push(buffer);
Expand Down
4 changes: 3 additions & 1 deletion src/floww.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use apres::{ MIDI };
use apres::MIDIEvent::{ NoteOn, NoteOff, SetTempo };
use term_basics_linux::UC;

use std::collections::{ HashMap };

Expand Down Expand Up @@ -35,7 +36,8 @@ impl FlowwBank{
self.names.insert(name, self.flowws.len() - 1);
Ok(())
} else {
Err(format!("Could not read midi file: {}", path))
Err(format!("{}Could not read midi file: {}\"{}\"{}.",
UC::Red, UC::Blue, path, UC::Red))
}
}

Expand Down
16 changes: 10 additions & 6 deletions src/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::floww::{ FlowwBank };
use crate::extensions::*;

use lv2hm::Lv2Host;
use term_basics_linux::UC;

use std::collections::{ HashMap };

Expand Down Expand Up @@ -78,11 +79,13 @@ impl Graph{
let a_res = self.name_map.get(a);
let b_res = self.name_map.get(b);
if a_res.is_none() {
println!("TermDaw: warning: vertex \"{}\" cannot be found and thus can't be connected.", a);
println!("{}TermDaw: warning: vertex {}\"{}\"{} cannot be found and thus can't be connected.",
UC::Yellow, UC::Blue, a, UC::Yellow);
return false;
}
if b_res.is_none() {
println!("TermDaw: warning: vertex \"{}\" cannot be found and thus can't be connected to.", b);
println!("{}TermDaw: warning: vertex {}\"{}\"{} cannot be found and thus can't be connected to.",
UC::Yellow, UC::Blue, b, UC::Yellow);
return false;
}
let a_index = *a_res.unwrap();
Expand Down Expand Up @@ -139,11 +142,11 @@ impl Graph{
pub fn check_graph(&self) -> bool{
let output = if let Some(out) = self.output_vertex{ out }
else {
println!("TermDaw: error: output vertex not found.");
println!("{}TermDaw: error: output vertex not found.", UC::Red);
return false;
};
if self.edges[output].is_empty() && self.vertices[output].has_input(){
println!("TermDaw: error: output receives no inputs.");
println!("{}TermDaw: error: output receives no inputs.", UC::Red);
return false;
}
let mut set = vec![false; self.vertices.len()];
Expand All @@ -156,7 +159,8 @@ impl Graph{
find_connected_component(output, &self.edges, &mut set);
for (i, x) in set.into_iter().enumerate(){
if x { continue; }
println!("TermDaw: warning: vertex \"{}\" does not reach output.", self.names[i]);
println!("{}TermDaw: warning: vertex {}\"{}\"{} does not reach output.",
UC::Yellow, UC::Blue, self.names[i], UC::Yellow);
}
true
}
Expand Down Expand Up @@ -200,7 +204,7 @@ impl Graph{
for (i, vertex) in self.vertices.iter().enumerate(){
let nv = vertex.ext.get_normalization_value();
if nv > 0.0 {
println!(" {}: {}", self.names[i], nv);
println!("{} {}: {}", UC::Magenta, self.names[i], nv);
}
}
}
Expand Down
70 changes: 49 additions & 21 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use mlua::prelude::*;
use skim::prelude::*;
use sdl2::audio::AudioSpecDesired;
use lv2hm::Lv2Host;
use term_basics_linux as tbl;
use term_basics_linux::*;

mod sample;
mod graph;
Expand All @@ -27,16 +27,24 @@ use config::*;
use state::*;
use bufferbank::*;

fn main() -> Result<(), String>{
fn main(){
let config = Config::read("project.toml");

println!("TermDaw: loading \"{}\" with \n\tbuffer_length = {} \n\tproject_samplerate = {} \n\tmain = \"{}\"",
config.project.name(),
config.settings.buffer_length(),
config.settings.project_samplerate(),
config.settings.main);
println!("{}TermDaw: loading {}\"{}\"{} with \n\tbuffer_length = {}{}{} \n\tproject_samplerate = {}{}{} \n\tmain = {}\"{}\"{}",
UC::Std, UC::Blue, config.project.name(), UC::Std,
UC::Blue, config.settings.buffer_length(), UC::Std,
UC::Blue, config.settings.project_samplerate(), UC::Std,
UC::Blue, config.settings.main, UC::Std);

let mut file = File::open(&config.settings.main).unwrap();
let mut file = match File::open(&config.settings.main){
Ok(f) => f,
Err(e) => {
println!("{}Error: could not open main lua file: {}\"{}\"{}.",
UC::Red, UC::Blue, config.settings.main, UC::Red);
println!("{}\t{}", UC::Red, e);
return;
}
};
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();
std::mem::drop(file);
Expand Down Expand Up @@ -66,14 +74,35 @@ fn main() -> Result<(), String>{
};
state.refresh();

let sdl_context = sdl2::init()?;
let audio_subsystem = sdl_context.audio()?;
let sdl_context = match sdl2::init(){
Ok(x) => x,
Err(e) => {
println!("{}Error: can't initialize sdl2 context.", UC::Red);
println!("{}\t{}", UC::Red, e);
return;
}
};
let audio_subsystem = match sdl_context.audio(){
Ok(x) => x,
Err(e) => {
println!("{}Error: can't get sdl audio subsystem.", UC::Red);
println!("{}\t{}", UC::Red, e);
return;
}
};
let desired_spec = AudioSpecDesired {
freq: Some(proj_sr as i32),
channels: Some(2),
samples: None,
};
let device = audio_subsystem.open_queue::<f32, _>(None, &desired_spec)?;
let device = match audio_subsystem.open_queue::<f32, _>(None, &desired_spec){
Ok(x) => x,
Err(e) => {
println!("{}Error: can't open sdl audio queue.", UC::Red);
println!("{}\t{}", UC::Red, e);
return;
}
};
let mut playing = false;
let mut since = Instant::now();
let mut millis_generated = 0f32;
Expand All @@ -94,7 +123,7 @@ fn main() -> Result<(), String>{

if let Some(item) = selected_items.get(0){
let command = item.output();
println!("---- {}", command);
println!("{}---- {}", UC::Magenta, command);
let tmsg = if command == "quit" { ThreadMsg::Quit }
else if command == "refresh" { ThreadMsg::Refresh }
else if command == "render" { ThreadMsg::Render }
Expand All @@ -106,26 +135,26 @@ fn main() -> Result<(), String>{
else if command == "<prev" { ThreadMsg::Prev }
else if command == "norm-vals" { ThreadMsg::NormVals }
else if command == "set" {
let raw = tbl::input_field();
let time: Option<f32> = tbl::string_to_value(&raw);
let raw = input_field();
let time: Option<f32> = string_to_value(&raw);
if let Some(float) = time{
if float >= 0.0{
let t = (float * proj_sr as f32) as usize;
ThreadMsg::Set(t)
} else {
println!("Error: time needs to be positive.");
println!("{}Error: time needs to be positive.", UC::Red);
ThreadMsg::None
}
} else {
println!("Error: could not parse time, did not set time.");
println!("{}Error: could not parse time, did not set time.", UC::Red);
ThreadMsg::None
}
}
else if command == "get" { ThreadMsg::Get }
else { ThreadMsg::None };
transmit_to_main.send(tmsg).unwrap();
} else {
println!("TermDaw: command not found!");
println!("{}TermDaw: command not found!", UC::Red);
continue;
}
for received in &receive_in_ui{
Expand All @@ -141,7 +170,7 @@ fn main() -> Result<(), String>{
macro_rules! check_loaded{
($b:block) => {
if !state.loaded{
println!("State not loaded!");
println!("{}State not loaded!", UC::Red);
} else {
$b;
}
Expand Down Expand Up @@ -219,7 +248,8 @@ fn main() -> Result<(), String>{
check_loaded!({
let t = state.g.get_time();
let tf = t as f32 / proj_sr as f32;
println!("Frame: {}, Time: {}", t, tf);
println!("{}Frame: {}{}{}, Time: {}{}",
UC::Std, UC::Blue, t, UC::Std, UC::Blue, tf);
});
}
ThreadMsg::NormVals => {
Expand Down Expand Up @@ -249,8 +279,6 @@ fn main() -> Result<(), String>{
}
std::thread::sleep(Duration::from_millis(10));
}

Ok(())
}

#[derive(PartialEq)]
Expand Down
19 changes: 13 additions & 6 deletions src/sample.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use rubato::{ Resampler, SincFixedIn, InterpolationType, InterpolationParameters, WindowFunction };
use term_basics_linux::UC;

use std::collections::{ HashMap, HashSet };

Expand All @@ -18,10 +19,12 @@ impl Sample{

pub fn from(l: Vec<f32>, r: Vec<f32>) -> Result<Self, String>{
if l.len() != r.len() {
return Err(format!("TermDaw: Sample::from: l and r do not have the same length: {} and {}.", l.len(), r.len()));
return Err(format!("{}TermDaw: Sample::from: l and r do not have the same length: {}{}{} and {}{}{}.",
UC::Red, UC::Blue, l.len(), UC::Red, UC::Blue, r.len(), UC::Red));
}
if l.is_empty(){
return Err("TermDaw: Sample::from: l and r have length 0.".to_owned());
return Err(format!("{}TermDaw: Sample::from: l and r have length {}0{}.",
UC::Red, UC::Blue, UC::Red));
}
Ok(Self{ l, r })
}
Expand Down Expand Up @@ -144,23 +147,27 @@ impl SampleBank{

pub fn add(&mut self, name: String, file: &str) -> Result<(), String>{
if self.names.get(&name).is_some() {
return Err(format!("TermDaw: SampleBank: there is already a sample with name \"{}\" present.", name));
return Err(format!("{}TermDaw: SampleBank: there is already a sample with name {}\"{}\"{} present.",
UC::Red, UC::Blue, name, UC::Red));
}
let mut reader = if let Ok(reader) = hound::WavReader::open(file){
reader
} else {
return Err(format!("TermDaw: SampleBank: could not open file {}.", file));
return Err(format!("{}TermDaw: SampleBank: could not open file {}\"{}\"{}.",
UC::Red, UC::Blue, file, UC::Red));
};
let specs = reader.spec();
if specs.channels != 2 {
return Err(format!("TermDaw: SampleBank: only stereo samples are supported yet, found {} channels.", specs.channels));
return Err(format!("{}TermDaw: SampleBank: only stereo samples are supported yet, found {}{}{} channels.",
UC::Red, UC::Blue, specs.channels, UC::Red));
}
let sr = specs.sample_rate as usize;
let bd = specs.bits_per_sample;
self.max_sr = self.max_sr.max(sr);
self.max_bd = self.max_bd.max(bd as usize);
if sr > self.sample_rate {
println!("TermDaw: warning: sample \"{}\" has a higher samplerate({}) than the project({}).", name, sr, self.sample_rate);
println!("{}TermDaw: warning: sample {}\"{}\"{} has a higher samplerate({}{}{}) than the project({}{}{}).",
UC::Yellow, UC::Blue, name, UC::Yellow, UC::Blue, sr, UC::Yellow, UC::Blue, self.sample_rate, UC::Yellow);
}
let mut l = Vec::new();
let mut r = Vec::new();
Expand Down
Loading

0 comments on commit 9b4aeb4

Please sign in to comment.