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("showScope", "Scope", 240 new BMessage(MSG_SHOW_SCOPE)); 241 showScope->SetValue(B_CONTROL_ON); 242 243 CreateInputMenu(); 244 CreateReverbMenu(); 245 246 volumeSlider = new BSlider("volumeSlider", NULL, NULL, 0, 100, 247 B_HORIZONTAL); 248 rgb_color col = { 152, 152, 255 }; 249 volumeSlider->UseFillColor(true, &col); 250 volumeSlider->SetModificationMessage(new BMessage(MSG_VOLUME)); 251 252 playButton = new BButton("playButton", "Play", new BMessage(MSG_PLAY_STOP)); 253 playButton->SetEnabled(false); 254 255 BBox* divider = new BBox(B_EMPTY_STRING, B_WILL_DRAW | B_FRAME_EVENTS, 256 B_FANCY_BORDER); 257 divider->SetExplicitMaxSize( 258 BSize(B_SIZE_UNLIMITED, 1)); 259 260 BStringView* volumeLabel = new BStringView(NULL, "Volume:"); 261 volumeLabel->SetAlignment(B_ALIGN_LEFT); 262 volumeLabel->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 263 264 // Build the layout 265 SetLayout(new BGroupLayout(B_HORIZONTAL)); 266 267 AddChild(BGroupLayoutBuilder(B_VERTICAL, 10) 268 .Add(scopeView) 269 .Add(BGridLayoutBuilder(10, 10) 270 .Add(BSpaceLayoutItem::CreateGlue(), 0, 0) 271 .Add(showScope, 1, 0) 272 273 .Add(reverbMenu->CreateLabelLayoutItem(), 0, 1) 274 .Add(reverbMenu->CreateMenuBarLayoutItem(), 1, 1) 275 276 .Add(inputMenu->CreateLabelLayoutItem(), 0, 2) 277 .Add(inputMenu->CreateMenuBarLayoutItem(), 1, 2) 278 279 .Add(volumeLabel, 0, 3) 280 .Add(volumeSlider, 1, 3) 281 ) 282 .AddGlue() 283 .Add(divider) 284 .AddGlue() 285 .Add(playButton) 286 .AddGlue() 287 .SetInsets(5, 5, 5, 5) 288 ); 289 } 290 291 //------------------------------------------------------------------------------ 292 293 void MidiPlayerWindow::CenterOnScreen() 294 { 295 BRect screenRect = BScreen(this).Frame(); 296 BRect windowRect = Frame(); 297 298 MoveTo( 299 (screenRect.Width() - windowRect.Width()) / 2, 300 (screenRect.Height() - windowRect.Height()) / 2); 301 } 302 303 //------------------------------------------------------------------------------ 304 305 void MidiPlayerWindow::InitControls() 306 { 307 Lock(); 308 309 showScope->SetValue(scopeEnabled ? B_CONTROL_ON : B_CONTROL_OFF); 310 scopeView->SetEnabled(scopeEnabled); 311 312 inputOff->SetMarked(true); 313 314 reverbNone->SetMarked(reverb == B_REVERB_NONE); 315 reverbCloset->SetMarked(reverb == B_REVERB_CLOSET); 316 reverbGarage->SetMarked(reverb == B_REVERB_GARAGE); 317 reverbIgor->SetMarked(reverb == B_REVERB_BALLROOM); 318 reverbCavern->SetMarked(reverb == B_REVERB_CAVERN); 319 reverbDungeon->SetMarked(reverb == B_REVERB_DUNGEON); 320 be_synth->SetReverb(reverb); 321 322 volumeSlider->SetValue(volume); 323 324 if (windowX != -1 && windowY != -1) 325 { 326 MoveTo(windowX, windowY); 327 } 328 else 329 { 330 CenterOnScreen(); 331 } 332 333 Unlock(); 334 } 335 336 //------------------------------------------------------------------------------ 337 338 void MidiPlayerWindow::LoadSettings() 339 { 340 BFile file(SETTINGS_FILE, B_READ_ONLY); 341 342 if (file.InitCheck() != B_OK) { return; } 343 if (file.Lock() != B_OK) { return; } 344 345 file.ReadAttr("Scope", B_BOOL_TYPE, 0, &scopeEnabled, sizeof(bool)); 346 file.ReadAttr("Reverb", B_INT32_TYPE, 0, &reverb, sizeof(int32)); 347 file.ReadAttr("Volume", B_INT32_TYPE, 0, &volume, sizeof(int32)); 348 file.ReadAttr("WindowX", B_FLOAT_TYPE, 0, &windowX, sizeof(float)); 349 file.ReadAttr("WindowY", B_FLOAT_TYPE, 0, &windowY, sizeof(float)); 350 351 file.Unlock(); 352 } 353 354 //------------------------------------------------------------------------------ 355 356 void MidiPlayerWindow::SaveSettings() 357 { 358 BFile file(SETTINGS_FILE, B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); 359 360 if (file.InitCheck() != B_OK) { return; } 361 if (file.Lock() != B_OK) { return; } 362 363 file.WriteAttr("Scope", B_BOOL_TYPE, 0, &scopeEnabled, sizeof(bool)); 364 file.WriteAttr("Reverb", B_INT32_TYPE, 0, &reverb, sizeof(int32)); 365 file.WriteAttr("Volume", B_INT32_TYPE, 0, &volume, sizeof(int32)); 366 file.WriteAttr("WindowX", B_FLOAT_TYPE, 0, &windowX, sizeof(float)); 367 file.WriteAttr("WindowY", B_FLOAT_TYPE, 0, &windowY, sizeof(float)); 368 369 file.Sync(); 370 file.Unlock(); 371 } 372 373 //------------------------------------------------------------------------------ 374 375 void MidiPlayerWindow::LoadFile(entry_ref* ref) 376 { 377 if (playing) 378 { 379 scopeView->SetPlaying(false); 380 scopeView->Invalidate(); 381 UpdateIfNeeded(); 382 383 StopSynth(); 384 } 385 386 synth.UnloadFile(); 387 388 if (synth.LoadFile(ref) == B_OK) 389 { 390 // Ideally, we would call SetVolume() in InitControls(), 391 // but for some reason that doesn't work: BMidiSynthFile 392 // will use the default volume instead. So we do it here. 393 synth.SetVolume(volume / 100.0f); 394 395 playButton->SetEnabled(true); 396 playButton->SetLabel("Stop"); 397 scopeView->SetHaveFile(true); 398 scopeView->SetPlaying(true); 399 scopeView->Invalidate(); 400 401 StartSynth(); 402 } 403 else 404 { 405 playButton->SetEnabled(false); 406 playButton->SetLabel("Play"); 407 scopeView->SetHaveFile(false); 408 scopeView->SetPlaying(false); 409 scopeView->Invalidate(); 410 411 (new BAlert( 412 NULL, "Could not load song", "Okay", NULL, NULL, 413 B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go(); 414 } 415 } 416 417 //------------------------------------------------------------------------------ 418 419 void MidiPlayerWindow::StartSynth() 420 { 421 synth.Start(); 422 synth.SetFileHook(_StopHook, (int32) this); 423 playing = true; 424 } 425 426 //------------------------------------------------------------------------------ 427 428 void MidiPlayerWindow::StopSynth() 429 { 430 if (!synth.IsFinished()) 431 { 432 synth.Fade(); 433 } 434 435 playing = false; 436 } 437 438 //------------------------------------------------------------------------------ 439 440 void MidiPlayerWindow::_StopHook(int32 arg) 441 { 442 ((MidiPlayerWindow*) arg)->StopHook(); 443 } 444 445 //------------------------------------------------------------------------------ 446 447 void MidiPlayerWindow::StopHook() 448 { 449 Lock(); // we may be called from the synth's thread 450 451 playing = false; 452 453 scopeView->SetPlaying(false); 454 scopeView->Invalidate(); 455 playButton->SetEnabled(true); 456 playButton->SetLabel("Play"); 457 458 Unlock(); 459 } 460 461 //------------------------------------------------------------------------------ 462 463 void MidiPlayerWindow::OnPlayStop() 464 { 465 if (playing) 466 { 467 playButton->SetEnabled(false); 468 scopeView->SetPlaying(false); 469 scopeView->Invalidate(); 470 UpdateIfNeeded(); 471 472 StopSynth(); 473 } 474 else 475 { 476 playButton->SetLabel("Stop"); 477 scopeView->SetPlaying(true); 478 scopeView->Invalidate(); 479 480 StartSynth(); 481 } 482 } 483 484 //------------------------------------------------------------------------------ 485 486 void MidiPlayerWindow::OnShowScope() 487 { 488 scopeEnabled = !scopeEnabled; 489 scopeView->SetEnabled(scopeEnabled); 490 scopeView->Invalidate(); 491 SaveSettings(); 492 } 493 494 //------------------------------------------------------------------------------ 495 496 void MidiPlayerWindow::OnInputChanged(BMessage* msg) 497 { 498 int32 newId; 499 if (msg->FindInt32("id", &newId) == B_OK) 500 { 501 BMidiProducer* endp; 502 503 endp = BMidiRoster::FindProducer(inputId); 504 if (endp != NULL) 505 { 506 endp->Disconnect(bridge); 507 endp->Release(); 508 } 509 510 inputId = newId; 511 512 endp = BMidiRoster::FindProducer(inputId); 513 if (endp != NULL) 514 { 515 if (!instrLoaded) 516 { 517 scopeView->SetLoading(true); 518 scopeView->Invalidate(); 519 UpdateIfNeeded(); 520 521 bridge->Init(B_BIG_SYNTH); 522 instrLoaded = true; 523 524 scopeView->SetLoading(false); 525 scopeView->Invalidate(); 526 } 527 528 endp->Connect(bridge); 529 endp->Release(); 530 531 scopeView->SetLiveInput(true); 532 scopeView->Invalidate(); 533 } 534 else 535 { 536 scopeView->SetLiveInput(false); 537 scopeView->Invalidate(); 538 } 539 } 540 } 541 542 //------------------------------------------------------------------------------ 543 544 void MidiPlayerWindow::OnReverb(reverb_mode mode) 545 { 546 reverb = mode; 547 be_synth->SetReverb(reverb); 548 SaveSettings(); 549 } 550 551 //------------------------------------------------------------------------------ 552 553 void MidiPlayerWindow::OnVolume() 554 { 555 volume = volumeSlider->Value(); 556 synth.SetVolume(volume / 100.0f); 557 SaveSettings(); 558 } 559 560 //------------------------------------------------------------------------------ 561 562 void MidiPlayerWindow::OnDrop(BMessage* msg) 563 { 564 entry_ref ref; 565 if (msg->FindRef("refs", &ref) == B_OK) 566 { 567 LoadFile(&ref); 568 } 569 } 570 571 //------------------------------------------------------------------------------ 572