home *** CD-ROM | disk | FTP | other *** search
- UNIT SongElements;
-
- INTERFACE
-
- USES Objects;
-
-
-
-
- {----------------------------------------------------------------------------}
- { Definitions for handling the format of individual notes. }
- { Notes are composed of four fields: }
- { }
- { Period: A number in the range 0..2047 which states the period of }
- { the note in units of 1/3584000 per sample. (this is a }
- { somewhat empyric number. If anyone knows the exact Amiga }
- { number, please, tell us). A zero means to keep using the }
- { same period used before. }
- { Instrument: A number in range 0..63 meaning the number of the instrument }
- { which will be used for the note. A zero means use the same. }
- { Command: A number (no real range) of the way the note should be }
- { played (i.e. Vibrato) a change in the playing sequence (i.e. }
- { pattern break) or a change in the general parameters of the }
- { module player (i.e. set tempo). All the possible values are }
- { defined in the TModCommand enumerated type below. }
- { Parameter: A parameter for the command. Its meaning differs from one }
- { command to another. Sometimes each nibble is considered as a }
- { different parameter. }
- {____________________________________________________________________________}
-
- TYPE
- TModCommand = (
- mcNone, { 0 00 } { Just play the note, without any special option. }
-
- mcArpeggio, { 0 xy } { Rotate through three notes rapidly. }
- mcTPortUp, { 1 xx } { Tone Portamento Up: Gradual change of tone towards high frequencies. }
- mcTPortDown, { 2 xx } { Tone Portamento Down: Gradual change of tone towards low frequencies. }
- mcNPortamento,{ 3 xy } { Note Portamento: Gradual change of tone towards a given note. }
- mcVibrato, { 4 xy } { Vibrato: Frequency changes around the note. }
- mcT_VSlide, { 5 xy } { Tone Port. Up + Volume slide: Parameter means vol. slide. }
- mcVib_VSlide, { 6 xy } { Vibrato + Volume slide: Parameter means vol. slide. }
- mcTremolo, { 7 xy } { Tremolo: I don't know for sure. Fast volume variations, I think. }
- mcNPI1, { 8 xx } { Do Nothing (as far as I know). }
- mcSampleOffs, { 9 xx } { Start the sample from the middle. }
- mcVolSlide, { A xy } { Volume slide: Gradual change in volume. }
- mcJumpPattern,{ B xx } { End pattern and continue from a different pattern sequence position. }
- mcSetVolume, { C xx } { Set the volume of the sound. }
- mcEndPattern, { D xx } { Continue at the start of the next pattern. }
- mcExtended, { E xy } { Extended set of commands (ProTracker). }
- mcSetTempo, { F xx } { Set the tempo of the music, in 1/50ths of a second. }
-
- mcSetFilter, { E 0x } { Set the output filter to the on or off value. }
- mcFinePortaUp,{ E 1x } { Like TPortUp, but slower. }
- mcFinePortaDn,{ E 2x } { Like TPortDown, but slower. }
- mcGlissCtrl, { E 3x } { ¿?¿?¿? }
- mcVibCtrl, { E 4x } { Set the vibrato waveform. }
- mcFineTune, { E 5x } { Fine tune the frequency of the sound. }
- mcJumpLoop, { E 6x } { Make a loop inside a pattern. }
- mcTremCtrl, { E 7x } { Set the tremolo waveform (I think). }
- mcNPI2, { E 8x } { Do Nothing (as far as I know). }
- mcRetrigNote, { E 9x } { ¿?¿?¿? }
- mcVolFineUp, { E Ax } { Like VolSlide, but slower and towards high frequencies. }
- mcVolFineDown,{ E Bx } { Like VolSlide, but slower and towards low frequencies. }
- mcNoteCut, { E Cx } { ¿?¿?¿? }
- mcNoteDelay, { E Dx } { Wait a little before starting note. }
- mcPattDelay, { E Ex } { ¿?¿?¿? }
- mcFunkIt, { E Fx } { No idea, but sounds funny. }
-
- mcOktArp, { } { Oktalizer arpeggio }
- mcOktArp2, { } { Oktalizer arpeggio2 }
-
- mcLast
- );
-
- TYPE
- PNoCommandNote = ^TNoCommandNote;
- TNoCommandNote = RECORD
- Instrument : BYTE;
- Period : WORD;
- Volume : BYTE;
- END;
-
- PCommandNote = ^TCommandNote;
- TCommandNote = RECORD
- Command : TModCommand;
- Parameter : BYTE;
- END;
-
- PFullNote = ^TFullNote;
- TFullNote = RECORD
- CASE BYTE OF
- 0 : ( Instrument : BYTE;
- Period : WORD;
- Volume : BYTE;
- Command : TModCommand;
- Parameter : BYTE );
- 1 : ( Note : TNoCommandNote;
- Comm : TCommandNote );
- END;
-
-
-
-
- {----------------------------------------------------------------------------}
- { Definitions for handling the instruments used in the module. }
- { Instruments are fragments of sampled sound (long arrays of bytes which }
- { describe the wave of the sound of the instrument). The samples used in }
- { music modules have a default volume and also, they can have a loop (for }
- { sustained instruments) and a fine tuning constant (not yet implemented). }
- {____________________________________________________________________________}
-
- CONST
- MaxSample = 65520;
- MaxInstruments = 255;
-
- LowQuality : BOOLEAN = TRUE;
-
- { Properties }
-
- ipMonoFreq = $0001; { Set if the instrument is played always at the same freq (not implemented). }
- ipLong = $0002; { Set if the instrument's sample is longer than 65520 bytes. }
-
- TYPE
- PSample = ^TSample;
- TSample = ARRAY[0..MaxSample-1] OF SHORTINT;
-
- TIProperties = WORD; { Properties of the instrument. }
-
- PInstrumentRec = ^TInstrumentRec;
- TInstrumentRec =
- RECORD
- Len, { Length of the instrument's sampled image. }
- Reps, { Starting offset of the repeated portion. }
- Repl : LONGINT; { Size of the repeated portion. }
- Vol : BYTE; { Default volume of the instrument (0..64) }
- Ftune : BYTE; { Fine tuning value for the instrument (not yet implemented). }
- NAdj : WORD; { Numerator of note adjutment. }
- DAdj : WORD; { Denominator of note adjutment. }
- Data : ^TSample; { Pointer to the first 65520 bytes of the sample. }
- Xtra : ^TSample; { Pointer to the second 65520 bytes of the sample (if there is such). }
- Prop : TIProperties; { Bit mapped properties value. }
- END;
-
- PInstrument = ^TInstrument;
- TInstrument =
- OBJECT(TObject)
- Name : PString;
- Instr : PInstrumentRec;
-
- CONSTRUCTOR Init;
- DESTRUCTOR Done; VIRTUAL;
-
- PROCEDURE FreeContents;
- PROCEDURE Desample;
-
- PROCEDURE Change(Instrument : PInstrumentRec);
- FUNCTION GetName : STRING;
- PROCEDURE SetName(S: STRING);
- END;
-
-
-
-
- {----------------------------------------------------------------------------}
- { Definitions for handling the tracks of which patterns are built. }
- { Tracks are lists of notes and command values of which the empty leading }
- { and trailing blanks have been removed (obviated). }
- {____________________________________________________________________________}
-
- TYPE
- PNoteTrack = ^TNoteTrack;
- TNoteTrack =
- RECORD
- NoteOffset : BYTE;
- NumNotes : BYTE;
- Notes : ARRAY[0..255] OF TNoCommandNote;
- END;
-
- PCommTrack = ^TCommTrack;
- TCommTrack =
- RECORD
- NoteOffset : BYTE;
- NumNotes : BYTE;
- Notes : ARRAY[0..255] OF TCommandNote;
- END;
-
- PFullTrack = ^TFullTrack;
- TFullTrack = ARRAY[0..255] OF TFullNote;
- {
- PTrackCache = ^TTrackCache;
- TTrackCache =
- RECORD
- InUse : BOOLEAN;
- Modified : BOOLEAN;
- LastUse : WORD;
- Track : PFullTrack;
- END;
-
- VAR
- TrackCaches = ARRAY[1..MaxChannels] OF TTrackCache;
- }
- TYPE
- PTrack = ^TTrack;
- TTrack =
- OBJECT(TObject)
- Name : PString;
- Note : PNoteTrack;
- Comm : PCommTrack;
-
- CONSTRUCTOR Init;
- DESTRUCTOR Done; VIRTUAL;
-
- PROCEDURE FreeContents;
-
- PROCEDURE ChangeNote(At: WORD; VAR FullNote: TFullNote);
- PROCEDURE GetNote (At: WORD; VAR FullNote: TFullNote);
-
- PROCEDURE GetFullTrack(VAR Track: TFullTrack);
- PROCEDURE SetFullTrack(VAR Track: TFullTrack);
-
- FUNCTION GetName : STRING;
- END;
-
-
-
-
- {----------------------------------------------------------------------------}
- { Definitions for handling the format of the patterns. }
- { Patterns are arrays of pointers to tracks (up to 12 tracks). }
- { A music module can have up to 255 individual patterns, arranged in a }
- { sequence of up to 255. }
- { Empty patterns are not counted. }
- {____________________________________________________________________________}
-
- CONST
- MaxSequence = 256;
- MaxPatterns = 256;
- MaxPatternLines = 256;
- MaxChannels = {10}16;
-
- TYPE
- PPatternRec = ^TPatternRec;
- TPatternRec =
- RECORD
- NNotes : BYTE;
- NChans : BYTE;
- Tempo : BYTE;
- BPM : BYTE;
- Channels : ARRAY[1..MaxChannels] OF WORD;
- END;
-
- PPattern = ^TPattern;
- TPattern =
- OBJECT(TObject)
- Name : PString;
- Patt : PPatternRec;
-
- CONSTRUCTOR Init(Chans: WORD);
- DESTRUCTOR Done; VIRTUAL;
-
- PROCEDURE FreeContents;
-
- FUNCTION GetName : STRING;
- END;
-
- PPatternSequence = ^TPatternSequence;
- TPatternSequence = ARRAY[1..MaxSequence] OF BYTE;
-
-
-
-
- {----------------------------------------------------------------------------}
- { General definitions for the song. }
- {____________________________________________________________________________}
-
- TYPE
- PSongComment = ^TSongComment;
- TSongComment = ARRAY[1..16] OF STRING[60];
-
-
-
-
- IMPLEMENTATION
-
- USES Heaps;
-
-
-
- {----------------------------------------------------------------------------}
- { TInstrument object implementation. }
- {____________________________________________________________________________}
-
- CONSTRUCTOR TInstrument.Init;
- BEGIN
- TObject.Init;
- END;
-
-
- DESTRUCTOR TInstrument.Done;
- BEGIN
- SetName('');
- FreeContents;
- TObject.Done;
- END;
-
-
- PROCEDURE TInstrument.FreeContents;
- BEGIN
- IF Instr = NIL THEN EXIT;
- IF Instr^.Len > 65520 THEN
- BEGIN
- FullHeap.HFreeMem(POINTER(Instr^.Xtra), Instr^.Len - 65520);
- Instr^.Len := 65520;
- END;
-
- FullHeap.HFreeMem(POINTER(Instr^.Data), Instr^.Len);
- FullHeap.HFreeMem(POINTER(Instr), SizeOf(Instr^));
- END;
-
-
- PROCEDURE TInstrument.Change(Instrument : PInstrumentRec);
- BEGIN
- FreeContents;
- IF Instrument <> NIL THEN
- BEGIN
- FullHeap.HGetMem(POINTER(Instr), SizeOf(Instr^));
- IF Instr <> NIL THEN
- BEGIN
- Move(Instrument^, Instr^, SizeOf(Instr^));
- IF Instr^.NAdj = 0 THEN
- BEGIN
- Instr^.NAdj := $2000;
- Instr^.DAdj := $2000;
- END;
- IF LowQuality THEN
- Desample;
- END;
- END;
- END;
-
- FUNCTION TInstrument.GetName : STRING;
- BEGIN
- IF Name <> NIL THEN
- GetName := Name^
- ELSE
- GetName := '';
- END;
-
-
- PROCEDURE TInstrument.Desample;
- VAR
- w : WORD;
- p : POINTER;
- SizeFree : WORD;
- BEGIN
- WITH Instr^ DO
- IF (Instr <> NIL) AND (Instr^.Data <> NIL) AND
- (Len > 128) AND ((Repl >= 2000) OR (Repl = 0)) THEN
- BEGIN
- FOR w := 0 TO Len DIV 2 - 1 DO
- Data^[w] := (INTEGER(Data^[w*2]) +
- INTEGER(Data^[w*2+1])) DIV 2;
-
- p := Ptr(SEG(Data^), OFS(Data^) + Len DIV 2 + 7);
- p := Ptr(SEG(p^) + (OFS(p^) SHR 4), OFS(p^) AND $8);
-
- SizeFree := Len -
- (WORD((SEG(p^) - SEG(Data^)) SHL 4) +
- WORD( OFS(p^) - OFS(Data^)) );
-
- FullHeap.HFreeMem(p, SizeFree);
-
- Len := Len DIV 2;
- Reps := Reps DIV 2;
- Repl := Repl DIV 2;
- NAdj := NADJ * 2;
- END;
- END;
-
-
-
- PROCEDURE TInstrument.SetName(S: STRING);
- BEGIN
- IF Name <> NIL THEN
- FullHeap.HDisposeStr(Name);
-
- IF S <> '' THEN
- Name := FullHeap.HNewStr(S);
- END;
-
-
-
- {----------------------------------------------------------------------------}
- { TTrack object implementation. }
- {____________________________________________________________________________}
-
- CONSTRUCTOR TTrack.Init;
- BEGIN
- TObject.Init;
- END;
-
-
- DESTRUCTOR TTrack.Done;
- BEGIN
- FullHeap.HDisposeStr(Name);
- FreeContents;
- TObject.Done;
- END;
-
-
- PROCEDURE TTrack.FreeContents;
- BEGIN
- IF Note <> NIL THEN
- FullHeap.HFreeMem(POINTER(Note), Note^.NumNotes*SizeOf(TNoCommandNote) + 2);
- IF Comm <> NIL THEN
- FullHeap.HFreeMem(POINTER(Comm), Comm^.NumNotes*SizeOf(TCommandNote) + 2);
- END;
-
-
- PROCEDURE TTrack.ChangeNote(At: WORD; VAR FullNote: TFullNote);
- VAR
- Track : TFullTrack;
- BEGIN
- GetFullTrack(Track);
- Track[At] := FullNote;
- SetFullTrack(Track);
- END;
-
-
- PROCEDURE TTrack.GetFullTrack(VAR Track: TFullTrack);
- VAR
- i : WORD;
- BEGIN
- FillChar(Track, SizeOf(Track), 0);
-
- IF Note <> NIL THEN
- FOR i := 0 TO Note^.NumNotes DO
- Track[i+Note^.NoteOffset].Note := Note^.Notes[i];
-
- IF Comm <> NIL THEN
- FOR i := 0 TO Note^.NumNotes DO
- Track[i+Note^.NoteOffset].Note := Note^.Notes[i];
- END;
-
-
- PROCEDURE TTrack.SetFullTrack(VAR Track: TFullTrack);
- VAR
- i : WORD;
- MNote : TNoteTrack;
- MComm : TCommTrack;
- BEGIN
- FillChar(MNote, SizeOf(MNote), 0);
- FillChar(MComm, SizeOf(MComm), 0);
- FOR i := 0 TO 255 DO
- BEGIN
- IF (Track[i].Instrument = 0) AND
- (Track[i].Period = 0) AND
- (Track[i].Volume = 0) THEN
- BEGIN
- IF MNote.NoteOffset = i THEN
- INC(MNote.NoteOffset);
- END
- ELSE
- BEGIN
- MNote.NumNotes := i - MNote.NoteOffset + 1;
- MNote.Notes[i - MNote.NoteOffset] := Track[i].Note;
- END;
-
- IF Track[i].Command = mcNone THEN
- BEGIN
- IF MComm.NoteOffset = i THEN
- INC(MComm.NoteOffset);
- END
- ELSE
- BEGIN
- MComm.NumNotes := i - MComm.NoteOffset + 1;
- MComm.Notes[i - MComm.NoteOffset] := Track[i].Comm;
- END;
- END;
-
- FreeContents;
-
- FullHeap.HGetMem(POINTER(Note), MNote.NumNotes*SizeOf(TNoCommandNote) + 2);
- FullHeap.HGetMem(POINTER(Comm), MComm.NumNotes*SizeOf(TCommandNote) + 2);
-
- IF Note <> NIL THEN
- Move(MNote, Note^, MNote.NumNotes*SizeOf(TNoCommandNote) + 2);
- IF Comm <> NIL THEN
- Move(MComm, Comm^, MComm.NumNotes*SizeOf(TCommandNote) + 2);
- END;
-
-
- PROCEDURE TTrack.GetNote(At: WORD; VAR FullNote: TFullNote);
- BEGIN
- DEC(At);
- FillChar(FullNote, SizeOf(FullNote), 0);
-
- IF (Note <> NIL) AND (At >= Note^.NoteOffset) AND
- (At < Note^.NoteOffset + Note^.NumNotes) THEN
- FullNote.Note := Note^.Notes[At - Note^.NoteOffset];
-
- IF (Comm <> NIL) AND (At >= Comm^.NoteOffset) AND
- (At < Comm^.NoteOffset + Comm^.NumNotes) THEN
- FullNote.Comm := Comm^.Notes[At - Comm^.NoteOffset];
- END;
-
-
- FUNCTION TTrack.GetName : STRING;
- BEGIN
- IF Name <> NIL THEN
- GetName := Name^
- ELSE
- GetName := '';
- END;
-
-
-
-
- {----------------------------------------------------------------------------}
- { TPattern object implementation. }
- {____________________________________________________________________________}
-
- CONSTRUCTOR TPattern.Init(Chans: WORD);
- BEGIN
- TObject.Init;
-
- FullHeap.HGetMem(POINTER(Patt), Chans*2 + 4);
-
- IF Patt <> NIL THEN
- FillChar(Patt^, Chans*2 + 4, 0);
- Patt^.NChans := Chans;
- END;
-
-
- DESTRUCTOR TPattern.Done;
- BEGIN
- FullHeap.HDisposeStr(Name);
- FreeContents;
- TObject.Done;
- END;
-
-
- PROCEDURE TPattern.FreeContents;
- BEGIN
- IF Patt <> NIL THEN
- FullHeap.HFreeMem(POINTER(Patt), Patt^.NChans*2 + 4);
- END;
-
-
- FUNCTION TPattern.GetName : STRING;
- BEGIN
- IF Name <> NIL THEN
- GetName := Name^
- ELSE
- GetName := '';
- END;
-
-
-
-
- END.
-