├── 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 |
--------------------------------------------------------------------------------