mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-11-10 13:07:26 +01:00
272 lines
7.6 KiB
C
272 lines
7.6 KiB
C
|
/*
|
||
|
* Copyright 2019 The Android Open Source Project
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
#ifndef OBOE_MULTICHANNEL_RESAMPLER_H
|
||
|
#define OBOE_MULTICHANNEL_RESAMPLER_H
|
||
|
|
||
|
#include <memory>
|
||
|
#include <vector>
|
||
|
#include <sys/types.h>
|
||
|
#include <unistd.h>
|
||
|
|
||
|
#ifndef MCR_USE_KAISER
|
||
|
// It appears from the spectrogram that the HyperbolicCosine window leads to fewer artifacts.
|
||
|
// And it is faster to calculate.
|
||
|
#define MCR_USE_KAISER 0
|
||
|
#endif
|
||
|
|
||
|
#if MCR_USE_KAISER
|
||
|
#include "oboe_flowgraph_resampler_KaiserWindow_android.h"
|
||
|
#else
|
||
|
#include "oboe_flowgraph_resampler_HyperbolicCosineWindow_android.h"
|
||
|
#endif
|
||
|
|
||
|
namespace resampler {
|
||
|
|
||
|
class MultiChannelResampler {
|
||
|
|
||
|
public:
|
||
|
|
||
|
enum class Quality : int32_t {
|
||
|
Fastest,
|
||
|
Low,
|
||
|
Medium,
|
||
|
High,
|
||
|
Best,
|
||
|
};
|
||
|
|
||
|
class Builder {
|
||
|
public:
|
||
|
/**
|
||
|
* Construct an optimal resampler based on the specified parameters.
|
||
|
* @return address of a resampler
|
||
|
*/
|
||
|
MultiChannelResampler *build();
|
||
|
|
||
|
/**
|
||
|
* The number of taps in the resampling filter.
|
||
|
* More taps gives better quality but uses more CPU time.
|
||
|
* This typically ranges from 4 to 64. Default is 16.
|
||
|
*
|
||
|
* For polyphase filters, numTaps must be a multiple of four for loop unrolling.
|
||
|
* @param numTaps number of taps for the filter
|
||
|
* @return address of this builder for chaining calls
|
||
|
*/
|
||
|
Builder *setNumTaps(int32_t numTaps) {
|
||
|
mNumTaps = numTaps;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Use 1 for mono, 2 for stereo, etc. Default is 1.
|
||
|
*
|
||
|
* @param channelCount number of channels
|
||
|
* @return address of this builder for chaining calls
|
||
|
*/
|
||
|
Builder *setChannelCount(int32_t channelCount) {
|
||
|
mChannelCount = channelCount;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Default is 48000.
|
||
|
*
|
||
|
* @param inputRate sample rate of the input stream
|
||
|
* @return address of this builder for chaining calls
|
||
|
*/
|
||
|
Builder *setInputRate(int32_t inputRate) {
|
||
|
mInputRate = inputRate;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Default is 48000.
|
||
|
*
|
||
|
* @param outputRate sample rate of the output stream
|
||
|
* @return address of this builder for chaining calls
|
||
|
*/
|
||
|
Builder *setOutputRate(int32_t outputRate) {
|
||
|
mOutputRate = outputRate;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set cutoff frequency relative to the Nyquist rate of the output sample rate.
|
||
|
* Set to 1.0 to match the Nyquist frequency.
|
||
|
* Set lower to reduce aliasing.
|
||
|
* Default is 0.70.
|
||
|
*
|
||
|
* @param normalizedCutoff anti-aliasing filter cutoff
|
||
|
* @return address of this builder for chaining calls
|
||
|
*/
|
||
|
Builder *setNormalizedCutoff(float normalizedCutoff) {
|
||
|
mNormalizedCutoff = normalizedCutoff;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
int32_t getNumTaps() const {
|
||
|
return mNumTaps;
|
||
|
}
|
||
|
|
||
|
int32_t getChannelCount() const {
|
||
|
return mChannelCount;
|
||
|
}
|
||
|
|
||
|
int32_t getInputRate() const {
|
||
|
return mInputRate;
|
||
|
}
|
||
|
|
||
|
int32_t getOutputRate() const {
|
||
|
return mOutputRate;
|
||
|
}
|
||
|
|
||
|
float getNormalizedCutoff() const {
|
||
|
return mNormalizedCutoff;
|
||
|
}
|
||
|
|
||
|
protected:
|
||
|
int32_t mChannelCount = 1;
|
||
|
int32_t mNumTaps = 16;
|
||
|
int32_t mInputRate = 48000;
|
||
|
int32_t mOutputRate = 48000;
|
||
|
float mNormalizedCutoff = kDefaultNormalizedCutoff;
|
||
|
};
|
||
|
|
||
|
virtual ~MultiChannelResampler() = default;
|
||
|
|
||
|
/**
|
||
|
* Factory method for making a resampler that is optimal for the given inputs.
|
||
|
*
|
||
|
* @param channelCount number of channels, 2 for stereo
|
||
|
* @param inputRate sample rate of the input stream
|
||
|
* @param outputRate sample rate of the output stream
|
||
|
* @param quality higher quality sounds better but uses more CPU
|
||
|
* @return an optimal resampler
|
||
|
*/
|
||
|
static MultiChannelResampler *make(int32_t channelCount,
|
||
|
int32_t inputRate,
|
||
|
int32_t outputRate,
|
||
|
Quality quality);
|
||
|
|
||
|
bool isWriteNeeded() const {
|
||
|
return mIntegerPhase >= mDenominator;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Write a frame containing N samples.
|
||
|
*
|
||
|
* @param frame pointer to the first sample in a frame
|
||
|
*/
|
||
|
void writeNextFrame(const float *frame) {
|
||
|
writeFrame(frame);
|
||
|
advanceWrite();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Read a frame containing N samples.
|
||
|
*
|
||
|
* @param frame pointer to the first sample in a frame
|
||
|
*/
|
||
|
void readNextFrame(float *frame) {
|
||
|
readFrame(frame);
|
||
|
advanceRead();
|
||
|
}
|
||
|
|
||
|
int getNumTaps() const {
|
||
|
return mNumTaps;
|
||
|
}
|
||
|
|
||
|
int getChannelCount() const {
|
||
|
return mChannelCount;
|
||
|
}
|
||
|
|
||
|
static float hammingWindow(float radians, float spread);
|
||
|
|
||
|
static float sinc(float radians);
|
||
|
|
||
|
protected:
|
||
|
|
||
|
explicit MultiChannelResampler(const MultiChannelResampler::Builder &builder);
|
||
|
|
||
|
/**
|
||
|
* Write a frame containing N samples.
|
||
|
* Call advanceWrite() after calling this.
|
||
|
* @param frame pointer to the first sample in a frame
|
||
|
*/
|
||
|
virtual void writeFrame(const float *frame);
|
||
|
|
||
|
/**
|
||
|
* Read a frame containing N samples using interpolation.
|
||
|
* Call advanceRead() after calling this.
|
||
|
* @param frame pointer to the first sample in a frame
|
||
|
*/
|
||
|
virtual void readFrame(float *frame) = 0;
|
||
|
|
||
|
void advanceWrite() {
|
||
|
mIntegerPhase -= mDenominator;
|
||
|
}
|
||
|
|
||
|
void advanceRead() {
|
||
|
mIntegerPhase += mNumerator;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Generate the filter coefficients in optimal order.
|
||
|
* @param inputRate sample rate of the input stream
|
||
|
* @param outputRate sample rate of the output stream
|
||
|
* @param numRows number of rows in the array that contain a set of tap coefficients
|
||
|
* @param phaseIncrement how much to increment the phase between rows
|
||
|
* @param normalizedCutoff filter cutoff frequency normalized to Nyquist rate of output
|
||
|
*/
|
||
|
void generateCoefficients(int32_t inputRate,
|
||
|
int32_t outputRate,
|
||
|
int32_t numRows,
|
||
|
double phaseIncrement,
|
||
|
float normalizedCutoff);
|
||
|
|
||
|
|
||
|
int32_t getIntegerPhase() {
|
||
|
return mIntegerPhase;
|
||
|
}
|
||
|
|
||
|
static constexpr int kMaxCoefficients = 8 * 1024;
|
||
|
std::vector<float> mCoefficients;
|
||
|
|
||
|
const int mNumTaps;
|
||
|
int mCursor = 0;
|
||
|
std::vector<float> mX; // delayed input values for the FIR
|
||
|
std::vector<float> mSingleFrame; // one frame for temporary use
|
||
|
int32_t mIntegerPhase = 0;
|
||
|
int32_t mNumerator = 0;
|
||
|
int32_t mDenominator = 0;
|
||
|
|
||
|
|
||
|
private:
|
||
|
|
||
|
#if MCR_USE_KAISER
|
||
|
KaiserWindow mKaiserWindow;
|
||
|
#else
|
||
|
HyperbolicCosineWindow mCoshWindow;
|
||
|
#endif
|
||
|
|
||
|
static constexpr float kDefaultNormalizedCutoff = 0.70f;
|
||
|
|
||
|
const int mChannelCount;
|
||
|
};
|
||
|
|
||
|
}
|
||
|
#endif //OBOE_MULTICHANNEL_RESAMPLER_H
|