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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3289 - (hide annotations) (download)
Fri Jun 23 12:06:00 2017 UTC (6 years, 10 months ago) by schoenebeck
File size: 18206 byte(s)
- Just fixes a compiler warning.

1 persson 1777 /***************************************************************************
2     * *
3 persson 2427 * Copyright (C) 2008 - 2013 Andreas Persson *
4 persson 1777 * *
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 persson 2347 #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 persson 1777 #endif
35    
36     #include "PluginVst.h"
37 schoenebeck 2372 #include "../../drivers/midi/MidiInstrumentMapper.h"
38 persson 1777
39 persson 1908 #ifndef CHANNELS
40 persson 1917 #define CHANNELS 32
41 persson 1908 #endif
42    
43 schoenebeck 2372 // 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 persson 1777 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 persson 2427 LockGuard lock(logmutex);
62 persson 2165 FILE* f = fopen((String(getenv("TEMP")) + "\\linuxsamplervst.log").c_str(), "a");
63 persson 1777 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 persson 1895 #ifdef WIN32
81     ProcessHandle = INVALID_HANDLE_VALUE;
82 persson 2347 #else
83     pid = 0;
84 persson 1895 #endif
85 persson 1777 }
86    
87 persson 1895 LinuxSamplerEditor::~LinuxSamplerEditor() {
88     close();
89     }
90    
91 persson 2347 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 persson 1777 bool LinuxSamplerEditor::open(void* ptr) {
142     dmsg(2, ("-->LinuxSamplerEditor::open\n"));
143     AEffEditor::open(ptr);
144    
145 persson 2347 // try to start the JSampler Fantasia GUI as a separate process
146 persson 1777 #ifdef WIN32
147 persson 1908 // 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 persson 1777
158 persson 2347 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 persson 1777
169 persson 2347 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 persson 2165 #ifdef _WIN64
185 persson 2347 // LS plugin is 64 bit - look for 32 bit java
186     path = String(windir) + "\\SysWOW64\\";
187 persson 2165 #else
188 persson 2347 // LS plugin is 32 bit - look for 64 bit java
189     path = String(windir) + "\\Sysnative\\";
190 persson 2165 #endif
191     }
192     }
193 persson 1777 }
194     }
195 persson 2347 #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 persson 1777 #endif
213 persson 2347 dmsg(1, ("exec failed %d %s\n", errno, strerror(errno)));
214    
215 persson 2360 // make sure something is executed, so the static
216 persson 2347 // destructors copied from the parent don't run
217     execl("/usr/bin/true", "/usr/bin/true", (char*)0);
218     }
219     }
220     #endif
221 persson 1777 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 persson 1908 CloseHandle(ProcessHandle);
232 persson 1895 ProcessHandle = INVALID_HANDLE_VALUE;
233 persson 1777 }
234 persson 2347 #else
235     if (pid) {
236     kill(pid, SIGTERM);
237     waitpid(pid, 0, 0);
238     pid = 0;
239     }
240 persson 1777 #endif
241     }
242    
243     bool LinuxSamplerEditor::getRect(ERect** rect) {
244     ERect* r = new ERect();
245     r->top = 0;
246     r->left = 0;
247 persson 2347 r->bottom = 1; // 0 makes EnergyXT and Ardour on Linux crash
248 persson 1777 r->right = 400;
249     *rect = r;
250     return true;
251     }
252    
253    
254     // *************** LinuxSamplerVst ***************
255     // *
256    
257     LinuxSamplerVst::LinuxSamplerVst(audioMasterCallback audioMaster) :
258 schoenebeck 2372 AudioEffectX(audioMaster, MAX_VST_PROGRAMS, 0),
259 persson 1777 StateBuf(0)
260     {
261     dmsg(2, ("-->constructor\n"));
262    
263     setProgram(0);
264     setNumInputs(0);
265 persson 1908 setNumOutputs(CHANNELS);
266 persson 1777 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 persson 2347 // 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 persson 1777 } else {
293 persson 2347 Init(int(sampleRate), blockSize, CHANNELS);
294 persson 1777 }
295     }
296 senoner 1942 AudioEffectX::resume();
297 persson 1777 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 schoenebeck 2372 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 persson 1777 }
335    
336     void LinuxSamplerVst::setProgramName(char* name) {
337 schoenebeck 2372 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 persson 1777 }
363    
364     void LinuxSamplerVst::getProgramName(char* name) {
365 schoenebeck 2372 if (!getProgramNameIndexed(0 /*dont care*/, curProgram, name)) {
366     vst_strncpy(name, "unknown", kVstMaxProgNameLen);
367     }
368 persson 1777 }
369    
370 schoenebeck 2372 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 persson 1777
393 schoenebeck 2372
394 persson 1777 bool LinuxSamplerVst::getOutputProperties(VstInt32 index,
395     VstPinProperties* properties) {
396 persson 1908 if (index < CHANNELS) {
397 persson 1777 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 schoenebeck 2372
431     // supported features
432 persson 1777 if (strcmp(text, "receiveVstEvents") == 0 ||
433 schoenebeck 2372 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 persson 1777 }
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 persson 1908 for (int i = 0 ; i < CHANNELS ; i++) {
461     pAudioDevice->Channel(i)->SetBuffer(outputs[i]);
462     }
463 persson 1777 pAudioDevice->Render(sampleFrames);
464     } else {
465 persson 1908 for (int i = 0 ; i < CHANNELS ; i++) {
466     memset(outputs[i], 0, sampleFrames * sizeof(float));
467     }
468 persson 1777 }
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 schoenebeck 3289 return (VstInt32) state.length() + 1;
497 persson 1777 }
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