root/sweep/trunk/src/file_speex.c

Revision 701, 57.6 kB (checked in by erikd, 2 years ago)

Replace deprecated gtk_timeout_add/remove functions with their replacements.

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