xref: /aosp_15_r20/external/oboe/apps/OboeTester/app/src/main/cpp/synth/Synthesizer.h (revision 05767d913155b055644481607e6fa1e35e2fe72c)
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef SYNTHMARK_SYNTHESIZER_H
18 #define SYNTHMARK_SYNTHESIZER_H
19 
20 #include <cstdint>
21 #include <math.h>
22 #include <memory>
23 #include <string.h>
24 #include <cassert>
25 #include "SynthTools.h"
26 #include "VoiceBase.h"
27 #include "SimpleVoice.h"
28 
29 namespace marksynth {
30 #define SAMPLES_PER_FRAME   2
31 
32 /**
33  * Manage an array of voices.
34  * Note that this is not a fully featured general purpose synthesizer.
35  * It is designed simply to have a similar CPU load as a common synthesizer.
36  */
37 class Synthesizer
38 {
39 public:
Synthesizer()40     Synthesizer()
41     : mMaxVoices(0)
42     , mActiveVoiceCount(0)
43     , mVoices(NULL)
44     {}
45 
~Synthesizer()46     virtual ~Synthesizer() {
47         delete[] mVoices;
48     };
49 
setup(int32_t sampleRate,int32_t maxVoices)50     int32_t setup(int32_t sampleRate, int32_t maxVoices) {
51         mMaxVoices = maxVoices;
52         UnitGenerator::setSampleRate(sampleRate);
53         mVoices = new SimpleVoice[mMaxVoices];
54         return (mVoices == NULL) ? -1 : 0;
55     }
56 
allNotesOn()57     void allNotesOn() {
58         notesOn(mMaxVoices);
59     }
60 
notesOn(int32_t numVoices)61     int32_t notesOn(int32_t numVoices) {
62         if (numVoices > mMaxVoices) {
63             return -1;
64         }
65         mActiveVoiceCount = numVoices;
66         // Leave some headroom so the resonant filter does not clip.
67         mVoiceAmplitude = 0.5f / sqrt(mActiveVoiceCount);
68 
69         int pitchIndex = 0;
70         synth_float_t pitches[] = {60.0, 64.0, 67.0, 69.0};
71         for(int iv = 0; iv < mActiveVoiceCount; iv++ ) {
72             SimpleVoice *voice = &mVoices[iv];
73             // Randomize pitches by a few cents to smooth out the CPU load.
74             float pitchOffset = 0.03f * (float) SynthTools::nextRandomDouble();
75             synth_float_t pitch = pitches[pitchIndex++] + pitchOffset;
76             if (pitchIndex > 3) pitchIndex = 0;
77             voice->noteOn(pitch, 1.0);
78         }
79         return 0;
80     }
81 
allNotesOff()82     void allNotesOff() {
83         for(int iv = 0; iv < mActiveVoiceCount; iv++ ) {
84             SimpleVoice *voice = &mVoices[iv];
85             voice->noteOff();
86         }
87     }
88 
renderStereo(float * output,int32_t numFrames)89     void renderStereo(float *output, int32_t numFrames) {
90         int32_t framesLeft = numFrames;
91         float *renderBuffer = output;
92 
93         // Clear mixing buffer.
94         memset(output, 0, numFrames * SAMPLES_PER_FRAME * sizeof(float));
95 
96         while (framesLeft > 0) {
97             int framesThisTime = std::min(kSynthmarkFramesPerRender, framesLeft);
98             for(int iv = 0; iv < mActiveVoiceCount; iv++ ) {
99                 SimpleVoice *voice = &mVoices[iv];
100                 voice->generate(framesThisTime);
101                 float *mix = renderBuffer;
102 
103                 synth_float_t leftGain = mVoiceAmplitude;
104                 synth_float_t rightGain = mVoiceAmplitude;
105                 if (mActiveVoiceCount > 1) {
106                     synth_float_t pan = iv / (mActiveVoiceCount - 1.0f);
107                     leftGain *= pan;
108                     rightGain *= 1.0 - pan;
109                 }
110                 for(int n = 0; n < kSynthmarkFramesPerRender; n++ ) {
111                     synth_float_t sample = voice->output[n];
112                     *mix++ += (float) (sample * leftGain);
113                     *mix++ += (float) (sample * rightGain);
114                 }
115             }
116             framesLeft -= framesThisTime;
117             mFrameCounter += framesThisTime;
118             renderBuffer += framesThisTime * SAMPLES_PER_FRAME;
119         }
120         assert(framesLeft == 0);
121     }
122 
getActiveVoiceCount()123     int32_t getActiveVoiceCount() {
124         return mActiveVoiceCount;
125     }
126 
127 private:
128     int32_t mMaxVoices;
129     int32_t mActiveVoiceCount;
130     int64_t mFrameCounter;
131     SimpleVoice *mVoices;
132     synth_float_t mVoiceAmplitude = 1.0;
133 };
134 };
135 #endif // SYNTHMARK_SYNTHESIZER_H
136