'=========================================================================== ' Subject: VOC TO SAMPLE DUMP STANDARD Date: 03-02-93 (00:00) ' Author: Monte Ferguson Code: QB, QBasic, PDS ' Origin: FidoNet QUIK_BAS Echo Packet: SOUND.ABC '=========================================================================== ' VOC2SDS by Monte Ferguson (C) Copyright 1993 Monte Ferguson ' ' Notes: This code was not written to be elegant or user friendly, or to be ' a tutorial on how to write good code - it was written to WORK the way *I* ' wanted it to. ' ' If you'd like to swipe the code or hack it, please feel free. I ask only ' that you send me a copy of anything you create with it - that would be my ' payment. Mention in your dox would be nice, too :-) ' ' Monte Ferguson ' 1250 Anita Drive #304 ' Kent, OH 44240 ' Fido: 1:157/200.39 ' ' Enjoy. ' ' P.S. - hardcoded stuff that's easy to change is generally marked with ' <<< LOOK << ' ie, channel numbers, sample number, etc. DECLARE FUNCTION GetBlkLen! () DECLARE FUNCTION GenPath$ (FSpec$) DECLARE FUNCTION GenSpec$ (FSpec$, DefExt$) DECLARE FUNCTION SngToM3$ (n!) DECLARE FUNCTION M3toDec! (m3$) DECLARE FUNCTION Hx$ (Text$) DEFINT A-Z ' ' VOC2SDS - Converts .VOC files to Sample Dump Standard ' Copyright 1993 Monte Ferguson ' ' First version 01-Mar-93 ' CONST Vers = "1.0" CONST LastUpdate = "02-Mar-93" CONST Copyright = "VOC2SDS Copyright 1993, Monte Ferguson" CONST False = 0 CONST True = NOT False TYPE VOCHeaderType Des AS STRING * 20 BlockOffset AS INTEGER Vers AS INTEGER VerComp AS INTEGER END TYPE TYPE SDSHeaderType f07e AS STRING * 2 Channel AS STRING * 1 One AS STRING * 1 SampleNum AS STRING * 2 Bits AS STRING * 1 Period AS STRING * 3 SLength AS STRING * 3 SustLoopStart AS STRING * 3 SustLoopEnd AS STRING * 3 LoopType AS STRING * 1 F7 AS STRING * 1 END TYPE TYPE SDSBLockType f07e AS STRING * 2 Channel AS STRING * 1 Two AS STRING * 1 PktCnt AS STRING * 1 DTA AS STRING * 120 ChkSum AS STRING * 1 F7 AS STRING * 1 END TYPE DIM VocHead AS VOCHeaderType DIM SDSHead AS SDSHeaderType DIM SDSBLock AS SDSBLockType FileSpec$ = GenSpec$(LTRIM$(UCASE$(COMMAND$)), "VOC") PRINT Copyright PRINT Vers + " " + LastUpdate PRINT "" IF LEN(FileSpec$) > 0 THEN FPath$ = GenPath$(FileSpec$) d$ = DIR$(FileSpec$) DO WHILE d$ <> "" KY$ = INKEY$ f$ = FPath$ + d$ PRINT "" a$ = "------" + f$ + "------" PRINT SPACE$(40 - LEN(a$) / 2) + a$ PRINT "" ' Examine the file OPEN f$ FOR BINARY AS #1 GET #1, , VocHead IF VocHead.Des <> "Creative Voice File" + CHR$(26) THEN PRINT "Bogus header, not a .VOC file." ELSE v$ = HEX$(VocHead.Vers) IF LEN(v$) < 4 THEN v$ = STRING$(4 - LEN(v$), "0") + v$ v$ = LTRIM$(STR$(VAL("&H" + LEFT$(v$, 2)))) + "." + LTRIM$(STR$(VAL("&H" + RIGHT$(v$, 2)))) PRINT "Version:"; v$ PRINT "Offset to 1st data block:"; VocHead.BlockOffset SEEK #1, VocHead.BlockOffset + 1 BlockCount = 0 ' 1 2 3 4 5 6 7 8 '12345678901234567890123456789012345678901234567890123456789012345678901234567890 'Blk Type Bytes Secs SmplRate Pack Other '## \ \ #,###,### ###.# ##,### \ \ \ \ PRINT "Blk Type Bytes Secs SmplRate Pack Other" PRINT STRING$(79, "-") Converted = False DO BlockCount = BlockCount + 1 BType$ = SPACE$(1) GET #1, , BType$ SELECT CASE ASC(BType$) CASE 0 BType$ = "Terminator" PRINT USING "## \ \ #,###,### ###.# ##,### \ \ \ \"; BlockCount; BType$; 0; 0; 0; "N/A" EXIT DO CASE 1 BL! = GetBlkLen s! = SEEK(1) BType$ = "Voice Data" SR$ = SPACE$(1) GET #1, , SR$ SR! = ASC(SR$) SR! = INT(1000000! / (256 - SR!) + .5) Secs! = INT((BL! / SR!) * 10) / 10 Pk$ = SPACE$(1) GET #1, , Pk$ SELECT CASE ASC(Pk$) CASE 0 PT$ = "Raw 8-bit" CASE 1 PT$ = "4-bit" CASE 2 PT$ = "2.6 bit" CASE 3 PT$ = "2 bit" CASE ELSE PT$ = "Unknown!" END SELECT PRINT USING "## \ \ #,###,### ###.# ##,### \ \ \ \"; BlockCount; BType$; BL!; Secs!; SR!; PT$ IF Pk$ <> CHR$(0) THEN PRINT " ---> PACKED BLOCK, CANNOT CONVERT!" ELSE IF NOT Converted THEN PRINT " ---> Converting..."; Target$ = FPath$ + d$ p = LEN(Target$) DO WHILE p >= 1 IF MID$(Target$, p, 1) = "." THEN EXIT DO END IF p = p - 1 LOOP IF p = 0 THEN Target$ = Target$ + ".SDS" ELSE Target$ = LEFT$(Target$, p) + "SDS" END IF OPEN Target$ FOR BINARY AS #2 SDSHead.f07e = CHR$(&HF0) + CHR$(&H7E) SDSHead.Channel = CHR$(0) ' <<<<<<<<<<<<<<<< LOOK <<<<<< SDSHead.One = CHR$(1) SDSHead.SampleNum = CHR$(0) + CHR$(0)' <<<<<<<<<<<<<<<< LOOK <<<<<< SDSHead.Bits = CHR$(16) ' <<<<<<<<<<<<<<<< LOOK <<<<<< SDSHead.Period = SngToM3$((1 / SR!) * 1000000000#) SDSHead.SLength = SngToM3$(BL!) SDSHead.SustLoopStart = SngToM3$(0)' <<<<<<<<<<<<<<<< LOOK <<<<<< SDSHead.SustLoopEnd = SngToM3$(BL!)' <<<<<<<<<<<<<<<< LOOK <<<<<< SDSHead.LoopType = CHR$(0) ' <<<<<<<<<<<<<<<< LOOK <<<<<< SDSHead.F7 = CHR$(&HF7) PUT #2, , SDSHead ' Now we create blocks by fetching 40 bytes of .VOC data ' at a shot. Since 16 bits takes 3 7-bit words, that gives ' us the correct 120 bytes/block length for SDS. nb! = BL! / 40 IF nb! <> INT(nb!) THEN nb! = INT(nb!) + 1 END IF ' Yes, this grunges the last block if it's not a multiple of ' 40 bytes. So sue me. I *told* you this was quick and dirty! :-) FOR i = 1 TO nb! Pkt = (i - 1) MOD 128' Packet Count Smp$ = SPACE$(40) GET #1, , Smp$ Chk = &H7E ' The running checksum Chk = Chk XOR 0 ' Channel Num Chk = Chk XOR 2 ' "Two" Chk = Chk XOR Pkt DTA$ = "" FOR j = 1 TO LEN(Smp$) Byte8 = ASC(MID$(Smp$, j, 1)) ' This next line converts the 8-bit sample to 16 bits: Byte16! = Byte8 * 256! ' And this stuff divides our 16 bits into three MIDI data bytes. ' The 1st bytes is 512s, the 2nd byte is 4 and the last bytes is the ' remainder (0-3) but LEFT JUSTIFIED within the 7-bit field. Hey, I ' didn't write the standard, I just live with it! :-) b1 = INT(Byte16! / 512) r1! = Byte16! - (b1 * 512!) b2 = INT(r1! / 4) r2! = r1! - (b2 * 4) b3 = r2! * 32 Chk = Chk XOR b1 Chk = Chk XOR b2 Chk = Chk XOR b3 DTA$ = DTA$ + CHR$(b1) + CHR$(b2) + CHR$(b3) NEXT j SDSBLock.f07e = CHR$(&HF0) + CHR$(&H7E) SDSBLock.Channel = CHR$(0) ' <<<<<<<< LOOK <<<<<<<<<<<< SDSBLock.Two = CHR$(2) SDSBLock.PktCnt = CHR$(Pkt) SDSBLock.DTA = DTA$ SDSBLock.ChkSum = CHR$(Chk) SDSBLock.F7 = CHR$(&HF7) PUT #2, , SDSBLock y = CSRLIN x = POS(0) PRINT INT((i / nb!) * 100); "%"; LOCATE y, x NEXT i CLOSE #2 PRINT "Done." Converted = True REM Stuff ELSE PRINT "(this version only converts the 1st block...)" END IF END IF SEEK #1, s! + BL! CASE 2 BL! = GetBlkLen s! = SEEK(1) BType$ = "Voice Continuation" PRINT USING "## \ \ #,###,### ###.# ##,### \ \ \ \"; BlockCount; BType$; BL!; Secs!; SR!; PT$ SEEK #1, s! + BL! CASE 3 BL! = GetBlkLen s! = SEEK(1) BType$ = "Silence" Pr$ = SPACE$(2) GET #1, , Pr$ Pr = CVI(Pr$) SR$ = SPACE$(1) GET #1, , SR$ SR! = ASC(SR$) SR! = INT(1000000! / (256 - SR!) + .5) Secs! = INT((Pr / SR!) * 10) / 10 PRINT USING "## \ \ #,###,### ###.# ##,### \ \ \ \"; BlockCount; BType$; BL!; Secs!; SR!; "N/A" SEEK #1, s! + BL! CASE 4 BL! = GetBlkLen s! = SEEK(1) BType$ = "Marker" Pr$ = SPACE$(2) GET #1, , Pr$ Pr = CVI(Pr$) PRINT USING "## \ \ #,###,### ###.# ##,### \ \ \ \"; BlockCount; BType$; BL!; Secs!; SR!; "N/A"; "Marker=" + LTRIM$(STR$(Pr)) SEEK #1, s! + BL! CASE 5 BL! = GetBlkLen BType$ = "ASCII Text" s! = SEEK(1) Txt$ = SPACE$(BL!) GET #1, , Txt$ PRINT USING "## \ \ #,###,### ###.# ##,### \ \ \ \"; BlockCount; BType$; BL!; 0; 0; "N/A"; "Text follows:" PRINT SPACE$(4); Txt$ SEEK #1, s! + BL! CASE 6 BL! = GetBlkLen s! = SEEK(1) BType$ = "Repeat" Pr$ = SPACE$(2) GET #1, , Pr$ Pr = CVI(Pr$) IF Pr <> &HFFFF THEN RP$ = "Repeat" + STR$(Pr) + " times." ELSE RP$ = "Repeat endlessly." END IF PRINT USING "## \ \ #,###,### ###.# ##,### \ \ \ \"; BlockCount; BType$; BL!; 0; 0; "N/A"; RP$ SEEK #1, s! + BL! CASE 7 BL! = GetBlkLen s! = SEEK(1) BType$ = "End Repeat" PRINT USING "## \ \ #,###,### ###.# ##,### \ \ \ \"; BlockCount; BType$; BL!; 0; 0; "N/A" SEEK #1, s! + BL! CASE ELSE BL! = GetBlkLen s! = SEEK(1) BType$ = "UNKNOWN:" + LTRIM$(STR$(ASC(BType$))) SR$ = SPACE$(1) GET #1, , SR$ SR! = ASC(SR$) SR! = INT(1000000! / (256 - SR!) + .5) Secs! = INT((BL! / SR!) * 10) / 10 Pk$ = SPACE$(1) GET #1, , Pk$ SELECT CASE ASC(Pk$) CASE 0 PT$ = "Raw 8-bit" CASE 1 PT$ = "4-bit" CASE 2 PT$ = "2.6 bit" CASE 3 PT$ = "2 bit" CASE ELSE PT$ = "Unknown!" END SELECT PRINT USING "## \ \ #,###,### ###.# ##,### \ \ \ \"; BlockCount; BType$; BL!; Secs!; SR!; PT$ SEEK #1, s! + BL! END SELECT IF BType$ = CHR$(0) OR KY$ = CHR$(27) THEN EXIT DO END IF LOOP END IF CLOSE #1 PRINT "" PRINT "" IF KY$ = CHR$(27) THEN EXIT DO END IF d$ = DIR$ LOOP ELSE PRINT "No files matching " + COMMAND$ PRINT "" PRINT "VOC2SDS - a utility to convert .VOC files to Sample Dump Standard MIDIEx data." PRINT "Copyright 1993 Monte Ferguson" PRINT "Vers: "; Vers; ", Last Updated:"; LastUpdate PRINT "Usage: VOC2SDS filespec" PRINT "" PRINT "filespec may contain wildcard characters, .VOC extension is assumed." PRINT "Data is written to filename.SDS. Only 8-bit RAW blocks can be converted!" PRINT "(and this version does only the 1st voice block)" END IF FUNCTION GenPath$ (FSpec$) ' Parses the path out of passed file spec (FSpec$) p = LEN(FSpec$) DO WHILE p > 0 IF INSTR("\:", MID$(FSpec$, p, 1)) > 0 THEN EXIT DO END IF p = p - 1 LOOP IF p > 0 THEN GenPath$ = LEFT$(FSpec$, p) ELSE GenPath$ = "" END IF END FUNCTION FUNCTION GenSpec$ (FSpec$, DefExt$) REM -------------------------------------------------------------------- REM Given a filespec (FSpec$) and a default extension (DefExt$) try to REM find some matching files REM REM t$ = FSpec$ ' Temp work variable REM Let's try as-is... IF LEN(DIR$(t$)) = 0 THEN ' Ok, let's add the default extention... IF RIGHT$(t$, 1) <> ":" THEN ' Keeps us from blowing up on "A:.TXT", etc t$ = t$ + "." + DefExt$ END IF IF LEN(DIR$(t$)) = 0 THEN ' Alright, let's do *.ext t$ = FSpec$ + "*." + DefExt$ IF LEN(DIR$(t$)) = 0 THEN ' Last try... add a directory slash AND *.ext t$ = FSpec$ + "\*." + DefExt$ IF LEN(DIR$(t$)) = 0 THEN ' I give up! t$ = "" END IF END IF END IF END IF GenSpec$ = t$ END FUNCTION FUNCTION GetBlkLen! a$ = SPACE$(3) GET #1, , a$ l = ASC(a$) M = ASC(MID$(a$, 2)) h = ASC(RIGHT$(a$, 1)) GetBlkLen! = h * 256! * 256! + M * 256! + l END FUNCTION FUNCTION Hx$ (Text$) h$ = "" FOR i = 1 TO LEN(Text$) a = ASC(MID$(Text$, i, 1)) d$ = HEX$(a) IF LEN(d$) < 2 THEN d$ = "0" + d$ IF LEN(h$) > 0 THEN h$ = h$ + SPACE$(1) END IF h$ = h$ + d$ NEXT i Hx$ = h$ END FUNCTION FUNCTION M3toDec! (m3$) IF LEN(m3$) <> 3 THEN STOP m1 = ASC(MID$(m3$, 1)) m2! = ASC(MID$(m3$, 2)) * 128 m3! = ASC(MID$(m3$, 3)) * 16384! M3toDec! = m1 + m2! + m3! END FUNCTION FUNCTION SngToM3$ (n!) i1 = INT(n! / 16384!) r! = n! - (i1 * 16384!) i2 = INT(r! / 128) i3 = r! - (i2 * 128) SngToM3$ = CHR$(i3) + CHR$(i2) + CHR$(i1) END FUNCTION