/*
* 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
*
[email protected] (Mats Peterson)
*/
/*
* Audio output thread
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include "xingmp3.h"
#include "ximp3.h"
#include "init.h"
#include "audio.h"
#include "info.h"
static void err_exit(void)
{
exit(1);
}
static void out_stats(int frames, int in_bytes, int out_bytes)
{
double sec;
int unplayed;
if (ioctl(v->audio_fd, SNDCTL_DSP_GETODELAY, &unplayed) == -1) {
perror("SNDCTL_DSP_GETODELAY");
err_exit();
}
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 int read_buffer(void)
{
static char *p = NULL;
static int toread = sizeof(PLAYBUF);
fd_set readfds;
struct timeval timeout;
int n;
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->pfds[0], &readfds);
timeout.tv_sec = 0;
timeout.tv_usec = 0;
if (! (n = select(v->pfds[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->pfds[0], p, toread)) < 0) {
perror("pipe read");
err_exit();
}
p += n;
toread -= n;
} while (toread);
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 int 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 1;
/* 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;
p = NULL;
return 0;
}
/* 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 1;
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 1;
null_p:
p = NULL;
return 1;
}
void * audio_thread(void *arg)
{
while (1) {
while (read_buffer());
if (! play_buffer())
return NULL;
usleep(1000);
}
}