#include "esd-server.h" #include /*******************************************************************/ /* 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( " 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( " 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 = ¤t->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; }