#include #include #include #include #include #include #include "../include/Y2/Y.h" #include "../include/Y2/Ylib.h" #include "../include/string.h" #include "../include/disk.h" static void handle_signal(int s); static void print_help(void); static Boolean interrupted; static Boolean verbose; static Boolean allow_change_mode; #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MAX(a,b) ((a) > (b) ? (a) : (b)) #define CLIP(a,l,h) (MIN(MAX((a),(l)),(h))) #define ABSOLUTE(x) (((x) < 0) ? ((x) * -1) : (x)) /* * Signal handler. */ static void handle_signal(int s) { switch(s) { #ifdef SIGINT case SIGINT: interrupted = True; break; #endif #ifdef SIGTERM case SIGTERM: interrupted = True; break; #endif #ifdef SIGPIPE case SIGPIPE: break; #endif #ifdef SIGHUP case SIGHUP: break; #endif #ifdef SIGCONT case SIGCONT: break; #endif } } /* * Prints help message. */ static void print_help(void) { printf("\ Usage: yplay [options] [file2] ...\n\ \n\ [options] can be any of the following:\n\ \n\ -m Allow change Audio mode as needed\n\ (may cause sound objects played by\n\ other applications to be killed).\n\ -s Applied sample rate in Hz.\n\ -r Repeat n times (-1 for infinate).\n\ -vol Specify volume coefficients.\n\ -v Print verbose status messages.\n\ --recorder Specify which Y server to connect to.\n\ \n\ [file2] ... are a list of files to be played. Their paths must\n\ be relative to the server, not your current directory on your computer.\n\ \n\ To interrupt the play at any time, press ctrl+c.\n\ \n\ Return values:\n\ \n\ 0 Success.\n\ 1 General error.\n\ 2 Unable to connect to the Y server.\n\ 3 Systems error.\n\ \n" ); } int main(int argc, char *argv[]) { int i; const char *arg_ptr; const char *con_arg = NULL; Boolean playing; long cycle; int total_repeats = 1; char **file = NULL; int total_files = 0; YConnection *con = NULL; YEvent event; YEventSoundObjectAttributes au_stats; YID play_id = YIDNULL; Coefficient vol_left = 1.0, vol_right = 1.0; int applied_sample_rate = 0; YEventSoundPlay value; char tmp_path[PATH_MAX + NAME_MAX]; /* Reset globals. */ interrupted = False; verbose = False; allow_change_mode = False; /* Set up signals to watch for. */ #ifdef SIGINT signal(SIGINT, handle_signal); #endif #ifdef SIGTERM signal(SIGTERM, handle_signal); #endif #ifdef SIGPIPE signal(SIGPIPE, handle_signal); #endif #ifdef SIGHUP signal(SIGHUP, handle_signal); #endif #ifdef SIGCONT signal(SIGCONT, handle_signal); #endif /* If there are not enough arguments given then print the help * message. */ if(argc < 2) { print_help(); return(1); } /* Parse arguments. */ for(i = 1; i < argc; i++) { arg_ptr = argv[i]; if(arg_ptr == NULL) continue; /* Help. */ if(strcasepfx(arg_ptr, "--h") || strcasepfx(arg_ptr, "-h") || !strcmp(arg_ptr, "?") ) { print_help(); return(0); } /* Connect address. */ else if(strcasepfx(arg_ptr, "--rec") || strcasepfx(arg_ptr, "-rec") ) { i++; arg_ptr = (i < argc) ? argv[i] : NULL; if(arg_ptr != NULL) { con_arg = arg_ptr; } else { fprintf( stderr, "%s: Requires argument.\n", argv[i - 1] ); continue; } } /* Repeat. */ else if(!strcasecmp(arg_ptr, "-r")) { i++; arg_ptr = (i < argc) ? argv[i] : NULL; if(arg_ptr != NULL) { total_repeats = atoi(arg_ptr); if(total_repeats == 0) fprintf( stderr, "%s: Warning: Value must be non-zero.\n", argv[i - 1] ); } else { fprintf( stderr, "%s: Requires argument.\n", argv[i - 1] ); continue; } } /* Change mode. */ else if(!strcasecmp(arg_ptr, "-m")) { allow_change_mode = True; } /* Volume. */ else if(!strcasecmp(argv[i], "-vol")) { i++; arg_ptr = (i < argc) ? argv[i] : NULL; if(arg_ptr != NULL) { vol_left = atof(arg_ptr); } else { fprintf( stderr, "%s: Requires at least 2 arguments.\n", argv[i - 1] ); continue; } i++; arg_ptr = (i < argc) ? argv[i] : NULL; if(arg_ptr != NULL) { vol_right = atof(arg_ptr); } else { fprintf( stderr, "%s: Requires at least 2 arguments.\n", argv[i - 1] ); continue; } } /* Applied sample rate. */ else if(!strcasecmp(arg_ptr, "-s")) { i++; arg_ptr = (i < argc) ? argv[i] : NULL; if(arg_ptr != NULL) { applied_sample_rate = MAX(atoi(arg_ptr), 0); } else { fprintf( stderr, "%s: Requires argument.\n", argv[i - 1] ); continue; } } /* Verbose. */ else if(!strcasecmp(arg_ptr, "-v")) { verbose = True; } /* All else assume argument to be a file. */ else if((*arg_ptr != '\0') && (*arg_ptr != '-') && (*arg_ptr != '+') ) { int n = total_files; total_files = n + 1; file = (char **)realloc( file, total_files * sizeof(char *) ); if(file == NULL) { total_files = 0; continue; } else { file[n] = strdup(arg_ptr); } } } /* Connect to Y server. */ con = YOpenConnection( NULL, /* No start argument. */ con_arg ); if(con == NULL) { fprintf(stderr, "Unable to connect to the Y server"); if(con_arg == NULL) con_arg = getenv("RECORDER"); if(con_arg == NULL) fprintf(stderr, ".\n"); else fprintf(stderr, ": %s\n", con_arg); /* Free list of files to be played. */ strlistfree(file, total_files); file = NULL; total_files = 0; return(2); } /* Begin playing. */ if(True) { /* Warn if repeating infinatly. */ if(total_repeats < 0) printf( "Infinatly repeating, press ctrl+c or send SIGINT to stop play.\n" ); /* Go through each file. */ for(i = 0; i < total_files; i++) { if(file[i] == NULL) continue; /* Get sound object attributes. */ if(YGetSoundObjectAttributes( con, file[i], &au_stats )) { fprintf( stderr, "%s: Unable to get object attributes.\n", file[i] ); fprintf( stderr, "Check path notation and object's format.\n" ); continue; } /* Change audio mode as needed. */ if((au_stats.format == SndObjTypeDSP) && allow_change_mode ) { /* Get listing of Audio modes available. */ int mc, mt; YAudioModeValuesStruct **m, *mp, *matched_mode = NULL; m = YGetAudioModes(con, &mt); for(mc = 0; mc < mt; mc++) { mp = m[mc]; if(mp == NULL) continue; if((mp->sample_rate == au_stats.sample_rate) && (mp->channels == au_stats.channels) && (mp->sample_size == au_stats.sample_size) ) { matched_mode = mp; break; } } if(matched_mode == NULL) { /* No Audio mode matched. */ cycle = YCalculateCycle( con, au_stats.sample_rate, /* Sample rate. */ au_stats.channels, /* Channels. */ au_stats.sample_size, /* Bits. */ 4096 /* Fragment size. */ ); if(cycle < 100) cycle = 100; YSetAudioModeValues( con, au_stats.sample_size, /* Bits. */ au_stats.channels, /* Channels. */ au_stats.sample_rate, /* Sample rate. */ 0, /* Play/record. */ 1, /* Allow fragmenting. */ 2, /* Num fragments. */ 4096 ); YSyncAll(con, True); YSetCycle(con, cycle); } else { /* Matched mode. */ YChangeAudioModePreset(con, matched_mode->name); } YFreeAudioModesList(m, mt); } /* Begin playing this sound object. */ value.flags = YPlayValuesFlagPosition | YPlayValuesFlagTotalRepeats | YPlayValuesFlagVolume | YPlayValuesFlagSampleRate; value.position = 0; value.total_repeats = total_repeats; value.left_volume = vol_left; value.right_volume = vol_right; value.sample_rate = applied_sample_rate; play_id = YStartPlaySoundObject(con, file[i], &value); if(play_id == YIDNULL) { fprintf( stderr, "%s: Unable to play.\n", file[i] ); continue; } /* Print info about this sound object as needed. */ if(verbose) { strncpy( tmp_path, file[i], sizeof(tmp_path) ); tmp_path[sizeof(tmp_path) - 1] = '\0'; StringShortenFL(tmp_path, 24); switch(au_stats.format) { case SndObjTypeDSP: printf( "ID: %ld Type: DSP SmpRate: %i Hz Bits: %i Ch: %i %s\n", play_id, au_stats.sample_rate, au_stats.sample_size, au_stats.channels, tmp_path ); break; case SndObjTypeMIDI: printf( "ID: %ld Type: MIDI %s\n", play_id, tmp_path ); break; default: printf( "ID: %ld Type: *Unknown* %s\n", play_id, tmp_path ); } } /* Wait untill audio is done playing. */ playing = True; while(playing && !interrupted) { if(YGetNextEvent( con, &event, False /* Nonblocking. */ ) > 0) { switch(event.type) { case YSoundObjectKill: if(event.kill.yid == play_id) { /* Current sound object no longer playing. */ playing = False; } else { /* Some other YID has stopped playing, * notify about this abnormality. */ fprintf( stderr, "Warning: Unmanaged play ID %ld has stopped.\n", event.kill.yid ); } break; case YDisconnect: case YShutdown: /* Free list of files to be played. */ strlistfree(file, total_files); file = NULL; total_files = 0; /* Consider disconnect/shutdown as an * interrupt. */ interrupted = True; /* Current sound object no longer playing. */ playing = False; /* Connection structure needs to be closed. */ YCloseConnection(con, False); con = NULL; break; } } usleep(8000); } /* Wait untill audio is done playing. */ /* If interupted during play, then stop playing. */ if(interrupted) { if(verbose) printf("Playback interrupted.\n"); break; } } } /* Disconnect from Y server. */ YCloseConnection(con, False); con = NULL; /* Free list of files to be played. */ strlistfree(file, total_files); file = NULL; total_files = 0; return(0); }