wmail: change "!stat(...) == 0" to "stat(...) == -1"
[dockapps.git] / ascd / libworkman / cdinfo.c
bloba75749adadf54749dc4be4bacd446f43644d277a
1 /*
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 $";
30 #include <errno.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
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;
72 * insert_trackinfo()
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.
78 void
79 insert_trackinfo(num)
80 int num;
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)
89 nomem:
90 perror("insert_trackinfo");
91 exit(1);
92 } else {
93 return;
94 } /* if() else */
95 } /* if() */
96 /* Stick the new entry in cd->trk[]. */
97 if ((newtrk = (struct wm_trackinfo *) malloc(sizeof(*newtrk) *
98 (cur_ntracks + 1))) == NULL)
99 goto nomem;
101 if (num)
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));
108 free(cd->trk);
109 cd->trk = newtrk;
113 * split_trackinfo()
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 )
129 int i, l, num;
131 if (pos < cd->trk[0].start)
132 return (0);
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)
138 return (0);
139 else if (cd->trk[num].start > pos)
140 break;
141 if (num == 0)
142 return (0);
144 /* Insert the new entry into the track array. */
145 insert_trackinfo(num);
147 /* Update the easy variables. */
148 if (cur_track > num)
149 cur_track++;
150 if (cur_firsttrack > num)
151 cur_firsttrack++;
152 if (cur_lasttrack > num)
153 cur_lasttrack++;
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)
168 playlist[i].start++;
169 if (playlist[i].end > num)
170 playlist[i].end++;
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;
177 else
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;
191 cur_ntracks++;
192 cur_nsections++;
194 for (i = num + 1; i < cur_ntracks; i++)
195 if (cd->trk[i].track == cd->trk[num].track)
196 cd->trk[i].section++;
198 return (1);
202 * remove_trackinfo()
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 )
213 int i, l;
215 if (num < 1 || num >= cur_ntracks || cd->trk[num].section < 2)
216 return (0);
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]));
223 if (cur_track > num)
224 cur_track--;
225 if (cur_firsttrack > num)
226 cur_firsttrack--;
227 if (cur_lasttrack > num)
228 cur_lasttrack--;
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)
243 playlist[i].start--;
244 if (playlist[i].end > num)
245 playlist[i].end--;
248 cur_ntracks--;
249 cur_nsections--;
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;
261 else
262 for (i = num; i < cur_ntracks; i++)
263 if (cd->trk[i].track == cd->trk[num - 1].track)
264 cd->trk[i].section--;
266 return (1);
270 * listentry()
272 * Return a scrolling list entry.
274 char *
275 listentry( int num )
277 static char buf[600];
278 char *name, tracknum[20];
279 int digits;
280 int sdigits;
282 if (num >= 0 && num < cur_ntracks)
286 if (big_spaces)
288 digits = 2;
289 sdigits = cur_nsections < 9 ? -1 : -2;
291 else
293 digits = cd->trk[num].track < 10 ? 3 : 2;
294 sdigits = cur_nsections < 9 ? -1 : -3;
298 digits = 2;
299 sdigits = cur_nsections < 9 ? -1 : -2;
301 name = cd->trk[num].songname ? cd->trk[num].songname : "";
303 if (cur_nsections)
305 if (cd->trk[num].section > 9)
307 sprintf(tracknum, "%*d.%d", digits,
308 cd->trk[num].track,
309 cd->trk[num].section);
310 } else {
311 if (cd->trk[num].section)
313 sprintf(tracknum, "%*d.%*d", digits,
314 cd->trk[num].track, sdigits,
315 cd->trk[num].section);
316 } else {
317 sprintf(tracknum, "%*d%*s", digits,
318 cd->trk[num].track,
319 2 - sdigits, " ");
320 /* 2 - sdigits - big_spaces, " ");*/
323 } else {
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);
331 } else {
332 sprintf(buf, "%s) %02d:%02d %s", tracknum,
333 cd->trk[num].length / 60,
334 cd->trk[num].length % 60, name);
337 return (buf);
338 } else {
339 return (NULL);
341 } /* listentry() */
344 * trackname()
346 * Return a track's name.
348 char *
349 trackname( int num )
351 if (num >= 0 && num < cur_ntracks)
353 if (cd->trk[num].songname)
355 return (cd->trk[num].songname);
356 } else {
357 return ("");
359 } else {
360 return (NULL);
362 } /* trackname() */
365 * tracklen()
367 * Return a track's length in seconds.
370 tracklen( int num )
372 if (cd != NULL && num >= 0 && num < cur_ntracks)
373 return (cd->trk[num].length);
374 else
375 return (0);
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 )
386 if (! track)
387 return (cd->volume);
388 else if (track <= cur_ntracks)
389 return (cd->trk[track - 1].volume);
390 else
391 return (0);
395 * get_contd()
397 * Return the contd value for a track.
400 get_contd( int num )
402 if (num >= 0 && num < cur_ntracks)
403 return (cd->trk[num].contd);
404 else
405 return (0);
409 * get_avoid()
411 * Return the avoid value for a track.
414 get_avoid( int num )
416 if (num >= 0 && num < cur_ntracks)
417 return (cd->trk[num].avoid);
418 else
419 return (0);
423 * get_autoplay()
425 * Is autoplay set on this disc?
428 get_autoplay( void )
430 return ( cd->autoplay );
434 * get_playmode()
436 * Return the default playmode for the CD.
439 get_playmode( void )
441 return ( cd->playmode );
445 * get_runtime()
447 * Return the total running time for the current playlist in seconds.
450 get_runtime( void )
452 int i;
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);
464 * default_volume()
466 * Set the default volume for the CD or a track.
468 void
469 default_volume( int track, int vol )
471 if (track == 0)
472 cd->volume = vol;
473 else if (track <= cur_ntracks)
474 cd->trk[track - 1].volume = vol;
478 * Play the next thing on the playlist, if any.
480 void
481 play_next_entry( int forward )
483 if (cd == NULL)
484 return;
485 if (playlist != NULL && playlist[cur_listno].start)
487 wm_cd_play(playlist[cur_listno].start, 0,
488 playlist[cur_listno].end);
489 cur_listno++;
491 else
492 wm_cd_stop();
496 * Play the next track, following playlists as necessary.
498 void
499 play_next_track( int forward )
501 if (cd == NULL)
502 return;
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 );
507 else
508 wm_cd_play(cur_track + 1, 0, playlist[cur_listno - 1].end);
512 * Play the previous track, hopping around the playlist as necessary.
514 void
515 play_prev_track( int forward )
517 if (cd == NULL)
518 return;
520 if (playlist == NULL)
521 return;
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);
526 else
527 if (cur_listno > 1)
529 cur_listno--;
530 wm_cd_play(playlist[cur_listno - 1].end - 1, 0,
531 playlist[cur_listno - 1].end);
533 else
534 wm_cd_play(playlist[0].start, 0, playlist[0].end);
538 * stash_cdinfo(artist, cdname)
540 void
541 stash_cdinfo(char *artist, char *cdname, int autoplay, int playmode )
543 if (cd != NULL)
545 if (strcmp(cd->artist, artist))
546 info_modified = 1;
547 strcpy(cd->artist, artist);
549 if (strcmp(cd->cdname, cdname))
550 info_modified = 1;
551 strcpy(cd->cdname, cdname);
553 if (!!cd->autoplay != !!autoplay)
554 info_modified = 1;
555 cd->autoplay = autoplay;
557 if (!!cd->playmode != !!playmode)
558 info_modified = 1;
559 cd->playmode = playmode;
561 } /* stash_cdinfo() */
564 * wipe_cdinfo()
566 * Clear out all a CD's soft information (presumably in preparation for
567 * reloading from the database.)
569 void
570 wipe_cdinfo( void )
572 struct wm_playlist *l;
573 int i;
575 if (cd != NULL)
577 cd->artist[0] = cd->cdname[0] = '\0';
578 cd->autoplay = cd->playmode = cd->volume = 0;
579 cd->whichdb = NULL;
580 freeup(&cd->otherrc);
581 freeup(&cd->otherdb);
583 if (thiscd.lists != NULL)
585 for (l = thiscd.lists; l->name != NULL; l++)
587 free(l->name);
588 free(l->list);
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.
611 void
612 stash_trkinfo( int track, char *songname, int contd, int avoid )
614 if (cd != NULL)
616 track--;
617 if (!!cd->trk[track].contd != !!contd)
618 info_modified = 1;
619 cd->trk[track].contd = track ? contd : 0;
621 if (!!cd->trk[track].avoid != !!avoid)
622 info_modified = 1;
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)))
629 info_modified = 1;
630 wm_strmcpy(&cd->trk[track].songname, songname);
636 * new_list()
638 * Add a playlist to a CD.
640 struct wm_playlist *
641 new_list(cd, listname)
642 struct wm_cdinfo *cd;
643 char *listname;
645 int nlists = 0;
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));
655 else
656 l = (struct wm_playlist *)malloc(2 * sizeof (struct wm_playlist));
658 if (l == NULL)
659 return (NULL);
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;
665 cd->lists = l;
667 return (&l[nlists]);
671 * make_playlist()
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.
682 void
683 make_playlist( int playmode, int starttrack )
685 int i, avoiding = 1, entry = 0, count, track,
686 *thislist;
688 cur_listno = 0;
689 if (playlist != NULL)
690 free(playlist);
691 playlist = malloc(sizeof (*playlist) * (cur_ntracks + 1));
692 if (playlist == NULL)
694 perror("playlist");
695 exit(1);
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;
703 playlist[0].end = 0;
704 playlist[1].start = 0;
705 return;
708 if (playmode == 1)
710 char *done = malloc(cur_ntracks);
712 if (done == NULL)
714 perror("randomizer");
715 exit(1);
718 count = cur_ntracks;
719 if (starttrack && cd->trk[starttrack - 1].avoid)
720 count++;
721 for (i = 0; i < cur_ntracks; i++)
722 if (cd->trk[i].contd || cd->trk[i].avoid ||
723 cd->trk[i].data)
725 done[i] = 1;
726 count--;
728 else
729 done[i] = 0;
731 for (i = 0; i < count; i++)
733 int end; /* for readability */
734 if (starttrack)
736 track = starttrack - 1;
737 starttrack = 0;
739 else
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 ||
749 cd->trk[end].data)
750 break;
751 playlist[i].end = end + 1;
753 done[track]++;
755 playlist[i].start = 0;
757 free(done);
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)
766 count++;
768 if (playlist != NULL)
769 free(playlist);
770 playlist = malloc(sizeof (*playlist) * count);
771 if (playlist == NULL)
773 perror("playlist");
774 exit(1);
777 count = 0;
778 if (starttrack)
780 playlist[0].start = starttrack;
781 for (track = 0; thislist[track]; track++)
782 if (starttrack == thislist[track])
783 break;
784 if (! thislist[track])
786 playlist[0].end = starttrack + 1;
787 playlist[1].start = thislist[0];
788 count = 1;
789 track = 0;
792 else
794 playlist[0].start = thislist[0];
795 track = 0;
798 for (i = track; thislist[i]; i++)
799 if (thislist[i + 1] != thislist[i] + 1)
801 playlist[count].end = thislist[i] + 1;
802 count++;
803 playlist[count].start = thislist[i + 1];
806 else
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;
812 avoiding = 0;
814 else if (! avoiding && (cd->trk[i].avoid ||
815 cd->trk[i].data))
817 playlist[entry].end = i + 1;
818 avoiding = 1;
819 entry++;
821 if (! avoiding)
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.
830 entry = count = 0;
831 do {
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).
845 void
846 pl_find_track( int track )
848 int i;
850 if (playlist == NULL)
852 fprintf(stderr, "Null playlist! Huh?\n");
853 return;
856 for (i = 0; playlist[i].start; i++)
857 if (track >= playlist[i].start && track < playlist[i].end)
859 cur_listno = i + 1;
860 cur_firsttrack = playlist[i].start;
861 cur_lasttrack = playlist[i].end - 1;
862 return;
866 * Couldn't find the track in question. Make a special entry with
867 * just that track.
869 if (! playlist[i].start)
871 playlist = realloc(playlist, (i + 2) * sizeof(*playlist));
872 if (playlist == NULL)
874 perror("playlist realloc");
875 exit(1);
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;
883 cur_listno = i + 1;
884 cur_firsttrack = track;
885 cur_lasttrack = track;