From 707cd5c5c602a5d128739febb209576bf50d4b34 Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Sat, 14 Sep 2024 13:03:21 +0700 Subject: [PATCH 01/14] Revert "don't use SDL_RenderSetLogicalSize (#1847)" (#1906) This reverts commit 8a8a47c29ef7d9a3c1f03fc8daaf0bbd7b05544a. --- src/i_video.c | 64 ++++----------------------------------------------- 1 file changed, 5 insertions(+), 59 deletions(-) diff --git a/src/i_video.c b/src/i_video.c index 5c00e4565..6b45515bc 100644 --- a/src/i_video.c +++ b/src/i_video.c @@ -265,9 +265,6 @@ static void FocusLost(void) #define FocusLost() #endif -static boolean letterboxed; -static void UpdateViewport(void); - // [FG] window event handling from Chocolate Doom 3.0 static void HandleWindowEvent(SDL_WindowEvent *event) @@ -317,7 +314,6 @@ static void HandleWindowEvent(SDL_WindowEvent *event) SDL_GetWindowPosition(screen, &window_x, &window_y); } window_resize = true; - UpdateViewport(); break; case SDL_WINDOWEVENT_MOVED: @@ -418,8 +414,6 @@ static void I_ToggleFullScreen(void) SDL_SetWindowResizable(screen, SDL_TRUE); SDL_SetWindowSize(screen, window_width, window_height); } - - UpdateViewport(); } static void I_ToggleExclusiveFullScreen(void) @@ -673,10 +667,7 @@ static void UpdateRender(void) SDL_UnlockTexture(texture); - if (letterboxed) - { - SDL_RenderClear(renderer); - } + SDL_RenderClear(renderer); if (texture_upscaled) { @@ -1405,60 +1396,15 @@ static void CreateUpscaledTexture(boolean force) SDL_SetTextureScaleMode(texture_upscaled, SDL_ScaleModeLinear); } -static void UpdateViewport(void) -{ - int w, h; - SDL_GetRendererOutputSize(renderer, &w, &h); - - double real_aspect = (double)w / h; - double want_aspect = CurrentAspectRatio(); - - // Clear the scale because we're setting viewport in output coordinates - SDL_RenderSetScale(renderer, 1.0f, 1.0f); - - SDL_Rect viewport = {0}; - - if (fabs(want_aspect - real_aspect) < 0.0001) - { - float scalex = (float)w / video.width; - float scaley = (float)h / actualheight; - viewport.w = w; - viewport.h = h; - SDL_RenderSetViewport(renderer, &viewport); - SDL_RenderSetScale(renderer, scalex, scaley); - letterboxed = false; - return; - } - - float scale; - - letterboxed = true; - - if (want_aspect > real_aspect) - { - scale = (float)w / video.width; - viewport.w = w; - viewport.h = (int)floor(actualheight * scale); - viewport.y = (h - viewport.h) / 2; - } - else - { - scale = (float)h / actualheight; - viewport.h = h; - viewport.w = (int)floor(video.width * scale); - viewport.x = (w - viewport.w) / 2; - } - - SDL_RenderSetViewport(renderer, &viewport); - SDL_RenderSetScale(renderer, scale, scale); -} - static void ResetLogicalSize(void) { blit_rect.w = video.width; blit_rect.h = video.height; - UpdateViewport(); + if (SDL_RenderSetLogicalSize(renderer, video.width, actualheight)) + { + I_Printf(VB_ERROR, "Failed to set logical size: %s", SDL_GetError()); + } if (smooth_scaling) { From a4eb56e4ce20b1a6e0550b4c8ea9111d0dc1813d Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Sat, 14 Sep 2024 18:43:56 +0700 Subject: [PATCH 02/14] increase the height of the fire sky texture (#1909) --- src/r_sky.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/r_sky.h b/src/r_sky.h index 142f4f71d..a4491af9d 100644 --- a/src/r_sky.h +++ b/src/r_sky.h @@ -49,7 +49,7 @@ byte R_GetSkyColor(int texturenum); void R_UpdateSky(void); #define FIRE_WIDTH 128 -#define FIRE_HEIGHT 256 +#define FIRE_HEIGHT 320 byte *R_GetFireColumn(int col); From 3bee945f2f5983a9a6209276d8768c3f70892228 Mon Sep 17 00:00:00 2001 From: jpernst Date: Sat, 14 Sep 2024 22:18:56 -0700 Subject: [PATCH 03/14] OPL3 Improvements (#1907) * Use correct OPL3 channel polarity by default * Support multiple OPL3 chips * Use native OPL sample rate and allow OpenAL to resample it * Bind DMX OPL stereo correction to a config variable; default to vanilla * Consolidate num_opl_chips variables; remove menu item Use BETWEEN macro for mix clamping * Disable upper register banks when dmx is in OPL2 mode --- opl/opl.c | 137 +++++++++++++++++++++++---------------------- opl/opl.h | 15 +++-- opl/opl_internal.h | 6 +- opl/opl_sdl.c | 64 ++++++++++++++++----- src/i_oplmusic.c | 69 +++++++++++++---------- 5 files changed, 170 insertions(+), 121 deletions(-) diff --git a/opl/opl.c b/opl/opl.c index 603a12143..17cff8154 100644 --- a/opl/opl.c +++ b/opl/opl.c @@ -32,7 +32,7 @@ static opl_driver_t *drivers[] = static opl_driver_t *driver = NULL; -unsigned int opl_sample_rate = 22050; +unsigned int opl_sample_rate = OPL_SAMPLE_RATE; // // Init/shutdown code. @@ -42,11 +42,11 @@ unsigned int opl_sample_rate = 22050; // true if an OPL is detected. static opl_init_result_t InitDriver(opl_driver_t *_driver, - unsigned int port_base) + unsigned int port_base, int num_chips) { // Initialize the driver. - if (!_driver->init_func(port_base)) + if (!_driver->init_func(port_base, num_chips)) { return OPL_INIT_NONE; } @@ -58,21 +58,21 @@ static opl_init_result_t InitDriver(opl_driver_t *_driver, driver = _driver; - printf("OPL_Init: Using driver '%s'.\n", driver->name); + printf("OPL_Init: Using driver '%s' with %d chip(s).\n", driver->name, num_opl_chips); return OPL_INIT_OPL3; } // Find a driver automatically by trying each in the list. -static opl_init_result_t AutoSelectDriver(unsigned int port_base) +static opl_init_result_t AutoSelectDriver(unsigned int port_base, int num_chips) { int i; opl_init_result_t result; for (i=0; drivers[i] != NULL; ++i) { - result = InitDriver(drivers[i], port_base); + result = InitDriver(drivers[i], port_base, num_chips); if (result != OPL_INIT_NONE) { return result; @@ -87,7 +87,7 @@ static opl_init_result_t AutoSelectDriver(unsigned int port_base) // Initialize the OPL library. Return value indicates type of OPL chip // detected, if any. -opl_init_result_t OPL_Init(unsigned int port_base) +opl_init_result_t OPL_Init(unsigned int port_base, int num_chips) { char *driver_name; int i; @@ -103,7 +103,7 @@ opl_init_result_t OPL_Init(unsigned int port_base) { if (!strcmp(driver_name, drivers[i]->name)) { - result = InitDriver(drivers[i], port_base); + result = InitDriver(drivers[i], port_base, num_chips); if (result) { return result; @@ -123,7 +123,7 @@ opl_init_result_t OPL_Init(unsigned int port_base) } else { - return AutoSelectDriver(port_base); + return AutoSelectDriver(port_base, num_chips); } } @@ -145,7 +145,7 @@ void OPL_SetSampleRate(unsigned int rate) opl_sample_rate = rate; } -void OPL_WritePort(opl_port_t port, unsigned int value) +void OPL_WritePort(int chip, opl_port_t port, unsigned int value) { if (driver != NULL) { @@ -153,11 +153,11 @@ void OPL_WritePort(opl_port_t port, unsigned int value) printf("OPL_write: %i, %x\n", port, value); fflush(stdout); #endif - driver->write_port_func(port, value); + driver->write_port_func(chip, port, value); } } -unsigned int OPL_ReadPort(opl_port_t port) +unsigned int OPL_ReadPort(int chip, opl_port_t port) { if (driver != NULL) { @@ -168,7 +168,7 @@ unsigned int OPL_ReadPort(opl_port_t port) fflush(stdout); #endif - result = driver->read_port_func(port); + result = driver->read_port_func(chip, port); #ifdef OPL_DEBUG_TRACE printf("OPL_read: %i -> %x\n", port, result); @@ -188,24 +188,24 @@ unsigned int OPL_ReadPort(opl_port_t port) // (register write, etc). // -unsigned int OPL_ReadStatus(void) +unsigned int OPL_ReadStatus(int chip) { - return OPL_ReadPort(OPL_REGISTER_PORT); + return OPL_ReadPort(chip, OPL_REGISTER_PORT); } // Write an OPL register value -void OPL_WriteRegister(int reg, int value) +void OPL_WriteRegister(int chip, int reg, int value) { int i; if (reg & 0x100) { - OPL_WritePort(OPL_REGISTER_PORT_OPL3, reg); + OPL_WritePort(chip, OPL_REGISTER_PORT_OPL3, reg); } else { - OPL_WritePort(OPL_REGISTER_PORT, reg); + OPL_WritePort(chip, OPL_REGISTER_PORT, reg); } // For timing, read the register port six times after writing the @@ -213,17 +213,17 @@ void OPL_WriteRegister(int reg, int value) for (i=0; i<6; ++i) { - OPL_ReadPort(OPL_DATA_PORT); + OPL_ReadPort(chip, OPL_DATA_PORT); } - OPL_WritePort(OPL_DATA_PORT, value); + OPL_WritePort(chip, OPL_DATA_PORT, value); // Read the register port 24 times after writing the value to // cause the appropriate delay for (i=0; i<24; ++i) { - OPL_ReadStatus(); + OPL_ReadStatus(chip); } } @@ -231,50 +231,15 @@ void OPL_WriteRegister(int reg, int value) void OPL_InitRegisters(int opl3) { - int r; - - // Initialize level registers - - for (r=OPL_REGS_LEVEL; r <= OPL_REGS_LEVEL + OPL_NUM_OPERATORS; ++r) - { - OPL_WriteRegister(r, 0x3f); - } - - // Initialize other registers - // These two loops write to registers that actually don't exist, - // but this is what Doom does ... - // Similarly, the <= is also intenational. - - for (r=OPL_REGS_ATTACK; r <= OPL_REGS_WAVEFORM + OPL_NUM_OPERATORS; ++r) + for (int c = 0; c < num_opl_chips; ++c) { - OPL_WriteRegister(r, 0x00); - } - - // More registers ... - - for (r=1; r < OPL_REGS_LEVEL; ++r) - { - OPL_WriteRegister(r, 0x00); - } - - // Re-initialize the low registers: - - // Reset both timers and enable interrupts: - OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x60); - OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x80); - - // "Allow FM chips to control the waveform of each operator": - OPL_WriteRegister(OPL_REG_WAVEFORM_ENABLE, 0x20); - - if (opl3) - { - OPL_WriteRegister(OPL_REG_NEW, 0x01); + int r; // Initialize level registers for (r=OPL_REGS_LEVEL; r <= OPL_REGS_LEVEL + OPL_NUM_OPERATORS; ++r) { - OPL_WriteRegister(r | 0x100, 0x3f); + OPL_WriteRegister(c, r, 0x3f); } // Initialize other registers @@ -284,23 +249,61 @@ void OPL_InitRegisters(int opl3) for (r=OPL_REGS_ATTACK; r <= OPL_REGS_WAVEFORM + OPL_NUM_OPERATORS; ++r) { - OPL_WriteRegister(r | 0x100, 0x00); + OPL_WriteRegister(c, r, 0x00); } // More registers ... for (r=1; r < OPL_REGS_LEVEL; ++r) { - OPL_WriteRegister(r | 0x100, 0x00); + OPL_WriteRegister(c, r, 0x00); } - } - // Keyboard split point on (?) - OPL_WriteRegister(OPL_REG_FM_MODE, 0x40); + // Re-initialize the low registers: - if (opl3) - { - OPL_WriteRegister(OPL_REG_NEW, 0x01); + // Reset both timers and enable interrupts: + OPL_WriteRegister(c, OPL_REG_TIMER_CTRL, 0x60); + OPL_WriteRegister(c, OPL_REG_TIMER_CTRL, 0x80); + + // "Allow FM chips to control the waveform of each operator": + OPL_WriteRegister(c, OPL_REG_WAVEFORM_ENABLE, 0x20); + + if (opl3) + { + OPL_WriteRegister(c, OPL_REG_NEW, 0x01); + + // Initialize level registers + + for (r=OPL_REGS_LEVEL; r <= OPL_REGS_LEVEL + OPL_NUM_OPERATORS; ++r) + { + OPL_WriteRegister(c, r | 0x100, 0x3f); + } + + // Initialize other registers + // These two loops write to registers that actually don't exist, + // but this is what Doom does ... + // Similarly, the <= is also intenational. + + for (r=OPL_REGS_ATTACK; r <= OPL_REGS_WAVEFORM + OPL_NUM_OPERATORS; ++r) + { + OPL_WriteRegister(c, r | 0x100, 0x00); + } + + // More registers ... + + for (r=1; r < OPL_REGS_LEVEL; ++r) + { + OPL_WriteRegister(c, r | 0x100, 0x00); + } + } + + // Keyboard split point on (?) + OPL_WriteRegister(c, OPL_REG_FM_MODE, 0x40); + + if (opl3) + { + OPL_WriteRegister(c, OPL_REG_NEW, 0x01); + } } } diff --git a/opl/opl.h b/opl/opl.h index a8f3b0ac6..7dce72083 100644 --- a/opl/opl.h +++ b/opl/opl.h @@ -38,6 +38,9 @@ typedef enum OPL_REGISTER_PORT_OPL3 = 2 } opl_port_t; +#define OPL_SAMPLE_RATE 49716 +#define OPL_MAX_CHIPS 6 + #define OPL_NUM_OPERATORS 21 #define OPL_NUM_VOICES 9 @@ -68,13 +71,15 @@ typedef enum #define OPL_MS ((uint64_t) 1000) #define OPL_US ((uint64_t) 1) +extern int num_opl_chips; + // // Low-level functions. // // Initialize the OPL subsystem. -opl_init_result_t OPL_Init(unsigned int port_base); +opl_init_result_t OPL_Init(unsigned int port_base, int num_chips); // Shut down the OPL subsystem. @@ -86,11 +91,11 @@ void OPL_SetSampleRate(unsigned int rate); // Write to one of the OPL I/O ports: -void OPL_WritePort(opl_port_t port, unsigned int value); +void OPL_WritePort(int chip, opl_port_t port, unsigned int value); // Read from one of the OPL I/O ports: -unsigned int OPL_ReadPort(opl_port_t port); +unsigned int OPL_ReadPort(int chip, opl_port_t port); // // Higher-level functions. @@ -98,11 +103,11 @@ unsigned int OPL_ReadPort(opl_port_t port); // Read the cuurrent status byte of the OPL chip. -unsigned int OPL_ReadStatus(void); +unsigned int OPL_ReadStatus(int chip); // Write to an OPL register. -void OPL_WriteRegister(int reg, int value); +void OPL_WriteRegister(int chip, int reg, int value); // Initialize all registers, performed on startup. diff --git a/opl/opl_internal.h b/opl/opl_internal.h index 0b948f5f8..e32f7010b 100644 --- a/opl/opl_internal.h +++ b/opl/opl_internal.h @@ -21,10 +21,10 @@ #include "opl.h" -typedef int (*opl_init_func)(unsigned int port_base); +typedef int (*opl_init_func)(unsigned int port_base, int num_chips); typedef void (*opl_shutdown_func)(void); -typedef unsigned int (*opl_read_port_func)(opl_port_t port); -typedef void (*opl_write_port_func)(opl_port_t port, unsigned int value); +typedef unsigned int (*opl_read_port_func)(int chip, opl_port_t port); +typedef void (*opl_write_port_func)(int chip, opl_port_t port, unsigned int value); typedef void (*opl_set_callback_func)(uint64_t us, opl_callback_t callback, void *data); diff --git a/opl/opl_sdl.c b/opl/opl_sdl.c index 09803a174..83c75ccb1 100644 --- a/opl/opl_sdl.c +++ b/opl/opl_sdl.c @@ -50,7 +50,7 @@ static uint64_t pause_offset; // OPL software emulator structure. -static opl3_chip opl_chip; +static opl3_chip opl_chips[OPL_MAX_CHIPS]; static int opl_opl3mode; // Register number that was written. @@ -137,7 +137,23 @@ int OPL_FillBuffer(byte *buffer, int buffer_samples) } // Add emulator output to buffer. - OPL3_GenerateStream(&opl_chip, (Bit16s *)(buffer + filled * 4), nsamples); + Bit16s *cursor = (Bit16s *)(buffer + filled * 4); + for (int s = 0; s < nsamples; ++s) + { + Bit32s mix[2] = {0, 0}; + for (int c = 0; c < num_opl_chips; ++c) + { + Bit16s sample[2]; + OPL3_GenerateResampled(&opl_chips[c], sample); + mix[0] += sample[0]; + mix[1] += sample[1]; + } + + cursor[0] = BETWEEN(-32768, 32767, mix[0]); + cursor[1] = BETWEEN(-32768, 32767, mix[1]); + cursor += 2; + } + //OPL3_GenerateStream(&opl_chip, (Bit16s *)(buffer + filled * 4), nsamples); filled += nsamples; // Invoke callbacks for this point in time. @@ -161,7 +177,7 @@ static void OPL_SDL_Shutdown(void) */ } -static int OPL_SDL_Init(unsigned int port_base) +static int OPL_SDL_Init(unsigned int port_base, int num_chips) { opl_sdl_paused = 0; pause_offset = 0; @@ -179,13 +195,14 @@ static int OPL_SDL_Init(unsigned int port_base) // Create the emulator structure: - OPL3_Reset(&opl_chip, mixing_freq); + for (int c = 0; c < num_opl_chips; ++c) + OPL3_Reset(&opl_chips[c], mixing_freq); opl_opl3mode = 0; return 1; } -static unsigned int OPL_SDL_PortRead(opl_port_t port) +static unsigned int OPL_SDL_PortRead(int chip, opl_port_t port) { unsigned int result = 0; @@ -194,6 +211,9 @@ static unsigned int OPL_SDL_PortRead(opl_port_t port) return 0xff; } + if (chip > 0) + return result; + if (timer1.enabled && current_time > timer1.expire_time) { result |= 0x80; // Either have expired @@ -224,18 +244,26 @@ static void OPLTimer_CalculateEndTime(opl_timer_t *timer) } } -static void WriteRegister(unsigned int reg_num, unsigned int value) +static void WriteRegister(int chip, unsigned int reg_num, unsigned int value) { switch (reg_num) { case OPL_REG_TIMER1: - timer1.value = value; - OPLTimer_CalculateEndTime(&timer1); + // Only allow timers on the first chip + if (chip == 0) + { + timer1.value = value; + OPLTimer_CalculateEndTime(&timer1); + } break; case OPL_REG_TIMER2: - timer2.value = value; - OPLTimer_CalculateEndTime(&timer2); + // Only allow timers on the first chip + if (chip == 0) + { + timer2.value = value; + OPLTimer_CalculateEndTime(&timer2); + } break; case OPL_REG_TIMER_CTRL: @@ -258,19 +286,25 @@ static void WriteRegister(unsigned int reg_num, unsigned int value) OPLTimer_CalculateEndTime(&timer2); } } - break; case OPL_REG_NEW: - opl_opl3mode = value & 0x01; + // Keep all chips synchronized with the first chip's opl3 mode + if (chip == 0) + { + for (int c = 0; c < num_opl_chips; ++c) + OPL3_WriteRegBuffered(&opl_chips[c], reg_num, value); + opl_opl3mode = value & 0x01; + } + break; default: - OPL3_WriteRegBuffered(&opl_chip, reg_num, value); + OPL3_WriteRegBuffered(&opl_chips[chip], reg_num, value); break; } } -static void OPL_SDL_PortWrite(opl_port_t port, unsigned int value) +static void OPL_SDL_PortWrite(int chip, opl_port_t port, unsigned int value) { if (port == OPL_REGISTER_PORT) { @@ -282,7 +316,7 @@ static void OPL_SDL_PortWrite(opl_port_t port, unsigned int value) } else if (port == OPL_DATA_PORT) { - WriteRegister(register_num, value); + WriteRegister(chip, register_num, value); } } diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index ba1e7ac2a..7cf73fc9b 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -26,6 +26,7 @@ #include "i_printf.h" #include "i_sound.h" #include "m_array.h" +#include "m_config.h" #include "m_io.h" #include "m_swap.h" #include "memio.h" @@ -327,9 +328,9 @@ static char (*percussion_names)[32]; // Voices: -static opl_voice_t voices[OPL_NUM_VOICES * 2]; -static opl_voice_t *voice_free_list[OPL_NUM_VOICES * 2]; -static opl_voice_t *voice_alloced_list[OPL_NUM_VOICES * 2]; +static opl_voice_t voices[OPL_NUM_VOICES * 2 * OPL_MAX_CHIPS]; +static opl_voice_t *voice_free_list[OPL_NUM_VOICES * 2 * OPL_MAX_CHIPS]; +static opl_voice_t *voice_alloced_list[OPL_NUM_VOICES * 2 * OPL_MAX_CHIPS]; static int voice_free_num; static int voice_alloced_num; static int opl_opl3mode; @@ -361,6 +362,7 @@ static unsigned int last_perc_count; static char *snd_dmxoption = "-opl3"; // [crispy] default to OPL3 emulation static int opl_io_port = 0x388; +int num_opl_chips = 1; // If true, OPL sound channels are reversed to their correct arrangement // (as intended by the MIDI standard) rather than the backwards one @@ -468,7 +470,7 @@ static void ReleaseVoice(int index) // Load data to the specified operator -static void LoadOperatorData(int operator, genmidi_op_t * data, +static void LoadOperatorData(int chip, int operator, genmidi_op_t * data, boolean max_level, unsigned int *volume) { int level; @@ -489,11 +491,11 @@ static void LoadOperatorData(int operator, genmidi_op_t * data, *volume = level; - OPL_WriteRegister(OPL_REGS_LEVEL + operator, level); - OPL_WriteRegister(OPL_REGS_TREMOLO + operator, data->tremolo); - OPL_WriteRegister(OPL_REGS_ATTACK + operator, data->attack); - OPL_WriteRegister(OPL_REGS_SUSTAIN + operator, data->sustain); - OPL_WriteRegister(OPL_REGS_WAVEFORM + operator, data->waveform); + OPL_WriteRegister(chip, OPL_REGS_LEVEL + operator, level); + OPL_WriteRegister(chip, OPL_REGS_TREMOLO + operator, data->tremolo); + OPL_WriteRegister(chip, OPL_REGS_ATTACK + operator, data->attack); + OPL_WriteRegister(chip, OPL_REGS_SUSTAIN + operator, data->sustain); + OPL_WriteRegister(chip, OPL_REGS_WAVEFORM + operator, data->waveform); } // Set the instrument for a particular voice. @@ -526,16 +528,16 @@ static void SetVoiceInstrument(opl_voice_t *voice, genmidi_instr_t *instr, // is set in SetVoiceVolume (below). If we are not using // modulating mode, we must set both to minimum volume. - LoadOperatorData(voice->op2 | voice->array, &data->carrier, true, + LoadOperatorData(voice->array >> 9, voice->op2 | (voice->array & 0x100), &data->carrier, true, &voice->car_volume); - LoadOperatorData(voice->op1 | voice->array, &data->modulator, !modulating, + LoadOperatorData(voice->array >> 9, voice->op1 | (voice->array & 0x100), &data->modulator, !modulating, &voice->mod_volume); // Set feedback register that control the connection between the // two operators. Turn on bits in the upper nybble; I think this // is for OPL3, where it turns on channel A/B. - OPL_WriteRegister((OPL_REGS_FEEDBACK + voice->index) | voice->array, + OPL_WriteRegister(voice->array >> 9, (OPL_REGS_FEEDBACK + voice->index) | (voice->array & 0x100), data->feedback | voice->reg_pan); // Calculate voice priority. @@ -571,7 +573,7 @@ static void SetVoiceVolume(opl_voice_t *voice, unsigned int volume) { voice->car_volume = car_volume | (voice->car_volume & 0xc0); - OPL_WriteRegister((OPL_REGS_LEVEL + voice->op2) | voice->array, + OPL_WriteRegister(voice->array >> 9, (OPL_REGS_LEVEL + voice->op2) | (voice->array & 0x100), voice->car_volume); // If we are using non-modulated feedback mode, we must set the @@ -591,7 +593,7 @@ static void SetVoiceVolume(opl_voice_t *voice, unsigned int volume) if (mod_volume != voice->mod_volume) { voice->mod_volume = mod_volume; - OPL_WriteRegister((OPL_REGS_LEVEL + voice->op1) | voice->array, + OPL_WriteRegister(voice->array >> 9, (OPL_REGS_LEVEL + voice->op1) | (voice->array & 0x100), mod_volume | (opl_voice->modulator.scale & 0xc0)); } } @@ -605,7 +607,7 @@ static void SetVoicePan(opl_voice_t *voice, unsigned int pan) voice->reg_pan = pan; opl_voice = &voice->current_instr->voices[voice->current_instr_voice]; - OPL_WriteRegister((OPL_REGS_FEEDBACK + voice->index) | voice->array, + OPL_WriteRegister(voice->array >> 9, (OPL_REGS_FEEDBACK + voice->index) | (voice->array & 0x100), opl_voice->feedback | pan); } @@ -627,7 +629,7 @@ static void InitVoices(void) voices[i].index = i % OPL_NUM_VOICES; voices[i].op1 = voice_operators[0][i % OPL_NUM_VOICES]; voices[i].op2 = voice_operators[1][i % OPL_NUM_VOICES]; - voices[i].array = (i / OPL_NUM_VOICES) << 8; + voices[i].array = (i / OPL_NUM_VOICES) << (opl_opl3mode ? 8 : 9); voices[i].current_instr = NULL; // Add this voice to the freelist. @@ -666,7 +668,7 @@ static void I_OPL_SetMusicVolume(int volume) static void VoiceKeyOff(opl_voice_t *voice) { - OPL_WriteRegister((OPL_REGS_FREQ_2 + voice->index) | voice->array, + OPL_WriteRegister(voice->array >> 9, (OPL_REGS_FREQ_2 + voice->index) | (voice->array & 0x100), voice->freq >> 8); } @@ -883,9 +885,9 @@ static void UpdateVoiceFrequency(opl_voice_t *voice) if (voice->freq != freq) { - OPL_WriteRegister((OPL_REGS_FREQ_1 + voice->index) | voice->array, + OPL_WriteRegister(voice->array >> 9, (OPL_REGS_FREQ_1 + voice->index) | (voice->array & 0x100), freq & 0xff); - OPL_WriteRegister((OPL_REGS_FREQ_2 + voice->index) | voice->array, + OPL_WriteRegister(voice->array >> 9, (OPL_REGS_FREQ_2 + voice->index) | (voice->array & 0x100), (freq >> 8) | 0x20); voice->freq = freq; @@ -1196,9 +1198,9 @@ static void PitchBendEvent(opl_track_data_t *track, midi_event_t *event) { opl_channel_data_t *channel; int i; - opl_voice_t *voice_updated_list[OPL_NUM_VOICES * 2] = {0}; + opl_voice_t *voice_updated_list[OPL_NUM_VOICES * 2 * OPL_MAX_CHIPS] = {0}; unsigned int voice_updated_num = 0; - opl_voice_t *voice_not_updated_list[OPL_NUM_VOICES * 2] = {0}; + opl_voice_t *voice_not_updated_list[OPL_NUM_VOICES * 2 * OPL_MAX_CHIPS] = {0}; unsigned int voice_not_updated_num = 0; // Update the channel bend value. Only the MSB of the pitch bend @@ -1445,9 +1447,9 @@ static boolean I_OPL_InitStream(int device) char *dmxoption; opl_init_result_t chip_type; - OPL_SetSampleRate(SND_SAMPLERATE); + OPL_SetSampleRate(OPL_SAMPLE_RATE); - chip_type = OPL_Init(opl_io_port); + chip_type = OPL_Init(opl_io_port, num_opl_chips); if (chip_type == OPL_INIT_NONE) { I_Printf(VB_ERROR, "Dude. The Adlib isn't responding."); @@ -1457,6 +1459,12 @@ static boolean I_OPL_InitStream(int device) // The DMXOPTION variable must be set to enable OPL3 support. // As an extension, we also allow it to be set from the config file. dmxoption = M_getenv("DMXOPTION"); + + // Secret, undocumented DMXOPTION that reverses the stereo channels + // into their correct orientation. + if (dmxoption != NULL && strstr(dmxoption, "-reverse") != NULL) + opl_stereo_correct = true; + if (dmxoption == NULL) { dmxoption = snd_dmxoption != NULL ? snd_dmxoption : ""; @@ -1465,18 +1473,14 @@ static boolean I_OPL_InitStream(int device) if (chip_type == OPL_INIT_OPL3 && strstr(dmxoption, "-opl3") != NULL) { opl_opl3mode = 1; - num_opl_voices = OPL_NUM_VOICES * 2; + num_opl_voices = OPL_NUM_VOICES * 2 * num_opl_chips; } else { opl_opl3mode = 0; - num_opl_voices = OPL_NUM_VOICES; + num_opl_voices = OPL_NUM_VOICES * num_opl_chips; } - // Secret, undocumented DMXOPTION that reverses the stereo channels - // into their correct orientation. - opl_stereo_correct = strstr(dmxoption, "-reverse") != NULL; - // Initialize all registers. OPL_InitRegisters(opl_opl3mode); @@ -1550,7 +1554,7 @@ static boolean I_OPL_OpenStream(void *data, ALsizei size, ALenum *format, } *format = AL_FORMAT_STEREO16; - *freq = SND_SAMPLERATE; + *freq = OPL_SAMPLE_RATE; *frame_size = 2 * sizeof(short); return true; @@ -1670,7 +1674,10 @@ static const char **I_OPL_DeviceList(void) static void I_OPL_BindVariables(void) { - ; + BIND_NUM(num_opl_chips, 1, 1, OPL_MAX_CHIPS, + "[OPL3 Emulation] Number of chips to emulate (1-6)"); + BIND_BOOL(opl_stereo_correct, false, + "[OPL3 Emulation] Use MIDI-correct stereo channel polarity"); } stream_module_t stream_opl_module = From 3e05f8ca3dc7331ab8f5ac45bf73020596f5096b Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Sun, 15 Sep 2024 12:33:19 +0700 Subject: [PATCH 04/14] fix clang-tidy padding warning --- src/g_game.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index e13807c69..19fe81763 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3226,26 +3226,26 @@ demo_version_t G_GetNamedComplevel(const char *arg) { const struct { - demo_version_t demover; const char *const name; + demo_version_t demover; int exe; } named_complevel[] = { - {DV_VANILLA, "vanilla", exe_indetermined}, - {DV_VANILLA, "doom2", exe_doom_1_9 }, - {DV_VANILLA, "1.9", exe_doom_1_9 }, - {DV_VANILLA, "2", exe_doom_1_9 }, - {DV_VANILLA, "ultimate", exe_ultimate }, - {DV_VANILLA, "3", exe_ultimate }, - {DV_VANILLA, "final", exe_final }, - {DV_VANILLA, "tnt", exe_final }, - {DV_VANILLA, "plutonia", exe_final }, - {DV_VANILLA, "4", exe_final }, - {DV_BOOM, "boom", exe_indetermined}, - {DV_BOOM, "9", exe_indetermined}, - {DV_MBF, "mbf", exe_indetermined}, - {DV_MBF, "11", exe_indetermined}, - {DV_MBF21, "mbf21", exe_indetermined}, - {DV_MBF21, "21", exe_indetermined}, + {"vanilla", DV_VANILLA, exe_indetermined}, + {"doom2", DV_VANILLA, exe_doom_1_9 }, + {"1.9", DV_VANILLA, exe_doom_1_9 }, + {"2", DV_VANILLA, exe_doom_1_9 }, + {"ultimate", DV_VANILLA, exe_ultimate }, + {"3", DV_VANILLA, exe_ultimate }, + {"final", DV_VANILLA, exe_final }, + {"tnt", DV_VANILLA, exe_final }, + {"plutonia", DV_VANILLA, exe_final }, + {"4", DV_VANILLA, exe_final }, + {"boom", DV_BOOM, exe_indetermined}, + {"9", DV_BOOM, exe_indetermined}, + {"mbf", DV_MBF, exe_indetermined}, + {"11", DV_MBF, exe_indetermined}, + {"mbf21", DV_MBF21, exe_indetermined}, + {"21", DV_MBF21, exe_indetermined}, }; for (int i = 0; i < arrlen(named_complevel); i++) From ad47fe9d7386d3ddd7b90ce682d1dc7022f4ba32 Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Sun, 15 Sep 2024 13:22:22 +0700 Subject: [PATCH 05/14] "Back" button/key works in help screen --- src/mn_menu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mn_menu.c b/src/mn_menu.c index 124e98bd6..d02436b32 100644 --- a/src/mn_menu.c +++ b/src/mn_menu.c @@ -2176,6 +2176,7 @@ static boolean ShortcutResponder(const event_t *ev) currentMenu = &HelpDef; // killough 10/98: new help screen + currentMenu->prevMenu = NULL; itemOn = 0; M_StartSound(sfx_swtchn); return true; From e5dc32642c52b5a4dc480cb4b01da0221f241085 Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Sun, 15 Sep 2024 14:19:55 +0700 Subject: [PATCH 06/14] implement m_json wrapper (#1904) --- src/CMakeLists.txt | 1 + src/m_json.c | 76 ++++++++++++++++++++++++++++++ src/m_json.h | 43 +++++++++++++++++ src/r_skydefs.c | 115 ++++++++++++++++++++++++--------------------- src/wi_interlvl.c | 112 +++++++++++++++++++++---------------------- src/wi_interlvl.h | 4 +- src/wi_stuff.c | 13 ++--- 7 files changed, 243 insertions(+), 121 deletions(-) create mode 100644 src/m_json.c create mode 100644 src/m_json.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d2930abee..b83e9099e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -63,6 +63,7 @@ set(WOOF_SOURCES m_fixed.h m_input.c m_input.h m_io.c m_io.h + m_json.c m_json.h mn_font.c mn_font.h mn_menu.c mn_menu.h mn_setup.c mn_internal.h diff --git a/src/m_json.c b/src/m_json.c new file mode 100644 index 000000000..e4a65e439 --- /dev/null +++ b/src/m_json.c @@ -0,0 +1,76 @@ +// +// Copyright(C) 2024 Roman Fomin +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +#include "m_json.h" + +#include "cjson/cJSON.h" + +json_t *JS_Open(const char *data) +{ + return cJSON_Parse(data); +} + +void JS_Close(json_t *json) +{ + cJSON_Delete(json); +} + +boolean JS_IsNull(json_t *json) +{ + return cJSON_IsNull(json); +} + +boolean JS_IsNumber(json_t *json) +{ + return cJSON_IsNumber(json); +} + +boolean JS_IsString(json_t *json) +{ + return cJSON_IsString(json); +} + +boolean JS_IsArray(json_t *json) +{ + return cJSON_IsArray(json); +} + +json_t *JS_GetObject(json_t *json, const char *string) +{ + return cJSON_GetObjectItemCaseSensitive(json, string); +} + +int JS_GetArraySize(json_t *json) +{ + return cJSON_GetArraySize(json); +} + +json_t *JS_GetArrayItem(json_t *json, int index) +{ + return cJSON_GetArrayItem(json, index); +} + +double JS_GetNumber(json_t *json) +{ + return json->valuedouble; +} + +int JS_GetInteger(json_t *json) +{ + return json->valueint; +} + +const char *JS_GetString(json_t *json) +{ + return json->valuestring; +} diff --git a/src/m_json.h b/src/m_json.h new file mode 100644 index 000000000..d7082bd2b --- /dev/null +++ b/src/m_json.h @@ -0,0 +1,43 @@ +// +// Copyright(C) 2024 Roman Fomin +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +#ifndef M_JSON_H +#define M_JSON_H + +#include "doomtype.h" + +typedef struct cJSON json_t; + +json_t *JS_Open(const char *data); +void JS_Close(json_t *json); + +json_t *JS_GetObject(json_t *json, const char *string); + +boolean JS_IsNull(json_t *json); +boolean JS_IsNumber(json_t *json); +boolean JS_IsString(json_t *json); +boolean JS_IsArray(json_t *json); + +double JS_GetNumber(json_t *json); +int JS_GetInteger(json_t *json); +const char *JS_GetString(json_t *json); + +int JS_GetArraySize(json_t *json); +json_t *JS_GetArrayItem(json_t *json, int index); + +#define JS_ArrayForEach(element, array) \ + for (int __index = 0, __size = JS_GetArraySize((array)); \ + __index < __size && ((element) = JS_GetArrayItem((array), __index)); \ + ++__index) + +#endif diff --git a/src/r_skydefs.c b/src/r_skydefs.c index 1c4154bc4..8efaff50b 100644 --- a/src/r_skydefs.c +++ b/src/r_skydefs.c @@ -18,73 +18,80 @@ #include "i_printf.h" #include "m_array.h" #include "m_fixed.h" +#include "m_json.h" #include "m_misc.h" #include "w_wad.h" -#include "cjson/cJSON.h" - -static boolean ParseFire(cJSON *json, fire_t *out) +static boolean ParseFire(json_t *json, fire_t *out) { - cJSON *updatetime = cJSON_GetObjectItemCaseSensitive(json, "updatetime"); - if (!cJSON_IsNumber(updatetime)) + json_t *updatetime = JS_GetObject(json, "updatetime"); + if (!JS_IsNumber(updatetime)) { return false; } - out->updatetime = updatetime->valuedouble * TICRATE; + out->updatetime = JS_GetNumber(updatetime) * TICRATE; - cJSON *palette = cJSON_GetObjectItemCaseSensitive(json, "palette"); - if (!cJSON_IsArray(palette)) + json_t *palette = JS_GetObject(json, "palette"); + if (!JS_IsArray(palette)) { return false; } - int size = cJSON_GetArraySize(palette); + int size = JS_GetArraySize(palette); for (int i = 0; i < size; ++i) { - cJSON *color = cJSON_GetArrayItem(palette, i); - array_push(out->palette, color->valueint); + json_t *color = JS_GetArrayItem(palette, i); + array_push(out->palette, JS_GetInteger(color)); } return true; } -static boolean ParseSkyTex(cJSON *json, skytex_t *out) +static boolean ParseSkyTex(json_t *json, skytex_t *out) { - cJSON *name = cJSON_GetObjectItemCaseSensitive(json, "name"); - if (!cJSON_IsString(name)) + json_t *name = JS_GetObject(json, "name"); + if (!JS_IsString(name)) { return false; } - out->name = M_StringDuplicate(name->valuestring); - - cJSON *mid = cJSON_GetObjectItemCaseSensitive(json, "mid"); - cJSON *scrollx = cJSON_GetObjectItemCaseSensitive(json, "scrollx"); - cJSON *scrolly = cJSON_GetObjectItemCaseSensitive(json, "scrolly"); - cJSON *scalex = cJSON_GetObjectItemCaseSensitive(json, "scalex"); - cJSON *scaley = cJSON_GetObjectItemCaseSensitive(json, "scaley"); - if (!cJSON_IsNumber(mid) - || !cJSON_IsNumber(scrollx) || !cJSON_IsNumber(scrolly) - || !cJSON_IsNumber(scalex) || !cJSON_IsNumber(scaley)) + out->name = M_StringDuplicate(JS_GetString(name)); + + json_t *mid = JS_GetObject(json, "mid"); + json_t *scrollx = JS_GetObject(json, "scrollx"); + json_t *scrolly = JS_GetObject(json, "scrolly"); + json_t *scalex = JS_GetObject(json, "scalex"); + json_t *scaley = JS_GetObject(json, "scaley"); + if (!JS_IsNumber(mid) + || !JS_IsNumber(scrollx) || !JS_IsNumber(scrolly) + || !JS_IsNumber(scalex) || !JS_IsNumber(scaley)) { return false; } - out->mid = mid->valuedouble; + out->mid = JS_GetNumber(mid); const double ticratescale = 1.0 / TICRATE; - out->scrollx = (scrollx->valuedouble * ticratescale) * FRACUNIT; - out->scrolly = (scrolly->valuedouble * ticratescale) * FRACUNIT; - out->scalex = scalex->valuedouble * FRACUNIT; - out->scaley = (1.0 / scaley->valuedouble) * FRACUNIT; + out->scrollx = (JS_GetNumber(scrollx) * ticratescale) * FRACUNIT; + out->scrolly = (JS_GetNumber(scrolly) * ticratescale) * FRACUNIT; + out->scalex = JS_GetNumber(scalex) * FRACUNIT; + double value = JS_GetNumber(scaley); + if (value) + { + out->scaley = (1.0 / value) * FRACUNIT; + } + else + { + out->scaley = FRACUNIT; + } return true; } -static boolean ParseSky(cJSON *json, sky_t *out) +static boolean ParseSky(json_t *json, sky_t *out) { - cJSON *type = cJSON_GetObjectItemCaseSensitive(json, "type"); - if (!cJSON_IsNumber(type)) + json_t *type = JS_GetObject(json, "type"); + if (!JS_IsNumber(type)) { return false; } - out->type = type->valueint; + out->type = JS_GetInteger(type); skytex_t background = {0}; if (!ParseSkyTex(json, &background)) @@ -93,17 +100,17 @@ static boolean ParseSky(cJSON *json, sky_t *out) } out->skytex = background; - cJSON *js_fire = cJSON_GetObjectItemCaseSensitive(json, "fire"); + json_t *js_fire = JS_GetObject(json, "fire"); fire_t fire = {0}; - if (!cJSON_IsNull(js_fire)) + if (!JS_IsNull(js_fire)) { ParseFire(js_fire, &fire); } out->fire = fire; - cJSON *js_foreground = cJSON_GetObjectItemCaseSensitive(json, "foregroundtex"); + json_t *js_foreground = JS_GetObject(json, "foregroundtex"); skytex_t foreground = {0}; - if (!cJSON_IsNull(js_foreground)) + if (!JS_IsNull(js_foreground)) { ParseSkyTex(js_foreground, &foreground); } @@ -112,12 +119,12 @@ static boolean ParseSky(cJSON *json, sky_t *out) return true; } -static boolean ParseFlatMap(cJSON *json, flatmap_t *out) +static boolean ParseFlatMap(json_t *json, flatmap_t *out) { - cJSON *flat = cJSON_GetObjectItemCaseSensitive(json, "flat"); - out->flat = M_StringDuplicate(flat->valuestring); - cJSON *sky = cJSON_GetObjectItemCaseSensitive(json, "sky"); - out->sky = M_StringDuplicate(sky->valuestring); + json_t *flat = JS_GetObject(json, "flat"); + out->flat = M_StringDuplicate(JS_GetString(flat)); + json_t *sky = JS_GetObject(json, "sky"); + out->sky = M_StringDuplicate(JS_GetString(sky)); return true; } @@ -129,26 +136,26 @@ skydefs_t *R_ParseSkyDefs(void) return NULL; } - cJSON *json = cJSON_Parse(W_CacheLumpNum(lumpnum, PU_CACHE)); + json_t *json = JS_Open(W_CacheLumpNum(lumpnum, PU_CACHE)); if (json == NULL) { I_Printf(VB_ERROR, "JSON: Error parsing SKYDEFS"); - cJSON_Delete(json); + JS_Close(json); return NULL; } - cJSON *data = cJSON_GetObjectItemCaseSensitive(json, "data"); - if (!cJSON_IsObject(data)) + json_t *data = JS_GetObject(json, "data"); + if (JS_IsNull(data)) { - cJSON_Delete(json); + JS_Close(json); return NULL; } skydefs_t *out = calloc(1, sizeof(*out)); - cJSON *js_skies = cJSON_GetObjectItemCaseSensitive(data, "skies"); - cJSON *js_sky = NULL; - cJSON_ArrayForEach(js_sky, js_skies) + json_t *js_skies = JS_GetObject(data, "skies"); + json_t *js_sky = NULL; + JS_ArrayForEach(js_sky, js_skies) { sky_t sky = {0}; if (ParseSky(js_sky, &sky)) @@ -157,9 +164,9 @@ skydefs_t *R_ParseSkyDefs(void) } } - cJSON *js_flatmapping = cJSON_GetObjectItemCaseSensitive(data, "flatmapping"); - cJSON *js_flatmap = NULL; - cJSON_ArrayForEach(js_flatmap, js_flatmapping) + json_t *js_flatmapping = JS_GetObject(data, "flatmapping"); + json_t *js_flatmap = NULL; + JS_ArrayForEach(js_flatmap, js_flatmapping) { flatmap_t flatmap = {0}; if (ParseFlatMap(js_flatmap, &flatmap)) @@ -168,6 +175,6 @@ skydefs_t *R_ParseSkyDefs(void) } } - cJSON_Delete(json); + JS_Close(json); return out; } diff --git a/src/wi_interlvl.c b/src/wi_interlvl.c index f17b4dc6e..bff85690a 100644 --- a/src/wi_interlvl.c +++ b/src/wi_interlvl.c @@ -13,6 +13,7 @@ #include "wi_interlvl.h" +#include "doomdef.h" #include "doomtype.h" #include "i_printf.h" #include "w_wad.h" @@ -23,67 +24,67 @@ #define M_ARRAY_FREE(ptr) Z_Free((ptr)) #include "m_array.h" -#include "cjson/cJSON.h" +#include "m_json.h" -static boolean ParseCondition(cJSON *json, interlevelcond_t *out) +static boolean ParseCondition(json_t *json, interlevelcond_t *out) { - cJSON *condition = cJSON_GetObjectItemCaseSensitive(json, "condition"); - if (!cJSON_IsNumber(condition)) + json_t *condition = JS_GetObject(json, "condition"); + if (!JS_IsNumber(condition)) { return false; } - out->condition = condition->valueint; + out->condition = JS_GetInteger(condition); - cJSON *param = cJSON_GetObjectItemCaseSensitive(json, "param"); - if (!cJSON_IsNumber(param)) + json_t *param = JS_GetObject(json, "param"); + if (!JS_IsNumber(param)) { return false; } - out->param = param->valueint; + out->param = JS_GetInteger(param); return true; } -static boolean ParseFrame(cJSON *json, interlevelframe_t *out) +static boolean ParseFrame(json_t *json, interlevelframe_t *out) { - cJSON *image_lump = cJSON_GetObjectItemCaseSensitive(json, "image"); - if (!cJSON_IsString(image_lump)) + json_t *image_lump = JS_GetObject(json, "image"); + if (!JS_IsString(image_lump)) { return false; } - out->image_lump = Z_StrDup(image_lump->valuestring, PU_LEVEL); + out->image_lump = Z_StrDup(JS_GetString(image_lump), PU_LEVEL); - cJSON *type = cJSON_GetObjectItemCaseSensitive(json, "type"); - if (!cJSON_IsNumber(type)) + json_t *type = JS_GetObject(json, "type"); + if (!JS_IsNumber(type)) { return false; } - out->type = type->valueint; + out->type = JS_GetInteger(type); - cJSON *duration = cJSON_GetObjectItemCaseSensitive(json, "duration"); - if (!cJSON_IsNumber(duration)) + json_t *duration = JS_GetObject(json, "duration"); + if (!JS_IsNumber(duration)) { return false; } - out->duration = duration->valuedouble; + out->duration = JS_GetNumber(duration) * TICRATE; - cJSON *maxduration = cJSON_GetObjectItemCaseSensitive(json, "maxduration"); - if (!cJSON_IsNumber(maxduration)) + json_t *maxduration = JS_GetObject(json, "maxduration"); + if (!JS_IsNumber(maxduration)) { return false; } - out->maxduration = maxduration->valuedouble; + out->maxduration = JS_GetNumber(maxduration) * TICRATE; return true; } -static boolean ParseAnim(cJSON *json, interlevelanim_t *out) +static boolean ParseAnim(json_t *json, interlevelanim_t *out) { - cJSON *js_frames = cJSON_GetObjectItemCaseSensitive(json, "frames"); - cJSON *js_frame = NULL; + json_t *js_frames = JS_GetObject(json, "frames"); + json_t *js_frame = NULL; interlevelframe_t *frames = NULL; - cJSON_ArrayForEach(js_frame, js_frames) + JS_ArrayForEach(js_frame, js_frames) { interlevelframe_t frame = {0}; if (ParseFrame(js_frame, &frame)) @@ -93,20 +94,20 @@ static boolean ParseAnim(cJSON *json, interlevelanim_t *out) } out->frames = frames; - cJSON *x_pos = cJSON_GetObjectItemCaseSensitive(json, "x"); - cJSON *y_pos = cJSON_GetObjectItemCaseSensitive(json, "y"); - if (!cJSON_IsNumber(x_pos) || !cJSON_IsNumber(y_pos)) + json_t *x_pos = JS_GetObject(json, "x"); + json_t *y_pos = JS_GetObject(json, "y"); + if (!JS_IsNumber(x_pos) || !JS_IsNumber(y_pos)) { return false; } - out->x_pos = x_pos->valueint; - out->y_pos = y_pos->valueint; + out->x_pos = JS_GetInteger(x_pos); + out->y_pos = JS_GetInteger(y_pos); - cJSON *js_conditions = cJSON_GetObjectItemCaseSensitive(json, "conditions"); - cJSON *js_condition = NULL; + json_t *js_conditions = JS_GetObject(json, "conditions"); + json_t *js_condition = NULL; interlevelcond_t *conditions = NULL; - cJSON_ArrayForEach(js_condition, js_conditions) + JS_ArrayForEach(js_condition, js_conditions) { interlevelcond_t condition = {0}; if (ParseCondition(js_condition, &condition)) @@ -119,13 +120,13 @@ static boolean ParseAnim(cJSON *json, interlevelanim_t *out) return true; } -static void ParseLevelLayer(cJSON *json, interlevellayer_t *out) +static void ParseLevelLayer(json_t *json, interlevellayer_t *out) { - cJSON *js_anims = cJSON_GetObjectItemCaseSensitive(json, "anims"); - cJSON *js_anim = NULL; + json_t *js_anims = JS_GetObject(json, "anims"); + json_t *js_anim = NULL; interlevelanim_t *anims = NULL; - cJSON_ArrayForEach(js_anim, js_anims) + JS_ArrayForEach(js_anim, js_anims) { interlevelanim_t anim = {0}; if (ParseAnim(js_anim, &anim)) @@ -135,11 +136,11 @@ static void ParseLevelLayer(cJSON *json, interlevellayer_t *out) } out->anims = anims; - cJSON *js_conditions = cJSON_GetObjectItemCaseSensitive(json, "conditions"); - cJSON *js_condition = NULL; + json_t *js_conditions = JS_GetObject(json, "conditions"); + json_t *js_condition = NULL; interlevelcond_t *conditions = NULL; - cJSON_ArrayForEach(js_condition, js_conditions) + JS_ArrayForEach(js_condition, js_conditions) { interlevelcond_t condition = {0}; if (ParseCondition(js_condition, &condition)) @@ -152,41 +153,40 @@ static void ParseLevelLayer(cJSON *json, interlevellayer_t *out) interlevel_t *WI_ParseInterlevel(const char *lumpname) { - cJSON *json = cJSON_Parse(W_CacheLumpName(lumpname, PU_CACHE)); + json_t *json = JS_Open(W_CacheLumpName(lumpname, PU_CACHE)); if (json == NULL) { I_Printf(VB_ERROR, "Error parsing %s", lumpname); - cJSON_Delete(json); + JS_Close(json); return NULL; } - cJSON *data = cJSON_GetObjectItemCaseSensitive(json, "data"); - if (!cJSON_IsObject(data)) + json_t *data = JS_GetObject(json, "data"); + if (JS_IsNull(data)) { - cJSON_Delete(json); + JS_Close(json); return NULL; } - cJSON *music = cJSON_GetObjectItemCaseSensitive(data, "music"); - cJSON *backgroundimage = - cJSON_GetObjectItemCaseSensitive(data, "backgroundimage"); + json_t *music = JS_GetObject(data, "music"); + json_t *backgroundimage = JS_GetObject(data, "backgroundimage"); - if (!cJSON_IsString(music) || !cJSON_IsString(backgroundimage)) + if (!JS_IsString(music) || !JS_IsString(backgroundimage)) { - cJSON_Delete(json); + JS_Close(json); return NULL; } interlevel_t *out = Z_Calloc(1, sizeof(*out), PU_LEVEL, NULL); - out->music_lump = Z_StrDup(music->valuestring, PU_LEVEL); - out->background_lump = Z_StrDup(backgroundimage->valuestring, PU_LEVEL); + out->music_lump = Z_StrDup(JS_GetString(music), PU_LEVEL); + out->background_lump = Z_StrDup(JS_GetString(backgroundimage), PU_LEVEL); - cJSON *js_layers = cJSON_GetObjectItemCaseSensitive(data, "layers"); - cJSON *js_layer = NULL; + json_t *js_layers = JS_GetObject(data, "layers"); + json_t *js_layer = NULL; interlevellayer_t *layers = NULL; - cJSON_ArrayForEach(js_layer, js_layers) + JS_ArrayForEach(js_layer, js_layers) { interlevellayer_t layer = {0}; ParseLevelLayer(js_layer, &layer); @@ -194,6 +194,6 @@ interlevel_t *WI_ParseInterlevel(const char *lumpname) } out->layers = layers; - cJSON_Delete(json); + JS_Close(json); return out; } diff --git a/src/wi_interlvl.h b/src/wi_interlvl.h index f98da48b0..5db1d9f80 100644 --- a/src/wi_interlvl.h +++ b/src/wi_interlvl.h @@ -59,8 +59,8 @@ typedef struct interlevelframe_s { char *image_lump; frametype_t type; - double duration; - double maxduration; + int duration; + int maxduration; } interlevelframe_t; typedef struct interlevelanim_s diff --git a/src/wi_stuff.c b/src/wi_stuff.c index 2dd5d4b02..165328e9b 100644 --- a/src/wi_stuff.c +++ b/src/wi_stuff.c @@ -517,22 +517,17 @@ static void UpdateAnimationStates(wi_animationstate_t *states) case Frame_RandomStart: if (state->frame_start) { - int maxtics = frame->duration * TICRATE; - tics = M_Random() % maxtics; + tics = M_Random() % frame->duration; break; } // fall through case Frame_FixedDuration: - tics = frame->duration * TICRATE; + tics = frame->duration; break; case Frame_RandomDuration: - { - int maxtics = frame->maxduration * TICRATE; - int mintics = frame->duration * TICRATE; - tics = M_Random() % maxtics; - tics = BETWEEN(mintics, maxtics, tics); - } + tics = M_Random() % frame->maxduration; + tics = BETWEEN(frame->duration, frame->maxduration, tics); break; default: From 036af1d0c865b8fd4ad8e670541c3fdf881da02f Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Sun, 15 Sep 2024 15:44:31 +0700 Subject: [PATCH 07/14] json lump: add type and version checking --- src/m_json.c | 48 +++++++++++++++++++++++++++++++++++++++++++++-- src/m_json.h | 9 ++++++++- src/r_skydefs.c | 4 ++-- src/wi_interlvl.c | 4 ++-- 4 files changed, 58 insertions(+), 7 deletions(-) diff --git a/src/m_json.c b/src/m_json.c index e4a65e439..833c79564 100644 --- a/src/m_json.c +++ b/src/m_json.c @@ -13,11 +13,55 @@ #include "m_json.h" +#include +#include "i_printf.h" + #include "cjson/cJSON.h" -json_t *JS_Open(const char *data) +json_t *JS_Open(const char *type, version_t version, const char *data) { - return cJSON_Parse(data); + const char *s; + + json_t *json = cJSON_Parse(data); + if (json == NULL) + { + I_Printf(VB_ERROR, "%s: error before %s", type, cJSON_GetErrorPtr()); + return NULL; + } + + json_t *js_type = JS_GetObject(json, "type"); + if (!JS_IsString(js_type)) + { + I_Printf(VB_ERROR, "%s: no type string", type); + return NULL; + } + + s = JS_GetString(js_type); + if (strcmp(s, type)) + { + I_Printf(VB_ERROR, "%s: wrong type %s", type, s); + return NULL; + } + + json_t *js_version = JS_GetObject(json, "version"); + if (!JS_IsString(js_version)) + { + I_Printf(VB_ERROR, "%s: no version string", type); + return NULL; + } + + s = JS_GetString(js_version); + version_t v = {0}; + sscanf(s, "%d.%d.%d", &v.major, &v.minor, &v.revision); + if (v.major != version.major || v.minor != version.minor + || v.revision != version.revision) + { + I_Printf(VB_ERROR, "%s: wrong version %d.%d.%d", type, v.major, v.minor, + v.revision); + return NULL; + } + + return json; } void JS_Close(json_t *json) diff --git a/src/m_json.h b/src/m_json.h index d7082bd2b..e595f0c73 100644 --- a/src/m_json.h +++ b/src/m_json.h @@ -18,7 +18,14 @@ typedef struct cJSON json_t; -json_t *JS_Open(const char *data); +typedef struct +{ + int major; + int minor; + int revision; +} version_t; + +json_t *JS_Open(const char *type, version_t version, const char *data); void JS_Close(json_t *json); json_t *JS_GetObject(json_t *json, const char *string); diff --git a/src/r_skydefs.c b/src/r_skydefs.c index 8efaff50b..f7e87c131 100644 --- a/src/r_skydefs.c +++ b/src/r_skydefs.c @@ -136,10 +136,10 @@ skydefs_t *R_ParseSkyDefs(void) return NULL; } - json_t *json = JS_Open(W_CacheLumpNum(lumpnum, PU_CACHE)); + json_t *json = JS_Open("skydefs", (version_t){1, 0, 0}, + W_CacheLumpNum(lumpnum, PU_CACHE)); if (json == NULL) { - I_Printf(VB_ERROR, "JSON: Error parsing SKYDEFS"); JS_Close(json); return NULL; } diff --git a/src/wi_interlvl.c b/src/wi_interlvl.c index bff85690a..59ad0a024 100644 --- a/src/wi_interlvl.c +++ b/src/wi_interlvl.c @@ -153,10 +153,10 @@ static void ParseLevelLayer(json_t *json, interlevellayer_t *out) interlevel_t *WI_ParseInterlevel(const char *lumpname) { - json_t *json = JS_Open(W_CacheLumpName(lumpname, PU_CACHE)); + json_t *json = JS_Open("interlevel", (version_t){1, 0, 0}, + W_CacheLumpName(lumpname, PU_CACHE)); if (json == NULL) { - I_Printf(VB_ERROR, "Error parsing %s", lumpname); JS_Close(json); return NULL; } From 6143dd500cf3c82a52a14f877676f149307a86af Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Sun, 15 Sep 2024 15:49:10 +0700 Subject: [PATCH 08/14] fix version check --- src/m_json.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/m_json.c b/src/m_json.c index 833c79564..84bd78e77 100644 --- a/src/m_json.c +++ b/src/m_json.c @@ -53,11 +53,11 @@ json_t *JS_Open(const char *type, version_t version, const char *data) s = JS_GetString(js_version); version_t v = {0}; sscanf(s, "%d.%d.%d", &v.major, &v.minor, &v.revision); - if (v.major != version.major || v.minor != version.minor - || v.revision != version.revision) + if (!(v.major == version.major && v.minor == version.minor + && v.revision == version.revision)) { - I_Printf(VB_ERROR, "%s: wrong version %d.%d.%d", type, v.major, v.minor, - v.revision); + I_Printf(VB_ERROR, "%s: unsupported version %d.%d.%d", type, v.major, + v.minor, v.revision); return NULL; } From 4e6ed4a1a076d1f09777b1dc248738cb82785b10 Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Sun, 15 Sep 2024 15:51:36 +0700 Subject: [PATCH 09/14] fix build --- src/m_json.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/m_json.c b/src/m_json.c index 84bd78e77..893765af2 100644 --- a/src/m_json.c +++ b/src/m_json.c @@ -14,6 +14,7 @@ #include "m_json.h" #include +#include #include "i_printf.h" #include "cjson/cJSON.h" From e87ac53bb04df47a26f0b87fb67886886ab44260 Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Mon, 16 Sep 2024 07:45:06 +0700 Subject: [PATCH 10/14] interpolation of wipe melt animation, adapted from R&R (#1903) * no need to skip DRS --- src/d_main.c | 23 ++--- src/f_wipe.c | 237 ++++++++++++++++++++++++++++++++------------------ src/m_fixed.h | 4 + 3 files changed, 164 insertions(+), 100 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index bff549081..3c7f0666a 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -292,7 +292,7 @@ void D_Display (void) if (gamestate != wipegamestate && (strictmode || screen_melt)) { wipe = true; - wipe_StartScreen(0, 0, video.unscaledw, SCREENHEIGHT); + wipe_StartScreen(0, 0, video.width, video.height); } if (!wipe) @@ -432,29 +432,24 @@ void D_Display (void) } // wipe update - wipe_EndScreen(0, 0, video.unscaledw, SCREENHEIGHT); + wipe_EndScreen(0, 0, video.width, video.height); wipestart = I_GetTime () - 1; do { - int nowtime, tics; - do - { - I_Sleep(1); - nowtime = I_GetTime(); - tics = nowtime - wipestart; - } - while (!tics); - wipestart = nowtime; + int nowtime = I_GetTime(); + int tics = nowtime - wipestart; + + fractionaltic = I_GetFracTime(); + done = wipe_ScreenWipe(strictmode ? wipe_Melt : screen_melt, - 0, 0, video.unscaledw, SCREENHEIGHT, tics); + 0, 0, video.width, video.height, tics); + wipestart = nowtime; M_Drawer(); // menu is drawn even on top of wipes I_FinishUpdate(); // page flip or blit buffer } while (!done); - - drs_skip_frame = true; // skip DRS after wipe } // diff --git a/src/f_wipe.c b/src/f_wipe.c index 0f4d21155..73ac84170 100644 --- a/src/f_wipe.c +++ b/src/f_wipe.c @@ -23,6 +23,7 @@ #include "f_wipe.h" #include "i_video.h" #include "m_random.h" +#include "r_main.h" #include "v_flextran.h" #include "v_video.h" #include "z_zone.h" @@ -33,10 +34,10 @@ // when they try to scale up the original logic to higher resolutions. // Number of "falling columns" on the screen. -static int num_columns; +static int wipe_columns; // Distance a column falls before it reaches the bottom of the screen. -#define COLUMN_MAX_Y 200 +#define WIPE_ROWS 200 // // SCREEN WIPE PACKAGE @@ -46,18 +47,6 @@ static byte *wipe_scr_start; static byte *wipe_scr_end; static byte *wipe_scr; -static void wipe_shittyColMajorXform(byte *array, int width, int height) -{ - byte *dest = Z_Malloc(width*height, PU_STATIC, 0); - int x, y; - - for(y=0;y not ready to scroll yet) - col_y[0] = -(M_Random()%16); - for (i=1;i not ready to scroll yet) + curry[0] = -(M_Random() % 16); + for (int i = 1; i < wipe_columns; i++) { - int r = (M_Random()%3) - 1; - col_y[i] = col_y[i-1] + r; - if (col_y[i] > 0) - col_y[i] = 0; - else - if (col_y[i] == -16) - col_y[i] = -15; + int r = (M_Random() % 3) - 1; + curry[i] = curry[i - 1] + r; + if (curry[i] > 0) + { + curry[i] = 0; + } + else if (curry[i] == -16) + { + curry[i] = -15; + } } - return 0; + + memcpy(prevy, curry, sizeof(int) * wipe_columns); + + return 0; } static int wipe_doMelt(int width, int height, int ticks) { - boolean done = true; - int x, y, xfactor, yfactor; + boolean done = true; - while (ticks--) - for (x=0;x 0) + { + while (ticks--) { - col_y[x]++; - done = false; + int *temp = prevy; + prevy = curry; + curry = temp; + + for (int col = 0; col < wipe_columns; ++col) + { + if (prevy[col] < 0) + { + curry[col] = prevy[col] + 1; + done = false; + } + else if (prevy[col] < WIPE_ROWS) + { + int dy = (prevy[col] < 16) ? prevy[col] + 1 : 8; + curry[col] = MIN(prevy[col] + dy, WIPE_ROWS); + + done = false; + } + else + { + curry[col] = WIPE_ROWS; + } + } } - else if (col_y[x] < COLUMN_MAX_Y) + } + else + { + for (int col = 0; col < wipe_columns; ++col) { - int dy = (col_y[x] < 16) ? col_y[x]+1 : 8; - if (col_y[x]+dy >= COLUMN_MAX_Y) - dy = COLUMN_MAX_Y - col_y[x]; - col_y[x] += dy; - done = false; + done &= curry[col] >= WIPE_ROWS; } + } - xfactor = (width + num_columns - 1) / num_columns; - yfactor = (height + COLUMN_MAX_Y - 1) / COLUMN_MAX_Y; + return done; +} - for (x = 0; x < width; x++) - { - byte *s, *d; - int scroff = col_y[x / xfactor] * yfactor; - if (scroff > height) - scroff = height; - - d = wipe_scr + x; - s = wipe_scr_end + x * height; - for (y = 0; y < scroff; ++y) - { - *d = *s; - d += video.pitch; - s++; - } - s = wipe_scr_start + x * height; - for (; y < height; ++y) +int wipe_renderMelt(int width, int height, int ticks) +{ + boolean done = true; + + // Scale up and then down to handle arbitrary dimensions with integer math + int vertblocksize = height * 100 / WIPE_ROWS; + int horizblocksize = width * 100 / wipe_columns; + int currcol; + int currcolend; + int currrow; + + V_UseBuffer(wipe_scr); + V_PutBlock(0, 0, width, height, wipe_scr_end); + V_RestoreBuffer(); + + for (int col = 0; col < wipe_columns; ++col) { - *d = *s; - d += video.pitch; - s++; + int current; + if (uncapped) + { + current = FixedToInt( + LerpFixed(IntToFixed(prevy[col]), IntToFixed(curry[col]))); + } + else + { + current = curry[col]; + } + + if (current < 0) + { + currcol = col * horizblocksize / 100; + currcolend = (col + 1) * horizblocksize / 100; + for (; currcol < currcolend; ++currcol) + { + pixel_t *source = wipe_scr_start + currcol; + pixel_t *dest = wipe_scr + currcol; + + for (int i = 0; i < height; ++i) + { + *dest = *source; + dest += video.pitch; + source += width; + } + } + } + else if (current < WIPE_ROWS) + { + currcol = col * horizblocksize / 100; + currcolend = (col + 1) * horizblocksize / 100; + + currrow = current * vertblocksize / 100; + + for (; currcol < currcolend; ++currcol) + { + pixel_t *source = wipe_scr_start + currcol; + pixel_t *dest = wipe_scr + currcol + (currrow * video.pitch); + + for (int i = 0; i < height - currrow; ++i) + { + *dest = *source; + dest += video.pitch; + source += width; + } + } + + done = false; + } } - } - return done; + + return done; } static int wipe_exitMelt(int width, int height, int ticks) { - Z_Free(col_y); + Z_Free(ybuff1); + Z_Free(ybuff2); wipe_exit(width, height, ticks); return 0; } int wipe_StartScreen(int x, int y, int width, int height) { - int size = video.width * video.height; + int size = width * height; wipe_scr_start = Z_Malloc(size * sizeof(*wipe_scr_start), PU_STATIC, NULL); I_ReadScreen(wipe_scr_start); return 0; @@ -202,14 +262,14 @@ int wipe_StartScreen(int x, int y, int width, int height) int wipe_EndScreen(int x, int y, int width, int height) { - int size = video.width * video.height; + int size = width * height; wipe_scr_end = Z_Malloc(size * sizeof(*wipe_scr_end), PU_STATIC, NULL); I_ReadScreen(wipe_scr_end); V_DrawBlock(x, y, width, height, wipe_scr_start); // restore start scr. return 0; } -static int wipe_NOP(int x, int y, int t) +static int wipe_NOP(int width, int height, int tics) { return 0; } @@ -266,7 +326,7 @@ static unsigned int lastrndval; static int wipe_initFizzle(int width, int height, int ticks) { int rndbits_x = log2_ceil(video.unscaledw); - rndbits_y = log2_ceil(COLUMN_MAX_Y); + rndbits_y = log2_ceil(WIPE_ROWS); int rndbits = rndbits_x + rndbits_y; if (rndbits < 17) @@ -285,7 +345,7 @@ static int wipe_initFizzle(int width, int height, int ticks) static int wipe_doFizzle(int width, int height, int ticks) { - const int pixperframe = (video.unscaledw * COLUMN_MAX_Y) >> 5; + const int pixperframe = (video.unscaledw * WIPE_ROWS) >> 5; unsigned int rndval = lastrndval; for (unsigned p = 0; p < pixperframe; p++) @@ -299,7 +359,7 @@ static int wipe_doFizzle(int width, int height, int ticks) rndval = (rndval >> 1) ^ (rndval & 1 ? 0 : rndmask); - if (x >= video.unscaledw || y >= COLUMN_MAX_Y) + if (x >= video.unscaledw || y >= WIPE_ROWS) { if (rndval == 0) // entire sequence has been completed { @@ -336,17 +396,21 @@ static int wipe_doFizzle(int width, int height, int ticks) } static int (*const wipes[])(int, int, int) = { + wipe_NOP, wipe_NOP, wipe_NOP, wipe_exit, wipe_initMelt, wipe_doMelt, + wipe_renderMelt, wipe_exitMelt, wipe_initColorXForm, wipe_doColorXForm, + wipe_NOP, wipe_exit, wipe_initFizzle, wipe_doFizzle, + wipe_NOP, wipe_exit, }; @@ -355,18 +419,19 @@ int wipe_ScreenWipe(int wipeno, int x, int y, int width, int height, int ticks) { static boolean go; // when zero, stop the wipe - width = video.width; - height = video.height; - if (!go) // initial stuff { go = 1; wipe_scr = I_VideoBuffer; - wipes[wipeno*3](width, height, ticks); + wipes[wipeno*4](width, height, ticks); } - if (wipes[wipeno*3+1](width, height, ticks)) // final stuff + + int rc = wipes[wipeno*4+1](width, height, ticks); + wipes[wipeno*4+2](width, height, ticks); + + if (rc) // final stuff { - wipes[wipeno*3+2](width, height, ticks); + wipes[wipeno*4+3](width, height, ticks); go = 0; } return !go; diff --git a/src/m_fixed.h b/src/m_fixed.h index e8cfa0f3f..9e740a623 100644 --- a/src/m_fixed.h +++ b/src/m_fixed.h @@ -44,6 +44,10 @@ #define FRACUNIT (1<> FRACBITS, (x)) typedef int fixed_t; From 58b7bdd0e955284f6ec8130feb8d494facf9cbd5 Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Mon, 16 Sep 2024 17:59:39 +0700 Subject: [PATCH 11/14] make death use action demo compatible (#1910) --- src/g_game.c | 28 ++++++++++++++++++++++++++-- src/p_user.c | 37 ++++++++++++++----------------------- src/p_user.h | 1 + 3 files changed, 41 insertions(+), 25 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 19fe81763..d8b395d29 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -546,6 +546,28 @@ void G_PrepGyroTiccmd(void) } } +static boolean FilterDeathUseAction(void) +{ + if (players[consoleplayer].playerstate & PST_DEAD) + { + switch (death_use_action) + { + case death_use_nothing: + return true; + case death_use_reload: + if (!demoplayback && !demorecording && !netgame) + { + activate_death_use_reload = true; + } + return true; + default: + break; + } + } + + return false; +} + // // G_BuildTiccmd // Builds a ticcmd from all of the available inputs @@ -703,7 +725,8 @@ void G_BuildTiccmd(ticcmd_t* cmd) if (M_InputGameActive(input_use)) // [FG] mouse button for "use" { - cmd->buttons |= BT_USE; + if (!FilterDeathUseAction()) + cmd->buttons |= BT_USE; // clear double clicks if hit use button dclick = false; } @@ -811,7 +834,8 @@ void G_BuildTiccmd(ticcmd_t* cmd) if (dclick) { dclick = false; - cmd->buttons |= BT_USE; + if (!FilterDeathUseAction()) + cmd->buttons |= BT_USE; } // special buttons diff --git a/src/p_user.c b/src/p_user.c index 295ef53c3..98f228013 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -31,7 +31,6 @@ #include "hu_stuff.h" #include "info.h" #include "m_cheat.h" -#include "m_input.h" #include "p_map.h" #include "p_mobj.h" #include "p_pspr.h" @@ -39,7 +38,6 @@ #include "p_user.h" #include "r_defs.h" #include "r_main.h" -#include "r_state.h" #include "st_stuff.h" static fixed_t PlayerSlope(player_t *player) @@ -259,6 +257,7 @@ void P_MovePlayer (player_t* player) #define ANG5 (ANG90/18) death_use_action_t death_use_action; +boolean activate_death_use_reload; // // P_DeathThink @@ -316,29 +315,21 @@ void P_DeathThink (player_t* player) if (player->cmd.buttons & BT_USE) { - if (demorecording || demoplayback || netgame) - player->playerstate = PST_REBORN; - else switch(death_use_action) + player->playerstate = PST_REBORN; + } + + if (activate_death_use_reload) + { + activate_death_use_reload = false; + + if (savegameslot >= 0) { - case death_use_default: - player->playerstate = PST_REBORN; - break; - case death_use_reload: - if (savegameslot >= 0) - { - char *file = G_SaveGameName(savegameslot); - G_LoadGame(file, savegameslot, false); - free(file); - // [Woof!] prevent on-death-action reloads from activating specials - M_InputGameDeactivate(input_use); - } - else - player->playerstate = PST_REBORN; - break; - case death_use_nothing: - default: - break; + char *file = G_SaveGameName(savegameslot); + G_LoadGame(file, savegameslot, false); + free(file); } + else + player->playerstate = PST_REBORN; } } diff --git a/src/p_user.h b/src/p_user.h index 2fafed16f..ade8cdc27 100644 --- a/src/p_user.h +++ b/src/p_user.h @@ -41,6 +41,7 @@ typedef enum } death_use_action_t; extern death_use_action_t death_use_action; +extern boolean activate_death_use_reload; extern boolean onground; // whether player is on ground or in air From e3ceb7f727bdec3ec7c9632bedf9803a7860f60d Mon Sep 17 00:00:00 2001 From: Fabian Greffrath Date: Mon, 16 Sep 2024 15:20:42 +0200 Subject: [PATCH 12/14] fix broken/inconsistent weapon switching (#1902) * fix broken/inconsistent weapon switching Fixes #1901 * get rid of doom_weapon_toggles * switch weapons like PrBoom+ * Revert "get rid of doom_weapon_toggles" This reverts commit 36f6408bc46894decd6e3f4410658cc5eef50652. * fix stuck behavior * select SSG with the "9" key again * doom_weapon_toggles does not affect prev/next weapon again * Revert "select SSG with the "9" key again" This reverts commit e708a6fac568548ef774bc12ed912e43474916c3. * Revert "doom_weapon_toggles does not affect prev/next weapon again" This reverts commit 0ab51f59b6364f3f2914602a342bdfa30a5290a2. * cosmetics --- src/g_game.c | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index d8b395d29..46240db7d 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -221,16 +221,20 @@ int bodyqueslot, bodyquesize, default_bodyquesize; // killough 2/8/98, 10/98 static int next_weapon = 0; -static const weapontype_t weapon_order_table[] = { - wp_fist, - wp_chainsaw, - wp_pistol, - wp_shotgun, - wp_supershotgun, - wp_chaingun, - wp_missile, - wp_plasma, - wp_bfg +static const struct +{ + weapontype_t weapon; + weapontype_t weapon_num; +} weapon_order_table[] = { + { wp_fist, wp_fist }, + { wp_chainsaw, wp_fist }, + { wp_pistol, wp_pistol }, + { wp_shotgun, wp_shotgun }, + { wp_supershotgun, wp_shotgun }, + { wp_chaingun, wp_chaingun }, + { wp_missile, wp_missile }, + { wp_plasma, wp_plasma }, + { wp_bfg, wp_bfg } }; static boolean WeaponSelectable(weapontype_t weapon) @@ -261,6 +265,7 @@ static boolean WeaponSelectable(weapontype_t weapon) // we also have the berserk pack. if (weapon == wp_fist + && demo_compatibility && players[consoleplayer].weaponowned[wp_chainsaw] && !players[consoleplayer].powers[pw_strength]) { @@ -288,7 +293,7 @@ static int G_NextWeapon(int direction) for (i=0; i Date: Mon, 16 Sep 2024 22:47:29 -0700 Subject: [PATCH 13/14] Activate OPL3 chips on-demand based on voice pressure (#1911) * Remove opl_sample_rate variable since it has no practical effect * Deactivate idle OPL chips Allocate free OPL voices from active chips before inactive ones * Add comments for new constants * Ensure OPL chip LFOs are kept in sync after dormancy * Check for chip activations before sample generation to preserve sync --- opl/opl.c | 7 ---- opl/opl.h | 4 -- opl/opl_internal.h | 2 - opl/opl_sdl.c | 99 +++++++++++++++++++++++++++++++++++++++++----- src/i_oplmusic.c | 37 +++++++++++------ 5 files changed, 115 insertions(+), 34 deletions(-) diff --git a/opl/opl.c b/opl/opl.c index 17cff8154..dfd461b78 100644 --- a/opl/opl.c +++ b/opl/opl.c @@ -32,8 +32,6 @@ static opl_driver_t *drivers[] = static opl_driver_t *driver = NULL; -unsigned int opl_sample_rate = OPL_SAMPLE_RATE; - // // Init/shutdown code. // @@ -140,11 +138,6 @@ void OPL_Shutdown(void) // Set the sample rate used for software OPL emulation. -void OPL_SetSampleRate(unsigned int rate) -{ - opl_sample_rate = rate; -} - void OPL_WritePort(int chip, opl_port_t port, unsigned int value) { if (driver != NULL) diff --git a/opl/opl.h b/opl/opl.h index 7dce72083..94a122d6b 100644 --- a/opl/opl.h +++ b/opl/opl.h @@ -85,10 +85,6 @@ opl_init_result_t OPL_Init(unsigned int port_base, int num_chips); void OPL_Shutdown(void); -// Set the sample rate used for software emulation. - -void OPL_SetSampleRate(unsigned int rate); - // Write to one of the OPL I/O ports: void OPL_WritePort(int chip, opl_port_t port, unsigned int value); diff --git a/opl/opl_internal.h b/opl/opl_internal.h index e32f7010b..27892ec4d 100644 --- a/opl/opl_internal.h +++ b/opl/opl_internal.h @@ -48,7 +48,5 @@ extern opl_driver_t opl_sdl_driver; // Sample rate to use when doing software emulation. -extern unsigned int opl_sample_rate; - #endif /* #ifndef OPL_INTERNAL_H */ diff --git a/opl/opl_sdl.c b/opl/opl_sdl.c index 83c75ccb1..458c3aae9 100644 --- a/opl/opl_sdl.c +++ b/opl/opl_sdl.c @@ -15,6 +15,8 @@ // OPL SDL interface. // +#include + #include "doomtype.h" #include "opl.h" #include "opl3.h" @@ -23,6 +25,10 @@ #define MAX_SOUND_SLICE_TIME 100 /* ms */ +#define OPL_SILENCE_THRESHOLD 36 // Allow for a worst case of +/-1 ripple in all 36 operators. +#define OPL_CHIP_TIMEOUT (OPL_SAMPLE_RATE / 2) // 0.5 seconds of silence with no key-on + // should be enough to ensure chip is "off" + typedef struct { unsigned int rate; // Number of times the timer is advanced per sec. @@ -51,6 +57,8 @@ static uint64_t pause_offset; // OPL software emulator structure. static opl3_chip opl_chips[OPL_MAX_CHIPS]; +static uint32_t opl_chip_keys[OPL_MAX_CHIPS]; +static uint32_t opl_chip_timeouts[OPL_MAX_CHIPS]; static int opl_opl3mode; // Register number that was written. @@ -62,7 +70,7 @@ static int register_num = 0; static opl_timer_t timer1 = { 12500, 0, 0, 0 }; static opl_timer_t timer2 = { 3125, 0, 0, 0 }; -static int mixing_freq, mixing_channels; +static int mixing_channels; // Advance time by the specified number of samples, invoking any // callback functions as appropriate. @@ -75,7 +83,7 @@ static void AdvanceTime(unsigned int nsamples) // Advance time. - us = ((uint64_t) nsamples * OPL_SECOND) / mixing_freq; + us = ((uint64_t) nsamples * OPL_SECOND) / OPL_SAMPLE_RATE; current_time += us; if (opl_sdl_paused) @@ -100,6 +108,39 @@ static void AdvanceTime(unsigned int nsamples) } } + +// When a chip is re-activated after being idle, we need to bring its +// internal global timers back into synch with the main chip to avoid +// possible beating artifacts + +static void ResyncChip (int chip) +{ + // Find an active chip to synchronize with; will usually be chip 0 + int sync = -1; + for (int c = 0; c < OPL_MAX_CHIPS; ++c) + { + if (opl_chip_timeouts[c] < OPL_CHIP_TIMEOUT) + { + sync = c; + break; + } + } + + if (sync >= 0) + { + // Synchronize the LFOs + opl_chips[chip].vibpos = opl_chips[sync].vibpos; + opl_chips[chip].tremolopos = opl_chips[sync].tremolopos; + opl_chips[chip].timer = opl_chips[sync].timer; + + // Synchronize the envelope clock + opl_chips[chip].eg_state = opl_chips[sync].eg_state; + opl_chips[chip].eg_timer = opl_chips[sync].eg_timer; + opl_chips[chip].eg_timerrem = opl_chips[sync].eg_timerrem; + } +} + + // Callback function to fill a new sound buffer: int OPL_FillBuffer(byte *buffer, int buffer_samples) @@ -127,7 +168,7 @@ int OPL_FillBuffer(byte *buffer, int buffer_samples) { next_callback_time = OPL_Queue_Peek(callback_queue) + pause_offset; - nsamples = (next_callback_time - current_time) * mixing_freq; + nsamples = (next_callback_time - current_time) * OPL_SAMPLE_RATE; nsamples = (nsamples + OPL_SECOND - 1) / OPL_SECOND; if (nsamples > buffer_samples - filled) @@ -140,19 +181,44 @@ int OPL_FillBuffer(byte *buffer, int buffer_samples) Bit16s *cursor = (Bit16s *)(buffer + filled * 4); for (int s = 0; s < nsamples; ++s) { + // Check for chip activations before we generate the sample + for (int c = 0; c < num_opl_chips; ++c) + { + // Reset chip timeout if any channels are active + if (opl_chip_keys[c]) + { + // Resync is necessary if the chip was idle + if (opl_chip_timeouts[c] >= OPL_CHIP_TIMEOUT) + ResyncChip(c); + opl_chip_timeouts[c] = 0; + } + } + + // Generate a sample from each chip and mix them Bit32s mix[2] = {0, 0}; for (int c = 0; c < num_opl_chips; ++c) { - Bit16s sample[2]; - OPL3_GenerateResampled(&opl_chips[c], sample); - mix[0] += sample[0]; - mix[1] += sample[1]; + // Run the chip if it's active or if it has pending register writes + if (opl_chip_timeouts[c] < OPL_CHIP_TIMEOUT || (opl_chips[c].writebuf[opl_chips[c].writebuf_cur].reg & 0x200)) + { + Bit16s sample[2]; + OPL3_Generate(&opl_chips[c], sample); + mix[0] += sample[0]; + mix[1] += sample[1]; + + // Reset chip timeout if it breaks the silence threshold + if (MAX(abs(sample[0]), abs(sample[1])) > OPL_SILENCE_THRESHOLD) + opl_chip_timeouts[c] = 0; + else + opl_chip_timeouts[c]++; + } } cursor[0] = BETWEEN(-32768, 32767, mix[0]); cursor[1] = BETWEEN(-32768, 32767, mix[1]); cursor += 2; } + //OPL3_GenerateStream(&opl_chip, (Bit16s *)(buffer + filled * 4), nsamples); filled += nsamples; @@ -191,14 +257,19 @@ static int OPL_SDL_Init(unsigned int port_base, int num_chips) // Only supports AUDIO_S16SYS mixing_channels = 2; - mixing_freq = opl_sample_rate; // Create the emulator structure: for (int c = 0; c < num_opl_chips; ++c) - OPL3_Reset(&opl_chips[c], mixing_freq); + OPL3_Reset(&opl_chips[c], OPL_SAMPLE_RATE); opl_opl3mode = 0; + for (int c = 0; c < OPL_MAX_CHIPS; ++c) + { + opl_chip_keys[c] = 0; + opl_chip_timeouts[c] = OPL_CHIP_TIMEOUT; + } + return 1; } @@ -299,6 +370,16 @@ static void WriteRegister(int chip, unsigned int reg_num, unsigned int value) break; default: + // Keep track of which channels are keyed-on so we know when the chip is in use + if ((reg_num & 0xff) >= 0xb0 && (reg_num & 0xff) <= 0xb8) + { + uint32_t key_bit = 1 << (((reg_num & 0x100) ? 9 : 0) + (reg_num & 0xf)); + if (value & (1 << 5)) + opl_chip_keys[chip] |= key_bit; + else + opl_chip_keys[chip] &= ~key_bit; + } + OPL3_WriteRegBuffered(&opl_chips[chip], reg_num, value); break; } diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index 7cf73fc9b..d7da137cb 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -403,22 +403,37 @@ static opl_voice_t *GetFreeVoice(void) return NULL; } - // Remove from free list + // Determine how many chips are needed based on current voice allocation + int used_chips = ((voice_alloced_num / (OPL_NUM_VOICES * (opl_opl3mode + 1)))) + 1; - result = voice_free_list[0]; + // Find the oldest available voice on one of the chips that are already being used. + // By preferring lower chips over higher ones, we allow higher chips to enter an + // idle state if they're not needed and save CPU time. + // If only 1 chip is emulated, this allocation pattern is identical to vanilla + for (int v = 0; v < voice_free_num; ++v) + { + if ((voice_free_list[v]->array >> 9) < used_chips) + { + // Remove from free list - voice_free_num--; + result = voice_free_list[v]; - for (i = 0; i < voice_free_num; i++) - { - voice_free_list[i] = voice_free_list[i + 1]; - } + voice_free_num--; - // Add to allocated list + for (i = v; i < voice_free_num; i++) + { + voice_free_list[i] = voice_free_list[i + 1]; + } - voice_alloced_list[voice_alloced_num++] = result; + // Add to allocated list - return result; + voice_alloced_list[voice_alloced_num++] = result; + + return result; + } + } + + return NULL; } // Release a voice back to the freelist. @@ -1447,8 +1462,6 @@ static boolean I_OPL_InitStream(int device) char *dmxoption; opl_init_result_t chip_type; - OPL_SetSampleRate(OPL_SAMPLE_RATE); - chip_type = OPL_Init(opl_io_port, num_opl_chips); if (chip_type == OPL_INIT_NONE) { From 5c952ca4a6b6cfb079357508319edeb90e9923e8 Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Fri, 20 Sep 2024 14:48:20 +0700 Subject: [PATCH 14/14] remove sub millisecond check in MIDI player loop From my tests, it appears that I_SleepUS for periods < 500 us just return immediately, at least on Windows. --- src/i_midimusic.c | 4 ---- src/i_timer.c | 9 +++------ 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/i_midimusic.c b/src/i_midimusic.c index 37978ae66..3b7e17c86 100644 --- a/src/i_midimusic.c +++ b/src/i_midimusic.c @@ -1237,10 +1237,6 @@ static int PlayerThread(void *unused) sleep = true; break; } - if (remaining_time > 0) - { - I_SleepUS(remaining_time); - } ProcessEvent(position.event, position.track); midi_state = STATE_PLAYING; } diff --git a/src/i_timer.c b/src/i_timer.c index 723e96c37..78f2c064a 100644 --- a/src/i_timer.c +++ b/src/i_timer.c @@ -148,17 +148,14 @@ void I_InitTimer(void) #ifdef _WIN32 // Create an unnamed waitable timer. - hTimer = NULL; #ifdef HAVE_HIGH_RES_TIMER hTimer = CreateWaitableTimerEx(NULL, NULL, CREATE_WAITABLE_TIMER_MANUAL_RESET | CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS); + #else + hTimer = CreateWaitableTimer(NULL, TRUE, NULL); #endif - if (hTimer == NULL) - { - hTimer = CreateWaitableTimer(NULL, TRUE, NULL); - } if (hTimer == NULL) { @@ -218,7 +215,7 @@ void I_SleepUS(uint64_t us) { #if defined(_WIN32) LARGE_INTEGER liDueTime; - liDueTime.QuadPart = -(LONGLONG)(us * 1000 / 100); + liDueTime.QuadPart = -(LONGLONG)(us * 10); if (SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, 0)) { WaitForSingleObject(hTimer, INFINITE);