[Scummvm-git-logs] scummvm master -> 0b334f4579e90bb72a04972d0e83567129129714
mikrosk
noreply at scummvm.org
Sat Aug 22 12:15:26 UTC 2026
This automated email contains information about 6 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
87d52b74e5 BACKENDS: ATARI: Don't write audio settings back to scummvm.ini
93fba2e972 BACKENDS: ATARI: Adjust audio latency
a232f869a4 BACKENDS: ATARI: Clamp at the end of mixing
d02ad6fe4a BACKENDS: ATARI: Add support for 16-bit mono
b7c1ff1f7a BACKENDS: ATARI: Rework the underrun algorithm
0b334f4579 BACKENDS: ATARI: Introduce SIDECART_OUTPUT for log messages
Commit: 87d52b74e590465f56d4b8e1c0b259a9447990d7
https://github.com/scummvm/scummvm/commit/87d52b74e590465f56d4b8e1c0b259a9447990d7
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-08-22T21:56:29+10:00
Commit Message:
BACKENDS: ATARI: Don't write audio settings back to scummvm.ini
Basically user can enter PC frequencies only and not worry about exact
Falcon/TT values.
Changed paths:
backends/mixer/atari/atari-mixer.cpp
backends/platform/atari/readme.txt
backends/platform/atari/readme.txt.in
diff --git a/backends/mixer/atari/atari-mixer.cpp b/backends/mixer/atari/atari-mixer.cpp
index 52428b6a1d7..55a02ba39b8 100644
--- a/backends/mixer/atari/atari-mixer.cpp
+++ b/backends/mixer/atari/atari-mixer.cpp
@@ -128,16 +128,10 @@ void AtariMixerManager::init() {
_samples = obtained.samples;
_downsample = (obtained.format == USoundFormatSigned8);
- ConfMan.setInt("output_rate", _outputRate, Common::ConfigManager::kApplicationDomain);
- ConfMan.setInt("output_channels", _outputChannels, Common::ConfigManager::kApplicationDomain);
- ConfMan.setInt("audio_buffer_size", _samples, Common::ConfigManager::kApplicationDomain);
-
debug("setting %d Hz mixing frequency (%d-bit, %s)",
_outputRate, obtained.format == USoundFormatSigned8 ? 8 : 16, _outputChannels == 1 ? "mono" : "stereo");
debug("sample buffer size: %d", _samples);
- ConfMan.flushToDisk();
-
_atariSampleBuffer = (byte*)Mxalloc(obtained.size * 2, MX_STRAM);
if (!_atariSampleBuffer)
error("Failed to allocate memory in ST RAM");
diff --git a/backends/platform/atari/readme.txt b/backends/platform/atari/readme.txt
index 2931b9cb076..aac1a64b41d 100644
--- a/backends/platform/atari/readme.txt
+++ b/backends/platform/atari/readme.txt
@@ -178,6 +178,12 @@ If you want to play with "audio_buffer_size", the rule of thumb is: (lag in ms)
= (audio_buffer_size / output_rate) * 1000. But it's totally OK just to double
the samples value to get rid of stuttering in a heavier game.
+Please note that unlike previous versions, these values will never be written
+back to scummvm.ini. This is beneficial if you want to switch between Falcon
+and TT or between Falcon with and without external DSP clock (as mentioned,
+DOS/Windows friendly values are automatically converted to a frequency which
+TT/Falcon supports).
+
"gaudio" debug channel: used for optimising sample playback (where
available). It prints input and output sample format as well as the name of the
converter used. See below for details.
diff --git a/backends/platform/atari/readme.txt.in b/backends/platform/atari/readme.txt.in
index d74fa7cebbd..377aaf0b232 100644
--- a/backends/platform/atari/readme.txt.in
+++ b/backends/platform/atari/readme.txt.in
@@ -178,6 +178,12 @@ If you want to play with "audio_buffer_size", the rule of thumb is: (lag in ms)
= (audio_buffer_size / output_rate) * 1000. But it's totally OK just to double
the samples value to get rid of stuttering in a heavier game.
+Please note that unlike previous versions, these values will never be written
+back to scummvm.ini. This is beneficial if you want to switch between Falcon
+and TT or between Falcon with and without external DSP clock (as mentioned,
+DOS/Windows friendly values are automatically converted to a frequency which
+TT/Falcon supports).
+
"gaudio" debug channel: used for optimising sample playback (where
available). It prints input and output sample format as well as the name of the
converter used. See below for details.
Commit: 93fba2e972277117c792f1251466feb53f571544
https://github.com/scummvm/scummvm/commit/93fba2e972277117c792f1251466feb53f571544
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-08-22T21:56:29+10:00
Commit Message:
BACKENDS: ATARI: Adjust audio latency
There was a bug in Lite, too: it set twice as big latency against Full.
Changed paths:
backends/mixer/atari/atari-mixer.cpp
diff --git a/backends/mixer/atari/atari-mixer.cpp b/backends/mixer/atari/atari-mixer.cpp
index 55a02ba39b8..26ec9c32adc 100644
--- a/backends/mixer/atari/atari-mixer.cpp
+++ b/backends/mixer/atari/atari-mixer.cpp
@@ -38,12 +38,13 @@
#ifdef DISABLE_FANCY_THEMES
#define DEFAULT_OUTPUT_RATE 11025
+#define DEFAULT_SAMPLES 512 // 2 * 46ms (42ms at 12292 Hz) latency
#else
#define DEFAULT_OUTPUT_RATE 22050
+#define DEFAULT_SAMPLES 1024 // 2 * 46ms (42ms at 24585 Hz) latency
#endif
#define DEFAULT_OUTPUT_CHANNELS 2
-#define DEFAULT_SAMPLES 2048 // 83ms
static USoundContext usoundContext;
Commit: a232f869a479c40c20c6a6044eaced448e28586b
https://github.com/scummvm/scummvm/commit/a232f869a479c40c20c6a6044eaced448e28586b
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-08-22T22:11:42+10:00
Commit Message:
BACKENDS: ATARI: Clamp at the end of mixing
Also document the optimization already merged: not mixing of muted
channels.
Changed paths:
backends/mixer/atari/atari-mixer.cpp
backends/mixer/atari/atari-mixer.h
backends/platform/atari/readme.txt
backends/platform/atari/readme.txt.in
diff --git a/backends/mixer/atari/atari-mixer.cpp b/backends/mixer/atari/atari-mixer.cpp
index 26ec9c32adc..cab782e0e26 100644
--- a/backends/mixer/atari/atari-mixer.cpp
+++ b/backends/mixer/atari/atari-mixer.cpp
@@ -100,8 +100,8 @@ AtariMixerManager::~AtariMixerManager() {
Mfree(_atariSampleBuffer);
_atariSampleBuffer = _atariPhysicalSampleBuffer = _atariLogicalSampleBuffer = nullptr;
- delete[] _samplesBuf;
- _samplesBuf = nullptr;
+ delete[] _sampleBuf;
+ _sampleBuf = nullptr;
}
void AtariMixerManager::init() {
@@ -149,9 +149,10 @@ void AtariMixerManager::init() {
// enable and mix both sources (ADC and connection matrix) to the output
Soundcmd(ADDERIN, MATIN|ADCIN);
- _samplesBuf = new uint8[_samples * _outputChannels * 2]; // always 16-bit
+ _sampleBufSize = _samples * _outputChannels * 4; // always 32-bit
+ _sampleBuf = new uint8[_sampleBufSize];
- _mixer = new Audio::MixerImpl(_outputRate, _outputChannels == 2, _samples);
+ _mixer = new Audio::MixerImpl(_outputRate, _outputChannels == 2, _samples, 4, false);
_mixer->setReady(true);
resumeAudio();
@@ -201,7 +202,7 @@ void AtariMixerManager::update() {
if (muted || endOfPlayback) {
endOfPlayback = false;
- processed = _mixer->mixCallback(_samplesBuf, _samples * _outputChannels * 2);
+ processed = _mixer->mixCallback(_sampleBuf, _sampleBufSize);
}
if (processed > 0) {
@@ -210,42 +211,54 @@ void AtariMixerManager::update() {
_atariLogicalSampleBuffer = tmp;
if (_downsample) {
- // use the trick with move.b (a7)+,dx which skips two bytes at once
- // basically supplying move.w (src)+,dx; asr.w #8,dx; move.b dx,(dst)+
__asm__ volatile(
- " move.l %%a7,%%d0\n"
- " move.l %0,%%a7\n"
- " moveq #0x0f,%%d1\n"
- " and.l %2,%%d1\n"
- " neg.l %%d1\n"
- " lsr.l #4,%2\n"
- " jmp (2f,%%pc,%%d1.l*2)\n"
- "1: move.b (%%a7)+,(%1)+\n"
- " move.b (%%a7)+,(%1)+\n"
- " move.b (%%a7)+,(%1)+\n"
- " move.b (%%a7)+,(%1)+\n"
- " move.b (%%a7)+,(%1)+\n"
- " move.b (%%a7)+,(%1)+\n"
- " move.b (%%a7)+,(%1)+\n"
- " move.b (%%a7)+,(%1)+\n"
- " move.b (%%a7)+,(%1)+\n"
- " move.b (%%a7)+,(%1)+\n"
- " move.b (%%a7)+,(%1)+\n"
- " move.b (%%a7)+,(%1)+\n"
- " move.b (%%a7)+,(%1)+\n"
- " move.b (%%a7)+,(%1)+\n"
- " move.b (%%a7)+,(%1)+\n"
- " move.b (%%a7)+,(%1)+\n"
- "2: dbra %2,1b\n"
- " move.l %%d0,%%a7\n"
+ " move.l #32768,%%d2\n"
+ " move.l #65535,%%d3\n"
+ " subq.l #1,%2\n"
+ "1: move.l (%0)+,%%d0\n"
+ " move.l %%d0,%%d1\n"
+ " add.l %%d2,%%d1\n"
+ " cmp.l %%d3,%%d1\n"
+ " bhi.b 3f\n"
+ "2: asr.l #8,%%d0\n" // TODO: tweak (there were reports that >> 8 is too quiet)
+ " move.b %%d0,(%1)+\n"
+ " dbra %2,1b\n"
+ " bra.b 4f\n"
+ "3: tst.l %%d0\n"
+ " spl %%d0\n"
+ " ext.w %%d0\n"
+ " add.w %%d2,%%d0\n"
+ " bra.b 2b\n"
+ "4:\n"
: // outputs
- : "g"(_samplesBuf), "a"(_atariPhysicalSampleBuffer), "d"(processed * _outputChannels * 2/2) // inputs
- : "d0", "d1", "cc" AND_MEMORY
+ : "a"(_sampleBuf), "a"(_atariPhysicalSampleBuffer), "d"(processed * _outputChannels) // inputs
+ : "d0", "d1", "d2", "d3", "cc" AND_MEMORY
);
memset(_atariPhysicalSampleBuffer + processed * _outputChannels * 2/2, 0, (_samples - processed) * _outputChannels * 2/2);
Setbuffer(SR_PLAY, _atariPhysicalSampleBuffer, _atariPhysicalSampleBuffer + _samples * _outputChannels * 2/2);
} else {
- memcpy(_atariPhysicalSampleBuffer, _samplesBuf, processed * _outputChannels * 2);
+ __asm__ volatile(
+ " move.l #32768,%%d2\n"
+ " move.l #65535,%%d3\n"
+ " subq.l #1,%2\n"
+ "1: move.l (%0)+,%%d0\n"
+ " move.l %%d0,%%d1\n"
+ " add.l %%d2,%%d1\n"
+ " cmp.l %%d3,%%d1\n"
+ " bhi.b 3f\n"
+ "2: move.w %%d0,(%1)+\n"
+ " dbra %2,1b\n"
+ " bra.b 4f\n"
+ "3: tst.l %%d0\n"
+ " spl %%d0\n"
+ " ext.w %%d0\n"
+ " add.w %%d2,%%d0\n"
+ " bra.b 2b\n"
+ "4:\n"
+ : // outputs
+ : "a"(_sampleBuf), "a"(_atariPhysicalSampleBuffer), "d"(processed * _outputChannels) // inputs
+ : "d0", "d1", "d2", "d3", "cc" AND_MEMORY
+ );
memset(_atariPhysicalSampleBuffer + processed * _outputChannels * 2, 0, (_samples - processed) * _outputChannels * 2);
Setbuffer(SR_PLAY, _atariPhysicalSampleBuffer, _atariPhysicalSampleBuffer + _samples * _outputChannels * 2);
}
diff --git a/backends/mixer/atari/atari-mixer.h b/backends/mixer/atari/atari-mixer.h
index 6e3e40af730..03a27247d43 100644
--- a/backends/mixer/atari/atari-mixer.h
+++ b/backends/mixer/atari/atari-mixer.h
@@ -46,7 +46,8 @@ private:
int _outputRate = 0;
int _outputChannels = 0;
int _samples = 0;
- uint8 *_samplesBuf = nullptr;
+ int _sampleBufSize = 0;
+ byte *_sampleBuf = nullptr;
byte *_atariSampleBuffer = nullptr;
byte *_atariPhysicalSampleBuffer = nullptr;
diff --git a/backends/platform/atari/readme.txt b/backends/platform/atari/readme.txt
index aac1a64b41d..4acf8ec410c 100644
--- a/backends/platform/atari/readme.txt
+++ b/backends/platform/atari/readme.txt
@@ -408,15 +408,20 @@ Mute vs. "No music"
Currently ScummVM requires each backend to mix samples, even though they may
contain muted output (i.e. zeroes). This is because the progression of sample
-playback tells ScummVM how much time has passed in e.g. an animation.
+playback tells ScummVM how much time has passed in e.g. an animation. However,
+an optimisation kicks in for muted channels: the input stream is still loaded
+and decoded but it is not mixed which alone leads to an enormous performance
+boost!
"No music" means using the null audio plugin which prevents generating any MIDI
music (and therefore avoiding the expensive synthesis emulation) but beware, it
-doesn't affect CD (*.wav) playback at all! Same applies for speech and sfx.
+doesn't affect CD (*.wav) playback at all! Same applies for speech and sfx. So
+mute one or all of those if frame rate drops noticeably.
The least amount of cycles is spent when:
- "No music" as "Preferred device": This prevents MIDI/OPL synthesis of any
kind.
+- "Mute all" in "Volume" setting.
- "output_rate" set to a DOS/Windows compatible value (default). Even if game
uses 22050 Hz and your Falcon supports 22050 Hz, it is always faster to use
11025 Hz!
diff --git a/backends/platform/atari/readme.txt.in b/backends/platform/atari/readme.txt.in
index 377aaf0b232..41cd1700f33 100644
--- a/backends/platform/atari/readme.txt.in
+++ b/backends/platform/atari/readme.txt.in
@@ -408,15 +408,20 @@ Mute vs. "No music"
Currently ScummVM requires each backend to mix samples, even though they may
contain muted output (i.e. zeroes). This is because the progression of sample
-playback tells ScummVM how much time has passed in e.g. an animation.
+playback tells ScummVM how much time has passed in e.g. an animation. However,
+an optimisation kicks in for muted channels: the input stream is still loaded
+and decoded but it is not mixed which alone leads to an enormous performance
+boost!
"No music" means using the null audio plugin which prevents generating any MIDI
music (and therefore avoiding the expensive synthesis emulation) but beware, it
-doesn't affect CD (*.wav) playback at all! Same applies for speech and sfx.
+doesn't affect CD (*.wav) playback at all! Same applies for speech and sfx. So
+mute one or all of those if frame rate drops noticeably.
The least amount of cycles is spent when:
- "No music" as "Preferred device": This prevents MIDI/OPL synthesis of any
kind.
+- "Mute all" in "Volume" setting.
- "output_rate" set to a DOS/Windows compatible value (default). Even if game
uses 22050 Hz and your Falcon supports 22050 Hz, it is always faster to use
11025 Hz!
Commit: d02ad6fe4abb7e4f83ee668997e9dc96647ac46c
https://github.com/scummvm/scummvm/commit/d02ad6fe4abb7e4f83ee668997e9dc96647ac46c
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-08-22T22:11:42+10:00
Commit Message:
BACKENDS: ATARI: Add support for 16-bit mono
Changed paths:
backends/mixer/atari/atari-mixer.cpp
backends/mixer/atari/atari-mixer.h
backends/platform/atari/readme.txt
backends/platform/atari/readme.txt.in
graphics/blit/blit-atari.cpp
diff --git a/backends/mixer/atari/atari-mixer.cpp b/backends/mixer/atari/atari-mixer.cpp
index cab782e0e26..d75522950f2 100644
--- a/backends/mixer/atari/atari-mixer.cpp
+++ b/backends/mixer/atari/atari-mixer.cpp
@@ -37,15 +37,15 @@
#include "common/textconsole.h"
#ifdef DISABLE_FANCY_THEMES
-#define DEFAULT_OUTPUT_RATE 11025
-#define DEFAULT_SAMPLES 512 // 2 * 46ms (42ms at 12292 Hz) latency
+#define DEFAULT_OUTPUT_RATE 11025
+#define DEFAULT_OUTPUT_CHANNELS 1
+#define DEFAULT_SAMPLES 512 // 2 * 46ms (42ms at 12292 Hz) latency
#else
-#define DEFAULT_OUTPUT_RATE 22050
-#define DEFAULT_SAMPLES 1024 // 2 * 46ms (42ms at 24585 Hz) latency
+#define DEFAULT_OUTPUT_RATE 22050
+#define DEFAULT_OUTPUT_CHANNELS 2
+#define DEFAULT_SAMPLES 1024 // 2 * 46ms (42ms at 24585 Hz) latency
#endif
-#define DEFAULT_OUTPUT_CHANNELS 2
-
static USoundContext usoundContext;
void AtariAudioShutdown() {
@@ -125,12 +125,24 @@ void AtariMixerManager::init() {
obtained.samples = desired.samples;
_outputRate = obtained.frequency;
- _outputChannels = obtained.channels;
- _samples = obtained.samples;
+ if (desired.channels == 1 && obtained.channels == 2 && obtained.format == USoundFormatSigned16MSB) {
+ _outputChannels = 1;
+ _emulated16bitMono = true;
+ } else {
+ _outputChannels = obtained.channels;
+ _emulated16bitMono = false;
+ }
_downsample = (obtained.format == USoundFormatSigned8);
+ _samples = obtained.samples;
debug("setting %d Hz mixing frequency (%d-bit, %s)",
- _outputRate, obtained.format == USoundFormatSigned8 ? 8 : 16, _outputChannels == 1 ? "mono" : "stereo");
+ _outputRate,
+ obtained.format == USoundFormatSigned8 ? 8 : 16,
+ _outputChannels == 2
+ ? "stereo"
+ : _emulated16bitMono
+ ? "mono (emulated)"
+ : "mono");
debug("sample buffer size: %d", _samples);
_atariSampleBuffer = (byte*)Mxalloc(obtained.size * 2, MX_STRAM);
@@ -210,19 +222,23 @@ void AtariMixerManager::update() {
_atariPhysicalSampleBuffer = _atariLogicalSampleBuffer;
_atariLogicalSampleBuffer = tmp;
+ // WARNING: loopCount, src and dst are modified by the asm code
+ int loopCount = processed * _outputChannels;
+ const byte *src = _sampleBuf;
+ byte *dst = _atariPhysicalSampleBuffer;
if (_downsample) {
__asm__ volatile(
" move.l #32768,%%d2\n"
" move.l #65535,%%d3\n"
- " subq.l #1,%2\n"
- "1: move.l (%0)+,%%d0\n"
+ " subq.l #1,%0\n"
+ "1: move.l (%1)+,%%d0\n"
" move.l %%d0,%%d1\n"
" add.l %%d2,%%d1\n"
" cmp.l %%d3,%%d1\n"
" bhi.b 3f\n"
"2: asr.l #8,%%d0\n" // TODO: tweak (there were reports that >> 8 is too quiet)
- " move.b %%d0,(%1)+\n"
- " dbra %2,1b\n"
+ " move.b %%d0,(%2)+\n"
+ " dbra %0,1b\n"
" bra.b 4f\n"
"3: tst.l %%d0\n"
" spl %%d0\n"
@@ -230,37 +246,68 @@ void AtariMixerManager::update() {
" add.w %%d2,%%d0\n"
" bra.b 2b\n"
"4:\n"
- : // outputs
- : "a"(_sampleBuf), "a"(_atariPhysicalSampleBuffer), "d"(processed * _outputChannels) // inputs
+ : "+d"(loopCount), "+a"(src), "+a"(dst) // outputs
+ : // inputs
: "d0", "d1", "d2", "d3", "cc" AND_MEMORY
- );
+ );
memset(_atariPhysicalSampleBuffer + processed * _outputChannels * 2/2, 0, (_samples - processed) * _outputChannels * 2/2);
Setbuffer(SR_PLAY, _atariPhysicalSampleBuffer, _atariPhysicalSampleBuffer + _samples * _outputChannels * 2/2);
} else {
- __asm__ volatile(
- " move.l #32768,%%d2\n"
- " move.l #65535,%%d3\n"
- " subq.l #1,%2\n"
- "1: move.l (%0)+,%%d0\n"
- " move.l %%d0,%%d1\n"
- " add.l %%d2,%%d1\n"
- " cmp.l %%d3,%%d1\n"
- " bhi.b 3f\n"
- "2: move.w %%d0,(%1)+\n"
- " dbra %2,1b\n"
- " bra.b 4f\n"
- "3: tst.l %%d0\n"
- " spl %%d0\n"
- " ext.w %%d0\n"
- " add.w %%d2,%%d0\n"
- " bra.b 2b\n"
- "4:\n"
- : // outputs
- : "a"(_sampleBuf), "a"(_atariPhysicalSampleBuffer), "d"(processed * _outputChannels) // inputs
- : "d0", "d1", "d2", "d3", "cc" AND_MEMORY
+ int bytesPerFrame = _outputChannels * 2;
+
+ if (!_emulated16bitMono) {
+ __asm__ volatile(
+ " move.l #32768,%%d2\n"
+ " move.l #65535,%%d3\n"
+ " subq.l #1,%0\n"
+ "1: move.l (%1)+,%%d0\n"
+ " move.l %%d0,%%d1\n"
+ " add.l %%d2,%%d1\n"
+ " cmp.l %%d3,%%d1\n"
+ " bhi.b 3f\n"
+ "2: move.w %%d0,(%2)+\n"
+ " dbra %0,1b\n"
+ " bra.b 4f\n"
+ "3: tst.l %%d0\n"
+ " spl %%d0\n"
+ " ext.w %%d0\n"
+ " add.w %%d2,%%d0\n"
+ " bra.b 2b\n"
+ "4:\n"
+ : "+d"(loopCount), "+a"(src), "+a"(dst) // outputs
+ : // inputs
+ : "d0", "d1", "d2", "d3", "cc" AND_MEMORY
+ );
+ } else {
+ bytesPerFrame *= 2;
+
+ __asm__ volatile(
+ " move.l #32768,%%d2\n"
+ " move.l #65535,%%d3\n"
+ " subq.l #1,%0\n"
+ "1: move.l (%1)+,%%d0\n"
+ " move.l %%d0,%%d1\n"
+ " add.l %%d2,%%d1\n"
+ " cmp.l %%d3,%%d1\n"
+ " bhi.b 3f\n"
+ "2: move.w %%d0,(%2)+\n"
+ " move.w %%d0,(%2)+\n"
+ " dbra %0,1b\n"
+ " bra.b 4f\n"
+ "3: tst.l %%d0\n"
+ " spl %%d0\n"
+ " ext.w %%d0\n"
+ " add.w %%d2,%%d0\n"
+ " bra.b 2b\n"
+ "4:\n"
+ : "+d"(loopCount), "+a"(src), "+a"(dst) // outputs
+ : // inputs
+ : "d0", "d1", "d2", "d3", "cc" AND_MEMORY
);
- memset(_atariPhysicalSampleBuffer + processed * _outputChannels * 2, 0, (_samples - processed) * _outputChannels * 2);
- Setbuffer(SR_PLAY, _atariPhysicalSampleBuffer, _atariPhysicalSampleBuffer + _samples * _outputChannels * 2);
+ }
+ const int bufferSize = processed * bytesPerFrame;
+ memset(_atariPhysicalSampleBuffer + bufferSize, 0, (_samples - processed) * bytesPerFrame);
+ Setbuffer(SR_PLAY, _atariPhysicalSampleBuffer, _atariPhysicalSampleBuffer + _samples * bytesPerFrame);
}
if (muted) {
diff --git a/backends/mixer/atari/atari-mixer.h b/backends/mixer/atari/atari-mixer.h
index 03a27247d43..fafe83bfb58 100644
--- a/backends/mixer/atari/atari-mixer.h
+++ b/backends/mixer/atari/atari-mixer.h
@@ -45,6 +45,8 @@ public:
private:
int _outputRate = 0;
int _outputChannels = 0;
+ bool _emulated16bitMono = false;
+ bool _downsample = false;
int _samples = 0;
int _sampleBufSize = 0;
byte *_sampleBuf = nullptr;
@@ -52,7 +54,6 @@ private:
byte *_atariSampleBuffer = nullptr;
byte *_atariPhysicalSampleBuffer = nullptr;
byte *_atariLogicalSampleBuffer = nullptr;
- bool _downsample = false;
};
#endif
diff --git a/backends/platform/atari/readme.txt b/backends/platform/atari/readme.txt
index 4acf8ec410c..db97ca79a9f 100644
--- a/backends/platform/atari/readme.txt
+++ b/backends/platform/atari/readme.txt
@@ -166,9 +166,10 @@ systems without an external DSP clock, these frequencies are adjusted to
the exact value; it will be rounded automatically to the nearest suitable
value.
-"output_channels" in scummvm.ini: mono (1) or stereo (2) mixing. Please note
-that Falcon doesn't allow mixing in 16-bit mono, so this will have no effect on
-this machine.
+"output_channels" in scummvm.ini: mono (1) or stereo (2) mixing. Falcon always
+outputs 16-bit stereo however when set to mono, a fast mono-to-stereo
+conversion takes place (so I encourage everyone to use mono when music playback
+is too demanding).
"audio_buffer_size" in scummvm.ini: number of samples to preload. Default is
2048 which equals to about 83ms of audio lag and seems to be about right for
diff --git a/backends/platform/atari/readme.txt.in b/backends/platform/atari/readme.txt.in
index 41cd1700f33..b8ce5749b04 100644
--- a/backends/platform/atari/readme.txt.in
+++ b/backends/platform/atari/readme.txt.in
@@ -166,9 +166,10 @@ systems without an external DSP clock, these frequencies are adjusted to
the exact value; it will be rounded automatically to the nearest suitable
value.
-"output_channels" in scummvm.ini: mono (1) or stereo (2) mixing. Please note
-that Falcon doesn't allow mixing in 16-bit mono, so this will have no effect on
-this machine.
+"output_channels" in scummvm.ini: mono (1) or stereo (2) mixing. Falcon always
+outputs 16-bit stereo however when set to mono, a fast mono-to-stereo
+conversion takes place (so I encourage everyone to use mono when music playback
+is too demanding).
"audio_buffer_size" in scummvm.ini: number of samples to preload. Default is
2048 which equals to about 83ms of audio lag and seems to be about right for
diff --git a/graphics/blit/blit-atari.cpp b/graphics/blit/blit-atari.cpp
index e8cfa81a272..5f301051ff8 100644
--- a/graphics/blit/blit-atari.cpp
+++ b/graphics/blit/blit-atari.cpp
@@ -186,10 +186,11 @@ void copyBlit(byte *dst, const byte *src,
" move.b (%0)+,(%1)+\n"
" move.b (%0)+,(%1)+\n"
"4:\n"
- : // outputs
- : "a"(src), "a"(dst), "g"(dstPitch * h) // inputs
+ : "+a"(src), "+a"(dst) // outputs
+ : "g"(dstPitch * h) // inputs
: "d0", "d1", "cc" AND_MEMORY
);
+ // WARNING: src and dst are modified by the asm code
} else {
#else
{
@@ -199,8 +200,9 @@ void copyBlit(byte *dst, const byte *src,
} else {
#ifdef USE_MOVE16
if (hasMove16() && isAligned(src) && isAligned(dst) && isAligned(srcPitch) && isAligned(dstPitch)) {
+ int loopCount = h - 1;
__asm__ volatile(
- " move.l %2,%%d0\n"
+ " move.l %3,%%d0\n"
" moveq #0x0f,%%d1\n"
" and.l %%d0,%%d1\n"
@@ -261,12 +263,13 @@ void copyBlit(byte *dst, const byte *src,
"4:\n"
" add.l %4,%1\n"
" add.l %5,%0\n"
- " dbra %3,0b\n"
- : // outputs
- : "a"(src), "a"(dst), "g"(w * bytesPerPixel), "d"(h - 1),
+ " dbra %2,0b\n"
+ : "+a"(src), "+a"(dst), "+d"(loopCount) // outputs
+ : "g"(w * bytesPerPixel),
"g"(dstPitch - w * bytesPerPixel), "g"(srcPitch - w * bytesPerPixel) // inputs
: "d0", "d1", "a0", "a1", "cc" AND_MEMORY
);
+ // WARNING: src and dst are modified by the asm code
} else {
#else
{
Commit: b7c1ff1f7af8a44ce9bf707c352df6be0a0a1c92
https://github.com/scummvm/scummvm/commit/b7c1ff1f7af8a44ce9bf707c352df6be0a0a1c92
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-08-22T22:11:50+10:00
Commit Message:
BACKENDS: ATARI: Rework the underrun algorithm
Changed paths:
backends/mixer/atari/atari-mixer.cpp
backends/mixer/atari/atari-mixer.h
diff --git a/backends/mixer/atari/atari-mixer.cpp b/backends/mixer/atari/atari-mixer.cpp
index d75522950f2..63947b95e1c 100644
--- a/backends/mixer/atari/atari-mixer.cpp
+++ b/backends/mixer/atari/atari-mixer.cpp
@@ -23,7 +23,6 @@
#include "backends/mixer/atari/atari-mixer.h"
-#include <math.h>
#include <mint/falcon.h>
#include <mint/osbind.h>
#include <mint/ostruct.h>
@@ -53,18 +52,30 @@ void AtariAudioShutdown() {
USoundDeinitXbios(&usoundContext);
}
-static volatile bool muted;
-static volatile bool endOfPlayback;
-static void __attribute__((interrupt)) timerA(void)
-{
- if (endOfPlayback && !muted) {
- *((volatile unsigned char *)0xFFFF8901L) &= 0xFC; // disable playback/repeat (and triggers another interrupt)
- muted = true;
- }
+static volatile enum {
+ kPlaybackStopped, // DMA not playing (initial or after starvation)
+ kPlay1stHalf, // DMA looping [Beg, Mid)
+ kPlay2ndHalf // DMA looping [Mid, End)
+} s_playbackState = kPlaybackStopped;
+
+static volatile uint32 s_updatePulse;
+static volatile bool s_dmaWrapped;
+static volatile bool s_isrStoppedDma;
+
+static void __attribute__((interrupt)) timerA(void) {
+ static uint32 s_lastPulseSeen;
- endOfPlayback = true;
+ if (s_updatePulse == s_lastPulseSeen) {
+ // update() didn't run since the previous wrap: stop the playback
+ *((volatile byte *)0xFFFF8901L) = 0;
+ s_isrStoppedDma = true;
+ } else {
+ s_dmaWrapped = true;
+ }
+ s_lastPulseSeen = s_updatePulse;
- *((volatile byte *)0xFFFFFA0FL) &= ~(1<<5); // clear in service bit
+ // clear in-service bit
+ *((volatile byte *)0xFFFFFA0FL) = ~(1 << 5);
}
AtariMixerManager::AtariMixerManager() : MixerManager() {
@@ -98,10 +109,10 @@ AtariMixerManager::~AtariMixerManager() {
AtariAudioShutdown();
Mfree(_atariSampleBuffer);
- _atariSampleBuffer = _atariPhysicalSampleBuffer = _atariLogicalSampleBuffer = nullptr;
+ _atariSampleBuffer = nullptr;
- delete[] _sampleBuf;
- _sampleBuf = nullptr;
+ delete[] _sampleBuffer;
+ _sampleBuffer = nullptr;
}
void AtariMixerManager::init() {
@@ -135,7 +146,7 @@ void AtariMixerManager::init() {
_downsample = (obtained.format == USoundFormatSigned8);
_samples = obtained.samples;
- debug("setting %d Hz mixing frequency (%d-bit, %s)",
+ debug("setting %d Hz mixing frequency, %d-bit, %s",
_outputRate,
obtained.format == USoundFormatSigned8 ? 8 : 16,
_outputChannels == 2
@@ -143,14 +154,14 @@ void AtariMixerManager::init() {
: _emulated16bitMono
? "mono (emulated)"
: "mono");
- debug("sample buffer size: %d", _samples);
+ debug("audio buffer size: %d", _samples);
- _atariSampleBuffer = (byte*)Mxalloc(obtained.size * 2, MX_STRAM);
- if (!_atariSampleBuffer)
+ _atariSampleBufferSize = obtained.size * 2; // two buffers
+ _atariSampleBuffer = (byte *)Mxalloc(_atariSampleBufferSize, MX_STRAM);
+ if (!_atariSampleBuffer) {
+ _atariSampleBufferSize = 0;
error("Failed to allocate memory in ST RAM");
-
- _atariPhysicalSampleBuffer = _atariSampleBuffer;
- _atariLogicalSampleBuffer = _atariSampleBuffer + obtained.size;
+ }
Setinterrupt(SI_TIMERA, SI_PLAY);
Xbtimer(XB_TIMERA, 1<<3, 1, timerA); // event count mode, count to '1'
@@ -161,8 +172,8 @@ void AtariMixerManager::init() {
// enable and mix both sources (ADC and connection matrix) to the output
Soundcmd(ADDERIN, MATIN|ADCIN);
- _sampleBufSize = _samples * _outputChannels * 4; // always 32-bit
- _sampleBuf = new uint8[_sampleBufSize];
+ _sampleBufferSize = _samples * _outputChannels * 4; // always 32-bit
+ _sampleBuffer = new uint8[_sampleBufferSize];
_mixer = new Audio::MixerImpl(_outputRate, _outputChannels == 2, _samples, 4, false);
_mixer->setReady(true);
@@ -174,7 +185,7 @@ void AtariMixerManager::suspendAudio() {
debug("suspendAudio");
Buffoper(0x00);
- muted = true;
+ s_playbackState = kPlaybackStopped;
_audioSuspended = true;
}
@@ -190,10 +201,9 @@ bool AtariMixerManager::notifyEvent(const Common::Event &event) {
switch (event.type) {
case Common::EVENT_QUIT:
case Common::EVENT_RETURN_TO_LAUNCHER:
- if (!muted) {
- Buffoper(0x00);
- muted = true;
+ if (s_playbackState != kPlaybackStopped) {
debug("silencing the mixer");
+ suspendAudio();
}
return false;
default:
@@ -210,114 +220,146 @@ void AtariMixerManager::update() {
assert(_mixer);
- int processed = -1;
+ s_updatePulse++;
- if (muted || endOfPlayback) {
- endOfPlayback = false;
- processed = _mixer->mixCallback(_sampleBuf, _sampleBufSize);
+ // Translate ISR's starvation signal into a state transition. Done
+ // here so that update() is the only writer of s_playbackState.
+ if (s_isrStoppedDma) {
+ s_isrStoppedDma = false;
+ s_playbackState = kPlaybackStopped;
}
- if (processed > 0) {
- byte* tmp = _atariPhysicalSampleBuffer;
- _atariPhysicalSampleBuffer = _atariLogicalSampleBuffer;
- _atariLogicalSampleBuffer = tmp;
+ byte *atariSampleBuffer1stHalf = _atariSampleBuffer;
+ byte *atariSampleBuffer2ndHalf = _atariSampleBuffer + _atariSampleBufferSize/2;
+ byte *atariSampleBufferEnd = _atariSampleBuffer + _atariSampleBufferSize;
+
+ bool needsMix = false;
+
+ if (s_playbackState == kPlaybackStopped) {
+ memset(_atariSampleBuffer, 0, _atariSampleBufferSize);
+ Setbuffer(SR_PLAY, atariSampleBuffer1stHalf, atariSampleBuffer2ndHalf);
+ Buffoper(SB_PLA_ENA | SB_PLA_RPT);
+ s_playbackState = kPlay1stHalf;
+ // Buffoper's 0->ENA transition can fire a spurious SI_PLAY which
+ // would set s_dmaWrapped here. The resulting extra state toggle
+ // is benign â it just shuffles which physical half holds the next
+ // mix. Audio output is continuous either way.
+ needsMix = true;
+ }
- // WARNING: loopCount, src and dst are modified by the asm code
- int loopCount = processed * _outputChannels;
- const byte *src = _sampleBuf;
- byte *dst = _atariPhysicalSampleBuffer;
- if (_downsample) {
+ if (s_dmaWrapped) {
+ s_dmaWrapped = false;
+ if (s_playbackState == kPlay1stHalf)
+ s_playbackState = kPlay2ndHalf;
+ else if (s_playbackState == kPlay2ndHalf)
+ s_playbackState = kPlay1stHalf;
+ needsMix = true;
+ }
+
+ if (!needsMix)
+ return;
+
+ // Mix into the half DMA is NOT currently playing, and Setbuffer to
+ // it so DMA wraps there at the next frame boundary.
+ byte *buf;
+ if (s_playbackState == kPlay1stHalf) {
+ buf = atariSampleBuffer2ndHalf;
+ Setbuffer(SR_PLAY, atariSampleBuffer2ndHalf, atariSampleBufferEnd);
+ } else {
+ buf = atariSampleBuffer1stHalf;
+ Setbuffer(SR_PLAY, atariSampleBuffer1stHalf, atariSampleBuffer2ndHalf);
+ }
+
+ int processed = _mixer->mixCallback(_sampleBuffer, _sampleBufferSize);
+
+ // WARNING: loopCount, src and dst are modified by the asm code
+ int loopCount = processed * _outputChannels;
+ const byte *src = _sampleBuffer;
+ byte *dst = buf;
+
+ if (_downsample) {
+ __asm__ volatile(
+ " subq.l #1,%0\n"
+ " bmi.b 3f\n"
+ " move.l #32768,%%d2\n"
+ " move.l #65535,%%d3\n"
+ " moveq #31,%%d4\n"
+ " moveq #0x7f,%%d5\n"
+ "1: move.l (%1)+,%%d0\n"
+ " move.l %%d0,%%d1\n"
+ " add.l %%d2,%%d1\n"
+ " cmp.l %%d3,%%d1\n"
+ " bhi.b 2f\n"
+ " asr.l #8,%%d0\n" // TODO: tweak (there were reports that >> 8 is too quiet)
+ " move.b %%d0,(%2)+\n"
+ " dbra %0,1b\n"
+ " bra.b 3f\n"
+ "2: asr.l %%d4,%%d0\n"
+ " eor.b %%d5,%%d0\n"
+ " move.b %%d0,(%2)+\n"
+ " dbra %0,1b\n"
+ "3:\n"
+ : "+d"(loopCount), "+a"(src), "+a"(dst) // outputs
+ : // inputs
+ : "d0", "d1", "d2", "d3", "d4", "d5", "cc" AND_MEMORY
+ );
+ memset(buf + processed * _outputChannels *2/2, 0, (_samples - processed) * _outputChannels * 2/2);
+ } else {
+ if (!_emulated16bitMono) {
__asm__ volatile(
+ " subq.l #1,%0\n"
+ " bmi.b 3f\n"
" move.l #32768,%%d2\n"
" move.l #65535,%%d3\n"
- " subq.l #1,%0\n"
+ " moveq #31,%%d4\n"
+ " move.w #0x7fff,%%d5\n"
"1: move.l (%1)+,%%d0\n"
" move.l %%d0,%%d1\n"
" add.l %%d2,%%d1\n"
" cmp.l %%d3,%%d1\n"
- " bhi.b 3f\n"
- "2: asr.l #8,%%d0\n" // TODO: tweak (there were reports that >> 8 is too quiet)
- " move.b %%d0,(%2)+\n"
+ " bhi.b 2f\n"
+ " move.w %%d0,(%2)+\n"
+ " dbra %0,1b\n"
+ " bra.b 3f\n"
+ "2: asr.l %%d4,%%d0\n"
+ " eor.w %%d5,%%d0\n"
+ " move.w %%d0,(%2)+\n"
" dbra %0,1b\n"
- " bra.b 4f\n"
- "3: tst.l %%d0\n"
- " spl %%d0\n"
- " ext.w %%d0\n"
- " add.w %%d2,%%d0\n"
- " bra.b 2b\n"
- "4:\n"
+ "3:\n"
: "+d"(loopCount), "+a"(src), "+a"(dst) // outputs
: // inputs
- : "d0", "d1", "d2", "d3", "cc" AND_MEMORY
+ : "d0", "d1", "d2", "d3", "d4", "d5", "cc" AND_MEMORY
);
- memset(_atariPhysicalSampleBuffer + processed * _outputChannels * 2/2, 0, (_samples - processed) * _outputChannels * 2/2);
- Setbuffer(SR_PLAY, _atariPhysicalSampleBuffer, _atariPhysicalSampleBuffer + _samples * _outputChannels * 2/2);
+ memset(buf + processed * _outputChannels * 2, 0, (_samples - processed) * _outputChannels * 2);
} else {
- int bytesPerFrame = _outputChannels * 2;
-
- if (!_emulated16bitMono) {
- __asm__ volatile(
- " move.l #32768,%%d2\n"
- " move.l #65535,%%d3\n"
- " subq.l #1,%0\n"
- "1: move.l (%1)+,%%d0\n"
- " move.l %%d0,%%d1\n"
- " add.l %%d2,%%d1\n"
- " cmp.l %%d3,%%d1\n"
- " bhi.b 3f\n"
- "2: move.w %%d0,(%2)+\n"
- " dbra %0,1b\n"
- " bra.b 4f\n"
- "3: tst.l %%d0\n"
- " spl %%d0\n"
- " ext.w %%d0\n"
- " add.w %%d2,%%d0\n"
- " bra.b 2b\n"
- "4:\n"
- : "+d"(loopCount), "+a"(src), "+a"(dst) // outputs
- : // inputs
- : "d0", "d1", "d2", "d3", "cc" AND_MEMORY
- );
- } else {
- bytesPerFrame *= 2;
-
- __asm__ volatile(
- " move.l #32768,%%d2\n"
- " move.l #65535,%%d3\n"
- " subq.l #1,%0\n"
- "1: move.l (%1)+,%%d0\n"
- " move.l %%d0,%%d1\n"
- " add.l %%d2,%%d1\n"
- " cmp.l %%d3,%%d1\n"
- " bhi.b 3f\n"
- "2: move.w %%d0,(%2)+\n"
- " move.w %%d0,(%2)+\n"
- " dbra %0,1b\n"
- " bra.b 4f\n"
- "3: tst.l %%d0\n"
- " spl %%d0\n"
- " ext.w %%d0\n"
- " add.w %%d2,%%d0\n"
- " bra.b 2b\n"
- "4:\n"
- : "+d"(loopCount), "+a"(src), "+a"(dst) // outputs
- : // inputs
- : "d0", "d1", "d2", "d3", "cc" AND_MEMORY
- );
- }
- const int bufferSize = processed * bytesPerFrame;
- memset(_atariPhysicalSampleBuffer + bufferSize, 0, (_samples - processed) * bytesPerFrame);
- Setbuffer(SR_PLAY, _atariPhysicalSampleBuffer, _atariPhysicalSampleBuffer + _samples * bytesPerFrame);
- }
-
- if (muted) {
- Buffoper(SB_PLA_ENA | SB_PLA_RPT);
- endOfPlayback = true;
- muted = false;
+ __asm__ volatile(
+ " subq.l #1,%0\n"
+ " bmi.b 3f\n"
+ " move.l #32768,%%d2\n"
+ " move.l #65535,%%d3\n"
+ " moveq #31,%%d4\n"
+ " move.w #0x7fff,%%d5\n"
+ "1: move.l (%1)+,%%d0\n"
+ " move.l %%d0,%%d1\n"
+ " add.l %%d2,%%d1\n"
+ " cmp.l %%d3,%%d1\n"
+ " bhi.b 2f\n"
+ " move.w %%d0,(%2)+\n"
+ " move.w %%d0,(%2)+\n"
+ " dbra %0,1b\n"
+ " bra.b 3f\n"
+ "2: asr.l %%d4,%%d0\n"
+ " eor.w %%d5,%%d0\n"
+ " move.w %%d0,(%2)+\n"
+ " move.w %%d0,(%2)+\n"
+ " dbra %0,1b\n"
+ "3:\n"
+ : "+d"(loopCount), "+a"(src), "+a"(dst) // outputs
+ : // inputs
+ : "d0", "d1", "d2", "d3", "d4", "d5", "cc" AND_MEMORY
+ );
+ memset(buf + processed * _outputChannels * 2*2, 0, (_samples - processed) * _outputChannels * 2*2);
}
- } else if (processed == 0 && !muted) {
- Buffoper(0x00);
- muted = true;
}
if (processed > 0 && processed != _samples) {
diff --git a/backends/mixer/atari/atari-mixer.h b/backends/mixer/atari/atari-mixer.h
index fafe83bfb58..4ada0178c6e 100644
--- a/backends/mixer/atari/atari-mixer.h
+++ b/backends/mixer/atari/atari-mixer.h
@@ -47,13 +47,13 @@ private:
int _outputChannels = 0;
bool _emulated16bitMono = false;
bool _downsample = false;
+
int _samples = 0;
- int _sampleBufSize = 0;
- byte *_sampleBuf = nullptr;
+ int _sampleBufferSize = 0;
+ byte *_sampleBuffer = nullptr;
+ int _atariSampleBufferSize = 0;
byte *_atariSampleBuffer = nullptr;
- byte *_atariPhysicalSampleBuffer = nullptr;
- byte *_atariLogicalSampleBuffer = nullptr;
};
#endif
Commit: 0b334f4579e90bb72a04972d0e83567129129714
https://github.com/scummvm/scummvm/commit/0b334f4579e90bb72a04972d0e83567129129714
Author: Miro Kropacek (miro.kropacek at gmail.com)
Date: 2026-08-22T22:11:50+10:00
Commit Message:
BACKENDS: ATARI: Introduce SIDECART_OUTPUT for log messages
When enabled, log messages are sent to the cartridge port so devices
like SidecarT can capture them. Disabled by default.
Also unify the _KEYTAB cast style.
Changed paths:
backends/events/atari/atari-events.cpp
backends/platform/atari/osystem_atari.cpp
diff --git a/backends/events/atari/atari-events.cpp b/backends/events/atari/atari-events.cpp
index ce848acbebe..c1dc3f7947e 100644
--- a/backends/events/atari/atari-events.cpp
+++ b/backends/events/atari/atari-events.cpp
@@ -50,7 +50,7 @@ AtariEventSource::AtariEventSource() {
_system = dynamic_cast<OSystem_Atari*>(g_system);
assert(_system != nullptr);
- _KEYTAB *pKeyTables = (_KEYTAB*)Keytbl(KT_NOCHANGE, KT_NOCHANGE, KT_NOCHANGE);
+ _KEYTAB *pKeyTables = (_KEYTAB *)Keytbl(KT_NOCHANGE, KT_NOCHANGE, KT_NOCHANGE);
memcpy(_unshiftToAscii, pKeyTables->unshift, 128);
memcpy(_shiftToAscii, pKeyTables->shift, 128);
diff --git a/backends/platform/atari/osystem_atari.cpp b/backends/platform/atari/osystem_atari.cpp
index 20ca45612ca..8230afd5018 100644
--- a/backends/platform/atari/osystem_atari.cpp
+++ b/backends/platform/atari/osystem_atari.cpp
@@ -59,6 +59,7 @@
#include "common/config-manager.h"
#include "common/debug.h"
+//#define SIDECART_OUTPUT
#define INPUT_ACTIVE
/*
@@ -474,6 +475,7 @@ void OSystem_Atari::logMessage(LogMessageType::Type type, const char *message) {
if (nf_stderr_id) {
nf_print(str);
} else {
+#ifndef SIDECART_OUTPUT
FILE *output = 0;
if (type == LogMessageType::kInfo || type == LogMessageType::kDebug)
@@ -483,6 +485,11 @@ void OSystem_Atari::logMessage(LogMessageType::Type type, const char *message) {
fputs(str, output);
fflush(output);
+#else
+#define CARTRIDGE_ROM3 0xFB0000ul
+ for (const char *s = str; *s; s++)
+ (void)(*((volatile uint16 *)(CARTRIDGE_ROM3 + ((*s & 0xFF)<<1))));
+#endif
}
}
More information about the Scummvm-git-logs
mailing list