51

Re: next gen engine ideas

I believe there may be another way of fak... err, I mean generating a saw wave on beeper. For this, we need to look at the frequency spectrum, rather than the waveform. In the frequency domain, a square wave is characterized by having a peak at each odd harmonic. That means, for a 440 Hz, 50% duty square wave, frequency components will be present at 440*3, 440*5, 440*7 Hz, and so forth, but not at 440*2, 440*4, 440*6 Hz etc.
A saw wave, on the other hand, peaks at both even and odd harmonics. Thus it can be approximated by mixing a series of square waves with frequencies of f, f*2, f*4, f*8, etc. With the "fast accumulation" technique (add hl,de \ ld a,h \ out (#fe),a) we can easily achieve this by rotating A to get the required multiples. Doing this with 3-4 frequencies should already give a reasonable approximation of a saw, because the missing components will be high enough to fall out of the range at which humans can reliably detect the difference.

52

Re: next gen engine ideas

Alright, got a new idea - use a "state memory". Now wtf is a state memory? Think about how we implement accumulative pin pulse engines (Octode XL, for example). We basically "remember" the state of the carry flag from add hl,de for a given time (which, in an accumulative pin pulse engine, will control the volume). But in pulse interleaving engines, we evaluate the state only once and then discard it immediately. But it might be useful to actually remember it for some time. For example, we can implement a primitive low-pass filter like this (excuse the sloppy code, it's early in the morning big_smile)

   ld hl,0        ;counter, clear it
   ld de,#40      ;freq divider
   ld b,0         ;"state memory"
loop
    add hl,de     ;nothing special going on here, just adding da stuff
    ld a,h        ;HL bit 12 = output state
    out (#fe),a
    
    and #10       ;store the current state in state memory
    or b
    ld b,a
    
    rrca          ;get old state from 8 loop iterations ago
    ;rrca         ;2x rrca = high cutoff, 1x rrca = low cutoff (filter moves slower)
    nop

    nop           ;timing
    
    out (#fe),a
    
    and #ef       ;delete old state from state memory
    ld b,a
    
    ld c,0        ;timing
    
    jr loop

High-pass could be implemented in a similar fashion, because HP_output is basically just (original_output - LP_output). There could be other uses for this "state memory" thing as well.

53

Re: next gen engine ideas

Proof of concept for my idea about saw wave generation:

;saw wave via square wave transform ([f,a]+[2f,½a]+[4f,¼a]...)

    ld hl,0
    ld de,#40
    ld c,#fe
    
loop
    add hl,de    ;11
    
    ld a,0        ;7    ;timing
    ld a,0        ;7    ;timing
    ld a,0        ;7    ;timing
    nop           ;4
    
    ld a,h        ;4

    out (c),a    ;12__64   ([f, a])
    rrca         ;4
    out (c),a    ;12__16   ([4f, ¼a])
    rrca         ;4
    ds 4         ;16
    out (c),a    ;12__32   ([2f, ½a])
    jr loop        ;12
Post's attachments

test.tap 120 b, 2 downloads since 2016-11-29 

You don't have the permssions to download the attachments of this post.

54

Re: next gen engine ideas

Been doing some thinking about vibratos. Here's an algo that is reasonably fast, has configurable parameters, and doesn't require a table lookup:

    ld hl,0
    ld de,note_divider
    
    ld c,1                    ;vibrato strength (max 0xf)
    ld b,2                    ;position init (must be ½ of vibrSpeed range - else detune)

loop
    add hl,de        ;11
    ld a,h            ;4
    out (#fe),a        ;11

    ld a,e            ;4
    jr nc,noVibrato        ;12/7

    ret nc            ;5        ;timing
vibrDir                    
    inc b            ;4        ;initial direction (probably needed as configurable parameter)
vibrSpeed equ $+1
    bit 2,b            ;8        ;speed (see above, bit 3 -> ld b,4 | bit 2 -> ld b,2...)
    jp z,vibrDown        ;10
                    
vibrUp
    add a,c            ;4        ;DE += C
    ld e,a            ;4
    adc a,d            ;4
    sub e            ;4
    ld d,a            ;4    
    
    jr loop            ;12
                ;96    

vibrDown
    sub c            ;4        ;DE -= C
    ld e,a            ;4
    sbc a,a            ;4    
    add a,d            ;4            
    ld d,a            ;4    
                    
    jr loop            ;12    
                    
    
noVibrato
    ;... waste some t's
    jr loop            ;12
                ;96

I have a feeling that this thing can be optimized further by using an sbc a,a fallthrough solution, but haven't managed to implement it for the downward slide yet. Let me know if you have any ideas for this.

Re: next gen engine ideas

A random idea for an engine - basic 1-bit speech synthesizer (like ones that Mister Beep used in some intros to his songs) designed to make music. Something like that: one channel for variable pitch one-shot and looped samples for consonants and vowels, another channel for tone (Phaser-like maybe) and non-interrupting drums (slide based, optional noise). Besides vowels, some basic looped waveforms could be used to create musical parts on the first channel.

As for controls, something like AlterEgo, but simplifed. Like, array of strings of syllable-separated transcription of the text, automatically loops on the vowels, advances to the next syllable (and pitch) with a new note.

56

Re: next gen engine ideas

As far as singing speech synths go, there's of course tavzx: https://www.youtube.com/watch?v=KkZKDJwwb2o
I agree, would be interesting to have something like this in an actual music engine. However, I'd be more interested in implementing actual formant synthesis. I've done a sample-based speech synth a couple of years ago (also with variable pitch btw), it's pretty boring work imo.

Re: next gen engine ideas

Hi guys

I think this is a great idea. As you know, I've always liked to try and include voice-like sounds in my Beeper tunes,
1-Bit Mechanistic, Mechanoid etc.

An engine I'd love to hear is a rombeep one that converts voice sounds right down to basic beeps. Almost like the effect used sometimes on synthpop music where a vocoder effect is turned up to the max.

I've thought of trying to analyse the waveform of some allophones and manually work out a 'beep-set' myself which could be used in a Beeper tune.

I tried it on my cover of 'Difficult for Weirdos' right at the end.

Another Beeper engine idea is QChan+
A hybrid engine with an AY bass channel (buzztone etc.)

I thought maybe one of the channels could be played as standard QChan but also the same notes as AY bass?
That way the song data could be used and exported from Beepola and incorporated into the Qchan+ engine .asm ?

What do you think utz????

58

Re: next gen engine ideas

Well, trying to imitate the waveforms of the allophones is basically already a sort of proto-formant synthesis wink

Hybrid Qchan would be doable. Generally, almost any beeper engine can be combined with AY without too much trouble. The main problem is that adding AY would significantly increase row transition noise on most engines, because (re)loading the AY registers takes a lot of time. The other problem is volume balance. For example the classic Qchan is not a good candidate for combining with AY buzzer, because the buzzer is much, much louder. This is why I chose Squeeker-type synthesis for my hybrid experiment, because this type of synthesis produces very loud sound, and it also cloaks row transition clicks pretty well.

Well, as I said earlier, I've retired from writing beeper engines for the time being. So don't put your hopes on me. I do hope though that someone else will pick up these ideas, and I'm of course more than willing to help if somebody is to take up the challenge.

Re: next gen engine ideas

Thinking on the 1-bit SuperSaw on ZX and messing up with assembly code a bit, I tried this thing. The idea behind it is the early electronic orgrans that used frequency dividers to form sound registers. So only one real counter here. Can't remember if we ever used something like this?

    ld hl,0
    ld de,400
    
loop
    
    add hl,de
    ld a,h
    cp #80    ;duty 1
    sbc a,a
    out (#fe),a

    push hl
    
    add hl,hl
    ld a,h
    cp #80   ;duty 2
    sbc a,a
    out (#fe),a
    
    add hl,hl
    ld a,h
    cp #80   ;duty 3
    sbc a,a
    out (#fe),a
    
    pop hl
    jp loop

This is one channel with 16-bit adder, the usual idea with duty cycle control like in Tritone. The trick is to generate a number of octave doubled derivatives (phase synced, though) by multiplying the adder by 2 (adding the adder to itself) a few times, two in this example. It adds octave doubles to the sound. Interesting thing is that the three duty cycles kind of have a limited control on the strength of the octave harmonics.

Another thing is that just applying a few duty checks to single unchanged adder also affects the timbre in a way:

    add hl,de
    ld a,h
    cp #80
    sbc a,a
    out (#fe),a

    ld a,h
    cp #08
    sbc a,a
    out (#fe),a

60 (edited by utz 2017-04-05 11:28:01)

Re: next gen engine ideas

The first example can be done faster with 12-bit counters, at the cost of loosing variable duty:

   add hl,de
   ld a,h
   out (#fe),a
   rrca
   out (#fe),a
   rrca
   out (#fe),a

Second example is very powerful. One can create all sorts of waveforms with this, especially if you also play with the distances between the OUT commands. If I understood fourier transformations, I'd have some great fun with this...

My idea was to use a free-running counter. Original idea of an 8-bit counter didn't work, so here is a pretty clumsy version with a 16-bit counter:

    ld de,#80
    xor a
    ld h,a
    ld l,a
    ld b,a
    ld c,a
    
loop
    out (#fe),a    ;11__36
    add hl,de      ;11
    inc bc         ;6
    ld a,h         ;4
    nop            ;4
    out (#fe),a    ;11__36
    add a,b        ;4
    ld r,a         ;9
    jr loop        ;12

Suprisingly it actually works, but wasting a whole 16-bit reg on this seems rather wasteful. Though on the other hand, one free-running counter can serve multiple channels, so maybe not so bad after all.

Edit: On second thought, that register is maybe not wasted at all... because it might be possible to use the frame length counter for this :D
Edit2: Trying out the idea in BeepModular. The effect is less pronounced here because of the volume difference (48 vs 64t), but generally abusing the timer for this seems like a good idea.

Post's attachments

test.tap 929 b, 4 downloads since 2017-04-05 

You don't have the permssions to download the attachments of this post.

Re: next gen engine ideas

I can't say much about this, but it sounds indeed great!