File indexing completed on 2024-05-05 09:08:27

0001 /*
0002  * This file is part of WorkMan, the civilized CD player library
0003  * Copyright (C) Alexander Kern <alex.kern@gmx.de>
0004  *
0005  * This library is free software; you can redistribute it and/or
0006  * modify it under the terms of the GNU Library General Public
0007  * License as published by the Free Software Foundation; either
0008  * version 2 of the License, or (at your option) any later version.
0009  *
0010  * This library is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013  * Library General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU Library General Public
0016  * License along with this library; if not, write to the Free
0017  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
0018  */
0019 
0020 #include <string.h>
0021 #include <sys/poll.h>
0022 #include <sys/wait.h>
0023 #include <arpa/inet.h> /* For htonl(3) */
0024 #include <stdio.h>
0025 #include <unistd.h>
0026 #include "include/wm_config.h"
0027 #include "include/wm_struct.h"
0028 #include "include/wm_cdda.h"
0029 #include "include/wm_cdrom.h"
0030 #include "include/wm_helpers.h"
0031 #include "include/wm_scsi.h"
0032 #include "audio/audio.h"
0033 
0034 #include <pthread.h>
0035 
0036 static pthread_t thread_read;
0037 static pthread_t thread_play;
0038 
0039 /* CDDABLKSIZE give us the 588 samples 4 bytes each(16 bit x 2 channel)
0040    by rate 44100 HZ, 588 samples are 1/75 sec
0041    if we read 15 frames(8820 samples), we get in each block, data for 1/5 sec */
0042 #define COUNT_CDDA_FRAMES_PER_BLOCK 15
0043 
0044 /* Only Linux and Sun define the number of blocks explicitly; assume all
0045    other systems are like Linux and have 10 blocks.
0046 */
0047 #ifndef COUNT_CDDA_BLOCKS
0048 #define COUNT_CDDA_BLOCKS 10
0049 #endif
0050 
0051 static struct wm_cdda_block blks[COUNT_CDDA_BLOCKS];
0052 static pthread_mutex_t blks_mutex[COUNT_CDDA_BLOCKS];
0053 static pthread_cond_t wakeup_audio;
0054 
0055 /*
0056  * This is non-null if we're saving audio to a file.
0057  */
0058 static FILE *output = NULL;
0059 
0060 /*
0061  * These are driverdependent oops
0062  *
0063  */
0064 static struct audio_oops *oops = NULL;
0065 
0066 /*
0067  * Audio file header format.
0068  */
0069 typedef unsigned long u_32;
0070 struct auheader {
0071     u_32 magic;
0072     u_32 hdr_size;
0073     u_32 data_size;
0074     u_32 encoding;
0075     u_32 sample_rate;
0076     u_32 channels;
0077 };
0078 
0079 static int cdda_status(struct wm_drive *d, int oldmode,
0080   int *mode, int *frame, int *track, int *ind)
0081 {
0082     if (d->cddax) {
0083         if(d->status)
0084           *mode = d->status;
0085         else
0086           *mode = oldmode;
0087 
0088         if (*mode == WM_CDM_PLAYING) {
0089             *track = d->track;
0090             *ind = d->index;
0091             *frame = d->frame;
0092         } else if (*mode == WM_CDM_CDDAERROR) {
0093             /*
0094              * An error near the end of the CD probably
0095              * just means we hit the end.
0096              */
0097             *mode = WM_CDM_TRACK_DONE;
0098         }
0099 
0100         return 0;
0101     }
0102 
0103     return -1;
0104 }
0105 
0106 static int cdda_play(struct wm_drive *d, int start, int end)
0107 {
0108     if (d->cddax) {
0109         d->command = WM_CDM_STOPPED;
0110         oops->wmaudio_stop();
0111 
0112         /* wait before reader, stops */
0113         while(d->status != d->command)
0114             wm_susleep(1000);
0115 
0116         d->current_position = start;
0117         d->ending_position = end;
0118 
0119         d->track =  -1;
0120         d->index =  0;
0121         d->frame = start;
0122         d->status = d->command = WM_CDM_PLAYING;
0123 
0124         return 0;
0125     }
0126 
0127     return -1;
0128 }
0129 
0130 static int cdda_pause(struct wm_drive *d)
0131 {
0132     if (d->cddax) {
0133         if(WM_CDM_PLAYING == d->command) {
0134             d->command = WM_CDM_PAUSED;
0135             if(oops->wmaudio_pause)
0136                 oops->wmaudio_pause();
0137         } else {
0138             d->command = WM_CDM_PLAYING;
0139         }
0140 
0141         return 0;
0142     }
0143 
0144     return -1;
0145 }
0146 
0147 static int cdda_stop(struct wm_drive *d)
0148 {
0149     if (d->cddax) {
0150         d->command = WM_CDM_STOPPED;
0151         oops->wmaudio_stop();
0152         return 0;
0153     }
0154 
0155     return -1;
0156 }
0157 
0158 static int cdda_set_volume(struct wm_drive *d, int left, int right)
0159 {
0160     if (d->cddax) {
0161          if(oops->wmaudio_balvol && !oops->wmaudio_balvol(1, &left, &right))
0162             return 0;
0163     }
0164 
0165     return -1;
0166 }
0167 
0168 static int cdda_get_volume(struct wm_drive *d, int *left, int *right)
0169 {
0170     if (d->cddax) {
0171         if(oops->wmaudio_balvol && !oops->wmaudio_balvol(0, left, right))
0172             return 0;
0173     }
0174 
0175     return -1;
0176 }
0177 
0178   #if 0
0179 /*
0180  * Tell the CDDA slave to start (or stop) saving to a file.
0181  */
0182 void
0183 cdda_save(struct wm_drive *, char *)
0184 {
0185 
0186   int len;
0187 
0188   if (filename == NULL || filename[0] == '\0')
0189     len = 0;
0190   else
0191     len = strlen(filename);
0192   write(d->cdda_slave, "F", 1);
0193   write(d->cdda_slave, &len, sizeof(len));
0194   if (len)
0195     write(d->cdda_slave, filename, len);
0196 
0197 
0198             read(0, &namelen, sizeof(namelen));
0199         if (output != NULL) {
0200             fclose(output);
0201             output = NULL;
0202         }
0203         if (namelen) {
0204             filename = malloc(namelen + 1);
0205             if (filename == NULL) {
0206                 perror("cddas");
0207                 wmcdda_close(cdda_device);
0208                 oops->wmaudio_close();
0209                 exit(1);
0210             }
0211 
0212             read(0, filename, namelen);
0213             filename[namelen] = '\0';
0214             output = fopen(filename, "w");
0215             if (output == NULL) {
0216                 perror(filename);
0217             } else {
0218                 /* Write an .au file header. */
0219                 hdr.magic = htonl(0x2e736e64);
0220                 hdr.hdr_size = htonl(sizeof(hdr) + 28);
0221                 hdr.data_size = htonl(~0);
0222                 hdr.encoding = htonl(3); /* linear-16 */
0223                 hdr.sample_rate = htonl(44100);
0224                 hdr.channels = htonl(2);
0225 
0226                 fwrite(&hdr, sizeof(hdr), 1, output);
0227                 fwrite("Recorded from CD by WorkMan", 28, 1, output);
0228             }
0229             free(filename);
0230 
0231 }
0232 #endif
0233 
0234 static int get_next_block(int x)
0235 {
0236     int y = ++x;
0237     return (y < COUNT_CDDA_BLOCKS)?y:0;
0238 }
0239 
0240 static void *cdda_fct_read(void* arg)
0241 {
0242     struct wm_drive *d = (struct wm_drive *)arg;
0243     int i, j, wakeup;
0244     long result;
0245 
0246     while (d->blocks) {
0247         while(d->command != WM_CDM_PLAYING) {
0248             d->status = d->command;
0249             wm_susleep(1000);
0250         }
0251 
0252         i = 0;
0253         (void) pthread_mutex_lock(&blks_mutex[i]);
0254         wakeup = 1;
0255 
0256         while(d->command == WM_CDM_PLAYING) {
0257             result = gen_cdda_read(d, &blks[i]);
0258             if (result <= 0 && blks[i].status != WM_CDM_TRACK_DONE) {
0259                 ERRORLOG("cdda: wmcdda_read failed, stop playing\n");
0260                 d->command = WM_CDM_STOPPED;
0261                 break;
0262             } else {
0263                 if (output)
0264                     fwrite(blks[i].buf, blks[i].buflen, 1, output);
0265             }
0266 
0267             j = get_next_block(i);
0268             (void) pthread_mutex_lock(&blks_mutex[j]);
0269 
0270             if(wakeup) {
0271                 wakeup = 0;
0272                 pthread_cond_signal(&wakeup_audio);
0273             }
0274 
0275             (void) pthread_mutex_unlock(&blks_mutex[i]);
0276             /* audio can start here */
0277 
0278             i = j;
0279         }
0280 
0281         (void) pthread_mutex_unlock(&blks_mutex[i]);
0282     }
0283 
0284     return 0;
0285 }
0286 
0287 static void *cdda_fct_play(void* arg)
0288 {
0289     struct wm_drive *d = (struct wm_drive *)arg;
0290     int i = 0;
0291 
0292     while (d->blocks) {
0293         if(d->command != WM_CDM_PLAYING) {
0294             i = 0;
0295             (void) pthread_mutex_lock(&blks_mutex[i]);
0296             pthread_cond_wait(&wakeup_audio, &blks_mutex[i]);
0297         } else {
0298             i = get_next_block(i);
0299             (void) pthread_mutex_lock(&blks_mutex[i]);
0300         }
0301 
0302         if (oops->wmaudio_play(&blks[i])) {
0303             oops->wmaudio_stop();
0304             ERRORLOG("cdda: wmaudio_play failed\n");
0305             d->command = WM_CDM_STOPPED;
0306         }
0307         if (oops->wmaudio_state)
0308             oops->wmaudio_state(&blks[i]);
0309 
0310         d->frame = blks[i].frame;
0311         d->track = blks[i].track;
0312         d->index = blks[i].index;
0313         if ((d->status = blks[i].status) == WM_CDM_TRACK_DONE)
0314             d->command = WM_CDM_STOPPED;
0315 
0316         (void) pthread_mutex_unlock(&blks_mutex[i]);
0317     }
0318 
0319     return 0;
0320 }
0321 
0322 /*
0323  * Try to initialize the CDDA slave.  Returns 0 on success.
0324  */
0325 int wm_cdda_init(struct wm_drive *d)
0326 {
0327     int ret = 0;
0328 
0329     if (d->cddax) {
0330         wm_cdda_destroy(d);
0331 
0332         wm_susleep(1000);
0333         d->blocks = 0;
0334         wm_susleep(1000);
0335     }
0336 
0337     memset(blks, 0, sizeof(blks));
0338 
0339     d->blocks = blks;
0340     d->frames_at_once = COUNT_CDDA_FRAMES_PER_BLOCK;
0341     d->numblocks = COUNT_CDDA_BLOCKS;
0342     d->status = WM_CDM_UNKNOWN;
0343 
0344     if ((ret = gen_cdda_init(d)))
0345         return ret;
0346 
0347     if ((ret = gen_cdda_open(d)))
0348         return ret;
0349 
0350     wm_scsi_set_speed(d, 4);
0351 
0352     oops = setup_soundsystem(d->soundsystem, d->sounddevice, d->ctldevice);
0353     if (!oops) {
0354         ERRORLOG("cdda: setup_soundsystem failed\n");
0355         gen_cdda_close(d);
0356         return -1;
0357     }
0358 
0359     if(pthread_create(&thread_read, NULL, cdda_fct_read, d)) {
0360         ERRORLOG("error by create pthread");
0361         oops->wmaudio_close();
0362         gen_cdda_close(d);
0363         return -1;
0364     }
0365 
0366     if(pthread_create(&thread_play, NULL, cdda_fct_play, d)) {
0367         ERRORLOG("error by create pthread");
0368         oops->wmaudio_close();
0369         gen_cdda_close(d);
0370         return -1;
0371     }
0372 
0373     d->proto.get_drive_status = cdda_status;
0374     d->proto.pause = cdda_pause;
0375     d->proto.resume = NULL;
0376     d->proto.stop = cdda_stop;
0377     d->proto.play = cdda_play;
0378     d->proto.set_volume = cdda_set_volume;
0379     d->proto.get_volume = cdda_get_volume;
0380     d->proto.scale_volume = NULL;
0381     d->proto.unscale_volume = NULL;
0382 
0383     d->cddax = (void *)1;
0384 
0385     return 0;
0386 }
0387 
0388 int wm_cdda_destroy(struct wm_drive *d)
0389 {
0390     if (d->cddax) {
0391         wm_scsi_set_speed(d, -1);
0392 
0393         d->command = WM_CDM_STOPPED;
0394         oops->wmaudio_stop();
0395         wm_susleep(2000);
0396         gen_cdda_close(d);
0397         oops->wmaudio_close();
0398 
0399         d->numblocks = 0;
0400         d->blocks = NULL;
0401         wait(NULL);
0402         d->cddax = NULL;
0403     }
0404     return 0;
0405 }