home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-387-Vol-3of3.iso / x / x_soundt.c next >
C/C++ Source or Header  |  1993-03-30  |  69KB  |  2,884 lines

  1. #if !defined(lint) && !defined(NOID)
  2. static  char sccsid[] = "@(#)x_soundtool.c 1.5 90/09/27 Copyr 1989 Sun Micro";
  3. #endif
  4.  
  5. /*
  6.  * Copyright 1989, Sun Microsystems, Inc.
  7.  */
  8.  
  9. #include <stdio.h>
  10. #include <errno.h>
  11. #include <strings.h>
  12. #include <math.h>
  13. #include <fcntl.h>
  14. #include <stropts.h>
  15. #include <sys/types.h>
  16. #include <sys/param.h>
  17. #include <sys/dir.h>
  18. #include <sys/file.h>
  19. #include <sys/stat.h>
  20. #include <sys/signal.h>
  21. #include <sys/ioctl.h>
  22. #include <sun/audioio.h>
  23.  
  24. #include <xview/xview.h>
  25. #include <xview/notice.h>
  26. #include <xview/panel.h>
  27. #include <xview/canvas.h>
  28. #include <xview/pixwin.h>
  29. #include <xview/scrollbar.h>
  30. #include <xview/tty.h>
  31. #include <xview/seln.h>
  32. #include <xview/cursor.h>
  33. #include <xview/screen.h>
  34. #include <xview/server.h>
  35. #include <xview/cms.h>
  36.  
  37. #include <multimedia/libaudio.h>
  38. #include <multimedia/audio_device.h>
  39. #include <multimedia/ulaw2linear.h>
  40.  
  41. Server_image soundtool_icon_image;
  42. Server_image waveform_cursor_image;
  43. Server_image hglass_cursor_image;
  44.  
  45. static short soundtool_image_bits[] = {
  46. #include "soundtool.icon"
  47. };
  48.  
  49. static short hglass_cursor_bits[] = {
  50. #include <images/hglass.cursor>
  51. };
  52.  
  53. static short waveform_cursor_bits[] = {
  54. #include <images/ibeam.cursor>
  55. };
  56.  
  57. #define    FPRINTF    (void) fprintf
  58. #define    SPRINTF    (void) sprintf
  59. #define    STRCPY    (void) strcpy
  60.  
  61.  
  62. #ifdef DEBUG
  63. #define    EVENTP(P, E)    (void) printf("%s: event %d\n", (P), event_id(E))
  64. #define    DEBUGF(args)    (void) printf args
  65. #define    PERROR(msg)    FPRINTF(stderr, "%s(%d): \"%s\" (%s)\n",    \
  66.                 prog, __LINE__, msg, sys_errlist[errno]);
  67. #else
  68. #define    EVENTP(P, E)
  69. #define    DEBUGF(args)
  70. #define    PERROR(msg)    FPRINTF(stderr, "%s: \"%s\" (%s)\n",    \
  71.                 prog, msg, sys_errlist[errno]);
  72. #endif    /*!DEBUG*/
  73.  
  74.  
  75. #define    REFRESH_RELOAD    (20)        /* only refresh every 20th time */
  76.  
  77. #define    BUTTON_WIDTH    (8)
  78. #define    SCROLLBAR_WIDTH    (18)
  79.  
  80. #define    WINDOW_WIDTH    (800)
  81. #define    SCOPE_HEIGHT    (256 + SCROLLBAR_WIDTH + 4)
  82.  
  83. #define    SCOPE_WIDTH    (256)
  84. #define    SCOPE_MASK    (SCOPE_WIDTH - 1)
  85. #define    WAVEFORM_WIDTH    (1600)
  86. #define    WAVEFORM_HEIGHT    (SCOPE_HEIGHT)
  87.  
  88. #define    INFO_SIZE    (80)        /* max length of info string */
  89. #define    MIN_BUFSIZE    (5)        /* min bufsize for looping */
  90. #define    MAX_RECORD_TIME    (5 * 60)    /* max record time in seconds */
  91. #define    MAX_GAIN    (100)        /* gain range */
  92.  
  93. #define    NOTICE_FILE    "alert.au"
  94. #define    AUDIO_DEV    "/dev/audio"
  95. #define    AUDIO_CTLDEV    "/dev/audioctl"
  96.  
  97. typedef struct Graph {
  98.     Xv_Window       PW;
  99.     Display        *dsp;
  100.     XID             xid;
  101.     GC              gc;
  102. }               GraphType;
  103.  
  104. char        *prog;
  105.  
  106. Frame        Base_frame;
  107. Panel        Main_panel;
  108. Panel_item    Main_play_item;
  109. Panel_item    Main_record_item;
  110. Panel_item    Main_pause_item;
  111. Panel_item    Main_playgain_item;
  112. Panel_item    Main_recordgain_item;
  113. Panel_item    Main_playport_item;
  114. Panel_item    Loop_item;
  115.  
  116. Frame        Describe_frame;
  117. Panel        Describe_panel;
  118. Panel_item    Describe_delta_item;
  119. Panel_item    Describe_length_item;
  120. Panel_item    Describe_channel_item;
  121. Panel_item    Describe_sample_item;
  122. Panel_item    Describe_bits_item;
  123. Panel_item    Describe_encoding_item;
  124. Panel_item    Describe_info_item;
  125.  
  126. Panel        File_panel;
  127. Panel_item    File_name_item;
  128. Panel_item    File_directory_item;
  129.  
  130. Xv_Cursor        File_save_cursor = NULL;
  131.  
  132. Xv_Cursor        File_wait_cursor = NULL;
  133.  
  134. Panel        Scope_canvas;
  135.  
  136. Canvas        Waveform_canvas;
  137. void        waveform_repaint_proc();
  138. Panel_item    Waveform_zoom_item;
  139. Panel        Waveform_panel;
  140.  
  141. Xv_Cursor      Waveform_cursor;
  142.  
  143. Scrollbar position_sb;
  144.  
  145. short        dash_tex[] = {8, 8, 0};
  146. short        dot_tex[] = {3, 3, 0};
  147. short        solid_tex[] = {1, 0};
  148. Pr_texture    solid = { solid_tex, 0};
  149. Pr_texture    dashed = { dash_tex, 0};
  150. Pr_texture    dotted = { dot_tex, 0};
  151.  
  152. Display *wc_display;
  153. Drawable wc_drawable;
  154. GC          wc_gc;
  155. XGCValues wc_gcval;
  156. Xv_Screen wc_screen;
  157. int    wc_screen_no;
  158.  
  159. Display *sc_display;
  160. Drawable sc_drawable;
  161. GC          sc_gc;
  162. XGCValues sc_gcval;
  163. Xv_Screen sc_screen;
  164. int    sc_screen_no;
  165.  
  166. Xv_Server soundtool_server;
  167.  
  168. unsigned    Zoom = 2;        /* waveform display scaling factor */
  169. int        Waveform_update_inhibit = FALSE;
  170. int        Show_describe;        /* TRUE if describe panel is up */
  171. int        Refresh_ctr;        /* slows waveform refresh down a bit */
  172. int        Loop_flag;        /* TRUE if looping */
  173. int        Wait_flag;        /* OR of PLAY and RECORD bits */
  174. int        Active_flag;        /* OR of PLAY and RECORD bits */
  175. #define    PLAY    1
  176. #define    RECORD    2
  177.  
  178. int        Audioctl_fd = -1;    /* fd for audio control device */
  179. int        Audio_fd = -1;        /* fd for audio i/o device */
  180. int        Alert_fd = -1;        /* fd for alert i/o */
  181. int        Sync_sched = FALSE;    /* sigpoll_sync_handler scheduled */
  182. Audio_info    Audio_state;        /* audio device state structure */
  183. Audio_hdr    Device_phdr;        /* Play configuration info */
  184. Audio_hdr    Device_rhdr;        /* Record configuration info */
  185.  
  186. struct sound_buffer {
  187.     Audio_hdr        hdr;        /* encoding info */
  188.     char            info[INFO_SIZE + 1]; /* info string */
  189.  
  190.     char            directory[MAXPATHLEN];
  191.     char            filename[MAXPATHLEN];
  192.  
  193.     unsigned char        *data;        /* ptr to data buffer */
  194.     unsigned        alloc_size;    /* size of data buffer */
  195.     int            draining;
  196.     int            paused;
  197.  
  198.     struct {
  199.         char        directory[MAXPATHLEN];
  200.         time_t        modified;
  201.         struct direct    **files;
  202.         Menu        menu;
  203.     }            menu;        /* cached menu */
  204.  
  205.     struct {
  206.         unsigned    start;        /* selected region to play */
  207.         unsigned    end;
  208.         unsigned    io_position;
  209.     }            play;
  210.     struct {
  211.         int    cursor_pos;        /* cursor position in buffer */
  212.         int    start;            /* marks selected region */
  213.         int    end;
  214.         int    position;
  215.         int    last;
  216.     }            display;
  217. } Buffer;
  218.  
  219.  
  220. /* Local functions */
  221. Notify_value    timer_handler();
  222. Notify_value    sigpoll_async_handler();
  223. Notify_value    sigpoll_sync_handler();
  224.  
  225. /* External functions */
  226. long        lseek();
  227. int        alphasort();
  228. extern int scope_repaint_proc();
  229.  
  230. extern char    *sys_errlist[];
  231.  
  232. /* Main entry point for soundtool demo */
  233. main(argc, argv, envp)
  234.     int    argc;
  235.     char    *argv[];
  236.     char    *envp[];
  237. {
  238.     /* Save the invocation command name for error messages */
  239.     prog = argv[0];
  240.  
  241.     soundtool_server = xv_init(XV_INIT_ARGC_PTR_ARGV, &argc, argv, 0);
  242.  
  243.     soundtool_icon_image = xv_create(NULL, SERVER_IMAGE,
  244.         XV_WIDTH, 64,
  245.         XV_HEIGHT, 64, 
  246.         SERVER_IMAGE_DEPTH, 1, 
  247.         SERVER_IMAGE_BITS, soundtool_image_bits, 
  248.         0);
  249.  
  250.     waveform_cursor_image = xv_create(NULL, SERVER_IMAGE,
  251.         XV_WIDTH, 16,
  252.         XV_HEIGHT, 16, 
  253.         SERVER_IMAGE_DEPTH, 1, 
  254.         SERVER_IMAGE_BITS, waveform_cursor_bits, 
  255.         0);
  256.  
  257.     hglass_cursor_image = xv_create(NULL, SERVER_IMAGE,
  258.         XV_WIDTH, 16,
  259.         XV_HEIGHT, 16, 
  260.         SERVER_IMAGE_DEPTH, 1, 
  261.         SERVER_IMAGE_BITS, hglass_cursor_bits, 
  262.         0);
  263.  
  264.  
  265.     /* Create base window */
  266.     Base_frame = xv_create((Xv_Window) 0, FRAME,
  267.         WIN_ROWS,                35,
  268.         XV_WIDTH,                WINDOW_WIDTH,
  269.         FRAME_ARGS,                argc, argv,
  270.         XV_LABEL,            "soundtool",
  271.         FRAME_ICON, soundtool_icon_image, 
  272.         0);
  273.  
  274.     if (Base_frame == (Frame) 0) {
  275.         PERROR("Can't get base frame");
  276.         exit(1);
  277.     }
  278.  
  279.     /* Set up to catch SIGPOLL asynchronously */
  280.     (void) notify_set_signal_func(Base_frame,
  281.         (Notify_func)sigpoll_async_handler, SIGPOLL, NOTIFY_ASYNC);
  282.  
  283.     /* Set a synchronous event handler for SIGPOLL to schedule */
  284.     (void) notify_set_event_func(SIGPOLL,
  285.         (Notify_func)sigpoll_sync_handler, NOTIFY_SAFE);
  286.  
  287.     /* Open audio control device for volume controls */
  288.     audio_control_init();
  289.     init_buffer();
  290.  
  291.     /* Create main panels */
  292.     main_create_panel();
  293.     file_create_panel();
  294.     waveform_create_panel();
  295.     scope_create_canvas();
  296.     waveform_create_canvas();
  297.  
  298.     /* Create the pop-up panels */
  299.     describe_create_panel();
  300.     window_fit_height(Base_frame);
  301.     (void) xv_set(Base_frame, WIN_SHOW, TRUE, 0);
  302.     
  303.     window_main_loop(Base_frame);
  304.  
  305.     /* Flush audio output and close the audio device, if it is open */
  306.     closedown();
  307. /*NOTREACHED*/
  308. }
  309.  
  310. /* Stop everything and exit */
  311. closedown()
  312. {
  313.     if ((Active_flag & PLAY) || (Wait_flag & PLAY))
  314.         stop_play();
  315.     if ((Active_flag & RECORD) || (Wait_flag & RECORD))
  316.         stop_record();
  317.     exit(0);
  318. /*NOTREACHED*/
  319. }
  320.  
  321. /* Routines that implement the main control panel */
  322.  
  323. /* Play/Stop button pressed */
  324. /*ARGSUSED*/
  325. main_play_proc(item, event)
  326.     Panel_item    item;
  327.     Event        *event;
  328. {
  329.     int     i;
  330.     EVENTP("main_play_proc", event);
  331.  
  332.     /* Play stops in-progress Record */
  333.     if ((Active_flag & RECORD) || (Wait_flag & RECORD))
  334.         stop_record();
  335.  
  336.     if (Wait_flag & PLAY) {
  337.         stop_play();            /* never got the device */
  338.         return;
  339.     }
  340.     else if (Active_flag & PLAY) {    /* Stop button pressed */
  341.         stop_play();
  342.         if (Audio_state.play.error) {
  343.             message_display("Underflow detected during Play.");
  344.         }
  345.         return;
  346.     }
  347.  
  348.     /* Turn off looping if very small selection */
  349.     if (selectcheck() == 0)
  350.         return;            /* no data in buffer */
  351.  
  352.     switch (audio_open(PLAY)) {
  353.     case 0:                /* open succeeded */
  354.         break;
  355.  
  356.     case 1:                /* open returned EBUSY */
  357.         Wait_flag |= PLAY;    /* SIGPOLL is sent on close() */
  358.         xv_set(Main_play_item,
  359.             PANEL_LABEL_STRING, "Waiting",
  360.             0);
  361.         /*
  362.          * Set the 'waiting' flag, which would have been set if we
  363.          * had done a blocking open(), in the off chance that the
  364.          * holder of the audio device is benevolent.
  365.          */
  366.         i = TRUE;
  367.         (void) audio_set_play_waiting(Audioctl_fd, &i);
  368.         return;
  369.  
  370.     case -1:            /* open error */
  371.     default:
  372.         message_display("Error opening audio device.");
  373.         return;
  374.     }
  375.  
  376.     start_play();
  377. }
  378.  
  379. /* Record/Stop button pressed */
  380. /*ARGSUSED*/
  381. main_record_proc(item, event)
  382.     Panel_item    item;
  383.     Event        *event;
  384. {
  385.     EVENTP("main_record_proc", event);
  386.  
  387.     /* Record stops in-progress Play */
  388.     if ((Active_flag & PLAY) || (Wait_flag & PLAY))
  389.         stop_play();
  390.  
  391.     if (Wait_flag & RECORD) {
  392.         stop_record();            /* never got the device */
  393.         return;
  394.     }
  395.     else if (Active_flag & RECORD) {    /* Stop button pressed */
  396.         stop_record();
  397.         if (Audio_state.record.error) {
  398.             message_display("Overflow detected during Record.");
  399.         }
  400.         return;
  401.     }
  402.  
  403.     switch (audio_open(RECORD)) {
  404.     case 0:                /* open succeeded */
  405.         break;
  406.  
  407.     case 1:                /* open returned EBUSY */
  408.         message_display("Audio device currently in use.");
  409.         return;
  410.  
  411.     case -1:            /* open error */
  412.     default:
  413.         message_display("Error opening audio device.");
  414.         return;
  415.     }
  416.  
  417.     start_record();
  418. }
  419.  
  420. /* Pause/Resume button pressed */
  421. /*ARGSUSED*/
  422. main_pause_proc(item, event)
  423.     Panel_item    item;
  424.     Event        *event;
  425. {
  426.     EVENTP("main_pause_proc", event);
  427.  
  428.     if (!Active_flag)
  429.         return;
  430.  
  431.     /*
  432.      * Set the audio device state to the converse of the current state
  433.      * and let the SIGPOLL handler update the buttons.
  434.      */
  435.     if (Active_flag & PLAY) {
  436.         if (Buffer.paused)
  437.             (void) audio_resume_play(Audioctl_fd);
  438.         else
  439.             (void) audio_pause_play(Audioctl_fd);
  440.     }
  441.     if (Active_flag & RECORD) {
  442.         if (Buffer.paused)
  443.             (void) audio_resume_record(Audioctl_fd);
  444.         else
  445.             (void) audio_pause_record(Audioctl_fd);
  446.     }
  447. }
  448.  
  449. /* Describe button pressed */
  450. /*ARGSUSED*/
  451. main_describe_proc(item, event)
  452.     Panel_item    item;
  453.     Event        *event;
  454. {
  455.     int        x;
  456.  
  457.     EVENTP("main_describe_proc", event);
  458.  
  459.     if (Show_describe) {
  460.         Show_describe = FALSE;
  461.         (void) xv_set(Describe_frame, WIN_SHOW, FALSE, 0);
  462.         return;
  463.     }
  464.  
  465.     Show_describe = TRUE;
  466.     describe_update_panel(TRUE);
  467.  
  468.     /* Place describe panel in the upper right-hand corner of the frame */
  469.     x = (int) xv_get(Base_frame, XV_WIDTH) -
  470.         (int) xv_get(Describe_frame, XV_WIDTH);
  471.     (void) xv_set(Describe_frame,
  472.         WIN_X,        x,
  473.         WIN_Y,        0,
  474.         WIN_SHOW,        TRUE,
  475.         0);
  476. }
  477.  
  478. /* Convert local gain into device parameters */
  479. double
  480. scale_gain(g)
  481.     int        g;
  482. {
  483.     return ((double)g / (double)MAX_GAIN);
  484. }
  485.  
  486. /* Convert device gain into the local scaling factor */
  487. int
  488. unscale_gain(g)
  489.     double        g;
  490. {
  491.     return (irint((double)MAX_GAIN * g));
  492. }
  493.  
  494.  
  495. /* Play volume slider moved */
  496. /*ARGSUSED*/
  497. main_play_volume_proc(item, value, event)
  498.     Panel_item    item;
  499.     int        value;
  500.     Event        *event;
  501. {
  502.     double        gain;
  503.  
  504.     EVENTP("main_play_volume_proc", event);
  505.  
  506.     /* Let SIGPOLL handler adjust displayed value */
  507.     gain = scale_gain(value);
  508.     Audio_state.play.gain = ~0;
  509.     (void) audio_set_play_gain(Audioctl_fd, &gain);
  510. }
  511.  
  512. /* Record volume slider moved */
  513. /*ARGSUSED*/
  514. main_record_volume_proc(item, value, event)
  515.     Panel_item    item;
  516.     int        value;
  517.     Event        *event;
  518. {
  519.     double        gain;
  520.  
  521.     EVENTP("main_record_volume_proc", event);
  522.  
  523.     /* Let SIGPOLL handler adjust displayed value */
  524.     gain = scale_gain(value);
  525.     Audio_state.record.gain = ~0;
  526.     (void) audio_set_record_gain(Audioctl_fd, &gain);
  527. }
  528.  
  529. /* 'Output to:' changed */
  530. /*ARGSUSED*/
  531. control_output_proc(item, value, event)
  532.     Panel_item    item;
  533.     int        value;
  534.     Event        *event;
  535. {
  536.     unsigned    port;
  537.  
  538.     EVENTP("control_output_proc", event);
  539.  
  540.     /* Change the output port */
  541.     Audio_state.play.port = (unsigned) value;
  542.     port = (value == 0 ? AUDIO_SPEAKER : AUDIO_HEADPHONE);
  543.     (void) audio_set_play_port(Audioctl_fd, &port);
  544. }
  545.  
  546. /* 'Looping:' changed */
  547. /*ARGSUSED*/
  548. control_loop_proc(item, value, event)
  549.     Panel_item    item;
  550.     int        value;
  551.     Event        *event;
  552. {
  553.     EVENTP("control_loop_proc", event);
  554.  
  555.     Loop_flag = (value != 0);
  556.     if (Loop_flag) {
  557.         /* Don't set Looping if tiny buffer */
  558.         (void) selectcheck();
  559.  
  560.         /* Keep going if already draining */
  561.         if (Active_flag & PLAY)
  562.             Buffer.draining = FALSE;
  563.     }
  564. }
  565.  
  566. /*
  567.  * Update the variable audio controls on the main panel from current state.
  568.  * Called when any parameter changes may have occurred.
  569.  */
  570. main_update_panel(init)
  571.     int        init;
  572. {
  573.     Audio_info    Audio_new;
  574.  
  575.     /* Only set the values if they have changed (prevents flashing) */
  576. #define    NEWVAL(X, Y)    {                        \
  577.             if (Audio_new.X != Audio_state.X)        \
  578.                 (void) xv_set(Y, PANEL_VALUE, Audio_new.X, 0);    \
  579.             }
  580.  
  581.     if (!audio_readstate(&Audio_new) && !init)
  582.         return;
  583.  
  584.     NEWVAL(play.gain, Main_playgain_item);
  585.     NEWVAL(record.gain, Main_recordgain_item);
  586.     NEWVAL(play.port, Main_playport_item);
  587.  
  588.     /* Make sure that the Pause/Resume button is in sync with reality */
  589.     /*
  590.      * XXX - Pause should save state and close the audio device,
  591.      * in order to let other audio applications access /dev/audio.
  592.      * Note that, if it did close /dev/audio, 'gaintool' could no
  593.      * longer resume this program.
  594.      */
  595.     if (Active_flag & PLAY)
  596.         Buffer.paused = Audio_new.play.pause;
  597.     if (Active_flag & RECORD)
  598.         Buffer.paused = Audio_new.record.pause;
  599.  
  600.     /* If not active, assume pause state only if both flags set */
  601.     if (!Active_flag) {
  602.         Buffer.paused = Audio_new.play.pause && Audio_new.record.pause;
  603.     }
  604.     else if (Active_flag & PLAY) {
  605.         /* turn off timer when paused; restart on resume */
  606.         if (Buffer.paused)
  607.             cancel_timer();
  608.         else
  609.             set_timer((double)SCOPE_WIDTH /
  610.                 (double)Buffer.hdr.sample_rate);
  611.     }
  612.  
  613.     /* Use the play.pause field to indicate the current button state */
  614.     Audio_new.play.pause = Buffer.paused;
  615.  
  616.     if (Audio_new.play.pause != Audio_state.play.pause) {
  617.         (void) xv_set(Main_pause_item, 
  618.             PANEL_LABEL_STRING,
  619.             (Buffer.paused ? "Resume" : "Pause"), 0);
  620.     }
  621.  
  622.     /* Copy the current display state for the next time */
  623.     Audio_state = Audio_new;
  624. }
  625.  
  626. /* Create the main control panel */
  627. main_create_panel()
  628. {
  629.     Main_panel = xv_create(Base_frame, PANEL,
  630.         XV_LEFT_MARGIN,    10,
  631.         XV_RIGHT_MARGIN,    10,
  632.         WIN_ROWS,        6,
  633.         0);
  634.  
  635.     /* Init buttons that flip values when pressed */
  636.  
  637.     Main_play_item = xv_create(Main_panel, PANEL_BUTTON,
  638.         XV_X, xv_col(Main_panel, 0),
  639.         XV_Y, xv_row(Main_panel, 0),
  640.         PANEL_LABEL_STRING, "Play",
  641.         PANEL_NOTIFY_PROC, main_play_proc,
  642.         0);
  643.  
  644.     Main_record_item = xv_create(Main_panel, PANEL_BUTTON,
  645.         XV_X, xv_col(Main_panel, 0),
  646.         XV_Y, xv_row(Main_panel, 1),
  647.         PANEL_LABEL_STRING, "Record",
  648.         PANEL_NOTIFY_PROC, main_record_proc,
  649.         0);
  650.  
  651.     Main_pause_item = xv_create(Main_panel, PANEL_BUTTON,
  652.         XV_X, xv_col(Main_panel, 0),
  653.         XV_Y, xv_row(Main_panel, 2),
  654.         PANEL_LABEL_STRING, "Pause",
  655.         PANEL_NOTIFY_PROC, main_pause_proc,
  656.         0);
  657.  
  658.     (void) xv_create(Main_panel, PANEL_BUTTON,
  659.         XV_X, xv_col(Main_panel, 0),
  660.         XV_Y, xv_row(Main_panel, 3),
  661.         PANEL_LABEL_STRING, "Describe",
  662.         PANEL_NOTIFY_PROC, main_describe_proc,
  663.         0);
  664.  
  665.     Main_playgain_item = xv_create(Main_panel, PANEL_SLIDER,
  666.         XV_X, xv_col(Main_panel, 14),
  667.         XV_Y, xv_row(Main_panel, 0),
  668.         PANEL_LABEL_STRING, "Play volume  ",
  669.         PANEL_MIN_VALUE, 0,
  670.         PANEL_MAX_VALUE, MAX_GAIN,
  671.         PANEL_SHOW_RANGE,    FALSE,
  672.         PANEL_SLIDER_WIDTH, MAX_GAIN + 1,
  673.         PANEL_NOTIFY_PROC, main_play_volume_proc,
  674.         PANEL_NOTIFY_LEVEL, PANEL_ALL,
  675.         PANEL_SHOW_RANGE,   FALSE,
  676.         PANEL_SHOW_VALUE,   TRUE,
  677.         0);
  678.  
  679.     Main_recordgain_item = xv_create(Main_panel, PANEL_SLIDER,
  680.         XV_X, xv_col(Main_panel, 14),
  681.         XV_Y, xv_row(Main_panel, 1),
  682.         PANEL_LABEL_STRING, "Record volume",
  683.         PANEL_MIN_VALUE, 0,
  684.         PANEL_MAX_VALUE, MAX_GAIN,
  685.         PANEL_SHOW_RANGE,    FALSE,
  686.         PANEL_SLIDER_WIDTH, MAX_GAIN + 1,
  687.         PANEL_NOTIFY_PROC, main_record_volume_proc,
  688.         PANEL_NOTIFY_LEVEL, PANEL_ALL,
  689.         PANEL_SHOW_RANGE,   FALSE,
  690.         PANEL_SHOW_VALUE,   TRUE,
  691.         0);
  692.  
  693.     Main_playport_item = xv_create(Main_panel, PANEL_CHOICE,
  694.         XV_X, xv_col(Main_panel, 14),
  695.         XV_Y, xv_row(Main_panel, 2),
  696.         PANEL_LABEL_STRING,    "Output to:",
  697.         PANEL_CHOICE_STRINGS, "Speaker", "Jack", 0,
  698.         PANEL_NOTIFY_PROC, control_output_proc,
  699.         0);
  700.  
  701.     Loop_item = xv_create(Main_panel, PANEL_CHOICE,
  702.         XV_X, xv_col(Main_panel, 14),
  703.         XV_Y, xv_row(Main_panel, 3),
  704.         PANEL_LABEL_STRING, "Looping:  ",
  705.         PANEL_CHOICE_STRINGS, "Off    ", "On", 0,
  706.         PANEL_NOTIFY_PROC, control_loop_proc,
  707.         0);
  708.  
  709.     /*
  710.      * Set initial values for volume and output from Audio_state,
  711.      * which should already have been initialized from the device state.
  712.      */
  713.     main_update_panel(TRUE);
  714.     window_fit_width(Main_panel);
  715. }
  716.  
  717. /* Routines that implement the file control panel */
  718.  
  719. /* Load button pressed */
  720. /*ARGSUSED*/
  721. file_load_proc(item, event)
  722.     Panel_item    item;
  723.     Event        *event;
  724. {
  725.     EVENTP("file_load_proc", event);
  726.     if (Active_flag & RECORD)
  727.         return;            /* no-op during record */
  728.     soundfile_read();        /* read in the new file */
  729.     waveform_repaint();
  730. }
  731.  
  732. /* Store button pressed */
  733. /*ARGSUSED*/
  734. file_store_proc(item, event)
  735.     Panel_item    item;
  736.     Event        *event;
  737. {
  738.     EVENTP("file_store_proc", event);
  739.  
  740.     if (Buffer.hdr.data_size == 0) {
  741.         message_display("No sound data to store.");
  742.         return;
  743.     }
  744.     soundfile_write(FALSE);
  745. }
  746.  
  747. /* Append button pressed */
  748. /*ARGSUSED*/
  749. file_append_proc(item, event)
  750.     Panel_item    item;
  751.     Event        *event;
  752. {
  753.     EVENTP("file_append_proc", event);
  754.  
  755.     if (Buffer.hdr.data_size == 0) {
  756.         message_display("No sound data to append.");
  757.         return;
  758.     }
  759.     soundfile_write(TRUE);
  760. }
  761.  
  762. /* Event handler for 'File:' item */
  763. Panel_setting 
  764. file_name_proc(item, event)
  765.     Panel_item    item;
  766.     Event        *event;
  767. {
  768.     EVENTP("file_name_proc", event);
  769.  
  770.     if (event_action(event) == ACTION_MENU && event_is_down(event)) {
  771.         file_name_menu(event); 
  772.     }
  773.     else {
  774.         (void) panel_default_handle_event(item, event);
  775.     }
  776.     return (panel_text_notify(item, event));
  777. }
  778.  
  779. file_wait_cursor()
  780. {
  781.     if(File_save_cursor == NULL) {
  782.         File_save_cursor =
  783.             cursor_copy((Xv_Cursor) xv_get(File_panel, WIN_CURSOR));
  784.     }
  785.  
  786.     xv_set(File_panel, WIN_CURSOR, File_wait_cursor, 0);
  787. }
  788.  
  789. file_restore_cursor()
  790. {
  791.     xv_set(File_panel, WIN_CURSOR, File_save_cursor, 0);
  792. }
  793.  
  794. /* Error handler, just like it says */
  795. file_error_handler(error_str)
  796.     char *error_str;
  797. {
  798.     Xv_Cursor curs;
  799.  
  800.     generic_notice(error_str);
  801.     curs = xv_get(File_panel, WIN_CURSOR);
  802.     if(curs == File_wait_cursor) xv_set(File_panel, WIN_CURSOR, File_save_cursor, 0);
  803. }
  804.  
  805. file_menu_proc(menu, menu_item)
  806.     Menu menu;
  807.     Menu_item menu_item;
  808. {
  809.     xv_set(File_name_item,
  810.         PANEL_VALUE, xv_get(menu_item, MENU_STRING),
  811.         0);
  812. }
  813.  
  814. /*
  815.  * File selector routine called by scandir().
  816.  * Return TRUE if filename is an audio file  or filename ends in
  817.  * ".au" or ".snd".
  818.  */
  819. int
  820. file_select(entry)
  821.     struct direct    *entry;
  822. {
  823.     char        *ptr;
  824.     char        tmp[MAXPATHLEN];
  825.  
  826.     if ((strcmp(entry->d_name, ".") == 0) ||
  827.         (strcmp(entry->d_name, "..") == 0))
  828.         return (FALSE);
  829.  
  830.     /* Check for .au or .snd filename extensions */
  831.     ptr = rindex(entry->d_name, '.');
  832.     if ((ptr != NULL) &&
  833.         ((strcmp(ptr, ".au") == 0) || (strcmp(ptr, ".snd") == 0)))
  834.         return (TRUE);
  835.  
  836.     /* Check for audio file header */
  837.     if (Buffer.directory[0] == '\0')
  838.         STRCPY(Buffer.directory, ".");
  839.     SPRINTF(tmp, "%s/%s", Buffer.directory, entry->d_name);
  840.     return (audio_isaudiofile(tmp));
  841. }
  842.  
  843. /* Build and display a menu of audio files in the current directory */
  844. file_name_menu(event)
  845.     Event        *event;
  846. {
  847.     int        i;
  848.     int        selection;
  849.     int        count;
  850.     int        columns;
  851.     struct stat    st;
  852.     Menu        menu;
  853.     Menu_item mi;
  854.  
  855.     EVENTP("file_name_menu", event);
  856.  
  857.     /* Get the directory name in case it changed. */
  858.     STRCPY(Buffer.directory, (char *) xv_get(File_directory_item, PANEL_VALUE));
  859.  
  860.     if (Buffer.directory[0] == '\0') STRCPY(Buffer.directory, ".");
  861.  
  862.     /* If invalid directory, don't bother screwing around */
  863.     if (stat(Buffer.directory, &st) < 0 || !(S_ISDIR(st.st_mode))) {
  864.         file_error_handler("Bad directory entry");
  865.         return;
  866.     }
  867.  
  868.     /* If there's already a cached menu, check that it's still good */
  869.     if (Buffer.menu.menu != NULL) {
  870.         if ((strcmp(Buffer.directory, Buffer.menu.directory) != 0) ||
  871.             (st.st_mtime != Buffer.menu.modified)) {
  872.             /* Directory changed...throw away old menu */
  873.             if (Buffer.menu.files != NULL) {
  874.                 (void) free((char *)Buffer.menu.files);
  875.                 Buffer.menu.files = NULL;
  876.             }
  877.             xv_destroy(Buffer.menu.menu);
  878.             Buffer.menu.menu = NULL;
  879.         }
  880.     }
  881.  
  882.     if (Buffer.menu.menu == NULL) {
  883.         /* Change the cursor to an hourglass and build a new menu */
  884.         file_wait_cursor();
  885.         Buffer.menu.modified = st.st_mtime;
  886.         STRCPY(Buffer.menu.directory, Buffer.directory);
  887.         count = 
  888.             scandir(Buffer.menu.directory, &Buffer.menu.files, file_select, alphasort);
  889.  
  890.         if (count < 0) Buffer.menu.files = NULL;
  891.  
  892.         /* If no files found, make a non-selectable menu item */
  893.         if (count <= 0) {
  894.             file_error_handler("No audio files in this directory");
  895.             return;
  896.         }
  897.             
  898.         Buffer.menu.menu = xv_create(NULL, MENU,
  899.             MENU_TITLE_ITEM, "Files:",
  900.             MENU_NOTIFY_PROC, file_menu_proc,
  901.         /*    MENU_RELEASE, */
  902.             0);
  903.  
  904.         /* Add a menu item for each audio file found */
  905.         for (i = 0; i < count; i++) {
  906.             mi = (Menu_item) xv_create(XV_NULL, 
  907.                 MENUITEM,
  908.                     MENU_STRING, Buffer.menu.files[i]->d_name,
  909.                     MENU_RELEASE,
  910.                     MENU_RELEASE_IMAGE,
  911.                     MENU_NOTIFY_PROC, file_menu_proc,
  912.                     0,
  913.                 0);
  914.  
  915.             (void) xv_set(Buffer.menu.menu, MENU_APPEND_ITEM, mi, 0);
  916.         }
  917.  
  918.         /*
  919.          * Calculate the number of columns in the menu.  We want a menu
  920.          * that is approximately square.  We assume that the typical
  921.          * filename is 12 characters long and that the intercharacter
  922.          * distance is twice the interline distance.
  923.          */
  924.         columns = sqrt((double)i / 6);
  925.         (void) xv_set(Buffer.menu.menu, MENU_NCOLS, columns, 0);
  926.  
  927.         /* Restore the cursor */
  928.         file_restore_cursor();
  929.     }
  930.  
  931.     /* Display menu, get selection (if any), and keep the menu cached */
  932.     menu_show(Buffer.menu.menu, File_panel, event, 0); 
  933.  
  934. }
  935.  
  936. file_create_panel()
  937. {
  938.     extern char *environ[];
  939.  
  940.     File_panel = xv_create(Base_frame, PANEL,
  941.         WIN_ROWS,        6,
  942.         WIN_RIGHT_OF,    Main_panel,
  943.         /*
  944.         CANVAS_AUTO_EXPAND,    TRUE,
  945.         */
  946.         0);
  947.  
  948.     (void) xv_create(File_panel, PANEL_BUTTON,
  949.         XV_X,    xv_col(File_panel, 0),
  950.         XV_Y,    xv_row(File_panel, 0),
  951.         PANEL_LABEL_STRING, "Load",
  952.         PANEL_NOTIFY_PROC, file_load_proc,
  953.         0);
  954.  
  955.     (void) xv_create(File_panel, PANEL_BUTTON,
  956.         XV_X,    xv_col(File_panel, 0),
  957.         XV_Y,    xv_row(File_panel, 1),
  958.         PANEL_LABEL_STRING, "Store",
  959.         PANEL_NOTIFY_PROC, file_store_proc,
  960.         0);
  961.  
  962.     (void) xv_create(File_panel, PANEL_BUTTON,
  963.         XV_X,    xv_col(File_panel, 0),
  964.         XV_Y,    xv_row(File_panel, 2),
  965.         PANEL_LABEL_STRING, "Append",
  966.         PANEL_NOTIFY_PROC, file_append_proc,
  967.         0);
  968.  
  969.     File_directory_item = xv_create(File_panel, PANEL_TEXT,
  970.         XV_X,    xv_col(File_panel, 0),
  971.         XV_Y,    xv_row(File_panel, 3),
  972.         PANEL_VALUE, getenv("PWD="),
  973.         PANEL_VALUE_STORED_LENGTH, 80,
  974.         PANEL_VALUE_DISPLAY_LENGTH, 30,
  975.         PANEL_LABEL_STRING, "Directory: ",
  976.         0);
  977.  
  978.     File_name_item = xv_create(File_panel, PANEL_TEXT,
  979.         XV_X,    xv_col(File_panel, 0),
  980.         XV_Y,    xv_row(File_panel, 4),
  981.         PANEL_VALUE_STORED_LENGTH, 80,
  982.         PANEL_VALUE_DISPLAY_LENGTH, 30,
  983.         PANEL_LABEL_STRING, "File: ",
  984.         PANEL_EVENT_PROC,    file_name_proc,
  985.         0);
  986.     
  987.     File_wait_cursor = xv_create(File_panel, CURSOR,
  988.         CURSOR_IMAGE, hglass_cursor_image,
  989.         0);
  990.  
  991. }
  992.  
  993. /* Allocate a buffer to hold data */
  994. alloc_buffer(size)
  995.     unsigned    size;
  996. {
  997.     if (Buffer.data != NULL)
  998.         (void) free((char *)Buffer.data);
  999.  
  1000.     /* allocate a buffer, shrinking if request is too big */
  1001.     do {
  1002.         Buffer.data = (unsigned char *)malloc(size);
  1003.     } while ((Buffer.data == NULL) && (size = size - (size / 8)));
  1004.  
  1005.     Buffer.alloc_size = size;
  1006. }
  1007.  
  1008. /* Initialize the buffer */
  1009. init_buffer()
  1010. {
  1011.     if (Buffer.data != NULL)
  1012.         (void) free((char *)Buffer.data);
  1013.     Buffer.data = NULL;
  1014.     Buffer.alloc_size = 0;
  1015.     Buffer.hdr = Device_phdr;
  1016.     Buffer.hdr.data_size = 0;
  1017. }
  1018.  
  1019. /*
  1020.  * Convert the file panel directory and filename values to a path.
  1021.  * Return TRUE if no filename is specified.
  1022.  */
  1023. int
  1024. soundfile_path(str)
  1025.     char    *str;
  1026. {
  1027.     STRCPY(Buffer.directory, (char *) xv_get(File_directory_item, PANEL_VALUE));
  1028.     STRCPY(Buffer.filename, (char *) xv_get(File_name_item, PANEL_VALUE));
  1029.  
  1030.     if (Buffer.filename[0] == '\0')
  1031.         return (TRUE);
  1032.  
  1033.     /* Need to do this in case user cleared the directory string */
  1034.     if (Buffer.directory[0] == '\0')
  1035.         STRCPY(Buffer.directory, ".");
  1036.  
  1037.     SPRINTF(str, "%s/%s", Buffer.directory, Buffer.filename);
  1038.     return (FALSE);
  1039. }
  1040.  
  1041. /* Open the named sound file and read it into memory.  Return TRUE if error. */
  1042. int
  1043. soundfile_read()
  1044. {
  1045.     int        fd;
  1046.     unsigned    size;
  1047.     int        valid;
  1048.     char        msg[256];
  1049.     char        path[MAXPATHLEN];
  1050.     struct stat    st;
  1051.  
  1052.     /* Get the soundfile path and make sure there's a filename */
  1053.     if (soundfile_path(path)) {
  1054.         message_display("No filename specified.");
  1055.         return;
  1056.     }
  1057.  
  1058.     DEBUGF(("Load file >%s<\n", path));
  1059.  
  1060.     if (((fd = open(path, O_RDONLY)) < 0) || (fstat(fd, &st) < 0)) {
  1061.         SPRINTF(msg, "Can't read '%s' (%s).", path,
  1062.             sys_errlist[errno]);
  1063.         message_display(msg);
  1064.         return;
  1065.     }
  1066.  
  1067.     /*
  1068.      * If the soundfile has a header, read it in and decode it.
  1069.      */
  1070.     valid = (AUDIO_SUCCESS == audio_read_filehdr(fd, &Buffer.hdr,
  1071.         Buffer.info, sizeof (Buffer.info)));
  1072.     if (valid) {
  1073.         if (Buffer.hdr.data_size == AUDIO_UNKNOWN_SIZE) {
  1074.             /* Calculate the data size, if not already known */
  1075.             Buffer.hdr.data_size =
  1076.                 st.st_size - lseek(fd, 0L, L_INCR);
  1077.         }
  1078.     }
  1079.     else {
  1080.         /* If no header, read the file raw and assume compatibility */
  1081.         Buffer.hdr  = Device_phdr;    /* use device configuration */
  1082.         (void) lseek(fd, 0L, L_SET);    /* rewind file */
  1083.         Buffer.hdr.data_size = st.st_size - lseek(fd, 0L, L_INCR);
  1084.         Buffer.info[0] = '\0';
  1085.     }
  1086.  
  1087.     /* Set info string in display */
  1088.     
  1089.     xv_set(Describe_info_item,
  1090.         PANEL_VALUE, Buffer.info,
  1091.         0);
  1092.  
  1093.     /*
  1094.      * If active output, set draining flag so that play_service() won't
  1095.      * try to access the obsolete buffer.  file_update() will turn
  1096.      * off the draining flag if output is still active.
  1097.      */
  1098.     if (Active_flag & PLAY)
  1099.         Buffer.draining = TRUE;
  1100.  
  1101.     /* Release the old buffer and allocate a new one to hold the data */
  1102.     size = Buffer.hdr.data_size;
  1103.     alloc_buffer(size);
  1104.  
  1105.     /* Read in as much data as possible and close the file. */
  1106.     Buffer.hdr.data_size = read(fd,
  1107.         (char *)Buffer.data, (int)Buffer.alloc_size);
  1108.     (void) close(fd);
  1109.  
  1110.     Buffer.display.start = Buffer.play.start = 0;
  1111.     Buffer.display.end = Buffer.play.end = Buffer.hdr.data_size;
  1112.     file_update();            /* display new file */
  1113.  
  1114.     /* If we could not allocate or load the whole file, show msg */
  1115.     if (size != Buffer.hdr.data_size) {
  1116.         SPRINTF(msg, "%.2f seconds of data truncated from '%s'.",
  1117.             audio_bytes_to_secs(&Buffer.hdr,
  1118.             (size - Buffer.hdr.data_size)), path);
  1119.         message_display(msg);
  1120.     }
  1121.  
  1122.     /* If file is not an audio file, display a warning */
  1123.     if (!valid) {
  1124.         SPRINTF(msg, "'%s' is not a valid audio file.  %s\n", path,
  1125.             "STORE will convert it to audio file format.");
  1126.         message_display(msg);
  1127.     }
  1128.     else if ((Buffer.hdr.encoding != AUDIO_ENCODING_ULAW) ||
  1129.         (Buffer.hdr.bytes_per_unit != 1) ||
  1130.         (Buffer.hdr.samples_per_unit != 1) ||
  1131.         (Buffer.hdr.channels != 1)) {
  1132.         SPRINTF(msg,
  1133.         "'%s' audio encoding cannot be played or displayed properly.\n",
  1134.             path);
  1135.         message_display(msg);
  1136.     }
  1137. }
  1138.  
  1139. /* Write or append to the named audio file */
  1140. soundfile_write(append)
  1141.     int        append;        /* TRUE if append to existing file */
  1142. {
  1143.     int        fd;
  1144.     int        bytes;
  1145.     int        exists;
  1146.     int        err;
  1147.     char        *info;
  1148.     struct stat    st;
  1149.     char        msg[256];
  1150.     char        path[MAXPATHLEN];
  1151.     Audio_hdr    tmphdr;
  1152.  
  1153.     bytes = buffer_selected_size();
  1154.     if (bytes <= 0)
  1155.         return;
  1156.  
  1157.     /* Get the soundfile path and make sure there's a filename */
  1158.     if (soundfile_path(path)) {
  1159.         message_display("No filename specified.");
  1160.         return;
  1161.     }
  1162.     exists = (stat(path, &st) == 0);
  1163.     if (!exists)
  1164.         append = FALSE;
  1165.  
  1166.     DEBUGF(("%s (%d bytes) to file >%s<\n",
  1167.         (append ? "Appending" : "Writing"), bytes, Buffer.filename));
  1168.  
  1169.     if (append) {
  1170.         /* Append to existing file */
  1171.         if ((fd = open(path, O_RDWR)) < 0) {
  1172.             SPRINTF(msg, "Can't open '%s' (%s).",
  1173.                 Buffer.filename, sys_errlist[errno]);
  1174.             message_display(msg);
  1175.             return;
  1176.         }
  1177.  
  1178.         /* Make sure this is already an audio file */
  1179.         if (!S_ISREG(st.st_mode) ||
  1180.             (audio_read_filehdr(fd, &tmphdr, (char *)NULL, 0) !=
  1181.             AUDIO_SUCCESS)) {
  1182.             SPRINTF(msg, "'%s' is not a valid audio file.",
  1183.                 Buffer.filename, sys_errlist[errno]);
  1184.             message_display(msg);
  1185.             goto closerr;
  1186.         }
  1187.  
  1188.         if ((int)lseek(fd, st.st_size, L_SET) < 0)
  1189.             goto writerr;
  1190.  
  1191.     }
  1192.     else {
  1193.         /* Create new file */
  1194.         if (exists) {
  1195.             if (!message_confirm(
  1196.                 "Existing file will be overwritten."))
  1197.                 goto closerr;
  1198.         }
  1199.  
  1200.         /* Get current info string */
  1201.         info = (char *) xv_get(Describe_info_item, PANEL_VALUE);
  1202.  
  1203.         fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
  1204.  
  1205.         /* write an audio file header first */
  1206.         tmphdr = Buffer.hdr;
  1207.         tmphdr.data_size = bytes;
  1208.         if ((fd < 0) || (audio_write_filehdr(fd, &tmphdr,
  1209.             info, ((unsigned)strlen(info) + 1)) != AUDIO_SUCCESS))
  1210.             goto writerr;
  1211.     }
  1212.  
  1213.     err = (write(fd, (char *)&Buffer.data[Buffer.play.start], bytes) !=
  1214.         bytes);
  1215.     if (append && !err) {
  1216.         if ((tmphdr.data_size != AUDIO_UNKNOWN_SIZE) &&
  1217.             (audio_rewrite_filesize(fd, (tmphdr.data_size + bytes)) !=
  1218.             AUDIO_SUCCESS))
  1219.             err++;
  1220.     }
  1221.  
  1222.     if (err) {
  1223. writerr:
  1224.         SPRINTF(msg, "Can't %s to '%s' (%s).",
  1225.             (append ? "append" : "write"), Buffer.filename,
  1226.             sys_errlist[errno]);
  1227.         message_display(msg);
  1228.     }
  1229. closerr:
  1230.     (void) close(fd);
  1231. }
  1232.  
  1233. /* Routines that implement the file description pop-up panel */
  1234.  
  1235. /* Called when the describe pop-up is destroyed */
  1236. /*ARGSUSED*/
  1237. Notify_value
  1238. describe_destroy_proc(frame)
  1239.     Frame        *frame;
  1240. {
  1241.     (void) xv_set(Describe_frame, WIN_SHOW, FALSE, 0);
  1242.     Show_describe = FALSE;
  1243.     return (NOTIFY_DONE);
  1244. }
  1245.  
  1246. /* Null event proc to make items readonly */
  1247. /*ARGSUSED*/
  1248. null_panel_event(item, event)
  1249.     Panel_item    item;
  1250.     Event        *event;
  1251. {
  1252. }
  1253.  
  1254. /* Initialize the pop-up panel */
  1255. describe_create_panel()
  1256. {
  1257. #define    DWIDTH    25
  1258.  
  1259.     Describe_frame = xv_create(Base_frame, FRAME,
  1260.         XV_LABEL,        "           Audio Buffer Status",
  1261.         FRAME_SHOW_LABEL,        TRUE,
  1262.         FRAME_DONE_PROC,        describe_destroy_proc,
  1263.         XV_LEFT_MARGIN,        10,
  1264.         XV_WIDTH,            xv_col(Base_frame, 15 + DWIDTH),
  1265.         0);
  1266.  
  1267.     Describe_panel = xv_create(Describe_frame, PANEL,
  1268.         0);
  1269.  
  1270.     Describe_sample_item = xv_create(Describe_panel, PANEL_TEXT,
  1271.         XV_X,        xv_col(Describe_panel, 0),
  1272.         XV_Y,        xv_row(Describe_panel, 0),
  1273.         PANEL_VALUE_X,        xv_col(Describe_panel, 15),
  1274.         PANEL_VALUE_DISPLAY_LENGTH,    DWIDTH,
  1275.         PANEL_LABEL_STRING,        "Sample Rate:  ",
  1276.         PANEL_EVENT_PROC,        null_panel_event,
  1277.         0);
  1278.  
  1279.     Describe_channel_item = xv_create(Describe_panel, PANEL_TEXT,
  1280.         XV_X,        xv_col(Describe_panel, 0),
  1281.         XV_Y,        xv_row(Describe_panel, 1),
  1282.         PANEL_VALUE_X,        xv_col(Describe_panel, 15),
  1283.         PANEL_VALUE_DISPLAY_LENGTH,    DWIDTH,
  1284.         PANEL_LABEL_STRING,        "Channels:     ",
  1285.         PANEL_EVENT_PROC,        null_panel_event,
  1286.         0);
  1287.  
  1288.     Describe_bits_item = xv_create(Describe_panel, PANEL_TEXT,
  1289.         XV_X,        xv_col(Describe_panel, 0),
  1290.         XV_Y,        xv_row(Describe_panel, 2),
  1291.         PANEL_VALUE_X,        xv_col(Describe_panel, 15),
  1292.         PANEL_VALUE_DISPLAY_LENGTH,    DWIDTH,
  1293.         PANEL_LABEL_STRING,        "Precision:    ",
  1294.         PANEL_EVENT_PROC,        null_panel_event,
  1295.         0);
  1296.  
  1297.     Describe_encoding_item = xv_create(Describe_panel, PANEL_TEXT,
  1298.         XV_X,        xv_col(Describe_panel, 0),
  1299.         XV_Y,        xv_row(Describe_panel, 3),
  1300.         PANEL_VALUE_X,        xv_col(Describe_panel, 15),
  1301.         PANEL_VALUE_DISPLAY_LENGTH,    DWIDTH,
  1302.         PANEL_LABEL_STRING,        "Encoding:     ",
  1303.         PANEL_EVENT_PROC,        null_panel_event,
  1304.         0);
  1305.  
  1306.     Describe_length_item = xv_create(Describe_panel, PANEL_TEXT,
  1307.         XV_X,        xv_col(Describe_panel, 0),
  1308.         XV_Y,        xv_row(Describe_panel, 4),
  1309.         PANEL_VALUE_X,        xv_col(Describe_panel, 15),
  1310.         PANEL_VALUE_DISPLAY_LENGTH,    DWIDTH,
  1311.         PANEL_LABEL_STRING,        "Total Length: ",
  1312.         PANEL_EVENT_PROC,        null_panel_event,
  1313.         0);
  1314.  
  1315.     Describe_delta_item = xv_create(Describe_panel, PANEL_TEXT,
  1316.         XV_X,        xv_col(Describe_panel, 0),
  1317.         XV_Y,        xv_row(Describe_panel, 5),
  1318.         PANEL_VALUE_X,        xv_col(Describe_panel, 15),
  1319.         PANEL_VALUE_DISPLAY_LENGTH,    DWIDTH,
  1320.         PANEL_LABEL_STRING,        "Selection:    ",
  1321.         PANEL_EVENT_PROC,        null_panel_event,
  1322.         0);
  1323.  
  1324.     Describe_info_item = xv_create(Describe_panel, PANEL_TEXT,
  1325.         XV_X,        xv_col(Describe_panel, 0),
  1326.         XV_Y,        xv_row(Describe_panel, 6),
  1327.         PANEL_VALUE_X,        xv_col(Describe_panel, 15),
  1328.         PANEL_VALUE_STORED_LENGTH,    (INFO_SIZE - 1),
  1329.         PANEL_VALUE_DISPLAY_LENGTH,    DWIDTH,
  1330.         PANEL_LABEL_STRING,        "Info string:  ",
  1331.         0);
  1332.  
  1333.     /* Put the caret on the info string */
  1334.     (void) panel_backup_caret(Describe_panel);
  1335.  
  1336.     
  1337.     window_fit(Describe_panel);
  1338.     window_fit(Describe_frame);
  1339. }
  1340.  
  1341. /*
  1342.  * Update the description panel with the description of the file we have
  1343.  * in memory.
  1344.  * If 'init' is TRUE, set all values.  Otherwise, just set length.
  1345.  */
  1346. describe_update_panel(init)
  1347.     int    init;
  1348. {
  1349.     char    sample_string[80];
  1350.     char    bit_string[80];
  1351.     char    channel_string[80];
  1352.     char    encoding_string[80];
  1353.     char    length_string[80];
  1354.  
  1355.     /* If not visible, don't bother */
  1356.     if (!Show_describe)
  1357.         return;
  1358.  
  1359.     if (init) {
  1360.         SPRINTF(sample_string, "%d", Buffer.hdr.sample_rate);
  1361.         SPRINTF(bit_string, "%d", ((8 * Buffer.hdr.bytes_per_unit) /
  1362.             Buffer.hdr.samples_per_unit));
  1363.         SPRINTF(channel_string, "%d", Buffer.hdr.channels);
  1364.  
  1365.         switch (Buffer.hdr.encoding) {
  1366.         case AUDIO_ENCODING_ULAW:
  1367.             STRCPY(encoding_string, "u-law");
  1368.             break;
  1369.         case AUDIO_ENCODING_ALAW:
  1370.             STRCPY(encoding_string, "A-law");
  1371.             break;
  1372.         default:
  1373.             SPRINTF(encoding_string, "unknown (%d)",
  1374.                 Buffer.hdr.encoding);
  1375.             break;
  1376.         }
  1377.  
  1378.         xv_set(Describe_sample_item,
  1379.             PANEL_VALUE, sample_string,
  1380.             0);
  1381.                 
  1382.         xv_set(Describe_bits_item,
  1383.             PANEL_VALUE, bit_string,
  1384.             0);
  1385.                 
  1386.         xv_set(Describe_channel_item,
  1387.             PANEL_VALUE, channel_string,
  1388.             0);
  1389.                 
  1390.         xv_set(Describe_encoding_item,
  1391.             PANEL_VALUE, encoding_string,
  1392.             0);
  1393.     }
  1394.     if (Buffer.hdr.data_size == 0) {
  1395.         STRCPY(length_string, "(buffer empty)");
  1396.     }
  1397.     else {
  1398.         char    tmp[AUDIO_MAX_TIMEVAL];
  1399.  
  1400.         SPRINTF(length_string, "%s",
  1401.             audio_secs_to_str(audio_bytes_to_secs(&Buffer.hdr,
  1402.             Buffer.hdr.data_size), tmp, 2));
  1403.     }
  1404.     
  1405.     xv_set(Describe_length_item, 
  1406.         PANEL_VALUE, length_string,
  1407.         0);
  1408. }
  1409.  
  1410. /* Routines that implement the waveform display control panel */
  1411.  
  1412. /* Zoom slider value changed */
  1413. /*ARGSUSED*/
  1414. waveform_zoom_proc(item, value, event)
  1415.     Panel_item    item;
  1416.     int        value;
  1417.     Event        *event;
  1418. {
  1419.     EVENTP("waveform_zoom_proc", event);
  1420.  
  1421.     if (value != Zoom) {
  1422.         waveform_set_zoom((unsigned)value);
  1423.     }
  1424. }
  1425.  
  1426. /* Initalize the waveform control panel */
  1427. waveform_create_panel()
  1428. {
  1429.     Waveform_panel = xv_create(Base_frame, PANEL,
  1430.         WIN_BELOW,    Main_panel,
  1431.         WIN_X,    0,
  1432.         0);
  1433.  
  1434.     Waveform_zoom_item = xv_create(Waveform_panel, PANEL_SLIDER,
  1435.         PANEL_LABEL_X,    xv_col(Waveform_panel, 25),
  1436.         PANEL_LABEL_STRING, "Zoom",
  1437.         PANEL_MIN_VALUE,    1,
  1438.         PANEL_MAX_VALUE,    2,
  1439.         PANEL_VALUE,    2,
  1440.         PANEL_SHOW_VALUE,    TRUE,
  1441.         PANEL_SHOW_RANGE,    FALSE,
  1442.         PANEL_SLIDER_WIDTH,    WINDOW_WIDTH - SCOPE_WIDTH - 100,
  1443.         PANEL_NOTIFY_PROC,    waveform_zoom_proc,
  1444.         PANEL_NOTIFY_LEVEL, PANEL_ALL,
  1445.         0);
  1446.  
  1447.     window_fit_height(Waveform_panel);
  1448. }
  1449.  
  1450. /* Routines that implement the oscilloscope display canvas */
  1451.  
  1452. /* Called to repair damage to the scope canvas */
  1453. /*ARGSUSED*/
  1454. scope_repaint_proc(canvas, pw, repaint_area)
  1455.     Canvas        canvas;
  1456.     Pixwin        *pw;
  1457.     Rectlist    *repaint_area;
  1458. {
  1459.     DEBUGF(("Repaint scope from %d, %d to %d, %d\n",
  1460.         repaint_area->rl_bound.r_left,
  1461.         repaint_area->rl_bound.r_top,
  1462.         repaint_area->rl_bound.r_width,
  1463.         repaint_area->rl_bound.r_height));
  1464.  
  1465.     /* We could speed this up by just redrawing the damaged area. */
  1466.     update_scope();
  1467. }
  1468.  
  1469. /* Initialize the oscilloscope canvas */
  1470. scope_create_canvas()
  1471. {
  1472.     Scope_canvas = xv_create(Base_frame, CANVAS,
  1473.         CANVAS_WIDTH,        SCOPE_WIDTH,
  1474.         CANVAS_HEIGHT,        SCOPE_HEIGHT,
  1475.         XV_WIDTH,            SCOPE_WIDTH,
  1476.         XV_HEIGHT,            SCOPE_HEIGHT,
  1477.         CANVAS_REPAINT_PROC,    scope_repaint_proc,
  1478.         WIN_BELOW,            Waveform_panel,
  1479.         0);
  1480.  
  1481.     sc_display = (Display *) xv_get(Base_frame, XV_DISPLAY);
  1482.     sc_drawable = (Drawable) xv_get(Base_frame, XV_XID);
  1483.     sc_screen = (Xv_Screen) xv_get(Base_frame, XV_SCREEN);
  1484.     sc_screen_no = xv_get(sc_screen, SCREEN_NUMBER);
  1485.     sc_gcval.foreground = BlackPixel(sc_display, sc_screen_no);
  1486.     sc_gcval.background = WhitePixel(sc_display, sc_screen_no);
  1487.     sc_gc = XCreateGC(sc_display, sc_drawable, GCForeground|GCBackground,
  1488.         &sc_gcval);
  1489. }
  1490.  
  1491. /* Clear the scope canvas */
  1492. scope_clear()
  1493. {
  1494.     (void) pw_writebackground(canvas_pixwin(Scope_canvas),
  1495.         0, 0, SCOPE_WIDTH, SCOPE_HEIGHT, PIX_SRC);
  1496.  
  1497.     /* reset counter to initiate waveform refresh on record */
  1498.     Refresh_ctr = 1;
  1499. }
  1500.  
  1501. /*
  1502.  * Redraw the scope canvas, getting data at the specified buffer offset.
  1503.  * If the cursor is active over the waveform panel, show it's position.
  1504.  */
  1505. scope_display(off)
  1506.     int        off;
  1507. {
  1508.     int        from;
  1509.     int        to;
  1510.     int        i;
  1511.     Pixwin        *pw;
  1512.     Rect        r;
  1513.     Pr_brush    brush;
  1514.  
  1515.  
  1516.     r.r_left = 0;
  1517.     r.r_top = 0;
  1518.     r.r_width = SCOPE_WIDTH;
  1519.     r.r_height = SCOPE_HEIGHT;
  1520.  
  1521.     pw = canvas_pixwin(Scope_canvas);
  1522.  
  1523.  
  1524.     (void) pw_writebackground(pw, 0, 0, SCOPE_WIDTH, SCOPE_HEIGHT, PIX_SRC);
  1525.  
  1526.     /* If the cursor is being displayed, show it in the scope, too */
  1527.     if (Buffer.display.cursor_pos > 0) {
  1528.         brush.width = 1;
  1529.         i = Buffer.display.cursor_pos - off;
  1530.         (void) pw_line(pw, i, 0, i, SCOPE_HEIGHT,
  1531.             &brush, &solid, PIX_SET);
  1532.     }
  1533.  
  1534.     /* If the start or end markers are in this region, show them */
  1535.     i = Buffer.display.start - off;
  1536.     if ((i > 0) && (i < SCOPE_WIDTH)) {
  1537.         brush.width = 3;
  1538.         (void) pw_line(pw, i, 0, i, SCOPE_HEIGHT,
  1539.             &brush, &dashed, PIX_SET);
  1540.     }
  1541.     i = Buffer.display.end - off;
  1542.     if ((i > 0) && (i < SCOPE_WIDTH)) {
  1543.         brush.width = 3;
  1544.         (void) pw_line(pw, i, 0, i, SCOPE_HEIGHT,
  1545.             &brush, &dotted, PIX_SET);
  1546.     }
  1547.  
  1548.     /* If the start point is before the data start, adjust the start */
  1549.     i = 0;
  1550.     if (off < 0) {
  1551.         i = -off;
  1552.         off = 0;
  1553.     }
  1554.     from = 127 - audio_u2c(Buffer.data[off++]);
  1555.     for (; i < SCOPE_WIDTH; i++) {
  1556.         if (off >= Buffer.hdr.data_size) {
  1557.             break;
  1558.         }
  1559.         to = 127 - audio_u2c(Buffer.data[off++]);
  1560.         (void) pw_vector(pw, i, from, (i + 1), to, PIX_SET, 1);
  1561.         from = to;
  1562.     }
  1563.  
  1564. }
  1565.  
  1566. /* Calculate a new range to display on the scope and repaint it */
  1567. update_scope()
  1568. {
  1569.     int        display_position;
  1570.     unsigned    s;
  1571.     int         i;
  1572.     short        try;
  1573.     short        last;
  1574.  
  1575.     if (Buffer.hdr.data_size == 0)
  1576.         return;
  1577.  
  1578.     if (Active_flag & RECORD) {            /* Recording */
  1579.         /*
  1580.          * Display the next piece of data.  We only want to display
  1581.          * full panels of data.
  1582.          */
  1583.         display_position =
  1584.             (Buffer.hdr.data_size - SCOPE_WIDTH) & ~SCOPE_MASK;
  1585.         if (display_position < 0) {
  1586.             display_position = 0;
  1587.         }
  1588.     }
  1589.     else if (Active_flag & PLAY) {        /* Playing */
  1590.         /*
  1591.          * Now display the scope waveform.  We want to display
  1592.          * starting at a multiple of 256 (the scope window size)
  1593.          * to keep repetitive waveforms from jittering.  If we are
  1594.          * not to the next such boundary, then return without
  1595.          * doing anything.
  1596.          */
  1597.         s = 0;
  1598.         (void) audio_get_play_samples(Audio_fd, &s);
  1599.         display_position = (Buffer.play.start +
  1600.             (s % (Buffer.play.end - Buffer.play.start)))
  1601.             & ~SCOPE_MASK;
  1602.     }
  1603.     else {
  1604.         display_position = Buffer.display.last;        /* Inactive */
  1605.         scope_display(display_position);    /* repair damage */
  1606.         return;
  1607.     }
  1608.  
  1609.     /* Look for a rising edge at a zero-crossing */
  1610.     last = audio_u2s(Buffer.data[display_position]);
  1611.     for (i = display_position - 1;
  1612.         (i >= 0) && (i > (display_position - (SCOPE_WIDTH / 2)));
  1613.         i--) {
  1614.         try = audio_u2s(Buffer.data[i]);
  1615.         if ((try < last) && (last >= 0) && (try <= 0)) {
  1616.             if (last < ((try < 0) ? -try : try))
  1617.                 i++;
  1618.             display_position = i;
  1619.             break;
  1620.         }
  1621.         last = try;
  1622.     }
  1623.  
  1624.     if (display_position != Buffer.display.last) {
  1625.         scope_display(display_position);
  1626.         Buffer.display.last = display_position;
  1627.         Refresh_ctr--;
  1628.     }
  1629. }
  1630.  
  1631. /* Routines that implement the waveform display canvas */
  1632.  
  1633. /* Called for all events concerning the waveform canvas */
  1634. /*ARGSUSED*/
  1635. void
  1636. waveform_cursor_proc(canvas, event, arg)
  1637.     Canvas    canvas;
  1638.     Event    *event;
  1639.     caddr_t    arg;
  1640. {
  1641.     int    position;
  1642.     int    cursor_loc = -1;
  1643.     static int    dragwave = -1;
  1644.     static int    dragstart = -1;
  1645.     static int    dragend = -1;
  1646.     static int    dragwhich = -1;
  1647.  
  1648.     EVENTP("waveform_cursor_proc", event);
  1649.  
  1650.     if (vuid_in_range(VUID_SCROLL, event_id(event))) {
  1651.         waveform_scrollbar_proc(position_sb, event);
  1652.         return;
  1653.     }
  1654.  
  1655.     if (Buffer.hdr.data_size == 0) return;
  1656.  
  1657.     EVENTP("waveform_cursor_proc", event);
  1658.  
  1659.     /* Get new mouse position */
  1660.     cursor_loc = event_x(event);
  1661.     position = event_x(event) * Zoom;
  1662.     if (position >= Buffer.hdr.data_size) position = Buffer.hdr.data_size - 1;
  1663.     if (position < 0) position = 0;
  1664.  
  1665.     switch (event_id(event)) {
  1666.     /*
  1667.     case LOC_RGNENTER:
  1668.         break;
  1669.     */
  1670.  
  1671.     case KBD_DONE:
  1672.     /*
  1673.     case LOC_RGNEXIT:
  1674.     */
  1675.         /* If adjusting markers, finish up */
  1676.         if (dragend >= 0) goto dragend_done;
  1677.  
  1678.         Buffer.display.cursor_pos = -1;
  1679.  
  1680.         if ((Active_flag == 0) || Buffer.paused) scope_display(Buffer.display.last);
  1681.         
  1682.         break;
  1683.  
  1684.     /* Ordinary cursor motion in the panel updates the scope */
  1685.     case LOC_MOVE:
  1686.         dragwave = -1;
  1687. locmove:
  1688.         /* Play/Record scope takes priority */
  1689.         if ((Active_flag == 0) || Buffer.paused) {
  1690.             int    half = SCOPE_WIDTH / 2;
  1691.  
  1692.             /* Set a new cursor position */
  1693.  
  1694.             Buffer.display.cursor_pos = (cursor_loc * Zoom);
  1695.  
  1696.             /* Figure out what to display in the scope */
  1697.  
  1698.             Buffer.display.last = Buffer.display.cursor_pos - half;
  1699.  
  1700.             if ((Buffer.display.last + half) > Buffer.hdr.data_size)
  1701.                 Buffer.display.last = Buffer.hdr.data_size - half;
  1702.  
  1703.             scope_display(Buffer.display.last);
  1704.         }
  1705.         break;
  1706.  
  1707.     case LOC_DRAG:
  1708.         /* If dragging a selection, update the scope */
  1709.         if (dragend >= 0) {
  1710.             dragend = position;
  1711.             goto locmove;
  1712.         }
  1713.  
  1714.         if (dragwave >= 0) {
  1715.             /*
  1716.              * move dragwave so that it is under the cursor.
  1717.              */
  1718.             int    newstart,
  1719.                 view_length,
  1720.                 object_size,
  1721.                 view_start;
  1722.  
  1723.             view_length = (int)xv_get(Waveform_canvas, XV_WIDTH);
  1724.             object_size = Buffer.hdr.data_size / Zoom;
  1725.             view_start = Buffer.display.position / Zoom;
  1726.  
  1727.             newstart = dragwave - event_x(event) + view_start;
  1728.  
  1729.             if (newstart < 0) newstart = 0;
  1730.  
  1731.             if ((newstart + view_length) <= object_size)
  1732.                 scrollbar_scroll_to(position_sb, newstart);
  1733.  
  1734.             break;
  1735.         }
  1736.         break;
  1737.  
  1738.     case MS_MIDDLE:
  1739.         if (dragend >= 0) break;
  1740.  
  1741.         if (event_is_down(event)) dragwave = cursor_loc;
  1742.         else dragwave = -1;
  1743.         
  1744.         break;
  1745.  
  1746.     case MS_LEFT:
  1747.     case MS_RIGHT:
  1748.         if (dragwave >= 0) break;
  1749.  
  1750.         /* If down event, set the start point and drag the end point */
  1751.  
  1752.         if (event_is_down(event)) {
  1753.             if (event_id(event) == MS_LEFT) {
  1754.                 Buffer.display.end = -1;
  1755.                 Buffer.display.start = position;
  1756.                 dragwhich = MS_LEFT;
  1757.             }
  1758.             else {
  1759.                 Buffer.display.start = -1;
  1760.                 Buffer.display.end = position;
  1761.                 dragwhich = MS_RIGHT;
  1762.             }
  1763.             dragstart = cursor_loc;
  1764.             dragend = position;
  1765.             waveform_repaint();
  1766.             break;
  1767.         }
  1768.  
  1769.         /* If up event, do nothing if there was no down */
  1770.  
  1771.         if (dragend < 0) break;
  1772.  
  1773.         /* Up event (or exiting the window) ends selection */
  1774.  
  1775. dragend_done:
  1776.  
  1777.         /* Very short selections are probably sloppy mouse clicks */
  1778.  
  1779.         position = abs(cursor_loc - dragstart);
  1780.  
  1781.         if (dragwhich == MS_LEFT) {
  1782.             if (position > 3) Buffer.display.end = dragend;
  1783.             else Buffer.display.end = Buffer.play.end;
  1784.         }
  1785.         else {
  1786.             if (position > 3) Buffer.display.start = dragend;
  1787.             else Buffer.display.start = Buffer.play.start;
  1788.         }
  1789.  
  1790.         dragstart = -1;
  1791.         dragend = -1;
  1792.  
  1793.         /* Swap cursors in case of crossover */
  1794.  
  1795.         if (Buffer.display.start > Buffer.display.end) {
  1796.             position = Buffer.display.start;
  1797.             Buffer.display.start = Buffer.display.end;
  1798.             Buffer.display.end = position;
  1799.         }
  1800.  
  1801.         Buffer.play.start = Buffer.display.start;
  1802.         Buffer.play.end = Buffer.display.end;
  1803.  
  1804.         /*
  1805.          * We could speed this up by just redrawing the cursors.
  1806.          */
  1807.         waveform_repaint();
  1808.  
  1809.         if ((Active_flag == 0) || Buffer.paused) scope_display(Buffer.display.last);
  1810.  
  1811.         /* Turn off looping if very small selection */
  1812.         (void) selectcheck();
  1813.  
  1814.         /* Give Play a chance to sync back up */
  1815.         play_update_cursor();
  1816.  
  1817.         describe_delta_update();
  1818.     default:
  1819.         break;
  1820.     }
  1821. }
  1822.  
  1823. /* Recalculate waveform display parameters when Zoom changes */
  1824. waveform_set_zoom(new_zoom)
  1825.     unsigned    new_zoom;
  1826. {
  1827.     long        obj_length,
  1828.             win_width,
  1829.             view_size,
  1830.             view_start,
  1831.             view_end,
  1832.             cursor_start,
  1833.             cursor_end,
  1834.             disp_center;
  1835.     int        start_visible,
  1836.             end_visible;
  1837.  
  1838.     cursor_start    = Buffer.display.start;
  1839.     cursor_end    = Buffer.display.end;
  1840.     view_start    = Buffer.display.position;
  1841.     win_width    = (int)xv_get(Waveform_canvas, XV_WIDTH);
  1842.     view_size    = win_width * Zoom;
  1843.     view_end    = view_start + view_size;
  1844.     obj_length    = Buffer.hdr.data_size;
  1845.     disp_center    = view_start + (view_size / 2);
  1846.  
  1847.     start_visible = ((cursor_start >= view_start) &&
  1848.         (cursor_start < view_end));
  1849.  
  1850.     end_visible = ((cursor_end >= view_start) && (cursor_end < view_end));
  1851.  
  1852.     /* Recalculate the displayed data descriptors */
  1853.     Zoom = new_zoom;
  1854.     view_size = win_width * Zoom;
  1855.     view_start = disp_center - (view_size / 2);
  1856.     view_end = view_start + view_size;
  1857.  
  1858.     /*
  1859.      * If one of the cursors is on the display, then try to keep
  1860.      * it on the display after zoom changes. The start cursor
  1861.      * has priority over the end cursor.
  1862.      */
  1863.     if (end_visible && (cursor_end > view_end)) {
  1864.         view_start = cursor_end - view_size + 1;
  1865.         view_end = view_start + view_size;
  1866.         disp_center = view_start + (view_size / 2);
  1867.     }
  1868.  
  1869.     if (start_visible && (cursor_start < view_start)) {
  1870.         view_start = cursor_start;
  1871.         view_end = view_start + view_size;
  1872.         disp_center = view_start + (view_size / 2);
  1873.     }
  1874.  
  1875.     /*
  1876.      * If trying to display beyond start or end of data, shift the view.
  1877.      */
  1878.     if (view_end > obj_length) {
  1879.         view_end = obj_length;
  1880.         view_start = view_end - view_size;
  1881.         disp_center = view_start + (view_size / 2);
  1882.     }
  1883.     if (view_start < 0) {
  1884.         view_start = 0;
  1885.         view_end = view_size;
  1886.         disp_center = view_start + (view_size / 2);
  1887.     }
  1888.     Buffer.display.position = view_start;
  1889.  
  1890.     /* Update the scrollbar and canvas offsets */
  1891.     (void) xv_set(position_sb,
  1892.         SCROLLBAR_OBJECT_LENGTH, obj_length / Zoom,
  1893.         SCROLLBAR_VIEW_START, view_start / Zoom,
  1894.         0);
  1895.  
  1896.     pw_set_x_offset(canvas_pixwin(Waveform_canvas), view_start / Zoom);
  1897.  
  1898.     /* This may trigger a repaint of the canvas.  Inhibit it. */
  1899.  
  1900.     Waveform_update_inhibit = TRUE;
  1901.     xv_set(Waveform_canvas,
  1902.         CANVAS_WIDTH, obj_length / Zoom,
  1903.         0);
  1904.  
  1905.     Waveform_update_inhibit = FALSE;
  1906.  
  1907.  
  1908.     /* Now force a repaint */
  1909.     waveform_repaint();
  1910. }
  1911.  
  1912. /*ARGSUSED*/
  1913. waveform_scrollbar_proc(sb, event)
  1914.     Scrollbar    sb;
  1915.     Event        *event;
  1916. {
  1917.     int        position;
  1918.  
  1919.     EVENTP("waveform_scrollbar_proc", event);
  1920.  
  1921.     position = (int)xv_get(sb, SCROLLBAR_VIEW_START);
  1922.  
  1923.     if (Buffer.display.position != position * Zoom) {
  1924.         Buffer.display.position = Zoom * position;
  1925.     }
  1926.     /* Canvas repaint routine is automatically called by scrollbar code */
  1927. }
  1928.  
  1929. waveform_repaint()
  1930. {
  1931.     Rectlist repaint;
  1932.     Rect *temp;
  1933.     Xv_Window pw;
  1934.  
  1935.     /*
  1936.         get the pixwin and rectlist for the entire canvas
  1937.     */
  1938.     
  1939.     pw = canvas_paint_window(Waveform_canvas);
  1940.  
  1941.     temp = (Rect*) xv_get(Waveform_canvas, CANVAS_VIEWABLE_RECT, pw);
  1942.  
  1943.     repaint.rl_bound.r_left = temp -> r_left;
  1944.     repaint.rl_bound.r_top = temp -> r_top;
  1945.     repaint.rl_bound.r_width = temp -> r_width;
  1946.     repaint.rl_bound.r_height = temp -> r_height;
  1947.  
  1948.     waveform_repaint_proc(Waveform_canvas, pw, repaint);
  1949.  
  1950. }
  1951.  
  1952. /*
  1953.  * This routine is called to repair damage to the waveform canvas.
  1954.  * Redraw the waveform according the current scaling factor.
  1955.  */
  1956. /*ARGSUSED*/
  1957. void
  1958. waveform_repaint_proc(canvas, pw, repaint_area)
  1959.     Canvas        canvas;
  1960.     Pixwin        *pw;
  1961.     Rectlist    *repaint_area;
  1962. {
  1963.     int            i;
  1964.     Rect            *rp;
  1965.     Pr_brush        brush;
  1966.     int            pt1,
  1967.                 pt2,
  1968.                 from,
  1969.                 to;
  1970.  
  1971.     if ((Buffer.data == NULL) || Waveform_update_inhibit)
  1972.         return;
  1973.  
  1974.     rp = &repaint_area->rl_bound;
  1975.  
  1976.     DEBUGF(("Repaint waveform from %d to %d\n",
  1977.         rp->r_left, rp->r_left+rp->r_width));
  1978.  
  1979.     brush.width = 3;
  1980.  
  1981.     /* Clear the damaged region */
  1982.     (void) pw_writebackground(pw, rp->r_left, rp->r_top,
  1983.         rp->r_left + rp->r_width, rp->r_top + rp->r_height, PIX_SRC);
  1984.  
  1985.     pt1 = (rp->r_left * Zoom);
  1986.  
  1987.     /* Draw the start and end markers */
  1988.     if (Buffer.display.start >= 0) {
  1989.         i = Buffer.display.start / Zoom;
  1990.         (void) pw_line(pw, i, rp->r_top, i, rp->r_top + rp->r_height,
  1991.             &brush, &dashed, PIX_SET);
  1992.     }
  1993.     if (Buffer.display.end >= 0) {
  1994.         i = Buffer.display.end / Zoom;
  1995.         (void) pw_line(pw, i, rp->r_top, i, rp->r_top + rp->r_height,
  1996.             &brush, &dotted, PIX_SET);
  1997.     }
  1998.  
  1999.     for (i = 0; i < rp->r_width; i++, pt1 = pt2) {
  2000.         if (i >= Buffer.display.end)
  2001.             break;
  2002.  
  2003.         pt2 = pt1 + Zoom;
  2004.         from = 127 - audio_u2c(Buffer.data[pt1]);
  2005.         to = 127 - audio_u2c(Buffer.data[pt2]);
  2006.  
  2007.         (void) pw_vector(pw,
  2008.             rp->r_left+i, from, rp->r_left+i+1, to, PIX_SET, 1);
  2009.     }
  2010.  
  2011. }
  2012.  
  2013.  
  2014. /* Create the canvas in which is displayed the entire waveform */
  2015. waveform_create_canvas()
  2016. {
  2017.     Xv_Window win;
  2018.     Xv_singlecolor fg, bg, *cmsp;
  2019. /*
  2020.     position_sb = xv_create(Waveform_panel, SCROLLBAR,
  2021.         SCROLLBAR_DIRECTION, SCROLLBAR_HORIZONTAL,
  2022.         0);
  2023.  
  2024.     Waveform_canvas = xv_create(Base_frame, CANVAS,
  2025.         XV_WIDTH,            WAVEFORM_WIDTH,
  2026.         XV_HEIGHT,            WAVEFORM_HEIGHT,
  2027.         WIN_BELOW,            Waveform_panel,
  2028.         WIN_RIGHT_OF,        Scope_canvas,
  2029.         WIN_HORIZONTAL_SCROLLBAR, position_sb,
  2030.         OPENWIN_AUTO_CLEAR,        FALSE,
  2031.         CANVAS_FIXED_IMAGE,        FALSE,
  2032.         CANVAS_RETAINED,        FALSE,
  2033.         CANVAS_REPAINT_PROC,    waveform_repaint_proc,
  2034.         PANEL_NOTIFY_LEVEL, PANEL_ALL,
  2035.         0);
  2036. */
  2037.     Waveform_canvas = xv_create(Base_frame, CANVAS,
  2038.         XV_WIDTH,            WAVEFORM_WIDTH,
  2039.         XV_HEIGHT,            WAVEFORM_HEIGHT,
  2040.         WIN_BELOW,            Waveform_panel,
  2041.         WIN_RIGHT_OF,        Scope_canvas,
  2042.         OPENWIN_AUTO_CLEAR,        FALSE,
  2043.         CANVAS_FIXED_IMAGE,        FALSE,
  2044.         CANVAS_RETAINED,        FALSE,
  2045.         CANVAS_REPAINT_PROC,    waveform_repaint_proc,
  2046.         CANVAS_AUTO_SHRINK,        FALSE,
  2047.         /*
  2048.         CANVAS_AUTO_EXPAND,        FALSE,
  2049.         */
  2050.         0);
  2051.     
  2052.     position_sb = xv_create(Waveform_canvas, SCROLLBAR,
  2053.         SCROLLBAR_DIRECTION, SCROLLBAR_HORIZONTAL,
  2054.         0);
  2055.  
  2056.     wc_display = (Display *) xv_get(Base_frame, XV_DISPLAY);
  2057.     wc_drawable = (Drawable) xv_get(Base_frame, XV_XID);
  2058.     wc_screen = (Xv_Screen) xv_get(Base_frame, XV_SCREEN);
  2059.     wc_screen_no = xv_get(wc_screen, SCREEN_NUMBER);
  2060.     wc_gcval.foreground = BlackPixel(wc_display, wc_screen_no);
  2061.     wc_gcval.background = WhitePixel(wc_display, wc_screen_no);
  2062.     wc_gc = XCreateGC(wc_display, wc_drawable, GCForeground|GCBackground,
  2063.         &wc_gcval);
  2064.  
  2065.     cmsp = (Xv_singlecolor*) xv_get(Base_frame, FRAME_FOREGROUND_COLOR);
  2066.     fg.red = cmsp -> red;
  2067.     fg.green = cmsp -> green;
  2068.     fg.blue = cmsp -> blue;
  2069.  
  2070.     cmsp = (Xv_singlecolor*) xv_get(Base_frame, FRAME_BACKGROUND_COLOR);
  2071.     bg.red = cmsp -> red;
  2072.     bg.green = cmsp -> green;
  2073.     bg.blue = cmsp -> blue;
  2074.  
  2075.     Waveform_cursor = xv_create(Waveform_panel, CURSOR,
  2076.         CURSOR_IMAGE, waveform_cursor_image,
  2077.         CURSOR_FOREGROUND_COLOR, &fg,
  2078.         CURSOR_BACKGROUND_COLOR, &bg,
  2079.         0);
  2080.     
  2081.     win = xv_get(Waveform_canvas, CANVAS_NTH_PAINT_WINDOW, 0);
  2082.  
  2083.     xv_set(win,
  2084.         WIN_CURSOR,         Waveform_cursor,
  2085.         WIN_EVENT_PROC,        waveform_cursor_proc,
  2086.         WIN_CONSUME_EVENTS,    
  2087.             WIN_MOUSE_BUTTONS,
  2088.             LOC_DRAG,
  2089.             LOC_MOVE,
  2090.             WIN_IN_TRANSIT_EVENTS,
  2091.             0,
  2092.         0);
  2093. }
  2094.  
  2095. /* Routines that implement Play and Record */
  2096.  
  2097. /* Start playing data (audio device is open) */
  2098. start_play()
  2099. {
  2100.     /* Verify buffer */
  2101.     if (selectcheck() <= 0) {
  2102.         stop_play();
  2103.         return;
  2104.     }
  2105.     xv_set(Main_play_item, PANEL_LABEL_STRING, "Stop", 0);
  2106.  
  2107.     Buffer.play.io_position = Buffer.play.start;
  2108.     Buffer.display.last = -1;
  2109.     Buffer.draining = FALSE;
  2110.  
  2111.     scope_clear();
  2112.     Active_flag |= PLAY;
  2113.  
  2114.     /* Set a timer to go off around once per scope width */
  2115.     set_timer((double)SCOPE_WIDTH / (double)Buffer.hdr.sample_rate);
  2116.  
  2117.     /* SIGPOLL kicks off play_service() */
  2118.     (void) kill(getpid(), SIGPOLL);
  2119. }
  2120.  
  2121. /* Asynchronous SIGPOLL handler.  Can't call hardly any SunView stuff. */
  2122. /*ARGSUSED*/
  2123. Notify_value
  2124. sigpoll_async_handler(client, sig, when)
  2125.     Notify_client        client;
  2126.     int            sig;
  2127.     Notify_signal_mode    when;
  2128. {
  2129.     int            save_errno;
  2130.  
  2131.     DEBUGF(("sigpoll_handler: %s\n",
  2132.         (Wait_flag ? "Waiting for open()" : "")));
  2133.  
  2134.     save_errno = errno;    /* XXX - workaround notifier bug */
  2135.  
  2136.     if (!Wait_flag) {
  2137.         /* Device is open.  Attempt to keep queues filled. */
  2138.         if (Active_flag & PLAY)
  2139.             play_service();
  2140.         if (Active_flag & RECORD)
  2141.             record_service();
  2142.     }
  2143.  
  2144.     /*
  2145.      * SIGPOLL is also sent if the state of the device changed.
  2146.      * Schedule the synchronous handler, unless it already was scheduled.
  2147.      */
  2148.     if (!Sync_sched) {
  2149.         Sync_sched = TRUE;
  2150.         (void) notify_post_event(SIGPOLL, NULL, NOTIFY_SAFE);
  2151.     }
  2152.  
  2153.     errno = save_errno;    /* XXX - workaround notifier bug */
  2154.     return (NOTIFY_DONE);
  2155. }
  2156.  
  2157. /*
  2158.  * This is the routine that is called from the main loop that writes
  2159.  * sound to the device.
  2160.  *
  2161.  * It needs the following state data:
  2162.  *    - cursor start and end (calculate in proc)
  2163.  *    - current position in the buffer
  2164.  */
  2165. play_service()
  2166. {
  2167.     int    start;
  2168.     int    end;
  2169.     int    outcnt;
  2170.     int    rtn;
  2171.  
  2172.     if (Buffer.draining)
  2173.         return;
  2174. again:
  2175.     start = Buffer.play.io_position;
  2176.     end = Buffer.play.end;
  2177.  
  2178.     outcnt = end - start;
  2179.  
  2180.     while (outcnt > 0) {
  2181.         /* write as much data as possible */
  2182.         rtn = write(Audio_fd, (char *)&Buffer.data[start], outcnt);
  2183.  
  2184.         if (rtn > 0) {
  2185.             outcnt -= rtn;
  2186.             start += rtn;
  2187.             Buffer.play.io_position = start;
  2188.         }
  2189.         else {
  2190.             break;
  2191.         }
  2192.     }
  2193.  
  2194.     /* Check for end of sound condition. */
  2195.     if (outcnt == 0) {
  2196.         if (Loop_flag && (Buffer.play.start < Buffer.play.end)) {
  2197.             Buffer.play.io_position = Buffer.play.start;
  2198.             goto again;
  2199.         }
  2200.         else if (!Buffer.draining) {
  2201.             Buffer.draining = TRUE;    /* drain if not looping */
  2202.             /* Write EOF marker */
  2203.             (void) write(Audio_fd, (char *)&Buffer.data[0], 0);
  2204.         }
  2205.     }
  2206. }
  2207.  
  2208. /* This routine is called when the buffer markers are changed */
  2209. play_update_cursor()
  2210. {
  2211.     Audio_info    tmpinfo;
  2212.  
  2213.     /* No need to take action if not actually playing */
  2214.     if (!(Active_flag & PLAY))
  2215.         return;
  2216.  
  2217.     /* If start and end points are the same, catch it here */
  2218.     if (Buffer.play.end <= Buffer.play.start) {
  2219.         stop_play();
  2220.         return;
  2221.     }
  2222.  
  2223.     /* Turn off the SIGPOLL handler for now */
  2224.     Active_flag &= ~PLAY;
  2225.  
  2226.     /* Flush queue and start over */
  2227.     (void) audio_flush_play(Audio_fd);
  2228.     Buffer.draining = FALSE;
  2229.     Buffer.play.io_position = Buffer.play.start;
  2230.     Active_flag |= PLAY;
  2231.  
  2232.     /* Reset sample, error, and eof counts.  SIGPOLL will kick play. */
  2233.     AUDIO_INITINFO(&tmpinfo);
  2234.     tmpinfo.play.eof = 0;
  2235.     tmpinfo.play.error = 0;
  2236.     tmpinfo.play.samples = 0;
  2237.     (void) audio_setinfo(Audio_fd, &tmpinfo);
  2238. }
  2239.  
  2240. /*  This routine is used when we stop playing sound, for whatever reason. */
  2241. stop_play()
  2242. {
  2243.     unsigned    u;
  2244.  
  2245.     Active_flag &= ~PLAY;
  2246.  
  2247.     /* If waiting for device open, quit waiting now. */
  2248.     if (Wait_flag & PLAY) {
  2249.         Wait_flag &= ~PLAY;
  2250.         xv_set(Main_play_item, PANEL_LABEL_STRING, "Play", 0);
  2251.         return;
  2252.     }
  2253.  
  2254.     Buffer.draining = FALSE;
  2255.  
  2256.     /* Get latest error count */
  2257.     (void) audio_get_play_error(Audio_fd, &u);
  2258.     Audio_state.play.error = u;
  2259.     if (!Active_flag) {
  2260.         audio_flushclose();
  2261.         cancel_timer();
  2262.     }
  2263.     xv_set(Main_play_item, PANEL_LABEL_STRING, "Play", 0);
  2264. }
  2265.  
  2266. /* Initiate a record operation (/dev/audio is already open) */
  2267. start_record()
  2268. {
  2269.     Audio_info    tmpinfo;
  2270.  
  2271.     /* Pause the input and flush the read queue */
  2272.     (void) audio_pause_record(Audio_fd);
  2273.     (void) audio_flush_record(Audio_fd);
  2274.  
  2275.     xv_set(Main_record_item, PANEL_LABEL_STRING, "Stop", 0);
  2276.  
  2277.     Buffer.hdr = Device_rhdr;
  2278.     Buffer.display.position = 0;
  2279.     Buffer.display.last = -1;
  2280.     Buffer.draining = FALSE;
  2281.     Buffer.hdr.data_size = 0;
  2282.     alloc_buffer(audio_secs_to_bytes(&Buffer.hdr, (double)MAX_RECORD_TIME));
  2283.  
  2284.     Buffer.display.start = -1;
  2285.     Buffer.play.start = 0;
  2286.     Buffer.display.end = -1;
  2287.     Buffer.play.end = 0;
  2288.     
  2289.     xv_set(Describe_delta_item,
  2290.         PANEL_VALUE, " ",
  2291.         0);
  2292.  
  2293.     /* Clear the scope/wave displays */
  2294.     
  2295.     xv_set(Waveform_zoom_item,
  2296.         PANEL_MIN_VALUE,    1,
  2297.         PANEL_MAX_VALUE,    2,
  2298.         PANEL_VALUE,    2,
  2299.         0);
  2300.  
  2301.     scope_clear();
  2302.  
  2303.     /* Restart input and reset counters.  SIGPOLL will start reading. */
  2304.     Active_flag |= RECORD;
  2305.     AUDIO_INITINFO(&tmpinfo);
  2306.     tmpinfo.record.pause = 0;
  2307.     tmpinfo.record.samples = 0;
  2308.     tmpinfo.record.error = 0;
  2309.     (void) audio_setinfo(Audio_fd, &tmpinfo);
  2310. }
  2311.  
  2312. /*
  2313.  * This is the routine that is called from the main loop that reads
  2314.  * sound from the device.
  2315.  */
  2316. record_service()
  2317. {
  2318.     int    read_size;
  2319.     int    rtn;
  2320.  
  2321.     /* This prevents an infinite recursion */
  2322.     if (Buffer.hdr.data_size >= Buffer.alloc_size)
  2323.         return;
  2324.  
  2325.     if (ioctl(Audio_fd, FIONREAD, &read_size) < 0) {
  2326.         PERROR("FIONREAD");
  2327.         return;
  2328.     }
  2329.  
  2330.     /*
  2331.      * Don't bother if there is nothing to read,
  2332.      * or if not much (and we're not draining input).
  2333.      */
  2334.     if ((read_size == 0) || ((read_size < SCOPE_WIDTH) && !Buffer.draining))
  2335.         return;
  2336.  
  2337.     for (;;) {
  2338.         if ((Buffer.hdr.data_size + read_size) > Buffer.alloc_size) {
  2339.             read_size = Buffer.alloc_size - Buffer.hdr.data_size;
  2340.         }
  2341.  
  2342.         if (read_size == 0)
  2343.             break;
  2344.  
  2345.         rtn = read(Audio_fd,
  2346.             (char *)&Buffer.data[Buffer.hdr.data_size], read_size);
  2347.  
  2348.         if (rtn < 0) {
  2349.             if (errno != EWOULDBLOCK)
  2350.                 PERROR("read");
  2351.             return;
  2352.         }
  2353.         Buffer.hdr.data_size += rtn;
  2354.  
  2355.         if (ioctl(Audio_fd, FIONREAD, &read_size) < 0)
  2356.             return;
  2357.     }
  2358.  
  2359.     if (Buffer.hdr.data_size >= Buffer.alloc_size) {
  2360.         stop_record();
  2361.         message_display("Maximum record time exceeded.");
  2362.         return;
  2363.     }
  2364. }
  2365.  
  2366. stop_record()
  2367. {
  2368.     unsigned    u;
  2369.  
  2370.     Active_flag &= ~RECORD;
  2371.  
  2372.     /* If waiting for device open, quit waiting now. */
  2373.     if (Wait_flag & RECORD) {
  2374.         Wait_flag &= ~RECORD;
  2375.         xv_set(Main_record_item, PANEL_LABEL_STRING, "Record", 0);
  2376.         return;
  2377.     }
  2378.  
  2379.     /* Pause the device so that we can drain the input */
  2380.     (void) audio_pause_record(Audio_fd);
  2381.  
  2382.     /* Read residual input, if any */
  2383.     Buffer.draining = TRUE;
  2384.     record_service();
  2385.     Buffer.draining = FALSE;
  2386.  
  2387.     (void) audio_get_record_error(Audio_fd, &u);
  2388.     Audio_state.record.error = u;
  2389.     if (!Active_flag) {
  2390.         audio_flushclose();
  2391.         cancel_timer();
  2392.     }
  2393.  
  2394.     xv_set(Main_record_item, PANEL_LABEL_STRING, "Record", 0);
  2395.  
  2396.     /* Set new endpoints (if not selected during record) & redisplay */
  2397.     if (Buffer.play.end == 0) {
  2398.         Buffer.display.end = Buffer.play.end = Buffer.hdr.data_size - 1;
  2399.     }
  2400.     Buffer.display.start = Buffer.play.start;
  2401.     file_update();
  2402.     wave_update();
  2403. }
  2404.  
  2405. /*
  2406.  * Synchronous SIGPOLL handler...entered when the asynchronous SIGPOLL
  2407.  * handler detects something to do synchronously, like updating displays.
  2408.  */
  2409. /*ARGSUSED*/
  2410. Notify_value
  2411. sigpoll_sync_handler(client, event, arg, when)
  2412.     Notify_client        client;
  2413.     Notify_event        event;
  2414.     Notify_arg        arg;
  2415.     Notify_event_type    when;
  2416. {
  2417.     Sync_sched = FALSE;    /* flag to async handler */
  2418.  
  2419.     /* Waiting for the device open to succeed */
  2420.     if (Wait_flag) {
  2421.         if (!audio_open(Wait_flag)) {
  2422.             /* device is open...start transfers */
  2423.             if (Wait_flag & PLAY) {
  2424.                 Wait_flag &= ~PLAY;
  2425.                 start_play();
  2426.             }
  2427.             if (Wait_flag & RECORD) {
  2428.                 Wait_flag &= ~RECORD;
  2429.                 start_record();
  2430.             }
  2431.         }
  2432.     }
  2433.     else if (Active_flag & RECORD) {
  2434.         update_scope();
  2435.         describe_update_panel(FALSE);
  2436.  
  2437.         /* Only update the waveform once in a while. */
  2438.         if (Refresh_ctr < 0) {
  2439.             Refresh_ctr = REFRESH_RELOAD;
  2440.             /* If user adjusted zoom, stop updating */
  2441.             if ((int) xv_get(Waveform_zoom_item, PANEL_VALUE) ==
  2442.                 (int) xv_get(Waveform_zoom_item, PANEL_MAX_VALUE))
  2443.                 wave_update();
  2444.         }
  2445.     }
  2446.  
  2447.     /* Get current audio status and update display accordingly */
  2448.     main_update_panel(FALSE);
  2449.  
  2450.     /*
  2451.      * Detect whether output is complete.  The play.eof flag is
  2452.      * incremented when a zero-length write has been processed.
  2453.      */
  2454.     if ((Active_flag & PLAY) && Audio_state.play.eof && Buffer.draining) {
  2455.         /* Output draining is complete. */
  2456.         stop_play();
  2457. #ifdef notdef
  2458.         if (Audio_state.play.error) {
  2459.             message_display("Underflow detected during Play.");
  2460.         }
  2461. #endif
  2462.     }
  2463.     return (NOTIFY_DONE);
  2464. }
  2465.  
  2466. /* Timer handler...entered whenever it's time to do something */
  2467. /*ARGSUSED*/
  2468. Notify_value
  2469. timer_handler(client, which)
  2470.     Notify_client    client;
  2471.     int        which;
  2472. {
  2473.     if (Active_flag & PLAY)
  2474.         update_scope();
  2475.  
  2476.     return (NOTIFY_DONE);
  2477. }
  2478.  
  2479. /* Set a periodic timer to poll for device open availability */
  2480. set_timer(time)
  2481.     double            time;
  2482. {
  2483.     struct itimerval    timer;
  2484.     int            secs;
  2485.     int            usecs;
  2486.  
  2487.     DEBUGF(("init timer (%.2f seconds)\n", time));
  2488.  
  2489.     secs = (int)time;
  2490.     usecs = (int)((time - (double)secs) * 1000000.);
  2491.  
  2492.     timer.it_value.tv_usec = usecs;
  2493.     timer.it_value.tv_sec = secs;
  2494.     timer.it_interval.tv_usec = usecs;
  2495.     timer.it_interval.tv_sec = secs;
  2496.     (void) notify_set_itimer_func(Base_frame, (Notify_func)timer_handler,
  2497.         ITIMER_REAL, &timer, ((struct itimerval *)0));
  2498. }
  2499.  
  2500. /* Cancel any outstanding periodic timer */
  2501. cancel_timer()
  2502. {
  2503.     (void) notify_set_itimer_func(Base_frame, (Notify_func)timer_handler,
  2504.         ITIMER_REAL, ((struct itimerval *)0), ((struct itimerval *)0));
  2505.  
  2506.     DEBUGF(("cancel timer\n"));
  2507. }
  2508.  
  2509. /* Update a display item's value only if it is changing (prevents flashing) */
  2510. /*VARARGS1*/
  2511. set_item_val(item, val)
  2512.     Panel_item    item;
  2513.     int        val;
  2514. {
  2515.     if (val != (int) xv_get(item, PANEL_VALUE))
  2516.         
  2517.     xv_set(item, PANEL_VALUE, val, 0);
  2518. }
  2519.  
  2520. /* Update the Describe panel selection length string */
  2521. describe_delta_update()
  2522. {
  2523.     char    delta_string[(2 * AUDIO_MAX_TIMEVAL) + 8];
  2524.     char    tmp1[AUDIO_MAX_TIMEVAL];
  2525.     char    tmp2[AUDIO_MAX_TIMEVAL];
  2526.  
  2527.     SPRINTF(delta_string, "%s - %s",
  2528.         audio_secs_to_str(audio_bytes_to_secs(
  2529.         &Buffer.hdr, (unsigned) Buffer.display.start), tmp1, 2),
  2530.         audio_secs_to_str(audio_bytes_to_secs(
  2531.         &Buffer.hdr, (unsigned) Buffer.display.end), tmp2, 2));
  2532.  
  2533.     xv_set(Describe_delta_item,
  2534.         PANEL_VALUE, delta_string,
  2535.         0);
  2536. }
  2537.  
  2538. /* Set up a new sound buffer and display it. */
  2539. file_update()
  2540. {
  2541.     Buffer.display.position = 0;
  2542.     Buffer.play.io_position = Buffer.play.start;
  2543.  
  2544.     play_update_cursor();
  2545.  
  2546.     /* Update the selection length in the Describe panel */
  2547.     describe_delta_update();
  2548.     describe_update_panel(TRUE);
  2549.     update_scope();
  2550.     wave_update();
  2551. }
  2552.  
  2553. /* wave_update - a new file has been loaded or additional data
  2554.  *    has been recorded. Redisplay the waveform panel and canvas.
  2555.  */
  2556. wave_update()
  2557. {
  2558.     int    window_width;
  2559.     int    max_zoom;
  2560.     int    min_zoom;
  2561.  
  2562.     window_width = ((int)xv_get(Waveform_canvas, XV_WIDTH));
  2563.     max_zoom = Buffer.hdr.data_size / window_width;
  2564.     if (max_zoom < 1)
  2565.         max_zoom = 2;
  2566.     /*
  2567.      * XXX - Use 0x7fff, max value of positive short,
  2568.      * because canvas coord's are signed shorts
  2569.      */
  2570.     min_zoom = (Buffer.hdr.data_size + 0x7fff) / 0x7fff;
  2571.     if (min_zoom < 1)
  2572.         min_zoom = 1;
  2573.  
  2574.     xv_set(Waveform_zoom_item,
  2575.         PANEL_MIN_VALUE,    min_zoom,
  2576.         PANEL_MAX_VALUE,    max_zoom,
  2577.         PANEL_VALUE,    max_zoom,
  2578.         0);
  2579.  
  2580.     waveform_set_zoom((unsigned)max_zoom);
  2581. }
  2582.  
  2583.  
  2584. /* Verify the selected region of the waveform buffer and return its size */
  2585. int
  2586. buffer_selected_size()
  2587. {
  2588.     int    cnt;
  2589.     char    msg[256];
  2590.  
  2591.     if (Buffer.hdr.data_size == 0) {
  2592.         message_display("No data in buffer.");
  2593.         return (0);
  2594.     }
  2595.  
  2596.     cnt = Buffer.play.end - Buffer.play.start;
  2597.  
  2598.     if (cnt == 0) {
  2599.         message_display("No data in selected region.");
  2600.         return (0);
  2601.     }
  2602.  
  2603.     /* XXX - sanity checks for now */
  2604.     if (Buffer.play.start >= Buffer.hdr.data_size) {
  2605.         SPRINTF(msg, "Start position (%d) beyond EOF (%d).",
  2606.             Buffer.play.start, Buffer.hdr.data_size);
  2607.         message_display(msg);
  2608.         return (-1);
  2609.     }
  2610.  
  2611.     if (cnt < 0) {
  2612.         SPRINTF(msg, "Start position (%d) beyond End position (%d).",
  2613.             Buffer.play.start, Buffer.play.end);
  2614.         message_display(msg);
  2615.         return (-1);
  2616.     }
  2617.     return (cnt);
  2618. }
  2619.  
  2620. /*
  2621.  * If you try to loop on a very small buffer, the window system will hang.
  2622.  * This routine filters such requests.  It returns the number of samples
  2623.  * in the buffer, or -1 if the buffer is too small.
  2624.  */
  2625. selectcheck()
  2626. {
  2627.     int    samples;
  2628.  
  2629.     samples = buffer_selected_size();
  2630.     if (samples <= 0)
  2631.         return (samples);
  2632.  
  2633.     if ((samples < MIN_BUFSIZE) && Loop_flag) {
  2634.         Loop_flag = 0;
  2635.         set_item_val(Loop_item, 0);
  2636.         message_display("Looping disabled: buffer size too small.");
  2637.         return (-1);
  2638.     }
  2639.     return (samples);
  2640. }
  2641.  
  2642. /* Display an alert (with audio alert, if any). */
  2643. message_display(msg)
  2644.     char    *msg;
  2645. {
  2646.     int    playing;
  2647.  
  2648.     playing = audio_play_alert();
  2649.  
  2650.     notice_prompt(Base_frame, (Event *) 0,
  2651.         NOTICE_MESSAGE_STRINGS, msg, 0,
  2652.         NOTICE_BUTTON_YES, "Ok",
  2653.         NOTICE_NO_BEEPING, playing,
  2654.         0);
  2655.  
  2656.     if (playing)
  2657.         alert_close();
  2658. }
  2659.  
  2660. /*
  2661.  * Display a confirmation box (with audio alert, if any).
  2662.  * Return TRUE if Confirm, FALSE if Cancel.
  2663.  */
  2664. message_confirm(msg)
  2665.     char    *msg;
  2666. {
  2667.     int    playing;
  2668.     int    result;
  2669.  
  2670.     playing = audio_play_alert();
  2671.  
  2672.     result = notice_prompt(Base_frame, (Event *) 0,
  2673.         NOTICE_MESSAGE_STRINGS, msg, 0,
  2674.         NOTICE_BUTTON_YES, "Confirm",
  2675.         NOTICE_BUTTON_NO,  "Cancel",
  2676.         NOTICE_NO_BEEPING, playing,
  2677.         0);
  2678.  
  2679.     if (playing)
  2680.         alert_close();
  2681.  
  2682.     return (result == NOTICE_YES);
  2683. }
  2684.  
  2685. /* Audio functions. */
  2686.  
  2687. /*
  2688.  * Open the audio device, using the Active_flag to derive open modes.
  2689.  * Returns:
  2690.  *    0    Successful open
  2691.  *    1    Audio device is busy (try again later)
  2692.  *    -1    Error during open
  2693.  */
  2694. int
  2695. audio_open(flag)
  2696.     int    flag;
  2697. {
  2698.     if (Audio_fd >= 0) {    /* already open */
  2699.         FPRINTF(stderr, "%s already open\n", AUDIO_DEV);
  2700.         return (-1);
  2701.     }
  2702.  
  2703.     /*
  2704.      * Read-only access if Record.
  2705.      * Write-only access if Play or Alert.
  2706.      */
  2707.     flag = ((flag & RECORD) ? O_RDONLY : O_WRONLY) | O_NDELAY;
  2708.  
  2709.     if ((Audio_fd = open(AUDIO_DEV, flag)) < 0) {
  2710.         if ((errno == EINTR) || (errno == EBUSY))
  2711.             return (1);
  2712.         PERROR(AUDIO_DEV);
  2713.         return (-1);
  2714.     }
  2715.     flag = fcntl(Audio_fd, F_GETFL, 0) | FNDELAY;
  2716.     flag = fcntl(Audio_fd, F_GETFL, 0) | FNDELAY;
  2717.     if (fcntl(Audio_fd, F_SETFL, flag) < 0)
  2718.         PERROR("F_SETFL fcntl");
  2719.     if (ioctl(Audio_fd, I_SETSIG, S_INPUT|S_OUTPUT|S_MSG) < 0)
  2720.         PERROR("I_SETSIG ioctl");
  2721.     return (0);
  2722. }
  2723.  
  2724. /* Flush queued output and close the audio device */
  2725. audio_flushclose()
  2726. {
  2727.     if (Audio_fd < 0)
  2728.         return;
  2729.     (void) audio_flush(Audio_fd);
  2730.     (void) close(Audio_fd);
  2731.     Audio_fd = -1;
  2732. }
  2733.  
  2734. /* Close audio device (queued output will drain) */
  2735. audio_close()
  2736. {
  2737.     if (Audio_fd < 0)
  2738.         return;            /* Already closed */
  2739.     (void) close(Audio_fd);
  2740.     Audio_fd = -1;
  2741. }
  2742.  
  2743. /*
  2744.  * Open audio control device (/dev/audioctl) and read its state.
  2745.  * This may be used for state get/set (eg, volume levels) without holding
  2746.  * the main audio device (/dev/audio) open.
  2747.  */
  2748. audio_control_init()
  2749. {
  2750.     /* open audio control device */
  2751.     if ((Audioctl_fd = open(AUDIO_CTLDEV, O_RDWR)) < 0) {
  2752.         PERROR(AUDIO_CTLDEV);
  2753.         Device_phdr.sample_rate = 8000;
  2754.         Device_phdr.channels = 1;
  2755.         Device_phdr.bytes_per_unit = 1;
  2756.         Device_phdr.samples_per_unit = 1;
  2757.         Device_phdr.encoding = AUDIO_ENCODING_ULAW;
  2758.         Device_rhdr = Device_phdr;
  2759.         Buffer.hdr = Device_phdr;
  2760.     }
  2761.     else {
  2762.  
  2763.         /* tell the driver to send SIGPOLL on device state changes */
  2764.         if (ioctl(Audioctl_fd, I_SETSIG, S_MSG) < 0)
  2765.             PERROR("Could not issue I_SETSIG ioctl");
  2766.  
  2767.         /* Get the device play & record configuration */
  2768.         if ((audio_get_play_config(Audioctl_fd, &Device_phdr) !=
  2769.             AUDIO_SUCCESS) ||
  2770.             (audio_get_record_config(Audioctl_fd, &Device_rhdr) !=
  2771.             AUDIO_SUCCESS)) {
  2772.             PERROR("Could not get encoding configuration");
  2773.         }
  2774.     }
  2775.     AUDIO_INITINFO(&Audio_state);
  2776. }
  2777.  
  2778. /* Read the audio device state and translate fields into values we understand */
  2779. int
  2780. audio_readstate(ip)
  2781.     Audio_info    *ip;
  2782. {
  2783.     if ((Audioctl_fd > 0) &&
  2784.         (audio_getinfo(Audioctl_fd, ip) != AUDIO_SUCCESS)) {
  2785.         /* If error, quit trying to access control device */
  2786.         Audioctl_fd = -1;
  2787.         PERROR(AUDIO_CTLDEV);
  2788.     }
  2789.     if (Audioctl_fd < 0) {
  2790.         /* Set dummy values */
  2791.         ip->play.gain = 35;
  2792.         ip->record.gain = 60;
  2793.         ip->play.port = 0;
  2794.         ip->play.eof = 0;
  2795.         ip->play.error = FALSE;
  2796.         ip->play.pause = FALSE;
  2797.         ip->record.error = FALSE;
  2798.         ip->record.pause = FALSE;
  2799.         return (FALSE);
  2800.     }
  2801.  
  2802.     /* Convert to values that we understand */
  2803.     ip->play.gain = unscale_gain((double)(ip->play.gain - AUDIO_MIN_GAIN) /
  2804.         (double)AUDIO_MAX_GAIN);
  2805.     ip->record.gain = unscale_gain(
  2806.         (double)(ip->record.gain - AUDIO_MIN_GAIN) /
  2807.         (double)AUDIO_MAX_GAIN);
  2808.     ip->play.port = (unsigned) ((ip->play.port == AUDIO_SPEAKER) ? 0 : 1);
  2809.     return (TRUE);
  2810. }
  2811.  
  2812.  
  2813. /*
  2814.  * Queue an entire sound file (maximum 2 seconds of data) to /dev/audio.
  2815.  * Leave /dev/audio open...do not wait for output to drain.
  2816.  * Return TRUE if audio output started; else return FALSE.
  2817.  *
  2818.  * XXX - buffer should be allocated dynamically to accomodate other formats.
  2819.  */
  2820. audio_play_alert()
  2821. {
  2822.     int        fd;
  2823.     int        rtn;
  2824.     Audio_hdr    hdr;
  2825.     char        buffer[16000];
  2826.  
  2827.     /*
  2828.      * If play active...don't interrupt it.
  2829.      * If record active, we might be able to play this alert.
  2830.      */
  2831.     if (Active_flag & PLAY)
  2832.         return (FALSE);
  2833.  
  2834.     /* Open, read, close the sound file */
  2835.     if ((fd = open(NOTICE_FILE, O_RDONLY)) < 0)
  2836.         return (FALSE);
  2837.     rtn = (AUDIO_SUCCESS == audio_read_filehdr(fd, &hdr, (char *)NULL, 0));
  2838.     if (rtn) {
  2839.         rtn = read(fd, buffer, sizeof (buffer));
  2840.     }
  2841.     (void) close(fd);
  2842.     if (rtn <= 0)
  2843.         return (FALSE);
  2844.  
  2845.     DEBUGF(("playing alert file >%s<\n", NOTICE_FILE));
  2846.  
  2847.     if ((Alert_fd = open(AUDIO_DEV, O_WRONLY)) < 0) {
  2848.         if ((errno != EINTR) && (errno != EBUSY))
  2849.             PERROR(AUDIO_DEV);
  2850.         return (FALSE);
  2851.     }
  2852.  
  2853.     if (write(Alert_fd, buffer, rtn) < 0) {
  2854.         PERROR("audio alert write");
  2855.         alert_close();
  2856.         return (FALSE);
  2857.     }
  2858.     return (TRUE);
  2859. }
  2860.  
  2861. /* Flush queued output and close the audio alert device */
  2862. alert_close()
  2863. {
  2864.     if (Alert_fd < 0)
  2865.         return;
  2866.     if (ioctl(Alert_fd, I_FLUSH, FLUSHW) < 0)
  2867.         PERROR("alert I_FLUSH ioctl");
  2868.     (void) close(Alert_fd);
  2869.     Alert_fd = -1;
  2870. }
  2871.  
  2872. generic_notice(msg1)
  2873.     char    *msg1;
  2874. {
  2875.     notice_prompt(Base_frame, (Event *)NULL,
  2876.         NOTICE_MESSAGE_STRINGS,
  2877.             msg1,
  2878.             0,
  2879.         NOTICE_BUTTON_YES, "Continue",
  2880.         0);
  2881. }
  2882.  
  2883.  
  2884.