[Scummvm-git-logs] scummvm master -> 5362b1b302ef63daa1129673086039f61cf586ab

NMIError 60350957+NMIError at users.noreply.github.com
Mon Sep 27 20:09:39 UTC 2021


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

Summary:
5362b1b302 AUDIO: Add command queue to PC speaker emulator


Commit: 5362b1b302ef63daa1129673086039f61cf586ab
    https://github.com/scummvm/scummvm/commit/5362b1b302ef63daa1129673086039f61cf586ab
Author: Coen Rampen (crampen at gmail.com)
Date: 2021-09-27T21:45:40+02:00

Commit Message:
AUDIO: Add command queue to PC speaker emulator

This adds a command queue to the PC speaker emulator for PWM-style sounds that
need timing of less than a millisecond. The play method only supports
milliseconds and requires real time control, which is difficult to achieve with
microsecond timings.
The new playQueue method allows you to queue up playback instructions with
microsecond timing, which are executed when audio samples need to be generated.

Changed paths:
    audio/softsynth/pcspk.cpp
    audio/softsynth/pcspk.h


diff --git a/audio/softsynth/pcspk.cpp b/audio/softsynth/pcspk.cpp
index e490583749..23559824af 100644
--- a/audio/softsynth/pcspk.cpp
+++ b/audio/softsynth/pcspk.cpp
@@ -23,11 +23,17 @@
 #include "audio/softsynth/pcspk.h"
 #include "audio/null.h"
 
+#include "common/queue.h"
+
 namespace Audio {
 
+PCSpeaker::Command::Command(WaveForm waveForm, float frequency, uint32 length) :
+	waveForm(waveForm), frequency(frequency), length(length) { };
+
 const PCSpeaker::generatorFunc PCSpeaker::generateWave[] =
 	{&PCSpeaker::generateSquare, &PCSpeaker::generateSine,
-	 &PCSpeaker::generateSaw,    &PCSpeaker::generateTriangle};
+	 &PCSpeaker::generateSaw,    &PCSpeaker::generateTriangle,
+	 &PCSpeaker::generateSilence};
 
 PCSpeaker::PCSpeaker(int rate) {
 	_rate = rate;
@@ -38,9 +44,12 @@ PCSpeaker::PCSpeaker(int rate) {
 	_remainingSamples = 0;
 	_mixedSamples = 0;
 	_volume = 255;
+	_commandQueue = new Common::Queue<Command>();
+	_commandActive = false;
 }
 
 PCSpeaker::~PCSpeaker() {
+	delete _commandQueue;
 }
 
 void PCSpeaker::play(WaveForm wave, int freq, int32 length) {
@@ -48,6 +57,11 @@ void PCSpeaker::play(WaveForm wave, int freq, int32 length) {
 
 	assert((wave >= kWaveFormSquare) && (wave <= kWaveFormTriangle));
 
+	if (_commandActive || !_commandQueue->empty())
+		// Currently playing back a queued instruction. Stop playback and clear
+		// the instruction queue.
+		stop(0);
+
 	_wave = wave;
 	_oscLength = _rate / freq;
 	_oscSamples = 0;
@@ -61,10 +75,24 @@ void PCSpeaker::play(WaveForm wave, int freq, int32 length) {
 	_mixedSamples = 0;
 }
 
+void PCSpeaker::playQueue(WaveForm wave, float freq, uint32 lengthus) {
+	Common::StackLock lock(_mutex);
+
+	// Put the new instruction in the queue. This will be picked up by the
+	// readBuffer method.
+	_commandQueue->push(Command(wave, freq, lengthus));
+}
+
 void PCSpeaker::stop(int32 delay) {
 	Common::StackLock lock(_mutex);
 
-	_remainingSamples = (_rate * delay) / 1000;
+	_commandQueue->clear();
+	uint32 delaySamples = (_rate * delay) / 1000;
+	if (_commandActive) {
+		_remainingSamples = MIN(_remainingSamples, delaySamples);
+	} else {
+		_remainingSamples = delaySamples;
+	}
 	_playForever = false;
 }
 
@@ -73,26 +101,59 @@ void PCSpeaker::setVolume(byte volume) {
 }
 
 bool PCSpeaker::isPlaying() const {
-	return _remainingSamples != 0;
+	Common::StackLock lock(_mutex);
+
+	return _remainingSamples != 0 || !_commandQueue->empty();
 }
 
 int PCSpeaker::readBuffer(int16 *buffer, const int numSamples) {
 	Common::StackLock lock(_mutex);
 
-	int i;
-
-	for (i = 0; _remainingSamples && (i < numSamples); i++) {
-		buffer[i] = generateWave[_wave](_oscSamples, _oscLength) * _volume;
-		if (_oscSamples++ >= _oscLength)
+	// The total number of samples generated.
+	int generatedSamples = 0;
+	// Keep generating samples when not enough have been generated and either
+	// the current instruction has not yet finished, or there are more
+	// instructions in the queue.
+	while (generatedSamples < numSamples && (_remainingSamples > 0 || !_commandQueue->empty())) {
+		if (!_commandActive && !_commandQueue->empty()) {
+			// No playback instruction is currently being processed, but there
+			// are instructions in the queue. Start processing the first queued
+			// instruction.
+			// Note that this will end playback started by the play method.
+			Command command = _commandQueue->pop();
+			_wave = command.waveForm;
+			_oscLength = _rate / command.frequency;
 			_oscSamples = 0;
-		if (!_playForever)
-			_remainingSamples--;
-		_mixedSamples++;
+			// Length is in microseconds.
+			_remainingSamples = (_rate * command.length) / 1000000;
+			_playForever = false;
+			_commandActive = true;
+		}
+
+		// The number of samples generated by this playback instruction.
+		int commandSamples;
+
+		// Offset the samples for this command by the number of previously
+		// generated samples.
+		for (commandSamples = 0; _remainingSamples && ((generatedSamples + commandSamples) < numSamples); commandSamples++) {
+			buffer[generatedSamples + commandSamples] = generateWave[_wave](_oscSamples, _oscLength) * _volume;
+			if (_oscSamples++ >= _oscLength)
+				_oscSamples = 0;
+			if (!_playForever)
+				_remainingSamples--;
+			_mixedSamples++;
+		}
+
+		generatedSamples += commandSamples;
+
+		if (_remainingSamples == 0)
+			// Current playback instruction has finished.
+			_commandActive = false;
 	}
 
 	// Clear the rest of the buffer
-	if (i < numSamples)
-		memset(buffer + i, 0, (numSamples - i) * sizeof(int16));
+	if (generatedSamples < numSamples)
+		memset(buffer + generatedSamples, 0, (numSamples - generatedSamples) * sizeof(int16));
 
 	return numSamples;
 }
@@ -125,6 +186,10 @@ int8 PCSpeaker::generateTriangle(uint32 x, uint32 oscLength) {
 	return (x <= (oscLength / 2)) ? y : (256 - y);
 }
 
+int8 PCSpeaker::generateSilence(uint32 x, uint32 oscLength) {
+	return 0;
+}
+
 } // End of namespace Audio
 
 
diff --git a/audio/softsynth/pcspk.h b/audio/softsynth/pcspk.h
index db64da5177..62b8442c74 100644
--- a/audio/softsynth/pcspk.h
+++ b/audio/softsynth/pcspk.h
@@ -25,6 +25,7 @@
 
 #include "audio/audiostream.h"
 #include "common/mutex.h"
+#include "common/queue.h"
 
 namespace Audio {
 
@@ -34,9 +35,21 @@ public:
 		kWaveFormSquare = 0,
 		kWaveFormSine,
 		kWaveFormSaw,
-		kWaveFormTriangle
+		kWaveFormTriangle,
+		kWaveFormSilence
 	};
 
+protected:
+	// PC speaker instruction: play this waveform at frequency x for y microseconds.
+	struct Command {
+		WaveForm waveForm;
+		float frequency;
+		uint32 length;
+
+		Command(WaveForm waveForm, float frequency, uint32 length);
+	};
+
+public:
 	PCSpeaker(int rate = 44100);
 	~PCSpeaker();
 
@@ -45,6 +58,27 @@ public:
 	 *  If length is negative, play until told to stop.
 	 */
 	void play(WaveForm wave, int freq, int32 length);
+	/**
+	 * Queue the specified playback instruction. It will be executed when all
+	 * previously queued instructions have finished. Use this method for
+	 * playback of effects which require timing precision of less than a
+	 * millisecond.
+	 *
+	 * Calling this method will terminate any waveform started with the play
+	 * method. Calling the play method will terminate the active queued
+	 * instruction and clear the instruction queue.
+	 *
+	 * Use isPlaying to check if all queued instructions have finished playing.
+	 * This will return true even if the current instruction is "playing"
+	 * silence.
+	 * 
+	 * @param wave The waveform to use. For PC speaker, use square wave or
+	 * silence.
+	 * @param freq The frequency (in Hertz) to play.
+	 * @param lengthus The length in microseconds for which to play the
+	 * waveform.
+	 */
+	void playQueue(WaveForm wave, float freq, uint32 lengthus);
 	/** Stop the currently playing note after delay ms. */
 	void stop(int32 delay = 0);
 	/** Adjust the volume. */
@@ -71,6 +105,13 @@ protected:
 	uint32 _mixedSamples;
 	byte _volume;
 
+	// The queue of playback instructions.
+	Common::Queue<Command> *_commandQueue;
+	// True if a playback instruction is currently being executed. False if
+	// current playback was started by the play method (or if there is no
+	// playback at all).
+	bool _commandActive;
+
 	typedef int8 (*generatorFunc)(uint32, uint32);
 	static const generatorFunc generateWave[];
 
@@ -78,6 +119,7 @@ protected:
 	static int8 generateSine(uint32 x, uint32 oscLength);
 	static int8 generateSaw(uint32 x, uint32 oscLength);
 	static int8 generateTriangle(uint32 x, uint32 oscLength);
+	static int8 generateSilence(uint32 x, uint32 oscLength);
 };
 
 } // End of namespace Audio




More information about the Scummvm-git-logs mailing list