summaryrefslogtreecommitdiff
path: root/players.c
diff options
context:
space:
mode:
Diffstat (limited to 'players.c')
-rw-r--r--players.c677
1 files changed, 677 insertions, 0 deletions
diff --git a/players.c b/players.c
new file mode 100644
index 0000000..73b9304
--- /dev/null
+++ b/players.c
@@ -0,0 +1,677 @@
+
+#include "esd-server.h"
+#include <errno.h>
+
+/*******************************************************************/
+/* globals */
+
+esd_player_t *esd_players_list = NULL;
+esd_player_t *esd_recorder_list = NULL;
+esd_player_t *esd_monitor_list = NULL;
+
+/*******************************************************************/
+/* for debugging purposes, dump the list of the clients and data */
+void dump_players(void)
+{
+ esd_player_t *player = esd_players_list;
+
+ if ( !esdbg_trace ) return;
+
+ while ( player != NULL ) {
+ printf( "-%02d- player: [%p]\n",
+ player->source_id, player );
+ player = player->next;
+ }
+ return;
+}
+
+/*******************************************************************/
+/* deallocate memory for the player */
+void free_player( esd_player_t *player )
+{
+ esd_sample_t *sample;
+
+ /* see if we need to do any housekeeping */
+ if ( ( player->format & ESD_MASK_MODE ) == ESD_STREAM ) {
+ /* TODO: erase client should be independent of players */
+ erase_client( player->parent );
+ } else if ( ( player->format & ESD_MASK_MODE ) == ESD_SAMPLE ) {
+ sample = (esd_sample_t *) (player->parent);
+ sample->ref_count--;
+ esd_playing_samples--;
+
+ ESDBG_TRACE( printf( "<%02d> free player: [%p] refs=%d erase?=%d samps=%d\n",
+ player->source_id, player, sample->ref_count,
+ sample->erase_when_done, esd_playing_samples ); );
+
+ if ( sample->erase_when_done && !sample->ref_count ) {
+ ESDBG_TRACE( printf( "<%02d> free_player: erasing sample\n",
+ sample->sample_id ); );
+
+ erase_sample( sample->sample_id, 0 );
+ }
+ }
+
+ /* free any memory allocated with the player */
+ free( player->data_buffer );
+
+ /* free the player memory itself */
+ free( player );
+
+ return;
+}
+
+/*******************************************************************/
+/* add a complete new player into the list of players at head */
+void add_player( esd_player_t *player )
+{
+ /* printf ( "adding player %p\n", new_player ); */
+ if ( !player ) {
+ ESDBG_TRACE( printf( "<NIL> can't add non-existent player!\n" ); );
+ return;
+ }
+
+ player->next = esd_players_list;
+ esd_players_list = player;
+
+ return;
+}
+
+/*******************************************************************/
+/* erase a player from the player list */
+void erase_player( esd_player_t *player )
+{
+ esd_player_t *previous = NULL;
+ esd_player_t *current = esd_players_list;
+
+ /* iterate until we hit a NULL */
+ while ( current != NULL )
+ {
+ /* see if we hit the target player */
+ if ( current == player ) {
+ if( previous != NULL ){
+ /* we are deleting in the middle of the list */
+ previous->next = current->next;
+ } else {
+ /* we are deleting the head of the list */
+ esd_players_list = current->next;
+ }
+
+ /* TODO: delete if needed */
+
+ free_player( player );
+
+ return;
+ }
+
+ /* iterate through the list */
+ previous = current;
+ current = current->next;
+ }
+
+ /* hmm, we didn't find the desired player, just get on with life */
+ ESDBG_TRACE( printf( "-%02d- player not found\n", player->source_id ); );
+ return;
+}
+
+
+/*******************************************************************/
+/* add a complete new recorder into the list of recorders at head */
+void add_recorder( esd_player_t *recorder )
+{
+ /* printf ( "adding player %p\n", new_player ); */
+ if ( !recorder ) {
+ ESDBG_TRACE( printf( "<NIL> can't add non-existent recorder!\n" ); );
+ return;
+ }
+
+ recorder->next = esd_recorder_list;
+ esd_recorder_list = recorder;
+
+ return;
+}
+
+/*******************************************************************/
+/* erase a recorder from the recorder list */
+void erase_recorder( esd_player_t *recorder )
+{
+ esd_player_t **prevp = &esd_recorder_list;
+ esd_player_t *current = esd_recorder_list;
+
+ /* iterate until we hit a NULL */
+ while ( current != NULL )
+ {
+ /* see if we hit the target player */
+ if ( current == recorder ) {
+ *prevp = current->next;
+
+ /* TODO: delete if needed */
+
+ free_player( recorder );
+
+ return;
+ }
+
+ /* iterate through the list */
+ prevp = &current->next;
+ current = current->next;
+ }
+
+ /* hmm, we didn't find the desired recorder, just get on with life */
+ ESDBG_TRACE( printf( "-%02d- recorder not found\n", recorder->source_id ); );
+ return;
+}
+
+
+/*******************************************************************/
+/* erase a monitor from the monitor list */
+void erase_monitor( esd_player_t *monitor )
+{
+ esd_player_t *previous = NULL;
+ esd_player_t *current = esd_monitor_list;
+
+ /* iterate until we hit a NULL */
+ while ( current != NULL )
+ {
+ /* see if we hit the target monitor */
+ if ( current == monitor ) {
+ if( previous != NULL ){
+ /* we are deleting in the middle of the list */
+ previous->next = current->next;
+ } else {
+ /* we are deleting the head of the list */
+ esd_monitor_list = current->next;
+ }
+
+ /* TODO: delete if needed */
+ free_player( monitor );
+
+ return;
+ }
+
+ /* iterate through the list */
+ previous = current;
+ current = current->next;
+ }
+
+ /* hmm, we didn't find the desired monitor, just get on with life */
+ ESDBG_TRACE( printf( "-%02d- monitor not found\n", monitor->source_id ); );
+ return;
+}
+
+
+/*******************************************************************/
+/* write block of data from client, return < 0 to have it erased */
+int write_player( esd_player_t *player, void *src_buffer, int src_length,
+ int src_rate, int src_format )
+{
+ fd_set wr_fds;
+ fd_set rd_fds;
+ int length = 0, actual = 0, ready = 0;
+ struct timeval timeout;
+ char message[ 100 ];
+ unsigned short data, *pos; /* used for swapping */
+ esd_client_t *client;
+ unsigned short *buffer;
+
+ /* use select to prevent blocking clients that are ready */
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+ FD_ZERO( &wr_fds );
+ FD_ZERO( &rd_fds );
+ FD_SET( player->source_id, &wr_fds );
+ /* EOF is only indicated in the read set */
+ FD_SET( player->source_id, &rd_fds );
+
+ /* if the data is ready, read a block */
+ ready = select( player->source_id + 1,
+ &rd_fds, &wr_fds, NULL, &timeout ) ;
+ if ( ready > 0 )
+ {
+ char ch;
+ if ( FD_ISSET(player->source_id, &rd_fds)
+ && (recv(player->source_id, &ch, 1, MSG_PEEK) < 1) ) {
+ /* Error or EOF */
+ return -1;
+ }
+
+ /* translate the data */
+ length = player->translate_func( player->data_buffer,
+ player->buffer_length,
+ player->rate,
+ player->format,
+ src_buffer,
+ src_length,
+ src_rate,
+ src_format );
+
+ /* endian swap multi-byte data if we need to */
+ client = (esd_client_t *) (player->parent);
+ if ( client->swap_byte_order
+ && ( (player->format & ESD_MASK_BITS) == ESD_BITS16 ) )
+ {
+ buffer = (unsigned short*) player->data_buffer;
+ for ( pos = buffer
+ ; pos < buffer + length / sizeof(unsigned short)
+ ; pos ++ )
+ {
+ data = swap_endian_16( (*pos) );
+ *pos = data;
+ }
+ }
+
+ /* write out the data */
+ ESD_WRITE_BIN( player->source_id, player->data_buffer,
+ player->buffer_length, length, "str rd" );
+
+ } else if ( ready < 0 ) {
+ sprintf( message, "error writing client (%d)\n",
+ player->source_id );
+ perror( message );
+ return -1;
+ }
+
+ /* check for end of stream */
+ if ( length < 0 ) return -1;
+ if ( length == 0 ) return 0;
+
+ return actual;
+}
+
+/*******************************************************************/
+/* read block of data from client, return < 0 to have it erased */
+int read_player( esd_player_t *player )
+{
+ fd_set rd_fds;
+ int actual = 0, actual_2nd = 0, can_read = 0;
+ struct timeval timeout;
+ char message[ 100 ];
+ unsigned short data, *pos; /* used for swapping */
+ esd_client_t *client;
+ unsigned short *buffer;
+
+ switch( player->format & ESD_MASK_MODE ) {
+ case ESD_STREAM:
+ /* use select to prevent blocking clients that are ready */
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+
+ FD_ZERO( &rd_fds );
+ FD_SET( player->source_id, &rd_fds );
+
+ /* if the data is ready, read a block */
+ can_read = (( player->actual_length < player->buffer_length ) &&
+ select( player->source_id + 1, &rd_fds, NULL, NULL, &timeout ));
+
+ if ( can_read > 0 )
+ {
+ ESD_READ_BIN( player->source_id,
+ player->data_buffer + player->actual_length,
+ player->buffer_length - player->actual_length,
+ actual, "str rd" );
+ /* check for end of stream */
+ if ( actual == 0
+ || ( actual < 0 && errno != EAGAIN && errno != EINTR ) )
+ return -1;
+
+ if (actual < player->buffer_length - player->actual_length)
+ break;
+
+ /* endian swap multi-byte data if we need to */
+ client = (esd_client_t *) (player->parent);
+ if ( client->swap_byte_order
+ && ( (player->format & ESD_MASK_BITS) == ESD_BITS16 ) )
+ {
+ buffer = (unsigned short*) player->data_buffer+player->actual_length;
+ for ( pos = buffer
+ ; pos < buffer + actual / sizeof(short)
+ ; pos ++ )
+ {
+ data = swap_endian_16( (*pos) );
+ *pos = data;
+ }
+ }
+
+ /* more data, save how much we got */
+ player->actual_length += actual;
+
+ } else if ( can_read < 0 ) {
+ sprintf( message, "error reading client (%d)\n",
+ player->source_id );
+ perror( message );
+ return -1;
+ }
+
+ break;
+
+ case ESD_SAMPLE:
+
+ /* printf( "player [%p], pos = %d, format = 0x%08x\n",
+ player, player->last_pos, player->format ); */
+
+ /* only keep going if we didn't want to stop looping */
+ if ( ( player->last_pos ) == 0 &&
+ ( ( ((esd_sample_t*)player->parent)->format & ESD_MASK_FUNC )
+ == ESD_STOP ) )
+ {
+ return -1;
+ }
+
+ /* copy the data from the sample to the player */
+ actual = ( ((esd_sample_t*)player->parent)->sample_length
+ - player->last_pos > player->buffer_length-player->actual_length )
+ ? player->buffer_length-player->actual_length
+ : ((esd_sample_t*)player->parent)->sample_length - player->last_pos;
+ if ( actual > 0 ) {
+ memcpy( player->data_buffer+player->actual_length,
+ ((esd_sample_t*)player->parent)->data_buffer
+ + player->last_pos, actual );
+ player->last_pos += actual;
+ player->actual_length += actual;
+ if ( player->actual_length == player->buffer_length || ( player->format & ESD_MASK_FUNC ) != ESD_LOOP ) {
+ /* we're done for this iteration */
+ break;
+ }
+ } else {
+ /* something horrible has happened to the sample */
+ return -1;
+ }
+
+ while ( player->actual_length < player->buffer_length ) {
+ /* we are looping, see if we need to copy another block */
+ if ( player->last_pos >= ((esd_sample_t*)player->parent)->sample_length ) {
+ player->last_pos = 0;
+
+ /* only keep going if we didn't want to stop looping */
+ if ( ( ((esd_sample_t*)player->parent)->format & ESD_MASK_FUNC )
+ == ESD_STOP )
+ break;
+ }
+
+ /* copy the data from the sample to the player */
+ actual_2nd = ( ((esd_sample_t*)player->parent)->sample_length
+ - player->last_pos > player->buffer_length-player->actual_length )
+ ? player->buffer_length-player->actual_length
+ : ((esd_sample_t*)player->parent)->sample_length - player->last_pos;
+ if ( actual_2nd > 0 ) {
+ memcpy( player->data_buffer+player->actual_length,
+ ((esd_sample_t*)player->parent)->data_buffer
+ + player->last_pos, actual_2nd );
+ player->last_pos += actual_2nd;
+ actual += actual_2nd;
+ player->actual_length += actual_2nd;
+
+ /* make sure we're not at the end */
+ if ( player->last_pos >= ((esd_sample_t*)player->parent)->sample_length ) {
+ player->last_pos = 0;
+ /* only keep going if we didn't want to stop looping */
+ if ( ( ((esd_sample_t*)player->parent)->format & ESD_MASK_FUNC )
+ == ESD_STOP )
+ break;
+ }
+ }
+ }
+
+ /* sample data is swapped as it's cached, no swap needed here */
+ break;
+
+ default:
+ ESDBG_TRACE( printf( "-%02d- read_player: format 0x%08x not supported\n",
+ player->source_id, player->format ); );
+ return -1;
+ }
+
+ return actual;
+}
+
+/*******************************************************************/
+/* send the players buffer to it's associated socket, erase if EOF */
+void monitor_write( void *output_buffer, int length ) {
+ fd_set wr_fds;
+ int can_write;
+ struct timeval timeout;
+ esd_player_t *monitor, *remove = NULL;
+
+ /* make sure we have a monitor connected */
+ if ( !esd_monitor_list )
+ return;
+
+ /* shuffle through the list of monitors */
+ monitor = esd_monitor_list;
+ while ( monitor != NULL ) {
+
+ /* see if we can write to the socket */
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+ FD_ZERO( &wr_fds );
+ FD_SET( monitor->source_id, &wr_fds );
+ can_write = select( monitor->source_id + 1, NULL, &wr_fds, NULL, &timeout );
+ if ( can_write > 0)
+ {
+
+ /* mix down the monitor's buffer */
+ length = monitor->translate_func( monitor->data_buffer,
+ monitor->buffer_length,
+ monitor->rate,
+ monitor->format,
+ output_buffer,
+ length,
+ esd_audio_rate,
+ esd_audio_format );
+
+ /* write the data buffer to the socket */
+ ESD_WRITE_BIN( monitor->source_id, monitor->data_buffer,
+ monitor->buffer_length, length, "mon wr" );
+
+ if ( length < 0 ) {
+ /* error on write, close it down */
+ ESDBG_TRACE( printf( "(%02d) closing monitor\n", monitor->source_id ); );
+ remove = monitor;
+ }
+ }
+
+ monitor = monitor->next;
+
+ if ( remove ) {
+ erase_client( remove->parent );
+ erase_monitor( remove );
+ remove = NULL;
+ }
+ }
+
+ return;
+}
+
+int recorder_write( void *buffer, int length ) {
+
+ esd_player_t *recorder, *remove = NULL;
+ int wrote_len = 0;
+
+ recorder = esd_recorder_list;
+
+ while (recorder != NULL) {
+ /* write it out */
+ ESDBG_TRACE( printf( "(%02d) writing recorder data\n",
+ recorder->source_id ); );
+ wrote_len = write_player(recorder, buffer, length,
+ esd_audio_rate, esd_audio_format);
+
+ /* see how it went */
+ if ( wrote_len < 0 ) {
+ /* couldn't send anything, close it down */
+ ESDBG_TRACE( printf( "(%02d) closing recorder\n",
+ recorder->source_id ); );
+
+ remove = recorder;
+ }
+
+ recorder = recorder->next;
+
+ if ( remove ) {
+ erase_client( remove->parent );
+ erase_recorder( remove );
+ remove = NULL;
+ }
+ }
+
+ if (!esd_recorder_list) {
+ /* stop recording */
+ esd_audio_close();
+ esd_audio_format &= ~ESD_RECORD;
+ esd_audio_open();
+ }
+
+ return wrote_len;
+}
+
+/*******************************************************************/
+/* allocate and initialize a player from client stream */
+esd_player_t *new_stream_player( esd_client_t *client )
+{
+ esd_player_t *player;
+
+ /* make sure we have the memory to save the client... */
+ player = (esd_player_t*) malloc( sizeof(esd_player_t) );
+ if ( player == NULL ) {
+ return NULL;
+ }
+
+ /* and initialize the player */
+ player->next = NULL;
+ player->parent = NULL;
+
+ player->format = *(int*)(client->proto_data);
+ player->rate = *(int*)(client->proto_data + sizeof(int));
+
+ player->format = maybe_swap_32( client->swap_byte_order, player->format );
+ player->format &= ~ESD_MASK_MODE; /* force to ESD_STREAM */
+ player->rate = maybe_swap_32( client->swap_byte_order, player->rate );
+
+ player->source_id = client->fd;
+ strncpy( player->name, client->proto_data + 2 * sizeof(int), ESD_NAME_MAX );
+ player->name[ ESD_NAME_MAX - 1 ] = '\0';
+
+ ESDBG_TRACE( printf( "(%02d) stream %s: 0x%08x at %d Hz\n", client ->fd,
+ player->name, player->format, player->rate ); );
+
+ /* Reduce buffers on sockets to the minimum needed */
+ esd_set_socket_buffers( player->source_id, player->format,
+ player->rate, esd_audio_rate );
+
+ /* calculate buffer length to match the mix buffer duration */
+ player->buffer_length = esd_buf_size_octets * player->rate / esd_audio_rate;
+ if ( (player->format & ESD_MASK_BITS) == ESD_BITS8 )
+ player->buffer_length /= 2;
+ if ( (player->format & ESD_MASK_CHAN) == ESD_MONO )
+ player->buffer_length /= 2;
+
+ /* force to an even multiple of 4 bytes (lower) */
+ player->buffer_length -= (player->buffer_length % 4);
+
+ player->actual_length = 0;
+ /* everything's ok, set the easy stuff */
+ player->left_vol_scale = player->right_vol_scale = ESD_VOLUME_BASE;
+
+ if ((player->mix_func = get_mix_func( player )) == NULL) {
+ free( player );
+ return NULL;
+ }
+
+ player->translate_func = NULL; /* no translating, just mixing */
+
+ player->data_buffer
+ = (void *) malloc( player->buffer_length );
+
+ /* if not enough room for data buffer, clean up, and return NULL */
+ if ( player->data_buffer == NULL ) {
+ free( player );
+ return NULL;
+ }
+
+ ESDBG_TRACE( printf( "(%02d) player: [%p]\n", player->source_id, player ); );
+
+ return player;
+}
+
+/*******************************************************************/
+/* allocate and initialize a player from client stream */
+esd_player_t *new_sample_player( int sample_id, int loop )
+{
+ esd_player_t *player;
+ esd_sample_t *sample;
+
+ /* find the sample we want to play */
+ for ( sample = esd_samples_list ; sample != NULL
+ ; sample = sample->next )
+ {
+ if ( sample->sample_id == sample_id ) {
+ break;
+ }
+ }
+ /* if we didn't find it, return NULL */
+ if ( sample == NULL ) {
+ return NULL;
+ }
+
+ /* make sure we have the memory to save the player... */
+ player = (esd_player_t*) malloc( sizeof(esd_player_t) );
+ if ( player == NULL ) {
+ return NULL;
+ }
+
+ /* and initialize the player */
+ player->next = NULL;
+ player->parent = sample;
+ player->format = sample->format & ~ESD_MASK_MODE;
+ player->format |= ESD_SAMPLE;
+ if ( loop ) {
+ player->format &= ~ESD_MASK_FUNC;
+ player->format |= ESD_LOOP;
+ }
+ player->rate = sample->rate;
+ player->source_id = sample->sample_id;
+ player->left_vol_scale = sample->left_vol_scale;
+ player->right_vol_scale = sample->right_vol_scale;
+
+ ESDBG_TRACE( printf( "<%02d> connection format: 0x%08x at %d Hz\n",
+ player->source_id, player->format, player->rate ); );
+
+ /* calculate buffer length to match the mix buffer duration */
+ player->buffer_length = esd_buf_size_octets * player->rate / esd_audio_rate;
+ if ( (player->format & ESD_MASK_BITS) == ESD_BITS8 )
+ player->buffer_length /= 2;
+ if ( (player->format & ESD_MASK_CHAN) == ESD_MONO )
+ player->buffer_length /= 2;
+
+ /* force to an even multiple of 4 */
+ player->buffer_length -= ( player->buffer_length % 4 );
+
+ if ((player->mix_func = get_mix_func( player )) == NULL) {
+ free( player );
+ return NULL;
+ }
+
+ player->translate_func = NULL; /* no translating, just mixing */
+
+ player->data_buffer
+ = (void *) malloc( player->buffer_length );
+
+ /* if not enough room for data buffer, clean up, and return NULL */
+ if ( player->data_buffer == NULL ) {
+ free( player );
+ return NULL;
+ }
+
+ /* update housekeeping values */
+ esd_playing_samples++;
+ player->last_pos = 0;
+ player->actual_length = 0;
+ sample->ref_count++;
+
+ ESDBG_TRACE( printf( "<%02d> new player: refs=%d samps=%d [%p]\n",
+ player->source_id, sample->ref_count,
+ esd_playing_samples, player ); );
+
+ /* everything's ok, return the allocated player */
+ return player;
+}