diff --git a/pop/loner/bin/halo3.py b/pop/loner/bin/halo3.py index b7f67182a7..30aa2eabe9 100644 --- a/pop/loner/bin/halo3.py +++ b/pop/loner/bin/halo3.py @@ -239,14 +239,23 @@ CHART = { 7: 2.0, 8: 2.0, 9: 4.0, 10: 3.0, 11: 3.0, # very longer, patiently slower - 14: 4.0, - # pa 2 · tient 1.5 · ly 1.5 = the - # same 5 beats patiently had - 15: 2.0, 16: 1.5, 17: 1.5 }, + # the tail on bars: just and waiting + # a bar each (waiting on bar 8:1), + # very as TWO notes, and "pa" held a + # full bar because at 2 beats it was + # too fast and abrupt + 12: 4.0, 13: 4.0, 14: 2.0, 15: 2.0, + 16: 4.0, 17: 1.5, 18: 1.5 }, # words whose syllables carry a melody must not # be flattened to one tone by THE HOLD # patiently is pa·tient·ly, three notes - "sylls": { 13: [(None, "pa"), (16.80, "tient"), (17.70, "ly")] }, + "sylls": { 12: [(None, "ve"), (15.05, "ry")], + 13: [(None, "pa"), (16.80, "tient"), + (17.70, "ly")] }, + # "pa" sings F4, a leap up from very's A#3. + # Down one scale step to D#4 keeps the phrase + # from jumping before it descends. + "shift": { 16: -2 }, # source seconds, read off her onsets, for the # words whisper-1 mistimed. PRE-split indices. "end": 24.25, @@ -830,6 +839,20 @@ for name, ch in CHART.items(): f0_o[o0:o1] = np.where(voiced_o[o0:o1], f0_o[o0:o1] * (1 - blend) + tgt * vib * blend, 0.0) + # PER-SYLLABLE TRANSPOSE. Sometimes the note she sang is not the note + # the score wants — @jeffrey: "the pa in patiently should be pitched + # down". `shift` moves one unit by N semitones, in her f0 AND in the + # chart, so the pluck doubles the new note rather than the old one. + for ui, semis in (ch.get("shift") or {}).items(): + if not (0 <= ui < len(ch["units"])): + continue + bt, du = ch["units"][ui] + o0 = Z + int(round(bt * SPB / FRAME_S)) + o1 = Z + int(round((bt + du) * SPB / FRAME_S)) + o0, o1 = max(0, o0), min(len(f0_o), o1) + if o1 > o0: + f0_o[o0:o1] *= 2.0 ** (semis / 12.0) + renders = {} out, _ = synth_from(a, idx, f0_o, fade=fade, rise=rise, rests=rests) sf.write(os.path.join(VOX4, f"{name}.wav"), out, fs) @@ -884,6 +907,7 @@ for name, ch in CHART.items(): notes = [] for u, (wd, (beat, durb)) in enumerate(zip(words, ch["units"])): st = int(np.round(12.0 * np.log2(wd["f0_hz"] / TONIC))) if wd["f0_hz"] else 0 + st += int((ch.get("shift") or {}).get(u, 0)) notes.append((beat, durb, st, wd["t"].strip(), round(ants[u] * FRAME_S / SPB, 4) if u < len(ants) else 0.0)) chart_c.append((name, lead_in, beats_total, notes)) diff --git a/pop/loner/c/loner-chart.h b/pop/loner/c/loner-chart.h index 6b64357c41..58e11f4d61 100644 --- a/pop/loner/c/loner-chart.h +++ b/pop/loner/c/loner-chart.h @@ -25,16 +25,17 @@ static const ChartNote w_whole_line_notes[] = { { 18.00, 4.00, 12 }, { 22.00, 3.00, 10 }, { 25.00, 3.00, 5 }, - { 28.00, 3.50, 2 }, - { 31.50, 4.50, 3 }, - { 36.00, 4.00, 0 }, - { 40.00, 2.00, 7 }, - { 42.00, 1.50, 5 }, - { 43.50, 1.50, 3 }, - { 45.00, 2.00, 5 }, - { 47.00, 4.50, 7 }, - { 51.50, 4.00, 3 }, - { 55.50, 3.00, 3 }, + { 28.00, 4.00, 2 }, + { 32.00, 4.00, 3 }, + { 36.00, 2.00, 0 }, + { 38.00, 2.00, -2 }, + { 40.00, 4.00, 5 }, + { 44.00, 1.50, 5 }, + { 45.50, 1.50, 3 }, + { 47.00, 2.00, 5 }, + { 49.00, 4.50, 7 }, + { 53.50, 4.00, 3 }, + { 57.50, 3.00, 3 }, }; static const ChartNote w_sitting_curled_notes[] = { { 0.00, 3.00, 6 }, @@ -91,7 +92,7 @@ static const ChartNote w_n_for_time_to_pass_notes[] = { }; static const ChartPhrase CHART[] = { - { "w-whole-line", 0.490, 58.50, 22, w_whole_line_notes }, + { "w-whole-line", 0.490, 60.50, 23, w_whole_line_notes }, { "w-sitting-curled", 0.020, 11.00, 5, w_sitting_curled_notes }, { "w-i-think", 0.020, 3.50, 2, w_i_think_notes }, { "w-of-a-stone", 0.000, 8.00, 3, w_of_a_stone_notes }, diff --git a/pop/loner/vox4/.chart.json b/pop/loner/vox4/.chart.json index de3c8308c5..eb5224daa1 100644 --- a/pop/loner/vox4/.chart.json +++ b/pop/loner/vox4/.chart.json @@ -1,7 +1,7 @@ { "w-whole-line": { "leadIn": 0.49, - "beats": 58.5, + "beats": 60.5, "voiced": [ [ 0.0, @@ -21,27 +21,27 @@ ], [ 24.9998, - 31.2015 + 31.6997 ], [ - 31.4963, - 41.8968 + 32.0047, + 43.8997 ], [ - 41.9985, - 44.9367 + 44.0013, + 46.9395 ], [ - 44.9977, - 46.8988 + 47.0005, + 48.9017 ], [ - 47.0005, - 55.0627 + 49.0033, + 57.0655 ], [ - 55.2558, - 59.3123 + 57.2587, + 61.3152 ] ], "notes": [ @@ -131,69 +131,76 @@ }, { "beat": 28.0, - "dur": 3.5, + "dur": 4.0, "st": 2, "t": "just", "lead": 0.0 }, { - "beat": 31.5, - "dur": 4.5, + "beat": 32.0, + "dur": 4.0, "st": 3, "t": "waiting", "lead": 0.244 }, { "beat": 36.0, - "dur": 4.0, + "dur": 2.0, "st": 0, - "t": "very", + "t": "ve", "lead": 0.0 }, { - "beat": 40.0, + "beat": 38.0, "dur": 2.0, - "st": 7, + "st": -2, + "t": "ry", + "lead": 0.0 + }, + { + "beat": 40.0, + "dur": 4.0, + "st": 5, "t": "pa", "lead": 0.0 }, { - "beat": 42.0, + "beat": 44.0, "dur": 1.5, "st": 5, "t": "tient", "lead": 0.0915 }, { - "beat": 43.5, + "beat": 45.5, "dur": 1.5, "st": 3, "t": "ly", "lead": 0.0 }, { - "beat": 45.0, + "beat": 47.0, "dur": 2.0, "st": 5, "t": "for", "lead": 0.0508 }, { - "beat": 47.0, + "beat": 49.0, "dur": 4.5, "st": 7, "t": "time", "lead": 0.0712 }, { - "beat": 51.5, + "beat": 53.5, "dur": 4.0, "st": 3, "t": "to", "lead": 0.0 }, { - "beat": 55.5, + "beat": 57.5, "dur": 3.0, "st": 3, "t": "pass", diff --git a/pop/loner/vox4/.manifest.json b/pop/loner/vox4/.manifest.json index 93a401f8f9..0a1e4dc78b 100644 --- a/pop/loner/vox4/.manifest.json +++ b/pop/loner/vox4/.manifest.json @@ -2,13 +2,13 @@ "w-whole-line": { "slice": "f-whole-line", "lead_in": 0.49, - "beats": 58.5, + "beats": 60.5, "renders": { - "lead": 29.66, - "8ve-a": 29.66, - "8ve-b": 29.66, - "low3": 29.66, - "low5": 29.66 + "lead": 30.645, + "8ve-a": 30.645, + "8ve-b": 30.645, + "low3": 30.645, + "low5": 30.645 }, "snaps": [ "curled +65ms", @@ -21,7 +21,8 @@ "stone -45ms", "just +80ms", "waiting -100ms", - "very +65ms", + "ve +65ms", + "ry +160ms", "pa +80ms", "tient -15ms", "ly -5ms", @@ -36,7 +37,7 @@ "think \u2212350ms", "stone \u2212675ms", "just \u2212195ms", - "very \u2212520ms", + "ry \u2212520ms", "tient \u2212480ms", "time \u2212855ms" ],