Programowanie: Odtwarzanie sampli

From Atariki

(Różnice między wersjami)
Jump to: navigation, search
Wersja z dnia 01:15, 22 mar 2020
Mono (Dyskusja | wkład)
(5.5-bit - link)
← Previous diff
Wersja z dnia 01:20, 22 mar 2020
Mono (Dyskusja | wkład)
(PWM - convtab)
Next diff →
Linia 331: Linia 331:
sta AUDF1 sta AUDF1
sta STIMER sta STIMER
 +
 +convtab:
 +:256 .byte #*63/255
</pre> </pre>
Zamiast synchronizacji z linią skanningową należy w tym przypadku zastosować zwykłą pętlę lub synchronizację z przerwaniami TIMER4. Zamiast synchronizacji z linią skanningową należy w tym przypadku zastosować zwykłą pętlę lub synchronizację z przerwaniami TIMER4.

Wersja z dnia 01:20, 22 mar 2020

Odtwarzanie sampli możliwe jest poprzez zastosowanie różnych technik.

Spis treści

PCM

Pulse-Code Modulation jest podstawową techniką generowania sampli i polega na bezpośrednim sterowaniu wychyleniem membrany głośnika w dowolnym rejestrze AUDCx.

4-bit

Technika ta w podstawowym wariancie pozwala na uzyskanie sampli o 4-bitowej jakości.

Włączenie trybu bezpośredniego sterowania membraną odbywa się przez ustawienie bitu 4. Bity odpowiadające za głośność (0..3) odpowiadają wtedy za wychylenie membrany: $0 - bez wychylenia, $F - maksymalne wychylenie.

$F :                              ==
$E :                            ==
$D :                          ==
$C :                        ==
$B :                      ==
$A :                    ==
$9 :                  ==
$8 :                ==
$7 :              ==
$6 :            ==
$5 :          ==
$4 :        ==
$3 :      ==
$2 :    ==
$1 :  ==
$0 +==------------------------------

Przetworniki DAC dla każdego kanału POKEY-a mają liniową charakterystykę więc nie jest potrzebne dodatkowe przetwarzanie wartości sampla.

Mając więc w młodszej połówce akumulatora wartość próbki wystarczy:

  ora #$10
  sta AUDCx

Jeśli próbka znajduje się w starszej połówce akumulatora można:

  sec
  ror
  ror
  ror
  ror
  sta AUDCx

co można nieco zoptymalizować za pomocą tablicy konwersji (kiedy przykładowy sample znajduje się w górnej połówce rejestru indeksowego X)

  lda convtabh,x
  sta AUDCx
  ...

convtabh:
:256 .byte (# >> 4) | $10

Jeśli z jakichś powodów nie można użyć akumulatora a dysponujemy próbką w młodszej połówce rejestru indeksowego (w poniższym przykładzie X) można zastosować analogiczną metodę:

  ldy convtabl,x
  sty AUDCx
  ...

convtabl:
:256 .byte (# & $F) | $10

Aby zyskać pewność, że procedura zawsze będzie się wykonywać stałą liczbę cykli należy tablice umieścić na początku strony.

5.5-bit

Technika została zastosowana przez Pecusia w SoundTracker Player i wykorzystuje fakt, że wartości wychylenia membrany ustawione na każdym kanale POKEY-a są sumowane. Mając więc 8-bitową próbkę można (prawie w jednej chwili) ustawić wartości w 3 kanałach POKEY-a a nie tylko w jednym:

  ldy convtab3,x
  lda convtab1,x
  pha
  lda convtab2,x
  tax
  pla
  sta AUDC1
  stx AUDC2
  sty AUDC3

Zapis czwartego kanału jakkolwiek możliwy, wprowadza jednak mocne zniekształcenia więc poprzestano na trzech co pozwala uzyskać 46 możliwych wartości - stąd też nazwa "PCM 5.5" (log246=5,5236).

Same tablice konwersji sampla zostały przez Pecusia dobrane eksperymentalnie:

convtab1:
:4 .byte $10
:4 .byte $10
:4 .byte $11
:4 .byte $11
:4 .byte $11
:4 .byte $12
:4 .byte $11
:4 .byte $12
:4 .byte $13
:4 .byte $12
:4 .byte $14
:4 .byte $13
:4 .byte $14
:4 .byte $13
:4 .byte $15
:4 .byte $14
:4 .byte $15
:4 .byte $13
:4 .byte $16
:4 .byte $14
:4 .byte $15
:4 .byte $15
:4 .byte $18
:4 .byte $16
:4 .byte $18
:4 .byte $16
:4 .byte $19
:4 .byte $17
:4 .byte $1A
:4 .byte $15
:4 .byte $1C
:4 .byte $17
:4 .byte $1D
:4 .byte $17
:4 .byte $19
:4 .byte $17
:4 .byte $1C
:4 .byte $1B
:4 .byte $18
:4 .byte $1E
:4 .byte $1A
:4 .byte $19
:4 .byte $1B
:4 .byte $1C
:4 .byte $1C
:4 .byte $1B
:4 .byte $1F
:4 .byte $1D
:4 .byte $1C
:4 .byte $1C
:4 .byte $1C
:4 .byte $1B
:4 .byte $1D
:4 .byte $1C
:4 .byte $1D
:4 .byte $1C
:4 .byte $1B
:4 .byte $1D
:4 .byte $1F
:4 .byte $1E
:4 .byte $1E
:4 .byte $1F
:4 .byte $1F
:4 .byte $1F

convtab2:
:4 .byte $10
:4 .byte $10
:4 .byte $10
:4 .byte $10
:4 .byte $11
:4 .byte $10
:4 .byte $11
:4 .byte $11
:4 .byte $11
:4 .byte $11
:4 .byte $10
:4 .byte $11
:4 .byte $11
:4 .byte $12
:4 .byte $11
:4 .byte $12
:4 .byte $11
:4 .byte $13
:4 .byte $11
:4 .byte $12
:4 .byte $13
:4 .byte $14
:4 .byte $11
:4 .byte $12
:4 .byte $12
:4 .byte $13
:4 .byte $12
:4 .byte $14
:4 .byte $12
:4 .byte $15
:4 .byte $11
:4 .byte $14
:4 .byte $11
:4 .byte $15
:4 .byte $15
:4 .byte $16
:4 .byte $12
:4 .byte $15
:4 .byte $15
:4 .byte $12
:4 .byte $18
:4 .byte $16
:4 .byte $16
:4 .byte $18
:4 .byte $15
:4 .byte $17
:4 .byte $15
:4 .byte $1A
:4 .byte $1A
:4 .byte $1B
:4 .byte $1B
:4 .byte $1A
:4 .byte $18
:4 .byte $1A
:4 .byte $1D
:4 .byte $1B
:4 .byte $1B
:4 .byte $1B
:4 .byte $1E
:4 .byte $1E
:4 .byte $1E
:4 .byte $1D
:4 .byte $1E
:4 .byte $1F

convtab3:
:4 .byte $10
:4 .byte $10
:4 .byte $10
:4 .byte $10
:4 .byte $10
:4 .byte $10
:4 .byte $11
:4 .byte $10
:4 .byte $10
:4 .byte $11
:4 .byte $10
:4 .byte $11
:4 .byte $10
:4 .byte $11
:4 .byte $10
:4 .byte $10
:4 .byte $11
:4 .byte $12
:4 .byte $11
:4 .byte $12
:4 .byte $11
:4 .byte $10
:4 .byte $10
:4 .byte $12
:4 .byte $10
:4 .byte $12
:4 .byte $10
:4 .byte $11
:4 .byte $10
:4 .byte $13
:4 .byte $10
:4 .byte $13
:4 .byte $10
:4 .byte $13
:4 .byte $11
:4 .byte $13
:4 .byte $12
:4 .byte $11
:4 .byte $14
:4 .byte $12
:4 .byte $10
:4 .byte $14
:4 .byte $13
:4 .byte $10
:4 .byte $14
:4 .byte $14
:4 .byte $13
:4 .byte $10
:4 .byte $12
:4 .byte $12
:4 .byte $13
:4 .byte $16
:4 .byte $17
:4 .byte $17
:4 .byte $14
:4 .byte $18
:4 .byte $1B
:4 .byte $1A
:4 .byte $17
:4 .byte $19
:4 .byte $1B
:4 .byte $1D
:4 .byte $1E
:4 .byte $1F

PWM

Generowanie sampli techniką Pulse-Width Modulation zostało opisane przez NRV w wątku https://atariage.com/forums/topic/278463-pwm-experiments/. Pozwala ona na uzyskanie sampli o 6-bitowej lub prawie 7-bitowej jakości zależnie od częstotliwości syntezy.

Trick polega na generowaniu za pomocą kanału 1 fali prostokątnej o zadanej szerokości pulsu co pozwala uzyskać n poziomów głośności pomiędzy 0 a ustawieniem rejestru AUDC. Okres fali powinien być ustawiony poza granicą słyszalności.

W najprostszym przypadku wykorzystuje się okres generowania pojedynczej linii skanningowej obrazu (114 cykli zegarowych), ze względu na łatwość synchronizacji syntezy za pomocą rejestru WSYNC. Generowana fala ma wtedy częstotliwość 15556,553 Hz, co powoduje słyszalny wysoki pisk. 

Konfiguracja POKEY-a polega na połączeniu dwóch kanałów w 16-bitowy i ustawieniu okresu fali na czas dłuższy niż linia skanningowa.

  lda #%01010000
  sta AUDCTL
  lda #$FF        ;okres fali 65535+7 cykli
  sta AUDF1
  sta AUDF2
  lda #$AF
  sta AUDC1
  lda #$10
  sta AUDC2

Poprzez sterowanie wartością młodszej połówki dzielnika częstotliwości odbywa się ustalenie wypełnienia fali.

Zapis 8-bitowej próbki dostępnej w X odbywa się cyklicznie co linię skanningową poprzez:

  lda convtab,x
  sta WSYNC
  sta AUDF1
  sta STIMER

convtab:
:256 .byte #*113/255

Zapis do STIMER powoduje restart dzielników częstotliwości i ustawienie wewnętrznego wyjścia AUDIO 1 i 2 w stan wysoki. Mimo, że generatory połączone są w parę, to po odliczeniu AUDF1 do zera wyjście AUDIO tegoż generatora zostanie przestawione w stan niski, po czym licznik będzie kontynuował odliczanie okresu 256 taktów zegara ponieważ generatory są połączone w parę. W międzyczasie program dokona ponownego ustawienia AUDF1 i zrestartuje dzielniki co spowoduje generowanie fali od nowa.

Regulacja głośności tak generowanego sampla odbywa się przez zapis wartości do AUDC1.

Ze względu na to, że w ten sposób można ustawić jedynie 114 poziomów ze 128, technika ta nazywana jest czasem "PWM -7-bits". W praktyce używa się 100 poziomów ponieważ minimalna szerokość pulsu PWM wynosi 7 cykli.

Przyjmując wyższą częstotliwość syntezy można pozbyć się uciążliwego pisku kosztem jednak jakości bitowej sampla. Przykładowo zakładając okres 72 cykli (24631,208 Hz) otrzymujemy 64 poziomy głośności i 6-bitową jakość sampli - stąd w tym wariancie spotyka się określenie "PWM 6-bits".

Zapis 8-bitowej próbki dostępnej w X odbywa się cyklicznie poprzez:

  lda convtab,x
  sta AUDF1
  sta STIMER

convtab:
:256 .byte #*63/255

Zamiast synchronizacji z linią skanningową należy w tym przypadku zastosować zwykłą pętlę lub synchronizację z przerwaniami TIMER4.

PDM

Ta technika opisana przez kool kitty89 w wątku https://atariage.com/forums/topic/244946-using-pulse-density-modulation-for-8-bit-pcm a zaimplementowana przez Xuel-a nazwana została Pulse Density Modulation i pozwala na uzyskanie sampli o ośmiobitowej jakości na standardowym POKEY-u.

Sztuczka polega na generowaniu górnej połówki sampla za pomocą PCM na kanale 3 POKEY-a, a dolnej za pomocą głośności fali o stałym wypełnieniu 1/16 skonfigurowanej na kanale 1 co powoduje uzyskanie pośredniego wychylenia membrany pomiędzy wychyleniem $0 a $1.

$F :==                              ==                              
$E :
$D :
$C :
$B :
$A :
$9 :
$8 :                                                                ==                              ==
$7 :
$6 :
$5 :
$4 :
$3 :
$2 :
$1 :
$0 +--==============================--==============================--==============================--==============================
                        1 1 1 1 1 1                     1 1 1 1 1 1                     1 1 1 1 1 1                     1 1 1 1 1 1
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5

Do działania wymagana jest jednorazowa konfiguracja POKEY-a pozwalająca na ciągłe generowanie fali o wypełnieniu 1/16 na kanale 1:

  lda #%01100100  ;1.77MHz dla #1 i #3, filtr w #1 sterowany przez #3
  sta AUDCTL
  ldx #7-4        ;teoretycznie 16-4
  ldy #9-4        ;teoretycznie 17-4
  stx AUDF1
  sty AUDF3
  sta STIMER
  stx AUDF3

Obydwa generatory ustawione są na ten sam okres (x), lecz fala w kanale 3 generowana jest z offsettem 2 cykli (y) względem kanału 1. Ponieważ włączony jest filtr powoduje to generowanie szpilki z ustalonym okresem.

           x             x             x             x             x
       :======= :     =:===== :     =======       =======       =======       ==
#AUDF1 :        : x    :      : x             x             x             x
       +-------=:=====-:-----=:=====-------=======-------=======-------=======
           y    :      :  x   :          x             x             x
       :========:      :======:       =======       =======       =======       
#AUDF3 :        :  x   :      :  x              x             x             x
       +--------:======:------:=======-------=======-------=======-------=======
                :      :      :
       :        :      :======:       =======       =======       =======       
LATCH  :        :      :      :
       +--------:======:------:=======-------=======-------=======-------=======
                :      :      :
#AUDF1 :        :     ==     ==     ==     ==     ==     ==     ==     ==     ==
XOR    :        :      :      :
LATCH  +--------:=====-:=====-:=====--=====--=====--=====--=====--=====--=====--

Na wyjściu generatora 1 pojawia się wynik będący resultatem operacji XOR między wewnętrznym rejestrem (LATCH) układu próbkowanym przy każdym przepełnieniu generatora 3 a wyjściem generatora AUDF1.

Teoretycznie wyliczone wartości generowały spory szum. W praktyce okazało się że najlepsze efekty daje konfiguracja fali o wypełnieniu 1/7.

Zapis 8-bitowej próbki dostępnej w X odbywa się poprzez:

  ldy convtabh,x
  lda convtabl,x
  sty AUDC3
  sta AUDC1

convtabl:
:256 .byte (# & $F) | $A0
convtabh:
:256 .byte (# >> 4) | $10
Personal tools