Sound_InstallVoice
Jon Abbott (1421) 2651 posts |
The PRM documentation implies that an existing Voice Generator can be replaced:
For Use, it says:
So I’d expect the following SWI to work: However, what happens is the new Voice Generator is not installed and it returns: This doesn’t match what the documentation says, as you’re expecting R0 to point to a null terminated string is there’s an error. Its consistent from RO3.1 right up to 5.21 so I’m guessing it’s the documentation that needs to detail what happens if you try to replace an existing Voice Generator. For the time being, I’ve updated the Wiki documentation to state explicitly what currently happens. I tested with the following code:
If you then remove WaveSynth-Beep via: And then install the new one: It returns: So it looks like R0 is preserved if the SWI succeeds, which again isn’t covered in the documentation. |
Rick Murray (539) 13840 posts |
“You’re holding it wrong.” Abort on Brain Fetch at &øflåøflåøflåøflå! To be honest, I think it is pretty dumb not to set R0 to Additionally, this is a grey area but…
Update: looks like the documentation is utterly wrong and this SWI cannot change an allocated voice (must be done manually – Remove old then Install new), and as such there is no possible way it can return the previous anything. It actually appears to return information on the current voice (see below). |
Jon Abbott (1421) 2651 posts |
I’m presuming if R1 = 0 to mean R0 will be a null terminated string if on exit R1 = 0 Neither the documentation or actual results make a lot of sense to me. Why for example would you return the previous voice name, if it’s been removed? |
Rick Murray (539) 13840 posts |
Yeah – this does look like a contradiction. The SWI “Sound_InstallVoice” appears to be capable – in the documentation – if replacing a specified voice with a new one (else why would R0 be the name of the previous voice on exit?), however the code does this:
…which to my eyes is pretty much a “something here so bomb out”. Furthermore, instead of returning a pointer to an error string, it just returns a pointer to the existing voice name. |
Rick Murray (539) 13840 posts |
<sheepish grin> – yeah, that would make sense. ;-)
Exactly what I pointed out above. I rather imagine the documentation writers had plans and aspirations that were stuck to a post-it note that fell down the back of a radiator and never made it to being actual code. |
Rick Murray (539) 13840 posts |
Eyeballing the code is complicated thanks to overloading the SWI with special values (R0 = 0-3 instead of a pointer). So…
[code path → handled in installvoice1] (nb. code paths given for those |
Jon Abbott (1421) 2651 posts |
LOL Just tested RO2 and it’s the same behaviour as above, so one can only presume the documentation needs amending. I don’t have an Arthur machine to test, but it’s probably the same. |
Rick Murray (539) 13840 posts |
Looks like this can return an error with V set – see line 924. What would this be? Null pointer? R0 is “preserved” in a manner of speaking. ;-) [code path if R1 = 0 → installvoice1 → %50 → %40 → CheckAttachments → install_change (if local name) → done |
Rick Murray (539) 13840 posts |
[code path → same as R0=0 with some added fluff] |
Rick Murray (539) 13840 posts |
R2 must be zero, else could return a V-set error (same code as for R0=1, line 924). [code path → installvoice1 → install_change] |
Rick Murray (539) 13840 posts |
And finally…
The preserving Z flag in CheckAttachments is so we know if R0 on entry was 1 or not (result of TEQ at line 864); as this shares code with R0=1 functionality. [code path if R1 = 0 → installvoice1 → %50 → %40 → CheckAttachments → done |
Rick Murray (539) 13840 posts |
Phew. I’m off to brew a nice cuppa. Good luck distilling that into an easy-to-read page of documentation! |
Jon Abbott (1421) 2651 posts |
Thanks Rick, I’ll endeavour to update the documentation based on your findings. |
Rick Murray (539) 13840 posts |
Open message to those more familiar with the RISC OS sound system… The installvoice2 code does a bail-out if a voice slot currently has a voice present. As a consequence, it sets R1 to zero (fail) and R0 to point to the name of the existing voice generator. This contravenes the published API12. Would there be any serious harm if instead the fail action was to stack the important registers, then call Sound_RemoveVoice automatically? Off the top of my head, this doesn’t look like a complicated job. 1 Documentation does not state, but implies that voices can be installed into slots with existing voices; hence the “previous name” return result. 2 The documentation says that if R1=0, then R0 points to a null-terminated error string. [note: it’s a string, not an error block] The name of a voice generator isn’t exactly a good error message, is it? Why did your sound handler fail to install? “WaveSynth-Beep”. Wait, are you Kafka? “Wibble.” |
Rick Murray (539) 13840 posts |
Done, with the exception that R0 on exit returns the address of the CURRENT voice handler, not the name of the previous. This is because I had no spare registers without messing with the entire Install function and its entry/exit points. I wanted this to be as simple as possible. It worked here with light testing (using Jon’s test code above). For those who want to try this and see if it blows up… The file is …castle.RiscOS.Sources.HWSupport.Sound.Sound1.s.Sound1. Change this (Zap line #842):
to this:
Go down a screenful (around Zap line 932) and you should see this:
Immediately below that, insert this (just above “NullVoice”):
This should sanely deal with the exit conditions, but this has not been tested (it’s stuff like OS_Module RMAFree failing). To build a version to play with, the easy-but-long way is to build a new ROM. The harder-but-simpler way is to build a softload. To go this, back up to the “Sound1” directory and copy MkROM as MkSA. Open MkSA and where it says After a bit of activity, your module will be in …Sound1.rm.ARM11ZF as “Sound1SA”. Double-click it. You will have no sounds and no voices. So open a TaskWindow and type:
Then you can play. :-) If you copy Jon’s program from the top of the page, you can tweak the name “TestVoice”, “TestVoice1”, “TestVoice2”, etc and you’ll see you can drop one on top of an existing and it’ll replace it. Note that Jon’s code specifies slot #1 so you’ll wipe out WaveSynth. It might be an idea to tweak it to initially use a different channel? Now, the really interesting thing is that if you use Jon’s code and knock out WaveSynth and then ReInit WaveSynth, it will nuke whatever was there and reinstate itself as voice #1. ;-) |
Rick Murray (539) 13840 posts |
Oh – and a quick note before anybody says anything. If you are using the test program, this is expected and correct:
The reason for this is simple. Jon’s test program assembles into a bit of BASIC memory and then links that into the sound handler. The addresses, name, pointers, etc are only valid for as long as that program is active. This leading to a quirky situation where if you run the test program in a taskwindow, using *Voices in the same taskwindow will work, but a different taskwindow will abort. Just thought I’d mention this before anybody stumbles upon it… [hint: quick fix: |
Jon Abbott (1421) 2651 posts |
I’ve modified the example code to use the RMA, so you don’t get an Abort after exiting BASIC. If this quirk has been around since RO2 and quite possibly Arthur, should we be changing the behaviour now? Admittedly it’s an unlikely scenario that code would rely on a voice failing to be replaced. It’s also unlikely that many people will be using the 8bit sound system these days, SharedSound has pretty much replaced it. My only reason for raising this was to clarify the documentation, which I’ve been adding to the Wiki as I’ve been coding the 26bit sound layer in ADFFS. The documentation in the PRM doesn’t provide concise enough information around implementing Channel Handlers, certainly not to the level of detail I required. As with MiscOp and DiscOp, I had to study the source code to find out what actually happens, not what’s supposed to happen! |
Steve Pampling (1551) 8170 posts |
At this point click Builder on the iconbar and select a “Build Directory” and “Environment” – causing builder to set up the appropriate paths (as below). Now close the builder window follow the rest below.
All works nicely. |