mirror of
https://github.com/RGBCube/serenity
synced 2025-07-19 02:17:35 +00:00

This has mainly performance benefits, so that we only need to call into all processors once for every audio buffer segment. It requires adjusting quite some logic in most processors and in Track, as we have to consider a larger collection of notes and samples at each step. There's some cautionary TODOs in the currently unused LibDSP tracks because they don't do things properly yet.
201 lines
5.4 KiB
C++
201 lines
5.4 KiB
C++
/*
|
|
* Copyright (c) 2021, kleines Filmröllchen <filmroellchen@serenityos.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <AK/FixedArray.h>
|
|
#include <AK/HashMap.h>
|
|
#include <AK/Noncopyable.h>
|
|
#include <AK/Types.h>
|
|
#include <AK/Variant.h>
|
|
#include <AK/Vector.h>
|
|
#include <LibAudio/Sample.h>
|
|
#include <LibDSP/Envelope.h>
|
|
|
|
namespace LibDSP {
|
|
|
|
using Sample = Audio::Sample;
|
|
|
|
constexpr Sample const SAMPLE_OFF = { 0.0, 0.0 };
|
|
|
|
struct RollNote {
|
|
constexpr u32 length() const { return (off_sample - on_sample) + 1; }
|
|
|
|
u32 on_sample;
|
|
u32 off_sample;
|
|
u8 pitch;
|
|
i8 velocity;
|
|
|
|
constexpr Envelope to_envelope(u32 time, u32 attack_samples, u32 decay_samples, u32 release_samples) const
|
|
{
|
|
i64 time_since_end = static_cast<i64>(time) - static_cast<i64>(off_sample);
|
|
// We're before the end of this note.
|
|
if (time_since_end < 0) {
|
|
i64 time_since_start = static_cast<i64>(time) - static_cast<i64>(on_sample);
|
|
if (time_since_start < 0)
|
|
return {};
|
|
|
|
if (time_since_start < attack_samples) {
|
|
if (attack_samples == 0)
|
|
return Envelope::from_attack(0);
|
|
return Envelope::from_attack(static_cast<double>(time_since_start) / static_cast<double>(attack_samples));
|
|
}
|
|
if (time_since_start < attack_samples + decay_samples) {
|
|
if (decay_samples == 0)
|
|
return Envelope::from_decay(0);
|
|
return Envelope::from_decay(static_cast<double>(time_since_start - attack_samples) / static_cast<double>(decay_samples));
|
|
}
|
|
// This is a note-dependent value!
|
|
u32 sustain_samples = length() - attack_samples - decay_samples;
|
|
return Envelope::from_sustain(static_cast<double>(time_since_start - attack_samples - decay_samples) / static_cast<double>(sustain_samples));
|
|
}
|
|
|
|
// Overshot the release time
|
|
if (time_since_end > release_samples)
|
|
return {};
|
|
return Envelope::from_release(static_cast<double>(time_since_end) / static_cast<double>(release_samples));
|
|
}
|
|
|
|
constexpr bool is_playing(u32 time) const { return on_sample <= time && time <= off_sample; }
|
|
};
|
|
|
|
enum class SignalType : u8 {
|
|
Invalid,
|
|
Sample,
|
|
Note
|
|
};
|
|
|
|
// Perfect hashing for note (MIDI) values. This just uses the note value as the hash itself.
|
|
class PerfectNoteHashTraits : Traits<u8> {
|
|
public:
|
|
static constexpr bool equals(u8 const& a, u8 const& b) { return a == b; }
|
|
static constexpr unsigned hash(u8 value)
|
|
{
|
|
return static_cast<unsigned>(value);
|
|
}
|
|
};
|
|
|
|
using RollNotes = OrderedHashMap<u8, RollNote, PerfectNoteHashTraits>;
|
|
|
|
struct Signal : public Variant<FixedArray<Sample>, RollNotes> {
|
|
using Variant::Variant;
|
|
AK_MAKE_NONCOPYABLE(Signal);
|
|
|
|
public:
|
|
Signal& operator=(Signal&&) = default;
|
|
Signal(Signal&&) = default;
|
|
|
|
ALWAYS_INLINE SignalType type() const
|
|
{
|
|
if (has<FixedArray<Sample>>())
|
|
return SignalType::Sample;
|
|
if (has<RollNotes>())
|
|
return SignalType::Note;
|
|
return SignalType::Invalid;
|
|
}
|
|
};
|
|
|
|
// Equal temperament, A = 440Hz
|
|
// We calculate note frequencies relative to A4:
|
|
// 440.0 * pow(pow(2.0, 1.0 / 12.0), N)
|
|
// Where N is the note distance from A.
|
|
constexpr Array<double, 84> note_frequencies = {
|
|
// Octave 1
|
|
32.703195662574764,
|
|
34.647828872108946,
|
|
36.708095989675876,
|
|
38.890872965260044,
|
|
41.203444614108669,
|
|
43.653528929125407,
|
|
46.249302838954222,
|
|
48.99942949771858,
|
|
51.913087197493056,
|
|
54.999999999999915,
|
|
58.270470189761156,
|
|
61.735412657015416,
|
|
// Octave 2
|
|
65.406391325149571,
|
|
69.295657744217934,
|
|
73.416191979351794,
|
|
77.781745930520117,
|
|
82.406889228217381,
|
|
87.307057858250872,
|
|
92.4986056779085,
|
|
97.998858995437217,
|
|
103.82617439498618,
|
|
109.99999999999989,
|
|
116.54094037952237,
|
|
123.4708253140309,
|
|
// Octave 3
|
|
130.8127826502992,
|
|
138.59131548843592,
|
|
146.83238395870364,
|
|
155.56349186104035,
|
|
164.81377845643485,
|
|
174.61411571650183,
|
|
184.99721135581709,
|
|
195.99771799087452,
|
|
207.65234878997245,
|
|
219.99999999999989,
|
|
233.08188075904488,
|
|
246.94165062806198,
|
|
// Octave 4
|
|
261.62556530059851,
|
|
277.18263097687202,
|
|
293.66476791740746,
|
|
311.12698372208081,
|
|
329.62755691286986,
|
|
349.22823143300383,
|
|
369.99442271163434,
|
|
391.99543598174927,
|
|
415.30469757994513,
|
|
440,
|
|
466.16376151808993,
|
|
493.88330125612413,
|
|
// Octave 5
|
|
523.25113060119736,
|
|
554.36526195374427,
|
|
587.32953583481526,
|
|
622.25396744416196,
|
|
659.25511382574007,
|
|
698.456462866008,
|
|
739.98884542326903,
|
|
783.99087196349899,
|
|
830.60939515989071,
|
|
880.00000000000034,
|
|
932.32752303618031,
|
|
987.76660251224882,
|
|
// Octave 6
|
|
1046.5022612023952,
|
|
1108.7305239074892,
|
|
1174.659071669631,
|
|
1244.5079348883246,
|
|
1318.5102276514808,
|
|
1396.9129257320169,
|
|
1479.977690846539,
|
|
1567.9817439269987,
|
|
1661.2187903197821,
|
|
1760.000000000002,
|
|
1864.6550460723618,
|
|
1975.5332050244986,
|
|
// Octave 7
|
|
2093.0045224047913,
|
|
2217.4610478149793,
|
|
2349.3181433392633,
|
|
2489.0158697766506,
|
|
2637.020455302963,
|
|
2793.8258514640347,
|
|
2959.9553816930793,
|
|
3135.9634878539991,
|
|
3322.437580639566,
|
|
3520.0000000000055,
|
|
3729.3100921447249,
|
|
3951.0664100489994,
|
|
};
|
|
|
|
constexpr double const middle_c = note_frequencies[36];
|
|
|
|
}
|