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