You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdemultimedia/kscd/libwm/cdda.c

459 lines
10 KiB

/***************************************************************************
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