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