/* * audsav_dlg.c * * Code for the audio save control dialog * * (C) 1997 Randall Hopper * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* ******************** Include Files ************** */ #include #include #include #include #include #include #include #include #include #include "voxware.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tvdefines.h" #include "glob.h" #include "actions.h" #include "xutil.h" #include "tvutil.h" #include "audsav_dlg.h" #include "app_rsrc.h" /* ******************** Local defines ************** */ #define RDWR_TIME 0.5 /* Seconds */ #define MAX_BYTES_PER_SEC 44100*2*2 #define MAX_RDWR_BUF_SIZE (((long)(MAX_BYTES_PER_SEC * RDWR_TIME))+3)/4*4 #define REC_CMD_MPEG2_RSRC "Fxtv.recordCmd.mpeg2" #define REC_CMD_MPEG3_RSRC "Fxtv.recordCmd.mpeg3" #define DO_IOCTL_SERR(str,arg) fprintf(stderr, "ioctl(%s, %ld) failed: %s\n",\ str, (long)arg, strerror(errno) ) typedef struct { TV_SOUND_FMT fmt_out; char **cmd; /* Cmd running and its args */ char fname_in [ MAXPATHLEN ], /* Input filename */ fname_out[ MAXPATHLEN ]; /* Output filename */ char *tmpstr; Widget dialog_shell; TV_BOOL mute_on; } TV_AUDIO_CMD_STATE; /* ******************** Private variables ************** */ static TV_AUDIO_FILE_FMT Sel_ffmt; static TV_AUDIO_SAMPLE_FMT Sel_sfmt; static TV_BOOL Sel_stereo; static TV_UINT32 Sel_rate; static Widget Dialog_wgt = NULL, Text_wgt = NULL, Ffmt_menu_btn = NULL, Sfmt_menu_btn = NULL, Chan_menu_btn = NULL, Rate_menu_btn = NULL, Record_btn = NULL, Stop_btn = NULL, Playback_btn = NULL, Dismiss_btn = NULL; static Boolean Recording = False, Playing = False; static TV_FFMT_ITEM_DEF Ffmt_item_def[] = { { TV_AUDIO_FILE_FMT_RAW , "raw" , NULL }, { TV_AUDIO_FILE_FMT_SUNAU , "au" , "-t raw -U -b -c 1 -r 8012" }, { TV_AUDIO_FILE_FMT_WAV , "wav" , "-t wav" }, { TV_AUDIO_FILE_FMT_VOC , "voc" , "-t voc" }, { TV_AUDIO_FILE_FMT_AIFF , "aiff" , "-t aiff" }, { TV_AUDIO_FILE_FMT_MPEG2 , "mp2" , "-t aiff" }, /* precond */ { TV_AUDIO_FILE_FMT_MPEG3 , "mp3" , "-t aiff" }, /* precond */ }; static TV_SFMT_ITEM_DEF Sfmt_item_def[] = { { TV_AUDIO_SAMPLE_FMT_MULAW_U8 , "mulawU8" , "-t raw -U -b" }, { TV_AUDIO_SAMPLE_FMT_LIN_S8 , "linS8" , "-t raw -s -b" }, { TV_AUDIO_SAMPLE_FMT_LIN_U8 , "linU8" , "-t raw -u -b" }, { TV_AUDIO_SAMPLE_FMT_LIN_S16_LE, "linS16LE" , "-t raw -s -w" }, { TV_AUDIO_SAMPLE_FMT_LIN_U16_LE, "linU16LE" , "-t raw -u -w" }, { TV_AUDIO_SAMPLE_FMT_LIN_S16_BE, "linS16BE" , "-t raw -s -w -x" }, { TV_AUDIO_SAMPLE_FMT_LIN_U16_BE, "linU16BE" , "-t raw -u -w -x" } }; static TV_CHAN_ITEM_DEF Chan_item_def[] = { { FALSE , "mono" }, { TRUE , "stereo" } }; static TV_RATE_ITEM_DEF Rate_item_def[] = { { 8012 , "r8012" }, { 11025 , "r11025" }, { 22050 , "r22050" }, { 44100 , "r44100" }, }; /* ******************** Forward declarations ************** */ /* ******************** Function Definitions ************** */ /* SetMenuSelection: Set the active selection of the option menus */ static void SetMenuSelection( Widget menu_btn, TV_UINT32 choice ) { TV_INT32 i; String label; if ( menu_btn == Ffmt_menu_btn ) { for ( i = 0; i < XtNumber( Ffmt_item_def ); i++ ) if ( Ffmt_item_def[i].fmt == choice ) { XtVaGetValues( Ffmt_item_def[i].wgt, XtNlabel, &label, NULL ); XtVaSetValues( menu_btn, XtNlabel, label, NULL ); break; } if ( i >= XtNumber( Ffmt_item_def ) ) { fprintf( stderr, "TVAUDSAVDIALOGSetSel: Unsupported filefmt %ld\n", choice ); exit(1); } Sel_ffmt = choice; } else if ( menu_btn == Sfmt_menu_btn ) { for ( i = 0; i < XtNumber( Sfmt_item_def ); i++ ) if ( Sfmt_item_def[i].fmt == choice ) { XtVaGetValues( Sfmt_item_def[i].wgt, XtNlabel, &label, NULL ); XtVaSetValues( menu_btn, XtNlabel, label, NULL ); break; } if ( i >= XtNumber( Sfmt_item_def ) ) { fprintf( stderr, "TVAUDSAVDIALOGSetSel: Unsupported sampfmt %ld\n", choice ); exit(1); } Sel_sfmt = choice; } else if ( menu_btn == Chan_menu_btn ) { for ( i = 0; i < XtNumber( Chan_item_def ); i++ ) if ( Chan_item_def[i].stereo == choice ) { XtVaGetValues( Chan_item_def[i].wgt, XtNlabel, &label, NULL ); XtVaSetValues( menu_btn, XtNlabel, label, NULL ); break; } if ( i >= XtNumber( Chan_item_def ) ) { fprintf( stderr, "TVAUDSAVDIALOGSetSel: Unsupported #chan %ld\n", choice ); exit(1); } Sel_stereo = choice; } else if ( menu_btn == Rate_menu_btn ) { for ( i = 0; i < XtNumber( Rate_item_def ); i++ ) if ( Rate_item_def[i].rate == choice ) { XtVaGetValues( Rate_item_def[i].wgt, XtNlabel, &label, NULL ); XtVaSetValues( menu_btn, XtNlabel, label, NULL ); break; } if ( i >= XtNumber( Rate_item_def ) ) { fprintf( stderr, "TVAUDSAVDIALOGSetSel: Unsupported rate %ld\n", choice ); exit(1); } Sel_rate = choice; } else { fprintf( stderr, "TVAUDSAV:SetMenuSelection: Bad menu_btn\n" ); exit(1); } } /* UpdateButtons - Enable/disable btns based on state */ static void UpdateButtons() { TV_BOOL rec, stop, play, dismiss; if ( Recording || Playing ) stop = True , rec = play = dismiss = False; else stop = False, rec = play = dismiss = True; XtSetSensitive( Record_btn , rec ); XtSetSensitive( Stop_btn , stop ); XtSetSensitive( Playback_btn, dismiss ); XtSetSensitive( Dismiss_btn , dismiss ); } /* PrepareForAudio - Take dialog values, save them in globals, open */ /* the audio device and setup its play/record parameters. */ static TV_BOOL PrepareForAudio( TV_BOOL recording, int buf_subdiv, int *dsp_fd, TV_INT32 *bps ) { TV_DISK *d = &G_glob.disk; TV_BOOL error = False; String filename; TV_SOUND snd; char *err_msg; *dsp_fd = -1; /* ...Filename base */ XtVaGetValues( Text_wgt, XtNstring, &filename, NULL ); if ( filename == NULL ) filename = ""; if ( strlen( filename ) == 0 ) { XUTILDialogPause( TVTOPLEVEL, "Error", "No filename specified.", TV_DIALOG_TYPE_OK ); error = True; goto RETURN; } /* Save off filename */ d->fn_audio_base[0] = '\0'; strncat( d->fn_audio_base, filename, sizeof( d->fn_audio_base ) - 1 ); /* Gather audio capture params */ snd.sample_fmt = Sel_sfmt, snd.stereo = Sel_stereo, snd.sample_rate = Sel_rate, snd.buf = NULL, snd.bytes = 0; /* FIXME: If AU file fmt selected, validate capture fmt */ /* Open & configure DSP device */ if ( !TVAUDIOOpenDsp( &snd, recording, dsp_fd, &err_msg ) ) { XUTILDialogPause( TVTOPLEVEL, "Error", err_msg, TV_DIALOG_TYPE_OK ); *dsp_fd = -1; error = True; goto RETURN; } *bps = TVAUDIOGetSampleFmtBps( snd.sample_fmt ); /* Ok, these record values are good, so save 'em off for the next time */ d->audio.sample_fmt = Sel_sfmt; d->audio.stereo = Sel_stereo; d->audio.sample_rate = Sel_rate; RETURN: /* Done recording, close up shop */ if ( error && ( *dsp_fd >= 0 )) { close( *dsp_fd ); *dsp_fd = -1; } return !error; } /* DoCmdFailDialog - Display a dialog citing the command that failed */ /* and its exit status; wait on user to dismiss. */ static void DoCmdFailDialog( char *cmd[], int status ) { char msg[ 2*MAXPATHLEN + 160 ]; TV_INT32 i; sprintf( msg, "Audio conversion failed.\nCMD = " ); for ( i = 0; cmd[i] != NULL; i++ ) sprintf( msg+strlen(msg), "%s ", cmd[i] ); sprintf( msg+strlen(msg), "\nSTATUS = 0x%.4x", status ); XUTILDialogPause( TVTOPLEVEL, "Error", msg, TV_DIALOG_TYPE_OK ); } /* RecordCmdCancelTestCB */ /* - Used by RecordCmd invocations of XUTILRunCmdAllowCancel to */ /* identify when the the user aborted the operation. */ static TV_BOOL RecordCmdCancelTestCB( void *cb_data ) { TV_AUDIO_CMD_STATE *state = (TV_AUDIO_CMD_STATE *) cb_data; return !XtIsRealized( state->dialog_shell ); } /* RecordCmdDoneCB */ /* - Called when audio conversion cmd completes or is aborted */ static void RecordCmdDoneCB( TV_BOOL aborted, int status, void *cb_data ) { TV_AUDIO_CMD_STATE *state = (TV_AUDIO_CMD_STATE *) cb_data; /* At this stage, always pull down the "wait" dialog and destroy it */ if ( !aborted || ( status != 0 ) ) XtPopdown( state->dialog_shell ); XtDestroyWidget( state->dialog_shell ); /* The TVAUDIOCNVT code has already told the user if a conversion */ /* command failed, so no need to do anything else about that here. */ /* If the command failed, tell the user about it */ /* Do post-cmd cleanup */ unlink( state->fname_in ); if ( aborted ) unlink( state->fname_out ); free( state ); return; } /* RecordCmdCB - Check record preconditions and start recording */ static void RecordCmdCB( Widget w, XtPointer cl, XtPointer cb ) { TV_DISK *d = &G_glob.disk; String filename; int out_fd = -1, dsp_fd = -1; TV_BOOL exists; struct stat sb; TV_INT32 bps; ssize_t read_bytes; char msg[MAXPATHLEN+160], recname[ MAXPATHLEN ]; TV_BOOL mute_on, conv_reqd; TV_AUDIO_CMD_STATE *state = NULL; if ( Dialog_wgt == NULL ) return; /* If already recording, ignore user */ if ( Recording || Playing ) { XBell( TVDISPLAY, 100 ); return; } /* Save off original mute state */ TVAUDIOGetMuteState( &mute_on ); /* Grab values off dialog */ if ( !PrepareForAudio( TRUE, 1, &dsp_fd, &bps ) ) goto RETURN; filename = d->fn_audio_base; TVAUDIOSelectLineForRecord(); /* See if the output file exists; if so, we need to verify user wants */ /* to overwrite. */ exists = TRUE; if ( stat( filename, &sb ) < 0 ) { if ( errno != ENOENT ) { fprintf( stderr, "Whoah! stat() failed on '%s'.\n", filename ); XBell( TVDISPLAY, 100 ); goto RETURN; } exists = FALSE; } if ( exists ) { sprintf( msg, "This file exists (%s).\nOverwrite?", filename ); if ( XUTILDialogPause( TVTOPLEVEL, "Confirm", msg, TV_DIALOG_TYPE_YES_NO ) != TV_DIALOG_YES ) goto RETURN; unlink( filename ); } /* Will conversion be required after we're done recording? */ conv_reqd = ( Sel_ffmt != TV_AUDIO_FILE_FMT_RAW ) && !( Sel_ffmt == TV_AUDIO_FILE_FMT_SUNAU && Sel_sfmt == TV_AUDIO_SAMPLE_FMT_MULAW_U8 && Sel_stereo == False && Sel_rate == 8012 ); /* Determine file to record to initially (tmpfile if conv req'd) */ recname[0] = '\0'; strncat( recname, filename, sizeof( recname )-1 ); if ( conv_reqd ) strcat( recname, ".fxtvTMP" ); /* Now open the desired output sound file. */ if ( (out_fd = open( recname, O_CREAT|O_TRUNC|O_WRONLY, 0666 )) < 0 ) { XUTILDialogPause( TVTOPLEVEL, "Error", "Couldn't open output file\n", TV_DIALOG_TYPE_OK ); goto RETURN; } Recording = True; /* Make sure mute is off, & disable all but stop btn */ TVAUDIOSetMuteState( False ); UpdateButtons(); /* Flush X events (update GUI buttons, etc.) */ XSync( TVDISPLAY, False ); /* All right sports fans, we're finally ready to record */ read_bytes = RDWR_TIME * (Sel_rate * bps * (Sel_stereo ? 2 : 1)); read_bytes = (read_bytes+3)/4*4; assert( read_bytes <= MAX_RDWR_BUF_SIZE ); while ( Recording ) { XEvent ev; char buf[ MAX_RDWR_BUF_SIZE ]; ssize_t len; if ( (len = read( dsp_fd, buf, read_bytes )) < 0 ) { sprintf( msg, "read() failed on audio device: %s\n", strerror(errno) ); XUTILDialogPause( TVTOPLEVEL, "Error", msg, TV_DIALOG_TYPE_OK ); Recording = False; break; } if ( write( out_fd, buf, len ) < 0 ) { sprintf( msg, "write() failed on output file: %s\n", strerror(errno) ); XUTILDialogPause( TVTOPLEVEL, "Error", msg, TV_DIALOG_TYPE_OK ); Recording = False; break; } /* FIXME: XtAppNextEvent will block if just a timer event is */ /* outstanding -- is there a better way to do this? */ while ( XtAppPending( TVAPPCTX ) & ~XtIMTimer ) { XtAppNextEvent( TVAPPCTX, &ev ); XtDispatchEvent( &ev ); } } close( dsp_fd ); dsp_fd = -1; close( out_fd ); out_fd = -1; /* If the selected format requires conversion, do it */ if ( conv_reqd ) { TV_SOUND_FMT fmt_in, fmt_out; Widget dialog_shell; /* Alloc state structure for use during command execution */ if ( (state = malloc( sizeof( *state ) )) == NULL ) TVUTILOutOfMemory(); memset( state, '\0', sizeof( *state ) ); dialog_shell = XUTILDialogBuild( TVTOPLEVEL, "Please Wait", "Format conversion in progress...", TV_DIALOG_TYPE_CANCEL ); XUTILXtPopup( dialog_shell, XtGrabNone, TVTOPLEVEL ); /* Fill in from- and to- sound formats */ fmt_in.file_fmt = TV_AUDIO_FILE_FMT_RAW; fmt_in.samp_fmt = Sel_sfmt, fmt_in.stereo = Sel_stereo, fmt_in.samp_rate = Sel_rate; memcpy( &fmt_out, &fmt_in, sizeof( fmt_out ) ); fmt_out.file_fmt = Sel_ffmt; /* Execute conversion cmd & wait on it to finish or user to cancel. */ /* FIXME: Also displaying output of child as it runs might */ /* be useful. */ memcpy( &state->fmt_out, &fmt_out, sizeof( state->fmt_out ) ); strcpy( state->fname_in , recname ); strcpy( state->fname_out, filename ); state->dialog_shell = dialog_shell; /* Kick off the conversion in the background */ TVAUDIOCNVTConvertFormat( -1, recname , &fmt_in, -1, filename, &fmt_out, RecordCmdCancelTestCB, state, RecordCmdDoneCB , state ); state = NULL; } RETURN: /* Done recording, close up shop */ if ( dsp_fd >= 0 ) close( dsp_fd ); if ( out_fd >= 0 ) close( out_fd ); if ( state != NULL ) free( state ); TVAUDIOSetMuteState( mute_on ); /* FIXME: Check code - make sure no problem (other than CPU) starting */ /* another record/play while conversion for previous still going on. */ UpdateButtons(); } /* RecordCmdCB - Stop recording, if we're recording now */ static void StopCmdCB( Widget w, XtPointer cl, XtPointer cb ) { if ( !Recording && !Playing ) { XBell( TVDISPLAY, 100 ); return; } Recording = Playing = False; UpdateButtons(); } /* DismissCmdCB - Dismiss the dialog, if we're not recording */ static void DismissCmdCB( Widget w, XtPointer cl, XtPointer cb ) { if ( Recording || Playing ) { XBell( TVDISPLAY, 100 ); return; } XtPopdown( Dialog_wgt ); } /* PlaybackCmdCancelTestCB */ /* - Used by PlaybackCmd invocations of XUTILRunCmdAllowCancel to */ /* identify when the the user aborted the operation. */ static TV_BOOL PlaybackCmdCancelTestCB( void *cb_data ) { return !Playing; } /* PlaybackCmdDoneCB */ /* - Called when playback cmd completes or is aborted */ static void PlaybackCmdDoneCB( TV_BOOL aborted, int status, void *cb_data ) { TV_AUDIO_CMD_STATE *state = (TV_AUDIO_CMD_STATE *) cb_data; Playing = False; UpdateButtons(); /* If the command failed, tell the user about it */ if ( !aborted && ( status != 0 ) ) DoCmdFailDialog( state->cmd, status ); TVAUDIOSetMuteState( state->mute_on ); /* Do post-cmd cleanup */ free( state->cmd ); free( state->tmpstr ); free( state ); return; } /* PlaybackCmdCB - Playback the selected audio file */ static void PlaybackCmdCB( Widget w, XtPointer cl, XtPointer cb ) { TV_DISK *d = &G_glob.disk; String filename; int dsp_fd = -1, in_fd = -1; #ifdef OLDSTUFF ssize_t read_bytes; char msg[160]; #endif TV_INT32 bps; TV_BOOL mute_on, conv_reqd; TV_INT32 i,j; TV_BOOL cmd_running = FALSE; TVUTIL_PIPE_END end[3] = {{ -1, FALSE }, { -1, FALSE }, { -1 } }; TV_AUDIO_CMD_STATE *state = NULL; char shell_cmd[ 2*MAXPATHLEN+80 ]; if ( Dialog_wgt == NULL ) return; /* If already recording, ignore user */ if ( Recording || Playing ) { XBell( TVDISPLAY, 100 ); return; } /* Save off original mute state */ TVAUDIOGetMuteState( &mute_on ); /* Grab values off dialog */ if ( !PrepareForAudio( FALSE, 1, &dsp_fd, &bps ) ) goto RETURN; filename = d->fn_audio_base; /* Now open the desired input sound file. */ if ( (in_fd = open( filename, O_RDONLY, 0 )) < 0 ) { XUTILDialogPause( TVTOPLEVEL, "Error", "Couldn't open input file\n", TV_DIALOG_TYPE_OK ); goto RETURN; } end[0].fd = in_fd; end[1].fd = dsp_fd; Playing = True; /* Make sure mute is on, & disable all but stop btn */ TVAUDIOSetMuteState( True ); UpdateButtons(); /* Flush X events (update GUI buttons, etc.) */ XSync( TVDISPLAY, False ); /* Will conversion be required to play this file? */ conv_reqd = ( Sel_ffmt != TV_AUDIO_FILE_FMT_RAW ) && !( Sel_ffmt == TV_AUDIO_FILE_FMT_SUNAU && Sel_sfmt == TV_AUDIO_SAMPLE_FMT_MULAW_U8 && Sel_stereo == False && Sel_rate == 8012 ); /* Alloc state structure for use during command execution */ if ( (state = malloc( sizeof( *state ) )) == NULL ) TVUTILOutOfMemory(); memset( state, '\0', sizeof( *state ) ); /* Construct conversion command. */ /* No conversion req'd -- use "cat" */ /* MPEG-2 or -3 -- use user command */ /* All else -- use "sox" */ if ( !conv_reqd ) strcpy( shell_cmd, "cat" ); else if (( Sel_ffmt == TV_AUDIO_FILE_FMT_MPEG2 ) || ( Sel_ffmt == TV_AUDIO_FILE_FMT_MPEG3 )) { char *play_cmd; /* Grab the right player cmd */ if ( Sel_ffmt == TV_AUDIO_FILE_FMT_MPEG2 ) play_cmd = App_res.play_cmd_mpeg2; else play_cmd = App_res.play_cmd_mpeg3; if ( strspn( play_cmd, " " ) == strlen( play_cmd ) ) { XUTILDialogPause( TVTOPLEVEL, "Error", "No MPEG conv cmd.", TV_DIALOG_TYPE_OK ); free(state); Playing = False; goto RETURN; } /* Build Cmd: " -" */ sprintf( shell_cmd, "%s -", play_cmd ); /* Let MPEG players open /dev/dsp themselves */ close( dsp_fd ); dsp_fd = -1; end[1].fd = -1; } else { /* FIXME: Make Sel_* pointers to records */ for ( i = 0; i < XtNumber( Sfmt_item_def ); i++ ) if ( Sfmt_item_def[i].fmt == Sel_sfmt ) break; for ( j = 0; j < XtNumber( Ffmt_item_def ); j++ ) if ( Ffmt_item_def[j].fmt == Sel_ffmt ) break; /* Build Cmd: "sox %s - %s -c %d -r %d -" */ sprintf( shell_cmd, "sox %s - %s -c %d -r %ld -", Ffmt_item_def[j].sox_opt, Sfmt_item_def[i].sox_opt, Sel_stereo ? 2 : 1, Sel_rate ); } TVUTILCmdStrToArgList( shell_cmd, &state->cmd, &state->tmpstr ); /* We now use "cat" as a subprocess when no conv is req'd (see above) */ #ifdef OLDSTUFF if ( !conv_reqd ) { /* FIXME: We probably wanna fork a child to handle this rather */ /* than try to handle X events AND blocking reads/writes. */ /* This is more of a problem for low-bitrate audio where the */ /* buffers fill and we just block for a while. */ read_bytes = RDWR_TIME * (Sel_rate * bps * (Sel_stereo ? 2 : 1)); read_bytes = (read_bytes+3)/4*4; assert( read_bytes <= MAX_RDWR_BUF_SIZE ); while ( Playing ) { XEvent ev; char buf[ MAX_RDWR_BUF_SIZE ]; ssize_t len; if ( (len = read( in_fd, buf, read_bytes )) < 0 ) { sprintf( msg, "read() failed on input file: %s\n", strerror(errno) ); XUTILDialogPause( TVTOPLEVEL, "Error", msg, TV_DIALOG_TYPE_OK); Playing = False; break; } if ( len == 0 ) { Playing = False; break; } if ( write( dsp_fd, buf, len ) < 0 ) { sprintf( msg, "write() failed on audio device: %s\n", strerror(errno) ); XUTILDialogPause( TVTOPLEVEL, "Error", msg, TV_DIALOG_TYPE_OK); Playing = False; break; } while ( XtAppPending( TVAPPCTX ) & ~XtIMTimer ) { XtAppNextEvent( TVAPPCTX, &ev ); XtDispatchEvent( &ev ); } } } else { /* conv_reqd */ #endif /* All right sports fans, we're finally ready to play. */ /* Execute conversion cmd & wait on it to finish or user to cancel. */ /* FIXME: Also displaying output of child as it runs might */ /* be useful. */ state->fmt_out.file_fmt = Sel_ffmt, state->fmt_out.samp_fmt = Sel_sfmt, state->fmt_out.stereo = Sel_stereo, state->fmt_out.samp_rate = Sel_rate, state->mute_on = mute_on; XUTILRunCmdAllowCancel( TVAPPCTX, state->cmd, end, PlaybackCmdCancelTestCB, state, PlaybackCmdDoneCB , state ); state = NULL; cmd_running = TRUE; RETURN: /* Done playing, close up shop */ if ( dsp_fd >= 0 ) close( dsp_fd ); if ( in_fd >= 0 ) close( in_fd ); if ( state != NULL ) free( state ); if ( !cmd_running ) { TVAUDIOSetMuteState( mute_on ); UpdateButtons(); } } static void TextValUpdate( Widget text_wgt, char *str ) { XawTextBlock tblk; char *old_str; int old_len; assert( text_wgt != NULL ); memset( &tblk, '\0', sizeof( tblk ) ); tblk.firstPos = 0; tblk.length = strlen( str ); tblk.ptr = str; tblk.format = XawFmt8Bit; XtVaGetValues( text_wgt, XtNstring, &old_str, NULL ); old_len = (old_str == NULL) ? 0 : strlen( old_str ); XawTextReplace( text_wgt, 0, old_len, &tblk ); } /* FFmtMenuItemCB - Update menu button text with selected choice */ static void FFmtMenuItemCB( Widget w, XtPointer cl, XtPointer cb ) { Widget menu_btn = Ffmt_menu_btn; TV_FFMT_ITEM_DEF *def = (TV_FFMT_ITEM_DEF *) cl; SetMenuSelection( menu_btn, def->fmt ); /* If Sun AU, default to 8-bit MULAW, Mono, & 8012 s/s */ if ( def->fmt == TV_AUDIO_FILE_FMT_SUNAU ) { SetMenuSelection( Sfmt_menu_btn, TV_AUDIO_SAMPLE_FMT_MULAW_U8 ); SetMenuSelection( Chan_menu_btn, FALSE ); SetMenuSelection( Rate_menu_btn, 8012 ); } /* If any of the rest, go for the max */ else { SetMenuSelection( Sfmt_menu_btn, TV_AUDIO_SAMPLE_FMT_LIN_S16_LE ); SetMenuSelection( Chan_menu_btn, TRUE ); SetMenuSelection( Rate_menu_btn, 44100 ); } } /* SFmtMenuItemCB - Update menu button text with selected choice */ static void SFmtMenuItemCB( Widget w, XtPointer cl, XtPointer cb ) { Widget menu_btn = Sfmt_menu_btn; TV_SFMT_ITEM_DEF *def = (TV_SFMT_ITEM_DEF *) cl; SetMenuSelection( menu_btn, def->fmt ); /* If 8-bit ULAW, default to Mono & 8012 s/s */ if ( def->fmt == TV_AUDIO_SAMPLE_FMT_MULAW_U8 ) { SetMenuSelection( Chan_menu_btn, FALSE ); SetMenuSelection( Rate_menu_btn, 8012 ); } } /* ChanMenuItemCB - Update menu button text with selected choice */ static void ChanMenuItemCB( Widget w, XtPointer cl, XtPointer cb ) { Widget menu_btn = Chan_menu_btn; TV_CHAN_ITEM_DEF *def = (TV_CHAN_ITEM_DEF *) cl; SetMenuSelection( menu_btn, def->stereo ); } /* RateMenuItemCB - Update menu button text with selected choice */ static void RateMenuItemCB( Widget w, XtPointer cl, XtPointer cb ) { Widget menu_btn = Rate_menu_btn; TV_RATE_ITEM_DEF *def = (TV_RATE_ITEM_DEF *) cl; SetMenuSelection( menu_btn, def->rate ); } static void TVAudSavDialogBuild( Widget *dialog_wgt ) { Position x, y; Dimension width; Widget w, box, gbox, cbox, fbox, lbox, form, menu_shell; TV_INT32 i; XtTranslations transl; /* Create the dialog widgets */ *dialog_wgt = XtVaCreatePopupShell( "audioSaveDialog", transientShellWidgetClass, TVTOPLEVEL, NULL ); box = XtVaCreateManagedWidget( "mainBox", boxWidgetClass, *dialog_wgt, XtNorientation, XtorientVertical, NULL ); gbox = XtVaCreateManagedWidget( "groupBox", boxWidgetClass, box, XtNorientation, XtorientVertical, NULL ); fbox = XtVaCreateManagedWidget( "fileBox", boxWidgetClass, gbox, XtNorientation, XtorientVertical, NULL ); cbox = XtVaCreateManagedWidget( "fileBaseBox", boxWidgetClass, fbox, XtNorientation, XtorientHorizontal, NULL ); w = XtVaCreateManagedWidget( "fileBaseLabel", labelWidgetClass, cbox, XtNjustify, XtJustifyRight, NULL ); Text_wgt = XtVaCreateManagedWidget( "fileBaseText", asciiTextWidgetClass, cbox, XtNtype , XawAsciiString, XtNuseStringInPlace, False, XtNscrollHorizontal, XawtextScrollNever, XtNscrollVertical , XawtextScrollNever, XtNdisplayCaret , False, XtNeditType , XawtextEdit, XtNresize , XawtextResizeNever, NULL ); /* Text widget translation overrides */ transl = XtParseTranslationTable( G_transl_ovr_ascii_text ); XtOverrideTranslations( Text_wgt, transl ); transl = XtParseTranslationTable( G_transl_ovr_ascii_text_1line ); XtOverrideTranslations( Text_wgt, transl ); cbox = XtVaCreateManagedWidget( "fileFmtBox", boxWidgetClass, fbox, XtNorientation, XtorientHorizontal, NULL ); w = XtVaCreateManagedWidget( "fileFmtLabel", labelWidgetClass, cbox, XtNjustify, XtJustifyRight, NULL ); /* File Format Choice Box */ Ffmt_menu_btn = XtVaCreateManagedWidget( "fileFmtMenu", menuButtonWidgetClass, cbox, XtNresize, XawtextResizeNever, NULL ); menu_shell = XtVaCreatePopupShell( "menu", simpleMenuWidgetClass, Ffmt_menu_btn, NULL ); for ( i = 0; i < XtNumber( Ffmt_item_def ); i++ ) { Widget item; item = XtVaCreateManagedWidget( Ffmt_item_def[i].file_ext, smeBSBObjectClass, menu_shell, NULL ); Ffmt_item_def[i].wgt = item; XtAddCallback( item, XtNcallback, FFmtMenuItemCB, &Ffmt_item_def[i] ); } fbox = XtVaCreateManagedWidget( "captureBox", boxWidgetClass, gbox, XtNorientation, XtorientHorizontal, NULL ); lbox = XtVaCreateManagedWidget( "sampFmtLabelBox", boxWidgetClass, fbox, XtNorientation, XtorientVertical, NULL ); w = XtVaCreateManagedWidget( "sampFmtLabel", labelWidgetClass, lbox, XtNresize, False, XtNencoding, XawTextEncoding8bit, NULL ); cbox = XtVaCreateManagedWidget( "sampFmtWgtBox", boxWidgetClass, fbox, XtNorientation, XtorientVertical, NULL ); /* Sample Format Choice Box */ Sfmt_menu_btn = XtVaCreateManagedWidget( "sampFmtMenu", menuButtonWidgetClass, cbox, XtNresize, XawtextResizeNever, NULL ); menu_shell = XtVaCreatePopupShell( "menu", simpleMenuWidgetClass, Sfmt_menu_btn, NULL ); for ( i = 0; i < XtNumber( Sfmt_item_def ); i++ ) { Widget item; item = XtVaCreateManagedWidget( Sfmt_item_def[i].wgt_name, smeBSBObjectClass, menu_shell, NULL ); Sfmt_item_def[i].wgt = item; XtAddCallback( item, XtNcallback, SFmtMenuItemCB, &Sfmt_item_def[i] ); } /* # Channels Choice Box */ w = XtVaCreateManagedWidget( "chanMenuBox", boxWidgetClass, cbox, XtNorientation, XtorientHorizontal, NULL ); Chan_menu_btn = XtVaCreateManagedWidget( "chanMenu", menuButtonWidgetClass, w, XtNresize, XawtextResizeNever, NULL ); menu_shell = XtVaCreatePopupShell( "menu", simpleMenuWidgetClass, Chan_menu_btn, NULL ); for ( i = 0; i < XtNumber( Chan_item_def ); i++ ) { Widget item; item = XtVaCreateManagedWidget( Chan_item_def[i].wgt_name, smeBSBObjectClass, menu_shell, NULL ); Chan_item_def[i].wgt = item; XtAddCallback( item, XtNcallback, ChanMenuItemCB, &Chan_item_def[i] ); } /* Sample Rate Choice Box */ w = XtVaCreateManagedWidget( "rateMenuBox", boxWidgetClass, cbox, XtNorientation, XtorientHorizontal, NULL ); Rate_menu_btn = XtVaCreateManagedWidget( "rateMenu", menuButtonWidgetClass, w, XtNresize, XawtextResizeNever, NULL ); menu_shell = XtVaCreatePopupShell( "menu", simpleMenuWidgetClass, Rate_menu_btn, NULL ); for ( i = 0; i < XtNumber( Rate_item_def ); i++ ) { Widget item; item = XtVaCreateManagedWidget( Rate_item_def[i].wgt_name, smeBSBObjectClass, menu_shell, NULL ); Rate_item_def[i].wgt = item; XtAddCallback( item, XtNcallback, RateMenuItemCB, &Rate_item_def[i] ); } /* Action button form */ form = XtVaCreateManagedWidget( "actionForm", formWidgetClass, box, NULL ); w = XtVaCreateManagedWidget( "recordCmd", commandWidgetClass, form, NULL ); XtAddCallback( w, XtNcallback, RecordCmdCB, NULL ); Record_btn = w; w = XtVaCreateManagedWidget( "stopCmd", commandWidgetClass, form, XtNfromHoriz, w, NULL ); XtAddCallback( w, XtNcallback, StopCmdCB, NULL ); Stop_btn = w; w = XtVaCreateManagedWidget( "playbackCmd", commandWidgetClass, form, XtNfromHoriz, w, NULL ); XtAddCallback( w, XtNcallback, PlaybackCmdCB, NULL ); Playback_btn = w; w = XtVaCreateManagedWidget( "dismissCmd", commandWidgetClass, form, XtNfromHoriz, w, NULL ); XtAddCallback( w, XtNcallback, DismissCmdCB, NULL ); Dismiss_btn = w; /* By default, position dialog just to right of main TV window */ XtVaGetValues( TVTOPLEVEL, XtNwidth, &width, NULL); XtTranslateCoords( TVTOPLEVEL, width, 0, &x, &y ); XtVaSetValues( *dialog_wgt, XtNx, x, XtNy, y, NULL ); } void TVAUDSAVDIALOGPopUp() { /* Do dialog */ if ( Dialog_wgt == NULL ) TVAudSavDialogBuild( &Dialog_wgt ); TVAUDSAVDIALOGResync(); XUTILXtPopup( Dialog_wgt, XtGrabNone, TVTOPLEVEL ); } void TVAUDSAVDIALOGResync() { TV_DISK *d = &G_glob.disk; /* FIXME: Also install EnterNotify handler for this dialog to */ /* resync values on entry of it's shell. */ if ( Dialog_wgt == NULL ) return; /* Set text field to current base filename */ TextValUpdate( Text_wgt, d->fn_audio_base ); /* Set selections based on active format */ SetMenuSelection( Ffmt_menu_btn, d->audio.file_fmt ); SetMenuSelection( Sfmt_menu_btn, d->audio.sample_fmt ); SetMenuSelection( Chan_menu_btn, d->audio.stereo ); SetMenuSelection( Rate_menu_btn, d->audio.sample_rate ); /* Set button sensitivites */ UpdateButtons(); }