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