/*
* 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)
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include "xingmp3.h"
#include "ximp3.h"
#include "init.h"
#include "audio.h"
#include "audiothread.h"
#include "info.h"
#include "id3.h"
VARS *v;
static int bs_fill(int fd)
{
unsigned int nread;
if (v->bs_bufbytes < 0)
v->bs_bufbytes = 0; // signed var could be negative
if (v->bs_bufbytes < v->bs_trigger) {
memmove(v->bs_buffer, v->bs_bufptr, v->bs_bufbytes);
nread = read(fd, v->bs_buffer + v->bs_bufbytes,
BS_BUFBYTES - v->bs_bufbytes);
if ((nread + 1) == 0) {
/*-- test for -1 = error --*/
v->bs_trigger = 0;
errmsg("File read error");
return 0;
}
v->bs_bufbytes += nread;
v->bs_bufptr = v->bs_buffer;
}
return 1;
}
static void add_playbuf(char *pcm, int size, int in_bytes, int frames)
{
PLAYBUF new;
memcpy(new.pcm, pcm, size);
new.size = size;
new.in_bytes = in_bytes;
new.frames = frames;
if (write(v->pfds[1], &new, sizeof(PLAYBUF)) == -1) {
perror("write");
exit(1);
}
}
static void play_file(char *filename)
{
char *pcm_buffer;
unsigned int pcm_bufbytes;
int fd;
int framebytes;
int frames;
MPEG_HEAD head;
MPEG m;
unsigned int skip;
IN_OUT x;
int in_bytes;
DEC_INFO decinfo;
int bitrate;
int r;
if (! v->remote)
printf("\nMPEG input file: %s\n", filename);
mpeg_init(&m, 1);
in_bytes = 0;
pcm_buffer = NULL;
pcm_bufbytes = 0;
fd = -1;
v->bs_buffer = NULL;
v->bs_bufbytes = 0;
v->bs_bufptr = v->bs_buffer;
v->bs_trigger = 2500;
/*--- test for valid cvt_to_wave compile ---*/
if (cvt_to_wave_test() != 0) {
errmsg("Invalid cvt_to_wave compile");
goto abort;
}
/*----- open audio device ------*/
if (! (v->audio_fd = open_audio(v->device)))
exit(1);
/*------ open mpeg file --------*/
if ((fd = open(filename, O_RDONLY)) < 0) {
errmsg("Couldn't open input file");
goto abort;
}
/*----- get id3 info -----*/
out_id3_info(fd, filename, v->remote);
/*--- allocate bs buffer ----*/
v->bs_buffer = malloc(BS_BUFBYTES);
if (v->bs_buffer == NULL) {
errmsg("Couldn't allocate buffer");
goto abort;
}
/*--- fill bs buffer ----*/
if (! bs_fill(fd))
goto abort;
/*---- parse mpeg header -------*/
framebytes = head_info3(v->bs_buffer, v->bs_bufbytes, &head,
&bitrate, &skip);
if (framebytes == 0) {
errmsg("Bad or unsupported MPEG file");
goto abort;
}
v->bs_bufptr += skip;
v->bs_bufbytes -= skip;
/*--- display mpeg info --*/
out_mpeg_info(&head, bitrate);
/*---- allocate temporary pcm buffer -----*/
pcm_buffer = malloc(PCM_BUFBYTES);
if (pcm_buffer == NULL) {
errmsg("Couldn't allocate PCM buffer");
goto abort;
}
/*------ init decoder -------*/
if (! audio_decode_init(&m, &head, framebytes, 0, 0, 0, 24000)) {
errmsg("Decoder init failed");
goto abort;
}
/*------ display decode info ------*/
out_decode_info(&m, &decinfo);
/*------ configure audio device ------*/
r = config_audio((int)decinfo.bits, decinfo.channels, decinfo.samprate);
if (r < 0)
exit(1);
if (! r)
goto abort;
v->bytes_sec = decinfo.samprate * (decinfo.bits / 8) * decinfo.channels;
/*--- init wave converter ---*/
cvt_to_wave_init(decinfo.bits);
if (! v->remote)
printf("\n");
/*---- Create audio output thread ----*/
if (pthread_create(&v->thr, NULL, audio_thread, NULL) < 0) {
fprintf(stderr, "Couldn't create audio thread\n");
exit(1);
}
/*----- DECODE -----*/
for (frames = 0;;) {
if (! bs_fill(fd))
break;
if (v->bs_bufbytes < framebytes)
break; /* end of file */
x = audio_decode(&m, v->bs_bufptr,
(short *) (pcm_buffer + pcm_bufbytes));
if (x.in_bytes <= 0) {
errmsg("Bad sync in MPEG file");
break;
}
v->bs_bufptr += x.in_bytes;
v->bs_bufbytes -= x.in_bytes;
pcm_bufbytes += x.out_bytes;
frames++;
if (pcm_bufbytes == PCM_BUFBYTES) {
pcm_bufbytes = cvt_to_wave(pcm_buffer, pcm_bufbytes);
add_playbuf(pcm_buffer, pcm_bufbytes, in_bytes, frames);
pcm_bufbytes = 0;
}
in_bytes += x.in_bytes;
}
/*---------------------*/
if (pcm_bufbytes > 0) {
pcm_bufbytes = cvt_to_wave(pcm_buffer, pcm_bufbytes);
add_playbuf(pcm_buffer, pcm_bufbytes, in_bytes, frames);
pcm_bufbytes = 0;
}
/*--- send terminating buffer and wait for thread to terminate ---*/
add_playbuf(NULL, 0, in_bytes, frames);
pthread_join(v->thr, NULL);
abort:
mpeg_cleanup(&m);
if (v->audio_fd)
close(v->audio_fd);
if (fd > 0)
close(fd);
if (v->bs_buffer)
free(v->bs_buffer);
if (pcm_buffer)
free(pcm_buffer);
}
static void shuffle_files(void)
{
int i, randnum;
srand(time(NULL));
v->shuffle_ord = (int *)malloc((v->flist_size + 1) * sizeof(int));
if (! v->shuffle_ord) {
perror("malloc");
exit(1);
}
/* write songs in 'correct' order */
for (i = 0; i < v->flist_size; i++) {
v->shuffle_ord[i] = i;
}
/* now shuffle them */
if (v->flist_size >= 2) {
for (i = 0; i < v->flist_size; i++) {
randnum = (rand() % (v->flist_size * 4 - 4)) / 4;
randnum += (randnum >= i);
v->shuffle_ord[i] ^= v->shuffle_ord[randnum];
v->shuffle_ord[randnum] ^= v->shuffle_ord[i];
v->shuffle_ord[i] ^= v->shuffle_ord[randnum];
}
}
}
static void add_file(char *fname)
{
static int alloc_size = 0;
if (v->flist_size + 2 > alloc_size) {
alloc_size += 8;
v->flist = (char **)realloc(v->flist, alloc_size * sizeof(char *));
if (! v->flist) {
perror("realloc");
exit(1);
}
}
if (! (v->flist[v->flist_size] = (char *)malloc(strlen(fname) + 1))) {
perror("malloc");
exit(1);
}
strcpy(v->flist[v->flist_size], fname);
v->flist_size++;
}
static void play(int argc, char **argv)
{
FILE *f;
char fname[1026];
char *p;
int i;
int newlist = 1;
if (v->flist) {
newlist = 0;
goto playshuf;
}
for (i = optind; i < argc; i++) {
p = strrchr(argv[i], '.');
if (p && ((! strcmp(p, ".m3u") || (! strcmp(p, ".M3U"))))) {
if (! (f = fopen(argv[i], "r"))) {
errmsg("Couldn't open playlist");
continue;
}
while (fgets(fname, 1024, f)) {
fname[strlen(fname) - 1] = '\0';
if (v->shuffle)
add_file(fname);
else
play_file(fname);
}
fclose(f);
} else {
if (v->shuffle)
add_file(argv[i]);
else
play_file(argv[i]);
}
}
if (! v->shuffle)
return;
playshuf:
if (newlist)
shuffle_files();
for (i = 0; i < v->flist_size; i++)
play_file(v->flist[v->shuffle_ord[i]]);
}
int main(int argc, char **argv)
{
init(argc, argv);
do {
play(argc, argv);
} while (v->loop);
return 0;
}