/*************************************************************************** cdda.c - description ------------------- begin : Mon Jan 27 2003 copyright : (C) 2003 by Alex Kern email : alex.kern@gmx.de ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ /*************************************************************************** * * * This is a common cddamaster piece of code * * * ***************************************************************************/ #include #include #include #include #include #include "include/wm_config.h" #include "include/wm_struct.h" #include "include/wm_cdda.h" #include "include/wm_cdrom.h" #include "audio/audio.h" #include #if defined(BUILD_CDDA) static pthread_t thread_read; static pthread_t thread_play; int get_next_block(int x); void *cdda_fct_read(void* arg); void *cdda_fct_play(void* arg); #define NUMBLOCKS 2 #define NUMFRAMES 10 static struct cdda_block blks[NUMBLOCKS]; static pthread_mutex_t blks_mutex[NUMBLOCKS]; static struct cdda_device dev; static pthread_cond_t wakeup_audio; /* * Loudness setting, plus the floating volume multiplier and decaying-average * volume level. */ static unsigned int loudness = 0, volume = 32768, level; /* * This is non-null if we're saving audio to a file. */ static FILE *output = NULL; /* * These are driverdependent oops * */ static struct audio_oops *oops = NULL; /* * Audio file header format. */ typedef unsigned long u_32; struct auheader { u_32 magic; u_32 hdr_size; u_32 data_size; u_32 encoding; u_32 sample_rate; u_32 channels; }; /* had to change #ifdef to #if -> see wm_cdda.h */ #ifdef __FreeBSD__ /* Phungus not with htonl on FreeBSD */ #include #else #if WM_BIG_ENDIAN # ifndef htonl # define htonl(x) (x) # endif #else extern unsigned long htonl(unsigned long); #endif #endif /* * Try to initialize the CDDA slave. Returns 0 on success. */ int gen_cdda_init( struct wm_drive *d ) { int ret = 0; if (d->cdda_slave > -1) return 0; memset(&blks, 0, sizeof(blks)); dev.fd = -1; dev.frames_at_once = NUMFRAMES; dev.blocks = blks; dev.numblocks = NUMBLOCKS; dev.status = WM_CDM_UNKNOWN; dev.devname = d->cd_device; if ((ret = wmcdda_init(&dev))) return ret; oops = setup_soundsystem(d->soundsystem, d->sounddevice, d->ctldevice); if (!oops) { ERRORLOG("cdda: setup_soundsystem failed\n"); wmcdda_close(&dev); return -1; } if(pthread_create(&thread_read, NULL, cdda_fct_read, &dev)) { ERRORLOG("error by create pthread"); oops->wmaudio_close(); wmcdda_close(&dev); return -1; } if(pthread_create(&thread_play, NULL, cdda_fct_play, &dev)) { ERRORLOG("error by create pthread"); oops->wmaudio_close(); wmcdda_close(&dev); return -1; } d->cdda_slave = 0; return 0; } int cdda_get_drive_status( struct wm_drive *d, int oldmode, int *mode, int *frame, int *track, int *ind ) { if (d->cdda_slave > -1) { if(dev.status) *mode = dev.status; else *mode = oldmode; if (*mode == WM_CDM_PLAYING) { *track = dev.track; *ind = dev.index; *frame = dev.frame; } else if (*mode == WM_CDM_CDDAERROR) { /* * An error near the end of the CD probably * just means we hit the end. */ *mode = WM_CDM_TRACK_DONE; } return 0; } return -1; } int cdda_play( struct wm_drive *d, int start, int end, int realstart ) { if (d->cdda_slave > -1) { dev.command = WM_CDM_STOPPED; wmcdda_setup(start, end, realstart); level = 2500; volume = 1 << 15; dev.track = -1; dev.index = 0; dev.frame = start; dev.command = WM_CDM_PLAYING; return 0; } return -1; } int cdda_pause( struct wm_drive *d ) { if (d->cdda_slave > -1) { if(WM_CDM_PLAYING == dev.command) { dev.command = WM_CDM_PAUSED; } else { dev.command = WM_CDM_PLAYING; } return 0; } return -1; } int cdda_stop( struct wm_drive *d ) { if (d->cdda_slave > -1) { dev.command = WM_CDM_STOPPED; oops->wmaudio_stop(); return 0; } return -1; } int cdda_eject( struct wm_drive *d ) { if (d->cdda_slave > -1) { dev.command = WM_CDM_EJECTED; oops->wmaudio_stop(); /*wmcdda_close(&dev);*/ return 0; } return -1; } int cdda_set_volume( struct wm_drive *d, int left, int right ) { if (d->cdda_slave > -1) { int bal, vol; bal = (right - left) + 100; bal *= 255; bal /= 200; if (right > left) vol = right; else vol = left; vol *= 255; vol /= 100; if(oops->wmaudio_balance) oops->wmaudio_balance(bal); if(oops->wmaudio_volume) oops->wmaudio_volume(vol); return 0; } return -1; } /* * Read the initial volume from the drive, if available. Each channel * ranges from 0 to 100, with -1 indicating data not available. */ int cdda_get_volume( struct wm_drive *d, int *left, int *right ) { if (d->cdda_slave > -1) { if(!oops->wmaudio_state) { dev.volume = -1; dev.balance = 128; } *left = *right = (dev.volume * 100 + 254) / 255; if (dev.balance < 110) *right = (((dev.volume * dev.balance + 127) / 128) * 100 + 254) / 255; else if (dev.balance > 146) *left = (((dev.volume * (255 - dev.balance) + 127) / 128) * 100 + 254) / 255; return 0; } return -1; } /* * Turn off the CDDA slave. */ void cdda_kill( struct wm_drive *d ) { if (d->cdda_slave > -1) { dev.command = WM_CDM_STOPPED; oops->wmaudio_stop(); sleep(1); wmcdda_close(&dev); oops->wmaudio_close(); dev.blocks = NULL; wait(NULL); d->cdda_slave = -1; } } /* * Tell the CDDA slave to set the loudness level. */ void cdda_set_loudness( struct wm_drive *d, int loud ) { if (d->cdda_slave > -1) { loudness = loud; } } /* * Tell the CDDA slave to start (or stop) saving to a file. */ void cdda_save( struct wm_drive *d, char *filename ) { #if 0 int len; if (filename == NULL || filename[0] == '\0') len = 0; else len = strlen(filename); write(d->cdda_slave, "F", 1); write(d->cdda_slave, &len, sizeof(len)); if (len) write(d->cdda_slave, filename, len); read(0, &namelen, sizeof(namelen)); if (output != NULL) { fclose(output); output = NULL; } if (namelen) { filename = malloc(namelen + 1); if (filename == NULL) { perror("cddas"); wmcdda_close(dev); oops->wmaudio_close(); exit(1); } read(0, filename, namelen); filename[namelen] = '\0'; output = fopen(filename, "w"); if (output == NULL) { perror(filename); } else { /* Write an .au file header. */ hdr.magic = htonl(0x2e736e64); hdr.hdr_size = htonl(sizeof(hdr) + 28); hdr.data_size = htonl(~0); hdr.encoding = htonl(3); /* linear-16 */ hdr.sample_rate = htonl(44100); hdr.channels = htonl(2); fwrite(&hdr, sizeof(hdr), 1, output); fwrite("Recorded from CD by WorkMan", 28, 1, output); } free(filename); #endif } int get_next_block(int x) { int y = ++x; return (y < NUMBLOCKS)?y:0; } void *cdda_fct_read(void* arg) { struct cdda_device *cddadev = (struct cdda_device*)arg; int i, j, wakeup; long result; while (cddadev->blocks) { while(cddadev->command != WM_CDM_PLAYING) { cddadev->status = cddadev->command; sleep(1); } i = 0; pthread_mutex_lock(&blks_mutex[i]); wakeup = 1; while(cddadev->command == WM_CDM_PLAYING) { result = wmcdda_read(cddadev, &blks[i]); if (result <= 0 && blks[i].status != WM_CDM_TRACK_DONE) { ERRORLOG("cdda: wmcdda_read failed, stop playing\n"); cddadev->command = WM_CDM_STOPPED; break; } else { if (output) fwrite(blks[i].buf, blks[i].buflen, 1, output); } j = get_next_block(i); pthread_mutex_lock(&blks_mutex[j]); if(wakeup) { wakeup = 0; pthread_cond_signal(&wakeup_audio); } pthread_mutex_unlock(&blks_mutex[i]); /* audio can start here */ i = j; } pthread_mutex_unlock(&blks_mutex[i]); } return 0; } void *cdda_fct_play(void* arg) { struct cdda_device *cddadev = (struct cdda_device*)arg; int i = 0; while (cddadev->blocks) { if(cddadev->command != WM_CDM_PLAYING) { i = 0; pthread_mutex_lock(&blks_mutex[i]); pthread_cond_wait(&wakeup_audio, &blks_mutex[i]); } else { i = get_next_block(i); pthread_mutex_lock(&blks_mutex[i]); } if (oops->wmaudio_play(&blks[i])) { oops->wmaudio_stop(); ERRORLOG("cdda: wmaudio_play failed\n"); cddadev->command = WM_CDM_STOPPED; } cddadev->frame = blks[i].frame; cddadev->track = blks[i].track; cddadev->index = blks[i].index; cddadev->status = blks[i].status; pthread_mutex_unlock(&blks_mutex[i]); } return 0; } #endif