xref: /haiku/src/apps/midiplayer/MidiPlayerWindow.cpp (revision 93aeb8c3bc3f13cb1f282e3e749258a23790d947)
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), "MIDI Player", B_TITLED_WINDOW,
39 	          B_ASYNCHRONOUS_CONTROLS | B_NOT_RESIZABLE)
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