Major internal code refactoring -- separated the actual SCSI-call interface with the CD drive from the rest of the app

CDEngine now uses the PlayList class to track which track to play - Shuffle and Loop now work properly
Track name is a little darker when the player is no longer active


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@13353 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
DarkWyrm
2005-06-29 18:42:22 +00:00
parent a66e28b1ff
commit 4f39b358ee
10 changed files with 548 additions and 651 deletions
+43 -21
View File
@@ -76,18 +76,38 @@ CDDBQuery::SetToSite(const char *server, int32 port)
} }
void void
CDDBQuery::SetToCD(const scsi_toc *toc) CDDBQuery::SetToCD(const char *path)
{ {
if (state == kInitial) { if(!path)
GetDiscID(toc, discID, numTracks, discLength, frameOffsetString, discIDStr); return;
} else {
// Get the SCSI table of contents from the device passed to us
int device = open(path, O_RDONLY);
if(device < 0)
return;
scsi_toc toc;
status_t result = ioctl(device, B_SCSI_GET_TOC, &toc);
close(device);
if(result != B_OK)
return;
if (state == kInitial)
{
GetDiscID(&toc, discID, numTracks, discLength, frameOffsetString, discIDStr);
}
else
{
int32 tmpDiscID; int32 tmpDiscID;
int32 tmpDiscLength; int32 tmpDiscLength;
int32 tmpNumTracks; int32 tmpNumTracks;
BString tmpFrameOffsetString; BString tmpFrameOffsetString;
BString tmpDiscIDStr; BString tmpDiscIDStr;
GetDiscID(toc, tmpDiscID, tmpNumTracks, tmpDiscLength, tmpFrameOffsetString, GetDiscID(&toc, tmpDiscID, tmpNumTracks, tmpDiscLength, tmpFrameOffsetString,
tmpDiscIDStr); tmpDiscIDStr);
if (discID == tmpDiscID && discLength == tmpDiscLength && numTracks == tmpNumTracks if (discID == tmpDiscID && discLength == tmpDiscLength && numTracks == tmpNumTracks
@@ -343,8 +363,10 @@ CDDBQuery::ReadFromServer(BDataIO *stream)
BString tmp; BString tmp;
ReadLine(tmp); ReadLine(tmp);
if (tmp.FindFirst("200") != 0) if (tmp.FindFirst("200") != 0)
{
printf("Error: %s\n",tmp.String());
return; return;
}
BString category; BString category;
GetToken(tmp.String() + 3, category); GetToken(tmp.String() + 3, category);
if (!category.Length()) if (!category.Length())
@@ -474,7 +496,7 @@ CDDBQuery::IdentifySelf()
strcpy(hostname, "unknown"); strcpy(hostname, "unknown");
BString tmp; BString tmp;
tmp << "cddb hello " << username << " " << hostname << " CDButton v1.0\n"; tmp << "cddb hello " << username << " " << hostname << " Haiku_CD_Player v1.0\n";
if (log) if (log)
printf(">%s", tmp.String()); printf(">%s", tmp.String());
@@ -535,7 +557,9 @@ CDDBQuery::FindOrCreateContentFileForDisk(BFile *file, entry_ref *fileRef, int32
return false; return false;
} }
#if 0 /*
#include "CDEngine.h"
bool bool
PrintSite(const char *site, int port, const char *latitude, PrintSite(const char *site, int port, const char *latitude,
@@ -549,35 +573,33 @@ PrintSite(const char *site, int port, const char *latitude,
int int
main() main()
{ {
try { CDDBQuery query("us.freedb.org", 888, true);
CDDBQuery query("us.cddb.com", 888, true);
// query.GetSites(&PrintSite, NULL);
int32 device = CDEngine::FindCDPlayerDevice(); int32 device = CDEngine::FindCDPlayerDevice();
if (!device) { if (!device)
{
printf("no device %s\n", strerror(device)); printf("no device %s\n", strerror(device));
return 0; return 0;
} }
CDEngine engine(device);
scsi_toc toc; scsi_toc toc;
engine.GetToc(&toc);
ioctl(device, B_SCSI_GET_TOC, &toc);
int32 discID = query.GetDiscID(&toc);
query.SetToCD(&toc); query.SetToCD(&toc);
BString title; BString title;
vector<BString> trackTitles; vector<BString> trackTitles;
query.GetTitles(title, trackTitles, 10000000); query.GetTitles(&title, &trackTitles, 10000000);
printf("CD Title:%s\n", title.String()); printf("CD Title: %s, ID: %lx\n", title.String(), discID);
int32 index = 0; int32 index = 0;
for (vector<BString>::iterator iterator = trackTitles.begin(); for (vector<BString>::iterator iterator = trackTitles.begin();
iterator != trackTitles.end(); index++, iterator++) iterator != trackTitles.end(); index++, iterator++)
printf("%d: %s\n", index, (*iterator).String()); printf("%ld: %s\n", index, (*iterator).String());
} catch(status_t error) {
printf("error %s\n", strerror(error));
}
return 0; return 0;
} }
#endif */
+24 -21
View File
@@ -9,14 +9,15 @@
// based on Jukebox by Chip Paul // based on Jukebox by Chip Paul
class CDDBQuery { class CDDBQuery
{
public: public:
CDDBQuery(const char *server, int32 port = 888, bool log = false); CDDBQuery(const char *server, int32 port = 888, bool log = false);
void SetToSite(const char *server, int32 port); void SetToSite(const char *server, int32 port);
void GetSites(bool (*)(const char *site, int port, const char *latitude, void GetSites(bool (*)(const char *site, int port, const char *latitude,
const char *longitude, const char *description, void *state), void *); const char *longitude, const char *description, void *state), void *);
void SetToCD(const scsi_toc *); void SetToCD(const char *devicepath);
bool GetTitles(BString *title, vector<BString> *tracks, bigtime_t timeout); bool GetTitles(BString *title, vector<BString> *tracks, bigtime_t timeout);
// title or tracks may be NULL if you are only interrested in one, not the other // title or tracks may be NULL if you are only interrested in one, not the other
@@ -39,25 +40,26 @@ private:
static int32 LookupBinder(void *); static int32 LookupBinder(void *);
class Connector { class Connector
public: {
Connector(CDDBQuery *client) public:
: client(client), Connector(CDDBQuery *client)
wasConnected(client->IsConnected()) : client(client),
{ wasConnected(client->IsConnected())
if (!wasConnected) {
client->Connect(); if (!wasConnected)
} client->Connect();
}
~Connector()
{
if (!wasConnected)
client->Disconnect();
}
private: ~Connector()
CDDBQuery *client; {
bool wasConnected; if (!wasConnected)
client->Disconnect();
}
private:
CDDBQuery *client;
bool wasConnected;
}; };
bool FindOrCreateContentFileForDisk(BFile *file, entry_ref *ref, int32 discID); bool FindOrCreateContentFileForDisk(BFile *file, entry_ref *ref, int32 discID);
@@ -87,7 +89,8 @@ private:
BString discIDStr; BString discIDStr;
// cached retrieved data // cached retrieved data
enum State { enum State
{
kInitial, kInitial,
kReading, kReading,
kDone, kDone,
+121 -374
View File
@@ -14,23 +14,18 @@
#include <errno.h> #include <errno.h>
#include "scsi.h" #include "scsi.h"
#include "CDEngine.h" #include "CDEngine.h"
#include "PlayList.h"
static PlayList sPlayList;
static CDAudioDevice sCDDevice;
const bigtime_t kPulseRate = 500000; const bigtime_t kPulseRate = 500000;
PeriodicWatcher::PeriodicWatcher(int devicefd) PeriodicWatcher::PeriodicWatcher(void)
: Notifier(),
devicefd(devicefd)
{ {
} }
PeriodicWatcher::PeriodicWatcher(BMessage *)
: devicefd(-1)
{
// not implemented yet
PRINT(("under construction"));
}
BHandler * BHandler *
PeriodicWatcher::RecipientHandler() const PeriodicWatcher::RecipientHandler() const
{ {
@@ -52,34 +47,29 @@ PeriodicWatcher::UpdateNow()
} }
PlayState::PlayState(int devicefd) PlayState::PlayState(CDEngine *engine)
: PeriodicWatcher(devicefd), : oldState(kNoCD),
oldState(kNoCD) fEngine(engine)
{ {
} }
bool bool
PlayState::UpdateState() PlayState::UpdateState(void)
{ {
// check the current CD play state and force a notification to CDState state = sCDDevice.GetState();
// be sent if it changed from last time if( state == kStopped && fEngine->GetState() == kPlaying)
scsi_position pos; {
status_t media_status = B_DEV_NO_MEDIA; // this means we have come to the end of a song, but probably not
ioctl(devicefd, B_GET_MEDIA_STATUS, &media_status, sizeof(media_status)); // the last song in the playlist
if (media_status != B_NO_ERROR) int16 next = sPlayList.GetNextTrack();
return CurrentState(kNoCD); if(next > 0)
{
status_t result = ioctl(devicefd, B_SCSI_GET_POSITION, &pos); sCDDevice.Play(next);
return CurrentState(kPlaying);
if (result != B_NO_ERROR) }
return CurrentState(kNoCD); }
else if ((!pos.position[1]) || (pos.position[1] >= 0x13) ||
((pos.position[1] == 0x12) && (!pos.position[6]))) return CurrentState(state);
return CurrentState(kStopped);
else if (pos.position[1] == 0x11)
return CurrentState(kPlaying);
else
return CurrentState(kPaused);
} }
CDState CDState
@@ -91,16 +81,16 @@ PlayState::GetState() const
bool bool
PlayState::CurrentState(CDState newState) PlayState::CurrentState(CDState newState)
{ {
if (newState != oldState) { if (newState != oldState)
{
oldState = newState; oldState = newState;
return true; return true;
} }
return false; return false;
} }
TrackState::TrackState(int devicefd) TrackState::TrackState(void)
: PeriodicWatcher(devicefd), : currentTrack(0)
currentTrack(0)
{ {
} }
@@ -113,46 +103,20 @@ TrackState::GetTrack() const
bool bool
TrackState::UpdateState() TrackState::UpdateState()
{ {
// check the current CD track number and force a notification to return CurrentState(sPlayList.GetCurrentTrack());
// be sent if it changed from last time
scsi_position pos;
status_t media_status = B_DEV_NO_MEDIA;
ioctl(devicefd, B_GET_MEDIA_STATUS, &media_status, sizeof(media_status));
if (media_status != B_NO_ERROR)
return CurrentState(-1);
status_t result = ioctl(devicefd, B_SCSI_GET_POSITION, &pos);
if (result != B_NO_ERROR)
return CurrentState(-1);
if (!pos.position[1] || pos.position[1] >= 0x13
|| (pos.position[1] == 0x12 && !pos.position[6]))
return CurrentState(0);
else
return CurrentState(pos.position[6]);
} }
int32 int32
TrackState::GetNumTracks() const TrackState::GetNumTracks() const
{ {
// get the number of tracks on the current CD return sCDDevice.CountTracks();
scsi_toc toc;
status_t result = ioctl(devicefd, B_SCSI_GET_TOC, &toc);
if (result != B_NO_ERROR)
return 0;
return toc.toc_data[3];
} }
bool bool
TrackState::CurrentState(int32 track) TrackState::CurrentState(int32 track)
{ {
if (track != currentTrack) { if (track != currentTrack)
{
currentTrack = track; currentTrack = track;
return true; return true;
} }
@@ -165,28 +129,19 @@ TimeState::UpdateState()
// check the current CD time and force a notification to // check the current CD time and force a notification to
// be sent if it changed from last time // be sent if it changed from last time
// currently only supports global time // currently only supports global time
scsi_position pos;
status_t media_status = B_DEV_NO_MEDIA;
ioctl(devicefd, B_GET_MEDIA_STATUS, &media_status, sizeof(media_status));
if (media_status != B_NO_ERROR)
return CurrentState(-1, -1, -1, -1);
status_t result = ioctl(devicefd, B_SCSI_GET_POSITION, &pos); cdaudio_time track;
cdaudio_time disc;
if (result != B_NO_ERROR) if(sCDDevice.GetTime(track,disc))
return CurrentState(-1, -1, -1, -1);
else
if ((!pos.position[1]) || (pos.position[1] >= 0x13) ||
((pos.position[1] == 0x12) && (!pos.position[6])))
{ {
// This indicates that we have a CD, but we are stopped. We *could* simply return CurrentState(disc.minutes, disc.seconds, track.minutes, track.seconds);
// return all 0's, but because the GUI expects valid numbers or -1's, we
// return -1's.
return CurrentState(-1, -1, -1, -1);
} }
else else
return CurrentState(pos.position[9], pos.position[10], pos.position[13], pos.position[14]); {
CurrentState(-1, -1, -1, -1);
return false;
}
} }
bool bool
@@ -218,17 +173,8 @@ TimeState::GetTrackTime(int32 &minutes, int32 &seconds) const
} }
CDContentWatcher::CDContentWatcher(int devicefd) CDContentWatcher::CDContentWatcher(void)
: PeriodicWatcher(devicefd), : cddbQuery("us.freedb.org", 888, true),
cddbQuery("us.freedb.org", 888, true),
discID(-1),
fReady(false)
{
}
CDContentWatcher::CDContentWatcher(BMessage *message)
: PeriodicWatcher(message),
cddbQuery("us.freedb.org", 888, true),
discID(-1), discID(-1),
fReady(false) fReady(false)
{ {
@@ -249,15 +195,13 @@ CDContentWatcher::UpdateState()
// from the old one whenever there is a CD in the drive // from the old one whenever there is a CD in the drive
if (engine->PlayStateWatcher()->GetState() != kNoCD) if (engine->PlayStateWatcher()->GetState() != kNoCD)
{ {
scsi_toc toc; newDiscID = sCDDevice.GetDiscID();
ioctl(devicefd, B_SCSI_GET_TOC, &toc);
newDiscID = cddbQuery.GetDiscID(&toc);
if (discID == newDiscID) if (discID == newDiscID)
return false; return false;
// We have changed CDs, so we are not ready until the CDDB lookup finishes // We have changed CDs, so we are not ready until the CDDB lookup finishes
cddbQuery.SetToCD(&toc); cddbQuery.SetToCD(sCDDevice.GetDrivePath());
fReady=false; fReady=false;
} }
@@ -274,21 +218,19 @@ CDContentWatcher::UpdateState()
return result; return result;
} }
VolumeState::VolumeState(int devicefd) VolumeState::VolumeState(void)
: PeriodicWatcher(devicefd), : fVolume(-1)
fVolume(-1)
{ {
} }
bool bool
VolumeState::UpdateState(void) VolumeState::UpdateState(void)
{ {
scsi_volume vol; uint8 volume = sCDDevice.GetVolume();
ioctl(devicefd, B_SCSI_GET_VOLUME, &vol); if(fVolume == volume)
if(vol.port0_volume == fVolume)
return false; return false;
fVolume = vol.port0_volume; fVolume = volume;
return true; return true;
} }
@@ -298,32 +240,16 @@ VolumeState::GetVolume(void) const
return fVolume; return fVolume;
} }
CDEngine::CDEngine(int devicefd) CDEngine::CDEngine(void)
: BHandler("CDEngine"), : BHandler("CDEngine"),
devicefd(devicefd), playState(this),
playState(devicefd), fEngineState(kStopped)
trackState(devicefd),
timeState(devicefd),
volumeState(devicefd),
contentWatcher(devicefd)
{
}
CDEngine::CDEngine(BMessage *message)
: BHandler(message),
devicefd(-1),
playState(message),
trackState(message),
timeState(message),
volumeState(message),
contentWatcher(devicefd)
{ {
sPlayList.SetTrackCount(sCDDevice.CountTracks());
} }
CDEngine::~CDEngine() CDEngine::~CDEngine()
{ {
if(devicefd >= 0)
close(devicefd);
} }
void void
@@ -337,249 +263,137 @@ CDEngine::AttachedToLooper(BLooper *looper)
contentWatcher.AttachedToLooper(this); contentWatcher.AttachedToLooper(this);
} }
void
CDEngine::PlayOrPause()
{
// if paused, or stopped, plays, if playing, pauses
playState.UpdateNow();
switch (playState.GetState()) {
case kNoCD:
Play();
return;
case kStopped:
Play();
break;
case kPaused:
PlayContinue();
break;
case kPlaying:
Pause();
break;
default:
break;
}
}
void void
CDEngine::Pause() CDEngine::Pause()
{ {
// pause the CD sCDDevice.Pause();
status_t result = ioctl(devicefd, B_SCSI_PAUSE_AUDIO); fEngineState = sCDDevice.GetState();
if (result != B_NO_ERROR) {
PRINT(("error %s pausing\n", strerror(errno)));
return;
}
} }
void void
CDEngine::Play() CDEngine::Play()
{ {
// play the CD if(fEngineState == kPaused)
if (playState.GetState() == kNoCD)
{ {
// no CD available, bail out sCDDevice.Resume();
ioctl(devicefd, B_LOAD_MEDIA, 0, 0); fEngineState = sCDDevice.GetState();
return;
} }
else
scsi_play_track track; if(fEngineState == kPlaying)
track.start_track = 1;
track.start_index = 1;
track.end_track = 99;
track.end_index = 1;
status_t result = ioctl(devicefd, B_SCSI_PLAY_TRACK, &track);
if (result != B_NO_ERROR)
{ {
PRINT(("error %s playing track\n", strerror(errno))); Pause();
return;
} }
} else
void
CDEngine::PlayContinue()
{
// continue after a pause
status_t result = ioctl(devicefd, B_SCSI_RESUME_AUDIO);
if (result != B_NO_ERROR)
{ {
PRINT(("error %s resuming\n", strerror(errno))); sCDDevice.Play(sPlayList.GetCurrentTrack());
return; fEngineState = sCDDevice.GetState();
} }
} }
void void
CDEngine::Stop() CDEngine::Stop()
{ {
// stop a playing CD fEngineState = kStopped;
status_t result = ioctl(devicefd, B_SCSI_STOP_AUDIO); sCDDevice.Stop();
if (result != B_OK)
{
PRINT(("error %s stopping\n", strerror(errno)));
return;
}
} }
void void
CDEngine::Eject() CDEngine::Eject()
{ {
// eject or load a CD sCDDevice.Eject();
status_t media_status = B_DEV_NO_MEDIA; fEngineState = sCDDevice.GetState();
// get the status first
ioctl(devicefd, B_GET_MEDIA_STATUS, &media_status, sizeof(media_status));
// if door open, load the media, else eject the CD
status_t result = ioctl(devicefd,
media_status == B_DEV_DOOR_OPEN ? B_LOAD_MEDIA : B_EJECT_DEVICE);
if (result != B_OK)
{
PRINT(("error %s ejecting\n", strerror(errno)));
return;
}
} }
void void
CDEngine::SkipOneForward() CDEngine::SkipOneForward()
{ {
// skip forward by one track int16 track = sPlayList.GetNextTrack();
CDState state = playState.GetState(); if(track <= 0)
if (state == kNoCD)
return; return;
bool wasPaused = state == kPaused CDState state = sCDDevice.GetState();
|| state == kStopped; if(state == kPlaying)
sCDDevice.Play(track);
SelectTrack(trackState.GetTrack() + 1);
if (wasPaused) // if(state == kPaused)
// make sure we don't start playing if we were paused before // sCDDevice.Pause();
Pause();
} }
void void
CDEngine::SkipOneBackward() CDEngine::SkipOneBackward()
{ {
// skip backward by one track int16 track = sPlayList.GetPreviousTrack();
CDState state = playState.GetState(); if(track <= 0)
if (state == kNoCD)
return; return;
bool wasPaused = state == kPaused
|| state == kStopped;
int32 track = trackState.GetTrack();
if (track > 1) CDState state = sCDDevice.GetState();
track--;
SelectTrack(track); if(state == kPlaying)
sCDDevice.Play(track);
if (wasPaused)
// make sure we don't start playing if we were paused before // if(state == kPaused)
Pause(); // sCDDevice.Pause();
} }
void void
CDEngine::StartSkippingBackward() CDEngine::StartSkippingBackward()
{ {
// start skipping sCDDevice.StartRewind();
CDState state = playState.GetState();
if (state == kNoCD)
return;
scsi_scan scan;
scan.direction = -1;
scan.speed = 1;
status_t result = ioctl(devicefd, B_SCSI_SCAN, &scan);
if (result != B_NO_ERROR) {
PRINT(("error %s skipping backward\n", strerror(errno)));
return;
}
} }
void void
CDEngine::StartSkippingForward() CDEngine::StartSkippingForward()
{ {
// start skipping sCDDevice.StartFastFwd();
CDState state = playState.GetState();
if (state == kNoCD)
return;
scsi_scan scan;
scan.direction = 1;
scan.speed = 1;
status_t result = ioctl(devicefd, B_SCSI_SCAN, &scan);
if (result != B_NO_ERROR) {
PRINT(("error %s skipping forward\n", strerror(errno)));
return;
}
} }
void void
CDEngine::StopSkipping() CDEngine::StopSkipping()
{ {
// stop skipping sCDDevice.StopFastFwd();
CDState state = playState.GetState();
if (state == kNoCD)
return;
scsi_scan scan;
scan.direction = 0;
scan.speed = 1;
status_t result = ioctl(devicefd, B_SCSI_SCAN, &scan);
if (result != B_NO_ERROR) {
PRINT(("error %s in stop skipping\n", strerror(errno)));
return;
}
result = ioctl(devicefd, B_SCSI_RESUME_AUDIO);
if (result != B_NO_ERROR) {
PRINT(("error %s resuming\n", strerror(errno)));
return;
}
} }
void void
CDEngine::SelectTrack(int32 trackNumber) CDEngine::SelectTrack(int32 trackNumber)
{ {
// go to a selected track sCDDevice.Play(trackNumber);
if (playState.GetState() == kNoCD)
return;
scsi_play_track track;
track.start_track = trackNumber;
track.start_index = 1;
track.end_track = 99;
track.end_index = 1;
status_t result = ioctl(devicefd, B_SCSI_PLAY_TRACK, &track);
if (result != B_NO_ERROR) {
PRINT(("error %s playing track\n", strerror(errno)));
return;
}
} }
void void
CDEngine::SetVolume(uint8 value) CDEngine::SetVolume(uint8 value)
{ {
scsi_volume vol; sCDDevice.SetVolume(value);
// change only port0's volume
vol.flags=2;
vol.port0_volume=value;
ioctl(devicefd,B_SCSI_SET_VOLUME,&vol);
} }
void
CDEngine::ToggleShuffle(void)
{
if(sPlayList.IsShuffled())
{
// If already in random mode, we will play to the end of the cd
int16 track = sPlayList.GetCurrentTrack();
sPlayList.SetShuffle(false);
sPlayList.SetStartingTrack(track);
sPlayList.SetTrackCount(sCDDevice.CountTracks());
}
else
{
// Not shuffled, so we will play the entire CD and randomly pick
sPlayList.SetTrackCount(sCDDevice.CountTracks());
sPlayList.SetShuffle(true);
}
}
void
CDEngine::ToggleRepeat(void)
{
if(sPlayList.IsLoop())
sPlayList.SetLoop(false);
else
sPlayList.SetLoop(true);
}
void void
CDEngine::DoPulse() CDEngine::DoPulse()
{ {
@@ -605,75 +419,8 @@ CDEngine::DoPulse()
void void
CDEngine::MessageReceived(BMessage *message) CDEngine::MessageReceived(BMessage *message)
{ {
if (message->what == 'slTk') { // handle observing
// handle message from menu selection if (!Notifier::HandleObservingMessages(message) &&
int32 track; !CDEngineFunctorFactory::DispatchIfFunctionObject(message))
if (message->FindInt32("track", &track) == B_OK)
SelectTrack(track);
} else
// handle observing
if (!Notifier::HandleObservingMessages(message)
&& !CDEngineFunctorFactory::DispatchIfFunctionObject(message))
BHandler::MessageReceived(message); BHandler::MessageReceived(message);
} }
static int
try_dir(const char *directory)
{
BDirectory dir;
dir.SetTo(directory);
if(dir.InitCheck() != B_NO_ERROR) {
return false;
}
dir.Rewind();
BEntry entry;
while(dir.GetNextEntry(&entry) >= 0) {
BPath path;
const char *name;
entry_ref e;
if(entry.GetPath(&path) != B_NO_ERROR)
continue;
name = path.Path();
if(entry.GetRef(&e) != B_NO_ERROR)
continue;
if(entry.IsDirectory()) {
if(strcmp(e.name, "floppy") == 0)
continue; // ignore floppy (it is not silent)
int devfd = try_dir(name);
if(devfd >= 0)
return devfd;
}
else {
int devfd;
device_geometry g;
if(strcmp(e.name, "raw") != 0)
continue; // ignore partitions
devfd = open(name, O_RDONLY);
if(devfd < 0)
continue;
if(ioctl(devfd, B_GET_GEOMETRY, &g, sizeof(g)) >= 0) {
if(g.device_type == B_CD)
{
return devfd;
}
}
close(devfd);
}
}
return B_ERROR;
}
int
CDEngine::FindCDPlayerDevice()
{
return try_dir("/dev/disk");
}
+125 -138
View File
@@ -15,222 +15,209 @@
#include <vector> #include <vector>
#include "CDAudioDevice.h"
#include "Observer.h" #include "Observer.h"
#include "FunctionObjectMessage.h" #include "FunctionObjectMessage.h"
#include "CDDBSupport.h" #include "CDDBSupport.h"
#include "PlayList.h"
class CDEngine; class CDEngine;
class PeriodicWatcher : public Notifier {
// watcher sits somewhere were it can get pulses and makes sure // watcher sits somewhere were it can get pulses and makes sure
// notices get sent if state changes // notices get sent if state changes
class PeriodicWatcher : public Notifier
{
public: public:
PeriodicWatcher(int devicefd); PeriodicWatcher(void);
PeriodicWatcher(BMessage *); virtual ~PeriodicWatcher() {}
virtual ~PeriodicWatcher() {}
void DoPulse(); void DoPulse();
void UpdateNow(); void UpdateNow();
void AttachedToLooper(CDEngine *engine) void AttachedToLooper(CDEngine *engine)
{ this->engine = engine; } { this->engine = engine; }
virtual BHandler *RecipientHandler() const;
virtual BHandler *RecipientHandler() const;
protected: protected:
virtual bool UpdateState() = 0; virtual bool UpdateState() = 0;
int devicefd; CDEngine *engine;
CDEngine *engine;
}; };
enum CDState { // this watcher sends notices to observers that are interrested
kNoCD=0, // about play state changes
kStopped, class PlayState : public PeriodicWatcher
kPaused, {
kPlaying,
kSkipping
};
class PlayState : public PeriodicWatcher {
// this watcher sends notices to observers that are interrested
// about play state changes
public: public:
PlayState(int devicefd); PlayState(CDEngine *engine);
PlayState(BMessage *message)
: PeriodicWatcher(message)
{}
CDState GetState() const; CDState GetState() const;
private: private:
bool UpdateState(); bool UpdateState();
bool CurrentState(CDState); bool CurrentState(CDState);
CDState oldState; CDState oldState;
CDEngine *fEngine;
}; };
class TrackState : public PeriodicWatcher { // this watcher sends notices to observers that are interested
// this watcher sends notices to observers that are interested // about changes in the current track
// about changes in the current track class TrackState : public PeriodicWatcher
{
public: public:
TrackState(int devicefd); TrackState(void);
TrackState(BMessage *message)
: PeriodicWatcher(message)
{}
int32 GetTrack() const; int32 GetTrack() const;
int32 GetNumTracks() const; int32 GetNumTracks() const;
private: private:
bool UpdateState(); bool UpdateState();
bool CurrentState(int32); bool CurrentState(int32);
int32 currentTrack; int32 currentTrack;
}; };
class TimeState : public PeriodicWatcher { // this watcher sends notices to observers that are interested
// this watcher sends notices to observers that are interested // about changes in the current time
// about changes in the current time class TimeState : public PeriodicWatcher
{
public: public:
TimeState(int devicefd) TimeState(void) : PeriodicWatcher() { }
: PeriodicWatcher(devicefd)
{ }
TimeState(BMessage *message)
: PeriodicWatcher(message)
{}
void GetDiscTime(int32 &minutes, int32 &seconds) const; void GetDiscTime(int32 &minutes, int32 &seconds) const;
void GetTrackTime(int32 &minutes, int32 &seconds) const; void GetTrackTime(int32 &minutes, int32 &seconds) const;
private: private:
bool UpdateState(); bool UpdateState();
bool CurrentState(int32 dmin, int32 dsec, int32 tmin, int32 tsec); bool CurrentState(int32 dmin, int32 dsec, int32 tmin, int32 tsec);
int32 fDiscMinutes; int32 fDiscMinutes;
int32 fDiscSeconds; int32 fDiscSeconds;
int32 fTrackMinutes; int32 fTrackMinutes;
int32 fTrackSeconds; int32 fTrackSeconds;
}; };
class CDContentWatcher : public PeriodicWatcher { class CDContentWatcher : public PeriodicWatcher
{
public: public:
CDContentWatcher(int devicefd); CDContentWatcher(void);
CDContentWatcher(BMessage *message);
bool GetContent(BString *title, vector<BString> *tracks); bool GetContent(BString *title, vector<BString> *tracks);
private: private:
bool UpdateState(); bool UpdateState();
CDDBQuery cddbQuery; CDDBQuery cddbQuery;
int32 discID; int32 discID;
bool fReady; bool fReady;
}; };
class VolumeState : public PeriodicWatcher { // this watcher sends notices to observers that are interested
// this watcher sends notices to observers that are interested // about changes in the current volume
// about changes in the current volume // currently not used yet
// currently not used yet class VolumeState : public PeriodicWatcher
{
public: public:
VolumeState(int devicefd); VolumeState(void);
VolumeState(BMessage *message)
: PeriodicWatcher(message) bool UpdateState();
{} int32 GetVolume() const;
bool UpdateState();
int32 GetVolume() const;
private: private:
int32 fVolume; int32 fVolume;
}; };
class CDEngine : public BHandler { // The CD engine defines all the different CD control calls; also,
// The CD engine defines all the different CD control calls; also, // it hosts the different state watchers and helps them send notices
// it hosts the different state watchers and helps them send notices // to observers about the CD state changes
// to observers about the CD state changes class CDEngine : public BHandler
{
public: public:
CDEngine(int devicefd); CDEngine(void);
CDEngine(BMessage *);
virtual ~CDEngine(); virtual ~CDEngine();
// observing supprt // observing support
virtual void MessageReceived(BMessage *); virtual void MessageReceived(BMessage *);
void AttachedToLooper(BLooper *); void AttachedToLooper(BLooper *);
void DoPulse(); void DoPulse();
// control calls // control calls
void PlayOrPause(); void Play();
void PlayContinue(); void Pause();
void Play(); void Stop();
void Pause(); void Eject();
void Stop(); void SkipOneForward();
void Eject(); void SkipOneBackward();
void SkipOneForward(); void StartSkippingBackward();
void SkipOneBackward(); void StartSkippingForward();
void StartSkippingBackward(); void StopSkipping();
void StartSkippingForward(); void SelectTrack(int32);
void StopSkipping();
void SelectTrack(int32); void SetVolume(uint8 value);
void ToggleShuffle(void);
void ToggleRepeat(void);
void SetVolume(uint8 value); CDState GetState(void) const { return fEngineState; }
// to find the current Track, you may call the GetTrack function
// TrackState defines
TrackState *TrackStateWatcher() TrackState *TrackStateWatcher()
{ return &trackState; } { return &trackState; }
// to find the current Track, you may call the GetTrack function
// TrackState defines
// to find the current play state, you may call the GetState function
// PlayState defines
PlayState *PlayStateWatcher() PlayState *PlayStateWatcher()
{ return &playState; } { return &playState; }
// to find the current play state, you may call the GetState function
// PlayState defines
// to find the current location on the CD, you may call the GetTime function
// TimeState defines
TimeState *TimeStateWatcher() TimeState *TimeStateWatcher()
{ return &timeState; } { return &timeState; }
// to find the current location on the CD, you may call the GetTime function
// TimeState defines
CDContentWatcher *ContentWatcher() CDContentWatcher *ContentWatcher()
{ return &contentWatcher; } { return &contentWatcher; }
// to find the current location on the CD, you may call the GetVolume function
// VolumeState defines
VolumeState *VolumeStateWatcher() VolumeState *VolumeStateWatcher()
// to find the current location on the CD, you may call the GetVolume function
// VolumeState defines
{ return &volumeState; } { return &volumeState; }
static int FindCDPlayerDevice();
private: private:
int devicefd; PlayState playState;
TrackState trackState;
TimeState timeState;
VolumeState volumeState;
CDContentWatcher contentWatcher;
PlayState playState; bigtime_t lastPulse;
TrackState trackState;
TimeState timeState; CDState fEngineState;
VolumeState volumeState;
CDContentWatcher contentWatcher;
bigtime_t lastPulse;
}; };
// some function object glue // some function object glue
class CDEngineFunctorFactory : public FunctorFactoryCommon { class CDEngineFunctorFactory : public FunctorFactoryCommon
{
public: public:
static BMessage *NewFunctorMessage(void (CDEngine::*func)(), static BMessage *NewFunctorMessage(void (CDEngine::*func)(),
CDEngine *target) CDEngine *target)
{ {
PlainMemberFunctionObject<void (CDEngine::*)(), PlainMemberFunctionObject<void (CDEngine::*)(),
CDEngine> tmp(func, target); CDEngine> tmp(func, target);
return NewMessage(&tmp); return NewMessage(&tmp);
} }
static BMessage *NewFunctorMessage(void (CDEngine::*func)(ulong), static BMessage *NewFunctorMessage(void (CDEngine::*func)(ulong),
CDEngine *target, ulong param) CDEngine *target, ulong param)
{ {
SingleParamMemberFunctionObject<void (CDEngine::*)(ulong), SingleParamMemberFunctionObject<void (CDEngine::*)(ulong),
CDEngine, ulong> tmp(func, target, param); CDEngine, ulong> tmp(func, target, param);
return NewMessage(&tmp); return NewMessage(&tmp);
} }
}; };
@@ -21,7 +21,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "CDButton.h" #include "CDPlayer.h"
#include "DrawButton.h" #include "DrawButton.h"
#include "TwoStateDrawButton.h" #include "TwoStateDrawButton.h"
#include <TranslationUtils.h> #include <TranslationUtils.h>
@@ -45,13 +45,13 @@ enum
M_SET_CD_TITLE M_SET_CD_TITLE
}; };
CDButton::CDButton(BRect frame, const char *name, uint32 resizeMask, uint32 flags) CDPlayer::CDPlayer(BRect frame, const char *name, uint32 resizeMask, uint32 flags)
: BView(frame, name, resizeMask, flags | B_FRAME_EVENTS) : BView(frame, name, resizeMask, flags | B_FRAME_EVENTS)
{ {
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
// TODO: Support multiple CD drives // TODO: Support multiple CD drives
engine = new CDEngine(CDEngine::FindCDPlayerDevice()); engine = new CDEngine;
BuildGUI(); BuildGUI();
@@ -66,18 +66,29 @@ CDButton::CDButton(BRect frame, const char *name, uint32 resizeMask, uint32 flag
fFastFwd->SetEnabled(false); fFastFwd->SetEnabled(false);
fRewind->SetEnabled(false); fRewind->SetEnabled(false);
fSave->SetEnabled(false); fSave->SetEnabled(false);
fCurrentTrack->SetHighColor(fStopColor);
} }
} }
CDButton::~CDButton() CDPlayer::~CDPlayer()
{ {
engine->Stop(); engine->Stop();
delete engine; delete engine;
} }
void CDButton::BuildGUI(void) void CDPlayer::BuildGUI(void)
{ {
fStopColor.red = 80;
fStopColor.green = 164;
fStopColor.blue = 80;
fStopColor.alpha = 255;
fPlayColor.red = 40;
fPlayColor.green = 230;
fPlayColor.blue = 40;
fPlayColor.alpha = 255;
BRect r(5,5,230,25); BRect r(5,5,230,25);
// Assemble the CD Title box // Assemble the CD Title box
@@ -88,7 +99,7 @@ void CDButton::BuildGUI(void)
view->SetViewColor(20,20,20); view->SetViewColor(20,20,20);
fCDBox->AddChild(view); fCDBox->AddChild(view);
fCDTitle = new BStringView(view->Bounds(),"CDTitle",""); fCDTitle = new BStringView(view->Bounds(),"CDTitle","", B_FOLLOW_ALL);
view->AddChild(fCDTitle); view->AddChild(fCDTitle);
fCDTitle->SetHighColor(200,200,200); fCDTitle->SetHighColor(200,200,200);
fCDTitle->SetFont(be_bold_font); fCDTitle->SetFont(be_bold_font);
@@ -103,7 +114,7 @@ void CDButton::BuildGUI(void)
fCurrentTrack = new BStringView( view->Bounds(),"TrackNumber","Track:",B_FOLLOW_ALL); fCurrentTrack = new BStringView( view->Bounds(),"TrackNumber","Track:",B_FOLLOW_ALL);
view->AddChild(fCurrentTrack); view->AddChild(fCurrentTrack);
fCurrentTrack->SetHighColor(40,230,40); fCurrentTrack->SetHighColor(fPlayColor);
fCurrentTrack->SetFont(be_bold_font); fCurrentTrack->SetFont(be_bold_font);
r.OffsetBy(0, r.Height() + 5); r.OffsetBy(0, r.Height() + 5);
@@ -142,7 +153,6 @@ void CDButton::BuildGUI(void)
fStop->SetDisabled(BTranslationUtils::GetBitmap(B_PNG_FORMAT,"stop_disabled")); fStop->SetDisabled(BTranslationUtils::GetBitmap(B_PNG_FORMAT,"stop_disabled"));
AddChild(fStop); AddChild(fStop);
// TODO: Play is a special button. Implement as two-state buttons
fPlay = new DrawButton( BRect(0,0,1,1), "Play", BTranslationUtils::GetBitmap(B_PNG_FORMAT,"play_up"), fPlay = new DrawButton( BRect(0,0,1,1), "Play", BTranslationUtils::GetBitmap(B_PNG_FORMAT,"play_up"),
BTranslationUtils::GetBitmap(B_PNG_FORMAT,"play_down"), BTranslationUtils::GetBitmap(B_PNG_FORMAT,"play_down"),
new BMessage(M_PLAY), B_FOLLOW_BOTTOM, B_WILL_DRAW); new BMessage(M_PLAY), B_FOLLOW_BOTTOM, B_WILL_DRAW);
@@ -201,29 +211,30 @@ void CDButton::BuildGUI(void)
AddChild(fSave); AddChild(fSave);
fSave->SetEnabled(false); fSave->SetEnabled(false);
// TODO: Shuffle and Repeat are special buttons. Implement as two-state buttons fShuffle = new TwoStateDrawButton( BRect(0,0,1,1), "Shuffle",
fShuffle = new DrawButton( BRect(0,0,1,1), "Shuffle", BTranslationUtils::GetBitmap(B_PNG_FORMAT,"shuffle_up"), BTranslationUtils::GetBitmap(B_PNG_FORMAT,"shuffle_up"),
BTranslationUtils::GetBitmap(B_PNG_FORMAT,"shuffle_down"), new BMessage(M_SHUFFLE), BTranslationUtils::GetBitmap(B_PNG_FORMAT,"shuffle_down"),
B_FOLLOW_NONE, B_WILL_DRAW); BTranslationUtils::GetBitmap(B_PNG_FORMAT,"shuffle_up_on"),
BTranslationUtils::GetBitmap(B_PNG_FORMAT,"shuffle_down_on"),
new BMessage(M_SHUFFLE), B_FOLLOW_NONE, B_WILL_DRAW);
fShuffle->ResizeToPreferred(); fShuffle->ResizeToPreferred();
fShuffle->MoveTo(fSave->Frame().right + 2, Bounds().bottom - 5 - fShuffle->Frame().Height()); fShuffle->MoveTo(fSave->Frame().right + 2, Bounds().bottom - 5 - fShuffle->Frame().Height());
fShuffle->SetDisabled(BTranslationUtils::GetBitmap(B_PNG_FORMAT,"shuffle_disabled"));
AddChild(fShuffle); AddChild(fShuffle);
fShuffle->SetEnabled(false);
fRepeat = new DrawButton( BRect(0,0,1,1), "Repeat", BTranslationUtils::GetBitmap(B_PNG_FORMAT,"repeat_up"), fRepeat = new TwoStateDrawButton( BRect(0,0,1,1), "Repeat",
BTranslationUtils::GetBitmap(B_PNG_FORMAT,"repeat_down"), new BMessage(M_REPEAT), BTranslationUtils::GetBitmap(B_PNG_FORMAT,"repeat_up"),
B_FOLLOW_NONE, B_WILL_DRAW); BTranslationUtils::GetBitmap(B_PNG_FORMAT,"repeat_down"),
BTranslationUtils::GetBitmap(B_PNG_FORMAT,"repeat_up_on"),
BTranslationUtils::GetBitmap(B_PNG_FORMAT,"repeat_down_on"),
new BMessage(M_REPEAT), B_FOLLOW_NONE, B_WILL_DRAW);
fRepeat->ResizeToPreferred(); fRepeat->ResizeToPreferred();
fRepeat->MoveTo(fShuffle->Frame().right + 2, Bounds().bottom - 5 - fRepeat->Frame().Height()); fRepeat->MoveTo(fShuffle->Frame().right + 2, Bounds().bottom - 5 - fRepeat->Frame().Height());
fRepeat->SetDisabled(BTranslationUtils::GetBitmap(B_PNG_FORMAT,"repeat_disabled"));
AddChild(fRepeat); AddChild(fRepeat);
fRepeat->SetEnabled(false);
} }
void void
CDButton::MessageReceived(BMessage *msg) CDPlayer::MessageReceived(BMessage *msg)
{ {
if(msg->WasDropped()) if(msg->WasDropped())
{ {
@@ -292,7 +303,7 @@ CDButton::MessageReceived(BMessage *msg)
} }
case M_STOP: case M_STOP:
{ {
if(engine->PlayStateWatcher()->GetState()==kPlaying) if(engine->GetState()==kPlaying)
{ {
fPlay->SetBitmaps(BTranslationUtils::GetBitmap(B_PNG_FORMAT,"play_up"), fPlay->SetBitmaps(BTranslationUtils::GetBitmap(B_PNG_FORMAT,"play_up"),
BTranslationUtils::GetBitmap(B_PNG_FORMAT,"play_down")); BTranslationUtils::GetBitmap(B_PNG_FORMAT,"play_down"));
@@ -304,7 +315,7 @@ CDButton::MessageReceived(BMessage *msg)
{ {
// If we are currently playing, then we will be showing // If we are currently playing, then we will be showing
// the pause images and will want to switch back to the play images // the pause images and will want to switch back to the play images
if(engine->PlayStateWatcher()->GetState()==kPlaying) if(engine->GetState()==kPlaying)
{ {
fPlay->SetBitmaps(BTranslationUtils::GetBitmap(B_PNG_FORMAT,"play_up"), fPlay->SetBitmaps(BTranslationUtils::GetBitmap(B_PNG_FORMAT,"play_up"),
BTranslationUtils::GetBitmap(B_PNG_FORMAT,"play_down")); BTranslationUtils::GetBitmap(B_PNG_FORMAT,"play_down"));
@@ -315,7 +326,7 @@ CDButton::MessageReceived(BMessage *msg)
fPlay->SetBitmaps(BTranslationUtils::GetBitmap(B_PNG_FORMAT,"pause_up"), fPlay->SetBitmaps(BTranslationUtils::GetBitmap(B_PNG_FORMAT,"pause_up"),
BTranslationUtils::GetBitmap(B_PNG_FORMAT,"pause_down")); BTranslationUtils::GetBitmap(B_PNG_FORMAT,"pause_down"));
} }
engine->PlayOrPause(); engine->Play();
break; break;
} }
case M_NEXT_TRACK: case M_NEXT_TRACK:
@@ -350,12 +361,12 @@ CDButton::MessageReceived(BMessage *msg)
} }
case M_SHUFFLE: case M_SHUFFLE:
{ {
// TODO: Implement engine->ToggleShuffle();
break; break;
} }
case M_REPEAT: case M_REPEAT:
{ {
// TODO: Implement engine->ToggleRepeat();
break; break;
} }
default: default:
@@ -372,7 +383,7 @@ CDButton::MessageReceived(BMessage *msg)
} }
void void
CDButton::NoticeChange(Notifier *notifier) CDPlayer::NoticeChange(Notifier *notifier)
{ {
PlayState *ps; PlayState *ps;
TrackState *trs; TrackState *trs;
@@ -402,10 +413,10 @@ CDButton::NoticeChange(Notifier *notifier)
fRewind->SetEnabled(false); fRewind->SetEnabled(false);
fSave->SetEnabled(false); fSave->SetEnabled(false);
} }
fCurrentTrack->SetHighColor(fStopColor);
fCurrentTrack->SetText("Track: "); fCurrentTrack->SetText("Track: ");
fTrackTime->SetText("Track --:-- / --:--"); fTrackTime->SetText("Track --:-- / --:--");
fDiscTime->SetText("Disc --:-- / --:--"); fDiscTime->SetText("Disc --:-- / --:--");
printf("Notification: No CD\n");
break; break;
} }
case kStopped: case kStopped:
@@ -418,12 +429,14 @@ CDButton::NoticeChange(Notifier *notifier)
fPrevTrack->SetEnabled(true); fPrevTrack->SetEnabled(true);
fFastFwd->SetEnabled(true); fFastFwd->SetEnabled(true);
fRewind->SetEnabled(true); fRewind->SetEnabled(true);
fSave->SetEnabled(true);
// TODO: Enable when Save is implemented
// fSave->SetEnabled(true);
} }
fCurrentTrack->SetText("Track: "); fCurrentTrack->SetHighColor(fStopColor);
fCurrentTrack->Invalidate();
fTrackTime->SetText("Track --:-- / --:--"); fTrackTime->SetText("Track --:-- / --:--");
fDiscTime->SetText("Disc --:-- / --:--"); fDiscTime->SetText("Disc --:-- / --:--");
printf("Notification: CD Stopped\n");
break; break;
} }
case kPaused: case kPaused:
@@ -437,9 +450,11 @@ CDButton::NoticeChange(Notifier *notifier)
fPrevTrack->SetEnabled(true); fPrevTrack->SetEnabled(true);
fFastFwd->SetEnabled(true); fFastFwd->SetEnabled(true);
fRewind->SetEnabled(true); fRewind->SetEnabled(true);
fSave->SetEnabled(true);
// TODO: Enable when Save is implemented
// fSave->SetEnabled(true);
} }
printf("Notification: CD Paused\n"); fCurrentTrack->SetHighColor(fPlayColor);
break; break;
} }
case kPlaying: case kPlaying:
@@ -452,9 +467,12 @@ CDButton::NoticeChange(Notifier *notifier)
fPrevTrack->SetEnabled(true); fPrevTrack->SetEnabled(true);
fFastFwd->SetEnabled(true); fFastFwd->SetEnabled(true);
fRewind->SetEnabled(true); fRewind->SetEnabled(true);
fSave->SetEnabled(true);
// TODO: Enable when Save is implemented
// fSave->SetEnabled(true);
} }
printf("Notification: CD Playing\n"); fCurrentTrack->SetHighColor(fPlayColor);
fCurrentTrack->Invalidate();
break; break;
} }
case kSkipping: case kSkipping:
@@ -467,9 +485,11 @@ CDButton::NoticeChange(Notifier *notifier)
fPrevTrack->SetEnabled(true); fPrevTrack->SetEnabled(true);
fFastFwd->SetEnabled(true); fFastFwd->SetEnabled(true);
fRewind->SetEnabled(true); fRewind->SetEnabled(true);
fSave->SetEnabled(true);
// TODO: Enable when Save is implemented
// fSave->SetEnabled(true);
} }
printf("Notification: CD Skipping\n"); fCurrentTrack->SetHighColor(fStopColor);
break; break;
} }
default: default:
@@ -502,7 +522,7 @@ CDButton::NoticeChange(Notifier *notifier)
} }
void void
CDButton::UpdateCDInfo(void) CDPlayer::UpdateCDInfo(void)
{ {
BString CDName, currentTrackName; BString CDName, currentTrackName;
vector<BString> trackNames; vector<BString> trackNames;
@@ -525,7 +545,7 @@ CDButton::UpdateCDInfo(void)
} }
void void
CDButton::UpdateTimeInfo(void) CDPlayer::UpdateTimeInfo(void)
{ {
int32 min,sec; int32 min,sec;
char string[1024]; char string[1024];
@@ -546,7 +566,7 @@ CDButton::UpdateTimeInfo(void)
} }
void void
CDButton::AttachedToWindow() CDPlayer::AttachedToWindow()
{ {
// start observing // start observing
engine->AttachedToLooper(Window()); engine->AttachedToLooper(Window());
@@ -570,7 +590,7 @@ CDButton::AttachedToWindow()
} }
void void
CDButton::FrameResized(float new_width, float new_height) CDPlayer::FrameResized(float new_width, float new_height)
{ {
// We implement this method because there is no resizing mode to split the window's // We implement this method because there is no resizing mode to split the window's
// width into two and have a box fill each half // width into two and have a box fill each half
@@ -595,13 +615,13 @@ CDButton::FrameResized(float new_width, float new_height)
} }
void void
CDButton::Pulse() CDPlayer::Pulse()
{ {
engine->DoPulse(); engine->DoPulse();
} }
void void
CDButton::SetBitmap(BBitmap *bitmap) CDPlayer::SetBitmap(BBitmap *bitmap)
{ {
if(!bitmap) if(!bitmap)
{ {
@@ -615,14 +635,14 @@ CDButton::SetBitmap(BBitmap *bitmap)
Invalidate(); Invalidate();
} }
class CDButtonWindow : public BWindow class CDPlayerWindow : public BWindow
{ {
public: public:
CDButtonWindow(void); CDPlayerWindow(void);
bool QuitRequested(void); bool QuitRequested(void);
}; };
CDButtonWindow::CDButtonWindow(void) CDPlayerWindow::CDPlayerWindow(void)
: BWindow(BRect (100, 100, 610, 200), "CD Player", B_TITLED_WINDOW, B_NOT_V_RESIZABLE | : BWindow(BRect (100, 100, 610, 200), "CD Player", B_TITLED_WINDOW, B_NOT_V_RESIZABLE |
B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS) B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS)
{ {
@@ -634,17 +654,17 @@ CDButtonWindow::CDButtonWindow(void)
SetSizeLimits(wmin,wmax,hmin,hmax); SetSizeLimits(wmin,wmax,hmin,hmax);
} }
bool CDButtonWindow::QuitRequested(void) bool CDPlayerWindow::QuitRequested(void)
{ {
be_app->PostMessage(B_QUIT_REQUESTED); be_app->PostMessage(B_QUIT_REQUESTED);
return true; return true;
} }
CDButtonApplication::CDButtonApplication() CDPlayerApplication::CDPlayerApplication()
: BApplication("application/x-vnd.Haiku-CDPlayer") : BApplication("application/x-vnd.Haiku-CDPlayer")
{ {
BWindow *window = new CDButtonWindow(); BWindow *window = new CDPlayerWindow();
BView *button = new CDButton(window->Bounds(), "CD"); BView *button = new CDPlayer(window->Bounds(), "CD");
window->AddChild(button); window->AddChild(button);
window->Show(); window->Show();
} }
@@ -652,7 +672,7 @@ CDButtonApplication::CDButtonApplication()
int int
main(int, char **argv) main(int, char **argv)
{ {
(new CDButtonApplication())->Run(); (new CDPlayerApplication())->Run();
return 0; return 0;
} }
@@ -4,8 +4,8 @@
*/ */
// This defines the replicant button // This defines the replicant button
#ifndef __CD_BUTTON__ #ifndef CDPLAYER_H
#define __CD_BUTTON__ #define CDPLAYER_H
#include <Application.h> #include <Application.h>
#include <View.h> #include <View.h>
@@ -16,21 +16,20 @@
#include <TextControl.h> #include <TextControl.h>
#include <StringView.h> #include <StringView.h>
#include "PlayList.h"
#include "Observer.h" #include "Observer.h"
#include "CDEngine.h" #include "CDEngine.h"
class DrawButton; class DrawButton;
class TwoStateDrawButton; class TwoStateDrawButton;
class CDButton : public BView, private Observer class CDPlayer : public BView, private Observer
{ {
public: public:
CDButton(BRect frame, const char *name, CDPlayer(BRect frame, const char *name,
uint32 resizeMask = B_FOLLOW_ALL, uint32 resizeMask = B_FOLLOW_ALL,
uint32 flags = B_WILL_DRAW | B_NAVIGABLE | uint32 flags = B_WILL_DRAW | B_NAVIGABLE |
B_PULSE_NEEDED); B_PULSE_NEEDED);
virtual ~CDButton(); virtual ~CDPlayer();
void BuildGUI(void); void BuildGUI(void);
@@ -46,36 +45,46 @@ public:
virtual void NoticeChange(Notifier *); virtual void NoticeChange(Notifier *);
private: private:
void UpdateCDInfo(void); void UpdateCDInfo(void);
void UpdateTimeInfo(void); void UpdateTimeInfo(void);
void SetBitmap(BBitmap *bitmap); void SetBitmap(BBitmap *bitmap);
CDEngine *engine; CDEngine *engine;
DrawButton *fStop, *fPlay, *fNextTrack, *fPrevTrack; DrawButton *fStop,
DrawButton *fFastFwd, *fRewind, *fEject, *fSave, *fShuffle, *fRepeat; *fPlay,
*fNextTrack,
*fPrevTrack,
BSlider *fVolume; *fFastFwd,
*fRewind,
BStringView *fCDTitle; *fEject,
BStringView *fCurrentTrack; *fSave;
BStringView *fTrackTime;
BStringView *fDiscTime; TwoStateDrawButton *fShuffle,
*fRepeat;
BBox *fCDBox;
BBox *fTrackBox; BSlider *fVolume;
BBox *fTimeBox;
BStringView *fCDTitle,
CDState fCDState; *fCurrentTrack,
PlayList fPlaylist; *fTrackTime,
*fDiscTime;
BBox *fCDBox,
*fTrackBox,
*fTimeBox;
CDState fCDState;
rgb_color fStopColor;
rgb_color fPlayColor;
}; };
class CDButtonApplication : public BApplication class CDPlayerApplication : public BApplication
{ {
public: public:
CDButtonApplication(); CDPlayerApplication();
}; };
+2 -1
View File
@@ -2,9 +2,10 @@ SubDir OBOS_TOP src apps cdplayer ;
AddResources CDPlayer : CDPlayer.rdef ; AddResources CDPlayer : CDPlayer.rdef ;
App CDPlayer : App CDPlayer :
CDButton.cpp CDAudioDevice.cpp
CDDBSupport.cpp CDDBSupport.cpp
CDEngine.cpp CDEngine.cpp
CDPlayer.cpp
DrawButton.cpp DrawButton.cpp
FunctionObjectMessage.cpp FunctionObjectMessage.cpp
Observer.cpp Observer.cpp
+113 -14
View File
@@ -3,20 +3,36 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
PlayList::PlayList(int16 tracks) PlayList::PlayList(int16 count, int16 start)
: fTrackCount(0), : fTrackCount(count),
fTrackIndex(0), fTrackIndex(0),
fStartingTrack(start),
fRandom(false), fRandom(false),
fLoop(false) fLoop(false)
{ {
srand(real_time_clock_usecs()); srand(real_time_clock_usecs());
SetTrackCount(tracks); if(fTrackCount < 0)
fTrackCount = 0;
else
if(fTrackCount > 500)
fTrackCount = 500;
if(fStartingTrack >= fTrackCount)
fStartingTrack = fTrackCount - 1;
if(fStartingTrack < 1)
fStartingTrack = 1;
memset(fTrackList,-1,500);
Unrandomize();
} }
void void
PlayList::SetTrackCount(const int16 &count) PlayList::SetTrackCount(const int16 &count)
{ {
fLocker.Lock();
if(count < 0) if(count < 0)
fTrackCount = 0; fTrackCount = 0;
else else
@@ -27,57 +43,140 @@ PlayList::SetTrackCount(const int16 &count)
memset(fTrackList,-1,500); memset(fTrackList,-1,500);
SetShuffle(IsShuffled()); SetShuffle(IsShuffled());
fLocker.Unlock();
}
void
PlayList::SetStartingTrack(const int16 &start)
{
fLocker.Lock();
if(start >= TrackCount())
fStartingTrack = TrackCount() - 1;
else
if(start < 1)
fStartingTrack = 1;
else
fStartingTrack = start;
fLocker.Unlock();
} }
void void
PlayList::Rewind(void) PlayList::Rewind(void)
{ {
fLocker.Lock();
fTrackIndex = 0; fTrackIndex = 0;
fLocker.Unlock();
} }
void void
PlayList::SetShuffle(const bool &random) PlayList::SetShuffle(const bool &random)
{ {
fLocker.Lock();
if(random) if(random)
Randomize(); Randomize();
else else
Unrandomize(); Unrandomize();
fLocker.Unlock();
} }
void void
PlayList::SetLoop(const bool &loop) PlayList::SetLoop(const bool &loop)
{ {
fLocker.Lock();
fLoop = loop; fLoop = loop;
fLocker.Unlock();
} }
int16
PlayList::GetCurrentTrack(void)
{
fLocker.Lock();
int16 value = fTrackList[fTrackIndex];
fLocker.Unlock();
return value;
}
int16 int16
PlayList::GetNextTrack(void) PlayList::GetNextTrack(void)
{ {
if(fTrackCount < 1) fLocker.Lock();
return -1;
if(fTrackIndex == fTrackCount) if(fTrackCount < 1)
{
fLocker.Unlock();
return -1;
}
if(fTrackIndex > (fTrackCount - fStartingTrack))
{ {
if(fLoop) if(fLoop)
fTrackIndex = 0; fTrackIndex = 0;
else else
{
fLocker.Unlock();
return -1; return -1;
}
}
else
fTrackIndex++;
int16 value = fTrackList[fTrackIndex];
fLocker.Unlock();
return value;
}
int16
PlayList::GetPreviousTrack(void)
{
fLocker.Lock();
if(fTrackCount < 1)
{
fLocker.Unlock();
return -1;
} }
return fTrackList[fTrackIndex++]; if(fTrackIndex == 0)
{
if(fLoop)
fTrackIndex = (fTrackCount - fStartingTrack);
else
{
fLocker.Unlock();
return -1;
}
}
else
fTrackIndex--;
int16 value = fTrackList[fTrackIndex];
fLocker.Unlock();
return value;
} }
void void
PlayList::Randomize(void) PlayList::Randomize(void)
{ {
// Reinitialize the count // Reinitialize the count
for(int16 i=0; i<fTrackCount; i++) for(int16 i=fStartingTrack; i<=fTrackCount; i++)
fTrackList[i] = i; fTrackList[i - fStartingTrack] = i;
// There are probably *much* better ways to do this, // There are probably *much* better ways to do this,
// but this is the only one I could think of. :( // but this is the only one I could think of. :(
int32 swapcount = fTrackCount * 2; int32 listcount = (fTrackCount - fStartingTrack);
int32 swapcount = listcount* 2;
int16 temp, first, second; int16 temp, first, second;
for(int32 i=0; i< swapcount; i++) for(int32 i=0; i< swapcount; i++)
@@ -86,8 +185,8 @@ PlayList::Randomize(void)
// This way we are sure to not have any duplicates and still have // This way we are sure to not have any duplicates and still have
// all tracks eventually be played. // all tracks eventually be played.
first = (int16)(fTrackCount * ((float)rand()/RAND_MAX)); first = (int16)(listcount * ((float)rand()/RAND_MAX));
second = (int16)(fTrackCount * ((float)rand()/RAND_MAX)); second = (int16)(listcount * ((float)rand()/RAND_MAX));
temp = fTrackList[first]; temp = fTrackList[first];
fTrackList[first] = fTrackList[second]; fTrackList[first] = fTrackList[second];
@@ -98,7 +197,7 @@ PlayList::Randomize(void)
void void
PlayList::Unrandomize(void) PlayList::Unrandomize(void)
{ {
for(int16 i=0; i<fTrackCount; i++) for(int16 i=fStartingTrack; i<=fTrackCount; i++)
fTrackList[i] = i; fTrackList[i - fStartingTrack] = i;
} }
+9 -2
View File
@@ -2,15 +2,19 @@
#define PLAYLIST_H #define PLAYLIST_H
#include <SupportDefs.h> #include <SupportDefs.h>
#include <Locker.h>
class PlayList class PlayList
{ {
public: public:
PlayList(int16 tracks = 0); PlayList(int16 tracks = 0, int16 startingtrack = 1);
void SetTrackCount(const int16 &count); void SetTrackCount(const int16 &count);
int16 TrackCount(void) const { return fTrackCount; } int16 TrackCount(void) const { return fTrackCount; }
void SetStartingTrack(const int16 &start);
int16 StartingTrack(void) const { return fStartingTrack; }
void Rewind(void); void Rewind(void);
void SetShuffle(const bool &random); void SetShuffle(const bool &random);
@@ -19,7 +23,9 @@ public:
void SetLoop(const bool &loop); void SetLoop(const bool &loop);
bool IsLoop(void) const { return fLoop; } bool IsLoop(void) const { return fLoop; }
int16 GetCurrentTrack(void);
int16 GetNextTrack(void); int16 GetNextTrack(void);
int16 GetPreviousTrack(void);
private: private:
void Randomize(void); void Randomize(void);
@@ -27,12 +33,13 @@ private:
int16 fTrackCount; int16 fTrackCount;
int16 fTrackIndex; int16 fTrackIndex;
int16 fStartingTrack;
int16 fTrackList[500]; // This should be big enough. :) int16 fTrackList[500]; // This should be big enough. :)
bool fRandom; bool fRandom;
bool fLoop; bool fLoop;
BLocker fLocker;
}; };
#endif #endif
+2
View File
@@ -25,6 +25,8 @@ public:
void MouseUp(BPoint pt); void MouseUp(BPoint pt);
// void MessageReceived(BMessage *msg); // void MessageReceived(BMessage *msg);
int32 GetState(void) { return fButtonState ? 1 : 0; };
private: private:
BBitmap *fUpOne, BBitmap *fUpOne,