1 /* wavbreaker - A tool to split a wave file up into multiple waves.
2  * Copyright (C) 2002-2006 Timothy Robinson
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18 
19 #include <config.h>
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <limits.h>
28 #include <gtk/gtk.h>
29 #include <stdint.h>
30 
31 #include "wavbreaker.h"
32 
33 #include "sample.h"
34 #include "wav.h"
35 #include "cdda.h"
36 #include "appconfig.h"
37 #include "overwritedialog.h"
38 #include "gettext.h"
39 
40 #if defined(HAVE_MPG123)
41 #include <mpg123.h>
42 #endif
43 
44 enum AudioType {
45     UNKNOWN = 0,
46     CDDA = 1,
47     WAV = 2,
48 #if defined(HAVE_MPG123)
49     MP3 = 3,
50 #endif
51 };
52 
53 SampleInfo sampleInfo;
54 static AudioFunctionPointers *audio_function_pointers;
55 static unsigned long sample_start = 0;
56 static int playing = 0;
57 static int writing = 0;
58 static gboolean kill_play_thread = FALSE;
59 static enum AudioType audio_type;
60 
61 static char *sample_file = NULL;
62 static FILE *read_sample_fp = NULL;
63 static FILE *write_sample_fp = NULL;
64 
65 #if defined(HAVE_MPG123)
66 static mpg123_handle *mpg123 = NULL;
67 static size_t mpg123_offset = 0;
68 #endif
69 
70 static GThread *thread;
71 static GMutex mutex;
72 
73 /* typedef and struct stuff for new thread open junk */
74 
75 typedef struct WriteThreadData_ WriteThreadData;
76 struct WriteThreadData_ {
77     GList *tbl;
78     WriteInfo *write_info;
79     char *outputdir;
80 };
81 WriteThreadData wtd;
82 
83 typedef struct MergeThreadData_ MergeThreadData;
84 struct MergeThreadData_ {
85     char *merge_filename;
86     int num_files;
87     GList *filenames;
88     WriteInfo *write_info;
89 };
90 MergeThreadData mtd;
91 
92 typedef struct OpenThreadData_ OpenThreadData;
93 struct OpenThreadData_ {
94     GraphData *graphData;
95     double *pct;
96 };
97 OpenThreadData open_thread_data;
98 
99 static void sample_max_min(GraphData *graphData, double *pct);
100 
101 static char *error_message;
102 
103 char *sample_get_error_message()
104 {
105     return g_strdup(error_message ?: "");
106 }
107 
108 void sample_set_error_message(const char *val)
109 {
110     if (error_message) {
111         g_free(error_message);
112     }
113 
114     error_message = g_strdup(val);
115 }
116 
117 #if defined(HAVE_MPG123)
118 int
119 mp3_read_sample(mpg123_handle *handle,
120                 unsigned char *buf,
121                 int buf_size,
122                 unsigned long start_pos)
123 {
124     size_t result;
125     gboolean retried = FALSE;
126 
127     if (mpg123_offset != start_pos) {
128 retry:
129         mpg123_seek(handle, start_pos / sampleInfo.blockAlign, SEEK_SET);
130         mpg123_offset = start_pos;
131     }
132 
133     if (mpg123_read(handle, buf, buf_size, &result) == MPG123_OK) {
134         mpg123_offset += result;
135         return result;
136     } else {
137         fprintf(stderr, "MP3 decoding failed: %s\n", mpg123_strerror(mpg123));
138         if (!retried) {
139             fprintf(stderr, "Retrying read at %zu...\n", mpg123_offset);
140             retried = TRUE;
141             goto retry;
142         }
143     }
144 
145     return -1;
146 }
147 
148 //#define WAVBREAKER_MP3_DEBUG
149 
150 static gboolean
151 mp3_parse_header(uint32_t header, uint32_t *bitrate, uint32_t *frequency, uint32_t *samples, uint32_t *framesize)
152 {
153     // http://www.datavoyage.com/mpgscript/mpeghdr.htm
154     int a = ((header >> 21) & 0x07ff);
155     int b = ((header >> 19) & 0x0003);
156     int c = ((header >> 17) & 0x0003);
157     int e = ((header >> 12) & 0x000f);
158     int f = ((header >> 10) & 0x0003);
159     int g = ((header >> 9) & 0x0001);
160 
161     static const int BITRATES_V1_L3[] = { -1, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1 };
162     static const int FREQUENCIES[] = { 44100, 48000, 32000, 0 };
163 
164     if (a != 0x7ff /* sync */ ||
165             b != 0x3 /* MPEG1 */ ||
166             c != 0x1 /* Layer III */ ||
167             e == 0x0 /* freeform bitrate */ || e == 0xf /* invalid bitrate */ ||
168             f == 0x3 /* invalid frequency */) {
169         return FALSE;
170     }
171 
172     *bitrate = BITRATES_V1_L3[e];
173     *frequency = FREQUENCIES[f];
174     *samples = 1152;
175     *framesize = (int)((*samples) / 8 * 1000 * (*bitrate) / (*frequency)) + g /* padding */;
176 
177 #if defined(WAVBREAKER_MP3_DEBUG)
178     static const char *VERSIONS[] = { "MPEG 2.5", NULL, "MPEG 2", "MPEG 1" };
179     static const char *LAYERS[] = { NULL, "III", "II", "I" };
180 
181     const char *mpeg_version = VERSIONS[b];
182     const char *layer = LAYERS[c];
183 
184     fprintf(stderr, "MP3 frame: %s %s, %dHz, %dkbps, %d samples (%d bytes)\n", mpeg_version, layer,
185             *frequency, *bitrate, *samples, *framesize);
186 #endif /* WAVBREAKER_MP3_DEBUG */
187 
188     return TRUE;
189 }
190 
191 void
192 mp3_write_file(FILE *input_file, const char *filename, SampleInfo *sampleInfo, unsigned long start_pos, unsigned long end_pos)
193 {
194     start_pos /= sampleInfo->blockSize;
195     end_pos /= sampleInfo->blockSize;
196 
197     FILE *output_file = fopen(filename, "wb");
198 
199     if (!output_file) {
200         fprintf(stderr, "Could not open '%s' for writing\n", filename);
201         return;
202     }
203 
204     fseek(input_file, 0, SEEK_SET);
205 
206     uint32_t header = 0x00000000;
207     uint32_t sample_position = 0;
208     uint32_t file_offset = 0;
209     uint32_t last_frame_end = 0;
210     uint32_t frames_written = 0;
211 
212     while (!feof(input_file)) {
213         fseek(input_file, file_offset, SEEK_SET);
214 
215         int a = fgetc(input_file);
216         if (a == EOF) {
217             break;
218         }
219 
220         header = ((header & 0xffffff) << 8) | (a & 0xff);
221 
222         uint32_t bitrate = 0;
223         uint32_t frequency = 0;
224         uint32_t samples = 0;
225         uint32_t framesize = 0;
226 
227         if (mp3_parse_header(header, &bitrate, &frequency, &samples, &framesize)) {
228             uint32_t frame_start = file_offset - 3;
229 
230             if (last_frame_end < frame_start) {
231                 fprintf(stderr, "Skipped non-frame data in MP3 @ 0x%08x (%d bytes)\n",
232                         last_frame_end, frame_start - last_frame_end);
233             }
234 
235             uint32_t start_samples = start_pos * frequency / CD_BLOCKS_PER_SEC;
236             if (start_samples <= sample_position) {
237                 // Write this frame to the output file
238                 char *buf = malloc(framesize);
239                 fseek(input_file, frame_start, SEEK_SET);
240                 if (fread(buf, 1, framesize, input_file) != framesize) {
241                     fprintf(stderr, "Tried to read over the end of the input file\n");
242                     break;
243                 }
244                 if (fwrite(buf, 1, framesize, output_file) != framesize) {
245                     fprintf(stderr, "Failed to write %d bytes to output file\n", framesize);
246                     break;
247                 }
248                 free(buf);
249 
250                 frames_written++;
251 
252                 uint32_t end_samples = end_pos * frequency / CD_BLOCKS_PER_SEC;
253                 if (end_samples > 0 && end_samples <= sample_position + samples) {
254                     // Done writing this part
255                     break;
256                 }
257             }
258 
259             sample_position += samples;
260 
261             file_offset = frame_start + framesize;
262             last_frame_end = file_offset;
263 
264             header = 0x00000000;
265         } else {
266             file_offset++;
267         }
268     }
269 
270 #if defined(WAVBREAKER_MP3_DEBUG)
271     fprintf(stderr, "Wrote %d MP3 frames from '%s' to '%s'\n", frames_written, sample_file, filename);
272 #endif /* WAVBREAKER_MP3_DEBUG */
273 
274     fclose(output_file);
275 }
276 #endif
277 
278 void sample_init()
279 {
280     g_mutex_init(&mutex);
281 
282 #if defined(HAVE_MPG123)
283     if (mpg123_init() != MPG123_OK) {
284         fprintf(stderr, "Failed to initialize libmpg123\n");
285     }
286 #endif
287 }
288 
289 static gpointer play_thread(gpointer thread_data)
290 {
291     int read_ret = 0;
292     int i;
293     guint *play_marker = (guint *)thread_data;
294     unsigned char *devbuf;
295 
296     audio_function_pointers = get_audio_function_pointers();
297 
298     /*
299     printf("play_thread: calling open_audio_device\n");
300     */
301     if (audio_function_pointers->audio_open_device(&sampleInfo) != 0) {
302         g_mutex_lock(&mutex);
303         playing = 0;
304         audio_function_pointers->audio_close_device();
305         g_mutex_unlock(&mutex);
306         //printf("play_thread: return from open_audio_device != 0\n");
307         return NULL;
308     }
309     //printf("play_thread: return from open_audio_device\n");
310 
311     i = 0;
312 
313     devbuf = malloc(sampleInfo.bufferSize);
314     if (devbuf == NULL) {
315         g_mutex_lock(&mutex);
316         playing = 0;
317         audio_function_pointers->audio_close_device();
318         g_mutex_unlock(&mutex);
319         printf("play_thread: out of memory\n");
320         return NULL;
321     }
322 
323     if (audio_type == CDDA) {
324         read_ret = cdda_read_sample(read_sample_fp, devbuf, sampleInfo.bufferSize,
325             sample_start + (sampleInfo.bufferSize * i++));
326     } else if (audio_type == WAV) {
327         read_ret = wav_read_sample(read_sample_fp, devbuf, sampleInfo.bufferSize,
328             sample_start + (sampleInfo.bufferSize * i++));
329 #if defined(HAVE_MPG123)
330     } else if (audio_type == MP3) {
331         read_ret = mp3_read_sample(mpg123, devbuf, sampleInfo.bufferSize,
332             sample_start + (sampleInfo.bufferSize * i++));
333 #endif
334     }
335 
336     while (read_ret > 0 && read_ret <= sampleInfo.bufferSize) {
337         /*
338         if (read_ret < 0) {
339             printf("read_ret: %d\n", read_ret);
340         }
341         */
342 
343         audio_function_pointers->audio_write(devbuf, read_ret);
344 
345         if (g_mutex_trylock(&mutex)) {
346             if (kill_play_thread == TRUE) {
347                 audio_function_pointers->audio_close_device();
348                 playing = 0;
349                 kill_play_thread = FALSE;
350                 g_mutex_unlock(&mutex);
351                 return NULL;
352             }
353             g_mutex_unlock(&mutex);
354         }
355 
356         if (audio_type == CDDA) {
357             read_ret = cdda_read_sample(read_sample_fp, devbuf,
358                 sampleInfo.bufferSize,
359                 sample_start + (sampleInfo.bufferSize * i++));
360         } else if (audio_type == WAV) {
361             read_ret = wav_read_sample(read_sample_fp, devbuf,
362                 sampleInfo.bufferSize,
363                 sample_start + (sampleInfo.bufferSize * i++));
364 #if defined(HAVE_MPG123)
365         } else if (audio_type == MP3) {
366             read_ret = mp3_read_sample(mpg123, devbuf,
367                 sampleInfo.bufferSize,
368                 sample_start + (sampleInfo.bufferSize * i++));
369 #endif
370         }
371 
372         *play_marker = ((sampleInfo.bufferSize * i) + sample_start) /
373 	    sampleInfo.blockSize;
374     }
375 
376     g_mutex_lock(&mutex);
377 
378     audio_function_pointers->audio_close_device();
379     playing = 0;
380 
381     g_mutex_unlock(&mutex);
382 
383     return NULL;
384 }
385 
386 int sample_is_playing()
387 {
388     return playing;
389 }
390 
391 int sample_is_writing()
392 {
393     return writing;
394 }
395 
396 int play_sample(gulong startpos, gulong *play_marker)
397 {
398     g_mutex_lock(&mutex);
399     if (playing) {
400         g_mutex_unlock(&mutex);
401         return 2;
402     }
403 
404     if (sample_file == NULL) {
405         g_mutex_unlock(&mutex);
406         return 3;
407     }
408 
409     playing = 1;
410     sample_start = startpos * sampleInfo.blockSize;
411 
412     /* setup thread */
413 
414     //printf("creating the thread\n");
415     fflush(stdout);
416     thread = g_thread_new("play_sample", play_thread, play_marker);
417 
418     g_mutex_unlock(&mutex);
419     //printf("finished creating the thread\n");
420     return 0;
421 }
422 
423 void stop_sample()
424 {
425     g_mutex_lock(&mutex);
426 
427     if (!playing) {
428         g_mutex_unlock(&mutex);
429         return;
430     }
431 
432     kill_play_thread = TRUE;
433     g_mutex_unlock(&mutex);
434 
435     // Wait for play thread to actually quit
436     g_thread_join(thread);
437     thread = NULL;
438 }
439 
440 static gpointer open_thread(gpointer data)
441 {
442     OpenThreadData *thread_data = data;
443 
444     sample_max_min(thread_data->graphData,
445                    thread_data->pct);
446 
447     return NULL;
448 }
449 
450 int ask_open_as_raw()
451 {
452     GtkMessageDialog *dialog;
453     gint result;
454     const gchar *message = _("Open as RAW audio");
455     const gchar *info_text = _("The file you selected does not contain a wave header. wavbreaker can interpret the file as \"Signed 16 bit, 44100 Hz, Stereo\" audio. Choose the byte order for the RAW audio or cancel to abort.");
456 
457     dialog = (GtkMessageDialog*)gtk_message_dialog_new( GTK_WINDOW(wavbreaker_get_main_window()),
458                                      GTK_DIALOG_DESTROY_WITH_PARENT,
459                                      GTK_MESSAGE_QUESTION,
460                                      GTK_BUTTONS_CANCEL,
461                                      "%s", message);
462 
463     gtk_dialog_add_button( GTK_DIALOG(dialog), _("Big endian"), WB_RESPONSE_BIG_ENDIAN);
464     gtk_dialog_add_button( GTK_DIALOG(dialog), _("Little endian"), WB_RESPONSE_LITTLE_ENDIAN);
465 
466     gtk_message_dialog_format_secondary_text( dialog, "%s", info_text);
467     gtk_window_set_title( GTK_WINDOW(dialog), message);
468 
469     result = gtk_dialog_run( GTK_DIALOG(dialog));
470     gtk_widget_destroy( GTK_WIDGET( dialog));
471 
472     return result;
473 }
474 
475 int sample_open_file(const char *filename, GraphData *graphData, double *pct)
476 {
477     int ask_result = 0;
478     sample_close_file();
479 
480     sample_file = g_strdup(filename);
481 
482     audio_type = UNKNOWN;
483     if( wav_read_header(sample_file, &sampleInfo, 0) == 0) {
484         audio_type = WAV;
485     }
486 
487 #if defined(HAVE_MPG123)
488     if (audio_type == UNKNOWN) {
489         fprintf(stderr, "Trying to open as MP3...\n");
490 
491         if (mpg123 != NULL) {
492             mpg123_close(mpg123), mpg123 = NULL;
493         }
494 
495         if ((mpg123 = mpg123_new(NULL, NULL)) == NULL) {
496             fprintf(stderr, "Failed to create MP3 decoder\n");
497         }
498 
499         if (mpg123_open(mpg123, sample_file) == MPG123_OK) {
500             fprintf(stderr, "Detected MP3 format\n");
501 
502             long rate;
503             int channels;
504             int encoding;
505             if (mpg123_getformat(mpg123, &rate, &channels, &encoding) != MPG123_OK ) {
506                 fprintf(stderr, "Could not get file format\n");
507             }
508 
509             fprintf(stderr, "Scanning MP3 file...\n");
510             if (mpg123_scan(mpg123) != MPG123_OK) {
511                 fprintf(stderr, "Failed to scan MP3\n");
512             }
513 
514             struct mpg123_frameinfo fi;
515             memset(&fi, 0, sizeof(fi));
516 
517             if (mpg123_info(mpg123, &fi) == MPG123_OK) {
518                 sampleInfo.channels = (fi.mode == MPG123_M_MONO) ? 1 : 2;
519                 sampleInfo.samplesPerSec = fi.rate;
520                 sampleInfo.bitsPerSample = 16;
521 
522                 sampleInfo.blockAlign = sampleInfo.channels * (sampleInfo.bitsPerSample / 8);
523                 sampleInfo.avgBytesPerSec = sampleInfo.blockAlign * sampleInfo.samplesPerSec;
524                 sampleInfo.bufferSize = DEFAULT_BUF_SIZE;
525                 sampleInfo.blockSize = sampleInfo.avgBytesPerSec / CD_BLOCKS_PER_SEC;
526                 sampleInfo.numBytes = mpg123_length(mpg123) * sampleInfo.blockAlign;
527                 fprintf(stderr, "Channels: %d, rate: %d, bits: %d, decoded size: %lu\n",
528                         sampleInfo.channels, sampleInfo.samplesPerSec,
529                         sampleInfo.bitsPerSample, sampleInfo.numBytes);
530 
531                 mpg123_format_none(mpg123);
532                 if (mpg123_format(mpg123, sampleInfo.samplesPerSec,
533                                   (sampleInfo.channels == 1) ? MPG123_STEREO : MPG123_MONO,
534                                   MPG123_ENC_SIGNED_16) != MPG123_OK) {
535                     fprintf(stderr, "Failed to set mpg123 format\n");
536                 } else {
537                     fprintf(stderr, "MP3 file reading successfully set up\n");
538                     audio_type = MP3;
539                 }
540             }
541         }
542 
543     }
544 #endif
545 
546 
547     if (audio_type == UNKNOWN) {
548         ask_result = ask_open_as_raw();
549         if( ask_result == GTK_RESPONSE_CANCEL) {
550             sample_set_error_message(wav_get_error_message());
551             return 1;
552         }
553         cdda_read_header(sample_file, &sampleInfo);
554         if( ask_result == WB_RESPONSE_BIG_ENDIAN) {
555             audio_type = CDDA;
556         } else {
557             audio_type = WAV;
558         }
559     }
560 
561     if (audio_type == WAV || audio_type == CDDA) {
562         sampleInfo.blockSize = (((sampleInfo.bitsPerSample / 8) *
563                                  sampleInfo.channels * sampleInfo.samplesPerSec) /
564                                 CD_BLOCKS_PER_SEC);
565 
566         if ((read_sample_fp = fopen(sample_file, "rb")) == NULL) {
567             if (error_message) {
568                 g_free(error_message);
569             }
570             error_message = g_strdup_printf(_("Error opening %s: %s"), sample_file, strerror(errno));
571             return 2;
572         }
573     }
574 
575     open_thread_data.graphData = graphData;
576     open_thread_data.pct = pct;
577 
578     fflush(stdout);
579     g_thread_unref(g_thread_new("open file", open_thread, &open_thread_data));
580 
581     return 0;
582 }
583 
584 void sample_close_file()
585 {
586 #if defined(HAVE_MPG123)
587     if (mpg123 != NULL) {
588         mpg123_close(mpg123), mpg123 = NULL;
589     }
590 #endif
591 
592     if( read_sample_fp != NULL) {
593         fclose( read_sample_fp);
594         read_sample_fp = NULL;
595     }
596 
597     if( sample_file != NULL) {
598         g_free(sample_file);
599         sample_file = NULL;
600     }
601 }
602 
603 static void sample_max_min(GraphData *graphData, double *pct)
604 {
605     int tmp = 0;
606     long int ret = 0;
607     int min, max, xtmp;
608     int min_sample, max_sample;
609     long int i, k;
610     long int numSampleBlocks;
611     long int tmp_sample_calc;
612     unsigned char devbuf[sampleInfo.blockSize];
613     Points *graph_data;
614 
615     tmp_sample_calc = sampleInfo.numBytes;
616     tmp_sample_calc = tmp_sample_calc / sampleInfo.blockSize;
617     numSampleBlocks = (tmp_sample_calc + 1);
618 
619     /* DEBUG CODE START */
620     /*
621     printf("\nsampleInfo.numBytes: %lu\n", sampleInfo.numBytes);
622     printf("sampleInfo.bitsPerSample: %d\n", sampleInfo.bitsPerSample);
623     printf("sampleInfo.blockSize: %d\n", sampleInfo.blockSize);
624     printf("sampleInfo.channels: %d\n", sampleInfo.channels);
625     printf("numSampleBlocks: %d\n\n", numSampleBlocks);
626     */
627     /* DEBUG CODE END */
628 
629     graph_data = (Points *)malloc(numSampleBlocks * sizeof(Points));
630 
631     if (graph_data == NULL) {
632         printf("NULL returned from malloc of graph_data\n");
633         return;
634     }
635 
636     i = 0;
637 
638     if (audio_type == CDDA) {
639         ret = cdda_read_sample(read_sample_fp, devbuf, sampleInfo.blockSize,
640 			       sampleInfo.blockSize * i);
641     } else if (audio_type == WAV) {
642         ret = wav_read_sample(read_sample_fp, devbuf, sampleInfo.blockSize,
643 			      sampleInfo.blockSize * i);
644 #if defined(HAVE_MPG123)
645     } else if (audio_type == MP3) {
646         ret = mp3_read_sample(mpg123, devbuf, sampleInfo.blockSize,
647 			      sampleInfo.blockSize * i);
648 #endif
649     }
650 
651     min_sample = SHRT_MAX; /* highest value for 16-bit samples */
652     max_sample = 0;
653 
654     while (ret == sampleInfo.blockSize && i < numSampleBlocks) {
655         min = max = 0;
656         for (k = 0; k < ret; k++) {
657             if (sampleInfo.bitsPerSample == 8) {
658                 tmp = devbuf[k];
659                 tmp -= 128;
660             } else if (sampleInfo.bitsPerSample == 16) {
661                 tmp = (char)devbuf[k+1] << 8 | (char)devbuf[k];
662                 k++;
663 	    } else if (sampleInfo.bitsPerSample == 24) {
664 		tmp   = ((char)devbuf[k]) | ((char)devbuf[k+1] << 8);
665 		tmp  &= 0x0000ffff;
666 		xtmp  =  (char)devbuf[k+2] << 16;
667 		tmp  |= xtmp;
668 		k += 2;
669             }
670 
671             if (tmp > max) {
672                 max = tmp;
673             } else if (tmp < min) {
674                 min = tmp;
675             }
676 
677             // skip over any extra channels
678             k += (sampleInfo.channels - 1) * (sampleInfo.bitsPerSample / 8);
679         }
680 
681         graph_data[i].min = min;
682         graph_data[i].max = max;
683 
684         if( min_sample > (max-min)) {
685             min_sample = (max-min);
686         }
687         if( max_sample < (max-min)) {
688             max_sample = (max-min);
689         }
690 
691         if (audio_type == CDDA) {
692             ret = cdda_read_sample(read_sample_fp, devbuf,
693 				   sampleInfo.blockSize,
694 				   sampleInfo.blockSize * i);
695         } else if (audio_type == WAV) {
696             ret = wav_read_sample(read_sample_fp, devbuf,
697 				  sampleInfo.blockSize,
698 				  sampleInfo.blockSize * i);
699 #if defined(HAVE_MPG123)
700         } else if (audio_type == MP3) {
701             ret = mp3_read_sample(mpg123, devbuf,
702 				  sampleInfo.blockSize,
703 				  sampleInfo.blockSize * i);
704 #endif
705         }
706 
707         *pct = (double) i / numSampleBlocks;
708         i++;
709     }
710 
711     *pct = 1.0;
712 
713     graphData->numSamples = numSampleBlocks;
714 
715     if (graphData->data != NULL) {
716         free(graphData->data);
717     }
718     graphData->data = graph_data;
719 
720     graphData->minSampleAmp = min_sample;
721     graphData->maxSampleAmp = max_sample;
722 
723     if (sampleInfo.bitsPerSample == 8) {
724         graphData->maxSampleValue = UCHAR_MAX;
725     } else if (sampleInfo.bitsPerSample == 16) {
726         graphData->maxSampleValue = SHRT_MAX;
727     } else if (sampleInfo.bitsPerSample == 24) {
728 	graphData->maxSampleValue = 0x7fffff;
729     }
730     /* DEBUG CODE START */
731     /*
732     printf("\ni: %d\n", i);
733     printf("graphData->numSamples: %ld\n", graphData->numSamples);
734     printf("graphData->maxSampleValue: %ld\n\n", graphData->maxSampleValue);
735     */
736     /* DEBUG CODE END */
737 }
738 
739 static gpointer
740 write_thread(gpointer data)
741 {
742     WriteThreadData *thread_data = data;
743 
744     GList *tbl_head = thread_data->tbl;
745     GList *tbl_cur, *tbl_next;
746     char *outputdir = thread_data->outputdir;
747     TrackBreak *tb_cur, *tb_next;
748     WriteInfo *write_info = thread_data->write_info;
749 
750     int i;
751     int index;
752     unsigned long start_pos, end_pos;
753     char filename[1024];
754 
755     write_info->num_files = 0;
756     write_info->cur_file = 0;
757     write_info->sync = 0;
758     write_info->sync_check_file_overwrite_to_write_progress = 0;
759     write_info->check_file_exists = 0;
760     write_info->skip_file = -1;
761 
762     i = 1;
763     tbl_cur = tbl_head;
764     while (tbl_cur != NULL) {
765         index = g_list_position(tbl_head, tbl_cur);
766         tb_cur = (TrackBreak *)g_list_nth_data(tbl_head, index);
767 
768         if (tb_cur->write == TRUE) {
769             write_info->num_files++;
770         }
771 
772         tbl_cur = g_list_next(tbl_cur);
773     }
774 
775     i = 1;
776     tbl_cur = tbl_head;
777     tbl_next = g_list_next(tbl_cur);
778 
779     while (tbl_cur != NULL) {
780         index = g_list_position(tbl_head, tbl_cur);
781         tb_cur = (TrackBreak *)g_list_nth_data(tbl_head, index);
782         if (tb_cur->write == TRUE) {
783             start_pos = tb_cur->offset * sampleInfo.blockSize;
784 
785             if (tbl_next == NULL) {
786                 end_pos = 0;
787                 tb_next = NULL;
788             } else {
789                 index = g_list_position(tbl_head, tbl_next);
790                 tb_next = (TrackBreak *)g_list_nth_data(tbl_head, index);
791                 end_pos = tb_next->offset * sampleInfo.blockSize;
792             }
793 
794             /* add output directory to filename */
795             strcpy(filename, outputdir);
796             strcat(filename, "/");
797 
798             strcat(filename, tb_cur->filename);
799 
800             /* add file extension to filename */
801             if ((audio_type == WAV) && (!strstr(filename, ".wav"))) {
802                 strcat(filename, ".wav");
803             } else if ((audio_type == CDDA) && (!strstr(filename, ".dat"))) {
804                 strcat(filename, ".dat");
805 #if defined(HAVE_MPG123)
806             } else if ((audio_type == MP3) && (!strstr(filename, ".mp3"))) {
807                 strcat(filename, ".mp3");
808 #endif
809             }
810             write_info->pct_done = 0.0;
811             write_info->cur_file++;
812             if (write_info->cur_filename != NULL) {
813                 g_free(write_info->cur_filename);
814             }
815             write_info->cur_filename = g_strdup(filename);
816 
817             if (write_info->skip_file < 2) {
818                 if (g_file_test(filename, G_FILE_TEST_EXISTS)) {
819                     write_info->skip_file = -1;
820                     write_info->check_file_exists = 1;
821                     // sync the threads to wait on overwrite question
822                     while (write_info->skip_file < 0) {
823                         sleep(1);
824                     }
825                 } else {
826                     write_info->skip_file = 1;
827                 }
828             }
829 
830             if (write_info->skip_file > 0) {
831                 if (audio_type == CDDA) {
832                     cdda_write_file(write_sample_fp, filename,
833                         sampleInfo.bufferSize, start_pos, end_pos);
834                 } else if (audio_type == WAV) {
835                     wav_write_file(write_sample_fp, filename,
836 					 sampleInfo.blockSize,
837 					 &sampleInfo, start_pos, end_pos,
838 					 &write_info->pct_done);
839 #if defined(HAVE_MPG123)
840                 } else if (audio_type == MP3) {
841                     mp3_write_file(write_sample_fp, filename, &sampleInfo, start_pos, end_pos);
842 #endif
843                 }
844                 i++;
845             }
846 
847             if (write_info->skip_file < 2) {
848                 write_info->skip_file = -1;
849             }
850         }
851 
852         tbl_cur = g_list_next(tbl_cur);
853         tbl_next = g_list_next(tbl_next);
854     }
855     write_info->sync = 1;
856     if (write_info->cur_filename != NULL) {
857         g_free(write_info->cur_filename);
858     }
859     write_info->cur_filename = NULL;
860 
861     fclose(write_sample_fp);
862 
863     writing = 0;
864     return NULL;
865 }
866 
867 void sample_write_files(GList *tbl, WriteInfo *write_info, char *outputdir)
868 {
869     wtd.tbl = tbl;
870     wtd.write_info = write_info;
871     wtd.outputdir = outputdir;
872 
873     writing = 1;
874 
875     if (sample_file == NULL) {
876         perror("Must open file first\n");
877         writing = 0;
878         return;
879     }
880 
881     if ((write_sample_fp = fopen(sample_file, "rb")) == NULL) {
882         printf("error opening %s\n", sample_file);
883         writing = 0;
884         return;
885     }
886 
887     g_thread_unref(g_thread_new("write data", write_thread, &wtd));
888 }
889 
890 static gpointer
891 merge_thread(gpointer data)
892 {
893     MergeThreadData *thread_data = data;
894     char *filenames[g_list_length(thread_data->filenames)];
895     GList *cur, *head;
896     int index, i;
897     char *list_data;
898 
899     head = thread_data->filenames;
900     cur = head;
901     i = 0;
902     while (cur != NULL) {
903         index = g_list_position(head, cur);
904         list_data = (char *)g_list_nth_data(head, index);
905 
906         filenames[i++] = list_data;
907 
908         cur = g_list_next(cur);
909     }
910 
911     wav_merge_files(thread_data->merge_filename,
912                         g_list_length(thread_data->filenames),
913                         filenames,
914                         DEFAULT_BUF_SIZE,
915                         thread_data->write_info);
916 
917     head = thread_data->filenames;
918     cur = head;
919     while (cur != NULL) {
920         index = g_list_position(head, cur);
921         list_data = (char *)g_list_nth_data(head, index);
922 
923         free(list_data);
924 
925         cur = g_list_next(cur);
926     }
927 
928     g_list_free(thread_data->filenames);
929 
930     return NULL;
931 }
932 
933 void sample_merge_files(char *merge_filename, GList *filenames, WriteInfo *write_info)
934 {
935     mtd.merge_filename = g_strdup(merge_filename);
936     mtd.filenames = filenames;
937     mtd.write_info = write_info;
938 
939     if (write_info->merge_filename != NULL) {
940         g_free(write_info->merge_filename);
941     }
942     write_info->merge_filename = mtd.merge_filename;
943 
944     g_thread_unref(g_thread_new("merge files", merge_thread, &mtd));
945 }
946 
947