@@ -13,19 +13,169 @@ type |
| 13 |
function GetPastedCells: TSelection; |
13 |
function GetPastedCells: TSelection; |
| 14 |
procedure CopyCells(Selection: TSelection); |
14 |
procedure CopyCells(Selection: TSelection); |
| 15 |
|
15 |
|
|
|
16 |
function GetFamitrackerPastedCells: TSelection; |
|
|
17 |
|
| 16 |
implementation |
18 |
implementation |
| 17 |
|
19 |
|
| 18 |
function ParseCell(Cell: String): TSelectedCell; |
20 |
const |
|
|
21 |
// FamiTrackerTypes.h |
|
|
22 |
FTM_EFFECTS: array[0..44] of Integer = ( |
|
|
23 |
$0, //EF_NONE = 0, |
|
|
24 |
$F, //EF_SPEED, // Speed |
|
|
25 |
$B, //EF_JUMP, // Jump |
|
|
26 |
$D, //EF_SKIP, // Skip |
|
|
27 |
$0, //EF_HALT, // Halt |
|
|
28 |
$C, //EF_VOLUME, // Volume |
|
|
29 |
$3, //EF_PORTAMENTO, // Porta on |
|
|
30 |
$0, //EF_PORTAOFF, // Porta off // unused |
|
|
31 |
$0, //EF_SWEEPUP, // Sweep up |
|
|
32 |
$0, //EF_SWEEPDOWN, // Sweep down |
|
|
33 |
$0, //EF_ARPEGGIO, // Arpeggio |
|
|
34 |
$4, //EF_VIBRATO, // Vibrato |
|
|
35 |
$0, //EF_TREMOLO, // Tremolo |
|
|
36 |
$0, //EF_PITCH, // Pitch |
|
|
37 |
$0, //EF_DELAY, // Note delay |
|
|
38 |
$0, //EF_DAC, // DAC setting |
|
|
39 |
$1, //EF_PORTA_UP, // Portamento up |
|
|
40 |
$2, //EF_PORTA_DOWN, // Portamento down |
|
|
41 |
$9, //EF_DUTY_CYCLE, // Duty cycle |
|
|
42 |
$0, //EF_SAMPLE_OFFSET, // Sample offset |
|
|
43 |
$0, //EF_SLIDE_UP, // Slide up |
|
|
44 |
$0, //EF_SLIDE_DOWN, // Slide down |
|
|
45 |
$A, //EF_VOLUME_SLIDE, // Volume slide |
|
|
46 |
$E, //EF_NOTE_CUT, // Note cut |
|
|
47 |
$0, //EF_RETRIGGER, // DPCM retrigger |
|
|
48 |
$0, //EF_DELAYED_VOLUME, // // // Delayed channel volume |
|
|
49 |
$0, //EF_FDS_MOD_DEPTH, // FDS modulation depth |
|
|
50 |
$0, //EF_FDS_MOD_SPEED_HI, // FDS modulation speed hi |
|
|
51 |
$0, //EF_FDS_MOD_SPEED_LO, // FDS modulation speed lo |
|
|
52 |
$0, //EF_DPCM_PITCH, // DPCM Pitch |
|
|
53 |
$0, //EF_SUNSOFT_ENV_TYPE, // Sunsoft envelope type |
|
|
54 |
$0, //EF_SUNSOFT_ENV_HI, // Sunsoft envelope high |
|
|
55 |
$0, //EF_SUNSOFT_ENV_LO, // Sunsoft envelope low |
|
|
56 |
$0, //EF_SUNSOFT_NOISE, // // // 050B Sunsoft noise period |
|
|
57 |
$0, //EF_VRC7_PORT, // // // 050B VRC7 custom patch port |
|
|
58 |
$0, //EF_VRC7_WRITE, // // // 050B VRC7 custom patch write |
|
|
59 |
$E, //EF_NOTE_RELEASE, // // // Delayed release |
|
|
60 |
$0, //EF_GROOVE, // // // Groove |
|
|
61 |
$0, //EF_TRANSPOSE, // // // Delayed transpose |
|
|
62 |
$0, //EF_N163_WAVE_BUFFER, // // // N163 wave buffer |
|
|
63 |
$0, //EF_FDS_VOLUME, // // // FDS volume envelope |
|
|
64 |
$0, //EF_FDS_MOD_BIAS, // // // FDS auto-FM bias |
|
|
65 |
$0, //EF_PHASE_RESET, // Reset waveform phase without retriggering note (VRC6-only so far) |
|
|
66 |
$0, //EF_HARMONIC, // Multiply the note pitch by an integer |
|
|
67 |
$0 //EF_COUNT |
|
|
68 |
); |
|
|
69 |
|
|
|
70 |
type |
|
|
71 |
// PatternEditorTypes.h |
|
|
72 |
{$PACKENUM 4} // https://www.freepascal.org/docs-html/ref/refsu4.html |
|
|
73 |
TFTMColumn = ( |
|
|
74 |
COLUMN_NOTE = 0, |
|
|
75 |
COLUMN_INSTRUMENT = 1, |
|
|
76 |
COLUMN_VOLUME = 2, |
|
|
77 |
COLUMN_EFF1 = 3, |
|
|
78 |
COLUMN_EFF2 = 4, |
|
|
79 |
COLUMN_EFF3 = 5, |
|
|
80 |
COLUMN_EFF4 = 6 |
|
|
81 |
); |
|
|
82 |
{$PACKENUM 1} |
|
|
83 |
|
|
|
84 |
// PatternEditorTypes.h |
|
|
85 |
TFTMClipInfo = packed record |
|
|
86 |
Channels: Integer; |
|
|
87 |
Rows: Integer; |
|
|
88 |
StartColumn: TFTMColumn; |
|
|
89 |
EndColumn: TFTMColumn; |
|
|
90 |
OleInfo: record |
|
|
91 |
ChanOffset: Integer; |
|
|
92 |
RowOffset: Integer; |
|
|
93 |
end; |
|
|
94 |
end; |
|
|
95 |
|
|
|
96 |
// PatternNote.h |
|
|
97 |
TFTMChanNote = packed record |
|
|
98 |
Note: Byte; |
|
|
99 |
Octave: Byte; |
|
|
100 |
Vol: Byte; |
|
|
101 |
Instrument: Byte; |
|
|
102 |
EffNumber: array[0..3] of Byte; |
|
|
103 |
EffParam: array[0..3] of Byte; |
|
|
104 |
end; |
|
|
105 |
|
|
|
106 |
function GetFamitrackerPastedCells: TSelection; |
|
|
107 |
function FTMNoteToCell(Note: TFTMChanNote): TCell; |
|
|
108 |
begin |
|
|
109 |
case Note.Note of |
|
|
110 |
0, 13, 14, 15: Result.Note := NO_NOTE; |
|
|
111 |
else Result.Note := (C_3 + (Note.Note-1)) + (12*(Note.Octave - 1)); |
|
|
112 |
end; |
|
|
113 |
|
|
|
114 |
if Note.Instrument = 64 then |
|
|
115 |
Result.Instrument := 0 |
|
|
116 |
else |
|
|
117 |
Result.Instrument := Note.Instrument; |
| 19 |
|
118 |
|
| 20 |
function StrToInt_(S: String; out I: Integer; Hex: Boolean = False): Boolean; |
119 |
if Note.Note in [13, 14, 15] then begin |
|
|
120 |
Result.EffectCode := $E; |
|
|
121 |
Result.EffectParams.Value := $00; |
|
|
122 |
end else begin |
|
|
123 |
Result.EffectCode := FTM_EFFECTS[Note.EffNumber[0]]; |
|
|
124 |
Result.EffectParams.Value := Note.EffParam[0]; |
|
|
125 |
end; |
|
|
126 |
end; |
|
|
127 |
function FTMColumnToPart(Col: TFTMColumn): TCellPart; |
|
|
128 |
begin |
|
|
129 |
case Col of |
|
|
130 |
COLUMN_NOTE: Exit(cpNote); |
|
|
131 |
COLUMN_INSTRUMENT: Exit(cpInstrument); |
|
|
132 |
COLUMN_VOLUME: Exit(cpVolume); |
|
|
133 |
COLUMN_EFF1..COLUMN_EFF4: Exit(cpEffectParams); |
|
|
134 |
end; |
|
|
135 |
end; |
|
|
136 |
var |
|
|
137 |
ClipInfo: TFTMClipInfo; |
|
|
138 |
FTMNotes: packed array of TFTMChanNote; |
|
|
139 |
S: TMemoryStream; |
|
|
140 |
CH, I: Integer; |
| 21 |
begin |
141 |
begin |
| 22 |
if Trim(S) = '' then Exit(False); |
142 |
S := TMemoryStream.Create; |
| 23 |
if Hex then S := 'x'+S; |
143 |
try |
| 24 |
if not TryStrToInt(S, I) then |
144 |
Clipboard.GetFormat(Clipboard.FindFormatID('FamiTracker Pattern'), S); |
| 25 |
I := 0; |
145 |
S.Seek(0, soBeginning); |
| 26 |
Result := True; |
146 |
|
|
|
147 |
S.Read(ClipInfo, SizeOf(ClipInfo)); |
|
|
148 |
SetLength(FTMNotes, ClipInfo.Channels * ClipInfo.Rows); |
|
|
149 |
S.Read(FTMNotes[0], SizeOf(TFTMChanNote) * ClipInfo.Channels * ClipInfo.Rows); |
|
|
150 |
|
|
|
151 |
SetLength(Result, ClipInfo.Rows); |
|
|
152 |
|
|
|
153 |
for I := 0 to ClipInfo.Rows-1 do begin |
|
|
154 |
SetLength(Result[I], ClipInfo.Channels); |
|
|
155 |
for CH := 0 to ClipInfo.Channels-1 do begin |
|
|
156 |
Result[I, CH].Cell := FTMNoteToCell(FTMNotes[(CH * ClipInfo.Rows) + I]); |
|
|
157 |
Result[I, CH].Parts := [cpNote, cpInstrument, cpVolume, cpEffectCode, cpEffectParams]; |
|
|
158 |
end; |
|
|
159 |
end; |
|
|
160 |
|
|
|
161 |
for I := 0 to ClipInfo.Rows-1 do begin |
|
|
162 |
Result[I, 0].Parts *= [FTMColumnToPart(ClipInfo.StartColumn)..cpEffectParams]; |
|
|
163 |
Result[I, ClipInfo.Channels-1].Parts *= [cpNote..FTMColumnToPart(ClipInfo.EndColumn)]; |
|
|
164 |
end; |
|
|
165 |
finally |
|
|
166 |
S.Free; |
|
|
167 |
end; |
| 27 |
end; |
168 |
end; |
| 28 |
|
169 |
|
|
|
170 |
function ParseCell(Cell: String): TSelectedCell; |
|
|
171 |
function StrToInt_(S: String; out I: Integer; Hex: Boolean = False): Boolean; |
|
|
172 |
begin |
|
|
173 |
if Trim(S) = '' then Exit(False); |
|
|
174 |
if Hex then S := 'x'+S; |
|
|
175 |
if not TryStrToInt(S, I) then |
|
|
176 |
I := 0; |
|
|
177 |
Result := True; |
|
|
178 |
end; |
| 29 |
var |
179 |
var |
| 30 |
Note, Instr, EffectCode, EffectParam: String; |
180 |
Note, Instr, EffectCode, EffectParam: String; |
| 31 |
Temp: Integer; |
181 |
Temp: Integer; |
@@ -34,8 +184,6 @@ begin |
| 34 |
Instr := Cell.Substring(3, 2); |
184 |
Instr := Cell.Substring(3, 2); |
| 35 |
EffectCode := Cell.Substring(8, 1); |
185 |
EffectCode := Cell.Substring(8, 1); |
| 36 |
EffectParam := Cell.Substring(9, 2); |
186 |
EffectParam := Cell.Substring(9, 2); |
| 37 |
//EffectParam1 := Cell.Substring(9, 1); |
|
|
| 38 |
//EffectParam2 := Cell.Substring(10, 1); |
|
|
| 39 |
|
187 |
|
| 40 |
Result.Parts := [cpNote, cpInstrument, cpEffectCode, cpEffectParams]; |
188 |
Result.Parts := [cpNote, cpInstrument, cpEffectCode, cpEffectParams]; |
| 41 |
|
189 |
|
@@ -60,7 +208,11 @@ var |
| 60 |
Row: String; |
208 |
Row: String; |
| 61 |
I, J: Integer; |
209 |
I, J: Integer; |
| 62 |
begin |
210 |
begin |
|
|
211 |
if Clipboard.HasFormatName('FamiTracker Pattern') then |
|
|
212 |
Exit(GetFamitrackerPastedCells); |
|
|
213 |
|
| 63 |
SL := TStringList.Create; |
214 |
SL := TStringList.Create; |
|
|
215 |
|
| 64 |
try |
216 |
try |
| 65 |
try |
217 |
try |
| 66 |
SL.Text := Clipboard.AsText; |
218 |
SL.Text := Clipboard.AsText; |