2 * $Id: cdinfo.c,v 1.6 1999/02/14 16:47:40 dirk Exp $
4 * This file is part of WorkMan, the civilized CD player library
5 * (c) 1991-1997 by Steven Grimm (original author)
6 * (c) by Dirk Försterling (current 'author' = maintainer)
7 * The maintainer can be contacted by his e-mail address:
8 * milliByte@DeathsDoor.com
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
20 * You should have received a copy of the GNU Library General Public
21 * License along with this library; if not, write to the Free
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 * Get information about a CD.
28 static char cdinfo_id
[] = "$Id: cdinfo.c,v 1.6 1999/02/14 16:47:40 dirk Exp $";
34 #include <sys/types.h>
36 #include "include/wm_config.h"
38 #include "include/wm_struct.h"
39 #include "include/wm_cdrom.h"
40 #include "include/wm_cdinfo.h"
41 #include "include/wm_database.h"
42 #include "include/wm_helpers.h"
44 struct wm_play
*playlist
= NULL
;
45 struct wm_cdinfo thiscd
, *cd
= &thiscd
;
47 int cur_track
= -1; /* Current track number, starting at 1 */
48 int cur_index
= 0; /* Current index mark */
49 int cur_lasttrack
= 999; /* Last track to play in current chunk */
50 int cur_firsttrack
= 0; /* First track of current chunk */
51 int cur_pos_abs
; /* Current absolute position in seconds */
52 int cur_frame
; /* Current frame number */
53 int cur_pos_rel
; /* Current track-relative position in seconds */
54 int cur_tracklen
; /* Length in seconds of current track */
55 int cur_cdlen
; /* Length in seconds of entire CD */
56 int cur_ntracks
; /* Number of tracks on CD (= tracks + sections) */
57 int cur_nsections
; /* Number of sections currently defined */
58 enum wm_cd_modes cur_cdmode
= WM_CDM_EJECTED
;
59 int cur_listno
; /* Current index into the play list, if playing */
60 char * cur_artist
; /* Name of current CD's artist */
61 char * cur_cdname
; /* Album name */
62 char * cur_trackname
; /* Take a guess */
63 char cur_contd
; /* Continued flag */
64 char cur_avoid
; /* Avoid flag */
66 int exit_on_eject
= 0;
68 int cur_stopmode
= -1;
69 extern int info_modified
;
74 * Add a new track to the CD info structure. Pass the position of the new
75 * entry in the track list -- 0 will make this the first track, 1 the second,
76 * etc. The new entry will be zeroed out.
82 struct wm_trackinfo
*newtrk
;
84 /* Easy case: the list is empty */
85 if (cd
->trk
== NULL
) {
86 if ((cd
->trk
= (struct wm_trackinfo
*) calloc(1,
87 sizeof(*newtrk
))) == NULL
)
90 perror("insert_trackinfo");
96 /* Stick the new entry in cd->trk[]. */
97 if ((newtrk
= (struct wm_trackinfo
*) malloc(sizeof(*newtrk
) *
98 (cur_ntracks
+ 1))) == NULL
)
102 memcpy(newtrk
, cd
->trk
, sizeof(*newtrk
) * num
);
103 memset(&newtrk
[num
], 0, sizeof(*newtrk
));
104 if (num
< cur_ntracks
)
105 memcpy(&newtrk
[num
+ 1], &cd
->trk
[num
], sizeof(*newtrk
) *
106 (cur_ntracks
- num
));
115 * Split a track in two at a particular position (absolute, in frames). All
116 * internal data structures and variables will be adjusted to the new
117 * numbering scheme. Pass in the track number (>=1) to split, which is also
118 * the index into cd->trk[] of the new entry.
120 * If pos is within 1 second of the start of another track, the split fails.
122 * Returns 1 on success, 0 if the track couldn't be inserted.
124 * Note: updating user interface elements is up to the caller.
127 split_trackinfo( int pos
)
131 if (pos
< cd
->trk
[0].start
)
134 /* First find the appropriate track. */
135 for (num
= 0; num
< cur_ntracks
; num
++)
136 if (cd
->trk
[num
].start
- 75 < pos
&&
137 cd
->trk
[num
].start
+ 75 > pos
)
139 else if (cd
->trk
[num
].start
> pos
)
144 /* Insert the new entry into the track array. */
145 insert_trackinfo(num
);
147 /* Update the easy variables. */
150 if (cur_firsttrack
> num
)
152 if (cur_lasttrack
> num
)
155 /* Update the user-defined playlists. */
156 if (cd
->lists
!= NULL
)
157 for (l
= 0; cd
->lists
[l
].name
!= NULL
; l
++)
158 if (cd
->lists
[l
].list
!= NULL
)
159 for (i
= 0; cd
->lists
[l
].list
[i
]; i
++)
160 if (cd
->lists
[l
].list
[i
] > num
)
161 cd
->lists
[l
].list
[i
]++;
163 /* Update the internal playlist. */
164 if (playlist
!= NULL
)
165 for (i
= 0; playlist
[i
].start
; i
++)
167 if (playlist
[i
].start
> num
)
169 if (playlist
[i
].end
> num
)
173 /* Now adjust the information in cd->trk[]. */
174 cd
->trk
[num
].start
= pos
;
175 if (num
== cur_ntracks
)
176 cd
->trk
[num
].length
= cur_cdlen
- pos
/ 75;
178 cd
->trk
[num
].length
= (cd
->trk
[num
+ 1].start
- pos
) / 75;
179 cd
->trk
[num
- 1].length
-= cd
->trk
[num
].length
;
180 if (cur_track
== num
)
181 cur_tracklen
-= cd
->trk
[num
].length
;
182 cd
->trk
[num
].track
= cd
->trk
[num
- 1].track
;
183 cd
->trk
[num
].data
= cd
->trk
[num
- 1].data
;
184 cd
->trk
[num
].contd
= 1;
185 cd
->trk
[num
].volume
= cd
->trk
[num
- 1].volume
;
187 if (cd
->trk
[num
- 1].section
== 0)
188 cd
->trk
[num
- 1].section
= 1;
189 cd
->trk
[num
].section
= cd
->trk
[num
- 1].section
+ 1;
194 for (i
= num
+ 1; i
< cur_ntracks
; i
++)
195 if (cd
->trk
[i
].track
== cd
->trk
[num
].track
)
196 cd
->trk
[i
].section
++;
204 * Remove a track's internal data. This is similar to split_trackinfo()
205 * above, but simpler. A track's initial section can't be removed. Track
206 * numbers start at 0.
208 * Returns 1 on success, 0 on failure.
211 remove_trackinfo( int num
)
215 if (num
< 1 || num
>= cur_ntracks
|| cd
->trk
[num
].section
< 2)
218 cd
->trk
[num
- 1].length
+= cd
->trk
[num
].length
;
220 for (i
= num
; i
< cur_ntracks
- 1; i
++)
221 memcpy(&cd
->trk
[i
], &cd
->trk
[i
+ 1], sizeof(cd
->trk
[0]));
225 if (cur_firsttrack
> num
)
227 if (cur_lasttrack
> num
)
230 /* Update the user-defined playlists. */
231 if (cd
->lists
!= NULL
)
232 for (l
= 0; cd
->lists
[l
].name
!= NULL
; l
++)
233 if (cd
->lists
[l
].list
!= NULL
)
234 for (i
= 0; cd
->lists
[l
].list
[i
]; i
++)
235 if (cd
->lists
[l
].list
[i
] > num
)
236 cd
->lists
[l
].list
[i
]--;
238 /* Update the internal playlist. */
239 if (playlist
!= NULL
)
240 for (i
= 0; playlist
[i
].start
; i
++)
242 if (playlist
[i
].start
> num
)
244 if (playlist
[i
].end
> num
)
252 * Update the section numbers for this track. If this is the only
253 * user-created section in a track, get rid of the section number
254 * in the track's entry.
256 if (num
== cur_ntracks
|| cd
->trk
[num
- 1].track
!= cd
->trk
[num
].track
)
258 if (cd
->trk
[num
- 1].section
== 1)
259 cd
->trk
[num
- 1].section
= 0;
262 for (i
= num
; i
< cur_ntracks
; i
++)
263 if (cd
->trk
[i
].track
== cd
->trk
[num
- 1].track
)
264 cd
->trk
[i
].section
--;
272 * Return a scrolling list entry.
277 static char buf
[600];
278 char *name
, tracknum
[20];
282 if (num
>= 0 && num
< cur_ntracks
)
289 sdigits = cur_nsections < 9 ? -1 : -2;
293 digits = cd->trk[num].track < 10 ? 3 : 2;
294 sdigits = cur_nsections < 9 ? -1 : -3;
299 sdigits
= cur_nsections
< 9 ? -1 : -2;
301 name
= cd
->trk
[num
].songname
? cd
->trk
[num
].songname
: "";
305 if (cd
->trk
[num
].section
> 9)
307 sprintf(tracknum
, "%*d.%d", digits
,
309 cd
->trk
[num
].section
);
311 if (cd
->trk
[num
].section
)
313 sprintf(tracknum
, "%*d.%*d", digits
,
314 cd
->trk
[num
].track
, sdigits
,
315 cd
->trk
[num
].section
);
317 sprintf(tracknum
, "%*d%*s", digits
,
320 /* 2 - sdigits - big_spaces, " ");*/
324 sprintf(tracknum
, "%*d", digits
, cd
->trk
[num
].track
);
327 if (cd
->trk
[num
].data
)
329 sprintf(buf
, "%s) %3dMB %s", tracknum
,
330 cd
->trk
[num
].length
/ 1024, name
);
332 sprintf(buf
, "%s) %02d:%02d %s", tracknum
,
333 cd
->trk
[num
].length
/ 60,
334 cd
->trk
[num
].length
% 60, name
);
346 * Return a track's name.
351 if (num
>= 0 && num
< cur_ntracks
)
353 if (cd
->trk
[num
].songname
)
355 return (cd
->trk
[num
].songname
);
367 * Return a track's length in seconds.
372 if (cd
!= NULL
&& num
>= 0 && num
< cur_ntracks
)
373 return (cd
->trk
[num
].length
);
379 * get_default_volume()
381 * Return the default volume (0-32, 0=none) for the CD or a track.
384 get_default_volume( int track
)
388 else if (track
<= cur_ntracks
)
389 return (cd
->trk
[track
- 1].volume
);
397 * Return the contd value for a track.
402 if (num
>= 0 && num
< cur_ntracks
)
403 return (cd
->trk
[num
].contd
);
411 * Return the avoid value for a track.
416 if (num
>= 0 && num
< cur_ntracks
)
417 return (cd
->trk
[num
].avoid
);
425 * Is autoplay set on this disc?
430 return ( cd
->autoplay
);
436 * Return the default playmode for the CD.
441 return ( cd
->playmode
);
447 * Return the total running time for the current playlist in seconds.
454 if (playlist
== NULL
|| playlist
[0].start
== 0 || cur_firsttrack
== -1)
455 return (cd
== NULL
? 0 : cd
->length
);
457 for (i
= 0; playlist
[i
].start
; i
++)
460 return (playlist
[i
].starttime
);
466 * Set the default volume for the CD or a track.
469 default_volume( int track
, int vol
)
473 else if (track
<= cur_ntracks
)
474 cd
->trk
[track
- 1].volume
= vol
;
478 * Play the next thing on the playlist, if any.
481 play_next_entry( int forward
)
485 if (playlist
!= NULL
&& playlist
[cur_listno
].start
)
487 wm_cd_play(playlist
[cur_listno
].start
, 0,
488 playlist
[cur_listno
].end
);
496 * Play the next track, following playlists as necessary.
499 play_next_track( int forward
)
504 /* Is the current playlist entry done? Move on, if so. */
505 if (playlist
== NULL
|| cur_track
+ 1 == playlist
[cur_listno
- 1].end
)
506 play_next_entry( forward
);
508 wm_cd_play(cur_track
+ 1, 0, playlist
[cur_listno
- 1].end
);
512 * Play the previous track, hopping around the playlist as necessary.
515 play_prev_track( int forward
)
520 if (playlist
== NULL
)
523 /* If we're in the middle of this playlist entry, go back one track */
524 if (cur_track
> playlist
[cur_listno
- 1].start
)
525 wm_cd_play(cur_track
- 1, 0, playlist
[cur_listno
- 1].end
);
530 wm_cd_play(playlist
[cur_listno
- 1].end
- 1, 0,
531 playlist
[cur_listno
- 1].end
);
534 wm_cd_play(playlist
[0].start
, 0, playlist
[0].end
);
538 * stash_cdinfo(artist, cdname)
541 stash_cdinfo(char *artist
, char *cdname
, int autoplay
, int playmode
)
545 if (strcmp(cd
->artist
, artist
))
547 strcpy(cd
->artist
, artist
);
549 if (strcmp(cd
->cdname
, cdname
))
551 strcpy(cd
->cdname
, cdname
);
553 if (!!cd
->autoplay
!= !!autoplay
)
555 cd
->autoplay
= autoplay
;
557 if (!!cd
->playmode
!= !!playmode
)
559 cd
->playmode
= playmode
;
561 } /* stash_cdinfo() */
566 * Clear out all a CD's soft information (presumably in preparation for
567 * reloading from the database.)
572 struct wm_playlist
*l
;
577 cd
->artist
[0] = cd
->cdname
[0] = '\0';
578 cd
->autoplay
= cd
->playmode
= cd
->volume
= 0;
580 freeup(&cd
->otherrc
);
581 freeup(&cd
->otherdb
);
583 if (thiscd
.lists
!= NULL
)
585 for (l
= thiscd
.lists
; l
->name
!= NULL
; l
++)
590 freeup( (char **)&thiscd
.lists
);
593 for (i
= 0; i
< cur_ntracks
; i
++)
595 freeup(&cd
->trk
[i
].songname
);
596 freeup(&cd
->trk
[i
].otherrc
);
597 freeup(&cd
->trk
[i
].otherdb
);
598 cd
->trk
[i
].avoid
= cd
->trk
[i
].contd
= 0;
599 cd
->trk
[i
].volume
= 0;
600 if (cd
->trk
[i
].section
> 1)
601 remove_trackinfo(i
--);
607 * stash_trkinfo(track, songname, contd, avoid)
609 * Update information about a track on the current CD.
612 stash_trkinfo( int track
, char *songname
, int contd
, int avoid
)
617 if (!!cd
->trk
[track
].contd
!= !!contd
)
619 cd
->trk
[track
].contd
= track
? contd
: 0;
621 if (!!cd
->trk
[track
].avoid
!= !!avoid
)
623 cd
->trk
[track
].avoid
= avoid
;
625 if ((cd
->trk
[track
].songname
== NULL
&& songname
[0]) ||
626 (cd
->trk
[track
].songname
!= NULL
&&
627 strcmp(cd
->trk
[track
].songname
, songname
)))
630 wm_strmcpy(&cd
->trk
[track
].songname
, songname
);
638 * Add a playlist to a CD.
641 new_list(cd
, listname
)
642 struct wm_cdinfo
*cd
;
646 struct wm_playlist
*l
;
648 if (cd
->lists
!= NULL
)
650 for (nlists
= 0; cd
->lists
[nlists
].name
!= NULL
; nlists
++)
652 l
= (struct wm_playlist
*)realloc(cd
->lists
, (nlists
+ 2) *
653 sizeof (struct wm_playlist
));
656 l
= (struct wm_playlist
*)malloc(2 * sizeof (struct wm_playlist
));
661 l
[nlists
+ 1].name
= NULL
;
662 l
[nlists
].name
= NULL
; /* so wm_strmcpy doesn't free() it */
663 wm_strmcpy(&l
[nlists
].name
, listname
);
664 l
[nlists
].list
= NULL
;
673 * Construct a playlist for the current CD. If we're in shuffle mode, play
674 * each non-avoided track once, keeping continued tracks in the right order.
676 * If playmode is 2, use playlist number (playmode-2). XXX should do
677 * bounds checking on this, probably.
679 * If consecutive tracks are being played, only make one playlist entry for
680 * them, so the CD player won't pause between tracks while we wake up.
683 make_playlist( int playmode
, int starttrack
)
685 int i
, avoiding
= 1, entry
= 0, count
, track
,
689 if (playlist
!= NULL
)
691 playlist
= malloc(sizeof (*playlist
) * (cur_ntracks
+ 1));
692 if (playlist
== NULL
)
698 /* If this is a data-only CD, we can't play it. */
699 if ((starttrack
&& cd
->trk
[starttrack
- 1].data
) ||
700 (cur_ntracks
== 1 && cd
->trk
[0].data
))
702 playlist
[0].start
= 0;
704 playlist
[1].start
= 0;
710 char *done
= malloc(cur_ntracks
);
714 perror("randomizer");
719 if (starttrack
&& cd
->trk
[starttrack
- 1].avoid
)
721 for (i
= 0; i
< cur_ntracks
; i
++)
722 if (cd
->trk
[i
].contd
|| cd
->trk
[i
].avoid
||
731 for (i
= 0; i
< count
; i
++)
733 int end
; /* for readability */
736 track
= starttrack
- 1;
740 while (done
[track
= rand() % cur_ntracks
])
743 playlist
[i
].start
= track
+ 1;
745 /* play all subsequent continuation tracks too */
746 for (end
= track
+ 1; end
< cur_ntracks
+ 1; end
++)
747 if (! cd
->trk
[end
].contd
||
748 cd
->trk
[end
].avoid
||
751 playlist
[i
].end
= end
+ 1;
755 playlist
[i
].start
= 0;
759 else if (playmode
>= 2 && cd
->lists
&& cd
->lists
[playmode
- 2].name
)
761 count
= 2; /* one terminating entry, and one for start */
762 thislist
= cd
->lists
[playmode
- 2].list
;
764 for (i
= 0; thislist
[i
]; i
++)
765 if (thislist
[i
+ 1] != thislist
[i
] + 1)
768 if (playlist
!= NULL
)
770 playlist
= malloc(sizeof (*playlist
) * count
);
771 if (playlist
== NULL
)
780 playlist
[0].start
= starttrack
;
781 for (track
= 0; thislist
[track
]; track
++)
782 if (starttrack
== thislist
[track
])
784 if (! thislist
[track
])
786 playlist
[0].end
= starttrack
+ 1;
787 playlist
[1].start
= thislist
[0];
794 playlist
[0].start
= thislist
[0];
798 for (i
= track
; thislist
[i
]; i
++)
799 if (thislist
[i
+ 1] != thislist
[i
] + 1)
801 playlist
[count
].end
= thislist
[i
] + 1;
803 playlist
[count
].start
= thislist
[i
+ 1];
808 for (i
= starttrack
? starttrack
- 1 : 0; i
< cur_ntracks
; i
++)
809 if (avoiding
&& ! (cd
->trk
[i
].avoid
|| cd
->trk
[i
].data
))
811 playlist
[entry
].start
= i
+ 1;
814 else if (! avoiding
&& (cd
->trk
[i
].avoid
||
817 playlist
[entry
].end
= i
+ 1;
822 playlist
[entry
].end
= i
+ 1;
823 playlist
[entry
+ !avoiding
].start
= 0;
827 * Now go through the list, whatever its source, and figure out
828 * cumulative starting times for each entry.
832 playlist
[entry
].starttime
= count
;
834 if (playlist
[entry
].start
)
835 for (i
= playlist
[entry
].start
; i
<
836 playlist
[entry
].end
; i
++)
837 count
+= cd
->trk
[i
- 1].length
;
838 } while (playlist
[entry
++].start
);
842 * Find a particular track's location in the current playlist. Sets the
843 * appropriate variables (cur_listno, cur_firsttrack, cur_lasttrack).
846 pl_find_track( int track
)
850 if (playlist
== NULL
)
852 fprintf(stderr
, "Null playlist! Huh?\n");
856 for (i
= 0; playlist
[i
].start
; i
++)
857 if (track
>= playlist
[i
].start
&& track
< playlist
[i
].end
)
860 cur_firsttrack
= playlist
[i
].start
;
861 cur_lasttrack
= playlist
[i
].end
- 1;
866 * Couldn't find the track in question. Make a special entry with
869 if (! playlist
[i
].start
)
871 playlist
= realloc(playlist
, (i
+ 2) * sizeof(*playlist
));
872 if (playlist
== NULL
)
874 perror("playlist realloc");
878 playlist
[i
+ 1].start
= playlist
[i
+ 1].end
= 0;
879 playlist
[i
+ 1].starttime
= playlist
[i
].starttime
+
880 cd
->trk
[track
- 1].length
;
881 playlist
[i
].start
= track
;
882 playlist
[i
].end
= track
+ 1;
884 cur_firsttrack
= track
;
885 cur_lasttrack
= track
;