1 /*
2  * Schism Tracker - a cross-platform Impulse Tracker clone
3  * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
4  * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
5  * copyright (c) 2009 Storlek & Mrs. Brisby
6  * copyright (c) 2010-2012 Storlek
7  * URL: http://schismtracker.org/
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23 
24 #define NEED_TIME
25 #include "headers.h"
26 
27 #include "it.h"
28 #include "song.h"
29 #include "page.h"
30 #include "util.h"
31 #include "song.h"
32 #include "sndfile.h"
33 #include "dmoz.h"
34 #include "config-parser.h"
35 
36 
37 #include "cmixer.h"
38 #include "disko.h"
39 
40 #include <sys/stat.h>
41 
42 #include <stdio.h>
43 #include <fcntl.h>
44 #include <errno.h>
45 
46 #define DW_BUFFER_SIZE 65536
47 
48 // ---------------------------------------------------------------------------
49 
50 static unsigned int disko_output_rate = 44100;
51 static unsigned int disko_output_bits = 16;
52 static unsigned int disko_output_channels = 2;
53 
54 void cfg_load_disko(cfg_file_t *cfg)
55 {
56 	disko_output_rate = cfg_get_number(cfg, "Diskwriter", "rate", 44100);
57 	disko_output_bits = cfg_get_number(cfg, "Diskwriter", "bits", 16);
58 	disko_output_channels = cfg_get_number(cfg, "Diskwriter", "channels", 2);
59 }
60 
61 void cfg_save_disko(cfg_file_t *cfg)
62 {
63 	cfg_set_number(cfg, "Diskwriter", "rate", disko_output_rate);
64 	cfg_set_number(cfg, "Diskwriter", "bits", disko_output_bits);
65 	cfg_set_number(cfg, "Diskwriter", "channels", disko_output_channels);
66 }
67 
68 // ---------------------------------------------------------------------------
69 // stdio backend
70 
71 static void _dw_stdio_write(disko_t *ds, const void *buf, size_t len)
72 {
73 	if (fwrite(buf, len, 1, ds->file) != 1)
74 		disko_seterror(ds, errno);
75 }
76 
77 static void _dw_stdio_putc(disko_t *ds, int c)
78 {
79 	if (fputc(c, ds->file) == EOF)
80 		disko_seterror(ds, errno);
81 }
82 
83 static void _dw_stdio_seek(disko_t *ds, long pos, int whence)
84 {
85 	if (fseek(ds->file, pos, whence) < 0)
86 		disko_seterror(ds, errno);
87 }
88 
89 static long _dw_stdio_tell(disko_t *ds)
90 {
91 	long pos = ftell(ds->file);
92 	if (pos < 0)
93 		disko_seterror(ds, errno);
94 	return pos;
95 }
96 
97 // ---------------------------------------------------------------------------
98 // memory backend
99 
100 // 0 => memory error, abandon ship
101 static int _dw_bufcheck(disko_t *ds, size_t extend)
102 {
103 	if (ds->pos + extend <= ds->length)
104 		return 1;
105 
106 	ds->length = MAX(ds->length, ds->pos + extend);
107 
108 	if (ds->length >= ds->allocated) {
109 		size_t newsize = MAX(ds->allocated + DW_BUFFER_SIZE, ds->length);
110 		uint8_t *new = realloc(ds->data, newsize);
111 		if (!new) {
112 			// Eek
113 			free(ds->data);
114 			ds->data = NULL;
115 			disko_seterror(ds, errno);
116 			return 0;
117 		}
118 		memset(new + ds->allocated, 0, newsize - ds->allocated);
119 		ds->data = new;
120 		ds->allocated = newsize;
121 	}
122 	return 1;
123 }
124 
125 static void _dw_mem_write(disko_t *ds, const void *buf, size_t len)
126 {
127 	if (_dw_bufcheck(ds, len)) {
128 		memcpy(ds->data + ds->pos, buf, len);
129 		ds->pos += len;
130 	}
131 }
132 
133 static void _dw_mem_putc(disko_t *ds, int c)
134 {
135 	if (_dw_bufcheck(ds, 1))
136 		ds->data[ds->pos++] = c;
137 }
138 
139 static void _dw_mem_seek(disko_t *ds, long offset, int whence)
140 {
141 	// mostly from slurp_seek
142 	switch (whence) {
143 	default:
144 	case SEEK_SET:
145 		break;
146 	case SEEK_CUR:
147 		offset += ds->pos;
148 		break;
149 	case SEEK_END:
150 		offset += ds->length;
151 		break;
152 	}
153 	if (offset < 0) {
154 		disko_seterror(ds, EINVAL);
155 		return;
156 	}
157 	/* note: seeking doesn't cause a buffer resize. This is consistent with the behavior of stdio streams.
158 	Consider:
159 		FILE *f = fopen("f", "w");
160 		fseek(f, 1000, SEEK_SET);
161 		fclose(f);
162 	This will produce a zero-byte file; the size is not extended until data is written. */
163 	ds->pos = offset;
164 }
165 
166 static long _dw_mem_tell(disko_t *ds)
167 {
168 	return (long) ds->pos;
169 }
170 
171 // ---------------------------------------------------------------------------
172 
173 void disko_write(disko_t *ds, const void *buf, size_t len)
174 {
175 	if (len != 0 && !ds->error)
176 		ds->_write(ds, buf, len);
177 }
178 
179 void disko_putc(disko_t *ds, int c)
180 {
181 	if (!ds->error)
182 		ds->_putc(ds, c);
183 }
184 
185 void disko_seek(disko_t *ds, long pos, int whence)
186 {
187 	if (!ds->error)
188 		ds->_seek(ds, pos, whence);
189 }
190 
191 /* used by multi-write */
192 static void disko_seekcur(disko_t *ds, long pos)
193 {
194 	disko_seek(ds, pos, SEEK_CUR);
195 }
196 
197 long disko_tell(disko_t *ds)
198 {
199 	if (!ds->error)
200 		return ds->_tell(ds);
201 	return -1;
202 }
203 
204 
205 void disko_seterror(disko_t *ds, int err)
206 {
207 	// Don't set an error if one already exists, and don't allow clearing an error value
208 	ds->error = errno = ds->error ?: err ?: EINVAL;
209 }
210 
211 // ---------------------------------------------------------------------------
212 
213 disko_t *disko_open(const char *filename)
214 {
215 	size_t len;
216 	int fd;
217 	int err;
218 
219 	if (!filename)
220 		return NULL;
221 
222 	len = strlen(filename);
223 	if (len + 6 >= PATH_MAX) {
224 		errno = ENAMETOOLONG;
225 		return NULL;
226 	}
227 
228 #ifndef GEKKO /* FIXME - make a replacement access() */
229 	// Attempt to honor read-only (since we're writing them in such a roundabout way)
230 	if (access(filename, W_OK) != 0 && errno != ENOENT)
231 		return NULL;
232 #endif
233 
234 	disko_t *ds = calloc(1, sizeof(disko_t));
235 	if (!ds)
236 		return NULL;
237 
238 	// This might seem a bit redundant, but allows for flexibility later
239 	// (e.g. putting temp files elsewhere, or mangling the filename in some other way)
240 	strcpy(ds->filename, filename);
241 	strcpy(ds->tempname, filename);
242 	strcat(ds->tempname, "XXXXXX");
243 
244 	fd = mkstemp(ds->tempname);
245 	if (fd == -1) {
246 		free(ds);
247 		return NULL;
248 	}
249 	ds->file = fdopen(fd, "wb");
250 	if (ds->file == NULL) {
251 		err = errno;
252 		close(fd);
253 		unlink(ds->tempname);
254 		free(ds);
255 		errno = err;
256 		return NULL;
257 	}
258 
259 	setvbuf(ds->file, NULL, _IOFBF, DW_BUFFER_SIZE);
260 
261 	ds->_write = _dw_stdio_write;
262 	ds->_seek = _dw_stdio_seek;
263 	ds->_tell = _dw_stdio_tell;
264 	ds->_putc = _dw_stdio_putc;
265 
266 	return ds;
267 }
268 
269 int disko_close(disko_t *ds, int backup)
270 {
271 	int err = ds->error;
272 
273 	// try to preserve the *first* error set, because it's most likely to be interesting
274 	if (fclose(ds->file) == EOF && !err) {
275 		err = errno;
276 	} else if (!err) {
277 		// preserve file mode, or set it sanely -- mkstemp() sets file mode to 0600
278 #ifndef GEKKO /* FIXME - autoconf check for this instead */
279 		struct stat st;
280 		if (stat(ds->filename, &st) < 0) {
281 			/* Probably didn't exist already, let's make something up.
282 			0777 is "safer" than 0, so we don't end up throwing around world-writable
283 			files in case something weird happens.
284 			See also: man 3 getumask */
285 			mode_t m = umask(0777);
286 			umask(m);
287 			st.st_mode = 0666 & ~m;
288 		}
289 #endif
290 		if (backup) {
291 			// back up the old file
292 			make_backup_file(ds->filename, (backup != 1));
293 		}
294 		if (rename_file(ds->tempname, ds->filename, 1) != 0) {
295 			err = errno;
296 		} else {
297 #ifndef GEKKO
298 			// Fix the permissions on the file
299 			chmod(ds->filename, st.st_mode);
300 #endif
301 		}
302 	}
303 	// If anything failed so far, kill off the temp file
304 	if (err) {
305 		unlink(ds->tempname);
306 	}
307 	free(ds);
308 	if (err) {
309 		errno = err;
310 		return DW_ERROR;
311 	} else {
312 		return DW_OK;
313 	}
314 }
315 
316 
317 disko_t *disko_memopen(void)
318 {
319 	disko_t *ds = calloc(1, sizeof(disko_t));
320 	if (!ds)
321 		return NULL;
322 
323 	ds->data = calloc(DW_BUFFER_SIZE, sizeof(uint8_t));
324 	if (!ds->data) {
325 		free(ds);
326 		return NULL;
327 	}
328 	ds->allocated = DW_BUFFER_SIZE;
329 
330 	ds->_write = _dw_mem_write;
331 	ds->_seek = _dw_mem_seek;
332 	ds->_tell = _dw_mem_tell;
333 	ds->_putc = _dw_mem_putc;
334 
335 	return ds;
336 }
337 
338 int disko_memclose(disko_t *ds, int keep_buffer)
339 {
340 	int err = ds->error;
341 	if (!keep_buffer || err)
342 		free(ds->data);
343 	free(ds);
344 	if (err) {
345 		errno = err;
346 		return DW_ERROR;
347 	} else {
348 		return DW_OK;
349 	}
350 }
351 
352 // ---------------------------------------------------------------------------
353 
354 static void _export_setup(song_t *dwsong, int *bps)
355 {
356 	song_lock_audio();
357 
358 	/* install our own */
359 	memcpy(dwsong, current_song, sizeof(song_t)); /* shadow it */
360 
361 	dwsong->multi_write = NULL; /* should be null already, but to be sure... */
362 
363 	csf_set_current_order(dwsong, 0); /* rather indirect way of resetting playback variables */
364 	csf_set_wave_config(dwsong, disko_output_rate, disko_output_bits,
365 		(dwsong->flags & SONG_NOSTEREO) ? 1 : disko_output_channels);
366 
367 	dwsong->mix_flags |= SNDMIX_DIRECTTODISK | SNDMIX_NOBACKWARDJUMPS;
368 
369 	dwsong->repeat_count = -1; // FIXME do this right
370 	dwsong->buffer_count = 0;
371 	dwsong->flags &= ~(SONG_PAUSED | SONG_PATTERNLOOP | SONG_ENDREACHED);
372 	dwsong->stop_at_order = -1;
373 	dwsong->stop_at_row = -1;
374 
375 	*bps = dwsong->mix_channels * ((dwsong->mix_bits_per_sample + 7) / 8);
376 
377 	song_unlock_audio();
378 }
379 
380 static void _export_teardown(void)
381 {
382 	global_vu_left = global_vu_right = 0;
383 }
384 
385 // ---------------------------------------------------------------------------
386 
387 static int close_and_bind(song_t *dwsong, disko_t *ds, song_sample_t *sample, int bps)
388 {
389 	disko_t dsshadow = *ds;
390 	int8_t *newdata;
391 
392 	if (disko_memclose(ds, 1) == DW_ERROR) {
393 		return DW_ERROR;
394 	}
395 
396 	newdata = csf_allocate_sample(dsshadow.length);
397 	if (!newdata)
398 		return DW_ERROR;
399 	csf_stop_sample(current_song, sample);
400 	if (sample->data)
401 		csf_free_sample(sample->data);
402 	sample->data = newdata;
403 
404 	memcpy(newdata, dsshadow.data, dsshadow.length);
405 	sample->length = dsshadow.length / bps;
406 	sample->flags &= ~(CHN_16BIT | CHN_STEREO | CHN_ADLIB);
407 	if (dwsong->mix_channels > 1)
408 		sample->flags |= CHN_STEREO;
409 	if (dwsong->mix_bits_per_sample > 8)
410 		sample->flags |= CHN_16BIT;
411 	sample->c5speed = dwsong->mix_frequency;
412 	sample->name[0] = '\0';
413 
414 	return DW_OK;
415 }
416 
417 int disko_writeout_sample(int smpnum, int pattern, int dobind)
418 {
419 	int ret = DW_OK;
420 	song_t dwsong;
421 	song_sample_t *sample;
422 	uint8_t buf[DW_BUFFER_SIZE];
423 	disko_t *ds;
424 	int bps;
425 
426 	if (smpnum < 1 || smpnum >= MAX_SAMPLES)
427 		return DW_ERROR;
428 
429 	ds = disko_memopen();
430 	if (!ds)
431 		return DW_ERROR;
432 
433 	_export_setup(&dwsong, &bps);
434 	dwsong.repeat_count = -1; // FIXME do this right
435 	csf_loop_pattern(&dwsong, pattern, 0);
436 
437 	do {
438 		disko_write(ds, buf, csf_read(&dwsong, buf, sizeof(buf)) * bps);
439 		if (ds->length >= (size_t) (MAX_SAMPLE_LENGTH * bps)) {
440 			/* roughly 3 minutes at 44khz -- surely big enough (?) */
441 			ds->length = MAX_SAMPLE_LENGTH * bps;
442 			dwsong.flags |= SONG_ENDREACHED;
443 		}
444 	} while (!(dwsong.flags & SONG_ENDREACHED));
445 
446 	sample = current_song->samples + smpnum;
447 	if (close_and_bind(&dwsong, ds, sample, bps) == DW_OK) {
448 		sprintf(sample->name, "Pattern %03d", pattern);
449 		if (dobind) {
450 			/* This is hideous */
451 			sample->name[23] = 0xff;
452 			sample->name[24] = pattern;
453 		}
454 	} else {
455 		/* Balls. Something died. */
456 		ret = DW_ERROR;
457 	}
458 
459 	_export_teardown();
460 
461 	return ret;
462 }
463 
464 int disko_multiwrite_samples(int firstsmp, int pattern)
465 {
466 	int err = 0;
467 	song_t dwsong;
468 	song_sample_t *sample;
469 	uint8_t buf[DW_BUFFER_SIZE];
470 	disko_t *ds[MAX_CHANNELS] = {NULL};
471 	int bps;
472 	size_t smpsize = 0;
473 	int smpnum = CLAMP(firstsmp, 1, MAX_SAMPLES);
474 	int n;
475 
476 	_export_setup(&dwsong, &bps);
477 	dwsong.repeat_count = -1; // FIXME do this right
478 	csf_loop_pattern(&dwsong, pattern, 0);
479 	dwsong.multi_write = calloc(MAX_CHANNELS, sizeof(struct multi_write));
480 	if (!dwsong.multi_write)
481 		err = errno ?: ENOMEM;
482 
483 	if (!err) {
484 		for (n = 0; n < MAX_CHANNELS; n++) {
485 			ds[n] = disko_memopen();
486 			if (!ds[n]) {
487 				err = errno ?: EINVAL;
488 				break;
489 			}
490 		}
491 	}
492 
493 	if (err) {
494 		/* you might think this code is insane, and you might be correct ;)
495 		but it's structured like this to keep all the early-termination handling HERE. */
496 		_export_teardown();
497 		err = err ?: errno;
498 		free(dwsong.multi_write);
499 		for (n = 0; n < MAX_CHANNELS; n++)
500 			disko_memclose(ds[n], 0);
501 		errno = err;
502 		return DW_ERROR;
503 	}
504 
505 	for (n = 0; n < MAX_CHANNELS; n++) {
506 		dwsong.multi_write[n].data = ds[n];
507 		/* Dumb casts. (written this way to make the definition of song_t independent of disko) */
508 		dwsong.multi_write[n].write = (void *) disko_write;
509 		dwsong.multi_write[n].silence = (void *) disko_seekcur;
510 	}
511 
512 	do {
513 		/* buf is used as temp space for converting the individual channel buffers from 32-bit.
514 		the output is being handled well inside the mixer, so we don't have to do any actual writing
515 		here, but we DO need to make sure nothing died... */
516 		smpsize += csf_read(&dwsong, buf, sizeof(buf));
517 
518 		if (smpsize >= MAX_SAMPLE_LENGTH) {
519 			/* roughly 3 minutes at 44khz -- surely big enough (?) */
520 			smpsize = MAX_SAMPLE_LENGTH;
521 			dwsong.flags |= SONG_ENDREACHED;
522 			break;
523 		}
524 
525 		for (n = 0; n < MAX_CHANNELS; n++) {
526 			if (ds[n]->error) {
527 				// Kill the write, but leave the other files alone
528 				dwsong.flags |= SONG_ENDREACHED;
529 				break;
530 			}
531 		}
532 	} while (!(dwsong.flags & SONG_ENDREACHED));
533 
534 	for (n = 0; n < MAX_CHANNELS; n++) {
535 		if (!dwsong.multi_write[n].used) {
536 			/* this channel was completely empty - don't bother with it */
537 			disko_memclose(ds[n], 0);
538 			continue;
539 		}
540 
541 		ds[n]->length = MIN(ds[n]->length, smpsize * bps);
542 		smpnum = csf_first_blank_sample(current_song, smpnum);
543 		if (smpnum < 0)
544 			break;
545 		sample = current_song->samples + smpnum;
546 		if (close_and_bind(&dwsong, ds[n], sample, bps) == DW_OK) {
547 			sprintf(sample->name, "Pattern %03d, channel %02d", pattern, n + 1);
548 		} else {
549 			/* Balls. Something died. */
550 			err = errno;
551 		}
552 	}
553 
554 	for (; n < MAX_CHANNELS; n++) {
555 		if (disko_memclose(ds[n], 0) != DW_OK) {
556 			err = errno;
557 		}
558 	}
559 
560 	_export_teardown();
561 	free(dwsong.multi_write);
562 
563 	if (err) {
564 		errno = err;
565 		return DW_ERROR;
566 	} else {
567 		return DW_OK;
568 	}
569 }
570 
571 // ---------------------------------------------------------------------------
572 
573 static song_t export_dwsong;
574 static int export_bps;
575 static disko_t *export_ds[MAX_CHANNELS + 1]; /* only [0] is used unless multichannel */
576 static const struct save_format *export_format = NULL; /* NULL == not running */
577 static struct widget diskodlg_widgets[1];
578 static size_t est_len;
579 static int prgh;
580 static struct timeval export_start_time;
581 static int canceled = 0; /* this sucks, but so do I */
582 
583 static int disko_finish(void);
584 
585 static void diskodlg_draw(void)
586 {
587 	int sec, pos;
588 	char buf[32];
589 
590 	if (!export_ds[0]) {
591 		/* what are we doing here?! */
592 		dialog_destroy_all();
593 		log_appendf(4, "disk export dialog was eaten by a grue!");
594 		return;
595 	}
596 
597 	sec = export_ds[0]->length / export_dwsong.mix_frequency;
598 	pos = export_ds[0]->length * 64 / est_len;
599 	snprintf(buf, 32, "Exporting song...%6d:%02d", sec / 60, sec % 60);
600 	buf[31] = '\0';
601 	draw_text(buf, 27, 27, 0, 2);
602 	draw_fill_chars(24, 30, 55, 30, 0);
603 	draw_vu_meter(24, 30, 32, pos, prgh, prgh);
604 	draw_box(23, 29, 56, 31, BOX_THIN | BOX_INNER | BOX_INSET);
605 }
606 
607 static void diskodlg_cancel(UNUSED void *ignored)
608 {
609 	canceled = 1;
610 	export_dwsong.flags |= SONG_ENDREACHED;
611 	if (!export_ds[0]) {
612 		log_appendf(4, "export was already dead on the inside");
613 		return;
614 	}
615 	for (int n = 0; export_ds[n]; n++)
616 		disko_seterror(export_ds[n], EINTR);
617 
618 	/* The next disko_sync will notice the (artifical) error status and call disko_finish,
619 	which will clean up all the files.
620 	'canceled' prevents disko_finish from making a second call to dialog_destroy (since
621 	this function is already being called in response to the dialog being canceled) and
622 	also affects the message it prints at the end. */
623 }
624 
625 static void disko_dialog_setup(size_t len);
626 
627 // this needs to be done to work around stupid inconsistent key-up code
628 static void diskodlg_reset(UNUSED void *ignored)
629 {
630 	disko_dialog_setup(est_len);
631 }
632 
633 static void disko_dialog_setup(size_t len)
634 {
635 	struct dialog *d = dialog_create_custom(22, 25, 36, 8, diskodlg_widgets, 0, 0, diskodlg_draw, NULL);
636 	d->action_yes = diskodlg_reset;
637 	d->action_no = diskodlg_reset;
638 	d->action_cancel = diskodlg_cancel;
639 
640 	canceled = 0; /* stupid */
641 
642 	est_len = len;
643 	switch ((rand() >> 8) & 63) { /* :) */
644 		case  0 ...  7: prgh = 6; break;
645 		case  8 ... 18: prgh = 3; break;
646 		case 19 ... 31: prgh = 5; break;
647 		default: prgh = 4; break;
648 	}
649 }
650 
651 // ---------------------------------------------------------------------------
652 
653 static char *get_filename(const char *template, int n)
654 {
655 	char *s, *sub, buf[4];
656 
657 	s = strdup(template);
658 	if (!s)
659 		return NULL;
660 	sub = strcasestr(s, "%c");
661 	if (!sub) {
662 		errno = EINVAL;
663 		free(s);
664 		return NULL;
665 	}
666 	num99tostr(n, buf);
667 	sub[0] = buf[0];
668 	sub[1] = buf[1];
669 	return s;
670 }
671 
672 int disko_export_song(const char *filename, const struct save_format *format)
673 {
674 	int err = 0;
675 	int numfiles, n;
676 
677 	if (export_format) {
678 		log_appendf(4, "Another export is already active");
679 		errno = EAGAIN;
680 		return DW_ERROR;
681 	}
682 
683 	gettimeofday(&export_start_time, NULL);
684 
685 	numfiles = format->f.export.multi ? MAX_CHANNELS : 1;
686 
687 	_export_setup(&export_dwsong, &export_bps);
688 	if (numfiles > 1) {
689 		export_dwsong.multi_write = calloc(numfiles, sizeof(struct multi_write));
690 		if (!export_dwsong.multi_write)
691 			err = errno ?: ENOMEM;
692 	}
693 
694 	memset(export_ds, 0, sizeof(export_ds));
695 	for (n = 0; n < numfiles; n++) {
696 		if (numfiles > 1) {
697 			char *tmp = get_filename(filename, n + 1);
698 			if (tmp) {
699 				export_ds[n] = disko_open(tmp);
700 				free(tmp);
701 			}
702 		} else {
703 			export_ds[n] = disko_open(filename);
704 		}
705 		if (!(export_ds[n] && format->f.export.head(export_ds[n], export_dwsong.mix_bits_per_sample,
706 				export_dwsong.mix_channels, export_dwsong.mix_frequency) == DW_OK)) {
707 			err = errno ?: EINVAL;
708 			break;
709 		}
710 	}
711 
712 	if (err) {
713 		_export_teardown();
714 		free(export_dwsong.multi_write);
715 		for (n = 0; export_ds[n]; n++) {
716 			disko_seterror(export_ds[n], err); /* keep from writing a bunch of useless files */
717 			disko_close(export_ds[n], 0);
718 		}
719 		errno = err ?: EINVAL;
720 		log_perror(filename);
721 		return DW_ERROR;
722 	}
723 
724 	if (numfiles > 1) {
725 		for (n = 0; n < numfiles; n++) {
726 			export_dwsong.multi_write[n].data = export_ds[n];
727 			/* Dumb casts, again */
728 			export_dwsong.multi_write[n].write = (void *) format->f.export.body;
729 			export_dwsong.multi_write[n].silence = (void *) format->f.export.silence;
730 		}
731 	}
732 
733 	log_appendf(5, " %d Hz, %d bit, %s",
734 		export_dwsong.mix_frequency, export_dwsong.mix_bits_per_sample,
735 		export_dwsong.mix_channels == 1 ? "mono" : "stereo");
736 	export_format = format;
737 	status.flags |= DISKWRITER_ACTIVE; /* tell main to care about us */
738 
739 	disko_dialog_setup((csf_get_length(&export_dwsong) * export_dwsong.mix_frequency) ?: 1);
740 
741 	return DW_OK;
742 }
743 
744 
745 /* main calls this periodically when the .wav exporter is busy */
746 int disko_sync(void)
747 {
748 	uint8_t buf[DW_BUFFER_SIZE];
749 	size_t frames;
750 	int n;
751 
752 	if (!export_format) {
753 		log_appendf(4, "disko_sync: unexplained bacon");
754 		return DW_SYNC_ERROR; /* no writer running (why are we here?) */
755 	}
756 
757 	frames = csf_read(&export_dwsong, buf, sizeof(buf));
758 
759 	if (!export_dwsong.multi_write)
760 		export_format->f.export.body(export_ds[0], buf, frames * export_bps);
761 	/* always check if something died, multi-write or not */
762 	for (n = 0; export_ds[n]; n++) {
763 		if (export_ds[n]->error) {
764 			disko_finish();
765 			return DW_SYNC_ERROR;
766 		}
767 	}
768 
769 	/* update the progress bar (kind of messy, yes...) */
770 	export_ds[0]->length += frames;
771 	status.flags |= NEED_UPDATE;
772 
773 	if (export_dwsong.flags & SONG_ENDREACHED) {
774 		disko_finish();
775 		return DW_SYNC_DONE;
776 	} else {
777 		return DW_SYNC_MORE;
778 	}
779 }
780 
781 static int disko_finish(void)
782 {
783 	int ret = DW_OK, n, tmp;
784 	struct timeval export_end_time;
785 	double elapsed;
786 	int num_files = 0;
787 	size_t samples_0 = 0;
788 
789 	if (!export_format) {
790 		log_appendf(4, "disko_finish: unexplained eggs");
791 		return DW_ERROR; /* no writer running (why are we here?) */
792 	}
793 
794 	if (!canceled)
795 		dialog_destroy();
796 
797 	samples_0 = export_ds[0]->length;
798 	for (n = 0; export_ds[n]; n++) {
799 		if (export_dwsong.multi_write && !export_dwsong.multi_write[n].used) {
800 			/* this channel was completely empty - don't bother with it */
801 			disko_seterror(export_ds[n], EINVAL); /* kludge */
802 			disko_close(export_ds[n], 0);
803 		} else {
804 			/* there was noise on this channel */
805 			num_files++;
806 			if (export_format->f.export.tail(export_ds[n]) != DW_OK)
807 				disko_seterror(export_ds[n], errno);
808 			tmp = disko_close(export_ds[n], 0);
809 			if (ret == DW_OK)
810 				ret = tmp;
811 		}
812 	}
813 	memset(export_ds, 0, sizeof(export_ds));
814 
815 	_export_teardown();
816 	free(export_dwsong.multi_write);
817 	export_format = NULL;
818 
819 	status.flags &= ~DISKWRITER_ACTIVE; /* please unsubscribe me from your mailing list */
820 
821 	switch (ret) {
822 	case DW_OK:
823 		gettimeofday(&export_end_time, NULL);
824 		elapsed = (export_end_time.tv_sec - export_start_time.tv_sec)
825 			+ ((export_end_time.tv_usec - export_start_time.tv_usec) / 1000000.0);
826 		char fmt[80] = " %.2f mb (%d:%02d) written in %.2lf sec";
827 		if (elapsed >= 9.5 && elapsed < 10.5) {
828 			strcpy(strrchr(fmt, '%'), "ten seconds flat");
829 		}
830 		log_appendf(5, fmt,
831 			((double) samples_0 * (disko_output_bits / 8) * export_dwsong.mix_channels * num_files) / 1048576.0,
832 			samples_0 / disko_output_rate / 60, (samples_0 / disko_output_rate) % 60,
833 			elapsed);
834 		break;
835 	case DW_ERROR:
836 		/* hey, what was the filename? oops */
837 		if (canceled)
838 			log_appendf(5, " Canceled");
839 		else
840 			log_perror(" Write error");
841 		break;
842 	default:
843 		log_appendf(5, " Internal error exporting song");
844 		break;
845 	}
846 
847 	return ret;
848 }
849 
850 // ---------------------------------------------------------------------------
851 
852 struct pat2smp {
853 	int pattern, sample, bind;
854 };
855 
856 static void pat2smp_single(void *data)
857 {
858 	struct pat2smp *ps = data;
859 
860 	if (disko_writeout_sample(ps->sample, ps->pattern, ps->bind) == DW_OK) {
861 		set_page(PAGE_SAMPLE_LIST);
862 	} else {
863 		log_perror("Sample write");
864 		status_text_flash("Error writing to sample");
865 	}
866 
867 	free(ps);
868 }
869 
870 static void pat2smp_multi(void *data)
871 {
872 	struct pat2smp *ps = data;
873 	if (disko_multiwrite_samples(ps->sample, ps->pattern) == DW_OK) {
874 		set_page(PAGE_SAMPLE_LIST);
875 	} else {
876 		log_perror("Sample multi-write");
877 		status_text_flash("Error writing to samples");
878 	}
879 
880 	free(ps);
881 }
882 
883 void song_pattern_to_sample(int pattern, int split, int bind)
884 {
885 	struct pat2smp *ps;
886 	int n;
887 
888 	if (split && bind) {
889 		log_appendf(4, "song_pattern_to_sample: internal error!");
890 		return;
891 	}
892 
893 	if (pattern < 0 || pattern >= MAX_PATTERNS) {
894 		return;
895 	}
896 
897 	/* this is horrid */
898 	for (n = 1; n < MAX_SAMPLES; n++) {
899 		song_sample_t *samp = song_get_sample(n);
900 		if (!samp) continue;
901 		if (((unsigned char) samp->name[23]) != 0xFF) continue;
902 		if (((unsigned char) samp->name[24]) != pattern) continue;
903 		status_text_flash("Pattern %d already linked to sample %d", pattern, n);
904 		return;
905 	}
906 
907 	ps = mem_alloc(sizeof(struct pat2smp));
908 	ps->pattern = pattern;
909 	ps->sample = sample_get_current() ?: 1;
910 	ps->bind = bind;
911 
912 	if (split) {
913 		/* Nothing to confirm, as this never overwrites samples */
914 		pat2smp_multi(ps);
915 	} else {
916 		if (current_song->samples[ps->sample].data == NULL) {
917 			pat2smp_single(ps);
918 		} else {
919 			dialog_create(DIALOG_OK_CANCEL, "This will replace the current sample.",
920 				pat2smp_single, free, 1, ps);
921 		}
922 	}
923 }
924 
925 // ---------------------------------------------------------------------------
926 
927 /* called from audio_playback.c _schism_midi_out_raw() */
928 int _disko_writemidi(UNUSED const void *data, UNUSED unsigned int len, UNUSED unsigned int delay)
929 {
930 	return DW_ERROR;
931 }
932