// Copyright 2015 Olivier Gillet.
//
// Author: Olivier Gillet (ol.gillet@gmail.com)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
#ifndef MARBLES_TEST_FIXTURES_H_
#define MARBLES_TEST_FIXTURES_H_
#include
#include
#include "marbles/ramp/ramp_divider.h"
#include "marbles/ramp/ramp_extractor.h"
const size_t kSampleRate = 32000;
const size_t kAudioBlockSize = 8;
namespace marbles {
using namespace std;
using namespace stmlib;
class PulseGenerator {
public:
PulseGenerator() {
counter_ = 0;
previous_state_ = 0;
}
~PulseGenerator() { }
void AddPulses(int total_duration, int on_duration, int num_repetitions) {
Pulse p;
p.total_duration = total_duration;
p.on_duration = on_duration;
p.num_repetitions = num_repetitions;
pulses_.push_back(p);
}
void Render(GateFlags* clock, size_t size) {
while (size--) {
bool current_state = pulses_.size() && counter_ < pulses_[0].on_duration;
++counter_;
if (pulses_.size() && counter_ >= pulses_[0].total_duration) {
counter_ = 0;
--pulses_[0].num_repetitions;
if (pulses_[0].num_repetitions == 0) {
pulses_.erase(pulses_.begin());
}
}
previous_state_ = *clock++ = ExtractGateFlags(previous_state_, current_state);
}
}
private:
struct Pulse {
int total_duration;
int on_duration;
int num_repetitions;
};
int counter_;
GateFlags previous_state_;
vector pulses_;
DISALLOW_COPY_AND_ASSIGN(PulseGenerator);
};
enum PatternDifficulty {
FRIENDLY_PATTERNS,
FAST_PATTERNS,
TRICKY_PATTERNS,
PAUSE_PATTERNS,
};
class ClockGeneratorPatterns {
public:
ClockGeneratorPatterns(PatternDifficulty difficulty) {
if (difficulty == FRIENDLY_PATTERNS) {
pulse_generator_.AddPulses(800, 400, 100);
pulse_generator_.AddPulses(400, 32, 100);
for (int i = 0; i < 15; ++i) {
for (int j = 0; j < 5; ++j) {
pulse_generator_.AddPulses(600 - j * 100, 3, 2);
}
}
for (int i = 0; i < 300; ++i) {
int t = 200 + (rand() % 400);
pulse_generator_.AddPulses(t, t / 4, 1);
}
// Completely random clock.
for (int i = 0; i < 400; ++i) {
int t = 200 + (rand() % 800);
int pw = t / 4 + (rand() % (t / 2));
pulse_generator_.AddPulses(t, pw, 1);
}
return;
} else if (difficulty == FAST_PATTERNS) {
pulse_generator_.AddPulses(32, 16, 100);
pulse_generator_.AddPulses(16, 8, 100);
pulse_generator_.AddPulses(12, 6, 100);
pulse_generator_.AddPulses(8, 4, 100);
pulse_generator_.AddPulses(6, 3, 100);
pulse_generator_.AddPulses(4, 2, 100);
pulse_generator_.AddPulses(8, 4, 100);
pulse_generator_.AddPulses(12, 6, 100);
return;
} else if (difficulty == PAUSE_PATTERNS) {
pulse_generator_.AddPulses(800, 400, 100);
pulse_generator_.AddPulses(32000 * 5 + 10, 400, 1);
pulse_generator_.AddPulses(800, 400, 100);
}
// Steady clock
pulse_generator_.AddPulses(400, 200, 250);
pulse_generator_.AddPulses(4000 + (rand() % 1000), 2000, 10);
pulse_generator_.AddPulses(100, 10, 50);
pulse_generator_.AddPulses(16, 5, 100);
// Periodic clock with some jitter
for (int i = 0; i < 50; ++i) {
pulse_generator_.AddPulses(100, 10, 3);
pulse_generator_.AddPulses(400 + (i % 4), 10, 1);
pulse_generator_.AddPulses((i == 40) ? 40 : 300, 10, 1);
}
for (int i = 0; i < 50; ++i) {
pulse_generator_.AddPulses(100 + (i % 10), 10, 3);
pulse_generator_.AddPulses(200 + (i % 4), 10, 2);
pulse_generator_.AddPulses(300, 10, 1);
}
// Really long pattern that hashes well
for (int i = 0; i < 15; ++i) {
for (int j = 0; j < 10; ++j) {
pulse_generator_.AddPulses(100 + j * 30, 10, 1);
}
}
for (int i = 0; i < 15; ++i) {
for (int j = 0; j < 6; ++j) {
pulse_generator_.AddPulses(300 - j * 50, 3, 2);
}
}
// Random clock with reliable pulse width
for (int i = 0; i < 300; ++i) {
int t = 100 + (rand() % 400);
pulse_generator_.AddPulses(t, t / 4, 1);
}
// Completely random clock.
for (int i = 0; i < 400; ++i) {
int t = 100 + (rand() % 400);
int pw = t / 4 + (rand() % (t / 2));
pulse_generator_.AddPulses(t, pw, 1);
}
}
~ClockGeneratorPatterns() { }
void Render(size_t size) {
pulse_generator_.Render(buffer_, size);
}
GateFlags* clock() { return buffer_; }
private:
GateFlags buffer_[kAudioBlockSize];
PulseGenerator pulse_generator_;
DISALLOW_COPY_AND_ASSIGN(ClockGeneratorPatterns);
};
class MasterSlaveRampGenerator {
public:
MasterSlaveRampGenerator() {
ramp_extractor_.Init(4000.0f / kSampleRate);
ramp_divider_[0].Init();
ramp_divider_[1].Init();
}
~MasterSlaveRampGenerator() { }
void Process(const GateFlags* clock, size_t size) {
Ratio r = { 1, 1 };
ramp_extractor_.Process(r, true, clock, master_ramp_, size);
r.q = 2;
ramp_divider_[0].Process(r, master_ramp_, slave_ramp_1_, size);
r.p = 1; r.q = 2;
ramp_divider_[1].Process(r, master_ramp_, slave_ramp_2_, size);
}
Ramps ramps() {
Ramps r;
r.external = external_ramp_;
r.master = master_ramp_;
r.slave[0] = slave_ramp_1_;
r.slave[1] = slave_ramp_2_;
return r;
}
float* master_ramp() { return master_ramp_; }
float* slave_ramp_1() { return slave_ramp_1_; }
float* slave_ramp_2() { return slave_ramp_2_; }
private:
RampExtractor ramp_extractor_;
RampDivider ramp_divider_[2];
float external_ramp_[kAudioBlockSize];
float master_ramp_[kAudioBlockSize];
float slave_ramp_1_[kAudioBlockSize];
float slave_ramp_2_[kAudioBlockSize];
DISALLOW_COPY_AND_ASSIGN(MasterSlaveRampGenerator);
};
} // namespace marbles
#endif // MARBLES_TEST_FIXTURES_H_