--- SIDPlayer/src/main.cpp 2000/07/28 12:42:30 1.1 +++ SIDPlayer/src/main.cpp 2003/10/21 16:56:19 1.7 @@ -1,7 +1,7 @@ /* - * main.cpp - SIDPlayer main program + * main.cpp - SIDPlayer common routines * - * SIDPlayer (C) Copyright 1996-2000 Christian Bauer + * SIDPlayer (C) Copyright 1996-2003 Christian Bauer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,18 +20,15 @@ #include "sys.h" -#include -#include -#include -#include -#include #include #include -#include + +#if defined(__BEOS__) +#include +#endif #include "main.h" #include "prefs.h" -#include "prefs_window.h" #include "mem.h" #include "cpu.h" #include "sid.h" @@ -40,148 +37,29 @@ // Global variables uint32 f_rand_seed = 1; +int number_of_songs, current_song; +char module_name[64]; +char author_name[64]; +char copyright_info[64]; // Flag: PSID file loaded and ready static bool psid_loaded = false; // Data from PSID header -static int number_of_songs; // Total number of songs in module -static uint16 init_adr; // C64 init routine address -uint16 play_adr; // C64 replay routine address -static uint32 speed_flags; // Speed flags (1 bit/song) -char module_name[64]; // Module name -char author_name[64]; // Author name -char copyright_info[64]; // Copyright info - -// Currently played song number (0..n) -static int current_song; - - -// Message codes -const uint32 MSG_NEW_MODULE = 'load'; -const uint32 MSG_NEW_SONG = 'song'; -const uint32 MSG_SHOW_PREFS = 'pref'; -const uint32 MSG_PLAY_PAUSE = 'plpa'; -const uint32 MSG_STOP = 'stop'; -const uint32 MSG_NEXT = 'next'; -const uint32 MSG_PREV = 'prev'; - - -class MainWindow; -class SpeedSlider; - -// Application object -class SIDPlayer : public BApplication { -public: - SIDPlayer(); - virtual ~SIDPlayer(); - virtual void ArgvReceived(int32 argc, char **argv); - virtual void RefsReceived(BMessage *msg); - virtual void MessageReceived(BMessage *msg); - virtual void ReadyToRun(void); - virtual void AboutRequested(void); - -private: - static void buffer_proc(void *cookie, void *buffer, size_t size, const media_raw_audio_format &format); - - MainWindow *main_window; - PrefsWindow *prefs_window; - BSoundPlayer player; - bool player_stopped; -}; - - -// Main window object -class MainWindow : public BWindow { -public: - MainWindow(); - virtual bool QuitRequested(void); - virtual void MessageReceived(BMessage *msg); - -private: - BStringView *make_name_display(BRect frame, char *label_text, BView *parent); - - rgb_color fill_color; - - BStringView *name_view; - BStringView *author_view; - BStringView *copyright_view; - BStringView *position_view; - SpeedSlider *speed_slider; -}; - - -// Top view object (handles drag&drop) -class TopView : public BView { -public: - TopView(BRect frame, const char *name, uint32 resizingMode, uint32 flags); - virtual void MessageReceived(BMessage *msg); - virtual void KeyDown(const char *bytes, int32 numBytes); -}; - - -// Buttons -class PlayPauseButton : public BButton { -public: - PlayPauseButton(BRect frame, BMessage *msg); - virtual void Draw(BRect update); -}; - -class StopButton : public BButton { -public: - StopButton(BRect frame, BMessage *msg); - virtual void Draw(BRect update); -}; - -class NextButton : public BButton { -public: - NextButton(BRect frame, BMessage *msg); - virtual void Draw(BRect update); -}; - -class PrevButton : public BButton { -public: - PrevButton(BRect frame, BMessage *msg); - virtual void Draw(BRect update); -}; - - -// Speed slider -class SpeedSlider : public BSlider { -public: - SpeedSlider(BRect frame, const char *name) : BSlider(frame, name, "Speed", NULL, -100, 100, B_TRIANGLE_THUMB) - { - SetHashMarks(B_HASH_MARKS_TOP); - SetHashMarkCount(3); - const rgb_color bar_color = {128, 128, 216, 0}; - SetBarColor(bar_color); - SetValue(0); - } - virtual ~SpeedSlider() {} - virtual void SetValue(int32 value) - { - int percent = value < 0 ? 100 + value / 2 : 100 + value; - sprintf(status, "%d%%", percent); - SIDAdjustSpeed(percent); - BSlider::SetValue(value); - } - virtual char *UpdateText(void) const - { - return status; - } +static uint16 init_adr; // C64 init routine address +uint16 play_adr; // C64 replay routine address +static bool play_adr_from_irq_vec; // Flag: dynamically update play_adr from IRQ vector ($0314/$0315 or $fffe/$ffff) +static uint32 speed_flags; // Speed flags (1 bit/song) -private: - mutable char status[32]; -}; /* * Init everything */ -void InitAll(void) +void InitAll(int &argc, char **&argv) { - PrefsInit(0, NULL); + PrefsInit(argc, argv); MemoryInit(); SIDInit(); CPUInit(); @@ -192,7 +70,7 @@ void InitAll(void) * Exit everything */ -void ExitAll(void) +void ExitAll() { CPUExit(); SIDExit(); @@ -209,11 +87,11 @@ bool LoadPSIDHeader(const char *file, ui { // Read header memset(p, 0, PSID_MAX_HEADER_LENGTH); - int fd = open(file, O_RDONLY); - if (fd < 0) + FILE *f = fopen(file, "rb"); + if (f == NULL) return false; - ssize_t actual = read(fd, p, PSID_MAX_HEADER_LENGTH); - close(fd); + size_t actual = fread(p, 1, PSID_MAX_HEADER_LENGTH, f); + fclose(f); return actual >= PSID_MIN_HEADER_LENGTH; } @@ -227,7 +105,7 @@ bool IsPSIDHeader(const uint8 *p) // Check signature and version uint32 id = read_psid_32(p, PSID_ID); uint16 version = read_psid_16(p, PSID_VERSION); - return id == 'PSID' && (version == 1 || version == 2); + return id == 0x50534944 && (version == 1 || version == 2); } @@ -254,8 +132,8 @@ bool IsPSIDFile(const char *file) bool LoadPSIDFile(const char *file) { // Open file - int fd = open(file, O_RDONLY); - if (fd < 0) + FILE *f = fopen(file, "rb"); + if (f == NULL) return false; // Clear C64 RAM @@ -264,9 +142,10 @@ bool LoadPSIDFile(const char *file) // Load and check header uint8 header[PSID_MAX_HEADER_LENGTH]; - ssize_t actual = read(fd, header, PSID_MAX_HEADER_LENGTH); - if (actual < 0 || !IsPSIDHeader(header)) { - close(fd); + memset(header, 0, PSID_MAX_HEADER_LENGTH); + size_t actual = fread(header, 1, PSID_MAX_HEADER_LENGTH, f); + if (actual < PSID_MIN_HEADER_LENGTH || !IsPSIDHeader(header)) { + fclose(f); return false; } @@ -282,9 +161,11 @@ bool LoadPSIDFile(const char *file) init_adr = read_psid_16(header, PSID_INIT); play_adr = read_psid_16(header, PSID_MAIN); + play_adr_from_irq_vec = (play_adr == 0); speed_flags = read_psid_32(header, PSID_SPEED); +#if defined(__BEOS__) int32 sl = 32, dl = 64, state = 0; convert_to_utf8(B_ISO1_CONVERSION, (char *)(header + PSID_NAME), &sl, module_name, &dl, &state); sl = 32, dl = 64, state = 0; @@ -294,36 +175,35 @@ bool LoadPSIDFile(const char *file) module_name[63] = 0; author_name[63] = 0; copyright_info[63] = 0; +#else + strncpy(module_name, (char *)(header + PSID_NAME), 32); + strncpy(author_name, (char *)(header + PSID_AUTHOR), 32); + strncpy(copyright_info, (char *)(header + PSID_COPYRIGHT), 32); + module_name[32] = 0; + author_name[32] = 0; + copyright_info[32] = 0; +#endif // Seek to start of module data - lseek(fd, read_psid_16(header, PSID_LENGTH), SEEK_SET); + fseek(f, read_psid_16(header, PSID_LENGTH), SEEK_SET); // Find load address uint16 load_adr = read_psid_16(header, PSID_START); if (load_adr == 0) { // Load address is at start of module data - uint8 hi, lo; - read(fd, &lo, 1); - read(fd, &hi, 1); + uint8 lo = fgetc(f); + uint8 hi = fgetc(f); load_adr = (hi << 8) | lo; } if (init_adr == 0) // Init routine address is equal to load address init_adr = load_adr; // Load module data to C64 RAM - read(fd, ram + load_adr, RAM_SIZE - load_adr); - close(fd); + fread(ram + load_adr, 1, RAM_SIZE - load_adr, f); + fclose(f); // Select default song SelectSong(current_song); - // Set replay routine address if not given in header - if (play_adr == 0) { // Replay routine address is given by interrupt vector - if (ram[1] & 2) // Kernal ROM switched in - play_adr = (ram[0x0315] << 8) | ram[0x0314]; - else // Kernal ROM switched out - play_adr = (ram[0xffff] << 8) | ram[0xfffe]; - } - // Everything OK psid_loaded = true; return true; @@ -331,6 +211,16 @@ bool LoadPSIDFile(const char *file) /* + * PSID file loaded and ready? + */ + +bool IsPSIDLoaded() +{ + return psid_loaded; +} + + +/* * Select song for playing */ @@ -356,501 +246,15 @@ void SelectSong(int num) /* - * Application constructor - */ - -#if B_HOST_IS_LENDIAN -const media_raw_audio_format audio_format = {44100.0, 2, media_raw_audio_format::B_AUDIO_SHORT, B_MEDIA_LITTLE_ENDIAN, 4096}; -#else -const media_raw_audio_format audio_format = {44100.0, 2, media_raw_audio_format::B_AUDIO_SHORT, B_MEDIA_BIG_ENDIAN, 4096}; -#endif - -SIDPlayer::SIDPlayer() : BApplication("application/x-vnd.cebix-SIDPlayer"), player(&audio_format, "SIDPlayer", buffer_proc) -{ - main_window = NULL; - player.SetHasData(true); - player_stopped = true; -} - - -/* - * Application destructor - */ - -SIDPlayer::~SIDPlayer() -{ - main_window = NULL; - prefs_window = NULL; - player.Stop(); - player_stopped = true; -} - - -/* - * Shell arguments received - */ - -void SIDPlayer::ArgvReceived(int32 argc, char **argv) -{ - if (argc >= 2) { - player.Stop(); - LoadPSIDFile(argv[1]); - player.Start(); - player_stopped = false; - if (main_window) - main_window->PostMessage(MSG_NEW_MODULE); - } -} - - -/* - * Tracker arguments received - */ - -void SIDPlayer::RefsReceived(BMessage *msg) -{ - entry_ref the_ref; - if (msg->FindRef("refs", &the_ref) == B_NO_ERROR) { - BEntry the_entry; - if (the_entry.SetTo(&the_ref) == B_NO_ERROR) { - if (the_entry.IsFile()) { - BPath the_path; - the_entry.GetPath(&the_path); - player.Stop(); - LoadPSIDFile(the_path.Path()); - player.Start(); - player_stopped = false; - if (main_window) - main_window->PostMessage(MSG_NEW_MODULE); - } - } - } -} - - -/* - * Message received - */ - -void SIDPlayer::MessageReceived(BMessage *msg) -{ - switch (msg->what) { - - case B_SIMPLE_DATA: // Dropped message - RefsReceived(msg); - break; - - case MSG_SHOW_PREFS: - if (!prefs_window_open) - prefs_window = new PrefsWindow(); - else if (prefs_window) - prefs_window->Activate(true); - break; - - case MSG_PLAY_PAUSE: - if (player_stopped) { - player.Start(); - player_stopped = false; - } else { - player.Stop(); - player_stopped = true; - } - break; - - case MSG_STOP: - player.Stop(); - player_stopped = true; - SelectSong(current_song); - main_window->PostMessage(MSG_NEW_SONG); - break; - - case MSG_NEXT: - if (current_song < number_of_songs-1) { - player.Stop(); - SelectSong(current_song + 1); - main_window->PostMessage(MSG_NEW_SONG); - if (!player_stopped) - player.Start(); - } - break; - - case MSG_PREV: - if (current_song > 0) { - player.Stop(); - SelectSong(current_song - 1); - main_window->PostMessage(MSG_NEW_SONG); - if (!player_stopped) - player.Start(); - } - break; - - default: - BApplication::MessageReceived(msg); - break; - } -} - - -/* - * Arguments processed, open player window - */ - -void SIDPlayer::ReadyToRun(void) -{ - main_window = new MainWindow(); - if (psid_loaded) - main_window->PostMessage(MSG_NEW_MODULE); -} - - -/* - * Show About window - */ - -void AboutWindow(void) -{ - BAlert *theAlert = new BAlert("", - "SIDPlayer\nVersion 4.0\n\n" - "Copyright " B_UTF8_COPYRIGHT " 1996-2000 Christian Bauer\n" - "E-mail: Christian.Bauer@uni-mainz.de\n" - "http://www.uni-mainz.de/~bauec002/\n\n" - "SIDPlayer comes with ABSOLUTELY NO\n" - "WARRANTY. This is free software, and\n" - "you are welcome to redistribute it\n" - "under the terms of the GNU General\n" - "Public License.\n", - "OK", NULL, NULL, B_WIDTH_FROM_LABEL); - - BTextView *theText = theAlert->TextView(); - if (theText) { - theText->SetStylable(true); - theText->Select(0, 9); - BFont ourFont; - theText->SetFontAndColor(be_bold_font); - theText->GetFontAndColor(2, &ourFont, NULL); - ourFont.SetSize(24); - theText->SetFontAndColor(&ourFont); - } - theAlert->Go(); -} - -void SIDPlayer::AboutRequested(void) -{ - AboutWindow(); -} - - -/* - * SoundPlayer buffer procedure + * Update play_adr from IRQ vector if necessary */ -void SIDPlayer::buffer_proc(void *cookie, void *buffer, size_t size, const media_raw_audio_format &format) +void UpdatePlayAdr() { - SIDCalcBuffer((uint8 *)buffer, size); -} - - -/* - * Main window constructor - */ - -MainWindow::MainWindow() : BWindow(BRect(0, 0, 284, 96), "SIDPlayer", B_TITLED_WINDOW, B_NOT_ZOOMABLE | B_NOT_RESIZABLE | B_ASYNCHRONOUS_CONTROLS) -{ - fill_color = ui_color(B_PANEL_BACKGROUND_COLOR); - BRect b = Bounds(); - - // Move window to right position - Lock(); - MoveTo(80, 80); - - // Create menu bar - BMenuBar *bar = new BMenuBar(BRect(0, 0, b.right, b.bottom), "menubar"); - BMenu *menu = new BMenu("File"); - menu->AddItem(new BMenuItem("About SIDPlayer" B_UTF8_ELLIPSIS, new BMessage(B_ABOUT_REQUESTED))); - menu->AddItem(new BSeparatorItem()); - menu->AddItem(new BMenuItem("Sound Control" B_UTF8_ELLIPSIS, new BMessage(MSG_SHOW_PREFS), 'P')); - menu->AddItem(new BSeparatorItem()); - menu->AddItem(new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED), 'Q')); - menu->SetTargetForItems(be_app); - bar->AddItem(menu); - AddChild(bar); - SetKeyMenuBar(bar); - float menu_height = bar->Bounds().Height(); - - // Resize window to fit menu bar - ResizeBy(0, menu_height); - - // Light gray background - TopView *top = new TopView(BRect(0, menu_height, b.right, b.bottom + menu_height), "main", B_FOLLOW_NONE, B_WILL_DRAW); - AddChild(top); - top->SetViewColor(fill_color); - - // Name/author/copyright display - name_view = make_name_display(BRect(0, 5, 279, 21), "Name", top); - author_view = make_name_display(BRect(0, 25, 279, 41), "Author", top); - copyright_view = make_name_display(BRect(0, 45, 279, 61), "Copyright", top); - - // Buttons - top->AddChild(new PlayPauseButton(BRect(6, 67, 36, 91), new BMessage(MSG_PLAY_PAUSE))); - top->AddChild(new StopButton(BRect(37, 67, 67, 91), new BMessage(MSG_STOP))); - top->AddChild(new PrevButton(BRect(68, 67, 98, 91), new BMessage(MSG_PREV))); - top->AddChild(new NextButton(BRect(99, 67, 129, 91), new BMessage(MSG_NEXT))); - - // Position indicator - top->AddChild(position_view = new BStringView(BRect(134, 72, 193, 85), "position", "")); - position_view->SetViewColor(fill_color); - - // Speed slider - top->AddChild(speed_slider = new SpeedSlider(BRect(194, 62, 279, 63), "speed")); - - // Show window - top->MakeFocus(); - Unlock(); - Show(); -} - - -/* - * Create name display field - */ - -BStringView *MainWindow::make_name_display(BRect frame, char *label_text, BView *parent) -{ - // Label to the left of the display field - BRect label_rect = frame; - label_rect.right = label_rect.left + 65; - - BStringView *label = new BStringView(label_rect, "", label_text); - parent->AddChild(label); - label->SetViewColor(fill_color); - label->SetLowColor(fill_color); - label->SetAlignment(B_ALIGN_RIGHT); - label->SetFont(be_bold_font); - - // Box around display field - BRect frame_rect = frame; - frame_rect.left += 70; - - BBox *box = new BBox(frame_rect); - parent->AddChild(box); - box->SetViewColor(fill_color); - box->SetLowColor(fill_color); - - // The display field - BRect textview_rect = frame_rect; - textview_rect.OffsetTo(0, 0); - textview_rect.InsetBy(4, 2); - - BStringView *text = new BStringView(textview_rect, "", ""); - box->AddChild(text); - text->SetViewColor(fill_color); - text->SetLowColor(fill_color); - text->SetFont(be_plain_font); - return text; -} - - -/* - * Main window closed, quit program - */ - -bool MainWindow::QuitRequested(void) -{ - be_app->PostMessage(B_QUIT_REQUESTED); - return true; -} - - -/* - * Message received - */ - -void MainWindow::MessageReceived(BMessage *msg) -{ - switch (msg->what) { - case MSG_NEW_MODULE: - // Update text views - Lock(); - name_view->SetText(module_name); - author_view->SetText(author_name); - copyright_view->SetText(copyright_info); - Unlock(); - // falls through - - case MSG_NEW_SONG: - // Update position indicator and speed slider - if (number_of_songs > 0) { - char str[16]; - sprintf(str, "Song %d/%d", current_song + 1, number_of_songs); - position_view->SetText(str); - } - speed_slider->SetValue(0); - break; - - case MSG_SHOW_PREFS: - case MSG_PLAY_PAUSE: - case MSG_STOP: - case MSG_NEXT: - case MSG_PREV: - be_app->PostMessage(msg); - break; - - default: - BWindow::MessageReceived(msg); - break; - } -} - - -/* - * TopView handles dropped messages (load new PSID module) and keypresses - */ - -TopView::TopView(BRect frame, const char *name, uint32 resizingMode, uint32 flags) - : BView(frame, name, resizingMode, flags) {} - -void TopView::MessageReceived(BMessage *msg) -{ - if (msg->what == B_SIMPLE_DATA) - be_app->PostMessage(msg); - else - BView::MessageReceived(msg); -} - -void TopView::KeyDown(const char *bytes, int32 numBytes) -{ - BMessage *msg = Window()->CurrentMessage(); - uint32 modifiers = 0; - msg->FindInt32("modifiers", (int32 *)&modifiers); - - switch (bytes[0]) { - case 'p': - case 'P': - Window()->PostMessage(MSG_PLAY_PAUSE); - break; - - case B_ESCAPE: - case B_SPACE: - case 's': - case 'S': - Window()->PostMessage(MSG_STOP); - break; - - case B_LEFT_ARROW: - Window()->PostMessage(MSG_PREV); - break; - - case B_RIGHT_ARROW: - case 'n': - case 'N': - Window()->PostMessage(MSG_NEXT); - break; - - case 'q': - case 'Q': - be_app->PostMessage(B_QUIT_REQUESTED); - break; + if (play_adr_from_irq_vec) { + if (ram[1] & 2) // Kernal ROM switched in + play_adr = (ram[0x0315] << 8) | ram[0x0314]; + else // Kernal ROM switched out + play_adr = (ram[0xffff] << 8) | ram[0xfffe]; } } - - -/* - * Play/Pause button - */ - -PlayPauseButton::PlayPauseButton(BRect frame, BMessage *msg) : BButton(frame, "play", "", msg) {}; - -void PlayPauseButton::Draw(BRect update) -{ - // First draw normal button - BButton::Draw(update); - - // Then draw play/pause image on top of it - if (Value()) - SetHighColor(255, 255, 255); - else - SetHighColor(0, 0, 0); - - FillRect(BRect(11, 8, 13, 16)); - FillTriangle(BPoint(16, 8), BPoint(16, 16), BPoint(20, 12)); -} - - -/* - * Stop button - */ - -StopButton::StopButton(BRect frame, BMessage *msg) : BButton(frame, "stop", "", msg) {}; - -void StopButton::Draw(BRect update) -{ - // First draw normal button - BButton::Draw(update); - - // Then draw stop image on top of it - if (Value()) - SetHighColor(255, 255, 255); - else - SetHighColor(0, 0, 0); - - FillRect(BRect(11, 8, 20, 16)); -} - - -/* - * "Next" button - */ - -NextButton::NextButton(BRect frame, BMessage *msg) : BButton(frame, "next", "", msg) {}; - -void NextButton::Draw(BRect update) -{ - // First draw normal button - BButton::Draw(update); - - // Then draw "next" image on top of it - if (Value()) - SetHighColor(255, 255, 255); - else - SetHighColor(0, 0, 0); - - FillTriangle(BPoint(12, 8), BPoint(12, 16), BPoint(16, 12)); - FillRect(BRect(17, 8, 19, 16)); -} - - -/* - * "Prev" button - */ - -PrevButton::PrevButton(BRect frame, BMessage *msg) : BButton(frame, "prev", "", msg) {}; - -void PrevButton::Draw(BRect update) -{ - // First draw normal button - BButton::Draw(update); - - // Then draw "prev" image on top of it - if (Value()) - SetHighColor(255, 255, 255); - else - SetHighColor(0, 0, 0); - - FillRect(BRect(12, 8, 14, 16)); - FillTriangle(BPoint(19, 8), BPoint(19, 16), BPoint(15, 12)); -} - - -/* - * Main program - */ - -int main(int argc, char **argv) -{ - InitAll(); - SIDPlayer *the_app = new SIDPlayer(); - the_app->Run(); - delete the_app; - ExitAll(); - return 0; -}