File indexing completed on 2024-04-28 15:08:06
0001 /* miniSynth - A Simple Software Synthesizer 0002 SPDX-FileCopyrightText: 2015 Ville Räisänen <vsr at vsr.name> 0003 0004 SPDX-License-Identifier: GPL-3.0-or-later 0005 */ 0006 0007 0008 #include "preset.h" 0009 #include "linearSynthesis.h" 0010 #include "generator.h" 0011 #include <qendian.h> 0012 #include <QMutableListIterator> 0013 0014 Generator::Generator(const QAudioFormat &_format, QObject *parent) : QIODevice(parent), format(_format) { 0015 linSyn = new LinearSynthesis(Waveform::MODE_SIN); 0016 curtime = 0; 0017 0018 defaultEnv.attackTime = 100; 0019 defaultEnv.decayTime = 400; 0020 defaultEnv.releaseTime = 100; 0021 0022 defaultEnv.initialAmpl = 0; 0023 defaultEnv.peakAmpl = 1; 0024 defaultEnv.sustainAmpl = 0.8; 0025 0026 mod_waveform = new Waveform(Waveform::MODE_SIN); 0027 0028 synthData = new qreal[maxUsedBytes]; 0029 0030 for (unsigned int indMaxRead = 0; indMaxRead < maxUsedBytes; indMaxRead++) { 0031 synthData[indMaxRead] = 0; 0032 } 0033 } 0034 0035 Generator::~Generator() { 0036 delete linSyn; 0037 delete [] synthData; 0038 delete mod_waveform; 0039 } 0040 0041 void 0042 Generator::start() { 0043 open(QIODevice::ReadOnly); 0044 } 0045 0046 void 0047 Generator::stop() { 0048 close(); 0049 } 0050 0051 void 0052 Generator::addWave(unsigned char note, unsigned char vel) { 0053 Wave wav; 0054 wav.state = wav.STATE_ATTACK; 0055 wav.note = note; 0056 wav.vel = vel; 0057 wav.state_age = 0; 0058 wav.age = 0; 0059 wav.env = defaultEnv; 0060 0061 m_lock.lock(); 0062 waveList.push_back(wav); 0063 m_lock.unlock(); 0064 } 0065 0066 qint64 0067 Generator::readData(char *data, qint64 len) { 0068 // QAudioOutput tends to ask large packets of data, which can lead to a 0069 // large delay between noteOn requests and the generation of audio. Thus, 0070 // in order to provide more responsive interface, the packet size is 0071 // limited to 2048 bytes ~ 1024 samples. 0072 if (len > maxUsedBytes) len = maxUsedBytes; 0073 0074 generateData(len); 0075 memcpy(data, m_buffer.constData(), len); 0076 curtime += (qreal)len/(m_samplingRate*2); 0077 return len; 0078 } 0079 0080 // Not used. 0081 qint64 0082 Generator::writeData(const char *data, qint64 len) { 0083 Q_UNUSED(data); 0084 Q_UNUSED(len); 0085 return 0; 0086 } 0087 0088 // Doesn't seem to be called by QAudioOutput. 0089 qint64 0090 Generator::bytesAvailable() const { 0091 return m_buffer.size() + QIODevice::bytesAvailable(); 0092 } 0093 0094 void 0095 Generator::noteOn(unsigned char chan, unsigned char note, unsigned char vel) { 0096 // Velocity of 255 is assumed since a "pleasant" relationship between the 0097 // velocity in the MIDI event and the parameters of the corresponding Wave 0098 // cannot be currently selected by the user. 0099 0100 if (vel > 0) vel = 255; 0101 addWave(note, vel); 0102 Q_UNUSED(chan); 0103 } 0104 0105 void 0106 Generator::noteOff(unsigned char chan, unsigned char note) { 0107 QMutableListIterator<Wave> i(waveList); 0108 0109 while (i.hasNext()) { 0110 Wave wav = i.next(); 0111 if (wav.note == note && wav.state != Wave::STATE_RELEASE) { 0112 // To avoid discontinuity in the envelope, the initial value for 0113 // the release part of the envelope should be equal to current 0114 // value. 0115 0116 wav.env.sustainAmpl = wav.env.eval(wav.state_age, wav.state); 0117 0118 wav.state = Wave::STATE_RELEASE; 0119 wav.state_age = 0; 0120 } 0121 i.setValue(wav); 0122 } 0123 Q_UNUSED(chan); 0124 } 0125 0126 void 0127 Generator::setMode(unsigned int _mode) { 0128 delete linSyn; 0129 linSyn = new LinearSynthesis(_mode); 0130 curtime = 0; 0131 } 0132 0133 void 0134 Generator::setTimbre(QVector<int> &litudes, QVector<int> &phases) { 0135 linSyn->setTimbre(amplitudes, phases); 0136 } 0137 0138 void 0139 Generator::generateData(qint64 len) { 0140 unsigned int numSamples = len/2; 0141 m_buffer.resize(len); 0142 0143 // Raw synthesized data is assembled into synthData. 0144 memset(synthData, 0, numSamples*sizeof(qreal)); 0145 0146 // All samples for each active note in waveList are synthesized separately. 0147 m_lock.lock(); 0148 QMutableListIterator<Wave> i(waveList); 0149 0150 while (i.hasNext()) { 0151 Wave wav = i.next(); 0152 qreal attackTime = 0.001*(qreal)wav.env.attackTime; 0153 qreal releaseTime = 0.001*(qreal)wav.env.releaseTime; 0154 0155 qreal freq = 8.175 * 0.5 * qPow(2, ((qreal)wav.note)/12); 0156 qreal ampl = 0.5*((qreal)wav.vel)/256; 0157 0158 qreal stateAge = wav.state_age; 0159 qreal wavAge = wav.age; 0160 0161 const qreal step = 1.f / m_samplingRate; 0162 qreal samplePerStep = 0.f; 0163 for (unsigned int sample = 0; sample < numSamples; sample++, samplePerStep += step) { 0164 qreal t = curtime + samplePerStep; 0165 qreal envt = stateAge + samplePerStep; 0166 qreal modt = wavAge + samplePerStep; 0167 0168 // Handle timed change of state in the ADSR-envelopes ATTACK->DECAY 0169 // and RELEASE->OFF. 0170 switch(wav.state) { 0171 case ADSREnvelope::STATE_ATTACK: 0172 if (envt > attackTime) { 0173 stateAge -= attackTime; 0174 wav.state = ADSREnvelope::STATE_DECAY; 0175 wav.state_age -= attackTime; 0176 envt = stateAge + samplePerStep; 0177 } 0178 break; 0179 case ADSREnvelope::STATE_RELEASE: 0180 if (envt > releaseTime) { 0181 stateAge = 0; 0182 wav.state = ADSREnvelope::STATE_OFF; 0183 } 0184 break; 0185 } 0186 0187 if (wav.state == ADSREnvelope::STATE_OFF) { 0188 break; 0189 } else { 0190 qreal freqmod = 0; 0191 qreal amod = 0; 0192 0193 // Compute modulation waves. 0194 0195 if (mod.FM_freq > 0) { 0196 qreal envVal = mod.useEnvelope ? wav.env.eval(envt, wav.state) : 1; 0197 if (mod.propFreq) { 0198 freqmod = mod.FM_ampl 0199 * envVal* mod_waveform->eval(2*M_PI*mod.FM_freq*freq*modt); 0200 } else { 0201 freqmod = mod.FM_ampl 0202 * mod_waveform->eval(2*M_PI*mod.FM_freq*modt); 0203 } 0204 } 0205 if (mod.AM_freq > 0) { 0206 amod = (1 - qExp(-modt/mod.AM_time))*mod.AM_ampl * mod_waveform->eval(2*M_PI*mod.AM_freq*t); 0207 } 0208 0209 // Evaluate the output wave for the current note and add to the 0210 // output obtained with other notes. 0211 0212 qreal envVal = wav.env.eval(envt, wav.state); 0213 qreal newVal = envVal * (ampl + amod) 0214 * 0.5 * linSyn->evalTimbre(2*M_PI*(freq+freqmod)*(modt+100)); 0215 0216 synthData[sample] += newVal; 0217 } 0218 } 0219 wav.age += (qreal)numSamples/m_samplingRate; 0220 if (wav.state != ADSREnvelope::STATE_OFF) { 0221 wav.state_age += (qreal)numSamples/m_samplingRate; 0222 i.setValue(wav); 0223 } 0224 else { 0225 i.remove(); 0226 } 0227 } 0228 m_lock.unlock(); 0229 0230 // Convert data from qreal to qint16. 0231 const int channelBytes = format.sampleSize() / 8; 0232 unsigned char *ptr = reinterpret_cast<unsigned char *>(m_buffer.data()); 0233 for (unsigned int sample = 0; sample < numSamples; sample++) { 0234 if (synthData[sample] > 1) synthData[sample] = 1; 0235 if (synthData[sample] < -1) synthData[sample] = -1; 0236 qint16 value = static_cast<qint16>(synthData[sample] * 32767); 0237 qToLittleEndian<qint16>(value, ptr); 0238 ptr += channelBytes; 0239 } 0240 } 0241 0242 void 0243 Generator::setEnvelope(const ADSREnvelope &env) { 0244 defaultEnv = env; 0245 } 0246 0247 void 0248 Generator::setModulation(Modulation &modulation) { 0249 if (modulation.mode != mod_waveform->mode) { 0250 delete mod_waveform; 0251 mod_waveform = new Waveform(modulation.mode); 0252 } 0253 mod = modulation; 0254 } 0255 0256 void Generator::setPreset(Preset &preset) { 0257 setModulation(preset.mod); 0258 setMode(preset.waveformMode); 0259 setTimbre(preset.timbreAmplitudes, preset.timbrePhases); 0260 setEnvelope(preset.env); 0261 } 0262 0263 #include "moc_generator.cpp"