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