-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
178 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
/// Exercise 8.1 | ||
/// | ||
/// Gradually scale the volume of each event in the | ||
/// performance by a factor of 1 through 1 + x, | ||
/// using linear interpolation. | ||
const fn scale_volume() { | ||
// this is already done in the definition of Fancy Player | ||
// see the inflate in the `fn fancy_interpret_phrase`. | ||
} | ||
|
||
// TODO: Exercises 8.2, 8.3, 8.5 should be defined for the "Fancy" player | ||
// 8.2: Ornament::Trill, Ornament::Mordent, Ornament::InvMordent, Ornament::Turn; | ||
// 8.3: Articulation::Pedal, Ornament::ArpeggioUp, Ornament::ArpeggioDown; | ||
// 8.5: Ornament::DiatonicTrans; | ||
|
||
/// Exercise 8.4 | ||
/// | ||
/// Define a player `jazzMan` that plays a melody using a jazz “swing” feel. | ||
/// Since there are different kinds and degrees of swing, we can be more specific | ||
/// as follows: whenever there is a sequence of two eighth notes, | ||
/// they should be interpreted instead as a quarter note followed by an eighth note, | ||
/// but with tempo 3/2. So in essence, the first note is lengthened, | ||
/// and the second note is shortened, so that the first note is twice as long as the second, | ||
/// but they still take up the same amount of overall time. | ||
/// | ||
/// (Hint: There are several ways to solve this problem. | ||
/// One surprisingly effective and straightforward solution | ||
/// is to implement `jazzMan` as a `NoteFun`, not a `PhraseFun`. | ||
/// In jazz, if an eighth note falls on a quarter-note beat it is | ||
/// said to fall on the “downbeat”, and the eighth notes that are in between are | ||
/// said to fall on the “upbeat”. | ||
/// | ||
/// For example, in the phrase `c4 en :+: d4 en :+: e4 en :+: f4 en`, | ||
/// the `C` and `E` fall on the downbeat, and the `D` and `F` fall | ||
/// on the upbeat. So to get a “swing feel,” the notes on the down beat need to | ||
/// be lengthened, and ones on the upbeat need to be delayed and shortened. | ||
/// Whether an event falls on a downbeat or upbeat can be determined from | ||
/// the `start_time` and `duration` of the context.) | ||
mod jazz_man { | ||
use std::sync::Arc; | ||
|
||
use num_rational::Ratio; | ||
|
||
use musik::{ | ||
music::AttrNote, | ||
performance::{defaults::default_note_attribute_handler, Context, Event}, | ||
Dur, Performance, Player, | ||
}; | ||
|
||
pub fn swing_play_note( | ||
ctx: Context<AttrNote>, | ||
dur: Dur, | ||
(note_pitch, attrs): &AttrNote, | ||
) -> Performance { | ||
let Context { | ||
start_time, | ||
player: _ignore_player, | ||
instrument, | ||
whole_note, | ||
pitch, | ||
volume, | ||
key: _ignore_key, | ||
} = ctx.clone(); | ||
|
||
let number_of_beats_since_start = start_time / whole_note; | ||
// denom belongs to {1, 2, 4} | ||
let is_downbeat = 4 % (*number_of_beats_since_start.denom()) == 0; | ||
let is_upbeat = number_of_beats_since_start.denom() == &8; | ||
|
||
let (start_time, dur) = { | ||
// only for eight notes | ||
if dur == Dur::EN { | ||
if is_downbeat { | ||
(start_time, dur * Ratio::new(4, 3)) | ||
} else if is_upbeat { | ||
let lengthened_on = Ratio::new(1, 24) * ctx.whole_note; | ||
(start_time + lengthened_on, dur * Ratio::new(2, 3)) | ||
} else { | ||
(start_time, dur) | ||
} | ||
} else { | ||
(start_time, dur) | ||
} | ||
}; | ||
|
||
let init = Event { | ||
start_time, | ||
instrument, | ||
pitch: note_pitch.abs() + pitch, | ||
duration: dur.into_ratio() * whole_note, | ||
volume, | ||
params: vec![], | ||
}; | ||
|
||
let event = attrs.iter().fold(init, |acc, attr| { | ||
default_note_attribute_handler()(&ctx, attr, acc) | ||
}); | ||
Performance::with_events(vec![event]) | ||
} | ||
|
||
fn get_simple_swing_player() -> Player<AttrNote> { | ||
Player { | ||
name: "Jazz".to_string(), | ||
play_note: Arc::new(swing_play_note), | ||
..Player::default() | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use std::{borrow::Cow, collections::HashMap}; | ||
|
||
use musik::{ | ||
instruments::StandardMidiInstrument, music::Volume, AbsPitch, Music, Octave, Pitch, | ||
PitchClass, | ||
}; | ||
|
||
use super::*; | ||
|
||
#[test] | ||
fn simple_swing() { | ||
let oc4 = Octave::ONE_LINED; | ||
let m: Music<AttrNote> = Music::line( | ||
[PitchClass::C, PitchClass::D, PitchClass::E, PitchClass::F] | ||
.into_iter() | ||
.map(|pc| Music::note(Dur::EN, Pitch::new(pc, oc4))) | ||
.collect(), | ||
) | ||
.into(); | ||
|
||
let players: HashMap<_, _> = Some(get_simple_swing_player()) | ||
.into_iter() | ||
.map(|p| (p.name.clone(), p)) | ||
.collect(); | ||
let ctx = Context::with_player(Cow::Borrowed(players.get("Jazz").unwrap())); | ||
let perf = m.perform(&players, ctx); | ||
|
||
assert_eq!( | ||
perf.into_events(), | ||
[ | ||
Event { | ||
start_time: Ratio::from_integer(0), | ||
instrument: StandardMidiInstrument::AcousticGrandPiano.into(), | ||
pitch: AbsPitch::from(48), | ||
duration: Ratio::new(1, 3), | ||
volume: Volume::loudest(), | ||
params: vec![] | ||
}, | ||
Event { | ||
start_time: Ratio::new(1, 3), | ||
instrument: StandardMidiInstrument::AcousticGrandPiano.into(), | ||
pitch: AbsPitch::from(50), | ||
duration: Ratio::new(1, 6), | ||
volume: Volume::loudest(), | ||
params: vec![] | ||
}, | ||
Event { | ||
start_time: Ratio::new(1, 2), | ||
instrument: StandardMidiInstrument::AcousticGrandPiano.into(), | ||
pitch: AbsPitch::from(52), | ||
duration: Ratio::new(1, 3), | ||
volume: Volume::loudest(), | ||
params: vec![] | ||
}, | ||
Event { | ||
start_time: Ratio::new(5, 6), | ||
instrument: StandardMidiInstrument::AcousticGrandPiano.into(), | ||
pitch: AbsPitch::from(53), | ||
duration: Ratio::new(1, 6), | ||
volume: Volume::loudest(), | ||
params: vec![] | ||
} | ||
] | ||
); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ mod ch4; | |
mod ch5; | ||
mod ch6; | ||
mod ch7; | ||
mod ch8; | ||
|
||
fn simple<A, B, C, D, E>(x: A, y: B, z: C) -> E | ||
where | ||
|