aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Gilbert2012-02-05 11:02:29 +1100
committerStrangerke2012-04-06 08:20:50 +0200
commit3a2cd5a223bb7097acddc7acda95298db20bf1f5 (patch)
tree04f71960e0b27bef6e0df8d96cebd7793770bb83
parent42ff1a89db077ede823425c75339ad235f047916 (diff)
downloadscummvm-rg350-3a2cd5a223bb7097acddc7acda95298db20bf1f5.tar.gz
scummvm-rg350-3a2cd5a223bb7097acddc7acda95298db20bf1f5.tar.bz2
scummvm-rg350-3a2cd5a223bb7097acddc7acda95298db20bf1f5.zip
MORTEVIELLE: In progress implementation of PC Speaker music player
-rw-r--r--engines/mortevielle/mortevielle.cpp19
-rw-r--r--engines/mortevielle/mortevielle.h3
-rw-r--r--engines/mortevielle/ovd1.cpp4
-rw-r--r--engines/mortevielle/parole.cpp3
-rw-r--r--engines/mortevielle/parole2.cpp4
-rw-r--r--engines/mortevielle/sound.cpp144
-rw-r--r--engines/mortevielle/sound.h76
-rw-r--r--engines/mortevielle/var_mor.cpp5
-rw-r--r--engines/mortevielle/var_mor.h4
9 files changed, 246 insertions, 16 deletions
diff --git a/engines/mortevielle/mortevielle.cpp b/engines/mortevielle/mortevielle.cpp
index 88a3ecd9e5..fde3d424c5 100644
--- a/engines/mortevielle/mortevielle.cpp
+++ b/engines/mortevielle/mortevielle.cpp
@@ -37,7 +37,8 @@ namespace Mortevielle {
MortevielleEngine *g_vm;
MortevielleEngine::MortevielleEngine(OSystem *system, const ADGameDescription *gameDesc):
- Engine(system), _gameDescription(gameDesc), _randomSource("mortevielle") {
+ Engine(system), _gameDescription(gameDesc), _randomSource("mortevielle"),
+ _soundManager(_mixer) {
g_vm = this;
_lastGameFrame = 0;
_mouseClick = false;
@@ -298,6 +299,22 @@ void MortevielleEngine::setMousePos(const Common::Point &pt) {
_mousePos = newPoint;
}
+/**
+ * Delay by a given amount
+ */
+void MortevielleEngine::delay(int amount) {
+ uint32 endTime = g_system->getMillis() + amount;
+
+ while (g_system->getMillis() < endTime) {
+ if (g_system->getMillis() > (_lastGameFrame + GAME_FRAME_DELAY)) {
+ _lastGameFrame = g_system->getMillis();
+ g_vm->_screenSurface.updateScreen();
+ }
+
+ g_system->delayMillis(10);
+ }
+}
+
/*-------------------------------------------------------------------------*/
Common::Error MortevielleEngine::run() {
diff --git a/engines/mortevielle/mortevielle.h b/engines/mortevielle/mortevielle.h
index 75aaeda580..86ec947200 100644
--- a/engines/mortevielle/mortevielle.h
+++ b/engines/mortevielle/mortevielle.h
@@ -33,6 +33,7 @@
#include "common/error.h"
#include "graphics/surface.h"
#include "mortevielle/graphics.h"
+#include "mortevielle/sound.h"
namespace Mortevielle {
@@ -67,6 +68,7 @@ public:
PaletteManager _paletteManager;
GfxSurface _backgroundSurface;
Common::RandomSource _randomSource;
+ SoundManager _soundManager;
public:
MortevielleEngine(OSystem *system, const ADGameDescription *gameDesc);
~MortevielleEngine();
@@ -80,6 +82,7 @@ public:
void setMousePos(const Common::Point &pt);
bool getMouseClick() const { return _mouseClick; }
void setMouseClick(bool v) { _mouseClick = v; }
+ void delay(int amount);
};
extern MortevielleEngine *g_vm;
diff --git a/engines/mortevielle/ovd1.cpp b/engines/mortevielle/ovd1.cpp
index 3c23a62b28..bfa6f47790 100644
--- a/engines/mortevielle/ovd1.cpp
+++ b/engines/mortevielle/ovd1.cpp
@@ -339,7 +339,7 @@ void ani50() {
fic.read(&mem[0x47a0 * 16 + 0], 123);
fic.close();
- demus(&mem[0x3800 * 16], &mem[0x5000 * 16], 623);
+ g_vm->_soundManager.demus(&mem[0x3800 * 16], &mem[0x5000 * 16], 623);
addfix = (float)((tempo_mus - addv[1])) / 256;
cctable(tbi);
@@ -347,7 +347,7 @@ void ani50() {
k = 0;
do {
fin = keypressed();
- musyc(tbi, 9958 , tempo_mus);
+ g_vm->_soundManager.musyc(tbi, 9958 , tempo_mus);
k = k + 1;
fin = fin | keypressed() | (k >= 5);
} while (!fin);
diff --git a/engines/mortevielle/parole.cpp b/engines/mortevielle/parole.cpp
index 768b021454..ba43b5955d 100644
--- a/engines/mortevielle/parole.cpp
+++ b/engines/mortevielle/parole.cpp
@@ -28,6 +28,7 @@
#include "common/file.h"
#include "mortevielle/parole.h"
#include "mortevielle/sound.h"
+#include "mortevielle/mortevielle.h"
namespace Mortevielle {
@@ -129,7 +130,7 @@ void veracf(byte b) {
f.read(&mem[0x7414 * 16 + 0], 273);
/*blockread(f,mem[adson * 16+0],300);
blockread(f,mem[adson * 16+2400+0],245);*/
- demus(&mem[0x7414 * 16], &mem[adson * 16], 273);
+ g_vm->_soundManager.demus(&mem[0x7414 * 16], &mem[adson * 16], 273);
f.close();
}
diff --git a/engines/mortevielle/parole2.cpp b/engines/mortevielle/parole2.cpp
index cba5588ffc..e0aaf83af0 100644
--- a/engines/mortevielle/parole2.cpp
+++ b/engines/mortevielle/parole2.cpp
@@ -29,6 +29,8 @@
#include "mortevielle/level15.h"
#include "mortevielle/parole2.h"
#include "mortevielle/parole.h"
+#include "mortevielle/mortevielle.h"
+#include "mortevielle/sound.h"
#include "mortevielle/var_mor.h"
namespace Mortevielle {
@@ -116,7 +118,7 @@ void parole(int rep, int ht, int typ) {
break;
}
trait_ph();
- litph(tbi, typ, tempo);
+ g_vm->_soundManager.litph(tbi, typ, tempo);
if (typlec != 0)
for (i = 0; i <= 500; i ++) {
t_cph[i] = savph[i];
diff --git a/engines/mortevielle/sound.cpp b/engines/mortevielle/sound.cpp
index f2b70cf8ee..08dbe0ba02 100644
--- a/engines/mortevielle/sound.cpp
+++ b/engines/mortevielle/sound.cpp
@@ -27,15 +27,125 @@
#include "common/scummsys.h"
#include "mortevielle/sound.h"
+#include "mortevielle/mortevielle.h"
namespace Mortevielle {
+/**
+ * Constructor
+ */
+PCSpeaker::PCSpeaker(int rate) {
+ _rate = rate;
+ _oscLength = 0;
+ _oscSamples = 0;
+ _remainingSamples = 0;
+ _volume = 255;
+}
+
+/**
+ * Destructor
+ */
+PCSpeaker::~PCSpeaker() {
+}
+
+/**
+ * Adds a new note to the queue of notes to be played.
+ */
+void PCSpeaker::play(int freq, uint32 length) {
+ assert((freq > 0) && (length > 0));
+ Common::StackLock lock(_mutex);
+
+ _pendingNotes.push(SpeakerNote(freq, length));
+}
+
+/**
+ * Stops the currently playing song
+ */
+void PCSpeaker::stop() {
+ Common::StackLock lock(_mutex);
+
+ _remainingSamples = 0;
+ _pendingNotes.clear();
+}
+
+void PCSpeaker::setVolume(byte volume) {
+ _volume = volume;
+}
+
+/**
+ * Return true if a song is currently playing
+ */
+bool PCSpeaker::isPlaying() const {
+ return !_pendingNotes.empty() || (_remainingSamples != 0);
+}
+
+/**
+ * Method used by the mixer to pull off pending samples to play
+ */
+int PCSpeaker::readBuffer(int16 *buffer, const int numSamples) {
+ Common::StackLock lock(_mutex);
+
+ int i;
+
+ for (i = 0; (_remainingSamples || !_pendingNotes.empty()) && (i < numSamples); i++) {
+ if (!_remainingSamples)
+ // Used up the current note, so queue the next one
+ dequeueNote();
+
+ buffer[i] = generateSquare(_oscSamples, _oscLength) * _volume;
+ if (_oscSamples++ >= _oscLength)
+ _oscSamples = 0;
+
+ _remainingSamples--;
+ }
+
+ // Clear the rest of the buffer
+ if (i < numSamples)
+ memset(buffer + i, 0, (numSamples - i) * sizeof(int16));
+
+ return numSamples;
+}
+
+/**
+ * Dequeues a note from the pending note list
+ */
+void PCSpeaker::dequeueNote() {
+ SpeakerNote note = _pendingNotes.pop();
+
+ _oscLength = _rate / note.freq;
+ _oscSamples = 0;
+ _remainingSamples = (_rate * note.length) / 1000000;
+ assert((_oscLength > 0) && (_remainingSamples > 0));
+}
+
+/**
+ * Support method for generating a square wave
+ */
+int8 PCSpeaker::generateSquare(uint32 x, uint32 oscLength) {
+ return (x < (oscLength / 2)) ? 127 : -128;
+}
+
+/*-------------------------------------------------------------------------*/
+
const int tab[16] = { -96, -72, -48, -32, -20, -12, -8, -4, 0, 4, 8, 12, 20, 32, 48, 72 };
+SoundManager::SoundManager(Audio::Mixer *mixer) {
+ _mixer = mixer;
+ _speakerStream = new PCSpeaker(mixer->getOutputRate());
+ _mixer->playStream(Audio::Mixer::kSFXSoundType, &_speakerHandle,
+ _speakerStream, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+}
+
+SoundManager::~SoundManager() {
+ _mixer->stopHandle(_speakerHandle);
+ delete _speakerStream;
+
+}
+
/**
* Decode music data
*/
-void demus(const byte *PSrc, byte *PDest, int NbreSeg) {
+void SoundManager::demus(const byte *PSrc, byte *PDest, int NbreSeg) {
int seed = 128;
int v;
@@ -53,4 +163,36 @@ void demus(const byte *PSrc, byte *PDest, int NbreSeg) {
}
}
+void SoundManager::litph(tablint &t, int typ, int tempo) {
+ return;
+}
+
+void SoundManager::playNote(int frequency, int32 length) {
+ _speakerStream->play(frequency, length);
+}
+
+
+void SoundManager::musyc(tablint &tb, int nbseg, int att) {
+#ifdef DEBUG
+ const byte *pSrc = &mem[0x5000 * 16];
+
+ // Convert the countdown amount to a tempo rate, and then to note length in microseconds
+ int tempo = 1193180 / att;
+ int length = 1000000 / tempo;
+
+ for (int noteIndex = 0; noteIndex < (nbseg * 16); ++noteIndex) {
+ int lookupValue = *pSrc++;
+ int noteCountdown = tb[lookupValue];
+ int noteFrequency = 1193180 / noteCountdown;
+
+ playNote(noteFrequency, length);
+ }
+
+ // Keep waiting until the song has been finished
+ while (_speakerStream->isPlaying() && !g_vm->shouldQuit()) {
+ g_vm->delay(10);
+ }
+#endif
+}
+
} // End of namespace Mortevielle
diff --git a/engines/mortevielle/sound.h b/engines/mortevielle/sound.h
index 6e4f7b2a4d..f089974c6b 100644
--- a/engines/mortevielle/sound.h
+++ b/engines/mortevielle/sound.h
@@ -28,9 +28,83 @@
#ifndef MORTEVIELLE_SOUND_H
#define MORTEVIELLE_SOUND_H
+#include "audio/audiostream.h"
+#include "audio/mixer.h"
+#include "common/mutex.h"
+#include "common/queue.h"
+#include "mortevielle/var_mor.h"
+
namespace Mortevielle {
-extern void demus(const byte *PSrc, byte *PDest, int NbreSeg);
+/**
+ * Structure used to store pending notes to play
+ */
+struct SpeakerNote {
+ int freq;
+ uint32 length;
+
+ SpeakerNote(int noteFreq, uint32 noteLength) {
+ freq = noteFreq;
+ length = noteLength;
+ }
+};
+
+/**
+ * This is a modified PC Speaker class that allows the queueing of an entire song
+ * sequence one note at a time.
+ */
+class PCSpeaker : public Audio::AudioStream {
+private:
+ Common::Queue<SpeakerNote> _pendingNotes;
+ Common::Mutex _mutex;
+
+ int _rate;
+ uint32 _oscLength;
+ uint32 _oscSamples;
+ uint32 _remainingSamples;
+ uint32 _mixedSamples;
+ byte _volume;
+
+ void dequeueNote();
+protected:
+ static int8 generateSquare(uint32 x, uint32 oscLength);
+public:
+ PCSpeaker(int rate = 44100);
+ ~PCSpeaker();
+
+ /** Play a note for length microseconds.
+ */
+ void play(int freq, uint32 length);
+ /** Stop the currently playing sequence */
+ void stop();
+ /** Adjust the volume. */
+ void setVolume(byte volume);
+
+ bool isPlaying() const;
+
+ int readBuffer(int16 *buffer, const int numSamples);
+
+ bool isStereo() const { return false; }
+ bool endOfData() const { return false; }
+ bool endOfStream() const { return false; }
+ int getRate() const { return _rate; }
+};
+
+class SoundManager {
+private:
+ Audio::Mixer *_mixer;
+ PCSpeaker *_speakerStream;
+ Audio::SoundHandle _speakerHandle;
+public:
+ SoundManager(Audio::Mixer *mixer);
+ ~SoundManager();
+
+ void playNote(int frequency, int32 length);
+
+ void demus(const byte *PSrc, byte *PDest, int NbreSeg);
+ void litph(tablint &t, int typ, int tempo);
+ void musyc(tablint &tb, int nbseg, int att);
+};
} // End of namespace Mortevielle
diff --git a/engines/mortevielle/var_mor.cpp b/engines/mortevielle/var_mor.cpp
index 38a374e82b..bd432124db 100644
--- a/engines/mortevielle/var_mor.cpp
+++ b/engines/mortevielle/var_mor.cpp
@@ -359,9 +359,4 @@ void musyc(tablint &tb, int nbseg, int att) {
warning("TODO: musyc");
}
-// (* external 'c:\mc\phint.com'; *)
-void litph(tablint &t, int typ, int tempo) {
- warning("TODO: litph");
-}
-
} // End of namespace Mortevielle
diff --git a/engines/mortevielle/var_mor.h b/engines/mortevielle/var_mor.h
index b8a18a1253..5d62e77a1c 100644
--- a/engines/mortevielle/var_mor.h
+++ b/engines/mortevielle/var_mor.h
@@ -454,10 +454,6 @@ extern void box(int c, int Gd, int xo, int yo, int xi, int yi, int patt);
extern void decomp(int seg, int dep);
// (* external 'c:\mc\affich.com'; *)
extern void afff(int Gd, int seg, int dep, int x, int y);
-// (* external 'c:\mc\reusint.com'; *)
-extern void musyc(tablint &tb, int nbseg, int att);
-// (* external 'c:\mc\phint.com'; *)
-extern void litph(tablint &t, int typ, int tempo);
} // End of namespace Mortevielle