notebook entry; started popgen
This commit is contained in:
parent
b3d0d98bad
commit
2eefec9b8e
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
target/
|
14
code/popgen/Cargo.toml
Normal file
14
code/popgen/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "popgen"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
name = "popgen"
|
||||
path = "src/popgen.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.23", features = ["derive"] }
|
||||
rand = "0.8.5"
|
||||
regex = "1.11.1"
|
||||
|
33
code/popgen/README.md
Normal file
33
code/popgen/README.md
Normal file
@ -0,0 +1,33 @@
|
||||
## Background
|
||||
Like many, I also watched the 4 Chords music video from the The Axis of Awesome on youtube roughly a decade ago. At the time, it provided a possible explination on why pop songs seemed somewhat unoriginal to me despite most of my peers enjoying pop. After that thought I resumed playing side two of The Wall and queued up Quadrophenia to play next.
|
||||
|
||||
Playing around with the provided [popgen.py](https://github.com/pdx-cs-sound/popgen) is both neat and addictive. I have decided that the focus of this portolio objective will be to reimpliment this program in rust, for the following reasons:
|
||||
* Rust is more efficient, and it would be fun to run this on an embedded device which could be resource constrained.
|
||||
* Rust is cool and it would serve me well to practice and keep up better with it
|
||||
* Reimplementing the code forces me to essentially understand every line
|
||||
* Despite being given explicit permission to do so with proper attribution, I prefer not to officially commit the code of another to my repository having a commit matching my name.
|
||||
|
||||
## Setup
|
||||
|
||||
It is required to install the [rustup rust toolchain](https://rustup.rs/)
|
||||
|
||||
## Run
|
||||
```bash
|
||||
# Basic
|
||||
cargo run
|
||||
|
||||
# See help
|
||||
cargo run -- --help
|
||||
|
||||
# Example with flags
|
||||
cargo run -- -b 80 --root C[2]
|
||||
```
|
||||
|
||||
|
||||
## View Source
|
||||
[popgen.rs](./src/popgen.rs)
|
||||
|
||||
## Access outputs
|
||||
|
||||
|
||||
## Reflections, Results, Analysis
|
119
code/popgen/src/popgen.rs
Normal file
119
code/popgen/src/popgen.rs
Normal file
@ -0,0 +1,119 @@
|
||||
// "Pop Music Generator" - Rust Edition
|
||||
// David Westgate 2024
|
||||
// Initial python implementation (and most comments) from Bart Massey
|
||||
// https://github.com/pdx-cs-sound/popgen/blob/main/popgen.py
|
||||
|
||||
use clap::Parser;
|
||||
use rand::Rng;
|
||||
use regex::Regex;
|
||||
use rodio::{OutputStream, buffer::SamplesBuffer, Sink};
|
||||
use std::{f32::consts::PI, vec};
|
||||
|
||||
const C5: &str = "C[5]";
|
||||
|
||||
// 11 canonical note names.
|
||||
const NAMES: [&str; 12] = [
|
||||
"C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B",
|
||||
];
|
||||
|
||||
const NO_OUTPUT: &str = "";
|
||||
|
||||
const REGEX_BASE: &str = r"([A-G]b?)(\[([0-8])\])?";
|
||||
|
||||
// Relative notes of a major scale.
|
||||
const MAJOR_SCALE: [u8; 7] = [0, 2, 4, 5, 7, 9, 11];
|
||||
|
||||
// Major chord scale tones — one-based.
|
||||
const MAJOR_CHORD: [u8; 3] = [1, 3, 5];
|
||||
|
||||
// Root note offset for each chord in scale tones — one-based.
|
||||
const CHORD_LOOP: [u8; 4] = [8, 5, 6, 4];
|
||||
|
||||
// Given a MIDI key number and an optional number of beats of
|
||||
// note duration, return a sine wave for that note.
|
||||
fn make_note(key: i8, n: Option<u8>, beat_samples: u32, samplerate: u16) -> Vec<f32> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Given a scale note with root note 0, return a key offset
|
||||
// from the corresponding root MIDI key.
|
||||
fn note_to_key_offset(note: i8) -> i8 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
// Given a position within a chord, return a scale note
|
||||
// offset — zero-based.
|
||||
fn chord_to_note_offset(posn: i8) -> i8 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn pick_notes(chord_root: u8, position: &mut i8, rng: &mut rand::prelude::ThreadRng) -> [i8; 4] {
|
||||
todo!()
|
||||
}
|
||||
|
||||
// Turn a note name into a corresponding MIDI key number.
|
||||
// Format is name with optional bracketed octave, for example
|
||||
// "D" or "Eb[5]". Default is octave 4 if no octave is
|
||||
// specified.
|
||||
fn parse_note(note_str: &str, regex: Regex) -> u32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about, long_about = None)]
|
||||
struct Args {
|
||||
#[arg(short, long, default_value_t = 90)]
|
||||
bpm: u8,
|
||||
|
||||
#[arg(short, long, default_value_t = 48_000)]
|
||||
samplerate: u16,
|
||||
|
||||
#[arg(short, long, default_value_t = C5.to_string())]
|
||||
root: String,
|
||||
|
||||
#[arg(long, default_value_t = 2)]
|
||||
bassoctave: u32,
|
||||
|
||||
#[arg(long, default_value_t = 5)]
|
||||
balance: i8,
|
||||
|
||||
#[arg(short, long, default_value_t = -3)]
|
||||
gain: i8,
|
||||
|
||||
#[arg(short, long, default_value_t = NO_OUTPUT.to_string())]
|
||||
output: String,
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn main() {
|
||||
println!("Hello, pop!");
|
||||
let args: Args = Args::parse();
|
||||
let note_name_regexp: Regex = Regex::new(REGEX_BASE).unwrap();
|
||||
let mut rng: rand::prelude::ThreadRng = rand::thread_rng();
|
||||
let mut position: i8 = 0;
|
||||
|
||||
// Tempo in beats per minute.
|
||||
let bpm: u8 = args.bpm;
|
||||
|
||||
// Audio sample rate in samples per second.
|
||||
let samplerate: u16 = args.samplerate;
|
||||
|
||||
let melody_gain = args.balance;
|
||||
|
||||
// MIDI key where melody goes.
|
||||
let melody_root: u32 = parse_note(&args.root, note_name_regexp);
|
||||
let melody_root_i8: i8 = melody_root.try_into().unwrap();
|
||||
|
||||
// Bass MIDI key is below melody root.
|
||||
let bass_root: i8 = ((melody_root as i32) - (12i32 * args.bassoctave as i32))
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
BIN
midi-work.png
Normal file
BIN
midi-work.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 MiB |
11
notebook.md
11
notebook.md
@ -1,3 +1,14 @@
|
||||
### Friday 29-Nov-2024
|
||||
🤔 There was focus on rhosy this week during lecture, including fixing some bugs and adding polyphonic support
|
||||
|
||||
📝 [Popgen](./code/popgen/README.md) - I began work on this portfolio objective
|
||||
|
||||
🤔 I am exicited to find that with the proper adapter, my keyboard does in-fact work using MIDI. No additional efforts were needed aside from this. Qsynth and rhosy both worked successfully.
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
### Monday 25-Nov-2024
|
||||
🤔 I do not have much documentable work to show for the past week but my relevant focus has been on doing my best to follow along with [rhosy](https://github.com/pdx-cs-sound/rhosy), as well as continuing to try to get my keyboard to work with MIDI.
|
||||
* Rhosy has been an interesting project to watch unfold, and I have made a few minor contributions with short question responses during lecture. Creating electronic synthesis (well) turns out to be no small task, even in python. I'd like to commit to this project in the near future but at the moment my primary goal is to keep up with the ongoing progress of rhosy from the instructor.
|
||||
|
Reference in New Issue
Block a user