1 /*
2  * Sweep, a sound wave editor.
3  *
4  * Copyright (C) 2000 Conrad Parker
5  * Copyright (C) 2002 CSIRO Australia
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */
21 
22 /*
23  * This file adapted from "speexdec.c" and "speexenc.c" in the Speex coded
24  * source code, Copyright (C) 2002 Jean-Marc Valin
25  *
26  * Redistribution and use in source and binary forms, with or without
27  * modification, are permitted provided that the following conditions
28  * are met:
29  *
30  * - Redistributions of source code must retain the above copyright
31  * notice, this list of conditions and the following disclaimer.
32  *
33  *  - Redistributions in binary form must reproduce the above copyright
34  * notice, this list of conditions and the following disclaimer in the
35  * documentation and/or other materials provided with the distribution.
36  *
37  * - Neither the name of the Xiph.org Foundation nor the names of its
38  * contributors may be used to endorse or promote products derived from
39  * this software without specific prior written permission.
40  *
41  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
42  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
43  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
44  * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
45  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
46  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
47  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
48  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
49  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
50  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
51  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
52  */
53 
54 #ifdef HAVE_CONFIG_H
55 #  include <config.h>
56 #endif
57 
58 #ifdef HAVE_SPEEX
59 
60 #include <stdlib.h>
61 #include <stdio.h>
62 #include <string.h>
63 #include <sys/types.h>
64 #include <sys/stat.h>
65 #include <fcntl.h>
66 #include <unistd.h>
67 #include <math.h>
68 #include <pthread.h>
69 #include <errno.h>
70 #include <ctype.h>
71 
72 #include <ogg/ogg.h>
73 #ifdef HAVE_SPEEX_SUBDIR
74 #include <speex/speex.h>
75 #include <speex/speex_header.h>
76 #include <speex/speex_stereo.h>
77 #include <speex/speex_callbacks.h>
78 #else
79 #include <speex.h>
80 #include <speex_header.h>
81 #include <speex_stereo.h>
82 #include <speex_callbacks.h>
83 #endif
84 
85 #define BUFFER_LEN 1024
86 
87 #include <glib.h>
88 #include <gdk/gdkkeysyms.h>
89 #include <gtk/gtk.h>
90 
91 #include <sweep/sweep_i18n.h>
92 #include <sweep/sweep_types.h>
93 #include <sweep/sweep_typeconvert.h>
94 #include <sweep/sweep_sample.h>
95 #include <sweep/sweep_undo.h>
96 #include <sweep/sweep_sounddata.h>
97 
98 #include "sample.h"
99 #include "interface.h"
100 #include "file_dialogs.h"
101 #include "file_sndfile.h"
102 #include "question_dialogs.h"
103 #include "preferences.h"
104 #include "print.h"
105 #include "view.h"
106 
107 #include "../pixmaps/xifish.xpm"
108 #include "../pixmaps/speex_logo.xpm"
109 
110 /* MAINTENANCE:
111  *
112  * Upon release of Speex 1.0, force a requirement on that version in
113  * configure. Then, remove all references to SPEEX_HAVE_BETA4 (assume
114  * this is true, as those features will be available), and also assume
115  * that SPEEX_NB_MODES > 2. This should reduce the random ifdef'ing
116  * present to accomodate the flux of prerelease versions of Speex.
117  */
118 
119 #ifdef SPEEX_SET_DTX
120 #define HAVE_SPEEX_BETA4
121 #endif
122 
123 #define BUF_LEN 128
124 
125 #define MODE_KEY "Speex_Mode"
126 #define FEATURES_KEY "Speex_Features"
127 #define QUALITY_KEY "Speex_Quality"
128 #define BR_KEY "Speex_BR"
129 #define BITRATE_KEY "Speex_Bitrate"
130 #define COMPLEXITY_KEY "Speex_Complexity"
131 #define SERIALNO_KEY "OggSpeex_Serialno"
132 #define FRAMEPACK_KEY "OggSpeex_FramePack"
133 
134 /* Mode choices */
135 #define MODE_NARROWBAND 0
136 #define MODE_WIDEBAND 1
137 #define MODE_ULTRAWIDEBAND 2
138 
139 /* Feature flags */
140 #define FEAT_VBR 1
141 #define FEAT_VAD 2
142 #define FEAT_DTX 4
143 
144 #ifdef HAVE_SPEEX_BETA4
145 #define DEFAULT_FEATURES (FEAT_VBR | FEAT_VAD | FEAT_DTX)
146 #else
147 #define DEFAULT_FEATURES (FEAT_VBR)
148 #endif
149 
150 #define DEFAULT_QUALITY 8.0
151 #define DEFAULT_COMPLEXITY 3.0
152 #define DEFAULT_FRAMEPACK 1
153 
154 #define DEFAULT_ENH_ENABLED 1
155 
156 extern GtkStyle * style_bw;
157 
158 #define READ_SIZE 200
159 
160 /*
161  * file_is_ogg_speex (pathname)
162  *
163  * This function attempts to determine if a given file is an ogg speex file
164  * by attempting to parse enough of the stream to decode an initial speex
165  * header. If any steps along the way fail, it returns false;
166  */
167 static gboolean
file_is_ogg_speex(const char * pathname)168 file_is_ogg_speex (const char * pathname)
169 {
170   int fd;
171   ssize_t nread;
172 
173   ogg_sync_state oy;
174   ogg_page og;
175   ogg_packet op;
176   ogg_stream_state os;
177 
178   char * ogg_data;
179 
180   const SpeexMode *mode;
181   SpeexHeader *header;
182 
183   fd = open (pathname, O_RDONLY);
184   if (fd == -1) {
185     return FALSE;
186   }
187 
188   ogg_sync_init (&oy);
189   ogg_data = ogg_sync_buffer (&oy, READ_SIZE);
190   if (ogg_data == NULL) goto out_false_sync;
191   if ((nread = read (fd, ogg_data, READ_SIZE)) <= 0) goto out_false_sync;
192   ogg_sync_wrote (&oy, nread);
193   if (ogg_sync_pageout (&oy, &og) != 1) goto out_false_sync;
194   ogg_stream_init (&os, ogg_page_serialno (&og));
195   ogg_stream_pagein (&os, &og);
196   if (ogg_stream_packetout (&os, &op) != 1) goto out_false_stream;
197   header = speex_packet_to_header (op.packet, op.bytes);
198   if (!header) goto out_false_stream;
199   if (header->mode >= SPEEX_NB_MODES) goto out_false_stream;
200   mode = speex_mode_list[header->mode];
201   if (mode->bitstream_version != header->mode_bitstream_version)
202     goto out_false_stream;
203 
204   ogg_sync_clear (&oy);
205   ogg_stream_clear (&os);
206 
207   close (fd);
208 
209   return TRUE;
210 
211  out_false_stream:
212   ogg_stream_clear (&os);
213 
214  out_false_sync:
215   ogg_sync_clear (&oy);
216 
217   close (fd);
218 
219   return FALSE;
220 }
221 
222 static void *
process_header(ogg_packet * op,int enh_enabled,int * frame_size,int * rate,int * nframes,int forceMode,int * channels,SpeexStereoState * stereo,int * extra_headers)223 process_header(ogg_packet *op, int enh_enabled, int * frame_size, int * rate,
224 	       int * nframes, int forceMode, int * channels,
225 	       SpeexStereoState * stereo, int * extra_headers)
226 {
227   void *st;
228   SpeexMode *mode;
229   SpeexHeader *header;
230   int modeID;
231   SpeexCallback callback;
232 
233   header = speex_packet_to_header((char*)op->packet, op->bytes);
234   if (!header) {
235     info_dialog_new ("Speex error", NULL, "Speex: cannot read header");
236     return NULL;
237   }
238   if (header->mode >= SPEEX_NB_MODES || header->mode < 0) {
239     info_dialog_new ("Speex error", NULL,
240 		     "Mode number %d does not (any longer) exist in this version\n",
241 		     header->mode);
242     return NULL;
243   }
244 
245   modeID = header->mode;
246   if (forceMode!=-1)
247     modeID = forceMode;
248   mode = (SpeexMode *)speex_mode_list[modeID];
249 
250 #ifdef HAVE_SPEEX_BETA4
251   if (header->speex_version_id > 1) {
252     info_dialog_new ("Speex error", NULL,
253 		     "This file was encoded with Speex bit-stream version %d, "
254 		     "which I don't know how to decode\n",
255 		     header->speex_version_id);
256     return NULL;
257   }
258 #endif
259 
260   if (mode->bitstream_version < header->mode_bitstream_version) {
261     info_dialog_new ("Speex error", NULL,
262 		     "The file was encoded with a newer version of Speex. "
263 		     "You need to upgrade in order to play it.\n");
264     return NULL;
265   }
266 
267   if (mode->bitstream_version > header->mode_bitstream_version) {
268     info_dialog_new ("Speex error", NULL,
269 		     "The file was encoded with an older version of Speex. "
270 		     "You would need to downgrade the version in order to play it.\n");
271     return NULL;
272   }
273 
274   st = speex_decoder_init(mode);
275   if (!st) {
276     info_dialog_new ("Speex error", NULL,
277 		     "Decoder initialization failed.\n");
278     return NULL;
279   }
280 
281   speex_decoder_ctl(st, SPEEX_SET_ENH, &enh_enabled);
282   speex_decoder_ctl(st, SPEEX_GET_FRAME_SIZE, frame_size);
283 
284   if (!(*channels==1))
285     {
286       callback.callback_id = SPEEX_INBAND_STEREO;
287       callback.func = speex_std_stereo_request_handler;
288       callback.data = stereo;
289       speex_decoder_ctl(st, SPEEX_SET_HANDLER, &callback);
290     }
291   if (*rate==-1)
292     *rate = header->rate;
293   /* Adjust rate if --force-* options are used */
294   if (forceMode!=-1)
295     {
296       if (header->mode < forceMode)
297 	*rate <<= (forceMode - header->mode);
298       if (header->mode > forceMode)
299 	*rate >>= (header->mode - forceMode);
300     }
301 
302   speex_decoder_ctl(st, SPEEX_SET_SAMPLING_RATE, rate);
303 
304   *nframes = header->frames_per_packet;
305 
306   if (*channels == -1)
307     *channels = header->nb_channels;
308 
309 #ifdef DEBUG
310   fprintf (stderr, "Decoding %d Hz audio using %s mode",
311 	   *rate, mode->modeName);
312 
313   if (*channels==1)
314       fprintf (stderr, " (mono");
315    else
316       fprintf (stderr, " (stereo");
317 
318   if (header->vbr)
319     fprintf (stderr, " (VBR)\n");
320   else
321     fprintf(stderr, "\n");
322 #endif
323 
324 #ifdef HAVE_SPEEX_BETA4
325   *extra_headers = header->extra_headers;
326 #else
327   *extra_headers = 0;
328 #endif
329 
330   free(header);
331 
332   return st;
333 }
334 
335 static sw_sample *
sample_load_speex_data(sw_op_instance * inst)336 sample_load_speex_data (sw_op_instance * inst)
337 {
338   sw_sample * sample = inst->sample;
339 
340   int fd;
341   struct stat statbuf;
342 
343   void * st = NULL;
344   SpeexBits bits;
345   int frame_size = 0;
346   int rate = -1;
347   int channels = -1;
348   int extra_headers;
349   SpeexStereoState stereo = SPEEX_STEREO_STATE_INIT;
350   int packet_count = 0;
351   int stream_init = 0;
352 
353   ogg_sync_state oy;
354   ogg_page og;
355   ogg_packet op;
356   ogg_stream_state os;
357 
358   char * ogg_data;
359 
360   int enh_enabled = DEFAULT_ENH_ENABLED;
361   int nframes = 2;
362   int eos = 0;
363   int forceMode = -1;
364 
365   int i, j;
366   sw_audio_t * d = NULL;
367   sw_framecount_t frames_total = 0, frames_decoded = 0;
368   size_t file_length, remaining, n;
369   ssize_t nread;
370   gint percent;
371 
372   gboolean active = TRUE;
373 
374   fd = open (sample->pathname, O_RDONLY);
375   if (fd == -1) {
376     sweep_perror (errno, "failed open in sample_load_speex_data");
377     return NULL;
378   }
379 
380   if (fstat (fd, &statbuf) == -1) {
381     sweep_perror (errno, "failed stat in sample_load_speex_data");
382     return NULL;
383   }
384 
385   file_length = remaining = statbuf.st_size;
386 
387   /* Init Ogg sync */
388   ogg_sync_init (&oy);
389 
390   speex_bits_init (&bits);
391 
392   while (active && remaining > 0) {
393     g_mutex_lock (sample->ops_mutex);
394 
395     if (sample->edit_state == SWEEP_EDIT_STATE_CANCEL) {
396       active = FALSE;
397     } else {
398       n = MIN (remaining, READ_SIZE);
399 
400       ogg_data = ogg_sync_buffer (&oy, n);
401       nread = read (fd, ogg_data, n);
402       if (nread == -1) {
403 	sweep_perror (errno, "speex: %s", sample->pathname);
404 	active = FALSE;
405       } else if (nread == 0) {
406 	/* eof */
407 	active = FALSE;
408       } else {
409 	ogg_sync_wrote (&oy, nread);
410 	n = (size_t)nread;
411       }
412 
413       /* Loop for all complete pages we got */
414       while (active && ogg_sync_pageout (&oy, &og) == 1) {
415 	if (stream_init == 0) {
416 	  ogg_stream_init (&os, ogg_page_serialno (&og));
417 	  stream_init = 1;
418 	}
419 
420 	/* Add page to the bitstream */
421 	ogg_stream_pagein (&os, &og);
422 
423 	/* Extract all available packets */
424 	while (!eos && ogg_stream_packetout (&os, &op) == 1) {
425 	  if (packet_count == 0) {/* header */
426 	    st = process_header (&op, enh_enabled, &frame_size, &rate,
427 				 &nframes, forceMode, &channels, &stereo,
428 				 &extra_headers);
429 	    if (st == NULL) {
430 	      /*printf ("Not Speex!\n");*/
431 	      active = FALSE;
432 	    }
433 
434 	    sample->sounddata->format->rate = rate;
435 	    sample->sounddata->format->channels = channels;
436 
437 	    if (nframes == 0)
438 	      nframes = 1;
439 
440 	  } else if (packet_count <= 1+extra_headers) {
441 	    /* XXX: metadata, extra_headers: ignore */
442 	  } else {
443 	    if (op.e_o_s)
444 	      eos = 1;
445 
446 	    /* Copy Ogg packet to Speex bitstream */
447 	    speex_bits_read_from (&bits, (char *)op.packet, op.bytes);
448 
449 	    frames_total += nframes * frame_size;
450 
451 	    if (sample->sounddata->nr_frames != frames_total) {
452 	      sample->sounddata->data =
453 		g_realloc (sample->sounddata->data,
454 			   frames_total * channels * sizeof (sw_audio_t));
455 	    }
456 
457 	    sample->sounddata->nr_frames = frames_total;
458 
459 	    d = &((sw_audio_t *)sample->sounddata->data)
460 		  [frames_decoded * channels];
461 
462 	    if (d != NULL) {
463 	      for (j = 0; j < nframes; j++) {
464 		/* Decode frame */
465 		speex_decode (st, &bits, d);
466 #ifdef DEBUG
467 		if (speex_bits_remaining (&bits) < 0) {
468 		  info_dialog_new ("Speex warning", NULL,
469 				   "Speex: decoding overflow -- corrupted stream at frame %ld", frames_decoded + (j * frame_size));
470 		}
471 #endif
472 		if (channels == 2)
473 		  speex_decode_stereo (d, frame_size, &stereo);
474 
475 		for (i = 0; i < frame_size * channels; i++) {
476 		  d[i] /= 32767.0;
477 		}
478 		d += (frame_size * channels);
479 		frames_decoded += frame_size;
480 	      }
481 	    }
482 	  }
483 
484 	  packet_count ++;
485 	}
486 
487 	remaining -= n;
488 
489 	percent = (file_length - remaining) * 100 / file_length;
490 	sample_set_progress_percent (sample, percent);
491       }
492     }
493 
494     g_mutex_unlock (sample->ops_mutex);
495   }
496 
497   if (st) speex_decoder_destroy (st);
498   speex_bits_destroy (&bits);
499   ogg_sync_clear (&oy);
500   ogg_stream_clear (&os);
501 
502   close (fd);
503 
504   if (remaining <= 0) {
505     stat (sample->pathname, &statbuf);
506     sample->last_mtime = statbuf.st_mtime;
507     sample->edit_ignore_mtime = FALSE;
508     sample->modified = FALSE;
509   }
510 
511   sample_set_edit_state (sample, SWEEP_EDIT_STATE_DONE);
512 
513   return sample;
514 }
515 
516 static sw_operation speex_load_op = {
517   SWEEP_EDIT_MODE_FILTER,
518   (SweepCallback)sample_load_speex_data,
519   (SweepFunction)NULL,
520   (SweepCallback)NULL, /* undo */
521   (SweepFunction)NULL,
522   (SweepCallback)NULL, /* redo */
523   (SweepFunction)NULL
524 };
525 
526 static sw_sample *
sample_load_speex_info(sw_sample * sample,char * pathname)527 sample_load_speex_info (sw_sample * sample, char * pathname)
528 {
529 #undef BUF_LEN
530 #define BUF_LEN 128
531   char buf[BUF_LEN];
532 
533   gboolean isnew = (sample == NULL);
534 
535   sw_view * v;
536 
537   if (!file_is_ogg_speex (pathname)) return NULL;
538 
539   /* Create the sample/sounddata, initially with length 0, to be grown
540    * as the file is decoded
541    */
542   if (sample == NULL) {
543     /* Channels and rate will be set during decoding and are basically
544      * irrelevent here. Set them to 1, 8000 assuming these are the most
545      * likely values, in which case the file info displayed in the window
546      * will not change suddenly
547      */
548     sample = sample_new_empty(pathname, 1, 8000, 0);
549   } else {
550     int channels, rate;
551 
552     /* Set the channels and rate of the recreated sounddata to be the same
553      * as the old one, as they are most likely the same after a reload */
554     channels = sample->sounddata->format->channels;
555     rate = sample->sounddata->format->rate;
556 
557     sounddata_destroy (sample->sounddata);
558     sample->sounddata = sounddata_new_empty (channels, rate, 0);
559   }
560 
561   if(!sample) {
562     return NULL;
563   }
564 
565   sample->file_method = SWEEP_FILE_METHOD_SPEEX;
566   sample->file_info = NULL;
567 
568   sample_bank_add(sample);
569 
570   if (isnew) {
571     v = view_new_all (sample, 1.0);
572     sample_add_view (sample, v);
573   } else {
574     trim_registered_ops (sample, 0);
575   }
576 
577   g_snprintf (buf, BUF_LEN, _("Loading %s"), g_basename (sample->pathname));
578 
579   schedule_operation (sample, buf, &speex_load_op, sample);
580 
581   return sample;
582 }
583 
584 sw_sample *
speex_sample_reload(sw_sample * sample)585 speex_sample_reload (sw_sample * sample)
586 {
587   if (sample == NULL) return NULL;
588 
589   return sample_load_speex_info (sample, sample->pathname);
590 }
591 
592 sw_sample *
speex_sample_load(char * pathname)593 speex_sample_load (char * pathname)
594 {
595   if (pathname == NULL) return NULL;
596 
597   return sample_load_speex_info (NULL, pathname);
598 }
599 
600 
601 /*
602  * comment creation: from speexenc.c
603  */
604 
605 /*
606  Comments will be stored in the Vorbis style.
607  It is describled in the "Structure" section of
608     http://www.xiph.org/ogg/vorbis/doc/v-comment.html
609 
610 The comment header is decoded as follows:
611   1) [vendor_length] = read an unsigned integer of 32 bits
612   2) [vendor_string] = read a UTF-8 vector as [vendor_length] octets
613   3) [user_comment_list_length] = read an unsigned integer of 32 bits
614   4) iterate [user_comment_list_length] times {
615      5) [length] = read an unsigned integer of 32 bits
616      6) this iteration's user comment = read a UTF-8 vector as [length] octets
617      }
618   7) [framing_bit] = read a single bit as boolean
619   8) if ( [framing_bit]  unset or end of packet ) then ERROR
620   9) done.
621 
622   If you have troubles, please write to ymnk@jcraft.com.
623  */
624 
625 #define readint(buf, base) (((buf[base+3]<<24)&0xff000000)| \
626                            ((buf[base+2]<<16)&0xff0000)| \
627                            ((buf[base+1]<<8)&0xff00)| \
628                             (buf[base]&0xff))
629 #define writeint(buf, base, val) do{ buf[base+3]=(val>>24)&0xff; \
630                                      buf[base+2]=(val>>16)&0xff; \
631                                      buf[base+1]=(val>>8)&0xff; \
632                                      buf[base]=(val)&0xff; \
633                                  }while(0)
634 
comment_init(char ** comments,int * length,char * vendor_string)635 void comment_init(char **comments, int* length, char *vendor_string)
636 {
637   int vendor_length=strlen(vendor_string);
638   int user_comment_list_length=0;
639   int len=4+vendor_length+4;
640   char *p=(char*)malloc(len);
641   if(p==NULL){
642   }
643   writeint(p, 0, vendor_length);
644   memcpy(p+4, vendor_string, vendor_length);
645   writeint(p, 4+vendor_length, user_comment_list_length);
646   *length=len;
647   *comments=p;
648 }
comment_add(char ** comments,int * length,char * tag,char * val)649 void comment_add(char **comments, int* length, char *tag, char *val)
650 {
651   char* p=*comments;
652   int vendor_length=readint(p, 0);
653   int user_comment_list_length=readint(p, 4+vendor_length);
654   int tag_len=(tag?strlen(tag):0);
655   int val_len=strlen(val);
656   int len=(*length)+4+tag_len+val_len;
657 
658   p=realloc(p, len);
659   if(p==NULL){
660   }
661 
662   writeint(p, *length, (tag_len+val_len));      /* length of comment */
663   if(tag) memcpy(p+*length+4, tag, tag_len);  /* comment */
664   memcpy(p+*length+4+tag_len, val, val_len);  /* comment */
665   writeint(p, 4+vendor_length, (user_comment_list_length+1));
666 
667   *comments=p;
668   *length=len;
669 }
670 #undef readint
671 #undef writeint
672 
673 
674 typedef struct {
675   gchar * pathname;
676   gint mode;
677   gint features;
678   gboolean use_br;
679   gfloat quality; /* either way */
680   gint bitrate;
681   gint complexity;
682   gint framepack;
683   long serialno;
684 } speex_save_options;
685 
686 #define MAX_FRAME_SIZE 2000
687 #define MAX_FRAME_BYTES 2000
688 
689 static int
speex_sample_save_thread(sw_op_instance * inst)690 speex_sample_save_thread (sw_op_instance * inst)
691 {
692   sw_sample * sample = inst->sample;
693   gchar * pathname = (gchar *)inst->do_data;
694 
695   FILE * outfile;
696   sw_format * format;
697   sw_audio_t * d;
698   sw_framecount_t remaining, len, run_total;
699   sw_framecount_t nr_frames, cframes;
700   gint percent = 0;
701 
702   speex_save_options * so;
703 
704   ogg_stream_state os; /* take physical pages, weld into a logical
705                           stream of packets */
706   ogg_page         og; /* one Ogg bitstream page. Speex packets are inside */
707   ogg_packet       op; /* one raw packet of data for decode */
708 
709   float input[MAX_FRAME_SIZE];
710   gchar cbits[MAX_FRAME_BYTES];
711   int nbBytes;
712   int id = 0;
713 
714   int frame_size;
715   SpeexMode * mode = NULL;
716   SpeexHeader header;
717   void * st;
718   SpeexBits bits;
719 
720   gchar * vendor_string = "Encoded with Sweep " VERSION " (metadecks.org)";
721   gchar * comments = NULL;
722   int comments_length = 0;
723 
724   int eos = 0;
725   int i, j;
726 
727   gboolean active = TRUE;
728 
729   size_t n, bytes_written = 0;
730   double average_bitrate = 0.0;
731 
732   struct stat statbuf;
733   int errno_save = 0;
734 
735   if (sample == NULL) return -1;
736 
737   so = (speex_save_options *)sample->file_info;
738 
739   format = sample->sounddata->format;
740 
741   nr_frames = sample->sounddata->nr_frames;
742   cframes = nr_frames / 100;
743   if (cframes == 0) cframes = 1;
744 
745   remaining = nr_frames;
746   run_total = 0;
747 
748 #if 0
749   if (format->channels != 1) {
750     fprintf (stderr, "Unsupported channel count for Speex encoding\n");
751     return -1;
752   }
753 #endif
754 
755   if (!(outfile = fopen (pathname, "w"))) {
756     sweep_perror (errno, pathname);
757     return -1;
758   }
759 
760   switch (so->mode) {
761   case MODE_NARROWBAND:
762     mode = (SpeexMode *) &speex_nb_mode;
763     break;
764   case MODE_WIDEBAND:
765     mode = (SpeexMode *) &speex_wb_mode;
766     break;
767 #if (SPEEX_NB_MODES > 2)
768   case MODE_ULTRAWIDEBAND:
769     mode = (SpeexMode *) &speex_uwb_mode;
770     break;
771 #endif
772   default:
773     mode = (SpeexMode *) &speex_nb_mode;
774     break;
775   }
776 
777   speex_init_header (&header, format->rate, 1 , mode);
778   header.frames_per_packet = so->framepack;
779   header.vbr = (so->features & FEAT_VBR) ? 1 : 0;
780   header.nb_channels = format->channels;
781 
782 #ifdef DEBUG
783   fprintf (stderr, "Encoding %d Hz audio using %s mode\n",
784 	   header.rate, mode->modeName);
785 #endif
786 
787   /* initialise Speex encoder */
788   st = speex_encoder_init (mode);
789 
790   /* initialise comments */
791   comment_init (&comments, &comments_length, vendor_string);
792 
793   /* set up our packet->stream encoder */
794   ogg_stream_init (&os, so->serialno);
795 
796   /* write header */
797 
798   {
799     int bytes = op.bytes;
800     op.packet = (unsigned char *)
801       speex_header_to_packet (&header, &bytes);
802     op.bytes = bytes;
803     op.b_o_s = 1;
804     op.e_o_s = 0;
805     op.granulepos = 0;
806     op.packetno = 0;
807     ogg_stream_packetin(&os, &op);
808     free(op.packet);
809 
810     op.packet = (unsigned char *)comments;
811     op.bytes = comments_length;
812     op.b_o_s = 0;
813     op.e_o_s = 0;
814     op.granulepos = 0;
815     op.packetno = 1;
816     ogg_stream_packetin(&os, &op);
817 
818     /* This ensures the actual
819      * audio data will start on a new page, as per spec
820      */
821     while(!eos){
822       int result = ogg_stream_flush (&os, &og);
823       if (result == 0) break;
824 
825       n = fwrite (og.header, 1, og.header_len, outfile);
826       n += fwrite (og.body, 1, og.body_len, outfile);
827 
828       if (fflush (outfile) == 0) {
829 	bytes_written += n;
830       } else {
831 	errno_save = errno;
832 	eos = 1; /* pffft -- this encoding wasn't going anywhere */
833       }
834     }
835   }
836 
837   if (comments) g_free (comments);
838 
839   speex_encoder_ctl (st, SPEEX_SET_SAMPLING_RATE, &format->rate);
840 
841   speex_encoder_ctl (st, SPEEX_GET_FRAME_SIZE, &frame_size);
842   speex_encoder_ctl (st, SPEEX_SET_COMPLEXITY, &so->complexity);
843   if (so->features & FEAT_VBR) {
844     int tmp = 1;
845     speex_encoder_ctl (st, SPEEX_SET_VBR, &tmp);
846     speex_encoder_ctl (st, SPEEX_SET_VBR_QUALITY, &so->quality);
847 #ifdef HAVE_SPEEX_BETA4
848     if (so->use_br) {
849       speex_encoder_ctl (st, SPEEX_SET_ABR, &so->bitrate);
850     }
851 #endif
852   } else {
853     int tmp = (int)floor(so->quality);
854     speex_encoder_ctl (st, SPEEX_SET_QUALITY, &tmp);
855     if (so->use_br) {
856       speex_encoder_ctl (st, SPEEX_SET_BITRATE, &so->bitrate);
857     }
858   }
859 
860 #ifdef HAVE_SPEEX_BETA4
861   if (so->features & FEAT_VAD) {
862     int tmp = 1;
863     speex_encoder_ctl (st, SPEEX_SET_VAD, &tmp);
864     if (so->features & FEAT_DTX) {
865       speex_encoder_ctl (st, SPEEX_SET_DTX, &tmp);
866     }
867   }
868 #endif
869 
870   speex_bits_init (&bits);
871 
872   while (!eos) {
873     g_mutex_lock (sample->ops_mutex);
874 
875     if (sample->edit_state == SWEEP_EDIT_STATE_CANCEL) {
876       active = FALSE;
877     }
878 
879     if (active == FALSE || remaining <= 0) {
880       /* Mark the end of stream */
881       /* XXX: this will be set when this packet is paged out: eos = 1; */
882       op.e_o_s = 1;
883     } else {
884       op.e_o_s = 0;
885 
886       /* data to encode */
887 
888       for (i = 0; i < so->framepack; i++) {
889 	if (remaining > 0) {
890 	  len = MIN (remaining, frame_size);
891 
892 	  d = &((sw_audio_t *)sample->sounddata->data)
893 	    [run_total * format->channels];
894 
895 	  memcpy (input, d, sizeof (sw_audio_t) * len * format->channels);
896 
897 	  /* rip channel 0 out, in required format */
898 	  for (j = 0; j < len * format->channels; j++) {
899 	    input[j] *= 32767.0;
900 	  }
901 
902 	  if (format->channels == 2)
903 	    speex_encode_stereo (input, len, &bits);
904 	  speex_encode (st, input, &bits);
905 
906 	  remaining -= len;
907 
908 	  run_total += len;
909 	  percent = run_total / cframes;
910 	  sample_set_progress_percent (sample, percent);
911 	} else {
912 	  /*speex_bits_pack (&bits, 0, 7);*/
913 	  speex_bits_pack (&bits, 15, 5);
914 	}
915 
916 	id++;
917       }
918     }
919 
920     g_mutex_unlock (sample->ops_mutex);
921 
922     nbBytes = speex_bits_write (&bits, cbits, MAX_FRAME_BYTES);
923     speex_bits_reset (&bits);
924 
925     /* Put it in an ogg packet */
926     op.packet = (unsigned char *)cbits;
927     op.bytes = nbBytes;
928     op.b_o_s = 0;
929     /* op.e_o_s was set above */
930 #if 0 /* XXX: was set above */
931     if (eos)
932       op.e_o_s = 1;
933     else
934       op.e_o_s = 0;
935 #endif
936     op.granulepos = id * frame_size;
937     op.packetno = 2 + (id-1)/so->framepack;
938 
939     /* weld the packet into the bitstream */
940     ogg_stream_packetin(&os,&op);
941 
942     /* write out pages (if any) */
943     while(!eos){
944       int result=ogg_stream_pageout(&os,&og);
945       if(result==0)break;
946 
947       n = fwrite (og.header, 1, og.header_len, outfile);
948       n += fwrite (og.body, 1, og.body_len, outfile);
949 
950       if (fflush (outfile) == 0) {
951 	bytes_written += n;
952       } else {
953 	errno_save = errno;
954 	active = FALSE;
955       }
956 
957       /* this could be set above, but for illustrative purposes, I do
958 	 it here (to show that we know where the stream ends) */
959       if (ogg_page_eos(&og)) eos=1;
960     }
961   }
962 
963 #if 0
964   /*Flush all pages left to be written*/
965   while (ogg_stream_flush(&os, &og))
966     {
967       n = fwrite (og.header, 1, og.header_len, outfile);
968       n += fwrite (og.body, 1, og.body_len, outfile);
969 
970       if (fflush (outfile) == 0) {
971 	bytes_written += n;
972       } else {
973 	errno_save = errno;
974 	active = FALSE;
975       }
976     }
977 #endif
978 
979   /* clean up and exit.  speex_info_clear() must be called last */
980 
981   speex_encoder_destroy (st);
982   speex_bits_destroy (&bits);
983   ogg_stream_clear(&os);
984 
985   fclose (outfile);
986 
987   /* Report success or failure; Calculate and display statistics */
988 
989 #undef BUF_LEN
990 #define BUF_LEN 16
991 
992   if (remaining <= 0) {
993     char time_buf[BUF_LEN], bytes_buf[BUF_LEN];
994 
995 #if 1
996     sample_store_and_free_pathname (sample, pathname);
997 #else
998     g_free (pathname);
999 #endif
1000 
1001     /* Mark the last mtime for this sample */
1002 
1003     stat (sample->pathname, &statbuf);
1004     sample->last_mtime = statbuf.st_mtime;
1005     sample->edit_ignore_mtime = FALSE;
1006     sample->modified = FALSE;
1007 
1008     snprint_time (time_buf, BUF_LEN,
1009 		  frames_to_time (format, nr_frames - remaining));
1010 
1011     snprint_bytes (bytes_buf, BUF_LEN, bytes_written);
1012 
1013     average_bitrate =
1014       8.0/1000.0*((double)bytes_written/((double)nr_frames/(double)format->rate));
1015 
1016     info_dialog_new (_("Speex encoding results"), xifish_xpm,
1017 		     "Encoding of %s succeeded.\n\n"
1018 		     "%s written, %s audio\n"
1019 		     "Average bitrate: %.1f kbps",
1020 		     g_basename (sample->pathname),
1021 		     bytes_buf, time_buf,
1022 		     average_bitrate);
1023   } else {
1024     char time_buf[BUF_LEN], bytes_buf[BUF_LEN];
1025 
1026     snprint_time (time_buf, BUF_LEN,
1027 		  frames_to_time (format, nr_frames - remaining));
1028 
1029     snprint_bytes (bytes_buf, BUF_LEN, bytes_written);
1030 
1031     average_bitrate =
1032       8.0/1000.0*((double)bytes_written/((double)(nr_frames - remaining)/(double)format->rate));
1033     if (isnan(average_bitrate)) average_bitrate = 0.0;
1034 
1035     if (errno_save == 0) {
1036       info_dialog_new (_("Speex encoding results"), xifish_xpm,
1037 		       "Encoding of %s FAILED\n\n"
1038 		       "%s written, %s audio (%d%% complete)\n"
1039 		       "Average bitrate: %.1f kbps",
1040 		       g_basename (pathname), bytes_buf, time_buf, percent,
1041 		       average_bitrate);
1042     } else {
1043       sweep_perror (errno_save,
1044 		    "Encoding of %s FAILED\n\n"
1045 		    "%s written, %s audio (%d%% complete)\n"
1046 		    "Average bitrate: %.1f kbps",
1047 		    g_basename (pathname), bytes_buf, time_buf, percent,
1048 		    average_bitrate);
1049     }
1050   }
1051 
1052   sample_set_edit_state (sample, SWEEP_EDIT_STATE_DONE);
1053 
1054   return 0;
1055 }
1056 
1057 static sw_operation speex_save_op = {
1058   SWEEP_EDIT_MODE_META,
1059   (SweepCallback)speex_sample_save_thread,
1060   (SweepFunction)NULL,
1061   (SweepCallback)NULL, /* undo */
1062   (SweepFunction)NULL,
1063   (SweepCallback)NULL, /* redo */
1064   (SweepFunction)NULL
1065 };
1066 
1067 int
speex_sample_save(sw_sample * sample,char * pathname)1068 speex_sample_save (sw_sample * sample, char * pathname)
1069 {
1070 #undef BUF_LEN
1071 #define BUF_LEN 64
1072   char buf[BUF_LEN];
1073 
1074   g_snprintf (buf, BUF_LEN, _("Saving %s"), g_basename (pathname));
1075 
1076   schedule_operation (sample, buf, &speex_save_op, pathname);
1077 
1078   return 0;
1079 }
1080 
1081 static void
speex_save_options_dialog_ok_cb(GtkWidget * widget,gpointer data)1082 speex_save_options_dialog_ok_cb (GtkWidget * widget, gpointer data)
1083 {
1084   sw_sample * sample = (sw_sample *)data;
1085   GtkWidget * dialog;
1086   speex_save_options * so;
1087   GtkWidget * checkbutton;
1088   GtkWidget * entry;
1089   const gchar * text;
1090 
1091   gboolean use_br;
1092   GtkObject * adj;
1093   int mode, features, quality, bitrate, complexity, framepack;
1094   gboolean rem_encode;
1095   long serialno;
1096   gboolean rem_serialno;
1097 
1098   char * pathname;
1099 
1100   so = g_malloc (sizeof(speex_save_options));
1101 
1102   dialog = gtk_widget_get_toplevel (widget);
1103 
1104   /* Mode */
1105 
1106   mode =
1107     GPOINTER_TO_INT(g_object_get_data (G_OBJECT(dialog), "mode_choice"));
1108 
1109   /* Features */
1110 
1111   features =
1112     GPOINTER_TO_INT(g_object_get_data (G_OBJECT(dialog),
1113 					 "features_choice"));
1114 
1115   adj = GTK_OBJECT(g_object_get_data (G_OBJECT(dialog), "quality_adj"));
1116   quality = (int)GTK_ADJUSTMENT(adj)->value;
1117 
1118   adj = GTK_OBJECT(g_object_get_data (G_OBJECT(dialog), "complexity_adj"));
1119   complexity = (int)GTK_ADJUSTMENT(adj)->value;
1120 
1121   adj = GTK_OBJECT(g_object_get_data (G_OBJECT(dialog), "framepack_adj"));
1122   framepack = (int)GTK_ADJUSTMENT(adj)->value;
1123 
1124   checkbutton =
1125     GTK_WIDGET(g_object_get_data (G_OBJECT(dialog), "br_chb"));
1126   use_br = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(checkbutton));
1127 
1128   entry =
1129     GTK_WIDGET(g_object_get_data (G_OBJECT(dialog), "br_entry"));
1130   text = gtk_entry_get_text (GTK_ENTRY(entry));
1131   bitrate = (int)strtol (text, (char **)NULL, 0);
1132 
1133   /* rem encode */
1134   checkbutton =
1135     GTK_WIDGET(g_object_get_data (G_OBJECT(dialog), "rem_encode_chb"));
1136   rem_encode =
1137     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(checkbutton));
1138 
1139   entry =
1140     GTK_WIDGET(g_object_get_data (G_OBJECT(dialog), "serialno_entry"));
1141   text = gtk_entry_get_text (GTK_ENTRY(entry));
1142   serialno = strtol (text, (char **)NULL, 0);
1143   if (serialno == LONG_MIN || serialno == LONG_MAX) serialno = random ();
1144 
1145   checkbutton =
1146     GTK_WIDGET(g_object_get_data (G_OBJECT(dialog), "rem_serialno_chb"));
1147   rem_serialno =
1148     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(checkbutton));
1149 
1150   pathname = g_object_get_data (G_OBJECT(dialog), "pathname");
1151 
1152   gtk_widget_destroy (dialog);
1153 
1154   if (rem_encode) {
1155     prefs_set_int (MODE_KEY, mode);
1156     prefs_set_int (FEATURES_KEY, features);
1157     prefs_set_int (BR_KEY, use_br);
1158     prefs_set_int (QUALITY_KEY, quality);
1159     prefs_set_int (BITRATE_KEY, bitrate);
1160     prefs_set_int (COMPLEXITY_KEY, complexity);
1161     prefs_set_int (FRAMEPACK_KEY, framepack);
1162   }
1163 
1164   if (rem_serialno) {
1165     prefs_set_long (SERIALNO_KEY, serialno);
1166   } else {
1167     prefs_delete (SERIALNO_KEY);
1168   }
1169 
1170   if (sample->file_info) {
1171     g_free (sample->file_info);
1172   }
1173 
1174   so->mode = mode;
1175   so->features = features;
1176   so->use_br = use_br;
1177   so->quality = quality;
1178   so->bitrate = bitrate;
1179   so->complexity = complexity;
1180   so->framepack = framepack;
1181 
1182   so->serialno = serialno;
1183 
1184   sample->file_info = so;
1185 
1186   speex_sample_save (sample, pathname);
1187 }
1188 
1189 static void
speex_save_options_dialog_cancel_cb(GtkWidget * widget,gpointer data)1190 speex_save_options_dialog_cancel_cb (GtkWidget * widget, gpointer data)
1191 {
1192   GtkWidget * dialog;
1193 
1194   dialog = gtk_widget_get_toplevel (widget);
1195   gtk_widget_destroy (dialog);
1196 
1197   /* if the sample bank is empty, quit the program */
1198   sample_bank_remove (NULL);
1199 }
1200 
1201 typedef struct {
1202   int number;
1203   char * name;
1204   char * desc;
1205 } sw_choice;
1206 
1207 static sw_choice mode_choices[] = {
1208   { MODE_NARROWBAND, N_("Narrowband ~8 kHz (telephone quality)"), NULL },
1209   { MODE_WIDEBAND, N_("Wideband ~16 kHz"), NULL },
1210 #if SPEEX_NB_MODES > 2
1211   { MODE_ULTRAWIDEBAND, N_("Ultra-wideband 32-48 kHz"), NULL },
1212 #endif
1213   { 0, NULL, NULL }
1214 };
1215 
1216 static sw_choice feature_choices[] = {
1217 #ifdef HAVE_SPEEX_BETA4
1218   { 0, N_("Constant bitrate (CBR) with no features"),
1219     NULL
1220   },
1221   { FEAT_VAD, N_("CBR with Voice Activity Detection (VAD)"),
1222     N_("VAD generates low bitrate comfort noise to replace non-speech")
1223   },
1224   { FEAT_VAD | FEAT_DTX,
1225     N_("CBR with VAD and Discontinuous Transmission (DTX)"),
1226     N_("DTX marks extended pauses with a minimum bitrate signal")
1227   },
1228   { FEAT_VBR | FEAT_VAD,
1229     N_("Variable bitrate (VBR) with VAD"),
1230     N_("VBR allows the bitrate to adapt to the complexity of the speech; "
1231        "this selection uses VBR without DTX, which may improve performance "
1232        "compared to full VBR in the presence of background noise.")
1233   },
1234   { FEAT_VBR | FEAT_VAD | FEAT_DTX,
1235     N_("Variable bitrate (VBR) with all features"),
1236     N_("VBR allows the bitrate to adapt to the complexity of the speech, "
1237        "and handles pauses using VAD and DTX")
1238   },
1239 #else
1240   { 0, N_("Constant bitrate (CBR)"), NULL },
1241   { FEAT_VBR,
1242     N_("Variable bitrate (VBR)"),
1243     N_("VBR allows the bitrate to adapt to the complexity of the speech.")
1244   },
1245 #endif
1246   { 0, NULL, NULL }
1247 };
1248 
1249 static void
speex_encode_options_update_cb(GtkWidget * widget,gpointer data)1250 speex_encode_options_update_cb (GtkWidget * widget, gpointer data)
1251 {
1252   GtkWidget * dialog;
1253   GtkWidget * br_checkbutton;
1254   GtkWidget * quality_label;
1255   GtkWidget * quality_hscale;
1256   GtkWidget * br_label;
1257   GtkWidget * br_entry;
1258   GtkWidget * br_units;
1259 
1260   gboolean br;
1261   gint features;
1262 
1263   dialog = gtk_widget_get_toplevel (widget);
1264 
1265   /* Features */
1266   features =
1267     GPOINTER_TO_INT (g_object_get_data (G_OBJECT(dialog),
1268 					  "features_choice"));
1269 
1270   /* Quality */
1271 
1272   quality_label =
1273     GTK_WIDGET(g_object_get_data (G_OBJECT(dialog), "quality_label"));
1274   quality_hscale =
1275     GTK_WIDGET(g_object_get_data (G_OBJECT(dialog), "quality_hscale"));
1276 
1277   /* Bitrate */
1278 
1279   br_checkbutton =
1280     GTK_WIDGET(g_object_get_data (G_OBJECT(dialog), "br_chb"));
1281   br = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(br_checkbutton));
1282 
1283   br_label =
1284     GTK_WIDGET(g_object_get_data (G_OBJECT(dialog), "br_label"));
1285   br_entry =
1286     GTK_WIDGET(g_object_get_data (G_OBJECT(dialog), "br_entry"));
1287   br_units =
1288     GTK_WIDGET(g_object_get_data (G_OBJECT(dialog), "br_units"));
1289 
1290   gtk_widget_set_sensitive (br_label, br);
1291   gtk_widget_set_sensitive (br_entry, br);
1292   gtk_widget_set_sensitive (br_units, br);
1293   gtk_widget_set_sensitive (quality_label, !br);
1294   gtk_widget_set_sensitive (quality_hscale, !br);
1295 
1296   if (features & FEAT_VBR) {
1297     gtk_scale_set_digits (GTK_SCALE (quality_hscale), 1);
1298     gtk_label_set_text (GTK_LABEL(br_label), _("Average bitrate"));
1299   } else {
1300     gtk_scale_set_digits (GTK_SCALE (quality_hscale), 0);
1301     gtk_label_set_text (GTK_LABEL(br_label), _("Maximum bitrate"));
1302   }
1303 
1304 }
1305 
1306 static void
speex_encode_options_mode_cb(GtkWidget * widget,gpointer data)1307 speex_encode_options_mode_cb (GtkWidget * widget, gpointer data)
1308 {
1309   GtkWidget * dialog = GTK_WIDGET (data);
1310   int mode;
1311 
1312   mode = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(widget), "default"));
1313 
1314   g_object_set_data (G_OBJECT(dialog), "mode_choice",
1315 		       GINT_TO_POINTER(mode));
1316 }
1317 
1318 static void
speex_encode_options_mode_auto_cb(GtkWidget * widget,gpointer data)1319 speex_encode_options_mode_auto_cb (GtkWidget * widget, gpointer data)
1320 {
1321   sw_sample * sample = (sw_sample *)data;
1322   sw_format * f = sample->sounddata->format;
1323   GtkWidget * dialog;
1324   GtkOptionMenu * option_menu;
1325   int mode;
1326 
1327   dialog = gtk_widget_get_toplevel (widget);
1328   option_menu =
1329     GTK_OPTION_MENU(g_object_get_data (G_OBJECT(dialog), "mode_menu"));
1330 
1331 #if (SPEEX_NB_MODES > 2)
1332   if (f->rate >= 32000) {
1333     mode = MODE_ULTRAWIDEBAND;
1334   } else
1335 #endif
1336     if (f->rate >= 16000) {
1337       mode = MODE_WIDEBAND;
1338     } else {
1339       mode = MODE_NARROWBAND;
1340     }
1341 
1342   gtk_option_menu_set_history (option_menu, mode);
1343   g_object_set_data (G_OBJECT(dialog), "mode_choice",
1344 		       GINT_TO_POINTER(mode));
1345 }
1346 
1347 static void
speex_encode_options_set_features(GtkWidget * dialog,int features)1348 speex_encode_options_set_features (GtkWidget * dialog, int features)
1349 {
1350   GtkOptionMenu * option_menu;
1351   int i;
1352 
1353   option_menu =
1354     GTK_OPTION_MENU(g_object_get_data (G_OBJECT(dialog), "features_menu"));
1355 
1356   for (i = 0; feature_choices[i].name != NULL; i++) {
1357     if (feature_choices[i].number == features)
1358       gtk_option_menu_set_history (option_menu, i);
1359   }
1360 
1361   g_object_set_data (G_OBJECT(dialog), "features_choice",
1362 		       GINT_TO_POINTER(features));
1363 }
1364 
1365 static void
speex_encode_options_features_cb(GtkWidget * widget,gpointer data)1366 speex_encode_options_features_cb (GtkWidget * widget, gpointer data)
1367 {
1368   GtkWidget * dialog = GTK_WIDGET (data);
1369   int features;
1370 
1371   features = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(widget), "default"));
1372 
1373   g_object_set_data (G_OBJECT(dialog), "features_choice",
1374 		       GINT_TO_POINTER(features));
1375 
1376   speex_encode_options_update_cb (dialog, data);
1377 }
1378 
1379 
1380 static void
speex_encode_options_reset_cb(GtkWidget * widget,gpointer data)1381 speex_encode_options_reset_cb (GtkWidget * widget, gpointer data)
1382 {
1383   GtkWidget * dialog;
1384 
1385   GtkObject * adj;
1386   int * i, features, quality, complexity, framepack;
1387 
1388   dialog = gtk_widget_get_toplevel (widget);
1389 
1390   /* Mode menu */
1391   speex_encode_options_mode_auto_cb (widget, data);
1392 
1393   /* Features menu */
1394   i = prefs_get_int (FEATURES_KEY);
1395 
1396   if (i == NULL) {
1397     features = DEFAULT_FEATURES;
1398   } else {
1399     features = *i;
1400   }
1401 
1402   speex_encode_options_set_features (dialog, features);
1403 
1404   /* Quality */
1405 
1406   adj = GTK_OBJECT(g_object_get_data (G_OBJECT(dialog), "quality_adj"));
1407 
1408   i = prefs_get_int (QUALITY_KEY);
1409 
1410   if (i == NULL) {
1411     quality = DEFAULT_QUALITY;
1412   } else {
1413     quality = *i;
1414   }
1415 
1416   gtk_adjustment_set_value (GTK_ADJUSTMENT(adj), (float)quality);
1417 
1418   /* Complexity */
1419 
1420   adj = GTK_OBJECT(g_object_get_data (G_OBJECT(dialog), "complexity_adj"));
1421 
1422   i = prefs_get_int (COMPLEXITY_KEY);
1423 
1424   if (i == NULL) {
1425     complexity = DEFAULT_COMPLEXITY;
1426   } else {
1427     complexity = *i;
1428   }
1429 
1430   gtk_adjustment_set_value (GTK_ADJUSTMENT(adj), (float)complexity);
1431 
1432   /* Framepack */
1433 
1434   adj = GTK_OBJECT(g_object_get_data (G_OBJECT(dialog), "framepack_adj"));
1435 
1436   i = prefs_get_int (FRAMEPACK_KEY);
1437 
1438   if (i == NULL) {
1439     framepack = DEFAULT_FRAMEPACK;
1440   } else {
1441     framepack = *i;
1442   }
1443 
1444   gtk_adjustment_set_value (GTK_ADJUSTMENT(adj), (float)framepack);
1445 
1446   speex_encode_options_update_cb (widget, data);
1447 }
1448 
1449 static void
speex_encode_options_default_cb(GtkWidget * widget,gpointer data)1450 speex_encode_options_default_cb (GtkWidget * widget, gpointer data)
1451 {
1452   GtkWidget * dialog;
1453   GtkObject * quality_adj;
1454   GtkObject * complexity_adj;
1455   GtkObject * framepack_adj;
1456 
1457   dialog = gtk_widget_get_toplevel (widget);
1458 
1459   /* Mode menu */
1460   speex_encode_options_mode_auto_cb (widget, data);
1461 
1462   /* Features menu */
1463   speex_encode_options_set_features (dialog, DEFAULT_FEATURES);
1464 
1465   /* Quality */
1466   quality_adj =
1467     GTK_OBJECT(g_object_get_data (G_OBJECT(dialog), "quality_adj"));
1468   gtk_adjustment_set_value (GTK_ADJUSTMENT(quality_adj), DEFAULT_QUALITY);
1469 
1470   /* Complexity */
1471   complexity_adj =
1472     GTK_OBJECT(g_object_get_data (G_OBJECT(dialog), "complexity_adj"));
1473   gtk_adjustment_set_value (GTK_ADJUSTMENT(complexity_adj),
1474 			    DEFAULT_COMPLEXITY);
1475 
1476   /* Framepack */
1477   framepack_adj =
1478     GTK_OBJECT(g_object_get_data (G_OBJECT(dialog), "framepack_adj"));
1479   gtk_adjustment_set_value (GTK_ADJUSTMENT(framepack_adj), DEFAULT_FRAMEPACK);
1480 
1481   speex_encode_options_update_cb (widget, data);
1482 }
1483 
1484 
1485 static void
remember_serialno_clicked_cb(GtkWidget * widget,gpointer data)1486 remember_serialno_clicked_cb (GtkWidget * widget, gpointer data)
1487 {
1488   sw_sample * sample = (sw_sample *)data;
1489   gboolean active;
1490 
1491   active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget));
1492 
1493   if (active) {
1494     sample_set_tmp_message (sample, _("Hack the planet!"));
1495   } else {
1496     sample_clear_tmp_message (sample);
1497   }
1498 }
1499 
1500 static gboolean
randomise_serialno(gpointer data)1501 randomise_serialno (gpointer data)
1502 {
1503   GtkWidget * entry = (GtkWidget *)data;
1504   gchar * new_text;
1505 
1506   new_text = g_strdup_printf ("%ld", random ());
1507   gtk_entry_set_text (GTK_ENTRY (entry), new_text);
1508   g_free (new_text);
1509 
1510   return TRUE;
1511 }
1512 
1513 static void
randomise_serialno_pressed_cb(GtkWidget * widget,gpointer data)1514 randomise_serialno_pressed_cb (GtkWidget * widget, gpointer data)
1515 {
1516   GtkWidget * dialog;
1517   GtkWidget * checkbutton;
1518   gint tag;
1519 
1520   dialog = gtk_widget_get_toplevel (widget);
1521 
1522   checkbutton =
1523     GTK_WIDGET(g_object_get_data (G_OBJECT(dialog), "rem_serialno_chb"));
1524   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(checkbutton), FALSE);
1525 
1526   tag = gtk_timeout_add (30, randomise_serialno, data);
1527   g_object_set_data (G_OBJECT(widget), "tag", GINT_TO_POINTER(tag));
1528 }
1529 
1530 static void
randomise_serialno_released_cb(GtkWidget * widget,gpointer data)1531 randomise_serialno_released_cb (GtkWidget * widget, gpointer data)
1532 {
1533   gint tag;
1534 
1535   tag = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(widget), "tag"));
1536   gtk_timeout_remove (tag);
1537 }
1538 
1539 
1540 
1541 static GtkWidget *
create_speex_encoding_options_dialog(sw_sample * sample,char * pathname)1542 create_speex_encoding_options_dialog (sw_sample * sample, char * pathname)
1543 {
1544   GtkWidget * dialog;
1545   GtkWidget * ok_button, * button;
1546   GtkWidget * main_vbox;
1547   GtkWidget * ebox;
1548   GtkWidget * vbox;
1549   GtkWidget * hbox, * hbox2;
1550   GtkWidget * option_menu;
1551   GtkWidget * menu;
1552   GtkWidget * menuitem;
1553   GtkWidget * table;
1554   GtkWidget * label;
1555   GtkWidget * pixmap;
1556 
1557   GtkWidget * notebook;
1558 
1559   GtkWidget * checkbutton;
1560   GtkObject * quality_adj;
1561   GtkWidget * quality_hscale;
1562   GtkObject * complexity_adj;
1563   GtkWidget * complexity_hscale;
1564   GtkObject * framepack_adj;
1565   GtkWidget * framepack_spin;
1566 
1567   GtkWidget * entry;
1568 
1569   GtkWidget * separator;
1570   GtkTooltips * tooltips;
1571   GtkWidget *speex_logo;
1572 
1573 /*  GtkStyle * style; */
1574 
1575   int i;
1576   long * l;
1577 
1578   dialog = gtk_dialog_new ();
1579   gtk_window_set_title (GTK_WINDOW(dialog),
1580 			_("Sweep: Speex save options"));
1581   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
1582   sweep_set_window_icon(GTK_WINDOW(dialog));
1583 
1584   attach_window_close_accel(GTK_WINDOW(dialog));
1585 
1586   g_object_set_data (G_OBJECT(dialog), "pathname", pathname);
1587 
1588   main_vbox = GTK_DIALOG(dialog)->vbox;
1589 
1590   ebox = gtk_event_box_new ();
1591   gtk_box_pack_start (GTK_BOX(main_vbox), ebox, FALSE, TRUE, 0);
1592   gtk_widget_set_style (ebox, style_bw);
1593   gtk_widget_show (ebox);
1594 
1595   vbox = gtk_vbox_new (FALSE, 0);
1596   gtk_container_add (GTK_CONTAINER(ebox), vbox);
1597   gtk_widget_show (vbox);
1598 
1599   /* Ogg Speex pixmaps */
1600 
1601   hbox = gtk_hbox_new (FALSE, 0);
1602   gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1603   gtk_container_set_border_width (GTK_CONTAINER(hbox), 4);
1604   gtk_widget_show (hbox);
1605 
1606   speex_logo = create_widget_from_xpm (dialog, speex_logo_xpm);
1607   gtk_box_pack_start (GTK_BOX(hbox), speex_logo, FALSE, FALSE, 0);
1608   gtk_widget_show (speex_logo);
1609 
1610    /* filename */
1611 
1612 
1613 /* worth changing this over to pango?
1614 
1615   style = gtk_style_new ();
1616   gdk_font_unref (style->font);
1617   style->font =
1618   gdk_font_load("-*-helvetica-medium-r-normal-*-*-180-*-*-*-*-*-*");
1619   gtk_widget_push_style (style);
1620 */
1621   label = gtk_label_new (g_basename (pathname));
1622   gtk_box_pack_start (GTK_BOX(vbox), label, TRUE, FALSE, 0);
1623   gtk_widget_show (label);
1624 
1625 /* gtk_widget_pop_style (); */
1626 
1627   notebook = gtk_notebook_new ();
1628   gtk_box_pack_start (GTK_BOX(main_vbox), notebook, TRUE, TRUE, 4);
1629   gtk_widget_show (notebook);
1630 
1631   label = gtk_label_new (_("Speex encoding"));
1632 
1633   vbox = gtk_vbox_new (FALSE, 0);
1634   gtk_notebook_append_page (GTK_NOTEBOOK(notebook), vbox, label);
1635   gtk_container_set_border_width (GTK_CONTAINER(vbox), 4);
1636   gtk_widget_show (vbox);
1637 
1638   /* Mode */
1639 
1640   hbox = gtk_hbox_new (FALSE, 4);
1641   gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, TRUE, 4);
1642   gtk_widget_show (hbox);
1643 
1644   label = gtk_label_new (_("Mode:"));
1645   gtk_box_pack_start (GTK_BOX(hbox), label, FALSE, FALSE, 0);
1646   gtk_widget_show (label);
1647 
1648   option_menu = gtk_option_menu_new ();
1649   gtk_box_pack_start (GTK_BOX (hbox), option_menu, TRUE, TRUE, 4);
1650   gtk_widget_show (option_menu);
1651 
1652   menu = gtk_menu_new ();
1653 
1654   for (i = 0; mode_choices[i].name != NULL; i++) {
1655     menuitem =
1656       gtk_menu_item_new_with_label (_(mode_choices[i].name));
1657     gtk_menu_append (GTK_MENU(menu), menuitem);
1658     g_object_set_data (G_OBJECT(menuitem), "default",
1659 			      GINT_TO_POINTER(mode_choices[i].number));
1660     gtk_widget_show (menuitem);
1661 
1662     g_signal_connect (G_OBJECT(menuitem), "activate",
1663 			G_CALLBACK(speex_encode_options_mode_cb),
1664 			dialog);
1665   }
1666   gtk_option_menu_set_menu (GTK_OPTION_MENU(option_menu), menu);
1667 
1668   g_object_set_data (G_OBJECT(dialog), "mode_menu", option_menu);
1669 
1670   button = gtk_button_new_with_label (_("Auto"));
1671   gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 4);
1672   g_signal_connect (G_OBJECT(button), "clicked",
1673 		      G_CALLBACK(speex_encode_options_mode_auto_cb),
1674 		      sample);
1675   gtk_widget_show (button);
1676 
1677   tooltips = gtk_tooltips_new ();
1678   gtk_tooltips_set_tip (tooltips, button,
1679 			_("Automatically select the encoding mode based on "
1680 			  "the sampling rate of the file."),
1681 			NULL);
1682 
1683   separator = gtk_hseparator_new ();
1684   gtk_box_pack_start (GTK_BOX(vbox), separator, FALSE, TRUE, 4);
1685   gtk_widget_show (separator);
1686 
1687   /* Features */
1688 
1689   option_menu = gtk_option_menu_new ();
1690   gtk_box_pack_start (GTK_BOX (vbox), option_menu, FALSE, FALSE, 4);
1691   gtk_widget_show (option_menu);
1692 
1693   menu = gtk_menu_new ();
1694 
1695   for (i = 0; feature_choices[i].name != NULL; i++) {
1696     menuitem =
1697       gtk_menu_item_new_with_label (_(feature_choices[i].name));
1698     gtk_menu_append (GTK_MENU(menu), menuitem);
1699     g_object_set_data (G_OBJECT(menuitem), "default",
1700 			      GINT_TO_POINTER(feature_choices[i].number));
1701     gtk_widget_show (menuitem);
1702 
1703     g_signal_connect (G_OBJECT(menuitem), "activate",
1704 			G_CALLBACK(speex_encode_options_features_cb),
1705 			dialog);
1706 
1707     if (feature_choices[i].desc != NULL) {
1708       tooltips = gtk_tooltips_new ();
1709       gtk_tooltips_set_tip (tooltips, menuitem, _(feature_choices[i].desc),
1710 			    NULL);
1711     }
1712   }
1713   gtk_option_menu_set_menu (GTK_OPTION_MENU(option_menu), menu);
1714 
1715   g_object_set_data (G_OBJECT(dialog), "features_menu", option_menu);
1716 
1717 
1718   table = gtk_table_new (3, 2, TRUE);
1719   gtk_box_pack_start (GTK_BOX(vbox), table, FALSE, FALSE, 4);
1720   gtk_container_set_border_width (GTK_CONTAINER(table), 8);
1721   gtk_widget_show (table);
1722 
1723   /* quality */
1724 
1725   hbox = gtk_hbox_new (FALSE, 4);
1726   /*gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 4);*/
1727   gtk_table_attach (GTK_TABLE(table), hbox, 0, 1, 0, 1,
1728 		    GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
1729   /*gtk_container_set_border_width (GTK_CONTAINER(hbox), 12);*/
1730   gtk_widget_show (hbox);
1731 
1732   label = gtk_label_new (_("Encoding quality:"));
1733   gtk_box_pack_start (GTK_BOX(hbox), label, FALSE, FALSE, 4);
1734   gtk_widget_show (label);
1735 
1736   g_object_set_data (G_OBJECT (dialog), "quality_label", label);
1737 
1738   quality_adj = gtk_adjustment_new (DEFAULT_QUALITY, /* value */
1739 				    1.0,  /* lower */
1740 				    11.0, /* upper */
1741 				    1.0,  /* step incr */
1742 				    1.0,  /* page incr */
1743 				    1.0   /* page size */
1744 				    );
1745 
1746   {
1747     /* How sucky ... we create a vbox in order to center the hscale within
1748      * its allocation, thus actually lining it up with its label ...
1749      */
1750     GtkWidget * vbox_pants;
1751 
1752     vbox_pants = gtk_vbox_new (FALSE, 0);
1753     /*gtk_box_pack_start (GTK_BOX(hbox), vbox_pants, TRUE, TRUE, 0);*/
1754     gtk_table_attach (GTK_TABLE(table), vbox_pants, 1, 2, 0, 1,
1755 		      GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
1756     gtk_widget_show (vbox_pants);
1757 
1758     quality_hscale = gtk_hscale_new (GTK_ADJUSTMENT(quality_adj));
1759     gtk_box_pack_start (GTK_BOX (vbox_pants), quality_hscale, TRUE, TRUE, 0);
1760     gtk_scale_set_draw_value (GTK_SCALE (quality_hscale), TRUE);
1761     gtk_scale_set_digits (GTK_SCALE (quality_hscale), 0);
1762     gtk_widget_set_usize (quality_hscale, gdk_screen_width() / 8, -1);
1763     gtk_widget_show (quality_hscale);
1764 
1765     g_object_set_data (G_OBJECT (dialog), "quality_hscale",
1766 			 quality_hscale);
1767 
1768     label = gtk_label_new (NULL);
1769     gtk_box_pack_start (GTK_BOX(vbox_pants), label, FALSE, FALSE, 0);
1770     gtk_widget_show (label);
1771   }
1772 
1773   tooltips = gtk_tooltips_new ();
1774   gtk_tooltips_set_tip (tooltips, quality_hscale,
1775 			_("Encoding quality between 0 (lowest quality, "
1776 			  "smallest file) and 10 (highest quality, largest "
1777 			  "file)."),
1778 			NULL);
1779 
1780   g_object_set_data (G_OBJECT (dialog), "quality_adj", quality_adj);
1781 
1782   /* Bit rate */
1783 
1784   checkbutton =
1785     gtk_check_button_new_with_label (_("Enable bitrate management"));
1786   gtk_table_attach (GTK_TABLE(table), checkbutton, 0, 2, 1, 2,
1787 		    GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
1788   gtk_widget_show (checkbutton);
1789 
1790   g_signal_connect (G_OBJECT(checkbutton), "toggled",
1791 		      G_CALLBACK(speex_encode_options_update_cb),
1792 		      dialog);
1793 
1794   g_object_set_data (G_OBJECT (dialog), "br_chb", checkbutton);
1795 
1796   tooltips = gtk_tooltips_new ();
1797   gtk_tooltips_set_tip (tooltips, checkbutton,
1798 			_("For non-VBR (constant bitrate) encoding, "
1799 			  "this sets the maximum bitrate."
1800 			  "For VBR encoding, this sets the average bitrate."),
1801 			NULL);
1802 
1803   label = gtk_label_new (_("Average bitrate"));
1804   gtk_table_attach (GTK_TABLE(table), label, 0, 1, 2, 3,
1805 		    GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
1806   gtk_widget_show (label);
1807 
1808   g_object_set_data (G_OBJECT (dialog), "br_label", label);
1809 
1810   hbox = gtk_hbox_new (FALSE, 0);
1811   gtk_table_attach (GTK_TABLE(table), hbox, 1, 2, 2, 3,
1812 		    GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
1813   gtk_widget_show (hbox);
1814 
1815   entry = gtk_entry_new ();
1816   gtk_box_pack_start (GTK_BOX(hbox), entry, TRUE, TRUE, 0);
1817   gtk_widget_show (entry);
1818 
1819   g_object_set_data (G_OBJECT (dialog), "br_entry", entry);
1820 
1821   label = gtk_label_new (_("bps"));
1822   gtk_box_pack_start (GTK_BOX(hbox), label, FALSE, FALSE, 4);
1823   gtk_widget_show (label);
1824 
1825   g_object_set_data (G_OBJECT (dialog), "br_units", label);
1826 
1827   label = gtk_label_new (_("Extra"));
1828 
1829   vbox = gtk_vbox_new (FALSE, 0);
1830   gtk_notebook_append_page (GTK_NOTEBOOK(notebook), vbox, label);
1831   gtk_container_set_border_width (GTK_CONTAINER(vbox), 4);
1832   gtk_widget_show (vbox);
1833 
1834   table = gtk_table_new (2, 2, TRUE);
1835   gtk_box_pack_start (GTK_BOX(vbox), table, FALSE, FALSE, 4);
1836   /*gtk_container_set_border_width (GTK_CONTAINER(table), 8);*/
1837   gtk_widget_show (table);
1838 
1839 
1840   /* Encoder complexity */
1841 
1842   hbox = gtk_hbox_new (FALSE, 4);
1843   /*gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 4);*/
1844   gtk_table_attach (GTK_TABLE(table), hbox, 0, 1, 0, 1,
1845 		    GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
1846   /*gtk_container_set_border_width (GTK_CONTAINER(hbox), 12);*/
1847   gtk_widget_show (hbox);
1848 
1849   label = gtk_label_new (_("Encoding complexity:"));
1850   gtk_box_pack_start (GTK_BOX(hbox), label, FALSE, FALSE, 4);
1851   gtk_widget_show (label);
1852 
1853   complexity_adj = gtk_adjustment_new (DEFAULT_COMPLEXITY, /* value */
1854 				       1.0,  /* lower */
1855 				       11.0, /* upper */
1856 				       1.0,  /* step incr */
1857 				       1.0,  /* page incr */
1858 				       1.0   /* page size */
1859 				       );
1860 
1861   {
1862     /* How sucky ... we create a vbox in order to center the hscale within
1863      * its allocation, thus actually lining it up with its label ...
1864      */
1865     GtkWidget * vbox_pants;
1866 
1867     vbox_pants = gtk_vbox_new (FALSE, 0);
1868     /*gtk_box_pack_start (GTK_BOX(hbox), vbox_pants, TRUE, TRUE, 0);*/
1869     gtk_table_attach (GTK_TABLE(table), vbox_pants, 1, 2, 0, 1,
1870 		      GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
1871     gtk_widget_show (vbox_pants);
1872 
1873     complexity_hscale = gtk_hscale_new (GTK_ADJUSTMENT(complexity_adj));
1874     gtk_box_pack_start (GTK_BOX (vbox_pants), complexity_hscale,
1875 			TRUE, TRUE, 0);
1876     gtk_scale_set_draw_value (GTK_SCALE (complexity_hscale), TRUE);
1877     gtk_scale_set_digits (GTK_SCALE (complexity_hscale), 0);
1878     gtk_widget_set_usize (complexity_hscale, gdk_screen_width() / 8, -1);
1879     gtk_widget_show (complexity_hscale);
1880 
1881     label = gtk_label_new (NULL);
1882     gtk_box_pack_start (GTK_BOX(vbox_pants), label, FALSE, FALSE, 0);
1883     gtk_widget_show (label);
1884   }
1885 
1886   tooltips = gtk_tooltips_new ();
1887   gtk_tooltips_set_tip (tooltips, complexity_hscale,
1888 			_("This sets the encoding speed/quality tradeoff "
1889 			  "between 0 (faster encoding) "
1890 			  "and 10 (slower encoding)"),
1891 			NULL);
1892 
1893   g_object_set_data (G_OBJECT (dialog), "complexity_adj", complexity_adj);
1894 
1895   /* Frames per packet */
1896 
1897   hbox = gtk_hbox_new (FALSE, 4);
1898   /*gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 4);*/
1899   gtk_table_attach (GTK_TABLE(table), hbox, 0, 1, 1, 2,
1900 		    GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
1901   /*gtk_container_set_border_width (GTK_CONTAINER(hbox), 12);*/
1902   gtk_widget_show (hbox);
1903 
1904   label = gtk_label_new (_("Speex frames per Ogg packet:"));
1905   gtk_box_pack_start (GTK_BOX(hbox), label, FALSE, FALSE, 4);
1906   gtk_widget_show (label);
1907 
1908   hbox = gtk_hbox_new (FALSE, 4);
1909   /*gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 4);*/
1910   gtk_table_attach (GTK_TABLE(table), hbox, 1, 2, 1, 2,
1911 		    GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
1912   /*gtk_container_set_border_width (GTK_CONTAINER(hbox), 12);*/
1913   gtk_widget_show (hbox);
1914 
1915   framepack_adj = gtk_adjustment_new (DEFAULT_FRAMEPACK, /* value */
1916 				      1.0,  /* lower */
1917 				      10.0, /* upper */
1918 				      1.0,  /* step incr */
1919 				      1.0,  /* page incr */
1920 				      1.0   /* page size */
1921 				      );
1922 
1923   framepack_spin = gtk_spin_button_new (GTK_ADJUSTMENT(framepack_adj),
1924 					  1.0, 0);
1925   gtk_box_pack_start (GTK_BOX (hbox), framepack_spin, FALSE, FALSE, 0);
1926   gtk_widget_show (framepack_spin);
1927 
1928   tooltips = gtk_tooltips_new ();
1929   gtk_tooltips_set_tip (tooltips, framepack_spin,
1930 			_("Number of Speex frames to pack into each Ogg "
1931 			  "packet. Higher values save space at low "
1932 			  "bitrates."),
1933 			NULL);
1934 
1935   g_object_set_data (G_OBJECT (dialog), "framepack_adj", framepack_adj);
1936 
1937   /* Remember / Reset */
1938 
1939   hbox = gtk_hbox_new (FALSE, 4);
1940   gtk_box_pack_start (GTK_BOX(main_vbox), hbox, FALSE, FALSE, 4);
1941   gtk_container_set_border_width (GTK_CONTAINER(hbox), 12);
1942   gtk_widget_show (hbox);
1943 
1944   checkbutton =
1945     gtk_check_button_new_with_label (_("Remember these encoding options"));
1946   gtk_box_pack_start (GTK_BOX (hbox), checkbutton, TRUE, TRUE, 0);
1947   gtk_widget_show (checkbutton);
1948 
1949   g_object_set_data (G_OBJECT (dialog), "rem_encode_chb", checkbutton);
1950 
1951   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(checkbutton), TRUE);
1952 
1953   hbox2 = gtk_hbox_new (TRUE, 4);
1954   gtk_box_pack_start (GTK_BOX (hbox), hbox2, FALSE, TRUE, 0);
1955   gtk_widget_show (hbox2);
1956 
1957   button = gtk_button_new_with_label (_("Reset"));
1958   gtk_box_pack_start (GTK_BOX (hbox2), button, FALSE, TRUE, 4);
1959   g_signal_connect (G_OBJECT(button), "clicked",
1960 		      G_CALLBACK(speex_encode_options_reset_cb), sample);
1961   gtk_widget_show (button);
1962 
1963   tooltips = gtk_tooltips_new ();
1964   gtk_tooltips_set_tip (tooltips, button,
1965 			_("Reset to the last remembered encoding options."),
1966 			NULL);
1967 
1968   /* Call the reset callback now to set remembered options */
1969   speex_encode_options_reset_cb (button, sample);
1970 
1971   button = gtk_button_new_with_label (_("Defaults"));
1972   gtk_box_pack_start (GTK_BOX (hbox2), button, FALSE, TRUE, 4);
1973   g_signal_connect (G_OBJECT(button), "clicked",
1974 		      G_CALLBACK(speex_encode_options_default_cb),
1975 		      sample);
1976   gtk_widget_show (button);
1977 
1978   tooltips = gtk_tooltips_new ();
1979   gtk_tooltips_set_tip (tooltips, button,
1980 			_("Automatically select best encoding options for this file."),
1981 			NULL);
1982 
1983   /* Ogg stream */
1984 
1985   label = gtk_label_new (_("Ogg stream"));
1986 
1987   vbox = gtk_vbox_new (FALSE, 4);
1988   gtk_notebook_append_page (GTK_NOTEBOOK(notebook), vbox, label);
1989   gtk_widget_show (vbox);
1990 
1991   /* Stream serial no. */
1992 
1993   hbox = gtk_hbox_new (FALSE, 0);
1994   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1995   gtk_container_set_border_width (GTK_CONTAINER(hbox), 12);
1996   gtk_widget_show (hbox);
1997 
1998   label = gtk_label_new (_("Ogg stream serial number:"));
1999   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 4);
2000   gtk_widget_show (label);
2001 
2002   entry = gtk_entry_new ();
2003   gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 4);
2004   gtk_widget_show (entry);
2005 
2006   g_object_set_data (G_OBJECT (dialog), "serialno_entry", entry);
2007 
2008   /* Remember serialno ? */
2009 
2010   hbox = gtk_hbox_new (FALSE, 0);
2011   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
2012   gtk_widget_show (hbox);
2013 
2014   button = gtk_hseparator_new ();
2015   gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 8);
2016   gtk_widget_show (button);
2017 
2018   checkbutton =
2019     gtk_check_button_new_with_label (_("Remember this serial number"));
2020   gtk_box_pack_start (GTK_BOX (hbox), checkbutton, FALSE, TRUE, 0);
2021   gtk_widget_show (checkbutton);
2022 
2023   g_signal_connect (G_OBJECT(checkbutton), "toggled",
2024 		      G_CALLBACK(remember_serialno_clicked_cb),
2025 		      sample);
2026 
2027   tooltips = gtk_tooltips_new ();
2028   gtk_tooltips_set_tip (tooltips, checkbutton,
2029 			_("Remember this serial number for future re-use.\n"
2030 			  "USE OF THIS OPTION IS NOT RECOMMENDED.\n"
2031 			  "Each encoded file should have a different "
2032 			  "serial number; "
2033 			  "re-use of Ogg serial numbers in different files "
2034 			  "may create incompatabilities with streaming "
2035 			  "applications. "
2036 			  "This option is provided for bitstream engineering "
2037 			  "purposes only.\n"
2038 			  "If this option is not checked, new serial numbers "
2039 			  "will be randomly generated for each file encoded."),
2040 			NULL);
2041 
2042   g_object_set_data (G_OBJECT (dialog), "rem_serialno_chb", checkbutton);
2043 
2044   l = prefs_get_long (SERIALNO_KEY);
2045 
2046   if (l == NULL) {
2047     randomise_serialno (entry);
2048     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(checkbutton), FALSE);
2049   } else {
2050     gtk_entry_set_text (GTK_ENTRY(entry), g_strdup_printf ("%ld", *l));
2051     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(checkbutton), TRUE);
2052   }
2053 
2054   /* Randomise serialno! */
2055 
2056   button = gtk_button_new_with_label (_("Randomize!"));
2057   gtk_container_set_border_width (GTK_CONTAINER(button), 64);
2058   gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);
2059   gtk_widget_show (button);
2060 
2061   tooltips = gtk_tooltips_new ();
2062   gtk_tooltips_set_tip (tooltips, button,
2063 			_("Generate a random serial number for the "
2064 			  "Ogg bitstream. The number will change while "
2065 			  "this button is held down."),
2066 			NULL);
2067 
2068   g_signal_connect (G_OBJECT(button), "pressed",
2069 		      G_CALLBACK(randomise_serialno_pressed_cb),
2070 		      entry);
2071 
2072   g_signal_connect (G_OBJECT(button), "released",
2073 		      G_CALLBACK(randomise_serialno_released_cb),
2074 		      entry);
2075 
2076   /* About */
2077 
2078   label = gtk_label_new (_("About"));
2079 
2080   ebox = gtk_event_box_new ();
2081   gtk_notebook_append_page (GTK_NOTEBOOK(notebook), ebox, label);
2082   gtk_widget_set_style (ebox, style_bw);
2083   gtk_widget_show (ebox);
2084 
2085   gtk_notebook_set_tab_label_packing (GTK_NOTEBOOK(notebook), ebox,
2086 				      TRUE, TRUE, GTK_PACK_END);
2087 
2088   vbox = gtk_vbox_new (FALSE, 16);
2089   gtk_container_add (GTK_CONTAINER(ebox), vbox);
2090   gtk_container_set_border_width (GTK_CONTAINER(vbox), 8);
2091   gtk_widget_show (vbox);
2092 
2093   label =
2094     gtk_label_new (_("Speex is a high quality speech codec designed for\n"
2095 		     "voice over IP (VoIP) and file-based compression.\n"
2096 		     "It is free, open and unpatented."));
2097   gtk_box_pack_start (GTK_BOX(vbox), label, FALSE, FALSE, 0);
2098   gtk_widget_show (label);
2099 
2100   hbox = gtk_hbox_new (FALSE, 16);
2101   gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
2102   gtk_widget_show (hbox);
2103 
2104   label =
2105     gtk_label_new (_("Ogg, Speex, Xiph.org Foundation and their logos\n"
2106 		     "are trademarks (tm) of the Xiph.org Foundation.\n"
2107 		     "Used with permission."));
2108   gtk_box_pack_start (GTK_BOX(hbox), label, FALSE, FALSE, 8);
2109   gtk_widget_show (label);
2110 
2111   pixmap = create_widget_from_xpm (dialog, xifish_xpm);
2112   gtk_box_pack_start (GTK_BOX(hbox), pixmap, FALSE, FALSE, 8);
2113   gtk_widget_show (pixmap);
2114 
2115 
2116   button = gtk_hseparator_new ();
2117   gtk_box_pack_start (GTK_BOX(vbox), button, FALSE, FALSE, 8);
2118   gtk_widget_show (button);
2119 
2120   label = gtk_label_new (_("This user interface by Conrad Parker,\n"
2121 			   "Copyright (C) 2002 CSIRO Australia.\n\n"));
2122   gtk_box_pack_start (GTK_BOX(vbox), label, FALSE, FALSE, 0);
2123   gtk_widget_show (label);
2124 
2125   /* OK */
2126 
2127   ok_button = gtk_button_new_with_label (_("Save"));
2128   GTK_WIDGET_SET_FLAGS (GTK_WIDGET (ok_button), GTK_CAN_DEFAULT);
2129   gtk_box_pack_start (GTK_BOX (GTK_DIALOG(dialog)->action_area), ok_button,
2130 		      TRUE, TRUE, 0);
2131   gtk_widget_show (ok_button);
2132   g_signal_connect (G_OBJECT(ok_button), "clicked",
2133 		      G_CALLBACK (speex_save_options_dialog_ok_cb),
2134 		      sample);
2135 
2136   /* Cancel */
2137 
2138   button = gtk_button_new_with_label (_("Don't save"));
2139   GTK_WIDGET_SET_FLAGS (GTK_WIDGET (button), GTK_CAN_DEFAULT);
2140   gtk_box_pack_start (GTK_BOX (GTK_DIALOG(dialog)->action_area), button,
2141 		      TRUE, TRUE, 0);
2142   gtk_widget_show (button);
2143   g_signal_connect (G_OBJECT(button), "clicked",
2144 		      G_CALLBACK (speex_save_options_dialog_cancel_cb),
2145 		      sample);
2146 
2147   gtk_widget_grab_default (ok_button);
2148 
2149   return (dialog);
2150 }
2151 
2152 int
speex_save_options_dialog(sw_sample * sample,char * pathname)2153 speex_save_options_dialog (sw_sample * sample, char * pathname)
2154 {
2155   GtkWidget * dialog;
2156 
2157   dialog = create_speex_encoding_options_dialog (sample, pathname);
2158   gtk_widget_show (dialog);
2159 
2160   return 0;
2161 }
2162 
2163 #endif /* HAVE_SPEEX */
2164