I was asked this question and got no feedback, so I'd like to ask you for any feedback on my solution, Thanks!
Think of an audio sample as a rubber-band that you want to pin to a musical time ruler. The pins are called Warp Markers. A Warp Marker locks a specific point in the sample (in sample time) to a specific place in a measure (in beat time). You can use any number of Warp Markers to create an arbitrary mapping of the sample’s inherent rhythm to a musical meter. For the sake of this task, please assume the following behavior:
- An audio clip contains a reference to a sample and a collection of Warp Markers.
- There is at least one Warp Marker in the clip.
- Between two Warp Markers, the tempo is constant.
- The tempo before the first Warp Marker is the same as the tempo after the first Warp Marker.
- The tempo after the last Warp Marker is specified separately in the input.
- Beat time is measured in beats, sample time is measured in seconds, and tempo is measured in beats per second. Input description
There are 4 kinds of lines in the input:
- Warp Marker definition
- Definition of the tempo after the last marker
- Sample time to beat time conversion
- Beat time to sample time conversion
At least one Warp Marker and the tempo after the last Warp Marker should be defined before the first conversion; otherwise, these can appear in any order. Each line consists of a keyword followed by numeric arguments. All numeric values are entered as doubles without units. Warp Marker and tempo definitions affect only the conversions that come later in the input.
marker <beat time> <sample time>
end_tempo <value>
s2b <sample time>
b2s <beat time>
Output description For each of the s2b and b2s lines, the corresponding output time is printed without unit. Example
Input
marker 0.0 0.0
marker 1.0 5.0
end_tempo 10.0
b2s 0.5
s2b 6.0
Output
2.5 11.0
Here's my solution:
//one.h
#include <vector>
#include <string>
#include <map>
using namespace std;
#ifndef SAMPLER_H
#define SAMPLER_H
class Sampler {
public:
map<double, double> beats = {};
map<double, double> samples = {};
double endTempo;
void addMarker(double beat, double sample);
double b2s(double beat);
double s2b(double sample);
};
#endif
and
// one.cc
#include "one.h"
using namespace std;
void Sampler::addMarker(double beat, double sample) {
beats[beat] = sample;
samples[sample] = beat;
}
double Sampler::b2s(double beat) {
auto marker2 = beats.upper_bound(beat);
if(marker2 == beats.begin()) marker2 = next(marker2);
auto marker1 = prev(marker2);
auto firstBeat = marker1->first;
auto firstSample = marker1->second;
double tempo;
if(marker2 == beats.end()) {
tempo = endTempo;
} else {
auto secondBeat = marker2->first;
auto secondSample = marker2->second;
tempo = (secondBeat - firstBeat) / (secondSample - firstSample);
}
return firstSample + ((beat - firstBeat) / tempo);
}
double Sampler::s2b(double sample) {
auto marker2 = samples.upper_bound(sample);
if(marker2 == samples.begin()) marker2 = next(marker2);
auto marker1 = prev(marker2);
auto firstBeat = marker1->second;
auto firstSample = marker1->first;
double tempo;
if(marker2 == samples.end()) {
tempo = endTempo;
} else {
auto secondBeat = marker2->second;
auto secondSample = marker2->first;
tempo = (secondBeat - firstBeat) / (secondSample - firstSample);
}
return firstBeat + ((sample - firstSample) * tempo);
}
and here's main
#include <iostream>
#include <vector>
#include <fstream>
#include <sstream>
#include "one.h"
int main() {
Sampler sampler;
string str;
while(true) {
getline(cin, str);
istringstream ss(str);
string command;
getline(ss, command, ' ');
if(command == "marker") {
string strBeat;
string strSample;
getline(ss, strBeat, ' ');
getline(ss, strSample, ' ');
double beat = stod(strBeat);
double sample = stod(strSample);
sampler.addMarker(beat, sample);
} else if(command == "end_tempo") {
string strTempo;
getline(ss, strTempo, ' ');
sampler.endTempo = stod(strTempo);
} else if(command == "s2b") {
string s;
getline(ss, s, ' ');
cout << sampler.s2b(stod(s)) << endl;
} else if(command == "b2s") {
string b;
getline(ss, b, ' ');
cout << sampler.b2s(stod(b)) << endl;
} else {
break;
}
}
}
and heres my tests
#include "one.h"
#include "gtest/gtest.h"
namespace {
class OneMarker : public ::testing::Test {
protected:
Sampler * sampler;
virtual void SetUp() {
sampler = new Sampler();
sampler->addMarker(3, 5);
sampler->endTempo = 10;
}
virtual void TearDown() {
delete sampler;
}
};
TEST_F(OneMarker, Before) {
EXPECT_EQ(4.6, sampler->b2s(-1));
EXPECT_EQ(-57, sampler->s2b(-1));
}
TEST_F(OneMarker, After) {
EXPECT_EQ(5.7, sampler->b2s(10));
EXPECT_EQ(53, sampler->s2b(10));
}
class TwoMarkers : public ::testing::Test {
protected:
Sampler * sampler;
virtual void SetUp() {
sampler = new Sampler();
sampler->addMarker(0, 0);
sampler->addMarker(1, 5);
sampler->endTempo = 10;
}
virtual void TearDown() {
delete sampler;
}
};
TEST_F(TwoMarkers, Before) {
EXPECT_EQ(-5.0, sampler->b2s(-1));
EXPECT_EQ(-0.2, sampler->s2b(-1));
}
TEST_F(TwoMarkers, Between) {
EXPECT_EQ(2.5, sampler->b2s(0.5));
EXPECT_EQ(11.0, sampler->s2b(6.0));
}
TEST_F(TwoMarkers, After) {
EXPECT_EQ(5.9, sampler->b2s(10));
EXPECT_EQ(51, sampler->s2b(10));
}
TEST_F(TwoMarkers, Mutation) {
EXPECT_EQ(5.9, sampler->b2s(10));
EXPECT_EQ(51, sampler->s2b(10));
sampler->addMarker(3,6);
EXPECT_EQ(6.7, sampler->b2s(10));
EXPECT_EQ(43, sampler->s2b(10));
}
}