Azasel music engine
-
- Posts: 88
- Joined: Mon Jun 23, 2008 1:58 pm
Azasel music engine
Ok, here's the isolated music/sound engine based on the work with Air Zonk engine. The sample format has been changed to uncompressed version. Instead of RLE compression for size, the samples now have range compression. In Azasel, the channel volume is treated as internal (similar to TL on FM chips) for the volume envelope system. Channel volume by the musician instead is routed through pan volume. The samples take advantage of this by taking the upper 3bits of the 8bit sample and subtracting that from TL (0x1F) for range compression. This means older samples used by PCE games and such play fine as long as they don't have the top 3 bits set. Also, there's is no sample length - a termination value of 0x2E is used instead (unless cut short by the length argument).
Anyway, the engine itself is now separate module in which (hopefully) you can incorporate into your own code/design. For now, there is no mml ascii converter or tracker. All songs must be composed in a pseudo MML structure using data defines. It's not so bad actually. I've include 3 examples songs and equates to facilitate the process. I've included the channel waveforms from Air Zonk which are located in the .wf file. Air_Zonk_WF_view.pce is a waveform viewer so you can see what the waveforms look like. I've only included up to 38 different waveforms. There's also a document describing the functions of this engine - Az_mml.txt. The document is meant for an up and coming MML ascii converter, but you can still use the information about the functions and FX (envelopes, vibrato, portamento,etc). MML.equ holds the additional info you'll need.
I've included a batch file and PCEAS just in case.
azasel.asm - the sound/music engine
mml.asm - and interface to Azasel engine. Provided gamepad support and onscreen display. Also contains the song file includes.
Things to note about the data define format:
".loop" = using a "." tells the assembler that it's a local label. Local labels exist inbetween regular labels. With local labels, you can re-use the label one.
"label:" = a regular label is defined with a ":". You can use these if you want, but you can't re-use the name once you declared it.
".db" = define byte. It defines a byte in a series/string of data. Use the "," to separate bytes. Some commands are WORD size, they must be stored as LSB,MSB format
"low()/high()" = low(some label or value) returns the LSB of a WORD data types. High returns the MSB. You only need this for jump style commands and loading a waveform.
"$" = if you want to use a hex number instead of a decimal number, prefix the value with a "$". You probably want to do this for negative numbers, especially WORD sized negative numbers.
The names of functions in the song examples are equates. They equate to a value. You can add/subtract/multiply/divide/use logic operations, etc on any value or function. The only function that you need to modify though, is the "rest" function. You add the rest period value to the function itself (it's an embedded operand in the command).
Also, PCEAS doesn't seem to like long lines of equates. I've broken my examples up into lines of less than 94 characters long. You'll get a syntax error on that line of it's too long. You don't need to end a line with a ",". I think that about covers it.
Oh, one more thing. Notes. PCEAS doesn't like "#" in the equate system, so all sharps use an "x". A normal note is "C2." and a sharp is "C2x". The middle number signifies the octave.
Oh, one more more thing. A command string ends with either: a "note" or a "rest" function. If you jump in a loop that has neither, the engine will lock in an infinite loop and freeze playback.
Edit: new url
Ver 1.0 - alexandria66.2mhost.com/~pcengine/music/Azasel.zip
.
Anyway, the engine itself is now separate module in which (hopefully) you can incorporate into your own code/design. For now, there is no mml ascii converter or tracker. All songs must be composed in a pseudo MML structure using data defines. It's not so bad actually. I've include 3 examples songs and equates to facilitate the process. I've included the channel waveforms from Air Zonk which are located in the .wf file. Air_Zonk_WF_view.pce is a waveform viewer so you can see what the waveforms look like. I've only included up to 38 different waveforms. There's also a document describing the functions of this engine - Az_mml.txt. The document is meant for an up and coming MML ascii converter, but you can still use the information about the functions and FX (envelopes, vibrato, portamento,etc). MML.equ holds the additional info you'll need.
I've included a batch file and PCEAS just in case.
azasel.asm - the sound/music engine
mml.asm - and interface to Azasel engine. Provided gamepad support and onscreen display. Also contains the song file includes.
Things to note about the data define format:
".loop" = using a "." tells the assembler that it's a local label. Local labels exist inbetween regular labels. With local labels, you can re-use the label one.
"label:" = a regular label is defined with a ":". You can use these if you want, but you can't re-use the name once you declared it.
".db" = define byte. It defines a byte in a series/string of data. Use the "," to separate bytes. Some commands are WORD size, they must be stored as LSB,MSB format
"low()/high()" = low(some label or value) returns the LSB of a WORD data types. High returns the MSB. You only need this for jump style commands and loading a waveform.
"$" = if you want to use a hex number instead of a decimal number, prefix the value with a "$". You probably want to do this for negative numbers, especially WORD sized negative numbers.
The names of functions in the song examples are equates. They equate to a value. You can add/subtract/multiply/divide/use logic operations, etc on any value or function. The only function that you need to modify though, is the "rest" function. You add the rest period value to the function itself (it's an embedded operand in the command).
Also, PCEAS doesn't seem to like long lines of equates. I've broken my examples up into lines of less than 94 characters long. You'll get a syntax error on that line of it's too long. You don't need to end a line with a ",". I think that about covers it.
Oh, one more thing. Notes. PCEAS doesn't like "#" in the equate system, so all sharps use an "x". A normal note is "C2." and a sharp is "C2x". The middle number signifies the octave.
Oh, one more more thing. A command string ends with either: a "note" or a "rest" function. If you jump in a loop that has neither, the engine will lock in an infinite loop and freeze playback.
Edit: new url
Ver 1.0 - alexandria66.2mhost.com/~pcengine/music/Azasel.zip
.
Last edited by tomaitheous on Mon Nov 09, 2009 7:28 pm, edited 1 time in total.
Re: Azasel music engine
this is cool but to be honest, i was looking forward to the .mod player you talked about making: viewtopic.php?f=5&t=22#p69
btw, did you realize that that "Azasel" sounds like "asshole". if you dont believe me, walk up to someone and call them an "Azasel" and hear what they say.
btw, did you realize that that "Azasel" sounds like "asshole". if you dont believe me, walk up to someone and call them an "Azasel" and hear what they say.
-
- Posts: 88
- Joined: Mon Jun 23, 2008 1:58 pm
Re: Azasel music engine
MOD schmod. Chiptunes rule. MOD format itself isn't really optimal for PCE. A variant of MOD, or just custom wavetable synth engine would be needed. The problem is, as with Azasel, is that you'd have to write a 'tracker' for it and you definitely want to be able to hear what you're working in realtime.Gravis wrote:this is cool but to be honest, i was looking forward to the .mod player you talked about making: viewtopic.php?f=5&t=22#p69
Uhm.. it sounds like Azazel. Ah-zai-zel.btw, did you realize that that "Azasel" sounds like "asshole". if you dont believe me, walk up to someone and call them an "Azasel" and hear what they say.
-
- Posts: 88
- Joined: Mon Jun 23, 2008 1:58 pm
Re: Azasel music engine
another double post?
Last edited by tomaitheous on Thu Mar 19, 2009 5:52 pm, edited 1 time in total.
Re: Azasel music engine
Here's a commented version. There're some still some uncommented constants (for example in the Azasel routine).
- Attachments
-
- contrib.zip
- (148.91 KiB) Downloaded 539 times
Re: Azasel music engine
I'm trying to generate triangle shaped tremolo using Bresenham line algorithm.
It's specified by 3 arguments : phase, rate and depth.
Following Bresenham with time as x and volume delta as y, we have at the initialization phase:
Between x=0 to phase, y slope is positive. Whereas from phase to rate, it's negative. Also note that x slope is always positive.
And then we have :
What's bothering me is the loop for the case where deltaX < deltaY. I don't know if it'll kill performances
The only way to avoid it is using standard DDA algorithm but we'll have to make a div and use 8:8 fixed point math.
Oh and the loop can be rewritten in a more readible way :
It's specified by 3 arguments : phase, rate and depth.
Following Bresenham with time as x and volume delta as y, we have at the initialization phase:
Code: Select all
deltaX = phase << 1;
deltaY = depth << 1;
x = 0;
y = 0;
sY = 1;
if(deltaX < deltaY)
error = deltaX - (deltaY >> 1);
else
error = deltaY - (deltaX >> 1);
Code: Select all
if(x == phase)
{
y = depth;
sY = -1;
deltaX = (rate - phase) << 1;
if(deltaX < deltaY)
{
error = deltaX - (deltaY >> 1);
}
else
{
error = deltaY - (deltaX >> 1);
}
}
else if(x == rate)
{
x = 0;
y = 0;
sY = 1;
deltaX = phase << 1;
if(deltaX < deltaY)
error = deltaX - (deltaY >> 1);
else
error = deltaY - (deltaX >> 1);
}
Code: Select all
if(deltaX < deltaY)
{
while(1)
{
if(error < 0)
{
++x;
error -= deltaY;
break;
}
y += sY;
error += deltaX;
}
}
else
{
if(error > 0)
{
y += sY;
error -= deltaX;
}
++x;
error += deltaY;
}
out_volume = volume - y;
![Sad :(](./images/smilies/icon_e_sad.gif)
Oh and the loop can be rewritten in a more readible way :
Code: Select all
while(error <= 0)
{
y += sY;
error += deltaX;
}
++x;
error -= deltaY;
Re: Azasel music engine
The next effect in the pipe in the good old arpeggio.
An arpeggio is a quick alteration of the note pitch between the base note, a semitone offset x and a semitone offset y. Each tone is played for 1 tick. So if we have A-2 as base note, x=3 and y=7 as offsets, we'll consecutively play A-2, C-3 and E-3.
According to MilkyTracker doc, if the speed is greater than 3 ticks the sequence is looped. It's turned off by setting the semitone offsets to 0.
The code for this effect is pretty simple :
We can also define an extended arpeggio effect which takes a semitone offset table (and its size), a delay (in ticks) between each note and maybe a flag to tell if we are looping or not.
An arpeggio is a quick alteration of the note pitch between the base note, a semitone offset x and a semitone offset y. Each tone is played for 1 tick. So if we have A-2 as base note, x=3 and y=7 as offsets, we'll consecutively play A-2, C-3 and E-3.
According to MilkyTracker doc, if the speed is greater than 3 ticks the sequence is looped. It's turned off by setting the semitone offsets to 0.
The code for this effect is pretty simple :
Code: Select all
setArpeggio:
semitoneOffset[0] = 0;
semitoneOffset[1] = data[offset++];
semitoneOffset[2] = data[offset++];
currentSemitone = 0;
nbSemitone = 3;
Code: Select all
msxUpdate:
note += semitoneOffset[currentSemitone];
++currentSemitone;
if(currentSemitone >= nbSemitone)
currentSemitone = 0;
if(note > MAX_NOTE) /* maybe unececessary. If it sounds like crap, blame the musician :) */
note = MAX_NOTE;
else if(note < 0)
note = 0;
freqLo = freqTableLo[note];
freqHi = freqTableHi[note];
Re: Azasel music engine
im not sure how you implemented this code but i think this version may require fewer instructions.MooZ wrote:I'm trying to generate triangle shaped tremolo using Bresenham line algorithm.
It's specified by 3 arguments : phase, rate and depth.
Code: Select all
if(deltaX < deltaY) { while(1) { if(error < 0) { ++x; error -= deltaY; break; } y += sY; error += deltaX; } } else { if(error > 0) { y += sY; error -= deltaX; } ++x; error += deltaY; } out_volume = volume - y;
Code: Select all
if(deltaX < deltaY)
{
while (!error & 0x80)
{
y += sY;
error += deltaX;
}
error -= deltaY;
}
else
{
if(!error & 0x80)
{
y += sY;
error -= deltaX;
}
error += deltaY;
}
++x;
out_volume = volume - y;