VOYEUR: Merge of upstream

This commit is contained in:
Paul Gilbert 2013-11-30 20:44:23 -05:00
commit ede418b67a
1497 changed files with 117501 additions and 46167 deletions

17
.gitignore vendored
View File

@ -93,6 +93,7 @@ project.xcworkspace
/dists/msvc*/*.SAV
/dists/msvc*/*.dat
/dists/msvc*/*.dll
/dists/msvc*/test_runner.cpp
/doc/*.aux
/doc/*.dvi
@ -104,6 +105,9 @@ project.xcworkspace
/plugins
/engines/plugins_table.h
/engines/engines.mk
/test/runner
/test/runner.cpp
/test/*.dSYM
@ -116,10 +120,14 @@ project.xcworkspace
/devtools/create_kyradat/create_kyradat
/devtools/create_lure/create_lure
/devtools/create_mads/create_mads
/devtools/create_mortdat/create_mortdat
/devtools/create_neverhood/create_neverhood
/devtools/create_project/create_project
/devtools/create_teenagent/create_teenagent
/devtools/create_tony/create_tony
/devtools/create_toon/create_toon
/devtools/create_translations/create_translations
/devtools/extract_mort/extract_mort
/devtools/qtable/qtable
/devtools/skycpt/skycpt
@ -159,6 +167,12 @@ ipch/
#Ignore default Visual Studio build folders
[Dd]ebug/
[Rr]elease/
[Dd]ebug32/
[Rr]elease32/
[Dd]ebug64/
[Rr]elease64/
LLVM32/
LLVM64/
#Ignore Qt Creator project files
ScummVM.config
@ -168,3 +182,6 @@ ScummVM.includes
#Ignore Komodo IDE/Edit project files
*.komodoproject
#Ignore Mac DS_Store files
.DS_Store

412
AUTHORS
View File

@ -6,9 +6,9 @@ ScummVM Team
PR Office
---------
Arnaud Boutonne - Public Relations Officer, Project
Administrator
Eugene Sandulenko - Project Leader
Arnaud Boutonne - Public Relations Officer, Project
Administrator
Eugene Sandulenko - Project Leader
Core Team
---------
@ -19,30 +19,30 @@ ScummVM Team
Retired Project Leaders
-----------------------
James Brown
Vincent Hamm - ScummVM co-founder, Original Cruise/CinE
author
Vincent Hamm - ScummVM co-founder, Original Cruise/CinE
author
Max Horn
Ludvig Strigeus - Original ScummVM and SimonVM author
Ludvig Strigeus - Original ScummVM and SimonVM author
Engine Teams
------------
SCUMM:
Torbjorn Andersson
James Brown - (retired)
Jonathan Gray - (retired)
Vincent Hamm - (retired)
Max Horn - (retired)
James Brown - (retired)
Jonathan Gray - (retired)
Vincent Hamm - (retired)
Max Horn - (retired)
Travis Howell
Pawel Kolodziejski - Codecs, iMUSE, Smush, etc.
Gregory Montoir - (retired)
Eugene Sandulenko - FT INSANE, MM NES, MM C64, game detection,
Herc/CGA
Ludvig Strigeus - (retired)
Pawel Kolodziejski - Codecs, iMUSE, Smush, etc.
Gregory Montoir - (retired)
Eugene Sandulenko - FT INSANE, MM NES, MM C64, game
detection, Herc/CGA
Ludvig Strigeus - (retired)
HE:
Jonathan Gray - (retired)
Jonathan Gray - (retired)
Travis Howell
Gregory Montoir - (retired)
Gregory Montoir - (retired)
Eugene Sandulenko
AGI:
@ -50,26 +50,30 @@ ScummVM Team
Matthew Hoops
Filippos Karapetis
Pawel Kolodziejski
Walter van Niftrik - (retired)
Walter van Niftrik - (retired)
Kari Salminen
Eugene Sandulenko
David Symonds - (retired)
David Symonds - (retired)
AGOS:
Torbjorn Andersson
Paul Gilbert
Travis Howell
Oliver Kiehl - (retired)
Ludvig Strigeus - (retired)
Oliver Kiehl - (retired)
Ludvig Strigeus - (retired)
AVALANCHE:
Peter Bozso
Arnaud Boutonne
CGE:
Arnaud Boutonne
Paul Gilbert
Cine:
Vincent Hamm - (retired)
Vincent Hamm - (retired)
Pawel Kolodziejski
Gregory Montoir - (retired)
Gregory Montoir - (retired)
Kari Salminen
Eugene Sandulenko
@ -78,7 +82,7 @@ ScummVM Team
CruisE:
Paul Gilbert
Vincent Hamm - (retired)
Vincent Hamm - (retired)
Draci:
Denis Kasak
@ -92,7 +96,7 @@ ScummVM Team
Torbjorn Andersson
Bertrand Augereau
Filippos Karapetis
Vladimir Menshakov - (retired)
Vladimir Menshakov - (retired)
Willem Jan Palenstijn
Gob:
@ -116,10 +120,10 @@ ScummVM Team
Eugene Sandulenko
Kyra:
Torbjorn Andersson - VQA Player
Torbjorn Andersson - VQA Player
Oystein Eftevaag
Florian Kagerer
Gregory Montoir - (retired)
Gregory Montoir - (retired)
Johannes Schickel
Lastexpress:
@ -142,6 +146,14 @@ ScummVM Team
Eugene Sandulenko
David Turner
Mortevielle:
Arnaud Boutonne
Paul Gilbert
Neverhood:
Benjamin Haisch
Filippos Karapetis
Parallaction:
peres
@ -149,14 +161,14 @@ ScummVM Team
Matthew Hoops
Queen:
David Eriksson - (retired)
Gregory Montoir - (retired)
David Eriksson - (retired)
Gregory Montoir - (retired)
Joost Peters
SAGA:
Torbjorn Andersson
Daniel Balsom - Original engine reimplementation author
(retired)
Daniel Balsom - Original engine reimplementation author
(retired)
Filippos Karapetis
Andrew Kurushin
Eugene Sandulenko
@ -164,46 +176,46 @@ ScummVM Team
SCI:
Greg Frieger
Paul Gilbert
Max Horn - (retired)
Max Horn - (retired)
Filippos Karapetis
Martin Kiewitz
Walter van Niftrik - (retired)
Walter van Niftrik - (retired)
Willem Jan Palenstijn
Jordi Vilalta Prat
Lars Skovlund
Sky:
Robert Goeffringmann - (retired)
Oliver Kiehl - (retired)
Robert Goeffringmann - (retired)
Oliver Kiehl - (retired)
Joost Peters
Sword1:
Fabio Battaglia - PSX version support
Thierry Crozat - Mac version support
Robert Goeffringmann - (retired)
Fabio Battaglia - PSX version support
Thierry Crozat - Mac version support
Robert Goeffringmann - (retired)
Sword2:
Torbjorn Andersson
Fabio Battaglia - PSX version support
Jonathan Gray - (retired)
Fabio Battaglia - PSX version support
Jonathan Gray - (retired)
Sword25:
Torbjorn Andersson
Paul Gilbert
Max Horn - (retired)
Max Horn - (retired)
Filippos Karapetis
Eugene Sandulenko
TeenAgent:
Robert Megone - Help with callback rewriting
Vladimir Menshakov - (retired)
Robert Megone - Help with callback rewriting
Vladimir Menshakov - (retired)
Tinsel:
Torbjorn Andersson
Fabio Battaglia - PSX version support
Fabio Battaglia - PSX version support
Paul Gilbert
Sven Hesse
Max Horn - (retired)
Max Horn - (retired)
Filippos Karapetis
Joost Peters
@ -220,27 +232,27 @@ ScummVM Team
Sylvain Dupont
Touche:
Gregory Montoir - (retired)
Gregory Montoir - (retired)
TsAGE:
Arnaud Boutonne
Paul Gilbert
Tucker:
Gregory Montoir - (retired)
Gregory Montoir - (retired)
Wintermute:
Einar Johan T. Somaaen
ZVision:
Adrian Astley
Backend Teams
-------------
Android:
Andre Heider
Angus Lees
BADA:
Chris Warren-Smith
Dreamcast:
Marcus Comstedt
@ -254,26 +266,26 @@ ScummVM Team
Lubomyr Lisen
Maemo:
Frantisek Dufka - (retired)
Frantisek Dufka - (retired)
Tarek Soliman
Nintendo 64:
Fabio Battaglia
Nintendo DS:
Bertrand Augereau - HQ software scaler
Bertrand Augereau - HQ software scaler
Neil Millstone
OpenPandora:
John Willis
PocketPC / WinCE:
Nicolas Bacca - (retired)
Nicolas Bacca - (retired)
Ismail Khatib
Kostas Nakos - (retired)
Kostas Nakos - (retired)
PlayStation 2:
Robert Goeffringmann - (retired)
Robert Goeffringmann - (retired)
Max Lingua
PSP (PlayStation Portable):
@ -281,13 +293,16 @@ ScummVM Team
Joost Peters
SDL (Win/Linux/OS X/etc.):
Max Horn - (retired)
Eugene Sandulenko - Asm routines, GFX layers
Max Horn - (retired)
Eugene Sandulenko - Asm routines, GFX layers
SymbianOS:
Jurgen Braam
Lars Persson
Tizen / BADA:
Chris Warren-Smith
WebOS:
Klaus Reimer
@ -297,9 +312,9 @@ ScummVM Team
Other subsystems
----------------
Infrastructure:
Max Horn - Backend & Engine APIs, file API, sound
mixer, audiostreams, data structures, etc.
(retired)
Max Horn - Backend & Engine APIs, file API, sound
mixer, audiostreams, data structures,
etc. (retired)
Eugene Sandulenko
Johannes Schickel
@ -309,33 +324,35 @@ ScummVM Team
Johannes Schickel
Miscellaneous:
David Corrales-Lopez - Filesystem access improvements (GSoC 2007
task) (retired)
Jerome Fisher - MT-32 emulator
Benjamin Haisch - Heavily improved de-/encoder for DXA videos
Jochen Hoenicke - Speaker & PCjr sound support, AdLib work
(retired)
Daniel ter Laan - Restoring original Drascula tracks, and
writing convert_dxa.bat
Chris Page - Return to launcher, savestate improvements,
leak fixes, ... (GSoC 2008 task) (retired)
Robin Watts - ARM assembly routines for nice speedups on
several ports; improvements to the sound
mixer
David Corrales-Lopez - Filesystem access improvements (GSoC
2007 task) (retired)
Jerome Fisher - MT-32 emulator
Benjamin Haisch - Heavily improved de-/encoder for DXA
videos
Jochen Hoenicke - Speaker & PCjr sound support, AdLib
work (retired)
Daniel ter Laan - Restoring original Drascula tracks, and
writing convert_dxa.bat
Chris Page - Return to launcher, savestate
improvements, leak fixes, ... (GSoC
2008 task) (retired)
Robin Watts - ARM assembly routines for nice speedups
on several ports; improvements to the
sound mixer
Website (code)
--------------
Fredrik Wendel - (retired)
Fredrik Wendel - (retired)
Website (maintenance)
---------------------
James Brown - IRC Logs maintainer
Thierry Crozat - Wiki maintainer
Andre Heider - Buildbot maintainer
Joost Peters - Doxygen Project Documentation maintainer
Jordi Vilalta Prat - Wiki maintainer
Eugene Sandulenko - Forum, IRC channel, Screen Shots and Mailing
list maintainer
James Brown - IRC Logs maintainer
Thierry Crozat - Wiki maintainer
Andre Heider - Buildbot maintainer
Joost Peters - Doxygen Project Documentation maintainer
Jordi Vilalta Prat - Wiki maintainer
Eugene Sandulenko - Forum, IRC channel, Screen Shots and
Mailing list maintainer
John Willis
Website (content)
@ -344,31 +361,31 @@ ScummVM Team
Documentation
-------------
Thierry Crozat - Numerous contributions to documentation
Joachim Eberhard - Numerous contributions to documentation
(retired)
Matthew Hoops - Wiki editor
Thierry Crozat - Numerous contributions to documentation
Joachim Eberhard - Numerous contributions to documentation
(retired)
Matthew Hoops - Wiki editor
Retired Team Members
--------------------
Chris Apers - Former PalmOS porter
Ralph Brorsen - Help with GUI implementation
Jamieson Christian - iMUSE, MIDI, all things musical
Felix Jakschitsch - Zak256 reverse engineering
Mutwin Kraus - Original MacOS porter
Peter Moraliyski - Port: GP32
Jeremy Newman - Former webmaster
Lionel Ulmer - Port: X11
Won Star - Former GP32 porter
Chris Apers - Former PalmOS porter
Ralph Brorsen - Help with GUI implementation
Jamieson Christian - iMUSE, MIDI, all things musical
Felix Jakschitsch - Zak256 reverse engineering
Mutwin Kraus - Original MacOS porter
Peter Moraliyski - Port: GP32
Jeremy Newman - Former webmaster
Lionel Ulmer - Port: X11
Won Star - Former GP32 porter
Other contributions
*******************
Packages
--------
AmigaOS 4:
Hans-Joerg Frieden - (retired)
Hans-Joerg Frieden - (retired)
Hubert Maier
Juha Niemimaki - (retired)
Juha Niemimaki - (retired)
Atari/FreeMiNT:
Keith Scroggins
@ -378,22 +395,22 @@ Other contributions
Luc Schrijvers
Debian GNU/Linux:
Tore Anderson - (retired)
Tore Anderson - (retired)
David Weinehall
Fedora / RedHat:
Willem Jan Palenstijn
Mac OS X:
Max Horn - (retired)
Max Horn - (retired)
Oystein Eftevaag
Mandriva:
Dominik Scherer - (retired)
Dominik Scherer - (retired)
MorphOS:
Fabien Coeurjoly
Ruediger Hanke - (retired)
Ruediger Hanke - (retired)
OS/2:
Paul Smedley
@ -411,12 +428,12 @@ Other contributions
Travis Howell
Win64:
Chris Gray - (retired)
Chris Gray - (retired)
Johannes Schickel
Translations
------------
Thierry Crozat - Translation Lead
GUI Translations
----------------
Thierry Crozat - Translation Lead
Basque:
Mikel Iturbe Urretxa
@ -444,7 +461,7 @@ Other contributions
German:
Simon Sawatzki
Lothar Serra Mari - (retired)
Lothar Serra Mari - (retired)
Hungarian:
Alex Bevilacqua
@ -478,97 +495,118 @@ Other contributions
Ukrainian:
Lubomyr Lisen
Game Translations
-----------------
CGE:
Dan Serban - Soltys English translation
Victor Gonzalez - Soltys Spanish translation
Alejandro Gomez de la Munoza - Soltys Spanish translation
Drascula:
Thierry Crozat - Improve French translation
Mortevielle:
Hugo Labrande - Improve English translation
Thierry Crozat - Improve English translation
Websites (design)
-----------------
Dobo Balazs - Website design
William Claydon - Skins for doxygen, buildbot and wiki
Yaroslav Fedevych - HTML/CSS for the website
Jean Marc Gimenez - ScummVM logo
David Jensen - SVG logo conversion
Raina - ScummVM forum buttons
Dobo Balazs - Website design
William Claydon - Skins for doxygen, buildbot and wiki
Yaroslav Fedevych - HTML/CSS for the website
Jean Marc Gimenez - ScummVM logo
David Jensen - SVG logo conversion
Raina - ScummVM forum buttons
Code contributions
------------------
Ori Avtalion - Subtitle control options in the GUI; BASS GUI
fixes
Stuart Caie - Decoders for Amiga and AtariST data files
(AGOS engine)
Paolo Costabel - PSP port contributions
Martin Doucha - CinE engine objectification
Thomas Fach-Pedersen - ProTracker module player, Smacker video
decoder
Tobias Gunkel - Sound support for C64 version of MM/Zak, Loom
PCE support
Janne Huttunen - V3 actor mask support, Dig/FT SMUSH audio
Kovacs Endre Janos - Several fixes for Simon1
Jeroen Janssen - Numerous readability and bugfix patches
Andreas Karlsson - Initial port for SymbianOS
Claudio Matsuoka - Daily Linux builds
Thomas Mayer - PSP port contributions
Sean Murray - ScummVM tools GUI application (GSoC 2007 task)
n0p - Windows CE port aspect ratio correction scaler
and right click input method
Mikesch Nepomuk - MI1 VGA floppy patches
Nicolas Noble - Config file and ALSA support
Tim Phillips - Initial MI1 CD music support
Quietust - Sound support for Amiga SCUMM V2/V3 games, MM
NES support
Robert Crossfield - Improved support for Apple II/C64 versions of
MM
Andreas Roever - Broken Sword I & II MPEG2 cutscene support
Edward Rudd - Fixes for playing MP3 versions of MI1/Loom
audio
Daniel Schepler - Final MI1 CD music support, initial Ogg Vorbis
support
Andre Souza - SDL-based OpenGL renderer
Tom Frost - WebOS port contributions
Ori Avtalion - Subtitle control options in the GUI; BASS
GUI fixes
Stuart Caie - Decoders for Amiga and AtariST data files
(AGOS engine)
Paolo Costabel - PSP port contributions
Martin Doucha - CinE engine objectification
Thomas Fach-Pedersen - ProTracker module player, Smacker video
decoder
Tobias Gunkel - Sound support for C64 version of MM/Zak,
Loom PCE support
Janne Huttunen - V3 actor mask support, Dig/FT SMUSH audio
Kovacs Endre Janos - Several fixes for Simon1
Jeroen Janssen - Numerous readability and bugfix patches
Keith Kaisershot - Several Pegasus Prime patches
Andreas Karlsson - Initial port for SymbianOS
Claudio Matsuoka - Daily Linux builds
Thomas Mayer - PSP port contributions
Sean Murray - ScummVM tools GUI application (GSoC 2007
task)
n0p - Windows CE port aspect ratio correction
scaler and right click input method
Mikesch Nepomuk - MI1 VGA floppy patches
Nicolas Noble - Config file and ALSA support
Tim Phillips - Initial MI1 CD music support
Quietust - Sound support for Amiga SCUMM V2/V3
games, MM NES support
Robert Crossfield - Improved support for Apple II/C64
versions of MM
Andreas Roever - Broken Sword I & II MPEG2 cutscene
support
Edward Rudd - Fixes for playing MP3 versions of
MI1/Loom audio
Daniel Schepler - Final MI1 CD music support, initial Ogg
Vorbis support
Andre Souza - SDL-based OpenGL renderer
Tom Frost - WebOS port contributions
FreeSCI Contributors
--------------------
Francois-R Boyer - MT-32 information and mapping code
Rainer Canavan - IRIX MIDI driver and bug fixes
Francois-R Boyer - MT-32 information and mapping code
Rainer Canavan - IRIX MIDI driver and bug fixes
Xiaojun Chen
Paul David Doherty - Game version information
Vyacheslav Dikonov - Config script improvements
Ruediger Hanke - Port to the MorphOS platform
Matt Hargett - Clean-ups, bugfixes, Hardcore QA, Win32
Max Horn - SetJump implementation
Ravi I. - SCI0 sound resource specification
Emmanuel Jeandel - Bugfixes and bug reports
Dmitry Jemerov - Port to the Win32 platform, numerous bugfixes
Chris Kehler - Makefile enhancements
Christopher T. Lansdown - Original CVS maintainer, Alpha compatibility
fixes
Sergey Lapin - Port of Carl's type 2 decompression code
Rickard Lind - MT-32->GM MIDI mapping magic, sound research
Hubert Maier - AmigaOS 4 port
Johannes Manhave - Document format translation
Claudio Matsuoka - CVS snapshots, daily builds, BeOS and cygwin
ports
Dark Minister - SCI research (bytecode and parser)
Carl Muckenhoupt - Sources to the SCI resource viewer tools that
started it all
Anders Baden Nielsen - PPC testing
Walter van Niftrik - Ports to the Dreamcast and GP32 platforms
Rune Orsval - Configuration file editor
Solomon Peachy - SDL ports and much of the sound subsystem
Robey Pointer - Bug tracking system hosting
Magnus Reftel - Heap implementation, Python class viewer,
bugfixes
Christoph Reichenbach - UN*X code, VM/Graphics/Sound/other
infrastructure
George Reid - FreeBSD package management
Lars Skovlund - Project maintenance, most documentation,
bugfixes, SCI1 support
Rink Springer - Port to the DOS platform, several bug fixes
Rainer De Temple - SCI research
Paul David Doherty - Game version information
Vyacheslav Dikonov - Config script improvements
Ruediger Hanke - Port to the MorphOS platform
Matt Hargett - Clean-ups, bugfixes, Hardcore QA, Win32
Max Horn - SetJump implementation
Ravi I. - SCI0 sound resource specification
Emmanuel Jeandel - Bugfixes and bug reports
Dmitry Jemerov - Port to the Win32 platform, numerous
bugfixes
Chris Kehler - Makefile enhancements
Christopher T. Lansdown - Original CVS maintainer, Alpha
compatibility fixes
Sergey Lapin - Port of Carl's type 2 decompression code
Rickard Lind - MT-32->GM MIDI mapping magic, sound
research
Hubert Maier - AmigaOS 4 port
Johannes Manhave - Document format translation
Claudio Matsuoka - CVS snapshots, daily builds, BeOS and
cygwin ports
Dark Minister - SCI research (bytecode and parser)
Carl Muckenhoupt - Sources to the SCI resource viewer tools
that started it all
Anders Baden Nielsen - PPC testing
Walter van Niftrik - Ports to the Dreamcast and GP32 platforms
Rune Orsval - Configuration file editor
Solomon Peachy - SDL ports and much of the sound subsystem
Robey Pointer - Bug tracking system hosting
Magnus Reftel - Heap implementation, Python class viewer,
bugfixes
Christoph Reichenbach - UN*X code, VM/Graphics/Sound/other
infrastructure
George Reid - FreeBSD package management
Lars Skovlund - Project maintenance, most documentation,
bugfixes, SCI1 support
Rink Springer - Port to the DOS platform, several bug
fixes
Rainer De Temple - SCI research
Sean Terrell
Hugues Valois - Game selection menu
Jordi Vilalta - Numerous code and website clean-up patches
Petr Vyhnak - The DCL-INFLATE algorithm, many Win32
improvements
Bas Zoetekouw - Man pages, debian package management, CVS
maintenance
Hugues Valois - Game selection menu
Jordi Vilalta - Numerous code and website clean-up
patches
Petr Vyhnak - The DCL-INFLATE algorithm, many Win32
improvements
Bas Zoetekouw - Man pages, debian package management, CVS
maintenance
Special thanks to Prof. Dr. Gary Nutt for allowing the FreeSCI VM
extension as a course project in his Advanced OS course.
@ -601,6 +639,8 @@ Special thanks to
Jimmi Thogersen - For ScummRev, and much obscure code/documentation
Tristan - For additional work on the original MT-32 emulator
James Woodcock - Soundtrack enhancements
Anton Yartsev - For the original re-implementation of the ZVision
engine
Tony Warriner and everyone at Revolution Software Ltd. for sharing with us
the source of some of their brilliant games, allowing us to release

View File

@ -32,8 +32,10 @@ ifeq "$(HAVE_GCC)" "1"
# being helpful.
#CXXFLAGS+= -Wmissing-format-attribute
# Disable RTTI and exceptions
ifneq "$(BACKEND)" "tizen"
# Disable RTTI and exceptions. These settings cause tizen apps to crash
CXXFLAGS+= -fno-rtti -fno-exceptions
endif
ifneq "$(HAVE_CLANG)" "1"
# enable checking of pointers returned by "new", but only when we do not
@ -44,6 +46,11 @@ endif
ifeq "$(HAVE_CLANG)" "1"
CXXFLAGS+= -Wno-conversion -Wno-shorten-64-to-32 -Wno-sign-compare -Wno-four-char-constants
# We use a anonymous nested type declaration in an anonymous union in
# common/str.h. This is no standard construct and clang warns about it.
# It works for all our target systems though, thus we simply disable that
# warning.
CXXFLAGS+= -Wno-nested-anon-types
endif
ifeq "$(HAVE_ICC)" "1"
@ -75,7 +82,8 @@ EXECUTABLE := $(EXEPRE)scummvm$(EXEEXT)
include $(srcdir)/Makefile.common
# check if configure has been run or has been changed since last run
config.h config.mk: $(srcdir)/configure $(srcdir)/engines/configure.engines
ENGINE_SUBDIRS_CONFIGURE := $(wildcard $(srcdir)/engines/*/configure.engine)
config.h: $(srcdir)/configure $(ENGINE_SUBDIRS_CONFIGURE)
ifeq "$(findstring config.mk,$(MAKEFILE_LIST))" "config.mk"
@echo "Running $(srcdir)/configure with the last specified parameters"
@sleep 2
@ -87,6 +95,14 @@ else
$(error You need to run $(srcdir)/configure before you can run make. Check $(srcdir)/configure --help for a list of parameters)
endif
config.mk engines/plugins_table.h engines/engines.mk: config.h
@if test -f $@; then \
touch $@; \
else \
rm -f config.h; \
$(MAKE) config.h; \
fi
ifneq ($(origin port_mk), undefined)
include $(srcdir)/$(port_mk)
endif

View File

@ -16,7 +16,7 @@ all: $(EXECUTABLE) plugins
PLUGINS :=
MODULES := test devtools base $(MODULES)
-include $(srcdir)/engines/engines.mk
-include engines/engines.mk
# After the game specific modules follow the shared modules
MODULES += \
@ -79,7 +79,7 @@ $(EXECUTABLE): $(OBJS)
$(QUIET_LINK)$(LD) $(LDFLAGS) $(PRE_OBJS_FLAGS) $+ $(POST_OBJS_FLAGS) $(LIBS) -o $@
distclean: clean clean-devtools
$(RM) config.h config.mk config.log
$(RM) config.h config.mk config.log engines/engines.mk engines/plugins_table.h
clean:
$(RM_REC) $(DEPDIRS)
@ -147,7 +147,7 @@ endif
# recreate them (which it can't), and in particular from looking for potential
# source files. This can save quite a bit of disk access time.
.PHONY: $(wildcard $(addsuffix /*.d,$(DEPDIRS))) $(addprefix $(srcdir)/, $(addsuffix /module.mk,$(MODULES))) \
$(srcdir)/$(port_mk) $(srcdir)/rules.mk $(srcdir)/engines/engines.mk
$(srcdir)/$(port_mk) $(srcdir)/rules.mk
######################################################################
# Get the current version information
@ -253,6 +253,12 @@ endif
ifdef ENABLE_LURE
DIST_FILES_ENGINEDATA+=lure.dat
endif
ifdef ENABLE_MORTEVIELLE
DIST_FILES_ENGINEDATA+=mort.dat
endif
ifdef ENABLE_NEVERHOOD
DIST_FILES_ENGINEDATA+=neverhood.dat
endif
ifdef ENABLE_QUEEN
DIST_FILES_ENGINEDATA+=queen.tbl
endif
@ -262,6 +268,9 @@ endif
ifdef ENABLE_TEENAGENT
DIST_FILES_ENGINEDATA+=teenagent.dat
endif
ifdef ENABLE_TONY
DIST_FILES_ENGINEDATA+=tony.dat
endif
ifdef ENABLE_TOON
DIST_FILES_ENGINEDATA+=toon.dat
endif

15
NEWS
View File

@ -1,7 +1,20 @@
For a more comprehensive changelog of the latest experimental code, see:
https://github.com/scummvm/scummvm/commits/
1.6.0 (????-??-??)
1.7.0 (????-??-??)
General:
- Updated MT-32 emulation code to version 1.3.0.
Gob:
- Improved video quality in Urban Runner
Broken Sword 1:
- Added back support for MPEG-2 videos
Broken Sword 2:
- Added back support for MPEG-2 videos
1.6.0 (2013-05-31)
New Games:
- Added support for 3 Skulls of the Toltecs.
- Added support for Eye of the Beholder.

View File

@ -44,7 +44,8 @@ _centerPitchWheelOnUnload(false),
_sendSustainOffOnNotesOff(false),
_numTracks(0),
_activeTrack(255),
_abortParse(0) {
_abortParse(false),
_jumpingToTick(false) {
memset(_activeNotes, 0, sizeof(_activeNotes));
memset(_tracks, 0, sizeof(_tracks));
_nextEvent.start = NULL;
@ -204,49 +205,22 @@ void MidiParser::onTimer() {
return;
}
if (info.event == 0xF0) {
// SysEx event
// Check for trailing 0xF7 -- if present, remove it.
if (info.ext.data[info.length-1] == 0xF7)
_driver->sysEx(info.ext.data, (uint16)info.length-1);
if (info.command() == 0x8) {
activeNote(info.channel(), info.basic.param1, false);
} else if (info.command() == 0x9) {
if (info.length > 0)
hangingNote(info.channel(), info.basic.param1, info.length * _psecPerTick - (endTime - eventTime));
else
_driver->sysEx(info.ext.data, (uint16)info.length);
} else if (info.event == 0xFF) {
// META event
if (info.ext.type == 0x2F) {
// End of Track must be processed by us,
// as well as sending it to the output device.
if (_autoLoop) {
jumpToTick(0);
parseNextEvent(_nextEvent);
} else {
stopPlaying();
_driver->metaEvent(info.ext.type, info.ext.data, (uint16)info.length);
}
return;
} else if (info.ext.type == 0x51) {
if (info.length >= 3) {
setTempo(info.ext.data[0] << 16 | info.ext.data[1] << 8 | info.ext.data[2]);
}
}
_driver->metaEvent(info.ext.type, info.ext.data, (uint16)info.length);
} else {
if (info.command() == 0x8) {
activeNote(info.channel(), info.basic.param1, false);
} else if (info.command() == 0x9) {
if (info.length > 0)
hangingNote(info.channel(), info.basic.param1, info.length * _psecPerTick - (endTime - eventTime));
else
activeNote(info.channel(), info.basic.param1, true);
}
sendToDriver(info.event, info.basic.param1, info.basic.param2);
activeNote(info.channel(), info.basic.param1, true);
}
processEvent(info);
if (!_abortParse) {
_position._lastEventTime = eventTime;
parseNextEvent(_nextEvent);
}
if (_abortParse)
break;
_position._lastEventTime = eventTime;
parseNextEvent(_nextEvent);
}
if (!_abortParse) {
@ -255,6 +229,45 @@ void MidiParser::onTimer() {
}
}
void MidiParser::processEvent(const EventInfo &info, bool fireEvents) {
if (info.event == 0xF0) {
// SysEx event
// Check for trailing 0xF7 -- if present, remove it.
if (fireEvents) {
if (info.ext.data[info.length-1] == 0xF7)
_driver->sysEx(info.ext.data, (uint16)info.length-1);
else
_driver->sysEx(info.ext.data, (uint16)info.length);
}
} else if (info.event == 0xFF) {
// META event
if (info.ext.type == 0x2F) {
// End of Track must be processed by us,
// as well as sending it to the output device.
if (_autoLoop) {
jumpToTick(0);
parseNextEvent(_nextEvent);
} else {
stopPlaying();
if (fireEvents)
_driver->metaEvent(info.ext.type, info.ext.data, (uint16)info.length);
}
_abortParse = true;
return;
} else if (info.ext.type == 0x51) {
if (info.length >= 3) {
setTempo(info.ext.data[0] << 16 | info.ext.data[1] << 8 | info.ext.data[2]);
}
}
if (fireEvents)
_driver->metaEvent(info.ext.type, info.ext.data, (uint16)info.length);
} else {
if (fireEvents)
sendToDriver(info.event, info.basic.param1, info.basic.param2);
}
}
void MidiParser::allNotesOff() {
if (!_driver)
return;
@ -370,6 +383,9 @@ bool MidiParser::jumpToTick(uint32 tick, bool fireEvents, bool stopNotes, bool d
if (_activeTrack >= _numTracks)
return false;
assert(!_jumpingToTick); // This function is not re-entrant
_jumpingToTick = true;
Tracker currentPos(_position);
EventInfo currentEvent(_nextEvent);
@ -390,34 +406,19 @@ bool MidiParser::jumpToTick(uint32 tick, bool fireEvents, bool stopNotes, bool d
_position._playTick = _position._lastEventTick;
_position._playTime = _position._lastEventTime;
if (info.event == 0xFF) {
if (info.ext.type == 0x2F) { // End of track
_position = currentPos;
_nextEvent = currentEvent;
return false;
} else {
if (info.ext.type == 0x51 && info.length >= 3) // Tempo
setTempo(info.ext.data[0] << 16 | info.ext.data[1] << 8 | info.ext.data[2]);
if (fireEvents)
_driver->metaEvent(info.ext.type, info.ext.data, (uint16) info.length);
}
} else if (fireEvents) {
if (info.event == 0xF0) {
if (info.ext.data[info.length-1] == 0xF7)
_driver->sysEx(info.ext.data, (uint16)info.length-1);
else
_driver->sysEx(info.ext.data, (uint16)info.length);
} else {
// The note on sending code is used by the SCUMM engine. Other engine using this code
// (such as SCI) have issues with this, as all the notes sent can be heard when a song
// is fast-forwarded. Thus, if the engine requests it, don't send note on events.
if (info.command() == 0x9 && dontSendNoteOn) {
// Don't send note on; doing so creates a "warble" with some instruments on the MT-32.
// Refer to patch #3117577
} else {
sendToDriver(info.event, info.basic.param1, info.basic.param2);
}
}
// Some special processing for the fast-forward case
if (info.command() == 0x9 && dontSendNoteOn) {
// Don't send note on; doing so creates a "warble" with
// some instruments on the MT-32. Refer to patch #3117577
} else if (info.event == 0xFF && info.ext.type == 0x2F) {
// End of track
// This means that we failed to find the right tick.
_position = currentPos;
_nextEvent = currentEvent;
_jumpingToTick = false;
return false;
} else {
processEvent(info, fireEvents);
}
parseNextEvent(_nextEvent);
@ -441,6 +442,7 @@ bool MidiParser::jumpToTick(uint32 tick, bool fireEvents, bool stopNotes, bool d
}
_abortParse = true;
_jumpingToTick = false;
return true;
}

View File

@ -105,8 +105,8 @@ struct EventInfo {
///< will occur, and the MidiParser will have to generate one itself.
///< For all other events, this value should always be zero.
byte channel() { return event & 0x0F; } ///< Separates the MIDI channel from the event.
byte command() { return event >> 4; } ///< Separates the command code from the event.
byte channel() const { return event & 0x0F; } ///< Separates the MIDI channel from the event.
byte command() const { return event >> 4; } ///< Separates the command code from the event.
};
/**
@ -287,12 +287,14 @@ protected:
///< so each event is parsed only once; this permits
///< simulated events in certain formats.
bool _abortParse; ///< If a jump or other operation interrupts parsing, flag to abort.
bool _jumpingToTick; ///< True if currently inside jumpToTick
protected:
static uint32 readVLQ(byte * &data);
virtual void resetTracking();
virtual void allNotesOff();
virtual void parseNextEvent(EventInfo &info) = 0;
virtual void processEvent(const EventInfo &info, bool fireEvents = true);
void activeNote(byte channel, byte note, bool active);
void hangingNote(byte channel, byte note, uint32 ticksLeft, bool recycle = true);

View File

@ -20,6 +20,8 @@
*
*/
#include "gui/EventRecorder.h"
#include "common/util.h"
#include "common/system.h"
#include "common/textconsole.h"
@ -427,6 +429,11 @@ void MixerImpl::pauseHandle(SoundHandle handle, bool paused) {
bool MixerImpl::isSoundIDActive(int id) {
Common::StackLock lock(_mutex);
#ifdef ENABLE_EVENTRECORDER
g_eventRec.updateSubsystems();
#endif
for (int i = 0; i != NUM_CHANNELS; i++)
if (_channels[i] && _channels[i]->getId() == id)
return true;
@ -443,6 +450,11 @@ int MixerImpl::getSoundID(SoundHandle handle) {
bool MixerImpl::isSoundHandleActive(SoundHandle handle) {
Common::StackLock lock(_mutex);
#ifdef ENABLE_EVENTRECORDER
g_eventRec.updateSubsystems();
#endif
const int index = handle._val % NUM_CHANNELS;
return _channels[index] && _channels[index]->getHandle()._val == handle._val;
}
@ -556,12 +568,12 @@ void Channel::pause(bool paused) {
_pauseLevel++;
if (_pauseLevel == 1)
_pauseStartTime = g_system->getMillis();
_pauseStartTime = g_system->getMillis(true);
} else if (_pauseLevel > 0) {
_pauseLevel--;
if (!_pauseLevel) {
_pauseTime = (g_system->getMillis() - _pauseStartTime);
_pauseTime = (g_system->getMillis(true) - _pauseStartTime);
_pauseStartTime = 0;
}
}
@ -579,7 +591,7 @@ Timestamp Channel::getElapsedTime() {
if (isPaused())
delta = _pauseStartTime - _mixerTimeStamp;
else
delta = g_system->getMillis() - _mixerTimeStamp - _pauseTime;
delta = g_system->getMillis(true) - _mixerTimeStamp - _pauseTime;
// Convert the number of samples into a time duration.
@ -599,13 +611,12 @@ int Channel::mix(int16 *data, uint len) {
assert(_stream);
int res = 0;
if (_stream->endOfData()) {
// TODO: call drain method
} else {
assert(_converter);
_samplesConsumed = _samplesDecoded;
_mixerTimeStamp = g_system->getMillis();
_mixerTimeStamp = g_system->getMillis(true);
_pauseTime = 0;
res = _converter->flow(*_stream, data, len, _volL, _volR);
_samplesDecoded += res;

View File

@ -163,7 +163,7 @@ int MidiDriver_FluidSynth::open() {
Common::String interpolation = ConfMan.get("fluidsynth_misc_interpolation");
int interpMethod = FLUID_INTERP_4THORDER;
if (interpolation == "none") {
interpMethod = FLUID_INTERP_NONE;
} else if (interpolation == "linear") {

View File

@ -62,7 +62,8 @@ protected:
// Callback for debug messages, in vprintf() format
void printDebug(const char *fmt, va_list list) {
debug(4, fmt, list);
Common::String out = Common::String::vformat(fmt, list);
debug(4, "%s", out.c_str());
}
// Callbacks for reporting various errors and information
@ -459,9 +460,6 @@ bool MT32EmuMusicPlugin::checkDevice(MidiDriver::DeviceHandle) const {
}
Common::Error MT32EmuMusicPlugin::createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle) const {
if (ConfMan.hasKey("extrapath"))
SearchMan.addDirectory("extrapath", ConfMan.get("extrapath"));
*mididriver = new MidiDriver_MT32(g_system->getMixer());
return Common::kNoError;

View File

@ -1,277 +0,0 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
* Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mt32emu.h"
#if MT32EMU_USE_REVERBMODEL == 1
#include "AReverbModel.h"
// Analysing of state of reverb RAM address lines gives exact sizes of the buffers of filters used. This also indicates that
// the reverb model implemented in the real devices consists of three series allpass filters preceded by a non-feedback comb (or a delay with a LPF)
// and followed by three parallel comb filters
namespace MT32Emu {
// Because LA-32 chip makes it's output available to process by the Boss chip with a significant delay,
// the Boss chip puts to the buffer the LA32 dry output when it is ready and performs processing of the _previously_ latched data.
// Of course, the right way would be to use a dedicated variable for this, but our reverb model is way higher level,
// so we can simply increase the input buffer size.
static const Bit32u PROCESS_DELAY = 1;
// Default reverb settings for modes 0-2. These correspond to CM-32L / LAPC-I "new" reverb settings. MT-32 reverb is a bit different.
// Found by tracing reverb RAM data lines (thanks go to Lord_Nightmare & balrog).
static const Bit32u NUM_ALLPASSES = 3;
static const Bit32u NUM_COMBS = 4; // Well, actually there are 3 comb filters, but the entrance LPF + delay can be perfectly processed via a comb here.
static const Bit32u MODE_0_ALLPASSES[] = {994, 729, 78};
static const Bit32u MODE_0_COMBS[] = {705 + PROCESS_DELAY, 2349, 2839, 3632};
static const Bit32u MODE_0_OUTL[] = {2349, 141, 1960};
static const Bit32u MODE_0_OUTR[] = {1174, 1570, 145};
static const Bit32u MODE_0_COMB_FACTOR[] = {0x3C, 0x60, 0x60, 0x60};
static const Bit32u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98,
0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98,
0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98};
static const Bit32u MODE_0_LEVELS[] = {10*1, 10*3, 10*5, 10*7, 11*9, 11*12, 11*15, 13*15};
static const Bit32u MODE_0_LPF_AMP = 6;
static const Bit32u MODE_1_ALLPASSES[] = {1324, 809, 176};
static const Bit32u MODE_1_COMBS[] = {961 + PROCESS_DELAY, 2619, 3545, 4519};
static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518};
static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274};
static const Bit32u MODE_1_COMB_FACTOR[] = {0x30, 0x60, 0x60, 0x60};
static const Bit32u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98,
0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98,
0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98};
static const Bit32u MODE_1_LEVELS[] = {10*1, 10*3, 11*5, 11*7, 11*9, 11*12, 11*15, 14*15};
static const Bit32u MODE_1_LPF_AMP = 6;
static const Bit32u MODE_2_ALLPASSES[] = {969, 644, 157};
static const Bit32u MODE_2_COMBS[] = {116 + PROCESS_DELAY, 2259, 2839, 3539};
static const Bit32u MODE_2_OUTL[] = {2259, 718, 1769};
static const Bit32u MODE_2_OUTR[] = {1136, 2128, 1};
static const Bit32u MODE_2_COMB_FACTOR[] = {0, 0x20, 0x20, 0x20};
static const Bit32u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0,
0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0,
0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0};
static const Bit32u MODE_2_LEVELS[] = {10*1, 10*3, 11*5, 11*7, 11*9, 11*12, 12*15, 14*15};
static const Bit32u MODE_2_LPF_AMP = 8;
static const AReverbSettings REVERB_MODE_0_SETTINGS = {MODE_0_ALLPASSES, MODE_0_COMBS, MODE_0_OUTL, MODE_0_OUTR, MODE_0_COMB_FACTOR, MODE_0_COMB_FEEDBACK, MODE_0_LEVELS, MODE_0_LPF_AMP};
static const AReverbSettings REVERB_MODE_1_SETTINGS = {MODE_1_ALLPASSES, MODE_1_COMBS, MODE_1_OUTL, MODE_1_OUTR, MODE_1_COMB_FACTOR, MODE_1_COMB_FEEDBACK, MODE_1_LEVELS, MODE_1_LPF_AMP};
static const AReverbSettings REVERB_MODE_2_SETTINGS = {MODE_2_ALLPASSES, MODE_2_COMBS, MODE_2_OUTL, MODE_2_OUTR, MODE_2_COMB_FACTOR, MODE_2_COMB_FEEDBACK, MODE_2_LEVELS, MODE_2_LPF_AMP};
static const AReverbSettings * const REVERB_SETTINGS[] = {&REVERB_MODE_0_SETTINGS, &REVERB_MODE_1_SETTINGS, &REVERB_MODE_2_SETTINGS, &REVERB_MODE_0_SETTINGS};
RingBuffer::RingBuffer(const Bit32u newsize) : size(newsize), index(0) {
buffer = new float[size];
}
RingBuffer::~RingBuffer() {
delete[] buffer;
buffer = NULL;
}
float RingBuffer::next() {
if (++index >= size) {
index = 0;
}
return buffer[index];
}
bool RingBuffer::isEmpty() const {
if (buffer == NULL) return true;
float *buf = buffer;
float max = 0.001f;
for (Bit32u i = 0; i < size; i++) {
if ((*buf < -max) || (*buf > max)) return false;
buf++;
}
return true;
}
void RingBuffer::mute() {
float *buf = buffer;
for (Bit32u i = 0; i < size; i++) {
*buf++ = 0;
}
}
AllpassFilter::AllpassFilter(const Bit32u useSize) : RingBuffer(useSize) {}
float AllpassFilter::process(const float in) {
// This model corresponds to the allpass filter implementation of the real CM-32L device
// found from sample analysis
const float bufferOut = next();
// store input - feedback / 2
buffer[index] = in - 0.5f * bufferOut;
// return buffer output + feedforward / 2
return bufferOut + 0.5f * buffer[index];
}
CombFilter::CombFilter(const Bit32u useSize) : RingBuffer(useSize) {}
void CombFilter::process(const float in) {
// This model corresponds to the comb filter implementation of the real CM-32L device
// found from sample analysis
// the previously stored value
float last = buffer[index];
// prepare input + feedback
float filterIn = in + next() * feedbackFactor;
// store input + feedback processed by a low-pass filter
buffer[index] = filterFactor * last - filterIn;
}
float CombFilter::getOutputAt(const Bit32u outIndex) const {
return buffer[(size + index - outIndex) % size];
}
void CombFilter::setFeedbackFactor(const float useFeedbackFactor) {
feedbackFactor = useFeedbackFactor;
}
void CombFilter::setFilterFactor(const float useFilterFactor) {
filterFactor = useFilterFactor;
}
AReverbModel::AReverbModel(const ReverbMode mode) : allpasses(NULL), combs(NULL), currentSettings(*REVERB_SETTINGS[mode]) {}
AReverbModel::~AReverbModel() {
close();
}
void AReverbModel::open() {
allpasses = new AllpassFilter*[NUM_ALLPASSES];
for (Bit32u i = 0; i < NUM_ALLPASSES; i++) {
allpasses[i] = new AllpassFilter(currentSettings.allpassSizes[i]);
}
combs = new CombFilter*[NUM_COMBS];
for (Bit32u i = 0; i < NUM_COMBS; i++) {
combs[i] = new CombFilter(currentSettings.combSizes[i]);
combs[i]->setFilterFactor(currentSettings.filterFactor[i] / 256.0f);
}
lpfAmp = currentSettings.lpfAmp / 16.0f;
mute();
}
void AReverbModel::close() {
if (allpasses != NULL) {
for (Bit32u i = 0; i < NUM_ALLPASSES; i++) {
if (allpasses[i] != NULL) {
delete allpasses[i];
allpasses[i] = NULL;
}
}
delete[] allpasses;
allpasses = NULL;
}
if (combs != NULL) {
for (Bit32u i = 0; i < NUM_COMBS; i++) {
if (combs[i] != NULL) {
delete combs[i];
combs[i] = NULL;
}
}
delete[] combs;
combs = NULL;
}
}
void AReverbModel::mute() {
if (allpasses == NULL || combs == NULL) return;
for (Bit32u i = 0; i < NUM_ALLPASSES; i++) {
allpasses[i]->mute();
}
for (Bit32u i = 0; i < NUM_COMBS; i++) {
combs[i]->mute();
}
}
void AReverbModel::setParameters(Bit8u time, Bit8u level) {
// FIXME: wetLevel definitely needs ramping when changed
// Although, most games don't set reverb level during MIDI playback
if (combs == NULL) return;
level &= 7;
time &= 7;
for (Bit32u i = 0; i < NUM_COMBS; i++) {
combs[i]->setFeedbackFactor(currentSettings.decayTimes[(i << 3) + time] / 256.0f);
}
wetLevel = (level == 0 && time == 0) ? 0.0f : 0.5f * lpfAmp * currentSettings.wetLevels[level] / 256.0f;
}
bool AReverbModel::isActive() const {
for (Bit32u i = 0; i < NUM_ALLPASSES; i++) {
if (!allpasses[i]->isEmpty()) return true;
}
for (Bit32u i = 0; i < NUM_COMBS; i++) {
if (!combs[i]->isEmpty()) return true;
}
return false;
}
void AReverbModel::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) {
float dry, link, outL1;
for (unsigned long i = 0; i < numSamples; i++) {
dry = wetLevel * (*inLeft + *inRight);
// Get the last stored sample before processing in order not to loose it
link = combs[0]->getOutputAt(currentSettings.combSizes[0] - 1);
combs[0]->process(-dry);
link = allpasses[0]->process(link);
link = allpasses[1]->process(link);
link = allpasses[2]->process(link);
// If the output position is equal to the comb size, get it now in order not to loose it
outL1 = 1.5f * combs[1]->getOutputAt(currentSettings.outLPositions[0] - 1);
combs[1]->process(link);
combs[2]->process(link);
combs[3]->process(link);
link = outL1 + 1.5f * combs[2]->getOutputAt(currentSettings.outLPositions[1]);
link += combs[3]->getOutputAt(currentSettings.outLPositions[2]);
*outLeft = link;
link = 1.5f * combs[1]->getOutputAt(currentSettings.outRPositions[0]);
link += 1.5f * combs[2]->getOutputAt(currentSettings.outRPositions[1]);
link += combs[3]->getOutputAt(currentSettings.outRPositions[2]);
*outRight = link;
inLeft++;
inRight++;
outLeft++;
outRight++;
}
}
}
#endif

View File

@ -1,87 +0,0 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
* Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MT32EMU_A_REVERB_MODEL_H
#define MT32EMU_A_REVERB_MODEL_H
namespace MT32Emu {
struct AReverbSettings {
const Bit32u * const allpassSizes;
const Bit32u * const combSizes;
const Bit32u * const outLPositions;
const Bit32u * const outRPositions;
const Bit32u * const filterFactor;
const Bit32u * const decayTimes;
const Bit32u * const wetLevels;
const Bit32u lpfAmp;
};
class RingBuffer {
protected:
float *buffer;
const Bit32u size;
Bit32u index;
public:
RingBuffer(const Bit32u size);
virtual ~RingBuffer();
float next();
bool isEmpty() const;
void mute();
};
class AllpassFilter : public RingBuffer {
public:
AllpassFilter(const Bit32u size);
float process(const float in);
};
class CombFilter : public RingBuffer {
float feedbackFactor;
float filterFactor;
public:
CombFilter(const Bit32u size);
void process(const float in);
float getOutputAt(const Bit32u outIndex) const;
void setFeedbackFactor(const float useFeedbackFactor);
void setFilterFactor(const float useFilterFactor);
};
class AReverbModel : public ReverbModel {
AllpassFilter **allpasses;
CombFilter **combs;
const AReverbSettings &currentSettings;
float lpfAmp;
float wetLevel;
void mute();
public:
AReverbModel(const ReverbMode mode);
~AReverbModel();
void open();
void close();
void setParameters(Bit8u time, Bit8u level);
void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples);
bool isActive() const;
};
}
#endif

View File

@ -15,10 +15,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//#include <memory.h>
#include "mt32emu.h"
#if MT32EMU_USE_REVERBMODEL == 2
#include "BReverbModel.h"
// Analysing of state of reverb RAM address lines gives exact sizes of the buffers of filters used. This also indicates that
@ -62,9 +60,9 @@ static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518};
static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274};
static const Bit32u MODE_1_COMB_FACTOR[] = {0x80, 0x60, 0x60, 0x60};
static const Bit32u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98,
0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98,
0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98};
0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98,
0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98,
0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98};
static const Bit32u MODE_1_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xE0};
static const Bit32u MODE_1_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0};
static const Bit32u MODE_1_LPF_AMP = 0x60;
@ -103,7 +101,11 @@ static const BReverbSettings * const REVERB_SETTINGS[] = {&REVERB_MODE_0_SETTING
// This algorithm tries to emulate exactly Boss multiplication operation (at least this is what we see on reverb RAM data lines).
// Also LA32 is suspected to use the similar one to perform PCM interpolation and ring modulation.
static Bit32s weirdMul(Bit32s a, Bit8u addMask, Bit8u carryMask) {
static Sample weirdMul(Sample a, Bit8u addMask, Bit8u carryMask) {
(void)carryMask;
#if MT32EMU_USE_FLOAT_SAMPLES
return a * addMask / 256.0f;
#elif MT32EMU_BOSS_REVERB_PRECISE_MODE
Bit8u mask = 0x80;
Bit32s res = 0;
for (int i = 0; i < 8; i++) {
@ -113,10 +115,13 @@ static Bit32s weirdMul(Bit32s a, Bit8u addMask, Bit8u carryMask) {
mask >>= 1;
}
return res;
#else
return Sample(((Bit32s)a * addMask) >> 8);
#endif
}
RingBuffer::RingBuffer(Bit32u newsize) : size(newsize), index(0) {
buffer = new Bit16s[size];
buffer = new Sample[size];
}
RingBuffer::~RingBuffer() {
@ -124,7 +129,7 @@ RingBuffer::~RingBuffer() {
buffer = NULL;
}
Bit32s RingBuffer::next() {
Sample RingBuffer::next() {
if (++index >= size) {
index = 0;
}
@ -134,52 +139,69 @@ Bit32s RingBuffer::next() {
bool RingBuffer::isEmpty() const {
if (buffer == NULL) return true;
Bit16s *buf = buffer;
#if MT32EMU_USE_FLOAT_SAMPLES
Sample max = 0.001f;
#else
Sample max = 8;
#endif
Sample *buf = buffer;
for (Bit32u i = 0; i < size; i++) {
if (*buf < -8 || *buf > 8) return false;
if (*buf < -max || *buf > max) return false;
buf++;
}
return true;
}
void RingBuffer::mute() {
Bit16s *buf = buffer;
#if MT32EMU_USE_FLOAT_SAMPLES
Sample *buf = buffer;
for (Bit32u i = 0; i < size; i++) {
*buf++ = 0;
}
#else
memset(buffer, 0, size * sizeof(Sample));
#endif
}
AllpassFilter::AllpassFilter(const Bit32u useSize) : RingBuffer(useSize) {}
Bit32s AllpassFilter::process(const Bit32s in) {
Sample AllpassFilter::process(const Sample in) {
// This model corresponds to the allpass filter implementation of the real CM-32L device
// found from sample analysis
Bit16s bufferOut = next();
const Sample bufferOut = next();
#if MT32EMU_USE_FLOAT_SAMPLES
// store input - feedback / 2
buffer[index] = in - 0.5f * bufferOut;
// return buffer output + feedforward / 2
return bufferOut + 0.5f * buffer[index];
#else
// store input - feedback / 2
buffer[index] = in - (bufferOut >> 1);
// return buffer output + feedforward / 2
return bufferOut + (buffer[index] >> 1);
#endif
}
CombFilter::CombFilter(const Bit32u useSize, const Bit32u useFilterFactor) : RingBuffer(useSize), filterFactor(useFilterFactor) {}
void CombFilter::process(const Bit32s in) {
void CombFilter::process(const Sample in) {
// This model corresponds to the comb filter implementation of the real CM-32L device
// the previously stored value
Bit32s last = buffer[index];
const Sample last = buffer[index];
// prepare input + feedback
Bit32s filterIn = in + weirdMul(next(), feedbackFactor, 0xF0 /* Maybe 0x80 ? */);
const Sample filterIn = in + weirdMul(next(), feedbackFactor, 0xF0 /* Maybe 0x80 ? */);
// store input + feedback processed by a low-pass filter
buffer[index] = weirdMul(last, filterFactor, 0x40) - filterIn;
}
Bit32s CombFilter::getOutputAt(const Bit32u outIndex) const {
Sample CombFilter::getOutputAt(const Bit32u outIndex) const {
return buffer[(size + index - outIndex) % size];
}
@ -190,15 +212,15 @@ void CombFilter::setFeedbackFactor(const Bit32u useFeedbackFactor) {
DelayWithLowPassFilter::DelayWithLowPassFilter(const Bit32u useSize, const Bit32u useFilterFactor, const Bit32u useAmp)
: CombFilter(useSize, useFilterFactor), amp(useAmp) {}
void DelayWithLowPassFilter::process(const Bit32s in) {
void DelayWithLowPassFilter::process(const Sample in) {
// the previously stored value
Bit32s last = buffer[index];
const Sample last = buffer[index];
// move to the next index
next();
// low-pass filter process
Bit32s lpfOut = weirdMul(last, filterFactor, 0xFF) + in;
Sample lpfOut = weirdMul(last, filterFactor, 0xFF) + in;
// store lpfOut multiplied by LPF amp factor
buffer[index] = weirdMul(lpfOut, amp, 0xFF);
@ -206,26 +228,26 @@ void DelayWithLowPassFilter::process(const Bit32s in) {
TapDelayCombFilter::TapDelayCombFilter(const Bit32u useSize, const Bit32u useFilterFactor) : CombFilter(useSize, useFilterFactor) {}
void TapDelayCombFilter::process(const Bit32s in) {
void TapDelayCombFilter::process(const Sample in) {
// the previously stored value
Bit32s last = buffer[index];
const Sample last = buffer[index];
// move to the next index
next();
// prepare input + feedback
// Actually, the size of the filter varies with the TIME parameter, the feedback sample is taken from the position just below the right output
Bit32s filterIn = in + weirdMul(getOutputAt(outR + MODE_3_FEEDBACK_DELAY), feedbackFactor, 0xF0);
const Sample filterIn = in + weirdMul(getOutputAt(outR + MODE_3_FEEDBACK_DELAY), feedbackFactor, 0xF0);
// store input + feedback processed by a low-pass filter
buffer[index] = weirdMul(last, filterFactor, 0xF0) - filterIn;
}
Bit32s TapDelayCombFilter::getLeftOutput() const {
Sample TapDelayCombFilter::getLeftOutput() const {
return getOutputAt(outL + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY);
}
Bit32s TapDelayCombFilter::getRightOutput() const {
Sample TapDelayCombFilter::getRightOutput() const {
return getOutputAt(outR + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY);
}
@ -327,14 +349,14 @@ bool BReverbModel::isActive() const {
return false;
}
void BReverbModel::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) {
Bit32s dry, link, outL1, outR1;
void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample *outLeft, Sample *outRight, unsigned long numSamples) {
Sample dry;
for (unsigned long i = 0; i < numSamples; i++) {
while (numSamples > 0) {
if (tapDelayMode) {
dry = Bit32s(*inLeft * 8192.0f) + Bit32s(*inRight * 8192.0f);
dry = *inLeft + *inRight;
} else {
dry = Bit32s(*inLeft * 8192.0f) / 2 + Bit32s(*inRight * 8192.0f) / 2;
dry = *inLeft / 2 + *inRight / 2;
}
// Looks like dryAmp doesn't change in MT-32 but it does in CM-32L / LAPC-I
@ -343,44 +365,53 @@ void BReverbModel::process(const float *inLeft, const float *inRight, float *out
if (tapDelayMode) {
TapDelayCombFilter *comb = static_cast<TapDelayCombFilter *> (*combs);
comb->process(dry);
*outLeft = weirdMul(comb->getLeftOutput(), wetLevel, 0xFF) / 8192.0f;
*outRight = weirdMul(comb->getRightOutput(), wetLevel, 0xFF) / 8192.0f;
*outLeft = weirdMul(comb->getLeftOutput(), wetLevel, 0xFF);
*outRight = weirdMul(comb->getRightOutput(), wetLevel, 0xFF);
} else {
// Get the last stored sample before processing in order not to loose it
link = combs[0]->getOutputAt(currentSettings.combSizes[0] - 1);
// If the output position is equal to the comb size, get it now in order not to loose it
Sample link = combs[0]->getOutputAt(currentSettings.combSizes[0] - 1);
// Entrance LPF. Note, comb.process() differs a bit here.
combs[0]->process(dry);
#if !MT32EMU_USE_FLOAT_SAMPLES
// This introduces reverb noise which actually makes output from the real Boss chip nondeterministic
link = link - 1;
#endif
link = allpasses[0]->process(link);
link = allpasses[1]->process(link);
link = allpasses[2]->process(link);
// If the output position is equal to the comb size, get it now in order not to loose it
outL1 = combs[1]->getOutputAt(currentSettings.outLPositions[0] - 1);
outL1 += outL1 >> 1;
Sample outL1 = combs[1]->getOutputAt(currentSettings.outLPositions[0] - 1);
combs[1]->process(link);
combs[2]->process(link);
combs[3]->process(link);
link = combs[2]->getOutputAt(currentSettings.outLPositions[1]);
link += link >> 1;
link += outL1;
link += combs[3]->getOutputAt(currentSettings.outLPositions[2]);
*outLeft = weirdMul(link, wetLevel, 0xFF) / 8192.0f;
Sample outL2 = combs[2]->getOutputAt(currentSettings.outLPositions[1]);
Sample outL3 = combs[3]->getOutputAt(currentSettings.outLPositions[2]);
Sample outR1 = combs[1]->getOutputAt(currentSettings.outRPositions[0]);
Sample outR2 = combs[2]->getOutputAt(currentSettings.outRPositions[1]);
Sample outR3 = combs[3]->getOutputAt(currentSettings.outRPositions[2]);
#if MT32EMU_USE_FLOAT_SAMPLES
*outLeft = 1.5f * (outL1 + outL2) + outL3;
*outRight = 1.5f * (outR1 + outR2) + outR3;
#else
outL1 += outL1 >> 1;
outL2 += outL2 >> 1;
*outLeft = outL1 + outL2 + outL3;
outR1 = combs[1]->getOutputAt(currentSettings.outRPositions[0]);
outR1 += outR1 >> 1;
link = combs[2]->getOutputAt(currentSettings.outRPositions[1]);
link += link >> 1;
link += outR1;
link += combs[3]->getOutputAt(currentSettings.outRPositions[2]);
*outRight = weirdMul(link, wetLevel, 0xFF) / 8192.0f;
outR2 += outR2 >> 1;
*outRight = outR1 + outR2 + outR3;
#endif
*outLeft = weirdMul(*outLeft, wetLevel, 0xFF);
*outRight = weirdMul(*outRight, wetLevel, 0xFF);
}
numSamples--;
inLeft++;
inRight++;
outLeft++;
@ -389,5 +420,3 @@ void BReverbModel::process(const float *inLeft, const float *inRight, float *out
}
}
#endif

View File

@ -36,14 +36,14 @@ struct BReverbSettings {
class RingBuffer {
protected:
Bit16s *buffer;
Sample *buffer;
const Bit32u size;
Bit32u index;
public:
RingBuffer(const Bit32u size);
virtual ~RingBuffer();
Bit32s next();
Sample next();
bool isEmpty() const;
void mute();
};
@ -51,7 +51,7 @@ public:
class AllpassFilter : public RingBuffer {
public:
AllpassFilter(const Bit32u size);
Bit32s process(const Bit32s in);
Sample process(const Sample in);
};
class CombFilter : public RingBuffer {
@ -61,8 +61,8 @@ protected:
public:
CombFilter(const Bit32u size, const Bit32u useFilterFactor);
virtual void process(const Bit32s in); // Actually, no need to make it virtual, but for sure
Bit32s getOutputAt(const Bit32u outIndex) const;
virtual void process(const Sample in);
Sample getOutputAt(const Bit32u outIndex) const;
void setFeedbackFactor(const Bit32u useFeedbackFactor);
};
@ -71,7 +71,7 @@ class DelayWithLowPassFilter : public CombFilter {
public:
DelayWithLowPassFilter(const Bit32u useSize, const Bit32u useFilterFactor, const Bit32u useAmp);
void process(const Bit32s in);
void process(const Sample in);
void setFeedbackFactor(const Bit32u) {}
};
@ -81,13 +81,13 @@ class TapDelayCombFilter : public CombFilter {
public:
TapDelayCombFilter(const Bit32u useSize, const Bit32u useFilterFactor);
void process(const Bit32s in);
Bit32s getLeftOutput() const;
Bit32s getRightOutput() const;
void process(const Sample in);
Sample getLeftOutput() const;
Sample getRightOutput() const;
void setOutputPositions(const Bit32u useOutL, const Bit32u useOutR);
};
class BReverbModel : public ReverbModel {
class BReverbModel {
AllpassFilter **allpasses;
CombFilter **combs;
@ -100,10 +100,12 @@ class BReverbModel : public ReverbModel {
public:
BReverbModel(const ReverbMode mode);
~BReverbModel();
// After construction or a close(), open() must be called at least once before any other call (with the exception of close()).
void open();
// May be called multiple times without an open() in between.
void close();
void setParameters(Bit8u time, Bit8u level);
void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples);
void process(const Sample *inLeft, const Sample *inRight, Sample *outLeft, Sample *outRight, unsigned long numSamples);
bool isActive() const;
};

View File

@ -1,142 +0,0 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
* Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//#include <cmath>
//#include <cstring>
#include "mt32emu.h"
#include "DelayReverb.h"
namespace MT32Emu {
// CONFIRMED: The values below are found via analysis of digital samples and tracing reverb RAM address / data lines. Checked with all time and level combinations.
// Obviously:
// rightDelay = (leftDelay - 2) * 2 + 2
// echoDelay = rightDelay - 1
// Leaving these separate in case it's useful for work on other reverb modes...
static const Bit32u REVERB_TIMINGS[8][3]= {
// {leftDelay, rightDelay, feedbackDelay}
{402, 802, 801},
{626, 1250, 1249},
{962, 1922, 1921},
{1490, 2978, 2977},
{2258, 4514, 4513},
{3474, 6946, 6945},
{5282, 10562, 10561},
{8002, 16002, 16001}
};
// Reverb amp is found as dryAmp * wetAmp
static const Bit32u REVERB_AMP[8] = {0x20*0x18, 0x50*0x18, 0x50*0x28, 0x50*0x40, 0x50*0x60, 0x50*0x80, 0x50*0xA8, 0x50*0xF8};
static const Bit32u REVERB_FEEDBACK67 = 0x60;
static const Bit32u REVERB_FEEDBACK = 0x68;
static const float LPF_VALUE = 0x68 / 256.0f;
static const Bit32u BUFFER_SIZE = 16384;
DelayReverb::DelayReverb() {
buf = NULL;
setParameters(0, 0);
}
DelayReverb::~DelayReverb() {
delete[] buf;
}
void DelayReverb::open() {
if (buf == NULL) {
delete[] buf;
buf = new float[BUFFER_SIZE];
recalcParameters();
// mute buffer
bufIx = 0;
if (buf != NULL) {
for (unsigned int i = 0; i < BUFFER_SIZE; i++) {
buf[i] = 0.0f;
}
}
}
}
void DelayReverb::close() {
delete[] buf;
buf = NULL;
}
// This method will always trigger a flush of the buffer
void DelayReverb::setParameters(Bit8u newTime, Bit8u newLevel) {
time = newTime;
level = newLevel;
recalcParameters();
}
void DelayReverb::recalcParameters() {
// Number of samples between impulse and eventual appearance on the left channel
delayLeft = REVERB_TIMINGS[time][0];
// Number of samples between impulse and eventual appearance on the right channel
delayRight = REVERB_TIMINGS[time][1];
// Number of samples between a response and that response feeding back/echoing
delayFeedback = REVERB_TIMINGS[time][2];
if (level < 3 || time < 6) {
feedback = REVERB_FEEDBACK / 256.0f;
} else {
feedback = REVERB_FEEDBACK67 / 256.0f;
}
// Overall output amp
amp = (level == 0 && time == 0) ? 0.0f : REVERB_AMP[level] / 65536.0f;
}
void DelayReverb::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) {
if (buf == NULL) return;
for (unsigned int sampleIx = 0; sampleIx < numSamples; sampleIx++) {
// The ring buffer write index moves backwards; reads are all done with positive offsets.
Bit32u bufIxPrev = (bufIx + 1) % BUFFER_SIZE;
Bit32u bufIxLeft = (bufIx + delayLeft) % BUFFER_SIZE;
Bit32u bufIxRight = (bufIx + delayRight) % BUFFER_SIZE;
Bit32u bufIxFeedback = (bufIx + delayFeedback) % BUFFER_SIZE;
// Attenuated input samples and feedback response are directly added to the current ring buffer location
float lpfIn = amp * (inLeft[sampleIx] + inRight[sampleIx]) + feedback * buf[bufIxFeedback];
// Single-pole IIR filter found on real devices
buf[bufIx] = buf[bufIxPrev] * LPF_VALUE - lpfIn;
outLeft[sampleIx] = buf[bufIxLeft];
outRight[sampleIx] = buf[bufIxRight];
bufIx = (BUFFER_SIZE + bufIx - 1) % BUFFER_SIZE;
}
}
bool DelayReverb::isActive() const {
if (buf == NULL) return false;
float *b = buf;
float max = 0.001f;
for (Bit32u i = 0; i < BUFFER_SIZE; i++) {
if ((*b < -max) || (*b > max)) return true;
b++;
}
return false;
}
}

View File

@ -1,50 +0,0 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
* Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MT32EMU_DELAYREVERB_H
#define MT32EMU_DELAYREVERB_H
namespace MT32Emu {
class DelayReverb : public ReverbModel {
private:
Bit8u time;
Bit8u level;
Bit32u bufIx;
float *buf;
Bit32u delayLeft;
Bit32u delayRight;
Bit32u delayFeedback;
float amp;
float feedback;
void recalcParameters();
public:
DelayReverb();
~DelayReverb();
void open();
void close();
void setParameters(Bit8u time, Bit8u level);
void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples);
bool isActive() const;
};
}
#endif

View File

@ -1,78 +0,0 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
* Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mt32emu.h"
#include "FreeverbModel.h"
#include "freeverb.h"
namespace MT32Emu {
FreeverbModel::FreeverbModel(float useScaleTuning, float useFiltVal, float useWet, Bit8u useRoom, float useDamp) {
freeverb = NULL;
scaleTuning = useScaleTuning;
filtVal = useFiltVal;
wet = useWet;
room = useRoom;
damp = useDamp;
}
FreeverbModel::~FreeverbModel() {
delete freeverb;
}
void FreeverbModel::open() {
if (freeverb == NULL) {
freeverb = new revmodel(scaleTuning);
}
freeverb->mute();
// entrance Lowpass filter factor
freeverb->setfiltval(filtVal);
// decay speed of high frequencies in the wet signal
freeverb->setdamp(damp);
}
void FreeverbModel::close() {
delete freeverb;
freeverb = NULL;
}
void FreeverbModel::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) {
freeverb->process(inLeft, inRight, outLeft, outRight, numSamples);
}
void FreeverbModel::setParameters(Bit8u time, Bit8u level) {
// wet signal level
// FIXME: need to implement some sort of reverb level ramping
freeverb->setwet((float)level / 7.0f * wet);
// wet signal decay speed
static float roomTable[] = {
0.25f, 0.37f, 0.54f, 0.71f, 0.78f, 0.86f, 0.93f, 1.00f,
-1.00f, -0.50f, 0.00f, 0.30f, 0.51f, 0.64f, 0.77f, 0.90f,
0.50f, 0.57f, 0.70f, 0.77f, 0.85f, 0.93f, 0.96f, 1.01f};
freeverb->setroomsize(roomTable[8 * room + time]);
}
bool FreeverbModel::isActive() const {
// FIXME: Not bothering to do this properly since we'll be replacing Freeverb soon...
return false;
}
}

View File

@ -1,44 +0,0 @@
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
* Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MT32EMU_FREEVERB_MODEL_H
#define MT32EMU_FREEVERB_MODEL_H
class revmodel;
namespace MT32Emu {
class FreeverbModel : public ReverbModel {
revmodel *freeverb;
float scaleTuning;
float filtVal;
float wet;
Bit8u room;
float damp;
public:
FreeverbModel(float useScaleTuning, float useFiltVal, float useWet, Bit8u useRoom, float useDamp);
~FreeverbModel();
void open();
void close();
void setParameters(Bit8u time, Bit8u level);
void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples);
bool isActive() const;
};
}
#endif

View File

@ -18,9 +18,7 @@
//#include <cmath>
#include "mt32emu.h"
#include "mmath.h"
#include "LegacyWaveGenerator.h"
#if MT32EMU_ACCURATE_WG == 1
#include "LA32FloatWaveGenerator.h"
namespace MT32Emu {
@ -317,15 +315,29 @@ void LA32PartialPair::generateNextSample(const PairType useMaster, const Bit32u
}
}
Bit16s LA32PartialPair::nextOutSample() {
float outputSample;
if (ringModulated) {
float ringModulatedSample = masterOutputSample * slaveOutputSample;
outputSample = mixed ? masterOutputSample + ringModulatedSample : ringModulatedSample;
} else {
outputSample = masterOutputSample + slaveOutputSample;
static inline float produceDistortedSample(float sample) {
if (sample < -1.0f) {
return sample + 2.0f;
} else if (1.0f < sample) {
return sample - 2.0f;
}
return Bit16s(outputSample * 8192.0f);
return sample;
}
float LA32PartialPair::nextOutSample() {
if (!ringModulated) {
return masterOutputSample + slaveOutputSample;
}
/*
* SEMI-CONFIRMED: Ring modulation model derived from sample analysis of specially constructed patches which exploit distortion.
* LA32 ring modulator found to produce distorted output in case if the absolute value of maximal amplitude of one of the input partials exceeds 8191.
* This is easy to reproduce using synth partials with resonance values close to the maximum. It looks like an integer overflow happens in this case.
* As the distortion is strictly bound to the amplitude of the complete mixed square + resonance wave in the linear space,
* it is reasonable to assume the ring modulation is performed also in the linear space by sample multiplication.
* Most probably the overflow is caused by limited precision of the multiplication circuit as the very similar distortion occurs with panning.
*/
float ringModulatedSample = produceDistortedSample(masterOutputSample) * produceDistortedSample(slaveOutputSample);
return mixed ? masterOutputSample + ringModulatedSample : ringModulatedSample;
}
void LA32PartialPair::deactivate(const PairType useMaster) {
@ -343,5 +355,3 @@ bool LA32PartialPair::isActive(const PairType useMaster) const {
}
}
#endif // #if MT32EMU_ACCURATE_WG == 1

View File

@ -15,8 +15,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if MT32EMU_ACCURATE_WG == 1
#ifndef MT32EMU_LA32_WAVE_GENERATOR_H
#define MT32EMU_LA32_WAVE_GENERATOR_H
@ -130,7 +128,7 @@ public:
void generateNextSample(const PairType master, const Bit32u amp, const Bit16u pitch, const Bit32u cutoff);
// Perform mixing / ring modulation and return the result
Bit16s nextOutSample();
float nextOutSample();
// Deactivate the WG engine
void deactivate(const PairType master);
@ -142,5 +140,3 @@ public:
} // namespace MT32Emu
#endif // #ifndef MT32EMU_LA32_WAVE_GENERATOR_H
#endif // #if MT32EMU_ACCURATE_WG == 1

View File

@ -20,7 +20,9 @@
#include "mmath.h"
#include "LA32WaveGenerator.h"
#if MT32EMU_ACCURATE_WG == 0
#if MT32EMU_USE_FLOAT_SAMPLES
#include "LA32FloatWaveGenerator.cpp"
#else
namespace MT32Emu {
@ -129,7 +131,8 @@ void LA32WaveGenerator::advancePosition() {
computePositions(highLinearLength, lowLinearLength, resonanceWaveLengthFactor);
// resonancePhase computation hack
*(int*)&resonancePhase = ((resonanceSinePosition >> 18) + (phase > POSITIVE_FALLING_SINE_SEGMENT ? 2 : 0)) & 3;
int *resonancePhaseAlias = (int *)&resonancePhase;
*resonancePhaseAlias = ((resonanceSinePosition >> 18) + (phase > POSITIVE_FALLING_SINE_SEGMENT ? 2 : 0)) & 3;
}
void LA32WaveGenerator::generateNextSquareWaveLogSample() {
@ -367,18 +370,12 @@ void LA32PartialPair::generateNextSample(const PairType useMaster, const Bit32u
}
}
Bit16s LA32PartialPair::unlogAndMixWGOutput(const LA32WaveGenerator &wg, const LogSample * const ringModulatingLogSample) {
if (!wg.isActive() || ((ringModulatingLogSample != NULL) && (ringModulatingLogSample->logValue == SILENCE.logValue))) {
Bit16s LA32PartialPair::unlogAndMixWGOutput(const LA32WaveGenerator &wg) {
if (!wg.isActive()) {
return 0;
}
LogSample firstLogSample = wg.getOutputLogSample(true);
LogSample secondLogSample = wg.getOutputLogSample(false);
if (ringModulatingLogSample != NULL) {
LA32Utilites::addLogSamples(firstLogSample, *ringModulatingLogSample);
LA32Utilites::addLogSamples(secondLogSample, *ringModulatingLogSample);
}
Bit16s firstSample = LA32Utilites::unlog(firstLogSample);
Bit16s secondSample = LA32Utilites::unlog(secondLogSample);
Bit16s firstSample = LA32Utilites::unlog(wg.getOutputLogSample(true));
Bit16s secondSample = LA32Utilites::unlog(wg.getOutputLogSample(false));
if (wg.isPCMWave()) {
return Bit16s(firstSample + ((Bit32s(secondSample - firstSample) * wg.getPCMInterpolationFactor()) >> 7));
}
@ -386,19 +383,32 @@ Bit16s LA32PartialPair::unlogAndMixWGOutput(const LA32WaveGenerator &wg, const L
}
Bit16s LA32PartialPair::nextOutSample() {
if (ringModulated) {
LogSample slaveFirstLogSample = slave.getOutputLogSample(true);
LogSample slaveSecondLogSample = slave.getOutputLogSample(false);
Bit16s sample = unlogAndMixWGOutput(master, &slaveFirstLogSample);
if (!slave.isPCMWave()) {
sample += unlogAndMixWGOutput(master, &slaveSecondLogSample);
}
if (mixed) {
sample += unlogAndMixWGOutput(master, NULL);
}
return sample;
if (!ringModulated) {
return unlogAndMixWGOutput(master) + unlogAndMixWGOutput(slave);
}
return unlogAndMixWGOutput(master, NULL) + unlogAndMixWGOutput(slave, NULL);
/*
* SEMI-CONFIRMED: Ring modulation model derived from sample analysis of specially constructed patches which exploit distortion.
* LA32 ring modulator found to produce distorted output in case if the absolute value of maximal amplitude of one of the input partials exceeds 8191.
* This is easy to reproduce using synth partials with resonance values close to the maximum. It looks like an integer overflow happens in this case.
* As the distortion is strictly bound to the amplitude of the complete mixed square + resonance wave in the linear space,
* it is reasonable to assume the ring modulation is performed also in the linear space by sample multiplication.
* Most probably the overflow is caused by limited precision of the multiplication circuit as the very similar distortion occurs with panning.
*/
Bit16s nonOverdrivenMasterSample = unlogAndMixWGOutput(master); // Store master partial sample for further mixing
Bit16s masterSample = nonOverdrivenMasterSample << 2;
masterSample >>= 2;
/* SEMI-CONFIRMED from sample analysis:
* We observe that for partial structures with ring modulation the interpolation is not applied to the slave PCM partial.
* It's assumed that the multiplication circuitry intended to perform the interpolation on the slave PCM partial
* is borrowed by the ring modulation circuit (or the LA32 chip has a similar lack of resources assigned to each partial pair).
*/
Bit16s slaveSample = slave.isPCMWave() ? LA32Utilites::unlog(slave.getOutputLogSample(true)) : unlogAndMixWGOutput(slave);
slaveSample <<= 2;
slaveSample >>= 2;
Bit16s ringModulatedSample = Bit16s(((Bit32s)masterSample * (Bit32s)slaveSample) >> 13);
return mixed ? nonOverdrivenMasterSample + ringModulatedSample : ringModulatedSample;
}
void LA32PartialPair::deactivate(const PairType useMaster) {
@ -415,4 +425,4 @@ bool LA32PartialPair::isActive(const PairType useMaster) const {
}
#endif // #if MT32EMU_ACCURATE_WG == 0
#endif // #if MT32EMU_USE_FLOAT_SAMPLES

View File

@ -15,7 +15,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if MT32EMU_ACCURATE_WG == 0
#if MT32EMU_USE_FLOAT_SAMPLES
#include "LA32FloatWaveGenerator.h"
#else
#ifndef MT32EMU_LA32_WAVE_GENERATOR_H
#define MT32EMU_LA32_WAVE_GENERATOR_H
@ -207,7 +209,7 @@ class LA32PartialPair {
bool ringModulated;
bool mixed;
static Bit16s unlogAndMixWGOutput(const LA32WaveGenerator &wg, const LogSample * const ringModulatingLogSample);
static Bit16s unlogAndMixWGOutput(const LA32WaveGenerator &wg);
public:
enum PairType {
@ -243,4 +245,4 @@ public:
#endif // #ifndef MT32EMU_LA32_WAVE_GENERATOR_H
#endif // #if MT32EMU_ACCURATE_WG == 0
#endif // #if MT32EMU_USE_FLOAT_SAMPLES

View File

@ -67,18 +67,12 @@ Part::Part(Synth *useSynth, unsigned int usePartNum) {
pitchBend = 0;
activePartialCount = 0;
memset(patchCache, 0, sizeof(patchCache));
for (int i = 0; i < MT32EMU_MAX_POLY; i++) {
freePolys.prepend(new Poly(this));
}
}
Part::~Part() {
while (!activePolys.isEmpty()) {
delete activePolys.takeFirst();
}
while (!freePolys.isEmpty()) {
delete freePolys.takeFirst();
}
}
void Part::setDataEntryMSB(unsigned char midiDataEntryMSB) {
@ -175,6 +169,7 @@ void Part::refresh() {
patchCache[t].reverb = patchTemp->patch.reverbSwitch > 0;
}
memcpy(currentInstr, timbreTemp->common.name, 10);
synth->newTimbreSet(partNum, patchTemp->patch.timbreGroup, currentInstr);
updatePitchBenderRange();
}
@ -207,7 +202,6 @@ void RhythmPart::setTimbre(TimbreParam * /*timbre*/) {
void Part::setTimbre(TimbreParam *timbre) {
*timbreTemp = *timbre;
synth->newTimbreSet(partNum, timbre->common.name);
}
unsigned int RhythmPart::getAbsTimbreNum() const {
@ -431,23 +425,10 @@ void Part::noteOn(unsigned int midiKey, unsigned int velocity) {
playPoly(patchCache, NULL, midiKey, key, velocity);
}
void Part::abortPoly(Poly *poly) {
if (poly->startAbort()) {
while (poly->isActive()) {
if (!synth->prerender()) {
synth->printDebug("%s (%s): Ran out of prerender space to abort poly gracefully", name, currentInstr);
poly->terminate();
break;
}
}
}
}
bool Part::abortFirstPoly(unsigned int key) {
for (Poly *poly = activePolys.getFirst(); poly != NULL; poly = poly->getNext()) {
if (poly->getKey() == key) {
abortPoly(poly);
return true;
return poly->startAbort();
}
}
return false;
@ -456,8 +437,7 @@ bool Part::abortFirstPoly(unsigned int key) {
bool Part::abortFirstPoly(PolyState polyState) {
for (Poly *poly = activePolys.getFirst(); poly != NULL; poly = poly->getNext()) {
if (poly->getState() == polyState) {
abortPoly(poly);
return true;
return poly->startAbort();
}
}
return false;
@ -474,8 +454,7 @@ bool Part::abortFirstPoly() {
if (activePolys.isEmpty()) {
return false;
}
abortPoly(activePolys.getFirst());
return true;
return activePolys.getFirst()->startAbort();
}
void Part::playPoly(const PatchCache cache[4], const MemParams::RhythmTemp *rhythmTemp, unsigned int midiKey, unsigned int key, unsigned int velocity) {
@ -489,6 +468,7 @@ void Part::playPoly(const PatchCache cache[4], const MemParams::RhythmTemp *rhyt
if ((patchTemp->patch.assignMode & 2) == 0) {
// Single-assign mode
abortFirstPoly(key);
if (synth->isAbortingPoly()) return;
}
if (!synth->partialManager->freePartials(needPartials, partNum)) {
@ -498,12 +478,13 @@ void Part::playPoly(const PatchCache cache[4], const MemParams::RhythmTemp *rhyt
#endif
return;
}
if (synth->isAbortingPoly()) return;
if (freePolys.isEmpty()) {
Poly *poly = synth->partialManager->assignPolyToPart(this);
if (poly == NULL) {
synth->printDebug("%s (%s): No free poly to play key %d (velocity %d)", name, currentInstr, midiKey, velocity);
return;
}
Poly *poly = freePolys.takeFirst();
if (patchTemp->patch.assignMode & 1) {
// Priority to data first received
activePolys.prepend(poly);
@ -533,7 +514,6 @@ void Part::playPoly(const PatchCache cache[4], const MemParams::RhythmTemp *rhyt
#if MT32EMU_MONITOR_PARTIALS > 1
synth->printPartialUsage();
#endif
synth->partStateChanged(partNum, true);
synth->polyStateChanged(partNum);
}
@ -597,6 +577,10 @@ unsigned int Part::getActivePartialCount() const {
return activePartialCount;
}
const Poly *Part::getFirstActivePoly() const {
return activePolys.getFirst();
}
unsigned int Part::getActiveNonReleasingPartialCount() const {
unsigned int activeNonReleasingPartialCount = 0;
for (Poly *poly = activePolys.getFirst(); poly != NULL; poly = poly->getNext()) {
@ -607,16 +591,17 @@ unsigned int Part::getActiveNonReleasingPartialCount() const {
return activeNonReleasingPartialCount;
}
Synth *Part::getSynth() const {
return synth;
}
void Part::partialDeactivated(Poly *poly) {
activePartialCount--;
if (!poly->isActive()) {
activePolys.remove(poly);
freePolys.prepend(poly);
synth->partialManager->polyFreed(poly);
synth->polyStateChanged(partNum);
}
if (activePartialCount == 0) {
synth->partStateChanged(partNum, false);
}
}
//#define POLY_LIST_DEBUG

View File

@ -51,13 +51,11 @@ private:
unsigned int activePartialCount;
PatchCache patchCache[4];
PolyList freePolys;
PolyList activePolys;
void setPatch(const PatchParam *patch);
unsigned int midiKeyToKey(unsigned int midiKey);
void abortPoly(Poly *poly);
bool abortFirstPoly(unsigned int key);
protected:
@ -110,8 +108,10 @@ public:
virtual void setTimbre(TimbreParam *timbre);
virtual unsigned int getAbsTimbreNum() const;
const char *getCurrentInstr() const;
const Poly *getFirstActivePoly() const;
unsigned int getActivePartialCount() const;
unsigned int getActiveNonReleasingPartialCount() const;
Synth *getSynth() const;
const MemParams::PatchTemp *getPatchTemp() const;

View File

@ -24,15 +24,10 @@
namespace MT32Emu {
#ifdef INACCURATE_SMOOTH_PAN
// Mok wanted an option for smoother panning, and we love Mok.
static const float PAN_NUMERATOR_NORMAL[] = {0.0f, 0.5f, 1.0f, 1.5f, 2.0f, 2.5f, 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, 5.5f, 6.0f, 6.5f, 7.0f};
#else
// CONFIRMED by Mok: These NUMERATOR values (as bytes, not floats, obviously) are sent exactly like this to the LA32.
static const float PAN_NUMERATOR_NORMAL[] = {0.0f, 0.0f, 1.0f, 1.0f, 2.0f, 2.0f, 3.0f, 3.0f, 4.0f, 4.0f, 5.0f, 5.0f, 6.0f, 6.0f, 7.0f};
#endif
static const float PAN_NUMERATOR_MASTER[] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f};
static const float PAN_NUMERATOR_SLAVE[] = {0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 7.0f, 7.0f, 7.0f, 7.0f, 7.0f, 7.0f, 7.0f};
static const Bit8u PAN_NUMERATOR_MASTER[] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7};
static const Bit8u PAN_NUMERATOR_SLAVE[] = {0, 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7};
static const Bit32s PAN_FACTORS[] = {0, 18, 37, 55, 73, 91, 110, 128, 146, 165, 183, 201, 219, 238, 256};
Partial::Partial(Synth *useSynth, int useDebugPartialNum) :
synth(useSynth), debugPartialNum(useDebugPartialNum), sampleNum(0) {
@ -87,7 +82,6 @@ void Partial::deactivate() {
if (poly != NULL) {
poly->partialDeactivated(this);
}
synth->partialStateChanged(this, tva->getPhase(), TVA_PHASE_DEAD);
#if MT32EMU_MONITOR_PARTIALS > 2
synth->printDebug("[+%lu] [Partial %d] Deactivated", sampleNum, debugPartialNum);
synth->printPartialUsage(sampleNum);
@ -117,23 +111,30 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us
structurePosition = patchCache->structurePosition;
Bit8u panSetting = rhythmTemp != NULL ? rhythmTemp->panpot : part->getPatchTemp()->panpot;
float panVal;
if (mixType == 3) {
if (structurePosition == 0) {
panVal = PAN_NUMERATOR_MASTER[panSetting];
panSetting = PAN_NUMERATOR_MASTER[panSetting] << 1;
} else {
panVal = PAN_NUMERATOR_SLAVE[panSetting];
panSetting = PAN_NUMERATOR_SLAVE[panSetting] << 1;
}
// Do a normal mix independent of any pair partial.
mixType = 0;
pairPartial = NULL;
} else {
panVal = PAN_NUMERATOR_NORMAL[panSetting];
// Mok wanted an option for smoother panning, and we love Mok.
#ifndef INACCURATE_SMOOTH_PAN
// CONFIRMED by Mok: exactly bytes like this (right shifted?) are sent to the LA32.
panSetting &= 0x0E;
#endif
}
// FIXME: Sample analysis suggests that the use of panVal is linear, but there are some some quirks that still need to be resolved.
stereoVolume.leftVol = panVal / 7.0f;
stereoVolume.rightVol = 1.0f - stereoVolume.leftVol;
leftPanValue = synth->reversedStereoEnabled ? 14 - panSetting : panSetting;
rightPanValue = 14 - leftPanValue;
#if !MT32EMU_USE_FLOAT_SAMPLES
leftPanValue = PAN_FACTORS[leftPanValue];
rightPanValue = PAN_FACTORS[rightPanValue];
#endif
// SEMI-CONFIRMED: From sample analysis:
// Found that timbres with 3 or 4 partials (i.e. one using two partial pairs) are mixed in two different ways.
@ -150,8 +151,8 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us
// For my personal taste, this behaviour rather enriches the sounding and should be emulated.
// Also, the current partial allocator model probably needs to be refined.
if (debugPartialNum & 8) {
stereoVolume.leftVol = -stereoVolume.leftVol;
stereoVolume.rightVol = -stereoVolume.rightVol;
leftPanValue = -leftPanValue;
rightPanValue = -rightPanValue;
}
if (patchCache->PCMPartial) {
@ -199,9 +200,6 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us
if (!hasRingModulatingSlave()) {
la32Pair.deactivate(LA32PartialPair::SLAVE);
}
// Temporary integration hack
stereoVolume.leftVol /= 8192.0f;
stereoVolume.rightVol /= 8192.0f;
}
Bit32u Partial::getAmpValue() {
@ -233,39 +231,6 @@ Bit32u Partial::getCutoffValue() {
return (tvf->getBaseCutoff() << 18) + cutoffModifierRampVal;
}
unsigned long Partial::generateSamples(Bit16s *partialBuf, unsigned long length) {
if (!isActive() || alreadyOutputed) {
return 0;
}
if (poly == NULL) {
synth->printDebug("[Partial %d] *** ERROR: poly is NULL at Partial::generateSamples()!", debugPartialNum);
return 0;
}
alreadyOutputed = true;
for (sampleNum = 0; sampleNum < length; sampleNum++) {
if (!tva->isPlaying() || !la32Pair.isActive(LA32PartialPair::MASTER)) {
deactivate();
break;
}
la32Pair.generateNextSample(LA32PartialPair::MASTER, getAmpValue(), tvp->nextPitch(), getCutoffValue());
if (hasRingModulatingSlave()) {
la32Pair.generateNextSample(LA32PartialPair::SLAVE, pair->getAmpValue(), pair->tvp->nextPitch(), pair->getCutoffValue());
if (!pair->tva->isPlaying() || !la32Pair.isActive(LA32PartialPair::SLAVE)) {
pair->deactivate();
if (mixType == 2) {
deactivate();
break;
}
}
}
*partialBuf++ = la32Pair.nextOutSample();
}
unsigned long renderedSamples = sampleNum;
sampleNum = 0;
return renderedSamples;
}
bool Partial::hasRingModulatingSlave() const {
return pair != NULL && structurePosition == 0 && (mixType == 1 || mixType == 2);
}
@ -289,7 +254,18 @@ Synth *Partial::getSynth() const {
return synth;
}
bool Partial::produceOutput(float *leftBuf, float *rightBuf, unsigned long length) {
TVA *Partial::getTVA() const {
return tva;
}
void Partial::backupCache(const PatchCache &cache) {
if (patchCache == &cache) {
cachebackup = cache;
patchCache = &cachebackup;
}
}
bool Partial::produceOutput(Sample *leftBuf, Sample *rightBuf, unsigned long length) {
if (!isActive() || alreadyOutputed || isRingModulatingSlave()) {
return false;
}
@ -297,15 +273,52 @@ bool Partial::produceOutput(float *leftBuf, float *rightBuf, unsigned long lengt
synth->printDebug("[Partial %d] *** ERROR: poly is NULL at Partial::produceOutput()!", debugPartialNum);
return false;
}
unsigned long numGenerated = generateSamples(myBuffer, length);
for (unsigned int i = 0; i < numGenerated; i++) {
*leftBuf++ = myBuffer[i] * stereoVolume.leftVol;
*rightBuf++ = myBuffer[i] * stereoVolume.rightVol;
}
for (; numGenerated < length; numGenerated++) {
*leftBuf++ = 0.0f;
*rightBuf++ = 0.0f;
alreadyOutputed = true;
for (sampleNum = 0; sampleNum < length; sampleNum++) {
if (!tva->isPlaying() || !la32Pair.isActive(LA32PartialPair::MASTER)) {
deactivate();
break;
}
la32Pair.generateNextSample(LA32PartialPair::MASTER, getAmpValue(), tvp->nextPitch(), getCutoffValue());
if (hasRingModulatingSlave()) {
la32Pair.generateNextSample(LA32PartialPair::SLAVE, pair->getAmpValue(), pair->tvp->nextPitch(), pair->getCutoffValue());
if (!pair->tva->isPlaying() || !la32Pair.isActive(LA32PartialPair::SLAVE)) {
pair->deactivate();
if (mixType == 2) {
deactivate();
break;
}
}
}
// Although, LA32 applies panning itself, we assume here it is applied in the mixer, not within a pair.
// Applying the pan value in the log-space looks like a waste of unlog resources. Though, it needs clarification.
Sample sample = la32Pair.nextOutSample();
// FIXME: Sample analysis suggests that the use of panVal is linear, but there are some quirks that still need to be resolved.
#if MT32EMU_USE_FLOAT_SAMPLES
Sample leftOut = (sample * (float)leftPanValue) / 14.0f;
Sample rightOut = (sample * (float)rightPanValue) / 14.0f;
*(leftBuf++) += leftOut;
*(rightBuf++) += rightOut;
#else
// FIXME: Dividing by 7 (or by 14 in a Mok-friendly way) looks of course pointless. Need clarification.
// FIXME2: LA32 may produce distorted sound in case if the absolute value of maximal amplitude of the input exceeds 8191
// when the panning value is non-zero. Most probably the distortion occurs in the same way it does with ring modulation,
// and it seems to be caused by limited precision of the common multiplication circuit.
// From analysis of this overflow, it is obvious that the right channel output is actually found
// by subtraction of the left channel output from the input.
// Though, it is unknown whether this overflow is exploited somewhere.
Sample leftOut = Sample((sample * leftPanValue) >> 8);
Sample rightOut = Sample((sample * rightPanValue) >> 8);
*leftBuf = Synth::clipBit16s((Bit32s)*leftBuf + (Bit32s)leftOut);
*rightBuf = Synth::clipBit16s((Bit32s)*rightBuf + (Bit32s)rightOut);
leftBuf++;
rightBuf++;
#endif
}
sampleNum = 0;
return true;
}

View File

@ -25,26 +25,22 @@ class Part;
class TVA;
struct ControlROMPCMStruct;
struct StereoVolume {
float leftVol;
float rightVol;
};
// A partial represents one of up to four waveform generators currently playing within a poly.
class Partial {
private:
Synth *synth;
const int debugPartialNum; // Only used for debugging
// Number of the sample currently being rendered by generateSamples(), or 0 if no run is in progress
// Number of the sample currently being rendered by produceOutput(), or 0 if no run is in progress
// This is only kept available for debugging purposes.
unsigned long sampleNum;
// Actually, this is a 4-bit register but we abuse this to emulate inverted mixing.
// Also we double the value to enable INACCURATE_SMOOTH_PAN, with respect to MoK.
Bit32s leftPanValue, rightPanValue;
int ownerPart; // -1 if unassigned
int mixType;
int structurePosition; // 0 or 1 of a structure pair
StereoVolume stereoVolume;
Bit16s myBuffer[MAX_SAMPLES_PER_RUN];
// Only used for PCM partials
int pcmNum;
@ -56,6 +52,11 @@ private:
int pulseWidthVal;
Poly *poly;
Partial *pair;
TVA *tva;
TVP *tvp;
TVF *tvf;
LA32Ramp ampRamp;
LA32Ramp cutoffModifierRamp;
@ -63,18 +64,13 @@ private:
// TODO: This should be owned by PartialPair
LA32PartialPair la32Pair;
const PatchCache *patchCache;
PatchCache cachebackup;
Bit32u getAmpValue();
Bit32u getCutoffValue();
public:
const PatchCache *patchCache;
TVA *tva;
TVP *tvp;
TVF *tvf;
PatchCache cachebackup;
Partial *pair;
bool alreadyOutputed;
Partial(Synth *synth, int debugPartialNum);
@ -97,14 +93,14 @@ public:
bool isPCM() const;
const ControlROMPCMStruct *getControlROMPCMStruct() const;
Synth *getSynth() const;
TVA *getTVA() const;
void backupCache(const PatchCache &cache);
// Returns true only if data written to buffer
// This function (unlike the one below it) returns processed stereo samples
// made from combining this single partial with its pair, if it has one.
bool produceOutput(float *leftBuf, float *rightBuf, unsigned long length);
// This function writes mono sample output to the provided buffer, and returns the number of samples written
unsigned long generateSamples(Bit16s *partialBuf, unsigned long length);
bool produceOutput(Sample *leftBuf, Sample *rightBuf, unsigned long length);
};
}

View File

@ -25,19 +25,26 @@ namespace MT32Emu {
PartialManager::PartialManager(Synth *useSynth, Part **useParts) {
synth = useSynth;
parts = useParts;
for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
partialTable = new Partial *[synth->getPartialCount()];
freePolys = new Poly *[synth->getPartialCount()];
firstFreePolyIndex = 0;
for (unsigned int i = 0; i < synth->getPartialCount(); i++) {
partialTable[i] = new Partial(synth, i);
freePolys[i] = new Poly();
}
}
PartialManager::~PartialManager(void) {
for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
for (unsigned int i = 0; i < synth->getPartialCount(); i++) {
delete partialTable[i];
if (freePolys[i] != NULL) delete freePolys[i];
}
delete[] partialTable;
delete[] freePolys;
}
void PartialManager::clearAlreadyOutputed() {
for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
for (unsigned int i = 0; i < synth->getPartialCount(); i++) {
partialTable[i]->alreadyOutputed = false;
}
}
@ -46,12 +53,12 @@ bool PartialManager::shouldReverb(int i) {
return partialTable[i]->shouldReverb();
}
bool PartialManager::produceOutput(int i, float *leftBuf, float *rightBuf, Bit32u bufferLength) {
bool PartialManager::produceOutput(int i, Sample *leftBuf, Sample *rightBuf, Bit32u bufferLength) {
return partialTable[i]->produceOutput(leftBuf, rightBuf, bufferLength);
}
void PartialManager::deactivateAll() {
for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
for (unsigned int i = 0; i < synth->getPartialCount(); i++) {
partialTable[i]->deactivate();
}
}
@ -69,7 +76,7 @@ Partial *PartialManager::allocPartial(int partNum) {
Partial *outPartial = NULL;
// Get the first inactive partial
for (int partialNum = 0; partialNum < MT32EMU_MAX_PARTIALS; partialNum++) {
for (unsigned int partialNum = 0; partialNum < synth->getPartialCount(); partialNum++) {
if (!partialTable[partialNum]->isActive()) {
outPartial = partialTable[partialNum];
break;
@ -83,7 +90,7 @@ Partial *PartialManager::allocPartial(int partNum) {
unsigned int PartialManager::getFreePartialCount(void) {
int count = 0;
for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
for (unsigned int i = 0; i < synth->getPartialCount(); i++) {
if (!partialTable[i]->isActive()) {
count++;
}
@ -94,7 +101,7 @@ unsigned int PartialManager::getFreePartialCount(void) {
// This function is solely used to gather data for debug output at the moment.
void PartialManager::getPerPartPartialUsage(unsigned int perPartPartialUsage[9]) {
memset(perPartPartialUsage, 0, 9 * sizeof(unsigned int));
for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
for (unsigned int i = 0; i < synth->getPartialCount(); i++) {
if (partialTable[i]->isActive()) {
perPartPartialUsage[partialTable[i]->getOwnerPart()]++;
}
@ -189,7 +196,7 @@ bool PartialManager::freePartials(unsigned int needed, int partNum) {
break;
}
#endif
if (getFreePartialCount() >= needed) {
if (synth->isAbortingPoly() || getFreePartialCount() >= needed) {
return true;
}
}
@ -206,7 +213,7 @@ bool PartialManager::freePartials(unsigned int needed, int partNum) {
if (!abortFirstPolyPreferHeldWhereReserveExceeded(partNum)) {
break;
}
if (getFreePartialCount() >= needed) {
if (synth->isAbortingPoly() || getFreePartialCount() >= needed) {
return true;
}
}
@ -222,7 +229,7 @@ bool PartialManager::freePartials(unsigned int needed, int partNum) {
if (!abortFirstPolyPreferHeldWhereReserveExceeded(-1)) {
break;
}
if (getFreePartialCount() >= needed) {
if (synth->isAbortingPoly() || getFreePartialCount() >= needed) {
return true;
}
}
@ -233,7 +240,7 @@ bool PartialManager::freePartials(unsigned int needed, int partNum) {
if (!parts[partNum]->abortFirstPolyPreferHeld()) {
break;
}
if (getFreePartialCount() >= needed) {
if (synth->isAbortingPoly() || getFreePartialCount() >= needed) {
return true;
}
}
@ -243,10 +250,39 @@ bool PartialManager::freePartials(unsigned int needed, int partNum) {
}
const Partial *PartialManager::getPartial(unsigned int partialNum) const {
if (partialNum > MT32EMU_MAX_PARTIALS - 1) {
if (partialNum > synth->getPartialCount() - 1) {
return NULL;
}
return partialTable[partialNum];
}
Poly *PartialManager::assignPolyToPart(Part *part) {
if (firstFreePolyIndex < synth->getPartialCount()) {
Poly *poly = freePolys[firstFreePolyIndex];
freePolys[firstFreePolyIndex] = NULL;
firstFreePolyIndex++;
poly->setPart(part);
return poly;
}
return NULL;
}
void PartialManager::polyFreed(Poly *poly) {
if (0 == firstFreePolyIndex) {
synth->printDebug("Cannot return freed poly, currently active polys:\n");
for (Bit32u partNum = 0; partNum < 9; partNum++) {
const Poly *activePoly = synth->getPart(partNum)->getFirstActivePoly();
Bit32u polyCount = 0;
while (activePoly != NULL) {
activePoly->getNext();
polyCount++;
}
synth->printDebug("Part: %i, active poly count: %i\n", partNum, polyCount);
}
}
poly->setPart(NULL);
firstFreePolyIndex--;
freePolys[firstFreePolyIndex] = poly;
}
}

View File

@ -24,17 +24,17 @@ class Synth;
class PartialManager {
private:
Synth *synth; // Only used for sending debug output
Synth *synth;
Part **parts;
Partial *partialTable[MT32EMU_MAX_PARTIALS];
Poly **freePolys;
Partial **partialTable;
Bit8u numReservedPartialsForPart[9];
Bit32u firstFreePolyIndex;
bool abortFirstReleasingPolyWhereReserveExceeded(int minPart);
bool abortFirstPolyPreferHeldWhereReserveExceeded(int minPart);
public:
PartialManager(Synth *synth, Part **parts);
~PartialManager();
Partial *allocPartial(int partNum);
@ -43,10 +43,12 @@ public:
bool freePartials(unsigned int needed, int partNum);
unsigned int setReserve(Bit8u *rset);
void deactivateAll();
bool produceOutput(int i, float *leftBuf, float *rightBuf, Bit32u bufferLength);
bool produceOutput(int i, Sample *leftBuf, Sample *rightBuf, Bit32u bufferLength);
bool shouldReverb(int i);
void clearAlreadyOutputed();
const Partial *getPartial(unsigned int partialNum) const;
Poly *assignPolyToPart(Part *part);
void polyFreed(Poly *poly);
};
}

View File

@ -19,8 +19,8 @@
namespace MT32Emu {
Poly::Poly(Part *usePart) {
part = usePart;
Poly::Poly() {
part = NULL;
key = 255;
velocity = 255;
sustain = false;
@ -32,10 +32,21 @@ Poly::Poly(Part *usePart) {
next = NULL;
}
void Poly::setPart(Part *usePart) {
part = usePart;
}
void Poly::reset(unsigned int newKey, unsigned int newVelocity, bool newSustain, Partial **newPartials) {
if (isActive()) {
// FIXME: Throw out some big ugly debug output with a lot of exclamation marks - we should never get here
terminate();
// This should never happen
part->getSynth()->printDebug("Resetting active poly. Active partial count: %i\n", activePartialCount);
for (int i = 0; i < 4; i++) {
if (partials[i] != NULL && partials[i]->isActive()) {
partials[i]->deactivate();
activePartialCount--;
}
}
state = POLY_Inactive;
}
key = newKey;
@ -92,41 +103,24 @@ bool Poly::startDecay() {
}
bool Poly::startAbort() {
if (state == POLY_Inactive) {
if (state == POLY_Inactive || part->getSynth()->isAbortingPoly()) {
return false;
}
for (int t = 0; t < 4; t++) {
Partial *partial = partials[t];
if (partial != NULL) {
partial->startAbort();
part->getSynth()->abortingPoly = this;
}
}
return true;
}
void Poly::terminate() {
if (state == POLY_Inactive) {
return;
}
for (int t = 0; t < 4; t++) {
Partial *partial = partials[t];
if (partial != NULL) {
partial->deactivate();
}
}
if (state != POLY_Inactive) {
// FIXME: Throw out lots of debug output - this should never happen
// (Deactivating the partials above should've made them each call partialDeactivated(), ultimately changing the state to POLY_Inactive)
state = POLY_Inactive;
}
}
void Poly::backupCacheToPartials(PatchCache cache[4]) {
for (int partialNum = 0; partialNum < 4; partialNum++) {
Partial *partial = partials[partialNum];
if (partial != NULL && partial->patchCache == &cache[partialNum]) {
partial->cachebackup = cache[partialNum];
partial->patchCache = &partial->cachebackup;
if (partial != NULL) {
partial->backupCache(cache[partialNum]);
}
}
}
@ -171,11 +165,14 @@ void Poly::partialDeactivated(Partial *partial) {
}
if (activePartialCount == 0) {
state = POLY_Inactive;
if (part->getSynth()->abortingPoly == this) {
part->getSynth()->abortingPoly = NULL;
}
}
part->partialDeactivated(this);
}
Poly *Poly::getNext() {
Poly *Poly::getNext() const {
return next;
}

View File

@ -44,13 +44,13 @@ private:
Poly *next;
public:
Poly(Part *part);
Poly();
void setPart(Part *usePart);
void reset(unsigned int key, unsigned int velocity, bool sustain, Partial **partials);
bool noteOff(bool pedalHeld);
bool stopPedalHold();
bool startDecay();
bool startAbort();
void terminate();
void backupCacheToPartials(PatchCache cache[4]);
@ -63,7 +63,7 @@ public:
void partialDeactivated(Partial *partial);
Poly *getNext();
Poly *getNext() const;
void setNext(Poly *poly);
};

View File

@ -38,6 +38,12 @@ typedef signed short int Bit16s;
typedef unsigned char Bit8u;
typedef signed char Bit8s;
#if MT32EMU_USE_FLOAT_SAMPLES
typedef float Sample;
#else
typedef Bit16s Sample;
#endif
// The following structures represent the MT-32's memory
// Since sysex allows this memory to be written to in blocks of bytes,
// we keep this packed so that we can copy data into the various

View File

@ -26,15 +26,7 @@
#include "mt32emu.h"
#include "mmath.h"
#include "PartialManager.h"
#if MT32EMU_USE_REVERBMODEL == 1
#include "AReverbModel.h"
#elif MT32EMU_USE_REVERBMODEL == 2
#include "BReverbModel.h"
#else
#include "FreeverbModel.h"
#endif
#include "DelayReverb.h"
namespace MT32Emu {
@ -50,84 +42,22 @@ static const ControlROMMap ControlROMMaps[7] = {
// (Note that all but CM-32L ROM actually have 86 entries for rhythmTemp)
};
static inline Bit16s *streamOffset(Bit16s *stream, Bit32u pos) {
return stream == NULL ? NULL : stream + pos;
}
static inline void muteStream(Sample *stream, Bit32u len) {
if (stream == NULL) return;
static inline void clearIfNonNull(Bit16s *stream, Bit32u len) {
if (stream != NULL) {
memset(stream, 0, len * sizeof(Bit16s));
}
}
static inline void mix(float *target, const float *stream, Bit32u len) {
while (len--) {
*target += *stream;
stream++;
target++;
}
}
static inline void clearFloats(float *leftBuf, float *rightBuf, Bit32u len) {
#if MT32EMU_USE_FLOAT_SAMPLES
// FIXME: Use memset() where compatibility is guaranteed (if this turns out to be a win)
while (len--) {
*leftBuf++ = 0.0f;
*rightBuf++ = 0.0f;
*stream++ = 0.0f;
}
#else
memset(stream, 0, len * sizeof(Sample));
#endif
}
static inline Bit16s clipBit16s(Bit32s a) {
// Clamp values above 32767 to 32767, and values below -32768 to -32768
if ((a + 32768) & ~65535) {
return (a >> 31) ^ 32767;
}
return a;
}
static void floatToBit16s_nice(Bit16s *target, const float *source, Bit32u len, float outputGain) {
float gain = outputGain * 16384.0f;
while (len--) {
// Since we're not shooting for accuracy here, don't worry about the rounding mode.
*target = clipBit16s((Bit32s)(*source * gain));
source++;
target++;
}
}
static void floatToBit16s_pure(Bit16s *target, const float *source, Bit32u len, float /*outputGain*/) {
while (len--) {
*target = clipBit16s((Bit32s)floor(*source * 8192.0f));
source++;
target++;
}
}
static void floatToBit16s_reverb(Bit16s *target, const float *source, Bit32u len, float outputGain) {
float gain = outputGain * 8192.0f;
while (len--) {
*target = clipBit16s((Bit32s)floor(*source * gain));
source++;
target++;
}
}
static void floatToBit16s_generation1(Bit16s *target, const float *source, Bit32u len, float outputGain) {
float gain = outputGain * 8192.0f;
while (len--) {
*target = clipBit16s((Bit32s)floor(*source * gain));
*target = (*target & 0x8000) | ((*target << 1) & 0x7FFE);
source++;
target++;
}
}
static void floatToBit16s_generation2(Bit16s *target, const float *source, Bit32u len, float outputGain) {
float gain = outputGain * 8192.0f;
while (len--) {
*target = clipBit16s((Bit32s)floor(*source * gain));
*target = (*target & 0x8000) | ((*target << 1) & 0x7FFE) | ((*target >> 14) & 0x0001);
source++;
target++;
static inline void advanceStreamPosition(Sample *&stream, Bit32u posDelta) {
if (stream != NULL) {
stream += posDelta;
}
}
@ -146,6 +76,7 @@ Synth::Synth(ReportHandler *useReportHandler) {
isOpen = false;
reverbEnabled = true;
reverbOverridden = false;
partialCount = DEFAULT_MAX_PARTIALS;
if (useReportHandler == NULL) {
reportHandler = new ReportHandler;
@ -155,28 +86,20 @@ Synth::Synth(ReportHandler *useReportHandler) {
isDefaultReportHandler = false;
}
#if MT32EMU_USE_REVERBMODEL == 1
reverbModels[REVERB_MODE_ROOM] = new AReverbModel(REVERB_MODE_ROOM);
reverbModels[REVERB_MODE_HALL] = new AReverbModel(REVERB_MODE_HALL);
reverbModels[REVERB_MODE_PLATE] = new AReverbModel(REVERB_MODE_PLATE);
reverbModels[REVERB_MODE_TAP_DELAY] = new DelayReverb();
#elif MT32EMU_USE_REVERBMODEL == 2
reverbModels[REVERB_MODE_ROOM] = new BReverbModel(REVERB_MODE_ROOM);
reverbModels[REVERB_MODE_HALL] = new BReverbModel(REVERB_MODE_HALL);
reverbModels[REVERB_MODE_PLATE] = new BReverbModel(REVERB_MODE_PLATE);
reverbModels[REVERB_MODE_TAP_DELAY] = new BReverbModel(REVERB_MODE_TAP_DELAY);
#else
reverbModels[REVERB_MODE_ROOM] = new FreeverbModel(0.76f, 0.687770909f, 0.63f, 0, 0.5f);
reverbModels[REVERB_MODE_HALL] = new FreeverbModel(2.0f, 0.712025098f, 0.86f, 1, 0.5f);
reverbModels[REVERB_MODE_PLATE] = new FreeverbModel(0.4f, 0.939522749f, 0.38f, 2, 0.05f);
reverbModels[REVERB_MODE_TAP_DELAY] = new DelayReverb();
#endif
reverbModel = NULL;
setDACInputMode(DACInputMode_NICE);
setMIDIDelayMode(MIDIDelayMode_DELAY_SHORT_MESSAGES_ONLY);
setOutputGain(1.0f);
setReverbOutputGain(0.68f);
setReverbOutputGain(1.0f);
setReversedStereoEnabled(false);
partialManager = NULL;
midiQueue = NULL;
lastReceivedMIDIEventTimestamp = 0;
memset(parts, 0, sizeof(parts));
renderedSampleCount = 0;
}
@ -197,29 +120,16 @@ void ReportHandler::showLCDMessage(const char *data) {
}
void ReportHandler::printDebug(const char *fmt, va_list list) {
vprintf(fmt, list);
printf("\n");
}
void Synth::partStateChanged(int partNum, bool isPartActive) {
reportHandler->onPartStateChanged(partNum, isPartActive);
vprintf(fmt, list);
printf("\n");
}
void Synth::polyStateChanged(int partNum) {
reportHandler->onPolyStateChanged(partNum);
}
void Synth::partialStateChanged(const Partial * const partial, int oldPartialPhase, int newPartialPhase) {
for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
if (getPartial(i) == partial) {
reportHandler->onPartialStateChanged(i, oldPartialPhase, newPartialPhase);
break;
}
}
}
void Synth::newTimbreSet(int partNum, char patchName[]) {
reportHandler->onProgramChanged(partNum, patchName);
void Synth::newTimbreSet(int partNum, Bit8u timbreGroup, const char patchName[]) {
reportHandler->onProgramChanged(partNum, timbreGroup, patchName);
}
void Synth::printDebug(const char *fmt, ...) {
@ -249,35 +159,72 @@ bool Synth::isReverbOverridden() const {
}
void Synth::setDACInputMode(DACInputMode mode) {
switch(mode) {
case DACInputMode_GENERATION1:
la32FloatToBit16sFunc = floatToBit16s_generation1;
reverbFloatToBit16sFunc = floatToBit16s_reverb;
break;
case DACInputMode_GENERATION2:
la32FloatToBit16sFunc = floatToBit16s_generation2;
reverbFloatToBit16sFunc = floatToBit16s_reverb;
break;
case DACInputMode_PURE:
la32FloatToBit16sFunc = floatToBit16s_pure;
reverbFloatToBit16sFunc = floatToBit16s_pure;
break;
case DACInputMode_NICE:
default:
la32FloatToBit16sFunc = floatToBit16s_nice;
reverbFloatToBit16sFunc = floatToBit16s_reverb;
break;
}
dacInputMode = mode;
}
DACInputMode Synth::getDACInputMode() const {
return dacInputMode;
}
void Synth::setMIDIDelayMode(MIDIDelayMode mode) {
midiDelayMode = mode;
}
MIDIDelayMode Synth::getMIDIDelayMode() const {
return midiDelayMode;
}
#if MT32EMU_USE_FLOAT_SAMPLES
void Synth::setOutputGain(float newOutputGain) {
outputGain = newOutputGain;
}
float Synth::getOutputGain() const {
return outputGain;
}
void Synth::setReverbOutputGain(float newReverbOutputGain) {
reverbOutputGain = newReverbOutputGain;
}
float Synth::getReverbOutputGain() const {
return reverbOutputGain;
}
#else // #if MT32EMU_USE_FLOAT_SAMPLES
void Synth::setOutputGain(float newOutputGain) {
if (newOutputGain < 0.0f) newOutputGain = -newOutputGain;
if (256.0f < newOutputGain) newOutputGain = 256.0f;
outputGain = int(newOutputGain * 256.0f);
}
float Synth::getOutputGain() const {
return outputGain / 256.0f;
}
void Synth::setReverbOutputGain(float newReverbOutputGain) {
if (newReverbOutputGain < 0.0f) newReverbOutputGain = -newReverbOutputGain;
float maxValue = 256.0f / CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR;
if (maxValue < newReverbOutputGain) newReverbOutputGain = maxValue;
reverbOutputGain = int(newReverbOutputGain * 256.0f);
}
float Synth::getReverbOutputGain() const {
return reverbOutputGain / 256.0f;
}
#endif // #if MT32EMU_USE_FLOAT_SAMPLES
void Synth::setReversedStereoEnabled(bool enabled) {
reversedStereoEnabled = enabled;
}
bool Synth::isReversedStereoEnabled() {
return reversedStereoEnabled;
}
bool Synth::loadControlROM(const ROMImage &controlROMImage) {
if (&controlROMImage == NULL) return false;
Common::File *file = controlROMImage.getFile();
@ -356,9 +303,9 @@ bool Synth::loadPCMROM(const ROMImage &pcmROMImage) {
bool Synth::initPCMList(Bit16u mapAddress, Bit16u count) {
ControlROMPCMStruct *tps = (ControlROMPCMStruct *)&controlROMData[mapAddress];
for (int i = 0; i < count; i++) {
size_t rAddr = tps[i].pos * 0x800;
size_t rLenExp = (tps[i].len & 0x70) >> 4;
size_t rLen = 0x800 << rLenExp;
Bit32u rAddr = tps[i].pos * 0x800;
Bit32u rLenExp = (tps[i].len & 0x70) >> 4;
Bit32u rLen = 0x800 << rLenExp;
if (rAddr + rLen > pcmROMSize) {
printDebug("Control ROM error: Wave map entry %d points to invalid PCM address 0x%04X, length 0x%04X", i, rAddr, rLen);
return false;
@ -420,17 +367,18 @@ bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, int count, int startTi
return true;
}
bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage) {
bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount) {
if (isOpen) {
return false;
}
prerenderReadIx = prerenderWriteIx = 0;
partialCount = usePartialCount;
abortingPoly = NULL;
#if MT32EMU_MONITOR_INIT
printDebug("Initialising Constant Tables");
#endif
#if !MT32EMU_REDUCE_REVERB_MEMORY
for (int i = 0; i < 4; i++) {
reverbModels[i]->open(useProp.sampleRate);
for (int i = REVERB_MODE_ROOM; i <= REVERB_MODE_TAP_DELAY; i++) {
reverbModels[i]->open();
}
#endif
@ -567,6 +515,8 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage) {
// For resetting mt32 mid-execution
mt32default = mt32ram;
midiQueue = new MidiEventQueue();
isOpen = true;
isEnabled = false;
@ -581,6 +531,9 @@ void Synth::close() {
return;
}
delete midiQueue;
midiQueue = NULL;
delete partialManager;
partialManager = NULL;
@ -601,12 +554,78 @@ void Synth::close() {
isOpen = false;
}
void Synth::playMsg(Bit32u msg) {
void Synth::flushMIDIQueue() {
if (midiQueue != NULL) {
for (;;) {
const MidiEvent *midiEvent = midiQueue->peekMidiEvent();
if (midiEvent == NULL) break;
if (midiEvent->sysexData == NULL) {
playMsgNow(midiEvent->shortMessageData);
} else {
playSysexNow(midiEvent->sysexData, midiEvent->sysexLength);
}
midiQueue->dropMidiEvent();
}
lastReceivedMIDIEventTimestamp = renderedSampleCount;
}
}
void Synth::setMIDIEventQueueSize(Bit32u useSize) {
if (midiQueue != NULL) {
flushMIDIQueue();
delete midiQueue;
midiQueue = new MidiEventQueue(useSize);
}
}
Bit32u Synth::getShortMessageLength(Bit32u msg) {
if ((msg & 0xF0) == 0xF0) return 1;
// NOTE: This calculation isn't quite correct
// as it doesn't consider the running status byte
return ((msg & 0xE0) == 0xC0) ? 2 : 3;
}
Bit32u Synth::addMIDIInterfaceDelay(Bit32u len, Bit32u timestamp) {
Bit32u transferTime = Bit32u((double)len * MIDI_DATA_TRANSFER_RATE);
// Dealing with wrapping
if (Bit32s(timestamp - lastReceivedMIDIEventTimestamp) < 0) {
timestamp = lastReceivedMIDIEventTimestamp;
}
timestamp += transferTime;
lastReceivedMIDIEventTimestamp = timestamp;
return timestamp;
}
bool Synth::playMsg(Bit32u msg) {
return playMsg(msg, renderedSampleCount);
}
bool Synth::playMsg(Bit32u msg, Bit32u timestamp) {
if (midiQueue == NULL) return false;
if (midiDelayMode != MIDIDelayMode_IMMEDIATE) {
timestamp = addMIDIInterfaceDelay(getShortMessageLength(msg), timestamp);
}
return midiQueue->pushShortMessage(msg, timestamp);
}
bool Synth::playSysex(const Bit8u *sysex, Bit32u len) {
return playSysex(sysex, len, renderedSampleCount);
}
bool Synth::playSysex(const Bit8u *sysex, Bit32u len, Bit32u timestamp) {
if (midiQueue == NULL) return false;
if (midiDelayMode == MIDIDelayMode_DELAY_ALL) {
timestamp = addMIDIInterfaceDelay(len, timestamp);
}
return midiQueue->pushSysex(sysex, len, timestamp);
}
void Synth::playMsgNow(Bit32u msg) {
// FIXME: Implement active sensing
unsigned char code = (unsigned char)((msg & 0x0000F0) >> 4);
unsigned char chan = (unsigned char)(msg & 0x00000F);
unsigned char note = (unsigned char)((msg & 0x00FF00) >> 8);
unsigned char velocity = (unsigned char)((msg & 0xFF0000) >> 16);
unsigned char note = (unsigned char)((msg & 0x007F00) >> 8);
unsigned char velocity = (unsigned char)((msg & 0x7F0000) >> 16);
isEnabled = true;
//printDebug("Playing chan %d, code 0x%01x note: 0x%02x", chan, code, note);
@ -619,11 +638,6 @@ void Synth::playMsg(Bit32u msg) {
return;
}
playMsgOnPart(part, code, note, velocity);
// This ensures minimum 1-sample delay between sequential MIDI events
// Without this, a sequence of NoteOn and immediately succeeding NoteOff messages is always silent
// Technically, it's also impossible to send events through the MIDI interface faster than about each millisecond
prerender();
}
void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity) {
@ -705,7 +719,7 @@ void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char
#if MT32EMU_MONITOR_MIDI > 0
printDebug("Unknown MIDI Control code: 0x%02x - vel 0x%02x", note, velocity);
#endif
break;
return;
}
break;
@ -722,13 +736,12 @@ void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char
#if MT32EMU_MONITOR_MIDI > 0
printDebug("Unknown Midi code: 0x%01x - %02x - %02x", code, note, velocity);
#endif
break;
return;
}
//midiOutShortMsg(m_out, msg);
reportHandler->onMIDIMessagePlayed();
}
void Synth::playSysex(const Bit8u *sysex, Bit32u len) {
void Synth::playSysexNow(const Bit8u *sysex, Bit32u len) {
if (len < 2) {
printDebug("playSysex: Message is too short for sysex (%d bytes)", len);
}
@ -823,6 +836,7 @@ void Synth::readSysex(unsigned char /*device*/, const Bit8u * /*sysex*/, Bit32u
}
void Synth::writeSysex(unsigned char device, const Bit8u *sysex, Bit32u len) {
reportHandler->onMIDIMessagePlayed();
Bit32u addr = (sysex[0] << 16) | (sysex[1] << 8) | (sysex[2]);
addr = MT32EMU_MEMADDR(addr);
sysex += 3;
@ -1245,7 +1259,7 @@ void Synth::refreshSystemReverbParameters() {
reportHandler->onNewReverbTime(mt32ram.system.reverbTime);
reportHandler->onNewReverbLevel(mt32ram.system.reverbLevel);
ReverbModel *newReverbModel = reverbModels[mt32ram.system.reverbMode];
BReverbModel *newReverbModel = reverbModels[mt32ram.system.reverbMode];
#if MT32EMU_REDUCE_REVERB_MEMORY
if (reverbModel != newReverbModel) {
if (reverbModel != NULL) {
@ -1321,179 +1335,229 @@ void Synth::reset() {
isEnabled = false;
}
void Synth::render(Bit16s *stream, Bit32u len) {
if (!isEnabled) {
memset(stream, 0, len * sizeof(Bit16s) * 2);
return;
MidiEvent::~MidiEvent() {
if (sysexData != NULL) {
delete[] sysexData;
}
}
void MidiEvent::setShortMessage(Bit32u useShortMessageData, Bit32u useTimestamp) {
if (sysexData != NULL) {
delete[] sysexData;
}
shortMessageData = useShortMessageData;
timestamp = useTimestamp;
sysexData = NULL;
sysexLength = 0;
}
void MidiEvent::setSysex(const Bit8u *useSysexData, Bit32u useSysexLength, Bit32u useTimestamp) {
if (sysexData != NULL) {
delete[] sysexData;
}
shortMessageData = 0;
timestamp = useTimestamp;
sysexLength = useSysexLength;
Bit8u *dstSysexData = new Bit8u[sysexLength];
sysexData = dstSysexData;
memcpy(dstSysexData, useSysexData, sysexLength);
}
MidiEventQueue::MidiEventQueue(Bit32u useRingBufferSize) : ringBufferSize(useRingBufferSize) {
ringBuffer = new MidiEvent[ringBufferSize];
memset(ringBuffer, 0, ringBufferSize * sizeof(MidiEvent));
reset();
}
MidiEventQueue::~MidiEventQueue() {
delete[] ringBuffer;
}
void MidiEventQueue::reset() {
startPosition = 0;
endPosition = 0;
}
bool MidiEventQueue::pushShortMessage(Bit32u shortMessageData, Bit32u timestamp) {
unsigned int newEndPosition = (endPosition + 1) % ringBufferSize;
// Is ring buffer full?
if (startPosition == newEndPosition) return false;
ringBuffer[endPosition].setShortMessage(shortMessageData, timestamp);
endPosition = newEndPosition;
return true;
}
bool MidiEventQueue::pushSysex(const Bit8u *sysexData, Bit32u sysexLength, Bit32u timestamp) {
unsigned int newEndPosition = (endPosition + 1) % ringBufferSize;
// Is ring buffer full?
if (startPosition == newEndPosition) return false;
ringBuffer[endPosition].setSysex(sysexData, sysexLength, timestamp);
endPosition = newEndPosition;
return true;
}
const MidiEvent *MidiEventQueue::peekMidiEvent() {
return (startPosition == endPosition) ? NULL : &ringBuffer[startPosition];
}
void MidiEventQueue::dropMidiEvent() {
// Is ring buffer empty?
if (startPosition != endPosition) {
startPosition = (startPosition + 1) % ringBufferSize;
}
}
void Synth::render(Sample *stream, Bit32u len) {
Sample tmpNonReverbLeft[MAX_SAMPLES_PER_RUN];
Sample tmpNonReverbRight[MAX_SAMPLES_PER_RUN];
Sample tmpReverbDryLeft[MAX_SAMPLES_PER_RUN];
Sample tmpReverbDryRight[MAX_SAMPLES_PER_RUN];
Sample tmpReverbWetLeft[MAX_SAMPLES_PER_RUN];
Sample tmpReverbWetRight[MAX_SAMPLES_PER_RUN];
while (len > 0) {
Bit32u thisLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len;
renderStreams(tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, thisLen);
for (Bit32u i = 0; i < thisLen; i++) {
stream[0] = clipBit16s((Bit32s)tmpNonReverbLeft[i] + (Bit32s)tmpReverbDryLeft[i] + (Bit32s)tmpReverbWetLeft[i]);
stream[1] = clipBit16s((Bit32s)tmpNonReverbRight[i] + (Bit32s)tmpReverbDryRight[i] + (Bit32s)tmpReverbWetRight[i]);
stream += 2;
#if MT32EMU_USE_FLOAT_SAMPLES
*(stream++) = tmpNonReverbLeft[i] + tmpReverbDryLeft[i] + tmpReverbWetLeft[i];
*(stream++) = tmpNonReverbRight[i] + tmpReverbDryRight[i] + tmpReverbWetRight[i];
#else
*(stream++) = clipBit16s((Bit32s)tmpNonReverbLeft[i] + (Bit32s)tmpReverbDryLeft[i] + (Bit32s)tmpReverbWetLeft[i]);
*(stream++) = clipBit16s((Bit32s)tmpNonReverbRight[i] + (Bit32s)tmpReverbDryRight[i] + (Bit32s)tmpReverbWetRight[i]);
#endif
}
len -= thisLen;
}
}
bool Synth::prerender() {
int newPrerenderWriteIx = (prerenderWriteIx + 1) % MAX_PRERENDER_SAMPLES;
if (newPrerenderWriteIx == prerenderReadIx) {
// The prerender buffer is full
return false;
}
doRenderStreams(
prerenderNonReverbLeft + prerenderWriteIx,
prerenderNonReverbRight + prerenderWriteIx,
prerenderReverbDryLeft + prerenderWriteIx,
prerenderReverbDryRight + prerenderWriteIx,
prerenderReverbWetLeft + prerenderWriteIx,
prerenderReverbWetRight + prerenderWriteIx,
1);
prerenderWriteIx = newPrerenderWriteIx;
return true;
}
static inline void maybeCopy(Bit16s *out, Bit32u outPos, Bit16s *in, Bit32u inPos, Bit32u len) {
if (out == NULL) {
return;
}
memcpy(out + outPos, in + inPos, len * sizeof(Bit16s));
}
void Synth::copyPrerender(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u pos, Bit32u len) {
maybeCopy(nonReverbLeft, pos, prerenderNonReverbLeft, prerenderReadIx, len);
maybeCopy(nonReverbRight, pos, prerenderNonReverbRight, prerenderReadIx, len);
maybeCopy(reverbDryLeft, pos, prerenderReverbDryLeft, prerenderReadIx, len);
maybeCopy(reverbDryRight, pos, prerenderReverbDryRight, prerenderReadIx, len);
maybeCopy(reverbWetLeft, pos, prerenderReverbWetLeft, prerenderReadIx, len);
maybeCopy(reverbWetRight, pos, prerenderReverbWetRight, prerenderReadIx, len);
}
void Synth::checkPrerender(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u &pos, Bit32u &len) {
if (prerenderReadIx > prerenderWriteIx) {
// There's data in the prerender buffer, and the write index has wrapped.
Bit32u prerenderCopyLen = MAX_PRERENDER_SAMPLES - prerenderReadIx;
if (prerenderCopyLen > len) {
prerenderCopyLen = len;
}
copyPrerender(nonReverbLeft, nonReverbRight, reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, pos, prerenderCopyLen);
len -= prerenderCopyLen;
pos += prerenderCopyLen;
prerenderReadIx = (prerenderReadIx + prerenderCopyLen) % MAX_PRERENDER_SAMPLES;
}
if (prerenderReadIx < prerenderWriteIx) {
// There's data in the prerender buffer, and the write index is ahead of the read index.
Bit32u prerenderCopyLen = prerenderWriteIx - prerenderReadIx;
if (prerenderCopyLen > len) {
prerenderCopyLen = len;
}
copyPrerender(nonReverbLeft, nonReverbRight, reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, pos, prerenderCopyLen);
len -= prerenderCopyLen;
pos += prerenderCopyLen;
prerenderReadIx += prerenderCopyLen;
}
if (prerenderReadIx == prerenderWriteIx) {
// If the ring buffer's empty, reset it to start at 0 to minimise wrapping,
// which requires two writes instead of one.
prerenderReadIx = prerenderWriteIx = 0;
}
}
void Synth::renderStreams(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u len) {
if (!isEnabled) {
clearIfNonNull(nonReverbLeft, len);
clearIfNonNull(nonReverbRight, len);
clearIfNonNull(reverbDryLeft, len);
clearIfNonNull(reverbDryRight, len);
clearIfNonNull(reverbWetLeft, len);
clearIfNonNull(reverbWetRight, len);
return;
}
Bit32u pos = 0;
// First, check for data in the prerender buffer and spit that out before generating anything new.
// Note that the prerender buffer is rarely used - see comments elsewhere for details.
checkPrerender(nonReverbLeft, nonReverbRight, reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, pos, len);
void Synth::renderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len) {
while (len > 0) {
Bit32u thisLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len;
doRenderStreams(
streamOffset(nonReverbLeft, pos),
streamOffset(nonReverbRight, pos),
streamOffset(reverbDryLeft, pos),
streamOffset(reverbDryRight, pos),
streamOffset(reverbWetLeft, pos),
streamOffset(reverbWetRight, pos),
thisLen);
// We need to ensure zero-duration notes will play so add minimum 1-sample delay.
Bit32u thisLen = 1;
if (!isAbortingPoly()) {
const MidiEvent *nextEvent = midiQueue->peekMidiEvent();
Bit32s samplesToNextEvent = (nextEvent != NULL) ? Bit32s(nextEvent->timestamp - renderedSampleCount) : MAX_SAMPLES_PER_RUN;
if (samplesToNextEvent > 0) {
thisLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len;
if (thisLen > (Bit32u)samplesToNextEvent) {
thisLen = samplesToNextEvent;
}
} else {
if (nextEvent->sysexData == NULL) {
playMsgNow(nextEvent->shortMessageData);
// If a poly is aborting we don't drop the event from the queue.
// Instead, we'll return to it again when the abortion is done.
if (!isAbortingPoly()) {
midiQueue->dropMidiEvent();
}
} else {
playSysexNow(nextEvent->sysexData, nextEvent->sysexLength);
midiQueue->dropMidiEvent();
}
}
}
doRenderStreams(nonReverbLeft, nonReverbRight, reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, thisLen);
advanceStreamPosition(nonReverbLeft, thisLen);
advanceStreamPosition(nonReverbRight, thisLen);
advanceStreamPosition(reverbDryLeft, thisLen);
advanceStreamPosition(reverbDryRight, thisLen);
advanceStreamPosition(reverbWetLeft, thisLen);
advanceStreamPosition(reverbWetRight, thisLen);
len -= thisLen;
pos += thisLen;
}
}
// FIXME: Using more temporary buffers than we need to
void Synth::doRenderStreams(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u len) {
clearFloats(&tmpBufMixLeft[0], &tmpBufMixRight[0], len);
if (!reverbEnabled) {
for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
if (partialManager->produceOutput(i, &tmpBufPartialLeft[0], &tmpBufPartialRight[0], len)) {
mix(&tmpBufMixLeft[0], &tmpBufPartialLeft[0], len);
mix(&tmpBufMixRight[0], &tmpBufPartialRight[0], len);
}
}
if (nonReverbLeft != NULL) {
la32FloatToBit16sFunc(nonReverbLeft, &tmpBufMixLeft[0], len, outputGain);
}
if (nonReverbRight != NULL) {
la32FloatToBit16sFunc(nonReverbRight, &tmpBufMixRight[0], len, outputGain);
}
clearIfNonNull(reverbDryLeft, len);
clearIfNonNull(reverbDryRight, len);
clearIfNonNull(reverbWetLeft, len);
clearIfNonNull(reverbWetRight, len);
void Synth::convertSamplesToOutput(Sample *target, const Sample *source, Bit32u len, bool reverb) {
if (target == NULL) return;
if (dacInputMode == DACInputMode_PURE) {
memcpy(target, source, len * sizeof(Sample));
return;
}
#if MT32EMU_USE_FLOAT_SAMPLES
float gain = reverb ? reverbOutputGain * CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR : 2.0f * outputGain;
while (len--) {
*(target++) = *(source++) * gain;
}
#else
int gain;
if (reverb) {
gain = int(reverbOutputGain * CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR);
} else {
for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
if (!partialManager->shouldReverb(i)) {
if (partialManager->produceOutput(i, &tmpBufPartialLeft[0], &tmpBufPartialRight[0], len)) {
mix(&tmpBufMixLeft[0], &tmpBufPartialLeft[0], len);
mix(&tmpBufMixRight[0], &tmpBufPartialRight[0], len);
}
gain = outputGain;
switch (dacInputMode) {
case DACInputMode_NICE:
// Since we're not shooting for accuracy here, don't worry about the rounding mode.
gain <<= 1;
break;
case DACInputMode_GENERATION1:
while (len--) {
*target = clipBit16s(Bit32s((*source * gain) >> 8));
*target = (*target & 0x8000) | ((*target << 1) & 0x7FFE);
source++;
target++;
}
}
if (nonReverbLeft != NULL) {
la32FloatToBit16sFunc(nonReverbLeft, &tmpBufMixLeft[0], len, outputGain);
}
if (nonReverbRight != NULL) {
la32FloatToBit16sFunc(nonReverbRight, &tmpBufMixRight[0], len, outputGain);
}
clearFloats(&tmpBufMixLeft[0], &tmpBufMixRight[0], len);
for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
if (partialManager->shouldReverb(i)) {
if (partialManager->produceOutput(i, &tmpBufPartialLeft[0], &tmpBufPartialRight[0], len)) {
mix(&tmpBufMixLeft[0], &tmpBufPartialLeft[0], len);
mix(&tmpBufMixRight[0], &tmpBufPartialRight[0], len);
}
return;
case DACInputMode_GENERATION2:
while (len--) {
*target = clipBit16s(Bit32s((*source * gain) >> 8));
*target = (*target & 0x8000) | ((*target << 1) & 0x7FFE) | ((*target >> 14) & 0x0001);
source++;
target++;
}
}
if (reverbDryLeft != NULL) {
la32FloatToBit16sFunc(reverbDryLeft, &tmpBufMixLeft[0], len, outputGain);
}
if (reverbDryRight != NULL) {
la32FloatToBit16sFunc(reverbDryRight, &tmpBufMixRight[0], len, outputGain);
}
// FIXME: Note that on the real devices, reverb input and output are signed linear 16-bit (well, kinda, there's some fudging) PCM, not float.
reverbModel->process(&tmpBufMixLeft[0], &tmpBufMixRight[0], &tmpBufReverbOutLeft[0], &tmpBufReverbOutRight[0], len);
if (reverbWetLeft != NULL) {
reverbFloatToBit16sFunc(reverbWetLeft, &tmpBufReverbOutLeft[0], len, reverbOutputGain);
}
if (reverbWetRight != NULL) {
reverbFloatToBit16sFunc(reverbWetRight, &tmpBufReverbOutRight[0], len, reverbOutputGain);
return;
default:
break;
}
}
while (len--) {
*(target++) = clipBit16s(Bit32s((*(source++) * gain) >> 8));
}
#endif
}
void Synth::doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len) {
if (isEnabled) {
Sample tmpBufMixLeft[MAX_SAMPLES_PER_RUN], tmpBufMixRight[MAX_SAMPLES_PER_RUN];
muteStream(tmpBufMixLeft, len);
muteStream(tmpBufMixRight, len);
for (unsigned int i = 0; i < getPartialCount(); i++) {
if (!reverbEnabled || !partialManager->shouldReverb(i)) {
partialManager->produceOutput(i, tmpBufMixLeft, tmpBufMixRight, len);
}
}
convertSamplesToOutput(nonReverbLeft, tmpBufMixLeft, len, false);
convertSamplesToOutput(nonReverbRight, tmpBufMixRight, len, false);
} else {
muteStream(nonReverbLeft, len);
muteStream(nonReverbRight, len);
}
if (isEnabled && reverbEnabled) {
Sample tmpBufMixLeft[MAX_SAMPLES_PER_RUN], tmpBufMixRight[MAX_SAMPLES_PER_RUN];
muteStream(tmpBufMixLeft, len);
muteStream(tmpBufMixRight, len);
for (unsigned int i = 0; i < getPartialCount(); i++) {
if (partialManager->shouldReverb(i)) {
partialManager->produceOutput(i, tmpBufMixLeft, tmpBufMixRight, len);
}
}
convertSamplesToOutput(reverbDryLeft, tmpBufMixLeft, len, false);
convertSamplesToOutput(reverbDryRight, tmpBufMixRight, len, false);
Sample tmpBufReverbOutLeft[MAX_SAMPLES_PER_RUN], tmpBufReverbOutRight[MAX_SAMPLES_PER_RUN];
reverbModel->process(tmpBufMixLeft, tmpBufMixRight, tmpBufReverbOutLeft, tmpBufReverbOutRight, len);
convertSamplesToOutput(reverbWetLeft, tmpBufReverbOutLeft, len, true);
convertSamplesToOutput(reverbWetRight, tmpBufReverbOutRight, len, true);
} else {
muteStream(reverbDryLeft, len);
muteStream(reverbDryRight, len);
muteStream(reverbWetLeft, len);
muteStream(reverbWetRight, len);
}
partialManager->clearAlreadyOutputed();
renderedSampleCount += len;
}
@ -1502,19 +1566,14 @@ void Synth::printPartialUsage(unsigned long sampleOffset) {
unsigned int partialUsage[9];
partialManager->getPerPartPartialUsage(partialUsage);
if (sampleOffset > 0) {
printDebug("[+%lu] Partial Usage: 1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d R: %02d TOTAL: %02d", sampleOffset, partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7], partialUsage[8], MT32EMU_MAX_PARTIALS - partialManager->getFreePartialCount());
printDebug("[+%lu] Partial Usage: 1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d R: %02d TOTAL: %02d", sampleOffset, partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7], partialUsage[8], getPartialCount() - partialManager->getFreePartialCount());
} else {
printDebug("Partial Usage: 1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d R: %02d TOTAL: %02d", partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7], partialUsage[8], MT32EMU_MAX_PARTIALS - partialManager->getFreePartialCount());
printDebug("Partial Usage: 1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d R: %02d TOTAL: %02d", partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7], partialUsage[8], getPartialCount() - partialManager->getFreePartialCount());
}
}
bool Synth::hasActivePartials() const {
if (prerenderReadIx != prerenderWriteIx) {
// Data in the prerender buffer means that the current isActive() states are "in the future".
// It also means that partials are definitely active at this render point.
return true;
}
for (int partialNum = 0; partialNum < MT32EMU_MAX_PARTIALS; partialNum++) {
for (unsigned int partialNum = 0; partialNum < getPartialCount(); partialNum++) {
if (partialManager->getPartial(partialNum)->isActive()) {
return true;
}
@ -1522,6 +1581,10 @@ bool Synth::hasActivePartials() const {
return false;
}
bool Synth::isAbortingPoly() const {
return abortingPoly != NULL;
}
bool Synth::isActive() const {
if (hasActivePartials()) {
return true;
@ -1536,6 +1599,10 @@ const Partial *Synth::getPartial(unsigned int partialNum) const {
return partialManager->getPartial(partialNum);
}
unsigned int Synth::getPartialCount() const {
return partialCount;
}
const Part *Synth::getPart(unsigned int partNum) const {
if (partNum > 8) {
return NULL;

View File

@ -27,6 +27,7 @@ class Partial;
class PartialManager;
class Part;
class ROMImage;
class BReverbModel;
/**
* Methods for emulating the connection between the LA32 and the DAC, which involves
@ -44,6 +45,7 @@ enum DACInputMode {
// * Much less likely to overdrive than any other mode.
// * Half the volume of any of the other modes, meaning its volume relative to the reverb
// output when mixed together directly will sound wrong.
// * Output gain is ignored for both LA32 and reverb output.
// * Perfect for developers while debugging :)
DACInputMode_PURE,
@ -58,7 +60,17 @@ enum DACInputMode {
DACInputMode_GENERATION2
};
typedef void (*FloatToBit16sFunc)(Bit16s *target, const float *source, Bit32u len, float outputGain);
enum MIDIDelayMode {
// Process incoming MIDI events immediately.
MIDIDelayMode_IMMEDIATE,
// Delay incoming short MIDI messages as if they where transferred via a MIDI cable to a real hardware unit and immediate sysex processing.
// This ensures more accurate timing of simultaneous NoteOn messages.
MIDIDelayMode_DELAY_SHORT_MESSAGES_ONLY,
// Delay all incoming MIDI events as if they where transferred via a MIDI cable to a real hardware unit.
MIDIDelayMode_DELAY_ALL
};
const Bit8u SYSEX_MANUFACTURER_ROLAND = 0x41;
@ -217,18 +229,6 @@ public:
ResetMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Reset, MT32EMU_MEMADDR(0x7F0000), 0x3FFF, 1) {}
};
class ReverbModel {
public:
virtual ~ReverbModel() {}
// After construction or a close(), open() will be called at least once before any other call (with the exception of close()).
virtual void open() = 0;
// May be called multiple times without an open() in between.
virtual void close() = 0;
virtual void setParameters(Bit8u time, Bit8u level) = 0;
virtual void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) = 0;
virtual bool isActive() const = 0;
};
class ReportHandler {
friend class Synth;
@ -244,15 +244,55 @@ protected:
virtual void onErrorControlROM() {}
virtual void onErrorPCMROM() {}
virtual void showLCDMessage(const char *message);
virtual void onMIDIMessagePlayed() {}
virtual void onDeviceReset() {}
virtual void onDeviceReconfig() {}
virtual void onNewReverbMode(Bit8u /* mode */) {}
virtual void onNewReverbTime(Bit8u /* time */) {}
virtual void onNewReverbLevel(Bit8u /* level */) {}
virtual void onPartStateChanged(int /* partNum */, bool /* isActive */) {}
virtual void onPolyStateChanged(int /* partNum */) {}
virtual void onPartialStateChanged(int /* partialNum */, int /* oldPartialPhase */, int /* newPartialPhase */) {}
virtual void onProgramChanged(int /* partNum */, char * /* patchName */) {}
virtual void onProgramChanged(int /* partNum */, int /* bankNum */, const char * /* patchName */) {}
};
/**
* Used to safely store timestamped MIDI events in a local queue.
*/
struct MidiEvent {
Bit32u shortMessageData;
const Bit8u *sysexData;
Bit32u sysexLength;
Bit32u timestamp;
~MidiEvent();
void setShortMessage(Bit32u shortMessageData, Bit32u timestamp);
void setSysex(const Bit8u *sysexData, Bit32u sysexLength, Bit32u timestamp);
};
/**
* Simple queue implementation using a ring buffer to store incoming MIDI event before the synth actually processes it.
* It is intended to:
* - get rid of prerenderer while retaining graceful partial abortion
* - add fair emulation of the MIDI interface delays
* - extend the synth interface with the default implementation of a typical rendering loop.
* THREAD SAFETY:
* It is safe to use either in a single thread environment or when there are only two threads - one performs only reading
* and one performs only writing. More complicated usage requires external synchronisation.
*/
class MidiEventQueue {
private:
MidiEvent *ringBuffer;
Bit32u ringBufferSize;
volatile Bit32u startPosition;
volatile Bit32u endPosition;
public:
MidiEventQueue(Bit32u ringBufferSize = DEFAULT_MIDI_EVENT_QUEUE_SIZE);
~MidiEventQueue();
void reset();
bool pushShortMessage(Bit32u shortMessageData, Bit32u timestamp);
bool pushSysex(const Bit8u *sysexData, Bit32u sysexLength, Bit32u timestamp);
const MidiEvent *peekMidiEvent();
void dropMidiEvent();
};
class Synth {
@ -260,6 +300,7 @@ friend class Part;
friend class RhythmPart;
friend class Poly;
friend class Partial;
friend class PartialManager;
friend class Tables;
friend class MemoryRegion;
friend class TVA;
@ -286,22 +327,32 @@ private:
Bit16s *pcmROMData;
size_t pcmROMSize; // This is in 16-bit samples, therefore half the number of bytes in the ROM
Bit8s chantable[32];
Bit32u renderedSampleCount;
unsigned int partialCount;
Bit8s chantable[32]; // FIXME: Need explanation why 32 is set, obviously it should be 16
MidiEventQueue *midiQueue;
volatile Bit32u lastReceivedMIDIEventTimestamp;
volatile Bit32u renderedSampleCount;
MemParams mt32ram, mt32default;
ReverbModel *reverbModels[4];
ReverbModel *reverbModel;
BReverbModel *reverbModels[4];
BReverbModel *reverbModel;
bool reverbEnabled;
bool reverbOverridden;
FloatToBit16sFunc la32FloatToBit16sFunc;
FloatToBit16sFunc reverbFloatToBit16sFunc;
MIDIDelayMode midiDelayMode;
DACInputMode dacInputMode;
#if MT32EMU_USE_FLOAT_SAMPLES
float outputGain;
float reverbOutputGain;
#else
int outputGain;
int reverbOutputGain;
#endif
bool reversedStereoEnabled;
bool isOpen;
@ -311,41 +362,18 @@ private:
PartialManager *partialManager;
Part *parts[9];
// FIXME: We can reorganise things so that we don't need all these separate tmpBuf, tmp and prerender buffers.
// This should be rationalised when things have stabilised a bit (if prerender buffers don't die in the mean time).
float tmpBufPartialLeft[MAX_SAMPLES_PER_RUN];
float tmpBufPartialRight[MAX_SAMPLES_PER_RUN];
float tmpBufMixLeft[MAX_SAMPLES_PER_RUN];
float tmpBufMixRight[MAX_SAMPLES_PER_RUN];
float tmpBufReverbOutLeft[MAX_SAMPLES_PER_RUN];
float tmpBufReverbOutRight[MAX_SAMPLES_PER_RUN];
Bit16s tmpNonReverbLeft[MAX_SAMPLES_PER_RUN];
Bit16s tmpNonReverbRight[MAX_SAMPLES_PER_RUN];
Bit16s tmpReverbDryLeft[MAX_SAMPLES_PER_RUN];
Bit16s tmpReverbDryRight[MAX_SAMPLES_PER_RUN];
Bit16s tmpReverbWetLeft[MAX_SAMPLES_PER_RUN];
Bit16s tmpReverbWetRight[MAX_SAMPLES_PER_RUN];
// These ring buffers are only used to simulate delays present on the real device.
// In particular, when a partial needs to be aborted to free it up for use by a new Poly,
// When a partial needs to be aborted to free it up for use by a new Poly,
// the controller will busy-loop waiting for the sound to finish.
Bit16s prerenderNonReverbLeft[MAX_PRERENDER_SAMPLES];
Bit16s prerenderNonReverbRight[MAX_PRERENDER_SAMPLES];
Bit16s prerenderReverbDryLeft[MAX_PRERENDER_SAMPLES];
Bit16s prerenderReverbDryRight[MAX_PRERENDER_SAMPLES];
Bit16s prerenderReverbWetLeft[MAX_PRERENDER_SAMPLES];
Bit16s prerenderReverbWetRight[MAX_PRERENDER_SAMPLES];
int prerenderReadIx;
int prerenderWriteIx;
// We emulate this by delaying new MIDI events processing until abortion finishes.
Poly *abortingPoly;
bool prerender();
void copyPrerender(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u pos, Bit32u len);
void checkPrerender(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u &pos, Bit32u &len);
void doRenderStreams(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u len);
Bit32u getShortMessageLength(Bit32u msg);
Bit32u addMIDIInterfaceDelay(Bit32u len, Bit32u timestamp);
void convertSamplesToOutput(Sample *target, const Sample *source, Bit32u len, bool reverb);
bool isAbortingPoly() const;
void doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len);
void playAddressedSysex(unsigned char channel, const Bit8u *sysex, Bit32u len);
void readSysex(unsigned char channel, const Bit8u *sysex, Bit32u len) const;
void initMemoryRegions();
void deleteMemoryRegions();
@ -370,13 +398,19 @@ private:
void printPartialUsage(unsigned long sampleOffset = 0);
void partStateChanged(int partNum, bool isPartActive);
void polyStateChanged(int partNum);
void partialStateChanged(const Partial * const partial, int oldPartialPhase, int newPartialPhase);
void newTimbreSet(int partNum, char patchName[]);
void newTimbreSet(int partNum, Bit8u timbreGroup, const char patchName[]);
void printDebug(const char *fmt, ...);
public:
static inline Bit16s clipBit16s(Bit32s sample) {
// Clamp values above 32767 to 32767, and values below -32768 to -32768
if ((sample + 32768) & ~65535) {
return (sample >> 31) ^ 32767;
}
return (Bit16s)sample;
}
static Bit8u calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum);
// Optionally sets callbacks for reporting various errors, information and debug messages
@ -386,18 +420,44 @@ public:
// Used to initialise the MT-32. Must be called before any other function.
// Returns true if initialization was sucessful, otherwise returns false.
// controlROMImage and pcmROMImage represent Control and PCM ROM images for use by synth.
bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage);
// usePartialCount sets the maximum number of partials playing simultaneously for this session.
bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount = DEFAULT_MAX_PARTIALS);
// Closes the MT-32 and deallocates any memory used by the synthesizer
void close(void);
// Sends a 4-byte MIDI message to the MT-32 for immediate playback
void playMsg(Bit32u msg);
// All the enqueued events are processed by the synth immediately.
void flushMIDIQueue();
// Sets size of the internal MIDI event queue.
// The queue is flushed before reallocation.
void setMIDIEventQueueSize(Bit32u);
// Enqueues a MIDI event for subsequent playback.
// The minimum delay involves the delay introduced while the event is transferred via MIDI interface
// and emulation of the MCU busy-loop while it frees partials for use by a new Poly.
// Calls from multiple threads must be synchronised, although,
// no synchronisation is required with the rendering thread.
// The MIDI event will be processed not before the specified timestamp.
// The timestamp is measured as the global rendered sample count since the synth was created.
bool playMsg(Bit32u msg, Bit32u timestamp);
bool playSysex(const Bit8u *sysex, Bit32u len, Bit32u timestamp);
// The MIDI event will be processed ASAP.
bool playMsg(Bit32u msg);
bool playSysex(const Bit8u *sysex, Bit32u len);
// WARNING:
// The methods below don't ensure minimum 1-sample delay between sequential MIDI events,
// and a sequence of NoteOn and immediately succeeding NoteOff messages is always silent.
// Sends a 4-byte MIDI message to the MT-32 for immediate playback.
void playMsgNow(Bit32u msg);
void playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity);
// Sends a string of Sysex commands to the MT-32 for immediate interpretation
// The length is in bytes
void playSysex(const Bit8u *sysex, Bit32u len);
void playSysexNow(const Bit8u *sysex, Bit32u len);
void playSysexWithoutFraming(const Bit8u *sysex, Bit32u len);
void playSysexWithoutHeader(unsigned char device, unsigned char command, const Bit8u *sysex, Bit32u len);
void writeSysex(unsigned char channel, const Bit8u *sysex, Bit32u len);
@ -407,20 +467,34 @@ public:
void setReverbOverridden(bool reverbOverridden);
bool isReverbOverridden() const;
void setDACInputMode(DACInputMode mode);
DACInputMode getDACInputMode() const;
void setMIDIDelayMode(MIDIDelayMode mode);
MIDIDelayMode getMIDIDelayMode() const;
// Sets output gain factor. Applied to all output samples and unrelated with the synth's Master volume.
// Ignored in DACInputMode_PURE
void setOutputGain(float);
float getOutputGain() const;
// Sets output gain factor for the reverb wet output. setOutputGain() doesn't change reverb output gain.
// Note: We're currently emulate CM-32L/CM-64 reverb quite accurately and the reverb output level closely
// corresponds to the level of digital capture. Although, according to the CM-64 PCB schematic,
// there is a difference in the reverb analogue circuit, and the resulting output gain is 0.68
// of that for LA32 analogue output. This factor is applied to the reverb output gain.
// Ignored in DACInputMode_PURE
void setReverbOutputGain(float);
float getReverbOutputGain() const;
void setReversedStereoEnabled(bool enabled);
bool isReversedStereoEnabled();
// Renders samples to the specified output stream.
// The length is in frames, not bytes (in 16-bit stereo,
// one frame is 4 bytes).
void render(Bit16s *stream, Bit32u len);
void render(Sample *stream, Bit32u len);
// Renders samples to the specified output streams (any or all of which may be NULL).
void renderStreams(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u len);
void renderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len);
// Returns true when there is at least one active partial, otherwise false.
bool hasActivePartials() const;
@ -430,6 +504,9 @@ public:
const Partial *getPartial(unsigned int partialNum) const;
// Returns the maximum number of partials playing simultaneously.
unsigned int getPartialCount() const;
void readMemory(Bit32u addr, Bit32u len, Bit8u *data);
// partNum should be 0..7 for Part 1..8, or 8 for Rhythm

View File

@ -34,9 +34,6 @@ TVA::TVA(const Partial *usePartial, LA32Ramp *useAmpRamp) :
}
void TVA::startRamp(Bit8u newTarget, Bit8u newIncrement, int newPhase) {
if (newPhase != phase) {
partial->getSynth()->partialStateChanged(partial, phase, newPhase);
}
target = newTarget;
phase = newPhase;
ampRamp->startRamp(newTarget, newIncrement);
@ -46,9 +43,6 @@ void TVA::startRamp(Bit8u newTarget, Bit8u newIncrement, int newPhase) {
}
void TVA::end(int newPhase) {
if (newPhase != phase) {
partial->getSynth()->partialStateChanged(partial, phase, newPhase);
}
phase = newPhase;
playing = false;
#if MT32EMU_MONITOR_TVA >= 1

View File

@ -181,7 +181,7 @@ void TVP::updatePitch() {
pitch = (Bit16u)newPitch;
// FIXME: We're doing this here because that's what the CM-32L does - we should probably move this somewhere more appropriate in future.
partial->tva->recalcSustain();
partial->getTVA()->recalcSustain();
}
void TVP::targetPitchOffsetReached() {

View File

@ -25,6 +25,11 @@ namespace MT32Emu {
// The output from the synth is supposed to be resampled to convert the sample rate.
const unsigned int SAMPLE_RATE = 32000;
// MIDI interface data transfer rate in samples. Used to simulate the transfer delay.
const double MIDI_DATA_TRANSFER_RATE = (double)SAMPLE_RATE / 31250.0 * 8.0;
const float CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR = 0.68f;
const int MIDDLEC = 60;
class Synth;

View File

@ -1,324 +0,0 @@
// Allpass filter implementation
//
// Written by Jezar at Dreampoint, June 2000
// http://www.dreampoint.co.uk
// This code is public domain
#include "freeverb.h"
allpass::allpass()
{
bufidx = 0;
}
void allpass::setbuffer(float *buf, int size)
{
buffer = buf;
bufsize = size;
}
void allpass::mute()
{
for (int i=0; i<bufsize; i++)
buffer[i]=0;
}
void allpass::setfeedback(float val)
{
feedback = val;
}
float allpass::getfeedback()
{
return feedback;
}
void allpass::deletebuffer()
{
delete[] buffer;
buffer = 0;
}
// Comb filter implementation
//
// Written by Jezar at Dreampoint, June 2000
// http://www.dreampoint.co.uk
// This code is public domain
comb::comb()
{
filterstore = 0;
bufidx = 0;
}
void comb::setbuffer(float *buf, int size)
{
buffer = buf;
bufsize = size;
}
void comb::mute()
{
for (int i=0; i<bufsize; i++)
buffer[i]=0;
}
void comb::setdamp(float val)
{
damp1 = val;
damp2 = 1-val;
}
float comb::getdamp()
{
return damp1;
}
void comb::setfeedback(float val)
{
feedback = val;
}
float comb::getfeedback()
{
return feedback;
}
void comb::deletebuffer()
{
delete[] buffer;
buffer = 0;
}
// Reverb model implementation
//
// Written by Jezar at Dreampoint, June 2000
// Modifications by Jerome Fisher, 2009, 2011
// http://www.dreampoint.co.uk
// This code is public domain
revmodel::revmodel(float scaletuning)
{
int i;
int bufsize;
// Allocate buffers for the components
for (i = 0; i < numcombs; i++) {
bufsize = int(scaletuning * combtuning[i]);
combL[i].setbuffer(new float[bufsize], bufsize);
bufsize += int(scaletuning * stereospread);
combR[i].setbuffer(new float[bufsize], bufsize);
}
for (i = 0; i < numallpasses; i++) {
bufsize = int(scaletuning * allpasstuning[i]);
allpassL[i].setbuffer(new float[bufsize], bufsize);
allpassL[i].setfeedback(0.5f);
bufsize += int(scaletuning * stereospread);
allpassR[i].setbuffer(new float[bufsize], bufsize);
allpassR[i].setfeedback(0.5f);
}
// Set default values
dry = initialdry;
wet = initialwet*scalewet;
damp = initialdamp*scaledamp;
roomsize = (initialroom*scaleroom) + offsetroom;
width = initialwidth;
mode = initialmode;
update();
// Buffer will be full of rubbish - so we MUST mute them
mute();
}
revmodel::~revmodel()
{
int i;
for (i = 0; i < numcombs; i++) {
combL[i].deletebuffer();
combR[i].deletebuffer();
}
for (i = 0; i < numallpasses; i++) {
allpassL[i].deletebuffer();
allpassR[i].deletebuffer();
}
}
void revmodel::mute()
{
int i;
if (getmode() >= freezemode)
return;
for (i=0;i<numcombs;i++)
{
combL[i].mute();
combR[i].mute();
}
for (i=0;i<numallpasses;i++)
{
allpassL[i].mute();
allpassR[i].mute();
}
// Init LPF history
filtprev1 = 0;
filtprev2 = 0;
}
void revmodel::process(const float *inputL, const float *inputR, float *outputL, float *outputR, long numsamples)
{
float outL,outR,input;
while (numsamples-- > 0)
{
int i;
outL = outR = 0;
input = (*inputL + *inputR) * gain;
// Implementation of 2-stage IIR single-pole low-pass filter
// found at the entrance of reverb processing on real devices
filtprev1 += (input - filtprev1) * filtval;
filtprev2 += (filtprev1 - filtprev2) * filtval;
input = filtprev2;
int s = -1;
// Accumulate comb filters in parallel
for (i=0; i<numcombs; i++)
{
outL += s * combL[i].process(input);
outR += s * combR[i].process(input);
s = -s;
}
// Feed through allpasses in series
for (i=0; i<numallpasses; i++)
{
outL = allpassL[i].process(outL);
outR = allpassR[i].process(outR);
}
// Calculate output REPLACING anything already there
*outputL = outL*wet1 + outR*wet2;
*outputR = outR*wet1 + outL*wet2;
inputL++;
inputR++;
outputL++;
outputR++;
}
}
void revmodel::update()
{
// Recalculate internal values after parameter change
int i;
wet1 = wet*(width/2 + 0.5f);
wet2 = wet*((1-width)/2);
if (mode >= freezemode)
{
roomsize1 = 1;
damp1 = 0;
gain = muted;
}
else
{
roomsize1 = roomsize;
damp1 = damp;
gain = fixedgain;
}
for (i=0; i<numcombs; i++)
{
combL[i].setfeedback(roomsize1);
combR[i].setfeedback(roomsize1);
}
for (i=0; i<numcombs; i++)
{
combL[i].setdamp(damp1);
combR[i].setdamp(damp1);
}
}
// The following get/set functions are not inlined, because
// speed is never an issue when calling them, and also
// because as you develop the reverb model, you may
// wish to take dynamic action when they are called.
void revmodel::setroomsize(float value)
{
roomsize = (value*scaleroom) + offsetroom;
update();
}
float revmodel::getroomsize()
{
return (roomsize-offsetroom)/scaleroom;
}
void revmodel::setdamp(float value)
{
damp = value*scaledamp;
update();
}
float revmodel::getdamp()
{
return damp/scaledamp;
}
void revmodel::setwet(float value)
{
wet = value*scalewet;
update();
}
float revmodel::getwet()
{
return wet/scalewet;
}
void revmodel::setdry(float value)
{
dry = value*scaledry;
}
float revmodel::getdry()
{
return dry/scaledry;
}
void revmodel::setwidth(float value)
{
width = value;
update();
}
float revmodel::getwidth()
{
return width;
}
void revmodel::setmode(float value)
{
mode = value;
update();
}
float revmodel::getmode()
{
if (mode >= freezemode)
return 1;
else
return 0;
}
void revmodel::setfiltval(float value)
{
filtval = value;
}

View File

@ -1,189 +0,0 @@
#ifndef _freeverb_
#define _freeverb_
// Reverb model tuning values
//
// Written by Jezar at Dreampoint, June 2000
// http://www.dreampoint.co.uk
// This code is public domain
const int numcombs = 8;
const int numallpasses = 4;
const float muted = 0;
const float fixedgain = 0.015f;
const float scalewet = 3;
const float scaledry = 2;
const float scaledamp = 0.4f;
const float scaleroom = 0.28f;
const float offsetroom = 0.7f;
const float initialroom = 0.5f;
const float initialdamp = 0.5f;
const float initialwet = 1/scalewet;
const float initialdry = 0;
const float initialwidth = 1;
const float initialmode = 0;
const float freezemode = 0.5f;
const int stereospread = 23;
const int combtuning[] = {1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617};
const int allpasstuning[] = {556, 441, 341, 225};
// Macro for killing denormalled numbers
//
// Written by Jezar at Dreampoint, June 2000
// http://www.dreampoint.co.uk
// Based on IS_DENORMAL macro by Jon Watte
// This code is public domain
static inline float undenormalise(float x) {
union {
float f;
unsigned int i;
} u;
u.f = x;
if ((u.i & 0x7f800000) == 0) {
return 0.0f;
}
return x;
}
// Allpass filter declaration
//
// Written by Jezar at Dreampoint, June 2000
// http://www.dreampoint.co.uk
// This code is public domain
class allpass
{
public:
allpass();
void setbuffer(float *buf, int size);
void deletebuffer();
inline float process(float inp);
void mute();
void setfeedback(float val);
float getfeedback();
// private:
float feedback;
float *buffer;
int bufsize;
int bufidx;
};
// Big to inline - but crucial for speed
inline float allpass::process(float input)
{
float output;
float bufout;
bufout = undenormalise(buffer[bufidx]);
output = -input + bufout;
buffer[bufidx] = input + (bufout*feedback);
if (++bufidx>=bufsize) bufidx = 0;
return output;
}
// Comb filter class declaration
//
// Written by Jezar at Dreampoint, June 2000
// http://www.dreampoint.co.uk
// This code is public domain
class comb
{
public:
comb();
void setbuffer(float *buf, int size);
void deletebuffer();
inline float process(float inp);
void mute();
void setdamp(float val);
float getdamp();
void setfeedback(float val);
float getfeedback();
private:
float feedback;
float filterstore;
float damp1;
float damp2;
float *buffer;
int bufsize;
int bufidx;
};
// Big to inline - but crucial for speed
inline float comb::process(float input)
{
float output;
output = undenormalise(buffer[bufidx]);
filterstore = undenormalise((output*damp2) + (filterstore*damp1));
buffer[bufidx] = input + (filterstore*feedback);
if (++bufidx>=bufsize) bufidx = 0;
return output;
}
// Reverb model declaration
//
// Written by Jezar at Dreampoint, June 2000
// Modifications by Jerome Fisher, 2009
// http://www.dreampoint.co.uk
// This code is public domain
class revmodel
{
public:
revmodel(float scaletuning);
~revmodel();
void mute();
void process(const float *inputL, const float *inputR, float *outputL, float *outputR, long numsamples);
void setroomsize(float value);
float getroomsize();
void setdamp(float value);
float getdamp();
void setwet(float value);
float getwet();
void setdry(float value);
float getdry();
void setwidth(float value);
float getwidth();
void setmode(float value);
float getmode();
void setfiltval(float value);
private:
void update();
private:
float gain;
float roomsize,roomsize1;
float damp,damp1;
float wet,wet1,wet2;
float dry;
float width;
float mode;
// LPF stuff
float filtval;
float filtprev1;
float filtprev2;
// Comb filters
comb combL[numcombs];
comb combR[numcombs];
// Allpass filters
allpass allpassL[numallpasses];
allpass allpassR[numallpasses];
};
#endif//_freeverb_

View File

@ -1,24 +1,19 @@
MODULE := audio/softsynth/mt32
MODULE_OBJS := \
AReverbModel.o \
BReverbModel.o \
DelayReverb.o \
FreeverbModel.o \
LA32Ramp.o \
LA32WaveGenerator.o \
LegacyWaveGenerator.o \
Part.o \
Partial.o \
PartialManager.o \
Poly.o \
ROMInfo.o \
Synth.o \
Tables.o \
TVA.o \
TVF.o \
TVP.o \
Tables.o \
freeverb.o
TVP.o
# Include common rules
include $(srcdir)/rules.mk

View File

@ -60,27 +60,24 @@
#define MT32EMU_MONITOR_TVF 0
// Configuration
// The maximum number of partials playing simultaneously
#define MT32EMU_MAX_PARTIALS 32
// The maximum number of notes playing simultaneously per part.
// No point making it more than MT32EMU_MAX_PARTIALS, since each note needs at least one partial.
#define MT32EMU_MAX_POLY 32
// If non-zero, deletes reverb buffers that are not in use to save memory.
// If zero, keeps reverb buffers for all modes around all the time to avoid allocating/freeing in the critical path.
#define MT32EMU_REDUCE_REVERB_MEMORY 1
// 0: Use legacy Freeverb
// 1: Use Accurate Reverb model aka AReverb
// 2: Use Bit-perfect Boss Reverb model aka BReverb (for developers, not much practical use)
#define MT32EMU_USE_REVERBMODEL 1
// 0: Maximum speed at the cost of a bit lower emulation accuracy.
// 1: Maximum achievable emulation accuracy.
#define MT32EMU_BOSS_REVERB_PRECISE_MODE 0
// 0: Use refined wave generator based on logarithmic fixed-point computations and LUTs
// 1: Use legacy accurate wave generator based on float computations
#define MT32EMU_ACCURATE_WG 0
// 0: Use 16-bit signed samples and refined wave generator based on logarithmic fixed-point computations and LUTs. Maximum emulation accuracy and speed.
// 1: Use float samples in the wave generator and renderer. Maximum output quality and minimum noise.
#define MT32EMU_USE_FLOAT_SAMPLES 0
namespace MT32Emu
{
// The default value for the maximum number of partials playing simultaneously.
const unsigned int DEFAULT_MAX_PARTIALS = 32;
// The higher this number, the more memory will be used, but the more samples can be processed in one run -
// various parts of sample generation can be processed more efficiently in a single run.
// A run's maximum length is that given to Synth::render(), so giving a value here higher than render() is ever
@ -90,11 +87,14 @@ namespace MT32Emu
// This value must be >= 1.
const unsigned int MAX_SAMPLES_PER_RUN = 4096;
// This determines the amount of memory available for simulating delays.
// If set too low, partials aborted to allow other partials to play will not end gracefully, but will terminate
// abruptly and potentially cause a pop/crackle in the audio output.
// This value must be >= 1.
const unsigned int MAX_PRERENDER_SAMPLES = 1024;
// The default size of the internal MIDI event queue.
// It holds the incoming MIDI events before the rendering engine actually processes them.
// The main goal is to fairly emulate the real hardware behaviour which obviously
// uses an internal MIDI event queue to gather incoming data as well as the delays
// introduced by transferring data via the MIDI interface.
// This also facilitates building of an external rendering loop
// as the queue stores timestamped MIDI events.
const unsigned int DEFAULT_MIDI_EVENT_QUEUE_SIZE = 1024;
}
#include "Structures.h"
@ -103,7 +103,6 @@ const unsigned int MAX_PRERENDER_SAMPLES = 1024;
#include "Poly.h"
#include "LA32Ramp.h"
#include "LA32WaveGenerator.h"
#include "LegacyWaveGenerator.h"
#include "TVA.h"
#include "TVP.h"
#include "TVF.h"

View File

@ -110,7 +110,7 @@ public:
/**
* Initialize the specified CD drive for audio playback.
* @param drive the drive id
* @return true if the CD drive was inited succesfully
* @return true if the CD drive was inited successfully
*/
virtual bool openCD(int drive) = 0;

View File

@ -57,7 +57,7 @@ void BaseBackend::initBackend() {
void BaseBackend::fillScreen(uint32 col) {
Graphics::Surface *screen = lockScreen();
if (screen && screen->pixels)
memset(screen->pixels, col, screen->h * screen->pitch);
if (screen && screen->getPixels())
memset(screen->getPixels(), col, screen->h * screen->pitch);
unlockScreen();
}

View File

@ -54,6 +54,8 @@ DefaultEventManager::DefaultEventManager(Common::EventSource *boss) :
_currentKeyDown.ascii = 0;
_currentKeyDown.flags = 0;
_keyRepeatTime = 0;
#ifdef ENABLE_VKEYBD
_vk = new Common::VirtualKeyboard();
#endif
@ -84,7 +86,8 @@ void DefaultEventManager::init() {
}
bool DefaultEventManager::pollEvent(Common::Event &event) {
uint32 time = g_system->getMillis();
// Skip recording of these events
uint32 time = g_system->getMillis(true);
bool result = false;
_dispatcher.dispatch();

View File

@ -106,7 +106,9 @@ void SdlEventSource::processMouseEvent(Common::Event &event, int x, int y) {
}
void SdlEventSource::handleKbdMouse() {
uint32 curTime = g_system->getMillis();
// Skip recording of these events
uint32 curTime = g_system->getMillis(true);
if (curTime >= _km.last_time + _km.delay_time) {
_km.last_time = curTime;
if (_km.x_down_count == 1) {
@ -389,8 +391,17 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) {
return false;
case SDL_VIDEORESIZE:
if (_graphicsManager)
if (_graphicsManager) {
_graphicsManager->notifyResize(ev.resize.w, ev.resize.h);
// If the screen changed, send an Common::EVENT_SCREEN_CHANGED
int screenID = ((OSystem_SDL *)g_system)->getGraphicsManager()->getScreenChangeID();
if (screenID != _lastScreenID) {
_lastScreenID = screenID;
event.type = Common::EVENT_SCREEN_CHANGED;
return true;
}
}
return false;
case SDL_QUIT:

View File

@ -100,7 +100,7 @@ public:
* @param mode Mode to use while listing the directory.
* @param hidden Whether to include hidden files or not in the results.
*
* @return true if succesful, false otherwise (e.g. when the directory does not exist).
* @return true if successful, false otherwise (e.g. when the directory does not exist).
*/
virtual bool getChildren(AbstractFSList &list, ListMode mode, bool hidden) const = 0;

View File

@ -335,7 +335,7 @@ bool AmigaOSFilesystemNode::isReadable() const {
// Regular RWED protection flags are low-active or inverted, thus the negation.
// moreover pseudo root filesystem (null _pFileLock) is readable whatever the
// protection says
bool readable = !(_nProt & EXDF_READ) || _pFileLock == 0;
bool readable = !(_nProt & EXDF_OTR_READ) || _pFileLock == 0;
return readable;
}
@ -344,7 +344,7 @@ bool AmigaOSFilesystemNode::isWritable() const {
// Regular RWED protection flags are low-active or inverted, thus the negation.
// moreover pseudo root filesystem (null _pFileLock) is never writable whatever
// the protection says (because of the pseudo nature)
bool writable = !(_nProt & EXDF_WRITE) && _pFileLock !=0;
bool writable = !(_nProt & EXDF_OTR_WRITE) && _pFileLock !=0;
return writable;
}
@ -367,8 +367,14 @@ AbstractFSList AmigaOSFilesystemNode::listVolumes() const {
dosList = IDOS->NextDosEntry(dosList, LDF_VOLUMES);
while (dosList) {
if (dosList->dol_Type == DLT_VOLUME &&
dosList->dol_Name &&
dosList->dol_Task) {
dosList->dol_Name) {
// Original was
// dosList->dol_Name &&
// dosList->dol_Task) {
// which errored using SDK 53.24 with a 'struct dosList' has no member called 'dol_Task'
// I removed dol_Task because it's not used anywhere else
// and it neither brought up further errors nor crashes or regressions.
// Copy name to buffer
IDOS->CopyStringBSTRToC(dosList->dol_Name, buffer, MAXPATHLEN);

View File

@ -37,6 +37,27 @@ class GraphicsManager : public PaletteManager {
public:
virtual ~GraphicsManager() {}
/**
* Makes this graphics manager active. That means it should be ready to
* process inputs now. However, even without being active it should be
* able to query the supported modes and other bits.
*
* HACK: Actually this is specific to SdlGraphicsManager subclasses.
* But sadly we cannot cast from GraphicsManager to SdlGraphicsManager
* because there is no relation between these two.
*/
virtual void activateManager() {}
/**
* Makes this graphics manager inactive. This should allow another
* graphics manager to become active again.
*
* HACK: Actually this is specific to SdlGraphicsManager subclasses.
* But sadly we cannot cast from GraphicsManager to SdlGraphicsManager
* because there is no relation between these two.
*/
virtual void deactivateManager() {}
virtual bool hasFeature(OSystem::Feature f) = 0;
virtual void setFeatureState(OSystem::Feature f, bool enable) = 0;
virtual bool getFeatureState(OSystem::Feature f) = 0;

View File

@ -0,0 +1,65 @@
/* 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 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "backends/graphics/opengl/debug.h"
#include "backends/graphics/opengl/opengl-sys.h"
#include "common/str.h"
#include "common/textconsole.h"
#ifdef OPENGL_DEBUG
namespace OpenGL {
namespace {
Common::String getGLErrStr(GLenum error) {
switch (error) {
case GL_INVALID_ENUM:
return "GL_INVALID_ENUM";
case GL_INVALID_VALUE:
return "GL_INVALID_VALUE";
case GL_INVALID_OPERATION:
return "GL_INVALID_OPERATION";
case GL_STACK_OVERFLOW:
return "GL_STACK_OVERFLOW";
case GL_STACK_UNDERFLOW:
return "GL_STACK_UNDERFLOW";
case GL_OUT_OF_MEMORY:
return "GL_OUT_OF_MEMORY";
}
return Common::String::format("(Unknown GL error code 0x%X)", error);
}
} // End of anonymous namespace
void checkGLError(const char *expr, const char *file, int line) {
GLenum error = glGetError();
if (error != GL_NO_ERROR) {
// We cannot use error here because we do not know whether we have a
// working screen or not.
warning("GL ERROR: %s on %s (%s:%d)", getGLErrStr(error).c_str(), expr, file, line);
}
}
} // End of namespace OpenGL
#endif

View File

@ -0,0 +1,39 @@
/* 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 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef BACKENDS_GRAPHICS_OPENGL_DEBUG_H
#define BACKENDS_GRAPHICS_OPENGL_DEBUG_H
#define OPENGL_DEBUG
#ifdef OPENGL_DEBUG
namespace OpenGL {
void checkGLError(const char *expr, const char *file, int line);
} // End of namespace OpenGL
#define GLCALL(x) do { (x); OpenGL::checkGLError(#x, __FILE__, __LINE__); } while (false)
#else
#define GLCALL(x) do { (x); } while (false)
#endif
#endif

View File

@ -0,0 +1,48 @@
/* 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 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "backends/graphics/opengl/extensions.h"
#include "backends/graphics/opengl/opengl-sys.h"
#include "common/tokenizer.h"
namespace OpenGL {
bool g_extNPOTSupported = false;
void initializeGLExtensions() {
const char *extString = (const char *)glGetString(GL_EXTENSIONS);
// Initialize default state.
g_extNPOTSupported = false;
Common::StringTokenizer tokenizer(extString, " ");
while (!tokenizer.empty()) {
Common::String token = tokenizer.nextToken();
if (token == "GL_ARB_texture_non_power_of_two") {
g_extNPOTSupported = true;
}
}
}
} // End of namespace OpenGL

View File

@ -0,0 +1,41 @@
/* 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 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef BACKENDS_GRAPHICS_OPENGL_EXTENSIONS_H
#define BACKENDS_GRAPHICS_OPENGL_EXTENSIONS_H
namespace OpenGL {
/**
* Checks for availability of extensions we want to use and initializes them
* when available.
*/
void initializeGLExtensions();
/**
* Whether non power of two textures are supported
*/
extern bool g_extNPOTSupported;
} // End of namespace OpenGL
#endif

View File

@ -1,225 +0,0 @@
/* 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 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.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "common/scummsys.h"
#if defined(USE_OPENGL)
#include "backends/graphics/opengl/gltexture.h"
#include "backends/graphics/opengl/glerrorcheck.h"
#include "common/rect.h"
#include "common/array.h"
#include "common/util.h"
#include "common/tokenizer.h"
// Supported GL extensions
static bool npot_supported = false;
static bool glext_inited = false;
/*static inline GLint xdiv(int numerator, int denominator) {
assert(numerator < (1 << 16));
return (numerator << 16) / denominator;
}*/
static GLuint nextHigher2(GLuint v) {
if (v == 0)
return 1;
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
return ++v;
}
void GLTexture::initGLExtensions() {
// Return if extensions were already checked
if (glext_inited)
return;
// Get a string with all extensions
const char *ext_string = (const char *)glGetString(GL_EXTENSIONS);
CHECK_GL_ERROR();
Common::StringTokenizer tokenizer(ext_string, " ");
// Iterate all string tokens
while (!tokenizer.empty()) {
Common::String token = tokenizer.nextToken();
if (token == "GL_ARB_texture_non_power_of_two")
npot_supported = true;
}
glext_inited = true;
}
GLTexture::GLTexture(byte bpp, GLenum internalFormat, GLenum format, GLenum type)
:
_bytesPerPixel(bpp),
_internalFormat(internalFormat),
_glFormat(format),
_glType(type),
_textureWidth(0),
_textureHeight(0),
_realWidth(0),
_realHeight(0),
_refresh(false),
_filter(GL_NEAREST) {
// Generate the texture ID
glGenTextures(1, &_textureName); CHECK_GL_ERROR();
}
GLTexture::~GLTexture() {
// Delete the texture
glDeleteTextures(1, &_textureName); CHECK_GL_ERROR();
}
void GLTexture::refresh() {
// Delete previous texture
glDeleteTextures(1, &_textureName); CHECK_GL_ERROR();
// Generate the texture ID
glGenTextures(1, &_textureName); CHECK_GL_ERROR();
_refresh = true;
}
void GLTexture::allocBuffer(GLuint w, GLuint h) {
_realWidth = w;
_realHeight = h;
if (!_refresh) {
if (npot_supported && _filter == GL_LINEAR) {
// Check if we already allocated a correctly-sized buffer
// This is so we don't need to duplicate the last row/column
if (w == _textureWidth && h == _textureHeight)
return;
} else {
// Check if we already have a large enough buffer
if (w <= _textureWidth && h <= _textureHeight)
return;
}
}
if (npot_supported) {
_textureWidth = w;
_textureHeight = h;
} else {
_textureWidth = nextHigher2(w);
_textureHeight = nextHigher2(h);
}
// Select this OpenGL texture
glBindTexture(GL_TEXTURE_2D, _textureName); CHECK_GL_ERROR();
// Set the texture parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _filter); CHECK_GL_ERROR();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _filter); CHECK_GL_ERROR();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); CHECK_GL_ERROR();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); CHECK_GL_ERROR();
// Allocate room for the texture
glTexImage2D(GL_TEXTURE_2D, 0, _internalFormat,
_textureWidth, _textureHeight, 0, _glFormat, _glType, NULL); CHECK_GL_ERROR();
_refresh = false;
}
void GLTexture::updateBuffer(const void *buf, int pitch, GLuint x, GLuint y, GLuint w, GLuint h) {
// Skip empty updates.
if (w * h == 0)
return;
// Select this OpenGL texture
glBindTexture(GL_TEXTURE_2D, _textureName); CHECK_GL_ERROR();
// Check if the buffer has its data contiguously
if ((int)w * _bytesPerPixel == pitch) {
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h,
_glFormat, _glType, buf); CHECK_GL_ERROR();
} else {
// Update the texture row by row
const byte *src = (const byte *)buf;
GLuint curY = y;
GLuint height = h;
do {
glTexSubImage2D(GL_TEXTURE_2D, 0, x, curY,
w, 1, _glFormat, _glType, src); CHECK_GL_ERROR();
curY++;
src += pitch;
} while (--height);
}
// If we're in linear filter mode, repeat the last row/column if the real dimensions
// doesn't match the texture dimensions.
if (_filter == GL_LINEAR) {
if (_realWidth != _textureWidth && x + w == _realWidth) {
const byte *src = (const byte *)buf + (w - 1) * _bytesPerPixel;
GLuint curY = y;
GLuint height = h;
do {
glTexSubImage2D(GL_TEXTURE_2D, 0, x + w,
curY, 1, 1, _glFormat, _glType, src); CHECK_GL_ERROR();
curY++;
src += pitch;
} while (--height);
}
if (_realHeight != _textureHeight && y + h == _realHeight) {
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y + h,
w, 1, _glFormat, _glType, (const byte *)buf + pitch * (h - 1)); CHECK_GL_ERROR();
}
}
}
void GLTexture::drawTexture(GLshort x, GLshort y, GLshort w, GLshort h) {
// Select this OpenGL texture
glBindTexture(GL_TEXTURE_2D, _textureName); CHECK_GL_ERROR();
// Calculate the texture rect that will be drawn
const GLfloat texWidth = (GLfloat)_realWidth / _textureWidth;//xdiv(_surface.w, _textureWidth);
const GLfloat texHeight = (GLfloat)_realHeight / _textureHeight;//xdiv(_surface.h, _textureHeight);
const GLfloat texcoords[] = {
0, 0,
texWidth, 0,
0, texHeight,
texWidth, texHeight,
};
glTexCoordPointer(2, GL_FLOAT, 0, texcoords); CHECK_GL_ERROR();
// Calculate the screen rect where the texture will be drawn
const GLshort vertices[] = {
x, y,
(GLshort)(x + w), y,
x, (GLshort)(y + h),
(GLshort)(x + w), (GLshort)(y + h),
};
glVertexPointer(2, GL_SHORT, 0, vertices); CHECK_GL_ERROR();
// Draw the texture to the screen buffer
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); CHECK_GL_ERROR();
}
#endif

View File

@ -1,131 +0,0 @@
/* 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 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.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef BACKENDS_GRAPHICS_OPENGL_GLTEXTURE_H
#define BACKENDS_GRAPHICS_OPENGL_GLTEXTURE_H
#include "common/scummsys.h"
#ifdef WIN32
#if defined(ARRAYSIZE) && !defined(_WINDOWS_)
#undef ARRAYSIZE
#endif
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef ARRAYSIZE
#endif
// HACK: At this point in Windows platforms, common/util.h has been included
// via common/rect.h (from backends/graphics/sdl/sdl-graphics.h), via
// backends/graphics/openglsdl/openglsdl-graphics.h. Thus, we end up with
// COMMON_UTIL_H defined, and ARRAYSIZE undefined (bad!). Therefore,
// ARRAYSIZE is undefined in openglsdl-graphics.cpp. This is a temporary
// hackish solution fo fix compilation under Windows.
#if !defined(ARRAYSIZE) && defined(COMMON_UTIL_H)
#define ARRAYSIZE(x) ((int)(sizeof(x) / sizeof(x[0])))
#endif
#if defined(BADA)
#include <FGraphicsOpengl.h>
using namespace Osp::Graphics::Opengl;
#elif defined(USE_GLES)
#include <GLES/gl.h>
#elif defined(SDL_BACKEND)
#include <SDL_opengl.h>
#else
#include <GL/gl.h>
#endif
#include "graphics/surface.h"
/**
* OpenGL texture manager class
*/
class GLTexture {
public:
/**
* Initialize OpenGL Extensions
*/
static void initGLExtensions();
GLTexture(byte bpp, GLenum internalFormat, GLenum format, GLenum type);
~GLTexture();
/**
* Refresh the texture after a context change. The
* process will be completed on next allocBuffer call.
*/
void refresh();
/**
* Allocates memory needed for the given size.
*/
void allocBuffer(GLuint width, GLuint height);
/**
* Updates the texture pixels.
*/
void updateBuffer(const void *buf, int pitch, GLuint x, GLuint y,
GLuint w, GLuint h);
/**
* Draws the texture to the screen buffer.
*/
void drawTexture(GLshort x, GLshort y, GLshort w, GLshort h);
/**
* Get the texture width.
*/
GLuint getWidth() const { return _realWidth; }
/**
* Get the texture height.
*/
GLuint getHeight() const { return _realHeight; }
/**
* Get the bytes per pixel.
*/
uint getBytesPerPixel() const { return _bytesPerPixel; }
/**
* Set the texture filter.
* @filter the filter type, GL_NEAREST or GL_LINEAR
*/
void setFilter(GLint filter) { _filter = filter; }
private:
const byte _bytesPerPixel;
const GLenum _internalFormat;
const GLenum _glFormat;
const GLenum _glType;
GLuint _realWidth;
GLuint _realHeight;
GLuint _textureName;
GLuint _textureWidth;
GLuint _textureHeight;
GLint _filter;
bool _refresh;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -8,338 +8,464 @@
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef BACKENDS_GRAPHICS_OPENGL_H
#define BACKENDS_GRAPHICS_OPENGL_H
#ifndef BACKENDS_GRAPHICS_OPENGL_OPENGL_GRAPHICS_H
#define BACKENDS_GRAPHICS_OPENGL_OPENGL_GRAPHICS_H
#include "backends/graphics/opengl/gltexture.h"
#include "backends/graphics/opengl/opengl-sys.h"
#include "backends/graphics/graphics.h"
#include "common/array.h"
#include "common/rect.h"
#include "graphics/font.h"
#include "graphics/pixelformat.h"
// Uncomment this to enable the 'on screen display' code.
#define USE_OSD 1
#include "common/frac.h"
#include "common/mutex.h"
namespace Graphics {
class Font;
} // End of namespace Graphics
namespace OpenGL {
// The OpenGL GFX modes. They have to be inside the OpenGL namespace so they
// do not clash with the SDL GFX modes.
// HACK: We use glColor in the OSD code. This might not be working on GL ES but
// we still enable it because Tizen already shipped with it. Also, the
// SurfaceSDL backend enables it and disabling it can cause issues in sdl.cpp.
#define USE_OSD 1
class Texture;
enum {
GFX_NORMAL = 0,
GFX_CONSERVE = 1,
GFX_ORIGINAL = 2
GFX_LINEAR = 0,
GFX_NEAREST = 1
};
}
/**
* OpenGL graphics manager. This is an abstract class, it does not do the
* window and OpenGL context initialization.
* Derived classes should at least override internUpdateScreen for doing
* the buffers swap, and implement loadGFXMode for handling the window/context if
* needed. If USE_RGB_COLOR is enabled, getSupportedFormats must be implemented.
*/
class OpenGLGraphicsManager : public GraphicsManager {
public:
OpenGLGraphicsManager();
virtual ~OpenGLGraphicsManager();
// GraphicsManager API
virtual bool hasFeature(OSystem::Feature f);
virtual void setFeatureState(OSystem::Feature f, bool enable);
virtual bool getFeatureState(OSystem::Feature f);
static const OSystem::GraphicsMode *supportedGraphicsModes();
virtual const OSystem::GraphicsMode *getSupportedGraphicsModes() const;
virtual int getDefaultGraphicsMode() const;
virtual bool setGraphicsMode(int mode);
virtual int getGraphicsMode() const;
virtual void resetGraphicsScale();
virtual void resetGraphicsScale() {}
#ifdef USE_RGB_COLOR
virtual Graphics::PixelFormat getScreenFormat() const;
virtual Common::List<Graphics::PixelFormat> getSupportedFormats() const = 0;
#endif
virtual void initSize(uint width, uint height, const Graphics::PixelFormat *format = NULL);
virtual int getScreenChangeID() const;
virtual void beginGFXTransaction();
virtual OSystem::TransactionError endGFXTransaction();
virtual int16 getHeight();
virtual int16 getWidth();
protected:
// PaletteManager API
virtual void setPalette(const byte *colors, uint start, uint num);
virtual void grabPalette(byte *colors, uint start, uint num);
virtual int getScreenChangeID() const;
virtual void initSize(uint width, uint height, const Graphics::PixelFormat *format);
virtual int16 getWidth();
virtual int16 getHeight();
public:
virtual void copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h);
virtual void fillScreen(uint32 col);
virtual void setShakePos(int shakeOffset);
virtual void updateScreen();
virtual Graphics::Surface *lockScreen();
virtual void unlockScreen();
virtual void fillScreen(uint32 col);
virtual void updateScreen();
virtual void setShakePos(int shakeOffset);
virtual void setFocusRectangle(const Common::Rect &rect);
virtual void setFocusRectangle(const Common::Rect& rect);
virtual void clearFocusRectangle();
virtual int16 getOverlayWidth();
virtual int16 getOverlayHeight();
virtual void showOverlay();
virtual void hideOverlay();
virtual Graphics::PixelFormat getOverlayFormat() const;
virtual void copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h);
virtual void clearOverlay();
virtual void grabOverlay(void *buf, int pitch);
virtual void copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h);
virtual int16 getOverlayHeight();
virtual int16 getOverlayWidth();
virtual bool showMouse(bool visible);
virtual void warpMouse(int x, int y);
virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL);
virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format);
virtual void setCursorPalette(const byte *colors, uint start, uint num);
virtual void displayMessageOnOSD(const char *msg);
// PaletteManager interface
virtual void setPalette(const byte *colors, uint start, uint num);
virtual void grabPalette(byte *colors, uint start, uint num);
protected:
/**
* Setup OpenGL settings
* Set up the actual screen size available for the OpenGL code to do any
* drawing.
*
* @param width The width of the screen.
* @param height The height of the screen.
*/
virtual void initGL();
void setActualScreenSize(uint width, uint height);
/**
* Creates and refreshs OpenGL textures.
* Notify the manager of a OpenGL context change. This should be the first
* thing to call when you create an OpenGL (ES) context!
*
* @param defaultFormat The new default format for the game screen
* (this is used for the CLUT8 game screens).
* @param defaultFromatAlpha The new default format with an alpha channel
* (this is used for the overlay and cursor).
*/
virtual void loadTextures();
void notifyContextChange(const Graphics::PixelFormat &defaultFormat, const Graphics::PixelFormat &defaultFormatAlpha);
/**
* Adjust the physical mouse coordinates according to the currently visible screen.
*/
void adjustMousePosition(int16 &x, int16 &y);
/**
* Set up the mouse position for graphics output.
*
* @param x X coordinate in physical coordinates.
* @param y Y coordinate in physical coordinates.
*/
void setMousePosition(int x, int y) { _cursorX = x; _cursorY = y; }
/**
* Query the mouse position in physical coordinates.
*/
void getMousePosition(int16 &x, int16 &y) const { x = _cursorX; y = _cursorY; }
/**
* Set up the mouse position for the (event) system.
*
* @param x X coordinate in physical coordinates.
* @param y Y coordinate in physical coordinates.
*/
virtual void setInternalMousePosition(int x, int y) = 0;
private:
//
// GFX and video
// Transaction support
//
enum {
struct VideoState {
VideoState() : valid(false), gameWidth(0), gameHeight(0),
#ifdef USE_RGB_COLOR
gameFormat(),
#endif
aspectRatioCorrection(false), graphicsMode(GFX_LINEAR) {
}
bool valid;
uint gameWidth, gameHeight;
#ifdef USE_RGB_COLOR
Graphics::PixelFormat gameFormat;
#endif
bool aspectRatioCorrection;
int graphicsMode;
bool operator==(const VideoState &right) {
return gameWidth == right.gameWidth && gameHeight == right.gameHeight
#ifdef USE_RGB_COLOR
&& gameFormat == right.gameFormat
#endif
&& aspectRatioCorrection == right.aspectRatioCorrection
&& graphicsMode == right.graphicsMode;
}
bool operator!=(const VideoState &right) {
return !(*this == right);
}
};
/**
* The currently setup video state.
*/
VideoState _currentState;
/**
* The old video state used when doing a transaction rollback.
*/
VideoState _oldState;
protected:
enum TransactionMode {
kTransactionNone = 0,
kTransactionActive = 1,
kTransactionRollback = 2
};
struct TransactionDetails {
bool sizeChanged;
bool needRefresh;
bool needUpdatescreen;
bool filterChanged;
#ifdef USE_RGB_COLOR
bool formatChanged;
#endif
};
TransactionDetails _transactionDetails;
int _transactionMode;
struct VideoState {
bool setup;
bool fullscreen;
int mode;
int scaleFactor;
bool antialiasing;
bool aspectRatioCorrection;
int screenWidth, screenHeight;
int overlayWidth, overlayHeight;
int hardwareWidth, hardwareHeight;
#ifdef USE_RGB_COLOR
Graphics::PixelFormat format;
#endif
};
VideoState _videoMode, _oldVideoMode;
TransactionMode getTransactionMode() const { return _transactionMode; }
private:
/**
* Sets the OpenGL texture format for the given pixel format. If format is not support will raise an error.
* The current transaction mode.
*/
virtual void getGLPixelFormat(Graphics::PixelFormat pixelFormat, byte &bpp, GLenum &intFormat, GLenum &glFormat, GLenum &type);
virtual void internUpdateScreen();
virtual bool loadGFXMode();
virtual void unloadGFXMode();
TransactionMode _transactionMode;
/**
* Setup the fullscreen mode state.
* The current screen change ID.
*/
void setFullscreenMode(bool enable);
int _screenChangeID;
protected:
/**
* Query the fullscreen state.
*/
inline bool getFullscreenMode() const { return _videoMode.fullscreen; }
/**
* Set the scale factor.
* Set up the requested video mode. This takes parameters which describe
* what resolution the game screen requests (this is possibly aspect ratio
* corrected!).
*
* This can only be used in a GFX transaction.
* A sub-class should take these parameters as hints. It might very well
* set up a mode which it thinks suites the situation best.
*
* @param newScale New scale factor.
* @parma requestedWidth This is the requested actual game screen width.
* @param requestedHeight This is the requested actual game screen height.
* @param format This is the requested pixel format of the virtual game screen.
* @return true on success, false otherwise
*/
void setScale(int newScale);
virtual bool loadVideoMode(uint requestedWidth, uint requestedHeight, const Graphics::PixelFormat &format) = 0;
/**
* Query the scale factor.
*/
inline int getScale() const { return _videoMode.scaleFactor; }
/**
* Toggle the antialiasing state of the current video mode.
* Save a screenshot of the full display as BMP to the given file. This
* uses Common::DumpFile for writing the screenshot.
*
* This can only be used in a GFX transaction.
* @param filename The output filename.
*/
void toggleAntialiasing();
void saveScreenshot(const Common::String &filename) const;
private:
//
// OpenGL utilities
//
/**
* Query the antialiasing state.
* Try to determine the internal parameters for a given pixel format.
*
* @return true when the format can be used, false otherwise.
*/
inline bool getAntialiasingState() const { return _videoMode.antialiasing; }
bool getGLPixelFormat(const Graphics::PixelFormat &pixelFormat, GLenum &glIntFormat, GLenum &glFormat, GLenum &glType) const;
// Drawing coordinates for the current display mode and scale
int _displayX;
int _displayY;
int _displayWidth;
int _displayHeight;
//
// Actual hardware screen
//
virtual const char *getCurrentModeName();
/**
* The width of the physical output.
*/
uint _outputScreenWidth;
virtual void calculateDisplaySize(int &width, int &height);
virtual void refreshDisplaySize();
/**
* The height of the physical output.
*/
uint _outputScreenHeight;
uint getAspectRatio() const;
/**
* @return The desired aspect of the game screen.
*/
frac_t getDesiredGameScreenAspect() const;
void setFormatIsBGR(bool isBGR) { _formatBGR = isBGR; }
bool _formatBGR;
/**
* Recalculates the area used to display the game screen.
*/
void recalculateDisplayArea();
/**
* The X coordinate of the game screen.
*/
uint _displayX;
/**
* The Y coordinate of the game screen.
*/
uint _displayY;
/**
* The width of the game screen in physical coordinates.
*/
uint _displayWidth;
/**
* The height of the game screen in physical coordinates.
*/
uint _displayHeight;
/**
* The default pixel format of the backend.
*/
Graphics::PixelFormat _defaultFormat;
/**
* The default pixel format with an alpha channel.
*/
Graphics::PixelFormat _defaultFormatAlpha;
//
// Game screen
//
GLTexture *_gameTexture;
Graphics::Surface _screenData;
int _screenChangeCount;
bool _screenNeedsRedraw;
Common::Rect _screenDirtyRect;
#ifdef USE_RGB_COLOR
Graphics::PixelFormat _screenFormat;
#endif
byte *_gamePalette;
/**
* The virtual game screen.
*/
Texture *_gameScreen;
virtual void refreshGameScreen();
/**
* The game palette if in CLUT8 mode.
*/
byte _gamePalette[3 * 256];
// Shake mode
int _shakePos;
/**
* The offset by which the screen is moved vertically.
*/
int _gameScreenShakeOffset;
//
// Overlay
//
GLTexture *_overlayTexture;
Graphics::Surface _overlayData;
Graphics::PixelFormat _overlayFormat;
/**
* The overlay screen.
*/
Texture *_overlay;
/**
* Whether the overlay is visible or not.
*/
bool _overlayVisible;
bool _overlayNeedsRedraw;
Common::Rect _overlayDirtyRect;
virtual void refreshOverlay();
//
// Mouse
// Cursor
//
struct MousePos {
// The mouse position in hardware screen coordinates.
int16 x, y;
// The size and hotspot of the original cursor image.
int16 w, h;
int16 hotX, hotY;
/**
* Set up the correct cursor palette.
*/
void updateCursorPalette();
// The size and hotspot of the scaled cursor, in real coordinates.
int16 rW, rH;
int16 rHotX, rHotY;
/**
* The cursor image.
*/
Texture *_cursor;
// The size and hotspot of the scaled cursor, in game coordinates.
int16 vW, vH;
int16 vHotX, vHotY;
/**
* X coordinate of the cursor in phyiscal coordinates.
*/
uint _cursorX;
MousePos() : x(0), y(0), w(0), h(0), hotX(0), hotY(0),
rW(0), rH(0), rHotX(0), rHotY(0), vW(0), vH(0),
vHotX(0), vHotY(0) {}
};
/**
* Y coordinate of the cursor in physical coordinates.
*/
uint _cursorY;
GLTexture *_cursorTexture;
Graphics::Surface _cursorData;
Graphics::PixelFormat _cursorFormat;
byte *_cursorPalette;
bool _cursorPaletteDisabled;
MousePos _cursorState;
bool _cursorVisible;
/**
* The X offset for the cursor hotspot in unscaled coordinates.
*/
uint _cursorHotspotX;
/**
* The Y offset for the cursor hotspot in unscaled coordinates.
*/
uint _cursorHotspotY;
/**
* Recalculate the cursor scaling. Scaling is always done according to
* the game screen.
*/
void recalculateCursorScaling();
/**
* The X offset for the cursor hotspot in scaled coordinates.
*/
uint _cursorHotspotXScaled;
/**
* The Y offset for the cursor hotspot in scaled coordinates.
*/
uint _cursorHotspotYScaled;
/**
* The width of the cursor scaled coordinates.
*/
uint _cursorWidthScaled;
/**
* The height of the cursor scaled coordinates.
*/
uint _cursorHeightScaled;
/**
* The key color.
*/
uint32 _cursorKeyColor;
/**
* Whether the cursor is actually visible.
*/
bool _cursorVisible;
/**
* Whether no cursor scaling should be applied.
*/
bool _cursorDontScale;
bool _cursorNeedsRedraw;
/**
* Set up the mouse position for graphics output.
*
* @param x X coordinate in native coordinates.
* @param y Y coordinate in native coordinates.
* Whether the special cursor palette is enabled.
*/
void setMousePosition(int x, int y) { _cursorState.x = x; _cursorState.y = y; }
virtual void refreshCursor();
virtual void refreshCursorScale();
bool _cursorPaletteEnabled;
/**
* Set up the mouse position for the (event) system.
*
* @param x X coordinate in native coordinates.
* @param y Y coordinate in native coordinates.
* The special cursor palette in case enabled.
*/
virtual void setInternalMousePosition(int x, int y) = 0;
/**
* Adjusts hardware screen coordinates to either overlay or game screen
* coordinates depending on whether the overlay is visible or not.
*
* @param x X coordinate of the mouse position.
* @param y Y coordinate of the mouse position.
*/
virtual void adjustMousePosition(int16 &x, int16 &y);
//
// Misc
//
virtual bool saveScreenshot(const char *filename);
byte _cursorPalette[3 * 256];
#ifdef USE_OSD
//
// OSD
//
protected:
/**
* Returns the font used for on screen display
*/
virtual const Graphics::Font *getFontOSD();
private:
/**
* Update the OSD texture / surface.
* The OSD's contents.
*/
void updateOSD();
Texture *_osd;
/**
* The OSD contents.
* Current opacity level of the OSD.
*/
Common::Array<Common::String> _osdLines;
GLTexture *_osdTexture;
Graphics::Surface _osdSurface;
uint8 _osdAlpha;
/**
* When fading the OSD has started.
*/
uint32 _osdFadeStartTime;
bool _requireOSDUpdate;
/**
* Mutex to allow displayMessageOnOSD to be used from the audio thread.
*/
Common::Mutex _osdMutex;
enum {
kOSDFadeOutDelay = 2 * 1000,
kOSDFadeOutDuration = 500,
@ -348,4 +474,6 @@ protected:
#endif
};
} // End of namespace OpenGL
#endif

View File

@ -8,26 +8,26 @@
* 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
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef BACKENDS_GRAPHICS_OPENGL_OPENGL_H
#define BACKENDS_GRAPHICS_OPENGL_OPENGL_H
// The purpose of this header is to include the OpenGL headers in an uniform
// fashion. A notable example for a non standard port is the Tizen port.
#include "common/scummsys.h"
#if defined(DEBUG) && defined(USE_OPENGL)
#include "backends/graphics/opengl/glerrorcheck.h"
#include "common/textconsole.h"
#include "common/str.h"
#ifdef WIN32
#if defined(ARRAYSIZE) && !defined(_WINDOWS_)
#undef ARRAYSIZE
@ -37,31 +37,21 @@
#undef ARRAYSIZE
#endif
#if defined(USE_GLES)
// HACK: In case common/util.h has been included already we need to make sure
// to define ARRAYSIZE again in case of Windows.
#if !defined(ARRAYSIZE) && defined(COMMON_UTIL_H)
#define ARRAYSIZE(x) ((int)(sizeof(x) / sizeof(x[0])))
#endif
#if defined(TIZEN)
#include <FGraphicsOpengl.h>
using namespace Tizen::Graphics::Opengl;
#elif defined(USE_GLES)
#include <GLES/gl.h>
#elif defined(MACOSX)
#include <OpenGL/gl.h>
#elif defined(SDL_BACKEND)
#include <SDL_opengl.h>
#else
#include <GL/gl.h>
#endif
static Common::String getGlErrStr(GLenum error) {
switch (error) {
case GL_NO_ERROR: return "GL_NO_ERROR";
case GL_INVALID_ENUM: return "GL_INVALID_ENUM";
case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION";
case GL_STACK_OVERFLOW: return "GL_STACK_OVERFLOW";
case GL_STACK_UNDERFLOW: return "GL_STACK_UNDERFLOW";
case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY";
}
return Common::String::format("(Unknown GL error code 0x%x)", error);
}
void checkGlError(const char *file, int line) {
GLenum error = glGetError();
if (error != GL_NO_ERROR)
warning("%s:%d: GL error: %s", file, line, getGlErrStr(error).c_str());
}
#endif

View File

@ -0,0 +1,371 @@
/* 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 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "backends/graphics/opengl/texture.h"
#include "backends/graphics/opengl/extensions.h"
#include "backends/graphics/opengl/debug.h"
#include "common/rect.h"
#include "common/textconsole.h"
namespace OpenGL {
static GLuint nextHigher2(GLuint v) {
if (v == 0)
return 1;
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
return ++v;
}
GLint Texture::_maxTextureSize = 0;
void Texture::queryTextureInformation() {
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &_maxTextureSize);
debug(5, "OpenGL maximum texture size: %d", _maxTextureSize);
}
Texture::Texture(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format)
: _glIntFormat(glIntFormat), _glFormat(glFormat), _glType(glType), _format(format), _glFilter(GL_NEAREST),
_glTexture(0), _textureData(), _userPixelData(), _allDirty(false) {
recreateInternalTexture();
}
Texture::~Texture() {
releaseInternalTexture();
_textureData.free();
}
void Texture::releaseInternalTexture() {
GLCALL(glDeleteTextures(1, &_glTexture));
_glTexture = 0;
}
void Texture::recreateInternalTexture() {
// Get a new texture name.
GLCALL(glGenTextures(1, &_glTexture));
// Set up all texture parameters.
GLCALL(glBindTexture(GL_TEXTURE_2D, _glTexture));
GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _glFilter));
GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _glFilter));
GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
// In case there is an actual texture setup we reinitialize it.
if (_textureData.getPixels()) {
// Allocate storage for OpenGL texture.
GLCALL(glTexImage2D(GL_TEXTURE_2D, 0, _glIntFormat, _textureData.w,
_textureData.h, 0, _glFormat, _glType, NULL));
// Mark dirts such that it will be completely refreshed the next time.
flagDirty();
}
}
void Texture::enableLinearFiltering(bool enable) {
if (enable) {
_glFilter = GL_LINEAR;
} else {
_glFilter = GL_NEAREST;
}
GLCALL(glBindTexture(GL_TEXTURE_2D, _glTexture));
GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _glFilter));
GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _glFilter));
}
void Texture::allocate(uint width, uint height) {
uint texWidth = width, texHeight = height;
if (!g_extNPOTSupported) {
texWidth = nextHigher2(texWidth);
texHeight = nextHigher2(texHeight);
}
// In case the needed texture dimension changed we will reinitialize the
// texture.
if (texWidth != _textureData.w || texHeight != _textureData.h) {
// Create a buffer for the texture data.
_textureData.create(texWidth, texHeight, _format);
// Set the texture.
GLCALL(glBindTexture(GL_TEXTURE_2D, _glTexture));
// Allocate storage for OpenGL texture.
GLCALL(glTexImage2D(GL_TEXTURE_2D, 0, _glIntFormat, _textureData.w,
_textureData.h, 0, _glFormat, _glType, NULL));
}
// Create a sub-buffer for raw access.
_userPixelData = _textureData.getSubArea(Common::Rect(width, height));
}
void Texture::copyRectToTexture(uint x, uint y, uint w, uint h, const void *srcPtr, uint srcPitch) {
Graphics::Surface *dstSurf = getSurface();
assert(x + w <= dstSurf->w);
assert(y + h <= dstSurf->h);
// *sigh* Common::Rect::extend behaves unexpected whenever one of the two
// parameters is an empty rect. Thus, we check whether the current dirty
// area is valid. In case it is not we simply use the parameters as new
// dirty area. Otherwise, we simply call extend.
if (_dirtyArea.isEmpty()) {
_dirtyArea = Common::Rect(x, y, x + w, y + h);
} else {
_dirtyArea.extend(Common::Rect(x, y, x + w, y + h));
}
const byte *src = (const byte *)srcPtr;
byte *dst = (byte *)dstSurf->getBasePtr(x, y);
const uint pitch = dstSurf->pitch;
const uint bytesPerPixel = dstSurf->format.bytesPerPixel;
if (srcPitch == pitch && x == 0 && w == dstSurf->w) {
memcpy(dst, src, h * pitch);
} else {
while (h-- > 0) {
memcpy(dst, src, w * bytesPerPixel);
dst += pitch;
src += srcPitch;
}
}
}
void Texture::fill(uint32 color) {
Graphics::Surface *dst = getSurface();
dst->fillRect(Common::Rect(dst->w, dst->h), color);
flagDirty();
}
void Texture::draw(GLuint x, GLuint y, GLuint w, GLuint h) {
// Only do any processing when the Texture is initialized.
if (!_textureData.getPixels()) {
return;
}
// First update any potentional changes.
updateTexture();
// Set the texture.
GLCALL(glBindTexture(GL_TEXTURE_2D, _glTexture));
// Calculate the texture rect that will be drawn.
const GLfloat texWidth = (GLfloat)_userPixelData.w / _textureData.w;
const GLfloat texHeight = (GLfloat)_userPixelData.h / _textureData.h;
const GLfloat texcoords[4*2] = {
0, 0,
texWidth, 0,
0, texHeight,
texWidth, texHeight
};
GLCALL(glTexCoordPointer(2, GL_FLOAT, 0, texcoords));
// Calculate the screen rect where the texture will be drawn.
const GLshort vertices[4*2] = {
(GLshort)x, (GLshort)y,
(GLshort)(x + w), (GLshort)y,
(GLshort)x, (GLshort)(y + h),
(GLshort)(x + w), (GLshort)(y + h)
};
GLCALL(glVertexPointer(2, GL_SHORT, 0, vertices));
// Draw the texture to the screen buffer.
GLCALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
}
void Texture::updateTexture() {
if (!isDirty()) {
return;
}
Common::Rect dirtyArea = getDirtyArea();
// In case we use linear filtering we might need to duplicate the last
// pixel row/column to avoid glitches with filtering.
if (_glFilter == GL_LINEAR) {
if (dirtyArea.right == _userPixelData.w && _userPixelData.w != _textureData.w) {
uint height = dirtyArea.height();
const byte *src = (const byte *)_textureData.getBasePtr(_userPixelData.w - 1, dirtyArea.top);
byte *dst = (byte *)_textureData.getBasePtr(_userPixelData.w, dirtyArea.top);
while (height-- > 0) {
memcpy(dst, src, _textureData.format.bytesPerPixel);
dst += _textureData.pitch;
src += _textureData.pitch;
}
// Extend the dirty area.
++dirtyArea.right;
}
if (dirtyArea.bottom == _userPixelData.h && _userPixelData.h != _textureData.h) {
const byte *src = (const byte *)_textureData.getBasePtr(dirtyArea.left, _userPixelData.h - 1);
byte *dst = (byte *)_textureData.getBasePtr(dirtyArea.left, _userPixelData.h);
memcpy(dst, src, dirtyArea.width() * _textureData.format.bytesPerPixel);
// Extend the dirty area.
++dirtyArea.bottom;
}
}
// Set the texture.
GLCALL(glBindTexture(GL_TEXTURE_2D, _glTexture));
// Update the actual texture.
// Although we keep track of the dirty part of the texture buffer we
// cannot take advantage of the left/right boundries here because it is
// not possible to specify a pitch to glTexSubImage2D. To be precise, with
// plain OpenGL we could set GL_UNPACK_ROW_LENGTH to achieve this. However,
// OpenGL ES 1.0 does not support GL_UNPACK_ROW_LENGTH. Thus, we are left
// with the following options:
//
// 1) (As we do right now) Simply always update the whole texture lines of
// rect changed. This is simplest to implement. In case performance is
// really an issue we can think of switching to another method.
//
// 2) Copy the dirty rect to a temporary buffer and upload that by using
// glTexSubImage2D. This is what the Android backend does. It is more
// complicated though.
//
// 3) Use glTexSubImage2D per line changed. This is what the old OpenGL
// graphics manager did but it is much slower! Thus, we do not use it.
GLCALL(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, dirtyArea.top, _textureData.w, dirtyArea.height(),
_glFormat, _glType, _textureData.getBasePtr(0, dirtyArea.top)));
// We should have handled everything, thus not dirty anymore.
clearDirty();
}
Common::Rect Texture::getDirtyArea() const {
if (_allDirty) {
return Common::Rect(_userPixelData.w, _userPixelData.h);
} else {
return _dirtyArea;
}
}
TextureCLUT8::TextureCLUT8(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format)
: Texture(glIntFormat, glFormat, glType, format), _clut8Data(), _palette(new byte[256 * format.bytesPerPixel]) {
memset(_palette, 0, sizeof(byte) * format.bytesPerPixel);
}
TextureCLUT8::~TextureCLUT8() {
delete[] _palette;
_palette = nullptr;
_clut8Data.free();
}
void TextureCLUT8::allocate(uint width, uint height) {
Texture::allocate(width, height);
// We only need to reinitialize our CLUT8 surface when the output size
// changed.
if (width == _clut8Data.w && height == _clut8Data.h) {
return;
}
_clut8Data.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
}
Graphics::PixelFormat TextureCLUT8::getFormat() const {
return Graphics::PixelFormat::createFormatCLUT8();
}
namespace {
template<typename ColorType>
inline void convertPalette(ColorType *dst, const byte *src, uint colors, const Graphics::PixelFormat &format) {
while (colors-- > 0) {
*dst++ = format.RGBToColor(src[0], src[1], src[2]);
src += 3;
}
}
} // End of anonymous namespace
void TextureCLUT8::setPalette(uint start, uint colors, const byte *palData) {
const Graphics::PixelFormat &hardwareFormat = getHardwareFormat();
if (hardwareFormat.bytesPerPixel == 2) {
convertPalette<uint16>((uint16 *)_palette + start, palData, colors, hardwareFormat);
} else if (hardwareFormat.bytesPerPixel == 4) {
convertPalette<uint32>((uint32 *)_palette + start, palData, colors, hardwareFormat);
} else {
warning("TextureCLUT8::setPalette: Unsupported pixel depth: %d", hardwareFormat.bytesPerPixel);
}
// A palette changes means we need to refresh the whole surface.
flagDirty();
}
namespace {
template<typename PixelType>
inline void doPaletteLookUp(PixelType *dst, const byte *src, uint width, uint height, uint dstPitch, uint srcPitch, const PixelType *palette) {
uint srcAdd = srcPitch - width;
uint dstAdd = dstPitch - width * sizeof(PixelType);
while (height-- > 0) {
for (uint x = width; x > 0; --x) {
*dst++ = palette[*src++];
}
dst = (PixelType *)((byte *)dst + dstAdd);
src += srcAdd;
}
}
} // End of anonymous namespace
void TextureCLUT8::updateTexture() {
if (!isDirty()) {
return;
}
// Do the palette look up
Graphics::Surface *outSurf = Texture::getSurface();
Common::Rect dirtyArea = getDirtyArea();
if (outSurf->format.bytesPerPixel == 2) {
doPaletteLookUp<uint16>((uint16 *)outSurf->getBasePtr(dirtyArea.left, dirtyArea.top),
(const byte *)_clut8Data.getBasePtr(dirtyArea.left, dirtyArea.top),
dirtyArea.width(), dirtyArea.height(),
outSurf->pitch, _clut8Data.pitch, (const uint16 *)_palette);
} else if (outSurf->format.bytesPerPixel == 4) {
doPaletteLookUp<uint32>((uint32 *)outSurf->getBasePtr(dirtyArea.left, dirtyArea.top),
(const byte *)_clut8Data.getBasePtr(dirtyArea.left, dirtyArea.top),
dirtyArea.width(), dirtyArea.height(),
outSurf->pitch, _clut8Data.pitch, (const uint32 *)_palette);
} else {
warning("TextureCLUT8::updateTexture: Unsupported pixel depth: %d", outSurf->format.bytesPerPixel);
}
// Do generic handling of updating the texture.
Texture::updateTexture();
}
} // End of namespace OpenGL

View File

@ -0,0 +1,175 @@
/* 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 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef BACKENDS_GRAPHICS_OPENGL_TEXTURE_H
#define BACKENDS_GRAPHICS_OPENGL_TEXTURE_H
#include "backends/graphics/opengl/opengl-sys.h"
#include "graphics/pixelformat.h"
#include "graphics/surface.h"
#include "common/rect.h"
namespace OpenGL {
/**
* An OpenGL texture wrapper. It automatically takes care of all OpenGL
* texture handling issues and also provides access to the texture data.
*/
class Texture {
public:
/**
* Create a new texture with the specific internal format.
*
* @param glIntFormat The internal format to use.
* @param glFormat The input format.
* @param glType The input type.
* @param format The format used for the texture input.
*/
Texture(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format);
virtual ~Texture();
/**
* Destroy the OpenGL texture name.
*/
void releaseInternalTexture();
/**
* Create the OpenGL texture name and flag the whole texture as dirty.
*/
void recreateInternalTexture();
/**
* Enable or disable linear texture filtering.
*
* @param enable true to enable and false to disable.
*/
void enableLinearFiltering(bool enable);
/**
* Allocate texture space for the desired dimensions. This wraps any
* handling of requirements for POT textures.
*
* @param width The desired logical width.
* @param height The desired logical height.
*/
virtual void allocate(uint width, uint height);
void copyRectToTexture(uint x, uint y, uint w, uint h, const void *src, uint srcPitch);
void fill(uint32 color);
void draw(GLuint x, GLuint y, GLuint w, GLuint h);
void flagDirty() { _allDirty = true; }
bool isDirty() const { return _allDirty || !_dirtyArea.isEmpty(); }
uint getWidth() const { return _userPixelData.w; }
uint getHeight() const { return _userPixelData.h; }
/**
* @return The hardware format of the texture data.
*/
const Graphics::PixelFormat &getHardwareFormat() const { return _format; }
/**
* @return The logical format of the texture data.
*/
virtual Graphics::PixelFormat getFormat() const { return _format; }
virtual Graphics::Surface *getSurface() { return &_userPixelData; }
virtual const Graphics::Surface *getSurface() const { return &_userPixelData; }
/**
* @return Whether the texture data is using a palette.
*/
virtual bool hasPalette() const { return false; }
virtual void setPalette(uint start, uint colors, const byte *palData) {}
virtual void *getPalette() { return 0; }
virtual const void *getPalette() const { return 0; }
/**
* Query texture related OpenGL information from the context. This only
* queries the maximum texture size for now.
*/
static void queryTextureInformation();
/**
* @return Return the maximum texture dimensions supported.
*/
static GLint getMaximumTextureSize() { return _maxTextureSize; }
protected:
virtual void updateTexture();
Common::Rect getDirtyArea() const;
private:
const GLenum _glIntFormat;
const GLenum _glFormat;
const GLenum _glType;
const Graphics::PixelFormat _format;
GLint _glFilter;
GLuint _glTexture;
Graphics::Surface _textureData;
Graphics::Surface _userPixelData;
bool _allDirty;
Common::Rect _dirtyArea;
void clearDirty() { _allDirty = false; _dirtyArea = Common::Rect(); }
static GLint _maxTextureSize;
};
class TextureCLUT8 : public Texture {
public:
TextureCLUT8(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format);
virtual ~TextureCLUT8();
virtual void allocate(uint width, uint height);
virtual Graphics::PixelFormat getFormat() const;
virtual bool hasPalette() const { return true; }
virtual void setPalette(uint start, uint colors, const byte *palData);
virtual void *getPalette() { return _palette; }
virtual const void *getPalette() const { return _palette; }
virtual Graphics::Surface *getSurface() { return &_clut8Data; }
virtual const Graphics::Surface *getSurface() const { return &_clut8Data; }
protected:
virtual void updateTexture();
private:
Graphics::Surface _clut8Data;
byte *_palette;
};
} // End of namespace OpenGL
#endif

File diff suppressed because it is too large Load Diff

View File

@ -8,117 +8,109 @@
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef BACKENDS_GRAPHICS_OPENGLSDL_H
#define BACKENDS_GRAPHICS_OPENGLSDL_H
#ifndef BACKENDS_GRAPHICS_OPENGLSDL_OPENGLSDL_GRAPHICS_H
#define BACKENDS_GRAPHICS_OPENGLSDL_OPENGLSDL_GRAPHICS_H
#include "backends/platform/sdl/sdl-sys.h"
#if defined(ARRAYSIZE) && !defined(_WINDOWS_)
#undef ARRAYSIZE
#endif
#include "backends/graphics/sdl/sdl-graphics.h"
#include "backends/graphics/opengl/opengl-graphics.h"
#include "backends/graphics/sdl/sdl-graphics.h"
#include "backends/platform/sdl/sdl-sys.h"
#include "common/array.h"
#include "common/events.h"
/**
* SDL OpenGL graphics manager
*/
class OpenGLSdlGraphicsManager : public OpenGLGraphicsManager, public SdlGraphicsManager, public Common::EventObserver {
class OpenGLSdlGraphicsManager : public OpenGL::OpenGLGraphicsManager, public SdlGraphicsManager, public Common::EventObserver {
public:
OpenGLSdlGraphicsManager(SdlEventSource *eventSource);
OpenGLSdlGraphicsManager(uint desktopWidth, uint desktopHeight, SdlEventSource *eventSource);
virtual ~OpenGLSdlGraphicsManager();
// GraphicsManager API
virtual void activateManager();
virtual void deactivateManager();
virtual bool hasFeature(OSystem::Feature f);
virtual void setFeatureState(OSystem::Feature f, bool enable);
virtual bool getFeatureState(OSystem::Feature f);
virtual bool setGraphicsMode(int mode);
virtual void resetGraphicsScale();
#ifdef USE_RGB_COLOR
virtual Common::List<Graphics::PixelFormat> getSupportedFormats() const;
#endif
virtual void initEventObserver();
virtual bool notifyEvent(const Common::Event &event);
virtual void updateScreen();
// SdlGraphicsManager interface
// EventObserver API
virtual bool notifyEvent(const Common::Event &event);
// SdlGraphicsManager API
virtual void notifyVideoExpose();
virtual void notifyResize(const uint width, const uint height);
virtual void transformMouseCoordinates(Common::Point &point);
virtual void notifyMousePos(Common::Point mouse);
protected:
virtual void internUpdateScreen();
virtual bool loadGFXMode();
virtual void unloadGFXMode();
virtual bool isHotkey(const Common::Event &event);
#ifdef USE_RGB_COLOR
Common::List<Graphics::PixelFormat> _supportedFormats;
/**
* Update the list of supported pixel formats.
* This method is invoked by loadGFXMode().
*/
void detectSupportedFormats();
#endif
/**
* Toggles fullscreen.
* @loop loop direction for switching fullscreen mode, if 0 toggles it.
*/
virtual void toggleFullScreen(int loop);
int _activeFullscreenMode;
/**
* Setup the fullscreen mode.
* @return false if failed finding a mode, true otherwise.
*/
virtual bool setupFullscreenMode();
virtual void setInternalMousePosition(int x, int y);
int _lastFullscreenModeWidth;
int _lastFullscreenModeHeight;
int _desktopWidth;
int _desktopHeight;
virtual bool loadVideoMode(uint requestedWidth, uint requestedHeight, const Graphics::PixelFormat &format);
private:
bool setupMode(uint width, uint height);
// Hardware screen
SDL_Surface *_hwscreen;
uint32 _lastVideoModeLoad;
SDL_Surface *_hwScreen;
// If screen was resized by the user
bool _screenResized;
uint _lastRequestedWidth;
uint _lastRequestedHeight;
uint _graphicsScale;
bool _ignoreLoadVideoMode;
bool _gotResize;
// Ignore resize events for the number of updateScreen() calls.
// Normaly resize events are user generated when resizing the window
// from its borders, but in some cases a resize event can be generated
// after a fullscreen change.
int _ignoreResizeFrames;
bool _wantsFullScreen;
uint _ignoreResizeEvents;
#ifdef USE_OSD
/**
* Displays a mode change message in OSD
*/
void displayModeChangedMsg();
struct VideoMode {
VideoMode() : width(0), height(0) {}
VideoMode(uint w, uint h) : width(w), height(h) {}
/**
* Displays a scale change message in OSD
*/
void displayScaleChangedMsg();
#endif
bool operator<(const VideoMode &right) const {
if (width < right.width) {
return true;
} else if (width == right.width && height < right.height) {
return true;
} else {
return false;
}
}
bool operator==(const VideoMode &right) const {
return width == right.width && height == right.height;
}
bool operator!=(const VideoMode &right) const {
return !(*this == right);
}
uint width, height;
};
typedef Common::Array<VideoMode> VideoModeArray;
VideoModeArray _fullscreenVideoModes;
uint _desiredFullscreenWidth;
uint _desiredFullscreenHeight;
virtual bool isHotkey(const Common::Event &event);
};
#endif

View File

@ -26,10 +26,15 @@
SdlGraphicsManager::SdlGraphicsManager(SdlEventSource *source)
: _eventSource(source) {
_eventSource->setGraphicsManager(this);
}
SdlGraphicsManager::~SdlGraphicsManager() {
_eventSource->setGraphicsManager(0);
}
void SdlGraphicsManager::initEventSource() {
_eventSource->setGraphicsManager(this);
}
void SdlGraphicsManager::deinitEventSource() {
_eventSource->setGraphicsManager(0);
}

View File

@ -80,6 +80,9 @@ public:
virtual void notifyMousePos(Common::Point mouse) = 0;
protected:
void initEventSource();
void deinitEventSource();
SdlEventSource *_eventSource;
};

View File

@ -40,6 +40,7 @@
#include "graphics/scaler.h"
#include "graphics/scaler/aspect.h"
#include "graphics/surface.h"
#include "gui/EventRecorder.h"
static const OSystem::GraphicsMode s_supportedGraphicsModes[] = {
{"1x", _s("Normal (no scaling)"), GFX_NORMAL},
@ -135,19 +136,12 @@ SurfaceSdlGraphicsManager::SurfaceSdlGraphicsManager(SdlEventSource *sdlEventSou
_paletteDirtyStart(0), _paletteDirtyEnd(0),
_screenIsLocked(false),
_graphicsMutex(0),
_displayDisabled(false),
#ifdef USE_SDL_DEBUG_FOCUSRECT
_enableFocusRectDebugCode(false), _enableFocusRect(false), _focusRect(),
#endif
_transactionMode(kTransactionNone) {
if (SDL_InitSubSystem(SDL_INIT_VIDEO) == -1) {
error("Could not initialize SDL: %s", SDL_GetError());
}
// This is also called in initSDL(), but initializing graphics
// may reset it.
SDL_EnableUNICODE(1);
// allocate palette storage
_currentPalette = (SDL_Color *)calloc(sizeof(SDL_Color), 256);
_cursorPalette = (SDL_Color *)calloc(sizeof(SDL_Color), 256);
@ -163,8 +157,6 @@ SurfaceSdlGraphicsManager::SurfaceSdlGraphicsManager(SdlEventSource *sdlEventSou
_enableFocusRectDebugCode = ConfMan.getBool("use_sdl_debug_focusrect");
#endif
SDL_ShowCursor(SDL_DISABLE);
memset(&_oldVideoMode, 0, sizeof(_oldVideoMode));
memset(&_videoMode, 0, sizeof(_videoMode));
memset(&_transactionDetails, 0, sizeof(_transactionDetails));
@ -191,10 +183,6 @@ SurfaceSdlGraphicsManager::SurfaceSdlGraphicsManager(SdlEventSource *sdlEventSou
}
SurfaceSdlGraphicsManager::~SurfaceSdlGraphicsManager() {
// Unregister the event observer
if (g_system->getEventManager()->getEventDispatcher() != NULL)
g_system->getEventManager()->getEventDispatcher()->unregisterObserver(this);
unloadGFXMode();
if (_mouseSurface)
SDL_FreeSurface(_mouseSurface);
@ -209,11 +197,24 @@ SurfaceSdlGraphicsManager::~SurfaceSdlGraphicsManager() {
free(_mouseData);
}
void SurfaceSdlGraphicsManager::initEventObserver() {
void SurfaceSdlGraphicsManager::activateManager() {
GraphicsManager::activateManager();
initEventSource();
// Register the graphics manager as a event observer
g_system->getEventManager()->getEventDispatcher()->registerObserver(this, 10, false);
}
void SurfaceSdlGraphicsManager::deactivateManager() {
// Unregister the event observer
if (g_system->getEventManager()->getEventDispatcher()) {
g_system->getEventManager()->getEventDispatcher()->unregisterObserver(this);
}
deinitEventSource();
GraphicsManager::deactivateManager();
}
bool SurfaceSdlGraphicsManager::hasFeature(OSystem::Feature f) {
return
(f == OSystem::kFeatureFullscreenMode) ||
@ -261,10 +262,6 @@ bool SurfaceSdlGraphicsManager::getFeatureState(OSystem::Feature f) {
}
}
const OSystem::GraphicsMode *SurfaceSdlGraphicsManager::supportedGraphicsModes() {
return s_supportedGraphicsModes;
}
const OSystem::GraphicsMode *SurfaceSdlGraphicsManager::getSupportedGraphicsModes() const {
return s_supportedGraphicsModes;
}
@ -765,9 +762,20 @@ bool SurfaceSdlGraphicsManager::loadGFXMode() {
fixupResolutionForAspectRatio(_videoMode.desiredAspectRatio, _videoMode.hardwareWidth, _videoMode.hardwareHeight);
}
_hwscreen = SDL_SetVideoMode(_videoMode.hardwareWidth, _videoMode.hardwareHeight, 16,
_videoMode.fullscreen ? (SDL_FULLSCREEN|SDL_SWSURFACE) : SDL_SWSURFACE
);
#ifdef ENABLE_EVENTRECORDER
_displayDisabled = ConfMan.getBool("disable_display");
if (_displayDisabled) {
_hwscreen = g_eventRec.getSurface(_videoMode.hardwareWidth, _videoMode.hardwareHeight);
} else
#endif
{
_hwscreen = SDL_SetVideoMode(_videoMode.hardwareWidth, _videoMode.hardwareHeight, 16,
_videoMode.fullscreen ? (SDL_FULLSCREEN|SDL_SWSURFACE) : SDL_SWSURFACE
);
}
#ifdef USE_RGB_COLOR
detectSupportedFormats();
#endif
@ -1188,7 +1196,9 @@ void SurfaceSdlGraphicsManager::internUpdateScreen() {
#endif
// Finally, blit all our changes to the screen
SDL_UpdateRects(_hwscreen, _numDirtyRects, _dirtyRectList);
if (!_displayDisabled) {
SDL_UpdateRects(_hwscreen, _numDirtyRects, _dirtyRectList);
}
}
_numDirtyRects = 0;
@ -1293,15 +1303,13 @@ Graphics::Surface *SurfaceSdlGraphicsManager::lockScreen() {
if (SDL_LockSurface(_screen) == -1)
error("SDL_LockSurface failed: %s", SDL_GetError());
_framebuffer.pixels = _screen->pixels;
_framebuffer.w = _screen->w;
_framebuffer.h = _screen->h;
_framebuffer.pitch = _screen->pitch;
_framebuffer.init(_screen->w, _screen->h, _screen->pitch, _screen->pixels,
#ifdef USE_RGB_COLOR
_framebuffer.format = _screenFormat;
_screenFormat
#else
_framebuffer.format = Graphics::PixelFormat::createFormatCLUT8();
Graphics::PixelFormat::createFormatCLUT8()
#endif
);
return &_framebuffer;
}
@ -1325,8 +1333,8 @@ void SurfaceSdlGraphicsManager::unlockScreen() {
void SurfaceSdlGraphicsManager::fillScreen(uint32 col) {
Graphics::Surface *screen = lockScreen();
if (screen && screen->pixels)
memset(screen->pixels, col, screen->h * screen->pitch);
if (screen && screen->getPixels())
memset(screen->getPixels(), col, screen->h * screen->pitch);
unlockScreen();
}
@ -2047,15 +2055,12 @@ void SurfaceSdlGraphicsManager::displayMessageOnOSD(const char *msg) {
error("displayMessageOnOSD: SDL_LockSurface failed: %s", SDL_GetError());
Graphics::Surface dst;
dst.pixels = _osdSurface->pixels;
dst.w = _osdSurface->w;
dst.h = _osdSurface->h;
dst.pitch = _osdSurface->pitch;
dst.format = Graphics::PixelFormat(_osdSurface->format->BytesPerPixel,
8 - _osdSurface->format->Rloss, 8 - _osdSurface->format->Gloss,
8 - _osdSurface->format->Bloss, 8 - _osdSurface->format->Aloss,
_osdSurface->format->Rshift, _osdSurface->format->Gshift,
_osdSurface->format->Bshift, _osdSurface->format->Ashift);
dst.init(_osdSurface->w, _osdSurface->h, _osdSurface->pitch, _osdSurface->pixels,
Graphics::PixelFormat(_osdSurface->format->BytesPerPixel,
8 - _osdSurface->format->Rloss, 8 - _osdSurface->format->Gloss,
8 - _osdSurface->format->Bloss, 8 - _osdSurface->format->Aloss,
_osdSurface->format->Rshift, _osdSurface->format->Gshift,
_osdSurface->format->Bshift, _osdSurface->format->Ashift));
// The font we are going to use:
const Graphics::Font *font = FontMan.getFontByUsage(Graphics::FontManager::kLocalizedFont);

View File

@ -80,13 +80,13 @@ public:
SurfaceSdlGraphicsManager(SdlEventSource *sdlEventSource);
virtual ~SurfaceSdlGraphicsManager();
virtual void initEventObserver();
virtual void activateManager();
virtual void deactivateManager();
virtual bool hasFeature(OSystem::Feature f);
virtual void setFeatureState(OSystem::Feature f, bool enable);
virtual bool getFeatureState(OSystem::Feature f);
static const OSystem::GraphicsMode *supportedGraphicsModes();
virtual const OSystem::GraphicsMode *getSupportedGraphicsModes() const;
virtual int getDefaultGraphicsMode() const;
virtual bool setGraphicsMode(int mode);
@ -232,6 +232,9 @@ protected:
int _scalerType;
int _transactionMode;
// Indicates whether it is needed to free _hwsurface in destructor
bool _displayDisabled;
bool _screenIsLocked;
Graphics::Surface _framebuffer;

View File

@ -128,7 +128,7 @@ public:
* @param name name of the keymap to push
* @param transparent if true keymapper will iterate down the
* stack if it cannot find a key in the new map
* @return true if succesful
* @return true if successful
*/
bool pushKeymap(const String& name, bool transparent = false);

View File

@ -148,7 +148,7 @@ MidiDriver_TIMIDITY::MidiDriver_TIMIDITY() {
int MidiDriver_TIMIDITY::open() {
char *res;
char timidity_host[MAXHOSTNAMELEN];
char timidity_host[NI_MAXHOST];
int timidity_port, data_port, i;
/* count ourselves open */

View File

@ -0,0 +1,75 @@
/* 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 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.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "backends/mixer/nullmixer/nullsdl-mixer.h"
#include "common/savefile.h"
NullSdlMixerManager::NullSdlMixerManager() : SdlMixerManager() {
_outputRate = 22050;
_callsCounter = 0;
_callbackPeriod = 10;
_samples = 8192;
while (_samples * 16 > _outputRate * 2)
_samples >>= 1;
_samplesBuf = new uint8[_samples * 4];
}
NullSdlMixerManager::~NullSdlMixerManager() {
delete _samplesBuf;
}
void NullSdlMixerManager::init() {
_mixer = new Audio::MixerImpl(g_system, _outputRate);
assert(_mixer);
_mixer->setReady(true);
}
void NullSdlMixerManager::suspendAudio() {
_audioSuspended = true;
}
int NullSdlMixerManager::resumeAudio() {
if (!_audioSuspended) {
return -2;
}
_audioSuspended = false;
return 0;
}
void NullSdlMixerManager::startAudio() {
}
void NullSdlMixerManager::callbackHandler(byte *samples, int len) {
assert(_mixer);
_mixer->mixCallback(samples, len);
}
void NullSdlMixerManager::update() {
if (_audioSuspended) {
return;
}
_callsCounter++;
if ((_callsCounter % _callbackPeriod) == 0) {
callbackHandler(_samplesBuf, _samples);
}
}

View File

@ -0,0 +1,62 @@
/* 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 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.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef BACKENDS_MIXER_NULLSDL_H
#define BACKENDS_MIXER_NULLSDL_H
#include "backends/mixer/sdl/sdl-mixer.h"
#include "common/str.h"
/** Audio mixer which in fact does not output audio.
*
* It is used by events recorder since the recorder is intentionally
* turning sound off to avoid stuttering.
*
* It returns correct output and shoots callbacks, so all OSystem
* users could work without modifications.
*/
class NullSdlMixerManager : public SdlMixerManager {
public:
NullSdlMixerManager();
virtual ~NullSdlMixerManager();
virtual void init();
void update();
virtual void suspendAudio();
virtual int resumeAudio();
protected:
virtual void startAudio();
virtual void callbackHandler(byte *samples, int len);
private:
uint32 _outputRate;
uint32 _callsCounter;
uint8 _callbackPeriod;
uint32 _samples;
uint8 *_samplesBuf;
};
#endif

View File

@ -26,6 +26,7 @@
#include "backends/graphics/graphics.h"
#include "backends/mutex/mutex.h"
#include "gui/EventRecorder.h"
#include "audio/mixer.h"
#include "graphics/pixelformat.h"
@ -52,7 +53,7 @@ bool ModularBackend::hasFeature(Feature f) {
}
void ModularBackend::setFeatureState(Feature f, bool enable) {
return _graphicsManager->setFeatureState(f, enable);
_graphicsManager->setFeatureState(f, enable);
}
bool ModularBackend::getFeatureState(Feature f) {
@ -141,7 +142,15 @@ void ModularBackend::fillScreen(uint32 col) {
}
void ModularBackend::updateScreen() {
#ifdef ENABLE_EVENTRECORDER
g_eventRec.preDrawOverlayGui();
#endif
_graphicsManager->updateScreen();
#ifdef ENABLE_EVENTRECORDER
g_eventRec.postDrawOverlayGui();
#endif
}
void ModularBackend::setShakePos(int shakeOffset) {

View File

@ -40,14 +40,6 @@ MODULE_OBJS += \
keymapper/remap-dialog.o
endif
ifdef USE_OPENGL
MODULE_OBJS += \
graphics/opengl/glerrorcheck.o \
graphics/opengl/gltexture.o \
graphics/opengl/opengl-graphics.o \
graphics/openglsdl/openglsdl-graphics.o
endif
ifdef ENABLE_VKEYBD
MODULE_OBJS += \
vkeybd/image-map.o \
@ -57,6 +49,15 @@ MODULE_OBJS += \
vkeybd/virtual-keyboard-parser.o
endif
# OpenGL specific source files.
ifdef USE_OPENGL
MODULE_OBJS += \
graphics/opengl/debug.o \
graphics/opengl/extensions.o \
graphics/opengl/opengl-graphics.o \
graphics/opengl/texture.o
endif
# SDL specific source files.
# We cannot just check $BACKEND = sdl, as various other backends
# derive from the SDL backend, and they all need the following files.
@ -70,12 +71,17 @@ MODULE_OBJS += \
mutex/sdl/sdl-mutex.o \
plugins/sdl/sdl-provider.o \
timer/sdl/sdl-timer.o
# SDL 1.3 removed audio CD support
ifndef USE_SDL13
MODULE_OBJS += \
audiocd/sdl/sdl-audiocd.o
endif
ifdef USE_OPENGL
MODULE_OBJS += \
graphics/openglsdl/openglsdl-graphics.o
endif
endif
ifdef POSIX
@ -120,9 +126,9 @@ MODULE_OBJS += \
mixer/sdl13/sdl13-mixer.o
endif
ifeq ($(BACKEND),bada)
ifeq ($(BACKEND),tizen)
MODULE_OBJS += \
timer/bada/timer.o
timer/tizen/timer.o
endif
ifeq ($(BACKEND),ds)
@ -214,5 +220,11 @@ MODULE_OBJS += \
plugins/wii/wii-provider.o
endif
ifdef ENABLE_EVENTRECORDER
MODULE_OBJS += \
mixer/nullmixer/nullsdl-mixer.o \
saves/recorder/recorder-saves.o
endif
# Include common rules
include $(srcdir)/rules.mk

View File

@ -33,15 +33,15 @@ OSystem::MutexRef SdlMutexManager::createMutex() {
}
void SdlMutexManager::lockMutex(OSystem::MutexRef mutex) {
SDL_mutexP((SDL_mutex *) mutex);
SDL_mutexP((SDL_mutex *)mutex);
}
void SdlMutexManager::unlockMutex(OSystem::MutexRef mutex) {
SDL_mutexV((SDL_mutex *) mutex);
SDL_mutexV((SDL_mutex *)mutex);
}
void SdlMutexManager::deleteMutex(OSystem::MutexRef mutex) {
SDL_DestroyMutex((SDL_mutex *) mutex);
SDL_DestroyMutex((SDL_mutex *)mutex);
}
#endif

View File

@ -146,7 +146,8 @@ OSystem_Android::OSystem_Android(int audio_sample_rate, int audio_buffer_size) :
_touchpad_scale(66),
_dpad_scale(4),
_fingersDown(0),
_trackball_scale(2) {
_trackball_scale(2),
_joystick_scale(10) {
_fsFactory = new POSIXFilesystemFactory();
@ -450,7 +451,7 @@ bool OSystem_Android::getFeatureState(Feature f) {
}
}
uint32 OSystem_Android::getMillis() {
uint32 OSystem_Android::getMillis(bool skipRecord) {
timeval curTime;
gettimeofday(&curTime, 0);

View File

@ -231,6 +231,7 @@ private:
int _touchpad_scale;
int _trackball_scale;
int _dpad_scale;
int _joystick_scale;
int _fingersDown;
void clipMouse(Common::Point &p);
@ -274,7 +275,7 @@ public:
virtual void setCursorPalette(const byte *colors, uint start, uint num);
virtual bool pollEvent(Common::Event &event);
virtual uint32 getMillis();
virtual uint32 getMillis(bool skipRecord = false);
virtual void delayMillis(uint msecs);
virtual MutexRef createMutex(void);

View File

@ -25,13 +25,19 @@ PATH_RESOURCES = $(PATH_DIST)/res
PORT_DISTFILES = $(PATH_DIST)/README.Android
# FIXME: OUYA specific.
# "values-television" not present in vanilla Android.
# $(PATH_RESOURCES)/../res-ouya/values-television/margins.xml \
RESOURCES = \
$(PATH_RESOURCES)/values/strings.xml \
$(PATH_RESOURCES)/values/margins.xml \
$(PATH_RESOURCES)/layout/main.xml \
$(PATH_RESOURCES)/layout/splash.xml \
$(PATH_RESOURCES)/drawable/gradient.xml \
$(PATH_RESOURCES)/drawable/scummvm.png \
$(PATH_RESOURCES)/drawable/scummvm_big.png
$(PATH_RESOURCES)/drawable/scummvm_big.png \
$(PATH_RESOURCES)/drawable-xhdpi/ouya_icon.png
PLUGIN_RESOURCES = \
$(PATH_RESOURCES)/values/strings.xml \

View File

@ -64,6 +64,10 @@ enum {
JE_RMB_DOWN = 11,
JE_RMB_UP = 12,
JE_MOUSE_MOVE = 13,
JE_GAMEPAD = 14,
JE_JOYSTICK = 15,
JE_MMB_DOWN = 16,
JE_MMB_UP = 17,
JE_QUIT = 0x1000
};
@ -109,6 +113,25 @@ enum {
JKEYCODE_DPAD_CENTER = 23
};
// gamepad
enum {
JKEYCODE_BUTTON_A = 96,
JKEYCODE_BUTTON_B = 97,
JKEYCODE_BUTTON_C = 98,
JKEYCODE_BUTTON_X = 99,
JKEYCODE_BUTTON_Y = 100,
JKEYCODE_BUTTON_Z = 101,
JKEYCODE_BUTTON_L1 = 102,
JKEYCODE_BUTTON_R1 = 103,
JKEYCODE_BUTTON_L2 = 104,
JKEYCODE_BUTTON_R2 = 105,
JKEYCODE_BUTTON_THUMBL = 106,
JKEYCODE_BUTTON_THUMBR = 107,
JKEYCODE_BUTTON_START = 108,
JKEYCODE_BUTTON_SELECT = 109,
JKEYCODE_BUTTON_MODE = 110,
};
// meta modifier
enum {
JMETA_SHIFT = 0x01,
@ -827,6 +850,94 @@ void OSystem_Android::pushEvent(int type, int arg1, int arg2, int arg3,
return;
case JE_GAMEPAD:
switch (arg1) {
case JACTION_DOWN:
e.type = Common::EVENT_KEYDOWN;
break;
case JACTION_UP:
e.type = Common::EVENT_KEYUP;
break;
default:
LOGE("unhandled jaction on gamepad key: %d", arg1);
return;
}
switch (arg2) {
case JKEYCODE_BUTTON_A:
case JKEYCODE_BUTTON_B:
switch (arg1) {
case JACTION_DOWN:
e.type = (arg2 == JKEYCODE_BUTTON_A?
Common::EVENT_LBUTTONDOWN :
Common::EVENT_RBUTTONDOWN);
break;
case JACTION_UP:
e.type = (arg2 == JKEYCODE_BUTTON_A?
Common::EVENT_LBUTTONUP :
Common::EVENT_RBUTTONUP);
break;
}
e.mouse = getEventManager()->getMousePos();
break;
case JKEYCODE_BUTTON_X:
e.kbd.keycode = Common::KEYCODE_ESCAPE;
e.kbd.ascii = Common::ASCII_ESCAPE;
break;
default:
LOGW("unmapped gamepad key: %d", arg2);
return;
}
lockMutex(_event_queue_lock);
_event_queue.push(e);
unlockMutex(_event_queue_lock);
break;
case JE_JOYSTICK:
e.mouse = getEventManager()->getMousePos();
switch (arg1) {
case JACTION_MULTIPLE:
e.type = Common::EVENT_MOUSEMOVE;
// already multiplied by 100
e.mouse.x += arg2 * _joystick_scale / _eventScaleX;
e.mouse.y += arg3 * _joystick_scale / _eventScaleY;
clipMouse(e.mouse);
break;
default:
LOGE("unhandled jaction on joystick: %d", arg1);
return;
}
lockMutex(_event_queue_lock);
_event_queue.push(e);
unlockMutex(_event_queue_lock);
return;
case JE_MMB_DOWN:
e.type = Common::EVENT_MAINMENU;
lockMutex(_event_queue_lock);
_event_queue.push(e);
unlockMutex(_event_queue_lock);
return;
case JE_MMB_UP:
// No action
return;
case JE_QUIT:
e.type = Common::EVENT_QUIT;

View File

@ -552,7 +552,7 @@ Graphics::Surface *OSystem_Android::lockScreen() {
GLTHREADCHECK;
Graphics::Surface *surface = _game_texture->surface();
assert(surface->pixels);
assert(surface->getPixels());
return surface;
}
@ -645,7 +645,7 @@ void OSystem_Android::grabOverlay(void *buf, int pitch) {
assert(surface->format.bytesPerPixel == sizeof(uint16));
byte *dst = (byte *)buf;
const byte *src = (const byte *)surface->pixels;
const byte *src = (const byte *)surface->getPixels();
uint h = surface->h;
do {

View File

@ -14,6 +14,7 @@ public class MouseHelper {
private long _rmbGuardTime;
private boolean _rmbPressed;
private boolean _lmbPressed;
private boolean _mmbPressed;
/**
* Class initialization fails when this throws an exception.
@ -114,6 +115,23 @@ public class MouseHelper {
_rmbPressed = false;
}
boolean mmbDown = (buttonState & MotionEvent.BUTTON_TERTIARY) == MotionEvent.BUTTON_TERTIARY;
if (mmbDown) {
if (!_mmbPressed) {
// middle mouse button was pressed just now
_scummvm.pushEvent(ScummVMEvents.JE_MMB_DOWN, (int)e.getX(), (int)e.getY(), e.getButtonState(), 0, 0);
}
_mmbPressed = true;
} else {
if (_mmbPressed) {
// middle mouse button was released just now
_scummvm.pushEvent(ScummVMEvents.JE_MMB_UP, (int)e.getX(), (int)e.getY(), e.getButtonState(), 0, 0);
}
_mmbPressed = false;
}
return true;
}

View File

@ -240,6 +240,14 @@ public class ScummVMActivity extends Activity {
return false;
}
@Override
public boolean onGenericMotionEvent(final MotionEvent e) {
if (_events != null)
return _events.onGenericMotionEvent(e);
return false;
}
private void showKeyboard(boolean show) {
SurfaceView main_surface = (SurfaceView)findViewById(R.id.main_surface);
InputMethodManager imm = (InputMethodManager)

View File

@ -9,6 +9,7 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.GestureDetector;
import android.view.InputDevice;
import android.view.inputmethod.InputMethodManager;
public class ScummVMEvents implements
@ -31,6 +32,10 @@ public class ScummVMEvents implements
public static final int JE_RMB_DOWN = 11;
public static final int JE_RMB_UP = 12;
public static final int JE_MOUSE_MOVE = 13;
public static final int JE_GAMEPAD = 14;
public static final int JE_JOYSTICK = 15;
public static final int JE_MMB_DOWN = 16;
public static final int JE_MMB_UP = 17;
public static final int JE_QUIT = 0x1000;
final protected Context _context;
@ -63,6 +68,18 @@ public class ScummVMEvents implements
return true;
}
public boolean onGenericMotionEvent(final MotionEvent e) {
if((e.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
_scummvm.pushEvent(JE_JOYSTICK, e.getAction(),
(int)(e.getAxisValue(MotionEvent.AXIS_X)*100),
(int)(e.getAxisValue(MotionEvent.AXIS_Y)*100),
0, 0);
return true;
}
return false;
}
final static int MSG_MENU_LONG_PRESS = 1;
final private Handler keyHandler = new Handler() {
@ -177,6 +194,25 @@ public class ScummVMEvents implements
(int)(e.getEventTime() - e.getDownTime()),
e.getRepeatCount(), 0);
return true;
case KeyEvent.KEYCODE_BUTTON_A:
case KeyEvent.KEYCODE_BUTTON_B:
case KeyEvent.KEYCODE_BUTTON_C:
case KeyEvent.KEYCODE_BUTTON_X:
case KeyEvent.KEYCODE_BUTTON_Y:
case KeyEvent.KEYCODE_BUTTON_Z:
case KeyEvent.KEYCODE_BUTTON_L1:
case KeyEvent.KEYCODE_BUTTON_R1:
case KeyEvent.KEYCODE_BUTTON_L2:
case KeyEvent.KEYCODE_BUTTON_R2:
case KeyEvent.KEYCODE_BUTTON_THUMBL:
case KeyEvent.KEYCODE_BUTTON_THUMBR:
case KeyEvent.KEYCODE_BUTTON_START:
case KeyEvent.KEYCODE_BUTTON_SELECT:
case KeyEvent.KEYCODE_BUTTON_MODE:
_scummvm.pushEvent(JE_GAMEPAD, action, keyCode,
(int)(e.getEventTime() - e.getDownTime()),
e.getRepeatCount(), 0);
return true;
}
_scummvm.pushEvent(JE_KEY, action, keyCode,

View File

@ -233,7 +233,7 @@ void GLESTexture::allocBuffer(GLuint w, GLuint h) {
_pixels = new byte[w * h * _surface.format.bytesPerPixel];
assert(_pixels);
_surface.pixels = _pixels;
_surface.setPixels(_pixels);
fillBuffer(0);
@ -256,7 +256,7 @@ void GLESTexture::updateBuffer(GLuint x, GLuint y, GLuint w, GLuint h,
}
void GLESTexture::fillBuffer(uint32 color) {
assert(_surface.pixels);
assert(_surface.getPixels());
if (_pixelFormat.bytesPerPixel == 1 ||
((color & 0xff) == ((color >> 8) & 0xff)))
@ -377,7 +377,7 @@ void GLESFakePaletteTexture::allocBuffer(GLuint w, GLuint h) {
assert(_pixels);
// fixup surface, for the outside this is a CLUT8 surface
_surface.pixels = _pixels;
_surface.setPixels(_pixels);
fillBuffer(0);
@ -386,8 +386,8 @@ void GLESFakePaletteTexture::allocBuffer(GLuint w, GLuint h) {
}
void GLESFakePaletteTexture::fillBuffer(uint32 color) {
assert(_surface.pixels);
memset(_surface.pixels, color & 0xff, _surface.pitch * _surface.h);
assert(_surface.getPixels());
memset(_surface.getPixels(), color & 0xff, _surface.pitch * _surface.h);
setDirty();
}

View File

@ -1,111 +0,0 @@
/* 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 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "engines/engine.h"
#include "backends/platform/bada/form.h"
#include "backends/platform/bada/system.h"
#include "backends/platform/bada/application.h"
using namespace Osp::System;
using namespace Osp::Ui::Controls;
Application *BadaScummVM::createInstance() {
return new BadaScummVM();
}
BadaScummVM::BadaScummVM() : _appForm(0) {
}
BadaScummVM::~BadaScummVM() {
logEntered();
if (g_system) {
BadaSystem *system = (BadaSystem *)g_system;
system->destroyBackend();
delete system;
g_system = 0;
}
}
bool BadaScummVM::OnAppInitializing(AppRegistry &appRegistry) {
_appForm = systemStart(this);
return (_appForm != NULL);
}
bool BadaScummVM::OnAppTerminating(AppRegistry &appRegistry,
bool forcedTermination) {
logEntered();
return true;
}
void BadaScummVM::OnUserEventReceivedN(RequestId requestId,
Osp::Base::Collection::IList *args) {
logEntered();
if (requestId == USER_MESSAGE_EXIT) {
// normal program termination
Terminate();
} else if (requestId == USER_MESSAGE_EXIT_ERR) {
// assertion failure termination
String *message = NULL;
if (args) {
message = (String *)args->GetAt(0);
}
if (!message) {
message = new String("Unknown error");
}
MessageBox messageBox;
messageBox.Construct(L"Oops...", *message, MSGBOX_STYLE_OK);
int modalResult;
messageBox.ShowAndWait(modalResult);
Terminate();
}
}
void BadaScummVM::OnForeground(void) {
logEntered();
pauseGame(false);
}
void BadaScummVM::OnBackground(void) {
logEntered();
pauseGame(true);
}
void BadaScummVM::OnBatteryLevelChanged(BatteryLevel batteryLevel) {
}
void BadaScummVM::OnLowMemory(void) {
}
void BadaScummVM::pauseGame(bool pause) {
if (_appForm) {
if (pause && g_engine && !g_engine->isPaused()) {
_appForm->pushKey(Common::KEYCODE_SPACE);
}
if (g_system) {
((BadaSystem *)g_system)->setMute(pause);
}
}
}

View File

@ -1,5 +0,0 @@
# Bada specific modules are built under eclipse
$(EXECUTABLE): $(OBJS)
rm -f $@
ar Tru $@ $(OBJS)

View File

@ -1,464 +0,0 @@
/* 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 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include <FAppApplication.h>
#include "common/translation.h"
#include "base/main.h"
#include "backends/platform/bada/form.h"
#include "backends/platform/bada/system.h"
using namespace Osp::Base::Runtime;
using namespace Osp::Ui;
using namespace Osp::Ui::Controls;
// number of volume levels
#define LEVEL_RANGE 5
// round down small Y touch values to 1 to allow the
// cursor to be positioned at the top of the screen
#define MIN_TOUCH_Y 10
// block for up to 2.5 seconds during shutdown to
// allow the game thread to exit gracefully.
#define EXIT_SLEEP_STEP 10
#define EXIT_SLEEP 250
//
// BadaAppForm
//
BadaAppForm::BadaAppForm() :
_gameThread(0),
_state(kInitState),
_buttonState(kLeftButton),
_shortcut(kSetVolume) {
_eventQueueLock = new Mutex();
_eventQueueLock->Create();
}
result BadaAppForm::Construct() {
result r = Form::Construct(Controls::FORM_STYLE_NORMAL);
if (IsFailed(r)) {
return r;
}
BadaSystem *badaSystem = NULL;
_gameThread = NULL;
badaSystem = new BadaSystem(this);
r = badaSystem != NULL ? E_SUCCESS : E_OUT_OF_MEMORY;
if (!IsFailed(r)) {
r = badaSystem->Construct();
}
if (!IsFailed(r)) {
_gameThread = new Thread();
r = _gameThread != NULL ? E_SUCCESS : E_OUT_OF_MEMORY;
}
if (!IsFailed(r)) {
r = _gameThread->Construct(*this);
}
if (IsFailed(r)) {
if (badaSystem != NULL) {
delete badaSystem;
}
if (_gameThread != NULL) {
delete _gameThread;
_gameThread = NULL;
}
} else {
g_system = badaSystem;
}
return r;
}
BadaAppForm::~BadaAppForm() {
logEntered();
if (_gameThread && _state != kErrorState) {
terminate();
_gameThread->Stop();
if (_state != kErrorState) {
_gameThread->Join();
}
delete _gameThread;
_gameThread = NULL;
}
if (_eventQueueLock) {
delete _eventQueueLock;
_eventQueueLock = NULL;
}
logLeaving();
}
//
// abort the game thread
//
void BadaAppForm::terminate() {
if (_state == kActiveState) {
((BadaSystem *)g_system)->setMute(true);
_eventQueueLock->Acquire();
Common::Event e;
e.type = Common::EVENT_QUIT;
_eventQueue.push(e);
_state = kClosingState;
_eventQueueLock->Release();
// block while thread ends
AppLog("waiting for shutdown");
for (int i = 0; i < EXIT_SLEEP_STEP && _state == kClosingState; i++) {
Thread::Sleep(EXIT_SLEEP);
}
if (_state == kClosingState) {
// failed to terminate - Join() will freeze
_state = kErrorState;
}
}
}
void BadaAppForm::exitSystem() {
_state = kErrorState;
if (_gameThread) {
_gameThread->Stop();
delete _gameThread;
_gameThread = NULL;
}
}
result BadaAppForm::OnInitializing(void) {
logEntered();
SetOrientation(ORIENTATION_LANDSCAPE);
AddOrientationEventListener(*this);
AddTouchEventListener(*this);
AddKeyEventListener(*this);
// set focus to enable receiving key events
SetFocusable(true);
SetFocus();
return E_SUCCESS;
}
result BadaAppForm::OnDraw(void) {
logEntered();
if (g_system) {
BadaSystem *system = (BadaSystem *)g_system;
BadaGraphicsManager *graphics = system->getGraphics();
if (graphics && graphics->isReady()) {
g_system->updateScreen();
}
}
return E_SUCCESS;
}
bool BadaAppForm::pollEvent(Common::Event &event) {
bool result = false;
_eventQueueLock->Acquire();
if (!_eventQueue.empty()) {
event = _eventQueue.pop();
result = true;
}
_eventQueueLock->Release();
return result;
}
void BadaAppForm::pushEvent(Common::EventType type, const Point &currentPosition) {
BadaSystem *system = (BadaSystem *)g_system;
BadaGraphicsManager *graphics = system->getGraphics();
if (graphics) {
// graphics could be NULL at startup or when
// displaying the system error screen
Common::Event e;
e.type = type;
e.mouse.x = currentPosition.x;
e.mouse.y = currentPosition.y > MIN_TOUCH_Y ? currentPosition.y : 1;
bool moved = graphics->moveMouse(e.mouse.x, e.mouse.y);
_eventQueueLock->Acquire();
if (moved && type != Common::EVENT_MOUSEMOVE) {
Common::Event moveEvent;
moveEvent.type = Common::EVENT_MOUSEMOVE;
moveEvent.mouse = e.mouse;
_eventQueue.push(moveEvent);
}
_eventQueue.push(e);
_eventQueueLock->Release();
}
}
void BadaAppForm::pushKey(Common::KeyCode keycode) {
Common::Event e;
e.synthetic = false;
e.kbd.keycode = keycode;
e.kbd.ascii = keycode;
e.kbd.flags = 0;
_eventQueueLock->Acquire();
e.type = Common::EVENT_KEYDOWN;
_eventQueue.push(e);
e.type = Common::EVENT_KEYUP;
_eventQueue.push(e);
_eventQueueLock->Release();
}
void BadaAppForm::OnOrientationChanged(const Control &source,
OrientationStatus orientationStatus) {
logEntered();
if (_state == kInitState) {
_state = kActiveState;
_gameThread->Start();
}
}
Object *BadaAppForm::Run(void) {
scummvm_main(0, 0);
if (_state == kActiveState) {
Application::GetInstance()->SendUserEvent(USER_MESSAGE_EXIT, NULL);
}
_state = kDoneState;
return NULL;
}
void BadaAppForm::setButtonShortcut() {
switch (_buttonState) {
case kLeftButton:
g_system->displayMessageOnOSD(_("Right Click Once"));
_buttonState = kRightButtonOnce;
break;
case kRightButtonOnce:
g_system->displayMessageOnOSD(_("Right Click"));
_buttonState = kRightButton;
break;
case kRightButton:
g_system->displayMessageOnOSD(_("Move Only"));
_buttonState = kMoveOnly;
break;
case kMoveOnly:
g_system->displayMessageOnOSD(_("Left Click"));
_buttonState = kLeftButton;
break;
}
}
void BadaAppForm::setShortcut() {
// cycle to the next shortcut
switch (_shortcut) {
case kControlMouse:
g_system->displayMessageOnOSD(_("Escape Key"));
_shortcut = kEscapeKey;
break;
case kEscapeKey:
g_system->displayMessageOnOSD(_("Game Menu"));
_shortcut = kGameMenu;
break;
case kGameMenu:
g_system->displayMessageOnOSD(_("Show Keypad"));
_shortcut = kShowKeypad;
break;
case kSetVolume:
// fallthru
case kShowKeypad:
g_system->displayMessageOnOSD(_("Control Mouse"));
_shortcut = kControlMouse;
break;
}
}
void BadaAppForm::setVolume(bool up, bool minMax) {
int level = ((BadaSystem *)g_system)->setVolume(up, minMax);
if (level != -1) {
char message[32];
char ind[LEVEL_RANGE]; // 1..5 (0=off)
int j = LEVEL_RANGE - 1; // 0..4
for (int i = 1; i <= LEVEL_RANGE; i++) {
ind[j--] = level >= i ? '|' : ' ';
}
snprintf(message, sizeof(message), "Volume: [ %c%c%c%c%c ]",
ind[0], ind[1], ind[2], ind[3], ind[4]);
g_system->displayMessageOnOSD(message);
}
}
void BadaAppForm::showKeypad() {
// display the soft keyboard
_buttonState = kLeftButton;
pushKey(Common::KEYCODE_F7);
}
void BadaAppForm::OnTouchDoublePressed(const Control &source,
const Point &currentPosition,
const TouchEventInfo &touchInfo) {
if (_buttonState != kMoveOnly) {
pushEvent(_buttonState == kLeftButton ? Common::EVENT_LBUTTONDOWN : Common::EVENT_RBUTTONDOWN,
currentPosition);
pushEvent(_buttonState == kLeftButton ? Common::EVENT_LBUTTONDOWN : Common::EVENT_RBUTTONDOWN,
currentPosition);
}
}
void BadaAppForm::OnTouchFocusIn(const Control &source,
const Point &currentPosition,
const TouchEventInfo &touchInfo) {
}
void BadaAppForm::OnTouchFocusOut(const Control &source,
const Point &currentPosition,
const TouchEventInfo &touchInfo) {
}
void BadaAppForm::OnTouchLongPressed(const Control &source,
const Point &currentPosition,
const TouchEventInfo &touchInfo) {
if (_buttonState != kLeftButton) {
pushKey(Common::KEYCODE_RETURN);
}
}
void BadaAppForm::OnTouchMoved(const Control &source,
const Point &currentPosition,
const TouchEventInfo &touchInfo) {
pushEvent(Common::EVENT_MOUSEMOVE, currentPosition);
}
void BadaAppForm::OnTouchPressed(const Control &source,
const Point &currentPosition,
const TouchEventInfo &touchInfo) {
if (_buttonState != kMoveOnly) {
pushEvent(_buttonState == kLeftButton ? Common::EVENT_LBUTTONDOWN : Common::EVENT_RBUTTONDOWN,
currentPosition);
}
}
void BadaAppForm::OnTouchReleased(const Control &source,
const Point &currentPosition,
const TouchEventInfo &touchInfo) {
if (_buttonState != kMoveOnly) {
pushEvent(_buttonState == kLeftButton ? Common::EVENT_LBUTTONUP : Common::EVENT_RBUTTONUP,
currentPosition);
if (_buttonState == kRightButtonOnce) {
_buttonState = kLeftButton;
}
// flick to skip dialog
if (touchInfo.IsFlicked()) {
pushKey(Common::KEYCODE_PERIOD);
}
}
}
void BadaAppForm::OnKeyLongPressed(const Control &source, KeyCode keyCode) {
logEntered();
switch (keyCode) {
case KEY_SIDE_UP:
_shortcut = kSetVolume;
setVolume(true, true);
return;
case KEY_SIDE_DOWN:
_shortcut = kSetVolume;
setVolume(false, true);
return;
case KEY_CAMERA:
_shortcut = kShowKeypad;
showKeypad();
return;
default:
break;
}
}
void BadaAppForm::OnKeyPressed(const Control &source, KeyCode keyCode) {
switch (keyCode) {
case KEY_SIDE_UP:
if (_shortcut != kSetVolume) {
_shortcut = kSetVolume;
} else {
setVolume(true, false);
}
return;
case KEY_SIDE_DOWN:
switch (_shortcut) {
case kControlMouse:
setButtonShortcut();
break;
case kEscapeKey:
pushKey(Common::KEYCODE_ESCAPE);
break;
case kGameMenu:
_buttonState = kLeftButton;
pushKey(Common::KEYCODE_F5);
break;
case kShowKeypad:
showKeypad();
break;
default:
setVolume(false, false);
break;
}
break;
case KEY_CAMERA:
setShortcut();
break;
default:
break;
}
}
void BadaAppForm::OnKeyReleased(const Control &source, KeyCode keyCode) {
}

View File

@ -1,108 +0,0 @@
/* 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 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef BADA_FORM_H
#define BADA_FORM_H
#include <FApp.h>
#include <FUi.h>
#include <FSystem.h>
#include <FBase.h>
#include <FUiITouchEventListener.h>
#include <FUiITextEventListener.h>
#include "config.h"
#include "common/scummsys.h"
#include "common/events.h"
#include "common/queue.h"
#include "common/mutex.h"
//
// BadaAppForm
//
class BadaAppForm : public Osp::Ui::Controls::Form,
public Osp::Ui::IOrientationEventListener,
public Osp::Ui::ITouchEventListener,
public Osp::Ui::IKeyEventListener,
public Osp::Base::Runtime::IRunnable {
public:
BadaAppForm();
~BadaAppForm();
result Construct();
bool pollEvent(Common::Event &event);
bool isClosing() { return _state == kClosingState; }
void pushKey(Common::KeyCode keycode);
void exitSystem();
private:
Object *Run();
result OnInitializing(void);
result OnDraw(void);
void OnOrientationChanged(const Osp::Ui::Control &source,
Osp::Ui::OrientationStatus orientationStatus);
void OnTouchDoublePressed(const Osp::Ui::Control &source,
const Osp::Graphics::Point &currentPosition,
const Osp::Ui::TouchEventInfo &touchInfo);
void OnTouchFocusIn(const Osp::Ui::Control &source,
const Osp::Graphics::Point &currentPosition,
const Osp::Ui::TouchEventInfo &touchInfo);
void OnTouchFocusOut(const Osp::Ui::Control &source,
const Osp::Graphics::Point &currentPosition,
const Osp::Ui::TouchEventInfo &touchInfo);
void OnTouchLongPressed(const Osp::Ui::Control &source,
const Osp::Graphics::Point &currentPosition,
const Osp::Ui::TouchEventInfo &touchInfo);
void OnTouchMoved(const Osp::Ui::Control &source,
const Osp::Graphics::Point &currentPosition,
const Osp::Ui::TouchEventInfo &touchInfo);
void OnTouchPressed(const Osp::Ui::Control &source,
const Osp::Graphics::Point &currentPosition,
const Osp::Ui::TouchEventInfo &touchInfo);
void OnTouchReleased(const Osp::Ui::Control &source,
const Osp::Graphics::Point &currentPosition,
const Osp::Ui::TouchEventInfo &touchInfo);
void OnKeyLongPressed(const Osp::Ui::Control &source,
Osp::Ui::KeyCode keyCode);
void OnKeyPressed(const Osp::Ui::Control &source,
Osp::Ui::KeyCode keyCode);
void OnKeyReleased(const Osp::Ui::Control &source,
Osp::Ui::KeyCode keyCode);
void pushEvent(Common::EventType type,
const Osp::Graphics::Point &currentPosition);
void terminate();
void setButtonShortcut();
void setShortcut();
void setVolume(bool up, bool minMax);
void showKeypad();
// event handling
Osp::Base::Runtime::Thread *_gameThread;
Osp::Base::Runtime::Mutex *_eventQueueLock;
Common::Queue<Common::Event> _eventQueue;
enum { kInitState, kActiveState, kClosingState, kDoneState, kErrorState } _state;
enum { kLeftButton, kRightButtonOnce, kRightButton, kMoveOnly } _buttonState;
enum { kControlMouse, kEscapeKey, kGameMenu, kShowKeypad, kSetVolume } _shortcut;
};
#endif

View File

@ -151,7 +151,7 @@ public:
void setShakePos(int shake_pos);
// Get the number of milliseconds since the program was started.
uint32 getMillis();
uint32 getMillis(bool skipRecord = false);
// Delay for a specified amount of milliseconds
void delayMillis(uint msecs);

View File

@ -711,11 +711,7 @@ Graphics::Surface *OSystem_Dreamcast::lockScreen()
if (!screen)
return 0;
_framebuffer.pixels = screen;
_framebuffer.w = _screen_w;
_framebuffer.h = _screen_h;
_framebuffer.pitch = SCREEN_W*2;
_framebuffer.format = screenFormats[_screenFormat];
_framebuffer.init(_screen_w, _screen_h, SCREEN_W*2, screen, screenFormats[_screenFormat]);
return &_framebuffer;
}

View File

@ -219,7 +219,7 @@ static int findGames(Game *games, int max, bool use_ini)
if (use_ini) {
ConfMan.loadDefaultConfigFile();
Common::ConfigManager::DomainMap &game_domains = ConfMan.getGameDomains();
const Common::ConfigManager::DomainMap &game_domains = ConfMan.getGameDomains();
for(Common::ConfigManager::DomainMap::const_iterator i =
game_domains.begin(); curr_game < max && i != game_domains.end(); i++) {
Common::String path = (*i)._value["path"];

View File

@ -26,7 +26,7 @@
#include "dc.h"
uint32 OSystem_Dreamcast::getMillis()
uint32 OSystem_Dreamcast::getMillis(bool skipRecord)
{
static uint32 msecs=0;
static unsigned int t0=0;

View File

@ -2280,7 +2280,7 @@ void VBlankHandler(void) {
//REG_IF = IRQ_VBLANK;
}
int getMillis() {
int getMillis(bool skipRecord) {
return currentTimeMillis;
// return frameCount * FRAME_TIME;
}

View File

@ -88,7 +88,7 @@ void setGamma(int gamma);
// Timers
void setTimerCallback(OSystem_DS::TimerProc proc, int interval); // Setup a callback function at a regular interval
int getMillis(); // Return the current runtime in milliseconds
int getMillis(bool skipRecord = false); // Return the current runtime in milliseconds
void doTimerCallback(); // Call callback function if required
// Sound

View File

@ -296,7 +296,7 @@ void OSystem_DS::copyRectToScreen(const void *buf, int pitch, int x, int y, int
// to save a few pennies/euro cents on the hardware.
if (_frameBufferExists) {
bg = (u16 *)_framebuffer.pixels;
bg = (u16 *)_framebuffer.getPixels();
stride = _framebuffer.pitch;
} else {
bg = (u16 *)DS::get8BitBackBuffer();
@ -455,7 +455,7 @@ void OSystem_DS::copyRectToScreen(const void *buf, int pitch, int x, int y, int
dmaCopyHalfWords(3, src, dest1, w);
if ((!_frameBufferExists) || (buf == _framebuffer.pixels)) {
if ((!_frameBufferExists) || (buf == _framebuffer.getPixels())) {
dmaCopyHalfWords(2, src, dest2, w);
}
@ -476,7 +476,7 @@ void OSystem_DS::updateScreen() {
_frameBufferExists = false;
// Copy temp framebuffer back to screen
copyRectToScreen((byte *)_framebuffer.pixels, _framebuffer.pitch, 0, 0, _framebuffer.w, _framebuffer.h);
copyRectToScreen((byte *)_framebuffer.getPixels(), _framebuffer.pitch, 0, 0, _framebuffer.w, _framebuffer.h);
}
DS::displayMode16BitFlipBuffer();
@ -656,7 +656,7 @@ bool OSystem_DS::pollEvent(Common::Event &event) {
return false;
}
uint32 OSystem_DS::getMillis() {
uint32 OSystem_DS::getMillis(bool skipRecord) {
return DS::getMillis();
}
@ -755,11 +755,8 @@ Graphics::Surface *OSystem_DS::createTempFrameBuffer() {
if (DS::isCpuScalerEnabled()) {
_framebuffer.pixels = DS::getScalerBuffer();
_framebuffer.w = DS::getGameWidth();
_framebuffer.h = DS::getGameHeight();
_framebuffer.pitch = DS::getGameWidth();
_framebuffer.format = Graphics::PixelFormat::createFormatCLUT8();
_framebuffer.init(DS::getGameWidth(), DS::getGameHeight(), DS::getGameWidth(),
DS::getScalerBuffer(), Graphics::PixelFormat::createFormatCLUT8());
} else {
@ -780,11 +777,7 @@ Graphics::Surface *OSystem_DS::createTempFrameBuffer() {
dmaCopyHalfWords(3, srcLine, destLine, width);
}
_framebuffer.pixels = dest;
_framebuffer.w = width;
_framebuffer.h = height;
_framebuffer.pitch = width;
_framebuffer.format = Graphics::PixelFormat::createFormatCLUT8();
_framebuffer.init(width, height, width, dest, Graphics::PixelFormat::createFormatCLUT8());
}
@ -798,8 +791,8 @@ Graphics::Surface *OSystem_DS::createTempFrameBuffer() {
for (int y = 0; y < DS::getGameHeight(); y++) {
DC_FlushRange(image + (y * imageStrideInWords), DS::getGameWidth());
for (int x = 0; x < DS::getGameWidth() >> 1; x++) {
*(((u16 *) (_framebuffer.pixels)) + y * (DS::getGameWidth() >> 1) + x) = image[(y * imageStrideInWords) + x];
// *(((u16 *) (surf->pixels)) + y * (DS::getGameWidth() >> 1) + x) = image[y * imageStrideInWords + x];
*(((u16 *) (_framebuffer.getPixels())) + y * (DS::getGameWidth() >> 1) + x) = image[(y * imageStrideInWords) + x];
// *(((u16 *) (surf->getPixels())) + y * (DS::getGameWidth() >> 1) + x) = image[y * imageStrideInWords + x];
}
}*/

View File

@ -117,7 +117,7 @@ public:
virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, u32 keycolor, bool dontScale, const Graphics::PixelFormat *format);
virtual bool pollEvent(Common::Event &event);
virtual uint32 getMillis();
virtual uint32 getMillis(bool skipRecord = false);
virtual void delayMillis(uint msecs);
virtual void getTimeAndDate(TimeDate &t) const;

View File

@ -172,7 +172,7 @@ void OSystem_GPH::initSDL() {
// Check if SDL has not been initialized
if (!_initedSDL) {
uint32 sdlFlags = SDL_INIT_EVENTTHREAD;
uint32 sdlFlags = SDL_INIT_EVENTTHREAD | SDL_INIT_VIDEO;
if (ConfMan.hasKey("disable_sdl_parachute"))
sdlFlags |= SDL_INIT_NOPARACHUTE;

View File

@ -365,7 +365,7 @@ const char *iPhone_getDocumentsDir() {
_mouseTexCoords[5] = _mouseTexCoords[7] = _videoContext.mouseHeight / (GLfloat)_videoContext.mouseTexture.h;
glBindTexture(GL_TEXTURE_2D, _mouseCursorTexture); printOpenGLError();
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _videoContext.mouseTexture.w, _videoContext.mouseTexture.h, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, _videoContext.mouseTexture.pixels); printOpenGLError();
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _videoContext.mouseTexture.w, _videoContext.mouseTexture.h, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, _videoContext.mouseTexture.getPixels()); printOpenGLError();
}
- (void)updateMainSurface {
@ -377,7 +377,7 @@ const char *iPhone_getDocumentsDir() {
// Unfortunately we have to update the whole texture every frame, since glTexSubImage2D is actually slower in all cases
// due to the iPhone internals having to convert the whole texture back from its internal format when used.
// In the future we could use several tiled textures instead.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _videoContext.screenTexture.w, _videoContext.screenTexture.h, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, _videoContext.screenTexture.pixels); printOpenGLError();
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _videoContext.screenTexture.w, _videoContext.screenTexture.h, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, _videoContext.screenTexture.getPixels()); printOpenGLError();
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); printOpenGLError();
}
@ -386,7 +386,7 @@ const char *iPhone_getDocumentsDir() {
glTexCoordPointer(2, GL_FLOAT, 0, _overlayTexCoords); printOpenGLError();
glBindTexture(GL_TEXTURE_2D, _overlayTexture); printOpenGLError();
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _videoContext.overlayTexture.w, _videoContext.overlayTexture.h, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, _videoContext.overlayTexture.pixels); printOpenGLError();
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _videoContext.overlayTexture.w, _videoContext.overlayTexture.h, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, _videoContext.overlayTexture.getPixels()); printOpenGLError();
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); printOpenGLError();
}

View File

@ -77,8 +77,8 @@ OSystem_IPHONE::~OSystem_IPHONE() {
delete _mixer;
// Prevent accidental freeing of the screen texture here. This needs to be
// checked since we might use the screen texture as framebuffer in the case
// of hi-color games for example.
if (_framebuffer.pixels == _videoContext->screenTexture.pixels)
// of hi-color games for example. Otherwise this can lead to a double free.
if (_framebuffer.getPixels() != _videoContext->screenTexture.getPixels())
_framebuffer.free();
_mouseBuffer.free();
}
@ -166,7 +166,7 @@ void OSystem_IPHONE::suspendLoop() {
_timeSuspended += getMillis() - startTime;
}
uint32 OSystem_IPHONE::getMillis() {
uint32 OSystem_IPHONE::getMillis(bool skipRecord) {
//printf("getMillis()\n");
struct timeval currentTime;

View File

@ -165,7 +165,7 @@ public:
virtual void setCursorPalette(const byte *colors, uint start, uint num);
virtual bool pollEvent(Common::Event &event);
virtual uint32 getMillis();
virtual uint32 getMillis(bool skipRecord = false);
virtual void delayMillis(uint msecs);
virtual MutexRef createMutex(void);

View File

@ -76,8 +76,8 @@ void OSystem_IPHONE::initSize(uint width, uint height, const Graphics::PixelForm
// In case we use the screen texture as frame buffer we reset the pixels
// pointer here to avoid freeing the screen texture.
if (_framebuffer.pixels == _videoContext->screenTexture.pixels)
_framebuffer.pixels = 0;
if (_framebuffer.getPixels() == _videoContext->screenTexture.getPixels())
_framebuffer.setPixels(0);
// Create the screen texture right here. We need to do this here, since
// when a game requests hi-color mode, we actually set the framebuffer
@ -310,7 +310,7 @@ void OSystem_IPHONE::hideOverlay() {
void OSystem_IPHONE::clearOverlay() {
//printf("clearOverlay()\n");
bzero(_videoContext->overlayTexture.getBasePtr(0, 0), _videoContext->overlayTexture.h * _videoContext->overlayTexture.pitch);
bzero(_videoContext->overlayTexture.getPixels(), _videoContext->overlayTexture.h * _videoContext->overlayTexture.pitch);
dirtyFullOverlayScreen();
}
@ -319,7 +319,7 @@ void OSystem_IPHONE::grabOverlay(void *buf, int pitch) {
int h = _videoContext->overlayHeight;
byte *dst = (byte *)buf;
const byte *src = (const byte *)_videoContext->overlayTexture.getBasePtr(0, 0);
const byte *src = (const byte *)_videoContext->overlayTexture.getPixels();
do {
memcpy(dst, src, _videoContext->overlayWidth * sizeof(uint16));
src += _videoContext->overlayTexture.pitch;
@ -417,7 +417,7 @@ void OSystem_IPHONE::setMouseCursor(const void *buf, uint w, uint h, int hotspot
#endif
assert(pixelFormat.bytesPerPixel == 1 || pixelFormat.bytesPerPixel == 2);
if (_mouseBuffer.w != w || _mouseBuffer.h != h || _mouseBuffer.format != pixelFormat || !_mouseBuffer.pixels)
if (_mouseBuffer.w != w || _mouseBuffer.h != h || _mouseBuffer.format != pixelFormat || !_mouseBuffer.getPixels())
_mouseBuffer.create(w, h, pixelFormat);
_videoContext->mouseWidth = w;
@ -428,7 +428,7 @@ void OSystem_IPHONE::setMouseCursor(const void *buf, uint w, uint h, int hotspot
_mouseKeyColor = keycolor;
memcpy(_mouseBuffer.getBasePtr(0, 0), buf, h * _mouseBuffer.pitch);
memcpy(_mouseBuffer.getPixels(), buf, h * _mouseBuffer.pitch);
_mouseDirty = true;
_mouseNeedTextureUpdate = true;
@ -464,7 +464,7 @@ void OSystem_IPHONE::updateMouseTexture() {
else
palette = _gamePaletteRGBA5551;
uint16 *mouseBuf = (uint16 *)mouseTexture.getBasePtr(0, 0);
uint16 *mouseBuf = (uint16 *)mouseTexture.getPixels();
for (uint x = 0; x < _videoContext->mouseWidth; ++x) {
for (uint y = 0; y < _videoContext->mouseHeight; ++y) {
const byte color = *(const byte *)_mouseBuffer.getBasePtr(x, y);
@ -475,12 +475,12 @@ void OSystem_IPHONE::updateMouseTexture() {
}
}
} else {
if (crossBlit((byte *)mouseTexture.getBasePtr(0, 0), (const byte *)_mouseBuffer.getBasePtr(0, 0), mouseTexture.pitch,
if (crossBlit((byte *)mouseTexture.getPixels(), (const byte *)_mouseBuffer.getPixels(), mouseTexture.pitch,
_mouseBuffer.pitch, _mouseBuffer.w, _mouseBuffer.h, mouseTexture.format, _mouseBuffer.format)) {
if (!_mouseBuffer.format.aBits()) {
// Apply color keying since the original cursor had no alpha channel.
const uint16 *src = (const uint16 *)_mouseBuffer.getBasePtr(0, 0);
uint8 *dstRaw = (uint8 *)mouseTexture.getBasePtr(0, 0);
const uint16 *src = (const uint16 *)_mouseBuffer.getPixels();
uint8 *dstRaw = (uint8 *)mouseTexture.getPixels();
for (uint y = 0; y < _mouseBuffer.h; ++y, dstRaw += mouseTexture.pitch) {
uint16 *dst = (uint16 *)dstRaw;
@ -495,7 +495,7 @@ void OSystem_IPHONE::updateMouseTexture() {
} else {
// TODO: Log this!
// Make the cursor all transparent... we really need a better fallback ;-).
memset(mouseTexture.getBasePtr(0, 0), 0, mouseTexture.h * mouseTexture.pitch);
memset(mouseTexture.getPixels(), 0, mouseTexture.h * mouseTexture.pitch);
}
}

View File

@ -1,8 +1,14 @@
scummvm (1.6.0~git) unstable; urgency=low
scummvm (1.7.0~git) unstable; urgency=low
* Development snapshot
-- Tarek Soliman <tsoliman@scummvm.org> Tue, 10 Jul 2012 23:02:00 -0500
-- Tarek Soliman <tsoliman@scummvm.org> Sat, 01 Jun 2013 21:03:52 -0500
scummvm (1.6.0) unstable; urgency=low
* 1.6.0 release
-- Tarek Soliman <tsoliman@scummvm.org> Fri, 31 May 2013 23:02:00 -0500
scummvm (1.5.0) unstable; urgency=low

View File

@ -184,7 +184,7 @@ public:
virtual void setCursorPalette(const byte *colors, uint start, uint num);
virtual bool pollEvent(Common::Event &event);
virtual uint32 getMillis();
virtual uint32 getMillis(bool skipRecord = false);
virtual void delayMillis(uint msecs);
virtual MutexRef createMutex(void);

View File

@ -605,11 +605,7 @@ void OSystem_N64::updateScreen() {
}
Graphics::Surface *OSystem_N64::lockScreen() {
_framebuffer.pixels = _offscreen_pal;
_framebuffer.w = _gameWidth;
_framebuffer.h = _gameHeight;
_framebuffer.pitch = _screenWidth;
_framebuffer.format = Graphics::PixelFormat::createFormatCLUT8();
_framebuffer.init(_gameWidth, _gameHeight, _screenWidth, _offscreen_pal, Graphics::PixelFormat::createFormatCLUT8());
return &_framebuffer;
}
@ -810,7 +806,7 @@ void OSystem_N64::setMouseCursor(const void *buf, uint w, uint h, int hotspotX,
return;
}
uint32 OSystem_N64::getMillis() {
uint32 OSystem_N64::getMillis(bool skipRecord) {
return getMilliTick();
}

Some files were not shown because too many files have changed in this diff Show More