1 bites hanghatások Z80-nal

4MHz-es Z80-nal szeretnék egyszerű 1 bites hanghatásokat generálni, hasonlóan a következő kódhoz:

    LD DE, 0
    LD BC, 0
LOOP:
    INC BC
    INC DE
    LD A, C
    AND D
    AND %11111100
    JR Z, PLAY_BIT_0
    JR PLAY_BIT_1

PLAY_BIT_0:
    ; ... gépfüggő kód, ami kikapcsolja a hangszórót
    JR LOOP

PLAY_BIT_1:
    ; ... gépfüggő kód, ami bekapcsolja a hangszórót
    JR LOOP

A generált hang itt hallható.

Ilyen típusú dallamok a kód kisebb módosításával még készíthetőek, de a fentitől lényegében eltérő hatásokat keresek.

A legnagyobb szükségem valamiféle fehér-zajra generálására lenne, de a ROM tartalom olvasásával csak másodperces nagyságrendben tudok értékelhető zajt generálni, a rövidebb lejátszások inkább hasonlítanak kalapácsütéshez, mint zajhoz.

Tehát, ha van valakinek érdemi ötlete, örömmel fogadnám.

A válaszok alapján a következő Z80 kódok születtek:
Zaj generálása:

LFSR_TAP_H: EQU %01000001
LFSR_TAP_L: EQU %10100111
LFSR_CURRENT_DATA16:  DW 15                ;  uint32_t       in <> 0
LFSR:
    LD A, (LFSR_CURRENT_DATA16)
    BIT 0, A
    JR Z, LFSR_CURRENT_DATA_EVEN
    LD HL, (LFSR_CURRENT_DATA16)
    LD A, H
    XOR LFSR_TAP_H
    LD H, A
    LD A, L
    XOR LFSR_TAP_L
    LD L, A
    RR H
    RR L
    LD DE, %1000000000000000
    LD A, D
    OR H
    LD A, E
    OR L
    LD (LFSR_CURRENT_DATA16), HL
    RET
LFSR_CURRENT_DATA_EVEN:
    LD HL, (LFSR_CURRENT_DATA16)
    RR H
    RR L
    LD (LFSR_CURRENT_DATA16), HL
    RET

A CALL LFSR minden hívása után 16 bit használható az (LFSR_CURRENT_DATA16) címről. A végtelen ciklussal generált hang itt hallható.

Hozzászólások

Szerkesztve: 2024. 01. 22., h – 16:58

de a ROM tartalom olvasásával csak másodperces nagyságrendben tudok értékelhető zajt generálni

Muszáj random 1-bites zajnak lennie? Mármint, használhatnál akár saját több bites hangmintát is, ami zajnak hangzik, és akkor véletlenül sem lenne kalapácsütés. A PICO-8 és a TIC-80 például 32 darab 4 bites mintát tárol. Az a plusz 16 bájt nem tűnik soknak, cserébe készen kapsz egy valag hangmintát, ami közül válogathatsz. Vagy akár előre le is generálhatod magadnak.

Minta adatsorokat is tudok adni ehhez:

static uint8_t picowave[256] = {
    0x76, 0x54, 0x32, 0x10, 0xf0, 0x0e, 0xdc, 0xba, 0xba, 0xdc, 0x0e, 0xf0, 0x10, 0x32, 0x54, 0x76, /* 0 - sine */
    0xba, 0xbc, 0xdc, 0xd0, 0x0e, 0xf0, 0x00, 0x00, 0x10, 0x02, 0x32, 0x34, 0x54, 0x56, 0x30, 0xda, /* 1 - triangle */
    0x00, 0x10, 0x12, 0x32, 0x34, 0x04, 0x50, 0x06, 0x0a, 0xb0, 0x0c, 0xdc, 0xde, 0xfe, 0xf0, 0x00, /* 2 - sawtooth */
    0x30, 0x30, 0x30, 0x30, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0x30, 0x30, 0x30, 0x30, /* 3 - square */
    0x04, 0x04, 0x04, 0x04, 0x04, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, /* 4 - short square / pulse */
    0x34, 0x12, 0xf0, 0xde, 0xdc, 0xfe, 0xf0, 0x00, 0x00, 0xf0, 0xfe, 0xdc, 0xde, 0xf0, 0x12, 0x34, /* 5 - ringing / organ */
    0xf0, 0xd0, 0xf0, 0x1e, 0xb0, 0x0e, 0xf0, 0x52, 0xfa, 0x0e, 0xf0, 0xd4, 0x0e, 0x06, 0x34, 0x3a, /* 6 - noise */
    0x32, 0x12, 0x00, 0xf0, 0xfe, 0xd0, 0xbc, 0xba, 0x0a, 0xbc, 0xd0, 0xfe, 0xf0, 0x00, 0x12, 0x32, /* 7 - ringing sine / phaser */

Elvileg jók, hacsak el nem basztam :-D Mindenesetre a generáló forrása is mellékelve vagyon, valamint a TIC-80 SFX-el is készíthetsz ilyen mintákat (amit aztán a lementett .tic fájlból kell kibányászni, ami nem túl vészes, de ha nincs kedved hozzá, akkor a cart2prj sima szöveges bájtsorrá alakítja, ilyenné)

A random nem követelmény, az 1 bit sajnos a hardver korlátja, azt nem tudom átlépni. Bár trükközni lehet, ha az ember csak nagyon rövid ideig kapcsolja be a hangszórót, akkor nem tud teljes mértékben kitérni, és így talán 2-3 bitesre fel lehet növelni a felbontást, de ezek így már nagyon tárigényes hangminták lesznek. Én inkább kis tárigényű hanghatásokat keresek, mint előre digitalizált adatot.

az 1 bit sajnos a hardver korlátja, azt nem tudom átlépni.

Kizárt dolognak tartom, hogy a hanggenerátor frekvenciatartománya 1 bites legyen. Szerintem benézel valamit.

de ezek így már nagyon tárigényes hangminták lesznek. Én inkább kis tárigényű hanghatásokat keresek, mint előre digitalizált adatot.

Pont ezért írtam, amit írtam, a 16 bájt rohadtul nem "tárigényes". De használhatnál 8 darab egyenként egy bájtos mintát is, ezt csak azért írtam, mert rengeteg pont ilyen mintát találsz a neten a PICO-8-nak és a TIC-80-nak hála.

Egyébként ilyen alacsony számú (32) és kis felbontású (16 lehetséges értékű) minta esetén nem beszélhetünk digitalizált adatról, erre a "chiptune" a szakkifejezés.

Valószínűleg nagyon mást értettünk 1 bites hang alatt. Amivel én eddig találkoztam, ott a bitszám a különböző amplitúdó értékeket jelentette, de már felfogtam, hogy nem erre gondoltál.

Ha 4 biten frekvenciákat tárolok, akkor nekem hiányzik még egy paraméter, hogy a megadott frekvenciák mennyi ideig szóljanak. De köszönöm utánanézek a PICO-8-nak és TIC-80-nak.

Rossz válasz.

A hangszóró induktivitása és a mechanikus tehetetlensége másodfokú aluláteresztő szűrőt képez, ami pont jól jön egy DAC kimenetére.

Az 1 bit nem ritkaság, hiszen a SACD (Szuper Audio CD) is 1 bitet használ a nagyfelbontású multicsatornás felvételek tárolásához. A technológia a szigma-delta ADC/DAC,

A frekvenciataromány pedig a mintavételi frekvencia/2/bitszám lehet. Tehát a fő  kérdés az, hogy milyen szaporán lehet kiadni azt az 1 bitet.

Az SACD-n úgy emlékszem MASH jel van rögzítve, ami tulajdonképpen egy nagy frekvenciás PWM. De ezt csak emlékezetből mondom.

A szigma-delta ADC egy visszacsatolt struktúra, ami a kvantálási zajt feltranszformálja a már nem hallható tartományba, ami aztán levágásra kerül --> e miatt az effektív bitszám sokkal jobban növekszik, mint a sima túlmintavételezésnél.

Just for the records.

SACD:

Direct Stream Digital (DSD) is a trademark used by Sony and Philips for their system for digitally encoding audio signals for the Super Audio CD (SACD).

DSD uses delta-sigma modulation a form of pulse-density modulation encoding, a technique to represent audio signals in digital format, a sequence of single-bit values at a sampling rate of 2.8224 MHz.

A 0 es 1 nem be- vagy kikapcsolja a hangszorot.

Az 1 bites hanggenrator eseten a valtasok frekvenciajaval operalsz leginkabb, illetve a PWM kitoltesi tenytezojevel is.

Ezt a valtozo frekvenciaju es kitoltesu negyszogjelet (ami mar alapesetben is jo sok szinusz linearkombinacioja) rakuldod egy savszurore (mondjuk 20Hz-20kHz), majd azt a kimenetet hallod.

Hangmernok kell, vagy jo matematikus, aki visszafejti a csatornat, es atlatja, hogy milyen bemeneti jelnek mi a hatasa.

Egyszeru feherzaj-generator: 

uint32_t lfsr_32(uint32_t in)
{
 uint32_t       out;
 uint32_t       tap=0x1EDC6F41;
 if ( in & 1 )
        out = (1<<31)|((in^tap)>>1);
 else
        out = in>>1;

 return(out);
}

Elinditod barmilyen nem-nulla ertekrol, es 2^32-1 lepes mulva kezdi el ismetelni magat, mikozben folyamatosan jon egy 32bites stream (szoval mint bitstream, a periodusa egesz pontosan 137438953440 lesz... egy darabig talaln eleg :)).

Ez ugyan C kod, de az effele bit-csamcsogasos algoritmusoknak pont az a szepsege hogy assemblybol jobb kodot tudsz csinalni mint C-bol ;) (es ez marcsak izombol-hardverbol, pl Verilogban lenne meg egyszerubb). 

Ezzel inkább az a bökkenő, hogy a Z80-nak csak két 8 bites regisztere van operandusként a műveletekre, szóval nem tud 32 biten dolgozni, csak max. 16 biten. Viszont maga az ötlet szerintem nagyon jó, egy kis átalakítással

uint16_t lfsr_16(uint16_t in)
{
 uint16_t       out;
 if ( in & 1 )
        out = (1<<15)|((in*16807)>>1);
 else
        out = in>>1;

 return(out);
}

Akár még Z80-on is működhet. (Megjegyzés, a 16807 nemcsak egy hasraütésszerű szám, hanem ebből az 1988-as véletlenszámokkal foglalkozó, kiváló tanulmányból ered.)

Jaja, kevesebb bites LFSR is jo lehet ha azert eleg hosszu a periodusa. Ez itten legjobb esetben is 131070-es bitperiodust ad, ez mondjuk 10-20 masodpercre mar eleg lehet. 

Mondjuk igen, itt annyi a konnyebbseg hogy a kolleganak spektralisan feherzaj kell es nem valodi(bb) (al)veletlen sorozat, ez azert sokat konnyit a dolgon... 

Jól gondolom, hogy az első hívás visszatérő értékét kell a következő hívásban paraméterként megadni?

Igen!

és az out érték valamelyik (0.?) bitje jelzi majd, hogy mikor kell be- vagy kikapcsolnom a hangszórót?

Ebben az a jo hogy a teljes bitsorozatot tudod hasznalni. 

Egyebkent assemblyben ugy tudsz trukkozni az effele LFSR jellegu bitcsamcsogasokkal (meg a rokon muveleteknel, pl Galois algebras cuccoknal, CRC-knel, stb), hogy:

  • elshifteled jobbra a bemenetet eggyel
  • ha nincs carry, akkor atugrod a kovetkezot
  • xor-olsz a tap ertekekkel
  • ide ugrasz ha nincs carry bit

ez eleg sok architekturan siman mukodik igy, beleertve azt is hogy X bites architekturan N*X bites regisztered van. Ugyanis, a jobb shiftnek van olyan opcioja (altalaban) hogy a carry bitet behozza balrol. De nyugodtan forgathatsz balra is, akkor az add + add-with-carry a baratod a ror-jellegu utasitasok helyett. Csak altalaban a jobbra tolas a megszokott a gyakorlatban, az algebras dolgoknal az a "logikusabb". De ha ez nem fontos (pl itt nalad, zajgeneralasnal) akkor az ADD + ADC is fasza (csak akkor a tap erteket meg kell forditani bit reverse modon).

Ez egy eleg speci mintazat, es a C forditok altalaban nem ismerik fel. Szoval itt erdemes assemblyt hasznalni valoban.

Közben én is keresgélek még, és tényleg sok verziója van. Pseudorandom generátor néven jókat hoz a google, de nem mindegyik generál fehér zajnak tűnő hangot.

Találtam még egy blum blum shub módszert is, ami rotálás helyett maradékosztályokkal variál, de sajnos szorzás van benne (pontosabban négyzetreemelés), így nekem túl lassú.

Irtam is fentebb hogy ide erre a probemara nem pszeudorandom-generator kell hanem feherzaj-generator :) Ezutobbi joval egyszerubb, eleg hozza az XOR muvelet (plusz persze a szukseges rosszak egyebek:  bitshift, carry-fuggo elagazasok, stb). Pszudorandomnal mar nem uszod meg az osszetettebb dolgokat, amik tenyleg mar szamitasigenyesebbek. 

Ha erdekel a tema, olvasgass utana pl a Gold code reszleteinek meg hogy hogy is mukodik a GPS.

na. szereztél egy zx-spectrumot ?

*ja nem, ott csak 3.5Mhz a cpu.

HUP te Zsiga !