diff --git a/src/wmlib/audio/audio_alsa.c b/src/wmlib/audio/audio_alsa.c index af1a1cb..7c5ffe7 100644 --- a/src/wmlib/audio/audio_alsa.c +++ b/src/wmlib/audio/audio_alsa.c @@ -1,352 +1,355 @@ /* This file is part of the KDE project Copyright (C) Alexander Kern Adapted to support both ALSA V0.x and V1.x APIs for PCM calls (Philip Nelson 2004-03-15) based on mpg123 , Anders Semb Hermansen , Jaroslav Kysela , Ville Syrjala This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "audio.h" #include "../include/wm_struct.h" #include "../include/wm_config.h" #include #ifdef HAVE_ALSA +#define _BSD_SOURCE /* strdup */ +#define _DEFAULT_SOURCE /* stop glibc whining about the previous line */ + #include static char *device = NULL; static snd_pcm_t *handle; static snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */ #if (SND_LIB_MAJOR < 1) int rate = 44100; /* stream rate */ int new_rate; int channels = 2; /* count of channels */ int buffer_time = 2000000; /* ring buffer length in us */ int period_time = 100000; /* period time in us */ #else unsigned int rate = 44100; /* stream rate */ unsigned int new_rate; int channels = 2; /* count of channels */ unsigned int buffer_time = 2000000; /* ring buffer length in us */ unsigned int period_time = 100000; /* period time in us */ #endif #if (SND_LIB_MAJOR < 1) snd_pcm_sframes_t buffer_size; snd_pcm_sframes_t period_size; #else snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t period_size; #endif int alsa_open(void); int alsa_close(void); int alsa_stop(void); int alsa_play(struct wm_cdda_block *blk); int alsa_state(struct wm_cdda_block *blk); struct audio_oops* setup_alsa(const char *dev, const char *ctl); static int set_hwparams(snd_pcm_hw_params_t *params, snd_pcm_access_t accesspar) { int err, dir; /* choose all parameters */ err = snd_pcm_hw_params_any(handle, params); if (err < 0) { ERRORLOG("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err)); return err; } /* set the interleaved read/write format */ err = snd_pcm_hw_params_set_access(handle, params, accesspar); if (err < 0) { ERRORLOG("Access type not available for playback: %s\n", snd_strerror(err)); return err; } /* set the sample format */ err = snd_pcm_hw_params_set_format(handle, params, format); if (err < 0) { ERRORLOG("Sample format not available for playback: %s\n", snd_strerror(err)); return err; } /* set the count of channels */ err = snd_pcm_hw_params_set_channels(handle, params, channels); if (err < 0) { ERRORLOG("Channels count (%i) not available for playbacks: %s\n", channels, snd_strerror(err)); return err; } /* set the stream rate */ #if (SND_LIB_MAJOR < 1) err = new_rate = snd_pcm_hw_params_set_rate_near(handle, params, rate, 0); #else new_rate = rate; err = snd_pcm_hw_params_set_rate_near(handle, params, &rate, 0); #endif if (err < 0) { ERRORLOG("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err)); return err; } if (new_rate != rate) { ERRORLOG("Rate does not match (requested %iHz, get %iHz)\n", rate, new_rate); return -EINVAL; } /* set the buffer time */ #if (SND_LIB_MAJOR < 1) err = snd_pcm_hw_params_set_buffer_time_near(handle, params, buffer_time, &dir); #else err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir); #endif if (err < 0) { ERRORLOG("Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err)); return err; } #if (SND_LIB_MAJOR < 1) buffer_size = snd_pcm_hw_params_get_buffer_size(params); #else err = snd_pcm_hw_params_get_buffer_size(params, &buffer_size); if (err < 0) { ERRORLOG("Unable to get buffer size : %s\n", snd_strerror(err)); return err; } #endif DEBUGLOG("buffersize %lu\n", buffer_size); /* set the period time */ #if (SND_LIB_MAJOR < 1) err = snd_pcm_hw_params_set_period_time_near(handle, params, period_time, &dir); #else err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir); #endif if (err < 0) { ERRORLOG("Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err)); return err; } #if (SND_LIB_MAJOR < 1) period_size = snd_pcm_hw_params_get_period_size(params, &dir); #else err = snd_pcm_hw_params_get_period_size(params, &period_size, &dir); if (err < 0) { ERRORLOG("Unable to get hw period size: %s\n", snd_strerror(err)); } #endif DEBUGLOG("period_size %lu\n", period_size); /* write the parameters to device */ err = snd_pcm_hw_params(handle, params); if (err < 0) { ERRORLOG("Unable to set hw params for playback: %s\n", snd_strerror(err)); return err; } return 0; } static int set_swparams(snd_pcm_sw_params_t *swparams) { int err; /* get the current swparams */ err = snd_pcm_sw_params_current(handle, swparams); if (err < 0) { ERRORLOG("Unable to determine current swparams for playback: %s\n", snd_strerror(err)); return err; } /* start the transfer when the buffer is full */ err = snd_pcm_sw_params_set_start_threshold(handle, swparams, buffer_size); if (err < 0) { ERRORLOG("Unable to set start threshold mode for playback: %s\n", snd_strerror(err)); return err; } /* allow the transfer when at least period_size samples can be processed */ err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_size); if (err < 0) { ERRORLOG("Unable to set avail min for playback: %s\n", snd_strerror(err)); return err; } /* align all transfers to 1 sample */ err = snd_pcm_sw_params_set_xfer_align(handle, swparams, 1); if (err < 0) { ERRORLOG("Unable to set transfer align for playback: %s\n", snd_strerror(err)); return err; } /* write the parameters to the playback device */ err = snd_pcm_sw_params(handle, swparams); if (err < 0) { ERRORLOG("Unable to set sw params for playback: %s\n", snd_strerror(err)); return err; } return 0; } int alsa_open( void ) { int err; snd_pcm_hw_params_t *hwparams; snd_pcm_sw_params_t *swparams; DEBUGLOG("alsa_open\n"); snd_pcm_hw_params_alloca(&hwparams); snd_pcm_sw_params_alloca(&swparams); if((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0/*SND_PCM_NONBLOCK*/)) < 0 ) { ERRORLOG("open failed: %s\n", snd_strerror(err)); return -1; } if((err = set_hwparams(hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { ERRORLOG("Setting of hwparams failed: %s\n", snd_strerror(err)); return -1; } if((err = set_swparams(swparams)) < 0) { ERRORLOG("Setting of swparams failed: %s\n", snd_strerror(err)); return -1; } return 0; } int alsa_close( void ) { int err; DEBUGLOG("alsa_close\n"); err = alsa_stop(); #if (SND_LIB_MAJOR < 1) err = snd_pcm_close(handle); #else err = snd_pcm_close(handle); #endif free(device); return err; } /* * Play some audio and pass a status message upstream, if applicable. * Returns 0 on success. */ int alsa_play(struct wm_cdda_block *blk) { signed short *ptr; int err = 0, frames; ptr = (signed short *)blk->buf; frames = blk->buflen / (channels * 2); DEBUGLOG("play %i frames, %lu bytes\n", frames, blk->buflen); while (frames > 0) { err = snd_pcm_writei(handle, ptr, frames); if (err == -EAGAIN) continue; if(err == -EPIPE) { err = snd_pcm_prepare(handle); continue; } else if (err < 0) break; ptr += err * channels; frames -= err; DEBUGLOG("played %i, rest %i\n", err, frames); } if (err < 0) { ERRORLOG("alsa_write failed: %s\n", snd_strerror(err)); err = snd_pcm_prepare(handle); if (err < 0) { ERRORLOG("Unable to snd_pcm_prepare pcm stream: %s\n", snd_strerror(err)); } blk->status = WM_CDM_CDDAERROR; return err; } return 0; } /* * Stop the audio immediately. */ int alsa_stop( void ) { int err; DEBUGLOG("alsa_stop\n"); err = snd_pcm_drop(handle); if (err < 0) { ERRORLOG("Unable to drop pcm stream: %s\n", snd_strerror(err)); } err = snd_pcm_prepare(handle); if (err < 0) { ERRORLOG("Unable to snd_pcm_prepare pcm stream: %s\n", snd_strerror(err)); } return err; } static struct audio_oops alsa_oops = { .wmaudio_open = alsa_open, .wmaudio_close = alsa_close, .wmaudio_play = alsa_play, .wmaudio_stop = alsa_stop, .wmaudio_state = NULL, .wmaudio_balvol = NULL }; struct audio_oops* setup_alsa(const char *dev, const char *ctl) { static int init_complete = 0; DEBUGLOG("setup_alsa\n"); if(init_complete) { alsa_close(); init_complete = 0; } if(dev && strlen(dev) > 0) { device = strdup(dev); } else { device = strdup("plughw:0,0"); /* playback device */ } if(!alsa_open()) init_complete = 1; else return NULL; return &alsa_oops; } #endif /* HAVE_ALSA */ diff --git a/src/wmlib/cdrom.c b/src/wmlib/cdrom.c index 70d9e97..555a9c4 100644 --- a/src/wmlib/cdrom.c +++ b/src/wmlib/cdrom.c @@ -1,819 +1,822 @@ /* * This file is part of WorkMan, the civilized CD player library * Copyright (C) 1991-1997 by Steven Grimm (original author) * Copyright (C) by Dirk Försterling * Copyright (C) 2004-2006 Alexander Kern * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * Interface between most of WorkMan and the low-level CD-ROM library * routines defined in plat_*.c and drv_*.c. The goal is to have no * platform- or drive-dependent code here. */ +#define _BSD_SOURCE /* strdup */ +#define _DEFAULT_SOURCE /* stop glibc whining about the previous line */ + #include "include/wm_config.h" #include "include/wm_struct.h" #include "include/wm_cddb.h" #include "include/wm_cdrom.h" #include "include/wm_platform.h" #include "include/wm_helpers.h" #include "include/wm_cdtext.h" #include "include/wm_scsi.h" #include #include #include #include #include #include #include #ifdef CAN_CLOSE #include #endif /* local prototypes */ static int fixup_drive_struct(struct wm_drive *); static int read_toc(struct wm_drive *); static const char* gen_status(int); #define WM_MSG_CLASS WM_MSG_CLASS_CDROM /* extern struct wm_drive generic_proto, toshiba_proto, sony_proto; */ /* toshiba33_proto; <=== Somehow, this got lost */ /* * The supported drive types are listed here. NULL means match anything. * The first match in the list is used, and substring matches are done (so * put long names before their shorter prefixes.) */ static struct drivelist { const char *vendor; const char *model; const char *revision; int (*fixup)(struct wm_drive *d); } drives[] = { { "TOSHIBA", "XM-3501", NULL, toshiba_fixup }, { "TOSHIBA", "XM-3401", NULL, toshiba_fixup }, { "TOSHIBA", "XM-3301", NULL, toshiba_fixup }, { "SONY", "CDU-8012", NULL, sony_fixup }, { "SONY", "CDU 561", NULL, sony_fixup }, { "SONY", "CDU-76S", NULL, sony_fixup }, { NULL, NULL, NULL, NULL } }; /* * Solaris 2.2 will remove the device out from under us. Getting an ENOENT * is therefore sometimes not a problem. */ int intermittent_dev = 0; /* * Macro magic * */ #define CARRAY(id) ((id)-1) #define DATATRACK 1 /* * get the current position */ int wm_get_cur_pos_rel(void *p) { struct wm_drive *pdrive = (struct wm_drive *)p; return pdrive->thiscd.cur_pos_rel; } /* * get the current position */ int wm_get_cur_pos_abs(void *p) { struct wm_drive *pdrive = (struct wm_drive *)p; return pdrive->thiscd.cur_pos_abs; } /* * init the workmanlib */ int wm_cd_init(const char *cd_device, const char *soundsystem, const char *sounddevice, const char *ctldevice, void **ppdrive) { int err; struct wm_drive *pdrive; if(!ppdrive) return -1; pdrive = *ppdrive = (struct wm_drive *)malloc(sizeof(struct wm_drive)); if(!pdrive) return -1; memset(pdrive, 0, sizeof(*pdrive)); pdrive->cdda = (soundsystem && strcasecmp(soundsystem, "cdin")); pdrive->cd_device = cd_device ? strdup(cd_device) : strdup(DEFAULT_CD_DEVICE); pdrive->soundsystem = soundsystem ? strdup(soundsystem): NULL; pdrive->sounddevice = sounddevice ? strdup(sounddevice) : NULL; pdrive->ctldevice = ctldevice ? strdup(ctldevice) : NULL; if(!pdrive->cd_device) { err = -ENOMEM; goto init_failed; } pdrive->fd = -1; pdrive->proto.open = gen_open; pdrive->proto.close = gen_close; pdrive->proto.get_trackcount = gen_get_trackcount; pdrive->proto.get_cdlen = gen_get_cdlen; pdrive->proto.get_trackinfo = gen_get_trackinfo; pdrive->proto.get_drive_status = gen_get_drive_status; pdrive->proto.pause = gen_pause; pdrive->proto.resume = gen_resume; pdrive->proto.stop = gen_stop; pdrive->proto.play = gen_play; pdrive->proto.eject = gen_eject; pdrive->proto.closetray = gen_closetray; pdrive->proto.scsi = gen_scsi; pdrive->proto.set_volume = gen_set_volume; pdrive->proto.get_volume = gen_get_volume; pdrive->proto.scale_volume = gen_scale_volume; pdrive->proto.unscale_volume = gen_unscale_volume; pdrive->oldmode = WM_CDM_UNKNOWN; if((err = gen_init(pdrive)) < 0) goto init_failed; if ((err = pdrive->proto.open(pdrive)) < 0) goto open_failed; /* Can we figure out the drive type? */ if (wm_scsi_get_drive_type(pdrive)) { wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "plat_open(): inquiry failed\n"); } /* let it override some functions */ fixup_drive_struct(pdrive); #ifdef WMLIB_CDDA_BUILD if(pdrive->cdda && (err = wm_cdda_init(pdrive))) goto open_failed; #endif return wm_cd_status(pdrive); open_failed: wm_cd_destroy(pdrive); init_failed: free(pdrive->cd_device); free(pdrive->soundsystem); free(pdrive->sounddevice); free(pdrive->ctldevice); free(pdrive); return err; } int wm_cd_destroy(void *p) { struct wm_drive *pdrive = (struct wm_drive *)p; free_cdtext(); if(pdrive->cdda) wm_cdda_destroy(pdrive); pdrive->proto.close(pdrive); return 0; } /* * Give information about the drive we found during wmcd_open() */ const char *wm_drive_vendor(void *p) { struct wm_drive *pdrive = (struct wm_drive *)p; return pdrive->vendor?pdrive->vendor:""; } const char *wm_drive_model(void *p) { struct wm_drive *pdrive = (struct wm_drive *)p; return pdrive->model?pdrive->model:""; } const char *wm_drive_revision(void *p) { struct wm_drive *pdrive = (struct wm_drive *)p; return pdrive->revision?pdrive->revision:""; } const char *wm_drive_default_device() { return DEFAULT_CD_DEVICE; } unsigned long wm_cddb_discid(void *p) { struct wm_drive *pdrive = (struct wm_drive *)p; return cddb_discid(pdrive); } /* * Figure out which prototype drive structure we should be using based * on the vendor, model, and revision of the current pdrive-> */ static int fixup_drive_struct(struct wm_drive *d) { struct drivelist *driver; for (driver = drives; driver->vendor; driver++) { if((strncmp(driver->vendor, d->vendor, strlen(d->vendor))) || ((driver->model != NULL) && strncmp(driver->model, d->model, strlen(d->model))) || ((d->revision != NULL) && strncmp(driver->revision, d->revision, strlen(d->revision)))) continue; if(!(driver->fixup)) goto fail; driver->fixup(d); return 0; } fail: return -1; } /* find_drive_struct() */ /* * read_toc() * * Read the table of contents from the CD. Return a pointer to a wm_cdinfo * struct containing the relevant information (minus artist/cdname/etc.) * This is a static struct. Returns NULL if there was an error. * * XXX allocates one trackinfo too many. */ static int read_toc(struct wm_drive *pdrive) { int i; int pos; if(!pdrive->proto.get_trackcount || pdrive->proto.get_trackcount(pdrive, &pdrive->thiscd.ntracks) < 0) { return -1 ; } pdrive->thiscd.length = 0; pdrive->thiscd.cur_cdmode = WM_CDM_UNKNOWN; pdrive->thiscd.cd_cur_balance = WM_BALANCE_SYMMETRED; if (pdrive->thiscd.trk != NULL) free(pdrive->thiscd.trk); pdrive->thiscd.trk = malloc((pdrive->thiscd.ntracks + 1) * sizeof(struct wm_trackinfo)); if (pdrive->thiscd.trk == NULL) { perror("malloc"); return -1; } for (i = 0; i < pdrive->thiscd.ntracks; i++) { if(!pdrive->proto.get_trackinfo || pdrive->proto.get_trackinfo(pdrive, i + 1, &pdrive->thiscd.trk[i].data, &pdrive->thiscd.trk[i].start) < 0) { return -1; } pdrive->thiscd.trk[i].length = pdrive->thiscd.trk[i].start / 75; pdrive->thiscd.trk[i].track = i + 1; wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "track %i, start frame %i\n", pdrive->thiscd.trk[i].track, pdrive->thiscd.trk[i].start); } if(!pdrive->proto.get_cdlen || pdrive->proto.get_cdlen(pdrive, &pdrive->thiscd.trk[i].start) < 0) { return -1; } pdrive->thiscd.trk[i].length = pdrive->thiscd.trk[i].start / 75; /* Now compute actual track lengths. */ pos = pdrive->thiscd.trk[0].length; for (i = 0; i < pdrive->thiscd.ntracks; i++) { pdrive->thiscd.trk[i].length = pdrive->thiscd.trk[i+1].length - pos; pos = pdrive->thiscd.trk[i+1].length; if (pdrive->thiscd.trk[i].data) pdrive->thiscd.trk[i].length = (pdrive->thiscd.trk[i + 1].start - pdrive->thiscd.trk[i].start) * 2; } pdrive->thiscd.length = pdrive->thiscd.trk[pdrive->thiscd.ntracks].length; wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "read_toc() successful\n"); return 0; } /* read_toc() */ /* * wm_cd_status(pdrive) * * Return values: * see wm_cdrom.h * * Updates variables. */ int wm_cd_status(void *p) { struct wm_drive *pdrive = (struct wm_drive *)p; int mode = -1, tmp; if(!pdrive->proto.get_drive_status || (tmp = pdrive->proto.get_drive_status(pdrive, pdrive->oldmode, &mode, &pdrive->thiscd.cur_frame, &pdrive->thiscd.curtrack, &pdrive->thiscd.cur_index)) < 0) { perror("WM get_drive_status"); return -1; } wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "get_drive_status returns status %s, track %i, frame %i\n", gen_status(mode), pdrive->thiscd.curtrack, pdrive->thiscd.cur_frame); if(WM_CDS_NO_DISC(pdrive->oldmode) && WM_CDS_DISC_READY(mode)) { /* device changed */ pdrive->thiscd.ntracks = 0; if(read_toc(pdrive) || 0 == pdrive->thiscd.ntracks) { mode = WM_CDM_NO_DISC; } else /* refresh cdtext info */ get_glob_cdtext(pdrive, 1); wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "device status changed() from %s to %s\n", gen_status(pdrive->oldmode), gen_status(mode)); } pdrive->oldmode = mode; /* * it seems all driver have'nt state for stop */ if(WM_CDM_PAUSED == mode && 0 == pdrive->thiscd.cur_frame) { mode = WM_CDM_STOPPED; pdrive->thiscd.curtrack = 0; } switch (mode) { case WM_CDM_PLAYING: case WM_CDM_PAUSED: pdrive->thiscd.cur_pos_abs = pdrive->thiscd.cur_frame / 75; /* search for right track */ for(tmp = pdrive->thiscd.ntracks; tmp > 1 && pdrive->thiscd.cur_frame < pdrive->thiscd.trk[CARRAY(tmp)].start; tmp--) ; pdrive->thiscd.curtrack = tmp; /* Fall through */ case WM_CDM_UNKNOWN: if (mode == WM_CDM_UNKNOWN) { mode = WM_CDM_NO_DISC; } /* Fall through */ case WM_CDM_STOPPED: /*assert(thiscd.trk != NULL);*/ if(pdrive->thiscd.curtrack >= 1 && pdrive->thiscd.curtrack <= pdrive->thiscd.ntracks) { pdrive->thiscd.cur_pos_rel = (pdrive->thiscd.cur_frame - pdrive->thiscd.trk[CARRAY(pdrive->thiscd.curtrack)].start) / 75; if (pdrive->thiscd.cur_pos_rel < 0) pdrive->thiscd.cur_pos_rel = -pdrive->thiscd.cur_pos_rel; } if (pdrive->thiscd.cur_pos_abs < 0) pdrive->thiscd.cur_pos_abs = pdrive->thiscd.cur_frame = 0; if (pdrive->thiscd.curtrack < 1) pdrive->thiscd.curtracklen = pdrive->thiscd.length; else pdrive->thiscd.curtracklen = pdrive->thiscd.trk[CARRAY(pdrive->thiscd.curtrack)].length; /* Fall through */ case WM_CDM_TRACK_DONE: pdrive->thiscd.cur_cdmode = mode; break; case WM_CDM_FORWARD: case WM_CDM_EJECTED: pdrive->thiscd.cur_cdmode = mode; break; } wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wm_cd_status returns %s\n", gen_status(pdrive->thiscd.cur_cdmode)); return pdrive->thiscd.cur_cdmode; } int wm_cd_getcurtrack(void *p) { struct wm_drive *pdrive = (struct wm_drive *)p; if(WM_CDS_NO_DISC(pdrive->thiscd.cur_cdmode)) return 0; return pdrive->thiscd.curtrack; } int wm_cd_getcurtracklen(void *p) { struct wm_drive *pdrive = (struct wm_drive *)p; if(WM_CDS_NO_DISC(pdrive->thiscd.cur_cdmode)) return 0; return pdrive->thiscd.curtracklen; } int wm_cd_getcountoftracks(void *p) { struct wm_drive *pdrive = (struct wm_drive *)p; if(WM_CDS_NO_DISC(pdrive->thiscd.cur_cdmode)) return 0; return pdrive->thiscd.ntracks; } int wm_cd_gettracklen(void *p, int track) { struct wm_drive *pdrive = (struct wm_drive *)p; if (track < 1 || track > pdrive->thiscd.ntracks || pdrive->thiscd.trk == NULL) return 0; return pdrive->thiscd.trk[CARRAY(track)].length; } int wm_cd_gettrackstart(void *p, int track) { struct wm_drive *pdrive = (struct wm_drive *)p; if (track < 1 || track > (pdrive->thiscd.ntracks+1) || pdrive->thiscd.trk == NULL) return 0; return pdrive->thiscd.trk[CARRAY(track)].start; } int wm_cd_gettrackdata(void *p, int track) { struct wm_drive *pdrive = (struct wm_drive *)p; if (track < 1 || track > pdrive->thiscd.ntracks || pdrive->thiscd.trk == NULL) return 0; return pdrive->thiscd.trk[CARRAY(track)].data; } /* * wm_cd_play(starttrack, pos, endtrack) * * Start playing the CD or jump to a new position. "pos" is in seconds, * relative to start of track. */ int wm_cd_play(void *p, int start, int pos, int end) { struct wm_drive *pdrive = (struct wm_drive *)p; int real_start, real_end, status; int play_start, play_end; status = wm_cd_status(pdrive); if(WM_CDS_NO_DISC(status) || pdrive->thiscd.ntracks < 1) return -1; /* * check ranges */ for(real_end = pdrive->thiscd.ntracks; (pdrive->thiscd.trk[CARRAY(real_end)].data == DATATRACK); real_end--) ; for(real_start = 1; (pdrive->thiscd.trk[CARRAY(real_start)].data == DATATRACK); real_start++) ; if(end == WM_ENDTRACK) end = real_end; else if(end > real_end) end = real_end; /* * handle as overrun */ if(start < real_start) start = real_start; if(start > real_end) start = real_end; /* * Try to avoid mixed mode and CD-EXTRA data tracks */ if(start > end || pdrive->thiscd.trk[CARRAY(start)].data == DATATRACK) { wm_cd_stop(pdrive); return -1; } play_start = pdrive->thiscd.trk[CARRAY(start)].start + pos * 75; play_end = (end == pdrive->thiscd.ntracks) ? pdrive->thiscd.length * 75 : pdrive->thiscd.trk[CARRAY(end)].start - 1; --play_end; if (play_start >= play_end) play_start = play_end-1; if(pdrive->proto.play) pdrive->proto.play(pdrive, play_start, play_end); else return -1; /* So we don't update the display with the old frame number */ wm_cd_status(pdrive); return pdrive->thiscd.curtrack; } /* * wm_cd_pause() * * Pause the CD, if it's in play mode. If it's already paused, go back to * play mode. */ int wm_cd_pause(void *p) { struct wm_drive *pdrive = (struct wm_drive *)p; static int paused_pos; int status; status = wm_cd_status(pdrive); if(WM_CDS_NO_DISC(status)) return -1; if(WM_CDM_PLAYING == pdrive->thiscd.cur_cdmode) { paused_pos = pdrive->thiscd.cur_pos_rel; if(pdrive->proto.pause) return pdrive->proto.pause(pdrive); } else if(WM_CDM_PAUSED == status) { if(pdrive->proto.resume) return pdrive->proto.resume(pdrive); else if(pdrive->proto.play) return pdrive->proto.play(pdrive, pdrive->thiscd.cur_pos_rel, -1); } return -1; } /* wm_cd_pause() */ /* * wm_cd_stop() * * Stop the CD if it's not already stopped. */ int wm_cd_stop(void *p) { struct wm_drive *pdrive = (struct wm_drive *)p; int status; status = wm_cd_status(pdrive); if(WM_CDS_NO_DISC(status)) return -1; if (status != WM_CDM_STOPPED) { if(pdrive->proto.stop) pdrive->proto.stop(pdrive); status = wm_cd_status(pdrive); } return (status != WM_CDM_STOPPED); } /* wm_cd_stop() */ /* * Eject the current CD, if there is one, and set the mode to 5. * * Returns 0 on success, 1 if the CD couldn't be ejected, or 2 if the * CD contains a mounted filesystem. */ int wm_cd_eject(void *p) { struct wm_drive *pdrive = (struct wm_drive *)p; int err = -1; if(pdrive->proto.eject) err = pdrive->proto.eject(pdrive); if (err < 0) { if (err == -3) { return 2; } else { return 1; } } return (WM_CDM_EJECTED == wm_cd_status(pdrive)) ? 0 : -1; } int wm_cd_closetray(void *p) { struct wm_drive *pdrive = (struct wm_drive *)p; int status, err = -1; status = wm_cd_status(pdrive); if (status == WM_CDM_UNKNOWN || status == WM_CDM_NO_DISC) return -1; #ifdef CAN_CLOSE err = pdrive->proto.closetray(pdrive); if(err) { /* do close/open */ if(!pdrive->proto.close(pdrive)) { wm_susleep( 1000 ); err = pdrive->proto.open(pdrive); wm_susleep( 1000 ); } } #else err = 0; #endif return (err ? 0 : ((wm_cd_status(pdrive) == 2) ? 1 : 0)); } /* wm_cd_closetray() */ struct cdtext_info* wm_cd_get_cdtext(void *p) { struct wm_drive *pdrive = (struct wm_drive *)p; int status; status = wm_cd_status(pdrive); if(WM_CDS_NO_DISC(status)) return NULL; return get_glob_cdtext(pdrive, 0); } int wm_cd_set_verbosity(int level) { wm_lib_set_verbosity(level); return wm_lib_get_verbosity(); } /* * volume is valid WM_VOLUME_MUTE <= vol <= WM_VOLUME_MAXIMAL, * balance is valid WM_BALANCE_ALL_LEFTS <= balance <= WM_BALANCE_ALL_RIGHTS */ int wm_cd_volume(void *p, int vol, int bal) { struct wm_drive *pdrive = (struct wm_drive *)p; int left, right; const int bal1 = (vol - WM_VOLUME_MUTE)/(WM_BALANCE_ALL_RIGHTS - WM_BALANCE_SYMMETRED); /* * Set "left" and "right" to volume-slider values accounting for the * balance setting. * */ if(vol < WM_VOLUME_MUTE) vol = WM_VOLUME_MUTE; if(vol > WM_VOLUME_MAXIMAL) vol = WM_VOLUME_MAXIMAL; if(bal < WM_BALANCE_ALL_LEFTS) bal = WM_BALANCE_ALL_LEFTS; if(bal > WM_BALANCE_ALL_RIGHTS) bal = WM_BALANCE_ALL_RIGHTS; left = vol - (bal * bal1); right = vol + (bal * bal1); wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calculate volume left %i, right %i\n", left, right); if (left > WM_VOLUME_MAXIMAL) left = WM_VOLUME_MAXIMAL; if (right > WM_VOLUME_MAXIMAL) right = WM_VOLUME_MAXIMAL; if(pdrive->proto.scale_volume) pdrive->proto.scale_volume(&left, &right); if(pdrive->proto.set_volume) return pdrive->proto.set_volume(pdrive, left, right); return -1; } /* cd_volume() */ int wm_cd_getvolume(void *p) { struct wm_drive *pdrive = (struct wm_drive *)p; int left, right; if(!pdrive->proto.get_volume || pdrive->proto.get_volume(pdrive, &left, &right) < 0 || left == -1) return -1; if(pdrive->proto.unscale_volume) pdrive->proto.unscale_volume(&left, &right); if (left < right) { pdrive->thiscd.cd_cur_balance = (right - left) / 2; if (pdrive->thiscd.cd_cur_balance > WM_BALANCE_ALL_RIGHTS) pdrive->thiscd.cd_cur_balance = WM_BALANCE_ALL_RIGHTS; return right; } else if (left == right) { pdrive->thiscd.cd_cur_balance = WM_BALANCE_SYMMETRED; return left; } else { pdrive->thiscd.cd_cur_balance = (right - left) / 2; if (pdrive->thiscd.cd_cur_balance < WM_BALANCE_ALL_LEFTS) pdrive->thiscd.cd_cur_balance = WM_BALANCE_ALL_LEFTS; return left; } } int wm_cd_getbalance(void *p) { struct wm_drive *pdrive = (struct wm_drive *)p; int left, right; if(!pdrive->proto.get_volume || pdrive->proto.get_volume(pdrive, &left, &right) < 0 || left == -1) return WM_BALANCE_SYMMETRED; if(pdrive->proto.unscale_volume) pdrive->proto.unscale_volume(&left, &right); if (left < right) { pdrive->thiscd.cd_cur_balance = (right - left) / 2; if (pdrive->thiscd.cd_cur_balance > WM_BALANCE_ALL_RIGHTS) pdrive->thiscd.cd_cur_balance = WM_BALANCE_ALL_RIGHTS; } else if (left == right) { pdrive->thiscd.cd_cur_balance = WM_BALANCE_SYMMETRED; } else { pdrive->thiscd.cd_cur_balance = (right - left) / 2; if (pdrive->thiscd.cd_cur_balance < WM_BALANCE_ALL_LEFTS) pdrive->thiscd.cd_cur_balance = WM_BALANCE_ALL_LEFTS; } return pdrive->thiscd.cd_cur_balance; } static const char *gen_status(int status) { static char tmp[250]; switch(status) { case WM_CDM_TRACK_DONE: return "WM_CDM_TRACK_DONE"; case WM_CDM_PLAYING: return "WM_CDM_PLAYING"; case WM_CDM_FORWARD: return "WM_CDM_FORWARD"; case WM_CDM_PAUSED: return "WM_CDM_PAUSED"; case WM_CDM_STOPPED: return "WM_CDM_STOPPED"; case WM_CDM_EJECTED: return "WM_CDM_EJECTED"; case WM_CDM_DEVICECHANGED: return "WM_CDM_DEVICECHANGED"; case WM_CDM_NO_DISC: return "WM_CDM_NO_DISC"; case WM_CDM_UNKNOWN: return "WM_CDM_UNKNOWN"; case WM_CDM_CDDAERROR: return "WM_CDM_CDDAERROR"; case WM_CDM_CDDAACK: return "WM_CDM_CDDAACK"; case WM_CDM_LOADING: return "WM_CDM_LOADING"; case WM_CDM_BUFFERING: return "WM_CDM_BUFFERING"; default: sprintf(tmp, "unexpected status %i", status); return tmp; } } diff --git a/src/wmlib/wm_helpers.c b/src/wmlib/wm_helpers.c index 5179920..889048f 100644 --- a/src/wmlib/wm_helpers.c +++ b/src/wmlib/wm_helpers.c @@ -1,234 +1,237 @@ /* * This file is part of WorkMan, the civilized CD player library * Copyright (C) 1991-1997 by Steven Grimm * Copyright (C) by Dirk Försterling * Copyright (C) 2004-2006 Alexander Kern * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * Some helpful functions... * */ +#define _BSD_SOURCE /* strdup, timerclear */ +#define _DEFAULT_SOURCE /* stop glibc whining about the previous line */ + #include #include #include #include #include #include #include "include/workman_defs.h" #include "include/wm_config.h" #include "include/wm_helpers.h" #include "include/wm_struct.h" #define WM_MSG_CLASS WM_MSG_CLASS_MISC int wm_lib_verbosity = WM_MSG_LEVEL_ERROR | WM_MSG_CLASS_ALL; /* * Some seleced functions of version reporting follow... */ int wm_libver_major( void ){return WM_LIBVER_MAJOR;} int wm_libver_minor( void ){return WM_LIBVER_MINOR;} int wm_libver_pl( void ){return WM_LIBVER_PL;} char *wm_libver_name( void ) { char *s = NULL; wm_strmcat(&s, WM_LIBVER_NAME); return s; } /* wm_libver_name() */ char *wm_libver_number( void ) { char *s = NULL; s = malloc(10); /* this is not used very often, so don't care about speed...*/ sprintf(s, "%d.%d.%d", wm_libver_major(), wm_libver_minor(), wm_libver_pl()); return s; } /* wm_libver_number() */ char *wm_libver_date( void ) { char *s = NULL; wm_strmcat(&s, __DATE__); return s; } /* wm_libver_date() */ char *wm_libver_string( void ) { char *s = NULL; wm_strmcat( &s, wm_libver_name() ); wm_strmcat( &s, " " ); wm_strmcat( &s, wm_libver_number() ); return s; } /* wm_libver_string() */ /* * * Now for some memory management... * */ /* Free some memory and set a pointer to null. */ void freeup( char **x ) { if (*x != NULL) { free(*x); *x = NULL; } } /* freeup() */ /* Copy into a malloced string. */ void wm_strmcpy( char **t, const char *s ) { wm_lib_message(WM_MSG_CLASS_MISC | WM_MSG_LEVEL_DEBUG, "wm_strmcpy(%s, '%s')\n", *t, s); if (*t != NULL) { wm_lib_message(WM_MSG_CLASS_MISC | WM_MSG_LEVEL_DEBUG, "wm_strmcpy freeing pointer %p\n", *t); free(*t); } *t = malloc(strlen(s) + 1); if (*t == NULL) { perror("wm_strmcpy"); exit(1); } wm_lib_message(WM_MSG_CLASS_MISC | WM_MSG_LEVEL_DEBUG, "wm_strmcpy finally copying (%p, '%s')\n", *t, s); strncpy(*t, s, strlen(s)); } /* wm_strmcpy() */ /* Add to a malloced string. */ void wm_strmcat( char **t, const char *s) { int len = strlen(s) + 1; wm_lib_message(WM_MSG_CLASS_MISC | WM_MSG_LEVEL_DEBUG, "wm_strmcat(%s, %s)\n", *t, s); if (*s == '\0') return; if (*t != NULL) { len += strlen(*t); *t = realloc(*t, len); if (*t == NULL) { perror("wm_strmcat"); exit(1); } strcat(*t, s); } else wm_strmcpy(t, s); } /* wm_strmcat() */ /* Duplicate a string. Some systems have this in libc, but not all. */ char * wm_strdup( char *s ) { char *new; new = malloc(strlen(s) + 1); if (new) strcpy(new, s); return (new); } /* wm_strdup() */ /* * set and get verbosity level. */ void wm_lib_set_verbosity( int level ) { int l = level & WM_MSG_LEVEL_ALL; int c = level & WM_MSG_CLASS_ALL; if( WM_MSG_LEVEL_NONE <= l && l <= WM_MSG_LEVEL_DEBUG ) { wm_lib_verbosity = l | c; wm_lib_message(WM_MSG_CLASS_MISC | WM_MSG_LEVEL_DEBUG, "Verbosity set to 0x%x|0x%x\n", l, c); } } /* wm_lib_set_verbosity */ int wm_lib_get_verbosity( void ) { return wm_lib_verbosity; } /* * wm_lib_message(). * * any message that falls into allowed classes and has at least * verbosity level wm_lib_verbosity & 0xf will be printed. * * Usage: * * wm_lib_message( WM_MSG_LEVEL | WM_MSG_CLASS, "format", contents); * * To simplify the usage, you may simply use WM_MSG_CLASS. It should be * defined in each module to reflect the correct message class. * */ void wm_lib_message( unsigned int level, const char *fmt, ... ) { va_list ap; unsigned int l, c, vl, vc; /* verbosity level */ vl = wm_lib_verbosity & WM_MSG_LEVEL_ALL; /* allowed classes */ vc = wm_lib_verbosity & WM_MSG_CLASS_ALL; l = level & WM_MSG_LEVEL_ALL; c = level & WM_MSG_CLASS_ALL; /* * print it only if level and class are allowed. */ if( (l <= vl) && (vc & c) ) { fprintf(stderr, "libWorkMan: "); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } } /* wm_lib_message() */ /* * Simulate usleep() using select(). */ int wm_susleep( int usec ) { struct timeval tv; timerclear(&tv); tv.tv_sec = usec / 1000000; tv.tv_usec = usec % 1000000; return (select(0, NULL, NULL, NULL, &tv)); } /* wm_susleep() */