;--------------------------------------------------------------------------;
; AUDIO MANAGER 3.0                                                        ;
; Program by Kenneth Foo Chuan Khit 1994                                   ;
;--------------------------------------------------------------------------;
; AUDIO MANAGER 3.0 ********************************************************

;(stuffs deleted)

;
;STRUCTURES
SmpX    struc
	;MIRROR IMAGE OF AMS HEADER
	SMPmarker               dd 'SMA'
	SMPmemHandle            dd 0
	SMPmemPos               dd 0
	SMPreserved             dd 0
	SMPlength               dd 0    ;Not past offset!
	SMPloopB                dd 0
	SMPloopP                dd 0
	SMPmidC                 dd 0
	SMPdefaultRate          dw 0
	SMPvolume               db 0
	SMPinfo                 dw 0
	SMPname                 db 30 dup(0)
	SMPfileName             db 13 dup(0)
	SmpX    ends
;
;MUSIC STRUCTURES
; MUSIC INTERPRETER STATE STRUCTURE
MIX struc
	MIdivider               dd 14317056     ;NTSC divider (From S3M)
	MIspeed                 dw 0
	MItempo                 dw 0
	MItickCount             dw 0
	MInextSequence          dw 0
	MIcurrentSequence       dw 0
	MIcurrentPattern        dw 0
	MItargetSequence        dw 65535 ;Overriding values. 65535=None
	MItargetRow             dw 65535 ;Overriding values. 65535=None
	MInextRow               dw 0
	MIcurrentRow            dw 0
	MImasterVolume          dw 256
	MIpatternLoopSequence   dw 0
	MIpatternLoopStart      dw 0
	MIpatternLoopEnd        dw 0
	MIpatternLoopCount      db 0
	MIpatternDelayCount     db 0
	MIeventData             db 0
	MIflag                  db 0    ;Bit 0 1=Disable looping (sort of)
	MIsystemType            db 1    ;0=PAL 1=NTSC
	MIX ends
; NOTE STRUCTURE
NoteX struc
	NOTEnote                db 0    ;LowNibble=Note HighNibble=Octave
					;Value 254=Key off
					;      255=None specified.Use previous.
	NOTEsampleNumber        db 0    ;255=None specified. Use previous.
	NOTEvolume              db 0    ;255=None specified
	NOTEeffect              db 0    ;255=None specified
	NOTEeffectData          db 0
	NoteX ends
; MUSIC STRUCTURE (PART OF AMM HEADER)
MusicX  struc
	;MIRROR IMAGE OF AMM HEADER
	MUSmarker               dd 'MMA'
	MUSversion              dw 0
	MUSinfo                 dw 0
	MUSname                 db 40 dup(0)
	MUSnumberTracks         dw 0
	MUSnumberPatterns       dw 0
	MUSnumberSamples        dw 0
	MUSsongLength           dw 0
	MUSsongPlayLength       dw 0
	MUSmasterVolume         dw 256
	MUSamplification        dw 65535
	MUSinitSpeed            db 0
	MUSinitTempo            db 0
	MUSsource               db 0
	MUSextraDataLength      dd 0
	MUSnativeTrackerVersion dw 0
	MUSreserved             db 1+10 dup(0)
	MUSinitPan              db AM@MaxTracks DUP(0)
	MUSsequence             dw MI@MaxSequences dup(0)
	;*Pan flags and pattern sequence follows, but they
	;*are of variable size and are adjusted to fill in the MUSinitPan
	;*and MUSsequence variable.
	;EXTRA DATA/INFO/STUFFS
	MusicX  ends



;[]------------------------------------------------------------------------[]
;| AUDIO MANAGER III by Kenneth Foo aka TechnoMaestro 1994.                 |
;[]------------------------------------------------------------------------[]
;  AMM MUSIC LOADER
;
;NOTES
; Nothing much to say except that this is the native format of AM3 and
; therefore, is the smallest loader.
;
;
.CSEG   AM
AM_MI_AMM_INFO DB 'AUDIO MANAGER 3.0: AMM LOADER by Kenneth Foo',01ah
	AMM_PatLen      dd 0
;
;_AMmusicLoadAMM                                                          
;I:  FileHandle:W, PosInFile:D                                            
;O:  Carry0 = Success, EAX=Pos in file past music                         
;    Carry1 = Error. AX=Error.                                            
;
_AMmusicLoadAMM PROC USES ebx ecx edx si di es ds fs, FileHandle:WORD,PosInFile:DWORD
	LOCAL   TMPtrackCount:WORD,TMPtrackNumber:WORD,TMPtrackCount:WORD,\
		TMPtrackNumber:WORD,TMPtotalSampleSize:DWORD,TMPbuffer:WORD,\
		TMPprevSmp:BYTE,TMPprevEFXnum:BYTE,TMPprevEFXdata:BYTE

	setDS
	cld
	;CHECK AM SYSTEM, IF INITIALIZED.
	mov     ErrorCode,ERR_NotPossible
	test    AM.AM_Status,1b
	jz      @@Error
	;CHECK IF A MODULE IS ALREADY LOADED
	test    AM.AM_Status,1000000000000b
	mov     ErrorCode,ERR_AlreadyDone
	jnz     @@Error

	;LOAD AMM HEADER
	mov     ErrorCode,ERR_CannotManageDisk
	push    Filehandle
	push    PosInFile
	push    word ptr 0
	call    _DMseekFile
	jc      @@Error
	push    FileHandle                      ;Read 80-byte header
	call    _AMmusicGetInfo
	push    dx ax
	push    word ptr 80
	call    _DMreadFile
	jc      @@Error
	add     PosInFile,80

	;CHECK MARKER
	mov     ErrorCode,ERR_BadData
	cmp     Music.MUSmarker,'MMA'        ;Marker
	jne     @@Error

	;READ PAN POSITIONS
	movzx   eax,Music.MUSnumberTracks
	add     PosInFile,eax
	push    FileHandle
	push    seg Music.MUSinitPan
	push    offset Music.MUSinitPan+1       ;Start from Track 1!
	push    ax
	call    _DMreadFile
	jc      @@Error
	;READ SEQUENCES
	movzx   eax,Music.MUSsongLength
	shl     eax,1
	add     PosInFile,eax
	push    FileHandle
	push    seg Music.MUSsequence
	push    offset Music.MUSsequence
	push    ax
	call    _DMreadFile
	jc      @@Error
	;ALLOCATE MEMORY FOR TRACKS' PATTERNS
	mov     bx,2                            ;Alloc track memory
	mov     cx,Music.MUSnumberTracks        ;BX=2 (start from Track1)
	@@AllocateTracks:
	mov     ax,(64*(SIZE NoteX))
	mul     Music.MUSnumberPatterns
	xchg    ax,dx
	shl     eax,16
	mov     ax,dx
	push    eax
	call    _MEMqAlloc
	doerr   ERR_NotEnoughMemory
	mov     di,AM_TrackTable[bx]
	mov     [cs:TrackX.TRKmemHandle][di],eax
	inc     bx
	inc     bx
	loop    @@AllocateTracks
	;ALLOCATE TEMPORARY BUFFER
	mov     ax,(64*6)                       ;6-bytes max for each row...
	mul     Music.MUSnumberPatterns
	movzx   eax,ax
	push    eax
	call    _MEMallocBase
	doerr   ERR_NotEnoughMemory
	mov     TMPbuffer,dx
	;READ PATTERNS
	mov     ax,Music.MUSnumberTracks
	mov     TMPtrackCount,ax
	mov     TMPtrackNumber,1
	mov     ax,Music.MUSnumberPatterns
	mov     bx,(SIZE NoteX)*64
	mul     bx
	movzx   ecx,ax
	@@ReadTrackPatterns:
	mov     bx,TMPtrackNumber
	shl     bx,1
	mov     bx,AM_TrackTable[bx]
	push    [cs:bx][TrackX.TRKmemHandle]
	push    dword ptr 0
	call    _MEMqGetAddress
	doerr   ERR_CannotManageMemory
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;DECOMPRESS PATTERNS IF PATTERNS COMPRESSED
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	test    Music.MUSinfo,08000h    ;10000b
	jz      @@Raw
@@Encoded:
      comment ~
                 BIT 0 1=Note present
                     1 1=Instrument number present
                     2 1=Volume present
                     3 1=Effect present
                     4 1=Change in instrument number
                     5 1=Change in effect number
                     6 1=Change in effect data
                     7 1=Data present  0=Empty rows RLE encoded .
                         If bit set, then Bit 0-6 = Number of empty rows-1.
                         We do not have a signal for end of pattern because
                         it isn't necessary, as each pattern must be 64-rows
                         and if we reach more than 64 rows, it's time to
                         increment the pattern count.
                         This bit can save up to 128 empty rows with just
                         one byte! :-) Now that's COOL! :-)
      
      ~

	mov     TMPprevSmp,1                    ;DEFAULT STUFF
	mov     TMPprevEFXnum,MI@EFXsetSpeed
	mov     TMPprevEFXdata,6
	mov     es,dx
	mov     di,ax
	;===First, clear all data quickly===
	push    di
	mov     ax,(SIZE NoteX)*64
	mul     Music.MUSnumberPatterns
	mov     cx,ax
	mov     eax,0FFFFFFFFh
	mov     dx,cx
	shr     cx,2
	rep     stosd
	mov     cx,dx
	and     cx,11b
	rep     stosb
	pop     di
	;===Read encoded data & decode===
	push    FileHandle
	push    cs
	push    offset AMM_PatLen
	push    word ptr 4
	call    _DMreadFile
	doerr   ERR_CannotManageDisk
	push    FileHandle
	push    TMPbuffer
	push    word ptr 0
	push    word ptr AMM_PatLen
	call    _DMreadFile
	doerr   ERR_CannotManageDisk
	mov     ecx,AMM_PatLen                  ;ECX count for PosInFile update
	add     ecx,4
	mov     fs,TMPbuffer
	xor     si,si
      @@ReadEvent:
	mov     ah,[fs:si]
	inc     si
	test    ah,10000000b
	jnz     short @@EventPresent
	;===RLE decompression
	inc     ah                                      ;Count was less 1
	mov     al,SIZE NoteX
	mul     ah
	add     di,ax
	dec     AMM_PatLen
	jz      @@DoneDecompress
	jmp     short @@ReadEvent
      @@EventPresent:
	;===EVENTS DECODING
	test    ah,00000001b                            ;Note
	jz      short @@NoNote
	mov     al,[fs:si]
	mov     [es:di][NoteX.NOTEnote],al
	inc     si
	dec     AMM_PatLen
	@@NoNote:

	test    ah,00000010b                            ;Sample
	jz      short @@NoSmp
	mov     al,TMPprevSmp ;Chg in smp num?
	test    ah,00010000b
	jz      short @@NoNewSmp
	mov     al,[fs:si]
	mov     TMPprevSmp,al
	inc     si
	dec     AMM_PatLen
	@@NoNewSmp:
	mov     [es:di][NoteX.NOTEsampleNumber],al
	@@NoSmp:

	test    ah,00000100b                            ;Volume
	jz      short @@NoV
	mov     al,[fs:si]
	mov     [es:di][NoteX.NOTEvolume],al
	inc     si
	dec     AMM_PatLen
	@@NoV:

	test    ah,00001000b                            ;Efx
	jz      short @@NoEfx
	;===CHG IN EFX NUM?===
	mov     al,TMPprevEFXnum
	test    ah,00100000b
	jz      short @@NoNewEFXNum
	mov     al,[fs:si]
	mov     TMPprevEFXnum,al
	inc     si
	dec     AMM_PatLen
	@@NoNewEFXNum:
	mov     [es:di][NoteX.NOTEeffect],al
	;===CHG IN EFX DATA?===
	mov     al,TMPprevEFXdata
	test    ah,01000000b
	jz      short @@NoNewEFXdata
	mov     al,[fs:si]
	mov     TMPprevEFXdata,al
	inc     si
	dec     AMM_PatLen
	@@NoNewEFXdata:
	mov     [es:di][NoteX.NOTEeffectData],al
	@@NoEfx:

	add     di,SIZE NoteX
	;;===post-event RLE decompression===
	;test    Music.MUSinfo,04000h
	;jz      @@NoExtendedPack
	;shr     ax,4                                    ;No post-event RLE
	;and     ah,0111b
	;mov     al,SIZE NoteX
	;mul     ah
	;add     di,ax
	;@@NoExtendedPack:
	;;=================================
	dec     AMM_PatLen
	jz      short @@DoneDecompress
	jmp     @@ReadEvent
@@Raw:  push    FileHandle
	push    dx
	push    ax
	push    cx
	call    _DMreadFile
	doerr   ERR_CannotManageDisk
@@DoneDecompress:
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	add     PosInFile,ecx
	inc     TMPtrackNumber
	dec     TMPtrackCount
	jnz     @@ReadTrackPatterns

	;DEALLOCATE TEMPORARY BUFFER
	push    TMPbuffer
	push    word ptr 0
	call    _MEMdeallocBase
	doerr   ERR_CannotManageMemory
	;READ SAMPLE HEADERS
	push    FileHandle
	push    seg MI_SampleInfos
	push    (offset MI_SampleInfos)+(SIZE SmpX)     ;Sample 1!
	mov     ax,Music.MUSnumberSamples
	mov     bx,SIZE SmpX
	mul     bx
	movzx   eax,ax
	add     PosInFile,eax
	push    ax
	call    _DMreadFile
	doerr   ERR_CannotManageDisk
	;GET TOTAL SAMPLE SIZE
	mov     ErrorCode,ERR_BadData
	mov     TMPtotalSampleSize,0
	mov     cx,Music.MUSnumberSamples
	mov     bx,1
	@@ReadSampleHeaders:
	push    bx
	call    _AMmusicGetSampleInfo
	mov     fs,dx
	mov     si,ax
	cmp     [fs:si][SmpX.SMPmarker],'SMA'  ;Marker
	jne     @@Error
	mov     eax,TMPtotalSampleSize          ;Also, pos in mem!
	mov     [fs:si][SmpX.SMPmemPos],eax
	mov     eax,[fs:si][SmpX.SMPlength]
	add     eax,32-1                        ;Round to 32-byte boundary
	and     eax,0FFFFFFE0h
	add     TMPtotalSampleSize,eax
	inc     bx
	loop    @@ReadSampleHeaders
	;ALLOCATE MEMORY FOR SAMPLES
	push    TMPtotalSampleSize
	call    _AMallocMem
	doerr   ERR_NotEnoughMemory
	mov     MI_SampleMemHandle,eax
	;READ SAMPLES
	mov     TMPtotalSampleSize,0
	mov     cx,Music.MUSnumberSamples
	or      cx,cx
	jz      short @@NoSample
	mov     bx,1
	@@ReadSamples:
	push    bx
	call    _AMmusicGetSampleInfo
	mov     fs,dx
	mov     si,ax
	mov     eax,MI_SampleMemHandle
	mov     [fs:si][SmpX.SMPmemHandle],eax
	push    fs
	push    si
	push    FileHandle
	push    PosInFile
	call    _AMloadSample
	jc      @@Error2
	mov     PosInFile,eax
	inc     bx
	loop    @@ReadSamples
	@@NoSample:

	add     eax,Music.MUSextraDataLength    ;Real offset...skips data.

	or      AM.AM_Status,1000000000000b        ;Set LOADED
	quit
	ENDP
.CSEG_ENDS AM

