137 lines
4.3 KiB
C++
137 lines
4.3 KiB
C++
/*
|
|
* Copyright (C) 2016 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 SINE_GENERATOR_H
|
|
#define SINE_GENERATOR_H
|
|
|
|
#include <math.h>
|
|
|
|
class SineGenerator
|
|
{
|
|
public:
|
|
SineGenerator() {}
|
|
virtual ~SineGenerator() = default;
|
|
|
|
void setup(double frequency, double frameRate) {
|
|
mFrameRate = frameRate;
|
|
mPhaseIncrement = frequency * M_PI * 2 / frameRate;
|
|
}
|
|
|
|
void setSweep(double frequencyLow, double frequencyHigh, double seconds) {
|
|
mSweeping = seconds > 0.0;
|
|
if (mSweeping) {
|
|
mPhaseIncrementLow = frequencyLow * M_PI * 2 / mFrameRate;
|
|
mPhaseIncrementHigh = frequencyHigh * M_PI * 2 / mFrameRate;
|
|
double numFrames = seconds * mFrameRate;
|
|
mUpScaler = pow((frequencyHigh / frequencyLow), (1.0 / numFrames));
|
|
mDownScaler = 1.0 / mUpScaler;
|
|
}
|
|
}
|
|
|
|
float next() {
|
|
float value = sinf(mPhase) * mAmplitude;
|
|
advancePhase();
|
|
return value;
|
|
}
|
|
|
|
void render(int16_t *buffer, int32_t channelStride, int32_t numFrames) {
|
|
int sampleIndex = 0;
|
|
for (int i = 0; i < numFrames; i++) {
|
|
buffer[sampleIndex] = (int16_t) (INT16_MAX * next());
|
|
sampleIndex += channelStride;
|
|
}
|
|
}
|
|
|
|
void render(float *buffer, int32_t channelStride, int32_t numFrames) {
|
|
int sampleIndex = 0;
|
|
for (int i = 0; i < numFrames; i++) {
|
|
buffer[sampleIndex] = next();
|
|
sampleIndex += channelStride;
|
|
}
|
|
}
|
|
|
|
void render(int32_t *buffer, int32_t channelStride, int32_t numFrames) {
|
|
int sampleIndex = 0;
|
|
for (int i = 0; i < numFrames; i++) {
|
|
buffer[sampleIndex] = (int32_t) (INT32_MAX * next());
|
|
sampleIndex += channelStride;
|
|
}
|
|
}
|
|
|
|
void render24(uint8_t *buffer, int32_t channelStride, int32_t numFrames) {
|
|
int sampleIndex = 0;
|
|
constexpr int32_t INT24_MAX = (1 << 23) - 1;
|
|
constexpr int bytesPerSample = getBytesPerSample(AAUDIO_FORMAT_PCM_I24_PACKED);
|
|
const bool isLittleEndian = isNativeLittleEndian();
|
|
for (int i = 0; i < numFrames; i++) {
|
|
int32_t sample = (int32_t) (INT24_MAX * next());
|
|
uint32_t usample = (uint32_t) sample;
|
|
if (isLittleEndian) {
|
|
buffer[sampleIndex] = usample; // little end first
|
|
buffer[sampleIndex + 1] = usample >> 8;
|
|
buffer[sampleIndex + 2] = usample >> 16;
|
|
} else {
|
|
buffer[sampleIndex] = usample >> 16; // big end first
|
|
buffer[sampleIndex + 1] = usample >> 8;
|
|
buffer[sampleIndex + 2] = usample;
|
|
}
|
|
sampleIndex += channelStride * bytesPerSample;
|
|
}
|
|
}
|
|
|
|
void setAmplitude(double amplitude) {
|
|
mAmplitude = amplitude;
|
|
}
|
|
|
|
double getAmplitude() const {
|
|
return mAmplitude;
|
|
}
|
|
|
|
private:
|
|
void advancePhase() {
|
|
mPhase += mPhaseIncrement;
|
|
if (mPhase > M_PI * 2) {
|
|
mPhase -= M_PI * 2;
|
|
}
|
|
if (mSweeping) {
|
|
if (mGoingUp) {
|
|
mPhaseIncrement *= mUpScaler;
|
|
if (mPhaseIncrement > mPhaseIncrementHigh) {
|
|
mGoingUp = false;
|
|
}
|
|
} else {
|
|
mPhaseIncrement *= mDownScaler;
|
|
if (mPhaseIncrement < mPhaseIncrementLow) {
|
|
mGoingUp = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
double mAmplitude = 0.05; // unitless scaler
|
|
double mPhase = 0.0;
|
|
double mPhaseIncrement = 440 * M_PI * 2 / 48000;
|
|
double mFrameRate = 48000;
|
|
double mPhaseIncrementLow;
|
|
double mPhaseIncrementHigh;
|
|
double mUpScaler = 1.0;
|
|
double mDownScaler = 1.0;
|
|
bool mGoingUp = false;
|
|
bool mSweeping = false;
|
|
};
|
|
|
|
#endif /* SINE_GENERATOR_H */
|