1 /*
2     Copyright (C) 2003-2008 Fons Adriaensen <fons@kokkinizita.net>
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18 
19 #ifdef Q_OS_MAC
20 #include <locale.h>
21 #endif
22 
23 #include "aeolus.h"
24 #include "model.h"
25 
26 namespace Ms {
27       extern QString dataPath;
28       extern QString mscoreGlobalShare;
29       };
30 
31 #include "synthesizer/event.h"
32 #include "libmscore/xml.h"
33 #include "sparm_p.h"
34 
35 const std::vector<ParDescr> Aeolus::pd = {
36       { A_VOLUME,  "volume",  true, 0.32f,   0.00f,  1.00f },
37       { A_REVSIZE, "revsize", true, 0.075f,  0.025f, 0.15f },
38       { A_REVTIME, "revtime", true, 4.0f,    2.0f,   7.00f },
39       { A_STPOSIT, "stposit", true, 0.5f,   -1.00f,  1.0f  },
40       };
41 
42 //---------------------------------------------------------
43 //   createAeolus
44 //---------------------------------------------------------
45 
createAeolus()46 Synthesizer* createAeolus()
47       {
48       return new Aeolus();
49       }
50 
51 //---------------------------------------------------------
52 //   Aeolus
53 //---------------------------------------------------------
54 
Aeolus()55 Aeolus::Aeolus() : Synthesizer()
56       {
57       model = nullptr;
58       patchList.append(new MidiPatch { false, "Aeolus", 0, 0, 0, "Aeolus" });
59 
60       _sc_cmode = 0;
61       _sc_group = 0;
62       _running = false;
63       _hold = 0;
64       _nplay = 0;
65       _nasect = 0;
66       _ndivis = 0;
67       _revsize = 0.0f;
68       _revtime = 0.0f;
69       nout = 0;
70       _fsamp = 0.0f;
71       _fsize = 0;
72       _ifc_init = nullptr;
73       _midimap = { 0 };
74       _asectp = { nullptr };
75       _divisp = { nullptr };
76       _keymap = { 0 };
77       _audiopar = { 0.0f };
78       routb = { 0.0f };
79       loutb = { 0.0f };
80       _asectpar = { nullptr };
81       _ifelms = { 0 };
82       _tempstr = { 0 };
83       }
84 
~Aeolus()85 Aeolus::~Aeolus()
86       {
87       delete model;
88       for (int i = 0; i < _nasect; i++)
89             delete _asectp [i];
90       for (int i = 0; i < _ndivis; i++)
91             delete _divisp [i];
92       }
93 
94 //---------------------------------------------------------
95 //   init
96 //---------------------------------------------------------
97 
init(float samplerate)98 void Aeolus::init(float samplerate)
99       {
100       setlocale(LC_ALL, "C"); // scanf of floats does not work otherwise
101 
102       QString stops = mscoreGlobalShare + "/sound/aeolus/stops";
103       int n = strlen(qPrintable(stops));
104       char* stopsPath = new char[n+1];
105       strcpy(stopsPath, qPrintable(stops));
106 
107       QDir dir;
108       QString waves = dataPath + QString("/aeolus/waves%1").arg(int(samplerate));
109       dir.mkpath(waves);
110       n = strlen(qPrintable(waves));
111       char* wavesPath = new char[n+1];
112       strcpy(wavesPath, qPrintable(waves));
113 
114       audio_init(int(samplerate));
115       model = new Model (this, _midimap, stopsPath, "Aeolus", wavesPath);
116 
117       audio_start();
118       model->init();
119       }
120 
121 //---------------------------------------------------------
122 //   setMasterTuning
123 //---------------------------------------------------------
124 
setMasterTuning(double)125 void Aeolus::setMasterTuning(double)
126       {
127       }
128 
129 //---------------------------------------------------------
130 //   masterTuning
131 //---------------------------------------------------------
132 
masterTuning() const133 double Aeolus::masterTuning() const
134       {
135       return 440.0;
136       }
137 
138 //---------------------------------------------------------
139 //   play
140 //---------------------------------------------------------
141 
play(const PlayEvent & event)142 void Aeolus::play(const PlayEvent& event)
143       {
144       int ch   = event.channel();
145       int type = event.type();
146       int m    = _midimap [ch] & 0x7f;        // Keyboard and hold bits
147 
148       if (type == ME_NOTEON) {
149             int n = event.dataA();
150             int v = event.dataB();
151             if ((n >= 36) && (n <= 96)) {
152                   n -= 36;
153                   if (v == 0)
154                         key_off(n, m);
155                   else
156                         key_on(n, m);
157                   }
158             }
159       else if (type == ME_NOTEOFF) {
160             int n = event.dataA();
161             if ((n >= 36) && (n <= 96)) {
162                   n -= 36;
163                   key_off(n, m);
164                   }
165             }
166       else if (type == ME_CONTROLLER) {
167             int p = event.dataA();
168             int v = event.dataB();
169             switch(p) {
170                   case MIDICTL_IFELM:
171                         if (v & 0x40) {
172                               // Set mode or clear group.
173                               _sc_cmode = (v >> 4) & 3;
174                               _sc_group = v & 7;
175                               if (_sc_cmode == 0)
176                                     model->clr_group(_sc_group);
177                               }
178                         else if (_sc_cmode) {
179                               // Set, reset or toggle stop.
180                               model->set_ifelm (_sc_group, v & 0x1f, _sc_cmode - 1);
181                               }
182                         break;
183                   case CTRL_ALL_NOTES_OFF:
184                         allNotesOff(ch);
185                         break;
186                   }
187             }
188       }
189 
190 //---------------------------------------------------------
191 //   allNotesOff
192 //---------------------------------------------------------
193 
allNotesOff(int)194 void Aeolus::allNotesOff(int)
195       {
196       for (int i = 0; i < NNOTES; ++i)
197             _keymap[i] = 0x80;
198       }
199 
200 //---------------------------------------------------------
201 //   getPatchInfo
202 //---------------------------------------------------------
203 
getPatchInfo() const204 const QList<MidiPatch*>& Aeolus::getPatchInfo() const
205       {
206       return patchList;
207       }
208 
209 //---------------------------------------------------------
210 //   setValue
211 //---------------------------------------------------------
212 
setValue(int id,double value)213 void Aeolus::setValue(int id, double value)
214       {
215       const ParDescr* p = parameter(id);
216       if (p == 0)
217             return;
218       double v;
219       if (p->log)
220             v = exp(p->min + value * (p->max - p->min));
221       else
222             v = p->min + value * (p->max - p->min);
223 
224       switch (id) {
225             case A_VOLUME:   _audiopar[VOLUME]  = v; break;
226             case A_REVSIZE:  _audiopar[REVSIZE] = v; break;
227             case A_REVTIME:  _audiopar[REVTIME] = v; break;
228             case A_STPOSIT:  _audiopar[STPOSIT] = v; break;
229             }
230       }
231 
232 //---------------------------------------------------------
233 //   value
234 //---------------------------------------------------------
235 
value(int idx) const236 double Aeolus::value(int idx) const
237       {
238       double v = 0.0;
239       switch (idx) {
240             case A_VOLUME:   v = _audiopar[VOLUME];  break;
241             case A_REVSIZE:  v = _audiopar[REVSIZE]; break;
242             case A_REVTIME:  v = _audiopar[REVTIME]; break;
243             case A_STPOSIT:  v = _audiopar[STPOSIT]; break;
244             }
245       const ParDescr* p = parameter(idx);
246       if (p->log)
247             v = (log(v) - p->min)/(p->max - p->min);
248       else
249             v = (v - p->min)/(p->max - p->min);
250       return v;
251       }
252 
253 //---------------------------------------------------------
254 //   state
255 //---------------------------------------------------------
256 
state() const257 SynthesizerGroup Aeolus::state() const
258       {
259       SynthesizerGroup g;
260       g.setName(name());
261 
262       for (const ParDescr& d : pd)
263             g.push_back(IdValue(d.id, QString("%1").arg(value(d.id))));
264       return g;
265       }
266 
267 //---------------------------------------------------------
268 //   setState
269 //---------------------------------------------------------
270 
setState(const SynthesizerGroup & g)271 bool Aeolus::setState(const SynthesizerGroup& g)
272       {
273       for (const IdValue& v : g)
274             setValue(v.id, v.data.toDouble());
275       return true;
276       }
277 
278 //---------------------------------------------------------
279 //   parameter
280 //---------------------------------------------------------
281 
parameter(int idx) const282 const ParDescr* Aeolus::parameter(int idx) const
283       {
284       return &pd[idx];
285       }
286 
287