1 //
2 // SIDPLAY to .WAV/.AU
3 // Copyright (C) Michael Schwendt and Adam Lorentzon.
4 // Some /u-law specific code 'borrowed' from tracker 4.43 by Marc Espie.
5 //
6 //  This program is free software; you can redistribute it and/or modify
7 //  it under the terms of the GNU General Public License as published by
8 //  the Free Software Foundation; either version 2 of the License, or
9 //  (at your option) any later version.
10 //
11 //  This program is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //  GNU General Public License for more details.
15 //
16 //  You should have received a copy of the GNU General Public License
17 //  along with this program; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 //
20 
21 #include <iostream>
22 #include <iomanip>
23 #include <fstream>
24 #include <string.h>
25 
26 #include <stdlib.h>
27 #ifdef __MSDOS__
28 #include <dir.h>
29 #endif
30 #include <time.h>
31 
32 #if defined(__amigaos__)
33 #define EXIT_ERROR_STATUS (20)
34 #else
35 #define EXIT_ERROR_STATUS (-1)
36 #endif
37 
38 #include <sidplay/player.h>
39 #include <sidplay/fformat.h>
40 #include <sidplay/myendian.h>
41 
42 using std::cerr;
43 using std::cout;
44 using std::endl;
45 using std::dec;
46 using std::flush;
47 using std::hex;
48 using std::ios;
49 using std::setfill;
50 using std::setw;
51 
52 const char s2w_version[] = "1.8";
53 
54 struct wav_hdr                  // little endian
55 {
56 	char main_chunk[4];         // 'RIFF'
57 	udword length;              // filelength
58 	char chunk_type[4];         // 'WAVE'
59 
60 	char sub_chunk[4];          // 'fmt '
61 	udword clength;             // length of sub_chunk, always 16 bytes
62 	uword  format;              // currently always = 1 = PCM-Code
63 	uword  modus;               // 1 = mono, 2 = stereo
64 	udword samplefreq;          // sample-frequency
65 	udword bytespersec;         // frequency * bytespersmpl
66 	uword  bytespersmpl;        // bytes per sample; 1 = 8-bit, 2 = 16-bit
67 	uword  bitspersmpl;
68 	char data_chunk[4];         // keyword, begin of data chunk; = 'data'
69 	udword data_length;         // length of data
70 };
71 
72 struct au_hdr                   // big endian
73 {
74 	char id[4];                 // '.snd'
75 	udword hdrlength;           // const 0x18
76 	udword length;              // data length
77 	udword format;              // 1=ulaw
78 	udword frequency;
79 	udword channels;            // 1=mono, 2=stereo
80 	// Cool Edit v1.50 saves au files with a header that contains an extra
81 	// 4 byte field at the end. It has been filled with zeros in the
82 	// cases I've come across. This makes hdrlength 0x1C.
83 };
84 
85 // Default WAV header: PCM, mono, 44100 Hz, 8-bit
86 #if defined(SID_WORDS_LITTLEENDIAN)
87 wav_hdr my_wav_hdr =
88 {
89 	{'R','I','F','F'}, 0, {'W','A','V','E'},
90 	{'f','m','t',' '}, 16, 1, 1, 22050, 22050, 1, 8,
91 	{'d','a','t','a'}, 0
92 };
93 #else
94 wav_hdr my_wav_hdr =
95 {
96 	{'R','I','F','F'}, 0, {'W','A','V','E'},
97 	{'f','m','t',' '},
98 	convertEndianess ((udword)16),
99 	convertEndianess ((uword)1),
100 	convertEndianess ((uword)1),
101 	convertEndianess ((udword)22050),
102 	convertEndianess ((udword)22050),
103 	convertEndianess ((uword)1),
104 	convertEndianess ((uword)8),
105 	{'d','a','t','a'}, 0
106 };
107 #endif
108 
109 #if defined(SID_WORDS_LITTLEENDIAN)
110 au_hdr my_au_hdr =
111 {
112 	{'.','s','n','d'},
113 	convertEndianess ((udword)0x18),
114 	0,
115 	convertEndianess ((udword)1),
116 	convertEndianess ((udword)8000),           // 8000bytes = 0x1F40, 8012=0x1F4C
117 	convertEndianess ((udword)1)
118 };
119 #else
120 au_hdr my_au_hdr =
121 {
122 	{'.','s','n','d'}, 0x18, 0, 1, 8000, 1
123 };
124 #endif
125 
126 struct FadeParam
127 {
128 	int seconds;
129 	int step;
130 	int count;
131 	int currentLevel;
132 	void (*buffer)(ubyte*,udword);
133 } fadeIn, fadeOut;
134 
135 const int fadeLevel = 128;
136 
137 void fadeOut_buffer_8( ubyte*, udword );
138 void fadeIn_buffer_8( ubyte*, udword );
139 void fadeOut_buffer_16( ubyte*, udword );
140 void fadeIn_buffer_16( ubyte*, udword );
141 
142 void buffer2ulaw( ubyte* sampleBuffer, udword sampleBufferSize );
143 void endianswitch_buffer( ubyte* sampleBuffer, udword sampleBufferSize );
144 
145 enum
146 {
147 	TXT_TITLE,
148     ERR_NOT_ENOUGH_MEMORY,
149     ERR_SYNTAX,	ERR_ENGINE,
150 	ERR_ENDIANESS
151 };
152 void error( char*, char* );
153 void printtext( int );
154 
155 
156 int main(int argc, char *argv[])
157 {
158 	// ======================================================================
159 	// INITIALIZE THE EMULATOR ENGINE
160 	// ======================================================================
161 
162 	// Initialize the SID-Emulator Engine to defaults.
163 	emuEngine myEmuEngine;
164 	// Everything went okay ?
165 	if ( !myEmuEngine )
166 	{
167 		// So far the only possible error.
168 		printtext(ERR_NOT_ENOUGH_MEMORY);
169 	}
170 	if ( !myEmuEngine.verifyEndianess() )
171 	{
172 		printtext(ERR_ENDIANESS);
173 	}
174 
175 	struct emuConfig myEmuConfig;
176 	myEmuEngine.getConfig(myEmuConfig);
177 
178         // ---
179 
180         cout << "SID2WAV   Synthetic Waveform Generator   " << "Portable Version " << s2w_version << "/" << myEmuEngine.getVersionString() << endl
181             << "Copyright (c) 1994-97   All rights reserved." << endl
182             << "Authors: Michael Schwendt <sidplay@geocities.com>" << endl
183             << "         Adam Lorentzon   <d93-alo@nada.kth.se>" << endl
184 #if defined(__amigaos__)
185             << "Ported to AmigaOS: <phillwooller@geocities.com>" << endl
186 #endif
187             << endl;
188 
189 	// ======================================================================
190 	// GET ARGUMENT LINE PARAMETERS
191 	// LOAD SIDTUNE
192 	// CONFIGURE THE EMULATOR ENGINE
193 	// ======================================================================
194 
195 	const int FLAG_STDIN = 0x01;
196 	const int FLAG_ULAW = 0x08;
197 	int flags = 0;
198 
199 	// Defaults.
200 	myEmuConfig.frequency = 44100;
201 	myEmuConfig.channels = SIDEMU_MONO;
202 	myEmuConfig.bitsPerSample = SIDEMU_8BIT;
203 	myEmuConfig.memoryMode = MPU_BANK_SWITCHING;
204 	uword selectedSong = 0;
205 	int seconds = 60;
206 	int secondsToSkip = 0;
207 	int muteVal = 0;  // which voices to mute
208 	fadeIn.seconds = 0;
209 	fadeIn.buffer = fadeIn_buffer_8;
210 	fadeOut.seconds = 2;
211 	fadeOut.buffer = fadeOut_buffer_8;
212 
213 	// File argument numbers.
214 	int infile = 0, outfile = 0;
215 
216 	// Parse command line arguments.
217 	for ( int a = 1; a < argc; a++)
218 	{
219 		if ( argv[a][0] == '-')
220 		{
221 #ifndef __MSDOS__
222 			// Reading from stdin.
223 			if ( strlen(argv[a]) == 1 )
224 			{
225 				if ( infile == 0 )
226 				{
227 					infile = a;
228 					flags |= FLAG_STDIN;
229 				}
230 				else
231 				{
232 					printtext(ERR_SYNTAX);
233 				}
234 				break;
235 			}
236 #endif
237             if ( myStrNcaseCmp( &argv[a][1], "fout" ) == 0 )
238 			{
239 				fadeOut.seconds = atoi(argv[a]+5);
240 			}
241             else if ( myStrNcaseCmp( &argv[a][1], "fin" ) == 0 )
242 			{
243 				fadeIn.seconds = atoi(argv[a]+4);
244 			}
245             else if ( myStrNcaseCmp( &argv[a][1], "nf" ) == 0 )
246 			{
247 				myEmuConfig.emulateFilter = false;
248 			}
249             else if ( myStrNcaseCmp( &argv[a][1], "ns" ) == 0 )
250 			{
251                 myEmuConfig.mos8580 = true;
252 			}
253             else if ( myStrNcaseCmp( &argv[a][1], "a2" ) == 0 )
254 			{
255 				myEmuConfig.memoryMode = MPU_TRANSPARENT_ROM;
256 			}
257             else if ( myStrNcaseCmp( &argv[a][1], "a" ) == 0 )
258 			{
259 				myEmuConfig.memoryMode = MPU_PLAYSID_ENVIRONMENT;
260 			}
261             else if ( myStrNcaseCmp( &argv[a][1], "b" ) == 0 )
262 			{
263 				secondsToSkip = atoi(argv[a]+2);
264 			}
265             else if ( myStrNcaseCmp( &argv[a][1], "f" ) == 0 )
266 			{
267 				myEmuConfig.frequency = (ulong)atoi(argv[a]+2);
268 			}
269             else if ( myStrNcaseCmp( &argv[a][1], "h" ) == 0 )
270 			{
271 				printtext(ERR_SYNTAX);
272 			}
273             else if ( myStrNcaseCmp( &argv[a][1], "m" ) == 0 )
274 			{
275 				for ( ubyte j = 2; j < strlen(argv[a]); j++ )
276 				{
277 					if ( (argv[a][j]>='1') && (argv[a][j]<='4') )
278 					{
279 						muteVal |= (1 << argv[a][j]-'1');
280 					}
281 				}
282 				myEmuConfig.volumeControl = SIDEMU_VOLCONTROL;
283 			}
284             else if ( myStrNcaseCmp( &argv[a][1], "n" ) == 0 )
285 			{
286 				myEmuConfig.clockSpeed = SIDTUNE_CLOCK_NTSC;
287 				myEmuConfig.forceSongSpeed = true;
288 			}
289             else if ( myStrNcaseCmp( &argv[a][1], "o" ) == 0 )
290 			{
291 				selectedSong = atoi(argv[a]+2);
292 			}
293             else if ( myStrNcaseCmp( &argv[a][1], "t" ) == 0 )
294 			{
295 				seconds = atoi(argv[a]+2);
296 			}
297             else if ( myStrNcaseCmp( &argv[a][1], "ss" ) == 0 )
298 			{
299 				myEmuConfig.channels = SIDEMU_STEREO;
300 				myEmuConfig.volumeControl = SIDEMU_STEREOSURROUND;
301 			}
302             else if ( myStrNcaseCmp( &argv[a][1], "s" ) == 0 )
303 			{
304 				myEmuConfig.channels = SIDEMU_STEREO;
305 			}
306             else if ( myStrNcaseCmp( &argv[a][1], "u" ) == 0 )
307 			{
308 				flags |= FLAG_ULAW;
309 			}
310             else if ( myStrNcaseCmp( &argv[a][1], "16" ) == 0 )
311 			{
312 				myEmuConfig.bitsPerSample = SIDEMU_16BIT;
313 			}
314 			else
315 			{
316 				printtext(ERR_SYNTAX);
317 			}
318 		}
319 		else
320 		{
321 			// Set filename argument number.
322 			if ( infile == 0 )
323 			{
324 				infile = a;
325 			}
326 			else if ( outfile == 0 )
327 			{
328 				outfile = a;
329 			}
330 			else
331 			{
332 				printtext(ERR_SYNTAX);
333 			}
334 		}
335 	}
336 
337 	if ( infile == 0 )
338 	{
339 		printtext(ERR_SYNTAX);
340 	}
341 
342 	//
343 
344 	if (flags & FLAG_ULAW)
345 	{
346 		myEmuConfig.frequency = 8000;          // 8000 or 8012??
347 		myEmuConfig.channels = SIDEMU_MONO;
348 		myEmuConfig.bitsPerSample = SIDEMU_16BIT;
349 	}
350 	if (myEmuConfig.bitsPerSample == SIDEMU_16BIT)
351 	{
352 		fadeOut.buffer = fadeOut_buffer_16;
353 		fadeIn.buffer = fadeIn_buffer_16;
354 	}
355 
356 #ifdef __MSDOS__
357 	char waveFileName[MAXPATH];
358 	if ( outfile == 0 )
359 	{
360 		char indrive[MAXDRIVE], inpath[MAXDIR], inname[MAXFILE], inext[MAXEXT];
361 		fnsplit(argv[infile], indrive, inpath, inname, inext);
362 		if ( flags & FLAG_ULAW )
363 		{
364 			fnmerge(waveFileName, indrive, inpath, inname, ".au");
365 		}
366 		else
367 		{
368 			fnmerge(waveFileName, indrive, inpath, inname, ".wav");
369 		}
370 	}
371 	else
372 	{
373 		strcpy(waveFileName, argv[outfile]);
374 	}
375 #else
376 	char* waveFileName = 0;
377 	if ( outfile == 0 )
378 	{
379 		waveFileName = new char[strlen(argv[infile])+4+1];
380 		strcpy(waveFileName,argv[infile]);
381 		if ( flags & FLAG_ULAW )
382 		{
383 			strcpy(fileExtOfPath(waveFileName),".au");
384 		}
385 		else
386 		{
387 			strcpy(fileExtOfPath(waveFileName),".wav");
388 		}
389 	}
390 	else
391 	{
392 		waveFileName = new char[strlen(argv[outfile])+1];
393 		strcpy(waveFileName,argv[outfile]);
394 	}
395 #endif
396 
397 	// Create the sidtune object.
398 	sidTune myTune( argv[infile] );
399 	struct sidTuneInfo mySidInfo;
400 	myTune.getInfo( mySidInfo );
401 	if ( !myTune )
402 	{
403 		cerr << mySidInfo.statusString << endl;
404 		exit(EXIT_ERROR_STATUS);
405 	}
406 	else
407 	{
408 		cout << "File format  : " << mySidInfo.formatString << endl;
409 		cout << "Condition    : " << mySidInfo.statusString << endl;
410 		if ( mySidInfo.numberOfInfoStrings == 3 )
411 		{
412 			cout << "Name         : " << mySidInfo.nameString << endl;
413 			cout << "Author       : " << mySidInfo.authorString << endl;
414 			cout << "Copyright    : " << mySidInfo.copyrightString << endl;
415 		}
416 		else
417 		{
418 			for ( int infoi = 0; infoi < mySidInfo.numberOfInfoStrings; infoi++ )
419 			{
420 				cout << "Description  : " << mySidInfo.infoString[infoi] << endl;
421 			}
422 		}
423 		cout << "Load address : $" << hex << setw(4) << setfill('0')
424 			<< mySidInfo.loadAddr << endl;
425 		cout << "Init address : $" << hex << setw(4) << setfill('0')
426 			<< mySidInfo.initAddr << endl;
427 		cout << "Play address : $" << hex << setw(4) << setfill('0')
428 			<< mySidInfo.playAddr << dec << endl;
429 	}
430 
431 	// Alter the SIDPLAY Emulator Engine settings.
432 	myEmuConfig.sampleFormat = (myEmuConfig.bitsPerSample == SIDEMU_16BIT) ?  SIDEMU_SIGNED_PCM : SIDEMU_UNSIGNED_PCM;
433 	myEmuEngine.setConfig(myEmuConfig);
434 	// Here mute the voices, if requested.
435 	if (myEmuConfig.volumeControl == SIDEMU_VOLCONTROL)
436 	{
437 		for ( int voice = 1; voice <= 4; voice++ )
438 		{
439 			if ( (muteVal & (1<<(voice-1))) != 0 )
440 			{
441 				myEmuEngine.setVoiceVolume(voice,0,0,0);
442 			}
443 		}
444 	}
445 	// Get the current settings. We ignore the return value, because this code is
446 	// supposed to allow only valid settings.
447 	myEmuEngine.getConfig(myEmuConfig);
448     // Print the relevant settings.
449 	cout << "SID Filter   : " << ((myEmuConfig.emulateFilter == true) ? "Yes" : "No") << endl;
450 	if (myEmuConfig.memoryMode == MPU_PLAYSID_ENVIRONMENT)
451 	{
452 		cout << "Memory mode  : PlaySID (this is supposed to fix PlaySID-specific rips)" << endl;
453 	}
454 	else if (myEmuConfig.memoryMode == MPU_TRANSPARENT_ROM)
455 	{
456 		cout << "Memory mode  : Transparent ROM (SIDPLAY default)" << endl;
457 	}
458 	else if (myEmuConfig.memoryMode == MPU_BANK_SWITCHING)
459 	{
460 		cout << "Memory mode  : Bank Switching" << endl;
461 	}
462     cout << "Frequency    : " << dec << myEmuConfig.frequency << " Hz" << endl;
463 	cout << "Bits/sample  : ";
464 	if (flags & FLAG_ULAW)
465 	{
466 		cout << "8 (u-law)" << endl;
467 	}
468 	else
469 	{
470 	    cout << dec << myEmuConfig.bitsPerSample << endl;
471 	}
472 	cout << "Channels     : " << ((myEmuConfig.channels == SIDEMU_MONO) ? "Mono" : "Stereo") << endl;
473 
474 	myTune.setInfo( mySidInfo );
475 
476 	if ( !sidEmuInitializeSong(myEmuEngine,myTune,selectedSong) )
477 	{
478 		cerr << "ERROR: SID Emulator Engine components not ready" << endl;
479 		exit(EXIT_ERROR_STATUS);
480 	}
481 	myTune.getInfo( mySidInfo );
482 	if ( !myTune )
483 	{
484 		cerr << mySidInfo.statusString;
485 		exit(EXIT_ERROR_STATUS);
486 	}
487 	cout << "Setting song : " << mySidInfo.currentSong
488 		<< " out of " << mySidInfo.songs
489 		<< " (default = " << mySidInfo.startSong << ')' << endl;
490 	cout << "Song speed   : " << mySidInfo.speedString << endl;
491 
492     cout << "File length  : " << seconds << " second";
493 	if ( seconds > 1 )
494 	{
495 		cout << 's';
496 	}
497 	cout << endl;
498 
499 	// Open output file stream.
500 #if defined(SID_HAVE_IOS_BIN)
501     ofstream waveFile( waveFileName, ios::out|ios::bin|ios::app );
502 #else
503 	ofstream waveFile( waveFileName, ios::out|ios::binary|ios::app );
504 #endif
505 	if ( !waveFile || waveFile.tellp()>0 )
506 	{
507         cerr << "ERROR: Cannot create output file " << "``" << waveFileName << "'', "
508             << "probably already exits." << endl;
509 		exit(EXIT_ERROR_STATUS);
510 	}
511 	cout << "Output file  : " << waveFileName << endl;
512 
513 	udword sampleBufferSize;
514 	udword dataLength;
515 	uword  headerSize;
516 
517 	if (flags & FLAG_ULAW)
518 	{
519 		dataLength = seconds * myEmuConfig.frequency;
520 #if defined(SID_WORDS_LITTLEENDIAN)
521 		my_au_hdr.length = convertEndianess (dataLength);
522 #else
523 		my_au_hdr.length = dataLength;
524 #endif
525 		sampleBufferSize = myEmuConfig.frequency * 2;
526 		headerSize = sizeof(au_hdr);
527         waveFile.write( (char*)&my_au_hdr, headerSize );
528 	}
529 	else
530 	{
531 		udword bytesPerSample = myEmuConfig.channels*myEmuConfig.bitsPerSample/8;
532 		udword bytesPerSecond = myEmuConfig.frequency*bytesPerSample;
533 		dataLength = seconds * bytesPerSecond;
534 		sampleBufferSize = bytesPerSecond;
535 		headerSize = sizeof(wav_hdr);
536 #if defined (SID_WORDS_LITTLEENDIAN)
537 		my_wav_hdr.bitspersmpl = myEmuConfig.bitsPerSample;
538 		my_wav_hdr.bytespersmpl = bytesPerSample;
539 		my_wav_hdr.samplefreq = myEmuConfig.frequency;
540 		my_wav_hdr.modus = myEmuConfig.channels;
541 		my_wav_hdr.bytespersec = bytesPerSecond;
542 		my_wav_hdr.length = dataLength + headerSize - 8;
543 		my_wav_hdr.data_length = dataLength;
544 #else
545 		my_wav_hdr.bitspersmpl = convertEndianess ((uword)myEmuConfig.bitsPerSample);
546 		my_wav_hdr.bytespersmpl = convertEndianess ((uword)bytesPerSample);
547 		my_wav_hdr.samplefreq = convertEndianess ((udword)myEmuConfig.frequency);
548 		my_wav_hdr.modus = convertEndianess ((uword)myEmuConfig.channels);
549 		my_wav_hdr.bytespersec = convertEndianess ((udword)bytesPerSecond);
550 		my_wav_hdr.length = convertEndianess (dataLength + headerSize - 8);
551 		my_wav_hdr.data_length = convertEndianess (dataLength);
552 #endif
553         waveFile.write( (char*)&my_wav_hdr, headerSize );
554 	}
555 
556 	// Make a buffer that holds 1 second of audio data.
557 	ubyte* sampleBuffer = new ubyte[sampleBufferSize];
558 
559 	// Calculate fadeIn and -out variables.
560 	if ( seconds == 0 )
561 	{
562 		seconds = 60; // force the default
563 	}
564 	if ( seconds < fadeIn.seconds )
565 	{
566 		cerr << "Warning: Bad fade-in time." << endl;
567 		fadeIn.seconds = seconds /2;
568 	}
569 	if ( seconds < fadeOut.seconds )
570 	{
571 		cerr << "Warning: Bad fade-out time." << endl;
572 		fadeOut.seconds = seconds /2;
573 	}
574 	if (( fadeIn.seconds + fadeOut.seconds  ) > seconds )
575 	{
576 		cerr << "Warning: Bad total fading time." << endl;
577 		fadeIn.seconds = 0;
578 		fadeOut.seconds = 0;
579 	}
580 	fadeIn.currentLevel = 0;           // minimum volume (silence)
581 	fadeOut.currentLevel = fadeLevel;  // maximum volume
582 	fadeIn.step = ( fadeIn.seconds * sampleBufferSize / (myEmuConfig.bitsPerSample/8) ) / fadeLevel;
583 	fadeOut.step = ( fadeOut.seconds * sampleBufferSize / (myEmuConfig.bitsPerSample/8) ) / fadeLevel;
584 	fadeOut.count = ( fadeIn.count = 0 );
585 
586 	cout << endl;
587 	cout << "Generating sample data...don't interrupt !" << endl;
588 
589 	if (secondsToSkip > 0)
590 		cout << "Skipping seconds : ";
591 	int skipped = 0;
592 	while (skipped < secondsToSkip)
593 	{
594 		sidEmuFillBuffer( myEmuEngine, myTune, sampleBuffer, sampleBufferSize );
595 		skipped++;
596 		// Print progress report.
597 		cout << setw(5) << setfill(' ') << skipped << "\b\b\b\b\b" << flush;
598 	};
599 	if (secondsToSkip > 0)
600 		cout << endl;
601 
602 	cout << "Length of output file (bytes) : ";
603 
604 	dataLength = 0;
605 
606 	for ( int sec = 0; sec < seconds; sec++ )
607 	{
608 		sidEmuFillBuffer( myEmuEngine, myTune, sampleBuffer, sampleBufferSize );
609 		if ( sec < ( fadeIn.seconds ))
610 			(*fadeIn.buffer)( sampleBuffer, sampleBufferSize );
611 		if ( sec >= ( seconds - fadeOut.seconds ))
612 			(*fadeOut.buffer)( sampleBuffer, sampleBufferSize );
613 		if (flags & FLAG_ULAW)
614 		{
615 			buffer2ulaw(sampleBuffer, sampleBufferSize);
616 			waveFile.write( (char*)sampleBuffer, sampleBufferSize / 2 );
617 		}
618 		else
619 		{
620 #if defined(SID_WORDS_BIGENDIAN)
621 			if (myEmuConfig.bitsPerSample == SIDEMU_16BIT)
622 			{
623 				endianswitch_buffer( sampleBuffer, sampleBufferSize );
624 			}
625 #endif
626 			waveFile.write( (char*)sampleBuffer, sampleBufferSize );
627 		}
628 
629 		// Print progress report.
630 		cout << setw(10) << setfill(' ') << ( dataLength + headerSize) << "\b\b\b\b\b\b\b\b\b\b" << flush;
631 
632 		if (flags & FLAG_ULAW)
633 			dataLength += sampleBufferSize / 2;
634 		else
635 			dataLength += sampleBufferSize;
636 	}
637 	// Finish progress report.
638 	cout << setw(10) << setfill(' ') << ( dataLength + headerSize) << endl;
639 	waveFile.close();
640 	delete[] waveFileName;
641 	delete[] sampleBuffer;
642 
643 	cout << endl << "Please do not forget to give the credits whenever using a waveform created" << endl
644 		<< "by this application !" << endl << endl;
645 
646 	return 0;
647 }
648 
649 
650 void fadeOut_buffer_8( ubyte* sampleBuffer, udword sampleBufferSize )
651 {
652 	for ( udword i = 0; i < sampleBufferSize; i++ )
653     {
654 		sbyte sam = (sbyte)( 0x80 ^ *(sampleBuffer +i));
655 		sword modsam = sam * fadeOut.currentLevel;
656 		modsam /= fadeLevel;
657 		sam = (sbyte)modsam;
658 		*(sampleBuffer +i) = 0x80 ^ sam;
659 
660 		fadeOut.count++;
661 		if ( fadeOut.count >= fadeOut.step )
662 		{
663 			if ( fadeOut.currentLevel > 0 )
664 			{
665 				fadeOut.currentLevel--;
666 			}
667 			fadeOut.count = 0;
668 		}
669 	}
670 }
671 
672 void fadeOut_buffer_16( ubyte* sampleBuffer, udword sampleBufferSize )
673 {
674 	sword *buf = (sword *)sampleBuffer;
675 	sampleBufferSize /= 2;
676 	for ( udword i = 0; i < sampleBufferSize; i++ )
677 	{
678 		sword sam = *(buf +i);
679 		sdword modsam = sam * fadeOut.currentLevel;
680 		modsam /= fadeLevel;
681 		sam = (sword)modsam;
682 		*(buf +i) = sam;
683 
684 		fadeOut.count++;
685 		if ( fadeOut.count >= fadeOut.step )
686 		{
687 			if ( fadeOut.currentLevel > 0 )
688 			{
689 				fadeOut.currentLevel--;
690 			}
691 			fadeOut.count = 0;
692 		}
693 	}
694 }
695 
696 void fadeIn_buffer_8( ubyte* sampleBuffer, udword sampleBufferSize )
697 {
698 	for ( udword i = 0; i < sampleBufferSize; i++ )
699 	{
700 		sbyte sam = (sbyte)( 0x80 ^ *(sampleBuffer +i));
701 		sword modsam = sam * fadeIn.currentLevel;
702 		modsam /= fadeLevel;
703 		sam = (sbyte)modsam;
704 		*(sampleBuffer +i) = 0x80 ^ sam;
705 
706 		fadeIn.count++;
707 		if ( fadeIn.count >= fadeIn.step )
708 		{
709 			if ( fadeIn.currentLevel < fadeLevel )
710 			{
711 				fadeIn.currentLevel++;
712 			}
713 			fadeIn.count = 0;
714 		}
715 	}
716 }
717 
718 void fadeIn_buffer_16( ubyte* sampleBuffer, udword sampleBufferSize )
719 {
720 	sword *buf = (sword *)sampleBuffer;
721 	sampleBufferSize /= 2;
722 	for ( udword i = 0; i < sampleBufferSize; i++ )
723 	{
724 		sword sam = *(buf +i);
725 		sdword modsam = sam * fadeIn.currentLevel;
726 		modsam /= fadeLevel;
727 		sam = (sword)modsam;
728 		*(buf +i) = sam;
729 
730 		fadeIn.count++;
731 		if ( fadeIn.count >= fadeIn.step )
732 		{
733 			if ( fadeIn.currentLevel < fadeLevel )
734 			{
735 				fadeIn.currentLevel++;
736 			}
737 			fadeIn.count = 0;
738 		}
739 	}
740 }
741 
742 void error(char* s1, char* s2 = "")
743 {
744     cerr << "ERROR: " << s1 << ' ' << "``" << s2 << "''." << endl;
745 	exit(EXIT_ERROR_STATUS);
746 }
747 
748 
749 void printtext(int number)
750 {
751 	switch (number)
752 	{
753 	 case ERR_ENDIANESS:
754 		{
755 			cerr << "ERROR: Hardware endianess improperly configured." << endl;
756 			exit(EXIT_ERROR_STATUS);
757 			break;
758 		}
759 	 case ERR_ENGINE:  // currently the only true reason the engine should fail
760 	 case ERR_NOT_ENOUGH_MEMORY:
761 		{
762 			cerr << "ERROR: Not enough memory." << endl;
763 			exit(EXIT_ERROR_STATUS);
764 			break;
765 		}
766 	 case ERR_SYNTAX:
767 		{
768 #ifdef __MSDOS__
769 			cout << " syntax: sid2wav [-<commands>] <datafile> [outputfile]" << endl
770 #else
771 			cout << " syntax: sid2wav [-<commands>] <datafile>|- [outputfile]" << endl
772 #endif
773 				<< " commands: -h         display this screen" << endl
774 				<< "           -f<num>    set frequency in Hz (default: 44100)" << endl
775 				<< "           -16        16-bit (default: 8-bit)" << endl
776 				<< "           -s         stereo (default: mono)" << endl
777 				<< "           -ss        enable stereo surround" << endl
778 				<< "           -u         au output (8000Hz mono 8-bit u-law)" << endl
779 				<< "           -o<num>    set song number (default: preset)" << endl
780 				<< "           -a         improve PlaySID compatibility (not recommended)" << endl
781 				<< "           -a2        transparent ROM memory mode (overrides -a)" << endl
782 				<< "           -n         enable NTSC-clock speed for VBI tunes (not recommended)" << endl
783 				<< "           -nf        no SID filter emulation" << endl
784                 << "           -ns        MOS 8580 waveforms (default: MOS 6581)" << endl
785 				<< "           -m<num>    mute voices out of 1,2,3,4 (default: none)" << endl
786 				<< "                      example: -m13 (voices 1 and 3 off)" << endl
787 				<< "           -t<num>    set seconds to play (default: 60)" << endl
788 				<< "           -b<num>    skip first <num> seconds into the song (default: 0)" << endl
789 				<< "           -fin<num>  fade-in time in seconds (default: 0)" << endl
790 				<< "           -fout<num> fade-out time in seconds (default: 2)" << endl
791 				<< endl;
792 			exit(EXIT_ERROR_STATUS);
793 			break;
794 		}
795 	 default:
796 		{
797 			cerr << "ERROR: Internal system error." << endl;
798 			exit(EXIT_ERROR_STATUS);
799 			break;
800 		}
801 	}
802 }
803 
804 
805 
806 
807 
808 
809 
810 
811 
812 
813 
814 
815 
816 
817 
818 // ------------ Beginning of code 'borrowed' from tracker 4.43 by Marc Espie.
819 
820 // The only modifications to the code were a few changes from C style
821 // to C++ style to please the compiler.
822 
823 
824 short seg_end[8] =
825 {
826 	0xFF, 0x1FF, 0x3FF, 0x7FF,
827 	0xFFF, 0x1FFF, 0x3FFF, 0x7FFF
828 };
829 
830 int search(int val, short *table, int size)
831 {
832 	int	i;
833 
834 	for (i = 0; i < size; i++)
835 	{
836 		if (val <= *table++)
837 		{
838 			return i;
839 		}
840 	}
841 	return size;
842 }
843 
844 const int BIAS = 0x84;		    // Bias for linear code.
845 
846 // linear2ulaw() - Convert a linear PCM value to u-law
847 //
848 // In order to simplify the encoding process, the original linear magnitude
849 // is biased by adding 33 which shifts the encoding range from (0 - 8158) to
850 // (33 - 8191). The result can be seen in the following encoding table:
851 //
852 //	Biased Linear Input Code	Compressed Code
853 //	------------------------	---------------
854 //	00000001wxyza			000wxyz
855 //	0000001wxyzab			001wxyz
856 //	000001wxyzabc			010wxyz
857 //	00001wxyzabcd			011wxyz
858 //	0001wxyzabcde			100wxyz
859 //	001wxyzabcdef			101wxyz
860 //	01wxyzabcdefg			110wxyz
861 //	1wxyzabcdefgh			111wxyz
862 //
863 // Each biased linear code has a leading 1 which identifies the segment
864 // number. The value of the segment number is equal to 7 minus the number
865 // of leading 0's. The quantization interval is directly available as the
866 // four bits wxyz. // The trailing bits (a - h) are ignored.
867 //
868 // Ordinarily the complement of the resulting code word is used for
869 // transmission, and so the code word is complemented before it is returned.
870 //
871 // For further information see John C. Bellamy's Digital Telephony, 1982,
872 // John Wiley & Sons, pps 98-111 and 472-476.
873 
874 unsigned char linear2ulaw(int pcm_val)
875 // int pcm_val;	// 2's complement (16-bit range)
876 {
877 	int	mask;
878 	int	seg;
879 	unsigned char uval;
880 
881 	// Get the sign and the magnitude of the value.
882 	if (pcm_val < 0)
883     {
884 		pcm_val = BIAS - pcm_val;
885 		mask = 0x7F;
886     }
887 	else
888     {
889 		pcm_val += BIAS;
890 		mask = 0xFF;
891     }
892 
893 	// Convert the scaled magnitude to segment number.
894 	seg = search(pcm_val, seg_end, 8);
895 
896 	// Combine the sign, segment, quantization bits;
897 	// and complement the code word.
898 
899 	if (seg >= 8)		// out of range, return maximum value.
900 	{
901 		return 0x7F ^ mask;
902 	}
903 	else
904 	{
905 		uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF);
906 		return uval ^ mask;
907 	}
908 }
909 
910 //
911 // ------------------- End of code 'borrowed' from tracker 4.43 by Marc Espie
912 
913 
914 
915 
916 
917 
918 
919 
920 
921 
922 
923 
924 
925 //
926 // Assume incoming data is 16-bit signed values.
927 // sampleBufferSize is the size in bytes of the buffer sampleBuffer
928 //
929 void buffer2ulaw( ubyte* sampleBuffer, udword sampleBufferSize )
930 {
931 	sword *wordbuffer = (sword *) sampleBuffer;
932 	udword numsamples = sampleBufferSize / 2;
933 	for (udword i = 0; i < numsamples; i++)
934 	{
935 		sampleBuffer[i] = linear2ulaw ((~wordbuffer[i]) + 1);  // two's complement
936 	}
937 }
938 
939 
940 //
941 // Incoming data is 16-bit values which needs an endian-switch
942 // sampleBufferSize is the size in bytes of the buffer sampleBuffer
943 //
944 void endianswitch_buffer( ubyte* sampleBuffer, udword sampleBufferSize )
945 {
946 	uword *wordbuffer = (uword *) sampleBuffer;
947 	udword numsamples = sampleBufferSize / 2;
948 	for (udword i = 0; i < numsamples; i++)
949 	{
950 		wordbuffer[i] = convertEndianess (wordbuffer[i]);
951 	}
952 }
953