Virtualc64: Add support for multiple SIDs

Created on 30 Nov 2020  Â·  25Comments  Â·  Source: dirkwhoffmann/virtualc64

Bildschirmfoto 2020-11-30 um 21 43 35

VirtualC64 3.4: 🙈

Bildschirmfoto 2020-11-30 um 21 42 19

enhancement

Most helpful comment

I've got a prototype working that pretends to have two SIDs. It works in the sense that the demo thinks there is a second SID on board (I am using the old version of the demo which terminates if no second SID is found):

Bildschirmfoto 2020-12-07 um 16 25 17

My second SID does not contribute to the final audio stream yet. In other words: Only the primary SID is audible. The audio stream sounds strange, though. There are some pops in the background that I don't know where they come from. What I noticed is that the demo makes heavy use of the audio filter and model-specific SID properties. On the 6xxx, sound is very different from the 8xxx, and so it is when the audio filter is disabled. Maybe the pops would disappear if the audio filter is parameterized in a certain way (VICE allows setting filter parameters, VirtualC64 does not).

It'll be interesting to find out how the primary SID sounds like in VICE if the second SID is muted.

All 25 comments

On how to detect a second SID

https://csdb.dk/forums/index.php?roomid=14&topicid=99181&showallposts=1

On the end of this blog is a detection program and its source code

https://www.lemon64.com/forum/viewtopic.php?t=68500

For reference: Some demos using 2 SIDs (in case we decide to support this feature):

Nachrichten-Bild(350170708)

@mithrendal Thanks for searching

This turned out not to be a bug. It's just that the demo expects a second SID and does not find one. The good news is that it shouldn't be too difficult to support multiple SIDs in VirtualC64.

TODOs

  • Add support for up to 4 SIDs.
  • Add an Audio panel similar to the one in vAmiga. In vAmiga, the user can individually scale every audio channel and distribute them freely to the left or right speaker. In VirtualC64, the panel can have a similar look. Each Paula-channel will be the output of one SID in this case.

Screenshot from vAmiga:

Bildschirmfoto 2020-12-01 um 19 52 44

Some planning before coding...

  • Requirements:

    There are two approaches to map additional SIDs. The first one maps the auxiliary chips into the SID memory range ($D400 - $D7FF). The second one maps them into the cartridge memory range ($DE00 - $DFFF). VICE supports both. Since the latter case requires some additional checks inside the emulator (accesses to the cartridge port have to be rerouted if a SID is mapped into this area), we should try to find out how important this mapping area is. If it turns out that most demos expect the additional chips in the SID memory range, we should keep our hands away from the IO1 and IO2 space.

  • Architecture:

    The implementation is pretty much straight forward:

    • Replace SIDBridge::fastSID and SIDBridge::reSID by an array of four
    • Don’t clip the SID address range in C64Memory::pokeIO
    • Reroute to the correct fastSID or reSID chip in SIDBridge::peek / SIDBridge::poke
    • Replace the ring buffer by an array of four
    • Mix channels in SIDBridge::readStereoSamples
  • Preparation:

    Before adding new code…

    • Port vAmiga's RingBuffer class over to VirtualC64
    • Replace SIDBridge::ringBuffer by an object of the new class
    • Remove deprecated methods SIDBridge::readMonoSamples, SIDBridge::readStereoSamplesInterleaved

what about an auto detection of the attacked memory space ?

UserStory:
a program pokes into $d420 adress space .... to play a tune on a stereo sid

Action:
Sidbridge automatically activates an additional SID at $d420 without asking the User to do this via settings ....

what about an auto detection of the attacked memory space ?

Such approaches usually don't work reliably for the C64, because games and demos do all kind of "irregular" stuff. In the case of SID, the relevant memory address space is mirrored every 32 bytes, because only the lower 5 address bits are evaluated. This means that standard SID can be acceseed at a variety of different memory locations (in multi-SID mods, some addresses that would normally address the build-in SID are simply rerouted to the additional chips). As a result, additional SIDs could be auto-activated by "normal" programs, too.

I've got a prototype working that pretends to have two SIDs. It works in the sense that the demo thinks there is a second SID on board (I am using the old version of the demo which terminates if no second SID is found):

Bildschirmfoto 2020-12-07 um 16 25 17

My second SID does not contribute to the final audio stream yet. In other words: Only the primary SID is audible. The audio stream sounds strange, though. There are some pops in the background that I don't know where they come from. What I noticed is that the demo makes heavy use of the audio filter and model-specific SID properties. On the 6xxx, sound is very different from the 8xxx, and so it is when the audio filter is disabled. Maybe the pops would disappear if the audio filter is parameterized in a certain way (VICE allows setting filter parameters, VirtualC64 does not).

It'll be interesting to find out how the primary SID sounds like in VICE if the second SID is muted.

The cracking noise problem is solved (I think). It was just a missing else that caused all reads and writes that were supposed to be routed to the secondary SID to be routed to the primary SID, too. Now, I have a prototype working that produces and combines two audio streams. I tried to compare my audio output with the one produced by VICE, but for some reason I can't get the demo to work there.

@mithrendal: How did you run the demo in VICE?

I tried with the following settings:

Bildschirmfoto 2020-12-08 um 06 10 35

But it still complains that I only have one SID:

Bildschirmfoto 2020-12-08 um 06 08 35

@mithrendal: Thanks for advising me to use the no-sid-check-version in VICE:
http://csdb.dk/getinternalfile.php/206430/to_norah_nosidcheck.prg

VICE sounds exactly the same as the experimentally patched version of VirtualC64 which means we've successfully passed the "proof of concept" phase. Next step will be to decently integrate the new feature into the existing software architecture and GUI.

It seems many entries in csdb are tagged with 2sid 3sid but that doesn’t mean demos without it have only 1sid (to nora is an example)

https://csdb.dk/search/?seinsel=all&search=2sid

https://csdb.dk/search/?seinsel=all&search=3sid

Searching for 4sid results in empty list. Are there no 4sid music demos ? Why?

2sids = 6 voices, 3 sids = 9voices, 4 sids = 12 voices

Maybe 12 simultaneously voices is not so popular for electronic synthesizer music?

Here a top chart list ... many 2sids are inside
https://csdb.dk/toplist.php?type=release&subtype=%287%29&submit=Check%C2%A0chart

for comparison there is a deepsid direct start link ...
https://deepsid.chordian.net/?file=SID%20Happens/To_Norah_2SID.sid

grafik

by clicking on the 1 or 2 we can disable one sid and listen ...

I've added the necessary SID options to the hardware panel:

Bildschirmfoto 2020-12-10 um 11 23 41

It looks a bit cluttered. Maybe I should move the drive and port stuff to a separate Peripherals panel (as in vAmiga). 🤔

by clicking on the 1 or 2 we can disable one sid and listen ...

Very cool site 😎. Never heard about it before.

Bildschirmfoto 2020-12-10 um 11 29 06

The project is pretty much done.

All four SID channels can be mixed freely, just the same way as the four audio channels in vAmiga:

Bildschirmfoto 2020-12-11 um 16 32 39

The SID inspector has been adjusted, too. It allows to examine an arbitrary SID now:

Bildschirmfoto 2020-12-11 um 16 19 36

Last, but not least, the hardware panel has been simplified:

Bildschirmfoto 2020-12-11 um 16 24 07

The drive and control port settings reside on a separate tab now ("Peripherals" which also exists in vAmiga).

@mithrendal: If you wish to incorporate multi-SID support in the web version, the following API calls are key:

C64::configure(OPT_SID_ENABLE, <sid nr>, <bool>)
C64::configure(OPT_SID_ADDRESS, <sid nr>, <address>)

To make Norah sing, set <address> to 0xD420 and enable the second SID (<sid nr> = 1).

VirtualC64 supports "SID hot plugging" which means you can add or remove SIDs any time (assuming there are no bugs, because this wasn't that easy to implement).

when compiling the latest code from master to WASM (which is 32Bit) the emcc compiler complains again ... do you have an idea what it is complaining about ?

In file included from Emulator/C64.cpp:10:
In file included from Emulator/C64.h:45:
Emulator/SID/SIDBridge.h:239:9: error: invalid operands to binary expression ('SerReader' and 'long')
        & config.volL
        ^ ~~~~~~~~~~~
Emulator/SID/SIDBridge.h:256:41: note: in instantiation of function template specialization 'SIDBridge::applyToPersistentItems<SerReader>' requested here
    size_t _load(u8 *buffer) override { LOAD_SNAPSHOT_ITEMS }
                                        ^
Emulator/Foundation/HardwareComponent.h:266:27: note: expanded from macro 'LOAD_SNAPSHOT_ITEMS'
SerReader reader(buffer); \
                          ^
/Users/familie/projects/emsdk/upstream/emscripten/system/include/libcxx/cstddef:78:17: note: candidate function not viable: no known conversion from 'SerReader' to 'std::byte' for 1st argument
constexpr byte  operator& (byte  __lhs, byte __rhs) noexcept
                ^
Emulator/Foundation/Serialization.h:203:5: note: candidate function not viable: no known conversion from 'long' to 'bool &' for 1st argument
    DESERIALIZE8(bool)
    ^
Emulator/Foundation/Serialization.h:186:63: note: expanded from macro 'DESERIALIZE8'

when I look for the variable volL which the emcc compiler is comlaining about ... it is definded as a long in SIDTypes.h but in SIDBridge.h it is a float ...

I've changed the datatype in the config struct to

   // Input channel volumes and pan settings
    i64 vol[4];
    i64 pan[4];

    // Output channel volumes
    i64 volL;
    i64 volR;

The code is on the dev branch.

it is definded as a long in SIDTypes.h but in SIDBridge.h it is a float ...

It's two different variables. config.volis the config parameter which is always an integer (because it is channeled through the configure() API). vol is the internal scaling factor applied to sound samples. It is computed out of config.vol by some sophisticated scaling formula to take the logarithmic aspect of our ear into account.

Ah I see ... now with i64 the new Core with multiSID compiles now completely ... very good

Compiler stops now at the WASm SDL2 wrapper

mainsdl.cpp:310:14: error: no member named 'readMonoSamples' in 'SIDBridge'
    c64->sid.readMonoSamples((float *)stream, n);
    ~~~~~~~~ ^
mainsdl.cpp:713:21: error: no matching member function for call to 'configure'
      wrapper->c64->configure(DRIVE8, OPT_DRIVE_CONNECT,1);
      ~~~~~~~~~~~~~~^~~~~~~~~
Emulator/C64.h:237:10: note: candidate function not viable: no known conversion from 'DriveID' to 'ConfigOption' for 1st argument
    bool configure(ConfigOption option, long id, long value);
         ^
Emulator/C64.h:236:10: note: candidate function not viable: requires 2 arguments, but 3 were provided
    bool configure(ConfigOption option, long value);
         ^
Emulator/C64.h:240:10: note: candidate function not viable: requires single argument 'model', but 3 arguments were provided
    void configure(C64Model model);
         ^
2 errors generated.

definitily due to some API changes ... I will have a look for the replacements

The operand order has been cleaned up. Instead of

wrapper->c64->configure(DRIVE8, OPT_DRIVE_CONNECT,1);

use

wrapper->c64->configure(OPT_DRIVE_CONNECT, DRIVE8, 1);

Now, the config option types always comes first.

and readMonoSamples changed to copyMono ...

void MyAudioCallback(void*  thisC64,
                       Uint8* stream,
                       int    len)
{
    C64 *c64 = (C64 *)thisC64;

    int n = len /  sizeof(float);
    //c64->sid.readMonoSamples((float *)stream, n);
    c64->sid.copyMono((float *)stream, n);
    sum_samples += n;
}

now it completely compiled ... lets just ship it ..

Ok virtual64web starts up
but there is no sound at all ... when changing SID Chip Version an assertion says it is not running ...

I guess now its time to enable them ...

lets first try this

    c64->configure(OPT_SID_ENABLE, 1);
    c64->configure(OPT_SID_ADDRESS, 1, 0x400);

when changing SID Chip Version an assertion says it is not running ...

😱 What exactly does it say?

c64->configure(OPT_SID_ADDRESS, 1, 0x400);

Must be

c64->configure(OPT_SID_ADDRESS, 1, 0xD400);

it says

[0] ( 0, 1) 0000 SIDBridge: WARNING: Invalid SID address: 400

haha I missed the d ... hang on

no complains from the bridge anymore captain...

SIDBridge says ... SIDBridge: config.address[1] = 161ae42

but no sound ... and when I change SID Chip Revision it still complains "not running"

Assertion failed: !isRunning(), at: Emulator/SID/ReSID.cpp,130,setRevision

I have to start up the engine somehow, don't I ?

Assertion failed: !isRunning(), at: Emulator/SID/ReSID.cpp,130,setRevision

You need to embed the call in a suspend() / resume() block

SIDBridge says ... SIDBridge: config.address[1] = 161ae42

It should report 0xD420 🤔

BTW, SID 1 is the "second SID". The built in SID is SID 0. You only need to configure SID 1. Hence, set the address to 0xD420 which is the address where Nora is looking for it.

two SIDs are working fine in vc64web ... thanks dirk

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Alessandro1970 picture Alessandro1970  Â·  3Comments

tsupplis picture tsupplis  Â·  3Comments

bluecursor picture bluecursor  Â·  7Comments

Alessandro1970 picture Alessandro1970  Â·  3Comments

PakkunKinoppi picture PakkunKinoppi  Â·  4Comments