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

Possibility to play music while systemUse ? #160

Open
rveilleux opened this issue Oct 11, 2022 · 9 comments · May be fixed by #209
Open

Possibility to play music while systemUse ? #160

rveilleux opened this issue Oct 11, 2022 · 9 comments · May be fixed by #209

Comments

@rveilleux
Copy link
Contributor

Currently in ACE, enabling system (for example when reading files) will "stop" the music from ptPlayer:
If the music was already playing, it will start to "stutter" by looping the last buffer, up until SystemUnUse() is called.

Could it be possible to leave some interrupts running even when the System is in use?
(I my case, I am trying to PTPLAYER_USE_VBL instead of CIAB-TimerA, but the same problem applies to both cases).

@tehKaiN
Copy link
Member

tehKaiN commented Oct 11, 2022

Ideally, ACE should fully work with system enabled, but I really haven't got time to work on it. This needs writing os-friendly interrupt handlers/servers which would work when OS is enabled, switching back to raw interrupt handling functions when it's disabled.

It's a bit tricky to do in a "portable" way - VBCC and Bebbo's compilers allow declaring functions which expect their parameters in specific CPU registers, Bartman's suite not so much. Needs a bit of inline assembly trickery to properly handle input params and return value to handle those interrupts the way OS expects it to.

I remember doing one attempt but I can't see any branch with it. Perhaps I got stuck somewhere and haven't commited anything which would work in at least partial way. If you're willing to tackle it I can give you some hints and elaborate aforementioned stuff a bit more.

@rveilleux
Copy link
Contributor Author

rveilleux commented Oct 12, 2022 via email

@tehKaiN
Copy link
Member

tehKaiN commented Oct 12, 2022

Okay, so basically it goes like this:

  • notice that ace/managers/system.c has interrupt handler for each main interrupt as well as CIA interrupts
  • those handlers only work when system is off, because OS needs its own handlers to do things properly (iirc one of CIA timers handles multitasking) - required for memory allocation and file access
  • the OS-compliant way to hook into interrupts is to register the proper functions. Some interrupts need handlers, other require servers. There are some differences between them on how they work, introduce different amount of latency etc. See https://wiki.amigaos.net/wiki/Exec_Interrupts for more info.
  • IIRC, interrupt handlers as well as servers require a specific function format which has some stuff passed into them via proper registers - it's kinda similar to ACE's handling - one arg contains pointer to custom chip struct (set in a0?), the other is user-specified data (probably d0?). The function then must return its result as CPU's Z status flag - basically it's set to 1 if interrupt was handled by your function and to 0 if your function ignored it and should be processed by another registered handler. Or maybe values are the other way around, I don't quite remember it.
  • regarding implementing of those functions - it's quite easy on Bebbo's GCC or VBCC because ACE's REGARG() macros allow for treating specific registers as parameters. The Z flag thingy is done by adding amigainterrupt or interrupt attribute to the function declaration. On Bartman's suite it's not so trivial - you probably can write register void *foo __asm("a0"); at the beginning of the function body to get the value from the register, and set the Z flag with inline asm which e.g. xors d0 with d0, but there is no warranty that compiler won't reorder those things and put some instructions before/after them. I'm almost certain that I've checked the disassembly of such function and found out that compiler added some unwanted stuff, trashing the Z flag. What is needed to do to be 100% failproof is to write interrupt handler wrapper in asm, which would:
    • push register values from params to stack
    • call proper interrupt-handling C function
    • after returning, set the Z flag properly
  • also there's input problem. OS handles keyboard by itself and current interrupt code from ace/managers/key.c should only be used when OS is dead. Under system operation, creating a high priority input handler would be necessary, which would intercept all the key presses before other OS parts do anything with them. This is uncharted territory for me, but I've seen this suggestion quite often when discussing os-friendly low level input processing.

I hope that this wall of text won't spook you out of this. ;) The problem is quite elaborate and it won't be an easy one-hour fix, but it's almost certainly doable with a bit of persistence.

@rveilleux
Copy link
Contributor Author

Thank you for your explanation: I was able to hack by using AddIntServer(INTB_VERTB, &rvInt3Server); and hacking the ptPlayer to only use vblank (instead of using both vblank + CIA_B_timer_B. Unsure about the exact purpose of this timer B, since the music appears to play fine. In the vblank interrupt I simply do the three steps:
intPlay(pCustom);
intDmaOn(pCustom);
intSetRep(pCustom);
But perhaps it is not fully compatible with all use-case.
According to my tests, using my OS-friendly IntServer allows my music to continue to play while reading from disk (or harddrive).

@tehKaiN
Copy link
Member

tehKaiN commented Oct 14, 2022

Hi,

I've once asked Frank Wille, the author original asm version of ptplayer, about that:

What I wanted to ask you is the nature of timer interrupt B - it's used for dmaon / setrep and it's configured as one-shot to be triggered after 496 ticks. My question is - where does this exact delay come from? Why not launch that code instantly but wait for ~11 scanlines? I tried to look it up on the net but I've only found that P61 replayer uses this delay too. I'm really confused by that - it seems to be one of those "so trivial that it doesn't need explanation" things but I cant figure it out. 😉

The response was:

wanted to ask you is the nature of timer interrupt B - it's used for dmaon / setrep and it's configured as one-shot to be triggered after 496 ticks. My question is - where does this exact delay come from?

A raster line has a DMA slot for each audio channel. Depending on the current AUDxPER of the audio channel some of these DMA slots are not used. For example: when playing a very high frequency the DMA might have to read a new sample every line. But when playing a very low frequency there might be 10 or more raster lines without DMA activity for this channel.

The problem is that changes like DMA-on/off, or setting a new sample-pointer in AUDxLC, are only recognized by Paula when the next sample data are read from Chip-RAM.

So we have to be prepared for the worst case, which means that up to 11 raster lines may pass without DMA-activity (reading a new sample), when playing the lowest note from the lowest Protracker octave.

You could also solve that with audio interrupts, like the audio.device does. But most players just use a constant delay - and the worst players even busy-wait. 😉

Why not launch that code instantly but wait for ~11 scanlines?

Does not work. You have to cut the old sample before starting the new one.

When the player routine sees a new note/sample for a channel it will immediately set the new period and sample pointer. But we don't want to wait until the current instrument has played until the end (some instruments are long), but cut it immediately. So we will disable DMA for the channel at this point.

To make Paula see that DMA-Off we have to wait until the next DMA slot. When we reenable DMA before that point, then it remains unnoticed. This is the reason we have to wait for at least 496 ticks before enabling DMA for the new note/instrument.

Then you have to wait for the next DMA slot again, before you can change the sample pointer and length for the repeat. Otherwise Paula would play the repeat-part immediately.

I guess I should include some of that knowledge inside the code so that this design choice is better documented. So I guess the code works for you because your MOD's instruments have quite high sample rate and the natural time spent on code execution does its work. To make this work properly we'd need to have that CIA interrupt, sadly.

@rveilleux
Copy link
Contributor Author

Hi KaiN,

According to https://aminet.net/package/mus/play/ptplayer, you are using 576 ticks in your code so it would appear you are using ptplayer at least from version 6.0.
But in ptplayer.h, a comment says:
// Protracker V2.3B Playroutine Version 5.1
Do you know what version your C source code is based off?

Thank you very much!
(Indeed you are right, omitting the timerB is messing up with the playback of many MODs: it will have to revert some of my hacks)

@tehKaiN
Copy link
Member

tehKaiN commented Oct 16, 2022

it's based around 5.1 with the only 6.0 change being the 576 tick thingy of which I got tip about from the original author before 6.0 released. ;) There's an ACE branch which has changes up to version 6.2, but it's not tested yet and most probably is bugged and definitely breaks vblank thingy at the moment. When I get back from my leave I'll continue working on it.

@rveilleux
Copy link
Contributor Author

rveilleux commented Oct 16, 2022 via email

@tehKaiN
Copy link
Member

tehKaiN commented Oct 16, 2022

it's great to hear that!

Unfortunately my asm skills aren't that good and it was quite painful for me to do in the first place. The reason for the translation is that I wanted to have more control over the source code by having it easier to read, but it kinda backfired - now it's riddled with bugs due to inaccurate asm-C translation, but I guess we'll eventually get to the point of it being stable.

It's quite probable that my access to network connection will be limited next week, but I'll try to watch out for your pull requests and accept them as they go.

@tehKaiN tehKaiN linked a pull request Sep 24, 2023 that will close this issue
6 tasks
@tehKaiN tehKaiN added this to the TBD before C++ experiments milestone Oct 29, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: To do
Development

Successfully merging a pull request may close this issue.

2 participants