601

(5 replies, posted in Sinclair)

MDAL config available now in the MDAL repo wink Redownload the engine, too, if you want to use "b" instead of "h" as a note name.

Oh, that turned out really nice! Too bad Soundcloud made such a mess of the render. I'm still trying to figure out how that happens, sometimes their rendering is fine and sometimes they botch the tracks completely.

603

(20 replies, posted in Sinclair)

Updated BetaPhase on github, and also updated the MDAL config accordingly.
If you want to keep using the old version, just rename the old BetaPhase.cfg to something like BetaPhaseOld.cfg, and use that name for the CONFIG= command. Generally speaking though, MDAL modules made for BetaPhase 0.1 should be mostly compatible with the new 0.2, except for the following:
- DMOD is now called DMOD1 (because there's also a DMOD2 command now)
- PSCA2/PSCB2 are now pre-scalers

A special note about the new NOISE command - the current implementation is a bit awkward. Basically, when you enable NOISE, you must also set PSCA1=0 and PSCA2=0. Likewise, when disabling noise, you should set PSCA1/2 to a value of your choice. You might as well ignore the noise command and just set PSCA1=$cb and PSCA2=2. The problem exists because I didn't forsee this particular use case where an internal masking command (SET_IF) should overwrite an existing value rather than ORing to it. I'll need to implement another config flag to avoid this mess but this might take a while.

604

(20 replies, posted in Sinclair)

Little sneak preview of the next version on BetaPhase attached below.

Changes:
+ Ch1/2 post-scalers are now pre-scalers
- Old (slow) Ch1 duty mod removed
+ New Ch1 duty mod, can be heard in the intro (using osc sync method - runs a bit too fast for my taste, so it's not terribly useful in any case)
+ Ch1 noise support
+ Ch2 duty mod w/ custom sweep speed, can be heard in the interlude (using Shiru's implementation, but with a variable counter increase every 256 loops instead of constant increase every loop)

605

(20 replies, posted in Sinclair)

Ok, noted the problem with the comments in sequence. The current pattern parser needs some reworking anyway, so I'll fix this problem when I get around to that. I'll think about the comma thing too, it'll require some extra code but can't hurt to make the parser more robust in this respect.

Since you were wondering, I'll try to give a better explanation of what the effect settings do.

The basic thing you have to understand is that the first two channels each use two semi-independant oscillators. Semi-independant because they share the same frequency divider ("A" resp. "B"). The output of the two oscillators is combined using the MIXA resp. MIXB method. This works the same way as in Shiru's Phaser engines (namely Phaser2/3, Phaser1 has only XOR mixing).

Before the mixing, a pre-scaler is applied to the first oscillator (PSCA1/PSCB1). This prescaler will shift output from the first osc up or down by one octave. After mixing, a post-scaler is applied to the combined output (PSCA2/PSCB2), which again shifts one octave up or down. This feature will probably be removed in favour of a second pre-scaler in the next version, because it's not really necessary (I was originally afraid that tone range would be insufficient, but that seems to be not the case).

On channel 1, there's also the duty modulator (DMOD), which should theoretically do what the "Duty Sweep" effect in HT2 ch2 does. However, the implementation is so slow that the effect is barely noticable - I'll probably replace it with something better in the next version as well.

Last but not least, there's the phase offset (PHA/PHB). The value you enter here is used to initialize the second oscillator before the sound loop (the first osc is always initialized to 0). If you don't use the pre-scaler and set MIXx=xor, this will essentially act as a duty setting (4xx/5xx/6xx in HT2). Setting PHx to $1000 will give a 50:50 square wave, $800 would give 25:75. There's no point in using values >$1000 unless you have PSCx2 set to "down" - in this case, $2000 will give 50:50 duty. Likewise, if PSCx2 is set to "up", $800 will be the max.

Channel 3 on the other hand has only one oscillator. PSCC is a post-scaler. Here, post-scaling makes sense because it has an effect on slide speed.

Hope that makes sense wink

606

(20 replies, posted in Sinclair)

Thanks for your input mate, always appreciated!

garvalf wrote:

A little feedback.

First,
I think the parser could be less strict, for example you can't write:

A=a3, C=d2,

the last comma prevents to compile the song.
As long as there is no data except space, tab or line break, it could consider the comma doesn't exist.

I'll have to think about that. It would require some rather big changes to the compiler atm.

garvalf wrote:

I'd like to see a comment mark (for example to comment a part so it won't play during the rendering)

Single line comment is // wink
I'll put block comments on the to-do list.


garvalf wrote:

I like the idea of columns for entering notes. I has an interesting effect, you don't have to enter the value of some notes, like that:

You can totally do that, mdalc doesn't care about formatting except for line breaks.

garvalf wrote:

I think in addition to the current behaviour, it might worth adding, if possible, independent patterns, it means a pattern could contain only one voice, then you would mix the patterns together for different variations. The current behaviour is like on milkytracker, beepola or vortex tracker, I was thinking to something like on sidwizard or goattracker.

I definately plan to implement this, as there are some beeper engines that actually require data in this format (zx16, for example). However, it'll be a while before I get around to this. First, I want to finish preliminary documentation, clean up the code, and implement tables.

garvalf wrote:

(edit) thanks for the brackets addition, I'll have a look at it. Also for the value, what is correct, PHA=$800 or PHA=800, or is it the same?

With $ prefix, it's a hex number wink so PHA=$800 == PHA=2048

garvalf wrote:

I don't understand all the pattern commands effects, but I'll have a try.
In the betaphase.txt it's written
MIXB            Set mixing mode channel 1

I guess it's:
MIXB            Set mixing mode channel 2

Ah yes, of course. Well spotted wink

607

(20 replies, posted in Sinclair)

Nice little tune wink And glad to see that MDAL doesn't break immediately when other people use it big_smile

In other news, structuring with brackets in patterns is now possible. Just don't skip any of the commas, like I wrote above.
Also I believe I know where that glitch is coming from. Slides in BetaPhase don't stop when they reach the limit. So since the speed is falling back to $10 at the beginning of ptn1, the slide wraps to $ffxx and that's what's causing the glitch. Can't do much about that yikes

608

(20 replies, posted in Sinclair)

garvalf wrote:

ah yes, German notation... I was wondering.
Is it possible to use flat as well?

Yes. It's a matter of adjusting the equates.h file, MDAL itself doesn't care what you feed it (for now, at least).

garvalf wrote:

Just saying, in abc flat is _ before the note, ^ is for sharp and = for natural (but not applicable in mdal). So ^f2 would be f2 sharp and _g2 would be g2 flat.

Any special characters are likely to cause trouble with assemblers, unfortunately. For pasmo, ^ would be interpreted as XOR, and _xxx is a local label. For this reason, MDAL doesn't accept special chars in strings atm (it shouldn't, at least, not sure if I actually implemented that check already). On the long run, I could possibly work out a standardized pitch notation in MDAL, but for the time being it's not really feasible.

garvalf wrote:

I think you made something great with MDAL. It's perfectly suitable for this kind of music. The only criticism I could say is I find it less readable to get all the info on the same level for example:

A=a1, MIXA=xor, PHA=$800, PSCA1=down, C=c4, PSCC=down, SLIDESPD=$30, SPD=6

It might be more readable like this, with options between parenthesis:

A=a1 (MIXA=xor, PHA=$800, PSCA1=down), C=c4 (PSCC=down, SLIDESPD=$30, SPD=6)

(but maybe it's just me)

Good idea, and it should be fairly simple to add. Perhaps I'll do
A=a1, (MIXA=xor, PHA=$800, PSCA1=down), C=c4, (PSCC=down, SLIDESPD=$30, SPD=6)
though, then I can just simply strip out the brackets when parsing (and usage of the brackets would be optional).

garvalf wrote:

is the speed persistent across the patterns? For ptn1 if I remove SPD=12 then I get a little glitch (high pitch tone). If I set SPD=10 (default?), it's even different.
ok I understand, SPD=16 is default (and I get the sound glitch as well)
SPD=16 is 0x10

Probably a bug in the compiler. I'll have a look in the coming days. In theory, all values should fall back to their defaults at the beginning of a pattern ($10 for speed in this case). Btw I pushed a new commit a couple of hours ago where I changed some things related to this mechanism, do you have that already? (Simple check: if /config/BetaPhase.cfg has an INIT_DEFAULTS command in the CFG_PATTERNS block, you've got the latest version.

609

(20 replies, posted in Sinclair)

Ah, yes, I made a last minute change and probably forgot to update main.asm in the repo. Thanks for reporting, will fix asap. Edit: done.

The "-is" suffix is indeed for sharps, forgot to mention that as well. (German notation, hehe) Using an actual # char would break pasmo, so we'll have to live with "-is" I guess. Originally I had "h" instead of "b" as well yikes

Anyway, thanks for the cheers wink

Btw fancy website for MDAL: https://utz82.github.io/MDAL
Not much content in the wiki yet, will be filling that in in the coming days.

610

(20 replies, posted in Sinclair)

Good news everyone, BetaPhase is the first engine to get Music Data Abstraction Language (MDAL) support! (If you missed the discussion about MDAL, read up on it here.

I haven't written much documentation yet, but usage should be pretty straightforward. Check out docs/betaphase.txt and examples/betaphase-demo.mdal. Some general notes:

- Unlike standard music markup languages like MML or ABC, the notation in MDAL is row-based, just like you would do it in a tracker.
- If you don't want to change anything in a given row, you can use the dot command (.) to specify just that.
- Pattern names must be lowercase.
- Specifying a loop point in the sequence is optional (will be set to sequence start if not specified).

MDAL is still in very early beta stage, I'm expecting it'll have tons of bugs at this stage. Let me know if you find any.
Most notably, there are two problems at the moment:
1) Mixing of strings and bit flags is not yet supported. In case of BetaPhase, that means that upward slides cannot be used in conjunction with note names, so you'd have to supply a note divider instead (see pattern1b in betaphase-demo.mdal)
2) There's no support for global constants yet, so MDAL will fall back to internal defaults in some cases (for example, speed is reset at the beginning of every pattern)

source:

git clone https://github.com/utz82/MDAL.git

win32 build


In the meantime, I've found a way to generate noise in BetaPhase, but I haven't updated the engine yet. Stay tuned. Meanwhile, a new, crappy demo tune, produced by the MDAL converter:

Yippee, FloppusMaximus has fixed tilem2! Latest version is available via

svn checkout https://tilem.svn.sourceforge.net/svnroot/tilem/trunk tilem

Edit: win32 version: http://tilem.sourceforge.net/beta/tilem … 161022.exe

Hehehe, yes.
Anyway, let's continue this discussion here, if you like.

613

(20 replies, posted in Sinclair)

In continuation of this discussion, I wrote a new experimental engine based on Shiru's concepts. It's called BetaPhase because, uh, it's unfinished and probably will be in eternal beta status. It's intended more as a test-case rather than something you'd actually use for music production.

The engine may not sound all that impressive, it's more about the magic that's happening code-wise. Anyway, on the front end we've got 2 "simplified" Phaser channels, and one regular square wave channel with fixed 50% duty that can do slides. Phase offset is used to change the duty of the Phaser channels.

Special code features used:
- no use of duty threshold comparison trick (which was my self-set challenge for this engine), phase determines duty
- fast counter updates with 13-bit frequency resolution (bit 12 of the counter determines output state)
- and a little addition to the code discussed earlier: scalers. (zilogat0r proposed those a while back, but I think that's not quite what he meant big_smile)

   add hl,bc
   ex de,hl
   nop            ;swap with adc hl,bc for duty modulation
   add hl,bc
   ex de,hl
   ld a,h
   nop          ;pre-scaler, modified with rlca/rrca
   xor d
   nop          ;post-scaler, as above
   out (#fe),a

The prescaler can be heard on the bassline in test.tap, and briefly on the arpeggios as well. The post-scaler actually seems unnecessary, 12-bit frequency resolution is sufficient to reach low notes without detuning too much. So it might be more interesting to have two pre-scalers instead.
Also, duty modulation with adc hl,bc is very ineffective, so I might consider removing it. However, modifying it with inc bc is too fast and sounds unpleasant. Still need to find a solution for this. Also, some way to create noise would be nice. So, as I said, the engine is currently quite unfinished.

Anyway, source for the engine is here, feel free to play around with it.

Edit: Counting bits... it's hard, lol. Of course bit 12 of the counter determines the output state, not bit 11. Hence, we've actually got 13-bit frequency resolution. Better than AY wink

Cheers garvalf, take your time. I'll probably be working some more on ht2 in the coming days, so there'll be more crap betas wink
The 5xx/7xx bug turned out to be a phantom bug, caused by faulty z80 emulation. Everything works as intended on hardware, and also the new pitch slides work indeed much better (slower) than tilem2 suggests.

   ld hl,0
   ld de,#8000
   ld bc,#7
    
loop
   add hl,bc
   ex de,hl
   adc hl,bc    ;nice switch for toggling duty mod on/off
   ex de,hl
   ld a,h
   xor d
   out (#fe),a
   jr loop

19 byte 48K Spectrum intro xD

Hmm... so it basically does the "SID sound" thing, but at a different rate. Very interesting idea, looks unassuming but has a lot of potential.
Would be an interesting challenge to write a pulse interleaving engine that doesn't use the "compare counter hi-byte against duty threshold" method.

- 9xx is back (sounds slightly different and no longer disables 3xx)
- 6xx has a new additional parameter: enable ch3 grind effect when xx > 0x80
- Axx is now ch3 phase offset (can be used as primitive volume control in conjunction with a second channel playing the same note)
- old A0x is removed (A01 was redundant anyway since it can be set via 4xx, A02 might come back later)
- note tables are tuned correctly again
- channel volumes are rebalanced

EDIT: Seems I've broken the 5xx duty sweep and 7xx effects somehow. Stay tuned for updates.

Reworked the ch3 pitch slides today. The new systems runs slower (updates synced to main osc), so slides should be slightly more useful now. It doesn't make a huge difference, but it was quite a piece of work. Didn't think I would ever make such drastic changes to the player code at this point, but here we are. Some new possibilities have opened up as well, not sure yet what I'm going to do with all those free t-states that suddenly appeared tongue

Anyway, for the time being, the note table is out of tune again, sorry. Also, 9xx is disabled for the time being, and channel volume balance is pretty awful right now.

If anyone is feeling adventurous, there's a new beta on github.
New stuff in this version:

- Note table is now tuned to 440 Hz. (This means note A is no longer suited for ch1 noise mode, but there are a couple of other viable options instead.)
- Duty Sweep (5xx, xx > 0x80) is now configurable, ie. xx acts as a speed parameter (581 to get the old behaviour)
- Auto Chord (7xx) now has a "sync" mode (set xx > 0x80), enhances octave harmonics
- a minor drawback, 5xx and 7xx now share a common parameter, meaning they will impact each other in some circumstances

Beware that since this is a beta build, stuff may change without notice, and there's no ht2util support.

Good point. I still haven't found a useful application for this, though.
What I came up with some time ago was this:

   ld bc,DIVIDER     ;DIVIDER < #1000
   ld hl,0
   ld de,!0
loop
   add hl,bc
   ex de,hl
   add hl,bc
   ex de,hl
   ld a,h
   xor d
   out (#fe),a
   ...

It's obviously pretty fast, and 12-bit resolution more or less does the job. But as such, it's still seems quite a waste. I wonder if something more useful can be derived from it.

Part 11: Sound Tricks - Noise, Phasing, SID Sound, Earth Shaker, Duty Modulation

Digital/PCM sound is a powerful and flexible tool, but unfortunately it tends to consume a lot of RAM. So in this part of the tutorial, let's go back to the good old Pulse Interleaving technique, and talk about various tricks that can be used to spice up its sound.


Noise

Historically speaking, 1-bit routines have always been lacking in terms of percussive effects. However, converting a tone generator into a simple noise generator is surprisingly simple, and costs a mere 8 cycles on Z80-based systems. Consider the usual way we generate square wave tones:

   add hl,de           ;add base frequency divider (DE) to channel accumulator (HL)
   ld a,h              ;grab hi-byte of channel accumulator
   cp DUTY             ;compare against duty threshold value
   sbc a,a             ;set A to 0 or 0xFF depending on result
   out (#fe),a         ;output A to beeper port

Now, in order to generate noise instead of tones, one would obviously need to randomize the value held by the channel accumulator. But pseudo-random number generators are slow, so how do we do it? The answer is to simply reduce the quality of the PRNG as much as possible. Believe it or not, adding a single

   rlc h

after the add hl,de operation will provide enough entropy to create a convincing illusion of white noise. But it will do so only if it is fed a suitable
frequency divider as a seed. I usually use a fixed value of 0x2174 in my routines. Other values are possible of course, and may give slightly different results, though most values will just generate nasty glitch sounds instead of noise.

There's a nice side effect that you get for free - you can create the illusion of controlling the volume of the noise by changing the duty threshold. Changing the pitch of the noise however is much more difficult, and requires the use of additional counters. I'm still looking for an efficient way of doing it, if you have any ideas please let me know.

Last note, you can of course also rotate the hi-byte of frequency divider instead of the accu. The result of that however is almost guaranteed to be a glitch sound rather than noise.


Phasing

The Phasing technique was developed by Shiru, and is used to generate the signature sound of his Phaser1-3 engines. It comes at a rather heavy cost in terms of cycle count and register usage, but it's power and flexibility undoubtedly outweigh those drawbacks.

For regular tone generation, we use a single oscillator to generate the square wave (represented by the add hl,de operation). The main idea of Phasing, on the other hand, is to use two oscillators, and mix their outputs into a single signal. The mixing can be done via a binary XOR of the two oscillator outputs (the method used in Phaser1), or via a binary OR or AND (added in Phaser2/3).

   add hl,de           ;OSC 1: add base freq divider (DE) to channel accu (HL) as usual
   ld a,h              ;grab hi-byte of channel accumulator
   cp DUTY1            ;compare against duty threshold value
   sbc a,a             ;set A to 0 or 0xFF depending on result
   ld b,a              ;preserve result in B
   
   exx                 ;shadow register set, yay
   add hl,de           ;OSC 2: exactly the same operation as above
   ld a,h              ;grab hi-byte of channel accumulator
   cp DUTY2            ;compare against duty threshold value
   sbc a,a             ;set A to 0 or 0xFF depending on result
   exx                 ;back to primary register set
   
   xor b               ;combine output of OSC 1 and 2. (xor|or|and)
   out (#fe),a         ;output A to beeper port

As you can see, this method offers a wide range of parameters that affect timbre. The most important one, from which the technique derives its name, is the phase offset between the two oscillators. To make use of this feature, simply initialize the OSC1 accu to another value than the initial value of the OSC2 accu, eg. initialize HL to 0 and HL' to a non-zero value. Especially in conjunction with a slight offset between the OSC1 and OSC2 base dividers, some surprisingly complex timbres can be produced.

Side note: By using a binary OR to mix the signal and keeping the duty thresholds down to a reasonable level, the two oscillators can be used as independant tone generators. This method is used to mix channels in Squeeker and derived engines.


SID Sound

This effect, which derives its name from the key sound that can be heard in many of the early SID tunes, is formed by a simple duty cycle sweep. The velocity of the sweep is in sync with the frequency of the tone generator. Basically, every time the channel accumulator overflows, the duty threshold is increased or decreased. As with noise, this is trivial to pull off and costs only a few cycles. Using the standard tone generation procedure, we can implement it as follows

   add hl,de           ;add base frequency divider (DE) to channel accumulator (HL)
   sbc a,a             ;set A to 0 or 0xFF depending on result
   add a,c             ;add duty threshold (C)
   ld c,a              ;update duty threshold value (C = C - 1 if add hl,de carried)
   cp h                ;compare duty threshold value against hi-byte of channel accu
   sbc a,a             ;set A to 0 or 0xFF depending on result
   out (#fe),a         ;output A

As you can see, this operation costs a mere 4 cycles compared to the standard procedure without duty cycle sweep.


Earth Shaker

This effect is named after the game Earth Shaker, which used a rather unusual sound routine with two semi-independant tone channels, written by Michael Batty. As an actual method of generating multi-channel sound, it is of limited practicality, but it can be applied as an effect to regular Pulse Interleaving at a minimal cost. The core concept here is to continually modulate the duty threshold within the sound loop. Depending on the ratio of the duty cycle change vs the oscillator speed, the result can be a nice chord, phatness, or - in most cases - gruesome disharmony that will strike fear in the hearts of even the most accustomed 1-bit connaisseurs. A simple implementation, as used in HoustonTracker 2 for example, looks like this:

   add hl,de           ;add base frequency divider (DE) to channel accumulator (HL)
   ld a,c              ;load duty threshold (C)
   add a,DUTY_MOD      ;add duty threshold modifier
   ld c,a              ;store new duty threshold
   cp h                ;compare duty threshold value against hi-byte of channel accu
   sbc a,a             ;set A to 0 or 0xFF depending on result
   out (#fe),a         ;output A to beeper port

Duty Modulation

The aforementioned SID sound and Earth Shaker effects are actually basic implementations of a family of effects that may best be described as "Duty Modulation". As a first step into the world of Duty Modulation, let's take the Earth Shaker effect and modify it to change the duty threshold in sync with the main oscillator.

   add hl,de           ;add base frequency divider (DE) to channel accumulator (HL)
   sbc a,a             ;set A to 0 or 0xFF depending on result
   and DUTY_MOD        ;set A to 0 or DUTY_MOD
   xor c               ;XOR with current duty threshold (C)
   ld c,a              ;store new duty threshold
   cp h                ;compare duty threshold value against hi-byte of channel accu
   sbc a,a             ;set A to 0 or 0xFF depending on result
   out (#fe),a         ;output A to beeper port

By syncing the modulation in this way, the nasty glitches of the Earth Shaker effect can be avoided entirely (but also, no chords will be produced). Instead, we can now control harmonic components that share an octave relation with the base note. In other words, we can amplify over- and undertones at will, as long as they are a multiple of 12 half-tones away from the main note.


Things can be pushed even further by decoupling the sync and using a second oscillator to time the duty threshold updates.

   exx
   add hl,de           ;independant oscillator for timed duty threshold updates
   exx
   sbc a,a             ;set A to 0 or 0xFF depending on result
   and DUTY_MOD        ;set A to 0 or DUTY_MOD
   xor c               ;XOR with current duty threshold (C)
   ld c,a              ;store new duty threshold
   
   add hl,de           ;add base frequency divider (DE) to channel accumulator (HL)
   cp h                ;compare duty threshold value against hi-byte of channel accu
   sbc a,a             ;set A to 0 or 0xFF depending on result
   out (#fe),a         ;output A to beeper port

This way, we can create the octave effects from the previous example (by setting the "duty" oscillator to the same value as the main tone oscillator), as well as Earth Shaker style chords, while also gaining better control over the latter. Additionally, some interesting slow-running timbre changes can be achieved by setting the duty oscillator to a frequency near (but not equal to) the main oscillator.

The usefulness of this approach might seem a bit questionable considering the hefty cost in CPU cycles and register usage. However, the required code is almost the same as the one used for the Phasing technique, so with a tiny amount of self-modifying code, it can be implemented in a Phaser style engine at virtually no extra cost.

There's also an added bonus when combining this technique with the noise generator explained above. By setting the duty threshold to the same value as the duty modifier, the duty oscillator can be used as a tone generator, meaning you can actually mix noise and tone on the same channel!

That's all for this time. If you know of any other cool tricks please post them here!

The master of Savage engine strikes again! Thanks for digging this up, Tufty.

xxl, demo itself appears to be one-file, so it should be possible to rip .bin from emulator.

623

(5 replies, posted in Sinclair)

Cheers wink The demo doesn't really show the flexibility of this thing, I just picked one setting for the phaser channel that I really liked, and ch1 is running in standard SID mode. But actually, you can do all kinds of weird shit with it big_smile Hmm, I wonder how it would sound with some filters and volume envelopes on top... well, another time, maybe.

624

(5 replies, posted in Sinclair)

Surprise! Was a bit bored today...

PhaserX is an experimental 2-channel beeper engine for the ZX Spectrum. It uses
a new type of effect called "duty modulation". As the name suggests, this effect
modulates the duty cycle setting over time. Its operating speed is synced to the
affected tone generator, so despite the similarities with the tone generation in
engines like Earth Shaker, it does not affect pitch.

Each of the two tone channels has a different set of effects. Channel 1 produces
a simple square wave. In addition to the aforementioned duty modulation, it
features a SID-like duty cycle sweep effect, and can generate pseudo-white noise
instead of square waves.

Channel 2 uses two oscillators to produce Phaser-style sound. Each of the 
oscillators can have it's own duty modulation settings. This allows for very
complex timbres. Oscillator frequencies can of course be configured 
independantly, and can run at different phases. The channel mixer supports XOR
(Phaser1 standard), OR, and AND mixing methods. When using the OR method, the
oscillators can be decoupled and used as 2 independant channels, Squeeker style.

Credits go to Shiru for inventing the original Phaser effect.

No XM converter obviously, and no mp3 render either for the time being.
Source is on my github, as usual.

I also made a 3-channel core with this effect (Tritone style, no phaser fx), but I don't have time/motivation to finish it atm. Might try to squeeze a few more cycles from that one so I can add it to beepertoy. Or maybe I'll add it to this engine, got an unused flag bit which could be used for switching cores on the fly. Not sure yet.

625

(21 replies, posted in Sinclair)

A quick update on this: Just got the parser to output it's first blob of correct asm data. Unfortunately today is the last day I can work on it for the forseeable future, but I'm glad I got this far. There's still tons of functionality missing, and the code would probably give any self-respecting programmer a heart attack at this point. So long story short, there probably won't be any public release till beginning of 2017.

In the meantime, here's a little preview, to give you an idea how the thing works (it has a name now, too, btw - MDAL aka Music Data Abstraction Language).

The input music file looks like this:

CONFIG=PhaseSqueek

:SEQUENCE
    intro
    pattern0
    [loop]
    pattern0
    pattern2
    
:intro
    A=a1, B=c3, C=e3, GMIX=or, MIXAB=or, MIXCD=or, SPD=$8
    .
    A=a2
    .
    A=a3
    .
    A=a2, C=0
    .

:pattern0
    .

:pattern2
    .

which gives us this:

    dw intro
    dw pattern0
loop
    dw pattern0
    dw pattern2
    dw 0

intro

    dw #b000, fx0, #b000, a1, c3, #4040, #c6, #c6, #0, #b000, e3, #0, #4040, #0, #800
    dw #b085, #800
    dw #b001, #b0c4, a2, #0, #800
    dw #b085, #800
    dw #b001, #b0c4, a3, #0, #800
    dw #b085, #800
    dw #b001, #b0c4, a2, #0, #b0c0, #0, #0, #800
    dw #b085, #800
    db #40

pattern0

    dw #b085, #800
    db #40

pattern2

    dw #b085, #800
    db #40

based on this config:

MDAL_VERSION(0)
USE_SEQUENCE;
USE_PATTERNS;

WORD_DIRECTIVE("dw");
BYTE_DIRECTIVE("db");
HEX_PREFIX("#");


CFG_SEQUENCE {
    USE_END("dw 0");
    USE_LOOP(LABEL, "loop");
    USE_TRACKS(1);
}

CFG_PATTERNS {
    USE_END("db #40");
}
    
CFG_COMMANDS {
                
    WORD("FX","fx0");    
    WORD("A", 0, USE_LAST_SET|USE_RESTS(0));
    WORD("B", 0, USE_LAST_SET|USE_RESTS(0));
    BYTE("DA", $40, USE_LAST_SET);    
    BYTE("DB", $40, USE_LAST_SET);    
    BYTE("SIDA", $c6, USE_LAST_SET|FORCE_SUBSTITUTION("off"=$c6, "on"=$ce));
    BYTE("SIDB", $c6, USE_LAST_SET|FORCE_SUBSTITUTION("off "=$c6, "on"=$ce));
    BYTE("ESA", 0, USE_LAST_SET);
    BYTE("ESB", 0, USE_LAST_SET);
    WORD("PAB", 0, USE_LAST_SET);
    WORD("C", 0, USE_LAST_SET|USE_RESTS(0));
    WORD("D", 0, USE_LAST_SET|USE_RESTS(0));
    BYTE("DC", $40, USE_LAST_SET);
    BYTE("DD", $40, USE_LAST_SET);
    BOOL("NC", false, USE_LAST_SET|FORCE_SUBSTITUTION("off"=false, "on"=true));
    WORD("PCD", 0, USE_LAST_SET);
    
    BYTE("DRUM", 0, FORCE_SUBSTITUTION("kick"=$40, "hat"=$80));
    BYTE("GMIX", $b0, FORCE_REPEAT|FORCE_SUBSTITUTION("or"=$b0, "xor"=$a8, "and"=$a0));
    BYTE("MIXAB", $b0, USE_LAST_SET|FORCE_SUBSTITUTION("or"=$b0, "xor"=$a8, "and"=$a0));
    BYTE("MIXCD", $b0, USE_LAST_SET|FORCE_SUBSTITUTION("or"=$b0, "xor"=$a8, "and"=$a0));
    BYTE("GSPD", $10, GLOBAL_CONST);
    BYTE("SPD", $10, FORCE_REPEAT);
}
    
    
CFG_FIELDS {
    WORD(0, REQUIRED(), SET_IF(!(FX&A&B&DA&DB&SIDA&SIDB&ESA&ESB&PAB&C&D&DC&DD&NC&PCD&MIXAB&MIXCD), 4), SET_IF(!FX, 1), SET_IF(!(A&B&DA&DB&SIDA&SIDB&ESA&ESB&PAB&MIXAB), $80), SET_HI(GMIX));
    WORD("fx0", SET(FX));
    WORD(0, REQUIRED(A|B|DA|DB|SIDA|SIDB|ESA|ESB|PAB|MIXAB), SET_IF(!(A&B), 1), SET_IF(!(SIDA&SIDB&ESA&ESB), 4), SET_IF(!DA&DB, $40), SET_IF(!PAB, $80), SET_HI(MIXAB));
    WORD(1, REQUIRED(B), SET(A));
    WORD(2, REQUIRED(A), SET(B));
    WORD($4040, SET_HI(DA), SET_LO(DB));
    WORD($00c6, SET_HI(ESA), SET_LO(SIDA));
    WORD($00c6, SET_HI(ESB), SET_LO(SIDB));
    WORD(3, SET(PAB));
    WORD(4, REQUIRED(C|D|DC|DD|NC|PCD|MIXCD), SET_IF(!(C&D), 1), SET_IF(!(DC&DD), $40), SET_IF(!PCD, $80), SET_HI(MIXCD));
    WORD(5, REQUIRED(D), SET(C));
    WORD(6, REQUIRED(C), SET(D));
    WORD($4040, SET_HI(DC), SET_LO(DD));
    WORD(7, SET(PCD));    
    WORD($1000, REQUIRED(), SET_HI(SPD), SET_LO(DRUM));

    REQUIRE_SEQ_BEGIN();    
}