/[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 3289 - (show annotations) (download)
Fri Jun 23 12:06:00 2017 UTC (6 years, 9 months ago) by schoenebeck
File size: 18206 byte(s)
- Just fixes a compiler warning.

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

  ViewVC Help
Powered by ViewVC