annotate app_wrapper/app_main.cpp @ 1:2ca5d7440b5c tip

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