101

(164 replies, posted in Sinclair)

Also found the bug that causes peskysoundzx to segfault. It seems that Angelscript does not like implicit type conversions in conditionals. The following changes to peskysoundzx.1te will fix the problem:

line 542:

    if(row<int(songLoopStart))//start before loop

line 549:

    if(row<int(songLoopEnd))//start inside loop

line 748:

    if(takeLoop==1) if(row>=int(songLoopEnd)) row=int(songLoopStart);

102

(6 replies, posted in Sinclair)

Excellent. Though I would argue that the discovery of CrossPhase alone warrants a Phaser4 engine.

103

(164 replies, posted in Sinclair)

Spent some time trying to tackle the notorious "Z80Ass error: label already defined!" error again. I still don't know what exactly is going wrong here. The .loop label is indeed set twice under some circumstances (at the start of a block, in any case). So for esex, it will output this:

.song
.loop
    db #68,#00,#82,#e4,#82,#e4
.loop
    db #01,#01,#01
    db #00
    dw .loop

Anyway, I found a possible fix. In the affected engines, you usually have something like this near the beginning of the Compile function:

    row=startRow;
    takeLoop=0;

    if(oneShot==1)
    {
        len=1;
    }
    else
    {
        len=songLength-row;
        loopRow=-1;

        ...

Initializing loopRow to -1 before the conditional instead (so it affects both branches) prevents the error from occuring. Not sure if it causes any problems further down the line. I haven't noticed any yet.

Btw it seems the assembler always runs twice. Is this intentional?

104

(135 replies, posted in Sinclair)

Nom nom, that CrossPhase has a very satisfying sound. Great discovery.

I've used the idea behind ModPhase (without the xor modulation) a couple of places, BetaPhase for example. The good thing is that it can be done in the outer loop, which also allows for variable speed (by adding a variable amount to DE). In a pinch, one could even abuse the outer length counter for this, hehe.

105

(4 replies, posted in Sinclair)

Whoa, a wild garvalf appears! Good to see you're still around, mate.

106

(135 replies, posted in Sinclair)

    di
    ld hl,0
    ld de,0
    ld bc,#180
    ld sp,#38+#180/4

.play_note
    add hl,bc

    ex de,hl
    add hl,sp
    ccf
    sbc a,a
    ex de,hl
    and h
    ld h,a

    cp #20
    sbc a,a
    out (#fe),a

    dec ixl
    jr nz,.play_note
    dec bc
    jr .play_note

Not going to pretend I have any idea what's going on here. Some interesting sounds happening though. Seems that the note frequency is actually determined by the value in sp, not the one in bc. Just resetting the phase without additional modulation in the outer loop does something rather Earth Shaker-y. inc sp instead of dec bc is fun, too. No idea how to make any practical use of all of this, though.

107

(135 replies, posted in Sinclair)

Btw, I think I know what you mean re: maths now. It's often kinda tricky to know what the resulting perceived tonal frequency will be.

108

(135 replies, posted in Sinclair)

BASE=#18

init
    di
    ld hl,0
    ld de,BASE*#400
    exx
    ld hl,0
    ld de,0
    ld bc,BASE*#20
    ld sp,BASE*8

.play_note
    add hl,bc
    ld a,h
    cp #20
    sbc a,a
    out (#fe),a

    ex de,hl
    add hl,sp
    sbc a,a
.mod1=$+1
    and #40
    ex de,hl
    add a,h
    ld h,a

    exx
    add hl,de
    ld a,(.mod1)
    adc a,0
    ld (.mod1),a
    exx

    jr .play_note

??? Now what. If I could get it to only do the part where it goes "oooeoouuwww", that'd be cool.

109

(135 replies, posted in Sinclair)

I think that generally, pre-calc is a viable option. The number of unique notes in a song is usually not that high, so size-wise it's probably ok. More broadly speaking, pre-calc could open up quite a few new paths for beeper. We'd just need an editor that supports that *cough cough*

The alternative would be to, well, use a suitable modulator, which boils down to having one or more additional counters/oscs. Which is what lead me to the "scheduler" design I described above. The whole scheduling part is actually not that important. The core idea is to unroll the sound loop, say, 8 or 16 times, updating the main counters on each iteration and spreading out the more expensive computations across the various iterations. When using a 12-bit main counter, updating an auxilliary 8-bit counter every 16 sound loop iterations should give sufficient precision and speed.

My last, failed experiment uses something along those lines. It's running off 32 byte wavetables (which, in conjunction with an 8-bit main counter, gives a reasonable frequency range). When the aux counter overflows, a variable offset is added to the wavetable pointer, and the offset itself is negated. The problem is that my usual 16-32-64-112t PCM renderer is too noisy, so I would need to rewrite the whole thing using multi-core (ie, one copy of the sound loop for each volume level). It should be possible without duplicating all the "expensive calculation" versions of the sound loop, but eh... it's still a lot of tedious work.

Ah, it's made by nyanpasu64. Him and me were exchanging tracker design ideas a while back, as he was also working on an experimental next-gen tracker back then. He's also the main dev of Dn-FamiTracker, which is the only active Famitracker fork atm afaik.

111

(135 replies, posted in Sinclair)

Neat idea, don't think we've used that anywhere yet.

I wonder if it could be pushed even further. Like, instead of every frame, add the offset at a variable rate. That could perhaps turn into a sort of 1-bit FM, without the use of PCM emulation trickery. (Sorry for hijacking your idea immediately, but I recently tried to implement actual FM again, and came to the same conclusion as last time: It's possible to do the necessary calculations fast enough, but the output is too noisy for reasons not entirely clear to me.)

Thanks  for sharing.

Does your tool do spectrograms as well? Those sometimes look super interesting with 1-bit music,

113

(2 replies, posted in Sinclair)

TOO QUIET! big_smile big_smile big_smile

114

(130 replies, posted in Sinclair)

Shiru wrote:
2.   7.18   Synthetic Heartbeat by Ataritufty ^ 1bit Forum
3.   7.17   Noise In My Head by Shiru

That's insane. Btw, what's "iqm" supposed to be?

115

(130 replies, posted in Sinclair)

Ha, I thought Tufty's unce track was going to win, since it was so well received at the party.

Cosmic Puppy is indeed an excellent track. AER really re-invented himself with that. Though I think Noise In My Head is the best in terms of composition. Stellar work on the drums and those Shiru™ delays. Both your tracks are Earth Shaker EX, right Shiru?

Anyway, I think this was the strongest compo we've had in years. Good times for beeper.

116

(4 replies, posted in Other Platforms)

I tried to push some 1-bit techniques on the Vic some years ago, but didn't get very far. 656x sound is very weird, and it seems to me there's a large number of quirks and oddities that are completely undocumented.

So, until we can come up with something better, how about some classic out-of-the-box, non-1bit VIC bleeps instead: https://www.youtube.com/watch?v=-rRPMrTR7QY

117

(4 replies, posted in Sinclair)

Thanks! The bad news is that eliminating row noise in this way is tedious as hell. 0/10, would not do it again. Even zbmod with its 20something cloned cores was more fun to write.

Btw, forgot to mention, the engine breaks Fuse. A work-around is to go "Media"->"Tape"->"Clear" after starting the tune.

As I was looking for an alternative, I surveyed a few other emulators running on Linux on their beeper capabilities. Results were mixed.

  • Mame: Still the same. Passable beeper emulation, but not as good as Fuse's. Also, no auto-load capabilities.

  • jsspeccy: Very noisy beeper emulation. Not usable.

  • ZEsarUX: Errr, no. Just no. With "real beeper" mode enabled, the sound is muffled as if it came from under a pillow. Turn "real" mode off and you're left with a screeching, grinding noise that bears 0 resemblence to the actual sound.

  • Xpeccy: I remember this having rather bad beeper emulation, but apparently it's improved quite a lot. It's a bit too clean, but definitely usable. I would consider switching (also because of the fantastic debugger), but unfortunately graphics render with extreme tearing. Also, no key combo to exit the program AND ignoring SIGTERM? Come on.

So, all in all, let's hope Fuse will fix this problem. Submitted a bug report, but no response yet.

118

(4 replies, posted in Sinclair)

Ok, one more. This will be the last one for now. I've got some more ideas, but my motivation batteries are a bit exhausted for the time being. Also I do want to work on some other projects in the coming weeks.

Anyway, tftone is basically Tritone (Digi) without transition noise. No special tricks here, just plain and boring loop unrolling. Runs a bit slower than the original Tritone (216t loop), but row length can be controlled up to ¼ tick precision. So yep, it can go really fast.

Due to the big spaghetti mess there may be some bugs hiding here and there. Ultimately I would also like to get the synth loop a bit faster, 216t is still pretty noisy. I think at least 208t should be doable.

I did discover one neat trick. Probably nothing new to some of you, but. I normally like stack-based loaders, not just because they're fast, but also because you can do POP AF and have individually set Z,C,S and P flags for a quick&dirty compression scheme. With this engine however, stack-based loading wasn't feasible. So I found that actually, something functionally equivalent to POP AF can be achived with

  ld a,(hl)     ; or whatever the data pointer is
  or a          ; check for Z
  jr z,...        ; end of pattern or whatever
  sla a

Now the last one is where the magic happens. If bit 7 was set, Carry is now flagged. If bit 6 was set, Sign is flagged. SLA also flags Parity, which means we can turn that on or off individually as well, just need to pay attention to bit 6 and possibly flip another bit so the initial value is not 0 when we don't intend it to be.

Anyway. so much for that. Code's on my github repo as usual. Sorry for the crappy demo tune. Just wanted something fast to demonstrate that it does sound (relatively) clean, but of course that's really tedious to type up in asm. And this is actually speed 4, so it can go even faster.

119

(130 replies, posted in Sinclair)

Thanks. Planning to submit but don't have anything yet.

Thanks for the report, fixed. Also fixed a bug with pindsvin's drum timing.

I hope the new data format won't be too much trouble. In the worst case you could just write every row, I guess, though it certainly would be nice to have a proper dictionary of unique rows.

Ho ho ho! 'tis the season! What season? The season for new beeper engines of course.


Pulsatilla

2 squeeker channels + 2 pulse-interleaving channels, all with full duty control. Squeeker channels can be combined into a single Phaser-like channel. PuInt channels are asynchronous, channel 4 is about half as loud as channel 3. Channel 1 has a noise mode, and channel 4 has a duty sweep mode. Phase can be controlled precisely for advanced volume/timbre tricks. Also includes synthesized interrupting click drums. Standard 224t loop, engine size 418 bytes.


Pindsvin

2 squeeker channels with duty control + 3 PFM channels with volume control. Squeeker channels can be combined into a single Phaser-like channel. Also includes interrupting PWM drums with volume control. 7-bit tempo control at half-tick resolution, 292t loop, engine size 330 bytes.


nanobeep3

Yay, finally beat my own record after 7 years. A complete beeper engine in 54 bytes, this time with proper pulse interleaving sound. 8-bit dividers (acceptable tuning in the range of C-2 - B-5, lower notes can be reached but may be out of tune), and a compact split-channel data layout. No click drum, but kicks are easy to simulate in a reasonable amout of data thanks to per-row tempo control. Some additional making-of-ish ramblings here.


No tracker support for now. Source code is available in The Holy Github repo, as usual.

Whaa, what a crazy machine is this? Is it like a miniature BK or something?

Hotdamn, some very nice trickery going on in this one. And kickass music, as usual. Thanks a ton for publishing the source, I think I'll have some studying to do.

124

(11 replies, posted in Other Platforms)

Hmm, that's going to be a bit tricky. Unfortunately the disassembly is barely commented so it's kinda hard to tell what the code does by just quickly looking at it. Basically what you'd need to do is this: Find out which part makes up the main sound loop (I'm guessing lines 90-204). Count the cycles for this code. Calculate how many times you need to loop to get a 50 Hz rate. Set up a loop counter based on that. Afaict the AF' register is unused, so you could use A' to count, ie. before line 90 you set up A' with the correct value. Then just before line 204 you decrement A and then do a JP NZ instead of the JP. The fall-through (Z condition) should then jump to the former interrupt code.

125

(3 replies, posted in Other Platforms)

Looking great! Is that a VHS case you're using there?

I'm not a hardware guy so my understanding of the audio circuit is minimal, but I'm guessing the filter should give a nice, bassy sound?

Regarding which audio engine to use as a showcase, the best option would be to write a new one, of course big_smile So we could make proper use of those 8 MHz. I would actually be up for coding something, but I'm going to be pretty busy for the next month and a half, so I can't promise any quick results. That said, please post details about how to address the beeper in code, so I can start scheming.

In the meantime, I would recommend to go with one of the Squeeker-type engines. Shiru's Squat (available in his 1tracker) is probably a good starting point. The advantage here is that Squeeker-type engines use only one output command per sound loop, so you don't need to be too concerned about keeping multiple outputs properly aligned. Basically just stuff a bunch of wait states somewhere into the sound loop to adjust for the higher clock speed. Pretty much every newer ZX Spectrum beeper engine has the cycles for the sound loop counted out in the source code, so you just need to find that and multiply by 8/3.5 to get the number of cycles you need for your machine.