patternMinor
Musical Fizzbuzz
Viewed 0 times
musicalfizzbuzzstackoverflow
Problem
I wrote some code using ChucK : Strongly-timed, Concurrent, and On-the-fly
Music Programming Language and would like a review. I broke down the logic into a number of classes, all of which are shown below.
This is pretty new to me, and I would appreciate any and all constructive criticism. What the main script
Clarification
I want to clarify a few things that are specific to ChucK:
Code on Github
I uploaded a video to YouTube showing what it sounds like. (video not embedded, link opens YouTube)
OscPitch.ck
```
public class OscPitch {
this.freq => float freq;
getSemitoneRatio() => float SEMITONE;
// Change the pitch by N musical steps
// @param float steps : the number of steps to change the frequency, positive or negative
// @return float : the changed frequency
fun float change(float steps) {
if (Math.isnan(steps)) {
>>;
return freq;
} else {
// The semitone ratio has to be applied once for each step change
// hence the use of Math.pow which does everything
// in one operation, rather than having to use a loop
return freq * Math.pow(SEMITONE, steps) => freq;
}
}
// Get the ratio to apply to a frequency to go up or down one semitone
fun float getSemitoneRatio() {
// The ratio for a musical semitone is
// the 12th root of 2, or approximately 1.05946309436
// More info: https://en.wikipedia.org/wiki/Twelfth_ro
Music Programming Language and would like a review. I broke down the logic into a number of classes, all of which are shown below.
This is pretty new to me, and I would appreciate any and all constructive criticism. What the main script
fizzbuzz_run.ck does, besides the regular FizzBuzz modulus operations, is play different chords depending on the FizzBuzz result, and throughout print the results and primary oscillator frequency.Clarification
I want to clarify a few things that are specific to ChucK:
- printing statements to console:
>>;
- The ChucK operator
=>is a bit like your usual assignment, except reverse order. For example42 => int theMeaningOfLifeAndEverything;is how you would otherwiseint theMeaningOfLifeAndEverything = 42;.
- Time and duration are handled by the ChucK VM.
Code on Github
I uploaded a video to YouTube showing what it sounds like. (video not embedded, link opens YouTube)
OscPitch.ck
```
public class OscPitch {
this.freq => float freq;
getSemitoneRatio() => float SEMITONE;
// Change the pitch by N musical steps
// @param float steps : the number of steps to change the frequency, positive or negative
// @return float : the changed frequency
fun float change(float steps) {
if (Math.isnan(steps)) {
>>;
return freq;
} else {
// The semitone ratio has to be applied once for each step change
// hence the use of Math.pow which does everything
// in one operation, rather than having to use a loop
return freq * Math.pow(SEMITONE, steps) => freq;
}
}
// Get the ratio to apply to a frequency to go up or down one semitone
fun float getSemitoneRatio() {
// The ratio for a musical semitone is
// the 12th root of 2, or approximately 1.05946309436
// More info: https://en.wikipedia.org/wiki/Twelfth_ro
Solution
Really interesting thing you've created! I've never used ChucK, but I'll try to comment on some things. I also have some, but limited music theory knowledge. If language constraints is a reason for doing something I've mentioned below, please excuse my ignorance :)
Semitone ratio?
I wonder why you chose to create the
Additionally, I wouldn't actually call it a ratio. It's a factor.
Dry initialization
Since you already have the
Dry initialization 2
Another approach to this is creating a factory function for
Gain?
In
It's confusing that one is just named
Semitone ratio?
I wonder why you chose to create the
getSemitoneRatio function. It's only used to initialize the SEMITONE variable. Why not just remove that function and put the body as the initializer expression instead?Additionally, I wouldn't actually call it a ratio. It's a factor.
// The factor to apply to a frequency to go up or down one semitone
// More info: https://en.wikipedia.org/wiki/Twelfth_root_of_two
Math.pow(2.0, 1.0/12.0) => float SEMITONE_FACTOR;Dry initialization
// initialize oscillators
0 => I.freq;
0 => III.freq;
0 => V.freq;
0 => VII.freq;
mute => I.gain;
mute => III.gain;
mute => V.gain;
mute => VII.gain;
I => dac;
III => dac;
V => dac;
VII => dac;
// Sets all chord frequencies to 0, making them inaudible.
fun void noChord() {
0 => I.freq;
0 => III.freq;
0 => V.freq;
0 => VII.freq;
"no chord" => currentChord;
}Since you already have the
noChord function you can easily remove part of this code duplication. Just call noChord instead of the first four lines after the comment. The side-effect of initializing currentChord as well is probably just good.Dry initialization 2
Another approach to this is creating a factory function for
SawOsc. Compare the following examples.SawOsc I, III, V, VII;
0 => I.freq;
0 => III.freq;
0 => V.freq;
0 => VII.freq;
mute => I.gain;
mute => III.gain;
mute => V.gain;
mute => VII.gain;createSawOsc(0, mute) => SawOsc I;
createSawOsc(0, mute) => SawOsc III;
createSawOsc(0, mute) => SawOsc V;
createSawOsc(0, mute) => SawOsc VII;Gain?
In
OscChord there are two variables representing different gain levels.0.05 => float gain;
0.0 => float mute;It's confusing that one is just named
gain while the other one is called mute. Perhaps call them muteGain and playGain to show their relationship.Code Snippets
// The factor to apply to a frequency to go up or down one semitone
// More info: https://en.wikipedia.org/wiki/Twelfth_root_of_two
Math.pow(2.0, 1.0/12.0) => float SEMITONE_FACTOR;// initialize oscillators
0 => I.freq;
0 => III.freq;
0 => V.freq;
0 => VII.freq;
mute => I.gain;
mute => III.gain;
mute => V.gain;
mute => VII.gain;
I => dac;
III => dac;
V => dac;
VII => dac;
// Sets all chord frequencies to 0, making them inaudible.
fun void noChord() {
0 => I.freq;
0 => III.freq;
0 => V.freq;
0 => VII.freq;
"no chord" => currentChord;
}SawOsc I, III, V, VII;
0 => I.freq;
0 => III.freq;
0 => V.freq;
0 => VII.freq;
mute => I.gain;
mute => III.gain;
mute => V.gain;
mute => VII.gain;createSawOsc(0, mute) => SawOsc I;
createSawOsc(0, mute) => SawOsc III;
createSawOsc(0, mute) => SawOsc V;
createSawOsc(0, mute) => SawOsc VII;0.05 => float gain;
0.0 => float mute;Context
StackExchange Code Review Q#106791, answer score: 9
Revisions (0)
No revisions yet.