2 * Copyright (c) 2004 Matthijs Hollemans
3 * Copyright (c) 2008-2014 Haiku, Inc. All rights reserved.
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
25 #include "MidiPlayerWindow.h"
28 #include <LayoutBuilder.h>
30 #include <MidiProducer.h>
31 #include <MidiRoster.h>
32 #include <SeparatorView.h>
33 #include <StorageKit.h>
34 #include <SpaceLayoutItem.h>
36 #include "MidiPlayerApp.h"
37 #include "ScopeView.h"
38 #include "SynthBridge.h"
41 #define _W(a) (a->Frame().Width())
42 #define _H(a) (a->Frame().Height())
45 #undef B_TRANSLATION_CONTEXT
46 #define B_TRANSLATION_CONTEXT "Main Window"
49 // #pragma mark - MidiPlayerWindow
52 MidiPlayerWindow::MidiPlayerWindow()
54 BWindow(BRect(0, 0, 1, 1), B_TRANSLATE_SYSTEM_NAME("MidiPlayer"),
55 B_TITLED_WINDOW
, B_ASYNCHRONOUS_CONTROLS
| B_NOT_RESIZABLE
56 | B_NOT_ZOOMABLE
| B_AUTO_UPDATE_SIZE_LIMITS
)
60 fReverbMode
= B_REVERB_BALLROOM
;
65 fInstrumentLoaded
= false;
67 be_synth
->SetSamplingRate(44100);
69 fSynthBridge
= new SynthBridge
;
70 //fSynthBridge->Register();
78 MidiPlayerWindow::~MidiPlayerWindow()
82 //fSynthBridge->Unregister();
83 fSynthBridge
->Release();
88 MidiPlayerWindow::QuitRequested()
90 be_app
->PostMessage(B_QUIT_REQUESTED
);
96 MidiPlayerWindow::MessageReceived(BMessage
* message
)
98 switch (message
->what
) {
107 case MSG_INPUT_CHANGED
:
108 OnInputChanged(message
);
111 case MSG_REVERB_NONE
:
112 OnReverb(B_REVERB_NONE
);
115 case MSG_REVERB_CLOSET
:
116 OnReverb(B_REVERB_CLOSET
);
119 case MSG_REVERB_GARAGE
:
120 OnReverb(B_REVERB_GARAGE
);
123 case MSG_REVERB_IGOR
:
124 OnReverb(B_REVERB_BALLROOM
);
127 case MSG_REVERB_CAVERN
:
128 OnReverb(B_REVERB_CAVERN
);
131 case MSG_REVERB_DUNGEON
:
132 OnReverb(B_REVERB_DUNGEON
);
144 super::MessageReceived(message
);
151 MidiPlayerWindow::FrameMoved(BPoint origin
)
153 super::FrameMoved(origin
);
154 fWindowX
= Frame().left
;
155 fWindowY
= Frame().top
;
161 MidiPlayerWindow::MenusBeginning()
163 for (int32 t
= fInputPopUpMenu
->CountItems() - 1; t
> 0; --t
)
164 delete fInputPopUpMenu
->RemoveItem(t
);
166 // Note: if the selected endpoint no longer exists, then no endpoint is
167 // marked. However, we won't disconnect it until you choose another one.
169 fInputOffMenuItem
->SetMarked(fInputId
== -1);
172 while (BMidiEndpoint
* endpoint
= BMidiRoster::NextEndpoint(&id
)) {
173 if (endpoint
->IsProducer()) {
174 BMessage
* message
= new BMessage(MSG_INPUT_CHANGED
);
175 message
->AddInt32("id", id
);
177 BMenuItem
* item
= new BMenuItem(endpoint
->Name(), message
);
178 fInputPopUpMenu
->AddItem(item
);
179 item
->SetMarked(fInputId
== id
);
188 MidiPlayerWindow::CreateInputMenu()
190 fInputPopUpMenu
= new BPopUpMenu("inputPopUp");
192 BMessage
* message
= new BMessage(MSG_INPUT_CHANGED
);
193 message
->AddInt32("id", -1);
195 fInputOffMenuItem
= new BMenuItem(B_TRANSLATE("Off"), message
);
196 fInputPopUpMenu
->AddItem(fInputOffMenuItem
);
198 fInputMenuField
= new BMenuField(B_TRANSLATE("Live input:"),
204 MidiPlayerWindow::CreateReverbMenu()
206 BPopUpMenu
* reverbPopUpMenu
= new BPopUpMenu("reverbPopUp");
207 fReverbNoneMenuItem
= new BMenuItem(B_TRANSLATE("None"),
208 new BMessage(MSG_REVERB_NONE
));
209 fReverbClosetMenuItem
= new BMenuItem(B_TRANSLATE("Closet"),
210 new BMessage(MSG_REVERB_CLOSET
));
211 fReverbGarageMenuItem
= new BMenuItem(B_TRANSLATE("Garage"),
212 new BMessage(MSG_REVERB_GARAGE
));
213 fReverbIgorMenuItem
= new BMenuItem(B_TRANSLATE("Igor's lab"),
214 new BMessage(MSG_REVERB_IGOR
));
215 fReverbCavern
= new BMenuItem(B_TRANSLATE("Cavern"),
216 new BMessage(MSG_REVERB_CAVERN
));
217 fReverbDungeon
= new BMenuItem(B_TRANSLATE("Dungeon"),
218 new BMessage(MSG_REVERB_DUNGEON
));
220 reverbPopUpMenu
->AddItem(fReverbNoneMenuItem
);
221 reverbPopUpMenu
->AddItem(fReverbClosetMenuItem
);
222 reverbPopUpMenu
->AddItem(fReverbGarageMenuItem
);
223 reverbPopUpMenu
->AddItem(fReverbIgorMenuItem
);
224 reverbPopUpMenu
->AddItem(fReverbCavern
);
225 reverbPopUpMenu
->AddItem(fReverbDungeon
);
227 fReverbMenuField
= new BMenuField(B_TRANSLATE("Reverb:"), reverbPopUpMenu
);
232 MidiPlayerWindow::CreateViews()
234 // Set up needed views
235 fScopeView
= new ScopeView();
237 fShowScopeCheckBox
= new BCheckBox("showScope", B_TRANSLATE("Scope"),
238 new BMessage(MSG_SHOW_SCOPE
));
239 fShowScopeCheckBox
->SetValue(B_CONTROL_ON
);
244 fVolumeSlider
= new BSlider("volumeSlider", NULL
, NULL
, 0, 100,
246 rgb_color color
= (rgb_color
){ 152, 152, 255 };
247 fVolumeSlider
->UseFillColor(true, &color
);
248 fVolumeSlider
->SetModificationMessage(new BMessage(MSG_VOLUME
));
249 fVolumeSlider
->SetExplicitMinSize(
250 BSize(fScopeView
->Frame().Width(), B_SIZE_UNSET
));
252 fPlayButton
= new BButton("playButton", B_TRANSLATE("Play"),
253 new BMessage(MSG_PLAY_STOP
));
254 fPlayButton
->SetEnabled(false);
256 BStringView
* volumeLabel
= new BStringView(NULL
, B_TRANSLATE("Volume:"));
257 volumeLabel
->SetAlignment(B_ALIGN_LEFT
);
260 BLayoutBuilder::Group
<>(this, B_VERTICAL
, 0)
262 .AddGroup(B_VERTICAL
, 0)
263 .AddGrid(B_USE_DEFAULT_SPACING
, B_USE_SMALL_SPACING
)
264 .Add(fShowScopeCheckBox
, 1, 0)
266 .Add(fReverbMenuField
->CreateLabelLayoutItem(), 0, 1)
267 .AddGroup(B_HORIZONTAL
, 0.0f
, 1, 1)
268 .Add(fReverbMenuField
->CreateMenuBarLayoutItem())
272 .Add(fInputMenuField
->CreateLabelLayoutItem(), 0, 2)
273 .AddGroup(B_HORIZONTAL
, 0.0f
, 1, 2)
274 .Add(fInputMenuField
->CreateMenuBarLayoutItem())
278 .Add(volumeLabel
, 0, 3)
279 .Add(fVolumeSlider
, 0, 4, 2, 1)
282 .SetInsets(B_USE_WINDOW_SPACING
, B_USE_DEFAULT_SPACING
,
283 B_USE_WINDOW_SPACING
, B_USE_DEFAULT_SPACING
)
286 .Add(new BSeparatorView(B_HORIZONTAL
))
287 .AddGroup(B_VERTICAL
, 0)
289 .SetInsets(0, B_USE_DEFAULT_SPACING
, 0, B_USE_WINDOW_SPACING
)
296 MidiPlayerWindow::InitControls()
300 fShowScopeCheckBox
->SetValue(fScopeEnabled
? B_CONTROL_ON
: B_CONTROL_OFF
);
301 fScopeView
->SetEnabled(fScopeEnabled
);
303 fInputOffMenuItem
->SetMarked(true);
305 fReverbNoneMenuItem
->SetMarked(fReverbMode
== B_REVERB_NONE
);
306 fReverbClosetMenuItem
->SetMarked(fReverbMode
== B_REVERB_CLOSET
);
307 fReverbGarageMenuItem
->SetMarked(fReverbMode
== B_REVERB_GARAGE
);
308 fReverbIgorMenuItem
->SetMarked(fReverbMode
== B_REVERB_BALLROOM
);
309 fReverbCavern
->SetMarked(fReverbMode
== B_REVERB_CAVERN
);
310 fReverbDungeon
->SetMarked(fReverbMode
== B_REVERB_DUNGEON
);
311 be_synth
->SetReverb(fReverbMode
);
313 fVolumeSlider
->SetValue(fVolume
);
315 if (fWindowX
!= -1 && fWindowY
!= -1)
316 MoveTo(fWindowX
, fWindowY
);
325 MidiPlayerWindow::LoadSettings()
327 BFile
file(SETTINGS_FILE
, B_READ_ONLY
);
328 if (file
.InitCheck() != B_OK
|| file
.Lock() != B_OK
)
331 file
.ReadAttr("Scope", B_BOOL_TYPE
, 0, &fScopeEnabled
, sizeof(bool));
332 file
.ReadAttr("Reverb", B_INT32_TYPE
, 0, &fReverbMode
, sizeof(int32
));
333 file
.ReadAttr("Volume", B_INT32_TYPE
, 0, &fWindowX
, sizeof(int32
));
334 file
.ReadAttr("WindowX", B_FLOAT_TYPE
, 0, &fWindowX
, sizeof(float));
335 file
.ReadAttr("WindowY", B_FLOAT_TYPE
, 0, &fWindowY
, sizeof(float));
342 MidiPlayerWindow::SaveSettings()
344 BFile
file(SETTINGS_FILE
, B_CREATE_FILE
| B_ERASE_FILE
| B_WRITE_ONLY
);
345 if (file
.InitCheck() != B_OK
|| file
.Lock() != B_OK
)
348 file
.WriteAttr("Scope", B_BOOL_TYPE
, 0, &fScopeEnabled
, sizeof(bool));
349 file
.WriteAttr("Reverb", B_INT32_TYPE
, 0, &fReverbMode
, sizeof(int32
));
350 file
.WriteAttr("Volume", B_INT32_TYPE
, 0, &fVolume
, sizeof(int32
));
351 file
.WriteAttr("WindowX", B_FLOAT_TYPE
, 0, &fWindowX
, sizeof(float));
352 file
.WriteAttr("WindowY", B_FLOAT_TYPE
, 0, &fWindowY
, sizeof(float));
360 MidiPlayerWindow::LoadFile(entry_ref
* ref
)
363 fScopeView
->SetPlaying(false);
364 fScopeView
->Invalidate();
370 fMidiSynthFile
.UnloadFile();
372 if (fMidiSynthFile
.LoadFile(ref
) == B_OK
) {
373 // Ideally, we would call SetVolume() in InitControls(),
374 // but for some reason that doesn't work: BMidiSynthFile
375 // will use the default volume instead. So we do it here.
376 fMidiSynthFile
.SetVolume(fVolume
/ 100.0f
);
378 fPlayButton
->SetEnabled(true);
379 fPlayButton
->SetLabel(B_TRANSLATE("Stop"));
380 fScopeView
->SetHaveFile(true);
381 fScopeView
->SetPlaying(true);
382 fScopeView
->Invalidate();
386 fPlayButton
->SetEnabled(false);
387 fPlayButton
->SetLabel(B_TRANSLATE("Play"));
388 fScopeView
->SetHaveFile(false);
389 fScopeView
->SetPlaying(false);
390 fScopeView
->Invalidate();
392 BAlert
* alert
= new BAlert(NULL
, B_TRANSLATE("Could not load song"),
393 B_TRANSLATE("OK"), NULL
, NULL
,
394 B_WIDTH_AS_USUAL
, B_STOP_ALERT
);
395 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
402 MidiPlayerWindow::StartSynth()
404 fMidiSynthFile
.Start();
405 fMidiSynthFile
.SetFileHook(_StopHook
, (int32
)(addr_t
)this);
411 MidiPlayerWindow::StopSynth()
413 if (!fMidiSynthFile
.IsFinished())
414 fMidiSynthFile
.Fade();
421 MidiPlayerWindow::_StopHook(int32 arg
)
423 ((MidiPlayerWindow
*)(addr_t
)arg
)->StopHook();
428 MidiPlayerWindow::StopHook()
431 // we may be called from the synth's thread
435 fScopeView
->SetPlaying(false);
436 fScopeView
->Invalidate();
437 fPlayButton
->SetEnabled(true);
438 fPlayButton
->SetLabel(B_TRANSLATE("Play"));
445 MidiPlayerWindow::OnPlayStop()
448 fPlayButton
->SetEnabled(false);
449 fScopeView
->SetPlaying(false);
450 fScopeView
->Invalidate();
455 fPlayButton
->SetLabel(B_TRANSLATE("Stop"));
456 fScopeView
->SetPlaying(true);
457 fScopeView
->Invalidate();
465 MidiPlayerWindow::OnShowScope()
467 fScopeEnabled
= !fScopeEnabled
;
468 fScopeView
->SetEnabled(fScopeEnabled
);
469 fScopeView
->Invalidate();
475 MidiPlayerWindow::OnInputChanged(BMessage
* message
)
478 if (message
->FindInt32("id", &newId
) == B_OK
) {
479 BMidiProducer
* endpoint
= BMidiRoster::FindProducer(fInputId
);
480 if (endpoint
!= NULL
) {
481 endpoint
->Disconnect(fSynthBridge
);
487 endpoint
= BMidiRoster::FindProducer(fInputId
);
488 if (endpoint
!= NULL
) {
489 if (!fInstrumentLoaded
) {
490 fScopeView
->SetLoading(true);
491 fScopeView
->Invalidate();
494 fSynthBridge
->Init(B_BIG_SYNTH
);
495 fInstrumentLoaded
= true;
497 fScopeView
->SetLoading(false);
498 fScopeView
->Invalidate();
501 endpoint
->Connect(fSynthBridge
);
504 fScopeView
->SetLiveInput(true);
505 fScopeView
->Invalidate();
507 fScopeView
->SetLiveInput(false);
508 fScopeView
->Invalidate();
515 MidiPlayerWindow::OnReverb(reverb_mode mode
)
518 be_synth
->SetReverb(fReverbMode
);
524 MidiPlayerWindow::OnVolume()
526 fVolume
= fVolumeSlider
->Value();
527 fMidiSynthFile
.SetVolume(fVolume
/ 100.0f
);
533 MidiPlayerWindow::OnDrop(BMessage
* message
)
536 if (message
->FindRef("refs", &ref
) == B_OK
)