Removed the nasty threads because they made my head hurt.
Old code is tagged with "with_cool_scope". git-svn-id: file:///srv/svn/repos/haiku/trunk/current@8164 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -42,7 +42,6 @@ MidiPlayerWindow::MidiPlayerWindow()
|
||||
volume = 75;
|
||||
windowX = -1;
|
||||
windowY = -1;
|
||||
threadId = -1;
|
||||
inputId = -1;
|
||||
|
||||
be_synth->SetSamplingRate(44100);
|
||||
@@ -63,16 +62,8 @@ MidiPlayerWindow::~MidiPlayerWindow()
|
||||
|
||||
bool MidiPlayerWindow::QuitRequested()
|
||||
{
|
||||
// There is a race condition when you quit MidiPlayer while we're still
|
||||
// fading out, because fading happens in a separate thread. In this odd
|
||||
// case, we simply won't let the user quit the app :-)
|
||||
|
||||
if (threadId == -1)
|
||||
{
|
||||
be_app->PostMessage(B_QUIT_REQUESTED);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
be_app->PostMessage(B_QUIT_REQUESTED);
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -430,99 +421,53 @@ void MidiPlayerWindow::SaveSettings()
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int32 MidiPlayerWindow::_LoadThread(void* data)
|
||||
void MidiPlayerWindow::LoadFile(entry_ref* ref)
|
||||
{
|
||||
return ((MidiPlayerWindow*) data)->LoadThread();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int32 MidiPlayerWindow::LoadThread()
|
||||
{
|
||||
// We do this in a separate fire-and-forget thread, just in case a song
|
||||
// was already playing. The call to StopSynth() will block, and we need
|
||||
// to keep the window's looper free for repainting the ScopeView during
|
||||
// the fade out. The R5 MidiPlayer does that too and it looks neat.
|
||||
|
||||
if (playing)
|
||||
{
|
||||
scopeView->SetPlaying(false);
|
||||
scopeView->Invalidate();
|
||||
|
||||
StopSynth();
|
||||
}
|
||||
|
||||
synth.UnloadFile();
|
||||
|
||||
if (synth.LoadFile(&ref) == B_OK)
|
||||
if (synth.LoadFile(ref) == B_OK)
|
||||
{
|
||||
// Ideally, we would call SetVolume() in InitControls(),
|
||||
// but for some reason that doesn't work: BMidiSynthFile
|
||||
// will use the default volume instead. So we do it here.
|
||||
synth.SetVolume(volume / 100.0f);
|
||||
|
||||
Lock();
|
||||
playButton->SetEnabled(true);
|
||||
playButton->SetLabel("Stop");
|
||||
scopeView->SetHaveFile(true);
|
||||
scopeView->SetPlaying(true);
|
||||
scopeView->Invalidate();
|
||||
Unlock();
|
||||
|
||||
StartSynth();
|
||||
}
|
||||
else
|
||||
{
|
||||
Lock();
|
||||
playButton->SetEnabled(false);
|
||||
playButton->SetLabel("Play");
|
||||
scopeView->SetHaveFile(false);
|
||||
scopeView->SetPlaying(false);
|
||||
scopeView->Invalidate();
|
||||
Unlock();
|
||||
|
||||
(new BAlert(
|
||||
NULL, "Could not load song", "Okay", NULL, NULL,
|
||||
B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go();
|
||||
}
|
||||
|
||||
threadId = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int32 MidiPlayerWindow::_StopThread(void* data)
|
||||
{
|
||||
return ((MidiPlayerWindow*) data)->StopThread();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int32 MidiPlayerWindow::StopThread()
|
||||
{
|
||||
Lock();
|
||||
playButton->SetEnabled(false);
|
||||
Unlock();
|
||||
|
||||
StopSynth();
|
||||
|
||||
Lock();
|
||||
playButton->SetEnabled(true);
|
||||
playButton->SetLabel("Play");
|
||||
Unlock();
|
||||
|
||||
threadId = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void MidiPlayerWindow::StartSynth()
|
||||
{
|
||||
// When playback of the song ends, we don't automatically go back into
|
||||
// "stopped" mode. It is possible to do this with synth.SetFileHook(),
|
||||
// but that made the code kinda messy (with all the threads and stuff).
|
||||
// Note: SetFileHook(NULL) crashes the softsynth. In any case, should
|
||||
// we ever add this in, remember to call SetFileHook() *after* Start()
|
||||
// or it won't work.
|
||||
|
||||
synth.Start();
|
||||
synth.SetFileHook(_StopHook, (int32) this);
|
||||
playing = true;
|
||||
}
|
||||
|
||||
@@ -540,19 +485,44 @@ void MidiPlayerWindow::StopSynth()
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void MidiPlayerWindow::_StopHook(int32 arg)
|
||||
{
|
||||
((MidiPlayerWindow*) arg)->StopHook();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void MidiPlayerWindow::StopHook()
|
||||
{
|
||||
Lock(); // we may be called from the synth's thread
|
||||
|
||||
playing = false;
|
||||
|
||||
scopeView->SetPlaying(false);
|
||||
scopeView->Invalidate();
|
||||
playButton->SetEnabled(true);
|
||||
playButton->SetLabel("Play");
|
||||
|
||||
Unlock();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void MidiPlayerWindow::OnPlayStop()
|
||||
{
|
||||
if (playing)
|
||||
{
|
||||
threadId = spawn_thread(
|
||||
_StopThread, "StopThread", B_NORMAL_PRIORITY, this);
|
||||
playButton->SetEnabled(false);
|
||||
|
||||
resume_thread(threadId);
|
||||
StopSynth();
|
||||
}
|
||||
else
|
||||
{
|
||||
StartSynth();
|
||||
playButton->SetLabel("Stop");
|
||||
scopeView->SetPlaying(true);
|
||||
scopeView->Invalidate();
|
||||
|
||||
StartSynth();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -579,7 +549,7 @@ void MidiPlayerWindow::OnInputChanged(BMessage* msg)
|
||||
// be_synth->LoadInstruments(all)
|
||||
|
||||
// if id != -1
|
||||
// if playing -> Stop() // AARGH, need thread!
|
||||
// if playing -> Stop()
|
||||
// connect SynthBridge (from MidiUtil) to producer endpoint
|
||||
// else if SynthBridge still connected
|
||||
// disconnect SynthBridge
|
||||
@@ -611,12 +581,10 @@ void MidiPlayerWindow::OnVolume()
|
||||
|
||||
void MidiPlayerWindow::OnDrop(BMessage* msg)
|
||||
{
|
||||
entry_ref ref;
|
||||
if (msg->FindRef("refs", &ref) == B_OK)
|
||||
{
|
||||
threadId = spawn_thread(
|
||||
_LoadThread, "LoadThread", B_NORMAL_PRIORITY, this);
|
||||
|
||||
resume_thread(threadId);
|
||||
LoadFile(&ref);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,15 +68,13 @@ private:
|
||||
void LoadSettings();
|
||||
void SaveSettings();
|
||||
|
||||
static int32 _LoadThread(void* data);
|
||||
int32 LoadThread();
|
||||
|
||||
static int32 _StopThread(void* data);
|
||||
int32 StopThread();
|
||||
|
||||
void LoadFile(entry_ref* ref);
|
||||
void StartSynth();
|
||||
void StopSynth();
|
||||
|
||||
static void _StopHook(int32 arg);
|
||||
void StopHook();
|
||||
|
||||
void OnPlayStop();
|
||||
void OnShowScope();
|
||||
void OnInputChanged(BMessage* msg);
|
||||
@@ -101,14 +99,12 @@ private:
|
||||
|
||||
bool playing;
|
||||
bool scopeEnabled;
|
||||
int32 inputId;
|
||||
reverb_mode reverb;
|
||||
int32 volume;
|
||||
float windowX;
|
||||
float windowY;
|
||||
BMidiSynthFile synth;
|
||||
entry_ref ref;
|
||||
thread_id threadId;
|
||||
int32 inputId;
|
||||
};
|
||||
|
||||
#endif // MIDI_PLAYER_WINDOW_H
|
||||
|
||||
@@ -33,6 +33,7 @@ ScopeView::ScopeView()
|
||||
{
|
||||
SetViewColor(0, 0, 0);
|
||||
|
||||
playing = false;
|
||||
enabled = true;
|
||||
haveFile = false;
|
||||
|
||||
@@ -87,6 +88,10 @@ void ScopeView::Draw(BRect updateRect)
|
||||
{
|
||||
DrawDisabled();
|
||||
}
|
||||
else if (!playing)
|
||||
{
|
||||
DrawStopped();
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawPlaying();
|
||||
@@ -95,6 +100,13 @@ void ScopeView::Draw(BRect updateRect)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ScopeView::SetPlaying(bool flag)
|
||||
{
|
||||
playing = flag;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ScopeView::SetEnabled(bool flag)
|
||||
{
|
||||
enabled = flag;
|
||||
@@ -124,7 +136,7 @@ int32 ScopeView::Thread()
|
||||
|
||||
while (!finished)
|
||||
{
|
||||
if (enabled && haveFile)
|
||||
if (enabled && playing && haveFile)
|
||||
{
|
||||
if (LockLooperWithTimeout(50000) == B_OK)
|
||||
{
|
||||
@@ -172,6 +184,17 @@ void ScopeView::DrawDisabled()
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ScopeView::DrawStopped()
|
||||
{
|
||||
SetHighColor(0, 130, 0);
|
||||
|
||||
StrokeLine(
|
||||
BPoint(0, Bounds().Height() / 2),
|
||||
BPoint(Bounds().Width(), Bounds().Height() / 2));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ScopeView::DrawPlaying()
|
||||
{
|
||||
int32 width = (int32) Bounds().Width();
|
||||
|
||||
@@ -36,6 +36,7 @@ public:
|
||||
virtual void DetachedFromWindow();
|
||||
virtual void Draw(BRect updateRect);
|
||||
|
||||
void SetPlaying(bool flag);
|
||||
void SetEnabled(bool flag);
|
||||
void SetHaveFile(bool flag);
|
||||
|
||||
@@ -48,9 +49,11 @@ private:
|
||||
|
||||
void DrawNoFile();
|
||||
void DrawDisabled();
|
||||
void DrawStopped();
|
||||
void DrawPlaying();
|
||||
|
||||
bool finished;
|
||||
bool playing;
|
||||
bool enabled;
|
||||
bool haveFile;
|
||||
int32 sampleCount;
|
||||
|
||||
@@ -28,13 +28,10 @@
|
||||
<li>Play and Stop are now one and the same button.</li>
|
||||
<li>There is no Open File... function.</li>
|
||||
<li>You cannot set the Quality of the sound (always 44100 Hz).</li>
|
||||
<li>While the song fades out, the scope temporarily freezes; the R5 MidiPlayer kept repainting during the fade-out (which, admittedly, looks better). At one point, the Haiku MidiPlayer did that too but the code made my head hurt, so I pulled it out again :-)</li>
|
||||
</ul>
|
||||
|
||||
<p>If people really <i>really</i> want these missing features, then feel free to add them :-)</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