/[svn]/linuxsampler/trunk/src/hostplugins/vst/PluginVst.cpp
ViewVC logotype

Contents of /linuxsampler/trunk/src/hostplugins/vst/PluginVst.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2372 - (show annotations) (download)
Sat Sep 22 18:28:38 2012 UTC (11 years, 6 months ago) by schoenebeck
File size: 18213 byte(s)
* VST: implemented retrieval and switching of programs using the
  sampler's internal MIDI instrument mapping system
* VST: reply to "canDo" plugin features we don't know as "don't know"
* bumped version to 1.0.0.svn19

1 /***************************************************************************
2 * *
3 * Copyright (C) 2008 - 2012 Andreas Persson *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the Free Software *
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, *
18 * MA 02110-1301 USA *
19 ***************************************************************************/
20
21 #include <cstdio>
22 #include <cstdlib>
23 #include <cstring>
24
25 #ifdef WIN32
26 #include <tchar.h>
27 #else
28 #include <cerrno>
29 #include <signal.h>
30 #include <sys/wait.h>
31 #include <unistd.h>
32 #include <dirent.h>
33 #include <sys/stat.h>
34 #endif
35
36 #include "PluginVst.h"
37 #include "../../drivers/midi/MidiInstrumentMapper.h"
38
39 #ifndef CHANNELS
40 #define CHANNELS 32
41 #endif
42
43 // the sampler can actually hold up a very huge amount of programs
44 // (2^32 [maps] * 2^16 [banks MSB + LSB] * 2^8 [programs per bank] = 2^56 [total programs] )
45 // however I am not sure if we might crash some VST host with a number
46 // here that is too huge, however this should be enough in most cases ...
47 #define MAX_VST_PROGRAMS (128*128)
48
49 using namespace LinuxSampler;
50
51 namespace {
52
53 #if defined(WIN32) && CONFIG_DEBUG_LEVEL >= 2
54 // on windows, send debug logging to a file instead of a non-existent
55 // console
56 #include <cstdarg>
57 #include "../../common/Mutex.h"
58 LinuxSampler::Mutex logmutex;
59
60 void log(const char* fmt, ...) {
61 logmutex.Lock();
62 FILE* f = fopen((String(getenv("TEMP")) + "\\linuxsamplervst.log").c_str(), "a");
63 va_list ap;
64 va_start(ap, fmt);
65 vfprintf(f, fmt, ap);
66 va_end(ap);
67 fclose(f);
68 logmutex.Unlock();
69 }
70 #undef dmsg
71 #define dmsg(debuglevel,x) log x;
72 #endif
73
74
75 // *************** LinuxSamplerEditor ***************
76 // *
77
78 LinuxSamplerEditor::LinuxSamplerEditor(AudioEffect* effect) :
79 AEffEditor(effect) {
80 dmsg(2, ("-->LinuxSamplerEditor constructor\n"));
81 #ifdef WIN32
82 ProcessHandle = INVALID_HANDLE_VALUE;
83 #else
84 pid = 0;
85 #endif
86 }
87
88 LinuxSamplerEditor::~LinuxSamplerEditor() {
89 close();
90 }
91
92 String LinuxSamplerEditor::FindFantasia() {
93 String fantasia;
94 #ifdef WIN32
95 // assume Fantasia is in the same directory or one directory above
96 // the liblinuxsampler dll
97 String lspath = LinuxSampler::Sampler::GetInstallDir();
98 if (!lspath.empty()) {
99 lspath += "\\";
100 WIN32_FIND_DATA fd;
101 HANDLE hFind =
102 FindFirstFile((lspath + "Fantasia*.jar").c_str(), &fd);
103 if (hFind == INVALID_HANDLE_VALUE) {
104 lspath += "..\\";
105 hFind = FindFirstFile((lspath + "Fantasia*.jar").c_str(), &fd);
106 }
107 if (hFind != INVALID_HANDLE_VALUE) {
108 fantasia = fd.cFileName;
109 FindClose(hFind);
110 return lspath + fantasia;
111 }
112 }
113 #elif defined(__APPLE__)
114 // look for the java application wrapper
115 const char* cmd =
116 "/Applications/LinuxSampler/Fantasia.app"
117 "/Contents/MacOS/JavaApplicationStub";
118 fantasia = String(getenv("HOME")) + cmd;
119 struct stat buf;
120 if (stat(fantasia.c_str(), &buf) != 0) {
121 fantasia = stat(cmd, &buf) == 0 ? cmd : "";
122 }
123 #else
124 // search in <datadir>/java (default: /usr/local/share/java)
125 String path(DATADIR);
126 path += *(path.end() - 1) == '/' ? "java" : "/java";
127 DIR* dir = opendir(path.c_str());
128 if (dir) {
129 while (dirent* d = readdir(dir)) {
130 const char* name = d->d_name;
131 if (strncmp("Fantasia", name, 8) == 0 &&
132 strcmp(name + strlen(name) - 4, ".jar") == 0) {
133 fantasia = path + "/" + name;
134 }
135 }
136 closedir(dir);
137 }
138 #endif
139 return fantasia;
140 }
141
142 bool LinuxSamplerEditor::open(void* ptr) {
143 dmsg(2, ("-->LinuxSamplerEditor::open\n"));
144 AEffEditor::open(ptr);
145
146 // try to start the JSampler Fantasia GUI as a separate process
147 #ifdef WIN32
148 // first check if it's already running
149 if (ProcessHandle != INVALID_HANDLE_VALUE) {
150 DWORD exitCode;
151 if (GetExitCodeProcess(ProcessHandle, &exitCode)) {
152 if (exitCode == STILL_ACTIVE) return true;
153 }
154 free(Command);
155 CloseHandle(ProcessHandle);
156 ProcessHandle = INVALID_HANDLE_VALUE;
157 }
158
159 String fantasia = FindFantasia();
160 if (!fantasia.empty()) {
161 // start a java process
162 String path; // look in PATH first
163 for (int i = 0 ; i < 2 ; i++) { // two tries
164 STARTUPINFO si;
165 PROCESS_INFORMATION pi;
166 ZeroMemory(&si, sizeof(si));
167 si.cb = sizeof(si);
168 ZeroMemory(&pi, sizeof(pi));
169
170 Command = _tcsdup(TEXT((String("\"") + path +
171 "javaw\" -jar \"" + fantasia +
172 "\"").c_str()));
173 if (CreateProcess(NULL, Command, NULL, NULL, FALSE, 0, NULL,
174 NULL, &si, &pi)) {
175 ProcessHandle = pi.hProcess;
176 CloseHandle(pi.hThread);
177 break;
178 } else {
179 free(Command);
180 if (path.empty()) {
181 // java wasn't found in PATH, try again
182 // with an alternative directory
183 char* windir = getenv("windir");
184 if (!windir) break;
185 #ifdef _WIN64
186 // LS plugin is 64 bit - look for 32 bit java
187 path = String(windir) + "\\SysWOW64\\";
188 #else
189 // LS plugin is 32 bit - look for 64 bit java
190 path = String(windir) + "\\Sysnative\\";
191 #endif
192 }
193 }
194 }
195 }
196 #else
197 // first check if it's already running
198 if (pid && waitpid(pid, 0, WNOHANG) == 0) return true;
199
200 String fantasia = FindFantasia();
201 if (!fantasia.empty())
202 {
203 // start a java process
204 pid = fork();
205 if (pid == -1) {
206 dmsg(1, ("fork failed %d %s\n", errno, strerror(errno)));
207 pid = 0;
208 } else if (pid == 0) {
209 #ifdef __APPLE__
210 execl(fantasia.c_str(), fantasia.c_str(), (char*)0);
211 #else
212 execlp("java", "java", "-jar", fantasia.c_str(), (char*)0);
213 #endif
214 dmsg(1, ("exec failed %d %s\n", errno, strerror(errno)));
215
216 // make sure something is executed, so the static
217 // destructors copied from the parent don't run
218 execl("/usr/bin/true", "/usr/bin/true", (char*)0);
219 }
220 }
221 #endif
222 dmsg(2, ("<--LinuxSamplerEditor::open\n"));
223 return true;
224 }
225
226 void LinuxSamplerEditor::close() {
227 systemWindow = 0;
228 #ifdef WIN32
229 if (ProcessHandle != INVALID_HANDLE_VALUE) {
230 TerminateProcess(ProcessHandle, 0);
231 free(Command);
232 CloseHandle(ProcessHandle);
233 ProcessHandle = INVALID_HANDLE_VALUE;
234 }
235 #else
236 if (pid) {
237 kill(pid, SIGTERM);
238 waitpid(pid, 0, 0);
239 pid = 0;
240 }
241 #endif
242 }
243
244 bool LinuxSamplerEditor::getRect(ERect** rect) {
245 ERect* r = new ERect();
246 r->top = 0;
247 r->left = 0;
248 r->bottom = 1; // 0 makes EnergyXT and Ardour on Linux crash
249 r->right = 400;
250 *rect = r;
251 return true;
252 }
253
254
255 // *************** LinuxSamplerVst ***************
256 // *
257
258 LinuxSamplerVst::LinuxSamplerVst(audioMasterCallback audioMaster) :
259 AudioEffectX(audioMaster, MAX_VST_PROGRAMS, 0),
260 StateBuf(0)
261 {
262 dmsg(2, ("-->constructor\n"));
263
264 setProgram(0);
265 setNumInputs(0);
266 setNumOutputs(CHANNELS);
267 canProcessReplacing();
268 isSynth();
269 programsAreChunks();
270 setUniqueID(CCONST('L', 'S', 'm', 'p')); // (registred at Steinberg)
271 setEditor(new LinuxSamplerEditor(this));
272
273 suspend();
274 dmsg(2, ("<--constructor\n"));
275 }
276
277
278 void LinuxSamplerVst::resume() {
279 dmsg(2, ("-->resume\n"));
280
281 // Ardour initially sets blockSize to zero - we postpone the
282 // initialization until we have a blockSize
283 if (blockSize != 0) {
284 if (!pAudioDevice) {
285 Init(int(sampleRate), blockSize, CHANNELS);
286
287 if (!SavedChunk.empty()) {
288 SetState(SavedChunk);
289 SavedChunk.clear();
290 } else {
291 InitState();
292 }
293 } else {
294 Init(int(sampleRate), blockSize, CHANNELS);
295 }
296 }
297 AudioEffectX::resume();
298 dmsg(2, ("<--resume\n"));
299 }
300
301 LinuxSamplerVst::~LinuxSamplerVst() {
302 dmsg(2, ("-->destructor\n"));
303 if (StateBuf) free(StateBuf);
304 dmsg(2, ("<--destructor\n"));
305 }
306
307
308 void LinuxSamplerVst::setProgram(VstInt32 program) {
309 if (program < 0 || program >= MAX_VST_PROGRAMS ||
310 !pMidiDevice || !pMidiDevice->Port()) return;
311
312 int i = 0;
313 const std::vector<int> maps = MidiInstrumentMapper::Maps();
314 for (int iMap = 0; iMap < maps.size(); ++iMap) {
315 const int mapID = maps[iMap];
316 const std::map<midi_prog_index_t, MidiInstrumentMapper::entry_t> mappings =
317 MidiInstrumentMapper::Entries(maps[mapID]);
318 for (std::map<midi_prog_index_t, MidiInstrumentMapper::entry_t>::const_iterator iter = mappings.begin();
319 iter != mappings.end(); ++iter, ++i)
320 {
321 if (i == program) {
322 //TODO: switch MIDI instrument map before sending bank select and program change
323
324 const uint iMIDIChannel = 0;
325 pMidiDevice->Port()->DispatchBankSelectMsb(iter->first.midi_bank_msb, iMIDIChannel);
326 pMidiDevice->Port()->DispatchBankSelectLsb(iter->first.midi_bank_lsb, iMIDIChannel);
327 pMidiDevice->Port()->DispatchProgramChange(iter->first.midi_prog, iMIDIChannel);
328
329 curProgram = program;
330
331 return;
332 }
333 }
334 }
335 }
336
337 void LinuxSamplerVst::setProgramName(char* name) {
338 int i = 0;
339 const std::vector<int> maps = MidiInstrumentMapper::Maps();
340 for (int iMap = 0; iMap < maps.size(); ++iMap) {
341 const int mapID = maps[iMap];
342 const std::map<midi_prog_index_t, MidiInstrumentMapper::entry_t> mappings =
343 MidiInstrumentMapper::Entries(maps[mapID]);
344 for (std::map<midi_prog_index_t, MidiInstrumentMapper::entry_t>::const_iterator iter = mappings.begin();
345 iter != mappings.end(); ++iter, ++i)
346 {
347 if (i == curProgram) {
348 char buf[kVstMaxProgNameLen + 1] = {};
349 vst_strncpy(buf, name, kVstMaxProgNameLen);
350 MidiInstrumentMapper::entry_t entry = iter->second;
351 entry.Name = buf;
352 try {
353 MidiInstrumentMapper::AddOrReplaceEntry(
354 mapID, iter->first, entry, false/*bInBackground*/
355 );
356 } catch (Exception e) {
357 e.PrintMessage();
358 }
359 return;
360 }
361 }
362 }
363 }
364
365 void LinuxSamplerVst::getProgramName(char* name) {
366 if (!getProgramNameIndexed(0 /*dont care*/, curProgram, name)) {
367 vst_strncpy(name, "unknown", kVstMaxProgNameLen);
368 }
369 }
370
371 bool LinuxSamplerVst::getProgramNameIndexed(VstInt32 /*category*/, VstInt32 index,
372 char* text)
373 {
374 //NOTE: parameter 'category' is unused in VST 2.4
375
376 int i = 0;
377 const std::vector<int> maps = MidiInstrumentMapper::Maps();
378 for (int iMap = 0; iMap < maps.size(); ++iMap) {
379 const int mapID = maps[iMap];
380 const std::map<midi_prog_index_t, MidiInstrumentMapper::entry_t> mappings =
381 MidiInstrumentMapper::Entries(maps[mapID]);
382 for (std::map<midi_prog_index_t, MidiInstrumentMapper::entry_t>::const_iterator iter = mappings.begin();
383 iter != mappings.end(); ++iter, ++i)
384 {
385 if (i == index) {
386 vst_strncpy(text, iter->second.Name.c_str(), kVstMaxProgNameLen);
387 return true;
388 }
389 }
390 }
391 return false;
392 }
393
394
395 bool LinuxSamplerVst::getOutputProperties(VstInt32 index,
396 VstPinProperties* properties) {
397 if (index < CHANNELS) {
398 sprintf(properties->label, "LS %d", index + 1);
399 properties->flags = kVstPinIsActive | kVstPinIsStereo;
400 return true;
401 }
402 return false;
403 }
404
405
406 bool LinuxSamplerVst::getEffectName(char* name) {
407 vst_strncpy(name, "LinuxSampler", kVstMaxEffectNameLen);
408 return true;
409 }
410
411
412 bool LinuxSamplerVst::getVendorString(char* text) {
413 vst_strncpy(text, "linuxsampler.org", kVstMaxVendorStrLen);
414 return true;
415 }
416
417
418 bool LinuxSamplerVst::getProductString(char* text) {
419 vst_strncpy(text, "LinuxSampler VST", kVstMaxProductStrLen);
420 return true;
421 }
422
423
424 VstInt32 LinuxSamplerVst::getVendorVersion() {
425 return 1000;
426 }
427
428
429 VstInt32 LinuxSamplerVst::canDo(char* text) {
430 dmsg(2, ("canDo %s\n", text));
431
432 // supported features
433 if (strcmp(text, "receiveVstEvents") == 0 ||
434 strcmp(text, "receiveVstMidiEvent") == 0 ||
435 strcmp(text, "midiProgramNames") == 0) return 1;
436
437 // not supported features
438 if (strcmp(text, "sendVstEvents") == 0 ||
439 strcmp(text, "sendVstMidiEvent") == 0 ||
440 strcmp(text, "receiveVstTimeInfo") == 0 ||
441 strcmp(text, "offline") == 0 ||
442 strcmp(text, "bypass") == 0) return -1;
443
444 return 0; // "don't know", never heard of this feature
445 }
446
447 void LinuxSamplerVst::setSampleRate(float sampleRate) {
448 dmsg(2, ("linuxsampler: setSampleRate %f\n", sampleRate));
449 AudioEffectX::setSampleRate(sampleRate);
450 }
451
452 void LinuxSamplerVst::setBlockSize(VstInt32 blockSize) {
453 dmsg(2, ("linuxsampler: setBlockSize %d\n", blockSize));
454 AudioEffectX::setBlockSize(blockSize);
455 }
456
457
458 void LinuxSamplerVst::processReplacing(float** inputs, float** outputs,
459 VstInt32 sampleFrames) {
460 if (pAudioDevice) {
461 for (int i = 0 ; i < CHANNELS ; i++) {
462 pAudioDevice->Channel(i)->SetBuffer(outputs[i]);
463 }
464 pAudioDevice->Render(sampleFrames);
465 } else {
466 for (int i = 0 ; i < CHANNELS ; i++) {
467 memset(outputs[i], 0, sampleFrames * sizeof(float));
468 }
469 }
470 }
471
472
473 VstInt32 LinuxSamplerVst::processEvents(VstEvents* ev) {
474 if (pMidiDevice) {
475 for (int i = 0; i < ev->numEvents; i++)
476 {
477 if (ev->events[i]->type == kVstMidiType) {
478 VstMidiEvent* event = reinterpret_cast<VstMidiEvent*>(ev->events[i]);
479 pMidiDevice->Port()->DispatchRaw(reinterpret_cast<uint8_t*>(event->midiData),
480 event->deltaFrames);
481 }
482 }
483 }
484 return 1;
485 }
486
487
488 VstInt32 LinuxSamplerVst::getChunk(void** data, bool isPreset) {
489 dmsg(2, ("-->getChunk\n"));
490
491 String state = GetState();
492 dmsg(2, ("state=\"%s\"\n", state.c_str()));;
493 if (StateBuf) free(StateBuf);
494 StateBuf = strdup(state.c_str());
495 *data = StateBuf;
496 dmsg(2, ("<--getChunk\n"));
497 return state.length() + 1;
498 }
499
500
501 VstInt32 LinuxSamplerVst::setChunk(void* data, VstInt32 byteSize,
502 bool isPreset) {
503 dmsg(2, ("-->setChunk byteSize=%d isPreset=%d\n", byteSize, isPreset));
504
505 const char* state = static_cast<const char*>(data);
506 VstInt32 res = 0;
507 if (byteSize > 0 && state[byteSize - 1] == '\0') {
508 dmsg(2, ("state=\"%s\"\n", state));
509 if (pAudioDevice) {
510 res = SetState(state);
511 } else {
512 SavedChunk = state;
513 }
514 }
515 dmsg(2, ("<--setChunk\n"));
516 return res;
517 }
518 }
519
520
521 // *************** VST main function ***************
522 // *
523
524 AudioEffect* createEffectInstance(audioMasterCallback audioMaster)
525 {
526 return new LinuxSamplerVst(audioMaster);
527 }

  ViewVC Help
Powered by ViewVC