GithubHelp home page GithubHelp logo

Sound glitches about vamigaweb HOT 52 CLOSED

mras0 avatar mras0 commented on July 22, 2024
Sound glitches

from vamigaweb.

Comments (52)

chris70c avatar chris70c commented on July 22, 2024 2

This is your code:

  process(inputs, outputs) {
    if(buffer == null)
    {
      if(fetch_buffer == null && pending_post==false)
      { 
//        console.log("initial fetch");     
        this.fetch_data();
      }
      if(fetch_buffer!= null)
      {
//        console.log("buffer=fetch_buffer;");
        buffer=fetch_buffer;
        fetch_buffer=null;
        buf_addr=0;
      }
    }
    if(buffer != null && buf_addr>=buffer.length/2)
    {
      if(fetch_buffer==null && pending_post==false) 
      {       
//        console.log("buf address pointer="+ buf_addr)
        this.fetch_data();
      }
    }
    if(buffer!=null)
    {
      const output = outputs[0];
      for(let i=0;i<128;i++)
      {
        output[0][i]=buffer[buf_addr++];
        output[1][i]=buffer[buf_addr++];
      }
 
      if(buf_addr>=buffer.length)
      {
//        console.log("buffer empty. fetch_buffer ready="+(fetch_buffer!= null));
        buffer=null;
        buf_addr=0;
      }
    }
    else
    {
//      console.error("audio processor has no data");
    }
    return true;
  }

The buffer==null condition cannot ever be true because if the buffer is null means you have no data to output = glitches,
when you fetch the data the message is not returned immediately so what are you processing in the meantime?

Also why this?

let fetch_buffer=null;
let buffer=null;
let buf_addr=0;
let pending_post=false;

the audio-processor is a class, those variables should be member of the class not global, accessing global variables is much slower than class properties.
Your constructor should declare those properties on the class like this:

constructor(options) {
  this.fetch_buffer = null;
  this.buffer = null;
  this.buff_addr = 0;
  this.pending_post = false;
}

and you should always refer to them as this.buffer, etc.

Accessing variables/properties in js works like this: check local variables, not found, check class, not found, check prototype, not found, check globals, found!
So if they're properties of the class you get: local, not found, class found!
otherwise it must go back another 2 levels to found those globals.

from vamigaweb.

chris70c avatar chris70c commented on July 22, 2024 1

Just a small point, why 4096*2 when you can write 8192?
It doesn't make difference in this test but in real usage those are 400.000 multiplication that can be avoided at all, you have something similar in your current audio-buffer where you divide by 2 the length, but why?
If you don't know before hand the length you can store the value in a class variable when you receive the buffer, if you already know it just write 2048; it might seems a small detail but that division is done every 2ms, multiply that for 1 minute and you see how much you're wasting, remember that this is not C where most of that stuff get optimized during compilation, in js what you write is what happens.

from vamigaweb.

chris70c avatar chris70c commented on July 22, 2024 1

Just a suggestion but you might want to look into embinden, it will make your code so much clean and easy to use, no more need to add a ton of function to export, the ability to pass complex objects between C and js without using tricks like json (which is very slow) and in general it will make your life much easier at least that's my experience.

from vamigaweb.

chris70c avatar chris70c commented on July 22, 2024 1

Just a small explanation about why I insist so much about those console messages.
A lot of those came from printf in the wasm (C) code, the wasm code has no idea if the developer tools console is open or not which means that no matter what every time you do a printf there is a call from C to js, doesn't matter that the browser knows the console is closed and optimize that away the call from C to js has already happened, you have all those calls in the loop function meaning that you're calling back and forth 2, 3 times each frame; I mean you were worried about the call to C to get the buffer but then you have 10 times as much call just to print messages?

from vamigaweb.

mithrendal avatar mithrendal commented on July 22, 2024

@mras0 I am just playing with some buffer sizes ... my machine is very old ... macbook pro from 2007 ... so I disabled the rendering such that it now only does emulation execution and the audio output...

how strong is your machine also such an old flagship ... from ancient times?

Overflow means that it is produced too fast I think... that bad thing is Buffer underrun ... so I think it should be easy to fix ... I play a bit with it now ...

from vamigaweb.

mras0 avatar mras0 commented on July 22, 2024

Hi,

I posted my specs in the linked issue. My machine is quite beefy for a laptop, but it might be throttled or something. I think (just going by the comments in Muxer.cpp that overflow is because consumer is falling behind.

Now that I can investigate locally I'll do some more testing.

from vamigaweb.

mithrendal avatar mithrendal commented on July 22, 2024

turning down the samples size will make the system consume more fine grained
try
want.samples = 1024;
or
want.samples = 2048;

in mainsdl.cpp line 432

from vamigaweb.

mithrendal avatar mithrendal commented on July 22, 2024

another thing could be that we execute too often in a second ...
amiga->execute();

that would also produce too much sound data in a second hence overrun...

I have checked that ... it is at mainsdl line 253
int64_t targetFrameCount = (int64_t)(elapsedTimeInSeconds * 50);

50Hz should be right .. I think at least.

from vamigaweb.

mras0 avatar mras0 commented on July 22, 2024

Changing want.samples doesn't seem to have much of an impact (it's a hard to debug, since it's a bit random). Getting sound and video synced is a bit of challenge. In my emulator I have an audio thread running with highest priority and stall emulation if it gets ahead (if it's behind, there's not much you can do). I don't know if a similar approach can be taken here.

from vamigaweb.

dirkwhoffmann avatar dirkwhoffmann commented on July 22, 2024

@mithrendal: For debugging, it might be helpful to add some print statements to the function that grabs the audio samples. Just print out the ring buffer size (fill level) and see if it is constantly decreasing, increasing, or jumping weirdly.

from vamigaweb.

mithrendal avatar mithrendal commented on July 22, 2024

For debugging, it might be helpful to add some print statements to the function that grabs the audio samples. Just print out the ring buffer size (fill level) and see if it is constantly decreasing, increasing, or jumping weirdly.

yeah good idea... tomorrow I will test this ... but I fear @mras0 nailed the real cause ... the thing is that we run everything in only one thread ... emulation and audio and rendering ... it worked well in http://vc64web.github.io which is mono ... but vAmiga has maybe more latency ... and so this might be maybe responsible for these glitches ...

nevertheless I just published a new version with some minor optimisations ... but I would not be surprised if there are still sound glitches ...

I see in chrome in the javascript console log this message

vC64.js:1718 [Deprecation] The ScriptProcessorNode is deprecated. Use AudioWorkletNode instead. (https://bit.ly/audio-worklet)

and I think this might be the way to get away with the glitches... when I understand it correctly AudioWorklets are designed to be executed on a separate thread ... exactly what @mras0 is suggesting ...

the description of AudioWorklets says about our current approach
There are two problems in this (old) design: the event handling is asynchronous by design, and the code execution happens on the main thread. The former induces the latency, and the latter pressures the main thread that is commonly crowded with various UI and DOM-related tasks causing either UI to "jank" or audio to "glitch". Because of this fundamental design flaw, ScriptProcessorNode is deprecated from the specification and replaced with AudioWorklet.

from vamigaweb.

chris70c avatar chris70c commented on July 22, 2024

@mras0: would you mind to try out this version which uses the new web audio api and webgl2 as backends?

https://www.neoartcr.com/vamiganew/

from vamigaweb.

mras0 avatar mras0 commented on July 22, 2024

@mras0: would you mind to try out this version which uses the new web audio api and webgl2 as backends?

https://www.neoartcr.com/vamiganew/

Tried a few demos and the performance seems very good, no sound glitches at all. Few things I noticed: Floppy sounds aren't muted by "Audio Mute"-button, DMS images are detected but don't work and Darkness - High Density has a graphics glitch in the right part of the scroll text (not present in vAmigaWeb) highlighted in red by me:
image

from vamigaweb.

mithrendal avatar mithrendal commented on July 22, 2024

no sound glitches at all.

thats good news...😍 so I will just implement the new web audio api in vAmigaWeb and this will probably fix the glitches in sound ...

concerning graphic glitches in vamiganew ... I bet @chris70c is using a version which is a couple of days older than vAmigaWeb ... when @chris70c merges the commits from vAmiga these errors will probably also disappear

from vamigaweb.

chris70c avatar chris70c commented on July 22, 2024

Yes, audio-mute works only for the emulator and dms support is compiled but not active at the moment.
For the intro glitch I think it might actually be a newly introduced bug OR a different configuration, the online version is compiled from today base code (with the infinite drive speed setting fix); we should ask Dirk to try it on the latest Mac version.

from vamigaweb.

mithrendal avatar mithrendal commented on July 22, 2024

@mras0 the new audio-processor backend is now implemented and all the deprecated audio stuff from SDL is completely removed ...

I hope that will solve the sound glitches as audio output is now on a separate worker thread as you proposed ... can you retest? 😎

many thanks to @chris70c 😍 who advised me how to implement it.

@mras0 when you retest be sure the installation is updated... because it is a PWA and offline installs ...it does heavy caching and sometimes on some browser it does not always automatically update immediately as it is intented by the PWA spec when the serviceworker detects a change ... so to be really sure you have the new version you have to use the update button in the settings...

from vamigaweb.

mras0 avatar mras0 commented on July 22, 2024

Sorry, but it's much worse now: Getting buffer underflows even on the "Insert disk" screen:

Muxer:420 UNDERFLOW (r: 8192 w: 12044)
Muxer:420 UNDERFLOW (r: 12288 w: 15820)
Muxer:420 UNDERFLOW (r: 4096 w: 7948)

I tried changing the buffer size, but it either results in OVERFLOW or UNDERFLOW, so it appears to not be properly synchronized in some way.

from vamigaweb.

mithrendal avatar mithrendal commented on July 22, 2024

@mras0 πŸ˜‚ at least it is modern now !! hehe πŸ˜‰

ok lets think .... underflows... that means it consumes too much ?

I guess we have to go back to the drawing board...πŸ˜–

what happens

  1. we setup a audio processor on a separate thread which wants to fill 44100 floats a second...
  2. after initialised it has no data and it will request the first 4096*2 new floats from the main thread
  3. mainthread then requesting dirks vAmiga to copy 4096*2 floats from Emu LR order 32Bit floats and pushing this to the audio thread
  4. when data arrives the audio thread does continuously processing 128 floats *2 (left and right channel) at a time...
  5. when it consumed half of the 4096 * 2 samples it requests a new data 4096 * 2 from main thread (like in 3.) while continuing with the rest half of the buffer
  6. when it is all consumed it changes to the in the meantime newly fetched and therefore received buffer... and it goes to 5.

this whole thing will start when the audiocontext of the browser will be unmuted by a user action... (only exception firefox which unmutes without the need of a click from a user action) ... it is unmuted in other browsers on the first click somewhere ... due to audio policy of modern browsers

What could be the problem ?
One guess... Maybe the first buffer is too large ? because it already fetches a new chunk of 4096*2 after only processing the first half of data ? maybe that stresses the vAmiga internal ringbuffer so that it responds with underflow?

On the other hand we need to fetch early to guaranty to have new data ready in audio thread when the current data buffer is fully consumed ...

Does someone see the error in the concept?πŸ˜”

from vamigaweb.

mithrendal avatar mithrendal commented on July 22, 2024

I just spoke with dirk ...
πŸ™ˆ

@mras0 it could be that the sample rate on windows or whatever your system is ... is not 44100 Hz but maybe much higher lets say 48000hz then this would totally explain the situation... because I have coded it fix to 44100 ... I have to tell vAmiga he freq of the audio context (the consumer) and then the underflows should disappear ... hopefully ... could you check the freq at your browser?

or just try to fix the freq at consumer side where the AudioContext is created

audioContext= new AudioContext({
latencyHint: 'interactive',
sampleRate: 44100,
});

I did not set any param ... so it takes systems default

from vamigaweb.

mras0 avatar mras0 commented on July 22, 2024

Yeah, that was definitely the cause of the major regression. My system seems to default to 48Khz. Changing either the rate of paula/audio/muxer to 48000 or making your proposed change both made it much better.

However in both cases I got underflows AND overflows (however much much fewer), so I think there's a fundamental synchronization issue between the emulation and sound playback. One or the other is unavoidable if the emulation is falling behind realtime speed (I guess that would be underflow), but both happening seems wrong to me (but I may be missing something).

I don't know anything about Web Audio, but "4 . when data arrives the audio thread does continuously processing 128 floats *2 (left and right channel) at a time..." Sounds strange, why only 128 stereo samples? That's only ~3ms of audio. Where does that number (128) come from?

Final note: You may want to make define some kind of shared constant between the js and c++ code to allow the buffer size to be more easily changed without these values inadvertently falling out of sync.

from vamigaweb.

chris70c avatar chris70c commented on July 22, 2024

The sample rate should be set at the beginning when you create the audio context, just add a function to the C part and call it like:
proxy.sampleRate = audioContext.sampleRate,
nothing else needs to be done, you shouldn't try to force the audioContext to a specific frequency because there is no guarantee that it will be available on all devices, instead you should set the emulator muxer to the sampleRate set by the audioContext, I run at 48000Khz on my PC and there are no issues.

@mras0: the 128 samples is per spec, that is the kernel used by the web audio api and it cannot be changed, the idea is too have as low a latency as possible instead of the old js callback which had a huge latency.

@mithrendal: I'll take a look at the code later if you like.

from vamigaweb.

mras0 avatar mras0 commented on July 22, 2024

@chris70c: Ah, ok, I guess that makes sense if you're making a sequencer/soft synth etc. Always just weird to see constants like that :)

Random thought: It's probably because the emulator is trying to match both the video refresh rate and audio playback rate at the same time. If possible, it might be worth it to try to tie one to the other (with preference to the audio), i.e. when one frames worth of audio has been played (buffered) only then request/sync the display data. Don't know enough about the internals yet to suggest how that might be done best though.

from vamigaweb.

mithrendal avatar mithrendal commented on July 22, 2024

@mithrendal: I'll take a look at the code later if you like.

@chris70c definitely 😍

@dirkwhoffmann @mras0 @chris70c Ok then I will first set the muxers freq now... to the audiocontext.samplerate ... because that was the most obvious fault to not do it

from vamigaweb.

chris70c avatar chris70c commented on July 22, 2024

@mras0: no the audio and video backend are completely decoupled, the audio runs in its own high priority thread and doesn't care about the video, the emulator is perfectly fine to fill the buffer at 48K or 44.1K so the problem must be in the code given that I don't have any issue with the audio in my own version.

You can't really sync the video from the audio, if you want any chance of a decent frame rate is always the other way around, the webgl backend is driven by requestAnimationFrame and that's it, the audio backend is driven by the amount of samples consumed, they're separated not in sync and the audio cannot go out of sync as long as the code is doing it correctly, nor can the video as long as the emulator can run fast enough and that depends only from the host machine.

Normally the easy answer is always the right one, no need to think about weird problems that are not there :)

from vamigaweb.

mithrendal avatar mithrendal commented on July 22, 2024

ok I pushed it ... to main branch and github pages...

One thing is still unclear to me ... when I create that AudioContext ... do I have to disconnect somewhen ?

I first wanted to only create one only single instance of the AudioContext and declare that at the very top as a global const var of vAmiga_ui.js ... so I will not end up creating multiple instances of such a context ... but that did not worked ... when I did like so

//declare global vars
const AudioContext = window.AudioContext || window.webkitAudioContext;
const audioContext = new AudioContext();
let audio_connected=false; 

.... later ...
   //declare function 
   connect_audio_processor = async () => {     
        if(audioContext.state === 'suspended') {
            await audioContext.resume();  
        }
      ....
  }
...
  connect_audio_processor();
  //unlock audio on user action
  document.addEventListener('click',connect_audio_processor, false);


it has played somewhen double the speed ... which indicates that it somewhere consumed twice the emulators buffer and make it therefore skip data right?

so now I just create a new AudioContext everytime I try connect the processor to it and that does not result in skipping... but strange

connect_audio_processor = async () => {     
        const AudioContext = window.AudioContext || window.webkitAudioContext;
        const audioContext = new AudioContext();

        if(audioContext.state === 'suspended') {
            await audioContext.resume();  
        }
        if(audio_connected==true)
            return; 
        if(audioContext.state === 'suspended') {
            return;  
        }
        wasm_set_sample_rate(audioContext.sampleRate);
        console.log("try connecting audioprocessor");           
    

but isn't that wrong too ? I mean every click I do make a new instance of a AudioContext ? Or is it just giving me a shallow wrapper object with the only and single audio context inside ?

from vamigaweb.

mithrendal avatar mithrendal commented on July 22, 2024

ok I fixed that thing ... now creating the AudioContext just once as a global constant works

All in all I think this worklet solution is running a bit slower that the old thing ... I can see this on my slowest devices... I guess that calling constantly the wasm functions maybe generates some overhead...

maybe we can feed the processor more directly without the roundtrip with the wasm_get_soundbuffer method

I have to understand the following article ... where they somehow feed the samples directly from wasm into the audio processor ...

https://googlechromelabs.github.io/web-audio-samples/audio-worklet/design-pattern/wasm/

or another idea:
what can also be done is to create ring buffer in the main_sdl.cpp which we constantly fill and then make the js side just peek into the ringbuffer without calling everytime the wasm_soundbuffer (which now still triggers the muxer.copy)
the muxer copy has to be called in wasm when that ringbuffer is flagged as consumed . This maybe can be done by just poking into the wasm memory e.g.
Module.HEAPU8.buffer[adress_of_the_flag]

from vamigaweb.

chris70c avatar chris70c commented on July 22, 2024

They're not bypassing anything in that article, there is no way to bypass the copy to the audio-processor buffer, they just have a buffer on the heap, they read the data and copy it into the audio buffer, just like we do.

The call to the emulator copy buffer is not something you can't remove and doing it from js or not doesn't make any difference, it worked the same in SDL too, the reason you're getting lower performances on old devices is still the same, more threads doesn't always equal more speed, if you're cpu doesn't have enough core then those thread must all run on the same core and they get alternated (run first thread, do something, stop, run 2nd thread, do something, stop, etc.).

What you're describing in the last part of your comment is what you should be doing but remember that a transferable buffer when is transferred on another thread stop to exist in the original thread, they cannot be shared (they're not sharedArrayBuffers); so if you try to pass that buffer in the heap it won't let you, you must copy it on a different buffer and pass it as transferable, so in the end you have 3 copies.

The emulator copy the audio data in the c buffer, the pointer to that buffer is transferred to js, js copy the data into another buffer, that buffer is transferred to the audio-processor, the data is finally copied into the final audio buffer; you can't skip any of those passages, I can because I use sharedArrayBuffer, so I share 1 buffer between wasm and audio-processor and just copy the data 128 samples at a time.

You have way too many if (7) conditions in the process function and you don't need a loop to copy the data just use buffer.set, you have to understand that process is a very high priority function that must do everything in less than 2.5ms top.

from vamigaweb.

mithrendal avatar mithrendal commented on July 22, 2024

so I share 1 buffer between wasm and audio-processor and just copy the data 128 samples at a time.

you don't even need to use dirks copy method ?

You have way too many if (7) conditions in the process function and you don't need a loop to copy the data just use buffer.set

ok I will look into it and optimize it ...😍

What you're describing in the last part of your comment is what you should be doing but remember that a transferable buffer when is transferred on another thread stop to exist in the original thread, they cannot be shared (they're not sharedArrayBuffers);

Yes I know ... I can't use sharedArrayBuffers because I want to run vAmigaWeb on my iPhone and iPad πŸ™„

I have maybe another idea to optimize ... everytime I create that buffer for the audio thread from the heap I do an

let sound_buffer_address = wasm_get_sound_buffer();
let sound_buffer = new Float32Array(Module.HEAPF32.buffer, sound_buffer_address, 4096*2).slice(0);

slice will create a new copy of the buffer, whereas the new Float32Array is just a view on the wasm heap (at least to my understanding)

then this will be transferred to the audio thread

 worklet_node.port.postMessage(sound_buffer, [sound_buffer.buffer]);

the ownership of the sound_buffer.buffer will be transferred from main thread to the audio thread with the transferable object method ... so no copy of the buffer happens here

then in the process it will copy the 128 sample in chunks ...
I have to use

output[channel].set(current_buffer.subarray(buf_pos,128))

to get rid of too many ifs ...

now I have an idea ... instead of doing always the slice ... e.g. creating a new ArrayBuffer() ... and therefore maybe stressing the garbage collection ... I could reuse that buffer if I would transfer the ownership back to the main thread

in audio processor thread

  fetch_data()
  {
    pending_post=true;
    //transfer back the ownership of the already fully consumed buffer
    this.port.postMessage(consumed_buffer, [consumed_buffer.buffer]);
  }

and instead of using slice ... I could use

transferred_buffer.set(Module.HEAPF32.buffer.subarray(soundbuffer, 4096*2)) 

this also copies like slice but it won't put stress on memory garbage collection as no new buffer would be created right?

so what will effectively happen here is

  1. vAmiga.paula.muxer.copy,
  2. main thread copies 4096 from the heap into transfer buffer
  3. audio thread copies 128 chunks into sound output

this would safe the creation of the transfer buffer in step 2 ... and the garbage collection of all those nulled used up transfer buffers

@chris70c with shared array buffer you save step 2 right? but you still have 1. and 3. correct ?

from vamigaweb.

chris70c avatar chris70c commented on July 22, 2024

Everything you said and did is correct besides transferring back the ownership of the buffer.
You fetch the buffer to the audio process, that's where you have your 4096 samples and you can't transfer back the ownership because if you do that the audio-processor buffer will be null and you have not finished to copy those 4096 samples yet; that buffer can only exists in one thread at any time so the ownership is in the audio-thread or in the main thread.
You may also want to take down some of the messages you print in the console, lots of them are printed from C side that means a full roundtrip from c to js and it does have an impact even if the console is closed.

from vamigaweb.

mithrendal avatar mithrendal commented on July 22, 2024

if you do that the audio-processor buffer will be null

yes you are correct when we use two buffers as it is the case now...

  1. the fetch_buffer which holds the next prefetched buffer
  2. the buffer which is currently processed

but we could make a third buffer lets call it buffer_for_recycling

  1. buffer == null -> fetch into new buffer
  2. transferred buffer arrives -> buffer=transferred buffer
  3. process buffer
  4. buffer is processed to 2048 samples -> fetch into new buffer
  5. tranferred buffer arrives -> prefetch_buffer=transferred buffer
  6. buffer is empty -> buffer_for_recycling = buffer; buffer = prefetch_buffer;
  7. buffer is processed to 2048 samples -> fetch into buffer_for_recycling
  8. goto 5

the question is whether the transfer of the buffer ownership is less costly than creating constantly new buffers and garbage collect them from time to time

from vamigaweb.

mithrendal avatar mithrendal commented on July 22, 2024

Hi @chris70c thank you so much 😊 for doing the code review ... and the in depth javascript speed code explanations ... I edited it and placed the code fragments into <> to distinguish code better from comments

ok I have to do:

  • use buffer.set()
  • for this I must change from copy(buffer, n) to Muxer::copy(void *buffer1, void *buffer2, isize n) because I need the sample data for one channel en block
  • I must not use global vars but change it to class instance vars
  • I must not use so many ifs
  • I like to try the three buffer recycling concept
  • I must not print so many output into the console
  • try to poke directly into wasm heap to signal the need of a new buffer instead of wasm function call which is more expensive

Now I just have to find some spare time to do it 😩 between real work and social family life

πŸ€“ I will just do one point after the other ...

Ah and I forgot one thing πŸ˜‰ ... I like to study SharedArrayBuffers ... although I cannot use them when I want to support Safari on iPhones and iPads ... but maybe safari will allow SharedArrayBuffer somewhen in the future

One last question with SharedArrayBuffer you have to use Muxer::copy(void *buffer1, void *buffer2, isize n) too ... are you?

from vamigaweb.

mithrendal avatar mithrendal commented on July 22, 2024

Ok I just did

  • use buffer.set()
  • for this I must change from copy(buffer, n) to Muxer::copy(void *buffer1, void *buffer2, isize n) because I need the sample data for one channel en block
  • must not use global vars but change it to class instance vars

and published the thing ... the other things on the todo list will be done in my next spare time slot πŸ€“

from vamigaweb.

chris70c avatar chris70c commented on July 22, 2024

I use copy(buffer1, buffer2) because it makes the code clear but you don't actually have to, as long as you know where the left channel and the right channel data is you can just use one buffer; I assume that if you ask the emulator to copy 4096 on a single buffer you'll get back an 8192 buffer with the first 4096 float as left channel and the remaining ones as right channel, I don't think it is interleaved.

SharedArrayBuffers are exactly like regular ArrayBuffer but you can share them between threads, they were already supported by all browsers but at a certain point they were removed because of cpu vulnerabities (Meltdown, etc.), after that they came back in FF and Chrome but never on Safari, I guess they did not implemented the new security measures adopted by other browsers, no idea why.

Together with SharedArrayBuffers, Atomic operations are a really nice way to work with multiple workers on separate threads to bad Apple is not exactly fast when it comes to adopt these standards because on mobile you should get quite a nice speed up.

from vamigaweb.

mithrendal avatar mithrendal commented on July 22, 2024

I did some simple performance testing ... whether the recycle_buffer concept which I described above will bring extra efficiency ...

I unit tested the scenario with the

  • current slice method, which creates a new Float32Buffer() each 4096 samples
  • the reusing of a once created Float32buffer() and just writing the new data into it
const wasm_heap = new Float32Array(10000);
const wasm_sound_address=1024;
wasm_heap[wasm_sound_address+0]=1.0;
wasm_heap[wasm_sound_address+1]=2.0;
wasm_heap[wasm_sound_address+2]=3.0;

const sound_buffer_in_heap = new Float32Array(wasm_heap.buffer, wasm_sound_address*4 /* because float32 is 4 bytes*/, 4096);

console.time("slice");
for (let i = 0; i < 10000; ++i) {
    const new_transfer_buffer = sound_buffer_in_heap.slice(0,4096);
}
console.timeEnd("slice");


const recycled_transfer_buffer=new Float32Array(4096);
console.time("set");
for (let i = 0; i < 10000; ++i) {    
    recycled_transfer_buffer.set(sound_buffer_in_heap.subarray(0,4096),0);
}
console.timeEnd("set");

results in latest chrome ... old grandpa mac 2007

slice: 453.726806640625 ms
set: 66.195068359375 ms

looks promising ...😍

but the question is what performance impact the altered call in the audio processor will have

fetch_data()
{
  //audio processor requests new data from main thread
  ... 
  this.port.postMessage("fetch");   //current call

  -> has to be changed to this
  worklet_node.port.postMessage(recycle_buffer, [recycle_buffer.buffer]); 
}

@chris70c any thoughts? on how to test this?

from vamigaweb.

chris70c avatar chris70c commented on July 22, 2024

The last change has little to no impact on performances, basically it just transfer ownership of the array buffer from one thread to another, there's no copy and the impact of the message post is negligible; I'll say definitely go for it, the important part is the result of the first test, the less data is copied around the better, the worst thing you can do in js is to create and discard any kind of object (array buffer included) that will create spikes in memory that you can easily see if you use the developer recording tools, each time a spike reach the top garbage collection happens and that may take 2-3ms more than enough to make your audio glitch. BTW, using set is much much faster because internally V8 uses memcpy with all the optimizations that come with it, that was the reason I told you to use set instead of looping when copying the 128 samples in the loop, might not seems much (they're only 128 * 4 = 512 bytes to copy) but that must be repeated every 2.5ms more or less so it amount to a lot of data.

from vamigaweb.

mithrendal avatar mithrendal commented on July 22, 2024

@chris70c yeah with the set I did already yesterday... also I moved to the copy left right buffer method ... the other did do it LR intermediate encoded ... but that was not suitable for set... so I changed to the one dirk and you are using ...

I will set up a dummy audioworker and play ping pong with the mainthread and test how much each way (e.g. always a new buffer or with buffer recycling) samples I can transfer between mainthreads and audio thread ... this way I will perfomance test the whole processing chain and then we know for sure how much better recycling is

from vamigaweb.

chris70c avatar chris70c commented on July 22, 2024

Go for it but I wouldn't put too much faith in these synthetic tests, you have the emulator running in the main thread so performances are relative to that and you don't have it in your tests, if you want a definitive answer just implement it and see the real results, is gonna take less time than setup all these tests which don't really give you real word usage answers; I use to do a lot of that too but after finding out that most of the time it was useless because the results in a real application were always different I just decided to do stuff and see how it works, less time wasted.

from vamigaweb.

mithrendal avatar mithrendal commented on July 22, 2024

already done here are the results πŸ™‹πŸ»

400000 times newed a Arraybuffer with slice and copied data into it
πŸ‘€
audio thread receives data:

 handleMessage(event) {
    this.ping++;
    fetch_buffer = event.data;
    this.port.postMessage("give me a new buffer");
  }

main thread:

    worklet_node.port.onmessage = async (msg) => {
        pong++;
        if(pong<=400000)
        {
            let sendbuffer= waveForm.slice(0,4096*2);
            worklet_node.port.postMessage(sendbuffer, [sendbuffer.buffer]);
        }
        else
        {
            console.timeEnd("slice");
            alert("ready");
        }
    };

console output
slice: 67864.1259765625 ms

oh yes that took really 67 seconds ... only to push 8Kb 400000 times into the audio processor 😬

and now lets benchmark this to our new concept the recycle buffer strategy

that is
400000 times sending back a recycled buffer and just copy data into it

audio thread receives data

  handleMessage(event) {
    this.ping++;
    recycled_buffer = event.data;
    //here instead of requesting a new buffer,
    //we send the used buffer back to the main thread
    this.port.postMessage(recycled_buffer, [recycled_buffer.buffer]);
  }

main thread

    worklet_node.port.onmessage = async (msg) => {
        pong++;
        if(pong<=400000)
        {
            let recycled_transfer_buffer= msg.data; //we use the sent back buffer 
            recycled_transfer_buffer.set(waveForm.subarray(0,4096*2),0);
            worklet_node.port.postMessage(recycled_transfer_buffer, [recycled_transfer_buffer.buffer]);
        }
        else
        {
            console.timeEnd("recycled");
            alert("ready");
        }
    };

console output
recycled: 57076.5439453125 ms

Analysis:
57076ms with buffer recycling
67864ms with buffer renewing

buffer recycling is 19% faster ... I should put that hot stuff into vAmigaWebs AudioProcessor 😎

PS: πŸ™„ now I know what @chris70c meant with less time wasted... after writing this tests and analysis... my spare time is already used up and I have to go to bed ...😴 ..

from vamigaweb.

mithrendal avatar mithrendal commented on July 22, 2024

just pushed the recycling buffer audio processor to gh-pages ... it was too exciting to get it ready... πŸ€—

why 4096*2 ?

yes ...you are absolutely right ... more points for my todo list ...

I already did now

  • use buffer.set()
  • for this I must change from copy(buffer, n) to Muxer::copy(void *buffer1, void *buffer2, isize n) because I need the sample data for one channel en block
  • must not use global vars but change it to class instance vars
  • the three buffer recycling concept

I still have to do:

  • I must not use so many ifs
  • I must not print so many output into the console
  • try to poke directly into wasm heap to signal the need of a new buffer instead of wasm function call which is more expensive
  • [new] do not use arithmetic operations on constant numbers with constant factors because javascript is not a compiler

from vamigaweb.

mithrendal avatar mithrendal commented on July 22, 2024

@mras0 After we did go to the new modern web audio api (with heavy coaching support from @chris70c πŸ‘) we got all those buffer overflows ... I know now why ... because my implementation of the ringbuffer did not took into account that the bidirectional communication between main thread and audiothread was asynchron and also that it took some time to get the data message back from the main threads vAmiga-Core ... what has happened here was that while requesting for new data the audiothread had only 2048 samples left to process and this was quickly reached and the message with new sound data from the main thread was not yet arrived (not always but often) ... then it simply run out of data and for that time when it had no data it did nothing ... I mean it did not consume anything because it had no data ... but the emulator was still producing ... so over the time the audio thread consumed much less than the main thread produced... hence paula (sitting on main thread) could not hold so much sound data and complained that its 16384 byte long internal buffers were overflowing ...

I fixed it now... I do prefetch the next buffer right after start consuming on a new buffer instead of waiting that it is half consumed ... that way giving the whole system more time ...

discussing the matter with @dirkwhoffmann he told me that it would be even better to move all available sound data from paulas ringbuffers over to the audio thread ... not just 4096 ... effectively shifting paulas ringbuffer data all into the ringbuffer implementation of the new web api audio thread ...

I will do that too but for now ... I am happy that it works now already so good on my old machines and my ancient iPhone. 😎 @mras0 could you try again on windows?

from vamigaweb.

mras0 avatar mras0 commented on July 22, 2024

Tried it again in firefox and edge (both by building locally and the online version), and I still see both overflows and underflows.

E.g.:

Muxer:420 UNDERFLOW (r: 8192 w: 9955)
Muxer:448 OVERFLOW (r: 0 w: 15440)

At startup I also noticed this (don't know if it's significant):

emscripten_set_main_loop_timing: Cannot set timing mode for main loop since a main loop does not exist! Call emscripten_set_main_loop first to set one up.

from vamigaweb.

mithrendal avatar mithrendal commented on July 22, 2024

@mras0 are sure that it is not cached? Did you checked whether it is the new modified audioprocessor in edge source code tab in web dev tools? It is so much better now here. But occasionaly I see an overflow/underflow too. Running The whole rink a dink demo. Some minutes long … only two over/underflows.😎 Before hundreds of them. I guess I have to implement @dirkwhoffmann s advice and pull everything paulas internal buffers have into the audio thread.

I will look into the mainloop does not exist warning…

from vamigaweb.

mras0 avatar mras0 commented on July 22, 2024

I'm pretty sure it's not cached. I use update installation manually every time, and I can see that the relevant files are fetched. Output from python3 -m http.server when running locally:


Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
127.0.0.1 - - [06/Nov/2021 20:11:21] "GET /sw.js HTTP/1.1" 200 -
127.0.0.1 - - [06/Nov/2021 20:11:23] "GET /sw.js HTTP/1.1" 304 -
127.0.0.1 - - [06/Nov/2021 20:11:26] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [06/Nov/2021 20:11:26] "GET /css/bootstrap.min.css HTTP/1.1" 200 -
127.0.0.1 - - [06/Nov/2021 20:11:26] "GET /css/vAmiga.css HTTP/1.1" 200 -
127.0.0.1 - - [06/Nov/2021 20:11:26] "GET /js/jquery-3.5.0.min.js HTTP/1.1" 200 -
127.0.0.1 - - [06/Nov/2021 20:11:26] "GET /js/bootstrap.bundle.min.js HTTP/1.1" 200 -
127.0.0.1 - - [06/Nov/2021 20:11:26] "GET /js/vAmiga_browser.js HTTP/1.1" 200 -
127.0.0.1 - - [06/Nov/2021 20:11:26] "GET /js/vAmiga_ui.js HTTP/1.1" 200 -
127.0.0.1 - - [06/Nov/2021 20:11:26] "GET /js/vAmiga_storage.js HTTP/1.1" 200 -
127.0.0.1 - - [06/Nov/2021 20:11:26] "GET /js/vAmiga_keyboard.js HTTP/1.1" 200 -
127.0.0.1 - - [06/Nov/2021 20:11:26] "GET /js/jszip.min.js HTTP/1.1" 200 -
127.0.0.1 - - [06/Nov/2021 20:11:26] "GET /vAmiga.js HTTP/1.1" 200 -
127.0.0.1 - - [06/Nov/2021 20:11:26] "GET /js/vAmiga_action_script.js HTTP/1.1" 200 -
127.0.0.1 - - [06/Nov/2021 20:11:26] "GET /js/virtualjoystick.js HTTP/1.1" 200 -
127.0.0.1 - - [06/Nov/2021 20:11:26] "GET /img/rom_empty.png HTTP/1.1" 200 -
127.0.0.1 - - [06/Nov/2021 20:11:26] "GET /img/vAmigaIcon.png HTTP/1.1" 200 -
127.0.0.1 - - [06/Nov/2021 20:11:26] "GET /img/mega.png HTTP/1.1" 200 -
127.0.0.1 - - [06/Nov/2021 20:11:27] "GET /img/icon-96x96.png HTTP/1.1" 200 -
127.0.0.1 - - [06/Nov/2021 20:11:27] "GET /img/favicon.ico HTTP/1.1" 200 -
127.0.0.1 - - [06/Nov/2021 20:11:27] "GET /vAmiga.wasm HTTP/1.1" 200 -
127.0.0.1 - - [06/Nov/2021 20:11:27] "GET /img/rom_patched.png HTTP/1.1" 200 -

And to be clear I only see a couple of underflows/overflows when I run a demo (only tested a handful or so)

BTW I don't know why vAmiga adjusts the sample rate dynamically, but I don't think that's helping for the web port, so it might be an idea to get an option to avoid that.

from vamigaweb.

chris70c avatar chris70c commented on July 22, 2024

vAmiga doesn't adjust the sample rate dynamically, not that I can see at least, the sample rate in my version is set just once when the audio context is created based on your pc preferences (most likely 48K or 44.1K), after that there are no changes in sample rate ever.

from vamigaweb.

mras0 avatar mras0 commented on July 22, 2024

@chris70c: I mean these parts: https://github.com/dirkwhoffmann/vAmiga/blob/0731d60f5b9b8bd03f37ec345d982c8af89aa5ec/Emulator/Paula/Audio/Muxer.cpp#L429 and https://github.com/dirkwhoffmann/vAmiga/blob/0731d60f5b9b8bd03f37ec345d982c8af89aa5ec/Emulator/Paula/Audio/Muxer.cpp#L458 If you don't have issues with over/underflows they'll never show up.

from vamigaweb.

mithrendal avatar mithrendal commented on July 22, 2024

@chris70c if you have not set

static const int AUDBUF_DEBUG = 1; // Audio buffers

in config.h then you will probably never see that vAmiga changes the sample rate ... it does that dynamically but you won't see ... your audio context ist still constant but dirk has there an algorithm to adapt when overflows or underflows starting to happen...

BTW I don't know why vAmiga adjusts the sample rate dynamically, but I don't think that's helping for the web port, so it might be an idea to get an option to avoid that

@dirkwhoffmann advised me to get all available produced samples from paula not more and not less and move that data to the audio processor thread ... this way there should never again be an adaption when it under or overflows ... because that is handled in the audio thread then

from vamigaweb.

dirkwhoffmann avatar dirkwhoffmann commented on July 22, 2024

I don't know why vAmiga adjusts the sample rate dynamically

The "sample rate adjustment" code was originally implemented for VirtualC64. I had the issue that ReSID and the audio backend run at a slightly different pace (44080 vs 44100 or something like that) which caused a buffer overflow (or underflow) once in a while (not frequent, but periodically). The sample rate adjustments compensated this. Later I found out that the issue was due to the way I communicated with reSID. Once I cleaned that up, the sample rate adjustment became obsolete. However, I was so proud of it that I kept it 😎. If everything works as expected, the rate should never be changed.

from vamigaweb.

chris70c avatar chris70c commented on July 22, 2024

I see what you mean, but... technically there should never be any reason to get under/overflows; I'm pretty sure that the problem resides somewhere else 'cause if you're getting 2048 samples every 16ms and you're framerate is constant at 20ms there can't be any. At a certain point there is also to consider the possibility that getting more than what it is now from the single thread version is not possible. The best advice I can give at this point is to start removing all the debug outputs, is not possible to actually see if it is working or not if you have tons of messages printed in the console every frame; try to take everything out and remove all the debugging options from the emulator.

from vamigaweb.

chris70c avatar chris70c commented on July 22, 2024

These are 3 seconds of console output of the version currently online:

sw.js:49 sw: get from vAmiga_app_cache_v2021_11_05b cached resource: https://vamigaweb.github.io/
sw.js:49 sw: get from vAmiga_app_cache_v2021_11_05b cached resource: https://vamigaweb.github.io/css/bootstrap.min.css
sw.js:49 sw: get from vAmiga_app_cache_v2021_11_05b cached resource: https://vamigaweb.github.io/css/vAmiga.css
sw.js:49 sw: get from vAmiga_app_cache_v2021_11_05b cached resource: https://vamigaweb.github.io/js/jquery-3.5.0.min.js
sw.js:49 sw: get from vAmiga_app_cache_v2021_11_05b cached resource: https://vamigaweb.github.io/js/bootstrap.bundle.min.js
sw.js:49 sw: get from vAmiga_app_cache_v2021_11_05b cached resource: https://vamigaweb.github.io/js/vAmiga_browser.js
sw.js:49 sw: get from vAmiga_app_cache_v2021_11_05b cached resource: https://vamigaweb.github.io/js/vAmiga_ui.js
sw.js:49 sw: get from vAmiga_app_cache_v2021_11_05b cached resource: https://vamigaweb.github.io/js/vAmiga_storage.js
sw.js:49 sw: get from vAmiga_app_cache_v2021_11_05b cached resource: https://vamigaweb.github.io/js/vAmiga_keyboard.js
sw.js:49 sw: get from vAmiga_app_cache_v2021_11_05b cached resource: https://vamigaweb.github.io/js/vAmiga_action_script.js
sw.js:49 sw: get from vAmiga_app_cache_v2021_11_05b cached resource: https://vamigaweb.github.io/js/virtualjoystick.js
sw.js:49 sw: get from vAmiga_app_cache_v2021_11_05b cached resource: https://vamigaweb.github.io/js/jszip.min.js
vAmiga_ui.js:17

   The AudioContext was not allowed to start. It must be resumed (or created) after a user gesture on the page.

(anonymous) @ vAmiga_ui.js:17
sw.js:49 sw: get from vAmiga_app_cache_v2021_11_05b cached resource: https://vamigaweb.github.io/img/vAmigaIcon.png
sw.js:49 sw: get from vAmiga_app_cache_v2021_11_05b cached resource: https://vamigaweb.github.io/img/rom_empty.png
Error: attribute height: Expected length, "auto".
Error: attribute height: Expected length, "auto".
Error: attribute height: Expected length, "auto".
Error: attribute height: Expected length, "auto".
Error: attribute height: Expected length, "auto".
Error: attribute height: Expected length, "auto".
Error: attribute height: Expected length, "auto".
Error: attribute height: Expected length, "auto".
Error: attribute height: Expected length, "auto".
sw.js:49 sw: get from vAmiga_app_cache_v2021_11_05b cached resource: https://vamigaweb.github.io/img/mega.png
sw.js:49 sw: get from vAmiga_app_cache_v2021_11_05b cached resource: https://vamigaweb.github.io/vAmiga.js
(index):879 service worker registered
sw.js:49 sw: get from vAmiga_app_cache_v2021_11_05b cached resource: https://vamigaweb.github.io/vAmiga.wasm
sw.js:49 sw: get from vAmiga_app_cache_v2021_11_05b cached resource: https://vamigaweb.github.io/manifest.json
sw.js:49 sw: get from vAmiga_app_cache_v2021_11_05b cached resource: https://vamigaweb.github.io/img/icon-144x144.png
(index):790 constructing vAmiga ...
(index):814

   Muxer:79 clear()

printErr @ (index):814
(index):814

   Muxer:79 clear()

printErr @ (index):814
(index):790 adding a listener to vAmiga message queue...
(index):790 vAmiga message=DMA_DEBUG_OFF, data=0
(index):790 vAmiga message=LAYOUT, data=0
(index):790 vAmiga message=LAYOUT, data=0
(index):790 vAmiga message=MUTE_OFF, data=0
(index):790 vAmiga message=MUTE_OFF, data=0
(index):790 vAmiga message=DRIVE_CONNECT, data=0
(index):790 vAmiga message=CONFIG, data=0
(index):790 vAmiga message=DRIVE_DISCONNECT, data=1
(index):790 vAmiga message=CONFIG, data=0
(index):790 vAmiga message=DRIVE_DISCONNECT, data=2
(index):790 vAmiga message=CONFIG, data=0
(index):790 vAmiga message=DRIVE_DISCONNECT, data=3
(index):790 vAmiga message=CONFIG, data=0
(index):790 vAmiga message=LAYOUT, data=0
(index):790 vAmiga message=LAYOUT, data=0
(index):790 vAmiga message=LAYOUT, data=0
(index):790 vAmiga message=POWER_LED_DIM, data=0
(index):790 vAmiga message=LAYOUT, data=0
(index):790 vAmiga message=LAYOUT, data=0
(index):790 vAmiga message=LAYOUT, data=0
(index):790 vAmiga message=LAYOUT, data=0
(index):790 vAmiga message=RESET, data=0
(index):790 vAmiga message=REGISTER, data=0
(index):790 ***** put missing rom message
(index):790 v4 wrapper calls run on vAmiga->run() method
(index):790 vAmiga message=CONFIG, data=12
(index):790 vAmiga message=LAYOUT, data=0
(index):790 vAmiga message=CONFIG, data=13
(index):790 vAmiga message=LAYOUT, data=0
(index):790 vAmiga message=CONFIG, data=0
(index):790 waiting on emulator ready in javascript ...
vAmiga_ui.js:1254

   The AudioContext was not allowed to start. It must be resumed (or created) after a user gesture on the page.

connect_audio_processor @ vAmiga_ui.js:1254
(index):790 try to create 2d renderer
(index):790 got software renderer ...
(index):790 **** warp change 0 -> 0
(index):790 wasm_configure CLX_SPR_SPR = true
(index):790 vAmiga message=CONFIG, data=40
(index):790 wasm_configure CLX_SPR_PLF = true
(index):790 vAmiga message=CONFIG, data=41
(index):790 wasm_configure CLX_PLF_PLF = true
(index):790 vAmiga message=CONFIG, data=42
(index):790 wasm_configure BLITTER_ACCURACY = 2
(index):790 vAmiga message=CONFIG, data=43
(index):790 wasm_configure DRIVE_SPEED = 1
(index):790 vAmiga message=CONFIG, data=21
(index):790 wasm_configure AGNUS_REVISION = ECS_1MB
(index):790 vAmiga message=LAYOUT, data=0
(index):790 vAmiga message=CONFIG, data=0
(index):790 wasm_configure CHIP_RAM = 512
(index):790 vAmiga message=CONFIG, data=12
(index):790 wasm_configure SLOW_RAM = 512
(index):790 vAmiga message=CONFIG, data=13
(index):790 wasm_configure FAST_RAM = 0
(index):790 vAmiga message=CONFIG, data=14
(index):790 wasm_set_sid_engine ReSID - Fast
vAmiga_storage.js:25

   opening db ... 1.try

db @ vAmiga_storage.js:25
vAmiga_ui.js:1254

   The AudioContext was not allowed to start. It must be resumed (or created) after a user gesture on the page.

connect_audio_processor @ vAmiga_ui.js:1254
(index):790 load file=rom.bin len=262144, header bytes= 11, 11, 4e
(index):790 try to build Snapshot
(index):790 FILE_TYPE_MISMATCH
(index):790 try to build RomFile
(index):790 vAmiga message=LAYOUT, data=0
(index):790 Loaded ROM image rom.bin.
(index):790 sending ready message READY_TO_RUN.
(index):790 Kickstart 1.3, Rev 34.005, December 1987,
(index):790 wasm_run
(index):790 is running = 0
(index):790 **** State OFF
(index):790 **** State change OFF -> RUNNING
(index):790 vAmiga message=POWER_LED_DIM, data=0
(index):790 vAmiga message=LAYOUT, data=0
(index):814

   Muxer:79 clear()

printErr @ (index):814
(index):814

   Muxer:79 clear()

printErr @ (index):814
(index):790 vAmiga message=POWER_LED_DIM, data=0
(index):790 vAmiga message=LAYOUT, data=0
(index):790 vAmiga message=LAYOUT, data=0
(index):790 vAmiga message=LAYOUT, data=0
(index):790 vAmiga message=LAYOUT, data=0
(index):790 vAmiga message=RESET, data=0
sw.js:49 sw: get from vAmiga_app_cache_v2021_11_05b cached resource: https://vamigaweb.github.io/img/rom_patched.png
(index):790 vAmiga message=POWER_ON, data=0
(index):814

   Muxer:79 clear()

printErr @ (index):814
(index):790 vAmiga message=RUN, data=0
(index):790 **** State RUNNING
(index):790 emscripten_set_main_loop_arg() at MSG_RUN
(index):790 lost sync target=17, total_executed=0
(index):814

   Muxer:448 OVERFLOW (r: 0 w: 15976)

printErr @ (index):814
(index):790 time[ms]=1002, audio_samples=0, frames [executed=13, rendered=25] avg_fps=25
vAmiga_keyboard.js:26 keycode=F12, key=F12
(index):790 scheduleKeyPress ( 96, 0, 0 )
(index):790 scheduleKeyPress ( 0, 0, 0 )
(index):790 vAmiga message=POWER_LED_ON, data=0
(index):790 vAmiga message=LAYOUT, data=0
(index):790 vAmiga message=POWER_LED_DIM, data=0
vAmiga_keyboard.js:26 keycode=F12, key=F12
(index):790 scheduleKeyRelease ( 0, 0, 1 )
(index):790 scheduleKeyRelease ( 96, 0, 1 )
(index):814

   Muxer:448 OVERFLOW (r: 0 w: 16117)

printErr @ (index):814
(index):814

   Muxer:448 OVERFLOW (r: 0 w: 16117)

printErr @ (index):814
(index):814

   Muxer:448 OVERFLOW (r: 0 w: 16117)

printErr @ (index):814
(index):814

   Muxer:448 OVERFLOW (r: 0 w: 16118)

printErr @ (index):814
(index):814

   Muxer:448 OVERFLOW (r: 0 w: 16117)

printErr @ (index):814
(index):790 time[ms]=1000, audio_samples=0, frames [executed=50, rendered=100] avg_fps=62
(index):814

   Muxer:448 OVERFLOW (r: 0 w: 16117)

printErr @ (index):814
(index):814

   Muxer:448 OVERFLOW (r: 0 w: 16117)

printErr @ (index):814
(index):814

   Muxer:448 OVERFLOW (r: 0 w: 16118)

printErr @ (index):814
(index):790 set paula.muxer to freq= 48000
(index):790 paula.muxer.getSampleRate()==48000.000000
vAmiga_ui.js:1263 try connecting audioprocessor
sw.js:49 sw: get from vAmiga_app_cache_v2021_11_05b cached resource: https://vamigaweb.github.io/js/vAmiga_audioprocessor.js
vAmiga_audioprocessor.js:14 vAmiga_audioprocessor connected
vAmiga_audioprocessor.js:29 audio processor has no recycled buffer... creates new buffer
vAmiga_audioprocessor.js:29 audio processor has no recycled buffer... creates new buffer
(index):790 vAmiga message=POWER_LED_ON, data=0
vAmiga_keyboard.js:26 keycode=F12, key=F12
(index):790 scheduleKeyPress ( 96, 0, 0 )
(index):790 scheduleKeyPress ( 0, 0, 0 )
(index):790 time[ms]=1000, audio_samples=32768, frames [executed=50, rendered=100] avg_fps=75
vAmiga_keyboard.js:26 keycode=F12, key=F12
(index):790 scheduleKeyRelease ( 0, 0, 1 )
(index):790 scheduleKeyRelease ( 96, 0, 1 )
(index):790 time[ms]=1000, audio_samples=49152, frames [executed=50, rendered=100] avg_fps=81
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_STEP, data=1686110464
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_STEP, data=1686110208
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_POLL, data=1686110464
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_POLL, data=1686110208
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_POLL, data=1686110464
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_POLL, data=1686110208
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_POLL, data=1686110464
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_POLL, data=1686110208
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_POLL, data=1686110464
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_POLL, data=1686110208
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_POLL, data=1686110464
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 time[ms]=1001, audio_samples=49152, frames [executed=50, rendered=100] avg_fps=85
(index):790 time[ms]=1000, audio_samples=49152, frames [executed=50, rendered=100] avg_fps=87
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_POLL, data=1686110208
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 time[ms]=1001, audio_samples=45056, frames [executed=50, rendered=100] avg_fps=89
(index):790 time[ms]=1000, audio_samples=49152, frames [executed=50, rendered=100] avg_fps=90
(index):790 time[ms]=1001, audio_samples=49152, frames [executed=50, rendered=100] avg_fps=91
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_POLL, data=1686110464
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 time[ms]=1001, audio_samples=45056, frames [executed=51, rendered=100] avg_fps=92
(index):790 time[ms]=1001, audio_samples=49152, frames [executed=50, rendered=100] avg_fps=93
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_POLL, data=1686110208
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
sw.js:49 sw: get from vAmiga_app_cache_v2021_11_05b cached resource: https://vamigaweb.github.io/
sw.js:49 sw: get from vAmiga_app_cache_v2021_11_05b cached resource: https://vamigaweb.github.io/css/bootstrap.min.css
sw.js:49 sw: get from vAmiga_app_cache_v2021_11_05b cached resource: https://vamigaweb.github.io/css/vAmiga.css
sw.js:49 sw: get from vAmiga_app_cache_v2021_11_05b cached resource: https://vamigaweb.github.io/manifest.json
(index):790 lost sync target=560, total_executed=551
(index):814

   Muxer:420 UNDERFLOW (r: 0 w: 1780)

printErr @ (index):814
put_char @ vAmiga.js:2598
write @ vAmiga.js:2531
write @ vAmiga.js:3843
doWritev @ vAmiga.js:4645
_fd_write @ vAmiga.js:9059
$func6402 @ vAmiga.wasm:0x2d6027
$func6409 @ vAmiga.wasm:0x2d637a
$func6422 @ vAmiga.wasm:0x2d7c9c
$func6424 @ vAmiga.wasm:0x2d7ccb
$func5268 @ vAmiga.wasm:0x24784f
$func5270 @ vAmiga.wasm:0x2479bd
$wasm_get_sound_buffer @ vAmiga.wasm:0x8194
(anonymous) @ vAmiga.js:1553
ccall @ vAmiga.js:758
(anonymous) @ vAmiga.js:773
worklet_node.port.onmessage @ vAmiga_ui.js:1274
(index):790 time[ms]=1000, audio_samples=45056, frames [executed=40, rendered=81] avg_fps=92
(index):790 time[ms]=1001, audio_samples=49152, frames [executed=50, rendered=87] avg_fps=91
(index):790 time[ms]=1010, audio_samples=49152, frames [executed=50, rendered=99] avg_fps=92
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_POLL, data=1686110464
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 time[ms]=1001, audio_samples=45056, frames [executed=50, rendered=100] avg_fps=92
(index):790 time[ms]=1010, audio_samples=49152, frames [executed=51, rendered=101] avg_fps=93
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_POLL, data=1686110208
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 time[ms]=1001, audio_samples=49152, frames [executed=50, rendered=100] avg_fps=93
(index):790 time[ms]=1000, audio_samples=49152, frames [executed=50, rendered=100] avg_fps=94
(index):790 time[ms]=1000, audio_samples=45056, frames [executed=50, rendered=100] avg_fps=94
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_POLL, data=1686110464
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 vAmiga message=DRIVE_SELECT, data=0
(index):790 vAmiga message=DRIVE_SELECT, data=-1
(index):790 time[ms]=1000, audio_samples=49152, frames [executed=50, rendered=100] avg_fps=94
​ time[ms]=1001, audio_samples=49152, frames [executed=50, rendered=100] avg_fps=94
​ vAmiga message=DRIVE_SELECT, data=0
​ vAmiga message=DRIVE_SELECT, data=-1
​ vAmiga message=DRIVE_SELECT, data=0
​ vAmiga message=DRIVE_POLL, data=1686110208
​ vAmiga message=DRIVE_SELECT, data=-1
​ vAmiga message=DRIVE_SELECT, data=0
​ vAmiga message=DRIVE_SELECT, data=-1
​ time[ms]=1000, audio_samples=49152, frames [executed=50, rendered=99] avg_fps=95
​ time[ms]=1001, audio_samples=45056, frames [executed=50, rendered=100] avg_fps=95
​ time[ms]=1001, audio_samples=49152, frames [executed=50, rendered=100] avg_fps=95
​ vAmiga message=DRIVE_SELECT, data=0
​ vAmiga message=DRIVE_SELECT, data=-1
​ vAmiga message=DRIVE_SELECT, data=0
​ vAmiga message=DRIVE_POLL, data=1686110464
​ vAmiga message=DRIVE_SELECT, data=-1
​ vAmiga message=DRIVE_SELECT, data=0
​ vAmiga message=DRIVE_SELECT, data=-1
​ time[ms]=1000, audio_samples=49152, frames [executed=50, rendered=100] avg_fps=95
​ time[ms]=1002, audio_samples=45056, frames [executed=50, rendered=100] avg_fps=95
​ vAmiga message=DRIVE_SELECT, data=0
​ vAmiga message=DRIVE_SELECT, data=-1
​ vAmiga message=DRIVE_SELECT, data=0
​ vAmiga message=DRIVE_POLL, data=1686110208
​ vAmiga message=DRIVE_SELECT, data=-1
​ vAmiga message=DRIVE_SELECT, data=0
​ vAmiga message=DRIVE_SELECT, data=-1
​ time[ms]=1009, audio_samples=49152, frames [executed=51, rendered=99] avg_fps=95
​ time[ms]=1008, audio_samples=49152, frames [executed=50, rendered=100] avg_fps=96

from vamigaweb.

mithrendal avatar mithrendal commented on July 22, 2024

@mras0 now I took the big pump gun to erase the sound glitches ... a ringbuffer gun
image

the new designed audio processor sets up a ring of 16 soundbuffers each 1024 samples big that is 2048 bytes for left and right in total per each ... these do travel now between the audio and the mainthread ... I call them shuttles ... like space shuttles ... they are so many now (16 in total) that they are capable to suck all samples from paulas internal buffers ... and taxi them to the audioprocessors thread... when one is consumed it will be immediately sent back to the main thread which in turn will fill it ... (you can see the shuttles in the picture above 😎 small bullets ... will come back for sure) I only fill each completely e.g. when paula has only lets say 378 then the shuttle will be parked temporarily in the main thread ... when next time (after 1024 samples) the audio thread sends a empty shuttle and paula has for example 2578 samples, the main thread fills the recently parked empty shuttle plus the shuttle which is just arrived one after the other ... ok long story ... but it was so much fun to implement it ... @dirkwhoffmann advised me in the conceptual design.

now I did not see a single overrun or underrun in the console log 😎

@chris70c yes the log 😬... I will do that next and log only when the debug log is enabled in the settings to improve runtime performance ... as you told me ...

@mras0 I did also forgot to define RELEASEBUILD in config.h which I did now... which should give some extra efficiency as it leaves out all the assertions in the emulation code

I did all canges in the branch audioworklet2 which I have not yet pulled into main ... lets first test the new stuff a bit ... for this I pushed it to gh-pages

EDIT: also merged into main

from vamigaweb.

mras0 avatar mras0 commented on July 22, 2024

Tested a few demos with a locally built version (f74024b), and it seems to work a lot better! Nothing odd was printed in the debug console and the only times where I thought it sounded odd it's probably either me not remembering the module correctly or an actual emulation issue (probably the former), no "glitches" or hickups.

Great work πŸ‘

from vamigaweb.

mithrendal avatar mithrendal commented on July 22, 2024

when calling paula to copy its buffers into heap I use now a much faster direct js to c function call instead of a cwrapped function which was slower to call according to the emscripten manuals and also to what I see when I step with the debugger into the cwrapper code. At 48000Khz this function will be called up to 46 times ... so the faster direct call will definitely result in a bit less power consumption ... πŸ€“

see (2c70797)

from vamigaweb.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.