home *** CD-ROM | disk | FTP | other *** search
- /*
- * @(#)cdinfo.c 1.80 11/15/92
- *
- * Get information about a CD.
- */
- static char *ident = "@(#)cdinfo.c 1.80 11/15/92";
-
- #include <errno.h>
- #include <stdio.h>
- #include <sys/types.h>
- #include "struct.h"
-
- void *calloc(), *malloc(), *realloc(), print_cdinfo(), wipe_cdinfo();
- char *strchr(), *getenv();
-
- struct play *playlist = NULL;
- struct cdinfo thiscd, *cd = &thiscd;
-
- int cur_track = -1; /* Current track number, starting at 1 */
- int cur_index = 0; /* Current index mark */
- int cur_lasttrack = 999; /* Last track to play in current chunk */
- int cur_firsttrack = 0; /* First track of current chunk */
- int cur_pos_abs; /* Current absolute position in seconds */
- int cur_frame; /* Current frame number */
- int cur_pos_rel; /* Current track-relative position in seconds */
- int cur_tracklen; /* Length in seconds of current track */
- int cur_cdlen; /* Length in seconds of entire CD */
- int cur_ntracks; /* Number of tracks on CD (= tracks + sections) */
- int cur_nsections; /* Number of sections currently defined */
- int cur_cdmode; /* CD play mode (1=play, 3=pause, 4=stop, 5=eject) */
- int cur_listno; /* Current index into the play list, if playing */
- char * cur_artist; /* Name of current CD's artist */
- char * cur_cdname; /* Album name */
- char * cur_trackname; /* Take a guess */
- char cur_contd; /* Continued flag */
- char cur_avoid; /* Avoid flag */
-
- int exit_on_eject = 0;
-
- extern int cur_stopmode;
- extern int info_modified;
-
- /*
- * insert_trackinfo()
- *
- * Add a new track to the CD info structure. Pass the position of the new
- * entry in the track list -- 0 will make this the first track, 1 the second,
- * etc. The new entry will be zeroed out.
- */
- void
- insert_trackinfo(num)
- int num;
- {
- struct trackinfo *newtrk;
-
- /* Easy case: the list is empty */
- if (cd->trk == NULL)
- if ((cd->trk = (struct trackinfo *) calloc(1,
- sizeof(*newtrk))) == NULL)
- {
- nomem:
- perror("insert_trackinfo");
- exit(1);
- }
- else
- return;
-
- /* Stick the new entry in cd->trk[]. */
- if ((newtrk = (struct trackinfo *) malloc(sizeof(*newtrk) *
- (cur_ntracks + 1))) == NULL)
- goto nomem;
-
- if (num)
- memcpy(newtrk, cd->trk, sizeof(*newtrk) * num);
- memset(&newtrk[num], 0, sizeof(*newtrk));
- if (num < cur_ntracks)
- memcpy(&newtrk[num + 1], &cd->trk[num], sizeof(*newtrk) *
- (cur_ntracks - num));
-
- free(cd->trk);
- cd->trk = newtrk;
- }
-
- /*
- * split_trackinfo()
- *
- * Split a track in two at a particular position (absolute, in frames). All
- * internal data structures and variables will be adjusted to the new
- * numbering scheme. Pass in the track number (>=1) to split, which is also
- * the index into cd->trk[] of the new entry.
- *
- * If pos is within 1 second of the start of another track, the split fails.
- *
- * Returns 1 on success, 0 if the track couldn't be inserted.
- *
- * Note: updating user interface elements is up to the caller.
- */
- split_trackinfo(pos)
- int pos;
- {
- int i, l, num;
-
- if (pos < cd->trk[0].start)
- return (0);
-
- /* First find the appropriate track. */
- for (num = 0; num < cur_ntracks; num++)
- if (cd->trk[num].start - 75 < pos &&
- cd->trk[num].start + 75 > pos)
- return (0);
- else if (cd->trk[num].start > pos)
- break;
- if (num == 0)
- return (0);
-
- /* Insert the new entry into the track array. */
- insert_trackinfo(num);
-
- /* Update the easy variables. */
- if (cur_track > num)
- cur_track++;
- if (cur_firsttrack > num)
- cur_firsttrack++;
- if (cur_lasttrack > num)
- cur_lasttrack++;
-
- /* Update the user-defined playlists. */
- if (cd->lists != NULL)
- for (l = 0; cd->lists[l].name != NULL; l++)
- if (cd->lists[l].list != NULL)
- for (i = 0; cd->lists[l].list[i]; i++)
- if (cd->lists[l].list[i] > num)
- cd->lists[l].list[i]++;
-
- /* Update the internal playlist. */
- if (playlist != NULL)
- for (i = 0; playlist[i].start; i++)
- {
- if (playlist[i].start > num)
- playlist[i].start++;
- if (playlist[i].end > num)
- playlist[i].end++;
- }
-
- /* Now adjust the information in cd->trk[]. */
- cd->trk[num].start = pos;
- if (num == cur_ntracks)
- cd->trk[num].length = cur_cdlen - pos / 75;
- else
- cd->trk[num].length = (cd->trk[num + 1].start - pos) / 75;
- cd->trk[num - 1].length -= cd->trk[num].length;
- if (cur_track == num)
- cur_tracklen -= cd->trk[num].length;
- cd->trk[num].track = cd->trk[num - 1].track;
- cd->trk[num].data = cd->trk[num - 1].data;
- cd->trk[num].contd = 1;
- cd->trk[num].volume = cd->trk[num - 1].volume;
-
- if (cd->trk[num - 1].section == 0)
- cd->trk[num - 1].section = 1;
- cd->trk[num].section = cd->trk[num - 1].section + 1;
-
- cur_ntracks++;
- cur_nsections++;
-
- for (i = num + 1; i < cur_ntracks; i++)
- if (cd->trk[i].track == cd->trk[num].track)
- cd->trk[i].section++;
-
- return (1);
- }
-
- /*
- * remove_trackinfo()
- *
- * Remove a track's internal data. This is similar to split_trackinfo()
- * above, but simpler. A track's initial section can't be removed. Track
- * numbers start at 0.
- *
- * Returns 1 on success, 0 on failure.
- */
- remove_trackinfo(num)
- int num;
- {
- int i, l;
-
- if (num < 1 || num >= cur_ntracks || cd->trk[num].section < 2)
- return (0);
-
- cd->trk[num - 1].length += cd->trk[num].length;
-
- for (i = num; i < cur_ntracks - 1; i++)
- memcpy(&cd->trk[i], &cd->trk[i + 1], sizeof(cd->trk[0]));
-
- if (cur_track > num)
- cur_track--;
- if (cur_firsttrack > num)
- cur_firsttrack--;
- if (cur_lasttrack > num)
- cur_lasttrack--;
-
- /* Update the user-defined playlists. */
- if (cd->lists != NULL)
- for (l = 0; cd->lists[l].name != NULL; l++)
- if (cd->lists[l].list != NULL)
- for (i = 0; cd->lists[l].list[i]; i++)
- if (cd->lists[l].list[i] > num)
- cd->lists[l].list[i]--;
-
- /* Update the internal playlist. */
- if (playlist != NULL)
- for (i = 0; playlist[i].start; i++)
- {
- if (playlist[i].start > num)
- playlist[i].start--;
- if (playlist[i].end > num)
- playlist[i].end--;
- }
-
- cur_ntracks--;
- cur_nsections--;
-
- /* Update the section numbers for this track. */
- if (cd->trk[num - 1].track != cd->trk[num].track)
- {
- if (cd->trk[num - 1].section == 1)
- cd->trk[num - 1].section = 0;
- }
- else
- for (i = num; i < cur_ntracks; i++)
- if (cd->trk[i].track == cd->trk[num - 1].track)
- cd->trk[i].section--;
-
- return (1);
- }
-
- /*
- * listentry()
- *
- * Return a scrolling list entry.
- *
- * XXX - should count half-spaces to make entries line up when sections
- * are present
- */
- char *
- listentry(num)
- int num;
- {
- static char buf[600];
- char *name, tracknum[20];
- int digits;
-
- if (num >= 0 && num < cur_ntracks)
- {
- digits = cd->trk[num].track < 10 ? 3 : 2;
- name = cd->trk[num].songname ? cd->trk[num].songname : "";
-
- if (cur_nsections)
- if (cd->trk[num].section)
- sprintf(tracknum, "%*d.%d", digits,
- cd->trk[num].track,
- cd->trk[num].section);
- else
- sprintf(tracknum, "%*d ", digits,
- cd->trk[num].track);
- else
- sprintf(tracknum, "%*d", digits, cd->trk[num].track);
-
- if (cd->trk[num].data)
- sprintf(buf, "%s) %3dMB %s", tracknum,
- cd->trk[num].length / 1024, name);
- else
- sprintf(buf, "%s) %02d:%02d %s", tracknum,
- cd->trk[num].length / 60,
- cd->trk[num].length % 60, name);
-
- return (buf);
- }
- else
- return (NULL);
- }
-
- /*
- * trackname()
- *
- * Return a track's name.
- */
- char *
- trackname(num)
- int num;
- {
- if (num >= 0 && num < cur_ntracks)
- if (cd->trk[num].songname)
- return (cd->trk[num].songname);
- else
- return ("");
- else
- return (NULL);
- }
-
- /*
- * tracklen()
- *
- * Return a track's length in seconds.
- */
- tracklen(num)
- int num;
- {
- if (cd != NULL && num >= 0 && num < cur_ntracks)
- return (cd->trk[num].length);
- else
- return (0);
- }
-
- /*
- * get_default_volume()
- *
- * Return the default volume (0-32, 0=none) for the CD or a track.
- */
- get_default_volume(track)
- int track;
- {
- if (! track)
- return (cd->volume);
- else if (track <= cur_ntracks)
- return (cd->trk[track - 1].volume);
- else
- return (0);
- }
-
- /*
- * get_contd()
- *
- * Return the contd value for a track.
- */
- get_contd(num)
- int num;
- {
- if (num >= 0 && num < cur_ntracks)
- return (cd->trk[num].contd);
- else
- return (NULL);
- }
-
- /*
- * get_avoid()
- *
- * Return the avoid value for a track.
- */
- get_avoid(num)
- int num;
- {
- if (num >= 0 && num < cur_ntracks)
- return (cd->trk[num].avoid);
- else
- return (NULL);
- }
-
- /*
- * get_autoplay()
- *
- * Is autoplay set on this disc?
- */
- get_autoplay()
- {
- return (cd->autoplay);
- }
-
- /*
- * get_playmode()
- *
- * Return the default playmode for the CD.
- */
- get_playmode()
- {
- return (cd->playmode);
- }
-
- /*
- * get_runtime()
- *
- * Return the total running time for the current playlist in seconds.
- */
- get_runtime()
- {
- int i;
-
- if (playlist == NULL || playlist[0].start == 0 || cur_firsttrack == -1)
- return (cd == NULL ? 0 : cd->length);
-
- for (i = 0; playlist[i].start; i++)
- ;
-
- return (playlist[i].starttime);
- }
-
- /*
- * default_volume()
- *
- * Set the default volume for the CD or a track.
- */
- void
- default_volume(track, vol)
- int track, vol;
- {
- if (track == 0)
- cd->volume = vol;
- else if (track <= cur_ntracks)
- cd->trk[track - 1].volume = vol;
- }
-
- /*
- * Play the next thing on the playlist, if any.
- */
- void
- play_next_entry()
- {
- if (cd == NULL)
- return;
- if (playlist != NULL && playlist[cur_listno].start)
- {
- play_cd(playlist[cur_listno].start, 0,
- playlist[cur_listno].end);
- cur_listno++;
- }
- else
- stop_cd();
- }
-
- /*
- * Play the next track, following playlists as necessary.
- */
- void
- play_next_track()
- {
- if (cd == NULL)
- return;
-
- /* Is the current playlist entry done? Move on, if so. */
- if (playlist == NULL || cur_track + 1 == playlist[cur_listno - 1].end)
- play_next_entry();
- else
- play_cd(cur_track + 1, 0, playlist[cur_listno - 1].end);
- }
-
- /*
- * Play the previous track, hopping around the playlist as necessary.
- */
- void
- play_prev_track()
- {
- if (cd == NULL)
- return;
-
- if (playlist == NULL)
- return;
-
- /* If we're in the middle of this playlist entry, go back one track */
- if (cur_track > playlist[cur_listno - 1].start)
- play_cd(cur_track - 1, 0, playlist[cur_listno - 1].end);
- else
- if (cur_listno > 1)
- {
- cur_listno--;
- play_cd(playlist[cur_listno - 1].end - 1, 0,
- playlist[cur_listno - 1].end);
- }
- else
- play_cd(playlist[0].start, 0, playlist[0].end);
- }
-
- /*
- * stash_cdinfo(artist, cdname)
- */
- void
- stash_cdinfo(artist, cdname, autoplay, playmode)
- char *artist, *cdname;
- int autoplay, playmode;
- {
- int listsize, i;
-
- if (cd != NULL)
- {
- if (strcmp(cd->artist, artist))
- info_modified = 1;
- strcpy(cd->artist, artist);
-
- if (strcmp(cd->cdname, cdname))
- info_modified = 1;
- strcpy(cd->cdname, cdname);
-
- if (!!cd->autoplay != !!autoplay)
- info_modified = 1;
- cd->autoplay = autoplay;
-
- if (!!cd->playmode != !!playmode)
- info_modified = 1;
- cd->playmode = playmode;
- }
- }
-
- /* Free some memory and set a pointer to null. */
- void
- freeup(x)
- void **x;
- {
- if (*x != NULL)
- {
- free(*x);
- *x = NULL;
- }
- }
-
- /*
- * wipe_cdinfo()
- *
- * Clear out all a CD's soft information (presumably in preparation for
- * reloading from the database.)
- */
- void
- wipe_cdinfo()
- {
- struct playlist *l;
- int i;
-
- if (cd != NULL)
- {
- cd->artist[0] = cd->cdname[0] = '\0';
- cd->autoplay = cd->playmode = cd->volume = 0;
- cd->whichdb = NULL;
- freeup(&cd->otherrc);
- freeup(&cd->otherdb);
-
- if (thiscd.lists != NULL)
- {
- for (l = thiscd.lists; l->name != NULL; l++)
- {
- free(l->name);
- free(l->list);
- }
- freeup(&thiscd.lists);
- }
-
- for (i = 0; i < cur_ntracks; i++)
- {
- freeup(&cd->trk[i].songname);
- freeup(&cd->trk[i].otherrc);
- freeup(&cd->trk[i].otherdb);
- cd->trk[i].avoid = cd->trk[i].contd = 0;
- cd->trk[i].volume = 0;
- if (cd->trk[i].section > 1)
- remove_trackinfo(i--);
- }
- }
- }
-
- /*
- * stash_trkinfo(track, songname, contd, avoid)
- *
- * Update information about a track on the current CD.
- */
- void
- stash_trkinfo(track, songname, contd, avoid)
- int track, contd, avoid;
- char *songname;
- {
- if (cd != NULL)
- {
- track--;
- if (!!cd->trk[track].contd != !!contd)
- info_modified = 1;
- cd->trk[track].contd = track ? contd : 0;
-
- if (!!cd->trk[track].avoid != !!avoid)
- info_modified = 1;
- cd->trk[track].avoid = avoid;
-
- if ((cd->trk[track].songname == NULL && songname[0]) ||
- (cd->trk[track].songname != NULL &&
- strcmp(cd->trk[track].songname, songname)))
- {
- info_modified = 1;
- strmcpy(&cd->trk[track].songname, songname);
- }
- }
- }
-
- /*
- * new_list()
- *
- * Add a playlist to a CD.
- */
- struct playlist *
- new_list(cd, listname)
- struct cdinfo *cd;
- char *listname;
- {
- int nlists = 0;
- struct playlist *l;
-
- if (cd->lists != NULL)
- {
- for (nlists = 0; cd->lists[nlists].name != NULL; nlists++)
- ;
- l = (struct playlist *)realloc(cd->lists, (nlists + 2) *
- sizeof (struct playlist));
- }
- else
- l = (struct playlist *)malloc(2 * sizeof (struct playlist));
-
- if (l == NULL)
- return (NULL);
-
- l[nlists + 1].name = NULL;
- l[nlists].name = NULL; /* so strmcpy doesn't free() it */
- strmcpy(&l[nlists].name, listname);
- l[nlists].list = NULL;
- cd->lists = l;
-
- return (&l[nlists]);
- }
-
- /*
- * make_playlist()
- *
- * Construct a playlist for the current CD. If we're in shuffle mode, play
- * each non-avoided track once, keeping continued tracks in the right order.
- *
- * If playmode is 2, use playlist number (playmode-2). XXX should do
- * bounds checking on this, probably.
- *
- * If consecutive tracks are being played, only make one playlist entry for
- * them, so the CD player won't pause between tracks while we wake up.
- */
- void
- make_playlist(playmode, starttrack)
- int playmode, starttrack;
- {
- int i, listsize = 0, avoiding = 1, entry = 0, count, track,
- *thislist;
-
- cur_listno = 0;
- if (playlist != NULL)
- free(playlist);
- playlist = malloc(sizeof (*playlist) * (cur_ntracks + 1));
- if (playlist == NULL)
- {
- perror("playlist");
- exit(1);
- }
-
- /* If this is a data-only CD, we can't play it. */
- if ((starttrack && cd->trk[starttrack - 1].data) ||
- (cur_ntracks == 1 && cd->trk[0].data))
- {
- playlist[0].start = 0;
- playlist[0].end = 0;
- playlist[1].start = 0;
- return;
- }
-
- if (playmode == 1)
- {
- char *done = malloc(cur_ntracks);
-
- if (done == NULL)
- {
- perror("randomizer");
- exit(1);
- }
-
- count = cur_ntracks;
- if (starttrack && cd->trk[starttrack - 1].avoid)
- count++;
- for (i = 0; i < cur_ntracks; i++)
- if (cd->trk[i].contd || cd->trk[i].avoid ||
- cd->trk[i].data)
- {
- done[i] = 1;
- count--;
- }
- else
- done[i] = 0;
-
- for (i = 0; i < count; i++)
- {
- int end; /* for readability */
- if (starttrack)
- {
- track = starttrack - 1;
- starttrack = 0;
- }
- else
- while (done[track = rand() % cur_ntracks])
- ;
-
- playlist[i].start = track + 1;
-
- /* play all subsequent continuation tracks too */
- for (end = track + 1; end < cur_ntracks + 1; end++)
- if (! cd->trk[end].contd ||
- cd->trk[end].avoid ||
- cd->trk[end].data)
- break;
- playlist[i].end = end + 1;
-
- done[track]++;
- }
- playlist[i].start = 0;
-
- free(done);
- }
- else if (playmode >= 2 && cd->lists && cd->lists[playmode - 2].name)
- {
- count = 2; /* one terminating entry, and one for start */
- thislist = cd->lists[playmode - 2].list;
-
- for (i = 0; thislist[i]; i++)
- if (thislist[i + 1] != thislist[i] + 1)
- count++;
-
- if (playlist != NULL)
- free(playlist);
- playlist = malloc(sizeof (*playlist) * count);
- if (playlist == NULL)
- {
- perror("playlist");
- exit(1);
- }
-
- count = 0;
- if (starttrack)
- {
- playlist[0].start = starttrack;
- for (track = 0; thislist[track]; track++)
- if (starttrack == thislist[track])
- break;
- if (! thislist[track])
- {
- playlist[0].end = starttrack + 1;
- playlist[1].start = thislist[0];
- count = 1;
- track = 0;
- }
- }
- else
- {
- playlist[0].start = thislist[0];
- track = 0;
- }
-
- for (i = track; thislist[i]; i++)
- if (thislist[i + 1] != thislist[i] + 1)
- {
- playlist[count].end = thislist[i] + 1;
- count++;
- playlist[count].start = thislist[i + 1];
- }
- }
- else
- {
- for (i = starttrack ? starttrack - 1 : 0; i < cur_ntracks; i++)
- if (avoiding && ! (cd->trk[i].avoid || cd->trk[i].data))
- {
- playlist[entry].start = i + 1;
- avoiding = 0;
- }
- else if (! avoiding && (cd->trk[i].avoid ||
- cd->trk[i].data))
- {
- playlist[entry].end = i + 1;
- avoiding = 1;
- entry++;
- }
- if (! avoiding)
- playlist[entry].end = i + 1;
- playlist[entry + !avoiding].start = 0;
- }
-
- /*
- * Now go through the list, whatever its source, and figure out
- * cumulative starting times for each entry.
- */
- entry = count = 0;
- do {
- playlist[entry].starttime = count;
-
- if (playlist[entry].start)
- for (i = playlist[entry].start; i <
- playlist[entry].end; i++)
- count += cd->trk[i - 1].length;
- } while (playlist[entry++].start);
- }
-
- /*
- * Find a particular track's location in the current playlist. Sets the
- * appropriate variables (cur_listno, cur_firsttrack, cur_lasttrack).
- */
- void
- pl_find_track(track)
- int track;
- {
- int t, i;
-
- if (playlist == NULL)
- {
- fprintf(stderr, "Null playlist! Huh?\n");
- return;
- }
-
- for (i = 0; playlist[i].start; i++)
- if (track >= playlist[i].start && track < playlist[i].end)
- {
- cur_listno = i + 1;
- cur_firsttrack = playlist[i].start;
- cur_lasttrack = playlist[i].end - 1;
- return;
- }
-
- /*
- * Couldn't find the track in question. Make a special entry with
- * just that track.
- */
- if (! playlist[i].start)
- {
- playlist = realloc(playlist, (i + 2) * sizeof(*playlist));
- if (playlist == NULL)
- {
- perror("playlist realloc");
- exit(1);
- }
-
- playlist[i + 1].start = playlist[i + 1].end = 0;
- playlist[i + 1].starttime = playlist[i].starttime +
- cd->trk[track - 1].length;
- playlist[i].start = track;
- playlist[i].end = track + 1;
- cur_listno = i + 1;
- cur_firsttrack = track;
- cur_lasttrack = track;
- }
- }
-