#include #include #include #include #include #include #include #include #include #include #include #include #include #include /* For inet_ntoa */ #include #include #include "../include/string.h" #include "ytypes.h" #include "soundpaths.h" #include "ysound.h" #include "audiocd.h" #include "ymixer.h" #include "ymode.h" #include "yconnection.h" #include "yhost.h" #include "playstack.h" #include "ynet.h" #include "yiff.h" #include "options.h" static u_int64_t htonl64(u_int64_t x); static void YNetPrintError( FILE *stream, YConnectionNumber con_num, u_int32_t chunk_length, u_int16_t major_op_code, u_int16_t minor_op_code, const char *mesg ); static int YNetSendToConnection( YConnectionNumber con_num, const char *buf, int len ); void YShutdownSocket(int socket, int how, time_t timeout); void YCloseSocketWait(int socket, time_t timeout); int YNetSendAudioChangePreset( YConnectionNumber con_num, const char *audio_mode_name ); int YNetSendAudioChangeValues( YConnectionNumber con_num, int sample_size, int channels, YDataLength sample_rate, int direction, int allow_fragmenting, int num_fragments, int fragment_size ); int YNetParseAudioChange(YCNP_STD_INPUTS_PROTO); int YNetSendServerStats( YConnectionNumber con_num, int protocol_version_major, int protocol_version_minor, Coefficient cycle_load, unsigned long start_time, const char *vendor_name, const char *server_name, const char *sound_name ); int YNetParseServerStats(YCNP_STD_INPUTS_PROTO); int YNetSendCycleChange(YConnectionNumber con_num, long cycle_us); int YNetParseCycleChange(YCNP_STD_INPUTS_PROTO); int YNetSendDisconnect(YConnectionNumber con_num, u_int16_t reason); int YNetParseDisconnect(YCNP_STD_INPUTS_PROTO); int YNetSendSetHost( YConnectionNumber con_num, u_int16_t minor_op_code, const YIPUnion *ip ); int YNetParseSetHost(YCNP_STD_INPUTS_PROTO); int YNetSendSetMixerDevice( YConnectionNumber con_num, int mixer_code, Coefficient value1, Coefficient value2, Coefficient value3, Coefficient value4 ); int YNetParseMixerDevice(YCNP_STD_INPUTS_PROTO); int YNetSendSoundObjectPlay( YConnectionNumber con_num, YID yid, YDataPosition position, YDataLength length, int repeats, int total_repeats, YVolumeStruct *volume, int sample_rate ); int YNetParseSoundObjectPlay(YCNP_STD_INPUTS_PROTO); int YNetSendSoundObjectKill(YConnectionNumber con_num, YID yid); int YNetParseSoundObjectKill(YCNP_STD_INPUTS_PROTO); int YNetSendSoundObjectAttributes( YConnectionNumber con_num, const char *path, int format, int sample_size, int channels, int sample_rate, YDataLength length ); int YNetParseSoundObjectAttributes(YCNP_STD_INPUTS_PROTO); int YNetSendShutdown(YConnectionNumber con_num, u_int16_t reason); int YNetParseShutdown(YCNP_STD_INPUTS_PROTO); int YNetSendSync(YConnectionNumber con_num, long us); int YNetParseSync(YCNP_STD_INPUTS_PROTO); int YNetSendAudioStats( YConnectionNumber con_num, Audio *audio ); int YNetParseAudioStats(YCNP_STD_INPUTS_PROTO); int YNetSendListAudioModes( YConnectionNumber con_num, const char *audio_mode_name, int sample_rate, int channels, int sample_size, int fragment_size_bytes, char direction, Boolean allow_fragmenting, int num_fragments ); int YNetParseListAudioModes(YCNP_STD_INPUTS_PROTO); int YNetSendPlaySoundObjectValues( YConnectionNumber con_num, PlayStack *ps_ptr ); int YNetParsePlaySoundObjectValues(YCNP_STD_INPUTS_PROTO); int YNetSendClientMessage( YConnectionNumber con_num, int format, int type, const char *message, YDataLength length ); int YNetParseClientMessage(YCNP_STD_INPUTS_PROTO); int YNetSendYSHMSoundOpen( YConnectionNumber con_num, int yshm_id ); int YNetSendYSHMSoundClose( YConnectionNumber con_num, int yshm_id ); int YNetParseYSHMSoundOpen(YCNP_STD_INPUTS_PROTO); void YNetDoNotifyAllYSHMSoundClose(void); int YNetSendAudioCDPlayTrack( YConnectionNumber con_num, int track_number ); int YNetSendAudioCDStop( YConnectionNumber con_num ); int YNetSendAudioCDEject( YConnectionNumber con_num ); int YNetParseAudioCD(YCNP_STD_INPUTS_PROTO); int YNetSendListAudioCDTracks( YConnectionNumber con_num, int track_number, unsigned long track_start_time, unsigned long track_length, const char *track_name ); int YNetParseListAudioCDTracks(YCNP_STD_INPUTS_PROTO); static int YNetParse( YConnectionNumber con_num, const u_int8_t *buf, u_int32_t chunk_length, u_int16_t major_op_code, u_int16_t minor_op_code ); int YNetRecv(YConnectionNumber con_num);; #define ATOI(s) (((s) != NULL) ? atoi(s) : 0) #define ATOL(s) (((s) != NULL) ? atol(s) : 0) #define ATOF(s) (((s) != NULL) ? atof(s) : 0.0f) #define STRDUP(s) (((s) != NULL) ? strdup(s) : NULL) #define MAX(a,b) (((a) > (b)) ? (a) : (b)) #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #define CLIP(a,l,h) (MIN(MAX((a),(l)),(h))) #define STRLEN(s) (((s) != NULL) ? strlen(s) : 0) #define STRISEMPTY(s) (((s) != NULL) ? (*(s) == '\0') : 1) extern int YAntiShift(int in); /* In main.c */ /* * Marks currently handled connection to be closed. */ static Boolean close_this_connection; static u_int64_t htonl64(u_int64_t x) { return(htonl(x)); #if 0 u_int8_t *px = (u_int8_t *)&x, *py; py[7] = px[0]; py[6] = px[1]; py[5] = px[2]; py[4] = px[3]; py[3] = px[4]; py[2] = px[5]; py[1] = px[6]; py[0] = px[7]; return(*(u_int64_t *)py); #endif } /* * Procedure to print network error message. */ static void YNetPrintError( FILE *stream, YConnectionNumber con_num, u_int32_t chunk_length, u_int16_t major_op_code, u_int16_t minor_op_code, const char *mesg ) { if(stream == NULL) return; fprintf( stream, "Y server protocol error from connection: %i\n", con_num ); fprintf( stream, " Major OP Code: %i\n", major_op_code ); fprintf( stream, " Minor OP Code: %i\n", minor_op_code ); fprintf( stream, " Segment Size: %i bytes\n", chunk_length ); if(mesg != NULL) fprintf( stream, " Remarks: %s\n", mesg ); } /* * Send buffer buf of length len to connection, inputs assued valid * and connection assumed connected. * * The connection's socket is checked to see if it's available for * sending data. Returns -1 if it was not able to send the buffer * or the number of bytes sent. */ static int YNetSendToConnection( YConnectionNumber con_num, const char *buf, int len ) { struct timeval timeout; fd_set writefds; int bytes_sent; YConnection *con = yconnection[con_num]; /* Assumed valid */ int s = con->socket; timeout.tv_sec = 0; timeout.tv_usec = 0; FD_ZERO(&writefds); FD_SET(s, &writefds); select(s + 1, NULL, &writefds, NULL, &timeout); if(!FD_ISSET(s, &writefds)) return(-1); bytes_sent = send(s, buf, len, 0); return((bytes_sent >= 0) ? bytes_sent : -1); } /* * Shuts down the socket. */ void YShutdownSocket(int socket, int how, time_t timeout) { if(socket < 0) return; shutdown(socket, how); } /* * Calls recv() until it returns 0 and then closes the socket. */ void YCloseSocketWait(int socket, time_t timeout) { char buf[1024]; if(socket < 0) return; while(recv( socket, buf, sizeof(buf), MSG_NOSIGNAL ) > 0); close(socket); } /* * Send Audio mode change. */ int YNetSendAudioChangePreset( YConnectionNumber con_num, const char *audio_mode_name ) { /* * <...modename...> */ const YDataLength this_seg_len = 8 + YAudioNameMax; YDataLength actual_seg_len; int name_len; char buf[this_seg_len]; if(audio_mode_name == NULL) return(-1); *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len); *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioChange); *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioChangePreset); name_len = MIN(strlen(audio_mode_name), YAudioNameMax); strncpy( &buf[8], audio_mode_name, name_len ); /* Do not null terminate string */ /* Since this is a variable length segment we need to * recalculate the segment length after the formatting * above and update the segment header's chunk size. */ actual_seg_len = 8 + name_len; *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len); return(YNetSendToConnection(con_num, buf, actual_seg_len)); } /* * Sends audio values change. */ int YNetSendAudioChangeValues( YConnectionNumber con_num, int sample_size, int channels, YDataLength sample_rate, int direction, int allow_fragmenting, int num_fragments, int fragment_size ) { /* * * * */ const YDataLength this_seg_len = 8 + 8 + 1 + 9; char buf[this_seg_len]; *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len); *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioChange); *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioChangeValues); *(u_int16_t *)(&buf[8]) = htons((u_int16_t)sample_size); *(u_int16_t *)(&buf[10]) = htons((u_int16_t)channels); *(u_int32_t *)(&buf[12]) = htonl((u_int32_t)sample_rate); *(u_int8_t *)(&buf[16]) = (u_int8_t)direction; *(u_int8_t *)(&buf[17]) = (u_int8_t)allow_fragmenting; *(u_int32_t *)(&buf[18]) = htonl((u_int32_t)num_fragments); *(u_int32_t *)(&buf[22]) = htonl((u_int32_t)fragment_size); return(YNetSendToConnection(con_num, buf, this_seg_len)); } /* * Handles Audio mode change. */ int YNetParseAudioChange(YCNP_STD_INPUTS_PROTO) { int i; YConnection *con; YMode *ymode_ptr; Audio *audio_ptr; /* Change mode values or change to preset mode? */ if(minor_op_code == YAudioChangeValues) { /* Parse set Audio values */ /* * * * */ const YDataLength this_seg_len = 8 + 8 + 1 + 9; int sample_size; int channels; YDataLength sample_rate; int direction; int allow_fragmenting; int num_fragments; int fragment_size_bytes; if(chunk_length < this_seg_len) return(-1); /* Get Audio values */ sample_size = CLIP( ntohs(*(u_int16_t *)(&buf[8])), 8, 16 ); channels = CLIP( ntohs(*(u_int16_t *)(&buf[10])), 1, 2 ); sample_rate = MAX( ntohl(*(u_int32_t *)(&buf[12])), 0 ); direction = (int)buf[16]; allow_fragmenting = (int)buf[17]; num_fragments = ntohl(*(u_int32_t *)(&buf[18])); fragment_size_bytes = ntohl(*(u_int32_t *)(&buf[22])); /* Special case: If the sample rate is 0, this implies * that the DSP device should be closed (shelled out) * to let other applications open it. */ if(sample_rate == 0) { /* Shell out the recorder */ YSoundShellOut(recorder); /* Send back shell out response */ YNetSendAudioChangeValues( con_num, sample_size, channels, 0, /* Sample rate should be 0 */ direction, allow_fragmenting, num_fragments, fragment_size_bytes ); /* Return now, do not delete playstacks or modify Audio */ return(0); } /* Delete all playstacks and notify their respective owners * about the deletion. */ for(i = 0; i < total_playstacks; i++) YiffDestroyPlaystack(i); /* Notify all connections about YSHM sound closing */ YNetDoNotifyAllYSHMSoundClose(); /* Shutdown and delete the recorder */ YSoundShutdown(recorder); free(recorder); recorder = NULL; /* Set new Audio values to option Audio values */ audio_ptr = &option.audio; audio_ptr->sample_size = sample_size; audio_ptr->channels = channels; audio_ptr->sample_rate = sample_rate; #ifdef OSS_BUFFRAG audio_ptr->allow_fragments = ((allow_fragmenting) ? True : False ); audio_ptr->num_fragments = num_fragments; audio_ptr->fragment_size = YAntiShift(fragment_size_bytes - 1); #endif /* OSS_BUFFRAG */ audio_ptr->direction = direction; /* Allocate recorder structure */ recorder = (Recorder *)calloc(1, sizeof(Recorder)); if(recorder == NULL) return(-1); /* Initialize recorder */ if(YSoundInit(recorder, &option.audio)) { /* Failed to initialize */ /* Shutdown and delete the recorder */ YSoundShutdown(recorder); free(recorder); recorder = NULL; /* Send back notification of initialization failure */ YNetSendAudioChangeValues( con_num, 0, 0, 0, 0, 0, 0, 0 ); return(-1); } /* Need to sync audio device right after * initialization. */ YSoundSync(recorder, 0); /* Notify all connections about Audio values change */ for(i = 0; i < total_yconnections; i++) { con = yconnection[i]; if((con != NULL) ? (con->socket < 0) : True) continue; YNetSendAudioChangeValues( i, sample_size, channels, sample_rate, direction, allow_fragmenting, num_fragments, fragment_size_bytes ); } } else { /* Parse change preset Audio mode */ /* * <...modename...> */ const YDataLength base_seg_len = 8; int name_len; int mode_num; int mode_name_different = 0; char mode_name[YAudioNameMax]; Audio *audio_ptr; if(chunk_length < base_seg_len) return(-1); name_len = (YDataLength)chunk_length - (YDataLength)base_seg_len; if(name_len >= YAudioNameMax) name_len = YAudioNameMax - 1; if(name_len > 0) { strncpy( mode_name, &buf[8], name_len ); mode_name[name_len] = '\0'; /* Check if the given Audio mode name is different from * the one specified in the Audio structure. */ if(recorder->audio.audio_mode_name != NULL) { if(strcasecmp( recorder->audio.audio_mode_name, mode_name )) mode_name_different = 1; } else { /* Last Audio mode name not recorded on Audio * structure, so assume its different. */ mode_name_different = 1; } /* Mode name should always be considered different if * Audio is currently shelled out. */ if(recorder->audio.shelled_out) mode_name_different = 1; /* Match preset Audio mode, skip if current Audio * mode matches. */ mode_num = YModeMatch(mode_name); if(YModeIsAllocated(mode_num) && mode_name_different ) { ymode_ptr = ymode[mode_num]; /* Delete all playstacks and notify their respective * owners about the deletion. */ for(i = 0; i < total_playstacks; i++) YiffDestroyPlaystack(i); /* Notify all connections about YSHM sound closing */ YNetDoNotifyAllYSHMSoundClose(); /* Shutdown and delete the recorder */ YSoundShutdown(recorder); free(recorder); recorder = NULL; /* Set option Audio values to new mode's values */ audio_ptr = &option.audio; /* Audio modes are always considered user set */ audio_ptr->cycle_set = CYCLE_SET_USER; audio_ptr->cycle.ms = ymode_ptr->cycle.ms; audio_ptr->cycle.us = ymode_ptr->cycle.us; /* Leave audio_ptr->compensated_cycle alone */ audio_ptr->write_ahead.ms = ymode_ptr->write_ahead.ms; audio_ptr->write_ahead.us = ymode_ptr->write_ahead.us; audio_ptr->sample_size = ymode_ptr->sample_size; audio_ptr->channels = ymode_ptr->channels; audio_ptr->sample_rate = ymode_ptr->sample_rate; #ifdef OSS_BUFFRAG audio_ptr->allow_fragments = ymode_ptr->allow_fragments; audio_ptr->num_fragments = ymode_ptr->num_fragments; audio_ptr->fragment_size = ymode_ptr->fragment_size; #endif /* OSS_BUFFRAG */ audio_ptr->flip_stereo = ymode_ptr->flip_stereo; audio_ptr->direction = ymode_ptr->direction; /* Allocate recorder structure */ recorder = (Recorder *)calloc(1, sizeof(Recorder)); if(recorder == NULL) return(-1); /* Initialize recorder */ if(YSoundInit(recorder, audio_ptr)) { /* Failed to initialize */ /* Shutdown and delete the recorder */ YSoundShutdown(recorder); free(recorder); recorder = NULL; /* Send back notification of initialization * failure. */ YNetSendAudioChangePreset(con_num, ""); return(-1); } /* Need to sync audio device right after * initialization. */ YSoundSync(recorder, 0); /* Record name of preset Audio mode used to * set this Audio. */ free(recorder->audio.audio_mode_name); recorder->audio.audio_mode_name = STRDUP( ymode_ptr->name ); /* Notify all connections of Audio mode change */ for(i = 0; i < total_yconnections; i++) { con = yconnection[i]; if((con != NULL) ? (con->socket < 0) : True) continue; YNetSendAudioChangePreset(i, mode_name); } } /* Audio mode the same? */ else if(!mode_name_different) { /* Send back fake success response */ YNetSendAudioChangePreset(con_num, mode_name); } else { /* No such preset Audio mode */ /* Notify connection of Audio mode change * failure (send back empty Audio mode name). */ YNetSendAudioChangePreset(con_num, ""); } } else { /* No mode name specified */ /* Notify connection of Audio mode change * failure (send back empty Audio mode name). */ YNetSendAudioChangePreset(con_num, ""); } } return(0); } /* * Sends server stats. */ int YNetSendServerStats( YConnectionNumber con_num, int protocol_version_major, int protocol_version_minor, Coefficient cycle_load, unsigned long start_time, const char *vendor_name, const char *server_name, const char *sound_name ) { /* * * * * <...vendor_name, server_name, sound_name...> */ const YDataLength this_seg_len = 8 + 8 + 2 + 8 + YVendorNameMax + 1 + YServerNameMax + 1 + YSoundNameMax + 1; YDataLength actual_seg_len; int len, names_len; const char *s; char *ts, buf[this_seg_len]; *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len); *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YServerStats); *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YServerStatsSet); *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)protocol_version_major); *(u_int32_t *)(&buf[12]) = htonl((u_int32_t)protocol_version_minor); *(u_int16_t *)(&buf[16]) = htons((u_int16_t)( cycle_load * (Coefficient)((u_int16_t)-1) )); *(u_int64_t *)(&buf[18]) = htonl64((u_int64_t)start_time); ts = (char *)&buf[26]; names_len = 0; s = (vendor_name != NULL) ? vendor_name : ""; len = MIN(strlen(s), YVendorNameMax); if(len > 0) strncpy(&ts[names_len], s, len); ts[names_len + len] = '\0'; names_len += len + 1; s = (server_name != NULL) ? server_name : ""; len = MIN(strlen(s), YServerNameMax); if(len > 0) strncpy(&ts[names_len], s, len); ts[names_len + len] = '\0'; names_len += len + 1; s = (sound_name != NULL) ? sound_name : ""; len = MIN(strlen(s), YSoundNameMax); if(len > 0) strncpy(&ts[names_len], s, len); ts[names_len + len] = '\0'; names_len += len + 1; /* Since this is a variable length segment we need to * recalculate the segment length after the formatting * above and update the segment header's chunk size. */ actual_seg_len = 8 + 8 + 2 + 8 + names_len; *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len); return(YNetSendToConnection(con_num, buf, actual_seg_len)); } /* * Parses server stats. */ int YNetParseServerStats(YCNP_STD_INPUTS_PROTO) { if(minor_op_code == YServerStatsGet) { /* Parse server stats get */ /* */ const YDataLength this_seg_len = 8; if(chunk_length < this_seg_len) return(-1); YNetSendServerStats( con_num, YPROTOCOL_VERSION_MAJOR, YPROTOCOL_VERSION_MINOR, ystats.cycle_load, ystats.start_time, PROG_VENDOR_NAME, "", recorder->audio.sound_name ); } else if(minor_op_code == YServerStatsSet) { /* Parse server stats set */ /* */ const YDataLength base_seg_len = 8; if(chunk_length < base_seg_len) return(-1); } return(0); } /* * Sends cycle change. */ int YNetSendCycleChange(YConnectionNumber con_num, long cycle_us) { /* * */ const YDataLength this_seg_len = 8 + 4; char buf[this_seg_len]; /* Most UNIXes requires cycles > than 100 us */ if(cycle_us < 100) cycle_us = 100; *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len); *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YCycleChange); *(u_int16_t *)(&buf[6]) = htons((u_int16_t)0); *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)cycle_us); return(YNetSendToConnection(con_num, buf, this_seg_len)); } /* * Handles cycle change. */ int YNetParseCycleChange(YCNP_STD_INPUTS_PROTO) { /* * */ const YDataLength this_seg_len = 8 + 4; long cycle_us; int i; YConnection *con; if(chunk_length < this_seg_len) return(-1); cycle_us = ntohl(*(u_int32_t *)(&buf[8])); /* Most UNIXes requires this > than 100 us */ if(cycle_us < 100) cycle_us = 100; /* Set new cycle */ if(recorder == NULL) { /* Recorder not available, send error response */ YNetSendCycleChange(con_num, 0); } else { /* Set new cycle */ if(recorder->audio.cycle_set == CYCLE_SET_USER) { recorder->audio.cycle.ms = cycle_us / 1000; recorder->audio.cycle.us = cycle_us % 1000; recorder->audio.compensated_cycle.ms = recorder->audio.cycle.ms; recorder->audio.compensated_cycle.us = recorder->audio.cycle.us; recorder->audio.write_ahead.ms = (long)(cycle_us * 1.5) / 1000; recorder->audio.write_ahead.us = (long)(cycle_us * 1.5) % 1000; option.audio.write_ahead.ms = recorder->audio.write_ahead.ms; option.audio.write_ahead.us = recorder->audio.write_ahead.us; /* Notify all connections of cycle change */ for(i = 0; i < total_yconnections; i++) { con = yconnection[i]; if((con != NULL) ? (con->socket < 0) : True) continue; YNetSendCycleChange(i, cycle_us); } } else { /* Program or hardware set cycle */ cycle_us = (recorder->audio.cycle.ms * 1000) + recorder->audio.cycle.us; YNetSendCycleChange(con_num, cycle_us); } } return(0); } /* * Sends disconnect. */ int YNetSendDisconnect(YConnectionNumber con_num, u_int16_t reason) { /* * */ const YDataLength this_seg_len = 8 + 2; char buf[this_seg_len]; *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len); *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YDisconnect); *(u_int16_t *)(&buf[6]) = htons((u_int16_t)0); *(u_int16_t *)(&buf[8]) = htons((u_int16_t)reason); return(YNetSendToConnection(con_num, buf, this_seg_len)); } /* * Handle disconnect. */ int YNetParseDisconnect(YCNP_STD_INPUTS_PROTO) { /* * */ const YDataLength this_seg_len = 8 + 2; u_int16_t reason; if(chunk_length < this_seg_len) return(-1); reason = ntohs(*(u_int16_t *)(&buf[8])); /* Mark currently handled connection number to be closed */ close_this_connection = True; return(0); } /* * Sends set host. */ int YNetSendSetHost( YConnectionNumber con_num, u_int16_t minor_op_code, const YIPUnion *ip ) { /* * */ const YDataLength this_seg_len = 8 + 4; char buf[this_seg_len]; *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len); *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSetHost); *(u_int16_t *)(&buf[6]) = htons((u_int16_t)minor_op_code); *(u_int8_t *)(&buf[8]) = ip->charaddr[0]; *(u_int8_t *)(&buf[9]) = ip->charaddr[1]; *(u_int8_t *)(&buf[10]) = ip->charaddr[2]; *(u_int8_t *)(&buf[11]) = ip->charaddr[3]; return(YNetSendToConnection(con_num, buf, this_seg_len)); } /* * Handles set host. */ int YNetParseSetHost(YCNP_STD_INPUTS_PROTO) { /* * */ const YDataLength this_seg_len = 8 + 4; YIPUnion ip; int status; if(chunk_length < this_seg_len) return(-1); ip.charaddr[0] = (u_int8_t)buf[8]; ip.charaddr[1] = (u_int8_t)buf[9]; ip.charaddr[2] = (u_int8_t)buf[10]; ip.charaddr[3] = (u_int8_t)buf[11]; switch(minor_op_code) { case YSetHostAdd: status = YHostAllocate(&ip); if(status < 0) fprintf(stderr, "Cannot add YHost %i.%i.%i.%i.\n", ip.charaddr[0], ip.charaddr[1], ip.charaddr[2], ip.charaddr[3] ); break; case YSetHostRemove: status = YHostDeleteByHost(&ip); if(status < 0) fprintf(stderr, "Cannot remove YHost %i.%i.%i.%i.\n", ip.charaddr[0], ip.charaddr[1], ip.charaddr[2], ip.charaddr[3] ); break; } /* Notify connection of host add or remove */ YNetSendSetHost( con_num, minor_op_code, &ip ); return(0); } /* * Sends a mixer device set. */ int YNetSendSetMixerDevice( YConnectionNumber con_num, int mixer_code, Coefficient value1, Coefficient value2, Coefficient value3, Coefficient value4 ) { /* * * * */ const YDataLength this_seg_len = 8 + 2 + 8 + 8; char buf[this_seg_len]; u_int32_t ival[4]; ival[0] = (u_int32_t)rint(value1 * (Coefficient)((u_int32_t)-1)); ival[1] = (u_int32_t)rint(value2 * (Coefficient)((u_int32_t)-1)); ival[2] = (u_int32_t)rint(value3 * (Coefficient)((u_int32_t)-1)); ival[3] = (u_int32_t)rint(value4 * (Coefficient)((u_int32_t)-1)); *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len); *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YMixerChannel); *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YMixerChannelSet); *(u_int16_t *)(&buf[8]) = htons((u_int16_t)mixer_code); *(u_int32_t *)(&buf[10]) = htonl((u_int32_t)ival[0]); *(u_int32_t *)(&buf[14]) = htonl((u_int32_t)ival[1]); *(u_int32_t *)(&buf[18]) = htonl((u_int32_t)ival[2]); *(u_int32_t *)(&buf[22]) = htonl((u_int32_t)ival[3]); return(YNetSendToConnection(con_num, buf, this_seg_len)); } /* * Handles set mixer device. */ int YNetParseMixerDevice(YCNP_STD_INPUTS_PROTO) { if(minor_op_code == YMixerChannelSet) { /* * * * */ const YDataLength this_seg_len = 8 + 2 + 8 + 8; int i, status, mixer_code; Coefficient value[YMixerValues]; YConnection *con; if(chunk_length < this_seg_len) return(-1); mixer_code = ntohs(*(u_int16_t *)(&buf[8])); if(YMixerValues > 0) value[0] = (Coefficient)ntohl(*(u_int32_t *)(&buf[10])) / (Coefficient)((u_int32_t)-1); if(YMixerValues > 1) value[1] = (Coefficient)ntohl(*(u_int32_t *)(&buf[14])) / (Coefficient)((u_int32_t)-1); if(YMixerValues > 2) value[2] = (Coefficient)ntohl(*(u_int32_t *)(&buf[18])) / (Coefficient)((u_int32_t)-1); if(YMixerValues > 3) value[3] = (Coefficient)ntohl(*(u_int32_t *)(&buf[22])) / (Coefficient)((u_int32_t)-1); /* Set mixer device */ status = -1; if(recorder != NULL) { status = YMixerSet(recorder, mixer_code, value); } if(status < 0) { /* Error setting mixer values, notify error */ YNetSendSetMixerDevice( con_num, mixer_code, 0.0, 0.0, 0.0, 0.0 ); } else { /* Notify all connections about mixer channel change */ for(i = 0; i < total_yconnections; i++) { con = yconnection[i]; if((con != NULL) ? (con->socket < 0) : True) continue; YNetSendSetMixerDevice( i, mixer_code, (YMixerValues > 0) ? value[0] : 0.0, (YMixerValues > 1) ? value[1] : 0.0, (YMixerValues > 2) ? value[2] : 0.0, (YMixerValues > 3) ? value[3] : 0.0 ); } } } else if(minor_op_code == YMixerChannelGet) { /* * */ const YDataLength this_seg_len = 8 + 2; int status, mixer_code; Coefficient value[YMixerValues]; if(chunk_length < this_seg_len) return(-1); mixer_code = ntohs(*(u_int16_t *)(&buf[8])); /* Get mixer channel device values */ status = -1; if(recorder != NULL) { status = YMixerGet( recorder, mixer_code, value ); } if(status < 0) { /* Error getting values, respond with 0 channel values */ YNetSendSetMixerDevice( con_num, mixer_code, 0.0, 0.0, 0.0, 0.0 ); } else { YNetSendSetMixerDevice( con_num, mixer_code, (YMixerValues > 0) ? value[0] : 0.0, (YMixerValues > 1) ? value[1] : 0.0, (YMixerValues > 2) ? value[2] : 0.0, (YMixerValues > 3) ? value[3] : 0.0 ); } } return(0); } /* * Sends sound play. */ int YNetSendSoundObjectPlay( YConnectionNumber con_num, YID yid, YDataPosition position, YDataLength length, int repeats, int total_repeats, YVolumeStruct *volume, int sample_rate ) { /* * * * * * * */ const YDataLength this_seg_len = 8 + 8 + 8 + 8 + 4 + 4 + 4; char buf[this_seg_len]; /* Set flags to match client side (see Y2/Ylib.h) */ unsigned long flags = (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7); *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len); *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSoundObjectPlay); *(u_int16_t *)(&buf[6]) = htons((u_int16_t)0); *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)flags); *(u_int32_t *)(&buf[12]) = htonl((u_int32_t)yid); *(u_int32_t *)(&buf[16]) = htonl((u_int32_t)position); *(u_int32_t *)(&buf[20]) = htonl((u_int32_t)length); *(u_int32_t *)(&buf[24]) = htonl((u_int32_t)repeats); *(u_int32_t *)(&buf[28]) = htonl((u_int32_t)total_repeats); *(u_int16_t *)(&buf[32]) = htons((u_int16_t)( (volume != NULL) ? volume->left : 0 )); *(u_int16_t *)(&buf[34]) = htons((u_int16_t)( (volume != NULL) ? volume->right : 0 )); *(u_int16_t *)(&buf[36]) = htons((u_int16_t)( (volume != NULL) ? volume->back_left : 0 )); *(u_int16_t *)(&buf[38]) = htons((u_int16_t)( (volume != NULL) ? volume->back_right : 0 )); *(u_int32_t *)(&buf[40]) = htonl((u_int32_t)sample_rate); return(YNetSendToConnection(con_num, buf, this_seg_len)); } /* * Handles sound play. */ int YNetParseSoundObjectPlay(YCNP_STD_INPUTS_PROTO) { /* * * * * * <...path...> */ /* const YDataLength this_seg_len = 8 + 8 + 4 + 4 + 8 + YPathMax; */ const YDataLength this_seg_len_base = 8 + 8 + 4 + 4 + 8; YID yid; YDataPosition pos; u_int16_t vol[4]; int sample_rate; int repeats; int path_len; char path[YPathMax]; YVolumeStruct volume; char *strptr; if(chunk_length < this_seg_len_base) return(-1); yid = ntohl(*(u_int32_t *)(&buf[8])); pos = ntohl(*(u_int32_t *)(&buf[12])); vol[0] = ntohs(*(u_int16_t *)(&buf[16])); vol[1] = ntohs(*(u_int16_t *)(&buf[18])); vol[2] = ntohs(*(u_int16_t *)(&buf[20])); vol[3] = ntohs(*(u_int16_t *)(&buf[22])); sample_rate = ntohl(*(u_int32_t *)(&buf[24])); repeats = ntohl(*(u_int32_t *)(&buf[28])); path_len = (int)chunk_length - this_seg_len_base; if(path_len >= YPathMax) path_len = YPathMax - 1; if(path_len > 0) { strncpy( path, &buf[32], path_len ); path[path_len] = '\0'; } else { /* If path is not specified, then do not continue */ YNetSendSoundObjectPlay( con_num, YIDNULL, 0, 0, 0, 0, &volume, 0 ); return(0); } /* Try to match correct complete path */ strptr = SoundPathCompletePath(path); if(strptr == NULL) { YNetSendSoundObjectPlay( con_num, YIDNULL, 0, 0, 0, 0, &volume, 0 ); return(0); } /* Do not start playing if recorder is not initialized */ if(recorder == NULL) { YNetSendSoundObjectPlay( con_num, YIDNULL, 0, 0, 0, 0, &volume, 0 ); return(0); } /* Create playstack */ volume.left = vol[0]; volume.right = vol[1]; volume.back_left = vol[2]; volume.back_right = vol[3]; YiffCreatePlaystack( strptr, /* Path */ con_num, /* Owner */ yid, /* YID */ pos, /* Position */ &volume, sample_rate, repeats /* 0 means repeat forever */ ); /* YiffCreatePlaystack() will send response */ return(0); } /* * Sends sound kill. */ int YNetSendSoundObjectKill(YConnectionNumber con_num, YID yid) { /* * */ const YDataLength this_seg_len = 8 + 4; char buf[this_seg_len]; *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len); *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSoundObjectKill); *(u_int16_t *)(&buf[6]) = htons((u_int16_t)0); *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)yid); return(YNetSendToConnection(con_num, buf, this_seg_len)); } /* * Handles sound kill. */ int YNetParseSoundObjectKill(YCNP_STD_INPUTS_PROTO) { /* * */ const YDataLength this_seg_len = 8 + 4; YID yid; int i; PlayStack *ps; if(chunk_length < this_seg_len) return(-1); yid = ntohl(*(u_int32_t *)(&buf[8])); /* Kill playstack */ for(i = 0; i < total_playstacks; i++) { ps = playstack[i]; if(ps == NULL) continue; if(ps->owner != con_num) continue; if((ps->yid == YIDNULL) || (ps->yid != yid)) continue; /* Delete playstack and notify owner connection about it */ YiffDestroyPlaystack(i); } return(0); } /* * Sends sound object attributes. */ int YNetSendSoundObjectAttributes( YConnectionNumber con_num, const char *path, int format, int sample_size, int channels, int sample_rate, YDataLength length ) { /* * * * * <...path...> */ const YDataLength this_seg_len = 8 + 2 + 8 + 4 + YPathMax; char buf[this_seg_len]; YDataLength path_len, actual_seg_len; if(path == NULL) return(-1); *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len); *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSoundObjectAttributes); *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YSoundObjectAttributesSet); *(u_int16_t *)(&buf[8]) = htons((u_int16_t)format); *(u_int16_t *)(&buf[10]) = htons((u_int16_t)sample_size); *(u_int16_t *)(&buf[12]) = htons((u_int16_t)channels); *(u_int32_t *)(&buf[14]) = htonl((u_int32_t)sample_rate); *(u_int32_t *)(&buf[18]) = htonl((u_int32_t)length); path_len = MIN(strlen(path), YPathMax); if(path_len > 0) strncpy( &buf[22], path, path_len ); /* Do not null terminate string */ /* Since this is a variable length segment we need to * recalculate the segment length after the formatting * above and update the segment header's chunk size. */ actual_seg_len = 8 + 2 + 8 + 4 + path_len; *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len); return(YNetSendToConnection(con_num, buf, actual_seg_len)); } /* * Handles sound object attributes. */ int YNetParseSoundObjectAttributes(YCNP_STD_INPUTS_PROTO) { if(minor_op_code == YSoundObjectAttributesGet) { /* * <...path...> */ const YDataLength base_seg_len = 8; /* const YDataLength this_seg_len = 8 + YPathMax; */ YDataLength path_len; char path[YPathMax]; char *strptr; AFWDataStruct af_data; if(chunk_length < base_seg_len) return(-1); path_len = (YDataLength)chunk_length - (YDataLength)base_seg_len; if(path_len >= YPathMax) path_len = YPathMax - 1; if(path_len > 0) { strncpy( path, (char *)(&buf[8]), path_len ); path[path_len] = '\0'; } else { *path = '\0'; } /* Try to match correct complete path */ strptr = SoundPathCompletePath(path); if(strptr == NULL) { YNetSendSoundObjectAttributes( con_num, "", SndObjTypeNone, 0, 0, 0, 0 ); return(0); } /* Open audio file.*/ if(AFWOpen(strptr, &af_data)) { /* Error opening audio file, so reset audio file values * to indicate error. */ *path = '\0'; af_data.sndobj_format = SndObjTypeNone; af_data.sample_size = 0; af_data.channels = 0; af_data.sample_rate = 0; af_data.entire_length = 0; } /* Send audio file data */ YNetSendSoundObjectAttributes( con_num, path, /* Send back original path */ af_data.sndobj_format, af_data.sample_size, af_data.channels, af_data.sample_rate, af_data.entire_length ); /* Close audio file */ AFWClose( &af_data, (recorder != NULL) ? &recorder->audio : NULL ); } else if(minor_op_code == YSoundObjectAttributesSet) { } return(0); } /* * Sends shutdown. */ int YNetSendShutdown(YConnectionNumber con_num, u_int16_t reason) { /* * */ const YDataLength this_seg_len = 8 + 2; char buf[this_seg_len]; *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len); *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YShutdown); *(u_int16_t *)(&buf[6]) = htons((u_int16_t)0); *(u_int16_t *)(&buf[8]) = htons((u_int16_t)reason); return(YNetSendToConnection(con_num, buf, this_seg_len)); } /* * Handles shutdown. */ int YNetParseShutdown(YCNP_STD_INPUTS_PROTO) { /* * */ const YDataLength this_seg_len = 8 + 2; u_int16_t reason; if(chunk_length < this_seg_len) return(-1); reason = ntohs(*(u_int16_t *)(&buf[8])); /* Check if connection has permission to shut server down? */ /* Set runlevel to 1 to shutdown gently. Comfermation * will be sent to all connections just before shutdown. */ runlevel = 1; return(0); } /* * Sends sync. */ int YNetSendSync(YConnectionNumber con_num, long us) { /* * */ const YDataLength this_seg_len = 8 + 4; char buf[this_seg_len]; u_int32_t ca; if(us < 0) us = 0; ca = us; *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len); *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSync); *(u_int16_t *)(&buf[6]) = htons((u_int16_t)0); *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)ca); return(YNetSendToConnection(con_num, buf, this_seg_len)); } /* * Handles sync. */ int YNetParseSync(YCNP_STD_INPUTS_PROTO) { /* * */ const YDataLength this_seg_len = 8 + 4; long cahead_us; if(chunk_length < this_seg_len) return(-1); cahead_us = ntohl(*(u_int32_t *)(&buf[8])); if(recorder != NULL) { /* Recaliberate cycle if program calculates it */ if(recorder->audio.cycle_set == CYCLE_SET_PROGRAM) YSoundCaliberateCycle(recorder); /* Set new write ahead */ if(cahead_us > 0) { recorder->audio.write_ahead.ms = cahead_us / 1000; recorder->audio.write_ahead.us = cahead_us % 1000; } /* Sync sound device */ YSoundSync(recorder, 0); } /* Send sync comfermation */ YNetSendSync(con_num, cahead_us); return(0); } /* * Sends set audio stats. */ int YNetSendAudioStats( YConnectionNumber con_num, Audio *audio ) { /* * * * * * * * * * <...audiomodename...> */ YDataLength actual_seg_len = 8 + 1 + 8 + 8 + 8 + 4 + 9 + 1 + 1; const YDataLength this_seg_len = 8 + 1 + 8 + 8 + 8 + 4 + 9 + 1 + 1 + YAudioNameMax; char buf[this_seg_len]; if(audio == NULL) { /* Send blank values (implying recorder not available) */ memset(buf, 0x00, this_seg_len * sizeof(char)); *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len); *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioStats); *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioStatsSet); } else { int name_len; const char *audio_mode_name = (audio->audio_mode_name != NULL) ? audio->audio_mode_name : ""; *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len); *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioStats); *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioStatsSet); *(u_int8_t *)(&buf[8]) = (u_int8_t)audio->cycle_set; *(u_int32_t *)(&buf[9]) = htonl((u_int32_t)( (audio->cycle.ms * 1000) + audio->cycle.us )); *(u_int32_t *)(&buf[13]) = htonl((u_int32_t)( (audio->compensated_cycle.ms * 1000) + audio->compensated_cycle.us )); *(u_int32_t *)(&buf[17]) = htonl((u_int32_t)( (audio->write_ahead.ms * 1000) + audio->write_ahead.us )); *(u_int32_t *)(&buf[21]) = htonl((u_int32_t)( (audio->cumulative_latency.ms * 1000) + audio->cumulative_latency.us )); *(u_int16_t *)(&buf[25]) = htons((u_int16_t)audio->sample_size); *(u_int16_t *)(&buf[27]) = htons((u_int16_t)audio->channels); *(u_int32_t *)(&buf[29]) = htonl((u_int32_t)audio->sample_rate); *(u_int32_t *)(&buf[33]) = htonl((u_int32_t)audio->bytes_per_second); #ifdef OSS_BUFFRAG *(u_int8_t *)(&buf[37]) = (u_int8_t)((audio->allow_fragments) ? 1 : 0); *(u_int32_t *)(&buf[38]) = htonl((u_int32_t)audio->num_fragments); *(u_int32_t *)(&buf[42]) = htonl((u_int32_t)( 1 << audio->fragment_size )); #endif /* OSS_BUFFRAG */ *(u_int8_t *)(&buf[46]) = (u_int8_t)((audio->flip_stereo) ? 1 : 0); *(u_int8_t *)(&buf[47]) = (u_int8_t)audio->direction; name_len = MIN(strlen(audio_mode_name), YAudioNameMax); strncpy(&buf[48], audio_mode_name, name_len); /* Do not null terminate string */ /* Since this is a variable length segment we need to * recalculate the segment length after the formatting * above and update the segment header's chunk size. */ actual_seg_len = 8 + 1 + 8 + 8 + 8 + 4 + 9 + 1 + 1 + name_len; *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len); } return(YNetSendToConnection(con_num, buf, actual_seg_len)); } /* * Parses audio stats. */ int YNetParseAudioStats(YCNP_STD_INPUTS_PROTO) { if(minor_op_code == YAudioStatsGet) { /* */ const YDataLength this_seg_len = 8; if(chunk_length < this_seg_len) return(-1); if(recorder == NULL) YNetSendAudioStats(con_num, NULL); else YNetSendAudioStats(con_num, &recorder->audio); } else if(minor_op_code == YAudioStatsSet) { } return(0); } /* * Send list audio modes, mode values. */ int YNetSendListAudioModes( YConnectionNumber con_num, const char *audio_mode_name, int sample_rate, int channels, int sample_size, int fragment_size_bytes, char direction, Boolean allow_fragmenting, int num_fragments ) { /* * * * * <...audiomodename...> */ const YDataLength this_seg_len = 8 + 8 + 8 + 6 + YAudioNameMax; char buf[this_seg_len]; YDataLength name_len, actual_seg_len; if(audio_mode_name == NULL) return(-1); *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len); *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YListAudioModes); *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YListAudioModesSet); *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)sample_rate); *(u_int32_t *)(&buf[12]) = htonl((u_int32_t)channels); *(u_int32_t *)(&buf[16]) = htonl((u_int32_t)sample_size); *(u_int32_t *)(&buf[20]) = htonl((u_int32_t)fragment_size_bytes); *(u_int8_t *)(&buf[24]) = (u_int8_t)direction; *(u_int8_t *)(&buf[25]) = (u_int8_t)((allow_fragmenting) ? 1 : 0); *(u_int32_t *)(&buf[26]) = htonl((u_int32_t)num_fragments); name_len = MIN(strlen(audio_mode_name), YAudioNameMax); strncpy( &buf[30], audio_mode_name, name_len ); /* Do not null terminate string */ /* Since this is a variable length segment we need to * recalculate the segment length after the formatting * above and update the segment header's chunk size. */ actual_seg_len = 8 + 8 + 8 + 6 + name_len; *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len); return(YNetSendToConnection(con_num, buf, actual_seg_len)); } /* * Parses list audio modes. */ int YNetParseListAudioModes(YCNP_STD_INPUTS_PROTO) { if(minor_op_code == YListAudioModesGet) { /* Parse list audio modes get */ /* */ const YDataLength this_seg_len = 8; int i; YMode *m; if(chunk_length < this_seg_len) return(-1); /* Send list of preset audio modes */ for(i = 0; i < total_ymodes; i++) { m = ymode[i]; if(m == NULL) continue; YNetSendListAudioModes( con_num, m->name, m->sample_rate, m->channels, m->sample_size, #ifdef OSS_BUFFRAG (1 << m->fragment_size), #else 1024, #endif m->direction, #ifdef OSS_BUFFRAG m->allow_fragments, m->num_fragments #else False, 1 #endif ); } /* End with an audio mode with no name (indicating end) */ YNetSendListAudioModes( con_num, "", 0, 0, 0, 0, 0, False, 0 ); } else if(minor_op_code == YListAudioModesSet) { } return(0); } /* * Sends play sound object values. */ int YNetSendPlaySoundObjectValues( YConnectionNumber con_num, PlayStack *ps_ptr ) { /* * * * * * * */ const YDataLength this_seg_len = 8 + 8 + 8 + 8 + 4 + 4 + 4; char buf[this_seg_len]; /* Set flags to match client side (see Y2/Ylib.h) */ unsigned long flags = (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7); /* If no playstack is given then send error */ if(ps_ptr == NULL) { *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len); *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSoundObjectPlayValues); *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YSoundObjectPlayValuesSet); *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)flags); *(u_int32_t *)(&buf[12]) = htonl((u_int32_t)YIDNULL); return(YNetSendToConnection(con_num, buf, this_seg_len)); } #define COEFFICIENT_TO_BUF16(x) (u_int16_t)( \ (Coefficient)(x) * (Coefficient)((u_int16_t)-1) \ ) *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len); *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSoundObjectPlayValues); *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YSoundObjectPlayValuesSet); *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)flags); *(u_int32_t *)(&buf[12]) = htonl((u_int32_t)ps_ptr->yid); *(u_int32_t *)(&buf[16]) = htonl((u_int32_t)ps_ptr->position); *(u_int32_t *)(&buf[20]) = htonl((u_int32_t)ps_ptr->data_length); *(u_int32_t *)(&buf[24]) = htonl((u_int32_t)ps_ptr->repeats); *(u_int32_t *)(&buf[28]) = htonl((u_int32_t)ps_ptr->total_repeats); *(u_int16_t *)(&buf[32]) = htons(COEFFICIENT_TO_BUF16( ps_ptr->volume_left )); *(u_int16_t *)(&buf[34]) = htons(COEFFICIENT_TO_BUF16( ps_ptr->volume_right )); *(u_int16_t *)(&buf[36]) = htons(COEFFICIENT_TO_BUF16( ps_ptr->volume_back_left )); *(u_int16_t *)(&buf[38]) = htons(COEFFICIENT_TO_BUF16( ps_ptr->volume_back_right )); *(u_int32_t *)(&buf[40]) = htonl((u_int32_t)ps_ptr->applied_sample_rate); #undef COEFFICIENT_TO_BUF16 return(YNetSendToConnection(con_num, buf, this_seg_len)); } /* * Parses play sound object values. */ int YNetParsePlaySoundObjectValues(YCNP_STD_INPUTS_PROTO) { if(minor_op_code == YSoundObjectPlayValuesGet) { /* Parse play sound object values get */ /* * */ const YDataLength this_seg_len = 8 + 4; Boolean response_sent = False; YID yid; int i; PlayStack *ps; if(chunk_length < this_seg_len) return(-1); yid = (YID)ntohl(*(u_int32_t *)(&buf[8])); /* Check if yid exists */ for(i = 0; i < total_playstacks; i++) { ps = playstack[i]; if(ps == NULL) continue; if((ps->owner != con_num) || (ps->yid == YIDNULL) ) continue; if(ps->yid == yid) { /* Send playing sound object values */ YNetSendPlaySoundObjectValues(con_num, ps); response_sent = True; break; } } if(!response_sent) YNetSendPlaySoundObjectValues(con_num, NULL); } else if(minor_op_code == YSoundObjectPlayValuesSet) { /* Parse play sound object values set */ /* * * * * * */ const YDataLength this_seg_len = 8 + 8 + 8 + 4 + 4 + 4; int i; u_int32_t flags; YID yid; u_int32_t position; u_int32_t total_repeats; u_int16_t vol[4]; u_int32_t sample_rate; PlayStack *ps; if(chunk_length < this_seg_len) return(-1); flags = ntohl(*(u_int32_t *)(&buf[8])); yid = ntohl(*(u_int32_t *)(&buf[12])); position = ntohl(*(u_int32_t *)(&buf[16])); total_repeats = ntohl(*(u_int32_t *)(&buf[20])); vol[0] = ntohs(*(u_int16_t *)(&buf[24])); vol[1] = ntohs(*(u_int16_t *)(&buf[26])); vol[2] = ntohs(*(u_int16_t *)(&buf[28])); vol[3] = ntohs(*(u_int16_t *)(&buf[30])); sample_rate = ntohl(*(u_int32_t *)(&buf[32])); /* Check if yid exists */ for(i = 0; i < total_playstacks; i++) { ps = playstack[i]; if(ps == NULL) continue; if((ps->owner != con_num) || (ps->yid == YIDNULL) ) continue; if(ps->yid == yid) { /* Set new values to play stack */ /* Change position? */ if(flags & (1 << 2)) ps->position = (YDataPosition)position; /* Change total repeats? */ if(flags & (1 << 5)) ps->total_repeats = (int)total_repeats; /* Change volume? */ if(flags & (1 << 6)) { ps->volume_left = (double)vol[0] / (double)((u_int16_t)-1); ps->volume_right = (double)vol[1] / (double)((u_int16_t)-1); ps->volume_back_left = (double)vol[2] / (double)((u_int16_t)-1); ps->volume_back_right = (double)vol[3] / (double)((u_int16_t)-1); } /* Change sample size? */ if(flags & (1 << 7)) ps->applied_sample_rate = (int)sample_rate; /* Send response of values set */ YNetSendPlaySoundObjectValues(con_num, ps); break; } } } return(0); } /* * Sends client message. */ int YNetSendClientMessage( YConnectionNumber con_num, int format, int type, const char *message, YDataLength length ) { /* * * <...message...> */ const YDataLength this_seg_len = 8 + 8 + YClientMessageMessageMax; YDataLength actual_seg_len = 8 + 8; char buf[this_seg_len]; *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len); *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YClientMessage); *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YClientMessageReceive); *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)format); *(u_int32_t *)(&buf[12]) = htonl((u_int32_t)type); if((length > 0) && (message != NULL)) { memcpy(&buf[16], message, length); actual_seg_len += length; } *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len); return(YNetSendToConnection(con_num, buf, actual_seg_len)); } /* * Parses client message. */ int YNetParseClientMessage(YCNP_STD_INPUTS_PROTO) { if(minor_op_code == YClientMessageSend) { /* Parse client message send */ /* * * */ const YDataLength base_seg_len = 8 + 8 + 1; YConnection *con; int i, format, type; Boolean notify_self; char message[YClientMessageMessageMax]; YDataLength message_length; if(chunk_length < base_seg_len) return(-1); format = ntohl(*(u_int32_t *)(&buf[8])); type = ntohl(*(u_int32_t *)(&buf[12])); notify_self = (*(u_int8_t *)(&buf[16])) ? True : False; *message = '\0'; message_length = MIN( chunk_length - base_seg_len, YClientMessageMessageMax );; if(message_length > 0) memcpy(message, &buf[17], message_length); /* Notify connections about client message */ for(i = 0; i < total_yconnections; i++) { con = yconnection[i]; if((con != NULL) ? (con->socket < 0) : True) continue; if((i == con_num) && !notify_self) continue; YNetSendClientMessage( i, format, type, message, message_length ); } } return(0); } /* * Sends YSHM sound open. */ int YNetSendYSHMSoundOpen( YConnectionNumber con_num, int yshm_id ) { /* * */ const YDataLength this_seg_len = 8 + 4; char buf[this_seg_len]; *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len); *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSHMSound); *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YSHMSoundOpen); *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)yshm_id); return(YNetSendToConnection(con_num, buf, this_seg_len)); return(0); } /* * Sends YSHM sound close. */ int YNetSendYSHMSoundClose( YConnectionNumber con_num, int yshm_id ) { /* * */ const YDataLength this_seg_len = 8 + 4; char buf[this_seg_len]; *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len); *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSHMSound); *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YSHMSoundClose); *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)yshm_id); return(YNetSendToConnection(con_num, buf, this_seg_len)); return(0); } /* * Parses YSHM sound open. */ int YNetParseYSHMSoundOpen(YCNP_STD_INPUTS_PROTO) { if(minor_op_code == YSHMSoundOpen) { /* */ const YDataLength this_seg_len = 8; int shm_id = -1; if(chunk_length < this_seg_len) return(-1); #ifdef YSHM_SUPPORT if(recorder != NULL) { Sound *sound_ptr = &recorder->sound; if(sound_ptr->buffer != NULL) shm_id = sound_ptr->buffer_shm_id; } #endif /* YSHM_SUPPORT */ YNetSendYSHMSoundOpen(con_num, shm_id); } else if(minor_op_code == YSHMSoundClose) { /* Client should never tell the server to do this */ } return(0); } /* * Notify all connections about YSHM sound buffer closing. */ void YNetDoNotifyAllYSHMSoundClose(void) { YConnectionNumber i; YConnection *con; int shm_id = -1; #ifdef YSHM_SUPPORT if(recorder != NULL) { Sound *sound_ptr = &recorder->sound; if(sound_ptr->buffer != NULL) shm_id = sound_ptr->buffer_shm_id; } #endif /* YSHM_SUPPORT */ for(i = 0; i < total_yconnections; i++) { con = yconnection[i]; if((con != NULL) ? (con->socket < 0) : True) continue; YNetSendYSHMSoundClose(i, shm_id); } } /* * Sends audio CD play track. */ int YNetSendAudioCDPlayTrack( YConnectionNumber con_num, int track_number ) { /* * */ const YDataLength this_seg_len = 8 + 4; char buf[this_seg_len]; *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len); *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioCD); *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioCDPlayTrack); *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)track_number); return(YNetSendToConnection(con_num, buf, this_seg_len)); } /* * Sends audio CD stop. */ int YNetSendAudioCDStop( YConnectionNumber con_num ) { /* */ const YDataLength this_seg_len = 8; char buf[this_seg_len]; *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len); *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioCD); *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioCDStop); return(YNetSendToConnection(con_num, buf, this_seg_len)); } /* * Sends audio CD eject. */ int YNetSendAudioCDEject( YConnectionNumber con_num ) { /* */ const YDataLength this_seg_len = 8; char buf[this_seg_len]; *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len); *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioCD); *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioCDEject); return(YNetSendToConnection(con_num, buf, this_seg_len)); } /* * Parses audio CD (play track, stop, and eject). */ int YNetParseAudioCD(YCNP_STD_INPUTS_PROTO) { if(minor_op_code == YAudioCDStatsGet) { } else if(minor_op_code == YAudioCDStatsSet) { } else if(minor_op_code == YAudioCDPlayTrack) { /* Parse audio CD play track */ /* * */ const YDataLength this_seg_len = 8 + 4; int track_number; if(chunk_length < this_seg_len) return(-1); track_number = ntohl(*(u_int32_t *)(&buf[8])); if(recorder != NULL) { int i; YConnection *con; Audio *audio_ptr = &recorder->audio; /* Play audio CD track */ AudioCDPlayTrack(audio_ptr->audiocd_context, track_number); /* Notify all connections about play audio CD track */ for(i = 0; i < total_yconnections; i++) { con = yconnection[i]; if((con != NULL) ? (con->socket < 0) : True) continue; YNetSendAudioCDPlayTrack(i, track_number); } } } else if(minor_op_code == YAudioCDStop) { /* Parse stop audio CD */ /* */ const YDataLength this_seg_len = 8; if(chunk_length < this_seg_len) return(-1); if(recorder != NULL) { int i; YConnection *con; Audio *audio_ptr = &recorder->audio; /* Stop audio CD */ AudioCDStop(audio_ptr->audiocd_context); /* Notify all connections about stop audio CD */ for(i = 0; i < total_yconnections; i++) { con = yconnection[i]; if((con != NULL) ? (con->socket < 0) : True) continue; YNetSendAudioCDStop(i); } } } else if(minor_op_code == YAudioCDEject) { /* Parse eject audio CD */ /* */ const YDataLength this_seg_len = 8; if(chunk_length < this_seg_len) return(-1); if(recorder != NULL) { int i; YConnection *con; Audio *audio_ptr = &recorder->audio; /* Eject audio CD */ AudioCDEject(audio_ptr->audiocd_context); /* Notify all connections about eject audio CD */ for(i = 0; i < total_yconnections; i++) { con = yconnection[i]; if((con != NULL) ? (con->socket < 0) : True) continue; YNetSendAudioCDEject(i); } } } return(0); } /* * Sends audio CD track. */ int YNetSendListAudioCDTracks( YConnectionNumber con_num, int track_number, unsigned long track_start_time, unsigned long track_length, const char *track_name ) { /* * * * * <...track_name...> */ const YDataLength this_seg_len = 8 + 4 + 8 + 8 + YAudioCDTrackNameMax; YDataLength actual_seg_len; int len; char buf[this_seg_len]; *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len); *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioCDTracksList); *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioCDTracksListSet); *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)track_number); *(u_int32_t *)(&buf[12]) = htonl((u_int64_t)track_start_time); *(u_int32_t *)(&buf[20]) = htonl((u_int64_t)track_length); len = (track_name != NULL) ? MIN(strlen(track_name), YAudioCDTrackNameMax - 1) : 0; if(len > 0) strncpy(&buf[28], track_name, len); /* Do not null terminate string */ /* Since this is a variable length segment we need to * recalculate the segment length after the formatting * above and update the segment header's chunk size. */ actual_seg_len = 8 + 4 + 8 + 8 + len; *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len); return(YNetSendToConnection(con_num, buf, actual_seg_len)); } /* * Parses audio CD tracks list. */ int YNetParseListAudioCDTracks(YCNP_STD_INPUTS_PROTO) { if(minor_op_code == YAudioCDTracksListGet) { /* Parse list audio CD tracks get */ /* */ const YDataLength this_seg_len = 8; if(chunk_length < this_seg_len) return(-1); if(recorder != NULL) { Audio *audio_ptr = &recorder->audio; int i, total; AudioCDTrack **track, *t; /* Get list of audio CD tracks */ track = AudioCDListTracks( audio_ptr->audiocd_context, &total ); for(i = 0; i < total; i++) { t = track[i]; if(t == NULL) continue; /* Send this audio CD track */ YNetSendListAudioCDTracks( con_num, t->number, t->start_time, t->length, t->name ); } AudioCDDeleteTracksList(track, total); } /* End the list by sending track 0 to indicate end */ YNetSendListAudioCDTracks(con_num, 0, 0, 0, NULL); } else if(minor_op_code == YAudioCDTracksListSet) { } return(0); } /* * Parses the data in buf received by the connection con_num. * * This function is called by YNetRecv(). */ static int YNetParse( YConnectionNumber con_num, const u_int8_t *buf, u_int32_t chunk_length, u_int16_t major_op_code, u_int16_t minor_op_code ) { int status; YConnection *con; /* Connection assumed valid */ con = yconnection[con_num]; switch(major_op_code) { case YAudioChange: status = YNetParseAudioChange(YCNP_STD_INPUTS); break; case YCycleChange: status = YNetParseCycleChange(YCNP_STD_INPUTS); break; case YDisconnect: status = YNetParseDisconnect(YCNP_STD_INPUTS); break; case YSetHost: status = YNetParseSetHost(YCNP_STD_INPUTS); break; case YListHosts: /* Work on this later */ break; case YMixerChannel: status = YNetParseMixerDevice(YCNP_STD_INPUTS); break; case YListMixers: /* Work on this later */ break; case YSoundObjectPlay: status = YNetParseSoundObjectPlay(YCNP_STD_INPUTS); break; case YSoundObjectKill: status = YNetParseSoundObjectKill(YCNP_STD_INPUTS); break; case YSoundObjectAttributes: status = YNetParseSoundObjectAttributes(YCNP_STD_INPUTS); break; case YShutdown: status = YNetParseShutdown(YCNP_STD_INPUTS); break; case YSync: status = YNetParseSync(YCNP_STD_INPUTS); break; case YAudioStats: status = YNetParseAudioStats(YCNP_STD_INPUTS); break; case YServerStats: status = YNetParseServerStats(YCNP_STD_INPUTS); break; case YListAudioModes: status = YNetParseListAudioModes(YCNP_STD_INPUTS); break; case YSoundObjectPlayValues: status = YNetParsePlaySoundObjectValues(YCNP_STD_INPUTS); break; case YClientMessage: status = YNetParseClientMessage(YCNP_STD_INPUTS); break; case YSHMSound: status = YNetParseYSHMSoundOpen(YCNP_STD_INPUTS); break; case YAudioCD: status = YNetParseAudioCD(YCNP_STD_INPUTS); break; case YAudioCDTracksList: status = YNetParseListAudioCDTracks(YCNP_STD_INPUTS); break; default: YNetPrintError( stderr, con_num, chunk_length, major_op_code, minor_op_code, "Unsupported Major Op Code" ); break; } return(0); } /* * Handles incoming data from connection con_num. */ int YNetRecv(YConnectionNumber con_num) { YConnection *con; int i, n, s, status, loops; struct timeval timeout; fd_set readfds; u_int8_t *buf_ptr; int bytes_read, segments_handled; u_int32_t chunk_length; u_int16_t major_op_code, minor_op_code; /* Reset close connection marker */ close_this_connection = False; /* Connection is assumed valid and connected */ con = yconnection[con_num]; /* Get connection's socket number */ s = con->socket; if(con->buf == NULL) return(0); if(con->buf_cont < 0) con->buf_cont = 0; if((con->buf_len - con->buf_cont) <= 0) { /* Contents buffer overflowed and thus we are unable to * parse the current buffer any more. So reset the * contents buffer and and disconnect connection since * there is no way to seek the next chunk position. */ con->buf_cont = 0; /* Disconnect and delete connection */ if(con->socket > -1) { YShutdownSocket(con->socket, SHUT_RDWR, 30l); YCloseSocketWait(con->socket, 30l); con->socket = -1; } YiffCloseConnection(con_num); fprintf( stderr, "YNetRecv(): Connection %i: Contents overflowed buffer length %ld.\n", con_num, con->buf_len ); return(0); } /* Do not block, check if there is any data to be read */ timeout.tv_sec = 0; timeout.tv_usec = 0; FD_ZERO(&readfds); FD_SET(s, &readfds); status = select(s + 1, &readfds, NULL, NULL, &timeout); if(status == -1) perror("select"); if(!FD_ISSET(s, &readfds)) return(0); buf_ptr = con->buf + con->buf_cont; bytes_read = recv( s, buf_ptr, con->buf_len - con->buf_cont, MSG_NOSIGNAL ); if(bytes_read == 0) { /* Disconnect and delete connection */ YShutdownSocket(s, SHUT_RDWR, 30l); close(s); con->socket = s = -1; YiffCloseConnection(con_num); return(0); } else if(bytes_read < 0) { /* Handle error */ const int error_code = errno; switch(error_code) { case EWOULDBLOCK: #if (EAGAIN != EWOULDBLOCK) case EAGAIN: #endif break; case EINTR: break; default: /* Disconnect and delete connection */ YShutdownSocket(s, SHUT_RDWR, 30l); YCloseSocketWait(s, 30l); con->socket = -1; YiffCloseConnection(con_num); break; } return(0); } /* Increment buffer contents */ con->buf_cont += bytes_read; if(con->buf_cont > con->buf_len) con->buf_cont = con->buf_len; segments_handled = 0; for(loops = 0; loops < 30; loops++) { /* Check if we got at least the first 8 bytes, needed * for chunk size, major op code, and minor op code. */ if(con->buf_cont < 8) break; /* All chunks start off with the same format, that being: * <4 bytes, chunk length> <2 bytes, major op code> * <2 bytes, minor op code> */ chunk_length = (u_int32_t)MAX( (long)ntohl(*((u_int32_t *)(&con->buf[0]))), (long)0 ); major_op_code = ntohs(*((u_int16_t *)(&con->buf[4]))); minor_op_code = ntohs(*((u_int16_t *)(&con->buf[6]))); /* Specified chunk length must be 8 or greater */ if(chunk_length < 8) { YNetPrintError( stderr, con_num, chunk_length, major_op_code, minor_op_code, "Recieved a segment with header specified chunk length less than 8 bytes" ); /* Disconnect and delete connection */ YShutdownSocket(con->socket, SHUT_RDWR, 30l); YCloseSocketWait(con->socket, 30l); con->socket = -1; YiffCloseConnection(con_num); return(0); } /* Did we get the entire chunk? Is the indicated chunk * length greater than the contents buffer length? */ if((YDataLength)chunk_length > (YDataLength)con->buf_cont) break; #if 0 { printf("Server: %i %i %i: Recieved incomplete.\n", chunk_length, major_op_code, minor_op_code ); break; } else printf("Server: %i %i | %i %i: Recieved.\n", chunk_length, con->buf_cont, major_op_code, minor_op_code ); #endif /* Parse this segement and put data into event structure */ YNetParse( con_num, con->buf, chunk_length, major_op_code, minor_op_code ); segments_handled++; /* Stop parsing and close this connection? This would * be said if the connection needed to be closed for some * reason determined in a higher call to YNetParse() * above */ if(close_this_connection) { /* Disconnect and delete connection */ YShutdownSocket(con->socket, SHUT_RDWR, 30l); YCloseSocketWait(con->socket, 30l); con->socket = -1; YiffCloseConnection(con_num); break; } /* Shift the buffer */ for(i = 0, n = chunk_length; n < con->buf_cont; i++, n++ ) con->buf[i] = con->buf[n]; /* Decrease buffer contents */ con->buf_cont = (YDataLength)con->buf_cont - (YDataLength)chunk_length; if(con->buf_cont < 0) con->buf_cont = 0; } /* Return number of segments handled which implies events * handled. */ return(segments_handled); }