summaryrefslogtreecommitdiffstats
path: root/kscd/libwm/cdda.c
diff options
context:
space:
mode:
Diffstat (limited to 'kscd/libwm/cdda.c')
-rw-r--r--kscd/libwm/cdda.c458
1 files changed, 458 insertions, 0 deletions
diff --git a/kscd/libwm/cdda.c b/kscd/libwm/cdda.c
new file mode 100644
index 00000000..ca8d76ba
--- /dev/null
+++ b/kscd/libwm/cdda.c
@@ -0,0 +1,458 @@
+/***************************************************************************
+ 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 <string.h>
+#include <sys/poll.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <unistd.h>
+#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 <pthread.h>
+
+#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 <sys/param.h>
+#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