/* * ximp3 A simple mp3 player * * Copyright (C) 2001 Mats Peterson * * 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 program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Please send any comments/bug reports to * matsp888@yahoo.com (Mats Peterson) */ /* * Audio output fork stuff */ #include #include #include #include #include #include #include #include #include #include #include #include "xingmp3.h" #include "ximp3.h" #include "init.h" #include "audio.h" #include "info.h" static void err_exit(void) { kill(v->ppid, SIGTERM); _exit(1); } static void out_stats(int frames, int in_bytes, int out_bytes) { double sec; int unplayed; audio_buf_info info; if (ioctl(v->audio_fd, SNDCTL_DSP_GETOSPACE, &info) == -1) { perror("SNDCTL_DSP_GETOSPACE"); err_exit(); } unplayed = v->audio_bufsize - (info.fragments * info.fragsize); sec = (double)(out_bytes - unplayed) / (double)v->bytes_sec; if (v->remote) printf("@F %d %.2f\n", frames, sec); else { printf("\r Frames %6d Time %02d:%02d.%02d " "Bytes In %6d Bytes Out %6d", frames, (int)(sec / 60), (int)sec % 60, (int)(sec * 100) % 100, in_bytes, out_bytes - unplayed); fflush(stdout); } } static void init_buffers(void) { v->playbufs = (PLAYBUF*)malloc(sizeof(PLAYBUF) * v->tot_playbufs); if (! v->playbufs) { fprintf(stderr, "Couldn't allocate play buffers\n"); err_exit(); } v->playbuf = v->addbuf = 0; v->n_playbufs = 0; v->playing = 0; } static int read_buffer(void) { static char *p = NULL; static int toread = sizeof(PLAYBUF); fd_set readfds; struct timeval timeout; int n; DEC_INFO *decinfo; if (v->n_playbufs == v->tot_playbufs) { if (! v->playing) v->playing = 1; return 0; } /* read a full buffer */ do { FD_ZERO(&readfds); FD_SET(v->sfds[0], &readfds); timeout.tv_sec = 0; timeout.tv_usec = 0; if (! (n = select(v->sfds[0] + 1, &readfds, NULL, NULL, &timeout))) return 0; if (n < 0) { perror("select"); err_exit(); } if (! p) p = (char *)&(v->playbufs[v->addbuf]); if ((n = read(v->sfds[0], p, toread)) < 0) { perror("socket read"); err_exit(); } p += n; toread -= n; } while (toread); /* open and configure audio device */ if (v->playbufs[v->addbuf].frames == -1) { int retval; if (! (v->audio_fd = open_audio(v->device))) err_exit(); decinfo = (DEC_INFO *)&(v->playbufs[v->addbuf].pcm); v->bytes_sec = decinfo->samprate * (decinfo->bits / 8) * decinfo->channels; retval = config_audio((int)decinfo->bits, decinfo->channels, decinfo->samprate); if (retval == -1) err_exit(); kill(v->ppid, retval ? SIGUSR1 : SIGUSR2); goto set_p; } v->n_playbufs++; /* if last buffer, play remaining buffers now */ if (v->playbufs[v->addbuf].size == 0) { v->playing = 1; goto set_p; } v->addbuf = (v->addbuf == (v->tot_playbufs - 1)) ? 0 : v->addbuf + 1; set_p: p = (char *)&(v->playbufs[v->addbuf]); toread = sizeof(PLAYBUF); return 1; } static void play_buffer(void) { static char *p = NULL; static int towrite; fd_set writefds; struct timeval timeout; static int out_bytes = 0; audio_buf_info info; int n; if (! v->playing) return; /* last buffer */ if (v->playbufs[v->playbuf].size == 0) { if (v->verbose || v->remote) { int now_bytes = 0; while (1) { if (ioctl(v->audio_fd, SNDCTL_DSP_GETOSPACE, &info) == -1) { perror("SNDCTL_DSP_GETOSPACE"); err_exit(); } if ((info.bytes - now_bytes) >= PCM_BUFBYTES) { now_bytes = info.bytes; out_stats(v->playbufs[v->playbuf].frames, v->playbufs[v->playbuf].in_bytes, out_bytes); } /* Break if audio buffer empty */ if (info.bytes == v->audio_bufsize) { out_stats(v->playbufs[v->playbuf].frames, v->playbufs[v->playbuf].in_bytes, out_bytes); break; } usleep(1000); } } close(v->audio_fd); if (v->verbose && (! v->remote)) printf("\n"); v->n_playbufs = 0; v->playing = 0; out_bytes = 0; kill(v->ppid, SIGUSR1); goto null_p; } /* write a full buffer */ do { FD_ZERO(&writefds); FD_SET(v->audio_fd, &writefds); timeout.tv_sec = 0; timeout.tv_usec = 0; if (! (n = select(v->audio_fd + 1, NULL, &writefds, NULL, &timeout))) return; if (n < 0) { perror("select"); err_exit(); } if (! p) { p = v->playbufs[v->playbuf].pcm; towrite = v->playbufs[v->playbuf].size; } if ((n = write(v->audio_fd, p, towrite)) < 0) { fprintf(stderr, "DSP write error"); err_exit(); } p += n; towrite -= n; } while (towrite); out_bytes += v->playbufs[v->playbuf].size; if (v->verbose || v->remote) { out_stats(v->playbufs[v->playbuf].frames, v->playbufs[v->playbuf].in_bytes, out_bytes); } v->playbuf = (v->playbuf == (v->tot_playbufs - 1)) ? 0 : v->playbuf + 1; v->n_playbufs--; if (! v->n_playbufs) { v->playing = 0; goto null_p; } p = v->playbufs[v->playbuf].pcm; towrite = v->playbufs[v->playbuf].size; return; null_p: p = NULL; } int fork_audio(void) { if (socketpair(AF_UNIX, SOCK_STREAM, 0, v->sfds) < 0) { fprintf(stderr, "Couldn't create sockets\n"); return 0; } if ((v->pid = fork()) < 0) { fprintf(stderr, "Couldn't fork audio output process\n"); return 0; } if (! v->pid) { v->ppid = getppid(); close(v->sfds[1]); init_buffers(); while (1) { while (read_buffer()); play_buffer(); usleep(1000); } } close(v->sfds[0]); return 1; } .