Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding sound support to Model Racing Dribbling driver (part 2) #12147

Merged
merged 24 commits into from
Mar 21, 2024
Merged

Adding sound support to Model Racing Dribbling driver (part 2) #12147

merged 24 commits into from
Mar 21, 2024

Conversation

aovestdipaperino
Copy link
Contributor

@aovestdipaperino
Copy link
Contributor Author

aovestdipaperino commented Mar 17, 2024

Copying the conversation over.

I am opening up the PR (I will try or open a new one and link to here) but please provide guidance on:
LM339:

  1. the best option would be to create a comparator model in C++, but that's way beyond my understanding of how the netlist code works. If anyone can help, from a layman perspective it seems a much simpler circuit than the Schmitt Trigger, I will more than happy to help. I don't have the skills to help here
  2. second best option is to use a type 1 OpAmp (see here; idealized OpAmps are close to comparators. I can help with this.
  3. third best, is to use LM324. I can provide MicroCap charts and wavs showing how close these are; at least for the time being it could be acceptable until a 1. is done; this is what is already done.
  4. any other solution, I don't have the skills to do this

It’s not like the circuit will get anywhere near the LM339’s frequency response limits, and the output transistor will be in cutoff or saturation all the time anyway.

Exactly. That is why an OpAmp (idealized?) is a good approximation.

JFET:
as I mentioned above, I used the exact suggestion presented in the FAQ (linking it again here ). It suggests to use MOSFET(Q21, "NMOS(VTO=-1.0)")

Did you substitute depletion mode MOSFETs with appropriate Vgs=0 current? Did you try using a depletion mode MOSFET with a diode connected across the drain and source for the first JFET?

Nope, I can try.
As for a stopgap in case, the current netlist is pretty close from a sound perspective (compare the videos provided to see what I mean).

So can I get help and guidance? this is close to my skill border

Using named objects: I don't understand what it means, a link with an example would suffice. Keeping things as is it's an option as well since it's only 9 sounds total.

High pitch removal additional circuitry: let me know what you prefer, I can only comment that either we keep it (with a #define for now) or we add an OpAmp 1x amplifier to act as cut-off filter. My quick attempts at the latter haven't produced results as clean as the logic circuitry.

Logs: I agree to disagree but I am totally fine in putting them back as they don't hurt performance anyway.

Sorry if I closed comments that seemed to be just questions on why had I taken some choices, I believe the github threaded model doesn't help with this specific type of discussion.

The transistor-level model of an LM339 is probably too slow even for a high-end system. There’s a reason we don’t use transistor-level models of op-amps, either.
Exactly. I consider a Mac running an M2 quite high-end, and of course too many transistors for expecting real time.

One more thing: the STOP_PALLA (and PARATA I guess) require an higher resolution since the code switches the circuitry off too fast for the netlist code to realized that there was an event. They can be moved to a different netlist, but it won't help anyway since they require the LM339.

@cuavas
Copy link
Member

cuavas commented Mar 17, 2024

any other solution, I don't have the skills to do this

Look at the implementation of the op-amp in the C++ code. Most of it’s just a netlist. It uses a voltage-controlled current source for the output, and some resistors, capacitors and diodes.

You can build a simplified LM339 model in your netlist the same way. Use a similar arrangement of resistors for the input, and use a limited voltage-controlled current source for the output.

Exactly. That is why an OpAmp (idealized?) is a good approximation.

It isn’t though, because it will affect the waveforms of the function generators, and if you did it in real life it would burn out one of the JFETs.

The primary issue is that the LM339 has a single-ended output and cannot source current. An op-amp, which can source current, will produce different waveforms and also likely burn out one of the JFETs rather quickly.

From a quick look at the FET model:

  • Set parameter Is to the saturation current
  • Set parameter Vto to the negative threshold voltage
  • Set parameter Kp to a reasonable transconductance value

Logs: I agree to disagree but I am totally fine in putting them back as they don't hurt performance anyway.

They’re absolutely zero run time cost when they’re disabled. It’s never a bad thing to help the next person who comes along.

src/mame/mr/dribling.cpp Outdated Show resolved Hide resolved
Comment on lines 528 to 533
NETLIST_STREAM_OUTPUT(config, "snd_nl:cout0", 0, "OUTPUT").set_mult_offset(1.0, 0);


}

/*************************************
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There’s still the diff noise here from adding an extra blank line inside the function and getting rid of one from the two blank lines separating functions.

@cuavas
Copy link
Member

cuavas commented Mar 17, 2024

One more thing: the STOP_PALLA (and PARATA I guess) require an higher resolution since the code switches the circuitry off too fast for the netlist code to realized that there was an event.

Can you force an update of the sound simulation when the code changes the state of the signal? Is it possible to force the netlist sound device to update using update_to_current_time() when the outputs in question are changing?

@aovestdipaperino
Copy link
Contributor Author

aovestdipaperino commented Mar 17, 2024

Look at the implementation of the op-amp in the C++ code. Most of it’s just a netlist. It uses a voltage-controlled current source for the output, and some resistors, capacitors and diodes.

If you are referring to nld_opamps.cpp I don't understand any single line of it, it's way above my capabilities at the moment; I wouldn't know where to start and how to verify it even works. This is what I meant by "I don't have the skills to help here".

@simzy39
Copy link
Contributor

simzy39 commented Mar 17, 2024

@couriersud

@aovestdipaperino
Copy link
Contributor Author

aovestdipaperino commented Mar 17, 2024

Another option I've seen float around on forums.
Adding an additional input (B) to the Schmitt trigger which defaults to 0.
This line:
const auto va(m_A.Q_Analog() - m_supply.GND().Q_Analog());
then turns into:
const auto va(m_A.Q_Analog() - m_B.Q_Analog() - m_supply.GND().Q_Analog());

Spice has a library device called DIFFSCHMITT that does exactly this.
By setting the hysteresis to very low, ROH to large and ROL to small, it should behave like a comparator.

(Or maybe simply map V+ to SCHMITT input, V- to SCHMITT GND)

@aovestdipaperino
Copy link
Contributor Author

wrt

Can you force an update of the sound simulation when the code changes the state of the signal? Is it possible to force the netlist sound device to update using update_to_current_time() when the outputs in question are changing?

I will try and report back.

@aovestdipaperino
Copy link
Contributor Author

aovestdipaperino commented Mar 18, 2024

Another option I've seen float around on forums. Adding an additional input (B) to the Schmitt trigger which defaults to 0. This line: const auto va(m_A.Q_Analog() - m_supply.GND().Q_Analog()); then turns into: const auto va(m_A.Q_Analog() - m_B.Q_Analog() - m_supply.GND().Q_Analog());

Spice has a library device called DIFFSCHMITT that does exactly this. By setting the hysteresis to very low, ROH to large and ROL to small, it should behave like a comparator.

(Or maybe simply map V+ to SCHMITT input, V- to SCHMITT GND)

I've tested the idea and seems very promising. Without fine tuning the parameters in the SCHMITT based comparator the diffs are looking promising. This is the STOP_PALLA netlist running under both models:

output
output

Performance of the transistor model: 3.000000 seconds emulation took 1.167813 real time ==> 256.89%
Performance of the Schmitt model: 3.000000 seconds emulation took 0.167233 real time ==> 1793.90%

If there is consensus I can start a new PR for adding the DIFF_SCHMITT_TRIGGER and close the chapter on LM339 emulation once I obtain almost identical output.
As side effect, the netlist can run at a smaller resolution without requiring update_to_current_time()

@aovestdipaperino
Copy link
Contributor Author

Current output after tuning:
output
Microcap output:
stop_palla_microcap
Scale aside, I would say that the Schmitt based LM339 is more accurate than the transistor based simulation.

@aovestdipaperino
Copy link
Contributor Author

JFET/PARATA: I've tried MOSFET(name, "NMOS(VTO=-1 VGS=0 IS=0.0015 KP=0.0001) and MOSFET(name, "NMOS(VTO=-1 VGS=0 IS=0.0015 KP=250)
With and without a diode across GS. The output is a variation of this chart
output

Comment on lines 510 to 517
NETLIST_LOGIC_INPUT(config, m_i_pb.at(0), "I_PB0.IN", 0);
NETLIST_LOGIC_INPUT(config, m_i_pb.at(1), "I_PB1.IN", 0);
NETLIST_LOGIC_INPUT(config, m_i_pb.at(2), "I_PB2.IN", 0);
NETLIST_LOGIC_INPUT(config, m_i_pb.at(3), "I_PB3.IN", 0);
NETLIST_LOGIC_INPUT(config, m_i_pb.at(4), "I_PB4.IN", 0);
NETLIST_LOGIC_INPUT(config, m_i_pb.at(5), "I_PB5.IN", 0);
NETLIST_LOGIC_INPUT(config, m_i_pb.at(6), "I_PB6.IN", 0);
NETLIST_LOGIC_INPUT(config, m_i_pb.at(7), "I_PB7.IN", 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You’re supposed to index array-like things like arrays – m_i.pp[0] etc. Using at(...) has additional overhead because it does a bounds check to throw an exception if you pass it a bad index, which you can tell won’t happen at compile time.

src/mame/mr/dribling.cpp Outdated Show resolved Hide resolved
Comment on lines 510 to 527
NETLIST_LOGIC_INPUT(config, m_i_pb.at(0), "I_PB0.IN", 0);
NETLIST_LOGIC_INPUT(config, m_i_pb.at(1), "I_PB1.IN", 0);
NETLIST_LOGIC_INPUT(config, m_i_pb.at(2), "I_PB2.IN", 0);
NETLIST_LOGIC_INPUT(config, m_i_pb.at(3), "I_PB3.IN", 0);
NETLIST_LOGIC_INPUT(config, m_i_pb.at(4), "I_PB4.IN", 0);
NETLIST_LOGIC_INPUT(config, m_i_pb.at(5), "I_PB5.IN", 0);
NETLIST_LOGIC_INPUT(config, m_i_pb.at(6), "I_PB6.IN", 0);
NETLIST_LOGIC_INPUT(config, m_i_pb.at(7), "I_PB7.IN", 0);
NETLIST_LOGIC_INPUT(config, m_i_folla_b, "I_FOLLA_B.IN", 0);
NETLIST_LOGIC_INPUT(config, m_i_folla_m, "I_FOLLA_M.IN", 0);
NETLIST_LOGIC_INPUT(config, m_i_folla_a, "I_FOLLA_A.IN", 0);
NETLIST_LOGIC_INPUT(config, m_i_calcio_a, "I_CALCIO_A.IN", 0);
NETLIST_LOGIC_INPUT(config, m_i_fischio, "I_FISCHIO.IN",0);
NETLIST_LOGIC_INPUT(config, m_i_calcio_b, "I_CALCIO_B.IN", 0);
NETLIST_LOGIC_INPUT(config, m_i_contrasto, "I_CONTRASTO.IN", 0);
NETLIST_LOGIC_INPUT(config, m_i_stop_palla,"I_STOP_PALLA.IN", 0);
NETLIST_LOGIC_INPUT(config, m_i_parata, "I_PARATA.IN", 0);
NETLIST_LOGIC_INPUT(config, m_i_enable, "ENABLE_SOUND.IN",0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you’re going to tabulate these, please do it neatly so all the node names in the group line up properly. Otherwise just use one space after a comma. Right now there are various different numbers of spaces after commas, but the node names still don’t line up.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they look better now.

@cuavas
Copy link
Member

cuavas commented Mar 18, 2024

It would be nice to have a datasheet for the JFETs. The schematic seems to say that the JFETs are 2N3812, but the only 2N3812 I know of is a matched pair of NPN transistors for differential amplifier input stages. I’m wondering if the schematic is wrong and the JFETs are actually some other type. Do you have access to a board? Can you check what type of JFETs are used? Alternatively, is there a parts list or something that can be cross-checked against the schematic?

At any rate, the pinch-off voltage for these kinds of JFETs is usually around -3V, and given how they’re using it, the Vgs=0 current might be around 2mA at a guess given how they’re used.

Are you connecting the MOSFETs the right way around? For the one in the function generator, the source and gate need to be connected to the LM339 output, and the drain needs to be connected to the timing capacitor. The diode across it should have the anode connected to the gate/source and the cathode connected to the drain.

@cuavas
Copy link
Member

cuavas commented Mar 18, 2024

If there is consensus I can start a new PR for adding the DIFF_SCHMITT_TRIGGER and close the chapter on LM339 emulation once I obtain almost identical output.

It’s more complex than necessary – you’d be using a model with hysteresis to simulate something without hysteresis. It would be better if you just wait for someone to help you with a simplified LM339 model.

@rb6502
Copy link
Contributor

rb6502 commented Mar 18, 2024

It would be better if you just wait for someone to help you with a simplified LM339 model.

Given nobody actively works on this stuff and this is a major improvement over the current state as things stand, is there some acceptable imperfect version we can get in and IMPERFECT_SOUND? MAME's supposed to have the best version of what we know about the hardware, and this is significantly better than not having sound or using samples.

@cuavas
Copy link
Member

cuavas commented Mar 18, 2024

Can we at least try to work through this first?

  • A simplified LM339 model isn’t hard, but doing it looks like it’s going to need someone to help out, and that means waiting until someone is between tasks.
  • It would be good to work out what’s going on with the PARATA function generator. It’s not overly complex.
  • The fake circuit to suppress idle noise really needs to go. If additional filtering is required, it can be done with devices from sound/flt_biquad.cpp or sound/flt_rc.cpp for example (the TDA2003 datasheet claims a -3dB frequency of 15kHz). I suspect it’s like Cheeky Mouse where the idle noise really is present, but if it’s really 40kHz any part of it that actually made it out the speakers would be drowned out in a noisy arcade.
  • If it goes in without being fixed up, it at least needs clear markup indicating what’s wrong with it. The comments about the PARATA circuit don’t describe the actual circuit used.

I also get the feeling that if this goes in as-is, people will do a media blitz over-selling it as “accurate” dribbling, but it will just sit in that state forever without being fixed properly.

It’s feeling increasingly pushy. It’s only been open a few days. It seems I’m the only one actually looking at this, and I’m doing my best to explain things, but it’s now turning into a constant distraction making it hard to actually get other overdue stuff done.

@aovestdipaperino
Copy link
Contributor Author

It would be nice to have a datasheet for the JFETs. The schematic seems to say that the JFETs are 2N3812, but the only 2N3812 I know of is a matched pair of NPN transistors for differential amplifier input stages. I’m wondering if the schematic is wrong and the JFETs are actually some other type. Do you have access to a board? Can you check what type of JFETs are used? Alternatively, is there a parts list or something that can be cross-checked against the schematic?

I am pretty sure it's a typo in the schematic as there is 2N3821 JFET.

@cuavas
Copy link
Member

cuavas commented Mar 18, 2024

It would be nice to have a datasheet for the JFETs. The schematic seems to say that the JFETs are 2N3812, but the only 2N3812 I know of is a matched pair of NPN transistors for differential amplifier input stages. I’m wondering if the schematic is wrong and the JFETs are actually some other type. Do you have access to a board? Can you check what type of JFETs are used? Alternatively, is there a parts list or something that can be cross-checked against the schematic?

I am pretty sure it's a typo in the schematic as there is 2N3821 JFET.

That doesn’t seem right – a 2N3821 is a VHF amplifier JFET in a TO72 metal can package (i.e. for the RF or IF section of VHF radio receivers). I wouldn’t expect them to use something that expensive for a simple audio function generator.

@aovestdipaperino
Copy link
Contributor Author

It’s more complex than necessary – you’d be using a model with hysteresis to simulate something without hysteresis. It would be better if you just wait for someone to help you with a simplified LM339 model.

Considering that propagation delay are ignored I am having the exact opposite problem, i.e. the oscillation stops a bit too late because the last few oscillations shouldn't happen. I am working around it by using a minimum voltage check that reflects the real hardware cut-offs and the result is great.

@aovestdipaperino
Copy link
Contributor Author

aovestdipaperino commented Mar 18, 2024

It’s feeling increasingly pushy. It’s only been open a few days. It seems I’m the only one actually looking at this, and I’m doing my best to explain things, but it’s now turning into a constant distraction making it hard to actually get other overdue stuff done.

Vas, as much as I want to get this merged in, I agree with you as it seems we are very close. OTOH if you trust me to push this to completion, we can merge after:

  • you take a look at the Diff Schmitt trigger alternative and you agree (it's not very apart from a simplified comparator model); link to the draft here; this is a temporary stop-gap but good quality still, until a better model is provided.
  • I remove the artificial circuitry and replace with sound/flt_biquad.cpp or sound/flt_rc.cpp
  • I split the PARATA from STOP_PALLA netlists, and I put comments in what needs to be done with the JFETs; I can add the JFET netlist in there behind a define so it's easy to pick it up later or at least review that I haven't done something obviously wrong
  • we mark it as imperfect audio in the dribling.cpp

What do you think?

@aovestdipaperino
Copy link
Contributor Author

aovestdipaperino commented Mar 19, 2024

As rb6502 said:

Given nobody actively works on this stuff and this is a major improvement over the current state as things stand, is there some acceptable imperfect version we can get in and IMPERFECT_SOUND? MAME's supposed to have the best version of what we know about the hardware, and this is significantly better than not having sound or using samples.

Ok, the simplified ideal comparator works great! (kudos to Vas for providing the Schmitt trigger code; and me for adapting it). Below is the output of the STOP_PALLA sound and looks incredibly close to the expected, no work arounds required.

Considering that:

  • STOP_PALLA and (fake) PARATA are using this new LM339
  • the real PARATA netlist is in the code and documented for other people to pick up
  • no fake circuitry is present aside the 3rd order filter to remove aliasing as suggested
  • the ideal comparator can be improved further if necessary as it's well isolated from the rest
  • the GAME is marked MACHINE_IMPERFECT_SOUND until someone can fix the PARATA netlist

Can we converge on fixing the remaining code review issues - if any - and get it merged?

output

Comment on lines 583 to 590
// 3rd order Butterworth filter approximation.
SUBMODEL(output_filter, OUTPUT_FILTER)
SUBMODEL(output_filter, OUTPUT_FILTER_2)
SUBMODEL(output_filter, OUTPUT_FILTER_3)
NET_C(OUTPUT_FILTER.INPUT, Q_OUT.C)
NET_C(OUTPUT_FILTER.GND,OUTPUT_FILTER_2.GND, OUTPUT_FILTER_3.GND, GND)
NET_C(OUTPUT_FILTER.OUTPUT, OUTPUT_FILTER_2.INPUT)
NET_C(OUTPUT_FILTER_2.OUTPUT, OUTPUT_FILTER_3.INPUT)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

24kHz corner frequency is too high. For a 48kHz sample rate, the Nyquist frequency is 24kHz. You want anything above this to be suppressed significantly. This isn’t going to happen if you have the corner frequency right at the Nyquist frequency. The TDA2003 has a cutoff frequency of 15kHz – that might be a better choice.

Also, this is an over-complicated way of doing this. You don’t need to cascade three first-order filters. It’s more efficient to do it in a single stage with the Sallen-Key arrangement.

For example there’s a tool to calculate parameters here: http://sim.okawa-denshi.jp/en/Sallen3tool.php

For example you can select a Butterworth filter approximation and enter 15000 for the frequency and see what it produces.

And why are you using an LM324 in each stage of your output filter in the first place? You only need one op-amp, not four, and I suggested using an idealised op-amp model, as the purpose is just to suppress high-frequency components that would otherwise cause aliasing.

For example you could try OPAMP(TYPE=1 FPF=5 RI=1M RO=50 UGF=1M) as a starting point.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TDA2003 has a cutoff frequency of 15kHz – that might be a better choice.

This is (seemingly, unless I'm misreading the datasheet?) only true if the Cx and Rx components are populated, and in the case of Dribbling, this doesn't seem to be the case. I'm not sure if the TDA2003 circuit has any specific filter cutoff at all, without those components. Again, I could be wrong.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed that this works:

    // 15KHz low-pass filter
    OPAMP(AMP, "OPAMP(TYPE=1 FPF=5 RI=1M RO=50 UGF=1M SLEW=1M VLH=0.5 VLL=0.03 DAB=0.0015)")

I had to populate more parameters, but I used the ones from the example and it sounds well. But I am no expert.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TDA2003 has a cutoff frequency of 15kHz – that might be a better choice.

This is (seemingly, unless I'm misreading the datasheet?) only true if the Cx and Rx components are populated, and in the case of Dribbling, this doesn't seem to be the case. I'm not sure if the TDA2003 circuit has any specific filter cutoff at all, without those components. Again, I could be wrong.

Nah, 15kHz is the typical -3dB frequency with the recommended R1/R2/R3/C2/C5, which are all included on the Dribbling schematic. You can add Rx and Cx to lower the upper cutoff frequency

The formula they give is Cx=1÷(2×π×B×R1) so B=1÷(2×π×Cx×R1). With the test circuit values of R1=220Ω and Cx=39nF, you get B=18.5kHz. It adds additional first-order roll-off above 18kHz, but it doesn’t change the -3dB point.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TDA2003 has a cutoff frequency of 15kHz – that might be a better choice.

This is (seemingly, unless I'm misreading the datasheet?) only true if the Cx and Rx components are populated, and in the case of Dribbling, this doesn't seem to be the case. I'm not sure if the TDA2003 circuit has any specific filter cutoff at all, without those components. Again, I could be wrong.

The filter fixed the aliasing which is an emulator issue only though. Not really replacing the TDA2003 in strict sense.

@cuavas
Copy link
Member

cuavas commented Mar 19, 2024

Can you please slow down and take this one step at a time? It’s just going to take longer if you get ahead of yourself and keep making big changes like this.

Let’s get the filtering sorted out first, then worry about the other issues.

Please back out the comparator device. We’ll get to making a local approximation of the LM339 in your netlist later.

Please read my other comment:

  • You don’t need to cascade three filter stages.
  • You don’t need quad op-amp devices.
  • You only need a single op-amp to implement a third-order low-pass filter (this is one reason I suggested a third-order filter).
  • You can use an idealised op-amp model since we’re just adding the filter to suppress high frequencies that will cause aliasing issues.

@aovestdipaperino
Copy link
Contributor Author

aovestdipaperino commented Mar 19, 2024

Done.
Also, as mentioned earlier, I split the PARATA and STOP_PALLA netlists.
Duplicated the PARATA into "fake one but good enough" and one with the JFETs (not working).

I would say that, without a proper comparator, looking at JFET would be premature. How do we know they work properly if we use the LM324 instead?

Put my attempt at a poor man comparator here.

Copy link
Member

@cuavas cuavas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, this is looking better. I need a bit more time to get back to you on the next step. You’ll need to be patient, but I’m sure we’ll get to a state where it can be merged.

src/mame/mr/nl_dribling.cpp Outdated Show resolved Hide resolved
src/mame/mr/nl_dribling.cpp Outdated Show resolved Hide resolved
@aovestdipaperino
Copy link
Contributor Author

OK, this is looking better. I need a bit more time to get back to you on the next step. You’ll need to be patient, but I’m sure we’ll get to a state where it can be merged.

Okay, I am not impatient, it's just cognitive burden that I would love to get rid off ASAP.

@cuavas
Copy link
Member

cuavas commented Mar 20, 2024

Rather than trying to write a model in C++, I’d suggest just building a simplified LM339 model in your netlist using the AFUNC helper for the non-linear comparison function. Some of the AFUNC examples in src/lib/netlist/examples seem to be outdated, using postfix notation (RPN) while current code seems to use infix notation.

The AFUNC has numbered inputs A0, A1, etc., and output Q. I haven’t tested this, but the basic approach I’d try to use would something like this – the input is connected directly to the AFUNC, the output is a voltage-controlled current source limited to 20mA, with a gain of 15A/V, and the output impedance is 50Ω (arbitrary figure):

static NETLIST_START(lm339_ideal)
{
	AFUNC(COMPARE, 2, "max(A1 - A0, 0)")
	LVCCS(G1, G=15 CURLIM=0.02)
	RES(RO, 50)

	NET_C(COMPARE.Q, G1.IP)
	NET_C(G1.ON, RO.1)
	NET_C(GND, G1.IN, G1.OP, RO.2)

	ALIAS(PLUS, COMPARE.A0)
	ALIAS(MINUS, COMPARE.A1)
	ALIAS(OUT, G1.ON)
}

Things I’m not sure about:

  • I think the AFUNC output is referenced to ground, but I’m not sure – if it isn’t, the connection to G1.IN (negative input to the voltage-controlled current source) needs to be corrected.
  • I hopefully got the polarity right – if I didn’t, I think the G1.ON and G1.OP connections need to be reversed.

Is that enough for you to use as a starting point?

@cuavas
Copy link
Member

cuavas commented Mar 20, 2024

output impedance is 50Ω (arbitrary figure)

To be clear, I think that figure is too low. The output impedance should be higher than that since it acts as a current sink.

@aovestdipaperino
Copy link
Contributor Author

Sounds good.
I was directed towards a native C++ solution when you said

Look at the implementation of the op-amp in the C++ code.

This is a great starting point and if there are no hidden traps (perf?) I can finish from it.

In the meantime the full PARATA netlist is there for you to look for obvious mistakes or suggestions.

@aovestdipaperino
Copy link
Contributor Author

It works. I compared the FUNC based comparator with the NATIVE one and the results are similar.
Test code:

#include "netlist/devices/net_lib.h"
#define LM339_MODEL LM339_FUNC
// Path: src/mame/mr/comp_test.cpp
static NETLIST_START(LM339_NATIVE) 
{
	// * CONNECTIONS:   NON-INVERTING INPUT
	// *                | INVERTING INPUT
	// *                | | POSITIVE POWER SUPPLY
	// *                | | | NEGATIVE POWER SUPPLY
	// *                | | | | OPEN COLLECTOR OUTPUT
	// *                | | | | |
	// *                1 2 3 4 5
	NET_MODEL("DiffComp     DIFF_COMP(VI=5 RI=1000M VOFF=0.1 ROFF=0.1 VON=5 RON=100M)")
	DIFF_COMP(A, "DiffComp")
	ALIAS(1, A.PLUS)
	ALIAS(2, A.MINUS)
	ALIAS(3, A.VCC)
	ALIAS(4, A.GND)
	ALIAS(5, A.OUT)
    ANALOG_INPUT(V12, 12)
    ANALOG_INPUT(VM12, -12)
    NET_C(V12, A.VCC)
    NET_C(VM12, A.GND)
}

static NETLIST_START(LM339_FUNC) 
{
	AFUNC(COMPARE, 2, "max(A1 - A0, 0)")
	LVCCS(G1)
    PARAM(G1.G, 15)
    PARAM(G1.CURLIM, 0.02)
	RES(RO, RES_M(100))
    RES(RI1, RES_M(1000))
    RES(RI2, RES_M(1000))

    NET_C(PLUS, RI1.1)
    NET_C(MINUS, RI2.1)
    ANALOG_INPUT(GND, 0)
    NET_C(GND, RI1.2, RI2.2)

	NET_C(COMPARE.Q, G1.IP)
	NET_C(G1.ON, RO.1)
	NET_C(GND, G1.IN, G1.OP, RO.2)
	ALIAS(PLUS, COMPARE.A0)
	ALIAS(MINUS, COMPARE.A1)
	ALIAS(OUT, G1.ON)

    ALIAS(1, PLUS)
	ALIAS(2, MINUS)
	ALIAS(5, OUT)
}


static NETLIST_START(test)
{
    SOLVER(Solver, 1000)
	PARAM(Solver.DYNAMIC_TS, 1)
	PARAM(Solver.DYNAMIC_MIN_TIMESTEP, 1e-5)
    ANALOG_INPUT(I_V5, 5)
    TTL_INPUT(I_POS, 0)
	NET_C(GND, I_POS.GND)
	NET_C(I_V5, I_POS.VCC)
    TTL_INPUT(I_NEG, 0)
	NET_C(GND, I_NEG.GND)
	NET_C(I_V5, I_NEG.VCC)

	SUBMODEL(LM339_MODEL, DIFF_COMP)

    NET_C(I_POS, DIFF_COMP.1)
    NET_C(I_NEG, DIFF_COMP.2)



    RES(R1, RES_K(2.2))
    NET_C(I_V5, R1.1)
    NET_C(DIFF_COMP.5, R1.2)


    LOG(OUTPUT, R1.2)
}

Native implementation:

0.000000000e+00 0.000000e+00
2.000000000e-10 4.999736e+00
2.500000001e+00 -1.189923e+01
2.900000001e+00 4.999736e+00
3.500000000e+00 4.999736e+00

Func implementation:

0.000000000e+00 0.000000e+00
2.000000000e-10 4.999890e+00
2.500000001e+00 -3.899914e+01
2.800000001e+00 4.999890e+00
3.500000000e+00 4.999890e+00

Input file:

0.500,I_POS.IN,1.0
0.500,I_NEG.IN,0.9
1.000,I_POS.IN,0.7
1.500,I_POS.IN,5
2.000,I_NEG.IN,5
2.500,I_POS.IN,0
2.800,I_NEG.IN,0
2.900,I_POS.IN,0.001

I will replace the LM339 in the netlist with this one and generate the STOP_PALLA output next.

@aovestdipaperino
Copy link
Contributor Author

aovestdipaperino commented Mar 20, 2024

Nope, when I plug it in in the STOP_PALLA netlist it keeps running forever.
You can observe the same behavior when you remove the m_last_state out of the SCHMITT TRIGGER.
Checking the last state seems to be more than an optimization.

Replacing it with AFUNC(COMPARE, 2, "if(A1 > A0, 1, 0)") doesn't help (just for testing)

3.000000 seconds emulation took 12235.432605 real time ==> 0.02%

I don't know why it's this slow. Given that I already implemented the native C++ can we switch to that and make it do the same thing this code does with LVCCS, etc.?

@cuavas
Copy link
Member

cuavas commented Mar 21, 2024

Can you try this substitute LM339 model? It has a more detailed output stage. I tested it, and it seemed to sound OK. Hopefully it’s enough for now.

static NETLIST_START(LM339)
{
	// * CONNECTIONS:   NON-INVERTING INPUT
	// *                | INVERTING INPUT
	// *                | | POSITIVE POWER SUPPLY
	// *                | | | NEGATIVE POWER SUPPLY
	// *                | | | | OPEN COLLECTOR OUTPUT
	// *                | | | | |
	// *                1 2 3 4 5
	NET_MODEL("LM339_QO NPN(BF=200)")

	AFUNC(CMP, 2, "min(max(A1 - A0, 0), 1e-6)")
	VCCS(QB, 80)
	QBJT_EB(QO, "LM339_QO")
	ANALOG_INPUT(XGND, 0)
	RES(RDUMMY, RES_M(1000))

	NET_C(CMP.Q, QB.IP)
	NET_C(QB.OP, QO.B)
	NET_C(XGND, QB.IN)
	NET_C(QB.ON, QO.E, RDUMMY.2)

	ALIAS(1, CMP.A0)
	ALIAS(2, CMP.A1)
	ALIAS(3, RDUMMY.1)
	ALIAS(4, QO.E)
	ALIAS(5, QO.C)
}

I did a quick test of the PARATA using NMOS depletion mode MOSFETs. Unfortunately getting it to work isn’t going to be as easy as I hoped.

@aovestdipaperino
Copy link
Contributor Author

It doesn't sound OK, it sounds great.
I propose we merge this after code review.
Afterwards, we can work on an attempt to fix the JFET circuitry for max accuracy on the PARATA sound.

@cuavas cuavas merged commit aadd517 into mamedev:master Mar 21, 2024
5 checks passed
@cuavas
Copy link
Member

cuavas commented Mar 21, 2024

And we’re done. One more game with sound!

@cuavas
Copy link
Member

cuavas commented Mar 21, 2024

I’m getting decent performance with a debug build using the static solvers on a Coffee Lake notebook. It should be OK for anyone with a reasonably up-to-date PC.

@aovestdipaperino
Copy link
Contributor Author

I’m getting decent performance with a debug build using the static solvers on a Coffee Lake notebook. It should be OK for anyone with a reasonably up-to-date PC.

If this runs great on an RPi5 especially with bgfx on, I am going to build a dedicated mini-cabinet for it.
Thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants