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:
mahlzeit
2004-06-25 13:17:18 +00:00
parent 7383f6963f
commit b1902a09ef
5 changed files with 75 additions and 88 deletions
+42 -74
View File
@@ -42,7 +42,6 @@ MidiPlayerWindow::MidiPlayerWindow()
volume = 75; volume = 75;
windowX = -1; windowX = -1;
windowY = -1; windowY = -1;
threadId = -1;
inputId = -1; inputId = -1;
be_synth->SetSamplingRate(44100); be_synth->SetSamplingRate(44100);
@@ -63,16 +62,8 @@ MidiPlayerWindow::~MidiPlayerWindow()
bool MidiPlayerWindow::QuitRequested() bool MidiPlayerWindow::QuitRequested()
{ {
// There is a race condition when you quit MidiPlayer while we're still be_app->PostMessage(B_QUIT_REQUESTED);
// fading out, because fading happens in a separate thread. In this odd return true;
// case, we simply won't let the user quit the app :-)
if (threadId == -1)
{
be_app->PostMessage(B_QUIT_REQUESTED);
return true;
}
return false;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -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) if (playing)
{ {
scopeView->SetPlaying(false);
scopeView->Invalidate();
StopSynth(); StopSynth();
} }
synth.UnloadFile(); synth.UnloadFile();
if (synth.LoadFile(&ref) == B_OK) if (synth.LoadFile(ref) == B_OK)
{ {
// Ideally, we would call SetVolume() in InitControls(), // Ideally, we would call SetVolume() in InitControls(),
// but for some reason that doesn't work: BMidiSynthFile // but for some reason that doesn't work: BMidiSynthFile
// will use the default volume instead. So we do it here. // will use the default volume instead. So we do it here.
synth.SetVolume(volume / 100.0f); synth.SetVolume(volume / 100.0f);
Lock();
playButton->SetEnabled(true); playButton->SetEnabled(true);
playButton->SetLabel("Stop"); playButton->SetLabel("Stop");
scopeView->SetHaveFile(true); scopeView->SetHaveFile(true);
scopeView->SetPlaying(true);
scopeView->Invalidate(); scopeView->Invalidate();
Unlock();
StartSynth(); StartSynth();
} }
else else
{ {
Lock();
playButton->SetEnabled(false); playButton->SetEnabled(false);
playButton->SetLabel("Play"); playButton->SetLabel("Play");
scopeView->SetHaveFile(false); scopeView->SetHaveFile(false);
scopeView->SetPlaying(false);
scopeView->Invalidate(); scopeView->Invalidate();
Unlock();
(new BAlert( (new BAlert(
NULL, "Could not load song", "Okay", NULL, NULL, NULL, "Could not load song", "Okay", NULL, NULL,
B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go(); 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() 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.Start();
synth.SetFileHook(_StopHook, (int32) this);
playing = true; 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() void MidiPlayerWindow::OnPlayStop()
{ {
if (playing) if (playing)
{ {
threadId = spawn_thread( playButton->SetEnabled(false);
_StopThread, "StopThread", B_NORMAL_PRIORITY, this);
resume_thread(threadId); StopSynth();
} }
else else
{ {
StartSynth();
playButton->SetLabel("Stop"); playButton->SetLabel("Stop");
scopeView->SetPlaying(true);
scopeView->Invalidate();
StartSynth();
} }
} }
@@ -579,7 +549,7 @@ void MidiPlayerWindow::OnInputChanged(BMessage* msg)
// be_synth->LoadInstruments(all) // be_synth->LoadInstruments(all)
// if id != -1 // if id != -1
// if playing -> Stop() // AARGH, need thread! // if playing -> Stop()
// connect SynthBridge (from MidiUtil) to producer endpoint // connect SynthBridge (from MidiUtil) to producer endpoint
// else if SynthBridge still connected // else if SynthBridge still connected
// disconnect SynthBridge // disconnect SynthBridge
@@ -611,12 +581,10 @@ void MidiPlayerWindow::OnVolume()
void MidiPlayerWindow::OnDrop(BMessage* msg) void MidiPlayerWindow::OnDrop(BMessage* msg)
{ {
entry_ref ref;
if (msg->FindRef("refs", &ref) == B_OK) if (msg->FindRef("refs", &ref) == B_OK)
{ {
threadId = spawn_thread( LoadFile(&ref);
_LoadThread, "LoadThread", B_NORMAL_PRIORITY, this);
resume_thread(threadId);
} }
} }
+5 -9
View File
@@ -68,15 +68,13 @@ private:
void LoadSettings(); void LoadSettings();
void SaveSettings(); void SaveSettings();
static int32 _LoadThread(void* data); void LoadFile(entry_ref* ref);
int32 LoadThread();
static int32 _StopThread(void* data);
int32 StopThread();
void StartSynth(); void StartSynth();
void StopSynth(); void StopSynth();
static void _StopHook(int32 arg);
void StopHook();
void OnPlayStop(); void OnPlayStop();
void OnShowScope(); void OnShowScope();
void OnInputChanged(BMessage* msg); void OnInputChanged(BMessage* msg);
@@ -101,14 +99,12 @@ private:
bool playing; bool playing;
bool scopeEnabled; bool scopeEnabled;
int32 inputId;
reverb_mode reverb; reverb_mode reverb;
int32 volume; int32 volume;
float windowX; float windowX;
float windowY; float windowY;
BMidiSynthFile synth; BMidiSynthFile synth;
entry_ref ref;
thread_id threadId;
int32 inputId;
}; };
#endif // MIDI_PLAYER_WINDOW_H #endif // MIDI_PLAYER_WINDOW_H
+24 -1
View File
@@ -33,6 +33,7 @@ ScopeView::ScopeView()
{ {
SetViewColor(0, 0, 0); SetViewColor(0, 0, 0);
playing = false;
enabled = true; enabled = true;
haveFile = false; haveFile = false;
@@ -87,6 +88,10 @@ void ScopeView::Draw(BRect updateRect)
{ {
DrawDisabled(); DrawDisabled();
} }
else if (!playing)
{
DrawStopped();
}
else else
{ {
DrawPlaying(); DrawPlaying();
@@ -95,6 +100,13 @@ void ScopeView::Draw(BRect updateRect)
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void ScopeView::SetPlaying(bool flag)
{
playing = flag;
}
//------------------------------------------------------------------------------
void ScopeView::SetEnabled(bool flag) void ScopeView::SetEnabled(bool flag)
{ {
enabled = flag; enabled = flag;
@@ -124,7 +136,7 @@ int32 ScopeView::Thread()
while (!finished) while (!finished)
{ {
if (enabled && haveFile) if (enabled && playing && haveFile)
{ {
if (LockLooperWithTimeout(50000) == B_OK) 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() void ScopeView::DrawPlaying()
{ {
int32 width = (int32) Bounds().Width(); int32 width = (int32) Bounds().Width();
+3
View File
@@ -36,6 +36,7 @@ public:
virtual void DetachedFromWindow(); virtual void DetachedFromWindow();
virtual void Draw(BRect updateRect); virtual void Draw(BRect updateRect);
void SetPlaying(bool flag);
void SetEnabled(bool flag); void SetEnabled(bool flag);
void SetHaveFile(bool flag); void SetHaveFile(bool flag);
@@ -48,9 +49,11 @@ private:
void DrawNoFile(); void DrawNoFile();
void DrawDisabled(); void DrawDisabled();
void DrawStopped();
void DrawPlaying(); void DrawPlaying();
bool finished; bool finished;
bool playing;
bool enabled; bool enabled;
bool haveFile; bool haveFile;
int32 sampleCount; int32 sampleCount;
+1 -4
View File
@@ -28,13 +28,10 @@
<li>Play and Stop are now one and the same button.</li> <li>Play and Stop are now one and the same button.</li>
<li>There is no Open File... function.</li> <li>There is no Open File... function.</li>
<li>You cannot set the Quality of the sound (always 44100 Hz).</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> </ul>
<p>If people really <i>really</i> want these missing features, then feel free to add them :-)</p> <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> </body>
</html> </html>