News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu
avatar_Arnaud

CPCEC a new emulator from cngsoft

Started by Arnaud, 08:14, 16 March 19

Previous topic - Next topic

0 Members and 2 Guests are viewing this topic.

pelrun

GPL does not concern itself with embedded assets if they're not executable. Those files are covered by their own licenses, which of course you must follow just as you follow the GPL for the code itself.

BTW the GPL is explicitly not a contract, as contract law and copyright law are distinct in various jurisdictions.

cngsoft

#276
The legality of third-party data of any kind is indeed a problem: the default CPCEC package is already an impure compromise that mixes GPL source files and documentation, precompiled binaries for Windows and third-party firmware files. Ideally, the package should include the sources and docs, nothing else; it would be the user's task to compile the binaries and to bring his own firmware files.

In comparison, the Debian version of the Spectrum family emulator FUSE does it right: the "fuse-emulator-common" package doesn't include any firmwares on its own, and its only firmware-based dependency is "opense-basic", an open-source software replacement. The suggested packages include "spectrum-roms", unavailable on purpose from the main Debian repositories: Sinclair, Amstrad et al. may allow the distribution of the unmodified firmware files, but that doesn't make these files OSS-compliant, and the main Debian repositories cannot accept anything but pure OSS. You must go to the non-free repositories instead to fetch this particular package.

An important negative consequence is that as long as there aren't any OSS replacements of the CPC firmwares (or C64, now that I emulate it) CPCEC and CSFEC (as well as all CPC and C64 emulators in general) will be unacceptable in 100% pure open-source software environments.
(if you can't see the banner right now my server is currently offline)

AmatCoder

Thanks for answers.

So package (source tarball) could contain the ROM files but they cannot be embedded (firmware also is executable code after all...)
It's a pity because it would have been more comfortable for the end user :( .

By the way (and getting back on topic) I want report a possible bug:
The game "007 The Living Daylights" is flickering with last versions of CPCEC (with CRT type 1).

TotO

Quote from: cngsoft on 21:33, 11 August 22Ideally, the package should include the sources and docs, nothing else; it would be the user's task to compile the binaries and to bring his own firmware files.
And nobody will use it.

The problem is to have mixed the CPC/ZX/C64 emulators into your CPC emulator.
"You make one mistake in your life and the internet will never let you live it down" (Keith Goodyer)

cngsoft

#279
Quote from: AmatCoder on 17:54, 13 August 22So package (source tarball) could contain the ROM files but they cannot be embedded (firmware also is executable code after all...)
It's a pity because it would have been more comfortable for the end user :(
Making the job easy for the end users debases pure OSS into tainted freeware with undesired consequences; keeping it pure means all end users either have to install C compilers and learn to use them, or give up altogether as TotO says. Damned if we do, damned if we don't.

QuoteBy the way (and getting back on topic) I want report a possible bug:
The game "007 The Living Daylights" is flickering with last versions of CPCEC (with CRT type 1).
That's expected, the game doesn't setup its split screen properly (the debugger shows a clue of possible CRTC trouble: the screen is 512x304 pixels instead of the normal 512x312): launch that game on Winape or CPCEPower with CRTC1 and see what happens. It even looks like this game should also fail on PLUS ASIC hardware!
(if you can't see the banner right now my server is currently offline)

TotO

#280
It is a problem for you to split them?

- CPCEC (binary + ROMs)
- SPCEC
- C64EC

It is not possible to have a common open source base "EC" used by each CPC, SPC, C64 projects?
"You make one mistake in your life and the internet will never let you live it down" (Keith Goodyer)

Longshot

#281
QuoteThat's expected, the game doesn't setup its split screen properly (the debugger shows a clue of possible CRTC trouble: the screen is 512x304 pixels instead of the normal 512x312): launch that game on Winape or CPCEPower with CRTC1 and see what happens.

Launching the game on another emulator is not a guarantee.
Just that they all emulate badly.
Only the test on the real machine is important.

The game works fine without flicking on a real machine with CRTC 1.

The problem comes from a bad update of R52 when a request to reset the counter occurs exactly on the last microsecond of the HSYNC. In general, R52 is incremented on C0=R2+R3-1. But the reset request on this same position is taken into account after the incrementation.

So, on the C0 position of the OUT, R52 goes to 0 and is incremented to 1, instead of being incremented and zeroed. (this takes place at 130F during the game for information). This causes interrupts to be shifted one line earlier, and since the game handles all of its ruptures under interrupt, this has consequences. CRTC 0 and 2 are not impacted since the R4 update falls on the last line (this is only a problem for CRTC 1 whose counter C4 goes into overflow, and then encounters the value of R7 by triggering a VSYNC).

A priori, this game already used multiple ruptures in 1987. :o

It must have been developed on CRTC 0 or 2, because there is another bug in the game inherent in the operation of CRTC 1. The absence of text (or the repetition of the score during the game) in the lower part of the screen is linked to the characteristic of the CRTC 1 to take into account the update of R12/R13 as long as C4=0. In other words, this operation occurs too early and the author would have seen it if he had worked on a CRTC 1.

QuoteIt even looks like this game should also fail on PLUS ASIC hardware!
There's no reason it shouldn't work because it's not a CRTC issue. It works on CRTC 4.
Rhaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!!

cpcitor

> The problem comes from a bad update of R52 when a request to reset the counter occurs exactly on the last microsecond of the HSYNC. (...)

Now that's a deep knowledge of all the CRTC and their quirks... to each and every microsecond!  :o  :)
Had a CPC since 1985, currently software dev professional, including embedded systems.

I made in 2013 the first CPC cross-dev environment that auto-installs C compiler and tools: cpc-dev-tool-chain: a portable toolchain for C/ASM development targetting CPC, later forked into CPCTelera.

sb1903

Hi,

One thing that I noticed over time when using cpcec (which is the emulator I use most often, while cpcemu is on my rank #2 currently :) ), is that the sound is always a bit delayed. It's easiest to notice when just pushing backspace at the beginning of a basic line, to hear that beep. There is a noticeable delay. E.g. in cpcemu there is no such delay. This is on Linux, and I have compiled cpcec myself using the command that is told in the file CPCEC-E.TXT ("gcc -DSDL2 -O2 -xc cpcec.c -lSDL2 -ocpcec").

Any idea how to fix that delay?


Cheers!

cngsoft

#284
Quote from: sb1903 on 19:32, 01 November 22One thing that I noticed over time when using cpcec (which is the emulator I use most often, while cpcemu is on my rank #2 currently :) ), is that the sound is always a bit delayed. It's easiest to notice when just pushing backspace at the beginning of a basic line, to hear that beep. There is a noticeable delay. E.g. in cpcemu there is no such delay. This is on Linux, and I have compiled cpcec myself using the command that is told in the file CPCEC-E.TXT ("gcc -DSDL2 -O2 -xc cpcec.c -lSDL2 -ocpcec").
Any idea how to fix that delay?
The non-Win32 versions rely on SDL2, who handles both sound and timing on its own. There's one option in the emulator's menu ("Audio: audio acceleration") that shortens the internal audio buffer if you enable it.

However, if doesn't the job, you'll have to look for SDL_OpenAudioDevice in cpcec-ox.h , uncomment the preceeding statement "//spec.samples=4096;", turn it into "spec.samples=AUDIO_LENGTH_Z*N;" where N is a value you'll have to find by trial and error, and recompile the emulator. By default that setting is zero and SDL2 assigns it a supposedly reliable value on its own.
(if you can't see the banner right now my server is currently offline)

sb1903

#285
Thanks a lot for the pointer.

Quote from: cngsoft on 13:36, 15 November 22There's one option in the emulator's menu ("Audio: audio acceleration") that shortens the internal audio buffer if you enable it.
I tried that. Indeed, this option improves the situation, but the audio lag was still very obvious even with this switched on.

Quote from: cngsoft on 13:36, 15 November 22However, if doesn't the job, you'll have to look for SDL_OpenAudioDevice in cpcec-ox.h , uncomment the preceeding statement "//spec.samples=4096;", turn it into "spec.samples=AUDIO_LENGTH_Z*N;" where N is a value you'll have to find by trial and error, and recompile the emulator. By default that setting is zero and SDL2 assigns it a supposedly reliable value on its own.
I tried this next, but at first it seemed that whatever I set spec.samples to, it didn't really reduce the audio lag to any acceptable level. Even if I just put a very low plain number (like 5) in. Just when I went too low (like simply 1), cpcec crashed when playing audio.

So next, I looked a few lines down in cpcec-ox.h and found the following line:
int n=AUDIO_N_FRAMES>>session_hardplay;Well... that looked interesting. With simply adding a printf, I found out that without audio acceleration, n=16 (no wonder, quite at the beginning of cpcec-ox.h AUDIO_N_FRAMES is set to that value) and with video acceleration on, n=8. So my idea was, if I reduce the value of n, maybe the audio lag reduces. And well... it does. Unfortunately, if reduced beyond 4, sound started to get distorted while still some lag was there.
So my next idea was to look at your earlier hint again and play with the value of spec.samples and n together. Actually, what I found is that when I simply set spec.samples=AUDIO_LENGTH_Z (without any multiplier) and n=1, then there seems to be no distortion anymore and no audible lag (or at least it's now so low that it's easily acceptable). I did very limited testing and need to test this more, but first impression (quickly trying one simply basic game called "line runner" and MASK 2 as a commercial game) it seems to work well and I keep it like this now for further testing.

sb1903

I may now be completely wrong (then please excuse that, I am really not a champion of coding in C and still try to make a sense of things), but reviewing the following code further...
audio_session=SDL_GetQueuedAudioSize(session_audio)/sizeof(audio_buffer);
int n=AUDIO_N_FRAMES>>session_hardplay;
for (j=audio_session>0?audio_session>n?0:1:n;j>0;--j) // pump audio
    SDL_QueueAudio(session_audio,audio_buffer,sizeof(audio_buffer));

... I suspect that it may be simply buggy. What this code in my opinion does is at the beginning (when the emulator is started) to fill the audio queue 16 times with the audio_buffer (which at the start of the emulator will contain silence only, obviously). This is because at the very first execution, j will be initialized with the value of n, actually (so 16). At any further executions (except of the very first "run"), j always will be 1, so the audio_buffer will be normally appended. As a result, sound is playing normally, however with a delay of 16x(duration of audio buffer). This is what, in my opinion, causes the audio lag when the code is not changed as described in my earlier post.

walterg74

Hi.  Is the site for the emu dead?

robcfg


walterg74


cngsoft

Quote from: sb1903 on 00:21, 16 November 22I may now be completely wrong (then please excuse that, I am really not a champion of coding in C and still try to make a sense of things), but reviewing the following code further...
audio_session=SDL_GetQueuedAudioSize(session_audio)/sizeof(audio_buffer);
int n=AUDIO_N_FRAMES>>session_hardplay;
for (j=audio_session>0?audio_session>n?0:1:n;j>0;--j) // pump audio
    SDL_QueueAudio(session_audio,audio_buffer,sizeof(audio_buffer));

... I suspect that it may be simply buggy. What this code in my opinion does is at the beginning (when the emulator is started) to fill the audio queue 16 times with the audio_buffer (which at the start of the emulator will contain silence only, obviously). This is because at the very first execution, j will be initialized with the value of n, actually (so 16). At any further executions (except of the very first "run"), j always will be 1, so the audio_buffer will be normally appended. As a result, sound is playing normally, however with a delay of 16x(duration of audio buffer). This is what, in my opinion, causes the audio lag when the code is not changed as described in my earlier post.
The original idea was to keep the buffer as full as possible: whenever slowdowns delayed the refilling ot the buffer, the emulator was meant to fill it back, and reversely, when it was too full, the emulator wouldn't send the current chunk of audio to the queue. However, the method is indeed fragile: right now, as it is, if it somehow stays stable for long, the buffer can stay filled at just 1 frame of 16 (or 8 with acceleration on) and no delays are perceived, but if something causes a temporary slowdown, the buffer gets suddenly filled to full capacity and the delay grows to 16 frames (or 8 if accelerated).

As a result I'm rewriting this code at the moment to try to keep the buffer at 50% capacity at all times, either by refilling the buffer to half capacity when it becomes empty, or by "starving" it by half when it overflows. I'm trying to work around the general problem of this type of sound playback: it isn't independent of the operation of the remainder of the program. Unlike a videogame (where music and sound effects are asked to start whenever events in the game take place, but the game itself doesn't generate the sound; sound is defined as pre-existing assets) an emulator ties both action and sound tightly together. SDL2 simplifies audio by hiding platform-specific traits and merging audio logic into a simple data queue, but the price we pay is the unability to track the audio buffer, because to ensure full synchronisation between action and audio we must effectively use the sound hardware as the timer. I can do it with sufficient precision with the Win32 API, while SDL2 gives me a much more coarse approximation to the current queue status (SDL_GetQueuedAudioSize). For the curious, compiling the emulator with the optional compile time parameter -DDEBUG and then running it with "Video: Onscreen status" on will show the current state of the audio buffer on the bottom left corner as a bar.
(if you can't see the banner right now my server is currently offline)

sb1903

#291
Thanks for these explanations @cngsoft . I have a couple of questions/concerns here. Again, I am not a C coding expert at all, please excuse if some of these questions/concerns are stupid.

I understand the problem that you don't want the buffer to become empty, because if I read https://wiki.libsdl.org/SDL_QueueAudio correctly, SDL will then add silence and that probably will sound really bad/weird. Also understand if you don't want the buffer to become too filled, as this may add long lags that are not corrected then anymore.

However, I don't get how just adding the content of audio_buffer here several times will help. With that, you will play the current "tune" twice, thrice, etc (depending how often you add it). Wouldn't that sound equally bad/weird? But I understand that normally the buffer should hopefully not run empty, so hopefully this situation will not happen (very often).

Another question in mind is if the computation
audio_session=SDL_GetQueuedAudioSize(session_audio)/sizeof(audio_buffer);
audio_buffer is an array of type AUDIO_UNIT with AUDIO_LENGTH_Z*AUDIO_CHANNELS elements in it. SDL_GetQueuedAudioSize() gets you the number of bytes in the queue. But the size of AUDIO_UNIT itself is not necessarily one byte. In fact, on my system it seems to be two bytes. So, I believe (but I may be wrong) the computation would have to be

audio_session=SDL_GetQueuedAudioSize(session_audio)/(sizeof(audio_buffer)*sizeof(AUDIO_UNIT));

Regarding the "keep the buffer at 50%", my understanding is that your concept is that you always want to be 50% of certain multipliers of the size of audio_buffer units in the SDL queue. Is the 100% then 16  times for "non-accelerated" and 8 times for "accelerated"? So 8 and 4 respectively? My big doubt here is that with these numbers, the delay will always be very visible. Maybe the results on Windows are different (or maybe for Windows this all here doesn't play any role), but it's really irritating in a game when you, for example, shoot or move, and the corresponding sound for the first shot/move already comes when you already make the second shot/move. This is really what currently happens under Linux, I am not overstating :)

If you want to use this concept, you should think to make the "100%" configurable. I will probably set it for my system to 0 (or 1?)  then, so that simply in any run of session_render, the audio_buffer is appended to the SQL queue without any further checks (unless the queue becomes too long and the algorithm decides not to add the audio_buffer anymore).


sb1903

The hint to compile with the parameter -DDEBUG was a good one.

While watching darts in parallel ( :) ), I did some testing.

  • cpcec-220806, no modifications, sound acceleration turned off: buffer always nearly full, but a bit fluctuating, and clearly noticeable sound lag
  • cpcec-220806, no modifications, sound acceleration turned on: buffer always about half-full, but a bit fluctuating, and clearly noticeable sound lag (a bit better than without acceleration)
  • cpcec-220806, spec.samples=AUDIO_LENGTH_Z, sound acceleration turned off: buffer full up to 1 unit and staying exactly stable at that, and clearly noticeable sound lag
  • cpcec-220806, spec.samples=AUDIO_LENGTH_Z, sound acceleration turned on: buffer always half-full and staying exactly stable at that, and clearly noticeable sound lag (a bit bitter than without acceleration)
  • cpcec-220806, spec.samples=AUDIO_LENGTH_Z, AUDIO_N_FRAMES reduced to 2, sound acceleration turned off: buffer has now only 2 units, buffer fill stays stable at 1. sound has (nearly?) no lag.
  • cpcec-220806, spec.samples=AUDIO_LENGTH_Z, AUDIO_N_FRAMES reduced to 2, sound acceleration turned on: buffer has now only 2 units, buffer fill is shown constantly at 0. sound has no lag. despite of buffer at 0, nothing bad seems to happen
  • cpcec-220806, spec.samples not set (default), AUDIO_N_FRAMES reduced to 2, sound acceleration turned off: buffer has now only 2 units, buffer fill is fluctuating between 0 and 2, sound has no lag, but is now distorted.
  • cpcec-220806, spec.samples not set (default), AUDIO_N_FRAMES reduced to 2, sound acceleration turned on: buffer has now only 2 units, buffer fill is fluctuating between 0 and 2, sound has no lag, but is now even more distorted.
  • cpcec-220806, spec.samples not set (default), AUDIO_N_FRAMES reduced to 4, sound acceleration turned off: buffer has now 4 units, buffer fill is fluctuating between 2 and 4, sound has a bit of lag (would still disturb me)
  • cpcec-220806, spec.samples not set (default), AUDIO_N_FRAMES reduced to 4, sound acceleration turned on: buffer has now 4 units, buffer fill is fluctuating between 0 and 4, sound has a very low lag (probably not disturbing anymore), but is sometimes distorted
My conclusion is that it's best to set the spec.samples to AUDIO_LENGTH_Z and keep the buffer very small. At least there could be an option to do this.

I can do tests of more scenarios if that helps.



sb1903

Two more test scenarios. This time I have commented out the line "for (j=audio_session>0?audio_session>n?0:1:n;j>0;--j) // pump audio" so that the SDL_QueueAudio(...) command is simply every time executed exactly once. I believe with this change, setting sound acceleration doesn't make a change. I didn't change AUDIO_N_FRAMES (so it's set to 16).

  • spec.samples not set: buffer fill fluctuates between 0 and 2, some comes with a very small lag (much(!) better than unmodified cpcec-220806, probably acceptable for many scenarios)
  • spec.samples=AUDIO_LENGTH_Z: buffer fill now constantly 0, no sound lag and at the same time no problem with the sound (same as the above scenario "cpcec-220806, spec.samples=AUDIO_LENGTH_Z, AUDIO_N_FRAMES reduced to 2, sound acceleration turned on")

I believe the setting in the 2nd bullet point is the best result so far, although I also see the danger that on other systems, buffer may either run dry (which then likely results in distorted sound due to added silence) or runs full (due to no correction). Nevertheless, I would probably prefer to have at least an optional setting for such a risky sound setup (maybe there can be at least a correct for too long buffers still). Actually this probably is what "sound acceleration" actually should do :)

sb1903

#294
I thought a bit further about this. As said in my previous approach, I believe this approach

Quote from: cngsoft on 21:10, 17 November 22try to keep the buffer at 50% capacity at all times

is not optimal, because it will just keep a constant delay of the sound which is then played with a delay of AUDIO_N_FRAMES / 2 *  AUDIO_LENGTH_Z.

I believe it's better to define a minimum buffer fill (which maybe could even be switched off if sound acceleration is switched on) and add the audio_buffer twice to the queue if the buffer fill is below minimum (then automatically the buffer will fill up). Additionally, it is already the case, there should be a maximum buffer fill (AUDIO_N_FRAMES times the sizeof(audio_buffer)), and if the fill exceeds the maximum, audio_buffer won't be added until we are below the maximum again. (*see remark at the end)

The corresponding code would look like this:
if (session_audio)
{
    static BYTE s=1; if (s!=audio_disabled)
        if (s=audio_disabled) // silent mode needs cleanup
            memset(audio_buffer,AUDIO_ZERO,sizeof(audio_buffer));
    audio_bytes_queued=SDL_GetQueuedAudioSize(session_audio);       
    audio_session=audio_bytes_queued/sizeof(audio_buffer);

    // check if audio queue fill is below minimum and needs to be filled
    if (session_hardplay == 0 && audio_bytes_queued < AUDIO_MIN_BYTES)
        SDL_QueueAudio(session_audio,audio_buffer,sizeof(audio_buffer));
   
    // check if audio queue is too full. if it's not, add audio_buffer
    if (audio_session < AUDIO_N_FRAMES)
        SDL_QueueAudio(session_audio,audio_buffer,sizeof(audio_buffer));

}


In order for this to work, we also need to add at the beginning of the file

#define AUDIO_MIN_BYTES AUDIO_LENGTH_Z * AUDIO_CHANNELS * sizeof(AUDIO_UNIT) / 2 // minimum buffer fill
int audio_bytes_queued=0;

Also, I would suggest to set
spec.samples=AUDIO_LENGTH_Z;as this seems not to harm to make latency even a bit better.

* Now the remark: If the buffer gets too full, current solution is not to add audio_buffer to the audio queue anymore. With this, we will make sure that the buffer fill will end up below the maximum size again, but probably it will stay just below the maximum size, causing a constantly high latency. Potentially if such an event (buffer overrun) happens, it would be better not to add the audio_buffer anymore several times in a row. However, the above implementation does not consider this thought yet. This is a problem because it seems the audio buffer runs quite full when doing UI actions (I didn't see it running full during my tests as long as I didn't use the UI) , so we need a mechanism to the buffer empty again.

sb1903

Sorry for writing so much here :)

One additional thought (and again, please correct me if I am wrong... I am currently teaching myself how these things work, and I may make mistakes).
If I get it right, then one audio_buffer of cpcec represents 20 ms of sound (since AUDIO_LENGTH_Z = 41000 Hz / 50 Hz). That means if there is one full such audio_session (as you call the variable in the code) in SDL's audio queue, then you have already a latency of 20 ms. In the default, you are keeping (with "audio acceleration" enabled) 8 such audio_sessions in the queue, which gives a latency of 160 ms (it will be 320 ms without audio acceleration). Actually, this is really a high latency, no wonder that this is easily noticeable and irritating. Was it the intention to program things this way (i.e. is there a "technical" need for this?) or is this simply a bug? Or do I do a mistake in my thinking?

If my write-up is correct, we should probably change the code to keep this target for queued bytes much(!) lower. I need to do a bit more research what latency between audio and video usually is considered acceptable. I understand we don't want buffer to underrun, as then SDL will fill the gaps with silence and that creates the sound distortion that I heard in some of my earlier tests. First thought would be to keep the latency between 0 and 40 ms maybe?

Btw, to show the current latency, add following debug line to the "if (session_audio) { ... }" part:

printf("Current sound delay: %d ms\n",audio_bytes_queued/171); 

sb1903

New attempt of a diff to improve audio_latency.

Idea: target the audio queue (SDL queued audio) to be always filled with between 1/2 (with audio acceleration: 1/4) and 8 (with audio acceleration: 4) frames. Theoretically, that should keep the audio latency between 10 ms and 160 ms (with acceleration on: between 5 ms and 80 ms). If the queued audio is above these boundaries, do not add audio to the queue anymore until queue is either empty or below the minimum targeted fill.

Additionally, I have set "spec.samples=AUDIO_LENGTH_Z;" (as this seems to improve the latency also and seems to be smooth) and am also cleaning the audio_queue whenever the menu is used or the session is considered dirty.

Please see the diff for the file cpcec-ox.h. It's the only file I changed. Theoretically, one could make min and max for the buffer size (AUDIO_MIN_FRAMES_TH, AUDIO_N_FRAMES) configurable, but after limited(!) testing (on just my Linux system), it seems cpcec runs fine with these changes/settings.

--- cpcec-ox.h.backup    2022-11-15 22:21:51.977347379 +0100
+++ cpcec-ox.h    2022-11-19 15:26:26.932514709 +0100
@@ -60,8 +60,8 @@
     #define AUDIO_ZERO 0
 #endif // bitsize
 #define AUDIO_CHANNELS 2 // 1 for mono, 2 for stereo
-#define AUDIO_N_FRAMES 16 // safe on all machines, but slow; must be even!
-
+#define AUDIO_N_FRAMES 8 // original: 16, which is safe on all machines, but slow; must be even!
+#define AUDIO_MIN_FRAMES_TH 2 // keep audio queue over at least 1/2th of buffer length (>=10 ms latency)
 VIDEO_UNIT *video_frame,*menus_frame,*video_blend; // video and UI frames, allocated on runtime
 AUDIO_UNIT *audio_frame,audio_buffer[AUDIO_LENGTH_Z*AUDIO_CHANNELS]; // audio frame
 VIDEO_UNIT *video_target; // pointer to current video pixel
@@ -70,6 +70,8 @@
 BYTE video_interlaced=0,video_interlaces=0; // video scanline status
 char video_framelimit=0,video_framecount=0; // video frameskip counters; must be signed!
 BYTE audio_disabled=0,audio_session=0; // audio status and counter
+int audio_bytes_queued=0, audio_min_bytes_queued=0, audio_bytes_min=0, audio_bytes_max=0;
+BYTE audio_bytes_reset=0;
 unsigned char session_path[STRMAX],session_parmtr[STRMAX],session_tmpstr[STRMAX],session_substr[STRMAX],session_info[STRMAX]="";
 
 int session_timer,session_event=0; // timing synchronisation and user command
@@ -707,7 +709,8 @@
         session_shift=session_event=0; // quit!
     else
         session_shift=!!(session_event&0x4000),session_event&=0xBFFF;
-    return;
+    audio_bytes_reset = 1;
+    return;
 }
 
 void session_ui_textinit(char *s,char *t,char q) // used by session_ui_text and session_ui_scan
@@ -1353,6 +1356,8 @@
         spec.format=AUDIO_BITDEPTH>8?AUDIO_S16SYS:AUDIO_U8;
         spec.channels=AUDIO_CHANNELS;
         //spec.samples=4096; // safe value?
+        spec.samples=AUDIO_LENGTH_Z;
+//        printf("samples=%d\n",spec.samples);
         if (session_audio=SDL_OpenAudioDevice(NULL,0,&spec,NULL,0))
             audio_frame=audio_buffer;
     }
@@ -1613,7 +1618,7 @@
         }
     }
     if (session_dirty)
-        session_dirty=0,session_clean();
+        session_dirty=0,audio_bytes_reset=1,session_clean();
     return 0;
 }
 
@@ -1665,10 +1670,33 @@
         static BYTE s=1; if (s!=audio_disabled)
             if (s=audio_disabled) // silent mode needs cleanup
                 memset(audio_buffer,AUDIO_ZERO,sizeof(audio_buffer));
-        audio_session=SDL_GetQueuedAudioSize(session_audio)/sizeof(audio_buffer);
-        int n=AUDIO_N_FRAMES>>session_hardplay;
-        for (j=audio_session>0?audio_session>n?0:1:n;j>0;--j) // pump audio
-            SDL_QueueAudio(session_audio,audio_buffer,sizeof(audio_buffer));
+        audio_bytes_queued=SDL_GetQueuedAudioSize(session_audio);       
+        audio_session=audio_bytes_queued/sizeof(audio_buffer);
+        if (audio_bytes_queued<audio_bytes_min) audio_bytes_min = audio_bytes_queued;
+        if (audio_bytes_queued>audio_bytes_max) audio_bytes_max = audio_bytes_queued;
+
+        audio_min_bytes_queued = sizeof(audio_buffer) / (AUDIO_MIN_FRAMES_TH<<session_hardplay);
+//        printf("session_hardplay=%d, queued bytes=%d, min fill=%d, max fill times=%d, audio_buffer_size=%d, audio_session=%d\n",session_hardplay, audio_bytes_queued, audio_min_bytes_queued, AUDIO_N_FRAMES>>session_hardplay, sizeof(audio_buffer), audio_session);
+//        printf("Sound delay - current: %d ms  -  min: %d ms  -  max: %d ms  - reset: %d\n",audio_bytes_queued/171, audio_bytes_min/171, audio_bytes_max/171,audio_bytes_reset);
+       
+        // if queued bytes below the minimum (or 0), stop the resetting of the queue
+        if (audio_bytes_queued == 0 || audio_bytes_queued < audio_min_bytes_queued)
+            audio_bytes_reset = 0;
+
+        // check if audio queue fill is below minimum and needs to be filled
+        if (audio_bytes_queued < audio_min_bytes_queued) {
+            //printf("buffer needs to be filled!\n");
+            SDL_QueueAudio(session_audio,audio_buffer,sizeof(audio_buffer));
+        }
+
+        // check if audio queue is too full. if it's not, add audio_buffer. otherwise, reset the buffer
+        if (audio_session >= (AUDIO_N_FRAMES>>session_hardplay))
+            audio_bytes_reset = 1;
+        if (audio_bytes_reset == 0) {
+            // printf("adding audio buffer to queue\n");
+            SDL_QueueAudio(session_audio,audio_buffer,sizeof(audio_buffer));
+        }
+
     }
 
     if (session_wait) // resume activity after a pause

cngsoft

Quote from: sb1903 on 15:43, 19 November 22New attempt of a diff to improve audio_latency.
Interesting developments. I wonder how well they behave when disabling realtime for a while, then enabling it back (pressing F6, waiting some seconds, then hitting F6 again), or when loading sped-up tapes (that temporarily remove speed control)
(if you can't see the banner right now my server is currently offline)

sb1903

Hmm, I don't have any sped-up tapes here, but I tried F6 and also switched on/off different multipliers for the CPU clock. So far, I was not able to trigger any bad behavior with this on my setup.

Apollo

CPC - My beloved first computer!

Powered by SMFPacks Menu Editor Mod