26

Re: next gen engine ideas

Cheers wink Hehe, maybe not to my creativity, but unfortunately there are quite some limits to my maths skills, which is providing some major stumbling blocks lately. Trying to read some stuff on DSP at the moment, mostly not understanding the slightest bit about the mathematical theory of it. Wish I could just go into recluse and just study that stuff for a few years.

27

Re: next gen engine ideas

Discovered a new trick while working on Houstontracker. Consider the usual pulse interleaving with variable duty cycle:

   add hl,de
   ld a,b          ;b = duty
   cp h
   sbc a,a
   out (#fe),a

   ;blabla same for 2nd channel

Now, as you know, we can add a "SID-style" duty cycle sweep by incrementing the duty when the add counter (HL) overflows.

   add hl,de
   ld a,b          ;b = duty
   adc a,0
   ld b,a
   cp h
   sbc a,a
   out (#fe),a

So far, so good. Now for the new stuff, we'll add an extra parameter that will be XORed against the duty setting on every sound loop iteration.

   add hl,de
   ld a,b          ;b = duty
   adc a,0
   xor c           ;c = additional XOR parameter
   ld b,a
   cp h
   sbc a,a
   out (#fe),a

This will produce some very interesting timbres! A primitive form of square wave FM, if you will. The result tends to have some aliasing noise (because the harmonics will cross the Nyquist limit if I understood this correctly), but I suppose it could be filtered out with the low-pass filter algorithm I was talking about earlier.

Now I actually went another step beyond this. Since I'm using a split timer update as usual, I have time to add an offset to the XOR paramter (C) every 256 sound loop iterations. Now this is a double sided sword - it will act nicely as a third FM OP, but only if the value is 1/256th of the base frequency used. When it is not, then the results will vary from "interesting" to "ouch my ears".

The attached .tap demonstrates both techniques. The first pattern starts with a simple duty cycle sweep and the additional XORing disabled, followed by XOR parameters of 8, $10, $20, $40, and $80. The second pattern starts again with regular duty cycle sweep and XOR 0. Following this, the XOR parameter is raised by 1/256th of the base frequncy once per 256 sound loop iterations, and following this the XOR parameter is raised by a fixed value (8 in this case) every 256th iteration. This is then repeated but with the initial XOR set to $80.

Don't ponder too much about the code in the .tap, there is some additional leftover stuff from an earlier experiment.
Anyway, I'm busy with HT at the moment, so if someone wants to pick up this trick in the meantime, be my guest wink

EDIT: Realized I posted this in the wrong thread, moved for clarity.

Post's attachments

test.tap 971 b, 6 downloads since 2016-04-30 

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

Re: next gen engine ideas

Ha-ha, I actually have a soundcore prototype with this kind of idea. Never had time to complete it, maybe one day smile

29

Re: next gen engine ideas

Oh, I better hurry up with turning this into a proper engine then, eh? wink

Re: next gen engine ideas

Nope, one beeper demo per year is enough, thank you very much smile

31

Re: next gen engine ideas

Did you manage to solve the aliasing problem that comes with this approach, and if yes, what was your solution? I'm thinking to use a simple low-pass filter like the one I described earlier, not sure if that'll work out though.

Re: next gen engine ideas

I only modulated my "main wave" using another wave with the period that was exact multiple of the period. So only the duty cycle was a bit wobbly. Hence, I did not have any aliasing problems at all, and I did not need low-pass either, because I could do low-pass type sounds with the FM itself smile

33 (edited by utz 2016-08-09 13:48:44)

Re: next gen engine ideas

So I was curious whether it's possible to combine Shiru's Phaser technique with zilogat0r's Squeeker method. Turns out it works surprisingly well!
The downside is of course that the sound is somewhat less clean than with proper pulse interleaving. But there are various benefits to having only one OUT in the loop. It's not only faster, but also there's no need to keep the cycle count down because there's no need to render multiple volume levels.

I'm not motivated to turn this into a proper engine at the moment, since it would be impossible make an XM converter for it anyway. But if anyone wants to play around with this, please do!


EDIT: Added the good ol' adc a,n SID and Earth Shaker effects on channel 1. Because, why not. This brings the total of configurable parameters for the first channel up to 10:

- frequency OP1
- frequency OP2
- duty OP1
- duty OP2
- SID on/off OP1
- SID on/off OP2
- ES duty modulation OP1
- ES duty modulation OP2
- Phase
- Mixing method (xor | or | and)

All nicely aligned to 224 t-states big_smile

The next step could be to add some modulators which would update the parameters once per 256 loops or so. Or maybe digi rendering with lo-pass... ehhh... I've got a strange feeling this is getting outta hand... yikes

Post's attachments

main.asm 2.5 kb, 4 downloads since 2016-08-09 

music.asm 9.81 kb, 5 downloads since 2016-08-09 

test.tap 3.32 kb, 11 downloads since 2016-08-09 

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

Re: next gen engine ideas

it would make a really cool synth!
I love those sounds. That's awesome.

Re: next gen engine ideas

I wonder if there is any useful 1-bit application to this trick: generating saw wave just like we generate different duty cycles. I.e. we have 16-bit accumulator and adder - add hl,de; this automatically creates 0..255 saw waveform in H.

website - 1bit music - other music - youtube - bandcamp - patreon - twitter (latest news there)

36

Re: next gen engine ideas

Not sure I understand correctly what you mean, could you provide a pseudo-code or asm example?

The only thing I could think of right now would be to implement it via a digi core (like in fluidcore, zbmod, etc). But that would be pretty inefficient, as you'd need 4 shifts to derive a suitable volume value from H. So pointing to a predefined table of wave data would be much faster (but at the cost of using up an extra register, of course).

Speaking of digi core, there is a phenomenon that I haven't quite understood yet. When the overall calculated volume is >50% for longer periods, the actual output volume starts to ramp up, producing a saw-wave like timbre (check octode2k16, it is very noticable in the demo tune, when bass kicks in). My assumption was that this happens because the speaker cone does not have enough time to retract in "off" periods, and thus will start to ramp up volume. However, I made some code to specifically trigger that behaviour (by keeping calculated volume continually above certain thresholds), and it wouldn't produce any conclusive/consistent results.

Re: next gen engine ideas

utz wrote:

The only thing I could think of right now would be to implement it via a digi core (like in fluidcore, zbmod, etc). But that would be pretty inefficient, as you'd need 4 shifts to derive a suitable volume value from H. So pointing to a predefined table of wave data would be much faster (but at the cost of using up an extra register, of course).

I've made a simple engine like that last year. Example here (not optimized for contention whatsoever). Schematically it uses a number of routines like this:

.lvNN
    ld a,#C0
    add hl,de
    and h
    exx
    add hl,de
    add h
    rra
    rrca
    rrca
    and #38
    ld (.lv_jmp),a
.lv_jmp=$+1
    jr $+2
.lv00
    ...
.lv01
    ...

interspersed with OUT (#FE),A and such like

As far as efficiency goes, it depends. With wave tables, you're doing additions on table values, one addition per each channel. This method OTOH allows summing up 'the H's' themselves first and then derive the resulting level from that, so in the end it's probably more or less the same thing

38

Re: next gen engine ideas

@Hikaru: I don't quite understand your sample code. Is the .lvNN part executed every time, and then it will jump to .lv00-.lvXY, from where it'll loop back to .lvNN? Is beeper turned off before executing .lvNN? Wouldn't that give an overall very low volume?

Another thing that I was wondering about. In BetaPhase, phase channels are calculated as

   add hl,bc
   ex de,hl
   add hl,bc
   ld a,h
   xor d            ;or|and|xor
   out (#fe),a

which saves at least one register pair compared to a full Phaser implementation. During testing, I realized that actually 8-bit frequency dividers are enough in conjunction with scalers. So the above could be rewritten as

   ld hl,0
loop:
   ld a,b
   add a,nn
   ld b,a
   sbc a,a
   add a,h
   ld h,a
   ld a,c
   add a,nn
   ld c,a
   sbc a,a
   add a,l
   ld l,a
   xor h
   ds 2             ;2x scaler (rrca/rlca)
   out (#fe),a

freeing up another register pair in the process.
So here's the question: Can't  we get rid of one of the counters somehow (H,L in the above example)? In other words, is it possible to generate similar bitstreams as above (or at least something that offers timbre variation), but with a combined upper counter byte for both of the operators?

Re: next gen engine ideas

utz wrote:

@Hikaru: I don't quite understand your sample code. Is the .lvNN part executed every time, and then it will jump to .lv00-.lvXY, from where it'll loop back to .lvNN? Is beeper turned off before executing .lvNN? Wouldn't that give an overall very low volume?

I tried to illustrate the way the channels are calculated alone, throwing out loop counters etc. In the actual engine, there's only one .lv_jmp JR, which is located near the beginning of the loop and preceded by the fixed-position OUT. There's two OUTs each loop iteration. The placement of the second OUT varies according to the .lv routine (this is the only difference between them), i.e. each .lv routine sets a certain 'volume level' using PWM. Logically, they are divided into intervals of 11-13 T states for this purpose, made up from just what's in there, hence why it's so noisy esp. on contended machines. Overall there's 8 'levels' like that, each of the channels uses 4. I don't remember the reason for double-digit label naming, probably had 16 levels in there at some point

40

Re: next gen engine ideas

Ok, understood. It's essentially the same method I'm using in my digi-core engines, except that my sound output and calculations are fully interlaced (eg. during a loop iteration, I calculate the volume level for the next iteration, then jump at the end of the loop). That way, you can squeeze some more levels out (in theory, 224/8=28 levels would be possible, but there are some issues with that, so ~20 levels is more realistic).

Shiru, is this what you were trying to get at? Or did you have something else in mind?

Re: next gen engine ideas

I was figuring out how early digital analog synths (i.e. with digital oscillators) were working, I always wondered why they only had saw, pulse, and a combination of those, but not some other waveforms (triangle, sine). I thought that is related to the richer harmonic content the two gives, but it turned out in my experiments that a combined pulse/saw generator can be implemented as simple as add hl,de, so that's likely the reason. It naturally gives the saw waveform in the MSB, and putting a threshold on it (with cp N) gives pulse with duty cycle control. We're using the latter quite often, but maybe there is some use to the saw itself too?

I tried a naive approach, use top 4 bits to generate a short pulse of different width every loop, sounds bad and not usable.

website - 1bit music - other music - youtube - bandcamp - patreon - twitter (latest news there)

42

Re: next gen engine ideas

Ok, gotcha. Interesting point indeed. Well, it can always be faked via digi method (as can any other waveform at a minor cost), but the real nice thing would be to exploit this via an actual algorithm. So in abstract terms, the question would be how to translate the amplitude levels in H into the time domain. Pity that it doesn't work with pin pulses, that would've been my first try, too. Hmm, what about phase inversion? As you may remember volume can be controlled like this:

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

where the offset between HL and HL' will determine the volume (HL=0, HL'=#8000 is most quiet; perceived volume change is logarithmic). Perhaps it's possible to modulate HL' by H somehow (HL' += (256 - H) or something like that)?

43 (edited by utz 2016-11-16 22:50:00)

Re: next gen engine ideas

Ok, here's another, rather wild idea: Could this be of any use? Rui Martins once showed me an almost-working implementation of it, which operated on signed, range-limited PDM wavetable data. I never fully understood how it actually worked, though. It had problems in any case, mixing was unreliable. I'll see if I can get Rui's permission to publish his code here.

Re: next gen engine ideas

Well, pretty dumb thing like that gives some interesting modulations. Kind of a saw LFO. Unlike Phaser, the addition value for each of generators has different effect.

    ld hl,0
    ld de,60 ; this is like LFO frequency
    ld bc,0
    exx
    
    ld hl,0
    ld de,240 ; this is like base frequency
    ld bc,0
    
loop

    exx
    
    add hl,de
    ld a,h
    
    exx
    
    add hl,de
    ld c,a
    add hl,bc

    ld a,h
    rla
    sbc a,a
    out (#fe),a

    jp loop
website - 1bit music - other music - youtube - bandcamp - patreon - twitter (latest news there)

45

Re: next gen engine ideas

Hmm, in its current form it burns a lot of registers for the buck, I'd say. But generally speaking, anything that does pitch slides is always interesting. Do you think there's any chance to turn this into a vibrato effect somehow?

Meanwhile, I got permission from Rui, so find attached the source for his "WPDM16" player. A few notes:
- main.asm is the latest version. It does not attempt full PDM mixing, but rather a simplified version thereof.
- main_old.asm is an earlier version. Not sure if this one is working at all, so I include it mainly to illustrate how the player developed over time.
- Sample data should contain only signed offsets from -64 to +64. However, as I mentioned mixing is unreliable, so I created some samples that would mix with reliable results, but violate the specs.

Post's attachments

wpdm16.tar.gz 8.51 kb, 5 downloads since 2016-11-17 

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

Re: next gen engine ideas

To make vibrato, we need something that goes back and forth, while saw only goes one direction with jump reset. But it can be used as pointer to a table that turns repeating 0..255 into 0..127..0 in triangle or sine shape, that will give vibrato. But then we don't really need the tricky scheme with using a saw/pulse generator, and can just use a regular counter to go through the vibrato table.

website - 1bit music - other music - youtube - bandcamp - patreon - twitter (latest news there)

47

Re: next gen engine ideas

Yes, but then again vibrato via table lookup sucks for two reasons: One, it burns a load of cycles. Two, fixed offsets are not so useful - for a good vibrato, the frequency shift should ideally be calculated as a fraction of the base frequency. A more elegant solution would be nice to have. Though I don't see how at the moment, I'm having enough trouble pulling off normal one-way slides already (finally managed a somewhat acceptable solution in Betaphase, which is similar to what you use for synth drum generation in Phaser2 I believe).

48

Re: next gen engine ideas

Discovered something today. It may seem trivial, but nevertheless it was a bit of a revelation to me.
So basically I answered my own question about getting rid of the second counter in a simplified phaser algo. What I came up with is this:

   add hl,de
   ld a,h
   add a,c            ;C holds "second counter" offset
   and h               ;xor|or|and
   out (#fe),a

Now isn't it interesting how, by just swapping two bytes, this can be transformed into a regular "threshold" implementation? And yet this piece of code was derived from a Phaser implementation.

Re: next gen engine ideas

utz,

this is very interesting, I have been (too) busy elsewhere, but trying to follow what's going on here every day.
I am going to make some tests with this engine, looking now to ASM-files, especially music.asm, apparently need to look Phaser and Squuker methods as well, for the tunings, methods (1+2), shaker (?)...

Thanks of great job utz !!




utz wrote:

So I was curious whether it's possible to combine Shiru's Phaser technique with zilogat0r's Squeeker method. Turns out it works surprisingly well!
The downside is of course that the sound is somewhat less clean than with proper pulse interleaving. But there are various benefits to having only one OUT in the loop. It's not only faster, but also there's no need to keep the cycle count down because there's no need to render multiple volume levels.

I'm not motivated to turn this into a proper engine at the moment, since it would be impossible make an XM converter for it anyway. But if anyone wants to play around with this, please do!


EDIT: Added the good ol' adc a,n SID and Earth Shaker effects on channel 1. Because, why not. This brings the total of configurable parameters for the first channel up to 10:

- frequency OP1
- frequency OP2
- duty OP1
- duty OP2
- SID on/off OP1
- SID on/off OP2
- ES duty modulation OP1
- ES duty modulation OP2
- Phase
- Mixing method (xor | or | and)

All nicely aligned to 224 t-states big_smile

The next step could be to add some modulators which would update the parameters once per 256 loops or so. Or maybe digi rendering with lo-pass... ehhh... I've got a strange feeling this is getting outta hand... yikes

50

Re: next gen engine ideas

Cheers mate! This experiment eventually became the Phase Squeek engine. Have a look at MDAL, too, it makes composing for this engine slightly more convenient.