- Added version number.
- Added the Live Input menu, although it doesn't do anything yet. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@8159 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -13,4 +13,5 @@ App MidiPlayer :
|
||||
LinkSharedOSLibs MidiPlayer :
|
||||
be
|
||||
midi
|
||||
midi2
|
||||
;
|
||||
|
||||
@@ -47,7 +47,7 @@ void MidiPlayerApp::AboutRequested()
|
||||
{
|
||||
(new BAlert(
|
||||
NULL,
|
||||
"Haiku MIDI Player\n\n"
|
||||
"Haiku MIDI Player 1.0.0 beta\n\n"
|
||||
"This tiny program\n"
|
||||
"Knows how to play thousands of\n"
|
||||
"Cheesy sounding songs",
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <MidiRoster.h>
|
||||
#include <StorageKit.h>
|
||||
|
||||
#include "MidiPlayerApp.h"
|
||||
@@ -42,6 +43,7 @@ MidiPlayerWindow::MidiPlayerWindow()
|
||||
windowX = -1;
|
||||
windowY = -1;
|
||||
threadId = -1;
|
||||
inputId = -1;
|
||||
|
||||
be_synth->SetSamplingRate(44100);
|
||||
|
||||
@@ -86,6 +88,10 @@ void MidiPlayerWindow::MessageReceived(BMessage* msg)
|
||||
case MSG_SHOW_SCOPE:
|
||||
OnShowScope();
|
||||
break;
|
||||
|
||||
case MSG_INPUT_CHANGED:
|
||||
OnInputChanged(msg);
|
||||
break;
|
||||
|
||||
case MSG_REVERB_NONE:
|
||||
OnReverb(B_REVERB_NONE);
|
||||
@@ -137,9 +143,77 @@ void MidiPlayerWindow::FrameMoved(BPoint origin)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void MidiPlayerWindow::MenusBeginning()
|
||||
{
|
||||
for (int32 t = inputPopUp->CountItems() - 1; t > 0; --t)
|
||||
{
|
||||
delete inputPopUp->RemoveItem(t);
|
||||
}
|
||||
|
||||
bool seenInput = false;
|
||||
|
||||
if (inputId == -1)
|
||||
{
|
||||
inputOff->SetMarked(true);
|
||||
seenInput = true;
|
||||
}
|
||||
|
||||
int32 id = 0;
|
||||
BMidiEndpoint* endp;
|
||||
while ((endp = BMidiRoster::NextEndpoint(&id)) != NULL)
|
||||
{
|
||||
if (endp->IsProducer())
|
||||
{
|
||||
BMessage* msg = new BMessage;
|
||||
msg->what = MSG_INPUT_CHANGED;
|
||||
msg->AddInt32("id", id);
|
||||
|
||||
BMenuItem* item = new BMenuItem(endp->Name(), msg);
|
||||
inputPopUp->AddItem(item);
|
||||
|
||||
if (inputId == id)
|
||||
{
|
||||
item->SetMarked(true);
|
||||
seenInput = true;
|
||||
}
|
||||
}
|
||||
|
||||
endp->Release();
|
||||
}
|
||||
|
||||
if (!seenInput) // endpoint no longer exists
|
||||
{
|
||||
inputOff->SetMarked(true);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void MidiPlayerWindow::CreateInputMenu()
|
||||
{
|
||||
inputPopUp = new BPopUpMenu("inputPopUp");
|
||||
|
||||
BMessage* msg = new BMessage;
|
||||
msg->what = MSG_INPUT_CHANGED;
|
||||
msg->AddInt32("id", -1);
|
||||
|
||||
inputOff = new BMenuItem("Off", msg);
|
||||
|
||||
inputPopUp->AddItem(inputOff);
|
||||
|
||||
inputMenu = new BMenuField(
|
||||
BRect(0, 0, 128, 17), "inputMenu", "Live Input:", inputPopUp,
|
||||
B_FOLLOW_LEFT | B_FOLLOW_TOP);
|
||||
|
||||
inputMenu->SetDivider(55);
|
||||
inputMenu->ResizeToPreferred();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void MidiPlayerWindow::CreateReverbMenu()
|
||||
{
|
||||
BPopUpMenu* menu = new BPopUpMenu("reverbPopUp");
|
||||
BPopUpMenu* reverbPopUp = new BPopUpMenu("reverbPopUp");
|
||||
|
||||
reverbNone = new BMenuItem(
|
||||
"None", new BMessage(MSG_REVERB_NONE));
|
||||
@@ -159,18 +233,18 @@ void MidiPlayerWindow::CreateReverbMenu()
|
||||
reverbDungeon = new BMenuItem(
|
||||
"Dungeon", new BMessage(MSG_REVERB_DUNGEON));
|
||||
|
||||
menu->AddItem(reverbNone);
|
||||
menu->AddItem(reverbCloset);
|
||||
menu->AddItem(reverbGarage);
|
||||
menu->AddItem(reverbIgor);
|
||||
menu->AddItem(reverbCavern);
|
||||
menu->AddItem(reverbDungeon);
|
||||
reverbPopUp->AddItem(reverbNone);
|
||||
reverbPopUp->AddItem(reverbCloset);
|
||||
reverbPopUp->AddItem(reverbGarage);
|
||||
reverbPopUp->AddItem(reverbIgor);
|
||||
reverbPopUp->AddItem(reverbCavern);
|
||||
reverbPopUp->AddItem(reverbDungeon);
|
||||
|
||||
reverbMenu = new BMenuField(
|
||||
BRect(0, 0, 128, 17), "reverbMenu", "Reverb:", menu,
|
||||
BRect(0, 0, 128, 17), "reverbMenu", "Reverb:", reverbPopUp,
|
||||
B_FOLLOW_LEFT | B_FOLLOW_TOP);
|
||||
|
||||
reverbMenu->SetDivider(50);
|
||||
reverbMenu->SetDivider(55);
|
||||
reverbMenu->ResizeToPreferred();
|
||||
}
|
||||
|
||||
@@ -187,6 +261,7 @@ void MidiPlayerWindow::CreateViews()
|
||||
showScope->SetValue(B_CONTROL_ON);
|
||||
showScope->ResizeToPreferred();
|
||||
|
||||
CreateInputMenu();
|
||||
CreateReverbMenu();
|
||||
|
||||
volumeSlider = new BSlider(
|
||||
@@ -197,7 +272,7 @@ void MidiPlayerWindow::CreateViews()
|
||||
volumeSlider->UseFillColor(true, &col);
|
||||
volumeSlider->SetModificationMessage(new BMessage(MSG_VOLUME));
|
||||
volumeSlider->ResizeToPreferred();
|
||||
volumeSlider->ResizeTo(_W(scopeView) - 37, _H(volumeSlider));
|
||||
volumeSlider->ResizeTo(_W(scopeView) - 42, _H(volumeSlider));
|
||||
|
||||
playButton = new BButton(
|
||||
BRect(0, 1, 80, 1), "playButton", "Play", new BMessage(MSG_PLAY_STOP),
|
||||
@@ -226,13 +301,14 @@ void MidiPlayerWindow::CreateViews()
|
||||
float width = 8 + _W(scopeView) + 8;
|
||||
|
||||
float height =
|
||||
+ 8 + _H(scopeView)
|
||||
8 + _H(scopeView)
|
||||
+ 8 + _H(showScope)
|
||||
+ 4 + _H(reverbMenu)
|
||||
+ 4 + _H(inputMenu)
|
||||
+ _H(reverbMenu)
|
||||
+ 2 + _H(volumeSlider)
|
||||
+ 10 + _H(divider)
|
||||
+ 6 + _H(playButton)
|
||||
+ 10;
|
||||
+ 16;
|
||||
|
||||
ResizeTo(width, height);
|
||||
|
||||
@@ -241,6 +317,7 @@ void MidiPlayerWindow::CreateViews()
|
||||
background->AddChild(scopeView);
|
||||
background->AddChild(showScope);
|
||||
background->AddChild(reverbMenu);
|
||||
background->AddChild(inputMenu);
|
||||
background->AddChild(volumeLabel);
|
||||
background->AddChild(volumeSlider);
|
||||
background->AddChild(divider);
|
||||
@@ -250,14 +327,17 @@ void MidiPlayerWindow::CreateViews()
|
||||
scopeView->MoveTo(8, y);
|
||||
|
||||
y += _H(scopeView) + 8;
|
||||
showScope->MoveTo(8 + 50, y);
|
||||
showScope->MoveTo(8 + 55, y);
|
||||
|
||||
y += _H(showScope) + 4;
|
||||
inputMenu->MoveTo(8, y);
|
||||
|
||||
y += _H(inputMenu);
|
||||
reverbMenu->MoveTo(8, y);
|
||||
|
||||
y += _H(reverbMenu) + 2;
|
||||
volumeLabel->MoveTo(8, y);
|
||||
volumeSlider->MoveTo(8 + 44, y);
|
||||
volumeSlider->MoveTo(8 + 49, y);
|
||||
|
||||
y += _H(volumeSlider) + 10;
|
||||
divider->MoveTo(8, y);
|
||||
@@ -287,6 +367,8 @@ void MidiPlayerWindow::InitControls()
|
||||
showScope->SetValue(scopeEnabled ? B_CONTROL_ON : B_CONTROL_OFF);
|
||||
scopeView->SetEnabled(scopeEnabled);
|
||||
|
||||
inputOff->SetMarked(true);
|
||||
|
||||
reverbNone->SetMarked(reverb == B_REVERB_NONE);
|
||||
reverbCloset->SetMarked(reverb == B_REVERB_CLOSET);
|
||||
reverbGarage->SetMarked(reverb == B_REVERB_GARAGE);
|
||||
@@ -486,6 +568,29 @@ void MidiPlayerWindow::OnShowScope()
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void MidiPlayerWindow::OnInputChanged(BMessage* msg)
|
||||
{
|
||||
int32 newId;
|
||||
if (msg->FindInt32("id", &newId) == B_OK)
|
||||
{
|
||||
inputId = newId;
|
||||
|
||||
// if (!instrLoaded)
|
||||
// be_synth->LoadInstruments(all)
|
||||
|
||||
// if id != -1
|
||||
// if playing -> Stop() // AARGH, need thread!
|
||||
// connect SynthBridge (from MidiUtil) to producer endpoint
|
||||
// else if SynthBridge still connected
|
||||
// disconnect SynthBridge
|
||||
|
||||
// NOTE: we should also disconnect SynthBridge when launching
|
||||
// a MIDI file!!! (successfull or not)
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void MidiPlayerWindow::OnReverb(reverb_mode mode)
|
||||
{
|
||||
reverb = mode;
|
||||
|
||||
@@ -32,6 +32,7 @@ enum
|
||||
{
|
||||
MSG_PLAY_STOP = 1000,
|
||||
MSG_SHOW_SCOPE,
|
||||
MSG_INPUT_CHANGED,
|
||||
MSG_REVERB_NONE,
|
||||
MSG_REVERB_CLOSET,
|
||||
MSG_REVERB_GARAGE,
|
||||
@@ -53,11 +54,13 @@ public:
|
||||
virtual bool QuitRequested();
|
||||
virtual void MessageReceived(BMessage* msg);
|
||||
virtual void FrameMoved(BPoint origin);
|
||||
virtual void MenusBeginning();
|
||||
|
||||
private:
|
||||
|
||||
typedef BWindow super;
|
||||
|
||||
void CreateInputMenu();
|
||||
void CreateReverbMenu();
|
||||
void CreateViews();
|
||||
void CenterOnScreen();
|
||||
@@ -76,12 +79,16 @@ private:
|
||||
|
||||
void OnPlayStop();
|
||||
void OnShowScope();
|
||||
void OnInputChanged(BMessage* msg);
|
||||
void OnReverb(reverb_mode mode);
|
||||
void OnVolume();
|
||||
void OnDrop(BMessage* msg);
|
||||
|
||||
ScopeView* scopeView;
|
||||
BCheckBox* showScope;
|
||||
BMenuField* inputMenu;
|
||||
BPopUpMenu* inputPopUp;
|
||||
BMenuItem* inputOff;
|
||||
BMenuField* reverbMenu;
|
||||
BMenuItem* reverbNone;
|
||||
BMenuItem* reverbCloset;
|
||||
@@ -101,6 +108,7 @@ private:
|
||||
BMidiSynthFile synth;
|
||||
entry_ref ref;
|
||||
thread_id threadId;
|
||||
int32 inputId;
|
||||
};
|
||||
|
||||
#endif // MIDI_PLAYER_WINDOW_H
|
||||
|
||||
@@ -4,23 +4,37 @@
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>Haiku MIDI Player</h1>
|
||||
<h1>Haiku MIDI Player 1.0.0 beta</h1>
|
||||
|
||||
<p>This is a simple application to play MIDI songs.</p>
|
||||
|
||||
<p>Differences with the BeOS R5 MidiPlayer:</p>
|
||||
<p>To play a MIDI song, you can:</p>
|
||||
|
||||
<ul>
|
||||
<li>double-click it, or</li>
|
||||
<li>drag it on the MidiPlayer icon, or</li>
|
||||
<li>drag it into the MidiPlayer window, or</li>
|
||||
<li>type "MidiPlayer song.mid" in Terminal.</li>
|
||||
</ul>
|
||||
|
||||
<p>Hopefully, most of the options will be obvious, although Live Input may require a few lines of explanation.</p>
|
||||
|
||||
<p>The <b>Live Input</b> feature enables you to attach other applications to the Haiku software synthesizer, for example a virtual MIDI keyboard. MidiPlayer looks at the MIDI roster for compatible producer endpoints. If none are found, Live Input is not available.</p>
|
||||
|
||||
<h3>Differences with the BeOS R5 MidiPlayer:</h3>
|
||||
|
||||
<ul>
|
||||
<li>User interface looks slightly different (better?)</li>
|
||||
<li>Play and Stop are now one and the same button.</li>
|
||||
<li>There is no Open File... button. You open MIDI songs by dragging them into the window.</li>
|
||||
<li>There is no Live Input feature.</li>
|
||||
<li>There is no Open File... function.</li>
|
||||
<li>You cannot set the Quality of the sound (always 44100 Hz).</li>
|
||||
</ul>
|
||||
|
||||
<p>If people really <i>really</i> want these missing features, then feel free to add them :-)</p>
|
||||
|
||||
<p>Note: The code can be a little tricky at times, since it uses fire-and-forget threads to load new songs and to stop playback. The reason for this is the scope: we want to keep repainting the scope view even when fading out a song (because this looks cool), but fading blocks the calling thread. If we were to block the window's looper, then the scope wouldn't repaint during the fade-out. So we fade using a separate thread instead. Unfortunately, this makes the code much less clean as it introduces several race conditions. Yech. See the source for more details.</p>
|
||||
<h3>Implementation notes</h3>
|
||||
|
||||
<p>The code can be a little tricky at times, since it uses fire-and-forget threads to load new songs and to stop playback. The reason for this is the scope: we want to keep repainting the ScopeView even when fading out a song (because this looks cool), but fading blocks the calling thread. If we were to block the window's looper, then the scope wouldn't repaint during the fade-out. So we fade using a separate thread instead. Unfortunately, this makes the code much less clean as it introduces several race conditions. Yech. See the source for more details.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user