[Scummvm-git-logs] scummvm master -> 7c2ef344d817f37c2ae5331ac46540dd75bb66bc

sev- noreply at scummvm.org
Sun Jun 14 22:15:10 UTC 2026


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .

Summary:
7c2ef344d8 AUDIO: Separate generating sine waves from the PC Speaker code


Commit: 7c2ef344d817f37c2ae5331ac46540dd75bb66bc
    https://github.com/scummvm/scummvm/commit/7c2ef344d817f37c2ae5331ac46540dd75bb66bc
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2026-06-15T00:15:07+02:00

Commit Message:
AUDIO: Separate generating sine waves from the PC Speaker code

Changed paths:
  A audio/sine.h
    audio/softsynth/pcspk.cpp
    audio/softsynth/pcspk.h
    engines/made/scriptfuncs.cpp
    engines/made/scriptfuncs.h
    engines/testbed/sound.cpp


diff --git a/audio/sine.h b/audio/sine.h
new file mode 100644
index 00000000000..2134fc696e6
--- /dev/null
+++ b/audio/sine.h
@@ -0,0 +1,72 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef AUDIO_SINE_H
+#define AUDIO_SINE_H
+
+#include "audio/audiostream.h"
+#include "common/util.h"
+
+namespace Audio {
+
+class SineStream : public AudioStream {
+public:
+	SineStream(int freq, int rate = 44100) {
+		_rate = rate;
+		_oscLength = rate / freq;
+		_oscSamples = 0;
+		_volume = 20; // The maximum volume is 255
+	}
+
+
+	int readBuffer(int16 *buffer, const int numSamples) override {
+		for (int i = 0; i < numSamples; i++) {
+			buffer[i] = generateSine(_oscSamples, _oscLength) * _volume;
+			if (_oscSamples++ >= _oscLength)
+				_oscSamples = 0;
+		}
+
+		return numSamples;
+	}
+
+	bool isStereo() const override { return false; }
+	bool endOfData() const override { return false; }
+	bool endOfStream() const override { return false; }
+	int getRate() const override { return _rate; }
+
+protected:
+	int _rate;
+	uint32 _oscLength;
+	uint32 _oscSamples;
+	byte _volume;
+
+	int8 generateSine(uint32 x, uint32 oscLength) const {
+		if (oscLength == 0)
+			return 0;
+
+		// TODO: Maybe using a look-up-table would be better?
+		return CLIP<int16>((int16) (128 * sin(2.0 * M_PI * x / oscLength)), -128, 127);
+	}
+};
+
+}
+
+#endif
diff --git a/audio/softsynth/pcspk.cpp b/audio/softsynth/pcspk.cpp
index c356eabf083..fd74b09e65e 100644
--- a/audio/softsynth/pcspk.cpp
+++ b/audio/softsynth/pcspk.cpp
@@ -31,9 +31,7 @@ PCSpeakerStream::Command::Command(PCSpeaker::WaveForm aWaveForm, float aFrequenc
 	waveForm(aWaveForm), frequency(aFrequency), length(aLength) { }
 
 const PCSpeakerStream::generatorFunc PCSpeakerStream::generateWave[] =
-	{&PCSpeakerStream::generateSquare, &PCSpeakerStream::generateSine,
-	 &PCSpeakerStream::generateSaw,    &PCSpeakerStream::generateTriangle,
-	 &PCSpeakerStream::generateSilence};
+	{&PCSpeakerStream::generateSquare, &PCSpeakerStream::generateSilence};
 
 PCSpeakerStream::PCSpeakerStream(int rate) {
 	_rate = rate;
@@ -55,7 +53,7 @@ PCSpeakerStream::~PCSpeakerStream() {
 void PCSpeakerStream::play(PCSpeaker::WaveForm wave, int freq, int32 length) {
 	Common::StackLock lock(_mutex);
 
-	assert((wave >= PCSpeaker::kWaveFormSquare) && (wave <= PCSpeaker::kWaveFormTriangle));
+	assert(wave == PCSpeaker::kWaveFormSquare);
 
 	if (_commandActive || !_commandQueue->empty())
 		// Currently playing back a queued instruction. Stop playback and clear
@@ -158,30 +156,6 @@ int8 PCSpeakerStream::generateSquare(uint32 x, uint32 oscLength) {
 	return (x < (oscLength / 2)) ? 127 : -128;
 }
 
-int8 PCSpeakerStream::generateSine(uint32 x, uint32 oscLength) {
-	if (oscLength == 0)
-		return 0;
-
-	// TODO: Maybe using a look-up-table would be better?
-	return CLIP<int16>((int16) (128 * sin(2.0 * M_PI * x / oscLength)), -128, 127);
-}
-
-int8 PCSpeakerStream::generateSaw(uint32 x, uint32 oscLength) {
-	if (oscLength == 0)
-		return 0;
-
-	return ((x * (65536 / oscLength)) >> 8) - 128;
-}
-
-int8 PCSpeakerStream::generateTriangle(uint32 x, uint32 oscLength) {
-	if (oscLength == 0)
-		return 0;
-
-	int y = ((x * (65536 / (oscLength / 2))) >> 8) - 128;
-
-	return (x <= (oscLength / 2)) ? y : (256 - y);
-}
-
 int8 PCSpeakerStream::generateSilence(uint32 x, uint32 oscLength) {
 	return 0;
 }
diff --git a/audio/softsynth/pcspk.h b/audio/softsynth/pcspk.h
index 9cdd3ae0b5d..88f5ddb0fdb 100644
--- a/audio/softsynth/pcspk.h
+++ b/audio/softsynth/pcspk.h
@@ -35,9 +35,6 @@ class PCSpeaker {
 public:
 	enum WaveForm {
 		kWaveFormSquare = 0,
-		kWaveFormSine,
-		kWaveFormSaw,
-		kWaveFormTriangle,
 		kWaveFormSilence
 	};
 
diff --git a/engines/made/scriptfuncs.cpp b/engines/made/scriptfuncs.cpp
index 5a7bec868e8..238a409b39b 100644
--- a/engines/made/scriptfuncs.cpp
+++ b/engines/made/scriptfuncs.cpp
@@ -26,7 +26,7 @@
 #include "made/database.h"
 #include "made/pmvplayer.h"
 
-#include "audio/softsynth/pcspk.h"
+#include "audio/sine.h"
 
 #include "backends/audiocd/audiocd.h"
 
@@ -39,11 +39,6 @@
 namespace Made {
 
 ScriptFunctions::ScriptFunctions(MadeEngine *vm) : _vm(vm), _soundStarted(false), _gameAudioVolume(Audio::Mixer::kMaxChannelVolume) {
-	// Initialize the two tone generators
-	_pcSpeaker1 = new Audio::PCSpeaker();
-	_pcSpeaker2 = new Audio::PCSpeaker();
-	_pcSpeaker1->init();
-	_pcSpeaker2->init();
 	_soundResource = nullptr;
 	_soundWasPlaying = false;
 }
@@ -51,9 +46,6 @@ ScriptFunctions::ScriptFunctions(MadeEngine *vm) : _vm(vm), _soundStarted(false)
 ScriptFunctions::~ScriptFunctions() {
 	for (uint i = 0; i < _externalFuncs.size(); ++i)
 		delete _externalFuncs[i];
-
-	delete _pcSpeaker1;
-	delete _pcSpeaker2;
 }
 
 typedef Common::Functor2Mem<int16, int16*, int16, ScriptFunctions> ExternalScriptFunc;
@@ -338,10 +330,14 @@ int16 ScriptFunctions::sfPlayNote(int16 argc, int16 *argv) {
 
 	debug(4, "sfPlayNote: Note = %d, Volume(?) = %d", argv[0] - 1, argv[1]);
 
-	_pcSpeaker1->play(Audio::PCSpeaker::kWaveFormSine, freqTable[argv[0] - 1], -1);
+	_vm->_mixer->stopHandle(_sine1);
+
+	Audio::AudioStream *sine = new Audio::SineStream(freqTable[argv[0] - 1], _vm->_mixer->getOutputRate());
+
+	_vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sine1, sine);
 
 	// TODO: Figure out what to do with the second parameter
-	//_pcSpeaker1->setVolume(argv[1]);
+	//_pcSpeaker1->setChannelVolume(_sine1, argv[1]);
 
 	return 0;
 }
@@ -349,7 +345,7 @@ int16 ScriptFunctions::sfPlayNote(int16 argc, int16 *argv) {
 int16 ScriptFunctions::sfStopNote(int16 argc, int16 *argv) {
 	// Used in the same place as sfPlayNote, with the same parameters
 	// We just stop the wave generator here
-	_pcSpeaker1->stop();
+	_vm->_mixer->stopHandle(_sine1);
 	return 0;
 }
 
@@ -375,16 +371,23 @@ int16 ScriptFunctions::sfPlayTele(int16 argc, int16 *argv) {
 
 	debug(4, "sfPlayTele: Button = %d", argv[0]);
 
-	_pcSpeaker1->play(Audio::PCSpeaker::kWaveFormSine, freqTable1[argv[0]], -1);
-	_pcSpeaker2->play(Audio::PCSpeaker::kWaveFormSine, freqTable2[argv[0]], -1);
+	_vm->_mixer->stopHandle(_sine1);
+	_vm->_mixer->stopHandle(_sine2);
+
+	Audio::AudioStream *sine1 = new Audio::SineStream(freqTable1[argv[0]], _vm->_mixer->getOutputRate());
+	Audio::AudioStream *sine2 = new Audio::SineStream(freqTable2[argv[0]], _vm->_mixer->getOutputRate());
+
+	_vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sine1, sine1);
+	_vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sine2, sine2);
+
 	return 0;
 }
 
 int16 ScriptFunctions::sfStopTele(int16 argc, int16 *argv) {
 	// Used in the same place as sfPlayTele, with the same parameters
 	// We just stop both wave generators here
-	_pcSpeaker1->stop();
-	_pcSpeaker2->stop();
+	_vm->_mixer->stopHandle(_sine1);
+	_vm->_mixer->stopHandle(_sine2);
 	return 0;
 }
 
diff --git a/engines/made/scriptfuncs.h b/engines/made/scriptfuncs.h
index db6910e4d9d..b39a703d318 100644
--- a/engines/made/scriptfuncs.h
+++ b/engines/made/scriptfuncs.h
@@ -29,10 +29,6 @@
 #include "common/debug.h"
 #include "common/system.h"
 
-namespace Audio {
-class PCSpeaker;
-}
-
 namespace Made {
 
 class MadeEngine;
@@ -70,7 +66,7 @@ protected:
 	uint8 _gameAudioVolume;
 
 	// PlayNote/StopNote and PlayTele/StopTele wave generators
-	Audio::PCSpeaker *_pcSpeaker1, *_pcSpeaker2;
+	Audio::SoundHandle _sine1, _sine2;
 
 	Common::Array<const ExternalFunc *> _externalFuncs;
 	Common::Array<const char *> _externalFuncNames;
diff --git a/engines/testbed/sound.cpp b/engines/testbed/sound.cpp
index 7daa7e1b9da..4800cef31a8 100644
--- a/engines/testbed/sound.cpp
+++ b/engines/testbed/sound.cpp
@@ -19,7 +19,8 @@
  *
  */
 
-#include "audio/softsynth/pcspk.h"
+#include "audio/audiostream.h"
+#include "audio/sine.h"
 #include "audio/mods/mod_xm_s3m.h"
 #include "audio/mods/universaltracker.h"
 
@@ -55,14 +56,12 @@ SoundSubsystemDialog::SoundSubsystemDialog() : TestbedInteractionDialog(80, 60,
 
 	_mixer = g_system->getMixer();
 
-	// the three streams to be mixed
-	Audio::PCSpeakerStream *s1 = new Audio::PCSpeakerStream();
-	Audio::PCSpeakerStream *s2 = new Audio::PCSpeakerStream();
-	Audio::PCSpeakerStream *s3 = new Audio::PCSpeakerStream();
+	uint rate = _mixer->getOutputRate();
 
-	s1->play(Audio::PCSpeaker::kWaveFormSine, 1000, -1);
-	s2->play(Audio::PCSpeaker::kWaveFormSine, 1200, -1);
-	s3->play(Audio::PCSpeaker::kWaveFormSine, 1400, -1);
+	// the three streams to be mixed
+	Audio::AudioStream *s1 = new Audio::SineStream(1000, rate);
+	Audio::AudioStream *s2 = new Audio::SineStream(1200, rate);
+	Audio::AudioStream *s3 = new Audio::SineStream(1400, rate);
 
 	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_h1, s1);
 	_mixer->pauseHandle(_h1, true);
@@ -126,17 +125,18 @@ TestExitStatus SoundSubsystem::playBeeps() {
 		return kTestSkipped;
 	}
 
-	Audio::PCSpeakerStream *speaker = new Audio::PCSpeakerStream();
 	Audio::Mixer *mixer = g_system->getMixer();
+	uint rate = mixer->getOutputRate();
+	Audio::AudioStream *stream;
 	Audio::SoundHandle handle;
-	mixer->playStream(Audio::Mixer::kPlainSoundType, &handle, speaker);
 
 	// Left Beep
 	Testsuite::writeOnScreen("Left Beep", Common::Point(0, 100));
+	stream = new Audio::SineStream(1000, rate);
+	mixer->playStream(Audio::Mixer::kPlainSoundType, &handle, stream);
 	mixer->setChannelBalance(handle, -127);
-	speaker->play(Audio::PCSpeaker::kWaveFormSine, 1000, -1);
 	g_system->delayMillis(500);
-	mixer->pauseHandle(handle, true);
+	mixer->stopHandle(handle);
 
 	if (Testsuite::handleInteractiveInput("  Were you able to hear the left beep?  ", "Yes", "No", kOptionRight)) {
 		Testsuite::logDetailedPrintf("Error! Left Beep couldn't be detected : Error with Mixer::setChannelBalance()\n");
@@ -145,10 +145,11 @@ TestExitStatus SoundSubsystem::playBeeps() {
 
 	// Right Beep
 	Testsuite::writeOnScreen("Right Beep", Common::Point(0, 100));
+	stream = new Audio::SineStream(1000, rate);
+	mixer->playStream(Audio::Mixer::kPlainSoundType, &handle, stream);
 	mixer->setChannelBalance(handle, 127);
-	mixer->pauseHandle(handle, false);
 	g_system->delayMillis(500);
-	mixer->stopAll();
+	mixer->stopHandle(handle);
 
 	if (Testsuite::handleInteractiveInput("Were you able to hear the right beep?", "Yes", "No", kOptionRight)) {
 		Testsuite::logDetailedPrintf("Error! Right Beep couldn't be detected : Error with Mixer::setChannelBalance()\n");
@@ -160,7 +161,7 @@ TestExitStatus SoundSubsystem::playBeeps() {
 TestExitStatus SoundSubsystem::mixSounds() {
 	Testsuite::clearScreen();
 	TestExitStatus passed = kTestPassed;
-	Common::String info = "Testing Mixer Output by generating multichannel sound output using PC speaker emulator.\n"
+	Common::String info = "Testing Mixer Output by generating multichannel sound output using sine wave forms.\n"
 	"The mixer should be able to play them simultaneously\n";
 
 	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
@@ -313,16 +314,13 @@ TestExitStatus SoundSubsystem::sampleRates() {
 
 	TestExitStatus passed = kTestPassed;
 	Audio::Mixer *mixer = g_system->getMixer();
+	uint rate = mixer->getOutputRate();
 
-	Audio::PCSpeakerStream *s1 = new Audio::PCSpeakerStream();
+	Audio::AudioStream *s1 = new Audio::SineStream(1000, rate);
 	// Stream at half sampling rate
-	Audio::PCSpeakerStream *s2 = new Audio::PCSpeakerStream(s1->getRate() - 10000);
+	Audio::AudioStream *s2 = new Audio::SineStream(1000, rate - 10000);
 	// Stream at twice sampling rate
-	Audio::PCSpeakerStream *s3 = new Audio::PCSpeakerStream(s1->getRate() + 10000);
-
-	s1->play(Audio::PCSpeaker::kWaveFormSine, 1000, -1);
-	s2->play(Audio::PCSpeaker::kWaveFormSine, 1000, -1);
-	s3->play(Audio::PCSpeaker::kWaveFormSine, 1000, -1);
+	Audio::AudioStream *s3 = new Audio::SineStream(1000, rate + 10000);
 
 	Audio::SoundHandle handle;
 	Common::Point pt(0, 100);




More information about the Scummvm-git-logs mailing list