├── LICENSE ├── README.md ├── built ├── decode.exe ├── rdac2wav.exe ├── sd.exe └── vs.exe ├── rdac2wav ├── CMakeLists.txt └── src │ ├── decode.c │ ├── decode.h │ ├── main.c │ ├── wav.c │ └── wav.h └── vs2reaper ├── decode └── decode.go ├── go.mod ├── sd.go └── vsio └── vsio.go /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RDAC to WAV Decoding 2 | 3 | A collection of C and Go code to decode Roland's old RDAC audio encoding format used in their VS line of recorders. 4 | 5 | This is an initial upload of the code I have found on my old systems. I developed it on and off over the last 16 years. It could probably use a bit more organization :) 6 | 7 | All the important bits for decoding RDAC and unscrambling the custom FAT file format are here. 8 | 9 | Enjoy! 10 | 11 | Randy -------------------------------------------------------------------------------- /built/decode.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/randygordon/rdac/46c2f9b5b1a8cd4e074498e4fae6d3f671683dda/built/decode.exe -------------------------------------------------------------------------------- /built/rdac2wav.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/randygordon/rdac/46c2f9b5b1a8cd4e074498e4fae6d3f671683dda/built/rdac2wav.exe -------------------------------------------------------------------------------- /built/sd.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/randygordon/rdac/46c2f9b5b1a8cd4e074498e4fae6d3f671683dda/built/sd.exe -------------------------------------------------------------------------------- /built/vs.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/randygordon/rdac/46c2f9b5b1a8cd4e074498e4fae6d3f671683dda/built/vs.exe -------------------------------------------------------------------------------- /rdac2wav/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | project(RDAC C) 4 | 5 | add_executable(rdac2wav src/wav.c src/decode.c src/main.c) 6 | 7 | if(NOT WINDOWS) 8 | add_definitions(-Dstricmp=strcasecmp) 9 | endif() 10 | 11 | -------------------------------------------------------------------------------- /rdac2wav/src/decode.h: -------------------------------------------------------------------------------- 1 | /* 2 | This program is free software: you can redistribute it and/or modify 3 | it under the terms of the GNU Lesser General Public License as published by 4 | the Free Software Foundation, either version 3 of the License, or 5 | (at your option) any later version. 6 | 7 | It is distributed in the hope that it will be useful, 8 | but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | GNU Lesser General Public License for more details. 11 | 12 | You should have received a copy of the GNU Lesser General Public License 13 | along with this program. If not, see . 14 | */ 15 | 16 | void decodeMTP(int d0, unsigned char *in, int *out); 17 | 18 | void decodeMT1(int d0, unsigned char *in, int *out); 19 | 20 | void decodeMT2(int d0, unsigned char *in, int *out); 21 | 22 | void decodeM16(unsigned char *in, int *out); 23 | 24 | void decodeM24(unsigned char *in, int *out); 25 | 26 | void decodeCDR(unsigned char *in, int *out); 27 | 28 | -------------------------------------------------------------------------------- /rdac2wav/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "decode.h" 7 | #include "wav.h" 8 | 9 | /* 10 | VS to WAV file converter for Roland VS. Version 0.99 11 | 12 | Copyright 2006 LGPL by Randy Gordon (randy@integrand.com) 13 | 14 | Special thanks to Danielo for solving many of the MTP patterns. 15 | */ 16 | 17 | long fsize(const char *const name); 18 | 19 | void convertMTP(char *inFileName, char *outFileName, int sampleRate, int bitDepth); 20 | void convertVSR(char *inFileName, char *outFileName, int sampleRate, int bitDepth); 21 | void convertMT1(char *inFileName, char *outFileName, int sampleRate, int bitDepth); 22 | void convertMT2(char *inFileName, char *outFileName, int sampleRate, int bitDepth, char *clusterSize); 23 | void convertM16(char *inFileName, char *outFileName, int sampleRate, int bitDepth); 24 | void convertM24(char *inFileName, char *outFileName, int sampleRate, int bitDepth, char *clusterSize); 25 | void convertCDR(char *inFileName, char *outFileName, int sampleRate, int bitDepth); 26 | 27 | int isSupportedMode(char *mode); 28 | int isSupportedClusterSize(char *size); 29 | int isSupportedBitDepth(int bitDepth); 30 | long fsize(const char *const name); 31 | 32 | 33 | //***************************************************************************** 34 | int main(int argc, char **argv) 35 | { 36 | int i; 37 | 38 | char *inFileName = NULL; 39 | char *outFileName = NULL; 40 | 41 | char *rdacMode = "mtp"; 42 | char *clusterSize = "32k"; 43 | int sampleRate = 44100; 44 | int bitDepth = 0; 45 | 46 | // Very basic argument processing 47 | i = 1; 48 | while (i= argc) break; 52 | bitDepth = atoi(argv[i]); 53 | } 54 | else if (!strcmp(argv[i], "-r")) { 55 | if (++i >= argc) break; 56 | sampleRate = atoi(argv[i]); 57 | } 58 | else if (!strcmp(argv[i], "-m")) { 59 | if (++i >= argc) break; 60 | rdacMode = argv[i]; 61 | } 62 | else if (!strcmp(argv[i], "-c")) { 63 | if (++i >= argc) break; 64 | clusterSize = argv[i]; 65 | } 66 | else { 67 | // Process file name 68 | if (inFileName == NULL) inFileName = argv[i]; 69 | else outFileName = argv[i]; 70 | } 71 | i++; 72 | } 73 | 74 | // Set default bit-depth if not specified. 75 | if (bitDepth == 0) { 76 | if (stricmp(rdacMode, "mtp") == 0) bitDepth = 24; 77 | else if (stricmp(rdacMode, "vsr") == 0) bitDepth = 24; 78 | else if (stricmp(rdacMode, "m24") == 0) bitDepth = 24; 79 | else bitDepth = 16; 80 | } 81 | 82 | // Show usage if arguments are invalid 83 | if (inFileName == NULL || strlen(inFileName) == 0 || 84 | outFileName == NULL || strlen(outFileName) == 0 || 85 | !isSupportedBitDepth(bitDepth) || 86 | !isSupportedMode(rdacMode) || 87 | !isSupportedClusterSize(clusterSize)) { 88 | 89 | printf("\n"); 90 | printf("**********************************************************\n"); 91 | printf("* *\n"); 92 | printf("* RDAC to Wav File Converter (Version 0.99) *\n"); 93 | printf("* *\n"); 94 | printf("* Copyright Randy Gordon (randy@integrand.com) *\n"); 95 | printf("* *\n"); 96 | printf("**********************************************************\n"); 97 | printf("\n"); 98 | printf("Usage: rdac2wav [options] \n\n"); 99 | printf("\n"); 100 | printf("where options include:\n"); 101 | printf("\n"); 102 | printf(" -m : MTP,MT1,MT2,VSR,\n"); 103 | printf(" M16,M24,CDR (default: MTP)\n"); 104 | printf(" -r : select the sample rate (default: 44100)\n"); 105 | printf(" -d : select 24 or 16-bit WAV (default: mode dependent)\n"); 106 | printf(" -c : select 32K or 64K (default: 32K)\n"); 107 | printf("\n"); 108 | printf("eg.: rdac2wav take01df.vr6 guitar.wav\n"); 109 | printf("\n"); 110 | exit(0); 111 | } 112 | 113 | if (stricmp(rdacMode, "mtp") == 0) convertMTP(inFileName, outFileName, sampleRate, bitDepth); 114 | else if (stricmp(rdacMode, "vsr") == 0) convertVSR(inFileName, outFileName, sampleRate, bitDepth); 115 | else if (stricmp(rdacMode, "mt1") == 0) convertMT1(inFileName, outFileName, sampleRate, bitDepth); 116 | else if (stricmp(rdacMode, "mt2") == 0) convertMT2(inFileName, outFileName, sampleRate, bitDepth, clusterSize); 117 | else if (stricmp(rdacMode, "m16") == 0) convertM16(inFileName, outFileName, sampleRate, bitDepth); 118 | else if (stricmp(rdacMode, "m24") == 0) convertM24(inFileName, outFileName, sampleRate, bitDepth, clusterSize); 119 | else if (stricmp(rdacMode, "cdr") == 0) convertCDR(inFileName, outFileName, sampleRate, bitDepth); 120 | } 121 | //***************************************************************************** 122 | void convertMTP(char *inFileName, char *outFileName, int sampleRate, int bitDepth) 123 | { 124 | int i; 125 | 126 | FILE *fin = fopen(inFileName, "rb"); 127 | FILE *fout = fopen(outFileName, "wb"); 128 | 129 | if (fin == NULL) { 130 | printf("Unable to open input file: %s.\n", inFileName); 131 | exit(1); 132 | } 133 | if (fout == NULL) { 134 | printf("Unable to open output file: %s.\n", outFileName); 135 | exit(1); 136 | } 137 | 138 | int fileSize = fsize(inFileName); 139 | int numBlocks = fileSize/16; 140 | 141 | // Write the WAV header 142 | int numSamples = numBlocks*16; 143 | writeWavHeader(fout, numSamples, sampleRate, bitDepth, 1); 144 | 145 | unsigned char in[16]; // Input MTP RDAC block 146 | int out[16]; // Output 24-bit samples 147 | 148 | int d0 = 0; 149 | 150 | if (bitDepth == 24) { 151 | for (i=0; i= 65536) out[j] -= 65536; 206 | else if (out[j] <= -65536) out[j] += 65536; 207 | else out[j] = 0; 208 | } 209 | writeWavSamples24(fout, out); 210 | } 211 | } 212 | else if (bitDepth == 16) { 213 | for (i=0; i= 65536) out[j] -= 65536; 220 | else if (out[j] <= -65536) out[j] += 65536; 221 | else out[j] = 0; 222 | } 223 | writeWavSamples24as16(fout, out); 224 | } 225 | } 226 | fclose(fin); 227 | fclose(fout); 228 | } 229 | //***************************************************************************** 230 | void convertMT1(char *inFileName, char *outFileName, int sampleRate, int bitDepth) 231 | { 232 | int i; 233 | 234 | FILE *fin = fopen(inFileName, "rb"); 235 | FILE *fout = fopen(outFileName, "wb"); 236 | 237 | if (fin == NULL) { 238 | printf("Unable to open input file: %s.\n", inFileName); 239 | exit(1); 240 | } 241 | if (fout == NULL) { 242 | printf("Unable to open output file: %s.\n", outFileName); 243 | exit(1); 244 | } 245 | 246 | int fileSize = fsize(inFileName); 247 | int numBlocks = fileSize/16; 248 | 249 | // Write the WAV header 250 | int numSamples = numBlocks*16; 251 | writeWavHeader(fout, numSamples, sampleRate, bitDepth, 1); 252 | 253 | unsigned char in[16]; // Input MT1 RDAC block 254 | int out[16]; // Output 16-bit samples 255 | 256 | int d0 = 0; 257 | 258 | if (bitDepth == 24) { 259 | for (i=0; i 2 | 3 | /* 4 | VS to WAV file converter for Roland VS. Version 0.99 5 | 6 | Copyright 2006 LGPL by Randy Gordon (randy@integrand.com) 7 | 8 | Special thanks to Danielo for solving many of the MTP patterns. 9 | */ 10 | 11 | #define BYTE_0(xx) (xx&0xff) 12 | #define BYTE_1(xx) ((xx>>8)&0xff) 13 | #define BYTE_2(xx) ((xx>>16)&0xff) 14 | #define BYTE_3(xx) ((xx>>24)&0xff) 15 | 16 | //***************************************************************************** 17 | void writeWavHeader(FILE *fout, int numSamples, int sampleRate, int bitDepth, int numChannels) 18 | { 19 | // Write the WAV header (mono) 20 | int bytesPerSample = bitDepth/8; 21 | int numBytes = numSamples*bytesPerSample*numChannels; 22 | 23 | int numChunkBytes = numBytes + 38; 24 | 25 | unsigned char chunkid[] = {0x52,0x49,0x46,0x46}; 26 | fwrite(chunkid, 1, 4, fout); 27 | 28 | unsigned char chunksize[] = { 29 | BYTE_0(numChunkBytes), 30 | BYTE_1(numChunkBytes), 31 | BYTE_2(numChunkBytes), 32 | BYTE_3(numChunkBytes) 33 | }; 34 | fwrite(chunksize, 1, 4, fout); 35 | 36 | unsigned char format[] = {0x57,0x41,0x56,0x45}; 37 | fwrite(format, 1, 4, fout); 38 | 39 | unsigned char subchunk1id[] = {0x66,0x6d,0x74,0x20}; 40 | fwrite(subchunk1id, 1, 4, fout); 41 | 42 | unsigned char subchunk1size[] = {0x12,0x00,0x00,0x00}; 43 | fwrite(subchunk1size, 1, 4, fout); 44 | 45 | unsigned char audioformat[] = {0x01,0x00}; 46 | fwrite(audioformat, 1, 2, fout); 47 | 48 | unsigned char numchannels[] = { 49 | BYTE_0(numChannels), 50 | BYTE_1(numChannels) 51 | }; 52 | fwrite(numchannels, 1, 2, fout); 53 | 54 | unsigned char samplerate[] = { 55 | BYTE_0(sampleRate), 56 | BYTE_1(sampleRate), 57 | BYTE_2(sampleRate), 58 | BYTE_3(sampleRate) 59 | }; 60 | fwrite(samplerate, 1, 4, fout); 61 | 62 | unsigned char byterate[] = { 63 | BYTE_0(sampleRate*bytesPerSample*numChannels), 64 | BYTE_1(sampleRate*bytesPerSample*numChannels), 65 | BYTE_2(sampleRate*bytesPerSample*numChannels), 66 | BYTE_3(sampleRate*bytesPerSample*numChannels) 67 | }; 68 | fwrite(byterate, 1, 4, fout); 69 | 70 | unsigned char blockalign[] = { 71 | BYTE_0(bytesPerSample*numChannels), 72 | BYTE_1(bytesPerSample*numChannels) 73 | }; 74 | fwrite(blockalign, 1, 2, fout); 75 | 76 | unsigned char bitspersample[] = { 77 | BYTE_0(bitDepth), 78 | BYTE_1(bitDepth) 79 | }; 80 | fwrite(bitspersample, 1, 2, fout); 81 | 82 | unsigned char extraparamsize[] = {0x00,0x00}; 83 | fwrite(extraparamsize, 1, 2, fout); 84 | 85 | unsigned char subchunk2id[] = {0x64,0x61,0x74,0x61}; 86 | fwrite(subchunk2id, 1, 4, fout); 87 | 88 | unsigned char subchunk2size[] = { 89 | BYTE_0(numBytes), 90 | BYTE_1(numBytes), 91 | BYTE_2(numBytes), 92 | BYTE_3(numBytes) 93 | }; 94 | fwrite(subchunk2size, 1, 4, fout); 95 | } 96 | //***************************************************************************** 97 | void writeWavSamples24(FILE *fout, int samples[]) 98 | { 99 | // Write it to the WAV (little-endian) 100 | int i; 101 | for (i=0; i<16; i++) { 102 | fputc(BYTE_0(samples[i]), fout); 103 | fputc(BYTE_1(samples[i]), fout); 104 | fputc(BYTE_2(samples[i]), fout); 105 | } 106 | } 107 | //***************************************************************************** 108 | void writeWavSamples24as16(FILE *fout, int samples[]) 109 | { 110 | // Write it to the WAV (little-endian) 111 | // To convert the 24-bit to 16-bit, byte 0 is tossed. 112 | int i; 113 | for (i=0; i<16; i++) { 114 | fputc(BYTE_1(samples[i]), fout); 115 | fputc(BYTE_2(samples[i]), fout); 116 | } 117 | } 118 | //***************************************************************************** 119 | void writeWavSamples16(FILE *fout, int samples[]) 120 | { 121 | // Write it to the WAV (little-endian) 122 | int i; 123 | for (i=0; i<16; i++) { 124 | fputc(BYTE_0(samples[i]), fout); 125 | fputc(BYTE_1(samples[i]), fout); 126 | } 127 | } 128 | //***************************************************************************** 129 | void writeWavSamples16as24(FILE *fout, int samples[]) 130 | { 131 | // Write it to the WAV (little-endian) 132 | // To convert the 16-bit to 24-bit, set zero as low-order byte. 133 | int i; 134 | for (i=0; i<16; i++) { 135 | fputc(0, fout); 136 | fputc(BYTE_0(samples[i]), fout); 137 | fputc(BYTE_1(samples[i]), fout); 138 | } 139 | } 140 | //***************************************************************************** 141 | void writeWavSamples16Stereo(FILE *fout, int samples[]) 142 | { 143 | // Write it to the WAV (little-endian) 144 | int i; 145 | for (i=0; i<16; i++) { 146 | fputc(BYTE_0(samples[i*2]), fout); 147 | fputc(BYTE_1(samples[i*2]), fout); 148 | fputc(BYTE_0(samples[i*2+1]), fout); 149 | fputc(BYTE_1(samples[i*2+1]), fout); 150 | } 151 | } 152 | //***************************************************************************** 153 | void writeWavSamples16as24Stereo(FILE *fout, int samples[]) 154 | { 155 | // Write it to the WAV (little-endian) 156 | // To convert the 16-bit to 24-bit, set zero as low-order byte. 157 | int i; 158 | for (i=0; i<16; i++) { 159 | fputc(0, fout); 160 | fputc(BYTE_0(samples[i*2]), fout); 161 | fputc(BYTE_1(samples[i*2]), fout); 162 | fputc(0, fout); 163 | fputc(BYTE_0(samples[i*2+1]), fout); 164 | fputc(BYTE_1(samples[i*2+1]), fout); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /rdac2wav/src/wav.h: -------------------------------------------------------------------------------- 1 | /* 2 | This program is free software: you can redistribute it and/or modify 3 | it under the terms of the GNU Lesser General Public License as published by 4 | the Free Software Foundation, either version 3 of the License, or 5 | (at your option) any later version. 6 | 7 | It is distributed in the hope that it will be useful, 8 | but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | GNU Lesser General Public License for more details. 11 | 12 | You should have received a copy of the GNU Lesser General Public License 13 | along with this program. If not, see . 14 | */ 15 | 16 | void writeWavHeader(FILE *fout, int numSamples, int sampleRate, int bitDepth, int numChannels); 17 | 18 | void writeWavSamples24(FILE *fout, int samples[]); 19 | 20 | void writeWavSamples24as16(FILE *fout, int samples[]); 21 | 22 | void writeWavSamples16(FILE *fout, int samples[]); 23 | 24 | void writeWavSamples16as24(FILE *fout, int samples[]); 25 | 26 | void writeWavSamples16Stereo(FILE *fout, int samples[]); 27 | 28 | void writeWavSamples16as24Stereo(FILE *fout, int samples[]); 29 | 30 | -------------------------------------------------------------------------------- /vs2reaper/decode/decode.go: -------------------------------------------------------------------------------- 1 | package decode 2 | 3 | import ( 4 | "bufio" 5 | "encoding/binary" 6 | "fmt" 7 | "os" 8 | "strings" 9 | ) 10 | 11 | var patterns = [256]int{ 12 | 0, /* 00..00.. */ 13 | 0, /* 00..00.. */ 14 | 0, /* 00..00.. */ 15 | 0, /* 00..00.. */ 16 | 1, /* 00..01.. */ 17 | 1, /* 00..01.. */ 18 | 1, /* 00..01.. */ 19 | 1, /* 00..01.. */ 20 | 2, /* 00..10.. */ 21 | 2, /* 00..10.. */ 22 | 2, /* 00..10.. */ 23 | 2, /* 00..10.. */ 24 | 3, /* 00..11.. */ 25 | 3, /* 00..11.. */ 26 | 3, /* 00..11.. */ 27 | 3, /* 00..11.. */ 28 | 0, /* 00..00.. */ 29 | 0, /* 00..00.. */ 30 | 0, /* 00..00.. */ 31 | 0, /* 00..00.. */ 32 | 1, /* 00..01.. */ 33 | 1, /* 00..01.. */ 34 | 1, /* 00..01.. */ 35 | 1, /* 00..01.. */ 36 | 2, /* 00..10.. */ 37 | 2, /* 00..10.. */ 38 | 2, /* 00..10.. */ 39 | 2, /* 00..10.. */ 40 | 3, /* 00..11.. */ 41 | 3, /* 00..11.. */ 42 | 3, /* 00..11.. */ 43 | 3, /* 00..11.. */ 44 | 0, /* 00..00.. */ 45 | 0, /* 00..00.. */ 46 | 0, /* 00..00.. */ 47 | 0, /* 00..00.. */ 48 | 1, /* 00..01.. */ 49 | 1, /* 00..01.. */ 50 | 1, /* 00..01.. */ 51 | 1, /* 00..01.. */ 52 | 2, /* 00..10.. */ 53 | 2, /* 00..10.. */ 54 | 2, /* 00..10.. */ 55 | 2, /* 00..10.. */ 56 | 3, /* 00..11.. */ 57 | 3, /* 00..11.. */ 58 | 3, /* 00..11.. */ 59 | 3, /* 00..11.. */ 60 | 0, /* 00..00.. */ 61 | 0, /* 00..00.. */ 62 | 0, /* 00..00.. */ 63 | 0, /* 00..00.. */ 64 | 1, /* 00..01.. */ 65 | 1, /* 00..01.. */ 66 | 1, /* 00..01.. */ 67 | 1, /* 00..01.. */ 68 | 2, /* 00..10.. */ 69 | 2, /* 00..10.. */ 70 | 2, /* 00..10.. */ 71 | 2, /* 00..10.. */ 72 | 3, /* 00..11.. */ 73 | 3, /* 00..11.. */ 74 | 3, /* 00..11.. */ 75 | 3, /* 00..11.. */ 76 | 4, /* 01..00.. */ 77 | 4, /* 01..00.. */ 78 | 4, /* 01..00.. */ 79 | 4, /* 01..00.. */ 80 | 5, /* 01..01.. */ 81 | 5, /* 01..01.. */ 82 | 5, /* 01..01.. */ 83 | 5, /* 01..01.. */ 84 | 6, /* 01..10.. */ 85 | 6, /* 01..10.. */ 86 | 6, /* 01..10.. */ 87 | 6, /* 01..10.. */ 88 | 7, /* 01..11.. */ 89 | 7, /* 01..11.. */ 90 | 7, /* 01..11.. */ 91 | 7, /* 01..11.. */ 92 | 4, /* 01..00.. */ 93 | 4, /* 01..00.. */ 94 | 4, /* 01..00.. */ 95 | 4, /* 01..00.. */ 96 | 5, /* 01..01.. */ 97 | 5, /* 01..01.. */ 98 | 5, /* 01..01.. */ 99 | 5, /* 01..01.. */ 100 | 6, /* 01..10.. */ 101 | 6, /* 01..10.. */ 102 | 6, /* 01..10.. */ 103 | 6, /* 01..10.. */ 104 | 7, /* 01..11.. */ 105 | 7, /* 01..11.. */ 106 | 7, /* 01..11.. */ 107 | 7, /* 01..11.. */ 108 | 4, /* 01..00.. */ 109 | 4, /* 01..00.. */ 110 | 4, /* 01..00.. */ 111 | 4, /* 01..00.. */ 112 | 5, /* 01..01.. */ 113 | 5, /* 01..01.. */ 114 | 5, /* 01..01.. */ 115 | 5, /* 01..01.. */ 116 | 6, /* 01..10.. */ 117 | 6, /* 01..10.. */ 118 | 6, /* 01..10.. */ 119 | 6, /* 01..10.. */ 120 | 7, /* 01..11.. */ 121 | 7, /* 01..11.. */ 122 | 7, /* 01..11.. */ 123 | 7, /* 01..11.. */ 124 | 4, /* 01..00.. */ 125 | 4, /* 01..00.. */ 126 | 4, /* 01..00.. */ 127 | 4, /* 01..00.. */ 128 | 5, /* 01..01.. */ 129 | 5, /* 01..01.. */ 130 | 5, /* 01..01.. */ 131 | 5, /* 01..01.. */ 132 | 6, /* 01..10.. */ 133 | 6, /* 01..10.. */ 134 | 6, /* 01..10.. */ 135 | 6, /* 01..10.. */ 136 | 7, /* 01..11.. */ 137 | 7, /* 01..11.. */ 138 | 7, /* 01..11.. */ 139 | 7, /* 01..11.. */ 140 | 8, /* 10..00.. */ 141 | 8, /* 10..00.. */ 142 | 8, /* 10..00.. */ 143 | 8, /* 10..00.. */ 144 | 9, /* 10..01.. */ 145 | 9, /* 10..01.. */ 146 | 9, /* 10..01.. */ 147 | 9, /* 10..01.. */ 148 | 10, /* 10..10.. */ 149 | 10, /* 10..10.. */ 150 | 10, /* 10..10.. */ 151 | 10, /* 10..10.. */ 152 | 11, /* 10..11.. */ 153 | 11, /* 10..11.. */ 154 | 11, /* 10..11.. */ 155 | 11, /* 10..11.. */ 156 | 8, /* 10..00.. */ 157 | 8, /* 10..00.. */ 158 | 8, /* 10..00.. */ 159 | 8, /* 10..00.. */ 160 | 9, /* 10..01.. */ 161 | 9, /* 10..01.. */ 162 | 9, /* 10..01.. */ 163 | 9, /* 10..01.. */ 164 | 10, /* 10..10.. */ 165 | 10, /* 10..10.. */ 166 | 10, /* 10..10.. */ 167 | 10, /* 10..10.. */ 168 | 11, /* 10..11.. */ 169 | 11, /* 10..11.. */ 170 | 11, /* 10..11.. */ 171 | 11, /* 10..11.. */ 172 | 8, /* 10..00.. */ 173 | 8, /* 10..00.. */ 174 | 8, /* 10..00.. */ 175 | 8, /* 10..00.. */ 176 | 9, /* 10..01.. */ 177 | 9, /* 10..01.. */ 178 | 9, /* 10..01.. */ 179 | 9, /* 10..01.. */ 180 | 10, /* 10..10.. */ 181 | 10, /* 10..10.. */ 182 | 10, /* 10..10.. */ 183 | 10, /* 10..10.. */ 184 | 11, /* 10..11.. */ 185 | 11, /* 10..11.. */ 186 | 11, /* 10..11.. */ 187 | 11, /* 10..11.. */ 188 | 8, /* 10..00.. */ 189 | 8, /* 10..00.. */ 190 | 8, /* 10..00.. */ 191 | 8, /* 10..00.. */ 192 | 9, /* 10..01.. */ 193 | 9, /* 10..01.. */ 194 | 9, /* 10..01.. */ 195 | 9, /* 10..01.. */ 196 | 10, /* 10..10.. */ 197 | 10, /* 10..10.. */ 198 | 10, /* 10..10.. */ 199 | 10, /* 10..10.. */ 200 | 11, /* 10..11.. */ 201 | 11, /* 10..11.. */ 202 | 11, /* 10..11.. */ 203 | 11, /* 10..11.. */ 204 | 12, /* 110.000. */ 205 | 12, /* 110.000. */ 206 | 13, /* 110.001. */ 207 | 13, /* 110.001. */ 208 | 14, /* 110.010. */ 209 | 14, /* 110.010. */ 210 | 15, /* 110.011. */ 211 | 15, /* 110.011. */ 212 | 16, /* 110.100. */ 213 | 16, /* 110.100. */ 214 | 17, /* 110.101. */ 215 | 17, /* 110.101. */ 216 | 18, /* 110.110. */ 217 | 18, /* 110.110. */ 218 | 19, /* 110.111. */ 219 | 19, /* 110.111. */ 220 | 12, /* 110.000. */ 221 | 12, /* 110.000. */ 222 | 13, /* 110.001. */ 223 | 13, /* 110.001. */ 224 | 14, /* 110.010. */ 225 | 14, /* 110.010. */ 226 | 15, /* 110.011. */ 227 | 15, /* 110.011. */ 228 | 16, /* 110.100. */ 229 | 16, /* 110.100. */ 230 | 17, /* 110.101. */ 231 | 17, /* 110.101. */ 232 | 18, /* 110.110. */ 233 | 18, /* 110.110. */ 234 | 19, /* 110.111. */ 235 | 19, /* 110.111. */ 236 | 20, /* 111.000. */ 237 | 20, /* 111.000. */ 238 | 21, /* 111.001. */ 239 | 21, /* 111.001. */ 240 | 22, /* 111.010. */ 241 | 22, /* 111.010. */ 242 | 23, /* 111.011. */ 243 | 23, /* 111.011. */ 244 | 24, /* 111.100. */ 245 | 24, /* 111.100. */ 246 | 25, /* 11101010 */ 247 | 26, /* 11101011 */ 248 | 27, /* 11101100 */ 249 | 28, /* 11101101 */ 250 | 29, /* 11101110 */ 251 | 30, /* 11101111 */ 252 | 20, /* 111.000. */ 253 | 20, /* 111.000. */ 254 | 21, /* 111.001. */ 255 | 21, /* 111.001. */ 256 | 22, /* 111.010. */ 257 | 22, /* 111.010. */ 258 | 23, /* 111.011. */ 259 | 23, /* 111.011. */ 260 | 24, /* 111.100. */ 261 | 24, /* 111.100. */ 262 | 31, /* 11111010 */ 263 | 32, /* 11111011 */ 264 | 33, /* 11111100 */ 265 | 34, /* 11111101 */ 266 | 35, /* 11111110 */ 267 | 36, /* 11111111 */ 268 | } 269 | 270 | var symbolIndex = map[byte]int{ 271 | 'p': -1, 272 | '1': 0, 273 | '2': 1, 274 | '3': 2, 275 | '4': 3, 276 | '5': 4, 277 | '6': 5, 278 | '7': 6, 279 | '8': 7, 280 | '9': 8, 281 | 'a': 9, 282 | 'b': 10, 283 | 'c': 11, 284 | 'd': 12, 285 | 'e': 13, 286 | 'f': 14, 287 | 'g': 15, 288 | } 289 | 290 | var decoratedPatternA = "ppp88888 88888888 pppggggg gggggggg 87777776 66666655 gffffffe eeeeeedd 55554444 44444333 ddddcccc cccccbbb 33322222 22111111 bbbaaaaa aa999999" 291 | var decoratedPatternB = "pp888888 88888887 ppgggggg gggggggf 77777666 66666555 fffffeee eeeeeddd 55544444 44443333 dddccccc ccccbbbb 33222222 22111111 bbaaaaaa aa999999" 292 | var decoratedPatternB3 = "ppp88888 88888887 pppggggg gggggggf 77777666 66666555 fffffeee eeeeeddd 55544444 44443333 dddccccc ccccbbbb 33222222 22111111 bbaaaaaa aa999999" 293 | var decoratedPatternB4 = "pppp8888 88888887 ppppgggg gggggggf 77777766 66666555 ffffffee eeeeeddd 55554444 44433333 ddddcccc cccbbbbb 33222222 21111111 bbaaaaaa a9999999" 294 | var decoratedPatternC = "ppp88888 88888877 pppggggg ggggggff 77776666 66665555 ffffeeee eeeedddd 55444444 44443333 ddcccccc ccccbbbb 33222222 22111111 bbaaaaaa aa999999" 295 | var decoratedPatternD = "pp888888 88877777 ppgggggg gggfffff 77666666 66555555 ffeeeeee eedddddd 54444444 44333333 dccccccc ccbbbbbb 32222222 21111111 baaaaaaa a9999999" 296 | var decoratedPatternE = "pppp8888 88888877 ppppgggg ggggggff 77776666 66665555 ffffeeee eeeedddd 55444444 44443333 ddcccccc ccccbbbb 33222222 22111111 bbaaaaaa aa999999" 297 | var decoratedPatternF = "pppp8888 88887777 ppppgggg ggggffff 77766666 66655555 fffeeeee eeeddddd 55444444 44333333 ddcccccc ccbbbbbb 32222222 21111111 baaaaaaa a9999999" 298 | 299 | var decoratedPattern12A = "pp888888 88888777 ppgggggg gggggfff 76666665 55544444 feeeeeed dddccccc 44333322 22221111 ccbbbbaa aaaa9999" 300 | var decoratedPattern12B = "pp888888 87777766 ppgggggg gfffffee 66665555 54444444 eeeedddd dccccccc 33333222 22211111 bbbbbaaa aaa99999" 301 | var decoratedPattern12C = "pppp8888 88777776 ppppgggg ggfffffe 66666555 55444444 eeeeeddd ddcccccc 33333222 22211111 bbbbbaaa aaa99999" 302 | var decoratedPattern12D = "pppp8888 88887777 ppppgggg ggggffff 66666655 55444444 eeeeeedd ddcccccc 44333322 22221111 ccbbbbaa aaaa9999" 303 | var decoratedPattern12E = "ppp88888 88887777 pppggggg ggggffff 66666655 55444444 eeeeeedd ddcccccc 44333322 22221111 ccbbbbaa aaaa9999" 304 | var decoratedPattern12F = "ppp88888 88888887 pppggggg gggggggf 77766666 55554444 fffeeeee ddddcccc 44433332 22221111 cccbbbba aaaa9999" 305 | var decoratedPattern12G = "ppp88888 88888777 pppggggg gggggfff 76666665 55544444 feeeeeed dddccccc 44333322 22221111 ccbbbbaa aaaa9999" 306 | 307 | var patternA = stripSpaces(decoratedPatternA) 308 | var patternB = stripSpaces(decoratedPatternB) 309 | var patternB3 = stripSpaces(decoratedPatternB3) 310 | var patternB4 = stripSpaces(decoratedPatternB4) 311 | var patternC = stripSpaces(decoratedPatternC) 312 | var patternD = stripSpaces(decoratedPatternD) 313 | var patternE = stripSpaces(decoratedPatternE) 314 | var patternF = stripSpaces(decoratedPatternF) 315 | 316 | var pattern12A = stripSpaces(decoratedPattern12A) 317 | var pattern12B = stripSpaces(decoratedPattern12B) 318 | var pattern12C = stripSpaces(decoratedPattern12C) 319 | var pattern12D = stripSpaces(decoratedPattern12D) 320 | var pattern12E = stripSpaces(decoratedPattern12E) 321 | var pattern12F = stripSpaces(decoratedPattern12F) 322 | var pattern12G = stripSpaces(decoratedPattern12G) 323 | 324 | //***************************************************************************** 325 | func main() { 326 | /* 327 | dir, err := os.Getwd() 328 | if err != nil { 329 | log.Fatal(err) 330 | } 331 | fmt.Println("directory = %s\n", dir) 332 | */ 333 | banner() 334 | 335 | songDir := "C:/testout/Partition0/SONG0000.VR5" 336 | reaperDir := "C:/testout/reaper" 337 | 338 | ext := songDir[len(songDir)-3:] 339 | 340 | format := getFormatForExtension(ext) 341 | 342 | fmt.Printf("System: %s\n", format) 343 | 344 | switch format { 345 | //case "VS-1680": 346 | // convertVS1680ToReaper(songDir, reaperDir) 347 | case "VS-1880": 348 | convertVS1880ToReaper(songDir, reaperDir) 349 | //case "VS-2480": 350 | // convertVS2480ToReaper(songDir, reaperDir) 351 | default: 352 | fmt.Printf("Unsupported format: %s\n", format) 353 | } 354 | 355 | fmt.Printf("DONE\n") 356 | } 357 | 358 | //***************************************************************************** 359 | func banner() { 360 | fmt.Printf("\n") 361 | fmt.Printf("**********************************************************\n") 362 | fmt.Printf("* *\n") 363 | fmt.Printf("* Roland VS to Reaper Converter (Version 1.0) *\n") 364 | fmt.Printf("* *\n") 365 | fmt.Printf("* Copyright 2017 Randy Gordon (randy@integrand.com) *\n") 366 | fmt.Printf("* *\n") 367 | fmt.Printf("**********************************************************\n") 368 | fmt.Printf("\n") 369 | } 370 | 371 | //***************************************************************************** 372 | func convertMTP(inFileName string, outFileName string, sampleRate int, bitDepth int) { 373 | 374 | // Open input file 375 | fin, err := os.Open(inFileName) 376 | check(err) 377 | defer fin.Close() 378 | finInfo, err := fin.Stat() 379 | check(err) 380 | 381 | // Open output file 382 | fout, err := os.Create(outFileName) 383 | check(err) 384 | defer fout.Close() 385 | 386 | fileSize := finInfo.Size() 387 | numBlocks := int(fileSize / 16) 388 | 389 | // Write the WAV header 390 | numSamples := numBlocks * 16 391 | writeWavHeader(fout, numSamples, sampleRate, bitDepth, 1) 392 | 393 | in := make([]uint8, 16) 394 | out := make([]int, 16) 395 | d0 := 0 396 | 397 | r := bufio.NewReader(fin) 398 | 399 | for i := 0; i < numBlocks; i++ { 400 | _, err := r.Read(in) 401 | check(err) 402 | DecodeMTP(d0, in, out) 403 | writeWavSamples24(fout, out) 404 | d0 = out[15] 405 | 406 | if i%50000 == 0 { 407 | fmt.Printf(".") 408 | } 409 | } 410 | } 411 | 412 | //***************************************************************************** 413 | func writeWavHeader(fout *os.File, numSamples int, sampleRate int, bitDepth int, numChannels int) { 414 | 415 | w := bufio.NewWriter(fout) 416 | 417 | bytesPerSample := bitDepth / 8 418 | numBytes := numSamples * bytesPerSample * numChannels 419 | numChunkBytes := numBytes + 38 420 | 421 | chunkid := []byte{0x52, 0x49, 0x46, 0x46} 422 | 423 | w.Write(chunkid) 424 | 425 | chunksize := []byte{ 426 | BYTE_0(numChunkBytes), 427 | BYTE_1(numChunkBytes), 428 | BYTE_2(numChunkBytes), 429 | BYTE_3(numChunkBytes), 430 | } 431 | 432 | w.Write(chunksize) 433 | 434 | format := []byte{0x57, 0x41, 0x56, 0x45} 435 | w.Write(format) 436 | 437 | subchunk1id := []byte{0x66, 0x6d, 0x74, 0x20} 438 | w.Write(subchunk1id) 439 | 440 | subchunk1size := []byte{0x12, 0x00, 0x00, 0x00} 441 | w.Write(subchunk1size) 442 | 443 | audioformat := []byte{0x01, 0x00} 444 | w.Write(audioformat) 445 | 446 | numchannels := []byte{ 447 | BYTE_0(numChannels), 448 | BYTE_1(numChannels), 449 | } 450 | w.Write(numchannels) 451 | 452 | samplerate := []byte{ 453 | BYTE_0(sampleRate), 454 | BYTE_1(sampleRate), 455 | BYTE_2(sampleRate), 456 | BYTE_3(sampleRate), 457 | } 458 | w.Write(samplerate) 459 | 460 | byterate := []byte{ 461 | BYTE_0(sampleRate * bytesPerSample * numChannels), 462 | BYTE_1(sampleRate * bytesPerSample * numChannels), 463 | BYTE_2(sampleRate * bytesPerSample * numChannels), 464 | BYTE_3(sampleRate * bytesPerSample * numChannels), 465 | } 466 | w.Write(byterate) 467 | 468 | blockalign := []byte{ 469 | BYTE_0(bytesPerSample * numChannels), 470 | BYTE_1(bytesPerSample * numChannels), 471 | } 472 | w.Write(blockalign) 473 | 474 | bitspersample := []byte{ 475 | BYTE_0(bitDepth), 476 | BYTE_1(bitDepth), 477 | } 478 | w.Write(bitspersample) 479 | 480 | extraparamsize := []byte{0x00, 0x00} 481 | w.Write(extraparamsize) 482 | 483 | subchunk2id := []byte{0x64, 0x61, 0x74, 0x61} 484 | w.Write(subchunk2id) 485 | 486 | subchunk2size := []byte{ 487 | BYTE_0(numBytes), 488 | BYTE_1(numBytes), 489 | BYTE_2(numBytes), 490 | BYTE_3(numBytes), 491 | } 492 | w.Write(subchunk2size) 493 | 494 | w.Flush() 495 | } 496 | 497 | //***************************************************************************** 498 | func writeWavSamples24(fout *os.File, samples []int) { 499 | // Write it to the WAV (little-endian) 500 | w := bufio.NewWriter(fout) 501 | for i := 0; i < 16; i++ { 502 | sample := []byte{ 503 | BYTE_0(samples[i]), 504 | BYTE_1(samples[i]), 505 | BYTE_2(samples[i]), 506 | } 507 | w.Write(sample) 508 | } 509 | w.Flush() 510 | } 511 | 512 | //***************************************************************************** 513 | func DecodeMTP(d0 int, in []uint8, out []int) { 514 | 515 | // Decodes a 16-byte MTP RDAC block into 24-bit samples. 516 | 517 | patternIndex := (in[0] & 0xf0) | ((in[2] & 0xf0) >> 4) 518 | 519 | pattern := patterns[patternIndex] 520 | 521 | for i := 0; i < 16; i++ { 522 | out[i] = 0 523 | } 524 | //fmt.Printf("pattern = %d\n", pattern) 525 | switch pattern { 526 | 527 | // Pattern B 528 | 529 | case 0: 530 | patternStr := patternB 531 | applyPattern(in, out, patternStr) 532 | SHIFT_ROUND(out, 6) 533 | // 2 linear 534 | INTERPOLATE_2(d0, out) 535 | case 1: 536 | patternStr := patternB 537 | applyPattern(in, out, patternStr) 538 | SHIFT_ROUND(out, 7) 539 | // 2 linear 540 | INTERPOLATE_2(d0, out) 541 | case 2: 542 | patternStr := patternB 543 | applyPattern(in, out, patternStr) 544 | SHIFT_ROUND(out, 8) 545 | // 2 linear 546 | INTERPOLATE_2(d0, out) 547 | case 3: 548 | patternStr := patternB 549 | applyPattern(in, out, patternStr) 550 | SHIFT_ROUND(out, 9) 551 | // 2 linear 552 | INTERPOLATE_2(d0, out) 553 | case 4: 554 | patternStr := patternB 555 | applyPattern(in, out, patternStr) 556 | SHIFT_ROUND(out, 10) 557 | // 2 linear 558 | INTERPOLATE_2(d0, out) 559 | case 5: 560 | patternStr := patternB 561 | applyPattern(in, out, patternStr) 562 | SHIFT_ROUND(out, 11) 563 | // 2 linear 564 | INTERPOLATE_2(d0, out) 565 | 566 | // Pattern D 567 | 568 | case 6: 569 | patternStr := patternD 570 | applyPattern(in, out, patternStr) 571 | SHIFT_ROUND(out, 10) 572 | // 4 linear 573 | INTERPOLATE_4(d0, out) 574 | case 7: 575 | patternStr := patternD 576 | applyPattern(in, out, patternStr) 577 | SHIFT_ROUND(out, 11) 578 | // 4 linear 579 | INTERPOLATE_4(d0, out) 580 | case 8: 581 | patternStr := patternD 582 | applyPattern(in, out, patternStr) 583 | SHIFT_ROUND(out, 12) 584 | // 4 linear 585 | INTERPOLATE_4(d0, out) 586 | case 9: 587 | patternStr := patternD 588 | applyPattern(in, out, patternStr) 589 | SHIFT_ROUND(out, 13) 590 | // 4 linear 591 | INTERPOLATE_4(d0, out) 592 | case 10: 593 | patternStr := patternD 594 | applyPattern(in, out, patternStr) 595 | SHIFT_ROUND(out, 14) 596 | // 4 linear 597 | INTERPOLATE_4(d0, out) 598 | case 11: 599 | patternStr := patternD 600 | applyPattern(in, out, patternStr) 601 | SHIFT_ROUND(out, 15) 602 | // 4 linear 603 | INTERPOLATE_4(d0, out) 604 | 605 | // Pattern A 606 | 607 | case 12: 608 | patternStr := patternA 609 | applyPattern(in, out, patternStr) 610 | SHIFT_ROUND(out, 5) 611 | // 2 linear 612 | INTERPOLATE_2(d0, out) 613 | case 13: 614 | patternStr := patternA 615 | applyPattern(in, out, patternStr) 616 | SHIFT_ROUND(out, 6) 617 | // 2 linear 618 | INTERPOLATE_2(d0, out) 619 | case 14: 620 | patternStr := patternA 621 | applyPattern(in, out, patternStr) 622 | SHIFT_ROUND(out, 7) 623 | // 2 linear 624 | INTERPOLATE_2(d0, out) 625 | case 15: 626 | patternStr := patternA 627 | applyPattern(in, out, patternStr) 628 | SHIFT_ROUND(out, 8) 629 | // 2 linear 630 | INTERPOLATE_2(d0, out) 631 | case 16: 632 | patternStr := patternA 633 | applyPattern(in, out, patternStr) 634 | SHIFT_ROUND(out, 9) 635 | // 2 linear 636 | INTERPOLATE_2(d0, out) 637 | case 17: 638 | patternStr := patternA 639 | applyPattern(in, out, patternStr) 640 | SHIFT_ROUND(out, 10) 641 | // 2 linear 642 | INTERPOLATE_2(d0, out) 643 | 644 | // Pattern B3 645 | 646 | case 18: 647 | patternStr := patternB3 648 | applyPattern(in, out, patternStr) 649 | SHIFT_ROUND(out, 12) 650 | // 2 linear 651 | INTERPOLATE_2(d0, out) 652 | 653 | // Pattern C 654 | 655 | case 19: 656 | patternStr := patternC 657 | applyPattern(in, out, patternStr) 658 | SHIFT_ROUND(out, 8) 659 | // 2 linear 660 | INTERPOLATE_2(d0, out) 661 | case 20: 662 | patternStr := patternC 663 | applyPattern(in, out, patternStr) 664 | SHIFT_ROUND(out, 9) 665 | // 2 linear 666 | INTERPOLATE_2(d0, out) 667 | case 21: 668 | patternStr := patternC 669 | applyPattern(in, out, patternStr) 670 | SHIFT_ROUND(out, 10) 671 | // 2 linear 672 | INTERPOLATE_2(d0, out) 673 | case 22: 674 | patternStr := patternC 675 | applyPattern(in, out, patternStr) 676 | SHIFT_ROUND(out, 11) 677 | // 2 linear 678 | INTERPOLATE_2(d0, out) 679 | case 23: 680 | patternStr := patternC 681 | applyPattern(in, out, patternStr) 682 | SHIFT_ROUND(out, 12) 683 | // 2 linear 684 | INTERPOLATE_2(d0, out) 685 | case 24: 686 | patternStr := patternC 687 | applyPattern(in, out, patternStr) 688 | SHIFT_ROUND(out, 13) 689 | // 2 linear 690 | INTERPOLATE_2(d0, out) 691 | 692 | // Pattern F 693 | 694 | case 25: 695 | patternStr := patternF 696 | applyPattern(in, out, patternStr) 697 | SHIFT_ROUND(out, 12) 698 | // 8 linear 699 | INTERPOLATE_8(d0, out) 700 | case 26: 701 | patternStr := patternF 702 | applyPattern(in, out, patternStr) 703 | SHIFT_ROUND(out, 13) 704 | // 8 linear 705 | INTERPOLATE_8(d0, out) 706 | case 27: 707 | patternStr := patternF 708 | applyPattern(in, out, patternStr) 709 | SHIFT_ROUND(out, 14) 710 | // 8 linear 711 | INTERPOLATE_8(d0, out) 712 | case 28: 713 | patternStr := patternF 714 | applyPattern(in, out, patternStr) 715 | SHIFT_ROUND(out, 15) 716 | // 8 linear 717 | INTERPOLATE_8(d0, out) 718 | case 29: 719 | patternStr := patternF 720 | applyPattern(in, out, patternStr) 721 | SHIFT_ROUND(out, 16) 722 | // 8 linear 723 | INTERPOLATE_8(d0, out) 724 | case 30: 725 | patternStr := patternF 726 | applyPattern(in, out, patternStr) 727 | SHIFT_ROUND(out, 16) 728 | // 16 linear - but odd samples are doubled! 729 | DOUBLE_ODDS(out) 730 | 731 | // Pattern E 732 | 733 | case 31: 734 | patternStr := patternE 735 | applyPattern(in, out, patternStr) 736 | SHIFT_ROUND(out, 14) 737 | // 4 linear 738 | INTERPOLATE_4(d0, out) 739 | 740 | // Pattern B4 741 | 742 | case 32: 743 | patternStr := patternB4 744 | applyPattern(in, out, patternStr) 745 | SHIFT_ROUND(out, 4) 746 | // 2 linear 747 | INTERPOLATE_2(d0, out) 748 | case 33: 749 | patternStr := patternB4 750 | applyPattern(in, out, patternStr) 751 | SHIFT_ROUND(out, 5) 752 | // 2 linear 753 | INTERPOLATE_2(d0, out) 754 | case 34: 755 | patternStr := patternB4 756 | applyPattern(in, out, patternStr) 757 | // None: SHIFT_ROUND() 758 | // 2 linear 759 | INTERPOLATE_2(d0, out) 760 | case 35: 761 | patternStr := patternB4 762 | applyPattern(in, out, patternStr) 763 | SHIFT_ROUND(out, 2) 764 | // 2 linear 765 | INTERPOLATE_2(d0, out) 766 | case 36: 767 | patternStr := patternB4 768 | applyPattern(in, out, patternStr) 769 | SHIFT_ROUND(out, 3) 770 | // 2 linear 771 | INTERPOLATE_2(d0, out) 772 | default: 773 | } 774 | 775 | PREVENT_OVERFLOW_24(out) 776 | } 777 | 778 | //***************************************************************************** 779 | func DecodeMT1(d0 int, in []uint8, out []int) { 780 | 781 | // Decodes a 16-byte MT1 RDAC block into 16-bit samples. 782 | 783 | patternIndex := (in[0] & 0xf0) | ((in[2] & 0xf0) >> 4) 784 | 785 | pattern := patterns[patternIndex] 786 | 787 | for i := 0; i < 16; i++ { 788 | out[i] = 0 789 | } 790 | //fmt.Printf("pattern = %d\n", pattern) 791 | switch pattern { 792 | 793 | // Pattern B 794 | 795 | case 0: 796 | /* Unknown - never occurs? */ 797 | // fmt.Printf("DANGER! %d %v\n",pattern, in) 798 | case 1: 799 | /* Unknown - never occurs? */ 800 | fmt.Printf("DANGER! %d %v\n", pattern, in) 801 | case 2: 802 | patternStr := patternB 803 | applyPattern(in, out, patternStr) 804 | // 2 linear 805 | INTERPOLATE_2(d0, out) 806 | case 3: 807 | patternStr := patternB 808 | applyPattern(in, out, patternStr) 809 | SHIFT_ROUND(out, 1) 810 | // 2 linear 811 | INTERPOLATE_2(d0, out) 812 | case 4: 813 | patternStr := patternB 814 | applyPattern(in, out, patternStr) 815 | SHIFT_ROUND(out, 2) 816 | // 2 linear 817 | INTERPOLATE_2(d0, out) 818 | case 5: 819 | patternStr := patternB 820 | applyPattern(in, out, patternStr) 821 | SHIFT_ROUND(out, 3) 822 | // 2 linear 823 | INTERPOLATE_2(d0, out) 824 | 825 | // Pattern D 826 | 827 | case 6: 828 | patternStr := patternD 829 | applyPattern(in, out, patternStr) 830 | SHIFT_ROUND(out, 2) 831 | // 4 linear 832 | INTERPOLATE_4(d0, out) 833 | case 7: 834 | patternStr := patternD 835 | applyPattern(in, out, patternStr) 836 | SHIFT_ROUND(out, 3) 837 | // 4 linear 838 | INTERPOLATE_4(d0, out) 839 | case 8: 840 | patternStr := patternD 841 | applyPattern(in, out, patternStr) 842 | SHIFT_ROUND(out, 4) 843 | // 4 linear 844 | INTERPOLATE_4(d0, out) 845 | case 9: 846 | patternStr := patternD 847 | applyPattern(in, out, patternStr) 848 | SHIFT_ROUND(out, 5) 849 | // 4 linear 850 | INTERPOLATE_4(d0, out) 851 | case 10: 852 | patternStr := patternD 853 | applyPattern(in, out, patternStr) 854 | SHIFT_ROUND(out, 6) 855 | // 4 linear 856 | INTERPOLATE_4(d0, out) 857 | case 11: 858 | patternStr := patternD 859 | applyPattern(in, out, patternStr) 860 | SHIFT_ROUND(out, 7) 861 | // 4 linear 862 | INTERPOLATE_4(d0, out) 863 | 864 | // Pattern A 865 | 866 | case 12: 867 | /* Unknown - never occurs? */ 868 | fmt.Printf("DANGER! %d %v\n", pattern, in) 869 | case 13: 870 | /* Unknown - never occurs? */ 871 | fmt.Printf("DANGER! %d %v\n", pattern, in) 872 | case 14: 873 | /* Unknown - never occurs? */ 874 | fmt.Printf("DANGER! %d %v\n", pattern, in) 875 | case 15: 876 | patternStr := patternA 877 | applyPattern(in, out, patternStr) 878 | // 2 linear 879 | INTERPOLATE_2(d0, out) 880 | case 16: 881 | patternStr := patternA 882 | applyPattern(in, out, patternStr) 883 | SHIFT_ROUND(out, 1) 884 | // 2 linear 885 | INTERPOLATE_2(d0, out) 886 | case 17: 887 | patternStr := patternA 888 | applyPattern(in, out, patternStr) 889 | SHIFT_ROUND(out, 2) 890 | // 2 linear 891 | INTERPOLATE_2(d0, out) 892 | 893 | // Pattern B3 894 | 895 | case 18: 896 | patternStr := patternB3 897 | applyPattern(in, out, patternStr) 898 | SHIFT_ROUND(out, 4) 899 | // 2 linear 900 | INTERPOLATE_2(d0, out) 901 | 902 | // Pattern C 903 | 904 | case 19: 905 | patternStr := patternC 906 | applyPattern(in, out, patternStr) 907 | // 2 linear 908 | INTERPOLATE_2(d0, out) 909 | case 20: 910 | patternStr := patternC 911 | applyPattern(in, out, patternStr) 912 | SHIFT_ROUND(out, 1) 913 | // 2 linear 914 | INTERPOLATE_2(d0, out) 915 | case 21: 916 | patternStr := patternC 917 | applyPattern(in, out, patternStr) 918 | SHIFT_ROUND(out, 2) 919 | // 2 linear 920 | INTERPOLATE_2(d0, out) 921 | case 22: 922 | patternStr := patternC 923 | applyPattern(in, out, patternStr) 924 | SHIFT_ROUND(out, 3) 925 | // 2 linear 926 | INTERPOLATE_2(d0, out) 927 | case 23: 928 | patternStr := patternC 929 | applyPattern(in, out, patternStr) 930 | SHIFT_ROUND(out, 4) 931 | // 2 linear 932 | INTERPOLATE_2(d0, out) 933 | case 24: 934 | patternStr := patternC 935 | applyPattern(in, out, patternStr) 936 | SHIFT_ROUND(out, 5) 937 | // 2 linear 938 | INTERPOLATE_2(d0, out) 939 | 940 | // Pattern F 941 | 942 | case 25: 943 | patternStr := patternF 944 | applyPattern(in, out, patternStr) 945 | SHIFT_ROUND(out, 4) 946 | // 8 linear 947 | INTERPOLATE_8(d0, out) 948 | case 26: 949 | patternStr := patternF 950 | applyPattern(in, out, patternStr) 951 | SHIFT_ROUND(out, 5) 952 | // 8 linear 953 | INTERPOLATE_8(d0, out) 954 | case 27: 955 | patternStr := patternF 956 | applyPattern(in, out, patternStr) 957 | SHIFT_ROUND(out, 6) 958 | // 8 linear 959 | INTERPOLATE_8(d0, out) 960 | case 28: 961 | patternStr := patternF 962 | applyPattern(in, out, patternStr) 963 | SHIFT_ROUND(out, 7) 964 | // 8 linear 965 | INTERPOLATE_8(d0, out) 966 | case 29: 967 | patternStr := patternF 968 | applyPattern(in, out, patternStr) 969 | SHIFT_ROUND(out, 8) 970 | // 8 linear 971 | INTERPOLATE_8(d0, out) 972 | case 30: 973 | patternStr := patternF 974 | applyPattern(in, out, patternStr) 975 | SHIFT_ROUND(out, 8) 976 | // 16 linear - but odd samples are doubled! 977 | DOUBLE_ODDS(out) 978 | 979 | // Pattern E 980 | 981 | case 31: 982 | patternStr := patternE 983 | applyPattern(in, out, patternStr) 984 | SHIFT_ROUND(out, 6) 985 | // 4 linear 986 | INTERPOLATE_4(d0, out) 987 | 988 | // Pattern B4 989 | 990 | case 32: 991 | /* Unknown - never occurs? */ 992 | fmt.Printf("DANGER! %d %v\n", pattern, in) 993 | case 33: 994 | /* Unknown - never occurs? */ 995 | fmt.Printf("DANGER! %d %v\n", pattern, in) 996 | case 34: 997 | /* Unknown - never occurs? */ 998 | fmt.Printf("DANGER! %d %v\n", pattern, in) 999 | case 35: 1000 | /* Unknown - never occurs? */ 1001 | fmt.Printf("DANGER! %d %v\n", pattern, in) 1002 | case 36: 1003 | /* Unknown - never occurs? */ 1004 | fmt.Printf("DANGER! %d %v\n", pattern, in) 1005 | default: 1006 | } 1007 | 1008 | PREVENT_OVERFLOW_16(out) 1009 | } 1010 | 1011 | //***************************************************************************** 1012 | func DecodeMT2(d0 int, in []uint8, out []int) { 1013 | 1014 | // Decodes a 16-byte MT2 RDAC block into 16-bit samples. 1015 | 1016 | patternIndex := (in[0] & 0xf0) | ((in[2] & 0xf0) >> 4) 1017 | 1018 | pattern := patterns[patternIndex] 1019 | 1020 | for i := 0; i < 16; i++ { 1021 | out[i] = 0 1022 | } 1023 | 1024 | switch pattern { 1025 | 1026 | // Pattern 12A 1027 | 1028 | case 0: 1029 | patternStr := pattern12A 1030 | applyPattern12(in, out, patternStr) 1031 | // 2 linear 1032 | INTERPOLATE_2(d0, out) 1033 | case 1: 1034 | patternStr := pattern12A 1035 | applyPattern12(in, out, patternStr) 1036 | SHIFT_ROUND(out, 1) 1037 | // 2 linear 1038 | INTERPOLATE_2(d0, out) 1039 | case 2: 1040 | patternStr := pattern12A 1041 | applyPattern12(in, out, patternStr) 1042 | SHIFT_ROUND(out, 2) 1043 | // 2 linear 1044 | INTERPOLATE_2(d0, out) 1045 | case 3: 1046 | patternStr := pattern12A 1047 | applyPattern12(in, out, patternStr) 1048 | SHIFT_ROUND(out, 3) 1049 | // 2 linear 1050 | INTERPOLATE_2(d0, out) 1051 | case 4: 1052 | patternStr := pattern12A 1053 | applyPattern12(in, out, patternStr) 1054 | SHIFT_ROUND(out, 4) 1055 | // 2 linear 1056 | INTERPOLATE_2(d0, out) 1057 | case 5: 1058 | patternStr := pattern12A 1059 | applyPattern12(in, out, patternStr) 1060 | SHIFT_ROUND(out, 5) 1061 | // 2 linear 1062 | INTERPOLATE_2(d0, out) 1063 | 1064 | // Pattern 12B 1065 | 1066 | case 6: 1067 | patternStr := pattern12B 1068 | applyPattern12(in, out, patternStr) 1069 | SHIFT_ROUND(out, 4) 1070 | // 4 linear 1071 | INTERPOLATE_4(d0, out) 1072 | case 7: 1073 | patternStr := pattern12B 1074 | applyPattern12(in, out, patternStr) 1075 | SHIFT_ROUND(out, 5) 1076 | // 4 linear 1077 | INTERPOLATE_4(d0, out) 1078 | case 8: 1079 | patternStr := pattern12B 1080 | applyPattern12(in, out, patternStr) 1081 | SHIFT_ROUND(out, 6) 1082 | // 4 linear 1083 | INTERPOLATE_4(d0, out) 1084 | case 9: 1085 | patternStr := pattern12B 1086 | applyPattern12(in, out, patternStr) 1087 | SHIFT_ROUND(out, 7) 1088 | // 4 linear 1089 | INTERPOLATE_4(d0, out) 1090 | case 10: 1091 | patternStr := pattern12B 1092 | applyPattern12(in, out, patternStr) 1093 | SHIFT_ROUND(out, 8) 1094 | // 4 linear 1095 | INTERPOLATE_4(d0, out) 1096 | case 11: 1097 | patternStr := pattern12B 1098 | applyPattern12(in, out, patternStr) 1099 | SHIFT_ROUND(out, 9) 1100 | // 4 linear 1101 | INTERPOLATE_4(d0, out) 1102 | 1103 | // Pattern 12F 1104 | 1105 | case 12: 1106 | /* Unknown - never occurs? */ 1107 | fmt.Printf("DANGER! %d %v\n", pattern, in) 1108 | case 13: 1109 | patternStr := pattern12F 1110 | applyPattern12(in, out, patternStr) 1111 | // 2 linear 1112 | INTERPOLATE_2(d0, out) 1113 | case 14: 1114 | patternStr := pattern12F 1115 | applyPattern12(in, out, patternStr) 1116 | SHIFT_ROUND(out, 1) 1117 | // 4 linear 1118 | INTERPOLATE_2(d0, out) 1119 | case 15: 1120 | patternStr := pattern12F 1121 | applyPattern12(in, out, patternStr) 1122 | SHIFT_ROUND(out, 2) 1123 | // 4 linear 1124 | INTERPOLATE_2(d0, out) 1125 | case 16: 1126 | patternStr := pattern12F 1127 | applyPattern12(in, out, patternStr) 1128 | SHIFT_ROUND(out, 3) 1129 | // 4 linear 1130 | INTERPOLATE_2(d0, out) 1131 | case 17: 1132 | patternStr := pattern12F 1133 | applyPattern12(in, out, patternStr) 1134 | SHIFT_ROUND(out, 4) 1135 | // 4 linear 1136 | INTERPOLATE_2(d0, out) 1137 | 1138 | // Pattern 12G 1139 | 1140 | case 18: 1141 | patternStr := pattern12G 1142 | applyPattern12(in, out, patternStr) 1143 | SHIFT_ROUND(out, 6) 1144 | // 2 linear 1145 | INTERPOLATE_2(d0, out) 1146 | 1147 | // Pattern 12E 1148 | 1149 | case 19: 1150 | patternStr := pattern12E 1151 | applyPattern12(in, out, patternStr) 1152 | SHIFT_ROUND(out, 2) 1153 | // 2 linear 1154 | INTERPOLATE_2(d0, out) 1155 | case 20: 1156 | patternStr := pattern12E 1157 | applyPattern12(in, out, patternStr) 1158 | SHIFT_ROUND(out, 3) 1159 | // 2 linear 1160 | INTERPOLATE_2(d0, out) 1161 | case 21: 1162 | patternStr := pattern12E 1163 | applyPattern12(in, out, patternStr) 1164 | SHIFT_ROUND(out, 4) 1165 | // 2 linear 1166 | INTERPOLATE_2(d0, out) 1167 | case 22: 1168 | patternStr := pattern12E 1169 | applyPattern12(in, out, patternStr) 1170 | SHIFT_ROUND(out, 5) 1171 | // 2 linear 1172 | INTERPOLATE_2(d0, out) 1173 | case 23: 1174 | patternStr := pattern12E 1175 | applyPattern12(in, out, patternStr) 1176 | SHIFT_ROUND(out, 6) 1177 | // 2 linear 1178 | INTERPOLATE_2(d0, out) 1179 | case 24: 1180 | patternStr := pattern12E 1181 | applyPattern12(in, out, patternStr) 1182 | SHIFT_ROUND(out, 7) 1183 | // 2 linear 1184 | INTERPOLATE_2(d0, out) 1185 | 1186 | // Pattern 12C 1187 | 1188 | case 25: 1189 | patternStr := pattern12C 1190 | applyPattern12(in, out, patternStr) 1191 | SHIFT_ROUND(out, 6) 1192 | // 8 linear 1193 | INTERPOLATE_8(d0, out) 1194 | case 26: 1195 | patternStr := pattern12C 1196 | applyPattern12(in, out, patternStr) 1197 | SHIFT_ROUND(out, 7) 1198 | // 8 linear 1199 | INTERPOLATE_8(d0, out) 1200 | case 27: 1201 | patternStr := pattern12C 1202 | applyPattern12(in, out, patternStr) 1203 | SHIFT_ROUND(out, 8) 1204 | // 8 linear 1205 | INTERPOLATE_8(d0, out) 1206 | case 28: 1207 | patternStr := pattern12C 1208 | applyPattern12(in, out, patternStr) 1209 | SHIFT_ROUND(out, 9) 1210 | // 8 linear 1211 | INTERPOLATE_8(d0, out) 1212 | case 29: 1213 | patternStr := pattern12C 1214 | applyPattern12(in, out, patternStr) 1215 | SHIFT_ROUND(out, 10) 1216 | // 8 linear 1217 | INTERPOLATE_8(d0, out) 1218 | case 30: 1219 | patternStr := pattern12C 1220 | applyPattern12(in, out, patternStr) 1221 | SHIFT_ROUND(out, 10) 1222 | // 16 linear - but odd samples are doubled! 1223 | DOUBLE_ODDS(out) 1224 | 1225 | // Pattern 12D 1226 | 1227 | case 31: 1228 | patternStr := pattern12D 1229 | applyPattern12(in, out, patternStr) 1230 | SHIFT_ROUND(out, 8) 1231 | // 4 linear 1232 | INTERPOLATE_4(d0, out) 1233 | 1234 | // Pattern ? 1235 | 1236 | case 32: 1237 | /* Unknown - never occurs? */ 1238 | fmt.Printf("DANGER! %d %v\n", pattern, in) 1239 | case 33: 1240 | /* Unknown - never occurs? */ 1241 | fmt.Printf("DANGER! %d %v\n", pattern, in) 1242 | case 34: 1243 | /* Unknown - never occurs? */ 1244 | fmt.Printf("DANGER! %d %v\n", pattern, in) 1245 | case 35: 1246 | /* Unknown - never occurs? */ 1247 | fmt.Printf("DANGER! %d %v\n", pattern, in) 1248 | case 36: 1249 | /* Unknown - never occurs? */ 1250 | fmt.Printf("DANGER! %d %v\n", pattern, in) 1251 | default: 1252 | } 1253 | 1254 | PREVENT_OVERFLOW_16(out) 1255 | } 1256 | 1257 | //***************************************************************************** 1258 | func DecodeM16(d0 int, in []uint8, out []int) { 1259 | 1260 | // Decodes a 32-byte M16 block into 16 16-bit samples. 1261 | 1262 | for i := 0; i < 16; i++ { 1263 | out[i] = int(uint(in[i*2])<<8 | uint(in[i*2+1])) 1264 | out[i] = SIGN_EXTEND(out[i], 15) 1265 | } 1266 | } 1267 | 1268 | //***************************************************************************** 1269 | func DecodeM24(d0 int, in []uint8, out []int) { 1270 | 1271 | // Decodes a 48-byte M24 block into 16 24-bit samples. 1272 | 1273 | for i := 0; i < 16; i++ { 1274 | out[i] = int(uint(in[i*3])<<16 | uint(in[i*3])<<8 | uint(in[i*3+1])) 1275 | out[i] = SIGN_EXTEND(out[i], 23) 1276 | } 1277 | } 1278 | 1279 | //***************************************************************************** 1280 | func applyPattern(in []uint8, out []int, pattern string) { 1281 | 1282 | outPos := make([]uint, 16) 1283 | 1284 | for inPosition := 15; inPosition >= 0; inPosition-- { 1285 | bytePatternIndex := inPosition * 8 1286 | bytePatternStr := pattern[bytePatternIndex : bytePatternIndex+8] 1287 | 1288 | for bitPosition := 0; bitPosition <= 7; bitPosition++ { 1289 | symbol := bytePatternStr[7-bitPosition] 1290 | 1291 | //fmt.Printf("%s\n",string(symbol)) 1292 | outIndex := symbolIndex[symbol] 1293 | if outIndex == -1 { 1294 | continue 1295 | } 1296 | 1297 | hasBit := ((in[inPosition] >> uint(bitPosition)) & 0x01) == 0x01 1298 | 1299 | if hasBit { 1300 | out[outIndex] |= 0x01 << outPos[outIndex] 1301 | } 1302 | outPos[outIndex]++ 1303 | } 1304 | } 1305 | // Sign extend 1306 | for i := 0; i < 16; i++ { 1307 | // fmt.Printf(" %032b - %d\n", toUint32(out[i]), outPos[i]-1) 1308 | out[i] = SIGN_EXTEND(out[i], outPos[i]-1) 1309 | // fmt.Printf("x %032b - %d\n", toUint32(out[i]), outPos[i]-1) 1310 | } 1311 | } 1312 | 1313 | //***************************************************************************** 1314 | func applyPattern12(in []uint8, out []int, pattern string) { 1315 | 1316 | outPos := make([]uint, 16) 1317 | 1318 | for inPosition := 11; inPosition >= 0; inPosition-- { 1319 | bytePatternIndex := inPosition * 8 1320 | bytePatternStr := pattern[bytePatternIndex : bytePatternIndex+8] 1321 | 1322 | for bitPosition := 0; bitPosition <= 7; bitPosition++ { 1323 | symbol := bytePatternStr[7-bitPosition] 1324 | 1325 | //fmt.Printf("%s\n",string(symbol)) 1326 | outIndex := symbolIndex[symbol] 1327 | if outIndex == -1 { 1328 | continue 1329 | } 1330 | 1331 | hasBit := ((in[inPosition] >> uint(bitPosition)) & 0x01) == 0x01 1332 | 1333 | if hasBit { 1334 | out[outIndex] |= 0x01 << outPos[outIndex] 1335 | } 1336 | outPos[outIndex]++ 1337 | } 1338 | } 1339 | // Sign extend 1340 | for i := 0; i < 16; i++ { 1341 | // fmt.Printf(" %032b - %d\n", toUint32(out[i]), outPos[i]-1) 1342 | out[i] = SIGN_EXTEND(out[i], outPos[i]-1) 1343 | // fmt.Printf("x %032b - %d\n", toUint32(out[i]), outPos[i]-1) 1344 | } 1345 | } 1346 | 1347 | //***************************************************************************** 1348 | func check(e error) { 1349 | if e != nil { 1350 | panic(e) 1351 | } 1352 | } 1353 | 1354 | //***************************************************************************** 1355 | func BYTE_0(xx int) byte { return byte(xx & 0xff) } 1356 | func BYTE_1(xx int) byte { return byte((xx >> 8) & 0xff) } 1357 | func BYTE_2(xx int) byte { return byte((xx >> 16) & 0xff) } 1358 | func BYTE_3(xx int) byte { return byte((xx >> 24) & 0xff) } 1359 | 1360 | func SIGN_EXTEND(xx int, signPos uint) int { return -(xx & (0x01 << (signPos))) | xx } 1361 | 1362 | func SHIFT_ROUND(out []int, pos uint) { 1363 | for i := 0; i < 16; i++ { 1364 | out[i] = out[i]< 32767 { 1425 | return 32767 1426 | } else { 1427 | return xx 1428 | } 1429 | } 1430 | 1431 | func LIMIT_24(xx int) int { 1432 | if xx < -8388608 { 1433 | return -8388608 1434 | } else if xx > 8388607 { 1435 | return 8388607 1436 | } else { 1437 | return xx 1438 | } 1439 | } 1440 | 1441 | func toUint32(xx int) uint32 { 1442 | return uint32(xx & 0xffffffff) 1443 | // return uint32(uint32(xx) & 0xffffffff) 1444 | } 1445 | 1446 | func PREVENT_OVERFLOW_16(out []int) { 1447 | for i := 0; i < 16; i++ { 1448 | out[i] = LIMIT_16(out[i]) 1449 | } 1450 | } 1451 | 1452 | func PREVENT_OVERFLOW_24(out []int) { 1453 | for i := 0; i < 16; i++ { 1454 | out[i] = LIMIT_24(out[i]) 1455 | } 1456 | } 1457 | 1458 | func DOUBLE_ODDS(out []int) { 1459 | for i := 0; i < 16; i += 2 { 1460 | out[i] <<= 1 1461 | } 1462 | } 1463 | 1464 | //***************************************************************************** 1465 | func stripSpaces(s string) string { 1466 | s = strings.Replace(s, " ", "", -1) 1467 | return s 1468 | } 1469 | 1470 | //***************************************************************************** 1471 | func showPattern(s string) { 1472 | fmt.Printf("%s\n", s) 1473 | } 1474 | 1475 | //***************************************************************************** 1476 | func showBits8(out []uint8) { 1477 | for i := 0; i < 16; i++ { 1478 | fmt.Printf("%08b", out[i]) 1479 | } 1480 | fmt.Printf("\n") 1481 | } 1482 | 1483 | //***************************************************************************** 1484 | //func showBits32(out []uint32) { 1485 | func showBits32(out []int) { 1486 | for i := 0; i < 16; i++ { 1487 | fmt.Printf("%b ", toUint32(out[i])) 1488 | } 1489 | fmt.Printf("\n") 1490 | } 1491 | 1492 | //***************************************************************************** 1493 | func showInts(out []int) { 1494 | for i := 0; i < 16; i++ { 1495 | fmt.Printf("%d ", out[i]) 1496 | } 1497 | fmt.Printf("\n") 1498 | } 1499 | 1500 | //***************************************************************************** 1501 | func showBits(out uint32) { 1502 | fmt.Printf("%08b", out) 1503 | fmt.Printf("\n") 1504 | } 1505 | 1506 | //***************************************************************************** 1507 | func getSongInfo(vsFormat string, songFilePath string) (string, int, int) { 1508 | f, err := os.Open(songFilePath) 1509 | check(err) 1510 | defer f.Close() 1511 | 1512 | var songName [12]byte 1513 | var rate [1]byte 1514 | var format [1]byte 1515 | 1516 | if getFormat(format[0]) == "VS-2480" { 1517 | f.Seek(10, 0) 1518 | f.Read(songName[:]) 1519 | 1520 | f.Seek(22, 0) 1521 | f.Read(rate[:]) 1522 | f.Read(format[:]) 1523 | } else { 1524 | f.Seek(6, 0) 1525 | f.Read(songName[:]) 1526 | 1527 | f.Seek(18, 0) 1528 | f.Read(rate[:]) 1529 | f.Read(format[:]) 1530 | } 1531 | 1532 | return string(songName[:]), int(rate[0]), int(format[0]) 1533 | } 1534 | 1535 | //***************************************************************************** 1536 | func getSampleRate(rate int) float64 { 1537 | switch rate { 1538 | case 0: 1539 | return 48000 1540 | case 1: 1541 | return 44100 1542 | case 2: 1543 | return 32000 1544 | case 3: 1545 | return 96000 1546 | case 4: 1547 | return 88200 1548 | default: 1549 | return 64000 1550 | } 1551 | } 1552 | 1553 | //***************************************************************************** 1554 | func getModeName(mode int) string { 1555 | switch mode { 1556 | case 0: 1557 | return "MT1" 1558 | case 1: 1559 | return "MT2" 1560 | case 2: 1561 | return "LIV" 1562 | case 3: 1563 | return "M16" 1564 | case 4: 1565 | return "CDR" 1566 | case 5: 1567 | return "MTP" 1568 | case 6: 1569 | return "LV2" 1570 | case 7: 1571 | return "VSR" 1572 | case 8: 1573 | return "M24" 1574 | default: 1575 | return "UNKNOWN" 1576 | } 1577 | } 1578 | 1579 | //***************************************************************************** 1580 | func getFormat(format byte) string { 1581 | switch format { 1582 | case 1: 1583 | return "VS-1680" 1584 | case 2: 1585 | return "VS-1880" 1586 | case 3: 1587 | return "VS-2480" 1588 | case 4: 1589 | return "VS-880" 1590 | case 5: 1591 | return "VS-880EX" 1592 | case 6: 1593 | return "VS-890" 1594 | case 7: 1595 | return "VS-840" 1596 | case 8: 1597 | return "VS-840GX" 1598 | default: 1599 | return "UNKNOWN" 1600 | } 1601 | } 1602 | 1603 | //***************************************************************************** 1604 | func getFormatForExtension(ext string) string { 1605 | switch ext { 1606 | case "VR6": 1607 | return "VS-1680" 1608 | case "VR5": 1609 | return "VS-1880" 1610 | case "VR1": 1611 | return "VS-2480" 1612 | case "VR9": 1613 | return "VS-880EX" 1614 | case "VR7": 1615 | return "VS-890" 1616 | case "VR4": 1617 | return "VS-840GX" 1618 | case "VR8": 1619 | // It could be a VS-880 or a VS-840! 1620 | // Heuristic: If SYSTEM.VS1 exists, its a VS-840 1621 | // TBD 1622 | return "VS-840" 1623 | default: 1624 | return "UNKNOWN" 1625 | } 1626 | } 1627 | 1628 | //***************************************************************************** 1629 | func convertVS1880ToReaper(songDirPath string, wavDirPath string) { 1630 | 1631 | ext := songDirPath[len(songDirPath)-3:] 1632 | 1633 | bitDepth := 24 1634 | 1635 | // Create the target directory for the Reaper conversion 1636 | os.MkdirAll(wavDirPath, os.ModePerm) 1637 | 1638 | // Get song and event file paths. 1639 | 1640 | songFilePath := fmt.Sprintf("%s/SONG.%s", songDirPath, ext) 1641 | eventFilePath := fmt.Sprintf("%s/EVENTLST.%s", songDirPath, ext) 1642 | 1643 | // Determine sample rate from song file. 1644 | 1645 | songName, rate, mode := getSongInfo("VS-1880", songFilePath) 1646 | 1647 | if "MTP" != getModeName(mode) { 1648 | fmt.Printf("%s encodings are currently not supported.\n", getModeName(mode)) 1649 | return 1650 | } 1651 | 1652 | // Open the Reaper project file to create 1653 | // reaperProject := fmt.Sprintf("%s/%s", wavDirPath, "vs.rpp") 1654 | reaperProject := fmt.Sprintf("%s/%s.rpp", wavDirPath, songName) 1655 | fout, err := os.Create(reaperProject) 1656 | check(err) 1657 | defer fout.Close() 1658 | reaperWriter := bufio.NewWriter(fout) 1659 | defer reaperWriter.Flush() 1660 | 1661 | vsSampleRate := getSampleRate(rate) 1662 | 1663 | // Show some info 1664 | fmt.Printf("Song: %s\n", songName) 1665 | fmt.Printf("Mode: %s\n", getModeName(mode)) 1666 | fmt.Printf("Sample Rate: %d\n", int(vsSampleRate)) 1667 | fmt.Printf("Bit Depth: %d\n", bitDepth) 1668 | 1669 | // Open the event file. 1670 | 1671 | f, err := os.Open(eventFilePath) 1672 | check(err) 1673 | defer f.Close() 1674 | 1675 | // Skip past phrases 1676 | 1677 | var numPhrasesBytes [2]byte 1678 | f.Seek(16, 0) 1679 | f.Read(numPhrasesBytes[:]) 1680 | 1681 | numPhrases := int(binary.BigEndian.Uint16(numPhrasesBytes[:])) 1682 | f.Seek(int64(18+numPhrases*64), 0) 1683 | 1684 | reaperWriter.WriteString("\n") 1750 | } 1751 | } 1752 | reaperWriter.WriteString(">\n") 1753 | fmt.Printf("Created Reaper project file: %s\n", reaperProject) 1754 | } 1755 | -------------------------------------------------------------------------------- /vs2reaper/go.mod: -------------------------------------------------------------------------------- 1 | module vs 2 | 3 | go 1.14 4 | -------------------------------------------------------------------------------- /vs2reaper/sd.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | //"log" 6 | "encoding/binary" 7 | "os" 8 | //"errors" 9 | "strconv" 10 | "strings" 11 | //"io/ioutil" 12 | "vs/vsio" 13 | ) 14 | 15 | const SECTOR_SIZE = 512 16 | const SECTORS_PER_CLUSTER = 64 17 | const SECTORS_PER_FAT = 256 18 | const DIRECTORY = 16 19 | 20 | var targetDir = "c:/testout" 21 | 22 | var file *os.File 23 | 24 | type bootRecordType struct { 25 | formattingOS string 26 | bytesPerSector int 27 | sectorsPerCluster int 28 | reservedSectors int 29 | fatCopies int 30 | maxRootEntries int 31 | sectorsPerFAT int 32 | sectorsInPart int 33 | rootStartSector int 34 | clusterStartSector int 35 | fatStartSector int 36 | } 37 | 38 | type fileInfoType struct { 39 | name string 40 | ext string 41 | attr int 42 | cluster int 43 | size int 44 | data []byte 45 | dirFiles []fileInfoType 46 | clusters []int 47 | isDirectory bool 48 | } 49 | 50 | type contextType struct { 51 | file *os.File 52 | bootRecord bootRecordType 53 | fat []byte 54 | } 55 | 56 | //***************************************************************************** 57 | func main_old() { 58 | 59 | // Find the attached VS drive, if any 60 | file = findRolandDrive() 61 | 62 | // Get the start sectors for each partition 63 | partitionSectors := getPartitionSectors(file) 64 | //showPartitions(partitionSectors); 65 | 66 | os.MkdirAll(targetDir, os.ModePerm) 67 | 68 | for i := 0; i < 8; i++ { 69 | exportPartition(partitionSectors, i) 70 | } 71 | } 72 | 73 | //***************************************************************************** 74 | func main() { 75 | 76 | args := os.Args[1:] 77 | 78 | argCount := len(args) 79 | 80 | if argCount == 0 { 81 | //fmt.Printf("Invalid number of arguments.") 82 | //os.Exit(1) 83 | help() 84 | } 85 | 86 | command := args[0] 87 | 88 | if "help" == command { 89 | help() 90 | } else if "list" == command { 91 | vsio.List() 92 | } else if "get" == command { 93 | if argCount == 2 { 94 | songName := args[1] 95 | targetDir := fmt.Sprintf("./%s", songName) 96 | vsio.ConvertFirst(songName, targetDir) 97 | } else if argCount == 3 { 98 | songName := args[1] 99 | targetDir := fmt.Sprintf("%s/%s", args[2], songName) 100 | vsio.ConvertFirst(songName, targetDir) 101 | } else { 102 | fmt.Printf("Invalid number of arguments.") 103 | os.Exit(1) 104 | } 105 | } else if "getp" == command { 106 | if argCount == 3 { 107 | partition, err := strconv.Atoi(args[1]) 108 | if err != nil { 109 | fmt.Printf("Invalid partition: %s\n", args[1]) 110 | os.Exit(1) 111 | } 112 | songName := args[2] 113 | targetDir := fmt.Sprintf("./%s-%d", songName, partition) 114 | vsio.Convert(songName, partition, targetDir) 115 | } else if argCount == 4 { 116 | partition, err := strconv.Atoi(args[1]) 117 | if err != nil { 118 | fmt.Printf("Invalid partition: %s\n", args[1]) 119 | os.Exit(1) 120 | } 121 | songName := args[2] 122 | targetDir := fmt.Sprintf("%s/%s-%d", args[3], songName, partition) 123 | vsio.Convert(songName, partition, targetDir) 124 | } else { 125 | fmt.Printf("Invalid number of arguments.") 126 | os.Exit(1) 127 | } 128 | } else { 129 | fmt.Printf("Invalid command: %s\n", args[0]) 130 | os.Exit(1) 131 | } 132 | } 133 | 134 | //***************************************************************************** 135 | func help() { 136 | fmt.Printf("\n") 137 | fmt.Printf("Roland VS to Reaper Project Converter 1.01\n") 138 | fmt.Printf("------------------------------------------\n") 139 | fmt.Printf("\n") 140 | fmt.Printf("Copyright 2017 Randy Gordon (randy@integrand.com)\n") 141 | fmt.Printf("\n") 142 | fmt.Printf("Notes: 1) Must be run with administrator privileges.\n") 143 | fmt.Printf(" 2) Supported VS formats: VS-1880 and VS-1680.\n") 144 | fmt.Printf(" 3) Supported RDAC modes: MTP, MT2, MT1, M16, M24.\n") 145 | fmt.Printf("\n") 146 | fmt.Printf("Usage examples:\n") 147 | fmt.Printf("\n") 148 | fmt.Printf("vs help - Show help info\n") 149 | fmt.Printf("vs list - Show all songs on all partitions\n") 150 | fmt.Printf("vs get \"My Song\" - Find the first occurrence of song and convert to current directory\n") 151 | fmt.Printf("vs get \"My Song\" c:/foo - Find the first occurrence of song and convert to directory c:/foo\n") 152 | fmt.Printf("vs getp 5 \"My Song\" - Find song on partition 5 and convert to current directory\n") 153 | fmt.Printf("vs getp 5 \"My Song\" c:/foo - Find song on partition 5 and convert to directory c:/foo\n") 154 | fmt.Printf("\n") 155 | os.Exit(0) 156 | } 157 | 158 | //***************************************************************************** 159 | func exportPartition(partitionSectors []int, partition int) { 160 | 161 | fmt.Printf("Exporting Partition %d\n", partition) 162 | fmt.Printf("*********************\n") 163 | 164 | // Read the boot record for the partition 165 | bootRecord := getBootRecord(file, partitionSectors[partition]) 166 | //showBootRecord(bootRecord); 167 | 168 | context := newContext(file, bootRecord) 169 | 170 | fat := getFAT(context) 171 | //showFAT(fat); 172 | 173 | context.fat = fat 174 | 175 | // Get the root directory, and recurse to all (only one level!) subdirectories. 176 | files := getDirectoryEntries(context, bootRecord.rootStartSector, false) 177 | //showFileStructure(files); 178 | 179 | partitionDir := targetDir + "/Partition" + strconv.Itoa(partition) 180 | 181 | os.MkdirAll(partitionDir, os.ModePerm) 182 | 183 | // Copy all the gathered file and directory data into a normal Windows filesystem. 184 | writeFiles(context, partitionDir, files) 185 | } 186 | 187 | //***************************************************************************** 188 | func newContext(file *os.File, bootRecord bootRecordType) contextType { 189 | var context contextType 190 | context.file = file 191 | context.bootRecord = bootRecord 192 | return context 193 | } 194 | 195 | //***************************************************************************** 196 | func findRolandDrive() *os.File { 197 | // Find the Roland VS drive - look at up to 10 drives 198 | for i := 0; i < 10; i++ { 199 | drive := "\\\\.\\PhysicalDrive" + strconv.Itoa(i) 200 | file, err := os.Open(drive) 201 | if err != nil { 202 | continue 203 | } 204 | if isRolandDrive(file) { 205 | fmt.Printf("Found Roland VS drive at: %s\n\n", drive) 206 | return file 207 | } 208 | } 209 | 210 | fmt.Printf("%s\n", "Unable to locate an attached Roland VS drive") 211 | os.Exit(0) 212 | 213 | return nil 214 | } 215 | 216 | //***************************************************************************** 217 | func isRolandDrive(file *os.File) bool { 218 | // Check for the signature 219 | buffer := readSector(file, 63) 220 | fmt.Printf("drive = %s\n", string(buffer[3:9])) 221 | return "Roland" == string(buffer[3:9]) 222 | } 223 | 224 | //***************************************************************************** 225 | func getPartitionSectors(file *os.File) []int { 226 | 227 | partitionSectors := make([]int, 8) 228 | partitionOffset := 446 229 | 230 | buffer := readSector(file, 0) 231 | 232 | //fmt.Printf("read bytes: %x\n", buffer) 233 | 234 | for i := 0; i < 4; i++ { 235 | firstSector := getInt32(buffer, partitionOffset+8) 236 | //sectorCount := binary.LittleEndian.Uint32(buffer[partitionOffset+12:]) 237 | partitionSectors[i] = firstSector 238 | partitionOffset += 16 239 | } 240 | 241 | // Extrapolate extra four partitions 242 | delta := partitionSectors[1] - partitionSectors[0] 243 | 244 | nextPartitionSector := partitionSectors[3] + delta 245 | for i := 0; i < 4; i++ { 246 | partitionSectors[i+4] = nextPartitionSector 247 | nextPartitionSector += delta 248 | } 249 | 250 | return partitionSectors 251 | } 252 | 253 | //***************************************************************************** 254 | func getBootRecord(file *os.File, sector int) bootRecordType { 255 | 256 | buffer := readSector(file, sector) 257 | 258 | var bootRecord bootRecordType 259 | 260 | bootRecord.formattingOS = string(buffer[3:9]) 261 | bootRecord.bytesPerSector = getUint16(buffer, 11) 262 | bootRecord.sectorsPerCluster = getUint8(buffer, 13) 263 | bootRecord.reservedSectors = getUint16(buffer, 14) 264 | bootRecord.fatCopies = getUint8(buffer, 16) 265 | bootRecord.maxRootEntries = getUint16(buffer, 17) 266 | bootRecord.sectorsPerFAT = getUint16(buffer, 22) 267 | bootRecord.sectorsInPart = getUint32(buffer, 28) 268 | bootRecord.rootStartSector = sector + bootRecord.reservedSectors + int(bootRecord.fatCopies)*bootRecord.sectorsPerFAT 269 | bootRecord.clusterStartSector = bootRecord.rootStartSector + (bootRecord.maxRootEntries*32)/bootRecord.bytesPerSector 270 | bootRecord.fatStartSector = sector + bootRecord.reservedSectors 271 | 272 | return bootRecord 273 | } 274 | 275 | //***************************************************************************** 276 | func getFAT(context contextType) []byte { 277 | buffer := make([]byte, SECTOR_SIZE*SECTORS_PER_FAT) 278 | 279 | sector := context.bootRecord.fatStartSector 280 | 281 | pos := int64(sector * SECTOR_SIZE) 282 | context.file.Seek(pos, 0) 283 | 284 | _, err := context.file.Read(buffer) 285 | if err != nil { 286 | check(err) 287 | } 288 | return buffer 289 | } 290 | 291 | //***************************************************************************** 292 | func getDirectoryEntries(context contextType, sector int, isClusterFucked bool) []fileInfoType { 293 | done := false 294 | files := make([]fileInfoType, 0, 1000) 295 | 296 | for { 297 | buffer := readSector(file, sector) 298 | 299 | if isClusterFucked { 300 | unClusterFuck(buffer) 301 | } 302 | 303 | // Up to 16 32-byte entries per sector. 304 | for i := 0; i < 16; i++ { 305 | start := i * 32 306 | entry := buffer[start : start+32] 307 | if getUint8(buffer, 0) == 0 { 308 | done = true 309 | break 310 | } 311 | if !isWantedFile(entry) { 312 | continue // Ignore garbage 313 | } 314 | fileInfo := getFileInfo(context, entry) 315 | files = append(files, fileInfo) 316 | } 317 | if done { 318 | break 319 | } 320 | sector++ 321 | } 322 | 323 | return files 324 | } 325 | 326 | //***************************************************************************** 327 | func getSectorForCluster(context contextType, cluster int) int { 328 | // Convert cluster to sector. Note: Clusters start at 2 - historical anomaly! 329 | return context.bootRecord.clusterStartSector + (cluster-2)*SECTORS_PER_CLUSTER 330 | } 331 | 332 | //***************************************************************************** 333 | func unClusterFuck(buffer []byte) { 334 | // Sneaky Roland! Swap your partners: 335 | for i := 0; i < len(buffer); i += 2 { 336 | x := buffer[i] 337 | buffer[i] = buffer[i+1] 338 | buffer[i+1] = x 339 | } 340 | } 341 | 342 | //***************************************************************************** 343 | func isWantedFile(buffer []byte) bool { 344 | // As far as I know, any file we care about starts with "V" on the file extension. 345 | var fistCharOfExt = string(buffer[8:9]) 346 | return fistCharOfExt == "V" 347 | } 348 | 349 | //***************************************************************************** 350 | func getFileInfo(context contextType, buffer []byte) fileInfoType { 351 | 352 | var fileInfo fileInfoType 353 | 354 | fileInfo.name = string(buffer[0:8]) 355 | fileInfo.ext = string(buffer[8:11]) 356 | fileInfo.attr = getUint8(buffer, 11) 357 | fileInfo.cluster = getUint16(buffer, 26) 358 | fileInfo.size = getUint32(buffer, 28) 359 | fileInfo.isDirectory = (fileInfo.attr == DIRECTORY) 360 | 361 | sector := getSectorForCluster(context, fileInfo.cluster) 362 | 363 | if fileInfo.isDirectory { 364 | fileInfo.dirFiles = getDirectoryEntries(context, sector, true) 365 | } else { 366 | fileInfo.clusters = getClusterChain(context.fat, fileInfo.cluster) 367 | } 368 | 369 | return fileInfo 370 | } 371 | 372 | //***************************************************************************** 373 | func getClusterChain(fat []byte, cluster int) []int { 374 | chain := make([]int, 0, 100000) 375 | 376 | chain = append(chain, cluster) 377 | 378 | index := cluster * 2 379 | for { 380 | nextCluster := getUint16(fat, index) 381 | if nextCluster <= 0xffef { 382 | chain = append(chain, nextCluster) 383 | } else if nextCluster >= 0xfff8 { 384 | break 385 | } 386 | index += 2 387 | } 388 | return chain 389 | } 390 | 391 | //***************************************************************************** 392 | func getClusterData(context contextType, cluster int) []byte { //TBD 393 | sector := getSectorForCluster(context, cluster) 394 | buffer := make([]byte, SECTOR_SIZE*SECTORS_PER_CLUSTER) 395 | 396 | pos := int64(sector * SECTOR_SIZE) 397 | file.Seek(pos, 0) 398 | 399 | _, err := file.Read(buffer) 400 | if err != nil { 401 | check(err) 402 | } 403 | return buffer 404 | } 405 | 406 | //***************************************************************************** 407 | func writeFiles(context contextType, targetDir string, files []fileInfoType) { 408 | // Write out the unclusterfucked results to the target filesystem. 409 | for i := 0; i < len(files); i++ { 410 | file := files[i] 411 | targetFileName := targetDir + "/" + strings.TrimSpace(file.name) + "." + file.ext 412 | if file.attr == DIRECTORY { 413 | //if (!os..existsSync(targetFileName)){ 414 | // fs.mkdirSync(targetFileName); 415 | //} 416 | fmt.Printf("Creating dir: %s\n", targetFileName) 417 | os.MkdirAll(targetFileName, os.ModePerm) 418 | writeFiles(context, targetFileName, file.dirFiles) 419 | } else { 420 | fmt.Printf("Creating file: %s (%d)\n", targetFileName, file.size) 421 | writeFileData(context, targetFileName, file.cluster, file.size) 422 | } 423 | } 424 | } 425 | 426 | //***************************************************************************** 427 | func writeFileData(context contextType, targetFileName string, cluster int, size int) { 428 | clusterChain := getClusterChain(context.fat, cluster) 429 | remainingBytes := size 430 | 431 | f, err := os.Create(targetFileName) 432 | check(err) 433 | defer f.Close() 434 | 435 | for i := 0; i < len(clusterChain); i++ { 436 | cluster := clusterChain[i] 437 | clusterData := getClusterData(context, cluster) 438 | unClusterFuck(clusterData) 439 | 440 | if len(clusterData) <= remainingBytes { 441 | _, err := f.Write(clusterData) 442 | check(err) 443 | //fmt.Printf("wrote %d bytes\n", count) 444 | remainingBytes -= len(clusterData) 445 | } else { 446 | _, err := f.Write(clusterData[0:remainingBytes]) 447 | check(err) 448 | //fmt.Printf("wrote final %d bytes\n", count) 449 | break 450 | } 451 | } 452 | 453 | f.Sync() 454 | } 455 | 456 | //***************************************************************************** 457 | // Utilities 458 | //***************************************************************************** 459 | func readSector(file *os.File, sector int) []byte { 460 | 461 | //fmt.Printf("Reading sector " + strconv.Itoa(int(sector)) + "\n"); 462 | 463 | buffer := make([]byte, SECTOR_SIZE) 464 | 465 | pos := int64(sector * SECTOR_SIZE) 466 | file.Seek(pos, 0) 467 | 468 | _, err := file.Read(buffer) 469 | if err != nil { 470 | check(err) 471 | } 472 | return buffer 473 | } 474 | 475 | //***************************************************************************** 476 | func getUint8(buffer []byte, pos int) int { 477 | return int(buffer[pos]) 478 | } 479 | 480 | //***************************************************************************** 481 | func getUint16(buffer []byte, pos int) int { 482 | return int(binary.LittleEndian.Uint16(buffer[pos:])) 483 | } 484 | 485 | //***************************************************************************** 486 | func getUint32(buffer []byte, pos int) int { 487 | return int(binary.LittleEndian.Uint32(buffer[pos:])) 488 | } 489 | 490 | //***************************************************************************** 491 | func getInt32(buffer []byte, pos int) int { 492 | return int(binary.LittleEndian.Uint32(buffer[pos:])) 493 | } 494 | 495 | //***************************************************************************** 496 | func check(e error) { 497 | if e != nil { 498 | panic(e) 499 | } 500 | } 501 | 502 | //***************************************************************************** 503 | func showPartitions(partitionSectors []int) { 504 | fmt.Printf("===========\n") 505 | fmt.Printf("Partitions:\n") 506 | fmt.Printf("===========\n") 507 | fmt.Printf("%v\n", partitionSectors) 508 | } 509 | 510 | //***************************************************************************** 511 | func showBootRecord(bootRecord bootRecordType) { 512 | fmt.Printf("===========\n") 513 | fmt.Printf("Boot Record:\n") 514 | fmt.Printf("===========\n") 515 | fmt.Printf("%v\n", bootRecord) 516 | } 517 | 518 | //***************************************************************************** 519 | func showFAT(fat []byte) { 520 | fmt.Printf("===========\n") 521 | fmt.Printf("FAT:\n") 522 | fmt.Printf("===========\n") 523 | //fmt.Printf("%v\n", fat) 524 | } 525 | 526 | //***************************************************************************** 527 | func showFileStructure(files []fileInfoType) { 528 | fmt.Printf("===============\n") 529 | fmt.Printf("File structure:\n") 530 | fmt.Printf("===============\n") 531 | //fmt.Printf("%v\n", files) 532 | for i := 0; i < len(files); i++ { 533 | fmt.Printf("%s.%s\n", strings.TrimSpace(files[i].name), files[i].ext) 534 | } 535 | } 536 | -------------------------------------------------------------------------------- /vs2reaper/vsio/vsio.go: -------------------------------------------------------------------------------- 1 | package vsio 2 | 3 | import ( 4 | "bufio" 5 | "encoding/binary" 6 | "errors" 7 | "fmt" 8 | "io" 9 | "os" 10 | "strconv" 11 | "strings" 12 | "vs/decode" 13 | //"context" 14 | ) 15 | 16 | const SECTOR_SIZE = 512 17 | const SECTORS_PER_CLUSTER = 64 18 | const SECTORS_PER_FAT = 256 19 | const DIRECTORY = 16 20 | 21 | type bootRecordType struct { 22 | formattingOS string 23 | bytesPerSector int 24 | sectorsPerCluster int 25 | reservedSectors int 26 | fatCopies int 27 | maxRootEntries int 28 | sectorsPerFAT int 29 | sectorsInPart int 30 | rootStartSector int 31 | clusterStartSector int 32 | fatStartSector int 33 | } 34 | 35 | type fileInfoType struct { 36 | name string 37 | ext string 38 | attr int 39 | cluster int 40 | size int 41 | data []byte 42 | dirFiles []fileInfoType 43 | clusters []int 44 | isDirectory bool 45 | bootRecord bootRecordType 46 | fat []byte 47 | partition int 48 | songIndex map[string]string 49 | } 50 | 51 | type contextType struct { 52 | file *os.File 53 | bootRecord bootRecordType 54 | fat []byte 55 | partition int 56 | } 57 | 58 | var partitionSongIndexMap = make(map[int](map[string]string)) 59 | 60 | var vsDrive *os.File 61 | 62 | var vsMachine string 63 | var vsPartitionCount int 64 | var vsPartitionSize int 65 | 66 | //***************************************************************************** 67 | func List() { 68 | allFiles := getList() 69 | showList(allFiles) 70 | } 71 | 72 | //***************************************************************************** 73 | func ConvertFirst(songName string, targetDir string) { 74 | 75 | allFiles := getList() 76 | 77 | file, err := findSongFileByFriendlyName(allFiles, songName) 78 | check(err) 79 | 80 | switch vsMachine { 81 | case "VS-1680": 82 | convertVS1880ToReaper(file, targetDir) 83 | case "VS-1880": 84 | convertVS1880ToReaper(file, targetDir) 85 | default: 86 | fmt.Printf("Unsupported VS format: %s\n", vsMachine) 87 | } 88 | } 89 | 90 | //***************************************************************************** 91 | func Convert(songName string, partition int, targetDir string) { 92 | 93 | allFiles := getList() 94 | 95 | file, err := findSongFileOnPartition(allFiles, songName, partition) 96 | check(err) 97 | 98 | switch vsMachine { 99 | case "VS-1680": 100 | convertVS1880ToReaper(file, targetDir) 101 | case "VS-1880": 102 | convertVS1880ToReaper(file, targetDir) 103 | default: 104 | fmt.Printf("Unsupported VS format: %s\n", vsMachine) 105 | } 106 | } 107 | 108 | //***************************************************************************** 109 | func getList() []fileInfoType { 110 | 111 | // Find the attached VS drive, if any 112 | findRolandDrive() 113 | 114 | // Get the start sectors for each partition 115 | partitionSectors := getPartitionSectors(vsDrive) 116 | 117 | allFiles := getDirectoryEntriesForAllPartitions(partitionSectors) 118 | 119 | getSongIndexes(allFiles) 120 | 121 | return allFiles 122 | } 123 | 124 | //***************************************************************************** 125 | func getDirectoryEntriesForAllPartitions(partitionSectors []int) []fileInfoType { 126 | 127 | allFiles := make([]fileInfoType, 0, 1000) 128 | 129 | vsPartitionSize = (partitionSectors[1] - partitionSectors[0]) * 512 130 | vsPartitionCount = 0 131 | 132 | vsPartitionGBs := int(vsPartitionSize / 1000000000) 133 | 134 | for partition := 0; partition < len(partitionSectors); partition++ { 135 | // Read the boot record for the partition 136 | bootRecord, err := getBootRecord(vsDrive, partitionSectors[partition]) 137 | 138 | if err == nil { 139 | vsPartitionCount++ 140 | } else { 141 | break 142 | } 143 | //showBootRecord(bootRecord); 144 | 145 | context := newContext(vsDrive, bootRecord) 146 | 147 | fat := getFAT(context) 148 | //showFAT(fat); 149 | 150 | context.fat = fat 151 | context.partition = partition 152 | 153 | // Get the root directory, and recurse to all (only one level!) subdirectories. 154 | files := getDirectoryEntries(context, bootRecord.rootStartSector, false) 155 | //showFileStructure(files); 156 | 157 | //file, err := findSongListOnPartition(files, partition) 158 | 159 | allFiles = append(allFiles, files...) 160 | } 161 | 162 | fileInfo := allFiles[0] 163 | vsMachine = getFormatForExtension(fileInfo.ext) 164 | 165 | fmt.Printf("VS Format: %s\n", vsMachine) 166 | fmt.Printf("Partition Size: %dGB\n", vsPartitionGBs) 167 | fmt.Printf("Partition Count: %d\n\n", vsPartitionCount) 168 | 169 | return allFiles 170 | } 171 | 172 | //***************************************************************************** 173 | func newContext(file *os.File, bootRecord bootRecordType) contextType { 174 | var context contextType 175 | context.file = file 176 | context.bootRecord = bootRecord 177 | return context 178 | } 179 | 180 | //***************************************************************************** 181 | func findRolandDrive() { 182 | // Find the Roland VS drive - look at up to 10 drives 183 | for i := 0; i < 10; i++ { 184 | drive := "\\\\.\\PhysicalDrive" + strconv.Itoa(i) 185 | file, err := os.Open(drive) 186 | if err != nil { 187 | continue 188 | } 189 | if isRolandDrive(file) { 190 | vsDrive = file 191 | fmt.Printf("\nFound Roland VS drive at: %s\n\n", drive) 192 | return 193 | } 194 | } 195 | 196 | abort("\nUnable to locate an attached Roland VS drive") 197 | 198 | //return nil 199 | } 200 | 201 | //***************************************************************************** 202 | func isRolandDrive(file *os.File) bool { 203 | // Check for the signature 204 | buffer := readSector(file, 63) 205 | return "Roland" == string(buffer[3:9]) 206 | } 207 | 208 | //***************************************************************************** 209 | func getPartitionSectors(file *os.File) []int { 210 | 211 | partitionSectors := make([]int, 12) 212 | partitionOffset := 446 213 | 214 | buffer := readSector(file, 0) 215 | 216 | for i := 0; i < 4; i++ { 217 | firstSector := getInt32(buffer, partitionOffset+8) 218 | partitionSectors[i] = firstSector 219 | partitionOffset += 16 220 | } 221 | 222 | // Extrapolate extra four partitions 223 | delta := partitionSectors[1] - partitionSectors[0] 224 | 225 | nextPartitionSector := partitionSectors[3] + delta 226 | for i := 0; i < 8; i++ { 227 | partitionSectors[i+4] = nextPartitionSector 228 | nextPartitionSector += delta 229 | } 230 | 231 | return partitionSectors 232 | } 233 | 234 | //***************************************************************************** 235 | func getBootRecord(file *os.File, sector int) (bootRecordType, error) { 236 | 237 | buffer := readSector(file, sector) 238 | 239 | var bootRecord bootRecordType 240 | 241 | bootRecord.formattingOS = string(buffer[3:9]) 242 | 243 | if "Roland" != bootRecord.formattingOS { 244 | return bootRecord, errors.New("Invalid boot record") 245 | } 246 | 247 | bootRecord.bytesPerSector = getUint16(buffer, 11) 248 | bootRecord.sectorsPerCluster = getUint8(buffer, 13) 249 | bootRecord.reservedSectors = getUint16(buffer, 14) 250 | bootRecord.fatCopies = getUint8(buffer, 16) 251 | bootRecord.maxRootEntries = getUint16(buffer, 17) 252 | bootRecord.sectorsPerFAT = getUint16(buffer, 22) 253 | bootRecord.sectorsInPart = getUint32(buffer, 28) 254 | bootRecord.rootStartSector = sector + bootRecord.reservedSectors + int(bootRecord.fatCopies)*bootRecord.sectorsPerFAT 255 | bootRecord.clusterStartSector = bootRecord.rootStartSector + (bootRecord.maxRootEntries*32)/bootRecord.bytesPerSector 256 | bootRecord.fatStartSector = sector + bootRecord.reservedSectors 257 | 258 | return bootRecord, nil 259 | } 260 | 261 | //***************************************************************************** 262 | func getFAT(context contextType) []byte { 263 | buffer := make([]byte, SECTOR_SIZE*SECTORS_PER_FAT) 264 | 265 | sector := context.bootRecord.fatStartSector 266 | 267 | pos := int64(sector * SECTOR_SIZE) 268 | context.file.Seek(pos, 0) 269 | 270 | _, err := io.ReadFull(context.file, buffer) 271 | if err != nil { 272 | check(err) 273 | } 274 | return buffer 275 | } 276 | 277 | //***************************************************************************** 278 | func getDirectoryEntries(context contextType, sector int, isChildDirectory bool) []fileInfoType { 279 | done := false 280 | files := make([]fileInfoType, 0, 5000) 281 | 282 | isClusterFucked := false 283 | 284 | // First byte of first sector should be ".", if not the cluster is scrambled 285 | if isChildDirectory { 286 | buffer := readSector(vsDrive, sector) 287 | if buffer[0] == ' ' { 288 | isClusterFucked = true 289 | } 290 | } 291 | 292 | for { 293 | buffer := readSector(vsDrive, sector) 294 | 295 | if isClusterFucked { 296 | unClusterFuck(buffer) 297 | } 298 | 299 | // Up to 16 32-byte entries per sector. 300 | for i := 0; i < 16; i++ { 301 | start := i * 32 302 | entry := buffer[start : start+32] 303 | if getUint8(buffer, 0) == 0 { 304 | done = true 305 | break 306 | } 307 | if !isWantedFile(entry) { 308 | continue // Ignore garbage 309 | } 310 | 311 | fileInfo := getFileInfo(context, entry) 312 | files = append(files, fileInfo) 313 | } 314 | if done { 315 | break 316 | } 317 | sector++ 318 | } 319 | 320 | return files 321 | } 322 | 323 | //***************************************************************************** 324 | func getSectorForCluster(clusterStartSector int, cluster int) int { 325 | // Convert cluster to sector. Note: Clusters start at 2! 326 | return clusterStartSector + (cluster-2)*SECTORS_PER_CLUSTER 327 | } 328 | 329 | //***************************************************************************** 330 | func unClusterFuck(buffer []byte) { 331 | // Sneaky Roland! Swap your partners: 332 | for i := 0; i < len(buffer); i += 2 { 333 | x := buffer[i] 334 | buffer[i] = buffer[i+1] 335 | buffer[i+1] = x 336 | } 337 | } 338 | 339 | //***************************************************************************** 340 | func isWantedFile(buffer []byte) bool { 341 | // As far as I know, any file we care about starts with "V" on the file extension. 342 | var firstCharOfExt = string(buffer[8:9]) 343 | return firstCharOfExt == "V" 344 | } 345 | 346 | //***************************************************************************** 347 | /* 348 | func getExtension(buffer []byte) string { 349 | // As far as I know, any file we care about starts with "V" on the file extension. 350 | return string(buffer[8:11]) 351 | } 352 | */ 353 | //***************************************************************************** 354 | func getFileInfo(context contextType, buffer []byte) fileInfoType { 355 | 356 | var fileInfo fileInfoType 357 | 358 | fileInfo.name = strings.TrimSpace(string(buffer[0:8])) 359 | 360 | fileInfo.ext = string(buffer[8:11]) 361 | fileInfo.attr = getUint8(buffer, 11) 362 | fileInfo.cluster = getUint16(buffer, 26) 363 | fileInfo.size = getUint32(buffer, 28) 364 | fileInfo.isDirectory = (fileInfo.attr == DIRECTORY) 365 | fileInfo.bootRecord = context.bootRecord 366 | fileInfo.fat = context.fat 367 | fileInfo.partition = context.partition 368 | 369 | sector := getSectorForCluster(context.bootRecord.clusterStartSector, fileInfo.cluster) 370 | 371 | if fileInfo.isDirectory { 372 | fileInfo.dirFiles = getDirectoryEntries(context, sector, true) 373 | } else { 374 | fileInfo.clusters = getClusterChain(context.fat, fileInfo.cluster) 375 | } 376 | 377 | return fileInfo 378 | } 379 | 380 | //***************************************************************************** 381 | func getClusterChain(fat []byte, cluster int) []int { 382 | chain := make([]int, 0, 100000) 383 | 384 | chain = append(chain, cluster) 385 | 386 | index := cluster * 2 387 | for { 388 | nextCluster := getUint16(fat, index) 389 | if nextCluster == 0x0000 { 390 | fmt.Printf("AVAILABLE CLUSTER\n") 391 | } else if nextCluster <= 0xffef { 392 | //fmt.Printf("USED CLUSTER\n") 393 | chain = append(chain, nextCluster) 394 | index = nextCluster * 2 395 | } else if nextCluster > 0xffef && nextCluster <= 0xfff6 { 396 | fmt.Printf("RESERVED CLUSTER\n") 397 | } else if nextCluster == 0xfff7 { 398 | fmt.Printf("BAD CLUSTER\n") 399 | } else if nextCluster >= 0xfff8 { 400 | break 401 | } 402 | } 403 | return chain 404 | } 405 | 406 | //***************************************************************************** 407 | func getClusterData(clusterStartSector int, cluster int) []byte { //TBD 408 | sector := getSectorForCluster(clusterStartSector, cluster) 409 | buffer := make([]byte, SECTOR_SIZE*SECTORS_PER_CLUSTER) 410 | 411 | pos := int64(sector * SECTOR_SIZE) 412 | vsDrive.Seek(pos, 0) 413 | 414 | _, err := io.ReadFull(vsDrive, buffer) 415 | if err != nil { 416 | check(err) 417 | } 418 | return buffer 419 | } 420 | 421 | //***************************************************************************** 422 | func getSongIndexes(files []fileInfoType) { 423 | for i := 0; i < len(files); i++ { 424 | file := &files[i] 425 | if file.name == "SONGLIST" { 426 | file.songIndex = getSongIndex(*file) 427 | partitionSongIndexMap[file.partition] = file.songIndex 428 | } 429 | } 430 | } 431 | 432 | //***************************************************************************** 433 | func showList(files []fileInfoType) { 434 | fmt.Printf("Partition Song\n") 435 | fmt.Printf("--------------------------\n") 436 | for i := 0; i < len(files); i++ { 437 | file := files[i] 438 | if file.songIndex != nil { 439 | for key, _ := range file.songIndex { 440 | //fmt.Printf("%d \"%s\" \"%s\"\n", file.partition, key, value) 441 | fmt.Printf("%d \"%s\"\n", file.partition, key) 442 | } 443 | } 444 | } 445 | } 446 | 447 | //***************************************************************************** 448 | func readFileComplete(file fileInfoType) []byte { 449 | // For small files <1MB 450 | clusterChain := getClusterChain(file.fat, file.cluster) 451 | 452 | buffer := make([]byte, file.size) 453 | 454 | remainingBytes := file.size 455 | 456 | for i := 0; i < len(clusterChain); i++ { 457 | cluster := clusterChain[i] 458 | clusterData := getClusterData(file.bootRecord.clusterStartSector, cluster) 459 | unClusterFuck(clusterData) 460 | 461 | bufferPos := i * len(clusterData) 462 | if len(clusterData) <= remainingBytes { 463 | copy(buffer[bufferPos:], clusterData[0:]) 464 | remainingBytes -= len(clusterData) 465 | } else { 466 | copy(buffer[bufferPos:bufferPos+remainingBytes], clusterData[0:remainingBytes]) 467 | break 468 | } 469 | } 470 | return buffer 471 | } 472 | 473 | //***************************************************************************** 474 | func getSongIndex(file fileInfoType) map[string]string { 475 | 476 | buffer := readFileComplete(file) 477 | 478 | songIndex := make(map[string]string) 479 | 480 | numSongs := getUint16BE(buffer, 0) 481 | 482 | for i := 0; i < numSongs; i++ { 483 | 484 | pos := 6 + i*38 485 | songNumber := getUint16BE(buffer, pos) 486 | song := getString(buffer, pos+2, 12) 487 | 488 | songFileName := fmt.Sprintf("SONG%04d", songNumber) 489 | 490 | songIndex[song] = songFileName 491 | } 492 | 493 | return songIndex 494 | } 495 | 496 | //***************************************************************************** 497 | // Utilities 498 | //***************************************************************************** 499 | func readSector(file *os.File, sector int) []byte { 500 | 501 | //fmt.Printf("Reading sector " + strconv.Itoa(int(sector)) + "\n"); 502 | 503 | buffer := make([]byte, SECTOR_SIZE) 504 | 505 | pos := int64(sector * SECTOR_SIZE) 506 | file.Seek(pos, 0) 507 | 508 | _, err := io.ReadFull(file, buffer) 509 | if err != nil { 510 | check(err) 511 | } 512 | return buffer 513 | } 514 | 515 | //***************************************************************************** 516 | func getUint8(buffer []byte, pos int) int { 517 | return int(buffer[pos]) 518 | } 519 | 520 | //***************************************************************************** 521 | func getUint16(buffer []byte, pos int) int { 522 | return int(binary.LittleEndian.Uint16(buffer[pos : pos+2])) 523 | } 524 | 525 | //***************************************************************************** 526 | func getUint16BE(buffer []byte, pos int) int { 527 | return int(binary.BigEndian.Uint16(buffer[pos : pos+2])) 528 | } 529 | 530 | //***************************************************************************** 531 | func getUint32(buffer []byte, pos int) int { 532 | return int(binary.LittleEndian.Uint32(buffer[pos : pos+4])) 533 | } 534 | 535 | //***************************************************************************** 536 | func getUint32BE(buffer []byte, pos int) int { 537 | return int(binary.BigEndian.Uint32(buffer[pos : pos+4])) 538 | } 539 | 540 | //***************************************************************************** 541 | func getInt32(buffer []byte, pos int) int { 542 | return int(binary.LittleEndian.Uint32(buffer[pos : pos+4])) 543 | } 544 | 545 | //***************************************************************************** 546 | func getInt32BE(buffer []byte, pos int) int { 547 | return int(binary.BigEndian.Uint32(buffer[pos : pos+4])) 548 | } 549 | 550 | //***************************************************************************** 551 | func getString(buffer []byte, pos int, size int) string { 552 | return string(buffer[pos : pos+size]) 553 | } 554 | 555 | //***************************************************************************** 556 | func check(e error) { 557 | if e != nil { 558 | //panic(e) 559 | abort(e.Error()) 560 | } 561 | } 562 | 563 | //***************************************************************************** 564 | func showPartitions(partitionSectors []int) { 565 | fmt.Printf("===========\n") 566 | fmt.Printf("Partitions:\n") 567 | fmt.Printf("===========\n") 568 | fmt.Printf("%v\n", partitionSectors) 569 | } 570 | 571 | //***************************************************************************** 572 | func showBootRecord(bootRecord bootRecordType) { 573 | fmt.Printf("===========\n") 574 | fmt.Printf("Boot Record:\n") 575 | fmt.Printf("===========\n") 576 | fmt.Printf("%v\n", bootRecord) 577 | } 578 | 579 | //***************************************************************************** 580 | func showFAT(fat []byte) { 581 | fmt.Printf("===========\n") 582 | fmt.Printf("FAT:\n") 583 | fmt.Printf("===========\n") 584 | //fmt.Printf("%v\n", fat) 585 | } 586 | 587 | //***************************************************************************** 588 | func showFileStructure(files []fileInfoType) { 589 | fmt.Printf("===============\n") 590 | fmt.Printf("File structure:\n") 591 | fmt.Printf("===============\n") 592 | //fmt.Printf("%v\n", files) 593 | for i := 0; i < len(files); i++ { 594 | fmt.Printf("%s.%s\n", strings.TrimSpace(files[i].name), files[i].ext) 595 | } 596 | } 597 | 598 | //***************************************************************************** 599 | func getSongInfo(vsFormat string, file fileInfoType) (string, int, int) { 600 | 601 | buffer := readFileComplete(file) 602 | 603 | var songName string 604 | var rate byte 605 | var mode byte 606 | 607 | if getFormat(buffer[0]) == "VS-2480" { 608 | songName = getString(buffer, 10, 12) 609 | rate = buffer[22] 610 | mode = buffer[23] 611 | } else { 612 | songName = getString(buffer, 6, 12) 613 | rate = buffer[18] 614 | mode = buffer[19] 615 | } 616 | 617 | return songName, int(rate), int(mode) 618 | } 619 | 620 | //***************************************************************************** 621 | func getSampleRate(rate int) float64 { 622 | switch rate { 623 | case 0: 624 | return 48000 625 | case 1: 626 | return 44100 627 | case 2: 628 | return 32000 629 | case 3: 630 | return 96000 631 | case 4: 632 | return 88200 633 | default: 634 | return 64000 635 | } 636 | } 637 | 638 | //***************************************************************************** 639 | func getModeName(mode int) string { 640 | switch mode { 641 | case 0: 642 | return "MT1" 643 | case 1: 644 | return "MT2" 645 | case 2: 646 | return "LIV" 647 | case 3: 648 | return "M16" 649 | case 4: 650 | return "CDR" 651 | case 5: 652 | return "MTP" 653 | case 6: 654 | return "LV2" 655 | case 7: 656 | return "VSR" 657 | case 8: 658 | return "M24" 659 | default: 660 | return "UNKNOWN" 661 | } 662 | } 663 | 664 | //***************************************************************************** 665 | func getBitDepth(mode int) int { 666 | switch mode { 667 | case 0: 668 | return 16 669 | case 1: 670 | return 16 671 | case 2: 672 | return 16 673 | case 3: 674 | return 16 675 | case 4: 676 | return 16 677 | case 5: 678 | return 24 679 | case 6: 680 | return 16 681 | case 7: 682 | return 16 683 | case 8: 684 | return 24 685 | default: 686 | return 16 687 | } 688 | } 689 | 690 | //***************************************************************************** 691 | func getFormat(format byte) string { 692 | switch format { 693 | case 1: 694 | return "VS-1680" 695 | case 2: 696 | return "VS-1880" 697 | case 3: 698 | return "VS-2480" 699 | case 4: 700 | return "VS-880" 701 | case 5: 702 | return "VS-880EX" 703 | case 6: 704 | return "VS-890" 705 | case 7: 706 | return "VS-840" 707 | case 8: 708 | return "VS-840GX" 709 | default: 710 | return "UNKNOWN" 711 | } 712 | } 713 | 714 | //***************************************************************************** 715 | func getFormatForExtension(ext string) string { 716 | switch ext { 717 | case "VR6": 718 | return "VS-1680" 719 | case "VR5": 720 | return "VS-1880" 721 | case "VR1": 722 | return "VS-2480" 723 | case "VR9": 724 | return "VS-880EX" 725 | case "VR7": 726 | return "VS-890" 727 | case "VR4": 728 | return "VS-840GX" 729 | case "VR8": 730 | // It could be a VS-880 or a VS-840! 731 | // Heuristic: If SYSTEM.VS1 exists, its a VS-840 732 | // TBD 733 | return "VS-840" 734 | default: 735 | return "UNKNOWN" 736 | } 737 | } 738 | 739 | //***************************************************************************** 740 | /* 741 | func findSongListOnPartition(files []fileInfoType, partition int) (fileInfoType, error) { 742 | var nothing fileInfoType 743 | for _, file := range files { 744 | if file.name == "SONGLIST" && file.partition == partition { 745 | return file, nil 746 | } 747 | } 748 | return nothing, errors.New("Songlist file not found") 749 | } 750 | */ 751 | //***************************************************************************** 752 | func findSongFileOnPartition(files []fileInfoType, friendlyName string, partition int) (fileInfoType, error) { 753 | fileName := partitionSongIndexMap[partition][friendlyName] 754 | var nothing fileInfoType 755 | for _, file := range files { 756 | if file.name == fileName && file.partition == partition { 757 | return file, nil 758 | } 759 | } 760 | return nothing, errors.New(fmt.Sprintf("File not found: %s", friendlyName)) 761 | } 762 | 763 | //***************************************************************************** 764 | func findSongFileByFriendlyName(files []fileInfoType, friendlyName string) (fileInfoType, error) { 765 | var nothing fileInfoType 766 | for _, file := range files { 767 | fileName := partitionSongIndexMap[file.partition][friendlyName] 768 | if file.name == fileName { 769 | return file, nil 770 | } 771 | } 772 | return nothing, errors.New(fmt.Sprintf("File not found: %s", friendlyName)) 773 | } 774 | 775 | //***************************************************************************** 776 | func findFileInDirectory(songDir fileInfoType, fileName string) (fileInfoType, error) { 777 | var nothing fileInfoType 778 | for _, file := range songDir.dirFiles { 779 | if file.name == fileName { 780 | return file, nil 781 | } 782 | } 783 | return nothing, errors.New("File not found") 784 | } 785 | 786 | //***************************************************************************** 787 | func convertVS1880ToReaper(songDirPath fileInfoType, wavDirPath string) { 788 | 789 | //var bitDepth int 790 | 791 | // Create the target directory for the Reaper conversion 792 | os.MkdirAll(wavDirPath, os.ModePerm) 793 | 794 | // Get song and event file paths. 795 | 796 | songFile, err := findFileInDirectory(songDirPath, "SONG") 797 | check(err) 798 | 799 | eventFile, err := findFileInDirectory(songDirPath, "EVENTLST") 800 | check(err) 801 | 802 | // Determine sample rate from song file. 803 | 804 | songName, rate, mode := getSongInfo(vsMachine, songFile) 805 | 806 | modeName := getModeName(mode) 807 | 808 | switch modeName { 809 | case "MTP": 810 | case "MT1": 811 | case "MT2": 812 | case "M16": 813 | case "M24": // Actually won't happen with 1680/1880! 814 | default: 815 | abort(fmt.Sprintf("%s encodings are currently not supported.", modeName)) 816 | } 817 | 818 | // Open the Reaper project file to create 819 | reaperProject := fmt.Sprintf("%s/%s.rpp", wavDirPath, songName) 820 | fout, err := os.Create(reaperProject) 821 | check(err) 822 | defer fout.Close() 823 | reaperWriter := bufio.NewWriter(fout) 824 | defer reaperWriter.Flush() 825 | 826 | vsSampleRate := getSampleRate(rate) 827 | 828 | // Show some info 829 | fmt.Printf("Song: %s\n", songName) 830 | fmt.Printf("Mode: %s\n", getModeName(mode)) 831 | fmt.Printf("Sample Rate: %d\n", int(vsSampleRate)) 832 | fmt.Printf("Bit Depth: %d\n", getBitDepth(mode)) 833 | fmt.Printf("Partition: %d\n", songDirPath.partition) 834 | fmt.Printf("\n") 835 | 836 | // Open the event file. 837 | 838 | buffer := readFileComplete(eventFile) 839 | 840 | // Skip past phrases 841 | 842 | numPhrases := getUint16BE(buffer, 16) 843 | 844 | pos := 18 + numPhrases*64 845 | 846 | trackCount := 18 847 | if vsMachine == "VS-1680" { 848 | trackCount = 16 849 | } 850 | 851 | reaperWriter.WriteString("\n") 906 | 907 | message := fmt.Sprintf("Converting %s to %s", takeFileName, wavFilePath) 908 | takeFile, err := findFileInDirectory(songDirPath, takeFileName) 909 | check(err) 910 | switch modeName { 911 | case "MTP": 912 | convertMTP(takeFile, wavFilePath, int(vsSampleRate), message) 913 | case "MT1": 914 | convertMT1(takeFile, wavFilePath, int(vsSampleRate), message) 915 | case "MT2": 916 | convertMT2(takeFile, wavFilePath, int(vsSampleRate), message) 917 | case "M16": 918 | convertM16(takeFile, wavFilePath, int(vsSampleRate), message) 919 | case "M24": 920 | convertM24(takeFile, wavFilePath, int(vsSampleRate), message) // Won't happen with 1680/1880 921 | default: 922 | } 923 | fmt.Printf("\n") 924 | 925 | pos += 64 926 | } 927 | reaperWriter.WriteString(">\n") 928 | } 929 | } 930 | reaperWriter.WriteString(">\n") 931 | fmt.Printf("\nCreated Reaper project file: %s\n", reaperProject) 932 | } 933 | 934 | //***************************************************************************** 935 | func convertMTP(file fileInfoType, outFileName string, sampleRate int, message string) { 936 | cluster := file.cluster 937 | clusterChain := getClusterChain(file.fat, cluster) 938 | 939 | f, err := os.Create(outFileName) 940 | check(err) 941 | defer f.Close() 942 | 943 | wavWriter := bufio.NewWriter(f) 944 | 945 | fileSize := file.size 946 | totalBlocks := int(fileSize / 16) 947 | 948 | // Write the WAV header 949 | numSamples := totalBlocks * 16 950 | writeWavHeader(wavWriter, numSamples, sampleRate, 24, 1) 951 | 952 | in := make([]uint8, 16) 953 | out := make([]int, 16) 954 | d0 := 0 955 | 956 | for i := 0; i < len(clusterChain); i++ { 957 | cluster := clusterChain[i] 958 | clusterData := getClusterData(file.bootRecord.clusterStartSector, cluster) 959 | unClusterFuck(clusterData) 960 | 961 | numBlocks := len(clusterData) / 16 962 | 963 | var percentComplete float32 = float32(i+1) / float32(len(clusterChain)) * 100.0 964 | 965 | fmt.Printf("\r%s: %3d%%", message, int(percentComplete)) 966 | 967 | for j := 0; j < numBlocks; j++ { 968 | clusterPos := j * 16 969 | 970 | copy(in, clusterData[clusterPos:clusterPos+16]) 971 | 972 | decode.DecodeMTP(d0, in, out) 973 | writeWavSamples24(wavWriter, out) 974 | d0 = out[15] 975 | } 976 | wavWriter.Flush() 977 | f.Sync() 978 | } 979 | } 980 | 981 | //***************************************************************************** 982 | func convertMT1(file fileInfoType, outFileName string, sampleRate int, message string) { 983 | cluster := file.cluster 984 | clusterChain := getClusterChain(file.fat, cluster) 985 | 986 | f, err := os.Create(outFileName) 987 | check(err) 988 | defer f.Close() 989 | 990 | wavWriter := bufio.NewWriter(f) 991 | 992 | fileSize := file.size 993 | totalBlocks := int(fileSize / 16) 994 | 995 | // Write the WAV header 996 | numSamples := totalBlocks * 16 997 | writeWavHeader(wavWriter, numSamples, sampleRate, 16, 1) 998 | 999 | in := make([]uint8, 16) 1000 | out := make([]int, 16) 1001 | d0 := 0 1002 | 1003 | for i := 0; i < len(clusterChain); i++ { 1004 | cluster := clusterChain[i] 1005 | clusterData := getClusterData(file.bootRecord.clusterStartSector, cluster) 1006 | unClusterFuck(clusterData) 1007 | 1008 | numBlocks := len(clusterData) / 16 1009 | 1010 | var percentComplete float32 = float32(i+1) / float32(len(clusterChain)) * 100.0 1011 | 1012 | fmt.Printf("\r%s: %3d%%", message, int(percentComplete)) 1013 | 1014 | for j := 0; j < numBlocks; j++ { 1015 | clusterPos := j * 16 1016 | 1017 | copy(in, clusterData[clusterPos:clusterPos+16]) 1018 | 1019 | decode.DecodeMT1(d0, in, out) 1020 | writeWavSamples16(wavWriter, out) 1021 | d0 = out[15] 1022 | } 1023 | wavWriter.Flush() 1024 | f.Sync() 1025 | } 1026 | } 1027 | 1028 | //***************************************************************************** 1029 | func convertMT2(file fileInfoType, outFileName string, sampleRate int, message string) { 1030 | cluster := file.cluster 1031 | clusterChain := getClusterChain(file.fat, cluster) 1032 | 1033 | f, err := os.Create(outFileName) 1034 | check(err) 1035 | defer f.Close() 1036 | 1037 | wavWriter := bufio.NewWriter(f) 1038 | 1039 | fileSize := file.size 1040 | totalBlocks := int(fileSize / 12) 1041 | 1042 | // Write the WAV header 1043 | numSamples := totalBlocks * 16 1044 | writeWavHeader(wavWriter, numSamples, sampleRate, 16, 1) 1045 | 1046 | in := make([]uint8, 12) 1047 | out := make([]int, 16) 1048 | d0 := 0 1049 | 1050 | for i := 0; i < len(clusterChain); i++ { 1051 | cluster := clusterChain[i] 1052 | clusterData := getClusterData(file.bootRecord.clusterStartSector, cluster) 1053 | unClusterFuck(clusterData) 1054 | 1055 | numBlocks := len(clusterData) / 12 1056 | 1057 | var percentComplete float32 = float32(i+1) / float32(len(clusterChain)) * 100.0 1058 | 1059 | fmt.Printf("\r%s: %3d%%", message, int(percentComplete)) 1060 | 1061 | for j := 0; j < numBlocks; j++ { 1062 | clusterPos := j * 12 1063 | 1064 | copy(in, clusterData[clusterPos:clusterPos+12]) 1065 | 1066 | decode.DecodeMT2(d0, in, out) 1067 | writeWavSamples16(wavWriter, out) 1068 | d0 = out[15] 1069 | } 1070 | wavWriter.Flush() 1071 | f.Sync() 1072 | } 1073 | } 1074 | 1075 | //***************************************************************************** 1076 | func convertM16(file fileInfoType, outFileName string, sampleRate int, message string) { 1077 | cluster := file.cluster 1078 | clusterChain := getClusterChain(file.fat, cluster) 1079 | 1080 | f, err := os.Create(outFileName) 1081 | check(err) 1082 | defer f.Close() 1083 | 1084 | wavWriter := bufio.NewWriter(f) 1085 | 1086 | fileSize := file.size 1087 | totalBlocks := int(fileSize / 32) 1088 | 1089 | // Write the WAV header 1090 | numSamples := totalBlocks * 16 1091 | writeWavHeader(wavWriter, numSamples, sampleRate, 16, 1) 1092 | 1093 | in := make([]uint8, 32) 1094 | out := make([]int, 16) 1095 | d0 := 0 1096 | 1097 | for i := 0; i < len(clusterChain); i++ { 1098 | cluster := clusterChain[i] 1099 | clusterData := getClusterData(file.bootRecord.clusterStartSector, cluster) 1100 | unClusterFuck(clusterData) 1101 | 1102 | numBlocks := len(clusterData) / 32 1103 | 1104 | var percentComplete float32 = float32(i+1) / float32(len(clusterChain)) * 100.0 1105 | 1106 | fmt.Printf("\r%s: %3d%%", message, int(percentComplete)) 1107 | 1108 | for j := 0; j < numBlocks; j++ { 1109 | clusterPos := j * 32 1110 | 1111 | copy(in, clusterData[clusterPos:clusterPos+32]) 1112 | 1113 | decode.DecodeM16(d0, in, out) 1114 | writeWavSamples16(wavWriter, out) 1115 | d0 = out[15] 1116 | } 1117 | wavWriter.Flush() 1118 | f.Sync() 1119 | } 1120 | } 1121 | 1122 | //***************************************************************************** 1123 | func convertM24(file fileInfoType, outFileName string, sampleRate int, message string) { 1124 | cluster := file.cluster 1125 | clusterChain := getClusterChain(file.fat, cluster) 1126 | 1127 | f, err := os.Create(outFileName) 1128 | check(err) 1129 | defer f.Close() 1130 | 1131 | wavWriter := bufio.NewWriter(f) 1132 | 1133 | fileSize := file.size 1134 | totalBlocks := int(fileSize / 48) 1135 | 1136 | // Write the WAV header 1137 | numSamples := totalBlocks * 16 1138 | writeWavHeader(wavWriter, numSamples, sampleRate, 24, 1) 1139 | 1140 | in := make([]uint8, 48) 1141 | out := make([]int, 16) 1142 | d0 := 0 1143 | 1144 | for i := 0; i < len(clusterChain); i++ { 1145 | cluster := clusterChain[i] 1146 | clusterData := getClusterData(file.bootRecord.clusterStartSector, cluster) 1147 | unClusterFuck(clusterData) 1148 | 1149 | numBlocks := len(clusterData) / 48 1150 | 1151 | var percentComplete float32 = float32(i+1) / float32(len(clusterChain)) * 100.0 1152 | 1153 | fmt.Printf("\r%s: %3d%%", message, int(percentComplete)) 1154 | 1155 | for j := 0; j < numBlocks; j++ { 1156 | clusterPos := j * 48 1157 | 1158 | copy(in, clusterData[clusterPos:clusterPos+48]) 1159 | 1160 | decode.DecodeM24(d0, in, out) 1161 | writeWavSamples24(wavWriter, out) 1162 | d0 = out[15] 1163 | } 1164 | wavWriter.Flush() 1165 | f.Sync() 1166 | } 1167 | } 1168 | 1169 | //***************************************************************************** 1170 | func writeWavHeader(w *bufio.Writer, numSamples int, sampleRate int, bitDepth int, numChannels int) { 1171 | 1172 | bytesPerSample := bitDepth / 8 1173 | numBytes := numSamples * bytesPerSample * numChannels 1174 | numChunkBytes := numBytes + 38 1175 | 1176 | chunkid := []byte{0x52, 0x49, 0x46, 0x46} 1177 | 1178 | w.Write(chunkid) 1179 | 1180 | chunksize := []byte{ 1181 | BYTE_0(numChunkBytes), 1182 | BYTE_1(numChunkBytes), 1183 | BYTE_2(numChunkBytes), 1184 | BYTE_3(numChunkBytes), 1185 | } 1186 | 1187 | w.Write(chunksize) 1188 | 1189 | format := []byte{0x57, 0x41, 0x56, 0x45} 1190 | w.Write(format) 1191 | 1192 | subchunk1id := []byte{0x66, 0x6d, 0x74, 0x20} 1193 | w.Write(subchunk1id) 1194 | 1195 | subchunk1size := []byte{0x12, 0x00, 0x00, 0x00} 1196 | w.Write(subchunk1size) 1197 | 1198 | audioformat := []byte{0x01, 0x00} 1199 | w.Write(audioformat) 1200 | 1201 | numchannels := []byte{ 1202 | BYTE_0(numChannels), 1203 | BYTE_1(numChannels), 1204 | } 1205 | w.Write(numchannels) 1206 | 1207 | samplerate := []byte{ 1208 | BYTE_0(sampleRate), 1209 | BYTE_1(sampleRate), 1210 | BYTE_2(sampleRate), 1211 | BYTE_3(sampleRate), 1212 | } 1213 | w.Write(samplerate) 1214 | 1215 | byterate := []byte{ 1216 | BYTE_0(sampleRate * bytesPerSample * numChannels), 1217 | BYTE_1(sampleRate * bytesPerSample * numChannels), 1218 | BYTE_2(sampleRate * bytesPerSample * numChannels), 1219 | BYTE_3(sampleRate * bytesPerSample * numChannels), 1220 | } 1221 | w.Write(byterate) 1222 | 1223 | blockalign := []byte{ 1224 | BYTE_0(bytesPerSample * numChannels), 1225 | BYTE_1(bytesPerSample * numChannels), 1226 | } 1227 | w.Write(blockalign) 1228 | 1229 | bitspersample := []byte{ 1230 | BYTE_0(bitDepth), 1231 | BYTE_1(bitDepth), 1232 | } 1233 | w.Write(bitspersample) 1234 | 1235 | extraparamsize := []byte{0x00, 0x00} 1236 | w.Write(extraparamsize) 1237 | 1238 | subchunk2id := []byte{0x64, 0x61, 0x74, 0x61} 1239 | w.Write(subchunk2id) 1240 | 1241 | subchunk2size := []byte{ 1242 | BYTE_0(numBytes), 1243 | BYTE_1(numBytes), 1244 | BYTE_2(numBytes), 1245 | BYTE_3(numBytes), 1246 | } 1247 | w.Write(subchunk2size) 1248 | } 1249 | 1250 | //***************************************************************************** 1251 | func writeWavSamples16(w *bufio.Writer, samples []int) { 1252 | // Write it to the WAV (little-endian) 1253 | for i := 0; i < 16; i++ { 1254 | sample := []byte{ 1255 | BYTE_0(samples[i]), 1256 | BYTE_1(samples[i]), 1257 | } 1258 | w.Write(sample) 1259 | } 1260 | } 1261 | 1262 | //***************************************************************************** 1263 | func writeWavSamples24(w *bufio.Writer, samples []int) { 1264 | // Write it to the WAV (little-endian) 1265 | for i := 0; i < 16; i++ { 1266 | sample := []byte{ 1267 | BYTE_0(samples[i]), 1268 | BYTE_1(samples[i]), 1269 | BYTE_2(samples[i]), 1270 | } 1271 | w.Write(sample) 1272 | } 1273 | } 1274 | 1275 | //***************************************************************************** 1276 | func BYTE_0(xx int) byte { return byte(xx & 0xff) } 1277 | func BYTE_1(xx int) byte { return byte((xx >> 8) & 0xff) } 1278 | func BYTE_2(xx int) byte { return byte((xx >> 16) & 0xff) } 1279 | func BYTE_3(xx int) byte { return byte((xx >> 24) & 0xff) } 1280 | 1281 | //***************************************************************************** 1282 | func abort(message string) { 1283 | fmt.Printf("%s\n\n", message) 1284 | os.Exit(0) 1285 | } 1286 | --------------------------------------------------------------------------------