Mercurial > hg > apm2s
comparison app_wrapper/app_main.cpp @ 0:4606bd505630 tip
first import
author | Fiore Martin <f.martin@qmul.ac.uk> |
---|---|
date | Sat, 13 Jun 2015 15:08:10 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4606bd505630 |
---|---|
1 #include "app_main.h" | |
2 | |
3 #ifdef OS_WIN | |
4 #include <windows.h> | |
5 #include <shlobj.h> | |
6 #include <sys/stat.h> | |
7 #endif | |
8 | |
9 HWND gHWND; | |
10 | |
11 HINSTANCE gHINST; | |
12 UINT gScrollMessage; | |
13 IPlug* gPluginInstance; | |
14 RtAudio* gDAC = 0; | |
15 RtMidiIn *gMidiIn = 0; | |
16 RtMidiOut *gMidiOut = 0; | |
17 | |
18 AppState *gState; | |
19 AppState *gTempState; | |
20 AppState *gActiveState; | |
21 | |
22 char *gINIPath = new char[200]; // path of ini file | |
23 | |
24 unsigned int gIOVS = 512; | |
25 unsigned int gSigVS = 32; | |
26 unsigned int gBufIndex = 0; // Loops 0 to SigVS | |
27 unsigned int gVecElapsed = 0; | |
28 double gFadeMult = 0.; // Fade multiplier | |
29 | |
30 std::vector<unsigned int> gAudioInputDevs; | |
31 std::vector<unsigned int> gAudioOutputDevs; | |
32 std::vector<std::string> gMIDIInputDevNames; | |
33 std::vector<std::string> gMIDIOutputDevNames; | |
34 std::vector<std::string> gAudioIDDevNames; | |
35 | |
36 void UpdateINI() | |
37 { | |
38 char buf[100]; // temp buffer for writing integers to profile strings | |
39 | |
40 sprintf(buf, "%u", gState->mAudioDriverType); | |
41 WritePrivateProfileString("audio", "driver", buf, gINIPath); | |
42 | |
43 WritePrivateProfileString("audio", "indev", gState->mAudioInDev, gINIPath); | |
44 WritePrivateProfileString("audio", "outdev", gState->mAudioOutDev, gINIPath); | |
45 | |
46 sprintf(buf, "%u", gState->mAudioInChanL); | |
47 WritePrivateProfileString("audio", "in1", buf, gINIPath); | |
48 sprintf(buf, "%u", gState->mAudioInChanR); | |
49 WritePrivateProfileString("audio", "in2", buf, gINIPath); | |
50 sprintf(buf, "%u", gState->mAudioOutChanL); | |
51 WritePrivateProfileString("audio", "out1", buf, gINIPath); | |
52 sprintf(buf, "%u", gState->mAudioOutChanR); | |
53 WritePrivateProfileString("audio", "out2", buf, gINIPath); | |
54 sprintf(buf, "%u", gState->mAudioInIsMono); | |
55 WritePrivateProfileString("audio", "monoinput", buf, gINIPath); | |
56 | |
57 WritePrivateProfileString("audio", "iovs", gState->mAudioIOVS, gINIPath); | |
58 WritePrivateProfileString("audio", "sigvs", gState->mAudioSigVS, gINIPath); | |
59 | |
60 WritePrivateProfileString("audio", "sr", gState->mAudioSR, gINIPath); | |
61 | |
62 WritePrivateProfileString("midi", "indev", gState->mMidiInDev, gINIPath); | |
63 WritePrivateProfileString("midi", "outdev", gState->mMidiOutDev, gINIPath); | |
64 | |
65 sprintf(buf, "%u", gState->mMidiInChan); | |
66 WritePrivateProfileString("midi", "inchan", buf, gINIPath); | |
67 sprintf(buf, "%u", gState->mMidiOutChan); | |
68 WritePrivateProfileString("midi", "outchan", buf, gINIPath); | |
69 } | |
70 | |
71 // returns the device name. Core Audio device names are truncated | |
72 std::string GetAudioDeviceName(int idx) | |
73 { | |
74 return gAudioIDDevNames.at(idx); | |
75 } | |
76 | |
77 // returns the rtaudio device ID, based on the (truncated) device name | |
78 int GetAudioDeviceID(char* deviceNameToTest) | |
79 { | |
80 TRACE; | |
81 | |
82 for(int i = 0; i < gAudioIDDevNames.size(); i++) | |
83 { | |
84 if(!strcmp(deviceNameToTest, gAudioIDDevNames.at(i).c_str() )) | |
85 return i; | |
86 } | |
87 | |
88 return -1; | |
89 } | |
90 | |
91 unsigned int GetMIDIInPortNumber(const char* nameToTest) | |
92 { | |
93 int start = 1; | |
94 | |
95 if(!strcmp(nameToTest, "off")) return 0; | |
96 | |
97 #ifdef OS_OSX | |
98 start = 2; | |
99 if(!strcmp(nameToTest, "virtual input")) return 1; | |
100 #endif | |
101 | |
102 for (int i = 0; i < gMidiIn->getPortCount(); i++) | |
103 { | |
104 if(!strcmp(nameToTest, gMidiIn->getPortName(i).c_str())) | |
105 return (i + start); | |
106 } | |
107 | |
108 return -1; | |
109 } | |
110 | |
111 unsigned int GetMIDIOutPortNumber(const char* nameToTest) | |
112 { | |
113 int start = 1; | |
114 | |
115 if(!strcmp(nameToTest, "off")) return 0; | |
116 | |
117 #ifdef OS_OSX | |
118 start = 2; | |
119 if(!strcmp(nameToTest, "virtual output")) return 1; | |
120 #endif | |
121 | |
122 for (int i = 0; i < gMidiOut->getPortCount(); i++) | |
123 { | |
124 if(!strcmp(nameToTest, gMidiOut->getPortName(i).c_str())) | |
125 return (i + start); | |
126 } | |
127 | |
128 return -1; | |
129 } | |
130 | |
131 // find out which devices have input channels & which have output channels, add their ids to the lists | |
132 void ProbeAudioIO() | |
133 { | |
134 TRACE; | |
135 | |
136 RtAudio::DeviceInfo info; | |
137 | |
138 gAudioInputDevs.clear(); | |
139 gAudioOutputDevs.clear(); | |
140 gAudioIDDevNames.clear(); | |
141 | |
142 unsigned int nDevices = gDAC->getDeviceCount(); | |
143 | |
144 for (int i=0; i<nDevices; i++) | |
145 { | |
146 info = gDAC->getDeviceInfo(i); | |
147 std::string deviceName = info.name; | |
148 | |
149 #ifdef OS_OSX | |
150 size_t colonIdx = deviceName.rfind(": "); | |
151 | |
152 if(colonIdx != std::string::npos && deviceName.length() >= 2) | |
153 deviceName = deviceName.substr(colonIdx + 2, deviceName.length() - colonIdx - 2); | |
154 | |
155 #endif | |
156 | |
157 gAudioIDDevNames.push_back(deviceName); | |
158 | |
159 if ( info.probed == false ) | |
160 std::cout << deviceName << ": Probe Status = Unsuccessful\n"; | |
161 else if ( !strcmp("Generic Low Latency ASIO Driver", deviceName.c_str() )) | |
162 std::cout << deviceName << ": Probe Status = Unsuccessful\n"; | |
163 else | |
164 { | |
165 if(info.inputChannels > 0) | |
166 gAudioInputDevs.push_back(i); | |
167 | |
168 if(info.outputChannels > 0) | |
169 gAudioOutputDevs.push_back(i); | |
170 } | |
171 } | |
172 } | |
173 | |
174 void ProbeMidiIO() | |
175 { | |
176 if ( !gMidiIn || !gMidiOut ) | |
177 return; | |
178 else | |
179 { | |
180 int nInputPorts = gMidiIn->getPortCount(); | |
181 | |
182 gMIDIInputDevNames.push_back("off"); | |
183 | |
184 #ifndef OS_WIN | |
185 gMIDIInputDevNames.push_back("virtual input"); | |
186 #endif | |
187 | |
188 for (int i=0; i<nInputPorts; i++ ) | |
189 { | |
190 gMIDIInputDevNames.push_back(gMidiIn->getPortName(i)); | |
191 } | |
192 | |
193 int nOutputPorts = gMidiOut->getPortCount(); | |
194 | |
195 gMIDIOutputDevNames.push_back("off"); | |
196 | |
197 #ifndef _WIN32 | |
198 gMIDIOutputDevNames.push_back("virtual output"); | |
199 #endif | |
200 | |
201 for (int i=0; i<nOutputPorts; i++ ) | |
202 { | |
203 gMIDIOutputDevNames.push_back(gMidiOut->getPortName(i)); | |
204 //This means the virtual output port wont be added as an input | |
205 } | |
206 } | |
207 } | |
208 | |
209 bool AudioSettingsInStateAreEqual(AppState* os, AppState* ns) | |
210 { | |
211 if (os->mAudioDriverType != ns->mAudioDriverType) return false; | |
212 if (strcmp(os->mAudioInDev, ns->mAudioInDev)) return false; | |
213 if (strcmp(os->mAudioOutDev, ns->mAudioOutDev)) return false; | |
214 if (strcmp(os->mAudioSR, ns->mAudioSR)) return false; | |
215 if (strcmp(os->mAudioIOVS, ns->mAudioIOVS)) return false; | |
216 if (strcmp(os->mAudioSigVS, ns->mAudioSigVS)) return false; | |
217 if (os->mAudioInChanL != ns->mAudioInChanL) return false; | |
218 if (os->mAudioInChanR != ns->mAudioInChanR) return false; | |
219 if (os->mAudioOutChanL != ns->mAudioOutChanL) return false; | |
220 if (os->mAudioOutChanR != ns->mAudioOutChanR) return false; | |
221 if (os->mAudioInIsMono != ns->mAudioInIsMono) return false; | |
222 | |
223 return true; | |
224 } | |
225 | |
226 bool MIDISettingsInStateAreEqual(AppState* os, AppState* ns) | |
227 { | |
228 if (strcmp(os->mMidiInDev, ns->mMidiInDev)) return false; | |
229 if (strcmp(os->mMidiOutDev, ns->mMidiOutDev)) return false; | |
230 if (os->mMidiInChan != ns->mMidiInChan) return false; | |
231 if (os->mMidiOutChan != ns->mMidiOutChan) return false; | |
232 | |
233 return true; | |
234 } | |
235 | |
236 void MIDICallback( double deltatime, std::vector< unsigned char > *message, void *userData ) | |
237 { | |
238 if ( message->size() ) | |
239 { | |
240 IMidiMsg *myMsg; | |
241 | |
242 switch (message->size()) | |
243 { | |
244 case 1: | |
245 myMsg = new IMidiMsg(0, message->at(0), 0, 0); | |
246 break; | |
247 case 2: | |
248 myMsg = new IMidiMsg(0, message->at(0), message->at(1), 0); | |
249 break; | |
250 case 3: | |
251 myMsg = new IMidiMsg(0, message->at(0), message->at(1), message->at(2)); | |
252 break; | |
253 default: | |
254 DBGMSG("NOT EXPECTING %d midi callback msg len\n", (int) message->size()); | |
255 break; | |
256 } | |
257 | |
258 IMidiMsg msg(*myMsg); | |
259 | |
260 delete myMsg; | |
261 | |
262 // filter midi messages based on channel, if gStatus.mMidiInChan != all (0) | |
263 if (gState->mMidiInChan) | |
264 { | |
265 if (gState->mMidiInChan == msg.Channel() + 1 ) | |
266 gPluginInstance->ProcessMidiMsg(&msg); | |
267 } | |
268 else | |
269 { | |
270 gPluginInstance->ProcessMidiMsg(&msg); | |
271 } | |
272 } | |
273 } | |
274 | |
275 int AudioCallback(void *outputBuffer, | |
276 void *inputBuffer, | |
277 unsigned int nFrames, | |
278 double streamTime, | |
279 RtAudioStreamStatus status, | |
280 void *userData ) | |
281 { | |
282 if ( status ) | |
283 std::cout << "Stream underflow detected!" << std::endl; | |
284 | |
285 double* inputBufferD = (double*)inputBuffer; | |
286 double* outputBufferD = (double*)outputBuffer; | |
287 | |
288 int inRightOffset = 0; | |
289 | |
290 if(!gState->mAudioInIsMono) | |
291 inRightOffset = nFrames; | |
292 | |
293 if (gVecElapsed > N_VECTOR_WAIT) // wait N_VECTOR_WAIT * iovs before processing audio, to avoid clicks | |
294 { | |
295 for (int i=0; i<nFrames; i++) | |
296 { | |
297 gBufIndex %= gSigVS; | |
298 | |
299 if (gBufIndex == 0) | |
300 { | |
301 double* inputs[2] = {inputBufferD + i, inputBufferD + inRightOffset + i}; | |
302 double* outputs[2] = {outputBufferD + i, outputBufferD + nFrames + i}; | |
303 | |
304 gPluginInstance->LockMutexAndProcessDoubleReplacing(inputs, outputs, gSigVS); | |
305 } | |
306 | |
307 // fade in | |
308 if (gFadeMult < 1.) | |
309 { | |
310 gFadeMult += (1. / nFrames); | |
311 } | |
312 | |
313 outputBufferD[i] *= gFadeMult; | |
314 outputBufferD[i + nFrames] *= gFadeMult; | |
315 | |
316 outputBufferD[i] *= APP_MULT; | |
317 outputBufferD[i + nFrames] *= APP_MULT; | |
318 | |
319 gBufIndex++; | |
320 } | |
321 } | |
322 else | |
323 { | |
324 memset(outputBuffer, 0, nFrames * 2 * sizeof(double)); | |
325 } | |
326 | |
327 gVecElapsed++; | |
328 | |
329 return 0; | |
330 } | |
331 | |
332 bool TryToChangeAudioDriverType() | |
333 { | |
334 TRACE; | |
335 | |
336 if (gDAC) | |
337 { | |
338 if (gDAC->isStreamOpen()) | |
339 { | |
340 gDAC->closeStream(); | |
341 } | |
342 | |
343 DELETE_NULL(gDAC); | |
344 } | |
345 | |
346 #ifdef OS_WIN | |
347 if(gState->mAudioDriverType == DAC_ASIO) | |
348 gDAC = new RtAudio(RtAudio::WINDOWS_ASIO); | |
349 else | |
350 gDAC = new RtAudio(RtAudio::WINDOWS_DS); | |
351 #elif defined OS_OSX | |
352 if(gState->mAudioDriverType == DAC_COREAUDIO) | |
353 gDAC = new RtAudio(RtAudio::MACOSX_CORE); | |
354 //else | |
355 //gDAC = new RtAudio(RtAudio::UNIX_JACK); | |
356 #endif | |
357 | |
358 if(gDAC) | |
359 return true; | |
360 else | |
361 return false; | |
362 } | |
363 | |
364 bool TryToChangeAudio() | |
365 { | |
366 TRACE; | |
367 | |
368 int inputID = -1; | |
369 int outputID = -1; | |
370 | |
371 #ifdef OS_WIN | |
372 if(gState->mAudioDriverType == DAC_ASIO) | |
373 inputID = GetAudioDeviceID(gState->mAudioOutDev); | |
374 else | |
375 inputID = GetAudioDeviceID(gState->mAudioInDev); | |
376 #else | |
377 inputID = GetAudioDeviceID(gState->mAudioInDev); | |
378 #endif | |
379 | |
380 outputID = GetAudioDeviceID(gState->mAudioOutDev); | |
381 | |
382 int samplerate = atoi(gState->mAudioSR); | |
383 int iovs = atoi(gState->mAudioIOVS); | |
384 | |
385 if (inputID != -1 && outputID != -1) | |
386 { | |
387 return InitialiseAudio(inputID, outputID, samplerate, iovs, NUM_CHANNELS, gState->mAudioInChanL - 1, gState->mAudioOutChanL - 1); | |
388 } | |
389 | |
390 return false; | |
391 } | |
392 | |
393 bool InitialiseAudio(unsigned int inId, | |
394 unsigned int outId, | |
395 unsigned int sr, | |
396 unsigned int iovs, | |
397 unsigned int chnls, | |
398 unsigned int inChanL, | |
399 unsigned int outChanL | |
400 ) | |
401 { | |
402 TRACE; | |
403 | |
404 if (gDAC->isStreamOpen()) | |
405 { | |
406 if (gDAC->isStreamRunning()) | |
407 { | |
408 try | |
409 { | |
410 gDAC->abortStream(); | |
411 } | |
412 catch (RtError& e) | |
413 { | |
414 e.printMessage(); | |
415 } | |
416 } | |
417 | |
418 gDAC->closeStream(); | |
419 } | |
420 | |
421 RtAudio::StreamParameters iParams, oParams; | |
422 iParams.deviceId = inId; | |
423 iParams.nChannels = chnls; | |
424 iParams.firstChannel = inChanL; | |
425 | |
426 oParams.deviceId = outId; | |
427 oParams.nChannels = chnls; | |
428 oParams.firstChannel = outChanL; | |
429 | |
430 gIOVS = iovs; // gIOVS may get changed by stream | |
431 gSigVS = atoi(gState->mAudioSigVS); // This is done here so that it changes when the callback is stopped | |
432 | |
433 DBGMSG("\ntrying to start audio stream @ %i sr, %i iovs, %i sigvs\nindev = %i:%s\noutdev = %i:%s\n", sr, iovs, gSigVS, inId, GetAudioDeviceName(inId).c_str(), outId, GetAudioDeviceName(outId).c_str()); | |
434 | |
435 RtAudio::StreamOptions options; | |
436 options.flags = RTAUDIO_NONINTERLEAVED; | |
437 // options.streamName = BUNDLE_NAME; // JACK stream name, not used on other streams | |
438 | |
439 gBufIndex = 0; | |
440 gVecElapsed = 0; | |
441 gFadeMult = 0.; | |
442 | |
443 gPluginInstance->SetBlockSize(gSigVS); | |
444 gPluginInstance->SetSampleRate(sr); | |
445 gPluginInstance->Reset(); | |
446 | |
447 try | |
448 { | |
449 TRACE; | |
450 gDAC->openStream( &oParams, &iParams, RTAUDIO_FLOAT64, sr, &gIOVS, &AudioCallback, NULL, &options); | |
451 gDAC->startStream(); | |
452 | |
453 memcpy(gActiveState, gState, sizeof(AppState)); // copy state to active state | |
454 } | |
455 catch ( RtError& e ) | |
456 { | |
457 e.printMessage(); | |
458 return false; | |
459 } | |
460 | |
461 return true; | |
462 } | |
463 | |
464 bool InitialiseMidi() | |
465 { | |
466 try | |
467 { | |
468 gMidiIn = new RtMidiIn(); | |
469 } | |
470 catch ( RtError &error ) | |
471 { | |
472 FREE_NULL(gMidiIn); | |
473 error.printMessage(); | |
474 return false; | |
475 } | |
476 | |
477 try | |
478 { | |
479 gMidiOut = new RtMidiOut(); | |
480 } | |
481 catch ( RtError &error ) | |
482 { | |
483 FREE_NULL(gMidiOut); | |
484 error.printMessage(); | |
485 return false; | |
486 } | |
487 | |
488 gMidiIn->setCallback( &MIDICallback ); | |
489 gMidiIn->ignoreTypes( !ENABLE_SYSEX, !ENABLE_MIDICLOCK, !ENABLE_ACTIVE_SENSING ); | |
490 | |
491 return true; | |
492 } | |
493 | |
494 bool ChooseMidiInput(const char* pPortName) | |
495 { | |
496 unsigned int port = GetMIDIInPortNumber(pPortName); | |
497 | |
498 if(port == -1) | |
499 { | |
500 strcpy(gState->mMidiInDev, "off"); | |
501 UpdateINI(); | |
502 port = 0; | |
503 } | |
504 /* | |
505 IMidiMsg msg; | |
506 msg.MakeControlChangeMsg(IMidiMsg::kAllNotesOff, 127, 0); | |
507 | |
508 std::vector<unsigned char> message; | |
509 message.push_back( msg.mStatus ); | |
510 message.push_back( msg.mData1 ); | |
511 message.push_back( msg.mData2 ); | |
512 | |
513 gPluginInstance->ProcessMidiMsg(&msg); | |
514 */ | |
515 if (gMidiIn) | |
516 { | |
517 gMidiIn->closePort(); | |
518 | |
519 if (port == 0) | |
520 { | |
521 return true; | |
522 } | |
523 #ifdef OS_WIN | |
524 else | |
525 { | |
526 gMidiIn->openPort(port-1); | |
527 return true; | |
528 } | |
529 #else | |
530 else if(port == 1) | |
531 { | |
532 std::string virtualMidiInputName = "To "; | |
533 virtualMidiInputName += BUNDLE_NAME; | |
534 gMidiIn->openVirtualPort(virtualMidiInputName); | |
535 return true; | |
536 } | |
537 else | |
538 { | |
539 gMidiIn->openPort(port-2); | |
540 return true; | |
541 } | |
542 #endif | |
543 } | |
544 | |
545 return false; | |
546 } | |
547 | |
548 bool ChooseMidiOutput(const char* pPortName) | |
549 { | |
550 unsigned int port = GetMIDIOutPortNumber(pPortName); | |
551 | |
552 if(port == -1) | |
553 { | |
554 strcpy(gState->mMidiOutDev, "off"); | |
555 UpdateINI(); | |
556 port = 0; | |
557 } | |
558 | |
559 if (gMidiOut) | |
560 { | |
561 /* | |
562 IMidiMsg msg; | |
563 msg.MakeControlChangeMsg(IMidiMsg::kAllNotesOff, 127, 0); | |
564 | |
565 std::vector<unsigned char> message; | |
566 message.push_back( msg.mStatus ); | |
567 message.push_back( msg.mData1 ); | |
568 message.push_back( msg.mData2 ); | |
569 | |
570 gMidiOut->sendMessage( &message ); | |
571 */ | |
572 gMidiOut->closePort(); | |
573 | |
574 if (port == 0) | |
575 { | |
576 return true; | |
577 } | |
578 #ifdef OS_WIN | |
579 else | |
580 { | |
581 gMidiOut->openPort(port-1); | |
582 return true; | |
583 } | |
584 #else | |
585 else if(port == 1) | |
586 { | |
587 std::string virtualMidiOutputName = "From "; | |
588 virtualMidiOutputName += BUNDLE_NAME; | |
589 gMidiOut->openVirtualPort(virtualMidiOutputName); | |
590 return true; | |
591 } | |
592 else | |
593 { | |
594 gMidiOut->openPort(port-2); | |
595 return true; | |
596 } | |
597 #endif | |
598 } | |
599 | |
600 return false; | |
601 } | |
602 | |
603 extern bool AttachGUI() | |
604 { | |
605 IGraphics* pGraphics = gPluginInstance->GetGUI(); | |
606 | |
607 if (pGraphics) | |
608 { | |
609 #ifdef OS_WIN | |
610 if (!pGraphics->OpenWindow(gHWND)) | |
611 pGraphics=0; | |
612 #else // Cocoa OSX | |
613 if (!pGraphics->OpenWindow(gHWND)) | |
614 pGraphics=0; | |
615 #endif | |
616 if (pGraphics) | |
617 { | |
618 gPluginInstance->OnGUIOpen(); | |
619 return true; | |
620 } | |
621 } | |
622 | |
623 return false; | |
624 } | |
625 | |
626 void Init() | |
627 { | |
628 TryToChangeAudioDriverType(); // will init RTAudio with an API type based on gState->mAudioDriverType | |
629 ProbeAudioIO(); // find out what audio IO devs are available and put their IDs in the global variables gAudioInputDevs / gAudioOutputDevs | |
630 InitialiseMidi(); // creates RTMidiIn and RTMidiOut objects | |
631 ProbeMidiIO(); // find out what midi IO devs are available and put their names in the global variables gMidiInputDevs / gMidiOutputDevs | |
632 | |
633 // Initialise the plugin | |
634 gPluginInstance = MakePlug(gMidiOut, &gState->mMidiOutChan); | |
635 gPluginInstance->RestorePreset(0); | |
636 | |
637 ChooseMidiInput(gState->mMidiInDev); | |
638 ChooseMidiOutput(gState->mMidiOutDev); | |
639 | |
640 TryToChangeAudio(); | |
641 } | |
642 | |
643 void Cleanup() | |
644 { | |
645 try | |
646 { | |
647 // Stop the stream | |
648 gDAC->stopStream(); | |
649 } | |
650 catch (RtError& e) | |
651 { | |
652 e.printMessage(); | |
653 } | |
654 | |
655 gMidiIn->cancelCallback(); | |
656 gMidiIn->closePort(); | |
657 gMidiOut->closePort(); | |
658 | |
659 if ( gDAC->isStreamOpen() ) gDAC->closeStream(); | |
660 | |
661 delete gPluginInstance; | |
662 delete gState; | |
663 delete gTempState; | |
664 delete gActiveState; | |
665 delete gMidiIn; | |
666 delete gMidiOut; | |
667 delete gDAC; | |
668 delete [] gINIPath; | |
669 } | |
670 | |
671 #ifdef OS_WIN | |
672 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nShowCmd) | |
673 { | |
674 // first check to make sure this is the only instance running | |
675 // http://www.bcbjournal.org/articles/vol3/9911/Single-instance_applications.htm | |
676 try | |
677 { | |
678 // Try to open the mutex. | |
679 HANDLE hMutex = OpenMutex( | |
680 MUTEX_ALL_ACCESS, 0, BUNDLE_NAME); | |
681 | |
682 // If hMutex is 0 then the mutex doesn't exist. | |
683 if (!hMutex) | |
684 hMutex = CreateMutex(0, 0, BUNDLE_NAME); | |
685 else | |
686 { | |
687 // This is a second instance. Bring the | |
688 // original instance to the top. | |
689 HWND hWnd = FindWindow(0, BUNDLE_NAME); | |
690 SetForegroundWindow(hWnd); | |
691 | |
692 return 0; | |
693 } | |
694 | |
695 gHINST=hInstance; | |
696 | |
697 InitCommonControls(); | |
698 gScrollMessage = RegisterWindowMessage("MSWHEEL_ROLLMSG"); | |
699 | |
700 gState = new AppState(); | |
701 gTempState = new AppState(); | |
702 gActiveState = new AppState(); | |
703 | |
704 if (SHGetFolderPathA( NULL, CSIDL_LOCAL_APPDATA, NULL, 0, gINIPath ) != S_OK) | |
705 { | |
706 DBGMSG("could not retrieve the user's application data directory!\n"); | |
707 | |
708 //TODO error msg? | |
709 return 1; | |
710 } | |
711 | |
712 sprintf(gINIPath, "%s\\%s", gINIPath, BUNDLE_NAME); // Add the app name to the path | |
713 | |
714 struct stat st; | |
715 if(stat(gINIPath, &st) == 0) // if directory exists | |
716 { | |
717 sprintf(gINIPath, "%s\\%s", gINIPath, "settings.ini"); // add file name to path | |
718 | |
719 if(stat(gINIPath, &st) == 0) // if settings file exists read values into state | |
720 { | |
721 gState->mAudioDriverType = GetPrivateProfileInt("audio", "driver", 0, gINIPath); | |
722 | |
723 GetPrivateProfileString("audio", "indev", DEFAULT_INPUT_DEV, gState->mAudioInDev, 100, gINIPath); | |
724 GetPrivateProfileString("audio", "outdev", DEFAULT_OUTPUT_DEV, gState->mAudioOutDev, 100, gINIPath); | |
725 | |
726 //audio | |
727 gState->mAudioInChanL = GetPrivateProfileInt("audio", "in1", 1, gINIPath); // 1 is first audio input | |
728 gState->mAudioInChanR = GetPrivateProfileInt("audio", "in2", 2, gINIPath); | |
729 gState->mAudioOutChanL = GetPrivateProfileInt("audio", "out1", 1, gINIPath); // 1 is first audio output | |
730 gState->mAudioOutChanR = GetPrivateProfileInt("audio", "out2", 2, gINIPath); | |
731 gState->mAudioInIsMono = GetPrivateProfileInt("audio", "monoinput", 0, gINIPath); | |
732 | |
733 GetPrivateProfileString("audio", "iovs", "512", gState->mAudioIOVS, 100, gINIPath); | |
734 GetPrivateProfileString("audio", "sigvs", "32", gState->mAudioSigVS, 100, gINIPath); | |
735 GetPrivateProfileString("audio", "sr", "44100", gState->mAudioSR, 100, gINIPath); | |
736 | |
737 //midi | |
738 GetPrivateProfileString("midi", "indev", "no input", gState->mMidiInDev, 100, gINIPath); | |
739 GetPrivateProfileString("midi", "outdev", "no output", gState->mMidiOutDev, 100, gINIPath); | |
740 | |
741 gState->mMidiInChan = GetPrivateProfileInt("midi", "inchan", 0, gINIPath); // 0 is any | |
742 gState->mMidiOutChan = GetPrivateProfileInt("midi", "outchan", 0, gINIPath); // 1 is first chan | |
743 | |
744 UpdateINI(); // this will write over any invalid values in the file | |
745 } | |
746 else // settings file doesn't exist, so populate with default values | |
747 { | |
748 UpdateINI(); | |
749 } | |
750 } | |
751 else | |
752 { | |
753 // folder doesn't exist - make folder and make file | |
754 CreateDirectory(gINIPath, NULL); | |
755 sprintf(gINIPath, "%s%s", gINIPath, "settings.ini"); // add file name to path | |
756 UpdateINI(); // will write file if doesn't exist | |
757 } | |
758 | |
759 Init(); | |
760 | |
761 CreateDialog(gHINST,MAKEINTRESOURCE(IDD_DIALOG_MAIN),GetDesktopWindow(),MainDlgProc); | |
762 | |
763 for(;;) | |
764 { | |
765 MSG msg= {0,}; | |
766 int vvv = GetMessage(&msg,NULL,0,0); | |
767 if (!vvv) break; | |
768 | |
769 if (vvv<0) | |
770 { | |
771 Sleep(10); | |
772 continue; | |
773 } | |
774 if (!msg.hwnd) | |
775 { | |
776 DispatchMessage(&msg); | |
777 continue; | |
778 } | |
779 | |
780 if (gHWND && IsDialogMessage(gHWND,&msg)) continue; | |
781 | |
782 // default processing for other dialogs | |
783 HWND hWndParent=NULL; | |
784 HWND temphwnd = msg.hwnd; | |
785 do | |
786 { | |
787 if (GetClassLong(temphwnd, GCW_ATOM) == (INT)32770) | |
788 { | |
789 hWndParent=temphwnd; | |
790 if (!(GetWindowLong(temphwnd,GWL_STYLE)&WS_CHILD)) break; // not a child, exit | |
791 } | |
792 } | |
793 while (temphwnd = GetParent(temphwnd)); | |
794 | |
795 if (hWndParent && IsDialogMessage(hWndParent,&msg)) continue; | |
796 | |
797 TranslateMessage(&msg); | |
798 DispatchMessage(&msg); | |
799 | |
800 } | |
801 | |
802 // in case gHWND didnt get destroyed -- this corresponds to SWELLAPP_DESTROY roughly | |
803 if (gHWND) DestroyWindow(gHWND); | |
804 | |
805 Cleanup(); | |
806 | |
807 ReleaseMutex(hMutex); | |
808 } | |
809 catch(...) | |
810 { | |
811 //TODO proper error catching | |
812 DBGMSG("another instance running"); | |
813 } | |
814 return 0; | |
815 } | |
816 #else | |
817 | |
818 extern HMENU SWELL_app_stocksysmenu; | |
819 const char *homeDir; | |
820 | |
821 INT_PTR SWELLAppMain(int msg, INT_PTR parm1, INT_PTR parm2) | |
822 { | |
823 switch (msg) | |
824 { | |
825 case SWELLAPP_ONLOAD: | |
826 | |
827 gState = new AppState(); | |
828 gTempState = new AppState(); | |
829 gActiveState = new AppState(); | |
830 | |
831 homeDir = getenv("HOME"); | |
832 sprintf(gINIPath, "%s/Library/Application Support/%s/", homeDir, BUNDLE_NAME); | |
833 | |
834 struct stat st; | |
835 if(stat(gINIPath, &st) == 0) // if directory exists | |
836 { | |
837 sprintf(gINIPath, "%s%s", gINIPath, "settings.ini"); // add file name to path | |
838 | |
839 if(stat(gINIPath, &st) == 0) // if settings file exists read values into state | |
840 { | |
841 gState->mAudioDriverType = GetPrivateProfileInt("audio", "driver", 0, gINIPath); | |
842 | |
843 GetPrivateProfileString("audio", "indev", "Built-in Input", gState->mAudioInDev, 100, gINIPath); | |
844 GetPrivateProfileString("audio", "outdev", "Built-in Output", gState->mAudioOutDev, 100, gINIPath); | |
845 | |
846 //audio | |
847 gState->mAudioInChanL = GetPrivateProfileInt("audio", "in1", 1, gINIPath); // 1 is first audio input | |
848 gState->mAudioInChanR = GetPrivateProfileInt("audio", "in2", 2, gINIPath); | |
849 gState->mAudioOutChanL = GetPrivateProfileInt("audio", "out1", 1, gINIPath); // 1 is first audio output | |
850 gState->mAudioOutChanR = GetPrivateProfileInt("audio", "out2", 2, gINIPath); | |
851 gState->mAudioInIsMono = GetPrivateProfileInt("audio", "monoinput", 0, gINIPath); | |
852 | |
853 GetPrivateProfileString("audio", "iovs", "512", gState->mAudioIOVS, 100, gINIPath); | |
854 GetPrivateProfileString("audio", "sigvs", "32", gState->mAudioSigVS, 100, gINIPath); | |
855 GetPrivateProfileString("audio", "sr", "44100", gState->mAudioSR, 100, gINIPath); | |
856 | |
857 //midi | |
858 GetPrivateProfileString("midi", "indev", "no input", gState->mMidiInDev, 100, gINIPath); | |
859 GetPrivateProfileString("midi", "outdev", "no output", gState->mMidiOutDev, 100, gINIPath); | |
860 | |
861 gState->mMidiInChan = GetPrivateProfileInt("midi", "inchan", 0, gINIPath); // 0 is any | |
862 gState->mMidiOutChan = GetPrivateProfileInt("midi", "outchan", 0, gINIPath); // 1 is first chan | |
863 | |
864 UpdateINI(); // this will write over any invalid values in the file | |
865 } | |
866 else // settings file doesn't exist, so populate with default values | |
867 { | |
868 UpdateINI(); | |
869 } | |
870 | |
871 } | |
872 else // folder doesn't exist - make folder and make file | |
873 { | |
874 // http://blog.tremend.ro/2008/10/06/create-directories-in-c-using-mkdir-with-proper-permissions/ | |
875 | |
876 mode_t process_mask = umask(0); | |
877 int result_code = mkdir(gINIPath, S_IRWXU | S_IRWXG | S_IRWXO); | |
878 umask(process_mask); | |
879 | |
880 if(result_code) return 1; | |
881 else | |
882 { | |
883 sprintf(gINIPath, "%s%s", gINIPath, "settings.ini"); // add file name to path | |
884 UpdateINI(); // will write file if doesn't exist | |
885 } | |
886 } | |
887 break; | |
888 #pragma mark loaded | |
889 case SWELLAPP_LOADED: | |
890 { | |
891 Init(); | |
892 | |
893 HMENU menu = SWELL_GetCurrentMenu(); | |
894 | |
895 if (menu) | |
896 { | |
897 // other windows will get the stock (bundle) menus | |
898 //SWELL_SetDefaultModalWindowMenu(menu); | |
899 //SWELL_SetDefaultWindowMenu(menu); | |
900 | |
901 // work on a new menu | |
902 menu = SWELL_DuplicateMenu(menu); | |
903 HMENU src = LoadMenu(NULL,MAKEINTRESOURCE(IDR_MENU1)); | |
904 int x; | |
905 for (x=0; x<GetMenuItemCount(src)-1; x++) | |
906 { | |
907 HMENU sm = GetSubMenu(src,x); | |
908 if (sm) | |
909 { | |
910 char str[1024]; | |
911 MENUITEMINFO mii= {sizeof(mii),MIIM_TYPE,}; | |
912 mii.dwTypeData=str; | |
913 mii.cch=sizeof(str); | |
914 str[0]=0; | |
915 GetMenuItemInfo(src,x,TRUE,&mii); | |
916 MENUITEMINFO mi= {sizeof(mi),MIIM_STATE|MIIM_SUBMENU|MIIM_TYPE,MFT_STRING,0,0,SWELL_DuplicateMenu(sm),NULL,NULL,0,str}; | |
917 InsertMenuItem(menu,x+1,TRUE,&mi); | |
918 } | |
919 } | |
920 } | |
921 | |
922 if (menu) | |
923 { | |
924 HMENU sm=GetSubMenu(menu,1); | |
925 DeleteMenu(sm,ID_QUIT,MF_BYCOMMAND); // remove QUIT from our file menu, since it is in the system menu on OSX | |
926 DeleteMenu(sm,ID_PREFERENCES,MF_BYCOMMAND); // remove PREFERENCES from the file menu, since it is in the system menu on OSX | |
927 | |
928 // remove any trailing separators | |
929 int a= GetMenuItemCount(sm); | |
930 while (a > 0 && GetMenuItemID(sm,a-1)==0) DeleteMenu(sm,--a,MF_BYPOSITION); | |
931 | |
932 DeleteMenu(menu,1,MF_BYPOSITION); // delete file menu | |
933 } | |
934 | |
935 // if we want to set any default modifiers for items in the menus, we can use: | |
936 // SetMenuItemModifier(menu,commandID,MF_BYCOMMAND,'A',FCONTROL) etc. | |
937 | |
938 HWND hwnd = CreateDialog(gHINST,MAKEINTRESOURCE(IDD_DIALOG_MAIN),NULL,MainDlgProc); | |
939 if (menu) | |
940 { | |
941 SetMenu(hwnd, menu); // set the menu for the dialog to our menu (on Windows that menu is set from the .rc, but on SWELL | |
942 SWELL_SetDefaultModalWindowMenu(menu); // other windows will get the stock (bundle) menus | |
943 } | |
944 // we need to set it manually (and obviously we've edited the menu anyway) | |
945 } | |
946 | |
947 if(!AttachGUI()) DBGMSG("couldn't attach gui\n"); //todo error | |
948 | |
949 break; | |
950 case SWELLAPP_ONCOMMAND: | |
951 // this is to catch commands coming from the system menu etc | |
952 if (gHWND && (parm1&0xffff)) SendMessage(gHWND,WM_COMMAND,parm1&0xffff,0); | |
953 break; | |
954 #pragma mark destroy | |
955 case SWELLAPP_DESTROY: | |
956 | |
957 if (gHWND) DestroyWindow(gHWND); | |
958 Cleanup(); | |
959 break; | |
960 case SWELLAPP_PROCESSMESSAGE: // can hook keyboard input here | |
961 // parm1 = (MSG*), should we want it -- look in swell.h to see what the return values refer to | |
962 break; | |
963 } | |
964 return 0; | |
965 } | |
966 | |
967 #endif | |
968 | |
969 | |
970 #ifndef OS_WIN | |
971 #include "swell-dlggen.h" | |
972 | |
973 #define SET_IDD_SCALE 1. | |
974 #define SET_IDD_STYLE SWELL_DLG_WS_FLIPPED|SWELL_DLG_WS_NOAUTOSIZE | |
975 | |
976 SWELL_DEFINE_DIALOG_RESOURCE_BEGIN(IDD_DIALOG_MAIN, SET_IDD_STYLE, BUNDLE_NAME, GUI_WIDTH, GUI_HEIGHT, SET_IDD_SCALE) | |
977 BEGIN | |
978 // EDITTEXT IDC_EDIT1,59,50,145,14,ES_AUTOHSCROLL | |
979 // LTEXT "Enter some text here:",IDC_STATIC,59,39,73,8 | |
980 END | |
981 SWELL_DEFINE_DIALOG_RESOURCE_END(IDD_DIALOG_MAIN) | |
982 | |
983 SWELL_DEFINE_DIALOG_RESOURCE_BEGIN(IDD_DIALOG_PREF,SET_IDD_STYLE,"Preferences",320,420,SET_IDD_SCALE) | |
984 BEGIN | |
985 GROUPBOX "Audio Settings", IDC_STATIC, 5, 10, 300, 230 | |
986 | |
987 LTEXT "Driver Type", IDC_STATIC, 20, 32, 60, 20 | |
988 COMBOBOX IDC_COMBO_AUDIO_DRIVER, 20, 50, 150, 100, CBS_DROPDOWNLIST | |
989 | |
990 LTEXT "Input Device", IDC_STATIC, 20, 75, 80, 20 | |
991 COMBOBOX IDC_COMBO_AUDIO_IN_DEV, 20, 90, 150, 100, CBS_DROPDOWNLIST | |
992 | |
993 LTEXT "Output Device", IDC_STATIC, 20, 115, 80, 20 | |
994 COMBOBOX IDC_COMBO_AUDIO_OUT_DEV, 20, 130, 150, 100, CBS_DROPDOWNLIST | |
995 | |
996 LTEXT "In 1 (L)", IDC_STATIC, 20, 155, 90, 20 | |
997 COMBOBOX IDC_COMBO_AUDIO_IN_L, 20, 170, 46, 100, CBS_DROPDOWNLIST | |
998 | |
999 LTEXT "In 2 (R)", IDC_STATIC, 75, 155, 90, 20 | |
1000 COMBOBOX IDC_COMBO_AUDIO_IN_R, 75, 170, 46, 100, CBS_DROPDOWNLIST | |
1001 | |
1002 CHECKBOX "Mono", IDC_CB_MONO_INPUT, 125, 128, 56, 100, 0 | |
1003 | |
1004 LTEXT "Out 1 (L)", IDC_STATIC, 20, 195, 60, 20 | |
1005 COMBOBOX IDC_COMBO_AUDIO_OUT_L, 20, 210, 46, 100, CBS_DROPDOWNLIST | |
1006 | |
1007 LTEXT "Out 2 (R)", IDC_STATIC, 75, 195, 60, 20 | |
1008 COMBOBOX IDC_COMBO_AUDIO_OUT_R, 75, 210, 46, 100, CBS_DROPDOWNLIST | |
1009 | |
1010 LTEXT "IO Vector Size", IDC_STATIC, 200, 32, 80, 20 | |
1011 COMBOBOX IDC_COMBO_AUDIO_IOVS, 200, 50, 90, 100, CBS_DROPDOWNLIST | |
1012 | |
1013 LTEXT "Signal Vector Size", IDC_STATIC, 200, 75, 100, 20 | |
1014 COMBOBOX IDC_COMBO_AUDIO_SIGVS, 200, 90, 90, 100, CBS_DROPDOWNLIST | |
1015 | |
1016 LTEXT "Sampling Rate", IDC_STATIC, 200, 115, 80, 20 | |
1017 COMBOBOX IDC_COMBO_AUDIO_SR, 200, 130, 90, 100, CBS_DROPDOWNLIST | |
1018 | |
1019 PUSHBUTTON "Audio Midi Setup...", IDC_BUTTON_ASIO, 180, 170, 110, 40 | |
1020 | |
1021 GROUPBOX "MIDI Settings", IDC_STATIC, 5, 255, 300, 120 | |
1022 | |
1023 LTEXT "Input Device", IDC_STATIC, 20, 275, 100, 20 | |
1024 COMBOBOX IDC_COMBO_MIDI_IN_DEV, 20, 293, 150, 100, CBS_DROPDOWNLIST | |
1025 | |
1026 LTEXT "Output Device", IDC_STATIC, 20, 320, 100, 20 | |
1027 COMBOBOX IDC_COMBO_MIDI_OUT_DEV, 20, 338, 150, 100, CBS_DROPDOWNLIST | |
1028 | |
1029 LTEXT "Input Channel", IDC_STATIC, 200, 275, 100, 20 | |
1030 COMBOBOX IDC_COMBO_MIDI_IN_CHAN, 200, 293, 90, 100, CBS_DROPDOWNLIST | |
1031 | |
1032 LTEXT "Output Channel", IDC_STATIC, 200, 320, 100, 20 | |
1033 COMBOBOX IDC_COMBO_MIDI_OUT_CHAN, 200, 338, 90, 100, CBS_DROPDOWNLIST | |
1034 | |
1035 DEFPUSHBUTTON "OK", IDOK, 192, 383, 50, 20 | |
1036 PUSHBUTTON "Apply", IDAPPLY, 132, 383, 50, 20 | |
1037 PUSHBUTTON "Cancel", IDCANCEL, 252, 383, 50, 20 | |
1038 END | |
1039 SWELL_DEFINE_DIALOG_RESOURCE_END(IDD_DIALOG_PREF) | |
1040 | |
1041 #include "swell-menugen.h" | |
1042 | |
1043 SWELL_DEFINE_MENU_RESOURCE_BEGIN(IDR_MENU1) | |
1044 POPUP "&File" | |
1045 BEGIN | |
1046 // MENUITEM SEPARATOR | |
1047 MENUITEM "Preferences...", ID_PREFERENCES | |
1048 MENUITEM "&Quit", ID_QUIT | |
1049 END | |
1050 POPUP "&Help" | |
1051 BEGIN | |
1052 MENUITEM "&About", ID_ABOUT | |
1053 END | |
1054 SWELL_DEFINE_MENU_RESOURCE_END(IDR_MENU1) | |
1055 | |
1056 #endif |