1 /* 2 * Copyright (c) 2004 Matthijs Hollemans 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 * DEALINGS IN THE SOFTWARE. 21 */ 22 23 #include <Catalog.h> 24 #include <GridLayoutBuilder.h> 25 #include <GroupLayout.h> 26 #include <GroupLayoutBuilder.h> 27 #include <Locale.h> 28 #include <MidiProducer.h> 29 #include <MidiRoster.h> 30 #include <StorageKit.h> 31 #include <SpaceLayoutItem.h> 32 33 #include "MidiPlayerApp.h" 34 #include "MidiPlayerWindow.h" 35 #include "ScopeView.h" 36 #include "SynthBridge.h" 37 38 #define _W(a) (a->Frame().Width()) 39 #define _H(a) (a->Frame().Height()) 40 41 #undef B_TRANSLATE_CONTEXT 42 #define B_TRANSLATE_CONTEXT "Main Window" 43 44 //------------------------------------------------------------------------------ 45 46 MidiPlayerWindow::MidiPlayerWindow() 47 : BWindow(BRect(0, 0, 1, 1), B_TRANSLATE("MidiPlayer"), B_TITLED_WINDOW, 48 B_ASYNCHRONOUS_CONTROLS | B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS) 49 { 50 playing = false; 51 scopeEnabled = true; 52 reverb = B_REVERB_BALLROOM; 53 volume = 75; 54 windowX = -1; 55 windowY = -1; 56 inputId = -1; 57 instrLoaded = false; 58 59 be_synth->SetSamplingRate(44100); 60 61 bridge = new SynthBridge; 62 //bridge->Register(); 63 64 CreateViews(); 65 LoadSettings(); 66 InitControls(); 67 } 68 69 //------------------------------------------------------------------------------ 70 71 MidiPlayerWindow::~MidiPlayerWindow() 72 { 73 StopSynth(); 74 75 //bridge->Unregister(); 76 bridge->Release(); 77 } 78 79 //------------------------------------------------------------------------------ 80 81 bool MidiPlayerWindow::QuitRequested() 82 { 83 be_app->PostMessage(B_QUIT_REQUESTED); 84 return true; 85 } 86 87 //------------------------------------------------------------------------------ 88 89 void MidiPlayerWindow::MessageReceived(BMessage* msg) 90 { 91 switch (msg->what) 92 { 93 case MSG_PLAY_STOP: 94 OnPlayStop(); 95 break; 96 97 case MSG_SHOW_SCOPE: 98 OnShowScope(); 99 break; 100 101 case MSG_INPUT_CHANGED: 102 OnInputChanged(msg); 103 break; 104 105 case MSG_REVERB_NONE: 106 OnReverb(B_REVERB_NONE); 107 break; 108 109 case MSG_REVERB_CLOSET: 110 OnReverb(B_REVERB_CLOSET); 111 break; 112 113 case MSG_REVERB_GARAGE: 114 OnReverb(B_REVERB_GARAGE); 115 break; 116 117 case MSG_REVERB_IGOR: 118 OnReverb(B_REVERB_BALLROOM); 119 break; 120 121 case MSG_REVERB_CAVERN: 122 OnReverb(B_REVERB_CAVERN); 123 break; 124 125 case MSG_REVERB_DUNGEON: 126 OnReverb(B_REVERB_DUNGEON); 127 break; 128 129 case MSG_VOLUME: 130 OnVolume(); 131 break; 132 133 case B_SIMPLE_DATA: 134 OnDrop(msg); 135 break; 136 137 default: 138 super::MessageReceived(msg); 139 break; 140 } 141 } 142 143 //------------------------------------------------------------------------------ 144 145 void MidiPlayerWindow::FrameMoved(BPoint origin) 146 { 147 super::FrameMoved(origin); 148 windowX = Frame().left; 149 windowY = Frame().top; 150 SaveSettings(); 151 } 152 153 //------------------------------------------------------------------------------ 154 155 void MidiPlayerWindow::MenusBeginning() 156 { 157 for (int32 t = inputPopUp->CountItems() - 1; t > 0; --t) 158 { 159 delete inputPopUp->RemoveItem(t); 160 } 161 162 // Note: if the selected endpoint no longer exists, then no endpoint is 163 // marked. However, we won't disconnect it until you choose another one. 164 165 inputOff->SetMarked(inputId == -1); 166 167 int32 id = 0; 168 BMidiEndpoint* endp; 169 while ((endp = BMidiRoster::NextEndpoint(&id)) != NULL) 170 { 171 if (endp->IsProducer()) 172 { 173 BMessage* msg = new BMessage; 174 msg->what = MSG_INPUT_CHANGED; 175 msg->AddInt32("id", id); 176 177 BMenuItem* item = new BMenuItem(endp->Name(), msg); 178 inputPopUp->AddItem(item); 179 item->SetMarked(inputId == id); 180 } 181 182 endp->Release(); 183 } 184 } 185 186 //------------------------------------------------------------------------------ 187 188 void MidiPlayerWindow::CreateInputMenu() 189 { 190 inputPopUp = new BPopUpMenu("inputPopUp"); 191 192 BMessage* msg = new BMessage; 193 msg->what = MSG_INPUT_CHANGED; 194 msg->AddInt32("id", -1); 195 196 inputOff = new BMenuItem(B_TRANSLATE("Off"), msg); 197 198 inputPopUp->AddItem(inputOff); 199 200 inputMenu = new BMenuField(B_TRANSLATE("Live input:"), inputPopUp, NULL); 201 } 202 203 //------------------------------------------------------------------------------ 204 205 void MidiPlayerWindow::CreateReverbMenu() 206 { 207 BPopUpMenu* reverbPopUp = new BPopUpMenu("reverbPopUp"); 208 209 reverbNone = new BMenuItem( 210 B_TRANSLATE("None"), new BMessage(MSG_REVERB_NONE)); 211 212 reverbCloset = new BMenuItem( 213 B_TRANSLATE("Closet"), new BMessage(MSG_REVERB_CLOSET)); 214 215 reverbGarage = new BMenuItem( 216 B_TRANSLATE("Garage"), new BMessage(MSG_REVERB_GARAGE)); 217 218 reverbIgor = new BMenuItem( 219 B_TRANSLATE("Igor's lab"), new BMessage(MSG_REVERB_IGOR)); 220 221 reverbCavern = new BMenuItem( 222 B_TRANSLATE("Cavern"), new BMessage(MSG_REVERB_CAVERN)); 223 224 reverbDungeon = new BMenuItem( 225 B_TRANSLATE("Dungeon"), new BMessage(MSG_REVERB_DUNGEON)); 226 227 reverbPopUp->AddItem(reverbNone); 228 reverbPopUp->AddItem(reverbCloset); 229 reverbPopUp->AddItem(reverbGarage); 230 reverbPopUp->AddItem(reverbIgor); 231 reverbPopUp->AddItem(reverbCavern); 232 reverbPopUp->AddItem(reverbDungeon); 233 234 reverbMenu = new BMenuField(B_TRANSLATE("Reverb:"), reverbPopUp, NULL); 235 } 236 237 //------------------------------------------------------------------------------ 238 239 void MidiPlayerWindow::CreateViews() 240 { 241 // Set up needed views 242 scopeView = new ScopeView; 243 244 showScope = new BCheckBox("showScope", B_TRANSLATE("Scope"), 245 new BMessage(MSG_SHOW_SCOPE)); 246 showScope->SetValue(B_CONTROL_ON); 247 248 CreateInputMenu(); 249 CreateReverbMenu(); 250 251 volumeSlider = new BSlider("volumeSlider", NULL, NULL, 0, 100, 252 B_HORIZONTAL); 253 rgb_color col = { 152, 152, 255 }; 254 volumeSlider->UseFillColor(true, &col); 255 volumeSlider->SetModificationMessage(new BMessage(MSG_VOLUME)); 256 257 playButton = new BButton("playButton", B_TRANSLATE("Play"), 258 new BMessage(MSG_PLAY_STOP)); 259 playButton->SetEnabled(false); 260 261 BBox* divider = new BBox(B_EMPTY_STRING, B_WILL_DRAW | B_FRAME_EVENTS, 262 B_FANCY_BORDER); 263 divider->SetExplicitMaxSize( 264 BSize(B_SIZE_UNLIMITED, 1)); 265 266 BStringView* volumeLabel = new BStringView(NULL, B_TRANSLATE("Volume:")); 267 volumeLabel->SetAlignment(B_ALIGN_LEFT); 268 volumeLabel->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 269 270 // Build the layout 271 SetLayout(new BGroupLayout(B_HORIZONTAL)); 272 273 AddChild(BGroupLayoutBuilder(B_VERTICAL, 10) 274 .Add(scopeView) 275 .Add(BGridLayoutBuilder(10, 10) 276 .Add(BSpaceLayoutItem::CreateGlue(), 0, 0) 277 .Add(showScope, 1, 0) 278 279 .Add(reverbMenu->CreateLabelLayoutItem(), 0, 1) 280 .Add(reverbMenu->CreateMenuBarLayoutItem(), 1, 1) 281 282 .Add(inputMenu->CreateLabelLayoutItem(), 0, 2) 283 .Add(inputMenu->CreateMenuBarLayoutItem(), 1, 2) 284 285 .Add(volumeLabel, 0, 3) 286 .Add(volumeSlider, 1, 3) 287 ) 288 .AddGlue() 289 .Add(divider) 290 .AddGlue() 291 .Add(playButton) 292 .AddGlue() 293 .SetInsets(5, 5, 5, 5) 294 ); 295 } 296 297 //------------------------------------------------------------------------------ 298 299 void MidiPlayerWindow::InitControls() 300 { 301 Lock(); 302 303 showScope->SetValue(scopeEnabled ? B_CONTROL_ON : B_CONTROL_OFF); 304 scopeView->SetEnabled(scopeEnabled); 305 306 inputOff->SetMarked(true); 307 308 reverbNone->SetMarked(reverb == B_REVERB_NONE); 309 reverbCloset->SetMarked(reverb == B_REVERB_CLOSET); 310 reverbGarage->SetMarked(reverb == B_REVERB_GARAGE); 311 reverbIgor->SetMarked(reverb == B_REVERB_BALLROOM); 312 reverbCavern->SetMarked(reverb == B_REVERB_CAVERN); 313 reverbDungeon->SetMarked(reverb == B_REVERB_DUNGEON); 314 be_synth->SetReverb(reverb); 315 316 volumeSlider->SetValue(volume); 317 318 if (windowX != -1 && windowY != -1) 319 { 320 MoveTo(windowX, windowY); 321 } 322 else 323 { 324 CenterOnScreen(); 325 } 326 327 Unlock(); 328 } 329 330 //------------------------------------------------------------------------------ 331 332 void MidiPlayerWindow::LoadSettings() 333 { 334 BFile file(SETTINGS_FILE, B_READ_ONLY); 335 336 if (file.InitCheck() != B_OK) { return; } 337 if (file.Lock() != B_OK) { return; } 338 339 file.ReadAttr("Scope", B_BOOL_TYPE, 0, &scopeEnabled, sizeof(bool)); 340 file.ReadAttr("Reverb", B_INT32_TYPE, 0, &reverb, sizeof(int32)); 341 file.ReadAttr("Volume", B_INT32_TYPE, 0, &volume, sizeof(int32)); 342 file.ReadAttr("WindowX", B_FLOAT_TYPE, 0, &windowX, sizeof(float)); 343 file.ReadAttr("WindowY", B_FLOAT_TYPE, 0, &windowY, sizeof(float)); 344 345 file.Unlock(); 346 } 347 348 //------------------------------------------------------------------------------ 349 350 void MidiPlayerWindow::SaveSettings() 351 { 352 BFile file(SETTINGS_FILE, B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); 353 354 if (file.InitCheck() != B_OK) { return; } 355 if (file.Lock() != B_OK) { return; } 356 357 file.WriteAttr("Scope", B_BOOL_TYPE, 0, &scopeEnabled, sizeof(bool)); 358 file.WriteAttr("Reverb", B_INT32_TYPE, 0, &reverb, sizeof(int32)); 359 file.WriteAttr("Volume", B_INT32_TYPE, 0, &volume, sizeof(int32)); 360 file.WriteAttr("WindowX", B_FLOAT_TYPE, 0, &windowX, sizeof(float)); 361 file.WriteAttr("WindowY", B_FLOAT_TYPE, 0, &windowY, sizeof(float)); 362 363 file.Sync(); 364 file.Unlock(); 365 } 366 367 //------------------------------------------------------------------------------ 368 369 void MidiPlayerWindow::LoadFile(entry_ref* ref) 370 { 371 if (playing) 372 { 373 scopeView->SetPlaying(false); 374 scopeView->Invalidate(); 375 UpdateIfNeeded(); 376 377 StopSynth(); 378 } 379 380 synth.UnloadFile(); 381 382 if (synth.LoadFile(ref) == B_OK) 383 { 384 // Ideally, we would call SetVolume() in InitControls(), 385 // but for some reason that doesn't work: BMidiSynthFile 386 // will use the default volume instead. So we do it here. 387 synth.SetVolume(volume / 100.0f); 388 389 playButton->SetEnabled(true); 390 playButton->SetLabel(B_TRANSLATE("Stop")); 391 scopeView->SetHaveFile(true); 392 scopeView->SetPlaying(true); 393 scopeView->Invalidate(); 394 395 StartSynth(); 396 } 397 else 398 { 399 playButton->SetEnabled(false); 400 playButton->SetLabel(B_TRANSLATE("Play")); 401 scopeView->SetHaveFile(false); 402 scopeView->SetPlaying(false); 403 scopeView->Invalidate(); 404 405 (new BAlert(NULL, 406 B_TRANSLATE("Could not load song"), 407 B_TRANSLATE("OK"), NULL, NULL, 408 B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go(); 409 } 410 } 411 412 //------------------------------------------------------------------------------ 413 414 void MidiPlayerWindow::StartSynth() 415 { 416 synth.Start(); 417 synth.SetFileHook(_StopHook, (int32) this); 418 playing = true; 419 } 420 421 //------------------------------------------------------------------------------ 422 423 void MidiPlayerWindow::StopSynth() 424 { 425 if (!synth.IsFinished()) 426 { 427 synth.Fade(); 428 } 429 430 playing = false; 431 } 432 433 //------------------------------------------------------------------------------ 434 435 void MidiPlayerWindow::_StopHook(int32 arg) 436 { 437 ((MidiPlayerWindow*) arg)->StopHook(); 438 } 439 440 //------------------------------------------------------------------------------ 441 442 void MidiPlayerWindow::StopHook() 443 { 444 Lock(); // we may be called from the synth's thread 445 446 playing = false; 447 448 scopeView->SetPlaying(false); 449 scopeView->Invalidate(); 450 playButton->SetEnabled(true); 451 playButton->SetLabel(B_TRANSLATE("Play")); 452 453 Unlock(); 454 } 455 456 //------------------------------------------------------------------------------ 457 458 void MidiPlayerWindow::OnPlayStop() 459 { 460 if (playing) 461 { 462 playButton->SetEnabled(false); 463 scopeView->SetPlaying(false); 464 scopeView->Invalidate(); 465 UpdateIfNeeded(); 466 467 StopSynth(); 468 } 469 else 470 { 471 playButton->SetLabel(B_TRANSLATE("Stop")); 472 scopeView->SetPlaying(true); 473 scopeView->Invalidate(); 474 475 StartSynth(); 476 } 477 } 478 479 //------------------------------------------------------------------------------ 480 481 void MidiPlayerWindow::OnShowScope() 482 { 483 scopeEnabled = !scopeEnabled; 484 scopeView->SetEnabled(scopeEnabled); 485 scopeView->Invalidate(); 486 SaveSettings(); 487 } 488 489 //------------------------------------------------------------------------------ 490 491 void MidiPlayerWindow::OnInputChanged(BMessage* msg) 492 { 493 int32 newId; 494 if (msg->FindInt32("id", &newId) == B_OK) 495 { 496 BMidiProducer* endp; 497 498 endp = BMidiRoster::FindProducer(inputId); 499 if (endp != NULL) 500 { 501 endp->Disconnect(bridge); 502 endp->Release(); 503 } 504 505 inputId = newId; 506 507 endp = BMidiRoster::FindProducer(inputId); 508 if (endp != NULL) 509 { 510 if (!instrLoaded) 511 { 512 scopeView->SetLoading(true); 513 scopeView->Invalidate(); 514 UpdateIfNeeded(); 515 516 bridge->Init(B_BIG_SYNTH); 517 instrLoaded = true; 518 519 scopeView->SetLoading(false); 520 scopeView->Invalidate(); 521 } 522 523 endp->Connect(bridge); 524 endp->Release(); 525 526 scopeView->SetLiveInput(true); 527 scopeView->Invalidate(); 528 } 529 else 530 { 531 scopeView->SetLiveInput(false); 532 scopeView->Invalidate(); 533 } 534 } 535 } 536 537 //------------------------------------------------------------------------------ 538 539 void MidiPlayerWindow::OnReverb(reverb_mode mode) 540 { 541 reverb = mode; 542 be_synth->SetReverb(reverb); 543 SaveSettings(); 544 } 545 546 //------------------------------------------------------------------------------ 547 548 void MidiPlayerWindow::OnVolume() 549 { 550 volume = volumeSlider->Value(); 551 synth.SetVolume(volume / 100.0f); 552 SaveSettings(); 553 } 554 555 //------------------------------------------------------------------------------ 556 557 void MidiPlayerWindow::OnDrop(BMessage* msg) 558 { 559 entry_ref ref; 560 if (msg->FindRef("refs", &ref) == B_OK) 561 { 562 LoadFile(&ref); 563 } 564 } 565 566 //------------------------------------------------------------------------------ 567