## 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 TODO ## Reflections, Results, Analysis The effort involved for this portfolio objective was non trivial. I'll briefy discuss a some of the techniques and challenges involved specific to this implementation in rust. For a "how it works" perspective, I feel the comments copied over from python source and my few additions allow the code to be self documenting ### External Crates: * [clap](https://crates.io/crates/clap): In order to not drop any functionality from the original source, I wanted to ensure my application could handle all of the command-line argument as is done in popgen.py. I noticed there, [argparse](https://docs.python.org/3/library/argparse.html) was doing some heaving lifting. I only briefy pondered rolling my own command line parsing, but then I remembered a past experience doing so with much simpler arguments. That made using `clap` a straightforward decision. * [rand](https://crates.io/crates/rand): Picking the sequence of notes requires some randomness * [regex](https://crates.io/crates/regex): The go-to for regex, validating and parsing the note from the command line arguments * [rodio](https://crates.io/crates/rodio): Back in a portfolio objective rodio, cpal, portaudio-rs. I did a little research, and it seemed that rodio was the most abstract/easy to use, but still allowed me to manually set the sample rate as is necessary ### Typing hell The most tedious aspect of this objective was dealing with the typecasting needed for integer and float arithmetic. I won't discuss strong typing/rust arguments or any of that, but the crux of issues here came down to a few competing philosophies. * I want to use the smallest integer units possible to handle the basic parameters. For example, a `u8` should easily contain the `bpm` * Many parameters can be, and often are negative requiring the use of signed integers such as `melody_root`, `position`, etc. On one hand, I did not realize this in time, so some rework was required. On the other hand, there is a desire to capture initial values as unsigned if that seems appropriate, and cast them only as needed for signed arithmetic (Example: `chord_root` into `bass_note`). * I want to use the highest precision possible floating point representation for the wave data samples which go to the buffer, to ensure the most accurate audio. I initially chose `f64` for this before realizing that `rodio` seems to only work with `f32`. Aside from that, all sorts of casting was needed up from the base integer units. * Array indicies must be of type `usize` While the code here works it containers several occourences of risky `as` casting, and `.try_into().unwrap()` which are not properly handled. As I have time, I will fix some of this stuff up. ### Testing: I brought in the provided unit tests for `note_to_key_offset` and `chord_to_note_offset`. These turned out to be rather useful for ensuring my functions of the same name worked correctly. I began adding some tests for `make_notes` and may do the same for other functional units. I could move the tests into another source file, but decided not to, as to better parallel the source material. ### Results: