logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

Algorytm generacji szumu różowego

shg 20 Lip 2004 23:45 3517 3
  • #1 752956
    shg
    Poziom 35  
    Poszukuję jakiegoś algorytmu generującego szum różowy, nie koniecznie o doskonałych parmetrach, stawiam na szybkość. Szum będzie wykorzystywany do psychostymulacji w połączeniu z "binaural beats" (nie wiem, jak to na mowę Mickiewicza przetłumaczyć :D ).
    Sprawdziłem algorytm Vossa, ale nawet po optymalizacji jest wolny(?) (12kHz próbkowania na procu MC68000 @ 7.09Mhz), nie znacie czegoś szybszego od tego?, każdy algorytm mile widziany, może coś będzie bardziej podatne na optymalizację :D .
    Z góry dzięki wielkie :D
  • #2 753606
    h-doc
    Poziom 27  
    hmm, jedyne co mi przychodi do głowy to generator szumu białego i odpowiedni filtr dolnoprzepustowy.
    Widziałem projekty analogowe bazujące właśnie na tej zasadzie generacji. Nawet nie były bardzo skomplikowane.
    Największy problem to filtracja. Z tego co pamiętam, to w tym analogowym projekcie wykorzystano połączenie charakterystyk przejściowych 3 różnych filtrów.
    A może najprościej będzie wytworzyć taki szum np. w CoolEdit i potem w projekcie wykorzystać wygenerowane próbki?
  • #3 753980
    shg
    Poziom 35  
    Jasne, że prościej ! Docelowo generator ma zostać zaimplementowany w jakimś procu, tak, żeby całe urządzenie było przenośne. Na razie testy na Amidze. W sumie ten pomysł z generatorem "sprętowym" jest dobry, będe mógł dać słabszego procka, który zajmie se tylko generacją dwóch sinusów. Najtrudniej wpaść na rzeczy najprostsze :D
    dzięki :!:
  • #4 2914957
    shg
    Poziom 35  
    Trochę dawno, ale mam nadzieję, że komuś się przyda.
    A zrobiłem i szum, i sinusy. Wszystko śmiga na ATmega8. Próbkowanie 48kHz, 8 bit, stereo (dwa niezależne kanały szumu różowego i dwa sinusy). Z kwarcem 8MHz zostaje jeszcze troche mocy obliczeniowej w zapasie. Rozdzielczość 8 bitów jest jak najbardziej wystarczająca (w końcu to szum ;])

    Generacja oparta jest o algorytm Vossa-McCartneya, opisany tutaj: http://www.firstpr.com.au/dsp/pink-noise/
    Podstawowym błędem, jaki wcześniej zrobiłem była próba użycia algorytmu Vossa, modyfikacja wprowadzona przez McCartneja powoduje znaczne przyspieszenie. Dodatkowo stablicowałem wartości funkcji liczącej ilość końcowych zer w bajcie, co oczywiści przyczyniło się do kolejnego wzrostu wydajności.

    Jako generator ośmiobitowych liczb pseudolosowych zastosowałem osiem równolegle pracujących rejestrów LFSR o długości 31 bitów każdy (po jednym na każdy bit w wynikowej liczbie). Działa to to dużo szybciej niż pojedynczy generator, ale ma zasadniczą wadę - potrzebuje 32 bajtów pamięci.

    Kod poniżej generuje za jednym razem dwie próbki szumu różowego.
    Zostawiłem tylko sam generator i inicjalizację.
    
    .def	templ1	=	r0		; two temporary registers
    .def	templ2	=	r1
    
    .def	zero	=	r2		; always zero
    
    .def	pinkc	=	r3		; counter for CTZ table indexing
    
    .def	bufpi	=	r4		; play buffer index
    
    ; pink noise
    .def	rndl	=	r6		; left channel's value
    .def	rndr	=	r7		; right channel
    
    .def	temph1	=	r16		; two more temporary variables
    .def	temph2	=	r17
    
    .def	tap0	=	r18		; LSFR taps
    .def	tap2	=	r19
    .def	tap30	=	r20
    
    
    
    ;---------------------------------------
    ; RAM variables
    
    .dseg
    
    	.org	SRAM_START
    ; lfsr address is aligned to 32 bytes bonduary
    lfsr_pad:
    	.org	(((lfsr_pad + 0x1f) >> 5) << 5)
    lfsr:	.byte	32		; LSFR storage
    
    
    ; white noise octave decimator storage.
    ; aligned to 16 bytes
    octdec_pad:
    	.org	(((octdec_pad + 0x0f) >> 4) << 4)
    octdec:
    octdecl:	.byte	8	; left channel
    octdecr:	.byte	8	; right channel
    
    .cseg
    
    ;----------------------------------
    
    init:
    
    	clr		zero						; this register is always zero
    
    	ldi		tap0,	0					; setup tap positions
    	ldi		tap2,	2
    	ldi		tap30,	30
    
    	clr		pinkc						; clear CTZ counter
    
    
    ; load pseudo-random generator seed
    ; seed table contains values in range 0..255
    ; these values are and'ed with 0x1f to make the generator
    ; output only numbers in range 0..31
    
    	ldi		YH,		HIGH(lfsr)			; load lfsr table pointer into register pair
    	ldi		YL,		LOW(lfsr)			; this is zero
    
    	ldi		ZL,		LOW(rnd_seed<<1)	; rnd init values from program memory (word address)
    	ldi		ZH,		HIGH(rnd_seed<<1)
    	ldi		temph1,	32					; repeat loop 32 times
    seed_loop:
    	lpm		temph2,	Z+
    	andi	temph2,	0x1f				; VERY important.
    	st		Y+,		temph2
    	dec		temph1
    	brne	seed_loop
    
    ; the rndl, and rndr variables MUST be initialized to 0
    ; any other value can cause overflow while summing
    ; values from decimators
    	mov		rndl,	zero
    	mov		rndr,	zero
    
    ; clear decimator storage
    
    	ldi		temph1,	16					; repeat loop 16 times (for both octdec tables)
    	ldi		ZL,		LOW(octdecl)
    	ldi		ZH,		HIGH(octdecl)
    octdec_loop:
    	st		Z+,		zero
    	dec		temph1
    	brne	octdec_loop
    	
    ;----------------------------------
    ;---- pink noise
    ; Uses Voss-McCartney algorithm
    
    pink:
    
    ; CTZ table is aligned to 256 bytes bonduary,
    ; so index in this table is LSB of address
    	ldi		ZH,		HIGH(ctz8<<1)		;1 load MSB of CTZ table address
    	mov		ZL,		pinkc				;1 'add' CTZ table index
    	lpm		YL,		Z					;3 get octave number
    	subi	YL,		LOW(-octdec)		;1 add left decimator array address
    	sbci	YH,		HIGH(-octdec)		;1
    
    	inc		pinkc						;1 increment CTZ counter (index)
    	
    	ldd		templ1,	Y+0					;2 load previous random value from left decimator
    	ldd		templ2,	Y+8					;2 and from right decimator
    
    ; get new random value for left channel
    	ldi		ZH,		HIGH(lfsr)			;1 get lfsr table pointer
    	ldi		ZL,		LOW(lfsr)			;1
    	add		ZL,		tap2				;1 add tap2 index (aligned - no carry)
    	ld		temph1,	Z					;2 load value from table
    
    	ldi		ZL,		LOW(lfsr)			;1
    	add		ZL,		tap30				;1 add tap 30 position
    	ld		temph2,	Z					;2
    	eor		temph1,	temph2				;1 Ex-OR both taps
    
    ; shift LSFR
    ; in fact we don't shift the register, but position of each tap
    
    	subi	tap0, 	1					;1
    	andi	tap0,	0x1f				;1
    	subi	tap2, 	1					;1
    	andi	tap2,	0x1f				;1
    	subi	tap30,	1					;1
    	andi	tap30,	0x1f				;1
    
    	ldi		ZL,		LOW(lfsr)			;1
    	add		ZL,		tap0				;1 tap 0 position
    	st		Z,		temph1				;2 store new value at tap0
    
    
    	std		Y+0,	temph1				;2 replace old value in decimator
    	sub		rndl,	templ1				;1 subtract old value from random number
    	add		rndl,	temph1				;1 add new one
    	
    ; compute new random value for right channel
    	ldi		ZL,		LOW(lfsr)			;1
    	add		ZL,		tap2				;1 add tap2 index (aligned - no carry)
    	ld		temph1,	Z					;2 load value from table
    
    	ldi		ZL,		LOW(lfsr)			;1
    	add		ZL,		tap30				;1 add tap 30 position
    	ld		temph2,	Z					;2
    	eor		temph1,	temph2				;1 Ex-OR both taps
    
    ; shift LSFR
    
    	subi	tap0, 	1					;1
    	andi	tap0,	0x1f				;1
    	subi	tap2, 	1					;1
    	andi	tap2,	0x1f				;1
    	subi	tap30,	1					;1
    	andi	tap30,	0x1f				;1
    
    	ldi		ZL,		LOW(lfsr)			;1
    	add		ZL,		tap0				;1 tap 0 position
    	st		Z,		temph1				;2 store new value at tap0
    
    
    	std		Y+8,	temph1				;2
    	sub		rndr,	templ2				;1
    	add		rndr,	temph1				;1
    
    	ret
    ;=======================================
    ; data tables
    
    ; pseudo-random generator seed table
    rnd_seed:
    	.db		0xec, 0x07, 0x13, 0x42, 0x55, 0x73, 0x2b, 0x60 
    	.db		0x08, 0xf4, 0x26, 0x07, 0x59, 0x37, 0x9b, 0x40 
    	.db		0x04, 0xa6, 0x49, 0xe7, 0xe6, 0x31, 0x7d, 0x60 
    	.db		0x70, 0x02, 0xce, 0x92, 0x7e, 0xfc, 0x26, 0x1a
    
    
    ; Results of Count_Trailing_Zeros(i | 0x80) for i = 0..255.
    ; These are the numbers of rnd decimators to update in each cycle.
    ; First, and 128th number is 7, but this decimator should be updated
    ; only once. The function to generate this table should be CTZ(i)
    ; Probably no decimator should be updated when i==0,
    ; as suggested by Phil Burk [1]. However original Voss algorithm
    ; has no stage in cycle in wich no decimator is updated.
    
    ; store at BYTE address aligned to 256
    ctz8_pad:
    	.org	(((ctz8_pad + 0x7f) >> 7) << 7)	; align WORD address to 128
    ctz8:
    	.db		7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
    	.db		4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
    	.db		5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
    	.db		4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
    	.db		6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
    	.db		4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
    	.db		5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
    	.db		4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
    	.db		7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
    	.db		4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
    	.db		5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
    	.db		4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
    	.db		6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
    	.db		4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
    	.db		5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
    	.db		4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
    


    Projekt jeszcze nie jest skończony; brak sterowania, ale sama generacja działa bez zarzutu.

    A w załączniku próbka razem z sinusami. Sinusy generowane są na zasadzie DDS.
REKLAMA