comparison src/cdaudio-ng/cdaudio-ng.c @ 3116:b09c922c9f34

Finish fixing hang-ups; remove confusing and broken "rescan"; handle no-disk more gracefully.
author John Lindgren <john.lindgren@tds.net>
date Sun, 03 May 2009 20:53:48 -0400
parents 7c27b8612b7e
children e0b95157aa33
comparison
equal deleted inserted replaced
3115:3b4841de4255 3116:b09c922c9f34
1 /* 1 /*
2 * Audacious CD Digital Audio plugin 2 * Audacious CD Digital Audio plugin
3 * 3 *
4 * Copyright (c) 2007 Calin Crisan <ccrisan@gmail.com> 4 * Copyright (c) 2007 Calin Crisan <ccrisan@gmail.com>
5 * Portions copyright (c) 2009 John Lindgren <john.lindgren@tds.net>
5 * 6 *
6 * This program is free software; you can redistribute it and/or modify 7 * 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 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; under version 3 of the License. 9 * the Free Software Foundation; under version 3 of the License.
9 * 10 *
14 * 15 *
15 * You should have received a copy of the GNU General Public License 16 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses>. 17 * along with this program. If not, see <http://www.gnu.org/licenses>.
17 */ 18 */
18 19
19
20 /*
21 * todo :
22 * audacious -e cdda://track01.cda no longer works
23 *
24 *
25 */
26
27
28 #include "config.h" 20 #include "config.h"
29 21
30 #include <string.h> 22 #include <string.h>
31 #include <stdlib.h> 23 #include <stdlib.h>
32 #include <unistd.h> 24 #include <unistd.h>
57 cdng_cfg_t cdng_cfg; 49 cdng_cfg_t cdng_cfg;
58 static gint firsttrackno = -1; 50 static gint firsttrackno = -1;
59 static gint lasttrackno = -1; 51 static gint lasttrackno = -1;
60 static CdIo_t *pcdio = NULL; 52 static CdIo_t *pcdio = NULL;
61 static trackinfo_t *trackinfo = NULL; 53 static trackinfo_t *trackinfo = NULL;
62 static GMutex *trackinfo_mutex;
63 static volatile gboolean trackinfo_changed = FALSE;
64 static volatile gboolean is_paused = FALSE; 54 static volatile gboolean is_paused = FALSE;
65 static gint playing_track = -1; 55 static gint playing_track = -1;
66 static dae_params_t *pdae_params = NULL; 56 static dae_params_t *pdae_params = NULL;
67 static InputPlayback *pglobalinputplayback = NULL; 57 static InputPlayback *pglobalinputplayback = NULL;
68 58
69 #define N_MENUS 3 59 #define N_MENUS 3
70 static const int menus [N_MENUS] = {AUDACIOUS_MENU_MAIN, 60 static const int menus [N_MENUS] = {AUDACIOUS_MENU_MAIN,
71 AUDACIOUS_MENU_PLAYLIST_ADD, AUDACIOUS_MENU_PLAYLIST_RCLICK}; 61 AUDACIOUS_MENU_PLAYLIST_ADD, AUDACIOUS_MENU_PLAYLIST_RCLICK};
72 static GtkWidget * menu_items [N_MENUS * 2]; 62 static GtkWidget * menu_items [N_MENUS];
73 63
74 static void cdaudio_init(void); 64 static void cdaudio_init(void);
75 static void cdaudio_about(void); 65 static void cdaudio_about(void);
76 static void cdaudio_configure(void); 66 static void cdaudio_configure(void);
77 static gint cdaudio_is_our_file(gchar *filename); 67 static gint cdaudio_is_our_file(gchar *filename);
84 static gint cdaudio_set_volume(gint l, gint r); 74 static gint cdaudio_set_volume(gint l, gint r);
85 static void cdaudio_cleanup(void); 75 static void cdaudio_cleanup(void);
86 static void cdaudio_get_song_info(gchar *filename, gchar **title, gint *length); 76 static void cdaudio_get_song_info(gchar *filename, gchar **title, gint *length);
87 static Tuple *cdaudio_get_song_tuple(gchar *filename); 77 static Tuple *cdaudio_get_song_tuple(gchar *filename);
88 78
89 static void menu_click(void);
90 static void rescan_menu_click(void);
91 static Tuple *create_tuple_from_trackinfo_and_filename(gchar *filename); 79 static Tuple *create_tuple_from_trackinfo_and_filename(gchar *filename);
92 static void dae_play_loop(dae_params_t *pdae_params); 80 static void dae_play_loop(dae_params_t *pdae_params);
93 static void *scan_cd(void *nothing); 81 static void *scan_cd(void *nothing);
94 static void refresh_trackinfo(void); 82 static void refresh_trackinfo(void);
95 static gboolean refresh_playlist(gboolean force);
96 static void append_track_to_playlist(int trackno); 83 static void append_track_to_playlist(int trackno);
97 static gboolean show_noaudiocd_info(); 84 static gboolean show_noaudiocd_info();
98 static gint calculate_track_length(gint startlsn, gint endlsn); 85 static gint calculate_track_length(gint startlsn, gint endlsn);
99 static gint find_trackno_from_filename(gchar *filename); 86 static gint find_trackno_from_filename(gchar *filename);
100 static void cleanup_on_error(void); 87 static void cleanup_on_error(void);
142 vfprintf(stderr, fmt, ap); 129 vfprintf(stderr, fmt, ap);
143 va_end(ap); 130 va_end(ap);
144 } 131 }
145 } 132 }
146 133
134 static void remove_tracks (void)
135 {
136 int pos, length;
137
138 length = audacious_drct_pl_get_length ();
139
140 for (pos = 0; pos < length; )
141 {
142 if (cdaudio_is_our_file (audacious_drct_pl_get_file (pos)))
143 {
144 audacious_drct_pl_delete (pos);
145 length --;
146 }
147 else
148 pos ++;
149 }
150 }
151
152 static gboolean monitor (void)
153 {
154 static guint source = 0;
155
156 refresh_trackinfo ();
157
158 if (trackinfo)
159 {
160 if (! source)
161 source = g_timeout_add_seconds (3, (GSourceFunc) monitor, 0);
162 }
163 else
164 {
165 remove_tracks ();
166
167 if (source)
168 {
169 g_source_remove (source);
170 source = 0;
171 }
172 }
173
174 return 1;
175 }
176
177 static void add_tracks (void)
178 {
179 int track;
180
181 for (track = firsttrackno; track < lasttrackno; track ++)
182 append_track_to_playlist (track);
183 }
184
185 static void start_playback (void)
186 {
187 int pos, length;
188
189 length = audacious_drct_pl_get_length ();
190
191 for (pos = 0; pos < length; pos ++)
192 {
193 if (cdaudio_is_our_file (audacious_drct_pl_get_file (pos)))
194 {
195 audacious_drct_pl_set_pos (pos);
196 audacious_drct_play ();
197 break;
198 }
199 }
200 }
201
202 static void play_cd (void)
203 {
204 audacious_drct_stop ();
205 remove_tracks ();
206 monitor ();
207
208 if (trackinfo)
209 {
210 add_tracks ();
211 start_playback ();
212 }
213 else
214 show_noaudiocd_info ();
215 }
216
217 static void start_monitor (void)
218 {
219 static char started = 0;
220
221 if (! started)
222 {
223 monitor ();
224 started = 1;
225 }
226 }
147 227
148 static void cdaudio_init() 228 static void cdaudio_init()
149 { 229 {
150 mcs_handle_t *db; 230 mcs_handle_t *db;
151 int count; 231 int count;
203 return; 283 return;
204 } 284 }
205 285
206 libcddb_init(); 286 libcddb_init();
207 287
208 trackinfo_mutex = g_mutex_new();
209 trackinfo_changed = FALSE;
210
211 for (count = 0; count < N_MENUS; count ++) 288 for (count = 0; count < N_MENUS; count ++)
212 { 289 {
213 item = gtk_image_menu_item_new_with_label (_ ("Add CD")); 290 item = gtk_image_menu_item_new_with_label (_ ("Play CD"));
214 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), 291 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
215 gtk_image_new_from_stock (GTK_STOCK_CDROM, GTK_ICON_SIZE_MENU)); 292 gtk_image_new_from_stock (GTK_STOCK_CDROM, GTK_ICON_SIZE_MENU));
216 gtk_widget_show (item); 293 gtk_widget_show (item);
217 menu_items [2 * count] = item; 294 menu_items [count] = item;
218 audacious_menu_plugin_item_add (menus [count], item); 295 audacious_menu_plugin_item_add (menus [count], item);
219 g_signal_connect (G_OBJECT (item), "activate", 296 g_signal_connect (G_OBJECT (item), "activate",
220 G_CALLBACK (menu_click), NULL); 297 G_CALLBACK (play_cd), NULL);
221
222 item = gtk_image_menu_item_new_with_label (_ ("Rescan CD"));
223 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
224 gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU));
225 gtk_widget_show (item);
226 menu_items [2 * count + 1] = item;
227 audacious_menu_plugin_item_add (menus [count], item);
228 g_signal_connect (G_OBJECT (item), "activate",
229 G_CALLBACK (rescan_menu_click), NULL);
230 } 298 }
231 299
232 aud_uri_set_plugin("cdda://", &inputplugin); 300 aud_uri_set_plugin("cdda://", &inputplugin);
233 } 301 }
234 302
263 cdng_cfg.cddb_server, cdng_cfg.cddb_path, cdng_cfg.cddb_port, cdng_cfg.cddb_http, cdng_cfg.device, cdng_cfg.debug); 331 cdng_cfg.cddb_server, cdng_cfg.cddb_path, cdng_cfg.cddb_port, cdng_cfg.cddb_http, cdng_cfg.device, cdng_cfg.debug);
264 } 332 }
265 333
266 static gint cdaudio_is_our_file(gchar *filename) 334 static gint cdaudio_is_our_file(gchar *filename)
267 { 335 {
268 debug("cdaudio_is_our_file(\"%s\")\n", filename); 336 return ! strncmp (filename, "cdda://", 7);
269
270 if (filename != NULL && !strcmp(filename, CDDA_DUMMYPATH)) {
271 debug("\"%s\" will add the whole audio cd\n", filename);
272 refresh_trackinfo();
273 refresh_playlist(TRUE);
274 return FALSE;
275 }
276
277 if ((filename != NULL) && strlen(filename) > 4 && (!strcasecmp(filename + strlen(filename) - 4, ".cda"))) {
278 gint trackno = find_trackno_from_filename(filename);
279
280 refresh_trackinfo ();
281
282 /* check if the requested track actually exists on the current audio cd */
283 if (trackno < firsttrackno || trackno > lasttrackno) {
284 debug("\"%s\" is not our file (track number is out of the valid range)\n", filename);
285 return FALSE;
286 }
287
288 debug("\"%s\" is our file\n", filename);
289 return TRUE;
290 }
291 else {
292 debug("\"%s\" is not our file (unrecognized file name)\n", filename);
293 return FALSE;
294 }
295 } 337 }
296 338
297 339
298 static void cdaudio_set_strinfo(trackinfo_t *t, 340 static void cdaudio_set_strinfo(trackinfo_t *t,
299 const gchar *performer, const gchar *name, const gchar *genre) 341 const gchar *performer, const gchar *name, const gchar *genre)
319 Tuple *tuple; 361 Tuple *tuple;
320 gchar *title; 362 gchar *title;
321 363
322 debug("cdaudio_play_file(\"%s\")\n", pinputplayback->filename); 364 debug("cdaudio_play_file(\"%s\")\n", pinputplayback->filename);
323 365
366 start_monitor ();
367
324 pglobalinputplayback = pinputplayback; 368 pglobalinputplayback = pinputplayback;
325 369
326 refresh_trackinfo ();
327
328 gint trackno = find_trackno_from_filename(pinputplayback->filename); 370 gint trackno = find_trackno_from_filename(pinputplayback->filename);
329
330 if (refresh_playlist(FALSE))
331 return;
332 371
333 if (trackinfo == NULL) { 372 if (trackinfo == NULL) {
334 debug("no CD information can be retrieved, aborting\n"); 373 debug("no CD information can be retrieved, aborting\n");
335 pinputplayback->playing = FALSE; 374 pinputplayback->playing = FALSE;
336 return; 375 return;
549 int count; 588 int count;
550 589
551 debug("cdaudio_cleanup()\n"); 590 debug("cdaudio_cleanup()\n");
552 591
553 for (count = 0; count < N_MENUS; count ++) 592 for (count = 0; count < N_MENUS; count ++)
554 { 593 audacious_menu_plugin_item_remove (menus [count], menu_items [count]);
555 audacious_menu_plugin_item_remove (menus [count],
556 menu_items [2 * count]);
557 audacious_menu_plugin_item_remove (menus [count],
558 menu_items [2 * count + 1]);
559 }
560 594
561 if (pcdio != NULL) { 595 if (pcdio != NULL) {
562 if (playing_track != -1 && !cdng_cfg.use_dae) 596 if (playing_track != -1 && !cdng_cfg.use_dae)
563 cdio_audio_stop(pcdio); 597 cdio_audio_stop(pcdio);
564 cdio_destroy(pcdio); 598 cdio_destroy(pcdio);
567 if (trackinfo != NULL) { 601 if (trackinfo != NULL) {
568 g_free(trackinfo); 602 g_free(trackinfo);
569 trackinfo = NULL; 603 trackinfo = NULL;
570 } 604 }
571 playing_track = -1; 605 playing_track = -1;
572
573 g_mutex_free(trackinfo_mutex);
574 606
575 libcddb_shutdown(); 607 libcddb_shutdown();
576 608
577 // todo: destroy the gui 609 // todo: destroy the gui
578 610
611 debug("cdaudio_get_song_tuple(\"%s\")\n", filename); 643 debug("cdaudio_get_song_tuple(\"%s\")\n", filename);
612 644
613 return create_tuple_from_trackinfo_and_filename(filename); 645 return create_tuple_from_trackinfo_and_filename(filename);
614 } 646 }
615 647
616
617 /*
618 * auxiliar functions
619 */
620 static void menu_click(void)
621 {
622 debug("plugin services menu option selected\n");
623
624 /* reload the cd information if the media has changed, or no track information is available */
625 refresh_trackinfo();
626 (void)refresh_playlist(TRUE);
627 }
628
629 static void rescan_menu_click(void)
630 {
631 debug("plugin services rescan option selected\n");
632
633 refresh_trackinfo();
634 }
635
636 static Tuple *create_tuple_from_trackinfo_and_filename(gchar *filename) 648 static Tuple *create_tuple_from_trackinfo_and_filename(gchar *filename)
637 { 649 {
638 Tuple *tuple = aud_tuple_new_from_filename(filename); 650 Tuple *tuple = aud_tuple_new_from_filename(filename);
639 651
640 refresh_trackinfo (); 652 start_monitor ();
641 653
642 if (trackinfo == NULL) 654 if (trackinfo == NULL)
643 return tuple; 655 return tuple;
644 656
645 gint trackno = find_trackno_from_filename(filename); 657 gint trackno = find_trackno_from_filename(filename);
763 audacious_drct_pl_add_url_string(pathname); 775 audacious_drct_pl_add_url_string(pathname);
764 776
765 debug("added track \"%s\" to the playlist\n", pathname); 777 debug("added track \"%s\" to the playlist\n", pathname);
766 } 778 }
767 779
780 static void destroy_dialog (GtkWidget * dialog, gint response,
781 GtkWidget * * reference)
782 {
783 gtk_widget_destroy (dialog);
784 * reference = 0;
785 }
786
768 static gboolean show_noaudiocd_info() 787 static gboolean show_noaudiocd_info()
769 { 788 {
770 GDK_THREADS_ENTER();
771 const gchar *markup = 789 const gchar *markup =
772 N_("<b><big>No playable CD found.</big></b>\n\n" 790 N_("<b><big>No playable CD found.</big></b>\n\n"
773 "No CD inserted, or inserted CD is not an audio CD.\n"); 791 "No CD inserted, or inserted CD is not an audio CD.\n");
774 792 static GtkWidget * dialog = 0;
775 GtkWidget *dialog = 793
776 gtk_message_dialog_new_with_markup(NULL, 794 if (dialog)
795 gtk_window_present (GTK_WINDOW (dialog));
796 else
797 {
798 dialog = gtk_message_dialog_new_with_markup (NULL,
777 GTK_DIALOG_DESTROY_WITH_PARENT, 799 GTK_DIALOG_DESTROY_WITH_PARENT,
778 GTK_MESSAGE_ERROR, 800 GTK_MESSAGE_ERROR,
779 GTK_BUTTONS_OK, 801 GTK_BUTTONS_OK,
780 _(markup)); 802 _(markup));
781 gtk_dialog_run(GTK_DIALOG(dialog)); 803 gtk_widget_show (dialog);
782 gtk_widget_destroy(dialog); 804 g_signal_connect (G_OBJECT (dialog), "response",
783 805 G_CALLBACK (destroy_dialog), & dialog);
784 GDK_THREADS_LEAVE(); 806 }
807
785 return TRUE; 808 return TRUE;
786 } 809 }
787 810
788 static void *scan_cd(void *nothing) 811 static void *scan_cd(void *nothing)
789 { 812 {
794 /* find an available, audio capable, cd drive */ 817 /* find an available, audio capable, cd drive */
795 if (cdng_cfg.device != NULL && strlen(cdng_cfg.device) > 0) { 818 if (cdng_cfg.device != NULL && strlen(cdng_cfg.device) > 0) {
796 pcdio = cdio_open(cdng_cfg.device, DRIVER_UNKNOWN); 819 pcdio = cdio_open(cdng_cfg.device, DRIVER_UNKNOWN);
797 if (pcdio == NULL) { 820 if (pcdio == NULL) {
798 cdaudio_error("Failed to open CD device \"%s\".\n", cdng_cfg.device); 821 cdaudio_error("Failed to open CD device \"%s\".\n", cdng_cfg.device);
799 show_noaudiocd_info();
800 return NULL; 822 return NULL;
801 } 823 }
802 } 824 }
803 else { 825 else {
804 gchar **ppcd_drives = cdio_get_devices_with_cap(NULL, CDIO_FS_AUDIO, false); 826 gchar **ppcd_drives = cdio_get_devices_with_cap(NULL, CDIO_FS_AUDIO, false);
806 if (ppcd_drives != NULL && *ppcd_drives != NULL) { /* we have at least one audio capable cd drive */ 828 if (ppcd_drives != NULL && *ppcd_drives != NULL) { /* we have at least one audio capable cd drive */
807 pcdio = cdio_open(*ppcd_drives, DRIVER_UNKNOWN); 829 pcdio = cdio_open(*ppcd_drives, DRIVER_UNKNOWN);
808 if (pcdio == NULL) { 830 if (pcdio == NULL) {
809 cdaudio_error("Failed to open CD.\n"); 831 cdaudio_error("Failed to open CD.\n");
810 cleanup_on_error(); 832 cleanup_on_error();
811 show_noaudiocd_info();
812 return NULL; 833 return NULL;
813 } 834 }
814 debug("found cd drive \"%s\" with audio capable media\n", *ppcd_drives); 835 debug("found cd drive \"%s\" with audio capable media\n", *ppcd_drives);
815 } 836 }
816 else { 837 else {
817 cdaudio_error("Unable to find or access a CDDA capable drive.\n"); 838 cdaudio_error("Unable to find or access a CDDA capable drive.\n");
818 cleanup_on_error(); 839 cleanup_on_error();
819 show_noaudiocd_info();
820 return NULL; 840 return NULL;
821 } 841 }
822 if (ppcd_drives != NULL && *ppcd_drives != NULL) 842 if (ppcd_drives != NULL && *ppcd_drives != NULL)
823 cdio_free_device_list(ppcd_drives); 843 cdio_free_device_list(ppcd_drives);
824 } 844 }
835 firsttrackno = cdio_get_first_track_num(pcdrom_drive->p_cdio); 855 firsttrackno = cdio_get_first_track_num(pcdrom_drive->p_cdio);
836 lasttrackno = cdio_get_last_track_num(pcdrom_drive->p_cdio); 856 lasttrackno = cdio_get_last_track_num(pcdrom_drive->p_cdio);
837 if (firsttrackno == CDIO_INVALID_TRACK || lasttrackno == CDIO_INVALID_TRACK) { 857 if (firsttrackno == CDIO_INVALID_TRACK || lasttrackno == CDIO_INVALID_TRACK) {
838 cdaudio_error("Failed to retrieve first/last track number.\n"); 858 cdaudio_error("Failed to retrieve first/last track number.\n");
839 cleanup_on_error(); 859 cleanup_on_error();
840 show_noaudiocd_info();
841 return NULL; 860 return NULL;
842 } 861 }
843 debug("first track is %d and last track is %d\n", firsttrackno, lasttrackno); 862 debug("first track is %d and last track is %d\n", firsttrackno, lasttrackno);
844 863
845 g_free(trackinfo); 864 g_free(trackinfo);
857 "", "", ""); 876 "", "", "");
858 877
859 if (trackinfo[trackno].startlsn == CDIO_INVALID_LSN || trackinfo[trackno].endlsn == CDIO_INVALID_LSN) { 878 if (trackinfo[trackno].startlsn == CDIO_INVALID_LSN || trackinfo[trackno].endlsn == CDIO_INVALID_LSN) {
860 cdaudio_error("Failed to retrieve stard/end lsn for track %d.\n", trackno); 879 cdaudio_error("Failed to retrieve stard/end lsn for track %d.\n", trackno);
861 cleanup_on_error(); 880 cleanup_on_error();
862 show_noaudiocd_info();
863 return NULL; 881 return NULL;
864 } 882 }
865 } 883 }
866 884
867 /* get trackinfo[0] cdtext information (the disc) */ 885 /* get trackinfo[0] cdtext information (the disc) */
1027 return NULL; 1045 return NULL;
1028 } 1046 }
1029 1047
1030 static void refresh_trackinfo (void) 1048 static void refresh_trackinfo (void)
1031 { 1049 {
1032
1033 g_mutex_lock(trackinfo_mutex);
1034
1035 if (pcdio == NULL || cdio_get_media_changed(pcdio) || ! trackinfo) { 1050 if (pcdio == NULL || cdio_get_media_changed(pcdio) || ! trackinfo) {
1036 debug(pcdio ? "CD changed, rescanning\n" 1051 debug(pcdio ? "CD changed, rescanning\n"
1037 : "no CD information, scanning\n"); 1052 : "no CD information, scanning\n");
1038 scan_cd(NULL); 1053 scan_cd(NULL);
1039 trackinfo_changed = TRUE; 1054 }
1040 }
1041
1042 g_mutex_unlock(trackinfo_mutex);
1043 }
1044
1045 static gboolean refresh_playlist (gboolean force)
1046 {
1047 gint need_jump;
1048 gint trackno;
1049
1050 g_mutex_lock(trackinfo_mutex);
1051
1052 if ((! trackinfo_changed && ! force) || ! trackinfo) {
1053 g_mutex_unlock(trackinfo_mutex);
1054 return FALSE;
1055 }
1056
1057 need_jump = audacious_drct_pl_get_length () && trackinfo_changed;
1058
1059 audacious_drct_pl_clear ();
1060
1061 for (trackno = firsttrackno; trackno <= lasttrackno; trackno++)
1062 append_track_to_playlist(trackno);
1063
1064 if (need_jump) {
1065 audacious_drct_pq_clear ();
1066 audacious_drct_pq_add (0);
1067 }
1068
1069 trackinfo_changed = FALSE;
1070
1071 g_mutex_unlock(trackinfo_mutex);
1072
1073 return TRUE;
1074 } 1055 }
1075 1056
1076 static gint calculate_track_length(gint startlsn, gint endlsn) 1057 static gint calculate_track_length(gint startlsn, gint endlsn)
1077 { 1058 {
1078 return ((endlsn - startlsn + 1) * 1000) / 75; 1059 return ((endlsn - startlsn + 1) * 1000) / 75;