@@ -118,6 +118,15 @@ type |
| 118 |
etSetGlobalVolume = 22 // `Jxy` set global volume scale |
118 |
etSetGlobalVolume = 22 // `Jxy` set global volume scale |
| 119 |
); |
119 |
); |
| 120 |
|
120 |
|
|
|
121 |
const |
|
|
122 |
ContinuousEffects = [ |
|
|
123 |
etArpeggio, |
|
|
124 |
etPitchUp, |
|
|
125 |
etPitchDown, |
|
|
126 |
etAutoPortamento, |
|
|
127 |
etVibrato |
|
|
128 |
]; |
|
|
129 |
|
| 121 |
function TStreamHelper.ReadLString: String; |
130 |
function TStreamHelper.ReadLString: String; |
| 122 |
var |
131 |
var |
| 123 |
Len: Word; |
132 |
Len: Word; |
@@ -169,13 +178,16 @@ begin |
| 169 |
if Channel = itSquare then |
178 |
if Channel = itSquare then |
| 170 |
OutParams.Value := Params shl 6; |
179 |
OutParams.Value := Params shl 6; |
| 171 |
|
180 |
|
| 172 |
if Channel = itWave then |
181 |
if Channel = itWave then begin |
|
|
182 |
OutCode := $C; |
|
|
183 |
|
| 173 |
case Params of |
184 |
case Params of |
| 174 |
0: OutParams.Value := $00; |
185 |
0: OutParams.Value := $00; |
| 175 |
1: OutParams.Value := $01; |
186 |
1: OutParams.Value := $01; |
| 176 |
2: OutParams.Value := $08; |
187 |
2: OutParams.Value := $08; |
| 177 |
3: OutParams.Value := $0F; |
188 |
3: OutParams.Value := $0F; |
| 178 |
end; |
189 |
end; |
|
|
190 |
end; |
| 179 |
|
191 |
|
| 180 |
if Channel = itNoise then begin |
192 |
if Channel = itNoise then begin |
| 181 |
if Params > 0 then |
193 |
if Params > 0 then |
@@ -263,6 +275,10 @@ begin |
| 263 |
Continue; |
275 |
Continue; |
| 264 |
|
276 |
|
| 265 |
ConvertEffect(Row.Effects[I].EffectType, Row.Effects[I].Param, RowType, Result.EffectCode, Result.EffectParams); |
277 |
ConvertEffect(Row.Effects[I].EffectType, Row.Effects[I].Param, RowType, Result.EffectCode, Result.EffectParams); |
|
|
278 |
if TTBMEffectType(Row.Effects[I].EffectType) in ContinuousEffects then |
|
|
279 |
Result.Volume := 1 // Mark this effect as "continuous" |
|
|
280 |
else |
|
|
281 |
Result.Volume := 0; |
| 266 |
end; |
282 |
end; |
| 267 |
|
283 |
|
| 268 |
if Row.Note = 0 then |
284 |
if Row.Note = 0 then |
@@ -280,6 +296,8 @@ procedure CleanupPattern(Pat: PPattern; InstMap: TTBMInstrumentMap); |
| 280 |
var |
296 |
var |
| 281 |
I: Integer; |
297 |
I: Integer; |
| 282 |
CurNote: Integer = NO_NOTE; |
298 |
CurNote: Integer = NO_NOTE; |
|
|
299 |
ContinuousCode: Byte = 0; |
|
|
300 |
ContinuousParam: Byte = 0; |
| 283 |
begin |
301 |
begin |
| 284 |
for I := Low(TPattern) to High(TPattern) do begin |
302 |
for I := Low(TPattern) to High(TPattern) do begin |
| 285 |
if not InstMap.TryGetData(Pat^[I].Instrument-1, Pat^[I].Instrument) then |
303 |
if not InstMap.TryGetData(Pat^[I].Instrument-1, Pat^[I].Instrument) then |
@@ -290,6 +308,18 @@ begin |
| 290 |
|
308 |
|
| 291 |
if (Pat^[I].Instrument <> 0) and (Pat^[I].Note = NO_NOTE) then |
309 |
if (Pat^[I].Instrument <> 0) and (Pat^[I].Note = NO_NOTE) then |
| 292 |
Pat^[I].Note := CurNote; |
310 |
Pat^[I].Note := CurNote; |
|
|
311 |
|
|
|
312 |
if Pat^[I].Volume = 1 then begin |
|
|
313 |
ContinuousCode := Pat^[I].EffectCode; |
|
|
314 |
ContinuousParam := Pat^[I].EffectParams.Value; |
|
|
315 |
end; |
|
|
316 |
|
|
|
317 |
if (ContinuousParam <> 0) and (Pat^[I].EffectCode = 0) and (Pat^[I].EffectParams.Value = 0) then begin |
|
|
318 |
Pat^[I].EffectCode := ContinuousCode; |
|
|
319 |
Pat^[I].EffectParams.Value := ContinuousParam; |
|
|
320 |
end; |
|
|
321 |
|
|
|
322 |
Pat^[I].Volume := 0; |
| 293 |
end; |
323 |
end; |
| 294 |
end; |
324 |
end; |
| 295 |
|
325 |
|
@@ -341,7 +371,7 @@ begin |
| 341 |
Stream.ReadBuffer(SongFormat, SizeOf(TTBMSongFormat)); |
371 |
Stream.ReadBuffer(SongFormat, SizeOf(TTBMSongFormat)); |
| 342 |
Stream.ReadByte; // ????????????????? |
372 |
Stream.ReadByte; // ????????????????? |
| 343 |
|
373 |
|
| 344 |
Result.TicksPerRow := SongFormat.RowsPerBeat - 1; |
374 |
Result.TicksPerRow := SongFormat.RowsPerBeat; |
| 345 |
|
375 |
|
| 346 |
for I := Low(Result.OrderMatrix) to High(Result.OrderMatrix) do |
376 |
for I := Low(Result.OrderMatrix) to High(Result.OrderMatrix) do |
| 347 |
SetLength(Result.OrderMatrix[I], SongFormat.PatternCount+2); // off-by-one error on my part |
377 |
SetLength(Result.OrderMatrix[I], SongFormat.PatternCount+2); // off-by-one error on my part |
@@ -422,15 +452,20 @@ begin |
| 422 |
|
452 |
|
| 423 |
Ins^.SubpatternEnabled := True; |
453 |
Ins^.SubpatternEnabled := True; |
| 424 |
|
454 |
|
|
|
455 |
// Arpeggio sequence |
| 425 |
Stream.ReadBuffer(SeqFormat, SizeOf(TTBMSequenceFormat)); |
456 |
Stream.ReadBuffer(SeqFormat, SizeOf(TTBMSequenceFormat)); |
| 426 |
for J := 0 to SeqFormat.Length-1 do begin |
457 |
for J := 0 to SeqFormat.Length-1 do begin |
| 427 |
Offs := ShortInt(Stream.ReadByte); |
458 |
Offs := ShortInt(Stream.ReadByte); |
| 428 |
Ins^.Subpattern[J].Note := MIDDLE_NOTE + Offs; |
459 |
Ins^.Subpattern[J].Note := MIDDLE_NOTE + Offs; |
| 429 |
end; |
460 |
end; |
| 430 |
|
461 |
|
|
|
462 |
Ins^.Subpattern[SeqFormat.Length].Volume := SeqFormat.LoopIndex; |
|
|
463 |
|
|
|
464 |
// Panning sequence |
| 431 |
Stream.ReadBuffer(SeqFormat, SizeOf(TTBMSequenceFormat)); |
465 |
Stream.ReadBuffer(SeqFormat, SizeOf(TTBMSequenceFormat)); |
| 432 |
Stream.Seek(SeqFormat.Length, soCurrent); |
466 |
Stream.Seek(SeqFormat.Length, soCurrent); |
| 433 |
|
467 |
|
|
|
468 |
// Pitch sequence |
| 434 |
Stream.ReadBuffer(SeqFormat, SizeOf(TTBMSequenceFormat)); |
469 |
Stream.ReadBuffer(SeqFormat, SizeOf(TTBMSequenceFormat)); |
| 435 |
PitchOffset := 0; |
470 |
PitchOffset := 0; |
| 436 |
Offs := 0; |
471 |
Offs := 0; |
@@ -450,13 +485,43 @@ begin |
| 450 |
|
485 |
|
| 451 |
Ins^.Subpattern[SeqFormat.Length].Volume := SeqFormat.LoopIndex; |
486 |
Ins^.Subpattern[SeqFormat.Length].Volume := SeqFormat.LoopIndex; |
| 452 |
|
487 |
|
|
|
488 |
// Duty/noise-type sequence |
| 453 |
Stream.ReadBuffer(SeqFormat, SizeOf(TTBMSequenceFormat)); |
489 |
Stream.ReadBuffer(SeqFormat, SizeOf(TTBMSequenceFormat)); |
| 454 |
if (InsType = itSquare) and (SeqFormat.Length > 0) then begin |
490 |
case InsType of |
| 455 |
Ins^.Duty := Stream.ReadByte; |
491 |
itSquare: begin |
| 456 |
Stream.Seek(SeqFormat.Length-1, soCurrent); |
492 |
for J := 0 to SeqFormat.Length-1 do begin |
| 457 |
end |
493 |
Ins^.Subpattern[J].EffectCode := $9; |
| 458 |
else |
494 |
case Stream.ReadByte of |
| 459 |
Stream.Seek(SeqFormat.Length, soCurrent); |
495 |
0: Ins^.Subpattern[J].EffectParams.Value := $00; |
|
|
496 |
1: Ins^.Subpattern[J].EffectParams.Value := $40; |
|
|
497 |
2: Ins^.Subpattern[J].EffectParams.Value := $80; |
|
|
498 |
3: Ins^.Subpattern[J].EffectParams.Value := $C0; |
|
|
499 |
end; |
|
|
500 |
end; |
|
|
501 |
end; |
|
|
502 |
itWave: begin |
|
|
503 |
for J := 0 to SeqFormat.Length-1 do begin |
|
|
504 |
Ins^.Subpattern[J].EffectCode := $C; |
|
|
505 |
case Stream.ReadByte of |
|
|
506 |
0: Ins^.Subpattern[J].EffectParams.Value := $00; |
|
|
507 |
1: Ins^.Subpattern[J].EffectParams.Value := $01; |
|
|
508 |
2: Ins^.Subpattern[J].EffectParams.Value := $08; |
|
|
509 |
3: Ins^.Subpattern[J].EffectParams.Value := $0F; |
|
|
510 |
end; |
|
|
511 |
end; |
|
|
512 |
end; |
|
|
513 |
itNoise: begin |
|
|
514 |
for J := 0 to SeqFormat.Length-1 do begin |
|
|
515 |
Ins^.Subpattern[J].EffectCode := $9; |
|
|
516 |
if Stream.ReadByte = 0 then |
|
|
517 |
Ins^.Subpattern[J].EffectParams.Value := $00 |
|
|
518 |
else |
|
|
519 |
Ins^.Subpattern[J].EffectParams.Value := $80; |
|
|
520 |
end; |
|
|
521 |
end |
|
|
522 |
end; |
|
|
523 |
|
|
|
524 |
Ins^.Subpattern[SeqFormat.Length].Volume := SeqFormat.LoopIndex; |
| 460 |
end; |
525 |
end; |
| 461 |
|
526 |
|
| 462 |
// WAVE Block |
527 |
// WAVE Block |