[#31] Re: Quite OK Audio

@Krashan, post #30

Sorry, moj blad, nie zauwazylem, ze wrociles do poczatkow. Stary jestem.
Masz tu wersje z wolnym D1, w zaleznosci od tego jak uzyjesz wolny rejestr D1, to chyba powinna byc szybsza, niz aktualna.


;
; QOA decoder

;==============================================================================
; STEREO DECODING STRATEGY
;
; Stereo QOA files have interleaved slices in LR order. Decoding them in order
; means LMS state has to be swapped for each slice. To avoid this, there are 
; two passes over a frame. The first pass loads L channel LMS state then
; decodes only even slices and stores audio samples at output buffer offset 0,
; advancing 4 bytes after each sample. The second pass loads R channel LMS
; state, then decodes only odd slices and stores audio samples at output buffer
; offset 2, advancing 4 bytes after each sample.
;==============================================================================

;==============================================================================
; Decodes QOA mono frame to a buffer.
; INPUTS:
;   d0 - number of slices available in the frame buffer (1 to 256 including)
;   a0 - frame buffer
;   a1 - output buffer
;==============================================================================

		XDEF	_DecodeMonoFrame

_DecodeMonoFrame:
		MOVEM.L	d2-d7/a2-a6,-(sp)
		SUBQ.L	#1,d0
		LEA	sampoff,a2
		MOVE.W	#0,(a2)
		MOVE.W	d0,d7                  ; slice counter
		MOVEA.W	(a0)+,a2               ; loading LMS history
		MOVEA.W	(a0)+,a3
		MOVEA.W	(a0)+,a4
		MOVEA.W	(a0)+,a5
		MOVE.L	(a0)+,d2               ; loading LMS weights
		MOVE.L	(a0)+,d3
nextslice: 	LEA	dequant(pc),a6         ; pointer to lookup table
		MOVE.L	(a0)+,d0               ; the first half of slice
;		MOVE.L	(a0)+,d1              ; the second half of slice
		SWAP    d7
		BSR.S   slice                  ; decode slice
		SWAP    d7
		DBF	d7,nextslice
		MOVEM.L	(sp)+,d2-d7/a2-a6
		RTS

;==============================================================================
; Decodes QOA stereo frame to a buffer.
; INPUTS:
;   d0.w - number of slices per channel available in the frame buffer (1 to 256
;          including)
;   a0.l - frame buffer
;   a1.l - output buffer
;==============================================================================

		XDEF	_DecodeStereoFrame

_DecodeStereoFrame:
		MOVEM.L	d2-d7/a2-a6,-(sp)
		SUBQ.L	#1,d0
		LEA	sampoff,a2
		MOVE.W	#2,(a2)
		MOVE.L	a0,-(sp)
		MOVE.L	a1,-(sp)
		MOVE.W	d0,-(sp)

		; L channel pass

		MOVE.W	d0,d7                  ; slice counter
		MOVEA.W	(a0)+,a2               ; loading LMS history
		MOVEA.W	(a0)+,a3
		MOVEA.W	(a0)+,a4
		MOVEA.W	(a0)+,a5
		MOVE.L	(a0)+,d2               ; loading LMS weights
		MOVE.L	(a0)+,d3
		LEA	16(a0),a0              ; skip R channel LMS state
nextleft: 	LEA	dequant(pc),a6         ; pointer to lookup table
		MOVE.L	(a0)+,d0               ; the first half of slice
;		MOVE.L	(a0)+,d1               ; the second half of slice
		SWAP    d7
		BSR.S   slice                  ; decode slice
		SWAP    d7
		ADDQ.L	#8,a0                  ; skip R channel slice
		DBF	d7,nextleft

		; R channel pass

		MOVE.W	(sp)+,d7               ; slice counter
		MOVEA.L	(sp)+,a1               ; output buffer
		MOVEA.L	(sp)+,a0               ; input buffer
		ADDQ.L	#2,a1                  ; R channel samples
		LEA	16(a0),a0              ; skip L channel LMS state
		MOVEA.W	(a0)+,a2               ; loading LMS history
		MOVEA.W	(a0)+,a3
		MOVEA.W	(a0)+,a4
		MOVEA.W	(a0)+,a5
		MOVE.L	(a0)+,d2               ; loading LMS weights
		MOVE.L	(a0)+,d3
nextright: 	LEA	dequant(pc),a6         ; pointer to lookup table
		ADDQ	#8,a0                  ; skip L channel slice
		MOVE.L	(a0)+,d0               ; the first half of slice
;		MOVE.L	(a0)+,d1              ; the second half of slice
		SWAP    d7
		BSR.S   slice                  ; decode slice
		SWAP    d7
		DBF     d7,nextright

		MOVEM.L	(sp)+,d2-d7/a2-a6
		RTS

;==============================================================================
; Decodes QOA slice of mono/stereo stream.
; Registers usage:
;   d0,d1 - slice
;   d2,d3 - LMS weights (updated)
;   d4 - residual sample, quantized, dequantized, scaled
;   d5 - predicted sample
;   d6 - scratch register
;   d7 - not used (slice loop counter)
;   a0 - not used (input data pointer)
;   a1 - output data pointer (advanced)
;   a2,a3,a4,a5 - LMS history (updated)
;   a6 - pointer to 'dequant' lookup table (modified)
;==============================================================================

slice:		ROL.L	#8,d0
		MOVE.B	d0,d4
		ANDI.W	#$00F0,d4              ; scale factor in bits 7:4 of d4
		ADDA.W	d4,a6                  ; select lookup table row

		;extract 9 residuals from d0, r[0] is in position already

		MOVE.W  #8,d7              ; can't MOVEQ, upper half in use
		BSR.S   DecSamp

		; now the first bit of r[9] is in d0:0, pull two bits from d1
  move.l (A0),D4
		ADD.L   d4,d4
		ADDX.B  d0,d0
		ADD.L   d4,d4
		ADDX.B  d0,d0
		ADD.B   d0,d0
		MOVE.W  #0,d7
		BSR.S	DecSamp
		MOVE.L  (A0)+,d0
		ROL.L   #4,d0

		; extract 10 residuals from d0

		MOVE.W  #9,d7
		BRA.S   DecSamp            ; (ab)use RTS at end of DecSamp

;==============================================================================
; Decodes a single sample. 3-bit encoded sample is in bits 3:1 of register d4
;==============================================================================

DecLoop:	ROL.L   #3,d0

		; decode residual sample using lookup table, store in d4

DecSamp:	MOVEQ   #$E,d4
		AND.W   d0,d4                ; extract encoded sample in d4
		MOVE.W  (a6,d4.w),d4           ; decode with lookup table

		; calculate predicted sample, store in d5

		MOVE.W	a5,d5                  ; history[-1]
		MULS.W	d3,d5                  ; *= weights[-1]
		SWAP	d3
		MOVE.W	a4,d6                  ; history[-2]
		MULS.W	d3,d6                  ; *= weights[-2]
		ADD.L	d6,d5
		MOVE.W	a3,d6                  ; history[-3]
		MULS.W	d2,d6                  ; *= weights[-3]
		ADD.L	d6,d5
		SWAP	d2
		MOVE.W	a2,d6                  ; history[-4]
		MULS.W	d2,d6                  ; *= weights[-4]
		ADD.L	d6,d5
		ASR.L	#6,d5
		ASR.L	#7,d5                  ; predicted sample in d5

		; add predicted sample to reconstructed residual with clamp to
		; 16-bit signed range, store in d5, code by meynaf from EAB

		EXT.L	d4
		ADD.L	d4,d5
		CMPI.L	#32767,d5
		BLE.S	noupper
		MOVE.W	#32767,d5
		BRA.S	clamped
noupper:	CMPI.L	#-32768,d5
		BGE.S	clamped
		MOVE.W	#-32768,d5

		; update LMS weights, reconstructed sample in d5, decoded
		; residual in d4

clamped:	ASR.W	#4,d4                  ; scale residual signal down

		MOVE.W	a2,d6
		BMI.S	h4neg
		ADD.W	d4,d2
		BRA.S	h3
h4neg:  	SUB.W	d4,d2
h3:		SWAP	d2
		MOVE.W	a3,d6
		BMI.S	h3neg
		ADD.W	d4,d2
		BRA.S	h2
h3neg:		SUB.W	d4,d2
h2:		MOVE.W	a4,d6
		BMI.S	h2neg
		ADD.W	d4,d3
		BRA.S	h1
h2neg:		SUB.W	d4,d3
h1:		SWAP	d3
		MOVE.W	a5,d6
		BMI.S	h1neg
		ADD.W	d4,d3
		BRA.S	update
h1neg:  	SUB.W	d4,d3

		; update history vector

update: 	MOVEA.W	a3,a2
		MOVEA.W	a4,a3
		MOVEA.W	a5,a4
		MOVEA.W	d5,a5

		; store output sample

		MOVE.W 	d5,(a1)+
		ADDA.W	sampoff(pc),a1
		DBF     d7,DecLoop
		RTS

; not very effective, should be stored in some register once registers
; usage is optimized

sampoff:	DC.W	0

dequant:	DC.W	   1,    -1,    3,    -3,    5,    -5,     7,     -7
		DC.W	   5,    -5,   18,   -18,   32,   -32,    49,    -49
		DC.W	  16,   -16,   53,   -53,   95,   -95,   147,   -147
		DC.W	  34,   -34,  113,  -113,  203,  -203,   315,   -315
		DC.W	  63,   -63,  210,  -210,  378,  -378,   588,   -588
		DC.W	 104,  -104,  345,  -345,  621,  -621,   966,   -966
		DC.W	 158,  -158,  528,  -528,  950,  -950,  1477,  -1477
		DC.W	 228,  -228,  760,  -760, 1368, -1368,  2128,  -2128
		DC.W	 316,  -316, 1053, -1053, 1895, -1895,  2947,  -2947
		DC.W	 422,  -422, 1405, -1405, 2529, -2529,  3934,  -3934
		DC.W	 548,  -548, 1828, -1828, 3290, -3290,  5117,  -5117
		DC.W	 696,  -696, 2320, -2320, 4176, -4176,  6496,  -6496
		DC.W	 868,  -868, 2893, -2893, 5207, -5207,  8099,  -8099
		DC.W	1064, -1064, 3548, -3548, 6386, -6386,  9933,  -9933
		DC.W	1286, -1286, 4288, -4288, 7718, -7718, 12005, -12005
		DC.W	1536, -1536, 5120, -5120, 9216, -9216, 14336, -14336
[#32] Re: Quite OK Audio

@Don_Adan, post #31

Najprosciej cos takiego chyba
w inicie
move.l	#32767,d1


w petli
CMPI.L	d1,d5
		BLE.S	noupper
		MOVE.W	d1,d5
		BRA.S	clamped
[#33] Re: Quite OK Audio

@Krashan, post #30

(...) wolę się zabrać za player.


Prayer2 source code link
[#34] Re: Quite OK Audio

@Don_Adan, post #32

Więc dobra. Zacznijmy od tego, że podesłałeś niedziałający kod, co niespecjalnie świadczy o Twoim szacunku do mojego czasu. 20 mintu zeszło mi na debugowanie. W kodzie oryginalnym gdy wysuwam dwa bity z drugiej połowy slice do pierwszej, to ta druga połowa zostaje tym samym przesunięta w lewo o te dwa bity. W Twojej modyfikacji ładujesz tę drugą połowę jeszcze raz z pamięci, więc to przesunięcie znika. Trzeba to skompensować zmieniając ROL.L #4,d0 na ROL.L #6,d0 i wtedy dekoder działa (przy czym 68000 łapie dodatkowe 4 takty na tym).

Zrobiłem to, co proponujesz, czyli d1 wykorzystałem do trzymania tam liczby 32767, dzięki czemu jedno CMPI.L zostaje zastąpione przez CMP.L, co na 68000 daje 8 cykli (mogę też niżej zastąpić MOVE.W #32767, d5, przez MOVE.W d1,d5, ale tak jak wyżej pisałem, ta ścieżka kodu jest wykonywana ekstremalnie rzadko). Więc teoretycznie jest tak: za uwolnienie rejestru płacę dodatkowym załadowaniem z pamięci (12 cykli) i 4 cyklami na dłuższym ROL.L, czyli w sumie 16 cykli na 20 sampli, 0,8 cykla na sampel. A zarabiam 8 cykli na sampel, więc jestem do przodu. Co na to pomiary?

Dla 68000/28 dekodowanie pliku testowego przyspiesza z 51,71 do 50,87 sekundy (1,62%).
Dla 68020/28 dekodowanie pliku testowego przyspiesza z 18,03 do 18,00 sekundy (0,17%).

Wynik dla 68020 jest słaby pewnie dlatego, że procesor ma ICache, więc CMPI.L #x, Dn nie jest aż tak kosztowne w porównaniu z CMP.L Dm,Dn.

68000 robi na tym pliku średnio około 660 cykli na sampla (ma 1 242 424 sampli stereo), więc urwanie 7,2 cykla (teoretycznie) mniej więcej zgadza się z pomiarem.

Ostatnia aktualizacja: 03.05.2025 10:54:14 przez Krashan
1
[#35] Re: Quite OK Audio

@Krashan, post #34

Wykorzystałem twórczo ten zwolniony rejestr. Otóż użyłem go do trzymania jednego elementu historii predyktora, zamiast rejestru A5. I co mi to daje? Daje mi to możliwość powrotu do pomysłu na szybki clamping wartości 32-bitowej ze znakiem do zakresu 16-bitowego. Biorąc pod uwagę, że ścieżka kodu "nie ma clampingu" w tym przypadku jest wykonywana prawie zawsze, powinna być najkrótsza. Efekt? 4,85% przyspieszenia na 68000, 2,15% przyspieszenia na 68020. Kod już wrzuciłem na GitHuba.
3
[#36] Re: Quite OK Audio

@Krashan, post #35

Gratki, i sorry za problem z rol.l. Nie zauwazylem tego.
A nie mam dzialajacej Amigi teraz, wiec wszystko w pamieci robie.
Mozesz jeszcze
MOVE.L	(a0)+,d0               ; the first half of slice


Dac w samym Slice
slice:		
MOVE.L	(a0)+,d0               ; the first half of slice
ROL.L	#8,d0

Nie przyspieszy to raczej dzialania, ale skroci kod procedury o 4 bajty.
1
[#37] Re: Quite OK Audio

@Don_Adan, post #36

Spróbowałem jeszcze przyspieszyć aktualizację wag. Niestety zarówno wersja "zawsze dodaj, a jak trzeba dwa razy odejmij", jak i "zawsze odejmij, a jak trzeba dwa razy dodaj" okazały się wolniejsze od prostego kodu, więc tutaj już statystyka nie odgrywa roli. Próbowałem też wykorzystać wolną górną połowę D1 do prekalkulowania podwojonej wartości, ale koszty dwa razy SWAP i tego prekalkulowania zjadły cały zysk i było jeszcze wolniej.

Uznaję więc, że w tej postaci kodu nie da się już znacząco przyspieszyć. Wydam wersję 1.2 za kilka dni, i skupię się na playerze.
1
[#38] Re: Quite OK Audio

@Krashan, post #37

Zakladajac, ze nie chcesz powiekszac tablicy dwukrotnie to mozesz uzyc tego:

DecSamp:	MOVEQ   #$E,d4
		AND.W   d0,d4                ; extract encoded sample in d4
;		MOVE.W  (a6,d4.w),d4         ; decode with lookup table
   move.w  (a6,d4.w),A5         ; decode with lookup table
		; calculate predicted sample, store in d5

		MOVE.W	d1,d5                  ; history[-1]
		MULS.W	d3,d5                  ; *= weights[-1]
		SWAP	d3
		MOVE.W	a4,d6                  ; history[-2]
		MULS.W	d3,d6                  ; *= weights[-2]
		ADD.L	d6,d5
		MOVE.W	a3,d6                  ; history[-3]
		MULS.W	d2,d6                  ; *= weights[-3]
		ADD.L	d6,d5
		SWAP	d2
		MOVE.W	a2,d6                  ; history[-4]
		MULS.W	d2,d6                  ; *= weights[-4]
		ADD.L	d6,d5
		ASR.L	#6,d5
		ASR.L	#7,d5                  ; predicted sample in d5

		; add predicted sample to reconstructed residual with clamp to
		; 16-bit signed range, store in d5

;		EXT.L   d4
 move.l A5,D4
		ADD.L   d4,d5
 		MOVEA.W d5,a5              ; with sign-extend to 32 bits
		CMPA.L  d5,a5
		BEQ.S   clamped
		TST.L   d5
		SPL     d5                 ; ??FF positive, ??00 negative
		EXT.W   d5                 ; FFFF positive, 0000 negative
		EORI.W  #$8000,d5          ; 7FFF positive, 8000 negative

		; update LMS weights, reconstructed sample in d5, decoded
		; residual in d4

clamped:	ASR.W	#4,d4                  ; scale residual signal down


Powinno byc 2c szybciej na 68020.
Byc moze jeszcze gdzies da sie wykorzystac ten rejestr A5, jako temp, bo w sumie mozna go uzywac w dowolnym miejscu do takiego celu.
[#39] Re: Quite OK Audio

@Krashan, post #37

A i jeszcze jedno mi przyszlo do glowy.
Skoro masz wolny highword w D1, to gdybys zamiast D7 uzywal D1 jako licznika to wtedy mozesz uzywac moveq dla D7 dalej w kodzie programu. Troche krocej i minimalnie szybciej tez.
[#40] Re: Quite OK Audio

@Don_Adan, post #39

A tak przy okazji z ciekawosci zapytam.
Czy da sie tak zamieszac w kodzie zeby w 060 maksymalnie podkrecic predkosc superscalarem?
[#41] Re: Quite OK Audio

@Phibrizzo, post #40

Trochę się pewnie da, chociaż nie za bardzo, bo ten kod już jest dość ciasny i krótki. Zawsze możesz próbować, ja nie mam 68060, a optymalizacja pod 68060 na emulatorze nie ma sensu.
[#42] Re: Quite OK Audio

@Phibrizzo, post #40

Ja osobiscie nigdy nie kodowalem specjalnie pod 68060.
Choc kiedys ktos mi tam troche doradzal w sprawie kodu pod 68060, bo zwykle wystarczy odpowiednio zmienic kolejnosc instrukcji, zeby program byl szybszy na 68060.
Troche tylko probowalem, ale darowalem soobie, taki kod jest dla mnie duzo mniej czytelny.
Bo normalnie jak sie pisze kod to sie po kolei osiaga jakies rezultaty, a pod 68060 to jakby miksowac kolejnosc instrukcji i pisac naraz dwie czesci jakiejs procedury.
Przynajmniej tak to pamietam.
Mozesz sie zwrocic do paraj-a z EAB, pokazujac mu kod Krashana, paraj jest wedlug mnie dobry jesli chodzi o 68060.
Tak mi sie przynajmniej wydaje.
A w sprawie czytelnosci kodu, to jednak ponizsza wersja lepiej wyglada, niz moja poprzednia propozycja.

DecSamp:	MOVEQ   #$E,d4
		AND.W   d0,d4                ; extract encoded sample in d4
		MOVE.W  (a6,d4.w),d4         ; decode with lookup table
		; calculate predicted sample, store in d5

		MOVE.W	d1,d5                  ; history[-1]
		MULS.W	d3,d5                  ; *= weights[-1]
		SWAP	d3
		MOVE.W	a4,d6                  ; history[-2]
		MULS.W	d3,d6                  ; *= weights[-2]
		ADD.L	d6,d5
		MOVE.W	a3,d6                  ; history[-3]
		MULS.W	d2,d6                  ; *= weights[-3]
		ADD.L	d6,d5
		SWAP	d2
		MOVE.W	a2,d6                  ; history[-4]
		MULS.W	d2,d6                  ; *= weights[-4]
		ADD.L	d6,d5
		ASR.L	#6,d5
		ASR.L	#7,d5                  ; predicted sample in d5

		; add predicted sample to reconstructed residual with clamp to
		; 16-bit signed range, store in d5

;		EXT.L   d4
;		ADD.L   d4,d5
 move.w D4,A5
 add.l A5,D5
 		MOVEA.W d5,a5              ; with sign-extend to 32 bits
		CMPA.L  d5,a5
		BEQ.S   clamped
		TST.L   d5
		SPL     d5                 ; ??FF positive, ??00 negative
		EXT.W   d5                 ; FFFF positive, 0000 negative
		EORI.W  #$8000,d5          ; 7FFF positive, 8000 negative

		; update LMS weights, reconstructed sample in d5, decoded
		; residual in d4

clamped:	ASR.W	#4,d4                  ; scale residual signal down
1
[#43] Re: Quite OK Audio

@Krashan, post #34

Zacznijmy od tego, że podesłałeś niedziałający kod, co niespecjalnie świadczy o Twoim szacunku do mojego czasu. 20 mintu zeszło mi na debugowanie.

Przestań się mazać. Weź się w garść. Jest pan przecież mężczyzną.
[#44] Re: Quite OK Audio

@Don_Adan, post #42

Mozna by chyba jeszcze tak zrobic to laczenie bitow.
Ale nie jestem pewien czy to szybsze jest, bo krotsze tak.


; now the first bit of r[9] is in d0:0, pull two bits from d1
  swap D0
  move.w (A0),D0
  lsl.l #3,D0
  swap D0
		MOVE.W  #0,d7
		BSR.S	DecSamp
		MOVE.L  (A0)+,d0
		ROL.L   #6,d0
[#45] Re: Quite OK Audio

@Don_Adan, post #44

Albo tak?
; now the first bit of r[9] is in d0:0, pull two bits from d1
  lsr.w #1,D0
  move.l (A0)+,D0
  rolx.l #4,D0
 		MOVE.W  #0,d7
		BSR.S	DecSamp
		MOVE.L  (A0)+,d0
		ROL.L   #6,d0


Tylko trzeba by sprawdzic, czy z bitami nie namieszalem.

Ostatnia aktualizacja: 04.05.2025 06:34:39 przez Don_Adan
[#46] Re: Quite OK Audio

@Don_Adan, post #45

; now the first bit of r[9] is in d0:0, pull two bits from d1
  lsr.w #1,D0
  move.l (A0),D0
  rolx.l #4,D0
 		MOVE.W  #0,d7
		BSR.S	DecSamp
		MOVE.L  (A0)+,d0
		ROL.L   #6,d0
[#47] Re: Quite OK Audio

@Don_Adan, post #46

A moze cos takiego byloby szybkie i dzialalo?
; now the first bit of r[9] is in d0:0, pull two bits from d1
  lsr.w #1,D0
  move.l (A0)+,D0
  rolx.l #4,D0
 subx.l D7,D7 ; backup X bit
 		MOVE.W  #0,d7
		BSR.S	DecSamp
                lsr.l #1,D0
		add.l D7,D7 ; restore X bit
		addx.l D0,D0
[#48] Re: Quite OK Audio

@Don_Adan, post #47

Chyba znalazlem w koncu ladne rozwiazanie:

; now the first bit of r[9] is in d0:0, pull two bits from d1

  moveq #1,D4
  and.b D0,D4
  move.l (A0)+,D0
  add.l D0,D0
  addx.b D4,D4
  add.l D0,D0
  addx.b  D4,D4
  add.b D4,D4
  clr.w D7
  bsr.b OnlyOnce
  rol.l   #4,D0

		; extract 10 residuals from d0

		MOVE.W  #9,d7
		BRA.S   DecSamp            ; (ab)use RTS at end of DecSamp

;==============================================================================
; Decodes a single sample. 3-bit encoded sample is in bits 3:1 of register d4
;==============================================================================

DecLoop:	ROL.L   #3,d0

		; decode residual sample using lookup table, store in d4

DecSamp:	MOVEQ   #$E,d4
		AND.W   d0,d4                ; extract encoded sample in d4
OnlyOnce:
		MOVE.W  (a6,d4.w),d4           ; decode with lookup table

		; calculate predicted sample, store in d5
[#49] Re: Quite OK Audio

@Krashan, post #37

Jaka jest roznica w wielkosci dekodera w bajtach pomiedzy obiema wersjami?
Czyli ta z branchem (krotsza), i z podwojnym odejmowaniem/dodawaniem?
Bo mi to wyglada, ze procedura nie miesci sie w cache 68020/68030 o pare bajtow gora.
Zrob test obu wersji na 68000.
Jesli wersja z podwojnym/odejmowaniem bedzie szybsza od wersji z branchem to jest to kwestia cache 68020/68030.

Dlatego wszystkie optymalizacje wielkosci dekodera trzeba by zrobic, typu moveq D7, czy MOVE.L (a0)+,d0 w Slice.
Moze jeszcze cos sie znajdzie.


Ostatnia aktualizacja: 04.05.2025 12:54:38 przez Don_Adan
[#50] Re: Quite OK Audio

@Don_Adan, post #49

Ewentualnie wylacz cache dla 68020/68030 i wtedy zrob testy obu wersji.
Takie wyniki beda wtedy wiarygodne.
Jak jestes na granicy cache to wyniki moga byc zaklamane.
Podejrzewam ze dlatego clamping meynafa az o tyle przyspieszyl, choc byl bardzo rzadko robiony.
[#51] Re: Quite OK Audio

@Don_Adan, post #49

Aktualna wersja dekodera z GitHuba:
Procedura slice ma 178 bajtów.
Pozostała część kodu dla plików mono to 50 bajtów, dla plików stereo 88 bajtów. Czyli cały kod, jaki ma szansę znaleźć się w instruction cache to 228 bajtów przy mono i 266 bajtów przy stereo. Z tym, że cały nie musi, bo część "pozostałego kodu" jest wykonywana tylko raz na 5120/10240 próbek audio. Czyli dość rzadko i przy włączonym multitaskingu szanse na utrzymanie się tej części w ICache są niewielkie.
[#52] Re: Quite OK Audio

@Don_Adan, post #50

Ewentualnie wylacz cache dla 68020/68030 i wtedy zrob testy obu wersji. Takie wyniki beda wtedy wiarygodne.
Szeregowy użyszkodnik który sobie będzie chciał rozkodować plik (a niedługo i odegrać, player jest w robocie), a nawet np. programista gry, który będzie rozpakowywał dźwięki przy uruchamianiu, nie będzie przecież wyłączał cache. Ono po to dokładnie jest, żeby zasuwało szybciej. I to mnie interesuje, a nie jakieś akademickie rozważania o "zakłamywaniu wyników przez cache".

A tak żeby przejść do rzeczy bardziej praktycznych, z QoaToAiff można już teraz zrobić "odtwarzacz", jeżeli mamy zainstalowane w systemie AHI, a co za tym idzie urządzenie AUDIO:. Po skonfigurowaniu wybranej jednostki AHI w prefsach (np. jednostki 0, warunek: musi to być tryb "stereo++"), można użyć programu następująco:
QoaToAiff jakisplik.qoa AUDIO:UNIT/0
Co prawda ze względu na narzut AHI i niezbyt szczęśliwie w takiej konfiguracji rozwiązane buforowanie dźwięku, do płynnego odtwarzania 44,1 kHz stereo wymagana jest niebagatelna nadwyżka mocy obliczeniowej, więc to taka ciekawostka.
2
[#53] Re: Quite OK Audio

@Krashan, post #52

Nie zrozumielismy sie chyba.
Wiem, ze zwykly uzyszkodnik, nie bedzie wylaczal cache.
Ale to TY robisz teraz pomiary, i wedlug mnie wersja z podwojnym dodawaniem/odejmowaniem powinna byc szybsza niz wersja z ekstra skokiem, a nie jest.
I zeby to sprawdzic trzeba wylaczyc cache dla 68020/68030 dla obu wersji, wtedy wyniki beda marodajne.
Allbo podaj wyniki testow dla 68000, one zawsze beda miarodajne.
Wedlug moich obliczen wersja z ekstra skokiem jest o 8 bajtow krotsza niz wersja z podwojnym dodawaniem/odejmowaniem, o tyle da sie bez problemu zoptymalizowac ten dekoder.
[#54] Re: Quite OK Audio

@Krashan, post #1

Krashan świetna robota, możliwości eksploatacji konwertera i playera bardzo duże.

Rozważ delikatną modyfikację kodu dzięki której można usunąć tst.l d5

MOVEA.W d5,a5
CMP.L a5,d5
BEQ.S clamped
SGT d5
EXT.W d5
EORI.W #$8000,d5
3
[#55] Re: Quite OK Audio

@Don_Adan, post #53

Robienie tych pomiarów nie jest sztuką tajemną zarezerwowaną dla mnie. Też możesz je robić. Spróbuj mnie zrozumieć, zasypujesz wątek różnymi drobnymi modyfikacjami, których - jak sam przyznałeś - nawet nie testujesz czy dekoder pracuje poprawnie. Nie mówiąc o zmierzeniu czy i na ile przyspieszają kod. Mój czas nie jest z gumy, niestety. Primo pracuję na etacie, secundo mam warsztat produkcyjny. Nie to, żebym się żalił, ale doba ma 24 godziny i muszę wybierać.

Ostatnia aktualizacja: 04.05.2025 20:42:55 przez Krashan
4
[#56] Re: Quite OK Audio

@Tedy, post #54

Rozważ delikatną modyfikację kodu dzięki której można usunąć tst.l d5
Szybciej od tego się nie zrobiło (tak jak dyskutowaliśmy, clamping zdarza się raz na kilka tysięcy próbek), ale zawsze to 2 bajty krócej. OK
1
[#57] Re: Quite OK Audio

@Krashan, post #51

Procedura slice() ma 178 bajtów.

Już ma 174 (dekodowanie stereo do jednego bufora z przeplotem, jak w QoaToAiff) albo 172 (dekodowanie stereo do 2 oddzielnych buforów, jak w QoaPlay). Czyli również przy stereo już cały kod dekodera ramki mieści w 256 bajtach. Ale z włączonym ICache szybciej nie jest, niż było... Więc z wyłączonym pewnie też nie.
[#58] Re: Quite OK Audio

@Krashan, post #55

Nie, nie moge testowac szybkosci tej procedury.
Nie mam zadnej Amigi dzialajacej.
A do testow potrzebna jest Amiga z 68020/68030 i z fastem.
Jesli mnie pamiec nie myli, to 2 instrukcje add/sub to 4c na 68020, a 1 instrukcja bra.b to 6c.
Wiec matematycznie mi sie nie zgadza, zeby wersja z 2 instrukcjami add/sub byla wolniejsza niz wersja z 1 instrukcja bra.b.
Jedynie jesli cache by sie przepelnial to tak mogloby byc.
Zrobisz jak uwazasz.

Co do moich propozycji to wedlug mnie najlepsze sa pierwsza ta z 44 postu (najkrotsza) oraz ostatnia ta z 48 postu (najszybsza na 68000), innych nie musisz sprawdzac, bo mnie samemu sie nie podobaja.
Ta z 48 postu to w zasadzie jest przerobiona oryginalna procedura, tyle ze szybsza (1 instrukcja, 2 bajty mniej) i zwolniony rejestr D1.
1
[#59] Re: Quite OK Audio

@Krashan, post #51

Przeczytalem jeszcze raz co napisales.
266 bajty to za duzo i tutaj jest ewidentny problem z cache 68020/68030.
Z tego co pisal meynaf mi osobiscie i na EAB tez, to najlepiej jak max takiej procedury to jest 240 bajty .
W przypadku wiekszej procedury juz sa widoczne spowolnienia.
meynaf robil rozne testy na swoim Blizzardzie 68030 50 MHz
Te 16 bajtow sa jakos wykorzystywane przez procesor.
Wiec postaralbym sie zejsc z rozmiarem do 240 bajtow, bo absolutnie sie da to zrobic.
Zrobisz jak uwazasz, ale cos takiego jest calkowicie niewydajne jesli chodzi o wielkosc kodu w cache 68020/68030:

..
                MOVE.W	(sp)+,d7               ; slice counter
		MOVEA.L	(sp)+,a1               ; output buffer
		MOVEA.L	(sp)+,a0               ; input buffer
		ADDQ.L	#2,a1                  ; R channel samples
		LEA	16(a0),a0              ; skip L channel LMS state
		MOVEA.W	(a0)+,a2               ; loading LMS history
		MOVEA.W	(a0)+,a3
		MOVEA.W	(a0)+,a4
		MOVEA.W	(a0)+,a5
		MOVE.L	(a0)+,d2               ; loading LMS weights
		MOVE.L	(a0)+,d3

Zamiast tych wszystkich move.w/l powinny byc uzyte 2x movem.l i juz zyskujesz 10 bajtow na kodzie,
Na uzywaniu moveq D7, nastepne 4 bajty.
Na uzywaniu procedury z postu 44, nastepne 6 bajty, (8 bajtow jesli moveq d7 uzyjesz, mozesz zamiast d7 uzyc d1, zamieniajac uzycia obu rejestrow).
itd
Jest jeszcze minimum jedna optymalizacja rozmiaru kodu, ale to jak bedziesz chcial "pojsc ta droga".
A z tego co widze po blizszym przyjrzeniu sie to caly ten powyzszy kod da sie ogarnac za pomoca jednego movem.l (SP)+, (czyli 4 bajty, zamiast 24 bajty).
Wiec bez problemu w 240 bajtach sie mozna zmiescic, i wtedy ta procedura bedzie miala max speed na 68020/68030.
1
[#60] Re: Quite OK Audio

@Don_Adan, post #59

Ten kod, o który tak się troszczysz, jest wykonywany raz na 5120 sampli audio. Jego długość i szybkość wykonania ma marginalne znaczenie dla szybkości dekodowania. Duże znaczenie ma:

  • Kod wykonywany co 1 sampel, czyli od DecLoop do ostatniego RTS, 124 bajty
  • Kod wykonywany co 20 sampli, czyli od Slice do DecLoop, 50 bajtów

Sekwencja 6 rozkazów ładujących historię i wagi predyktora jest w aktualnej wersji kodu wydzielona do podprogramu. Ponieważ była w 3 miejscach dało to skrócenie kodu. Trochę kosztem szybkości, ale powtórzę jeszcze raz, ładowanie predyktora zachodzi raz na 5120 sampli. Urwanie na tym kodzie nawet 20 cykli to jest przyspieszenie (teoretycznie) o 0,002%. Mniej więcej takie samo znaczenie ma to, czy ta sekwencja będzie w cache, czy nie. Tracisz czas na rzeczy bez znaczenia. Urwij ze dwa cykle z DecLoop od razu masz procent szybkości do przodu... (na 68020, na 68000 procentowo będzie mniej, bo 000 zużywa na ten kod prawie trzy razy tyle cykli co 020).

Poza tym i tak będzie trzeba w playerze ten kod trochę przebudować. Tam się z każdego sampla zapisuje tylko górny bajt, oraz kanały L i R zapisuje się w oddzielne miejsca. To upraszcza, bo odpada ADDA.W sampoff(pc),a1 (sample zawsze są zapisane jeden za drugim). Natomiast należy te bajty zapisywać w chip RAM. Tu by można było pomyśleć o zbieraniu ich po 4 i zapisywaniu jednym MOVE.L. Ale do tego potrzebny jest jeden pusty rejestr i jeszcze jeden do liczenia ile już mam próbek nazbieranych i wypuszczania ich co cztery. Będzie więc kwestia zrzucenia czegoś na stos i przekonania się czy zapisywanie do chip RAM po 4 bajty na raz będzie na tyle szybsze, że zrównoważy koszty czasowe poniesione na uwolnienie dwóch rejestrów.

Ostatnia aktualizacja: 05.05.2025 12:59:09 przez Krashan
1
Na stronie www.PPA.pl, podobnie jak na wielu innych stronach internetowych, wykorzystywane są tzw. cookies (ciasteczka). Służą ona m.in. do tego, aby zalogować się na swoje konto, czy brać udział w ankietach. Ze względu na nowe regulacje prawne jesteśmy zobowiązani do poinformowania Cię o tym w wyraźniejszy niż dotychczas sposób. Dalsze korzystanie z naszej strony bez zmiany ustawień przeglądarki internetowej będzie oznaczać, że zgadzasz się na ich wykorzystywanie.
OK, rozumiem