VOGONS


Reply 80 of 112, by superfury

User metadata
Rank l33t
Rank
l33t

That little sinus only register was indeed a bug I didn't notice. Fixed it right away.

You've increased the envelope counter from word to uint_32. Is that really required?

About those other edits:
- Why did you add those strange retrigger operations? What do they add?
- Why did you remove the release key off check? Did you know those two lines break the emulation(keys only go into release phase for one sample and keep using the if to jump down. Also notes playing that are stopped won't have any effect, because you've removed the check itself(when the note is turned off, it won't have effect on the volume envelope anymore, resulting in stuck keys)?
- Why did you remove the negative index safety? Negative indexes(like negative modulation on a near-zero signal) can give incorrect results if it isn't checked?

Edit: Playing the Maeva DRO recorded from Dosbox playing the Megarace game, I notice some stuck notes. Does it have something to do with that?
Edit: After applying the patches, the bug still exists?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows and PSP on itch.io

Reply 81 of 112, by superfury

User metadata
Rank l33t
Rank
l33t

I've made a little recording of playing most(if not all) Megarace songs, as well as some zeliard.dro&nautical.dro(which is well known?) file and Ultima 6 recordings from Dosbox:

Track list within the wave file(in the order they're recorded):
z_ultima6_000.dro: Ultima 6 recording of the opening, main menu music and instroduction music.
z_racerpg_000.dro: First level recording from Dosbox.
nautical.dro: Nautical recording
z_adlib.dro: A simple recording from one vogons thread (don't remember which) that plays all drum instruments four times.
z_tokyo.dro: Megarace's Tokyo level.
zz_suburb.dro: Megarace's Suburb level.
zz_skyholder.dro: Megarace's Skyholder level.
zz_particle.dro/zz_ngloop.dro: Megarace's Ngloop level(particle accelerator).
zz_newsan.dro: First level again, aborted.
zz_newfac.dro: Megarace's Newfac level.
zz_maeva.dro: Megarace's Maeva('Atlantis') level.
zz_junkyard.dro: Megarace's Junkyard level.
zerliard.dro: Zeliard recording

There's still some little hanging notes with the 'bubble' effect in the Maeva song it seems. Most songs sound pretty close(compared to Youtube recordings).

Edit: Just finished uploading the wave file: https://www.dropbox.com/s/dtlcurx56sxfbtq/Uni … 4_1558.zip?dl=0

It does sound pretty good overall. Can you see what might cause these 'errors'?

Source code of the used adlib.c: https://bitbucket.org/superfury/unipcemu/src/ … lib.c?at=master

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows and PSP on itch.io

Reply 82 of 112, by superfury

User metadata
Rank l33t
Rank
l33t

I'm wondering about my OPL2 rhythm emulation:

OPTINLINE word getphase(byte operator, float frequency) //Get the current phrase of the operator!
{
return (word)(fmodf((adlibop[operator].time*frequency),1.0)*((float)0x3D0)); //Give the 10-bits value
}

word convertphase_real(word phase)
{
return ((phase&0x200)?0:SIGNBIT)|(word)((phase&0x1FF)*((1.0f/(float)0x1FF)*SIGNMASK)); //Give the phase to execute, as a full sinus modulation!
}

float convertphase(word phase)
{
return OPL2_Exponential(phaseconversion[phase]); //Lookup the phase translated!
}

OPTINLINE float adlibsample(uint8_t curchan) {
byte op6_1, op6_2, op7_1, op7_2, op8_1, op8_2; //The four slots used during Drum samples!
word tempop_phase; //Current phase of an operator!
float result;
float immresult; //The operator result and the final result!
byte op1,op2; //The two operators to use!
float op1frequency, op2frequency;
curchan &= 0xF;
if (curchan >= NUMITEMS(adlibch)) return 0; //No sample with invalid channel!

//Determine the modulator and carrier to use!
op1 = adliboperators[0][curchan]; //First operator number!
op2 = adliboperators[1][curchan]; //Second operator number!
op1frequency = adlibfreq(op1); //Load the modulator frequency!
op2frequency = adlibfreq(op2); //Load the carrier frequency!

if (adlibpercussion && (curchan >= 6) && (curchan <= 8)) //We're percussion?
{
#ifndef ADLIB_RHYTHM
return 0.0f; //Disable percussion!
#else
INLINEREGISTER word tempphase;
result = 0; //Initialise the result!
//Calculations based on http://bisqwit.iki.fi/source/opl3emu.html fmopl.c
//Load our four operators for processing!
op6_1 = adliboperators[0][6];
op6_2 = adliboperators[1][6];
op7_1 = adliboperators[0][7];
op7_2 = adliboperators[1][7];
op8_1 = adliboperators[0][8];
op8_2 = adliboperators[1][8];
switch (curchan) //What channel?
{
case 6: //Bass drum?
//Generate Bass drum samples!
//Special on Bass Drum: Additive synthesis(Operator 1) is ignored.

//Calculate the frequency to use!
result = calcOperator(6, op6_1,op6_1,op6_1, adlibfreq(op6_1), 0.0f,0x00); //Calculate the modulator for feedback!

if (adlibch[6].synthmode) //Additive synthesis?
{
result = calcOperator(6, op6_2,op6_2,op6_2, adlibfreq(op6_2), 0.0f, 0x08); //Calculate the carrier without applied modulator additive!
}
else //FM synthesis?
Show last 106 lines
				{
result = calcOperator(6, op6_2,op6_2,op6_2,adlibfreq(op6_2), result, 0x08); //Calculate the carrier with applied modulator!
}

return result; //Apply the exponential! The volume is always doubled!
break;

//Comments with information from fmopl.c:
/* Phase generation is based on: */
/* HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases) */
/* SD (16) channel 7->slot 1 */
/* TOM (14) channel 8->slot 1 */
/* TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) */


/* Envelope generation based on: */
/* HH channel 7->slot1 */
/* SD channel 7->slot2 */
/* TOM channel 8->slot1 */
/* TOP channel 8->slot2 */
//So phase modulation is based on the Modulator signal. The volume envelope is in the Modulator signal (Hi-hat/Tom-tom) or Carrier signal ()
case 7: //Hi-hat(Carrier)/Snare drum(Modulator)? High-hat uses modulator, Snare drum uses Carrier signals.
immresult = 0.0f; //Initialize immediate result!
if (adlibop[op7_1].volenvstatus) //Hi-hat on Modulator?
{
//Derive frequency from channel 7(modulator) and 8(carrier).
tempop_phase = getphase(op7_1,adlibfreq(op7_1)); //Save the phase!
tempphase = (tempop_phase>>2);
tempphase ^= (tempop_phase>>7);
tempphase |= (tempop_phase>>3);
tempphase &= 1; //Only 1 bit is used!
tempphase = tempphase?(0x200|(0xD0>>2)):0xD0;
tempop_phase = getphase(op8_2,adlibfreq(op8_2)); //Calculate the phase of channel 8 carrier signal!
if (((tempop_phase>>3)^(tempop_phase>>5))&1) tempphase = 0x200|(0xD0>>2);
if (tempphase&0x200)
{
if (OPL2_RNG) tempphase = 0x2D0;
}
else if (OPL2_RNG) tempphase = (0xD0>>2);

result = calcOperator(8, op8_2,op8_2,op8_2,adlibfreq(op8_2), 0.0f,((adlibop[op8_2].volenvstatus)?1:0)); //Calculate the modulator, but only use the current time(position in the sine wave)!
result = calcOperator(7, op7_1,op7_1,op7_1,adlibfreq(op7_1), convertphase(tempphase), 2|((adlibop[op8_2].volenvstatus||adlibop[op7_2].volenvstatus)?0x09:0x08)); //Calculate the carrier with applied modulator!
immresult += result; //Apply the tremolo!
}
if (adlibop[op7_2].volenvstatus) //Snare drum on Carrier volume?
{
//Derive frequency from channel 0.
tempphase = 0x100 << ((getphase(op7_1, adlibfreq(op7_1)) >> 8) & 1); //Bit8=0(Positive) then 0x100, else 0x200! Based on the phase to generate!
tempphase ^= (OPL2_RNG << 8); //Noise bits XOR'es phase by 0x100 when set!
result = calcOperator(7, op7_2,op7_2,op7_2,adlibfreq(op7_2), convertphase(tempphase), 0); //Calculate the carrier with applied modulator!
result = calcOperator(7, op7_1,op7_1,op7_2,adlibfreq(op7_1), convertphase(tempphase), ((adlibop[op8_2].volenvstatus) ? 0x09 : 0x08)); //Calculate the carrier with applied modulator!
immresult += result; //Apply the tremolo!
}
result = immresult; //Load the resulting channel!
result *= 0.5; //We only have half(two channels combined)!
return result; //Give the result, converted to short!
break;
case 8: //Tom-tom(Carrier)/Cymbal(Modulator)? Tom-tom uses Modulator, Cymbal uses Carrier signals.
immresult = 0.0f; //Initialize immediate result!
if (adlibop[op8_1].volenvstatus) //Tom-tom(Modulator)?
{
result = calcOperator(8, op8_1, op8_1, op8_1, adlibfreq(op8_1), 0.0f, 0x8); //Calculate the carrier without applied modulator additive! Ignore volume!
immresult += result*2.0f; //Apply the exponential!
}
if (adlibop[op8_2].volenvstatus) //Cymbal(Carrier)?
{
//Derive frequency from channel 7(modulator) and 8(carrier).
tempop_phase = getphase(op7_1,adlibfreq(op7_1)); //Save the phase!
tempphase = (tempop_phase>>2);
tempphase ^= (tempop_phase>>7);
tempphase |= (tempop_phase>>3);
tempphase &= 1; //Only 1 bit is used!
tempphase <<= 9; //0x200 when 1 makes it become 0x300
tempphase |= 0x100; //0x100 is always!
tempop_phase = getphase(op8_1,adlibfreq(op8_1)); //Calculate the phase of channel 8 carrier signal!
if (((tempop_phase>>3)^(tempop_phase>>5))&1) tempphase = 0x300;

result = calcOperator(7, op7_1,op7_1,op7_1, adlibfreq(op7_1), 0.0f,0); //Calculate the modulator, but only use the current time(position in the sine wave)!
result = calcOperator(8, op8_2,op8_2,op8_2, adlibfreq(op8_2), convertphase(tempphase), 0x8); //Calculate the carrier with applied modulator! Use volume!
immresult += result; //Apply the exponential!
}
result = immresult; //Load the resulting channel!
result *= 0.5; //We only have half(two channels combined)!
return result; //Give the result, converted to short!
break;
}
#endif
//Not a percussion channel? Pass through!
}

//Operator 1!
//Calculate the frequency to use!
result = calcOperator(curchan, op1,op1,op1, op1frequency, 0.0f,0x80); //Calculate the modulator for feedback!

if (adlibch[curchan].synthmode) //Additive synthesis?
{
result += calcOperator(curchan, op2,op2,op2, op2frequency, 0.0f,0x00); //Calculate the carrier without applied modulator additive!
}
else //FM synthesis?
{
result = calcOperator(curchan, op2,op2,op2, op2frequency, result, 0x00); //Calculate the carrier with applied modulator!
}

return result; //Give the result!
}

The phaseconversion lookup table being:

	n = 0; //Loop through again for te modified table!
do
{
buffer = OPL2_ExponentialLookup[n]; //Load the current value translated!
buffer *= generalmodulatorfactor; //Apply the general modulator factor to it to convert it to -1.0 to 1.0 range!
OPL2_ExponentialLookup2[n] = (float)buffer; //Store the value for fast lookup!
phaseconversion[n] = convertphase_real(n); //Set the phase conversion as well!
} while (++n<0x10000); //Loop while not finished processing all possibilities!

Is this behaviour correct? Are the phase conversions to/from OPL2-compatible format correct? (Everything from the final //Operator 1! comment is the normal, working OPL2 tone generation, which I believe is 100% accurate)

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows and PSP on itch.io

Reply 83 of 112, by superfury

User metadata
Rank l33t
Rank
l33t

Just tried the soundtrack of Simcity 2000 Special Edition(which I still have from back when I was a child).

https://www.dropbox.com/s/9eufzm0bp4fd3uv/rec … ng_193.wav?dl=0

Somehow, the percussion seems too soft? In my own source code I do see the volume doubled during operator calculation, and halved when mixing the two percussion channels together. Is that correct behaviour?

That's comparing the soundtrack to a Youtube recording:
https://www.youtube.com/watch?v=DDQY3zGEbQU

(Btw, those PC-speaker beeps in between are me trying to move the screen using the mouse keys on the keyboard, which it apparently doesn't like? (keyboard buffer full))

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows and PSP on itch.io

Reply 84 of 112, by superfury

User metadata
Rank l33t
Rank
l33t

I've managed to get about half the percussion sounding right now, just not the Hi-hat and Base drum(no sound or too little sound to even hear):
https://bitbucket.org/superfury/unipcemu/src/ … lib.c?at=master

I also found out that I've reversed the doubling of volume on said instruments(requiring a divide by 2 of the raw OPL floating point instead of a doubling of it(it's a value of attenuation after all).

Oddly enough, none of the percussion instruments sound well at all. They don't sound like their supposed sounds at all(just sine-wave-like sounds). And don't get me started on the Hi-hat and Base-drum: they seem to result in inaudible soft signals(so small they can't be heard)?

Even though I know modulation should be happening(and is happening according to the source code), barely anything of the noise channels is heard on the output of the three working half-channels(only cymbal shows some kind of distortion, and only slightly(bell-like sound)).

Anyone? Jepael?

Edit: Slight improvements: https://bitbucket.org/superfury/unipcemu/src/ … lib.c?at=master
Mainly volume being restored to what it was(correct) in some rhythm channels. Also some support I've been experimenting with(bit 7 generating a direct signal out of the modulator input itself instead of a normal signal, which seems to not work correctly).

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows and PSP on itch.io

Reply 85 of 112, by superfury

User metadata
Rank l33t
Rank
l33t

I've tried to max an approximation, but even when enabling the raw input feature(replacing the sine wave generator with the random signal), it just results in zero output instead of an odd sinus waveform(set bit 6(0x40) in the final parameter of calcOperator for signal generation of op7_1, op7_2 and op8_2)?

https://bitbucket.org/superfury/unipcemu/src/ … lib.c?at=master

Anyone?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows and PSP on itch.io

Reply 86 of 112, by superfury

User metadata
Rank l33t
Rank
l33t

I've tried converting the pseudo-random(10-bit) signal generated for output to a raw normalized phase(-1.0 to 1.0 on a sinus scale), which is passed through as a sinus carrier instead of a modulator(the 0x40 flag being set), replacing the normal frequency generation(frequency times time) with said value as an input into the OPL2 sinus tables). Everything after that is handled the same(except that the modulator of channel 7 and carrier of channel 8 are updated after channel 8 is rendered(since they both generate output based on those two signal phases).

https://bitbucket.org/superfury/unipcemu/src/ … lib.c?at=master

Oddly enough, now all channels but the channel 8 modulator are pretty much 'muted'? The 10-bit value is used to generate a random sinus signal in those random-generated channels(all 4 channel 7/8 channels, except the channel 8 modulator(which gives direct output without any special algorithm)), according to formulas I've found in another OPL2/3 emulator.

Jepael?

Edit: Looking at the set frequencies, they're ridiculous: one of 4Hz(Hi-hat), 4.12Hz(cymbal), Tom-tom(1040Hz), Snare-drum(4688Hz) and Base-drum(2Hz)?

Last edited by superfury on 2022-05-14, 12:21. Edited 1 time in total.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows and PSP on itch.io

Reply 87 of 112, by superfury

User metadata
Rank l33t
Rank
l33t

I've modified the OPL2 to now be using synthesis using integer math (mostly. The only place not using the counters like OPL3EMU etc. is currently the Tremolo/Vibrato effects).
They now use 10.10 fixed point math.

It sounds rougly fine, but for some reason the tones are way too high using the new method?
I've looked at how OPL3EMU calculates the speed of walking through the wave table, but it seems to be much too high?

The latest up-to-date source adlib source code can always be found inside UniPCemu/hardware/adlib.c inside UniPCemu's repository.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows and PSP on itch.io

Reply 88 of 112, by superfury

User metadata
Rank l33t
Rank
l33t

I've adjusted the factor it uses to create a OPL2 phase increase depending on the fnum number a bit.
Now it increases the phase(0-1024 being a full sine wave) by the following increment each sample:

		adlibop[adliboperators[0][channel]].increment = ((((uint_32)(adlibch[channel].m_fnum*(1.0/388.0)*(14318180.0 / 288.0))) >> (7 - adlibch[channel].m_block)) * adlibop[adliboperators[0][channel]].ModulatorFrequencyMultiple); //Calculate the effective frequency!

Anyone knows what would be correct behaviour in this case? It's running at a rate of 14.31818MHz divided by 288 as can be seen above.
Each time a sample is rendered, the increment variable is added to the current phase to provide a new wave position.
It's stored in integer units (10.10 fixed point format).

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows and PSP on itch.io

Reply 89 of 112, by Electronic Genets

User metadata
Rank Newbie
Rank
Newbie

I started my implementation of OPL2 like emulator about 1-2 months ago. The library is now finished. Maybe I find some bugs and I will correct them. Maybe I implement CSM if there was anything interesting there (unimplemented in many emulators).
You started to implement your OPL2/OPL3 GNU-licensed emulator 31st May 2015 or earlier (date of your first post here).
You can use GNU code and external linked LGPL libraries . Why do you spend the seventh year on it, instead of taking something ready-made that you can legally use in your project?

Until yesterday, I thought that you recently started a sound card project for your UniPCEmu.

From an economic point of view, it is beneficial to use Nuked-OPL3 in your project. You save time. Nuked-OPL3 (LGPL) can be dynamically linked in your GNU project.

Author of the DINO-2e OPL2-like emulator.
DINO-2e is not OPL2 emulator.

Reply 90 of 112, by superfury

User metadata
Rank l33t
Rank
l33t

Because the other parts were already written from scratch and it's fun to learn to do it with accuracy?

As of a seperate note, I've been thinking about the formula used to calculate the increment from the fnum and block number.

It might be way more simple than the whole floating point math being done?

Thinking about it:
known: 14318180/388 (let's call it 14D) ticks(steps)/second
known: 1024*1024(let's call it 1M) incr ticks=1 sinus(1Hz) period
calc: (Hz*1M)/14D=incr(step for Hz)
known: fnum=Hz*2^(20-block)/14D
calc: Hz=(fnum*14D)/2^(20-block)

So we end up with:
incr=(((fnum*14D)/(2^(20-block))*1M)/14D

14D cancels itself out(multiplied and divided), so ignore it.

So the end result is:
incr=(((fnum)/(2^(20-block))*1M)

But 1M=2^20, so at block=0 that can be optimized out as well? Each step up of block multiplies the result by 2 (due to 20-block lowering shifting less), so the end result would be:

incr=fnum*(2^block)

So, in effect (c-style code):
incr = fnum << block

Would that be correct?
Of course, it's further multiplied by 0.5-15 depending on the multiplier setting, which can easily be done by storing 1 for the first entry of the lookup and all other entries being doubled. The only other exceptions being entries 11(=20), 13(=24) and 14(=30). Then it can be applied by multiplying the increment with that and simply shifting it right one bit.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows and PSP on itch.io

Reply 91 of 112, by superfury

User metadata
Rank l33t
Rank
l33t

The formula seems to run fine.
So that probably happens on the real chip as well?

Now the next questions:
- Why do some Megarace effects (like the high sweeps at the start of the Factory Land) sound incorrect? It's mostly pure sine waves now?
- How do the Tremolo/Vibrato work on the real chip? I'd assume it's like with the sine wave timing, but linear instead(although not sure on it being 10.10 as well)? Then how is it applied to the output (Tremolo) and increment(Vibrato)?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows and PSP on itch.io

Reply 92 of 112, by superfury

User metadata
Rank l33t
Rank
l33t

OK. So there's a LFO AM and PM table that's used in the chip?
https://github.com/gtaylormb/opl3_fpga/blob/m … r/docs/ymf262.c

The AM depth not being set divides it by 2.
It's directly added to the attentuation from the volume envelope?

The PM table is an different case? It should adjust the fnum and block inputs (although still using fnum<<block in UniPCemu's case) to generate a special increment value to use instead of the original one?

UINT8 block;
unsigned int block_fnum = CH->block_fnum;

unsigned int fnum_lfo = (block_fnum&0x0380) >> 7;

signed int lfo_fn_table_index_offset = lfo_pm_table[chip->LFO_PM + 16*fnum_lfo ];

if (lfo_fn_table_index_offset) /* LFO phase modulation active */
{
block_fnum += lfo_fn_table_index_offset;
block = (block_fnum&0x1c00) >> 10;
op->Cnt += (chip->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul;
}
else /* LFO phase modulation = zero */
{
op->Cnt += op->Incr;
}

That should pretty much resolve to (rough pseudocode mixed with UniPCemu's behaviour):

byte block;
word block_fnum = adlibch[channel].m_fnum;

word fnum_lfo = (block_fnum&0x0380) >> 7;

sword lfo_fn_table_index_offset = lfo_pm_table[adlibch[channel].LFO_PM + (fnum_lfo<<4) ];

if (lfo_fn_table_index_offset) /* LFO phase modulation active */
{
block_fnum += lfo_fn_table_index_offset;
block = (block_fnum&0x1c00) >> 10;
operator->phase += ((((block_fnum&0x03ff)<<block) * operator->ModulatorFrequencyMultiple) >> 1);
}
else /* LFO phase modulation = zero */
{
operator->phase += operator->increment;
}
//Normal masking of the phase now!
operator->phase &= 0xFFFFF;

The LFO_PM and phase would be roughly calculated like this:

	if (++tremolovibrato[0].phasedivider==64) //Every 64 samples!
{
tremolovibrato[0].phasedivider = 0; //Reset!
++tremolovibrato[0].phase; //Increase the phase!
if ((tremolovibrato[0].phase>>24)>NUMITEMS(lfo_am_table)) //Wrap?
{
tremolovibrato[0].phase -= (NUMITEMS(lfo_am_table)<<24); //Wrap!
}
}
adlibch[channel].LFO_AM = lfo_am_table[tremolovibrato[0].phase>>24]; //AM!
if (!adlibch[channel].AMdepth) //AM depth not set?
{
adlibch[channel].LFO_AM >>= 2; //Divide by 4!
}
if (++tremolovibrato[1].phasedivider==1024) //Every 1024 samples!
{
tremolovibrato[1].phasedivider = 0; //Reset!
++tremolovibrato[1].phase; //Increase the phase!
}
adlibch[channel].LFO_PM = ((tremolovibrato[1].phase>>24) & 7) | adlibch[channel].lfo_pm_depth_range;

phasedivider is initialized to 0 of course (for both LFOs).

Where lfo_pm_depth_range is 8(set) or 0(cleared) depending on LFO depth set by the program for the channel?

The increment should for these be the same as OPL3EMU does, as it's too low of a LFO frequence to do it like UniPCemu handles the other phase generators (it's LFO after all, in this case too LF)?

Those two increment at every 64(AM) and 1024(PM) samples. So just ignore that entirely and simply increase by 1 after every 64 or 1024 samples rendered?

Would that be correct behaviour?
Edit: Looking at my code, tremolo and vibrato was never fully implemented nor enabled in any way!

Edit: I've implemented the above code (although adjusted as I was verifying it's behaviour by ear).
The new code is now on the UniPCemu repository.
So no more floating point algorithmic now (other than the summing of voices to the DAC and the creation of the lookup LogSin and Exponential tables).

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows and PSP on itch.io

Reply 93 of 112, by Electronic Genets

User metadata
Rank Newbie
Rank
Newbie

superfury I propose play on your emulator old-title-tune.hsc and crystal2.hsc. You will find them in cta-adlib.zip found by google. This zip has badly signed authors (including these tracks).

The old-title-tune.hsc helped me with attack/decay problems (there will be hearable differences if badly handled). The crystal2.hsc helped me with feedback at 24kHz mode (hearable additional sounds before improvement).

JazPopMix.hsc is also second test for ADSR Envelope.

I thought I finished, and then it turned out that I had to spend 10 days to find bugs and correct them.
The old-title-tune is also good for fighting with DC signal and clicks.
I cannot attach results before and after removing clicks and DC signal due to license of old-title-tune (and all WV tracks). This song belongs to Ubisoft / Take2 now (it was probably sold). But for private use you probably can use it if it is still online.

Author of the DINO-2e OPL2-like emulator.
DINO-2e is not OPL2 emulator.

Reply 94 of 112, by superfury

User metadata
Rank l33t
Rank
l33t
Electronic Genets wrote on 2022-05-19, 00:23:
superfury I propose play on your emulator old-title-tune.hsc and crystal2.hsc. You will find them in cta-adlib.zip found by goog […]
Show full quote

superfury I propose play on your emulator old-title-tune.hsc and crystal2.hsc. You will find them in cta-adlib.zip found by google. This zip has badly signed authors (including these tracks).

The old-title-tune.hsc helped me with attack/decay problems (there will be hearable differences if badly handled). The crystal2.hsc helped me with feedback at 24kHz mode (hearable additional sounds before improvement).

JazPopMix.hsc is also second test for ADSR Envelope.

I thought I finished, and then it turned out that I had to spend 10 days to find bugs and correct them.
The old-title-tune is also good for fighting with DC signal and clicks.
I cannot attach results before and after removing clicks and DC signal due to license of old-title-tune (and all WV tracks). This song belongs to Ubisoft / Take2 now (it was probably sold). But for private use you probably can use it if it is still online.

Found them. Any advice on meth0ds of playback? Do any 8088 players(maximum speed) exist?
Edit: Found one: Adlib OPL2 module players that can run on XT (i8088 cpu)

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows and PSP on itch.io

Reply 95 of 112, by superfury

User metadata
Rank l33t
Rank
l33t

I've just implemented looping into the player.
Although somehow when the loop occurs, the playback suffers because of hanging notes and/or a hiccup somehow (sounds like timings being skipped somehow or notes not finished when they should)?

I see nothing weird happening in the player itself. Once the end of the loop is reached (x samples after the start of the loop), during parsing the delay time (no matter if the delay in question is already finished or has time left to tick), the player position is reset to the address in the file as specified by the loop address in the header (with it's offset added to it) and the commands until the next delay command are immediately executed.
Perhaps that's part of the problem somehow?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows and PSP on itch.io

Reply 96 of 112, by superfury

User metadata
Rank l33t
Rank
l33t

OK. Changing the loop to be applied at the end of the songs always (command 66h) seems to fix the looping?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows and PSP on itch.io

Reply 97 of 112, by superfury

User metadata
Rank l33t
Rank
l33t

OK. Just ran the Dead End [Round 4] song from Rusty on my adlib player.

It sounds the same as https://www.youtube.com/watch?v=aLaUDFnNqQc&t=221s ?
So that might mean that the implementation is correct?
It just sounds softer, but that might be the difference between the low-pass filter types used for the chip and within the emulator? UniPCemu uses a simple RC filter, while (although I can see the board might have one as well at the very right of the board).

Although when I try the NewSan theme from Megarace (which I test with all the time, even before VGM support was added), I hear something weird at 1:28 in the video (about 2 seconds after that).
Although in the video i hear a sweep from low to high at 1:27-1:30, UniPCemu produces a sweep for a bit, then broken into 2 small mid to high sweeps after that?

You'd almost think there's an issue with high frequencies being used on the OPL2 chip that are drawn back to lower frequencies, giving the multiple sweep effect when it shouldn't?
It's not an effect caused by the low-pass filter, as even with it disabled it shows this behaviour?
Edit: Looking at https://www.youtube.com/watch?v=KOrv9-S1aj4 , at around 0:40.5, I see it's frequency (channel 4) going through octave 0 through 5, but the sweep in UniPCemu sounds different, with a much faster and about 2 short sweeps in the latter half? Probably octave 4&5 on IMFplay?
That's an increase of almost 15-bit overflow at octave 5 with frequency 1023?
Edit: Indeed. The maximum frequencies easily exceed 16-bit word increment size, so they would cause the increment variable for an operator to overflow. Having fixed the variable to be 32-bit fixed the issue.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows and PSP on itch.io

Reply 98 of 112, by Electronic Genets

User metadata
Rank Newbie
Rank
Newbie

superfury Record (in 49716 Hz WAV) your playback form JazzPopMix.HSC and Crystal2.HSC and PM* me I can check you play it correctly. I know these two tracks and there are good test for non-rhythm mode.

*) PM bacause of copyright.

Nobody will answer you whether you play a track well when you give it as MP4 / MPEG4 (lossy compression).

Author of the DINO-2e OPL2-like emulator.
DINO-2e is not OPL2 emulator.

Reply 99 of 112, by superfury

User metadata
Rank l33t
Rank
l33t
Electronic Genets wrote on 2022-05-26, 18:50:

superfury Record (in 49716 Hz WAV) your playback form JazzPopMix.HSC and Crystal2.HSC and PM* me I can check you play it correctly. I know these two tracks and there are good test for non-rhythm mode.

*) PM bacause of copyright.

Nobody will answer you whether you play a track well when you give it as MP4 / MPEG4 (lossy compression).

Should I use the mentioned software to play them (the 8088 one)?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows and PSP on itch.io