Damn, what an ordeal! Does this also happen if you try to transfer via command line ("tilp ti82 Silverlink ht2.82p")?

777

(2 replies, posted in General Discussion)

Hehe, I actually got a 128D sitting on my desk wink Was wondering about the Z80, too. But all the C= coders I've talked to so far have told me that it's not worth it because the Z80's power is seriously limited by the system. So, in order to make some 1-bit music on the machine, one would be better off coding for the 8502. I don't really like 65xx asm though, so... not for me wink

Btw check this out: http://csdb.dk/release/?id=94238 wink

Thx. Sent you a mail.

Abrimaal's collection (= AYGOR) has already been added, I think. And yes, moroz needs .ay. I have some cruddy (Linux) toolchain which helps with the conversion, but it's still a lot of manual work.

Neat! But yeah, definately needs more votes. Also yes, I think e.g. a lot of stuff from BotB is still missing. Unfortunately I don't have enough time to sift through and convert those entries atm. I think it would be really helpful if there was a conversion tool which would make the process a bit less time-consuming. Perhaps you could get in touch with puke7 from BotB to see if it's possible to utilize meta-data from BotB somehow?

In any case, I've added a link in the forum header, hope that'll help to generate a few clicks wink

Thanks for the info! I have to admit the whole thing is a bit of a mystery to me - was the engine actually ever released in source format?

782

(10 replies, posted in General Discussion)

Yeah, the usual problems... hope the site will be back up in a few days.

Ok, sorry to hear that. Could you let me know which Windows version you're using though? I'm trying to sort things out with the TiLP developer atm.

I'm afraid no. Could you post the console output with the ticalcs error? I'll try to pester the TiLP developer if I can't spot anything obvious.

Another option: Try the latest TiLP beta, perhaps there were some relevant bugfixes. See here: https://www.cemetech.net/forum/viewtopi … 917#244917

Yes, sure, that's what I'm trying to explain in the last paragraph. I didn't use it in the example code, because I wanted to first show a general, all-purpose approach before talking about possible optimizations, and because the logic behind the accu-MSB -> sample pointer LSB method may not be so obvious for beginners without some additional explanation (at least it wasn't for me when I was told about it).

Part 10: Achieving Simple PCM with Wavetable Synthesis

The idea behind pulse-code modulated sound (PCM) is remarkably simple. A PCM waveform consists of a set of samples which describe the relative volume at a given time interval of constant length. (Note the terminology of "sample" in this context, which has nothing to with a sample as we know it from MOD/XM, for example). For playback, each sample is translated into a discrete voltage level, which is then amplified and ultimately sent to an output device, typically a loudspeaker. The samples are read and output sequentially at a constant rate until the end of the waveform has been reached.

When attempting this on a 1-bit device, we face the problem that we obviously can't output variable voltages. Instead we only have the choice between two levels, silence or "full blast". So how can we do it, then?

In order to understand how we can output PCM on a 1-bit device, let's first recap how Pulse Interleaving works. The underlying principle of P.I. is that we can keep the speaker cone in a floating state between full extension and contraction by changing the output state of our 1-bit port at a very fast rate, thanks to the inherent latency of the cone. So we're actually creating multiple volume levels. I'm sure you've realized by now that the same principle can be applied for PCM playback.

So, say we want to output a single PCM waveform at a constant pitch. All we need to do is interpret the volume levels described by the samples as the amount of time we need to keep our 1-bit port switched on. So we just create a loop of constant length, in which we

- read a sample
- switch the 1-bit port on for a the amount of time which corresponds to the sample volume
- switch the 1-bit port off for the remaining loop time
- check if we've reached the end of the waveform, and loop if we haven't.

That's all - on we go with the next sample, rinse and repeat until the entire waveform has been played.

Loop duration is a critical parameter here, of course. We can't make our loop too long, or else the "floating speaker state" trick won't work. It seems that
a loop time of around 1/15000 seconds is the absolute maximum, but ideally you should do it a bit faster than that.

With common PCM WAVs, we'll run into a problem at this point. An 8-bit PCM WAV has samples which can take 256 different volume levels, take the more popular 16-bit ones and you've already got 65536 levels. How are we supposed to control timing that precisely in our loop? 1/15000 seconds corresponds to around 233 cycles on the ZX Spectrum. The fastest output command - OUT (n),A - takes 11 cycles, which means we can squeeze at most 21 of those into the loop - and that's not taking into account all the tasks we need to perform besides outputting. So how do we output 256 or even 65536 levels? The answer is: We don't. Instead, we'll reduce the sample depth (that is, the number of possible volume levels) to a suitable level. This will obviously degrade sound quality, but hey, it's better than nothing.

As far as the Spectrum is concerned, 10 levels seems to be a convenient choice. You might be able to do more with clever code (or on a faster machine), but for the purpose of this tutorial, let's keep it at 10. That is, if we want to output just a single waveform. But of course we want to mix multiple waveforms at variable pitches, let's say two of them. In this case, our source PCM waveforms should have 5 volume levels.

As you might have already guessed, we'll need to develop our own PCM data format to encode these 5 levels. How this format will look like depends on your sound loop code as well as the device you're targetting - anything goes to make things as fast as possible. On the Spectrum, we may take two things into account:

- bit 4 sets the output state (let's ignore the details for now...)
- we have a fast command available for rotating the accumulator.

So, our samples bytes might look like this:

volume  binary    hex
level   76543210
______________________
  0%    00000000  #00
 25%    00010000  #10
 50%    00011000  #18
 75%    00011100  #1c
100%    00011110  #1e

This reasoning behind this may not be self-evident, but it'll become clear when we look at a possible sound loop.

Unfortunately, this custom PCM format still won't allow us to create a sound loop that is fast enough, so let's apply another restriction - use waveforms with a fixed length of 256 byte-sized samples. You'll see in a moment why this comes in handy.

Our sound loop might look like this:

  set up sample pointer channel 1                             ld bc,waveform1
  set base frequency ch1                                      ld de,noteval1
  clear add counter ch1                                       ld hl,0
                                                              exx
  set up sample pointer channel 2                             ld bc,waveform2
  set base frequency ch2                                      ld de,noteval2
  clear add counter ch2                                       ld hl,0
  set timer                                                   ld ix,0

loop:
  load channel 1 sample byte to accumulator                   ld a,(bc)
  output accu to beeper                                       out (#fe),a
  rotate left accumulator                                     rlca
  output accu to beeper                                       out (#fe),a
  rotate left accumulator                                     rlca
  output accu to beeper                                       out (#fe),a
  rotate left accumulator                                     rlca
  output accu to beeper                                       out (#fe),a
  add base frequency ch1 to counter ch1                       add hl,de 
    IF counter overflows, advance sample pointer ch1          adc a,0 \ add a,c \ ld c,a
                                                              exx
  load channel 2 sample byte to accumulator                   ld a,(bc)
  output accu to beeper                                       out (#fe),a
  rotate left accumulator                                     rlca
  output accu to beeper                                       out (#fe),a
  rotate left accumulator                                     rlca
  output accu to beeper                                       out (#fe),a
  rotate left accumulator                                     rlca
  output accu to beeper                                       out (#fe),a
  add base frequency ch2 to counter ch2                       add hl,de
    IF counter overflows, advance sample pointer ch2          adc a,0 \ add a,c \ ld c,a
    
  decrement timer and loop if not 0                           dec iy \ ld a,iyh \ or iyl \ jp nz,loop

Now you also see why limiting waveforms to 256 bytes is useful - this way, we can loop through them without ever having to reset the sample pointer, which of course saves time.

However, there's a whole array of problems with this code. First of all, it's still quite slow - 218 cycles. Secondly, you can see that the last output from each channel last significantly longer than the first 3. A bit of difference in length is actually not a big problem, but in this case, the last frame is 3 times longer - that's simply too much. Thirdly and most critically, I/O contention has not been taken care of (this mainly concerns the Speccy, of course).

If you've followed the discussion in this thread, you'll have noticed that I normally don't pay as much attention to I/O contention as other coders, but in this case, aligning the outputs to 8 t-state limits does make a huge difference. I'll let you figure this out on your own though. Check my wtfx code if you need further inspiration.

I will tell you one important trick for speeding up the sound loop though. Credits for this one go to sorchard from World of Dragon.

In the above sample, we're actually using 24 bit frequency resolution, since we're keeping track of the overflow from adding our 16-bit counters. But 16 bits are quite enough to generate a sufficiently accurate 7-8 octave scale. So in the above example, instead of doing "adc a,0 \ add a,c \ ld c,a" to update the sample counter, you could simply do "ld c,h", saving a whopping 22 cycles in total. The high byte of our add counter thus becomes the low byte of our sample pointer. The downside of this is that our waveforms need to be simple - e.g. just one iteration of a basic wave (triangle, saw, square, etc.). It's less of a problem than it sounds though, as you won't be creating really complex waveforms in 256 bytes anyway. And for a kick drum or noise, you can simply use a frequency value <256, making sure that you step through every sample in the waveform.

And that's all for now, hope you find the information useful, and as always, let me know if you find any errors or have any further suggestions/ideas.

787

(5 replies, posted in Sinclair)

Yes, sadly the number of 1tracker users remains low, even though gotoandplay's new Phaser3 track might suck a few new people into trying it out. It's a shame, I certainly prefer the pattern-less approach. And I understand it would be rather messy to include wtfx. The same for Beepola, of course. I think the sample part could be solved in 1tracker by displaying a 16x16 hex matrix (plus WAV import), but the fx table part is indeed tricky. Another, somewhat smaller issue might also be that certain instruments (noise, kicks) need an alternate frequency table. Anyway, I don't have any real hopes to see wtfx in a tracker anytime soon.

Nevertheless, I've got to figure out how I can write plugins for 1tracker. It looks not even that difficult, seems rather I'm having some technical issues. I can copy and rename existing engines and they'll work, but as soon as I change anything in the code 1tracker will not "see" them anymore.

Btw just realized that I wasted a few cycles in the note data loader, will update in the next days.

Edit: Done. Optimized the data loader, and also replaced the noise samples, which had the volume levels wrong.

788

(135 replies, posted in Sinclair)

Sure thing, here you go. May not be the most useful thing though as it's hardly commented.

If you want to learn the basics about keyboard reading and such, check out the wonderful "How to Write ZX Spectrum Games" by Jonathan Cauldwell: http://www.sendspace.com/file/alhxcq

789

(5 replies, posted in Sinclair)

Thanks wink Yeah, definately worth doing the alignments for this one. I'm amazed how much it reduces noise in this case.

I'm afraid it'll be a while till someone makes a proper track with this. At least till I can figure out how to make 1tracker plugins (still haven't gotten beyond the "1tracker-will-simply-ignore-whatever-I-throw-at-it" phase), or number9 integrates it into Beepola.

790

(5 replies, posted in Sinclair)

First up, some bad news: I'm releasing this engine as source-only, because some of it's major features can't be simulated via an XM template. Sorry 'bout that...

Anyway, wtfx (aka "wavetable player with effects") is my latest attempt at creating some decent-sounding triangle waves on the beeper. While not perfect, it's quite an improvement over qaop and the like, I think.

Features:
- 2 channel wavetable mixing, 256 byte wavetables
- 4 volume levels per sample
- 17.5 KHz mixing
- tick-based effects (can change wavetables/pitches without having to set a new note)
- per-step tempo control

download
browse source

Enjoy! And thanks to introspec for stressing the importance of 8t output alignment wink

791

(135 replies, posted in Sinclair)

In the worst case you could still use my rom2pwm craptility, though it's definately not very useful.
My website is down again so I'm attaching it just in case.

792

(135 replies, posted in Sinclair)

Doing this without display output would indeed save a lot of time. Why do you need to write it in C/Basic with inline asm though? I think just asm might be easier even if you don't know a lot of asm.

Yeah, a vid would be awesome!

@gotoandplay: I just read somewhere that on newer Windowses, installers that require direct hw access might need to be run via right-click-"run as administrator" even if you are already admin. Dunno if that helps in your case?

794

(135 replies, posted in Sinclair)

Yeah, I've been wanting to make something like this for a long time. However, it's no trivial project, I'm looking at at least 2-3 months of work here. I still know near to nothing about Spectrum graphics, so I'd need to learn a lot of new things. I'm afraid I won't get around to it in 2016.

Ok, fixed the mute indicator bug, and AutoInc is now off by default. But the thing I can't figure out is how you manage to crash the calc. I'm on F00, pushing buttons like a maniac, and the damn thing just won't die. How do you do it exactly? Could you by any chance record a video of you crashing the thing?

796

(22 replies, posted in Sinclair)

The xm2squeek package has been updated, it now enables you to set the duty cycle per pattern.

797

(65 replies, posted in Sinclair)

True that, seems like it's recorded from TV speakers.

Edit: Just polished up the yawp package a little, correcting some errors in the documentation and fixing a faulty sample in the xm template.

I'm glad you're still trying to push HT2 to the limits, otherwise these bugs might go undiscovered. And it's probably me who is to blame for the RAM clears yikes Thanks for reporting in any case, I'll look into these. I'll also consider deactivating autoinc by default, since you're not the first to ask wink

799

(65 replies, posted in Sinclair)

Add 2 nops immediately after every out (#af),a instruction.

Not a wine user (my PC is too old/slow, and I consider it a security risk), so I'll have to try dcvg5k on my win xp box after all hmm Judging from the recording the guy put on soundcloud it sounds good ok though, except that the noise drums seem a bit quiet.

800

(65 replies, posted in Sinclair)

Glad you got it working!

Could you ask Daniel if he would be so kind to release the sources for the new version of dcvg5k, so I can build it on linux? I failed to build the old version btw, because ld can't find the DAsm function in dasmz80.c for some reason.

object/dcvg5kdesass.o: In function `Displaydesass':
dcvg5kdesass.c:(.text+0x26f): undefined reference to `DAsm'
collect2: error: ld returned 1 exit status
makefile:9: recipe for target 'dcvg5k' failed

Btw, on forum.system-cfg.com you advised to remove the "exit" section. This isn't such a good idea, because it will cause a stack imbalance if the routine actually exits (eg. if loop is disabled [jump in line 47 enabled] or if you put the keyboard check back in). A better way would be to remove lines 28-29 and 460-461 - these are only necessary because ZX Spectrum BASIC expects a certain value in HL'. You probably don't need to do this on VG5000.