This repository has been archived on 2025-04-28. You can view files and clone it, but cannot push or open issues or pull requests.
computers-sound-music-portf.../code/popgen/README.md
2024-12-08 03:06:44 -08:00

4.4 KiB

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 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

Run

# Basic
cargo run

# See help
cargo run -- --help

# Example with flags
cargo run -- -b 80 --root C[2]

View Source

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: 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 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: Picking the sequence of notes requires some randomness
  • regex: The go-to for regex, validating and parsing the note from the command line arguments
  • 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: