|
> typedef uint16_t uint16; does adding that little "_t" hurt you that much ? think that now every programmer reading your code will wonder "hmm... what is that uint16 type ? is it equivalent to uint16_t ? is it some weird macro designed to accomodate 1990 compilers ?" etc etc. > const char *fileName for the love of all things holy, use std::string_view for non-performance-critical stuff like this. More generally, the only remotely C++-like thing in your code is the use of std::vector. The rest is honestly more C than C++. This shows for instance in SampleChannelFractional with that ugly #if 0. Proper C++ design would have SampleChannelFractional be instead a function object that you could pass as template argument: this way, the user can choose which implementation he wants without requiring a recompilation, and without indirection cost. In addition, if you change some parts of your code, the C++ compiler will be able to check both code paths directly. That is : struct LinearSampleChannelFractional
{
float operator()(const std::vector<float>& input
, float sampleFloat
, uint16 channel
, uint16 numChannels)
{
// your linear implementation here
}
};
struct CubicSampleChannelFractional
{
float operator()(const std::vector<float>& input
, float sampleFloat
, uint16 channel
, uint16 numChannels)
{
// your cubic implementation here
}
private:
float CubicHermite (float A, float B, float C, float D, float t)
{
// encapsulate it here:
// the rest of your code does not care about this function.
}
};
// First modification: pass Fractional as template argument
template<typename Fractional>
void TimeAdjust (...)
{
// replace SampleChannelFractional:
output[...] = Fractional{}(input, srcSampleFloat, channel, numChannels);
}
template<typename Fractional>
void SplatGrainToOutput (...)
{
// same
output[...] = Fractional{}(...);
}
// Second modification: refactor this in a Granulator class of some sorts...
// and use the standard C++ naming convention
template<typename Fractional>
class granulator
{
public:
void time_adjust (...)
{
// uses the class template argument
output[...] = Fractional{}(input, srcSampleFloat, channel, numChannels);
}
void splat_grain_to_output (...)
{
// same
output[...] = Fractional{}(...);
}
void granular_time_pitch_adjust (...)
{
// ... splat_grain_to_output(...)
}
int main()
{
// now both kinds can be used at the same time in your code, and both will be
// just as efficient as with the #if 1 ; for instance the choice
// of the granulator to use can then be part of a configuration option
// in a GUI software
constexpr granulator<cubic_sample_channel_fractional> gran1;
gran1.time_adjust(source, out, numChannels, 0.7f);
constexpr granulator<linear_sample_channel_fractional> gran2;
gran2.time_adjust(source, out, numChannels, 0.7f);
}
|