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::InitControls() 294 { 295 Lock(); 296 297 showScope->SetValue(scopeEnabled ? B_CONTROL_ON : B_CONTROL_OFF); 298 scopeView->SetEnabled(scopeEnabled); 299 300 inputOff->SetMarked(true); 301 302 reverbNone->SetMarked(reverb == B_REVERB_NONE); 303 reverbCloset->SetMarked(reverb == B_REVERB_CLOSET); 304 reverbGarage->SetMarked(reverb == B_REVERB_GARAGE); 305 reverbIgor->SetMarked(reverb == B_REVERB_BALLROOM); 306 reverbCavern->SetMarked(reverb == B_REVERB_CAVERN); 307 reverbDungeon->SetMarked(reverb == B_REVERB_DUNGEON); 308 be_synth->SetReverb(reverb); 309 310 volumeSlider->SetValue(volume); 311 312 if (windowX != -1 && windowY != -1) 313 { 314 MoveTo(windowX, windowY); 315 } 316 else 317 { 318 CenterOnScreen(); 319 } 320 321 Unlock(); 322 } 323 324 //------------------------------------------------------------------------------ 325 326 void MidiPlayerWindow::LoadSettings() 327 { 328 BFile file(SETTINGS_FILE, B_READ_ONLY); 329 330 if (file.InitCheck() != B_OK) { return; } 331 if (file.Lock() != B_OK) { return; } 332 333 file.ReadAttr("Scope", B_BOOL_TYPE, 0, &scopeEnabled, sizeof(bool)); 334 file.ReadAttr("Reverb", B_INT32_TYPE, 0, &reverb, sizeof(int32)); 335 file.ReadAttr("Volume", B_INT32_TYPE, 0, &volume, sizeof(int32)); 336 file.ReadAttr("WindowX", B_FLOAT_TYPE, 0, &windowX, sizeof(float)); 337 file.ReadAttr("WindowY", B_FLOAT_TYPE, 0, &windowY, sizeof(float)); 338 339 file.Unlock(); 340 } 341 342 //------------------------------------------------------------------------------ 343 344 void MidiPlayerWindow::SaveSettings() 345 { 346 BFile file(SETTINGS_FILE, B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); 347 348 if (file.InitCheck() != B_OK) { return; } 349 if (file.Lock() != B_OK) { return; } 350 351 file.WriteAttr("Scope", B_BOOL_TYPE, 0, &scopeEnabled, sizeof(bool)); 352 file.WriteAttr("Reverb", B_INT32_TYPE, 0, &reverb, sizeof(int32)); 353 file.WriteAttr("Volume", B_INT32_TYPE, 0, &volume, sizeof(int32)); 354 file.WriteAttr("WindowX", B_FLOAT_TYPE, 0, &windowX, sizeof(float)); 355 file.WriteAttr("WindowY", B_FLOAT_TYPE, 0, &windowY, sizeof(float)); 356 357 file.Sync(); 358 file.Unlock(); 359 } 360 361 //------------------------------------------------------------------------------ 362 363 void MidiPlayerWindow::LoadFile(entry_ref* ref) 364 { 365 if (playing) 366 { 367 scopeView->SetPlaying(false); 368 scopeView->Invalidate(); 369 UpdateIfNeeded(); 370 371 StopSynth(); 372 } 373 374 synth.UnloadFile(); 375 376 if (synth.LoadFile(ref) == B_OK) 377 { 378 // Ideally, we would call SetVolume() in InitControls(), 379 // but for some reason that doesn't work: BMidiSynthFile 380 // will use the default volume instead. So we do it here. 381 synth.SetVolume(volume / 100.0f); 382 383 playButton->SetEnabled(true); 384 playButton->SetLabel("Stop"); 385 scopeView->SetHaveFile(true); 386 scopeView->SetPlaying(true); 387 scopeView->Invalidate(); 388 389 StartSynth(); 390 } 391 else 392 { 393 playButton->SetEnabled(false); 394 playButton->SetLabel("Play"); 395 scopeView->SetHaveFile(false); 396 scopeView->SetPlaying(false); 397 scopeView->Invalidate(); 398 399 (new BAlert( 400 NULL, "Could not load song", "OK", NULL, NULL, 401 B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go(); 402 } 403 } 404 405 //------------------------------------------------------------------------------ 406 407 void MidiPlayerWindow::StartSynth() 408 { 409 synth.Start(); 410 synth.SetFileHook(_StopHook, (int32) this); 411 playing = true; 412 } 413 414 //------------------------------------------------------------------------------ 415 416 void MidiPlayerWindow::StopSynth() 417 { 418 if (!synth.IsFinished()) 419 { 420 synth.Fade(); 421 } 422 423 playing = false; 424 } 425 426 //------------------------------------------------------------------------------ 427 428 void MidiPlayerWindow::_StopHook(int32 arg) 429 { 430 ((MidiPlayerWindow*) arg)->StopHook(); 431 } 432 433 //------------------------------------------------------------------------------ 434 435 void MidiPlayerWindow::StopHook() 436 { 437 Lock(); // we may be called from the synth's thread 438 439 playing = false; 440 441 scopeView->SetPlaying(false); 442 scopeView->Invalidate(); 443 playButton->SetEnabled(true); 444 playButton->SetLabel("Play"); 445 446 Unlock(); 447 } 448 449 //------------------------------------------------------------------------------ 450 451 void MidiPlayerWindow::OnPlayStop() 452 { 453 if (playing) 454 { 455 playButton->SetEnabled(false); 456 scopeView->SetPlaying(false); 457 scopeView->Invalidate(); 458 UpdateIfNeeded(); 459 460 StopSynth(); 461 } 462 else 463 { 464 playButton->SetLabel("Stop"); 465 scopeView->SetPlaying(true); 466 scopeView->Invalidate(); 467 468 StartSynth(); 469 } 470 } 471 472 //------------------------------------------------------------------------------ 473 474 void MidiPlayerWindow::OnShowScope() 475 { 476 scopeEnabled = !scopeEnabled; 477 scopeView->SetEnabled(scopeEnabled); 478 scopeView->Invalidate(); 479 SaveSettings(); 480 } 481 482 //------------------------------------------------------------------------------ 483 484 void MidiPlayerWindow::OnInputChanged(BMessage* msg) 485 { 486 int32 newId; 487 if (msg->FindInt32("id", &newId) == B_OK) 488 { 489 BMidiProducer* endp; 490 491 endp = BMidiRoster::FindProducer(inputId); 492 if (endp != NULL) 493 { 494 endp->Disconnect(bridge); 495 endp->Release(); 496 } 497 498 inputId = newId; 499 500 endp = BMidiRoster::FindProducer(inputId); 501 if (endp != NULL) 502 { 503 if (!instrLoaded) 504 { 505 scopeView->SetLoading(true); 506 scopeView->Invalidate(); 507 UpdateIfNeeded(); 508 509 bridge->Init(B_BIG_SYNTH); 510 instrLoaded = true; 511 512 scopeView->SetLoading(false); 513 scopeView->Invalidate(); 514 } 515 516 endp->Connect(bridge); 517 endp->Release(); 518 519 scopeView->SetLiveInput(true); 520 scopeView->Invalidate(); 521 } 522 else 523 { 524 scopeView->SetLiveInput(false); 525 scopeView->Invalidate(); 526 } 527 } 528 } 529 530 //------------------------------------------------------------------------------ 531 532 void MidiPlayerWindow::OnReverb(reverb_mode mode) 533 { 534 reverb = mode; 535 be_synth->SetReverb(reverb); 536 SaveSettings(); 537 } 538 539 //------------------------------------------------------------------------------ 540 541 void MidiPlayerWindow::OnVolume() 542 { 543 volume = volumeSlider->Value(); 544 synth.SetVolume(volume / 100.0f); 545 SaveSettings(); 546 } 547 548 //------------------------------------------------------------------------------ 549 550 void MidiPlayerWindow::OnDrop(BMessage* msg) 551 { 552 entry_ref ref; 553 if (msg->FindRef("refs", &ref) == B_OK) 554 { 555 LoadFile(&ref); 556 } 557 } 558 559 //------------------------------------------------------------------------------ 560