/* $Id: spool.c,v 1.6 1999/03/10 21:14:38 boyns Exp $ */
/*
* Copyright (C) 1993-99 Mark R. Boyns <boyns@doit.org>
*
* This file is part of rplay.
*
* rplay 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.
*
* rplay 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 rplay; see the file COPYING. If not, write to the
* Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include <unistd.h>
#include "rplayd.h"
#include "rplay.h"
#include "spool.h"
#include "sound.h"
#include "buffer.h"
#include "timer.h"
#include "connection.h"
#include "native.h"
#ifdef TEST_FLANGE
#include "flange.h"
#endif
SPOOL *spool = NULL; /* List of spool entries. */
int spool_size = 0; /* Number of entries in the spool. */
int spool_nplaying = 0; /* Number of playing sounds. */
int spool_npaused = 0; /* Number of paused sounds. */
int spool_prio = 0; /* Highest priority spool entry. */
int spool_needs_update = 0; /* Need to call spool_update(). */
/*
* initialize the sound spool
*/
void
spool_init()
{
SPOOL *sp, *sp_next;
/* Destroy any existing spool entries (reset). */
for (sp = spool; sp; sp = sp_next)
{
sp_next = sp->next;
spool_destroy(sp);
}
spool = NULL;
spool_nplaying = 0;
spool_prio = 0;
spool_size = 0;
}
/* Create a new spool entry. */
SPOOL *
spool_create()
{
SPOOL *sp = (SPOOL *) malloc(sizeof(SPOOL));
if (sp == NULL)
{
report(REPORT_ERROR, "spool_create: out of memory\n");
done(1);
}
/* `sp' must be initialized before being reset. */
memset((char *) sp, 0, sizeof(SPOOL));
sp->id = -1; /* XXX */
spool_reset(sp);
/* Insert the new entry into the spool list. */
sp->next = spool;
sp->prev = NULL;
if (spool)
{
spool->prev = sp;
}
spool = sp;
spool_size++;
return sp;
}
/* Destroy the given spool entry. */
#ifdef __STDC__
void
spool_destroy(SPOOL *sp)
#else
void
spool_destroy(sp)
SPOOL *sp;
#endif
{
/* Reset `sp' in case any resources are allocated. */
spool_reset(sp);
/* Remove `sp' from the spool list. */
if (sp->prev)
{
sp->prev->next = sp->next;
}
else
{
spool = sp->next;
}
if (sp->next)
{
sp->next->prev = sp->prev;
}
spool_size--;
free((char *) sp);
}
/*
* Return the next available spool entry.
*
* `priority' is the priority of a new sound and is used to replace
* lower priority spool entries when the spool is full.
*/
#ifdef __STDC__
SPOOL *
spool_next(int priority)
#else
SPOOL *
spool_next(priority)
int priority;
#endif
{
int i;
int min_priority = RPLAY_MAX_PRIORITY, index = -1;
SPOOL *sp, *lowest = NULL;
/* There's room for a new entry. */
if (spool_size < SPOOL_SIZE)
{
sp = spool_create();
sp->id = spool_id();
return sp;
}
/* The spool is full, try to replace an entry. */
/* Find the lowest priority sound. */
for (sp = spool; sp; sp = sp->next)
{
if (sp->state == SPOOL_PLAY)
{
if (sp->rp->priority < min_priority)
{
min_priority = sp->rp->priority;
lowest = sp;
}
}
}
/* Replace the lowest priority sound, if possible. */
if (priority > min_priority && lowest)
{
sp = lowest;
if (sp->notify_position)
{
connection_notify(0, NOTIFY_POSITION, sp);
sp->notify_position = 0;
}
connection_notify(0, NOTIFY_DONE, sp);
spool_reset(sp);
spool_nplaying--;
sp->id = spool_id();
return sp;
}
else
{
return NULL;
}
}
/* Reset a spool entry to an initial state.
`next' and `prev' are NOT changed. */
#ifdef __STDC__
void
spool_reset(SPOOL *sp)
#else
void
spool_reset(sp)
SPOOL *sp;
#endif
{
sp->state = SPOOL_NULL;
if (sp->id > 0)
{
/* Wakeup any connections that may be waiting for this
spool entry. Their flow data will be ignored. */
connection_flow_continue(sp);
}
sp->id = -1;
sp->time = 0;
if (sp->rp)
{
int i;
for (i = 0; i < sp->rp->nsounds; i++)
{
sound_clean(sp->sound[i]);
}
rplay_destroy(sp->rp);
}
sp->rp = NULL;
sp->ptr = NULL;
sp->ptr_end = NULL;
sp->sample_rate = 0;
sp->sample_index = 0;
sp->sample_factor = 0;
sp->oversample[0] = 0;
sp->oversample[1] = 0;
sp->oversample_inc[0] = 0;
sp->oversample_inc[1] = 0;
if (sp->curr_buffer)
{
buffer_dealloc(sp->curr_buffer, 1);
}
sp->curr_buffer = NULL;
if (sp->next_buffer)
{
buffer_dealloc(sp->next_buffer, 1);
}
sp->next_buffer = NULL;
sp->offset = 0;
if (sp->si)
{
sound_close(sp->si);
}
sp->si = NULL;
sp->to_native = NULL;
sp->skip_count = 0;
sp->auto_pause = 0;
sp->notify_position = 0;
spool_setprio();
}
/* Find the maximum spool priority. */
#ifdef __STDC__
void
spool_setprio(void)
#else
void
spool_setprio()
#endif
{
SPOOL *sp;
spool_prio = 0;
for (sp = spool; sp; sp = sp->next)
{
if (sp->state == SPOOL_PLAY)
{
spool_prio = MAX(spool_prio, sp->rp->priority);
}
}
}
/* Find the given RPLAY object in the spool. */
#ifdef __STDC__
int
spool_match(RPLAY *match, void (*action) (SPOOL *), struct sockaddr_in sin)
#else
int
spool_match(match, action, sin)
RPLAY *match;
void (*action) ();
struct sockaddr_in sin;
#endif
{
int i, id, nmatch = 0;
RPLAY *rp;
RPLAY_ATTRS *a1, *a2;
SPOOL *sp, *sp_next;
for (sp = spool; sp; sp = sp_next)
{
sp_next = sp->next; /* sp may be destroyed */
if (sp->state == SPOOL_NULL)
{
continue;
}
a2 = match->attrs;
if (a2 && a2->sound[0] == '#')
{
for (; a2; a2 = a2->next)
{
id = atoi(a2->sound + 1);
if (id == 0 || id == sp->id)
{
sp->sin = sin;
(*action) (sp);
nmatch++;
break;
}
}
continue;
}
rp = sp->rp;
for (a1 = rp->attrs, a2 = match->attrs; a1 && a2; a1 = a1->next, a2 = a2->next)
{
if (strcmp(a1->sound, a2->sound))
{
break;
}
}
if (!a1 && !a2)
{
sp->sin = sin;
(*action) (sp);
nmatch++;
}
}
return nmatch;
}
/* Remove any spool entries with `sound' from the spool. */
#ifdef __STDC__
void
spool_remove(SOUND *sound)
#else
void
spool_remove(sound)
SOUND *sound;
#endif
{
int i, j, n;
SPOOL *sp, *sp_next;
for (sp = spool; sp; sp = sp_next)
{
sp_next = sp->next; /* sp may be destroyed */
if (sp->state == SPOOL_NULL)
{
continue;
}
n = 0;
for (j = 0; j < sp->rp->nsounds; j++)
{
if (sp->sound[j] == sound)
{
n++;
}
}
if (n)
{
spool_done(sp);
}
}
}
/*
* Make all spool entries waiting for `sound' ready to be played.
*/
#ifdef __STDC__
void
spool_ready(SOUND *sound)
#else
void
spool_ready(sound)
SOUND *sound;
#endif
{
int j, number_not_ready, has_the_sound;
SPOOL *sp;
for (sp = spool; sp; sp = sp->next)
{
if (sp->state == SPOOL_WAIT)
{
number_not_ready = 0;
has_the_sound = 0;
for (j = 0; j < sp->rp->nsounds; j++)
{
if (sp->sound[j]->status != SOUND_READY)
{
number_not_ready++;
if (sp->sound[j] == sound)
{
number_not_ready--;
}
}
if (sp->sound[j] == sound)
{
has_the_sound++;
}
}
if (number_not_ready == 0 && has_the_sound)
{
for (j = 0; j < sp->rp->nsounds; j++)
{
sound_map(sp->sound[j]);
}
spool_play(sp);
}
}
}
}
/* Return a buffer list for the `list spool' command. */
BUFFER *
spool_list_create()
{
BUFFER *spool_list, *b;
int i, n;
char buf[RPTP_MAX_LINE];
char line[RPTP_MAX_LINE];
SOUND *s;
SPOOL *sp;
int length;
b = buffer_create();
spool_list = b;
SNPRINTF(SIZE(b->buf, BUFFER_SIZE), "+message=\"spool\"\r\n");
b->nbytes += strlen(b->buf);
for (sp = spool; sp; sp = sp->next)
{
length = 0;
line[0] = '\0';
SNPRINTF(SIZE(buf, sizeof(buf)), "id=#%d state=", sp->id);
strncat(line + length, buf, sizeof(line) - length);
length += strlen(buf);
switch (sp->state)
{
case SPOOL_PLAY:
SNPRINTF(SIZE(buf, sizeof(buf)), "play");
break;
case SPOOL_PAUSE:
SNPRINTF(SIZE(buf, sizeof(buf)), "pause");
break;
case SPOOL_WAIT:
SNPRINTF(SIZE(buf, sizeof(buf)), "wait");
break;
case SPOOL_NEXT:
SNPRINTF(SIZE(buf, sizeof(buf)), "next");
break;
case SPOOL_SKIP:
SNPRINTF(SIZE(buf, sizeof(buf)), "skip");
break;
default:
continue;
}
strncat(line + length, buf, sizeof(line) - length);
length += strlen(buf);
s = sp->sound[sp->curr_sound];
SNPRINTF(SIZE(buf, sizeof(buf)), "\
sound=\"%s\" host=%s volume=%d priority=%d count=%d position=%.2f remain=%.2f seconds=%.2f size=%d\
sample-rate=%d channels=%d bits=%g input=%s client-data=\"%s\" list-name=\"%s\"\r\n",
sp->curr_attrs->sound,
inet_ntoa(sp->sin.sin_addr),
sp->curr_attrs->volume,
sp->rp->priority,
sp->curr_count,
sp->sample_rate && s->samples ? sp->sample_index / sp->sample_rate : 0,
sp->sample_rate && s->samples ? ((double) s->samples - sp->sample_index) / sp->sample_rate : 0,
sp->sample_rate && s->samples ? (double) s->samples / sp->sample_rate : 0,
s->size,
sp->sample_rate,
s->channels,
s->input_precision,
input_to_string(s->type),
sp->curr_attrs->client_data,
sp->rp->list_name);
strncat(line + length, buf, sizeof(line) - length);
length += strlen(buf);
if (b->nbytes + length > BUFFER_SIZE)
{
b->next = buffer_create();
b = b->next;
}
strncat(b->buf + b->nbytes, line, BUFFER_SIZE - b->nbytes);
b->nbytes += length;
}
if (b->nbytes + 3 > BUFFER_SIZE)
{
b->next = buffer_create();
b = b->next;
}
strncat(b->buf + b->nbytes, ".\r\n", BUFFER_SIZE - b->nbytes);
b->nbytes += 3;
return spool_list;
}
/*
* Return a unique spool id.
*/
int
spool_id()
{
static int id = 0;
SPOOL *sp;
for (;;)
{
id++;
if (id > SPOOL_MAX_ID)
{
id = SPOOL_MIN_ID;
}
for (sp = spool; sp; sp = sp->next) /* see if the id is in use */
{
if (sp->state != SPOOL_NULL && sp->id == id)
{
break;
}
}
if (!sp)
{
return id;
}
}
}
#ifdef __STDC__
void
spool_stop(SPOOL *sp)
#else
void
spool_stop(sp)
SPOOL *sp;
#endif
{
timer_block();
switch (sp->state)
{
case SPOOL_PLAY:
case SPOOL_PAUSE:
case SPOOL_WAIT:
if (sp->state == SPOOL_PLAY)
{
spool_nplaying--;
}
else if (sp->state == SPOOL_PAUSE)
{
spool_npaused--;
}
if (sp->notify_position)
{
connection_notify(0, NOTIFY_POSITION, sp);
sp->notify_position = 0;
}
connection_notify(0, NOTIFY_STOP, sp);
connection_notify(0, NOTIFY_DONE, sp);
spool_destroy(sp);
break;
}
timer_unblock();
}
#ifdef __STDC__
void
spool_pause(SPOOL *sp)
#else
void
spool_pause(sp)
SPOOL *sp;
#endif
{
timer_block();
if (sp->state == SPOOL_PLAY)
{
sp->state = SPOOL_PAUSE;
spool_nplaying--;
spool_npaused++;
if (sp->notify_position)
{
connection_notify(0, NOTIFY_POSITION, sp);
sp->notify_position = 0;
}
connection_notify(0, NOTIFY_PAUSE, sp);
}
timer_unblock();
}
#ifdef __STDC__
void
spool_continue(SPOOL *sp)
#else
void
spool_continue(sp)
SPOOL *sp;
#endif
{
timer_block();
if (sp->state == SPOOL_PAUSE)
{
sp->state = SPOOL_PLAY;
if (rplay_audio_match)
{
rplayd_audio_match(sp);
}
spool_nplaying++;
spool_npaused--;
spool_needs_update++;
connection_notify(0, NOTIFY_CONTINUE, sp);
}
timer_unblock();
}
#ifdef __STDC__
void
spool_done(SPOOL *sp)
#else
void
spool_done(sp)
SPOOL *sp;
#endif
{
timer_block();
switch (sp->state)
{
case SPOOL_PLAY:
case SPOOL_PAUSE:
case SPOOL_WAIT:
if (sp->si && sp->si->is_flow && sp->state == SPOOL_PLAY)
{
sp->si->eof = 1; /* spool_update will deal with this */
}
else
{
if (sp->state == SPOOL_PLAY)
{
spool_nplaying--;
}
else if (sp->state == SPOOL_PAUSE)
{
spool_npaused--;
}
if (sp->notify_position)
{
connection_notify(0, NOTIFY_POSITION, sp);
sp->notify_position = 0;
}
connection_notify(0, NOTIFY_DONE, sp);
spool_destroy(sp);
}
break;
}
timer_unblock();
}
#ifdef __STDC__
void
spool_flow_pause(SPOOL *sp)
#else
void
spool_flow_pause(sp)
SPOOL *sp;
#endif
{
timer_block();
if (sp->state == SPOOL_PLAY)
{
sp->state = SPOOL_WAIT;
spool_nplaying--;
}
timer_unblock();
}
#ifdef __STDC__
void
spool_flow_continue(SPOOL *sp)
#else
void
spool_flow_continue(sp)
SPOOL *sp;
#endif
{
timer_block();
if (sp->state == SPOOL_WAIT)
{
sp->state = SPOOL_PLAY;
spool_nplaying++;
spool_needs_update++;
}
timer_unblock();
}
#ifdef __STDC__
int
spool_flow_insert(SPOOL *sp, BUFFER *b)
#else
int
spool_flow_insert(sp, b)
SPOOL *sp;
BUFFER *b;
#endif
{
SOUND *s;
BUFFER *tmp_buf;
s = sp->sound[sp->curr_sound];
*s->flowp = b;
for (tmp_buf = b; tmp_buf->next; tmp_buf = tmp_buf->next) ;
s->flowp = &tmp_buf->next;
/* Enable the sound. */
if (s->status == SOUND_NOT_READY)
{
s->status = SOUND_READY;
if (sound_map(s) < 0)
{
/* Punt - can't determine what type of sound it is. */
spool_remove(s);
/* spool_remove now deletes the sound - sound_delete (s, 0); */
return -1;
}
spool_ready(s);
}
for (tmp_buf = b; tmp_buf; tmp_buf = tmp_buf->next)
{
//s->size += tmp_buf->nbytes;
sp->si->water_mark += tmp_buf->nbytes;
}
s->samples = number_of_samples(s, sp->si->water_mark);
/* Wakeup the spool entry. */
if (sp->state == SPOOL_WAIT)
{
spool_flow_continue(sp);
}
return 0;
}
#ifdef __STDC__
void
spool_skip(SPOOL *sp, int count)
#else
void
spool_skip(sp, count)
SPOOL *sp;
int count;
#endif
{
SOUND *s;
timer_block();
s = sp->sound[sp->curr_sound];
/* Only sounds that are playing or paused can be skipped.
Flows can't be skipped right now. */
if ((sp->state == SPOOL_PLAY || sp->state == SPOOL_PAUSE)
&& s->type == SOUND_FILE)
{
if (sp->state == SPOOL_PAUSE)
{
spool_npaused--;
sp->auto_pause++;
}
sp->state = SPOOL_SKIP;
sp->skip_count = count;
spool_needs_update++;
connection_notify(0, NOTIFY_SKIP, sp);
}
timer_unblock();
}
#ifdef __STDC__
void
spool_play(SPOOL *sp)
#else
void
spool_play(sp)
SPOOL *sp;
#endif
{
SOUND *s;
s = sp->sound[sp->curr_sound];
#ifdef HAVE_CDROM
/* Parse the name of the cdrom device that was specified. */
if (s->type == SOUND_CDROM)
{
char name_buf[MAXPATHLEN];
char *p;
strncpy(name_buf, sp->curr_attrs->sound, sizeof(name_buf));
p = strchr(name_buf, ':');
if (p == NULL)
{
report(REPORT_DEBUG, "spool_play: can't parse cdrom name `%s'\n", s->name);
return;
}
*p++ = '\0';
if (strchr(p, '-'))
{
p = strtok(p, "-");
if (p)
{
s->starting_track = atoi(p);
p = strtok(NULL, "");
if (p)
{
s->ending_track = atoi(p);
}
}
}
else
{
s->starting_track = atoi(p);
s->ending_track = atoi(p);
}
}
#endif /* HAVE_CDROM */
sp->si = sound_open(s, 1);
if (sp->si == NULL)
{
spool_destroy(sp);
return;
}
sound_seek(sp->si, s->offset, SEEK_SET); /* skip the audio header */
sp->curr_buffer = NULL;
sp->next_buffer = NULL;
sp->sample_index = 0;
sp->to_native = native_table[s->format].to_native[s->byte_order - 1];
spool_set_sample_rate(sp,
(sp->curr_attrs->sample_rate == RPLAY_DEFAULT_SAMPLE_RATE)
? s->sample_rate : sp->curr_attrs->sample_rate);
/* Change the state to SPOOL_PLAY when ALL fields have been assigned. */
sp->state = SPOOL_PLAY;
spool_nplaying++;
spool_setprio();
spool_needs_update++;
connection_notify(0, NOTIFY_PLAY, sp);
connection_notify(0, NOTIFY_POSITION, sp);
if (sp->auto_pause)
{
spool_pause(sp);
sp->auto_pause = 0;
}
}
#ifdef __STDC__
void
spool_update(void)
#else
void
spool_update()
#endif
{
int i, n;
SPOOL *sp, *sp_next;
SOUND *s;
BUFFER *b;
time_t now;
spool_needs_update = 0;
now = time(0);
for (sp = spool; sp; sp = sp_next)
{
sp_next = sp->next; /* sp may be destroyed */
/* Some sounds must end here. */
if (sp->state == SPOOL_PLAY && !sp->curr_buffer && !sp->si)
{
sp->state = SPOOL_NEXT;
}
switch (sp->state)
{
case SPOOL_SKIP:
case SPOOL_NEXT:
if (sp->notify_position)
{
connection_notify(0, NOTIFY_POSITION, sp);
sp->notify_position = 0;
}
if (!sp->auto_pause) /* something was playing */
{
spool_nplaying--;
}
if (sp->si)
{
sound_close(sp->si);
sp->si = NULL;
}
buffer_dealloc(sp->curr_buffer, 0);
buffer_dealloc(sp->next_buffer, 0);
sp->curr_buffer = NULL;
sp->next_buffer = NULL;
sp->offset = 0;
if (sp->state == SPOOL_SKIP)
{
/* The current sound is now `done'. */
connection_notify(0, NOTIFY_DONE, sp);
if (sp->skip_count == 0)
{
sp->curr_count++; /* "play it again Sam" */
}
else
{
sp->curr_count = 1; /* end the current sound */
/*
* Skip to the next sound.
*/
/* Repeat the current sound if more than 1 second
has been played. Otherwise, move to the
previous sound. */
if (sp->skip_count < 0
&& sp->sample_index > rplay_audio_sample_rate)
{
sp->skip_count++;
}
sp->curr_sound += sp->skip_count - 1;
/* Skipping before the first sound will always
skip to the first sound. */
if (sp->curr_sound < 0)
{
sp->curr_sound = -1;
}
/* Skipping after the last sound will always
skip to the last sound. */
else if (sp->curr_sound == sp->rp->nsounds)
{
sp->curr_sound = sp->rp->nsounds - 1;
}
}
}
if (sp->curr_count > 1)
{
sp->curr_count--;
}
else if (sp->curr_count)
{
/* Save a pointer to the sound that was playing. */
s = sp->sound[sp->curr_sound];
sp->curr_sound++;
if (sp->curr_sound == sp->rp->nsounds)
{
if (sp->list_count > 1)
{
sp->list_count--;
sp->curr_attrs = sp->rp->attrs;
sp->curr_sound = 0;
}
else if (sp->list_count == 0)
{
sp->curr_attrs = sp->rp->attrs;
sp->curr_sound = 0;
}
else
/* The spool entry is finished. */
{
if (sp->state != SPOOL_SKIP) /* already sent `done' */
{
connection_notify(0, NOTIFY_DONE, sp);
}
spool_destroy(sp);
continue;
}
}
else
{
/* Update `curr_attrs' to point to the next
set of attributes. */
if (sp->state == SPOOL_SKIP)
{
/* This does a linear search. Bad, bad, bad */
int i;
sp->curr_attrs = sp->rp->attrs;
for (i = 0; i < sp->curr_sound; i++)
{
sp->curr_attrs = sp->curr_attrs->next;
}
sp->skip_count = 0;
}
else
{
sp->curr_attrs = sp->curr_attrs->next;
}
}
sp->curr_count = sp->curr_attrs->count;
}
spool_play(sp);
/* break; */
case SPOOL_PLAY:
if (!sp->curr_buffer ||
(!sp->next_buffer && sp->si && !sp->si->eof))
{
s = sp->sound[sp->curr_sound];
#ifdef HAVE_OSS
b = buffer_alloc(MIN(s->sample_rate * s->output_sample_size, SOUND_FILL_SIZE),
BUFFER_REUSE);
#else
b = buffer_alloc(MIN(MAX(curr_bufsize * curr_rate,
s->sample_rate * s->output_sample_size),
SOUND_FILL_SIZE),
BUFFER_REUSE);
#endif
n = sound_fill(sp->si, b, 0);
if (n <= 0)
{
buffer_dealloc(b, 1);
if (s->needs_helper)
{
if (!sp->curr_buffer && !sp->next_buffer)
{
if (sp->si->eof)
{
sound_close(sp->si);
sp->si = NULL; /* rplayd_write will see this as a dead sound */
spool_needs_update++;
}
else
{
spool_flow_pause(sp);
}
}
}
else if (s->type == SOUND_FILE)
{
sound_close(sp->si);
sp->si = NULL; /* rplayd_write will see this as a dead sound */
}
else if (s->type == SOUND_FLOW)
{
if (!sp->curr_buffer && !sp->next_buffer)
{
if (sp->si->eof)
{
sound_close(sp->si);
sp->si = NULL; /* rplayd_write will see this as a dead sound */
spool_needs_update++;
}
else
{
spool_flow_pause(sp);
}
}
}
#ifdef HAVE_CDROM
else if (s->type == SOUND_CDROM)
{
if (!sp->curr_buffer && !sp->next_buffer)
{
if (sp->si->eof)
{
sound_close(sp->si);
sp->si = NULL; /* rplayd_write will see this as a dead sound */
spool_needs_update++;
}
else
{
spool_flow_pause(sp);
}
}
#endif /* HAVE_CDROM */
}
}
else
{
sp->time = now;
if (!sp->curr_buffer)
{
timer_block();
sp->ptr = (unsigned char *) b->buf + sp->offset;
sp->ptr_end = (unsigned char *) b->buf + b->nbytes;
sp->curr_buffer = b;
timer_unblock();
}
else
{
sp->next_buffer = b;
}
#ifdef HAVE_HELPERS
if (s->needs_helper
&& ((sp->si->water_mark - sp->si->offset) < sp->si->low_water_mark))
{
FD_SET(sp->si->fd, &read_mask);
}
#else
if (0)
{
/* hack */
}
#endif /* HAVE_HELPERS */
else if (s->type == SOUND_FLOW
&& ((sp->si->water_mark - sp->si->offset) < sp->si->low_water_mark))
{
connection_notify(0, NOTIFY_FLOW, sp);
connection_flow_continue(sp);
}
#ifdef HAVE_CDROM
else if (s->type == SOUND_CDROM
&& ((sp->si->water_mark - sp->si->offset) < sp->si->low_water_mark))
{
FD_SET(sp->si->fd, &read_mask);
}
#endif /* HAVE_CDROM */
}
}
break;
}
}
}
#ifdef __STDC__
int
spool_process(char *buf, int nbytes)
#else
int
spool_process(buf, nbytes)
char *buf;
int nbytes;
#endif
{
SPOOL *sp, *sp_next;
int number_playing = 0;
int nsamples;
/* Determine the number of samples that need to be processed. */
nsamples = nbytes / ((rplay_audio_precision >> 3) * rplay_audio_channels);
zero_native(rplay_audio_buf, nsamples, rplay_audio_channels);
for (sp = spool; sp; sp = sp_next)
{
sp_next = sp->next;
if (sp->state == SPOOL_PLAY)
{
number_playing++;
/* See if the end of sound has been reached. */
if (!(*sp->to_native) (sp, rplay_audio_buf, nsamples, rplay_audio_channels))
{
sp->state = SPOOL_NEXT;
spool_needs_update++;
}
/* See if more buffers need to be filled by `spool_update'. */
else if (sp->curr_buffer == NULL ||
(sp->si && !sp->si->eof && !sp->next_buffer))
{
spool_needs_update++;
}
sp->notify_position++;
}
}
if (number_playing)
{
#ifdef TEST_FLANGE
flange(rplay_audio_buf, nsamples, rplay_audio_channels);
#endif
#ifdef FAKE_VOLUME
fake_volume(rplay_audio_buf, nsamples, rplay_audio_channels);
#endif
/* Only calculate left and right levels if there's a connection
that needs the information. */
if (connection_want_level_notify)
{
level(rplay_audio_buf, nsamples, rplay_audio_channels);
connection_level_notify++;
}
/* Convert rplay_audio_buf to the audio device format. */
(*native_table[rplay_audio_format].from_native[RPLAY_AUDIO_BYTE_ORDER - 1])
(rplay_audio_buf, nsamples, rplay_audio_channels);
rplay_audio_size = nbytes;
}
}
#ifdef __STDC__
SPOOL *
spool_find(int id)
#else
SPOOL *
spool_find(id)
int id;
#endif
{
SPOOL *sp;
for (sp = spool; sp; sp = sp->next)
{
if (sp->id == id)
{
break;
}
}
return sp;
}
/* This routine can be used to remove idle flows from the spool. */
#ifdef __STDC__
void
spool_cleanup(void)
#else
void
spool_cleanup()
#endif
{
SPOOL *sp;
int flows_cleaned = 0;
for (sp = spool; sp; sp = sp->next)
{
if (sp->state == SPOOL_WAIT
&& sp->sound[sp->curr_sound]->type == SOUND_FLOW)
{
spool_done(sp);
flows_cleaned++;
}
}
report(REPORT_DEBUG, "cleaning up the spool - %d flows\n",
flows_cleaned);
}
#ifdef __STDC__
void
spool_set_count(SPOOL *sp, int count)
#else
void
spool_set_count(sp, count)
SPOOL *sp;
int count;
#endif
{
if (sp)
{
sp->curr_count = count;
sp->curr_attrs->count = count;
}
}
#ifdef __STDC__
void
spool_set_list_count(SPOOL *sp, int count)
#else
void
spool_set_list_count(sp, count)
SPOOL *sp;
int count;
#endif
{
if (sp)
{
sp->list_count = count;
sp->rp->count = count;
}
}
#ifdef __STDC__
void
spool_set_priority(SPOOL *sp, int priority)
#else
void
spool_set_priority(sp, priority)
SPOOL *sp;
int priority;
#endif
{
if (sp)
{
sp->rp->priority = priority;
spool_setprio();
}
}
#ifdef __STDC__
void
spool_set_sample_rate(SPOOL *sp, int sample_rate)
#else
void
spool_set_sample_rate(sp, sample_rate)
SPOOL *sp;
int sample_rate;
#endif
{
if (sp)
{
sp->sample_rate = sample_rate > 0 ? sample_rate : sp->sound[sp->curr_sound]->sample_rate;
sp->curr_attrs->sample_rate = sample_rate;
sp->sample_factor = (double) sp->sample_rate / (double) rplay_audio_sample_rate;
if (rplay_audio_match)
{
rplayd_audio_match(sp);
}
}
}
#ifdef __STDC__
void
spool_set_volume(SPOOL *sp, int volume)
#else
void
spool_set_volume(sp, volume)
SPOOL *sp;
int volume;
#endif
{
if (sp)
{
sp->curr_attrs->volume = volume;
}
}
#ifdef __STDC__
void
spool_set_client_data(SPOOL *sp, char *client_data)
#else
void
spool_set_client_data(sp, client_data)
SPOOL *sp;
char *client_data;
#endif
{
if (sp)
{
if (*sp->curr_attrs->client_data)
{
free((char *) sp->curr_attrs->client_data);
}
sp->curr_attrs->client_data = strdup(client_data);
}
}
#ifdef __STDC__
SPOOL *
spool_find_pid(int pid)
#else
SPOOL *
spool_find_pid(pid)
int pid;
#endif
{
SPOOL *sp;
#if defined(HAVE_CDROM) || defined(HAVE_HELPERS)
for (sp = spool; sp; sp = sp->next)
{
if (sp->si && sp->si->pid == pid)
{
return sp;
}
}
#endif
return NULL;
}