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 <MidiProducer.h> 24 #include <MidiRoster.h> 25 #include <StorageKit.h> 26 27 #include "MidiPlayerApp.h" 28 #include "MidiPlayerWindow.h" 29 #include "ScopeView.h" 30 #include "SynthBridge.h" 31 32 #define _W(a) (a->Frame().Width()) 33 #define _H(a) (a->Frame().Height()) 34 35 //------------------------------------------------------------------------------ 36 37 MidiPlayerWindow::MidiPlayerWindow() 38 : BWindow(BRect(0, 0, 1, 1), "MidiPlayer", B_TITLED_WINDOW, 39 B_ASYNCHRONOUS_CONTROLS | B_NOT_RESIZABLE | B_NOT_ZOOMABLE) 40 { 41 playing = false; 42 scopeEnabled = true; 43 reverb = B_REVERB_BALLROOM; 44 volume = 75; 45 windowX = -1; 46 windowY = -1; 47 inputId = -1; 48 instrLoaded = false; 49 50 be_synth->SetSamplingRate(44100); 51 52 bridge = new SynthBridge; 53 //bridge->Register(); 54 55 CreateViews(); 56 LoadSettings(); 57 InitControls(); 58 } 59 60 //------------------------------------------------------------------------------ 61 62 MidiPlayerWindow::~MidiPlayerWindow() 63 { 64 StopSynth(); 65 66 //bridge->Unregister(); 67 bridge->Release(); 68 } 69 70 //------------------------------------------------------------------------------ 71 72 bool MidiPlayerWindow::QuitRequested() 73 { 74 be_app->PostMessage(B_QUIT_REQUESTED); 75 return true; 76 } 77 78 //------------------------------------------------------------------------------ 79 80 void MidiPlayerWindow::MessageReceived(BMessage* msg) 81 { 82 switch (msg->what) 83 { 84 case MSG_PLAY_STOP: 85 OnPlayStop(); 86 break; 87 88 case MSG_SHOW_SCOPE: 89 OnShowScope(); 90 break; 91 92 case MSG_INPUT_CHANGED: 93 OnInputChanged(msg); 94 break; 95 96 case MSG_REVERB_NONE: 97 OnReverb(B_REVERB_NONE); 98 break; 99 100 case MSG_REVERB_CLOSET: 101 OnReverb(B_REVERB_CLOSET); 102 break; 103 104 case MSG_REVERB_GARAGE: 105 OnReverb(B_REVERB_GARAGE); 106 break; 107 108 case MSG_REVERB_IGOR: 109 OnReverb(B_REVERB_BALLROOM); 110 break; 111 112 case MSG_REVERB_CAVERN: 113 OnReverb(B_REVERB_CAVERN); 114 break; 115 116 case MSG_REVERB_DUNGEON: 117 OnReverb(B_REVERB_DUNGEON); 118 break; 119 120 case MSG_VOLUME: 121 OnVolume(); 122 break; 123 124 case B_SIMPLE_DATA: 125 OnDrop(msg); 126 break; 127 128 default: 129 super::MessageReceived(msg); 130 break; 131 } 132 } 133 134 //------------------------------------------------------------------------------ 135 136 void MidiPlayerWindow::FrameMoved(BPoint origin) 137 { 138 super::FrameMoved(origin); 139 windowX = Frame().left; 140 windowY = Frame().top; 141 SaveSettings(); 142 } 143 144 //------------------------------------------------------------------------------ 145 146 void MidiPlayerWindow::MenusBeginning() 147 { 148 for (int32 t = inputPopUp->CountItems() - 1; t > 0; --t) 149 { 150 delete inputPopUp->RemoveItem(t); 151 } 152 153 // Note: if the selected endpoint no longer exists, then no endpoint is 154 // marked. However, we won't disconnect it until you choose another one. 155 156 inputOff->SetMarked(inputId == -1); 157 158 int32 id = 0; 159 BMidiEndpoint* endp; 160 while ((endp = BMidiRoster::NextEndpoint(&id)) != NULL) 161 { 162 if (endp->IsProducer()) 163 { 164 BMessage* msg = new BMessage; 165 msg->what = MSG_INPUT_CHANGED; 166 msg->AddInt32("id", id); 167 168 BMenuItem* item = new BMenuItem(endp->Name(), msg); 169 inputPopUp->AddItem(item); 170 item->SetMarked(inputId == id); 171 } 172 173 endp->Release(); 174 } 175 } 176 177 //------------------------------------------------------------------------------ 178 179 void MidiPlayerWindow::CreateInputMenu() 180 { 181 inputPopUp = new BPopUpMenu("inputPopUp"); 182 183 BMessage* msg = new BMessage; 184 msg->what = MSG_INPUT_CHANGED; 185 msg->AddInt32("id", -1); 186 187 inputOff = new BMenuItem("Off", msg); 188 189 inputPopUp->AddItem(inputOff); 190 191 inputMenu = new BMenuField( 192 BRect(0, 0, 128, 17), "inputMenu", "Live Input:", inputPopUp, 193 B_FOLLOW_LEFT | B_FOLLOW_TOP); 194 195 inputMenu->SetDivider(55); 196 inputMenu->ResizeToPreferred(); 197 } 198 199 //------------------------------------------------------------------------------ 200 201 void MidiPlayerWindow::CreateReverbMenu() 202 { 203 BPopUpMenu* reverbPopUp = new BPopUpMenu("reverbPopUp"); 204 205 reverbNone = new BMenuItem( 206 "None", new BMessage(MSG_REVERB_NONE)); 207 208 reverbCloset = new BMenuItem( 209 "Closet", new BMessage(MSG_REVERB_CLOSET)); 210 211 reverbGarage = new BMenuItem( 212 "Garage", new BMessage(MSG_REVERB_GARAGE)); 213 214 reverbIgor = new BMenuItem( 215 "Igor's Lab", new BMessage(MSG_REVERB_IGOR)); 216 217 reverbCavern = new BMenuItem( 218 "Cavern", new BMessage(MSG_REVERB_CAVERN)); 219 220 reverbDungeon = new BMenuItem( 221 "Dungeon", new BMessage(MSG_REVERB_DUNGEON)); 222 223 reverbPopUp->AddItem(reverbNone); 224 reverbPopUp->AddItem(reverbCloset); 225 reverbPopUp->AddItem(reverbGarage); 226 reverbPopUp->AddItem(reverbIgor); 227 reverbPopUp->AddItem(reverbCavern); 228 reverbPopUp->AddItem(reverbDungeon); 229 230 reverbMenu = new BMenuField( 231 BRect(0, 0, 128, 17), "reverbMenu", "Reverb:", reverbPopUp, 232 B_FOLLOW_LEFT | B_FOLLOW_TOP); 233 234 reverbMenu->SetDivider(55); 235 reverbMenu->ResizeToPreferred(); 236 } 237 238 //------------------------------------------------------------------------------ 239 240 void MidiPlayerWindow::CreateViews() 241 { 242 scopeView = new ScopeView; 243 244 showScope = new BCheckBox( 245 BRect(0, 0, 1, 1), "showScope", "Scope", 246 new BMessage(MSG_SHOW_SCOPE), B_FOLLOW_LEFT); 247 248 showScope->SetValue(B_CONTROL_ON); 249 showScope->ResizeToPreferred(); 250 251 CreateInputMenu(); 252 CreateReverbMenu(); 253 254 volumeSlider = new BSlider( 255 BRect(0, 0, 1, 1), "volumeSlider", NULL, NULL, 256 0, 100, B_TRIANGLE_THUMB); 257 258 rgb_color col = { 152, 152, 255 }; 259 volumeSlider->UseFillColor(true, &col); 260 volumeSlider->SetModificationMessage(new BMessage(MSG_VOLUME)); 261 volumeSlider->ResizeToPreferred(); 262 volumeSlider->ResizeTo(_W(scopeView) - 42, _H(volumeSlider)); 263 264 playButton = new BButton( 265 BRect(0, 1, 80, 1), "playButton", "Play", new BMessage(MSG_PLAY_STOP), 266 B_FOLLOW_RIGHT); 267 268 //playButton->MakeDefault(true); 269 playButton->ResizeToPreferred(); 270 playButton->SetEnabled(false); 271 272 BBox* background = new BBox( 273 BRect(0, 0, 1, 1), B_EMPTY_STRING, B_FOLLOW_ALL_SIDES, 274 B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE_JUMP, 275 B_PLAIN_BORDER); 276 277 BBox* divider = new BBox( 278 BRect(0, 0, 1, 1), B_EMPTY_STRING, B_FOLLOW_ALL_SIDES, 279 B_WILL_DRAW | B_FRAME_EVENTS, B_FANCY_BORDER); 280 281 divider->ResizeTo(_W(scopeView), 1); 282 283 BStringView* volumeLabel = new BStringView( 284 BRect(0, 0, 1, 1), NULL, "Volume:"); 285 286 volumeLabel->ResizeToPreferred(); 287 288 float width = 8 + _W(scopeView) + 8; 289 290 float height = 291 8 + _H(scopeView) 292 + 8 + _H(showScope) 293 + 4 + _H(inputMenu) 294 + _H(reverbMenu) 295 + 2 + _H(volumeSlider) 296 + 10 + _H(divider) 297 + 6 + _H(playButton) 298 + 16; 299 300 ResizeTo(width, height); 301 302 AddChild(background); 303 background->ResizeTo(width, height); 304 background->AddChild(scopeView); 305 background->AddChild(showScope); 306 background->AddChild(reverbMenu); 307 background->AddChild(inputMenu); 308 background->AddChild(volumeLabel); 309 background->AddChild(volumeSlider); 310 background->AddChild(divider); 311 background->AddChild(playButton); 312 313 float y = 8; 314 scopeView->MoveTo(8, y); 315 316 y += _H(scopeView) + 8; 317 showScope->MoveTo(8 + 55, y); 318 319 y += _H(showScope) + 4; 320 inputMenu->MoveTo(8, y); 321 322 y += _H(inputMenu); 323 reverbMenu->MoveTo(8, y); 324 325 y += _H(reverbMenu) + 2; 326 volumeLabel->MoveTo(8, y); 327 volumeSlider->MoveTo(8 + 49, y); 328 329 y += _H(volumeSlider) + 10; 330 divider->MoveTo(8, y); 331 332 y += _H(divider) + 6; 333 playButton->MoveTo((width - _W(playButton)) / 2, y); 334 } 335 336 //------------------------------------------------------------------------------ 337 338 void MidiPlayerWindow::CenterOnScreen() 339 { 340 BRect screenRect = BScreen(this).Frame(); 341 BRect windowRect = Frame(); 342 343 MoveTo( 344 (screenRect.Width() - windowRect.Width()) / 2, 345 (screenRect.Height() - windowRect.Height()) / 2); 346 } 347 348 //------------------------------------------------------------------------------ 349 350 void MidiPlayerWindow::InitControls() 351 { 352 Lock(); 353 354 showScope->SetValue(scopeEnabled ? B_CONTROL_ON : B_CONTROL_OFF); 355 scopeView->SetEnabled(scopeEnabled); 356 357 inputOff->SetMarked(true); 358 359 reverbNone->SetMarked(reverb == B_REVERB_NONE); 360 reverbCloset->SetMarked(reverb == B_REVERB_CLOSET); 361 reverbGarage->SetMarked(reverb == B_REVERB_GARAGE); 362 reverbIgor->SetMarked(reverb == B_REVERB_BALLROOM); 363 reverbCavern->SetMarked(reverb == B_REVERB_CAVERN); 364 reverbDungeon->SetMarked(reverb == B_REVERB_DUNGEON); 365 be_synth->SetReverb(reverb); 366 367 volumeSlider->SetValue(volume); 368 369 if (windowX != -1 && windowY != -1) 370 { 371 MoveTo(windowX, windowY); 372 } 373 else 374 { 375 CenterOnScreen(); 376 } 377 378 Unlock(); 379 } 380 381 //------------------------------------------------------------------------------ 382 383 void MidiPlayerWindow::LoadSettings() 384 { 385 BFile file(SETTINGS_FILE, B_READ_ONLY); 386 387 if (file.InitCheck() != B_OK) { return; } 388 if (file.Lock() != B_OK) { return; } 389 390 file.ReadAttr("Scope", B_BOOL_TYPE, 0, &scopeEnabled, sizeof(bool)); 391 file.ReadAttr("Reverb", B_INT32_TYPE, 0, &reverb, sizeof(int32)); 392 file.ReadAttr("Volume", B_INT32_TYPE, 0, &volume, sizeof(int32)); 393 file.ReadAttr("WindowX", B_FLOAT_TYPE, 0, &windowX, sizeof(float)); 394 file.ReadAttr("WindowY", B_FLOAT_TYPE, 0, &windowY, sizeof(float)); 395 396 file.Unlock(); 397 } 398 399 //------------------------------------------------------------------------------ 400 401 void MidiPlayerWindow::SaveSettings() 402 { 403 BFile file(SETTINGS_FILE, B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); 404 405 if (file.InitCheck() != B_OK) { return; } 406 if (file.Lock() != B_OK) { return; } 407 408 file.WriteAttr("Scope", B_BOOL_TYPE, 0, &scopeEnabled, sizeof(bool)); 409 file.WriteAttr("Reverb", B_INT32_TYPE, 0, &reverb, sizeof(int32)); 410 file.WriteAttr("Volume", B_INT32_TYPE, 0, &volume, sizeof(int32)); 411 file.WriteAttr("WindowX", B_FLOAT_TYPE, 0, &windowX, sizeof(float)); 412 file.WriteAttr("WindowY", B_FLOAT_TYPE, 0, &windowY, sizeof(float)); 413 414 file.Sync(); 415 file.Unlock(); 416 } 417 418 //------------------------------------------------------------------------------ 419 420 void MidiPlayerWindow::LoadFile(entry_ref* ref) 421 { 422 if (playing) 423 { 424 scopeView->SetPlaying(false); 425 scopeView->Invalidate(); 426 UpdateIfNeeded(); 427 428 StopSynth(); 429 } 430 431 synth.UnloadFile(); 432 433 if (synth.LoadFile(ref) == B_OK) 434 { 435 // Ideally, we would call SetVolume() in InitControls(), 436 // but for some reason that doesn't work: BMidiSynthFile 437 // will use the default volume instead. So we do it here. 438 synth.SetVolume(volume / 100.0f); 439 440 playButton->SetEnabled(true); 441 playButton->SetLabel("Stop"); 442 scopeView->SetHaveFile(true); 443 scopeView->SetPlaying(true); 444 scopeView->Invalidate(); 445 446 StartSynth(); 447 } 448 else 449 { 450 playButton->SetEnabled(false); 451 playButton->SetLabel("Play"); 452 scopeView->SetHaveFile(false); 453 scopeView->SetPlaying(false); 454 scopeView->Invalidate(); 455 456 (new BAlert( 457 NULL, "Could not load song", "Okay", NULL, NULL, 458 B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go(); 459 } 460 } 461 462 //------------------------------------------------------------------------------ 463 464 void MidiPlayerWindow::StartSynth() 465 { 466 synth.Start(); 467 synth.SetFileHook(_StopHook, (int32) this); 468 playing = true; 469 } 470 471 //------------------------------------------------------------------------------ 472 473 void MidiPlayerWindow::StopSynth() 474 { 475 if (!synth.IsFinished()) 476 { 477 synth.Fade(); 478 } 479 480 playing = false; 481 } 482 483 //------------------------------------------------------------------------------ 484 485 void MidiPlayerWindow::_StopHook(int32 arg) 486 { 487 ((MidiPlayerWindow*) arg)->StopHook(); 488 } 489 490 //------------------------------------------------------------------------------ 491 492 void MidiPlayerWindow::StopHook() 493 { 494 Lock(); // we may be called from the synth's thread 495 496 playing = false; 497 498 scopeView->SetPlaying(false); 499 scopeView->Invalidate(); 500 playButton->SetEnabled(true); 501 playButton->SetLabel("Play"); 502 503 Unlock(); 504 } 505 506 //------------------------------------------------------------------------------ 507 508 void MidiPlayerWindow::OnPlayStop() 509 { 510 if (playing) 511 { 512 playButton->SetEnabled(false); 513 scopeView->SetPlaying(false); 514 scopeView->Invalidate(); 515 UpdateIfNeeded(); 516 517 StopSynth(); 518 } 519 else 520 { 521 playButton->SetLabel("Stop"); 522 scopeView->SetPlaying(true); 523 scopeView->Invalidate(); 524 525 StartSynth(); 526 } 527 } 528 529 //------------------------------------------------------------------------------ 530 531 void MidiPlayerWindow::OnShowScope() 532 { 533 scopeEnabled = !scopeEnabled; 534 scopeView->SetEnabled(scopeEnabled); 535 scopeView->Invalidate(); 536 SaveSettings(); 537 } 538 539 //------------------------------------------------------------------------------ 540 541 void MidiPlayerWindow::OnInputChanged(BMessage* msg) 542 { 543 int32 newId; 544 if (msg->FindInt32("id", &newId) == B_OK) 545 { 546 BMidiProducer* endp; 547 548 endp = BMidiRoster::FindProducer(inputId); 549 if (endp != NULL) 550 { 551 endp->Disconnect(bridge); 552 endp->Release(); 553 } 554 555 inputId = newId; 556 557 endp = BMidiRoster::FindProducer(inputId); 558 if (endp != NULL) 559 { 560 if (!instrLoaded) 561 { 562 scopeView->SetLoading(true); 563 scopeView->Invalidate(); 564 UpdateIfNeeded(); 565 566 bridge->Init(B_BIG_SYNTH); 567 instrLoaded = true; 568 569 scopeView->SetLoading(false); 570 scopeView->Invalidate(); 571 } 572 573 endp->Connect(bridge); 574 endp->Release(); 575 576 scopeView->SetLiveInput(true); 577 scopeView->Invalidate(); 578 } 579 else 580 { 581 scopeView->SetLiveInput(false); 582 scopeView->Invalidate(); 583 } 584 } 585 } 586 587 //------------------------------------------------------------------------------ 588 589 void MidiPlayerWindow::OnReverb(reverb_mode mode) 590 { 591 reverb = mode; 592 be_synth->SetReverb(reverb); 593 SaveSettings(); 594 } 595 596 //------------------------------------------------------------------------------ 597 598 void MidiPlayerWindow::OnVolume() 599 { 600 volume = volumeSlider->Value(); 601 synth.SetVolume(volume / 100.0f); 602 SaveSettings(); 603 } 604 605 //------------------------------------------------------------------------------ 606 607 void MidiPlayerWindow::OnDrop(BMessage* msg) 608 { 609 entry_ref ref; 610 if (msg->FindRef("refs", &ref) == B_OK) 611 { 612 LoadFile(&ref); 613 } 614 } 615 616 //------------------------------------------------------------------------------ 617