ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SIDPlayer/src/main.cpp
Revision: 1.1
Committed: 2000-07-28T12:42:30Z (24 years, 3 months ago) by cebix
Branch: MAIN
Branch point for: cebix
Log Message:
Initial revision

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * main.cpp - SIDPlayer main program
3     *
4     * SIDPlayer (C) Copyright 1996-2000 Christian Bauer
5     *
6     * This program is free software; you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation; either version 2 of the License, or
9     * (at your option) any later version.
10     *
11     * This program is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with this program; if not, write to the Free Software
18     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19     */
20    
21     #include "sys.h"
22    
23     #include <AppKit.h>
24     #include <InterfaceKit.h>
25     #include <support/UTF8.h>
26     #include <storage/Path.h>
27     #include <media/SoundPlayer.h>
28     #include <stdio.h>
29     #include <string.h>
30     #include <unistd.h>
31    
32     #include "main.h"
33     #include "prefs.h"
34     #include "prefs_window.h"
35     #include "mem.h"
36     #include "cpu.h"
37     #include "sid.h"
38     #include "psid.h"
39    
40    
41     // Global variables
42     uint32 f_rand_seed = 1;
43    
44     // Flag: PSID file loaded and ready
45     static bool psid_loaded = false;
46    
47     // Data from PSID header
48     static int number_of_songs; // Total number of songs in module
49     static uint16 init_adr; // C64 init routine address
50     uint16 play_adr; // C64 replay routine address
51     static uint32 speed_flags; // Speed flags (1 bit/song)
52     char module_name[64]; // Module name
53     char author_name[64]; // Author name
54     char copyright_info[64]; // Copyright info
55    
56     // Currently played song number (0..n)
57     static int current_song;
58    
59    
60     // Message codes
61     const uint32 MSG_NEW_MODULE = 'load';
62     const uint32 MSG_NEW_SONG = 'song';
63     const uint32 MSG_SHOW_PREFS = 'pref';
64     const uint32 MSG_PLAY_PAUSE = 'plpa';
65     const uint32 MSG_STOP = 'stop';
66     const uint32 MSG_NEXT = 'next';
67     const uint32 MSG_PREV = 'prev';
68    
69    
70     class MainWindow;
71     class SpeedSlider;
72    
73     // Application object
74     class SIDPlayer : public BApplication {
75     public:
76     SIDPlayer();
77     virtual ~SIDPlayer();
78     virtual void ArgvReceived(int32 argc, char **argv);
79     virtual void RefsReceived(BMessage *msg);
80     virtual void MessageReceived(BMessage *msg);
81     virtual void ReadyToRun(void);
82     virtual void AboutRequested(void);
83    
84     private:
85     static void buffer_proc(void *cookie, void *buffer, size_t size, const media_raw_audio_format &format);
86    
87     MainWindow *main_window;
88     PrefsWindow *prefs_window;
89     BSoundPlayer player;
90     bool player_stopped;
91     };
92    
93    
94     // Main window object
95     class MainWindow : public BWindow {
96     public:
97     MainWindow();
98     virtual bool QuitRequested(void);
99     virtual void MessageReceived(BMessage *msg);
100    
101     private:
102     BStringView *make_name_display(BRect frame, char *label_text, BView *parent);
103    
104     rgb_color fill_color;
105    
106     BStringView *name_view;
107     BStringView *author_view;
108     BStringView *copyright_view;
109     BStringView *position_view;
110     SpeedSlider *speed_slider;
111     };
112    
113    
114     // Top view object (handles drag&drop)
115     class TopView : public BView {
116     public:
117     TopView(BRect frame, const char *name, uint32 resizingMode, uint32 flags);
118     virtual void MessageReceived(BMessage *msg);
119     virtual void KeyDown(const char *bytes, int32 numBytes);
120     };
121    
122    
123     // Buttons
124     class PlayPauseButton : public BButton {
125     public:
126     PlayPauseButton(BRect frame, BMessage *msg);
127     virtual void Draw(BRect update);
128     };
129    
130     class StopButton : public BButton {
131     public:
132     StopButton(BRect frame, BMessage *msg);
133     virtual void Draw(BRect update);
134     };
135    
136     class NextButton : public BButton {
137     public:
138     NextButton(BRect frame, BMessage *msg);
139     virtual void Draw(BRect update);
140     };
141    
142     class PrevButton : public BButton {
143     public:
144     PrevButton(BRect frame, BMessage *msg);
145     virtual void Draw(BRect update);
146     };
147    
148    
149     // Speed slider
150     class SpeedSlider : public BSlider {
151     public:
152     SpeedSlider(BRect frame, const char *name) : BSlider(frame, name, "Speed", NULL, -100, 100, B_TRIANGLE_THUMB)
153     {
154     SetHashMarks(B_HASH_MARKS_TOP);
155     SetHashMarkCount(3);
156     const rgb_color bar_color = {128, 128, 216, 0};
157     SetBarColor(bar_color);
158     SetValue(0);
159     }
160     virtual ~SpeedSlider() {}
161     virtual void SetValue(int32 value)
162     {
163     int percent = value < 0 ? 100 + value / 2 : 100 + value;
164     sprintf(status, "%d%%", percent);
165     SIDAdjustSpeed(percent);
166     BSlider::SetValue(value);
167     }
168     virtual char *UpdateText(void) const
169     {
170     return status;
171     }
172    
173     private:
174     mutable char status[32];
175     };
176    
177    
178     /*
179     * Init everything
180     */
181    
182     void InitAll(void)
183     {
184     PrefsInit(0, NULL);
185     MemoryInit();
186     SIDInit();
187     CPUInit();
188     }
189    
190    
191     /*
192     * Exit everything
193     */
194    
195     void ExitAll(void)
196     {
197     CPUExit();
198     SIDExit();
199     MemoryExit();
200     PrefsExit();
201     }
202    
203    
204     /*
205     * Read PSID file header to buffer
206     */
207    
208     bool LoadPSIDHeader(const char *file, uint8 *p)
209     {
210     // Read header
211     memset(p, 0, PSID_MAX_HEADER_LENGTH);
212     int fd = open(file, O_RDONLY);
213     if (fd < 0)
214     return false;
215     ssize_t actual = read(fd, p, PSID_MAX_HEADER_LENGTH);
216     close(fd);
217     return actual >= PSID_MIN_HEADER_LENGTH;
218     }
219    
220    
221     /*
222     * Check for PSID header
223     */
224    
225     bool IsPSIDHeader(const uint8 *p)
226     {
227     // Check signature and version
228     uint32 id = read_psid_32(p, PSID_ID);
229     uint16 version = read_psid_16(p, PSID_VERSION);
230     return id == 'PSID' && (version == 1 || version == 2);
231     }
232    
233    
234     /*
235     * Check whether file is a PSID file
236     */
237    
238     bool IsPSIDFile(const char *file)
239     {
240     // Load header
241     uint8 header[PSID_MAX_HEADER_LENGTH];
242     if (!LoadPSIDHeader(file, header))
243     return false;
244    
245     // Check header
246     return IsPSIDHeader(header);
247     }
248    
249    
250     /*
251     * Load PSID file for playing
252     */
253    
254     bool LoadPSIDFile(const char *file)
255     {
256     // Open file
257     int fd = open(file, O_RDONLY);
258     if (fd < 0)
259     return false;
260    
261     // Clear C64 RAM
262     MemoryClear();
263     psid_loaded = false;
264    
265     // Load and check header
266     uint8 header[PSID_MAX_HEADER_LENGTH];
267     ssize_t actual = read(fd, header, PSID_MAX_HEADER_LENGTH);
268     if (actual < 0 || !IsPSIDHeader(header)) {
269     close(fd);
270     return false;
271     }
272    
273     // Extract data from header
274     number_of_songs = read_psid_16(header, PSID_NUMBER);
275     if (number_of_songs == 0)
276     number_of_songs = 1;
277     current_song = read_psid_16(header, PSID_DEFSONG);
278     if (current_song)
279     current_song--;
280     if (current_song >= number_of_songs)
281     current_song = 0;
282    
283     init_adr = read_psid_16(header, PSID_INIT);
284     play_adr = read_psid_16(header, PSID_MAIN);
285    
286     speed_flags = read_psid_32(header, PSID_SPEED);
287    
288     int32 sl = 32, dl = 64, state = 0;
289     convert_to_utf8(B_ISO1_CONVERSION, (char *)(header + PSID_NAME), &sl, module_name, &dl, &state);
290     sl = 32, dl = 64, state = 0;
291     convert_to_utf8(B_ISO1_CONVERSION, (char *)(header + PSID_AUTHOR), &sl, author_name, &dl, &state);
292     sl = 32, dl = 64, state = 0;
293     convert_to_utf8(B_ISO1_CONVERSION, (char *)(header + PSID_COPYRIGHT), &sl, copyright_info, &dl, &state);
294     module_name[63] = 0;
295     author_name[63] = 0;
296     copyright_info[63] = 0;
297    
298     // Seek to start of module data
299     lseek(fd, read_psid_16(header, PSID_LENGTH), SEEK_SET);
300    
301     // Find load address
302     uint16 load_adr = read_psid_16(header, PSID_START);
303     if (load_adr == 0) { // Load address is at start of module data
304     uint8 hi, lo;
305     read(fd, &lo, 1);
306     read(fd, &hi, 1);
307     load_adr = (hi << 8) | lo;
308     }
309     if (init_adr == 0) // Init routine address is equal to load address
310     init_adr = load_adr;
311    
312     // Load module data to C64 RAM
313     read(fd, ram + load_adr, RAM_SIZE - load_adr);
314     close(fd);
315    
316     // Select default song
317     SelectSong(current_song);
318    
319     // Set replay routine address if not given in header
320     if (play_adr == 0) { // Replay routine address is given by interrupt vector
321     if (ram[1] & 2) // Kernal ROM switched in
322     play_adr = (ram[0x0315] << 8) | ram[0x0314];
323     else // Kernal ROM switched out
324     play_adr = (ram[0xffff] << 8) | ram[0xfffe];
325     }
326    
327     // Everything OK
328     psid_loaded = true;
329     return true;
330     }
331    
332    
333     /*
334     * Select song for playing
335     */
336    
337     void SelectSong(int num)
338     {
339     if (num >= number_of_songs)
340     num = 0;
341     current_song = num;
342    
343     // Reset SID
344     SIDReset(0);
345    
346     // Set replay frequency
347     int freq = 50;
348     if (num < 32)
349     freq = speed_flags & (1 << num) ? 60 : 50;
350     SIDSetReplayFreq(freq);
351     SIDAdjustSpeed(100);
352    
353     // Execute init routine
354     CPUExecute(init_adr, current_song, 0, 0, 1000000);
355     }
356    
357    
358     /*
359     * Application constructor
360     */
361    
362     #if B_HOST_IS_LENDIAN
363     const media_raw_audio_format audio_format = {44100.0, 2, media_raw_audio_format::B_AUDIO_SHORT, B_MEDIA_LITTLE_ENDIAN, 4096};
364     #else
365     const media_raw_audio_format audio_format = {44100.0, 2, media_raw_audio_format::B_AUDIO_SHORT, B_MEDIA_BIG_ENDIAN, 4096};
366     #endif
367    
368     SIDPlayer::SIDPlayer() : BApplication("application/x-vnd.cebix-SIDPlayer"), player(&audio_format, "SIDPlayer", buffer_proc)
369     {
370     main_window = NULL;
371     player.SetHasData(true);
372     player_stopped = true;
373     }
374    
375    
376     /*
377     * Application destructor
378     */
379    
380     SIDPlayer::~SIDPlayer()
381     {
382     main_window = NULL;
383     prefs_window = NULL;
384     player.Stop();
385     player_stopped = true;
386     }
387    
388    
389     /*
390     * Shell arguments received
391     */
392    
393     void SIDPlayer::ArgvReceived(int32 argc, char **argv)
394     {
395     if (argc >= 2) {
396     player.Stop();
397     LoadPSIDFile(argv[1]);
398     player.Start();
399     player_stopped = false;
400     if (main_window)
401     main_window->PostMessage(MSG_NEW_MODULE);
402     }
403     }
404    
405    
406     /*
407     * Tracker arguments received
408     */
409    
410     void SIDPlayer::RefsReceived(BMessage *msg)
411     {
412     entry_ref the_ref;
413     if (msg->FindRef("refs", &the_ref) == B_NO_ERROR) {
414     BEntry the_entry;
415     if (the_entry.SetTo(&the_ref) == B_NO_ERROR) {
416     if (the_entry.IsFile()) {
417     BPath the_path;
418     the_entry.GetPath(&the_path);
419     player.Stop();
420     LoadPSIDFile(the_path.Path());
421     player.Start();
422     player_stopped = false;
423     if (main_window)
424     main_window->PostMessage(MSG_NEW_MODULE);
425     }
426     }
427     }
428     }
429    
430    
431     /*
432     * Message received
433     */
434    
435     void SIDPlayer::MessageReceived(BMessage *msg)
436     {
437     switch (msg->what) {
438    
439     case B_SIMPLE_DATA: // Dropped message
440     RefsReceived(msg);
441     break;
442    
443     case MSG_SHOW_PREFS:
444     if (!prefs_window_open)
445     prefs_window = new PrefsWindow();
446     else if (prefs_window)
447     prefs_window->Activate(true);
448     break;
449    
450     case MSG_PLAY_PAUSE:
451     if (player_stopped) {
452     player.Start();
453     player_stopped = false;
454     } else {
455     player.Stop();
456     player_stopped = true;
457     }
458     break;
459    
460     case MSG_STOP:
461     player.Stop();
462     player_stopped = true;
463     SelectSong(current_song);
464     main_window->PostMessage(MSG_NEW_SONG);
465     break;
466    
467     case MSG_NEXT:
468     if (current_song < number_of_songs-1) {
469     player.Stop();
470     SelectSong(current_song + 1);
471     main_window->PostMessage(MSG_NEW_SONG);
472     if (!player_stopped)
473     player.Start();
474     }
475     break;
476    
477     case MSG_PREV:
478     if (current_song > 0) {
479     player.Stop();
480     SelectSong(current_song - 1);
481     main_window->PostMessage(MSG_NEW_SONG);
482     if (!player_stopped)
483     player.Start();
484     }
485     break;
486    
487     default:
488     BApplication::MessageReceived(msg);
489     break;
490     }
491     }
492    
493    
494     /*
495     * Arguments processed, open player window
496     */
497    
498     void SIDPlayer::ReadyToRun(void)
499     {
500     main_window = new MainWindow();
501     if (psid_loaded)
502     main_window->PostMessage(MSG_NEW_MODULE);
503     }
504    
505    
506     /*
507     * Show About window
508     */
509    
510     void AboutWindow(void)
511     {
512     BAlert *theAlert = new BAlert("",
513     "SIDPlayer\nVersion 4.0\n\n"
514     "Copyright " B_UTF8_COPYRIGHT " 1996-2000 Christian Bauer\n"
515     "E-mail: Christian.Bauer@uni-mainz.de\n"
516     "http://www.uni-mainz.de/~bauec002/\n\n"
517     "SIDPlayer comes with ABSOLUTELY NO\n"
518     "WARRANTY. This is free software, and\n"
519     "you are welcome to redistribute it\n"
520     "under the terms of the GNU General\n"
521     "Public License.\n",
522     "OK", NULL, NULL, B_WIDTH_FROM_LABEL);
523    
524     BTextView *theText = theAlert->TextView();
525     if (theText) {
526     theText->SetStylable(true);
527     theText->Select(0, 9);
528     BFont ourFont;
529     theText->SetFontAndColor(be_bold_font);
530     theText->GetFontAndColor(2, &ourFont, NULL);
531     ourFont.SetSize(24);
532     theText->SetFontAndColor(&ourFont);
533     }
534     theAlert->Go();
535     }
536    
537     void SIDPlayer::AboutRequested(void)
538     {
539     AboutWindow();
540     }
541    
542    
543     /*
544     * SoundPlayer buffer procedure
545     */
546    
547     void SIDPlayer::buffer_proc(void *cookie, void *buffer, size_t size, const media_raw_audio_format &format)
548     {
549     SIDCalcBuffer((uint8 *)buffer, size);
550     }
551    
552    
553     /*
554     * Main window constructor
555     */
556    
557     MainWindow::MainWindow() : BWindow(BRect(0, 0, 284, 96), "SIDPlayer", B_TITLED_WINDOW, B_NOT_ZOOMABLE | B_NOT_RESIZABLE | B_ASYNCHRONOUS_CONTROLS)
558     {
559     fill_color = ui_color(B_PANEL_BACKGROUND_COLOR);
560     BRect b = Bounds();
561    
562     // Move window to right position
563     Lock();
564     MoveTo(80, 80);
565    
566     // Create menu bar
567     BMenuBar *bar = new BMenuBar(BRect(0, 0, b.right, b.bottom), "menubar");
568     BMenu *menu = new BMenu("File");
569     menu->AddItem(new BMenuItem("About SIDPlayer" B_UTF8_ELLIPSIS, new BMessage(B_ABOUT_REQUESTED)));
570     menu->AddItem(new BSeparatorItem());
571     menu->AddItem(new BMenuItem("Sound Control" B_UTF8_ELLIPSIS, new BMessage(MSG_SHOW_PREFS), 'P'));
572     menu->AddItem(new BSeparatorItem());
573     menu->AddItem(new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED), 'Q'));
574     menu->SetTargetForItems(be_app);
575     bar->AddItem(menu);
576     AddChild(bar);
577     SetKeyMenuBar(bar);
578     float menu_height = bar->Bounds().Height();
579    
580     // Resize window to fit menu bar
581     ResizeBy(0, menu_height);
582    
583     // Light gray background
584     TopView *top = new TopView(BRect(0, menu_height, b.right, b.bottom + menu_height), "main", B_FOLLOW_NONE, B_WILL_DRAW);
585     AddChild(top);
586     top->SetViewColor(fill_color);
587    
588     // Name/author/copyright display
589     name_view = make_name_display(BRect(0, 5, 279, 21), "Name", top);
590     author_view = make_name_display(BRect(0, 25, 279, 41), "Author", top);
591     copyright_view = make_name_display(BRect(0, 45, 279, 61), "Copyright", top);
592    
593     // Buttons
594     top->AddChild(new PlayPauseButton(BRect(6, 67, 36, 91), new BMessage(MSG_PLAY_PAUSE)));
595     top->AddChild(new StopButton(BRect(37, 67, 67, 91), new BMessage(MSG_STOP)));
596     top->AddChild(new PrevButton(BRect(68, 67, 98, 91), new BMessage(MSG_PREV)));
597     top->AddChild(new NextButton(BRect(99, 67, 129, 91), new BMessage(MSG_NEXT)));
598    
599     // Position indicator
600     top->AddChild(position_view = new BStringView(BRect(134, 72, 193, 85), "position", ""));
601     position_view->SetViewColor(fill_color);
602    
603     // Speed slider
604     top->AddChild(speed_slider = new SpeedSlider(BRect(194, 62, 279, 63), "speed"));
605    
606     // Show window
607     top->MakeFocus();
608     Unlock();
609     Show();
610     }
611    
612    
613     /*
614     * Create name display field
615     */
616    
617     BStringView *MainWindow::make_name_display(BRect frame, char *label_text, BView *parent)
618     {
619     // Label to the left of the display field
620     BRect label_rect = frame;
621     label_rect.right = label_rect.left + 65;
622    
623     BStringView *label = new BStringView(label_rect, "", label_text);
624     parent->AddChild(label);
625     label->SetViewColor(fill_color);
626     label->SetLowColor(fill_color);
627     label->SetAlignment(B_ALIGN_RIGHT);
628     label->SetFont(be_bold_font);
629    
630     // Box around display field
631     BRect frame_rect = frame;
632     frame_rect.left += 70;
633    
634     BBox *box = new BBox(frame_rect);
635     parent->AddChild(box);
636     box->SetViewColor(fill_color);
637     box->SetLowColor(fill_color);
638    
639     // The display field
640     BRect textview_rect = frame_rect;
641     textview_rect.OffsetTo(0, 0);
642     textview_rect.InsetBy(4, 2);
643    
644     BStringView *text = new BStringView(textview_rect, "", "");
645     box->AddChild(text);
646     text->SetViewColor(fill_color);
647     text->SetLowColor(fill_color);
648     text->SetFont(be_plain_font);
649     return text;
650     }
651    
652    
653     /*
654     * Main window closed, quit program
655     */
656    
657     bool MainWindow::QuitRequested(void)
658     {
659     be_app->PostMessage(B_QUIT_REQUESTED);
660     return true;
661     }
662    
663    
664     /*
665     * Message received
666     */
667    
668     void MainWindow::MessageReceived(BMessage *msg)
669     {
670     switch (msg->what) {
671     case MSG_NEW_MODULE:
672     // Update text views
673     Lock();
674     name_view->SetText(module_name);
675     author_view->SetText(author_name);
676     copyright_view->SetText(copyright_info);
677     Unlock();
678     // falls through
679    
680     case MSG_NEW_SONG:
681     // Update position indicator and speed slider
682     if (number_of_songs > 0) {
683     char str[16];
684     sprintf(str, "Song %d/%d", current_song + 1, number_of_songs);
685     position_view->SetText(str);
686     }
687     speed_slider->SetValue(0);
688     break;
689    
690     case MSG_SHOW_PREFS:
691     case MSG_PLAY_PAUSE:
692     case MSG_STOP:
693     case MSG_NEXT:
694     case MSG_PREV:
695     be_app->PostMessage(msg);
696     break;
697    
698     default:
699     BWindow::MessageReceived(msg);
700     break;
701     }
702     }
703    
704    
705     /*
706     * TopView handles dropped messages (load new PSID module) and keypresses
707     */
708    
709     TopView::TopView(BRect frame, const char *name, uint32 resizingMode, uint32 flags)
710     : BView(frame, name, resizingMode, flags) {}
711    
712     void TopView::MessageReceived(BMessage *msg)
713     {
714     if (msg->what == B_SIMPLE_DATA)
715     be_app->PostMessage(msg);
716     else
717     BView::MessageReceived(msg);
718     }
719    
720     void TopView::KeyDown(const char *bytes, int32 numBytes)
721     {
722     BMessage *msg = Window()->CurrentMessage();
723     uint32 modifiers = 0;
724     msg->FindInt32("modifiers", (int32 *)&modifiers);
725    
726     switch (bytes[0]) {
727     case 'p':
728     case 'P':
729     Window()->PostMessage(MSG_PLAY_PAUSE);
730     break;
731    
732     case B_ESCAPE:
733     case B_SPACE:
734     case 's':
735     case 'S':
736     Window()->PostMessage(MSG_STOP);
737     break;
738    
739     case B_LEFT_ARROW:
740     Window()->PostMessage(MSG_PREV);
741     break;
742    
743     case B_RIGHT_ARROW:
744     case 'n':
745     case 'N':
746     Window()->PostMessage(MSG_NEXT);
747     break;
748    
749     case 'q':
750     case 'Q':
751     be_app->PostMessage(B_QUIT_REQUESTED);
752     break;
753     }
754     }
755    
756    
757     /*
758     * Play/Pause button
759     */
760    
761     PlayPauseButton::PlayPauseButton(BRect frame, BMessage *msg) : BButton(frame, "play", "", msg) {};
762    
763     void PlayPauseButton::Draw(BRect update)
764     {
765     // First draw normal button
766     BButton::Draw(update);
767    
768     // Then draw play/pause image on top of it
769     if (Value())
770     SetHighColor(255, 255, 255);
771     else
772     SetHighColor(0, 0, 0);
773    
774     FillRect(BRect(11, 8, 13, 16));
775     FillTriangle(BPoint(16, 8), BPoint(16, 16), BPoint(20, 12));
776     }
777    
778    
779     /*
780     * Stop button
781     */
782    
783     StopButton::StopButton(BRect frame, BMessage *msg) : BButton(frame, "stop", "", msg) {};
784    
785     void StopButton::Draw(BRect update)
786     {
787     // First draw normal button
788     BButton::Draw(update);
789    
790     // Then draw stop image on top of it
791     if (Value())
792     SetHighColor(255, 255, 255);
793     else
794     SetHighColor(0, 0, 0);
795    
796     FillRect(BRect(11, 8, 20, 16));
797     }
798    
799    
800     /*
801     * "Next" button
802     */
803    
804     NextButton::NextButton(BRect frame, BMessage *msg) : BButton(frame, "next", "", msg) {};
805    
806     void NextButton::Draw(BRect update)
807     {
808     // First draw normal button
809     BButton::Draw(update);
810    
811     // Then draw "next" image on top of it
812     if (Value())
813     SetHighColor(255, 255, 255);
814     else
815     SetHighColor(0, 0, 0);
816    
817     FillTriangle(BPoint(12, 8), BPoint(12, 16), BPoint(16, 12));
818     FillRect(BRect(17, 8, 19, 16));
819     }
820    
821    
822     /*
823     * "Prev" button
824     */
825    
826     PrevButton::PrevButton(BRect frame, BMessage *msg) : BButton(frame, "prev", "", msg) {};
827    
828     void PrevButton::Draw(BRect update)
829     {
830     // First draw normal button
831     BButton::Draw(update);
832    
833     // Then draw "prev" image on top of it
834     if (Value())
835     SetHighColor(255, 255, 255);
836     else
837     SetHighColor(0, 0, 0);
838    
839     FillRect(BRect(12, 8, 14, 16));
840     FillTriangle(BPoint(19, 8), BPoint(19, 16), BPoint(15, 12));
841     }
842    
843    
844     /*
845     * Main program
846     */
847    
848     int main(int argc, char **argv)
849     {
850     InitAll();
851     SIDPlayer *the_app = new SIDPlayer();
852     the_app->Run();
853     delete the_app;
854     ExitAll();
855     return 0;
856     }