301

(5 replies, posted in Sinclair)

Yay, congratulations on this achievement! And welcome to the rabbit hole of 1-bit engine making big_smile

Ehh, I wish I had Shiru's advice back in the day when I started to make my own engines. Could never wrap my head around it back in the day. Just like I couldn't figure out 16-bit dividers, lol. Speaking of which, I'd say try to figure those out next. Aside from having a greater note range, it actually makes things simpler wink

Of course, if you have any questions, ask away. More than happy to help.

1tracker definitely has the widest selection of beeper engines, including many modern ones.

I don't recommend Bintracker. It's buggy, and those bugs won't be fixed either. I'm working on a completely new version of it (see https://bintracker.org), but it's not ready yet.

1tracker can be a bit intimidating at first, but once you get used to its somewhat unusual workflow, you'll find that it's a very efficient and well thought-out tool. It doesn't use a sequence/order list, so you don't ever edit your sequence manually. Instead, you have one endless pattern that you can break into sections by hitting Space. You can navigate between sections with the Home and End keys. You'll be copy/pasting a lot so remember those keys as well: Shift + arrow keys or Home/End marks a selection, then Ctrl+C/X/V to copy/cut/paste WITHOUT shifting the following content, or Shift+X/V to cut/paste and shift the following content. Other important keys: Ctrl-+/- or Numpad +/- to increase/decrease the speed, Numpad *// to increase/decrease the base octave (or Shift+number to select octave), Ctrl+U/D/O/K to transpose selection up/down by a semitone/by an octave. On engines that have instruments, +/- cycles through instruments on the instrument screen (F4). Ctrl+F10 quits. F5 is play/stop, hold Enter to play from current row.

Hi Jumperror, welcome to the forum. Wow, you're doing a lot of cool stuff! Glad to have you aboard. Looking forward to hearing more 1-bit (and other) tracks from you. I'm curious what you could come up with on some of the newer beeper engines. If you have any questions feel free to ask anytime.

304

(2 replies, posted in Calculators & Pocket Computers)

That would be [ALPHA], [MODE].

305

(21 replies, posted in Sinclair)

Great little tune. A pity that CAFe didn't have a dedicated beeper compo though. Hmm maybe next year...

306

(135 replies, posted in Sinclair)

So if I understand correctly, the following code would be equivalent:

  add ix,bc
  ld a,ixh
  add a,l
  ld a,ixy
  adc a,h
  sbc a,a
  and #10
  out (#fe),a

307

(135 replies, posted in Sinclair)

Aye, it took a while for ye olde brain. I was fixated on the "volume control" part, but it's really all about the harmonics, right?

In that case, a word of caution: Emulators are often misleading in that respect, actual hardware can sound quite different. Generally 48k tends to be more noisy, and harmonics tend to be buried under the noise. Anyway, if you're going to explore further in this direction, I can help with some hw recordings.

The question remains where these harmonics come from in the first place. Is it because at these marginal pulse width threshold overflow errors from the frequency calculation become more significant?

When I worked on those multi-core engines with many volume levels, I also noticed another effect. If there are several consecutive frames with high total volume (mimicing a DC offset), some sort of volume ramping will occur, gradually turning rectangular waves into saws. Octode2k16 is perhaps the best example of this. I tried to control this effect but it was very unreliable. Still wonder though if it can be used somehow.

308

(135 replies, posted in Sinclair)

Hmmm, isn't this rather a (very fine-grained) duty sweep? Check what happens when you replace inc hl with inc h... I think for phase-shift controlled volume you'd need 2 outputs.

However, there are indeed some interesting harmonics going on here. No idea where they come from, but they do remind me of that time when I unsuccessfully tried to make a 15ch pwm engine: https://bitrotlabel.bandcamp.com/track/pad5
I really wonder where these harmonics come from.

Hi Dave,

In general you should be able to get away with LD ($6800),A. The thing is that, because of some hardware quirks, on the Spectrum we try to align all writes to port $FE to a multiple of 8 t-states for cleaner sound. Chances are you don't have such a requirement on your machine, so you can get away with shifting the timings a bit.

There is another problem here though. The engines that use this type of "output-and-rotate" approach heavily rely on the fact that the Speccy only looks at bit 4 for the output* and ignores the other bits. So writing the value of A directly to ($6800) probably won't work. You'd need to check bit 4 of A each time and set the output accordingly, or perhaps you could look into changing the sample format so it better suits your needs. I think if you changed the sample values as follows:

$88 -> $11
$cc -> $99
$ee -> $77
$ff -> $ff

things might work out.

* that's a bit over-simplified. Bit 3 also has an impact but often that fact is simply ignored.

310

(5 replies, posted in Sinclair)

Finally got around to finishing the engine based on Kurt James Werner's Velvet Noise technique, specifically Crushed Additive Random Noise. To my knowledge this is the first PFM (pin-pulse) ZX beeper engine that mixes tones and noise. In addition to the noise channel, there are 3 tone channels, all with volume envelopes. Added the synth kick from my new set of reusable click drums for good measure.

source

311

(6 replies, posted in Sinclair)

Thanks mate smile Good idea about the track listing. Gotta find my yt password first, though yikes

312

(4 replies, posted in Sinclair)

Yes, of course this will be in Bintracker eventually.

Yeah, I'm having too much fun with writing z80 asm again. Got two more engines almost ready, and I've got several ideas for more. But I definately need to focus on Bintracker. All those new engines will be pretty much useless without tracker support.

313

(4 replies, posted in Sinclair)

May I present to you the latest abomination from Utz Beeper Engine Constructions. Archie is sort of the spiritual successor to Pytha (hence the name), but then again it isn't, really.

From the readme:

Archie is an experimental 1-bit sound engine for the ZX Spectrum Beeper. It
draws inspiration from other modern beeper engines such as Pytha, povver, and
Tritone Digi.

Archie uses the pulse interleaving technique to create 5 tone channels with
asynchronous volume. The first 4 channels are intended to be used in pairs with
controllable phase offset. This simple setup is useful for producing a range of
different, sometimes surprising effects. The 5th channel is a regular pulse wave
channel with duty cycle control and duty cycle sweep.

Channels 1a and 2a are playing at roughly 1/3rd of the volume of channel 1 and
2, respectively. The 5th channel plays at about 2/3rds of the volume of channel
1/2.

In addition to the tone channels, Archie features an interrupting click drum
channel with 3-bit volume control. Click drums are arbitrary PWM samples played
at 31250 Hz. The core synthesis runs at 15625 Hz (standard 224 cycle loop).

source

There's no XM converter, for obvious reasons. Short demo track is attached below. Couldn't be bothered to to make it any longer because composing for this thing in asm is a major p.i.t.a.

314

(135 replies, posted in Sinclair)

Thanks mate! Been missing you around here lately, glad you're alive and kicking smile

315

(6 replies, posted in Sinclair)

Live @ Chipwrecked 2019:

https://www.youtube.com/watch?v=CtNVPV0DNeQ

Enjoy smile

Video recording by N3M3515, I wholeheartedly recommend checking out his stuff if you're into heavy chip metal.

Sorry, got a bit side-tracked with that other new idea.
Anway, thanks a lot, of course it's great to have this here as a reference (plus it confirms I'm actually implementing things as intended).

Regarding EVN, so far I only tried the CARN flavoured variant, and was a bit underwhelmed by the results. I mean your sound examples clearly demonstrate its usefulness, but it seems rather tricky to get the implementation right. It seems that for the sound I'm most interested in (I don't know how to describe it properly, basically a sound that is still more tone than noise... how do you even measure these things? Is there a term for the "noisyness" of a signal?), useful Δs fall into a very narrow range, which is hard to hit with the low sample rates and limited 16-bit frequency resolution that I'm using. From your example it sounds like COVN may be a bit more forgiving in that respect, so I'll have to experiment more with that. I also really want to properly finish the CARN based 1-bit engine though. Well, too many projects, and too little time, as usual.

EDIT: New beeper engine using CARN is ready: http://randomflux.info/1bit/viewtopic.php?pid=2033

317

(135 replies, posted in Sinclair)

Ha, nobody expects the Spanish Inqui... I mean this new trick I discovered!

So basically I found a way to approximate a sine wave with just two square waves. Which means it's cheaper to implement than what Pytha uses to generate the triangle wave. Quality isn't as good as Pytha (mainly due to the fact that it seems almost impossible to properly align outputs to 8t multiples), but hey, how about squeezing in a 3rd channel? There's also some sweet overtone tricks you can do with this, as can be heard at the end of this short demo track. The third channel is a regular pulse channel with duty cycle control/sweep. Was hoping I could do 3 sine channels but I ran out of cycles. Either I'd need an extra register pair, or I'd need to split frequency counter updates across two loop iterations, which would probably degrade sound quality quite a bit. Overall the technique seems to be less flexible than the Pytha method as well, but I haven't explored it all that much yet.

Found an optimization for the CARN code which speeds it up quite a bit. Also, the tone channels now have envelopes.
The core loop is currently 185 cycles (18918 Hz), which means I can probably get away with cramming in another tone channel. I'm focussing on CARN at the moment because the optimization will not work for COVN.

Also that low shelf characteristic of COVN got me thinking. What if we were to also apply a lower limit to the random number range? Then the result should become tonal at some point, right?

Edit: uh-oh, the gears in my good ol' brains are rattling pretty hard right now. For one, if the spectrum of COVN can be limited in this way, then I could use that to basically simulate a band-pass filter in 1-bit. That would obviously open up a whole new range of sound possibilities. It's going to be quite tricky though because this requires a way of generating random numbers within an arbitrary range. Secondly I think I just understood why velvet noise is useful for reverb. Hmmm 1-bit reverb... hehehe...

So, here's a test modified to use CARN, for comparison. The test implementation will occasionally produce errors (manifesting themselves as short blips), but other than that it sounds quite alright to my ears as well wink

The great thing is that since there's one variable less to track, CARN can be implemented in less cycles, which is always a plus. But both COVN and CARN obviously have their uses considering the differences in sound. So ideally I'd want to come up with an implementation that allows switching between the two at runtime. I'm sure it just needs a nice piece of self-modifying code big_smile Only problem, as mentioned, I'm out of practise as far as assembly coding is concerned, but I definately want to get back to this and produce a proper engine.

In the first test example, there may be some additional problems in addition to the bad RNG. Also, the pulse density is actually fairly high in both examples. The envelope in the COVN one starts with a window size of 9 and goes up to 129. For CARN, the envelope starts with numbers < 8 and goes up to numbers < 256. In any case, the random number sequence is actually not random at all, since I'm using a fixed seed for the PRNG. If you are interested in studying the characteristics, the sequence is calculated as follows:

// for COVN
// initialization
uint_16 seed = 0x2157
uint_16 state = 0
uint_8 window_size = x  // x ∈ {0x9, 0x11, 0x21, 0x41, 0x81}

// the actual generator
// as wasteful as it looks, it's actually just 3 bytes/ 19 cycles in Z80 asm - add hl,de; rlc h
uint_16 next = (state + 0x2157)
uint_16 temp = (((next >> 8) & 0xff) << 1)
state = (next & 0xff) + (temp << 8) + (temp & 0x100)

// by coincidence, this prevents two consecutive pulses colliding at the end of the current/beginning of next window
uint_8 next_pulse_delay = ((state >> 8) & (window_size - 2)) + 1

For CARN it's essentially the same, except that the window_size is replaced with a "range" value x where x ∈ {0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff}, and the pulse delay is calculated as

uint_8 next_pulse_delay = ((state >> 8) & range) + 1

The COVN example runs at 17412 Hz and the CARN example runs at 19662 Hz (actually slightly lower, because there is a short delay every 256 samples for updating the length counter, and there can also be some minor fluctuation depending on internal machine state). The window size/range is updated every 256*32 samples. There is an additional quirk: pulses across all channels will never coincide. If more than one pulse is triggered on the given sample, the additional pulse(s) will be delayed until the next sample(s). The tone channels always trigger a "double" pulse. I found this to yield a resonable volume balance. In an actual engine we'd want to make the pulse length variable for the tone channels to fake volume envelopes for those.

There are a number of different seeds that will work reasonably well, I'm just sticking to this one because I'm lazy and and it's the only one I can remember off the top of my head. Might also be worth lifting some ideas from Shiru's noise generator code to get better random numbers.

In any case, thanks for the detailed explanation regarding the spectral characteristics. I have to admit that the point in your paper where you start to talk about Power Spectral Density was about my brain switched off, especially with those scary-looking equations on page 4 yikes

Alright, a new test, this time noise is combined with two pulse-frequency modulated tone channels. Terrible code, seems my asm skills have gotten awfully rusty. Well, it works, at least.

A question, it sounds like there is a drop in pitch with increasing window size. Is this expected, or is it an effect of the bad PRNG?

Thanks a lot for the detailed explanation! It's all very cle... wait, so there's no backtracking involved, eg. the pulse location in window n+1 is independent of the pulse location in window n? If so, what's the difference between COVN and CARN, then?

Anyway, I'm just playing around with some naïve test code at the moment, assuming the above is true. It does sound noisy alright, even using my not-so-uniform el-cheapo XORshift. Computation takes 49 cycles, which is reasonable (though I'm cutting some corners at the moment). We normally can spend up to 224 cycles on the synthesis loop (a bit more when using Pulse Frequency Modulation), which will give use a sampe rate of 15625 Hz with the Spectrum's 3.5 MHz clock.

The attached example runs at 48.6 KHz, using a window size of 16 (so a density of around 3000 if I'm not mistaken). In any case it's just a quick&dirty test, more experiments to follow later. (It's a Spectrum tape file, so you'll need an emulator - I recommend Fuse if you aren't sure which one to pick).

kurt.james.werner wrote:

I'm realizing now that perhaps old-school 1-bit trackers don't even have a fixed sample rate the way that modern audio processing (e.g. in a VST) does, but that in your context you deal just with raw clock cycles of the computer (up in the MHz?). Is that correct?

Correct, we normally deal with raw clock cycles, though it's trivial to approximate the sample rate. Assuming the synthesis loop length is fixed (which is true to some extend for most existing 1-bit engines on Spectrum), and ignoring some hardware quirks, it's a matter of simply dividing the CPU clock speed by the number of cycles in the synthesis loop.

kurt.james.werner wrote:

And if so, does that mean that on the ZX Spectrum you don't think in terms of audio samples in discrete time (e.g. a pulse wave like 000100010001) but rather just the time to flip up to +1 and a time to flip back down to 0, which need to be very close for a pulse train?

I can only speak for myself here, but I tend to think about it terms of samples in discrete time more often that not. It depends a bit on the platform - for example for PC speaker the latter approach might be more natural, due to how the hardware works. On Spectrum the common approach is to run a fixed-length synthesis loop, so it makes more sense to think about it in discrete time terms.

kurt.james.werner wrote:

I probably have a lot to learn about the terminology and basic idea of your approaches, so thanks for bearing with me here.

Don't worry too much about our terminology, it's not very formalized and everybody kind of uses their own terms. You'll probably know better how to actually name things than us most of the time. In any case, if you have any questions please feel free to ask, of course!

In any case, I have to thank you again for the great input you have been providing. I haven't been playing so much with 1-bit synthesis lately, mainly because of working on a new, experimental tracker, but also because of a lack of inspiration. Well, this discussion certainly brought back some of the latter wink If you have any more "tricks" to teach, please don't hold back! Generally any sort of signal processing with low computational requirements is of interest (never mind the "1-bit-ness" of things, it's possible to simulate multiple volume levels on 1-bit).

I think I've mostly figured it out, the only missing puzzle piece is the discrete pulse index m resp. impulse location and how it relates to the sample index n.

Regarding a real-time implementation, the main challenge will be to generate the two random number sequences. The Spectrum hardware does not provide a source of random numbers, so computing such sequences is very costly (even more so considering the computation needs to happen in constant time). The question is how much we can deviate from a uniform distribution. But that will be something to explore via experimentation, I guess.

Anyway, please take your time with the response, I won't run away in the meantime wink

Been thinking a bit more about a feasible implementation. Since we have very tight memory constraints on our machines, ideally we'd want to generate the pulse sequences in realtime. However, I'm afraid the computation might be too expensive despite it not requiring multiplication. But since the pulse sequences are sparse, it should be possible to devise a format in which they can be stored efficiently after precalculating them - essentially we could just store the distance between the pulses. However, this would take away quite a bit of flexibility. Hmmm... my main struggle at this point though is still understanding how to generate the actual kCOVN/kCARN/kCTRN sequences.

This is very, very exiting. As you correctly noted, good quality noise is pretty much uncharted territory in the realm of practical 1-bit music, even more so the possibility to control the spectrum and volume. We do have some primitive means of controlling the volume (by means of tweaking a threshold value applied to a simplified XORshift algo), but judging from the demo tune your methods obviously produce much cleaner and versatile results.

I have to admit that due to my limited knowledge of both dsp and mathematics, I can only grasp the very rudimentary basics of your paper. However, I'm very interested in implementing this on ZX Spectrum. So, I would be very grateful if you would be willing to help me understand how these techniques work. Could you provide a dumbed down explanation, or ideally some C/Python/Nyquist/pseudo-code? Also, more specifically, what do the terms "discrete-time sample index", "discrete pulse index", and "window width" mean in this context?

By the way, much obliged for mentioning us in the paper. One small note regarding this: I think Shiru deserves some credit as well, as he broke a lot of ground for the "modern" 1-bit scene as it exists today.

Anyway, thanks a lot for bringing this to the attention of our humble little internet hangout.

Neat! I guess your next music album will be an "enhanced 1-bit" one?