4.6 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[4]
View Source
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 previous portfolio objective rodio, cpal, portaudio-rs were for audio playing in rust. I did a little research, and it seemed that
rodio
s 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 thebpm
- 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
intobass_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 thatrodio
seems to only work withf32
. 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 riskyas
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:
popgen.rs
seems to work ok. I have done some testing with various root note inputs and other parameters, but not exhaustively. Similar to popgen.py
, some note clicking is audible
Access outputs
Two output files were generated. I must have a slight bug in the save
function, because these sound are of somewhat lower quality.