mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-14 13:50:13 +00:00
VOYEUR: Merge of upstream
This commit is contained in:
commit
ede418b67a
17
.gitignore
vendored
17
.gitignore
vendored
@ -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
412
AUTHORS
@ -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
|
||||
|
20
Makefile
20
Makefile
@ -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
|
||||
|
@ -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
15
NEWS
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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") {
|
||||
|
@ -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;
|
||||
|
@ -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
|
@ -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 ¤tSettings;
|
||||
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
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
@ -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_
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
65
backends/graphics/opengl/debug.cpp
Normal file
65
backends/graphics/opengl/debug.cpp
Normal 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
|
39
backends/graphics/opengl/debug.h
Normal file
39
backends/graphics/opengl/debug.h
Normal 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
|
48
backends/graphics/opengl/extensions.cpp
Normal file
48
backends/graphics/opengl/extensions.cpp
Normal 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
|
41
backends/graphics/opengl/extensions.h
Normal file
41
backends/graphics/opengl/extensions.h
Normal 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
|
@ -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
|
@ -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
@ -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
|
||||
|
@ -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
|
371
backends/graphics/opengl/texture.cpp
Normal file
371
backends/graphics/opengl/texture.cpp
Normal 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
|
175
backends/graphics/opengl/texture.h
Normal file
175
backends/graphics/opengl/texture.h
Normal 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
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -80,6 +80,9 @@ public:
|
||||
virtual void notifyMousePos(Common::Point mouse) = 0;
|
||||
|
||||
protected:
|
||||
void initEventSource();
|
||||
void deinitEventSource();
|
||||
|
||||
SdlEventSource *_eventSource;
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 */
|
||||
|
75
backends/mixer/nullmixer/nullsdl-mixer.cpp
Normal file
75
backends/mixer/nullmixer/nullsdl-mixer.cpp
Normal 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);
|
||||
}
|
||||
}
|
62
backends/mixer/nullmixer/nullsdl-mixer.h
Normal file
62
backends/mixer/nullmixer/nullsdl-mixer.h
Normal 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
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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 \
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
# Bada specific modules are built under eclipse
|
||||
|
||||
$(EXECUTABLE): $(OBJS)
|
||||
rm -f $@
|
||||
ar Tru $@ $(OBJS)
|
@ -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 ¤tPosition) {
|
||||
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 ¤tPosition,
|
||||
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 ¤tPosition,
|
||||
const TouchEventInfo &touchInfo) {
|
||||
}
|
||||
|
||||
void BadaAppForm::OnTouchFocusOut(const Control &source,
|
||||
const Point ¤tPosition,
|
||||
const TouchEventInfo &touchInfo) {
|
||||
}
|
||||
|
||||
void BadaAppForm::OnTouchLongPressed(const Control &source,
|
||||
const Point ¤tPosition,
|
||||
const TouchEventInfo &touchInfo) {
|
||||
if (_buttonState != kLeftButton) {
|
||||
pushKey(Common::KEYCODE_RETURN);
|
||||
}
|
||||
}
|
||||
|
||||
void BadaAppForm::OnTouchMoved(const Control &source,
|
||||
const Point ¤tPosition,
|
||||
const TouchEventInfo &touchInfo) {
|
||||
pushEvent(Common::EVENT_MOUSEMOVE, currentPosition);
|
||||
}
|
||||
|
||||
void BadaAppForm::OnTouchPressed(const Control &source,
|
||||
const Point ¤tPosition,
|
||||
const TouchEventInfo &touchInfo) {
|
||||
if (_buttonState != kMoveOnly) {
|
||||
pushEvent(_buttonState == kLeftButton ? Common::EVENT_LBUTTONDOWN : Common::EVENT_RBUTTONDOWN,
|
||||
currentPosition);
|
||||
}
|
||||
}
|
||||
|
||||
void BadaAppForm::OnTouchReleased(const Control &source,
|
||||
const Point ¤tPosition,
|
||||
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) {
|
||||
}
|
@ -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 ¤tPosition,
|
||||
const Osp::Ui::TouchEventInfo &touchInfo);
|
||||
void OnTouchFocusIn(const Osp::Ui::Control &source,
|
||||
const Osp::Graphics::Point ¤tPosition,
|
||||
const Osp::Ui::TouchEventInfo &touchInfo);
|
||||
void OnTouchFocusOut(const Osp::Ui::Control &source,
|
||||
const Osp::Graphics::Point ¤tPosition,
|
||||
const Osp::Ui::TouchEventInfo &touchInfo);
|
||||
void OnTouchLongPressed(const Osp::Ui::Control &source,
|
||||
const Osp::Graphics::Point ¤tPosition,
|
||||
const Osp::Ui::TouchEventInfo &touchInfo);
|
||||
void OnTouchMoved(const Osp::Ui::Control &source,
|
||||
const Osp::Graphics::Point ¤tPosition,
|
||||
const Osp::Ui::TouchEventInfo &touchInfo);
|
||||
void OnTouchPressed(const Osp::Ui::Control &source,
|
||||
const Osp::Graphics::Point ¤tPosition,
|
||||
const Osp::Ui::TouchEventInfo &touchInfo);
|
||||
void OnTouchReleased(const Osp::Ui::Control &source,
|
||||
const Osp::Graphics::Point ¤tPosition,
|
||||
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 ¤tPosition);
|
||||
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
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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"];
|
||||
|
@ -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;
|
||||
|
@ -2280,7 +2280,7 @@ void VBlankHandler(void) {
|
||||
//REG_IF = IRQ_VBLANK;
|
||||
}
|
||||
|
||||
int getMillis() {
|
||||
int getMillis(bool skipRecord) {
|
||||
return currentTimeMillis;
|
||||
// return frameCount * FRAME_TIME;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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];
|
||||
}
|
||||
}*/
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user