@@ -354,197 +354,203 @@ var |
| 354 |
NewInstId: Integer; |
354 |
NewInstId: Integer; |
| 355 |
TracksForCleanup: array of TTBMTrackFormat; |
355 |
TracksForCleanup: array of TTBMTrackFormat; |
| 356 |
begin |
356 |
begin |
| 357 |
InitializeSong(Result); |
357 |
try |
|
|
358 |
InitializeSong(Result); |
| 358 |
|
359 |
|
| 359 |
Stream.ReadBuffer(Header, SizeOf(TTBMHeader)); |
360 |
Stream.ReadBuffer(Header, SizeOf(TTBMHeader)); |
| 360 |
|
361 |
|
| 361 |
if Header.SCount > 1 then |
362 |
if Header.SCount > 1 then |
| 362 |
raise ETBMException.Create('hUGETracker only supports loading TBM modules with one song.'); |
363 |
raise ETBMException.Create('hUGETracker only supports loading TBM modules with one song.'); |
| 363 |
|
364 |
|
| 364 |
// COMM block |
365 |
// COMM block |
| 365 |
Stream.ReadBuffer(BlockHeader, SizeOf(TTBMBlockHeader)); |
366 |
Stream.ReadBuffer(BlockHeader, SizeOf(TTBMBlockHeader)); |
| 366 |
Stream.Seek(BlockHeader.Length, soCurrent); // Skip the comment for now |
367 |
Stream.Seek(BlockHeader.Length, soCurrent); // Skip the comment for now |
| 367 |
|
368 |
|
| 368 |
// SONG block |
369 |
// SONG block |
| 369 |
Stream.ReadBuffer(BlockHeader, SizeOf(TTBMBlockHeader)); |
370 |
Stream.ReadBuffer(BlockHeader, SizeOf(TTBMBlockHeader)); |
| 370 |
Result.Name := Stream.ReadLString; |
371 |
Result.Name := Stream.ReadLString; |
| 371 |
Stream.ReadBuffer(SongFormat, SizeOf(TTBMSongFormat)); |
372 |
Stream.ReadBuffer(SongFormat, SizeOf(TTBMSongFormat)); |
| 372 |
|
373 |
|
| 373 |
// Number of visible effect columns-- undocumented currently but I asked |
374 |
// Number of visible effect columns-- undocumented currently but I asked |
| 374 |
// stoneface about it. |
375 |
// stoneface about it. |
| 375 |
Stream.ReadByte; |
376 |
Stream.ReadByte; |
| 376 |
|
377 |
|
| 377 |
Result.TicksPerRow := SongFormat.RowsPerBeat; |
378 |
Result.TicksPerRow := SongFormat.RowsPerBeat; |
| 378 |
|
379 |
|
| 379 |
for I := Low(Result.OrderMatrix) to High(Result.OrderMatrix) do |
380 |
for I := Low(Result.OrderMatrix) to High(Result.OrderMatrix) do |
| 380 |
SetLength(Result.OrderMatrix[I], SongFormat.PatternCount+2); // off-by-one error on my part |
381 |
SetLength(Result.OrderMatrix[I], SongFormat.PatternCount+2); // off-by-one error on my part |
| 381 |
|
382 |
|
| 382 |
for I := 0 to SongFormat.PatternCount do begin |
383 |
for I := 0 to SongFormat.PatternCount do begin |
| 383 |
Result.OrderMatrix[0, I] := 100 + Stream.ReadByte; |
384 |
Result.OrderMatrix[0, I] := 100 + Stream.ReadByte; |
| 384 |
Result.OrderMatrix[1, I] := 200 + Stream.ReadByte; |
385 |
Result.OrderMatrix[1, I] := 200 + Stream.ReadByte; |
| 385 |
Result.OrderMatrix[2, I] := 300 + Stream.ReadByte; |
386 |
Result.OrderMatrix[2, I] := 300 + Stream.ReadByte; |
| 386 |
Result.OrderMatrix[3, I] := 400 + Stream.ReadByte; |
387 |
Result.OrderMatrix[3, I] := 400 + Stream.ReadByte; |
| 387 |
end; |
388 |
end; |
| 388 |
|
389 |
|
| 389 |
SetLength(TracksForCleanup, SongFormat.NumberOfTracks); |
390 |
SetLength(TracksForCleanup, SongFormat.NumberOfTracks); |
| 390 |
for I := 0 to SongFormat.NumberOfTracks-1 do begin |
391 |
for I := 0 to SongFormat.NumberOfTracks-1 do begin |
| 391 |
Stream.ReadBuffer(TrackFormat, SizeOf(TTBMTrackFormat)); |
392 |
Stream.ReadBuffer(TrackFormat, SizeOf(TTBMTrackFormat)); |
| 392 |
TracksForCleanup[I] := TrackFormat; |
393 |
TracksForCleanup[I] := TrackFormat; |
| 393 |
|
394 |
|
| 394 |
case TrackFormat.Channel of |
395 |
case TrackFormat.Channel of |
| 395 |
0, 1: InsType := itSquare; |
396 |
0, 1: InsType := itSquare; |
| 396 |
2: InsType := itWave; |
397 |
2: InsType := itWave; |
| 397 |
3: InsType := itNoise; |
398 |
3: InsType := itNoise; |
| 398 |
end; |
399 |
end; |
| 399 |
|
400 |
|
| 400 |
Pat := Result.Patterns.GetOrCreateNew(((TrackFormat.Channel+1)*100) + TrackFormat.TrackId); |
401 |
Pat := Result.Patterns.GetOrCreateNew(((TrackFormat.Channel+1)*100) + TrackFormat.TrackId); |
| 401 |
for J := 0 to TrackFormat.Rows do begin |
402 |
for J := 0 to TrackFormat.Rows do begin |
| 402 |
Stream.ReadBuffer(RowFormat, SizeOf(TTBMRowFormat)); |
403 |
Stream.ReadBuffer(RowFormat, SizeOf(TTBMRowFormat)); |
| 403 |
Pat^[RowFormat.RowNo] := ConverTBMRow(RowFormat.RowData, InsType); |
404 |
Pat^[RowFormat.RowNo] := ConverTBMRow(RowFormat.RowData, InsType); |
|
|
405 |
end; |
| 404 |
end; |
406 |
end; |
| 405 |
end; |
|
|
| 406 |
|
407 |
|
| 407 |
// INST block |
408 |
// INST block |
| 408 |
SquareMap := TTBMInstrumentMap.Create; |
409 |
SquareMap := TTBMInstrumentMap.Create; |
| 409 |
WaveMap := TTBMInstrumentMap.Create; |
410 |
WaveMap := TTBMInstrumentMap.Create; |
| 410 |
NoiseMap := TTBMInstrumentMap.Create; |
411 |
NoiseMap := TTBMInstrumentMap.Create; |
| 411 |
SeenSquare := 1; |
412 |
SeenSquare := 1; |
| 412 |
SeenWave := 1; |
413 |
SeenWave := 1; |
| 413 |
SeenNoise := 1; |
414 |
SeenNoise := 1; |
| 414 |
|
415 |
|
| 415 |
for I := 0 to Header.ICount-1 do begin |
416 |
for I := 0 to Header.ICount-1 do begin |
| 416 |
Stream.ReadBuffer(BlockHeader, SizeOf(TTBMBlockHeader)); |
417 |
Stream.ReadBuffer(BlockHeader, SizeOf(TTBMBlockHeader)); |
| 417 |
|
418 |
|
| 418 |
InstId := Stream.ReadByte; |
419 |
InstId := Stream.ReadByte; |
| 419 |
InstName := Stream.ReadLString; |
420 |
InstName := Stream.ReadLString; |
| 420 |
|
421 |
|
| 421 |
Stream.ReadBuffer(InstFormat, SizeOf(TTBMInstrumentFormat)); |
422 |
Stream.ReadBuffer(InstFormat, SizeOf(TTBMInstrumentFormat)); |
| 422 |
|
423 |
|
| 423 |
case InstFormat.Channel of |
424 |
case InstFormat.Channel of |
| 424 |
0, 1: begin |
425 |
0, 1: begin |
| 425 |
InsType := itSquare; |
426 |
InsType := itSquare; |
| 426 |
SquareMap.Add(InstId, SeenSquare); |
427 |
SquareMap.Add(InstId, SeenSquare); |
| 427 |
NewInstId := SeenSquare; |
428 |
NewInstId := SeenSquare; |
| 428 |
Inc(SeenSquare); |
429 |
Inc(SeenSquare); |
| 429 |
end; |
430 |
end; |
| 430 |
2: begin |
431 |
2: begin |
| 431 |
InsType := itWave; |
432 |
InsType := itWave; |
| 432 |
WaveMap.Add(InstId, SeenWave); |
433 |
WaveMap.Add(InstId, SeenWave); |
| 433 |
NewInstId := SeenWave; |
434 |
NewInstId := SeenWave; |
| 434 |
Inc(SeenWave); |
435 |
Inc(SeenWave); |
| 435 |
end; |
436 |
end; |
| 436 |
3: begin |
437 |
3: begin |
| 437 |
InsType := itNoise; |
438 |
InsType := itNoise; |
| 438 |
NoiseMap.Add(InstId, SeenNoise); |
439 |
NoiseMap.Add(InstId, SeenNoise); |
| 439 |
NewInstId := SeenNoise; |
440 |
NewInstId := SeenNoise; |
| 440 |
Inc(SeenNoise); |
441 |
Inc(SeenNoise); |
|
|
442 |
end; |
| 441 |
end; |
443 |
end; |
| 442 |
end; |
|
|
| 443 |
|
444 |
|
| 444 |
Ins := @Result.Instruments.All[UnmodInst(InsType, NewInstId)]; |
445 |
Ins := @Result.Instruments.All[UnmodInst(InsType, NewInstId)]; |
| 445 |
Ins^.Name := InstName; |
446 |
Ins^.Name := InstName; |
| 446 |
|
447 |
|
| 447 |
EnvReg.ByteValue := InstFormat.Envelope; |
448 |
EnvReg.ByteValue := InstFormat.Envelope; |
| 448 |
Ins^.InitialVolume := EnvReg.InitialVolume; |
449 |
Ins^.InitialVolume := EnvReg.InitialVolume; |
| 449 |
if EnvReg.Direction then |
450 |
if EnvReg.Direction then |
| 450 |
Ins^.VolSweepDirection := stUp |
451 |
Ins^.VolSweepDirection := stUp |
| 451 |
else |
452 |
else |
| 452 |
Ins^.VolSweepDirection := stDown; |
453 |
Ins^.VolSweepDirection := stDown; |
| 453 |
Ins^.VolSweepAmount := EnvReg.SweepNumber; |
454 |
Ins^.VolSweepAmount := EnvReg.SweepNumber; |
| 454 |
Ins^.Waveform := InstFormat.Envelope; |
455 |
Ins^.Waveform := InstFormat.Envelope; |
| 455 |
|
|
|
| 456 |
Ins^.SubpatternEnabled := True; |
|
|
| 457 |
|
456 |
|
| 458 |
// Arpeggio sequence |
457 |
Ins^.SubpatternEnabled := True; |
| 459 |
Stream.ReadBuffer(SeqFormat, SizeOf(TTBMSequenceFormat)); |
|
|
| 460 |
for J := 0 to SeqFormat.Length-1 do begin |
|
|
| 461 |
Offs := ShortInt(Stream.ReadByte); |
|
|
| 462 |
Ins^.Subpattern[J].Note := MIDDLE_NOTE + Offs; |
|
|
| 463 |
end; |
|
|
| 464 |
|
458 |
|
| 465 |
Ins^.Subpattern[SeqFormat.Length].Volume := SeqFormat.LoopIndex; |
459 |
// Arpeggio sequence |
|
|
460 |
Stream.ReadBuffer(SeqFormat, SizeOf(TTBMSequenceFormat)); |
|
|
461 |
for J := 0 to SeqFormat.Length-1 do begin |
|
|
462 |
Offs := ShortInt(Stream.ReadByte); |
|
|
463 |
Ins^.Subpattern[J].Note := MIDDLE_NOTE + Offs; |
|
|
464 |
end; |
| 466 |
|
465 |
|
| 467 |
// Panning sequence |
466 |
Ins^.Subpattern[SeqFormat.Length].Volume := SeqFormat.LoopIndex; |
| 468 |
Stream.ReadBuffer(SeqFormat, SizeOf(TTBMSequenceFormat)); |
|
|
| 469 |
Stream.Seek(SeqFormat.Length, soCurrent); |
|
|
| 470 |
|
467 |
|
| 471 |
// Pitch sequence |
468 |
// Panning sequence |
| 472 |
Stream.ReadBuffer(SeqFormat, SizeOf(TTBMSequenceFormat)); |
469 |
Stream.ReadBuffer(SeqFormat, SizeOf(TTBMSequenceFormat)); |
| 473 |
PitchOffset := 0; |
470 |
Stream.Seek(SeqFormat.Length, soCurrent); |
| 474 |
Offs := 0; |
|
|
| 475 |
for J := 0 to SeqFormat.Length-1 do begin |
|
|
| 476 |
Offs := ShortInt(Stream.ReadByte); |
|
|
| 477 |
|
471 |
|
| 478 |
if Offs > PitchOffset then |
472 |
// Pitch sequence |
| 479 |
Ins^.Subpattern[J].EffectCode := $1 |
473 |
Stream.ReadBuffer(SeqFormat, SizeOf(TTBMSequenceFormat)); |
| 480 |
else if Offs < PitchOffset then |
474 |
PitchOffset := 0; |
| 481 |
Ins^.Subpattern[J].EffectCode := $2 |
475 |
Offs := 0; |
| 482 |
else |
476 |
for J := 0 to SeqFormat.Length-1 do begin |
| 483 |
Continue; |
477 |
Offs := ShortInt(Stream.ReadByte); |
| 484 |
|
478 |
|
| 485 |
Ins^.Subpattern[J].EffectParams.Value := Abs(PitchOffset - Offs); |
479 |
if Offs > PitchOffset then |
| 486 |
PitchOffset := Offs; |
480 |
Ins^.Subpattern[J].EffectCode := $1 |
| 487 |
end; |
481 |
else if Offs < PitchOffset then |
|
|
482 |
Ins^.Subpattern[J].EffectCode := $2 |
|
|
483 |
else |
|
|
484 |
Continue; |
| 488 |
|
485 |
|
| 489 |
Ins^.Subpattern[SeqFormat.Length].Volume := SeqFormat.LoopIndex; |
486 |
Ins^.Subpattern[J].EffectParams.Value := Abs(PitchOffset - Offs); |
|
|
487 |
PitchOffset := Offs; |
|
|
488 |
end; |
| 490 |
|
489 |
|
| 491 |
// Duty/noise-type sequence |
490 |
Ins^.Subpattern[SeqFormat.Length].Volume := SeqFormat.LoopIndex; |
| 492 |
Stream.ReadBuffer(SeqFormat, SizeOf(TTBMSequenceFormat)); |
491 |
|
| 493 |
case InsType of |
492 |
// Duty/noise-type sequence |
| 494 |
itSquare: begin |
493 |
Stream.ReadBuffer(SeqFormat, SizeOf(TTBMSequenceFormat)); |
| 495 |
for J := 0 to SeqFormat.Length-1 do begin |
494 |
case InsType of |
| 496 |
Ins^.Subpattern[J].EffectCode := $9; |
495 |
itSquare: begin |
| 497 |
case Stream.ReadByte of |
496 |
for J := 0 to SeqFormat.Length-1 do begin |
| 498 |
0: Ins^.Subpattern[J].EffectParams.Value := $00; |
497 |
Ins^.Subpattern[J].EffectCode := $9; |
| 499 |
1: Ins^.Subpattern[J].EffectParams.Value := $40; |
498 |
case Stream.ReadByte of |
| 500 |
2: Ins^.Subpattern[J].EffectParams.Value := $80; |
499 |
0: Ins^.Subpattern[J].EffectParams.Value := $00; |
| 501 |
3: Ins^.Subpattern[J].EffectParams.Value := $C0; |
500 |
1: Ins^.Subpattern[J].EffectParams.Value := $40; |
|
|
501 |
2: Ins^.Subpattern[J].EffectParams.Value := $80; |
|
|
502 |
3: Ins^.Subpattern[J].EffectParams.Value := $C0; |
|
|
503 |
end; |
| 502 |
end; |
504 |
end; |
| 503 |
end; |
505 |
end; |
| 504 |
end; |
506 |
itWave: begin |
| 505 |
itWave: begin |
507 |
for J := 0 to SeqFormat.Length-1 do begin |
| 506 |
for J := 0 to SeqFormat.Length-1 do begin |
508 |
Ins^.Subpattern[J].EffectCode := $C; |
| 507 |
Ins^.Subpattern[J].EffectCode := $C; |
509 |
case Stream.ReadByte of |
| 508 |
case Stream.ReadByte of |
510 |
0: Ins^.Subpattern[J].EffectParams.Value := $00; |
| 509 |
0: Ins^.Subpattern[J].EffectParams.Value := $00; |
511 |
1: Ins^.Subpattern[J].EffectParams.Value := $01; |
| 510 |
1: Ins^.Subpattern[J].EffectParams.Value := $01; |
512 |
2: Ins^.Subpattern[J].EffectParams.Value := $08; |
| 511 |
2: Ins^.Subpattern[J].EffectParams.Value := $08; |
513 |
3: Ins^.Subpattern[J].EffectParams.Value := $0F; |
| 512 |
3: Ins^.Subpattern[J].EffectParams.Value := $0F; |
514 |
end; |
| 513 |
end; |
515 |
end; |
| 514 |
end; |
516 |
end; |
|
|
517 |
itNoise: begin |
|
|
518 |
for J := 0 to SeqFormat.Length-1 do begin |
|
|
519 |
Ins^.Subpattern[J].EffectCode := $9; |
|
|
520 |
if Stream.ReadByte = 0 then |
|
|
521 |
Ins^.Subpattern[J].EffectParams.Value := $00 |
|
|
522 |
else |
|
|
523 |
Ins^.Subpattern[J].EffectParams.Value := $80; |
|
|
524 |
end; |
|
|
525 |
end |
| 515 |
end; |
526 |
end; |
| 516 |
itNoise: begin |
|
|
| 517 |
for J := 0 to SeqFormat.Length-1 do begin |
|
|
| 518 |
Ins^.Subpattern[J].EffectCode := $9; |
|
|
| 519 |
if Stream.ReadByte = 0 then |
|
|
| 520 |
Ins^.Subpattern[J].EffectParams.Value := $00 |
|
|
| 521 |
else |
|
|
| 522 |
Ins^.Subpattern[J].EffectParams.Value := $80; |
|
|
| 523 |
end; |
|
|
| 524 |
end |
|
|
| 525 |
end; |
|
|
| 526 |
|
527 |
|
| 527 |
Ins^.Subpattern[SeqFormat.Length].Volume := SeqFormat.LoopIndex; |
528 |
Ins^.Subpattern[SeqFormat.Length].Volume := SeqFormat.LoopIndex; |
| 528 |
end; |
529 |
end; |
| 529 |
|
530 |
|
| 530 |
// WAVE Block |
531 |
// WAVE Block |
| 531 |
for I := 0 to Header.WCount-1 do begin |
532 |
for I := 0 to Header.WCount-1 do begin |
| 532 |
Stream.ReadBuffer(BlockHeader, SizeOf(TTBMBlockHeader)); |
533 |
Stream.ReadBuffer(BlockHeader, SizeOf(TTBMBlockHeader)); |
| 533 |
WaveId := Stream.ReadByte; |
534 |
WaveId := Stream.ReadByte; |
| 534 |
WaveName := Stream.ReadLString; |
535 |
WaveName := Stream.ReadLString; |
| 535 |
Stream.ReadBuffer(FourBitWave, SizeOf(T4bitWave)); |
536 |
Stream.ReadBuffer(FourBitWave, SizeOf(T4bitWave)); |
| 536 |
Result.Waves[WaveId] := UnconvertWaveform(FourBitWave); |
537 |
Result.Waves[WaveId] := UnconvertWaveform(FourBitWave); |
| 537 |
end; |
538 |
end; |
| 538 |
|
539 |
|
| 539 |
// Cleanup patterns |
540 |
// Cleanup patterns |
| 540 |
for TrackFormat in TracksForCleanup do begin |
541 |
for TrackFormat in TracksForCleanup do begin |
| 541 |
Pat := Result.Patterns.GetOrCreateNew(((TrackFormat.Channel+1)*100) + TrackFormat.TrackId); |
542 |
Pat := Result.Patterns.GetOrCreateNew(((TrackFormat.Channel+1)*100) + TrackFormat.TrackId); |
| 542 |
|
543 |
|
| 543 |
case TrackFormat.Channel of |
544 |
case TrackFormat.Channel of |
| 544 |
0, 1: CleanupPattern(Pat, SquareMap); |
545 |
0, 1: CleanupPattern(Pat, SquareMap); |
| 545 |
2: CleanupPattern(Pat, WaveMap); |
546 |
2: CleanupPattern(Pat, WaveMap); |
| 546 |
3: CleanupPattern(Pat, NoiseMap); |
547 |
3: CleanupPattern(Pat, NoiseMap); |
|
|
548 |
end; |
| 547 |
end; |
549 |
end; |
|
|
550 |
finally |
|
|
551 |
if Assigned(SquareMap) then SquareMap.Free; |
|
|
552 |
if Assigned(WaveMap) then WaveMap.Free; |
|
|
553 |
if Assigned(NoiseMap) then NoiseMap.Free; |
| 548 |
end; |
554 |
end; |
| 549 |
end; |
555 |
end; |
| 550 |
|
556 |
|