diff --git a/CMakeLists.txt b/CMakeLists.txt index a4dec6d..9ede742 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -288,7 +288,7 @@ if(WARN_VARIABLE_LENGTH_ARRAY_AVAILABLE) endif(WARN_VARIABLE_LENGTH_ARRAY_AVAILABLE) if(EMSCRIPTEN) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-rtti -fno-exceptions -O3 -s USE_SDL=2 -s USE_ZLIB=1") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DEMSCRIPTEN -ffile-prefix-map=${CMAKE_SOURCE_DIR}=hatari -ffile-prefix-map=${CMAKE_BINARY_DIR}=build -fno-rtti -fno-exceptions -O3 -s USE_SDL=2 -s USE_ZLIB=1") set(CMAKE_EXECUTABLE_SUFFIX ".html") endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 540c97e..d070f0a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,7 +12,7 @@ set(SOURCES vdi.c inffile.c video.c wavFormat.c xbios.c ymFormat.c lilo.c) if(EMSCRIPTEN) - set(SOURCES ${SOURCES} emscripten.js emscripten_shell.html) + set(SOURCES ${SOURCES} lcweb.c emscripten.js emscripten_shell.html) endif() # Disk image code is shared with the hmsa tool, so we put it into a library: @@ -202,14 +202,7 @@ elseif(EMSCRIPTEN) ${APP_NAME} PROPERTIES LINK_FLAGS - "-O3 \ --fno-exceptions \ --s USE_SDL=2 -s USE_ZLIB=1 -s INITIAL_MEMORY=262144000 -s ASYNCIFY -s -s ASSERTIONS=0 \ --s EXPORTED_RUNTIME_METHODS='[\"ccall\",\"cwrap\"]' \ --s EXPORTED_FUNCTIONS=\"['_main','_Reset_Warm','_Reset_Cold','_Statusbar_UpdateInfo','_Main_UnPauseEmulation','_Floppy_InsertDiskIntoDrive','_Floppy_SetDiskFileName','_Floppy_EjectDiskFromDrive','_Configuration_ChangeMemory','_Configuration_ChangeSystem','_Configuration_ChangeTos','_Configuration_ChangeUseHardDiskDirectories','_Configuration_ChangeFastForward','_Configuration_Apply','_IoMem_UnInit','_IoMem_Init']\" \ ---preload-file ${CMAKE_BINARY_DIR}/files/@/share/hatari/ \ ---pre-js ${CMAKE_SOURCE_DIR}/src/emscripten.js \ ---shell-file ${CMAKE_SOURCE_DIR}/src/emscripten_shell.html" + "-O3 -fno-exceptions -sUSE_SDL=2 -sUSE_ZLIB=1 -sINITIAL_MEMORY=67108864 -sASYNCIFY -sASSERTIONS=0 -sEXPORTED_RUNTIME_METHODS=ccall,cwrap,FS,HEAPU8,ENV -sMODULARIZE -sEXPORT_ES6 -sEXPORT_NAME=createHatari -sENVIRONMENT=web -sINCOMING_MODULE_JS_API=arguments,canvas,print,printErr,preRun,postRun,preInit,locateFile,wasmBinary,onRuntimeInitialized,onAbort,setStatus,monitorRunDependencies -sEXPORTED_FUNCTIONS=_main,_Reset_Warm,_Reset_Cold,_Main_UnPauseEmulation,_Floppy_InsertDiskIntoDrive,_Floppy_SetDiskFileName,_Floppy_EjectDiskFromDrive,_Configuration_Apply,_malloc,_free,_lcweb_set_input,_lcweb_linea,_lcweb_ram,_lcweb_ram_size,_lcweb_screen_base,_lcweb_palette,_lcweb_shifter_mode,_lcweb_ram_hash,_lcweb_set_cursor_addr,_lcweb_mouse_delta,_lcweb_key,_lcweb_snapshot_save,_lcweb_snapshot_load,_lcweb_surface,_lcweb_surface_w,_lcweb_surface_h,_lcweb_surface_pitch,_lcweb_audio_take,_lcweb_stereo --preload-file ${CMAKE_BINARY_DIR}/files/@/share/hatari/ --pre-js ${CMAKE_SOURCE_DIR}/src/emscripten.js --shell-file ${CMAKE_SOURCE_DIR}/src/emscripten_shell.html ${LC_EXTRA_LINK}" ) # Other targets, use default sources else() diff --git a/src/audio.c b/src/audio.c index 41d39ab..dad389d 100644 --- a/src/audio.c +++ b/src/audio.c @@ -124,6 +124,14 @@ void Audio_Init(void) return; } +#ifdef __EMSCRIPTEN__ + /* The web page pulls the mixed samples itself (lcweb_audio_take) and plays + * them off the main thread, so no SDL device is opened. */ + SoundBufferSize = 1024; + bSoundWorking = true; + bPlayingBuffer = true; + return; +#endif /* Init the SDL's audio subsystem: */ if (SDL_WasInit(SDL_INIT_AUDIO) == 0) { @@ -195,7 +203,9 @@ void Audio_UnInit(void) /* Stop */ Audio_EnableAudio(false); +#ifndef __EMSCRIPTEN__ SDL_CloseAudio(); +#endif bSoundWorking = false; } @@ -208,6 +218,9 @@ void Audio_UnInit(void) */ void Audio_Lock(void) { +#ifdef __EMSCRIPTEN__ + return; +#endif SDL_LockAudio(); } @@ -218,6 +231,9 @@ void Audio_Lock(void) */ void Audio_Unlock(void) { +#ifdef __EMSCRIPTEN__ + return; +#endif SDL_UnlockAudio(); } @@ -270,6 +286,10 @@ void Audio_SetOutputAudioFreq(int nNewFrequency) */ void Audio_EnableAudio(bool bEnable) { +#ifdef __EMSCRIPTEN__ + bPlayingBuffer = bEnable; + return; +#endif if (bEnable && !bPlayingBuffer) { /* Start playing */ diff --git a/src/cpu/CMakeLists.txt b/src/cpu/CMakeLists.txt index e91d6a7..53df8ed 100644 --- a/src/cpu/CMakeLists.txt +++ b/src/cpu/CMakeLists.txt @@ -18,30 +18,33 @@ set(WINUAE_SRCS cpudefs.c cpummu.c cpummu030.c debug.c disasm.c newcpu_common.c # once for cross compiling (with calling the host cc directly) and once # for native compiling so that the rules also work for non-Unix environments... if(CMAKE_CROSSCOMPILING) + if(NOT HOST_CC) + set(HOST_CC cc) + endif() - add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/build68k - COMMAND cc -I${CMAKE_CURRENT_SOURCE_DIR} + add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/build68k${HOST_EXE} + COMMAND ${HOST_CC} -I${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/build68k.c ${CMAKE_CURRENT_SOURCE_DIR}/writelog.c - -o ${CMAKE_CURRENT_BINARY_DIR}/build68k + -o ${CMAKE_CURRENT_BINARY_DIR}/build68k${HOST_EXE} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/build68k.c) add_custom_command(OUTPUT cpudefs.c - COMMAND ./build68k < ${CMAKE_CURRENT_SOURCE_DIR}/table68k >cpudefs.c + COMMAND ${CMAKE_CURRENT_BINARY_DIR}/build68k${HOST_EXE} < ${CMAKE_CURRENT_SOURCE_DIR}/table68k >cpudefs.c DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/table68k - ${CMAKE_CURRENT_BINARY_DIR}/build68k) + ${CMAKE_CURRENT_BINARY_DIR}/build68k${HOST_EXE}) - add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/gencpu - COMMAND cc -I${CMAKE_CURRENT_SOURCE_DIR} + add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/gencpu${HOST_EXE} + COMMAND ${HOST_CC} -I${CMAKE_CURRENT_SOURCE_DIR} cpudefs.c ${CMAKE_CURRENT_SOURCE_DIR}/gencpu.c ${CMAKE_CURRENT_SOURCE_DIR}/readcpu.c - -o ${CMAKE_CURRENT_BINARY_DIR}/gencpu + -o ${CMAKE_CURRENT_BINARY_DIR}/gencpu${HOST_EXE} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/gencpu.c ${CMAKE_CURRENT_SOURCE_DIR}/readcpu.c cpudefs.c) add_custom_command(OUTPUT ${CPUEMU_SRCS} - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/gencpu - DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/gencpu) + COMMAND ${CMAKE_CURRENT_BINARY_DIR}/gencpu${HOST_EXE} + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/gencpu${HOST_EXE}) else() # Rules for normal build follow diff --git a/src/emscripten.js b/src/emscripten.js index 57a5860..8b12cfc 100644 --- a/src/emscripten.js +++ b/src/emscripten.js @@ -1,4 +1,5 @@ -Module['arguments']=[ - '--desktop','false', - '-d','/share/hatari/fs/', - '--machine','ste']; +Module['arguments'] = Module['arguments'] || [ + '--desktop', 'false', '--confirm-quit', 'false', '--statusbar', 'false', '--drive-led', 'false', + '--machine', 'st', '--memsize', '1024', '--tos', '/share/hatari/tos.img', + '--monitor', 'rgb', '--sound', 'off', + '-d', '/share/hatari/fs/', '--auto', 'C:\LASRCHES.PRG']; diff --git a/src/includes/lcweb.h b/src/includes/lcweb.h new file mode 100644 index 0000000..72610e0 --- /dev/null +++ b/src/includes/lcweb.h @@ -0,0 +1,19 @@ +/* + Laser Chess web front end glue (Emscripten only). +*/ +#ifndef HATARI_LCWEB_H +#define HATARI_LCWEB_H + +#include + +extern void LCWeb_Vbl(void); +extern void lcweb_set_input(int x, int y, int buttons); +extern uint32_t lcweb_linea(void); +extern uint8_t *lcweb_ram(void); +extern uint32_t lcweb_ram_size(void); +extern uint32_t lcweb_screen_base(void); +extern int lcweb_palette(int n); +extern int lcweb_shifter_mode(void); +extern uint32_t lcweb_ram_hash(void); + +#endif diff --git a/src/lcweb.c b/src/lcweb.c new file mode 100644 index 0000000..db4c8b2 --- /dev/null +++ b/src/lcweb.c @@ -0,0 +1,244 @@ +/* + Web front end glue (Emscripten only), shared by Laser Chess and + Star Trek: The Rebel Universe. + + JavaScript owns the clock: at every emulated VBL the emulator yields to + Module.lcVbl(frame), which resolves when the next frame may run (local + pacing, or once both online players' inputs for that frame are known). + Input is an absolute ST pointer position plus button state; it is turned + into relative IKBD mouse packets here, from the GEM cursor position kept + in the Line-A variables, so both online peers derive identical packets + from identical emulated state. +*/ +#include "main.h" +#include "configuration.h" +#include "stMemory.h" +#include "ikbd.h" +#include "ioMem.h" +#include "video.h" +#include "memorySnapShot.h" +#include "sound.h" +#include "screen.h" +#include "lcweb.h" +#include + +static int in_x = 160, in_y = 100, in_buttons = 0; /* wanted pointer state */ +static int cur_buttons = 0; /* buttons as sent to the ST */ +static int hold_frames = 0; +static uint32_t linea = 0; +static uint32_t frame_no = 0; +/* Games that draw their own pointer: where they keep its x and y words */ +static uint32_t cur_xaddr = 0, cur_yaddr = 0; +static int cur_poke = 0; /* write the pointer words instead of steering with deltas */ +static int raw_mode = 0, raw_dx = 0, raw_dy = 0; + +EM_ASYNC_JS(int, lc_js_vbl, (int frame), { + return await Module.lcVbl(frame); +}); + +EMSCRIPTEN_KEEPALIVE void lcweb_set_input(int x, int y, int buttons) +{ + in_x = x; + in_y = y; + in_buttons = buttons; +} + +/* Track the game's own pointer words instead of the GEM (Line-A) cursor */ +EMSCRIPTEN_KEEPALIVE void lcweb_set_cursor_addr(uint32_t xaddr, uint32_t yaddr, int poke) +{ + cur_poke = poke; + cur_xaddr = xaddr; + cur_yaddr = yaddr; +} + +/* Plain relative mouse motion, used while the pointer words are unknown */ +EMSCRIPTEN_KEEPALIVE void lcweb_mouse_delta(int dx, int dy) +{ + raw_mode = 1; + raw_dx += dx; + raw_dy += dy; +} + +EMSCRIPTEN_KEEPALIVE void lcweb_key(int scancode, int down) +{ + IKBD_PressSTKey((uint8_t)scancode, down != 0); +} + +/* Deferred to the end of the current instruction by Hatari itself */ +EMSCRIPTEN_KEEPALIVE void lcweb_snapshot_save(const char *path) +{ + MemorySnapShot_Capture(path, false); +} + +EMSCRIPTEN_KEEPALIVE void lcweb_snapshot_load(const char *path) +{ + MemorySnapShot_Restore(path, false); +} + +/* The frame as the ST monitor showed it (Hatari's own conversion), 0x00RRGGBB pixels */ +EMSCRIPTEN_KEEPALIVE uint8_t *lcweb_surface(void) +{ + return sdlscrn ? (uint8_t *)sdlscrn->pixels : NULL; +} + +EMSCRIPTEN_KEEPALIVE int lcweb_surface_w(void) { return sdlscrn ? sdlscrn->w : 0; } +EMSCRIPTEN_KEEPALIVE int lcweb_surface_h(void) { return sdlscrn ? sdlscrn->h : 0; } +EMSCRIPTEN_KEEPALIVE int lcweb_surface_pitch(void) { return sdlscrn ? sdlscrn->pitch : 0; } + +/* Sound: the page takes the mixed samples itself (audio.c opens no SDL device + * here) and plays them through an AudioWorklet. Returns stereo frames copied. */ +EMSCRIPTEN_KEEPALIVE int lcweb_audio_take(int16_t *out, int max_frames) +{ + int n = nGeneratedSamples < max_frames ? nGeneratedSamples : max_frames; + if (n < 0) + n = 0; + for (int i = 0; i < n; i++) + { + out[2 * i] = AudioMixBuffer[(AudioMixBuffer_pos_read + i) & AUDIOMIXBUFFER_SIZE_MASK][0]; + out[2 * i + 1] = AudioMixBuffer[(AudioMixBuffer_pos_read + i) & AUDIOMIXBUFFER_SIZE_MASK][1]; + } + AudioMixBuffer_pos_read = (AudioMixBuffer_pos_read + n) & AUDIOMIXBUFFER_SIZE_MASK; + nGeneratedSamples -= n; + return n; +} + +extern int Sound_StereoWidth; +EMSCRIPTEN_KEEPALIVE void lcweb_stereo(int width) +{ + Sound_StereoWidth = width < 0 ? 0 : width > 100 ? 100 : width; +} + +EMSCRIPTEN_KEEPALIVE uint32_t lcweb_linea(void) +{ + return linea; +} + +EMSCRIPTEN_KEEPALIVE uint8_t *lcweb_ram(void) +{ + return STRam; +} + +EMSCRIPTEN_KEEPALIVE uint32_t lcweb_ram_size(void) +{ + return STRamEnd; +} + +EMSCRIPTEN_KEEPALIVE uint32_t lcweb_screen_base(void) +{ + return Video_GetScreenBaseAddr(); +} + +/* ST palette register n as 0x0RGB (STE 4-bit nibbles) */ +EMSCRIPTEN_KEEPALIVE int lcweb_palette(int n) +{ + return IoMem_ReadWord(0xff8240 + n * 2); +} + +EMSCRIPTEN_KEEPALIVE int lcweb_shifter_mode(void) +{ + return IoMem_ReadByte(0xff8260) & 3; +} + +/* FNV-1a over ST RAM, for desync checks between online peers */ +EMSCRIPTEN_KEEPALIVE uint32_t lcweb_ram_hash(void) +{ + uint32_t h = 2166136261u; + for (uint32_t i = 0; i < STRamEnd; i++) + { + h ^= STRam[i]; + h *= 16777619u; + } + return h; +} + +/* The Line-A variable block: PLANES at base, WIDTH at +2, BYTES_LIN at -2, + * V_REZ_VT at -4, V_REZ_HZ at -0xc. Look for the ST low-res values. */ +static void find_linea(void) +{ + for (uint32_t a = 0x400; a + 4 < STRamEnd && a < 0x40000; a += 2) + { + if (STMemory_ReadWord(a) == 4 && STMemory_ReadWord(a + 2) == 160 + && STMemory_ReadWord(a - 2) == 160 && STMemory_ReadWord(a - 4) == 200 + && STMemory_ReadWord(a - 0xc) == 320) + { + linea = a; + return; + } + } +} + +static int clamp(int v, int lo, int hi) +{ + return v < lo ? lo : v > hi ? hi : v; +} + +static void apply_input(void) +{ + int dx = 0, dy = 0, at = 1; + uint32_t xa = 0, ya = 0; + + if (cur_xaddr && cur_yaddr) + { + /* the game's own pointer */ + xa = cur_xaddr; + ya = cur_yaddr; + } + else if (raw_mode) + { + dx = clamp(raw_dx, -60, 60); + dy = clamp(raw_dy, -60, 60); + raw_dx -= dx; + raw_dy -= dy; + } + else + { + /* the GEM cursor */ + if (!linea && frame_no > 50 && (frame_no % 25) == 0) + find_linea(); + if (linea) + { + xa = linea - 0x25a; + ya = linea - 0x258; + } + } + if (xa && cur_poke) + { + /* Put the pointer exactly where the page wants it, every frame: no + * lag, no overshoot, and the game can't drag it elsewhere. */ + STMemory_WriteWord(xa, clamp(in_x, 0, 319)); + STMemory_WriteWord(ya, clamp(in_y, 0, 199)); + } + else if (xa) + { + int cx = (int16_t)STMemory_ReadWord(xa); + int cy = (int16_t)STMemory_ReadWord(ya); + dx = clamp(in_x - cx, -60, 60); + dy = clamp(in_y - cy, -60, 60); + at = (dx == 0 && dy == 0); + } + KeyboardProcessor.Mouse.dx += dx; + KeyboardProcessor.Mouse.dy += dy; + + /* A button change waits until the cursor has arrived (at most 10 + * frames), so a click lands where it was made. */ + if (in_buttons != cur_buttons) + { + if (at || ++hold_frames > 10) + { + cur_buttons = in_buttons; + hold_frames = 0; + } + } + else + hold_frames = 0; + Keyboard.bLButtonDown = (Keyboard.bLButtonDown & ~BUTTON_MOUSE) | ((cur_buttons & 1) ? BUTTON_MOUSE : 0); + Keyboard.bRButtonDown = (Keyboard.bRButtonDown & ~BUTTON_MOUSE) | ((cur_buttons & 2) ? BUTTON_MOUSE : 0); +} + +/* Called from Main_WaitOnVbl in place of the host-clock wait. */ +void LCWeb_Vbl(void) +{ + frame_no++; + lc_js_vbl((int)frame_no); + apply_input(); +} diff --git a/src/main.c b/src/main.c index dacf9c3..d59b788 100644 --- a/src/main.c +++ b/src/main.c @@ -76,6 +76,7 @@ const char Main_fileid[] = "Hatari main.c"; #ifdef EMSCRIPTEN #include "emscripten.h" +#include "lcweb.h" #endif bool bQuitProgram = false; /* Flag to quit program cleanly */ @@ -355,6 +356,12 @@ void Main_WaitOnVbl(void) exit(0); } +#ifdef EMSCRIPTEN + /* Laser Chess web: JavaScript paces frames and feeds input */ + LCWeb_Vbl(); + return; +#endif + // FrameDuration_micro = (int64_t) ( 1000000.0 / nScreenRefreshRate + 0.5 ); /* round to closest integer */ FrameDuration_micro = ClocksTimings_GetVBLDuration_micro ( ConfigureParams.System.nMachineType , nScreenRefreshRate ); FrameDuration_micro *= nVBLSlowdown; @@ -1022,7 +1029,12 @@ void Main_ErrorExit(const char *msg1, const char *msg2, int errval) int main(int argc, char *argv[]) { /* Generate random seed */ +#ifdef EMSCRIPTEN + /* deterministic for online lockstep: the seed comes from the page */ + Hatari_srand(EM_ASM_INT({ return Module.lcSeed | 0; })); +#else Hatari_srand(time(NULL)); +#endif /* Setup for string conversions */ Str_Init(); diff --git a/src/sound.c b/src/sound.c index f8ec9c6..bb9faad 100644 --- a/src/sound.c +++ b/src/sound.c @@ -318,6 +318,13 @@ bool Sound_BufferIndexNeedReset = false; /* to have some kind of double buffering */ #define YM_BUFFER_250_SIZE_MASK ( YM_BUFFER_250_SIZE - 1 ) /* To limit index values inside the ring buffer */ ymsample YM_Buffer_250[ YM_BUFFER_250_SIZE ]; /* Ring buffer to store YM samples */ +#ifdef __EMSCRIPTEN__ +/* Web stereo: voice A minus voice C at 250 kHz, mixed in as a side signal + * (A to the left, C to the right, B in the middle). 0 = the ST's mono. */ +static int16_t YM_Side_250[ YM_BUFFER_250_SIZE ]; +int Sound_StereoWidth = 0; /* 0..100 */ +static int32_t Side_hpf_x, Side_hpf_y; +#endif static int YM_Buffer_250_pos_write; /* Current writing position into above buffer */ static int YM_Buffer_250_pos_read; /* Current reading position into above buffer */ @@ -1109,6 +1116,9 @@ static void YM2149_DoSamples_250 ( int SamplesToGenerate_250 ) Tone3Voices &= ( Env3Voices | Vol3Voices ); sample = ymout5[ Tone3Voices ]; /* 16 bits signed value */ +#ifdef __EMSCRIPTEN__ + YM_Side_250[ pos ] = ( ymout5[ Tone3Voices & YM_MASK_1VOICE ] - ymout5[ Tone3Voices & ( YM_MASK_1VOICE << 10 ) ] ) / 2; +#endif /* Apply low pass filter ? */ if ( YM2149_LPF_Filter == YM2149_LPF_FILTER_LPF_STF ) @@ -1739,7 +1749,29 @@ static int Sound_GenerateSamples(uint64_t CPU_Clock) { while ( ( ( YM_Buffer_250_pos_write - YM_Buffer_250_pos_read ) & YM_BUFFER_250_SIZE_MASK ) >= ym_margin ) { +#ifdef __EMSCRIPTEN__ + int p0 = YM_Buffer_250_pos_read; + int mid = Subsonic_IIR_HPF_Left( YM2149_NextSample_250() ); + int p1 = YM_Buffer_250_pos_read, cnt = 0; + int32_t side = 0; + if ( Sound_StereoWidth > 0 ) + { + /* box-average the side signal over the 250 kHz samples just consumed */ + for ( int p = p0 ; p != p1 && cnt < 64 ; p = ( p + 1 ) & YM_BUFFER_250_SIZE_MASK , cnt++ ) + side += YM_Side_250[ p ]; + if ( cnt ) + side /= cnt; + /* one-pole high pass (~20 Hz) so a held note doesn't pull the image sideways */ + Side_hpf_y = side - Side_hpf_x + ( Side_hpf_y * 4083 ) / 4096; + Side_hpf_x = side; + side = ( Side_hpf_y * Sound_StereoWidth ) / 100; + } + int l = mid + side, r = mid - side; + AudioMixBuffer[idx][0] = l > 32767 ? 32767 : l < -32768 ? -32768 : l; + AudioMixBuffer[idx][1] = r > 32767 ? 32767 : r < -32768 ? -32768 : r; +#else AudioMixBuffer[idx][0] = AudioMixBuffer[idx][1] = Subsonic_IIR_HPF_Left( YM2149_NextSample_250() ); +#endif idx = ( idx+1 ) & AUDIOMIXBUFFER_SIZE_MASK; Sample_Nbr++; }