/* * Copyright 2018 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 SHARED_SYNTH_SOUND_H #define SHARED_SYNTH_SOUND_H #include #include #include #include #include "IRenderableAudio.h" constexpr float kDefaultFrequency = 440.0; constexpr int32_t kDefaultSampleRate = 48000; constexpr float kPi = M_PI; constexpr float kTwoPi = kPi * 2; constexpr int32_t kNumSineWaves = 5; constexpr float kSustainMultiplier = 0.99999; constexpr float kReleaseMultiplier = 0.999; // Stop playing music below this cutoff constexpr float kMasterAmplitudeCutOff = 0.01; class SynthSound : public IRenderableAudio { public: SynthSound() { } ~SynthSound() { }; void noteOn() { mTrigger = true; // start a note envelope mAmplitudeScaler = kSustainMultiplier; } void noteOff() { mAmplitudeScaler = kReleaseMultiplier; } void setSampleRate(int32_t sampleRate) { mSampleRate = sampleRate; updatePhaseIncrement(); }; void setFrequency(float frequency) { mFrequency = frequency; updatePhaseIncrement(); }; // Amplitudes from https://epubs.siam.org/doi/pdf/10.1137/S00361445003822 inline void setAmplitude(float amplitude) { mAmplitudes[0] = amplitude * .2f; mAmplitudes[1] = amplitude; mAmplitudes[2] = amplitude * .1f; mAmplitudes[3] = amplitude * .02f; mAmplitudes[4] = amplitude * .15f; }; // From IRenderableAudio void renderAudio(float *audioData, int32_t numFrames) override { for (int i = 0; i < numFrames; ++i) { if (mTrigger.exchange(false)) { mMasterAmplitude = 1.0; mPhase = 0.0f; } else { mMasterAmplitude *= mAmplitudeScaler; } audioData[i] = 0; if (mMasterAmplitude < kMasterAmplitudeCutOff) { continue; } for (int j = 0; j < kNumSineWaves; ++j) { audioData[i] += sinf(mPhase * (j + 1)) * mAmplitudes[j] * mMasterAmplitude; } mPhase += mPhaseIncrement; if (mPhase > kTwoPi) { mPhase -= kTwoPi; } } }; private: std::atomic mTrigger { false }; float mMasterAmplitude = 0.0f; std::atomic mAmplitudeScaler { 0.0f }; std::array, kNumSineWaves> mAmplitudes; float mPhase = 0.0f; std::atomic mPhaseIncrement { 0 }; std::atomic mFrequency { kDefaultFrequency }; std::atomic mSampleRate { kDefaultSampleRate }; void updatePhaseIncrement(){ // Note how there is a division here. If this file is changed so that updatePhaseIncrement // is called more frequently, please cache 1/mSampleRate. This allows this operation to not // need divisions. mPhaseIncrement = kTwoPi * mFrequency / static_cast(mSampleRate); }; }; #endif //SHARED_SYNTH_SOUND_H