mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-15 06:08:35 +00:00
Merge branch 'master' into phantom
This commit is contained in:
commit
e5296ebf8d
16
AUTHORS
16
AUTHORS
@ -257,9 +257,12 @@ ScummVM Team
|
||||
|
||||
Wintermute:
|
||||
Einar Johan T. Somaaen
|
||||
Tobia Tesan
|
||||
|
||||
ZVision:
|
||||
Adrian Astley
|
||||
Filippos Karapetis
|
||||
Anton Yarcev
|
||||
|
||||
Backend Teams
|
||||
-------------
|
||||
@ -523,6 +526,12 @@ Other contributions
|
||||
Victor Gonzalez - Soltys Spanish translation
|
||||
Alejandro Gomez de la Munoza - Soltys Spanish translation
|
||||
|
||||
CGE2:
|
||||
Arnaud Boutonne - Sfinx English translation
|
||||
Thierry Crozat - Sfinx English translation
|
||||
Peter Bozso - Sfinx English translation editor
|
||||
Ryan Clark - Sfinx English translation editor
|
||||
|
||||
Drascula:
|
||||
Thierry Crozat - Improve French translation
|
||||
|
||||
@ -556,6 +565,7 @@ Other contributions
|
||||
Jeroen Janssen - Numerous readability and bugfix patches
|
||||
Keith Kaisershot - Several Pegasus Prime patches
|
||||
Andreas Karlsson - Initial port for SymbianOS
|
||||
Stefan Kristiansson - Initial work on SDL2 support
|
||||
Claudio Matsuoka - Daily Linux builds
|
||||
Thomas Mayer - PSP port contributions
|
||||
Sean Murray - ScummVM tools GUI application (GSoC 2007
|
||||
@ -651,7 +661,7 @@ Special thanks to
|
||||
repository, planet and doxygen sites as well as tons
|
||||
of HD space
|
||||
DOSBox Team - For their awesome OPL2 and OPL3 emulator
|
||||
Yusuke Kamiyamane - For contributing some GUI icons
|
||||
Yusuke Kamiyamane - For contributing some GUI icons
|
||||
Till Kresslein - For design of modern ScummVM GUI
|
||||
Jezar - For his freeverb filter implementation
|
||||
Jim Leiterman - Various info on his FM-TOWNS/Marty SCUMM ports
|
||||
@ -700,8 +710,8 @@ Special thanks to
|
||||
of Dreamweb and for their tremendous support.
|
||||
|
||||
Janusz Wisniewski and Miroslaw Liminowicz from Laboratorium Komputerowe
|
||||
Avalon for providing full source code for Soltys and letting us
|
||||
redistribute the game.
|
||||
Avalon for providing full source code for Soltys and Sfinx and letting us
|
||||
redistribute the games.
|
||||
|
||||
Jan Nedoma for providing the sources to the Wintermute-engine, and for his
|
||||
support while porting the engine to ScummVM.
|
||||
|
12
CONTRIBUTING.md
Normal file
12
CONTRIBUTING.md
Normal file
@ -0,0 +1,12 @@
|
||||
Thank you for considering contributing to ScummVM.
|
||||
|
||||
Please make sure to read our guidelines for contributions on our
|
||||
[wiki](http://wiki.scummvm.org/index.php/Developer_Central). In particular:
|
||||
|
||||
* [Coding style](http://wiki.scummvm.org/index.php/Code_Formatting_Conventions)
|
||||
* [Portability](http://wiki.scummvm.org/index.php/Coding_Conventions)
|
||||
* [Commit message style](http://wiki.scummvm.org/index.php/Commit_Guidelines)
|
||||
* License: GPLv2+
|
||||
|
||||
If you have any questions about code, style, procedure, or anything else, feel
|
||||
free to contact us on our mailing list at scummvm-devel@lists.sourceforge.net.
|
@ -1,5 +1,5 @@
|
||||
ScummVM
|
||||
Copyright (C) 2001-2014 by the following:
|
||||
Copyright (C) 2001-2015 by the following:
|
||||
|
||||
If you have contributed to this project then you deserve to be on this
|
||||
list. Contact us (see: AUTHORS) and we'll add you.
|
||||
|
1
Makefile
1
Makefile
@ -91,6 +91,7 @@ ifeq "$(findstring config.mk,$(MAKEFILE_LIST))" "config.mk"
|
||||
LDFLAGS="$(SAVED_LDFLAGS)" CXX="$(SAVED_CXX)" \
|
||||
CXXFLAGS="$(SAVED_CXXFLAGS)" CPPFLAGS="$(SAVED_CPPFLAGS)" \
|
||||
ASFLAGS="$(SAVED_ASFLAGS)" WINDRESFLAGS="$(SAVED_WINDRESFLAGS)" \
|
||||
SDL_CONFIG="$(SAVED_SDL_CONFIG)" \
|
||||
$(srcdir)/configure $(SAVED_CONFIGFLAGS)
|
||||
else
|
||||
$(error You need to run $(srcdir)/configure before you can run make. Check $(srcdir)/configure --help for a list of parameters)
|
||||
|
31
NEWS
31
NEWS
@ -2,14 +2,43 @@ For a more comprehensive changelog of the latest experimental code, see:
|
||||
https://github.com/scummvm/scummvm/commits/
|
||||
|
||||
1.8.0 (????-??-??)
|
||||
New Games:
|
||||
- Added support for Rex Nebular and the Cosmic Gender Bender.
|
||||
- Added support for Sfinx.
|
||||
- Added support for Zork Nemesis: The Forbidden Lands.
|
||||
- Added support for Zork: Grand Inquisitor.
|
||||
|
||||
Broken Sword 1:
|
||||
General:
|
||||
- Updated Munt MT-32 emulation code to version 1.5.0.
|
||||
|
||||
AGI:
|
||||
- It is now possible to disable mouse support (except for Amiga versions
|
||||
and fanmade games, that require a mouse).
|
||||
- Fix incorrect volume attenuation in PCjr sound code (bug #6858).
|
||||
|
||||
AGOS:
|
||||
- Fixed arpeggio effect used in music of Amiga version of Elvira 1.
|
||||
- Fixed loading and saving progress in the PC version of Waxworks.
|
||||
|
||||
Broken Sword 1:
|
||||
- Fix speech endianness detection on big endian systems for the mac
|
||||
version (bug #6720).
|
||||
- Fix crash when reloading a game from the Main Menu while in the bull's
|
||||
head scene (bug #6728). It may have been happening in other scenes as
|
||||
well.
|
||||
|
||||
SCI:
|
||||
- Handling of music priority has been greatly improved.
|
||||
- A lot of fixes for original game script bugs that also occurred when
|
||||
using the original interpreter.
|
||||
KQ6 (Dual Mode), LSL5, QfG1 (EGA), QfG1 (VGA), QfG2, QfG3, SQ1, SQ4 (CD)
|
||||
- Restoring from the ScummVM in-game menu should now work all the time.
|
||||
- Improve support for Japanese PC-9801 games.
|
||||
|
||||
SCUMM:
|
||||
- It is now possible to play Maniac Mansion from within Day of the
|
||||
Tentacle, with a few caveats. See README for details.
|
||||
|
||||
1.7.0 (2014-07-21)
|
||||
New Games:
|
||||
- Added support for Chivalry is Not Dead.
|
||||
|
304
README
304
README
@ -15,26 +15,27 @@ Table of Contents:
|
||||
* 2.1 Reporting Bugs
|
||||
3.0) Supported Games
|
||||
* 3.1 Copy Protection
|
||||
* 3.2 Commodore64 games notes
|
||||
* 3.3 Maniac Mansion NES notes
|
||||
* 3.4 Macintosh games notes
|
||||
* 3.5 Multi-CD games notes
|
||||
* 3.6 The Curse of Monkey Island notes
|
||||
* 3.7 Broken Sword games notes
|
||||
* 3.8 Beneath a Steel Sky notes
|
||||
* 3.9 Flight of the Amazon Queen notes
|
||||
* 3.10 Gobliiins notes
|
||||
* 3.11 Inherit the Earth: Quest for the Orb notes
|
||||
* 3.12 Simon the Sorcerer notes
|
||||
* 3.13 The Feeble Files notes
|
||||
* 3.14 The Legend of Kyrandia notes
|
||||
* 3.15 Sierra AGI games Predictive Input Dialog notes
|
||||
* 3.16 Mickey's Space Adventure notes
|
||||
* 3.17 Winnie the Pooh notes
|
||||
* 3.18 Troll's Tale notes
|
||||
* 3.19 Dragon History notes
|
||||
* 3.20 Simultaneous speech and subtitles in Sierra SCI games
|
||||
* 3.21 Known Problems
|
||||
* 3.2 Day of the Tentacle notes
|
||||
* 3.3 Commodore64 games notes
|
||||
* 3.4 Maniac Mansion NES notes
|
||||
* 3.5 Macintosh games notes
|
||||
* 3.6 Multi-CD games notes
|
||||
* 3.7 The Curse of Monkey Island notes
|
||||
* 3.8 Broken Sword games notes
|
||||
* 3.9 Beneath a Steel Sky notes
|
||||
* 3.10 Flight of the Amazon Queen notes
|
||||
* 3.11 Gobliiins notes
|
||||
* 3.12 Inherit the Earth: Quest for the Orb notes
|
||||
* 3.13 Simon the Sorcerer notes
|
||||
* 3.14 The Feeble Files notes
|
||||
* 3.15 The Legend of Kyrandia notes
|
||||
* 3.16 Sierra AGI games Predictive Input Dialog notes
|
||||
* 3.17 Mickey's Space Adventure notes
|
||||
* 3.18 Winnie the Pooh notes
|
||||
* 3.19 Troll's Tale notes
|
||||
* 3.20 Dragon History notes
|
||||
* 3.21 Simultaneous speech and subtitles in Sierra SCI games
|
||||
* 3.22 Known Problems
|
||||
4.0) Supported Platforms
|
||||
5.0) Running ScummVM
|
||||
* 5.1 Command Line Options
|
||||
@ -200,7 +201,7 @@ SCUMM Games by LucasArts:
|
||||
The Dig [dig]
|
||||
The Curse of Monkey Island [comi]
|
||||
|
||||
AGI Games by Sierra:
|
||||
AGI and preAGI Games by Sierra:
|
||||
The Black Cauldron [bc]
|
||||
Gold Rush! [goldrush]
|
||||
King's Quest I [kq1]
|
||||
@ -217,6 +218,9 @@ AGI Games by Sierra:
|
||||
Space Quest I: The Sarien Encounter [sq1]
|
||||
Space Quest II: Vohaul's Revenge [sq2]
|
||||
Fanmade Games [agi-fanmade]
|
||||
Mickey's Space Adventure [mickey]
|
||||
Troll's Tale [troll]
|
||||
Winnie the Pooh in the Hundred Acre Wood [winnie]
|
||||
|
||||
AGOS Games by Adventuresoft/Horrorsoft:
|
||||
Elvira - Mistress of the Dark [elvira1]
|
||||
@ -235,6 +239,13 @@ AGOS Games by Adventuresoft/Horrorsoft:
|
||||
- Swampy Adventures [swampy]
|
||||
The Feeble Files [feeble]
|
||||
|
||||
Composer Games by Animation Magic:
|
||||
Darby the Dragon [darby]
|
||||
Gregory and the Hot Air Balloon [gregory]
|
||||
Magic Tales: Liam Finds a Story [liam]
|
||||
The Princess and the Crab [princess]
|
||||
Sleeping Cub's Test of Courage [sleepingcub]
|
||||
|
||||
GOB Games by Coktel Vision:
|
||||
Bambou le sauveur de la jungle [bambou]
|
||||
Bargon Attack [bargon]
|
||||
@ -250,6 +261,22 @@ GOB Games by Coktel Vision:
|
||||
Urban Runner [urban]
|
||||
Ween: The Prophecy [ween]
|
||||
|
||||
Living Books Games:
|
||||
Aesop's Fables: The Tortoise and the Hare [tortoise]
|
||||
Arthur's Birthday [arthurbday]
|
||||
Arthur's Teacher Trouble [arthur]
|
||||
Dr. Seuss's ABC [seussabc]
|
||||
Green Eggs and Ham [greeneggs]
|
||||
Harry and the Haunted House [harryhh]
|
||||
Just Grandma and Me [grandma]
|
||||
Little Monster at School [lilmonster]
|
||||
Ruff's Bone [ruff]
|
||||
Sheila Rae, the Brave [sheila]
|
||||
Stellaluna [stellaluna]
|
||||
The Berenstain Bears Get in a Fight [bearfight]
|
||||
The Berenstain Bears in the Dark [beardark]
|
||||
The New Kid on the Block [newkid]
|
||||
|
||||
MADE Games by Activision:
|
||||
Leather Goddesses of Phobos 2 [lgop2]
|
||||
Return to Zork [rtz]
|
||||
@ -257,34 +284,106 @@ MADE Games by Activision:
|
||||
The Manhole [manhole]
|
||||
|
||||
Other Games:
|
||||
3 Skulls of the Toltecs [toltecs]
|
||||
Blue Force [blueforce]
|
||||
Beneath a Steel Sky [sky]
|
||||
Broken Sword: The Shadow of the Templars [sword1]
|
||||
Broken Sword II: The Smoking Mirror [sword2]
|
||||
Bud Tucker in Double Trouble [tucker]
|
||||
Cruise for a Corpse [cruise]
|
||||
Discworld [dw]
|
||||
Discworld 2: Missing Presumed ...!? [dw2]
|
||||
Dragon History [draci]
|
||||
Drascula: The Vampire Strikes Back [drascula]
|
||||
DreamWeb [dreamweb]
|
||||
Eye of the Beholder [eob]
|
||||
Eye of the Beholder II: The Legend of
|
||||
Darkmoon [eob2]
|
||||
Flight of the Amazon Queen [queen]
|
||||
Future Wars [fw]
|
||||
Hopkins FBI [hopkins]
|
||||
Hugo's House of Horrors [hugo1]
|
||||
Hugo 2: Whodunit? [hugo2]
|
||||
Hugo 3: Jungle of Doom [hugo3]
|
||||
I Have No Mouth, and I Must Scream [ihnm]
|
||||
Inherit the Earth: Quest for the Orb [ite]
|
||||
Nippon Safes Inc. [nippon]
|
||||
Lands of Lore: The Throne of Chaos [lol]
|
||||
Lure of the Temptress [lure]
|
||||
Mortville Manor [mortevielle]
|
||||
Nippon Safes Inc. [nippon]
|
||||
Ringworld: Revenge Of The Patriarch [ringworld]
|
||||
Return to Ringworld [ringworld2]
|
||||
Sfinx [sfinx]
|
||||
Soltys [soltys]
|
||||
TeenAgent [teenagent]
|
||||
The Journeyman Project: Pegasus Prime [pegasus]
|
||||
The Legend of Kyrandia [kyra1]
|
||||
The Legend of Kyrandia: The Hand of Fate [kyra2]
|
||||
The Legend of Kyrandia: Malcolm's Revenge [kyra3]
|
||||
The 7th Guest [t7g]
|
||||
The Neverhood [neverhood]
|
||||
Tony Tough and the Night of Roasted Moths [tony]
|
||||
Toonstruck [toon]
|
||||
Touche: The Adventures of the Fifth
|
||||
Musketeer [touche]
|
||||
Voyeur [voyeur]
|
||||
|
||||
SCI Games by Sierra Entertainment:
|
||||
Castle of Dr. Brain [castlebrain]
|
||||
Codename: ICEMAN [iceman]
|
||||
Conquests of Camelot [camelot]
|
||||
Conquests of the Longbow [longbow]
|
||||
EcoQuest: The Search for Cetus [ecoquest]
|
||||
EcoQuest 2: Lost Secret of the Rainforest [ecoquest2]
|
||||
Freddy Pharkas: Frontier Pharmacist [freddypharkas]
|
||||
Hoyle's Book of Games 1 [hoyle1]
|
||||
Hoyle's Book of Games 2 [hoyle2]
|
||||
Hoyle's Book of Games 3 [hoyle3]
|
||||
Hoyle Classic Card Games [hoyle4]
|
||||
Jones in the Fast Lane [jones]
|
||||
King's Quest I [kq1sci]
|
||||
King's Quest IV [kq4sci]
|
||||
King's Quest V [kq5]
|
||||
King's Quest VI [kq6]
|
||||
Laura Bow: The Colonel's Bequest [laurabow]
|
||||
Laura Bow 2: The Dagger of Amon Ra [laurabow2]
|
||||
Leisure Suit Larry 1 [lsl1sci]
|
||||
Leisure Suit Larry 2 [lsl2]
|
||||
Leisure Suit Larry 3 [lsl3]
|
||||
Leisure Suit Larry 5 [lsl5]
|
||||
Leisure Suit Larry 6 [lsl6]
|
||||
Mixed-up Fairy Tales [fairytales]
|
||||
Mixed-up Mother Goose [mothergoose]
|
||||
Pepper's Adventures in Time [pepper]
|
||||
Police Quest 1 [pq1sci]
|
||||
Police Quest 2 [pq2]
|
||||
Police Quest 3 [pq3]
|
||||
Quest for Glory 1/Hero's Quest [qfg1]
|
||||
Quest for Glory 1 [qfg1vga]
|
||||
Quest for Glory 2 [qfg2]
|
||||
Quest for Glory 3 [qfg3]
|
||||
Slater & Charlie Go Camping [slater]
|
||||
Space Quest I [sq1sci]
|
||||
Space Quest III [sq3]
|
||||
Space Quest IV [sq4]
|
||||
Space Quest V [sq5]
|
||||
The Island of Dr. Brain [islandbrain]
|
||||
|
||||
Wintermute Games:
|
||||
Chivalry is Not Dead [chivalry]
|
||||
|
||||
ZVISION Games by Activision:
|
||||
Zork Nemesis: The Forbidden Lands [znemesis]
|
||||
Zork: Grand Inquisitor [zgi]
|
||||
|
||||
SCUMM Games by Humongous Entertainment:
|
||||
Backyard Baseball [baseball]
|
||||
Backyard Baseball 2001 [baseball2001]
|
||||
Backyard Baseball 2003 [baseball2003]
|
||||
Backyard Football [football]
|
||||
Backyard Football 2002 [football2002]
|
||||
Bear Stormin' [brstorm]
|
||||
Big Thinkers First Grade [thinker1]
|
||||
Big Thinkers Kindergarten [thinkerk]
|
||||
Blue's 123 Time Activities [Blues123Time]
|
||||
@ -309,6 +408,7 @@ SCUMM Games by Humongous Entertainment:
|
||||
Let's Explore the Airport with Buzzy [airport]
|
||||
Let's Explore the Farm with Buzzy [farm]
|
||||
Let's Explore the Jungle with Buzzy [jungle]
|
||||
Pajama Sam: Games to Play on Any Day [pjgames]
|
||||
Pajama Sam 1: No Need to Hide When It's
|
||||
Dark Outside [pajama]
|
||||
Pajama Sam 2: Thunder and Lightning
|
||||
@ -333,38 +433,20 @@ SCUMM Games by Humongous Entertainment:
|
||||
SPY Fox in Cheese Chase [chase]
|
||||
SPY Fox in Hold the Mustard [mustard]
|
||||
|
||||
Living Books Games:
|
||||
Aesop's Fables: The Tortoise and the Hare [tortoise]
|
||||
Arthur's Birthday [arthurbday]
|
||||
Arthur's Teacher Trouble [arthur]
|
||||
Dr. Seuss's ABC [seussabc]
|
||||
Green Eggs and Ham [greeneggs]
|
||||
Harry and the Haunted House [harryhh]
|
||||
Just Grandma and Me [grandma]
|
||||
Little Monster at School [lilmonster]
|
||||
Ruff's Bone [ruff]
|
||||
Sheila Rae, the Brave [sheila]
|
||||
Stellaluna [stellaluna]
|
||||
The Berenstain Bears Get in a Fight [bearfight]
|
||||
The Berenstain Bears in the Dark [beardark]
|
||||
The New Kid on the Block [newkid]
|
||||
|
||||
The following games should load, but are not yet fully playable. Play
|
||||
these at your own risk, and please do not file bug reports about them.
|
||||
If you want the latest updates on game compatibility, visit our web site
|
||||
and view the compatibility chart.
|
||||
|
||||
Backyard Football 2002 [football2002]
|
||||
Backyard Soccer [soccer]
|
||||
Backyard Soccer MLS [soccermls]
|
||||
Backyard Soccer 2004 [soccer2004]
|
||||
Blue's Treasure Hunt [BluesTreasureHunt]
|
||||
Pajama Sam: Games to Play on Any Day [pjgames]
|
||||
|
||||
The following games are based on the SCUMM engine, but NOT supported
|
||||
by ScummVM (yet):
|
||||
|
||||
Other Humongous Entertainment games
|
||||
Moonbase Commander
|
||||
|
||||
Please be aware that the engines may contain bugs and unimplemented
|
||||
features that sometimes make it impossible to finish the game. Save
|
||||
@ -410,7 +492,29 @@ ScummVM will skip copy protection in the following games:
|
||||
* Zak McKracken and the Alien Mindbenders
|
||||
|
||||
|
||||
3.2) Commodore64 games notes:
|
||||
3.2) Day of the Tentacle notes:
|
||||
---- --------------------------
|
||||
|
||||
At one point in the game, you come across a computer that allows you
|
||||
to play the original Maniac Mansion as an easter egg. ScummVM supports
|
||||
this, with a few caveats:
|
||||
|
||||
ScummVM will scan your configuration file for a game that's in a
|
||||
'Maniac' sub-folder of your Day of the Tentacle folder. If you've
|
||||
copied the data files from the CD version, this should already be the
|
||||
case but you have to add the game to ScummVM as well.
|
||||
|
||||
To return to Day of the Tentacle, press F5 and select "Return to
|
||||
Launcher".
|
||||
|
||||
This means that you could in theory use any game as the easter egg.
|
||||
Indeed, there is a "secret" configuration setting, "easter_egg", to
|
||||
override the ID of the game to run. Be aware, though, that not all
|
||||
games support returning to the launcher, and setting it up to use Day
|
||||
of the Tentacle itself as the easter egg game is not recommended.
|
||||
|
||||
|
||||
3.3) Commodore64 games notes:
|
||||
---- ------------------------
|
||||
Both Maniac Mansion and Zak McKracken run but Maniac Mansion is not yet
|
||||
playable. Simply name the D64 disks "maniac1.d64" and "maniac2.d64"
|
||||
@ -424,10 +528,10 @@ to Commodore64. We recommend using the much simpler approach described
|
||||
in the previous paragraph.
|
||||
|
||||
|
||||
3.3) Maniac Mansion NES notes:
|
||||
3.4) Maniac Mansion NES notes:
|
||||
---- -------------------------
|
||||
Supported versions are English GB (E), French (F), German (G), Italian (I),
|
||||
Swedish (SW) and English US (U). ScummVM requires just the PRG section
|
||||
Swedish (SW) and English US (U). ScummVM requires just the PRG section
|
||||
to run and not the whole ROM.
|
||||
|
||||
In order to get the game working, you will have to strip out the first
|
||||
@ -453,7 +557,7 @@ section. To do so use the 'extract_mm_nes' utility from the tools
|
||||
package.
|
||||
|
||||
|
||||
3.4) Macintosh games notes:
|
||||
3.5) Macintosh games notes:
|
||||
---- ----------------------
|
||||
All LucasArts SCUMM based adventures, except COMI, also exist in versions
|
||||
for the Macintosh. ScummVM can use most (all?) of them, however, in some
|
||||
@ -481,7 +585,7 @@ disk see:
|
||||
http://wiki.scummvm.org/index.php/HOWTO-Mac_Games
|
||||
|
||||
|
||||
3.5) Multi-CD games notes:
|
||||
3.6) Multi-CD games notes:
|
||||
---- ---------------------
|
||||
In general, ScummVM does not deal very well with Multi-CD games. This is
|
||||
because ScummVM assumes everything about a game can be found in one
|
||||
@ -496,7 +600,7 @@ files. Usually, when a file appears on more than one CD you can pick
|
||||
either of them.
|
||||
|
||||
|
||||
3.6) The Curse of Monkey Island notes:
|
||||
3.7) The Curse of Monkey Island notes:
|
||||
---- ---------------------------------
|
||||
For this game, you will need the comi.la0, comi.la1 and comi.la2 files.
|
||||
The comi.la0 file can be found on either CD, but since they are
|
||||
@ -508,7 +612,7 @@ two CDs. Some of the files appear on both CDs, but again they're
|
||||
identical.
|
||||
|
||||
|
||||
3.7) Broken Sword games notes:
|
||||
3.8) Broken Sword games notes:
|
||||
---- -------------------------
|
||||
The instructions for the Broken Sword games are for the Sold-Out
|
||||
Software versions, with each game on two CDs, since these were the
|
||||
@ -517,7 +621,7 @@ them. Hopefully they are general enough to be useful to other releases
|
||||
as well.
|
||||
|
||||
|
||||
3.7.1) Broken Sword games cutscenes:
|
||||
3.8.1) Broken Sword games cutscenes:
|
||||
------ -----------------------------
|
||||
The cutscenes for the Broken Sword games have a bit of a history (see
|
||||
the next section, if you are interested), but in general all you need to
|
||||
@ -558,7 +662,7 @@ currently does not work when running PlayStation videos. (Broken Sword
|
||||
II already has subtitles; no extra work is needed for them.)
|
||||
|
||||
|
||||
3.7.2) Broken Sword games cutscenes, in retrospect:
|
||||
3.8.2) Broken Sword games cutscenes, in retrospect:
|
||||
------ --------------------------------------------
|
||||
The original releases of the Broken Sword games used RAD Game Tools's
|
||||
Smacker(tm) format. As RAD was unwilling to open the older legacy
|
||||
@ -583,7 +687,7 @@ decoding MPEG movies added a lot of complexity, and they didn't look as
|
||||
good as the Smacker and DXA versions anyway.
|
||||
|
||||
|
||||
3.7.3) Broken Sword:
|
||||
3.8.3) Broken Sword:
|
||||
------ -------------
|
||||
For this game, you will need all of the files from the clusters
|
||||
directories on both CDs. For the Windows and Macintosh versions, you
|
||||
@ -600,7 +704,7 @@ makes little difference. The PlayStation version requires tunes.dat and
|
||||
tunes.tab.
|
||||
|
||||
|
||||
3.7.4) Broken Sword II:
|
||||
3.8.4) Broken Sword II:
|
||||
------ ----------------
|
||||
For this game, you will need all of the files from the clusters
|
||||
directories on both CDs. (Actually, a few of them may not be strictly
|
||||
@ -615,7 +719,7 @@ In addition, you will need the cd.inf and, optionally, the startup.inf
|
||||
files from the sword2 directory on CD 1.
|
||||
|
||||
|
||||
3.8) Beneath a Steel Sky notes:
|
||||
3.9) Beneath a Steel Sky notes:
|
||||
---- --------------------------
|
||||
Starting with ScummVM 0.8.0 you need the additional 'SKY.CPT' file to
|
||||
run Beneath a Steel Sky.
|
||||
@ -626,7 +730,7 @@ files (SKY.DNR, SKY.DSK), in your extrapath, or in the directory where
|
||||
your ScummVM executable resides.
|
||||
|
||||
|
||||
3.9) Flight of the Amazon Queen notes:
|
||||
3.10) Flight of the Amazon Queen notes:
|
||||
---- ---------------------------------
|
||||
In order to use a non-freeware version of Flight of the Amazon Queen
|
||||
(from original CD), you will need to place the 'queen.tbl' file
|
||||
@ -641,7 +745,7 @@ specific version, and thus removing the run-time dependency on the
|
||||
sound effects with MP3, OGG or FLAC.
|
||||
|
||||
|
||||
3.10) Gobliiins notes:
|
||||
3.11) Gobliiins notes:
|
||||
----- ----------------
|
||||
The CD versions of the Gobliiins series contain one big audio track
|
||||
which you need to rip (see the section on using compressed audio files)
|
||||
@ -651,7 +755,7 @@ track and its volume is therefore changed with the music volume control
|
||||
as well.
|
||||
|
||||
|
||||
3.11) Inherit the Earth: Quest for the Orb notes:
|
||||
3.12) Inherit the Earth: Quest for the Orb notes:
|
||||
----- -------------------------------------------
|
||||
In order to run the Mac OS X Wyrmkeep re-release of the game you will
|
||||
need to copy over data from the CD to your hard disk. If you're on a PC
|
||||
@ -671,14 +775,14 @@ format, as they should include both resource and data forks. Copy all
|
||||
'ITE *' files.
|
||||
|
||||
|
||||
3.12) Simon the Sorcerer 1 and 2 notes:
|
||||
3.13) Simon the Sorcerer 1 and 2 notes:
|
||||
----- ---------------------------------
|
||||
If you have the dual version of Simon the Sorcerer 1 or 2 on CD, you
|
||||
will find the Windows version in the main directory of the CD and the
|
||||
DOS version in the DOS directory of the CD.
|
||||
|
||||
|
||||
3.13) The Feeble Files notes:
|
||||
3.14) The Feeble Files notes:
|
||||
----- -----------------------
|
||||
If you have the Windows version of The Feeble Files, there are several
|
||||
things to note.
|
||||
@ -696,7 +800,7 @@ Rename voices.wav on CD3 to voices3.wav
|
||||
Rename voices.wav on CD4 to voices4.wav
|
||||
|
||||
|
||||
3.14) The Legend of Kyrandia notes:
|
||||
3.15) The Legend of Kyrandia notes:
|
||||
----- -----------------------------
|
||||
To run The Legend of Kyrandia under ScummVM you need the 'kyra.dat'
|
||||
file. The file should always be included in official ScummVM packages.
|
||||
@ -707,7 +811,7 @@ thus you only need to grab it in case ScummVM complains about the file
|
||||
being missing.
|
||||
|
||||
|
||||
3.15) Sierra AGI games Predictive Input Dialog notes:
|
||||
3.16) Sierra AGI games Predictive Input Dialog notes:
|
||||
----- -----------------------------------------------
|
||||
The Predictive Input Dialog is a ScummVM aid for running AGI engine
|
||||
games (which notoriously require command line input) on devices with
|
||||
@ -761,7 +865,7 @@ naturally mapping the functionality to the numeric keypad. Also, the
|
||||
dialog's buttons can be navigated with the arrow and the enter keys.
|
||||
|
||||
|
||||
3.16) Mickey's Space Adventure notes:
|
||||
3.17) Mickey's Space Adventure notes:
|
||||
----- -------------------------------
|
||||
To run Mickey's Space Adventure under ScummVM, the original executable
|
||||
of the game (mickey.exe) is needed together with the game's data files.
|
||||
@ -776,7 +880,7 @@ game's screen to change location, similar to many adventure games, which
|
||||
is simpler and more straightforward than moving around using the menu.
|
||||
|
||||
|
||||
3.17) Winnie the Pooh notes:
|
||||
3.18) Winnie the Pooh notes:
|
||||
----- ----------------------
|
||||
It is possible to import saved games from the original interpreter of the
|
||||
game into ScummVM.
|
||||
@ -791,14 +895,14 @@ game's screen to change location, similar to many adventure games, which
|
||||
is simpler and more straightforward than moving around using the menu.
|
||||
|
||||
|
||||
3.18) Troll's Tale notes:
|
||||
3.19) Troll's Tale notes:
|
||||
----- -------------------
|
||||
The original game came in a PC booter disk, therefore it is necessary to
|
||||
dump the contents of that disk in an image file and name it "troll.img"
|
||||
to be able to play the game under ScummVM.
|
||||
|
||||
|
||||
3.19) Dragon History notes:
|
||||
3.20) Dragon History notes:
|
||||
----- ---------------------
|
||||
There are 4 language variants of the game: Czech, English, Polish and
|
||||
German. Each of them is distributed in a separate archive. The only
|
||||
@ -816,7 +920,7 @@ All game files and the walkthrough can be downloaded from
|
||||
http://www.ucw.cz/draci-historie/index-en.html
|
||||
|
||||
|
||||
3.20) Simultaneous speech and subtitles in Sierra SCI games:
|
||||
3.21) Simultaneous speech and subtitles in Sierra SCI games:
|
||||
----- ------------------------------------------------------
|
||||
Certain CD versions of Sierra SCI games had both speech and text
|
||||
resources. Some have an option to toggle between the two, but there are
|
||||
@ -864,7 +968,7 @@ Space Quest 4 CD:
|
||||
options dialog, or via ScummVM's audio options.
|
||||
|
||||
|
||||
3.21) Known Problems:
|
||||
3.22) Known Problems:
|
||||
----- ---------------
|
||||
This release has the following known problems. There is no need to
|
||||
report them, although patches to fix them are welcome. If you discover a
|
||||
@ -909,7 +1013,11 @@ site, please see the section on reporting bugs.
|
||||
Inherit the Earth: Quest for the Orb
|
||||
- Amiga versions aren't supported
|
||||
|
||||
Simon the Sorcerer 1:
|
||||
Lure of the Temptress
|
||||
- No Roland MT-32 support
|
||||
- Sound support is incomplete and doesn't sound like original
|
||||
|
||||
Simon the Sorcerer 1:
|
||||
- Subtitles aren't available in the English and German CD versions
|
||||
as they are missing the majority of subtitles.
|
||||
|
||||
@ -1244,6 +1352,7 @@ Engines which currently support returning to the Launcher are:
|
||||
TOUCHE
|
||||
TSAGE
|
||||
TUCKER
|
||||
ZVISION
|
||||
|
||||
|
||||
5.5) Hotkeys:
|
||||
@ -1408,6 +1517,26 @@ other games.
|
||||
t - Switch between 'Voice only',
|
||||
'Voice and Text' and 'Text only'
|
||||
|
||||
Zork: Grand Inquisitor
|
||||
Ctrl-s - Save
|
||||
Ctrl-r - Restore
|
||||
Ctrl-q - Quit
|
||||
Ctrl-p - Preferences
|
||||
F1 - Help
|
||||
F5 - Inventory
|
||||
F6 - Spellbook
|
||||
F7 - Score
|
||||
F8 - Put away current object/forget spell
|
||||
F9 - Extract coin (must have the coin bag)
|
||||
Space - Skips movies
|
||||
|
||||
Zork Nemesis: The Forbidden Lands
|
||||
Ctrl-s - Save
|
||||
Ctrl-r - Restore
|
||||
Ctrl-q - Quit
|
||||
Ctrl-p - Preferences
|
||||
Space - Skips movies
|
||||
|
||||
Note that using Ctrl-f or Ctrl-g is not recommended: games can crash
|
||||
when being run faster than their normal speed, as scripts will lose
|
||||
synchronisation.
|
||||
@ -1441,7 +1570,7 @@ The platforms that currently have a different default directory are:
|
||||
<windir>\Profiles\username\Application Data\ScummVM\Saved games\
|
||||
|
||||
Saved games are stored under a hidden area in Windows NT4/2000/XP/Vista/7,
|
||||
which can be accessed by running "%APPDATA%\ScummVM\Saved Games\" or by
|
||||
which can be accessed by running "%APPDATA%\ScummVM\Saved Games\" or by
|
||||
enabling hidden files in Windows Explorer.
|
||||
|
||||
Note for Windows NT4/2000/XP/Vista/7 users: The default saved games location
|
||||
@ -1527,6 +1656,7 @@ Where 'xxx' is exact the saved game slot (ie 001) under ScummVM
|
||||
TOUCHE
|
||||
TSAGE
|
||||
TUCKER
|
||||
ZVISION
|
||||
|
||||
--save-slot/-x:
|
||||
|
||||
@ -1559,6 +1689,7 @@ Where 'xxx' is exact the saved game slot (ie 001) under ScummVM
|
||||
TOUCHE
|
||||
TSAGE
|
||||
TUCKER
|
||||
ZVISION
|
||||
|
||||
|
||||
7.0) Music and Sound:
|
||||
@ -2137,6 +2268,10 @@ Sierra games using the AGI engine add the following non-standard keywords:
|
||||
|
||||
originalsaveload bool If true, the original save/load screens are
|
||||
used instead of the enhanced ScummVM ones
|
||||
altamigapalette bool Use an alternative palette, common for all
|
||||
Amiga games. This was the old behavior
|
||||
mousesupport bool Enables mouse support. Allows to use mouse
|
||||
for movement and in game menus
|
||||
|
||||
Sierra games using the SCI engine add the following non-standard keywords:
|
||||
|
||||
@ -2148,6 +2283,12 @@ Sierra games using the SCI engine add the following non-standard keywords:
|
||||
native_fb01 bool If true, the music driver for an IBM Music
|
||||
Feature card or a Yamaha FB-01 FM synth module
|
||||
is used for MIDI output
|
||||
use_cdaudio bool Use CD audio instead of in-game audio,
|
||||
when available
|
||||
windows_cursors bool Use the Windows cursors (smaller and monochrome)
|
||||
instead of the DOS ones (King's Quest 6)
|
||||
silver_cursors bool Use the alternate set of silver cursors,
|
||||
instead of the normal golden ones (Space Quest 4)
|
||||
|
||||
Broken Sword II adds the following non-standard keywords:
|
||||
|
||||
@ -2240,6 +2381,27 @@ The 7th Guest adds the following non-standard keyword:
|
||||
normal speed, to avoid music synchronization
|
||||
issues
|
||||
|
||||
Zork Nemesis: The Forbidden Lands adds the following non-standard keywords:
|
||||
|
||||
originalsaveload bool If true, the original save/load screens are
|
||||
used instead of the enhanced ScummVM ones
|
||||
doublefps bool If true, game FPS are increased from 30 to 60
|
||||
venusenabled bool If true, the in-game Venus help system is
|
||||
enabled
|
||||
noanimwhileturning bool If true, animations are disabled while turning
|
||||
in panoramic mode
|
||||
|
||||
Zork: Grand Inquisitor adds the following non-standard keywords:
|
||||
|
||||
originalsaveload bool If true, the original save/load screens are
|
||||
used instead of the enhanced ScummVM ones
|
||||
doublefps bool If true, game FPS are increased from 30 to 60
|
||||
noanimwhileturning bool If true, animations are disabled while turning
|
||||
in panoramic mode
|
||||
mpegmovies bool If true, the hires MPEG movies are used in the
|
||||
DVD version of the game, instead of the lowres
|
||||
AVI ones
|
||||
|
||||
|
||||
8.2) Custom game options that can be toggled via the GUI
|
||||
---- ---------------------------------------------------
|
||||
@ -2308,9 +2470,11 @@ debug messages (see http://www.sysinternals.com/ntw2k/freeware/debugview.shtml).
|
||||
* Type "./configure" in the ScummVM directory.
|
||||
* You can now type 'make' to create a command line binary.
|
||||
* To get a version you can run from Finder, type 'make bundle' which
|
||||
will create ScummVM.app (this only works out of the box if you
|
||||
installed SDL into /sw (as happens if you are using Fink). If you
|
||||
have installed SDL in another way, you will have to edit ports.mk).
|
||||
will create ScummVM.app (this tries to detect where the static libraries
|
||||
are installed, which should work in most cases, but if it doesn't you
|
||||
will need to specify this path with --with-staticlib-prefix= when calling
|
||||
configure - for example "./configure --with-staticlib-prefix=/Users/foo"
|
||||
if the libraries are in /Users/foo/lib).
|
||||
* For more information refer to:
|
||||
http://wiki.scummvm.org/index.php/Compiling_ScummVM/MacOS_X_Crosscompiling
|
||||
|
||||
|
@ -246,6 +246,23 @@ void MP3Stream::initStream() {
|
||||
_inStream->seek(0, SEEK_SET);
|
||||
_curTime = mad_timer_zero;
|
||||
_posInFrame = 0;
|
||||
|
||||
// Skip ID3 TAG if any
|
||||
// ID3v1 (beginning with with 'TAG') is located at the end of files. So we can ignore those.
|
||||
// ID3v2 can be located at the start of files and begins with a 10 bytes header, the first 3 bytes being 'ID3'.
|
||||
// The tag size is coded on the last 4 bytes of the 10 bytes header as a 32 bit synchsafe integer.
|
||||
// See http://id3.org/id3v2.4.0-structure for details.
|
||||
char data[10];
|
||||
_inStream->read(data, 10);
|
||||
if (data[0] == 'I' && data[1] == 'D' && data[2] == '3') {
|
||||
uint32 size = data[9] + 128 * (data[8] + 128 * (data[7] + 128 * data[6]));
|
||||
// This size does not include an optional 10 bytes footer. Check if it is present.
|
||||
if (data[5] & 0x10)
|
||||
size += 10;
|
||||
debug("Skipping ID3 TAG (%d bytes)", size + 10);
|
||||
_inStream->seek(size, SEEK_CUR);
|
||||
} else
|
||||
_inStream->seek(0, SEEK_SET);
|
||||
|
||||
// Update state
|
||||
_state = MP3_STATE_READY;
|
||||
|
@ -241,6 +241,15 @@ void QuickTimeAudioDecoder::QuickTimeAudioTrack::queueAudio(const Timestamp &len
|
||||
// If we have any samples that we need to skip (ie. we seeked into
|
||||
// the middle of a chunk), skip them here.
|
||||
if (_skipSamples != Timestamp()) {
|
||||
if (_skipSamples > chunkLength) {
|
||||
// If the amount we need to skip is greater than the size
|
||||
// of the chunk, just skip it altogether.
|
||||
_curMediaPos = _curMediaPos + chunkLength;
|
||||
_skipSamples = _skipSamples - chunkLength;
|
||||
delete stream;
|
||||
continue;
|
||||
}
|
||||
|
||||
skipSamples(_skipSamples, stream);
|
||||
_curMediaPos = _curMediaPos + _skipSamples;
|
||||
chunkLength = chunkLength - _skipSamples;
|
||||
|
@ -80,6 +80,12 @@ Config::DriverId Config::detect(OplType type) {
|
||||
}
|
||||
|
||||
DriverId drv = parse(ConfMan.get("opl_driver"));
|
||||
if (drv == kAuto) {
|
||||
// Since the "auto" can be explicitly set for a game, and this
|
||||
// driver shows up in the GUI as "<default>", check if there is
|
||||
// a global setting for it before resorting to auto-detection.
|
||||
drv = parse(ConfMan.get("opl_driver", Common::ConfigManager::kApplicationDomain));
|
||||
}
|
||||
|
||||
// When a valid driver is selected, check whether it supports
|
||||
// the requested OPL chip.
|
||||
|
@ -219,11 +219,10 @@ void ProtrackerStream::updateRow() {
|
||||
case 0x0:
|
||||
if (exy) {
|
||||
_track[track].arpeggio = true;
|
||||
if (note.period) {
|
||||
_track[track].arpeggioNotes[0] = note.note;
|
||||
_track[track].arpeggioNotes[1] = note.note + ex;
|
||||
_track[track].arpeggioNotes[2] = note.note + ey;
|
||||
}
|
||||
byte trackNote = _module.periodToNote(_track[track].period);
|
||||
_track[track].arpeggioNotes[0] = trackNote;
|
||||
_track[track].arpeggioNotes[1] = trackNote + ex;
|
||||
_track[track].arpeggioNotes[2] = trackNote + ey;
|
||||
}
|
||||
break;
|
||||
case 0x1:
|
||||
|
348
audio/softsynth/mt32/Analog.cpp
Normal file
348
audio/softsynth/mt32/Analog.cpp
Normal file
@ -0,0 +1,348 @@
|
||||
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
|
||||
* Copyright (C) 2011, 2012, 2013, 2014 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 <cstring>
|
||||
#include "Analog.h"
|
||||
|
||||
namespace MT32Emu {
|
||||
|
||||
#if MT32EMU_USE_FLOAT_SAMPLES
|
||||
|
||||
/* FIR approximation of the overall impulse response of the cascade composed of the sample & hold circuit and the low pass filter
|
||||
* of the MT-32 first generation.
|
||||
* The coefficients below are found by windowing the inverse DFT of the 1024 pin frequency response converted to the minimum phase.
|
||||
* The frequency response of the LPF is computed directly, the effect of the S&H is approximated by multiplying the LPF frequency
|
||||
* response by the corresponding sinc. Although, the LPF has DC gain of 3.2, we ignore this in the emulation and use normalised model.
|
||||
* The peak gain of the normalised cascade appears about 1.7 near 11.8 kHz. Relative error doesn't exceed 1% for the frequencies
|
||||
* below 12.5 kHz. In the higher frequency range, the relative error is below 8%. Peak error value is at 16 kHz.
|
||||
*/
|
||||
static const float COARSE_LPF_TAPS_MT32[] = {
|
||||
1.272473681f, -0.220267785f, -0.158039905f, 0.179603785f, -0.111484097f, 0.054137498f, -0.023518029f, 0.010997169f, -0.006935698f
|
||||
};
|
||||
|
||||
// Similar approximation for new MT-32 and CM-32L/LAPC-I LPF. As the voltage controlled amplifier was introduced, LPF has unity DC gain.
|
||||
// The peak gain value shifted towards higher frequencies and a bit higher about 1.83 near 13 kHz.
|
||||
static const float COARSE_LPF_TAPS_CM32L[] = {
|
||||
1.340615635f, -0.403331694f, 0.036005517f, 0.066156844f, -0.069672532f, 0.049563806f, -0.031113416f, 0.019169774f, -0.012421368f
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
static const unsigned int COARSE_LPF_FRACTION_BITS = 14;
|
||||
|
||||
// Integer versions of the FIRs above multiplied by (1 << 14) and rounded.
|
||||
static const SampleEx COARSE_LPF_TAPS_MT32[] = {
|
||||
20848, -3609, -2589, 2943, -1827, 887, -385, 180, -114
|
||||
};
|
||||
|
||||
static const SampleEx COARSE_LPF_TAPS_CM32L[] = {
|
||||
21965, -6608, 590, 1084, -1142, 812, -510, 314, -204
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/* Combined FIR that both approximates the impulse response of the analogue circuits of sample & hold and the low pass filter
|
||||
* in the audible frequency range (below 20 kHz) and attenuates unwanted mirror spectra above 28 kHz as well. It is a polyphase
|
||||
* filter intended for resampling the signal to 48 kHz yet for applying high frequency boost.
|
||||
* As with the filter above, the analogue LPF frequency response is obtained for 1536 pin grid for range up to 96 kHz and multiplied
|
||||
* by the corresponding sinc. The result is further squared, windowed and passed to generalised Parks-McClellan routine as a desired response.
|
||||
* Finally, the minimum phase factor is found that's essentially the coefficients below.
|
||||
* Relative error in the audible frequency range doesn't exceed 0.0006%, attenuation in the stopband is better than 100 dB.
|
||||
* This level of performance makes it nearly bit-accurate for standard 16-bit sample resolution.
|
||||
*/
|
||||
|
||||
// FIR version for MT-32 first generation.
|
||||
static const float ACCURATE_LPF_TAPS_MT32[] = {
|
||||
0.003429281f, 0.025929869f, 0.096587777f, 0.228884848f, 0.372413431f, 0.412386503f, 0.263980018f,
|
||||
-0.014504962f, -0.237394528f, -0.257043496f, -0.103436603f, 0.063996095f, 0.124562333f, 0.083703206f,
|
||||
0.013921662f, -0.033475018f, -0.046239712f, -0.029310921f, 0.00126585f, 0.021060961f, 0.017925605f,
|
||||
0.003559874f, -0.005105248f, -0.005647917f, -0.004157918f, -0.002065664f, 0.00158747f, 0.003762585f,
|
||||
0.001867137f, -0.001090028f, -0.001433979f, -0.00022367f, 4.34308E-05f, -0.000247827f, 0.000157087f,
|
||||
0.000605823f, 0.000197317f, -0.000370511f, -0.000261202f, 9.96069E-05f, 9.85073E-05f, -5.28754E-05f,
|
||||
-1.00912E-05f, 7.69943E-05f, 2.03162E-05f, -5.67967E-05f, -3.30637E-05f, 1.61958E-05f, 1.73041E-05f
|
||||
};
|
||||
|
||||
// FIR version for new MT-32 and CM-32L/LAPC-I.
|
||||
static const float ACCURATE_LPF_TAPS_CM32L[] = {
|
||||
0.003917452f, 0.030693861f, 0.116424199f, 0.275101674f, 0.43217361f, 0.431247894f, 0.183255659f,
|
||||
-0.174955671f, -0.354240244f, -0.212401714f, 0.072259178f, 0.204655344f, 0.108336211f, -0.039099027f,
|
||||
-0.075138174f, -0.026261906f, 0.00582663f, 0.003052193f, 0.00613657f, 0.017017951f, 0.008732535f,
|
||||
-0.011027427f, -0.012933664f, 0.001158097f, 0.006765958f, 0.00046778f, -0.002191106f, 0.001561017f,
|
||||
0.001842871f, -0.001996876f, -0.002315836f, 0.000980965f, 0.001817454f, -0.000243272f, -0.000972848f,
|
||||
0.000149941f, 0.000498886f, -0.000204436f, -0.000347415f, 0.000142386f, 0.000249137f, -4.32946E-05f,
|
||||
-0.000131231f, 3.88575E-07f, 4.48813E-05f, -1.31906E-06f, -1.03499E-05f, 7.71971E-06f, 2.86721E-06f
|
||||
};
|
||||
|
||||
// According to the CM-64 PCB schematic, there is a difference in the values of the LPF entrance resistors for the reverb and non-reverb channels.
|
||||
// This effectively results in non-unity LPF DC gain for the reverb channel of 0.68 while the LPF has unity DC gain for the LA32 output channels.
|
||||
// In emulation, the reverb output gain is multiplied by this factor to compensate for the LPF gain difference.
|
||||
static const float CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR = 0.68f;
|
||||
|
||||
static const unsigned int OUTPUT_GAIN_FRACTION_BITS = 8;
|
||||
static const float OUTPUT_GAIN_MULTIPLIER = float(1 << OUTPUT_GAIN_FRACTION_BITS);
|
||||
|
||||
static const unsigned int COARSE_LPF_DELAY_LINE_LENGTH = 8; // Must be a power of 2
|
||||
static const unsigned int ACCURATE_LPF_DELAY_LINE_LENGTH = 16; // Must be a power of 2
|
||||
static const unsigned int ACCURATE_LPF_NUMBER_OF_PHASES = 3; // Upsampling factor
|
||||
static const unsigned int ACCURATE_LPF_PHASE_INCREMENT_REGULAR = 2; // Downsampling factor
|
||||
static const unsigned int ACCURATE_LPF_PHASE_INCREMENT_OVERSAMPLED = 1; // No downsampling
|
||||
static const Bit32u ACCURATE_LPF_DELTAS_REGULAR[][ACCURATE_LPF_NUMBER_OF_PHASES] = { { 0, 0, 0 }, { 1, 1, 0 }, { 1, 2, 1 } };
|
||||
static const Bit32u ACCURATE_LPF_DELTAS_OVERSAMPLED[][ACCURATE_LPF_NUMBER_OF_PHASES] = { { 0, 0, 0 }, { 1, 0, 0 }, { 1, 0, 1 } };
|
||||
|
||||
class AbstractLowPassFilter {
|
||||
public:
|
||||
static AbstractLowPassFilter &createLowPassFilter(AnalogOutputMode mode, bool oldMT32AnalogLPF);
|
||||
static void muteRingBuffer(SampleEx *ringBuffer, unsigned int length);
|
||||
|
||||
virtual ~AbstractLowPassFilter() {}
|
||||
virtual SampleEx process(SampleEx sample) = 0;
|
||||
virtual bool hasNextSample() const;
|
||||
virtual unsigned int getOutputSampleRate() const;
|
||||
virtual unsigned int estimateInSampleCount(unsigned int outSamples) const;
|
||||
virtual void addPositionIncrement(unsigned int) {}
|
||||
};
|
||||
|
||||
class NullLowPassFilter : public AbstractLowPassFilter {
|
||||
public:
|
||||
SampleEx process(SampleEx sample);
|
||||
};
|
||||
|
||||
class CoarseLowPassFilter : public AbstractLowPassFilter {
|
||||
private:
|
||||
const SampleEx * const LPF_TAPS;
|
||||
SampleEx ringBuffer[COARSE_LPF_DELAY_LINE_LENGTH];
|
||||
unsigned int ringBufferPosition;
|
||||
|
||||
public:
|
||||
CoarseLowPassFilter(bool oldMT32AnalogLPF);
|
||||
SampleEx process(SampleEx sample);
|
||||
};
|
||||
|
||||
class AccurateLowPassFilter : public AbstractLowPassFilter {
|
||||
private:
|
||||
const float * const LPF_TAPS;
|
||||
const Bit32u (* const deltas)[ACCURATE_LPF_NUMBER_OF_PHASES];
|
||||
const unsigned int phaseIncrement;
|
||||
const unsigned int outputSampleRate;
|
||||
|
||||
SampleEx ringBuffer[ACCURATE_LPF_DELAY_LINE_LENGTH];
|
||||
unsigned int ringBufferPosition;
|
||||
unsigned int phase;
|
||||
|
||||
public:
|
||||
AccurateLowPassFilter(bool oldMT32AnalogLPF, bool oversample);
|
||||
SampleEx process(SampleEx sample);
|
||||
bool hasNextSample() const;
|
||||
unsigned int getOutputSampleRate() const;
|
||||
unsigned int estimateInSampleCount(unsigned int outSamples) const;
|
||||
void addPositionIncrement(unsigned int positionIncrement);
|
||||
};
|
||||
|
||||
Analog::Analog(const AnalogOutputMode mode, const ControlROMFeatureSet *controlROMFeatures) :
|
||||
leftChannelLPF(AbstractLowPassFilter::createLowPassFilter(mode, controlROMFeatures->isOldMT32AnalogLPF())),
|
||||
rightChannelLPF(AbstractLowPassFilter::createLowPassFilter(mode, controlROMFeatures->isOldMT32AnalogLPF())),
|
||||
synthGain(0),
|
||||
reverbGain(0)
|
||||
{}
|
||||
|
||||
Analog::~Analog() {
|
||||
delete &leftChannelLPF;
|
||||
delete &rightChannelLPF;
|
||||
}
|
||||
|
||||
void Analog::process(Sample **outStream, const Sample *nonReverbLeft, const Sample *nonReverbRight, const Sample *reverbDryLeft, const Sample *reverbDryRight, const Sample *reverbWetLeft, const Sample *reverbWetRight, Bit32u outLength) {
|
||||
if (outStream == NULL) {
|
||||
leftChannelLPF.addPositionIncrement(outLength);
|
||||
rightChannelLPF.addPositionIncrement(outLength);
|
||||
return;
|
||||
}
|
||||
|
||||
while (0 < (outLength--)) {
|
||||
SampleEx outSampleL;
|
||||
SampleEx outSampleR;
|
||||
|
||||
if (leftChannelLPF.hasNextSample()) {
|
||||
outSampleL = leftChannelLPF.process(0);
|
||||
outSampleR = rightChannelLPF.process(0);
|
||||
} else {
|
||||
SampleEx inSampleL = ((SampleEx)*(nonReverbLeft++) + (SampleEx)*(reverbDryLeft++)) * synthGain + (SampleEx)*(reverbWetLeft++) * reverbGain;
|
||||
SampleEx inSampleR = ((SampleEx)*(nonReverbRight++) + (SampleEx)*(reverbDryRight++)) * synthGain + (SampleEx)*(reverbWetRight++) * reverbGain;
|
||||
|
||||
#if !MT32EMU_USE_FLOAT_SAMPLES
|
||||
inSampleL >>= OUTPUT_GAIN_FRACTION_BITS;
|
||||
inSampleR >>= OUTPUT_GAIN_FRACTION_BITS;
|
||||
#endif
|
||||
|
||||
outSampleL = leftChannelLPF.process(inSampleL);
|
||||
outSampleR = rightChannelLPF.process(inSampleR);
|
||||
}
|
||||
|
||||
*((*outStream)++) = Synth::clipSampleEx(outSampleL);
|
||||
*((*outStream)++) = Synth::clipSampleEx(outSampleR);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int Analog::getOutputSampleRate() const {
|
||||
return leftChannelLPF.getOutputSampleRate();
|
||||
}
|
||||
|
||||
Bit32u Analog::getDACStreamsLength(Bit32u outputLength) const {
|
||||
return leftChannelLPF.estimateInSampleCount(outputLength);
|
||||
}
|
||||
|
||||
void Analog::setSynthOutputGain(float useSynthGain) {
|
||||
#if MT32EMU_USE_FLOAT_SAMPLES
|
||||
synthGain = useSynthGain;
|
||||
#else
|
||||
if (OUTPUT_GAIN_MULTIPLIER < useSynthGain) useSynthGain = OUTPUT_GAIN_MULTIPLIER;
|
||||
synthGain = SampleEx(useSynthGain * OUTPUT_GAIN_MULTIPLIER);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Analog::setReverbOutputGain(float useReverbGain, bool mt32ReverbCompatibilityMode) {
|
||||
if (!mt32ReverbCompatibilityMode) useReverbGain *= CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR;
|
||||
#if MT32EMU_USE_FLOAT_SAMPLES
|
||||
reverbGain = useReverbGain;
|
||||
#else
|
||||
if (OUTPUT_GAIN_MULTIPLIER < useReverbGain) useReverbGain = OUTPUT_GAIN_MULTIPLIER;
|
||||
reverbGain = SampleEx(useReverbGain * OUTPUT_GAIN_MULTIPLIER);
|
||||
#endif
|
||||
}
|
||||
|
||||
AbstractLowPassFilter &AbstractLowPassFilter::createLowPassFilter(AnalogOutputMode mode, bool oldMT32AnalogLPF) {
|
||||
switch (mode) {
|
||||
case AnalogOutputMode_COARSE:
|
||||
return *new CoarseLowPassFilter(oldMT32AnalogLPF);
|
||||
case AnalogOutputMode_ACCURATE:
|
||||
return *new AccurateLowPassFilter(oldMT32AnalogLPF, false);
|
||||
case AnalogOutputMode_OVERSAMPLED:
|
||||
return *new AccurateLowPassFilter(oldMT32AnalogLPF, true);
|
||||
default:
|
||||
return *new NullLowPassFilter;
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractLowPassFilter::muteRingBuffer(SampleEx *ringBuffer, unsigned int length) {
|
||||
|
||||
#if MT32EMU_USE_FLOAT_SAMPLES
|
||||
|
||||
SampleEx *p = ringBuffer;
|
||||
while (length--) {
|
||||
*(p++) = 0.0f;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
memset(ringBuffer, 0, length * sizeof(SampleEx));
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
bool AbstractLowPassFilter::hasNextSample() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int AbstractLowPassFilter::getOutputSampleRate() const {
|
||||
return SAMPLE_RATE;
|
||||
}
|
||||
|
||||
unsigned int AbstractLowPassFilter::estimateInSampleCount(unsigned int outSamples) const {
|
||||
return outSamples;
|
||||
}
|
||||
|
||||
SampleEx NullLowPassFilter::process(const SampleEx inSample) {
|
||||
return inSample;
|
||||
}
|
||||
|
||||
CoarseLowPassFilter::CoarseLowPassFilter(bool oldMT32AnalogLPF) :
|
||||
LPF_TAPS(oldMT32AnalogLPF ? COARSE_LPF_TAPS_MT32 : COARSE_LPF_TAPS_CM32L),
|
||||
ringBufferPosition(0)
|
||||
{
|
||||
muteRingBuffer(ringBuffer, COARSE_LPF_DELAY_LINE_LENGTH);
|
||||
}
|
||||
|
||||
SampleEx CoarseLowPassFilter::process(const SampleEx inSample) {
|
||||
static const unsigned int DELAY_LINE_MASK = COARSE_LPF_DELAY_LINE_LENGTH - 1;
|
||||
|
||||
SampleEx sample = LPF_TAPS[COARSE_LPF_DELAY_LINE_LENGTH] * ringBuffer[ringBufferPosition];
|
||||
ringBuffer[ringBufferPosition] = Synth::clipSampleEx(inSample);
|
||||
|
||||
for (unsigned int i = 0; i < COARSE_LPF_DELAY_LINE_LENGTH; i++) {
|
||||
sample += LPF_TAPS[i] * ringBuffer[(i + ringBufferPosition) & DELAY_LINE_MASK];
|
||||
}
|
||||
|
||||
ringBufferPosition = (ringBufferPosition - 1) & DELAY_LINE_MASK;
|
||||
|
||||
#if !MT32EMU_USE_FLOAT_SAMPLES
|
||||
sample >>= COARSE_LPF_FRACTION_BITS;
|
||||
#endif
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
||||
AccurateLowPassFilter::AccurateLowPassFilter(const bool oldMT32AnalogLPF, const bool oversample) :
|
||||
LPF_TAPS(oldMT32AnalogLPF ? ACCURATE_LPF_TAPS_MT32 : ACCURATE_LPF_TAPS_CM32L),
|
||||
deltas(oversample ? ACCURATE_LPF_DELTAS_OVERSAMPLED : ACCURATE_LPF_DELTAS_REGULAR),
|
||||
phaseIncrement(oversample ? ACCURATE_LPF_PHASE_INCREMENT_OVERSAMPLED : ACCURATE_LPF_PHASE_INCREMENT_REGULAR),
|
||||
outputSampleRate(SAMPLE_RATE * ACCURATE_LPF_NUMBER_OF_PHASES / phaseIncrement),
|
||||
ringBufferPosition(0),
|
||||
phase(0)
|
||||
{
|
||||
muteRingBuffer(ringBuffer, ACCURATE_LPF_DELAY_LINE_LENGTH);
|
||||
}
|
||||
|
||||
SampleEx AccurateLowPassFilter::process(const SampleEx inSample) {
|
||||
static const unsigned int DELAY_LINE_MASK = ACCURATE_LPF_DELAY_LINE_LENGTH - 1;
|
||||
|
||||
float sample = (phase == 0) ? LPF_TAPS[ACCURATE_LPF_DELAY_LINE_LENGTH * ACCURATE_LPF_NUMBER_OF_PHASES] * ringBuffer[ringBufferPosition] : 0.0f;
|
||||
if (!hasNextSample()) {
|
||||
ringBuffer[ringBufferPosition] = inSample;
|
||||
}
|
||||
|
||||
for (unsigned int tapIx = phase, delaySampleIx = 0; delaySampleIx < ACCURATE_LPF_DELAY_LINE_LENGTH; delaySampleIx++, tapIx += ACCURATE_LPF_NUMBER_OF_PHASES) {
|
||||
sample += LPF_TAPS[tapIx] * ringBuffer[(delaySampleIx + ringBufferPosition) & DELAY_LINE_MASK];
|
||||
}
|
||||
|
||||
phase += phaseIncrement;
|
||||
if (ACCURATE_LPF_NUMBER_OF_PHASES <= phase) {
|
||||
phase -= ACCURATE_LPF_NUMBER_OF_PHASES;
|
||||
ringBufferPosition = (ringBufferPosition - 1) & DELAY_LINE_MASK;
|
||||
}
|
||||
|
||||
return SampleEx(ACCURATE_LPF_NUMBER_OF_PHASES * sample);
|
||||
}
|
||||
|
||||
bool AccurateLowPassFilter::hasNextSample() const {
|
||||
return phaseIncrement <= phase;
|
||||
}
|
||||
|
||||
unsigned int AccurateLowPassFilter::getOutputSampleRate() const {
|
||||
return outputSampleRate;
|
||||
}
|
||||
|
||||
unsigned int AccurateLowPassFilter::estimateInSampleCount(unsigned int outSamples) const {
|
||||
Bit32u cycleCount = outSamples / ACCURATE_LPF_NUMBER_OF_PHASES;
|
||||
Bit32u remainder = outSamples - cycleCount * ACCURATE_LPF_NUMBER_OF_PHASES;
|
||||
return cycleCount * phaseIncrement + deltas[remainder][phase];
|
||||
}
|
||||
|
||||
void AccurateLowPassFilter::addPositionIncrement(const unsigned int positionIncrement) {
|
||||
phase = (phase + positionIncrement * phaseIncrement) % ACCURATE_LPF_NUMBER_OF_PHASES;
|
||||
}
|
||||
|
||||
}
|
57
audio/softsynth/mt32/Analog.h
Normal file
57
audio/softsynth/mt32/Analog.h
Normal file
@ -0,0 +1,57 @@
|
||||
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
|
||||
* Copyright (C) 2011, 2012, 2013, 2014 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_ANALOG_H
|
||||
#define MT32EMU_ANALOG_H
|
||||
|
||||
#include "mt32emu.h"
|
||||
|
||||
namespace MT32Emu {
|
||||
|
||||
class AbstractLowPassFilter;
|
||||
|
||||
/* Analog class is dedicated to perform fair emulation of analogue circuitry of hardware units that is responsible
|
||||
* for processing output signal after the DAC. It appears that the analogue circuit labeled "LPF" on the schematic
|
||||
* also applies audible changes to the signal spectra. There is a significant boost of higher frequencies observed
|
||||
* aside from quite poor attenuation of the mirror spectra above 16 kHz which is due to a relatively low filter order.
|
||||
*
|
||||
* As the final mixing of multiplexed output signal is performed after the DAC, this function is migrated here from Synth.
|
||||
* Saying precisely, mixing is performed within the LPF as the entrance resistors are actually components of a LPF
|
||||
* designed using the multiple feedback topology. Nevertheless, the schematic separates them.
|
||||
*/
|
||||
class Analog {
|
||||
public:
|
||||
Analog(AnalogOutputMode mode, const ControlROMFeatureSet *controlROMFeatures);
|
||||
~Analog();
|
||||
void process(Sample **outStream, const Sample *nonReverbLeft, const Sample *nonReverbRight, const Sample *reverbDryLeft, const Sample *reverbDryRight, const Sample *reverbWetLeft, const Sample *reverbWetRight, const Bit32u outLength);
|
||||
unsigned int getOutputSampleRate() const;
|
||||
Bit32u getDACStreamsLength(Bit32u outputLength) const;
|
||||
void setSynthOutputGain(float synthGain);
|
||||
void setReverbOutputGain(float reverbGain, bool mt32ReverbCompatibilityMode);
|
||||
|
||||
private:
|
||||
AbstractLowPassFilter &leftChannelLPF;
|
||||
AbstractLowPassFilter &rightChannelLPF;
|
||||
SampleEx synthGain;
|
||||
SampleEx reverbGain;
|
||||
|
||||
Analog(Analog &);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//#include <memory.h>
|
||||
//#include <cstring>
|
||||
#include "mt32emu.h"
|
||||
#include "BReverbModel.h"
|
||||
|
||||
@ -501,9 +501,9 @@ void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample *
|
||||
* Analysing of the algorithm suggests that the overflow is most probable when the combs output is added below.
|
||||
* So, despite this isn't actually accurate, we only add the check here for performance reasons.
|
||||
*/
|
||||
Sample outSample = Synth::clipBit16s(Synth::clipBit16s(Synth::clipBit16s(Synth::clipBit16s((Bit32s)outL1 + Bit32s(outL1 >> 1)) + (Bit32s)outL2) + Bit32s(outL2 >> 1)) + (Bit32s)outL3);
|
||||
Sample outSample = Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx((SampleEx)outL1 + SampleEx(outL1 >> 1)) + (SampleEx)outL2) + SampleEx(outL2 >> 1)) + (SampleEx)outL3);
|
||||
#else
|
||||
Sample outSample = Synth::clipBit16s((Bit32s)outL1 + Bit32s(outL1 >> 1) + (Bit32s)outL2 + Bit32s(outL2 >> 1) + (Bit32s)outL3);
|
||||
Sample outSample = Synth::clipSampleEx((SampleEx)outL1 + SampleEx(outL1 >> 1) + (SampleEx)outL2 + SampleEx(outL2 >> 1) + (SampleEx)outL3);
|
||||
#endif
|
||||
*(outLeft++) = weirdMul(outSample, wetLevel, 0xFF);
|
||||
}
|
||||
@ -515,9 +515,9 @@ void BReverbModel::process(const Sample *inLeft, const Sample *inRight, Sample *
|
||||
Sample outSample = 1.5f * (outR1 + outR2) + outR3;
|
||||
#elif MT32EMU_BOSS_REVERB_PRECISE_MODE
|
||||
// See the note above for the left channel output.
|
||||
Sample outSample = Synth::clipBit16s(Synth::clipBit16s(Synth::clipBit16s(Synth::clipBit16s((Bit32s)outR1 + Bit32s(outR1 >> 1)) + (Bit32s)outR2) + Bit32s(outR2 >> 1)) + (Bit32s)outR3);
|
||||
Sample outSample = Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx(Synth::clipSampleEx((SampleEx)outR1 + SampleEx(outR1 >> 1)) + (SampleEx)outR2) + SampleEx(outR2 >> 1)) + (SampleEx)outR3);
|
||||
#else
|
||||
Sample outSample = Synth::clipBit16s((Bit32s)outR1 + Bit32s(outR1 >> 1) + (Bit32s)outR2 + Bit32s(outR2 >> 1) + (Bit32s)outR3);
|
||||
Sample outSample = Synth::clipSampleEx((SampleEx)outR1 + SampleEx(outR1 >> 1) + (SampleEx)outR2 + SampleEx(outR2 >> 1) + (SampleEx)outR3);
|
||||
#endif
|
||||
*(outRight++) = weirdMul(outSample, wetLevel, 0xFF);
|
||||
}
|
||||
|
@ -95,7 +95,6 @@ class BReverbModel {
|
||||
const bool tapDelayMode;
|
||||
Bit32u dryAmp;
|
||||
Bit32u wetLevel;
|
||||
void mute();
|
||||
|
||||
static const BReverbSettings &getCM32L_LAPCSettings(const ReverbMode mode);
|
||||
static const BReverbSettings &getMT32Settings(const ReverbMode mode);
|
||||
@ -107,6 +106,7 @@ public:
|
||||
void open();
|
||||
// May be called multiple times without an open() in between.
|
||||
void close();
|
||||
void mute();
|
||||
void setParameters(Bit8u time, Bit8u level);
|
||||
void process(const Sample *inLeft, const Sample *inRight, Sample *outLeft, Sample *outRight, unsigned long numSamples);
|
||||
bool isActive() const;
|
||||
|
@ -18,7 +18,7 @@
|
||||
//#include <cmath>
|
||||
#include "mt32emu.h"
|
||||
#include "mmath.h"
|
||||
#include "LA32FloatWaveGenerator.h"
|
||||
#include "internals.h"
|
||||
|
||||
namespace MT32Emu {
|
||||
|
||||
|
@ -50,8 +50,8 @@ We haven't fully explored:
|
||||
//#include <cmath>
|
||||
|
||||
#include "mt32emu.h"
|
||||
#include "LA32Ramp.h"
|
||||
#include "mmath.h"
|
||||
#include "internals.h"
|
||||
|
||||
namespace MT32Emu {
|
||||
|
||||
|
@ -15,15 +15,15 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//#include <cmath>
|
||||
#include "mt32emu.h"
|
||||
#include "mmath.h"
|
||||
#include "LA32WaveGenerator.h"
|
||||
|
||||
#if MT32EMU_USE_FLOAT_SAMPLES
|
||||
#include "LA32FloatWaveGenerator.cpp"
|
||||
#else
|
||||
|
||||
//#include <cmath>
|
||||
#include "mt32emu.h"
|
||||
#include "mmath.h"
|
||||
#include "internals.h"
|
||||
|
||||
namespace MT32Emu {
|
||||
|
||||
static const Bit32u SINE_SEGMENT_RELATIVE_LENGTH = 1 << 18;
|
||||
|
124
audio/softsynth/mt32/MemoryRegion.h
Normal file
124
audio/softsynth/mt32/MemoryRegion.h
Normal file
@ -0,0 +1,124 @@
|
||||
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
|
||||
* Copyright (C) 2011, 2012, 2013, 2014 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_MEMORY_REGION_H
|
||||
#define MT32EMU_MEMORY_REGION_H
|
||||
|
||||
namespace MT32Emu {
|
||||
|
||||
enum MemoryRegionType {
|
||||
MR_PatchTemp, MR_RhythmTemp, MR_TimbreTemp, MR_Patches, MR_Timbres, MR_System, MR_Display, MR_Reset
|
||||
};
|
||||
|
||||
class MemoryRegion {
|
||||
private:
|
||||
Synth *synth;
|
||||
Bit8u *realMemory;
|
||||
Bit8u *maxTable;
|
||||
public:
|
||||
MemoryRegionType type;
|
||||
Bit32u startAddr, entrySize, entries;
|
||||
|
||||
MemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable, MemoryRegionType useType, Bit32u useStartAddr, Bit32u useEntrySize, Bit32u useEntries) {
|
||||
synth = useSynth;
|
||||
realMemory = useRealMemory;
|
||||
maxTable = useMaxTable;
|
||||
type = useType;
|
||||
startAddr = useStartAddr;
|
||||
entrySize = useEntrySize;
|
||||
entries = useEntries;
|
||||
}
|
||||
int lastTouched(Bit32u addr, Bit32u len) const {
|
||||
return (offset(addr) + len - 1) / entrySize;
|
||||
}
|
||||
int firstTouchedOffset(Bit32u addr) const {
|
||||
return offset(addr) % entrySize;
|
||||
}
|
||||
int firstTouched(Bit32u addr) const {
|
||||
return offset(addr) / entrySize;
|
||||
}
|
||||
Bit32u regionEnd() const {
|
||||
return startAddr + entrySize * entries;
|
||||
}
|
||||
bool contains(Bit32u addr) const {
|
||||
return addr >= startAddr && addr < regionEnd();
|
||||
}
|
||||
int offset(Bit32u addr) const {
|
||||
return addr - startAddr;
|
||||
}
|
||||
Bit32u getClampedLen(Bit32u addr, Bit32u len) const {
|
||||
if (addr + len > regionEnd())
|
||||
return regionEnd() - addr;
|
||||
return len;
|
||||
}
|
||||
Bit32u next(Bit32u addr, Bit32u len) const {
|
||||
if (addr + len > regionEnd()) {
|
||||
return regionEnd() - addr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Bit8u getMaxValue(int off) const {
|
||||
if (maxTable == NULL)
|
||||
return 0xFF;
|
||||
return maxTable[off % entrySize];
|
||||
}
|
||||
Bit8u *getRealMemory() const {
|
||||
return realMemory;
|
||||
}
|
||||
bool isReadable() const {
|
||||
return getRealMemory() != NULL;
|
||||
}
|
||||
void read(unsigned int entry, unsigned int off, Bit8u *dst, unsigned int len) const;
|
||||
void write(unsigned int entry, unsigned int off, const Bit8u *src, unsigned int len, bool init = false) const;
|
||||
};
|
||||
|
||||
class PatchTempMemoryRegion : public MemoryRegion {
|
||||
public:
|
||||
PatchTempMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_PatchTemp, MT32EMU_MEMADDR(0x030000), sizeof(MemParams::PatchTemp), 9) {}
|
||||
};
|
||||
class RhythmTempMemoryRegion : public MemoryRegion {
|
||||
public:
|
||||
RhythmTempMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_RhythmTemp, MT32EMU_MEMADDR(0x030110), sizeof(MemParams::RhythmTemp), 85) {}
|
||||
};
|
||||
class TimbreTempMemoryRegion : public MemoryRegion {
|
||||
public:
|
||||
TimbreTempMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_TimbreTemp, MT32EMU_MEMADDR(0x040000), sizeof(TimbreParam), 8) {}
|
||||
};
|
||||
class PatchesMemoryRegion : public MemoryRegion {
|
||||
public:
|
||||
PatchesMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_Patches, MT32EMU_MEMADDR(0x050000), sizeof(PatchParam), 128) {}
|
||||
};
|
||||
class TimbresMemoryRegion : public MemoryRegion {
|
||||
public:
|
||||
TimbresMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_Timbres, MT32EMU_MEMADDR(0x080000), sizeof(MemParams::PaddedTimbre), 64 + 64 + 64 + 64) {}
|
||||
};
|
||||
class SystemMemoryRegion : public MemoryRegion {
|
||||
public:
|
||||
SystemMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_System, MT32EMU_MEMADDR(0x100000), sizeof(MemParams::System), 1) {}
|
||||
};
|
||||
class DisplayMemoryRegion : public MemoryRegion {
|
||||
public:
|
||||
DisplayMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Display, MT32EMU_MEMADDR(0x200000), MAX_SYSEX_SIZE - 1, 1) {}
|
||||
};
|
||||
class ResetMemoryRegion : public MemoryRegion {
|
||||
public:
|
||||
ResetMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Reset, MT32EMU_MEMADDR(0x7F0000), 0x3FFF, 1) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
67
audio/softsynth/mt32/MidiEventQueue.h
Normal file
67
audio/softsynth/mt32/MidiEventQueue.h
Normal file
@ -0,0 +1,67 @@
|
||||
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
|
||||
* Copyright (C) 2011, 2012, 2013, 2014 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_MIDI_EVENT_QUEUE_H
|
||||
#define MT32EMU_MIDI_EVENT_QUEUE_H
|
||||
|
||||
namespace MT32Emu {
|
||||
|
||||
/**
|
||||
* 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 * const ringBuffer;
|
||||
const Bit32u ringBufferMask;
|
||||
volatile Bit32u startPosition;
|
||||
volatile Bit32u endPosition;
|
||||
|
||||
public:
|
||||
MidiEventQueue(Bit32u ringBufferSize = DEFAULT_MIDI_EVENT_QUEUE_SIZE); // Must be a power of 2
|
||||
~MidiEventQueue();
|
||||
void reset();
|
||||
bool pushShortMessage(Bit32u shortMessageData, Bit32u timestamp);
|
||||
bool pushSysex(const Bit8u *sysexData, Bit32u sysexLength, Bit32u timestamp);
|
||||
const MidiEvent *peekMidiEvent();
|
||||
void dropMidiEvent();
|
||||
bool isFull() const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -19,6 +19,7 @@
|
||||
//#include <cstring>
|
||||
|
||||
#include "mt32emu.h"
|
||||
#include "internals.h"
|
||||
#include "PartialManager.h"
|
||||
|
||||
namespace MT32Emu {
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include "mt32emu.h"
|
||||
#include "mmath.h"
|
||||
#include "internals.h"
|
||||
|
||||
namespace MT32Emu {
|
||||
|
||||
@ -312,8 +313,8 @@ bool Partial::produceOutput(Sample *leftBuf, Sample *rightBuf, unsigned long len
|
||||
// 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 = Synth::clipSampleEx((SampleEx)*leftBuf + (SampleEx)leftOut);
|
||||
*rightBuf = Synth::clipSampleEx((SampleEx)*rightBuf + (SampleEx)rightOut);
|
||||
leftBuf++;
|
||||
rightBuf++;
|
||||
#endif
|
||||
|
@ -18,6 +18,7 @@
|
||||
//#include <cstring>
|
||||
|
||||
#include "mt32emu.h"
|
||||
#include "internals.h"
|
||||
#include "PartialManager.h"
|
||||
|
||||
namespace MT32Emu {
|
||||
|
@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
#include "mt32emu.h"
|
||||
#include "internals.h"
|
||||
|
||||
namespace MT32Emu {
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
namespace MT32Emu {
|
||||
|
||||
class Part;
|
||||
class Partial;
|
||||
|
||||
enum PolyState {
|
||||
POLY_Playing,
|
||||
|
@ -21,8 +21,8 @@
|
||||
namespace MT32Emu {
|
||||
|
||||
static const ROMInfo *getKnownROMInfoFromList(unsigned int index) {
|
||||
static const ControlROMFeatureSet MT32_COMPATIBLE(true);
|
||||
static const ControlROMFeatureSet CM32L_COMPATIBLE(false);
|
||||
static const ControlROMFeatureSet MT32_COMPATIBLE(true, true);
|
||||
static const ControlROMFeatureSet CM32L_COMPATIBLE(false, false);
|
||||
|
||||
// Known ROMs
|
||||
static const ROMInfo CTRL_MT32_V1_04 = {65536, "5a5cb5a77d7d55ee69657c2f870416daed52dea7", ROMInfo::Control, "ctrl_mt32_1_04", "MT-32 Control v1.04", ROMInfo::Full, NULL, &MT32_COMPATIBLE};
|
||||
@ -106,7 +106,6 @@ void ROMImage::freeROMImage(const ROMImage *romImage) {
|
||||
delete romImage;
|
||||
}
|
||||
|
||||
|
||||
Common::File* ROMImage::getFile() const {
|
||||
return file;
|
||||
}
|
||||
@ -115,11 +114,17 @@ const ROMInfo* ROMImage::getROMInfo() const {
|
||||
return romInfo;
|
||||
}
|
||||
|
||||
ControlROMFeatureSet::ControlROMFeatureSet(bool useDefaultReverbMT32Compatible) : defaultReverbMT32Compatible(useDefaultReverbMT32Compatible) {
|
||||
}
|
||||
ControlROMFeatureSet::ControlROMFeatureSet(bool useDefaultReverbMT32Compatible, bool useOldMT32AnalogLPF) :
|
||||
defaultReverbMT32Compatible(useDefaultReverbMT32Compatible),
|
||||
oldMT32AnalogLPF(useOldMT32AnalogLPF)
|
||||
{}
|
||||
|
||||
bool ControlROMFeatureSet::isDefaultReverbMT32Compatible() const {
|
||||
return defaultReverbMT32Compatible;
|
||||
}
|
||||
|
||||
bool ControlROMFeatureSet::isOldMT32AnalogLPF() const {
|
||||
return oldMT32AnalogLPF;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -77,10 +77,12 @@ public:
|
||||
struct ControlROMFeatureSet {
|
||||
private:
|
||||
unsigned int defaultReverbMT32Compatible : 1;
|
||||
unsigned int oldMT32AnalogLPF : 1;
|
||||
|
||||
public:
|
||||
ControlROMFeatureSet(bool defaultReverbMT32Compatible);
|
||||
ControlROMFeatureSet(bool defaultReverbMT32Compatible, bool oldMT32AnalogLPF);
|
||||
bool isDefaultReverbMT32Compatible() const;
|
||||
bool isOldMT32AnalogLPF() const;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -31,19 +31,6 @@ namespace MT32Emu {
|
||||
#define MT32EMU_ALIGN_PACKED __attribute__((packed))
|
||||
#endif
|
||||
|
||||
typedef unsigned int Bit32u;
|
||||
typedef signed int Bit32s;
|
||||
typedef unsigned short int Bit16u;
|
||||
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
|
||||
@ -184,7 +171,37 @@ struct MemParams {
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
struct ControlROMPCMStruct;
|
||||
struct ControlROMMap {
|
||||
Bit16u idPos;
|
||||
Bit16u idLen;
|
||||
const char *idBytes;
|
||||
Bit16u pcmTable; // 4 * pcmCount bytes
|
||||
Bit16u pcmCount;
|
||||
Bit16u timbreAMap; // 128 bytes
|
||||
Bit16u timbreAOffset;
|
||||
bool timbreACompressed;
|
||||
Bit16u timbreBMap; // 128 bytes
|
||||
Bit16u timbreBOffset;
|
||||
bool timbreBCompressed;
|
||||
Bit16u timbreRMap; // 2 * timbreRCount bytes
|
||||
Bit16u timbreRCount;
|
||||
Bit16u rhythmSettings; // 4 * rhythmSettingsCount bytes
|
||||
Bit16u rhythmSettingsCount;
|
||||
Bit16u reserveSettings; // 9 bytes
|
||||
Bit16u panSettings; // 8 bytes
|
||||
Bit16u programSettings; // 8 bytes
|
||||
Bit16u rhythmMaxTable; // 4 bytes
|
||||
Bit16u patchMaxTable; // 16 bytes
|
||||
Bit16u systemMaxTable; // 23 bytes
|
||||
Bit16u timbreMaxTable; // 72 bytes
|
||||
};
|
||||
|
||||
struct ControlROMPCMStruct {
|
||||
Bit8u pos;
|
||||
Bit8u len;
|
||||
Bit8u pitchLSB;
|
||||
Bit8u pitchMSB;
|
||||
};
|
||||
|
||||
struct PCMWaveEntry {
|
||||
Bit32u addr;
|
||||
@ -216,8 +233,6 @@ struct PatchCache {
|
||||
const TimbreParam::PartialParam *partialParam;
|
||||
};
|
||||
|
||||
class Partial; // Forward reference for class defined in partial.h
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -22,12 +22,19 @@
|
||||
|
||||
#include "mt32emu.h"
|
||||
#include "mmath.h"
|
||||
#include "PartialManager.h"
|
||||
#include "internals.h"
|
||||
|
||||
#include "Analog.h"
|
||||
#include "BReverbModel.h"
|
||||
#include "common/debug.h"
|
||||
#include "MemoryRegion.h"
|
||||
#include "MidiEventQueue.h"
|
||||
#include "PartialManager.h"
|
||||
|
||||
namespace MT32Emu {
|
||||
|
||||
// MIDI interface data transfer rate in samples. Used to simulate the transfer delay.
|
||||
static const double MIDI_DATA_TRANSFER_RATE = (double)SAMPLE_RATE / 31250.0 * 8.0;
|
||||
|
||||
static const ControlROMMap ControlROMMaps[7] = {
|
||||
// ID IDc IDbytes PCMmap PCMc tmbrA tmbrAO, tmbrAC tmbrB tmbrBO, tmbrBC tmbrR trC rhythm rhyC rsrv panpot prog rhyMax patMax sysMax timMax
|
||||
{0x4014, 22, "\000 ver1.04 14 July 87 ", 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73A6, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A},
|
||||
@ -46,18 +53,15 @@ static inline void advanceStreamPosition(Sample *&stream, Bit32u posDelta) {
|
||||
}
|
||||
}
|
||||
|
||||
Bit8u Synth::calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum) {
|
||||
Bit8u Synth::calcSysexChecksum(const Bit8u *data, const Bit32u len, const Bit8u initChecksum) {
|
||||
unsigned int checksum = -initChecksum;
|
||||
for (unsigned int i = 0; i < len; i++) {
|
||||
checksum = checksum + data[i];
|
||||
checksum -= data[i];
|
||||
}
|
||||
checksum = checksum & 0x7f;
|
||||
if (checksum) {
|
||||
checksum = 0x80 - checksum;
|
||||
}
|
||||
return checksum;
|
||||
return Bit8u(checksum & 0x7f);
|
||||
}
|
||||
|
||||
Synth::Synth(ReportHandler *useReportHandler) {
|
||||
Synth::Synth(ReportHandler *useReportHandler) : mt32ram(*new MemParams()), mt32default(*new MemParams()) {
|
||||
isOpen = false;
|
||||
reverbOverridden = false;
|
||||
partialCount = DEFAULT_MAX_PARTIALS;
|
||||
@ -75,6 +79,7 @@ Synth::Synth(ReportHandler *useReportHandler) {
|
||||
reverbModels[i] = NULL;
|
||||
}
|
||||
reverbModel = NULL;
|
||||
analog = NULL;
|
||||
setDACInputMode(DACInputMode_NICE);
|
||||
setMIDIDelayMode(MIDIDelayMode_DELAY_SHORT_MESSAGES_ONLY);
|
||||
setOutputGain(1.0f);
|
||||
@ -92,6 +97,8 @@ Synth::~Synth() {
|
||||
if (isDefaultReportHandler) {
|
||||
delete reportHandler;
|
||||
}
|
||||
delete &mt32ram;
|
||||
delete &mt32default;
|
||||
}
|
||||
|
||||
void ReportHandler::showLCDMessage(const char *data) {
|
||||
@ -126,7 +133,7 @@ void Synth::printDebug(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
#if MT32EMU_DEBUG_SAMPLESTAMPS > 0
|
||||
reportHandler->printDebug("[%u] ", renderedSampleCount);
|
||||
reportHandler->printDebug("[%u] ", (char *)&renderedSampleCount);
|
||||
#endif
|
||||
reportHandler->printDebug(fmt, ap);
|
||||
va_end(ap);
|
||||
@ -211,10 +218,7 @@ MIDIDelayMode Synth::getMIDIDelayMode() const {
|
||||
void Synth::setOutputGain(float newOutputGain) {
|
||||
if (newOutputGain < 0.0f) newOutputGain = -newOutputGain;
|
||||
outputGain = newOutputGain;
|
||||
#if !MT32EMU_USE_FLOAT_SAMPLES
|
||||
if (256.0f < newOutputGain) newOutputGain = 256.0f;
|
||||
effectiveOutputGain = int(newOutputGain * 256.0f);
|
||||
#endif
|
||||
if (analog != NULL) analog->setSynthOutputGain(newOutputGain);
|
||||
}
|
||||
|
||||
float Synth::getOutputGain() const {
|
||||
@ -224,13 +228,7 @@ float Synth::getOutputGain() const {
|
||||
void Synth::setReverbOutputGain(float newReverbOutputGain) {
|
||||
if (newReverbOutputGain < 0.0f) newReverbOutputGain = -newReverbOutputGain;
|
||||
reverbOutputGain = newReverbOutputGain;
|
||||
if (!isMT32ReverbCompatibilityMode()) newReverbOutputGain *= CM32L_REVERB_TO_LA32_ANALOG_OUTPUT_GAIN_FACTOR;
|
||||
#if MT32EMU_USE_FLOAT_SAMPLES
|
||||
effectiveReverbOutputGain = newReverbOutputGain;
|
||||
#else
|
||||
if (256.0f < newReverbOutputGain) newReverbOutputGain = 256.0f;
|
||||
effectiveReverbOutputGain = int(newReverbOutputGain * 256.0f);
|
||||
#endif
|
||||
if (analog != NULL) analog->setReverbOutputGain(newReverbOutputGain, isMT32ReverbCompatibilityMode());
|
||||
}
|
||||
|
||||
float Synth::getReverbOutputGain() const {
|
||||
@ -393,7 +391,11 @@ bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, int count, int startTi
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount) {
|
||||
bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, AnalogOutputMode analogOutputMode) {
|
||||
return open(controlROMImage, pcmROMImage, DEFAULT_MAX_PARTIALS, analogOutputMode);
|
||||
}
|
||||
|
||||
bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount, AnalogOutputMode analogOutputMode) {
|
||||
if (isOpen) {
|
||||
return false;
|
||||
}
|
||||
@ -548,6 +550,10 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, u
|
||||
|
||||
midiQueue = new MidiEventQueue();
|
||||
|
||||
analog = new Analog(analogOutputMode, controlROMFeatures);
|
||||
setOutputGain(outputGain);
|
||||
setReverbOutputGain(reverbOutputGain);
|
||||
|
||||
isOpen = true;
|
||||
isEnabled = false;
|
||||
|
||||
@ -565,6 +571,9 @@ void Synth::close(bool forced) {
|
||||
delete midiQueue;
|
||||
midiQueue = NULL;
|
||||
|
||||
delete analog;
|
||||
analog = NULL;
|
||||
|
||||
delete partialManager;
|
||||
partialManager = NULL;
|
||||
|
||||
@ -603,16 +612,37 @@ void Synth::flushMIDIQueue() {
|
||||
}
|
||||
}
|
||||
|
||||
void Synth::setMIDIEventQueueSize(Bit32u useSize) {
|
||||
if (midiQueue != NULL) {
|
||||
flushMIDIQueue();
|
||||
delete midiQueue;
|
||||
midiQueue = new MidiEventQueue(useSize);
|
||||
Bit32u Synth::setMIDIEventQueueSize(Bit32u useSize) {
|
||||
static const Bit32u MAX_QUEUE_SIZE = (1 << 24); // This results in about 256 Mb - much greater than any reasonable value
|
||||
|
||||
if (midiQueue == NULL) return 0;
|
||||
flushMIDIQueue();
|
||||
|
||||
// Find a power of 2 that is >= useSize
|
||||
Bit32u binarySize = 1;
|
||||
if (useSize < MAX_QUEUE_SIZE) {
|
||||
// Using simple linear search as this isn't time critical
|
||||
while (binarySize < useSize) binarySize <<= 1;
|
||||
} else {
|
||||
binarySize = MAX_QUEUE_SIZE;
|
||||
}
|
||||
delete midiQueue;
|
||||
midiQueue = new MidiEventQueue(binarySize);
|
||||
return binarySize;
|
||||
}
|
||||
|
||||
Bit32u Synth::getShortMessageLength(Bit32u msg) {
|
||||
if ((msg & 0xF0) == 0xF0) return 1;
|
||||
if ((msg & 0xF0) == 0xF0) {
|
||||
switch (msg & 0xFF) {
|
||||
case 0xF1:
|
||||
case 0xF3:
|
||||
return 2;
|
||||
case 0xF2:
|
||||
return 3;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
// NOTE: This calculation isn't quite correct
|
||||
// as it doesn't consider the running status byte
|
||||
return ((msg & 0xE0) == 0xC0) ? 2 : 3;
|
||||
@ -638,6 +668,7 @@ bool Synth::playMsg(Bit32u msg, Bit32u timestamp) {
|
||||
if (midiDelayMode != MIDIDelayMode_IMMEDIATE) {
|
||||
timestamp = addMIDIInterfaceDelay(getShortMessageLength(msg), timestamp);
|
||||
}
|
||||
if (!isEnabled) isEnabled = true;
|
||||
return midiQueue->pushShortMessage(msg, timestamp);
|
||||
}
|
||||
|
||||
@ -650,16 +681,19 @@ bool Synth::playSysex(const Bit8u *sysex, Bit32u len, Bit32u timestamp) {
|
||||
if (midiDelayMode == MIDIDelayMode_DELAY_ALL) {
|
||||
timestamp = addMIDIInterfaceDelay(len, timestamp);
|
||||
}
|
||||
if (!isEnabled) isEnabled = true;
|
||||
return midiQueue->pushSysex(sysex, len, timestamp);
|
||||
}
|
||||
|
||||
void Synth::playMsgNow(Bit32u msg) {
|
||||
// FIXME: Implement active sensing
|
||||
// NOTE: Active sense IS implemented in real hardware. However, realtime processing is clearly out of the library scope.
|
||||
// It is assumed that realtime consumers of the library respond to these MIDI events as appropriate.
|
||||
|
||||
unsigned char code = (unsigned char)((msg & 0x0000F0) >> 4);
|
||||
unsigned char chan = (unsigned char)(msg & 0x00000F);
|
||||
unsigned char note = (unsigned char)((msg & 0x007F00) >> 8);
|
||||
unsigned char velocity = (unsigned char)((msg & 0x7F0000) >> 16);
|
||||
isEnabled = true;
|
||||
if (!isEnabled) isEnabled = true;
|
||||
|
||||
//printDebug("Playing chan %d, code 0x%01x note: 0x%02x", chan, code, note);
|
||||
|
||||
@ -831,7 +865,7 @@ void Synth::playSysexWithoutHeader(unsigned char device, unsigned char command,
|
||||
printDebug("playSysexWithoutHeader: Message is too short (%d bytes)!", len);
|
||||
return;
|
||||
}
|
||||
unsigned char checksum = calcSysexChecksum(sysex, len - 1, 0);
|
||||
Bit8u checksum = calcSysexChecksum(sysex, len - 1);
|
||||
if (checksum != sysex[len - 1]) {
|
||||
printDebug("playSysexWithoutHeader: Message checksum is incorrect (provided: %02x, expected: %02x)!", sysex[len - 1], checksum);
|
||||
return;
|
||||
@ -1410,9 +1444,8 @@ void MidiEvent::setSysex(const Bit8u *useSysexData, Bit32u useSysexLength, Bit32
|
||||
memcpy(dstSysexData, useSysexData, sysexLength);
|
||||
}
|
||||
|
||||
MidiEventQueue::MidiEventQueue(Bit32u useRingBufferSize) : ringBufferSize(useRingBufferSize) {
|
||||
ringBuffer = new MidiEvent[ringBufferSize];
|
||||
memset(ringBuffer, 0, ringBufferSize * sizeof(MidiEvent));
|
||||
MidiEventQueue::MidiEventQueue(Bit32u useRingBufferSize) : ringBuffer(new MidiEvent[useRingBufferSize]), ringBufferMask(useRingBufferSize - 1) {
|
||||
memset(ringBuffer, 0, useRingBufferSize * sizeof(MidiEvent));
|
||||
reset();
|
||||
}
|
||||
|
||||
@ -1426,7 +1459,7 @@ void MidiEventQueue::reset() {
|
||||
}
|
||||
|
||||
bool MidiEventQueue::pushShortMessage(Bit32u shortMessageData, Bit32u timestamp) {
|
||||
unsigned int newEndPosition = (endPosition + 1) % ringBufferSize;
|
||||
Bit32u newEndPosition = (endPosition + 1) & ringBufferMask;
|
||||
// Is ring buffer full?
|
||||
if (startPosition == newEndPosition) return false;
|
||||
ringBuffer[endPosition].setShortMessage(shortMessageData, timestamp);
|
||||
@ -1435,7 +1468,7 @@ bool MidiEventQueue::pushShortMessage(Bit32u shortMessageData, Bit32u timestamp)
|
||||
}
|
||||
|
||||
bool MidiEventQueue::pushSysex(const Bit8u *sysexData, Bit32u sysexLength, Bit32u timestamp) {
|
||||
unsigned int newEndPosition = (endPosition + 1) % ringBufferSize;
|
||||
Bit32u newEndPosition = (endPosition + 1) & ringBufferMask;
|
||||
// Is ring buffer full?
|
||||
if (startPosition == newEndPosition) return false;
|
||||
ringBuffer[endPosition].setSysex(sysexData, sysexLength, timestamp);
|
||||
@ -1450,31 +1483,36 @@ const MidiEvent *MidiEventQueue::peekMidiEvent() {
|
||||
void MidiEventQueue::dropMidiEvent() {
|
||||
// Is ring buffer empty?
|
||||
if (startPosition != endPosition) {
|
||||
startPosition = (startPosition + 1) % ringBufferSize;
|
||||
startPosition = (startPosition + 1) & ringBufferMask;
|
||||
}
|
||||
}
|
||||
|
||||
bool MidiEventQueue::isFull() const {
|
||||
return startPosition == ((endPosition + 1) & ringBufferMask);
|
||||
}
|
||||
|
||||
unsigned int Synth::getStereoOutputSampleRate() const {
|
||||
return (analog == NULL) ? SAMPLE_RATE : analog->getOutputSampleRate();
|
||||
}
|
||||
|
||||
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];
|
||||
if (!isEnabled) {
|
||||
renderedSampleCount += analog->getDACStreamsLength(len);
|
||||
analog->process(NULL, NULL, NULL, NULL, NULL, NULL, NULL, len);
|
||||
muteSampleBuffer(stream, len << 1);
|
||||
return;
|
||||
}
|
||||
|
||||
// As in AnalogOutputMode_ACCURATE mode output is upsampled, buffer size MAX_SAMPLES_PER_RUN is more than enough.
|
||||
Sample tmpNonReverbLeft[MAX_SAMPLES_PER_RUN], tmpNonReverbRight[MAX_SAMPLES_PER_RUN];
|
||||
Sample tmpReverbDryLeft[MAX_SAMPLES_PER_RUN], tmpReverbDryRight[MAX_SAMPLES_PER_RUN];
|
||||
Sample tmpReverbWetLeft[MAX_SAMPLES_PER_RUN], 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++) {
|
||||
#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;
|
||||
Bit32u thisPassLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len;
|
||||
renderStreams(tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, analog->getDACStreamsLength(thisPassLen));
|
||||
analog->process(&stream, tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, thisPassLen);
|
||||
len -= thisPassLen;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1518,7 +1556,10 @@ void Synth::renderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample
|
||||
// In GENERATION2 units, the output from LA32 goes to the Boss chip already bit-shifted.
|
||||
// In NICE mode, it's also better to increase volume before the reverb processing to preserve accuracy.
|
||||
void Synth::produceLA32Output(Sample *buffer, Bit32u len) {
|
||||
#if !MT32EMU_USE_FLOAT_SAMPLES
|
||||
#if MT32EMU_USE_FLOAT_SAMPLES
|
||||
(void)buffer;
|
||||
(void)len;
|
||||
#else
|
||||
switch (dacInputMode) {
|
||||
case DACInputMode_GENERATION2:
|
||||
while (len--) {
|
||||
@ -1528,7 +1569,7 @@ void Synth::produceLA32Output(Sample *buffer, Bit32u len) {
|
||||
break;
|
||||
case DACInputMode_NICE:
|
||||
while (len--) {
|
||||
*buffer = clipBit16s(Bit32s(*buffer) << 1);
|
||||
*buffer = clipSampleEx(SampleEx(*buffer) << 1);
|
||||
++buffer;
|
||||
}
|
||||
break;
|
||||
@ -1538,26 +1579,16 @@ void Synth::produceLA32Output(Sample *buffer, Bit32u len) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void Synth::convertSamplesToOutput(Sample *buffer, Bit32u len, bool reverb) {
|
||||
if (dacInputMode == DACInputMode_PURE) return;
|
||||
|
||||
void Synth::convertSamplesToOutput(Sample *buffer, Bit32u len) {
|
||||
#if MT32EMU_USE_FLOAT_SAMPLES
|
||||
float gain = reverb ? effectiveReverbOutputGain : outputGain;
|
||||
while (len--) {
|
||||
*(buffer++) *= gain;
|
||||
}
|
||||
(void)buffer;
|
||||
(void)len;
|
||||
#else
|
||||
int gain = reverb ? effectiveReverbOutputGain : effectiveOutputGain;
|
||||
if (dacInputMode == DACInputMode_GENERATION1) {
|
||||
while (len--) {
|
||||
Bit32s target = Bit16s((*buffer & 0x8000) | ((*buffer << 1) & 0x7FFE));
|
||||
*(buffer++) = clipBit16s((target * gain) >> 8);
|
||||
*buffer = Sample((*buffer & 0x8000) | ((*buffer << 1) & 0x7FFE));
|
||||
++buffer;
|
||||
}
|
||||
return;
|
||||
}
|
||||
while (len--) {
|
||||
*buffer = clipBit16s((Bit32s(*buffer) * gain) >> 8);
|
||||
++buffer;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -1566,18 +1597,18 @@ void Synth::doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sampl
|
||||
// Even if LA32 output isn't desired, we proceed anyway with temp buffers
|
||||
Sample tmpBufNonReverbLeft[MAX_SAMPLES_PER_RUN], tmpBufNonReverbRight[MAX_SAMPLES_PER_RUN];
|
||||
if (nonReverbLeft == NULL) nonReverbLeft = tmpBufNonReverbLeft;
|
||||
if (nonReverbLeft == NULL) nonReverbRight = tmpBufNonReverbRight;
|
||||
if (nonReverbRight == NULL) nonReverbRight = tmpBufNonReverbRight;
|
||||
|
||||
Sample tmpBufReverbDryLeft[MAX_SAMPLES_PER_RUN], tmpBufReverbDryRight[MAX_SAMPLES_PER_RUN];
|
||||
if (reverbDryLeft == NULL) reverbDryLeft = tmpBufReverbDryLeft;
|
||||
if (reverbDryRight == NULL) reverbDryRight = tmpBufReverbDryRight;
|
||||
|
||||
muteSampleBuffer(nonReverbLeft, len);
|
||||
muteSampleBuffer(nonReverbRight, len);
|
||||
muteSampleBuffer(reverbDryLeft, len);
|
||||
muteSampleBuffer(reverbDryRight, len);
|
||||
|
||||
if (isEnabled) {
|
||||
muteSampleBuffer(nonReverbLeft, len);
|
||||
muteSampleBuffer(nonReverbRight, len);
|
||||
muteSampleBuffer(reverbDryLeft, len);
|
||||
muteSampleBuffer(reverbDryRight, len);
|
||||
|
||||
for (unsigned int i = 0; i < getPartialCount(); i++) {
|
||||
if (partialManager->shouldReverb(i)) {
|
||||
partialManager->produceOutput(i, reverbDryLeft, reverbDryRight, len);
|
||||
@ -1591,8 +1622,8 @@ void Synth::doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sampl
|
||||
|
||||
if (isReverbEnabled()) {
|
||||
reverbModel->process(reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, len);
|
||||
if (reverbWetLeft != NULL) convertSamplesToOutput(reverbWetLeft, len, true);
|
||||
if (reverbWetRight != NULL) convertSamplesToOutput(reverbWetRight, len, true);
|
||||
if (reverbWetLeft != NULL) convertSamplesToOutput(reverbWetLeft, len);
|
||||
if (reverbWetRight != NULL) convertSamplesToOutput(reverbWetRight, len);
|
||||
} else {
|
||||
muteSampleBuffer(reverbWetLeft, len);
|
||||
muteSampleBuffer(reverbWetRight, len);
|
||||
@ -1601,15 +1632,20 @@ void Synth::doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sampl
|
||||
// Don't bother with conversion if the output is going to be unused
|
||||
if (nonReverbLeft != tmpBufNonReverbLeft) {
|
||||
produceLA32Output(nonReverbLeft, len);
|
||||
convertSamplesToOutput(nonReverbLeft, len, false);
|
||||
convertSamplesToOutput(nonReverbLeft, len);
|
||||
}
|
||||
if (nonReverbRight != tmpBufNonReverbRight) {
|
||||
produceLA32Output(nonReverbRight, len);
|
||||
convertSamplesToOutput(nonReverbRight, len, false);
|
||||
convertSamplesToOutput(nonReverbRight, len);
|
||||
}
|
||||
if (reverbDryLeft != tmpBufReverbDryLeft) convertSamplesToOutput(reverbDryLeft, len, false);
|
||||
if (reverbDryRight != tmpBufReverbDryRight) convertSamplesToOutput(reverbDryRight, len, false);
|
||||
if (reverbDryLeft != tmpBufReverbDryLeft) convertSamplesToOutput(reverbDryLeft, len);
|
||||
if (reverbDryRight != tmpBufReverbDryRight) convertSamplesToOutput(reverbDryRight, len);
|
||||
} else {
|
||||
// Avoid muting buffers that wasn't requested
|
||||
if (nonReverbLeft != tmpBufNonReverbLeft) muteSampleBuffer(nonReverbLeft, len);
|
||||
if (nonReverbRight != tmpBufNonReverbRight) muteSampleBuffer(nonReverbRight, len);
|
||||
if (reverbDryLeft != tmpBufReverbDryLeft) muteSampleBuffer(reverbDryLeft, len);
|
||||
if (reverbDryRight != tmpBufReverbDryRight) muteSampleBuffer(reverbDryRight, len);
|
||||
muteSampleBuffer(reverbWetLeft, len);
|
||||
muteSampleBuffer(reverbWetRight, len);
|
||||
}
|
||||
@ -1651,14 +1687,48 @@ bool Synth::isActive() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Partial *Synth::getPartial(unsigned int partialNum) const {
|
||||
return partialManager->getPartial(partialNum);
|
||||
}
|
||||
|
||||
unsigned int Synth::getPartialCount() const {
|
||||
return partialCount;
|
||||
}
|
||||
|
||||
void Synth::getPartStates(bool *partStates) const {
|
||||
for (int partNumber = 0; partNumber < 9; partNumber++) {
|
||||
const Part *part = parts[partNumber];
|
||||
partStates[partNumber] = part->getActiveNonReleasingPartialCount() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Synth::getPartialStates(PartialState *partialStates) const {
|
||||
static const PartialState partialPhaseToState[8] = {
|
||||
PartialState_ATTACK, PartialState_ATTACK, PartialState_ATTACK, PartialState_ATTACK,
|
||||
PartialState_SUSTAIN, PartialState_SUSTAIN, PartialState_RELEASE, PartialState_INACTIVE
|
||||
};
|
||||
|
||||
for (unsigned int partialNum = 0; partialNum < getPartialCount(); partialNum++) {
|
||||
const Partial *partial = partialManager->getPartial(partialNum);
|
||||
partialStates[partialNum] = partial->isActive() ? partialPhaseToState[partial->getTVA()->getPhase()] : PartialState_INACTIVE;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int Synth::getPlayingNotes(unsigned int partNumber, Bit8u *keys, Bit8u *velocities) const {
|
||||
unsigned int playingNotes = 0;
|
||||
if (isOpen && (partNumber < 9)) {
|
||||
const Part *part = parts[partNumber];
|
||||
const Poly *poly = part->getFirstActivePoly();
|
||||
while (poly != NULL) {
|
||||
keys[playingNotes] = (Bit8u)poly->getKey();
|
||||
velocities[playingNotes] = (Bit8u)poly->getVelocity();
|
||||
playingNotes++;
|
||||
poly = poly->getNext();
|
||||
}
|
||||
}
|
||||
return playingNotes;
|
||||
}
|
||||
|
||||
const char *Synth::getPatchName(unsigned int partNumber) const {
|
||||
return (!isOpen || partNumber > 8) ? NULL : parts[partNumber]->getCurrentInstr();
|
||||
}
|
||||
|
||||
const Part *Synth::getPart(unsigned int partNum) const {
|
||||
if (partNum > 8) {
|
||||
return NULL;
|
||||
|
@ -19,15 +19,31 @@
|
||||
#define MT32EMU_SYNTH_H
|
||||
|
||||
//#include <cstdarg>
|
||||
//#include <cstring>
|
||||
|
||||
namespace MT32Emu {
|
||||
|
||||
class TableInitialiser;
|
||||
class Analog;
|
||||
class BReverbModel;
|
||||
class MemoryRegion;
|
||||
class MidiEventQueue;
|
||||
class Part;
|
||||
class Poly;
|
||||
class Partial;
|
||||
class PartialManager;
|
||||
class Part;
|
||||
class ROMImage;
|
||||
class BReverbModel;
|
||||
|
||||
class PatchTempMemoryRegion;
|
||||
class RhythmTempMemoryRegion;
|
||||
class TimbreTempMemoryRegion;
|
||||
class PatchesMemoryRegion;
|
||||
class TimbresMemoryRegion;
|
||||
class SystemMemoryRegion;
|
||||
class DisplayMemoryRegion;
|
||||
class ResetMemoryRegion;
|
||||
|
||||
struct ControlROMMap;
|
||||
struct PCMWaveEntry;
|
||||
struct MemParams;
|
||||
|
||||
/**
|
||||
* Methods for emulating the connection between the LA32 and the DAC, which involves
|
||||
@ -43,8 +59,7 @@ enum DACInputMode {
|
||||
// Produces samples that exactly match the bits output from the emulated LA32.
|
||||
// * Nicer overdrive characteristics than the DAC hacks (it simply clips samples within range)
|
||||
// * 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.
|
||||
// * Half the volume of any of the other modes.
|
||||
// * Output gain is ignored for both LA32 and reverb output.
|
||||
// * Perfect for developers while debugging :)
|
||||
DACInputMode_PURE,
|
||||
@ -60,6 +75,7 @@ enum DACInputMode {
|
||||
DACInputMode_GENERATION2
|
||||
};
|
||||
|
||||
// Methods for emulating the effective delay of incoming MIDI messages introduced by a MIDI interface.
|
||||
enum MIDIDelayMode {
|
||||
// Process incoming MIDI events immediately.
|
||||
MIDIDelayMode_IMMEDIATE,
|
||||
@ -72,6 +88,35 @@ enum MIDIDelayMode {
|
||||
MIDIDelayMode_DELAY_ALL
|
||||
};
|
||||
|
||||
// Methods for emulating the effects of analogue circuits of real hardware units on the output signal.
|
||||
enum AnalogOutputMode {
|
||||
// Only digital path is emulated. The output samples correspond to the digital signal at the DAC entrance.
|
||||
AnalogOutputMode_DIGITAL_ONLY,
|
||||
// Coarse emulation of LPF circuit. High frequencies are boosted, sample rate remains unchanged.
|
||||
AnalogOutputMode_COARSE,
|
||||
// Finer emulation of LPF circuit. Output signal is upsampled to 48 kHz to allow emulation of audible mirror spectra above 16 kHz,
|
||||
// which is passed through the LPF circuit without significant attenuation.
|
||||
AnalogOutputMode_ACCURATE,
|
||||
// Same as AnalogOutputMode_ACCURATE mode but the output signal is 2x oversampled, i.e. the output sample rate is 96 kHz.
|
||||
// This makes subsequent resampling easier. Besides, due to nonlinear passband of the LPF emulated, it takes fewer number of MACs
|
||||
// compared to a regular LPF FIR implementations.
|
||||
AnalogOutputMode_OVERSAMPLED
|
||||
};
|
||||
|
||||
enum ReverbMode {
|
||||
REVERB_MODE_ROOM,
|
||||
REVERB_MODE_HALL,
|
||||
REVERB_MODE_PLATE,
|
||||
REVERB_MODE_TAP_DELAY
|
||||
};
|
||||
|
||||
enum PartialState {
|
||||
PartialState_INACTIVE,
|
||||
PartialState_ATTACK,
|
||||
PartialState_SUSTAIN,
|
||||
PartialState_RELEASE
|
||||
};
|
||||
|
||||
const Bit8u SYSEX_MANUFACTURER_ROLAND = 0x41;
|
||||
|
||||
const Bit8u SYSEX_MDL_MT32 = 0x16;
|
||||
@ -87,148 +132,10 @@ const Bit8u SYSEX_CMD_EOD = 0x45; // End of data
|
||||
const Bit8u SYSEX_CMD_ERR = 0x4E; // Communications error
|
||||
const Bit8u SYSEX_CMD_RJC = 0x4F; // Rejection
|
||||
|
||||
const int MAX_SYSEX_SIZE = 512;
|
||||
const int MAX_SYSEX_SIZE = 512; // FIXME: Does this correspond to a real MIDI buffer used in h/w devices?
|
||||
|
||||
const unsigned int CONTROL_ROM_SIZE = 64 * 1024;
|
||||
|
||||
struct ControlROMPCMStruct {
|
||||
Bit8u pos;
|
||||
Bit8u len;
|
||||
Bit8u pitchLSB;
|
||||
Bit8u pitchMSB;
|
||||
};
|
||||
|
||||
struct ControlROMMap {
|
||||
Bit16u idPos;
|
||||
Bit16u idLen;
|
||||
const char *idBytes;
|
||||
Bit16u pcmTable; // 4 * pcmCount bytes
|
||||
Bit16u pcmCount;
|
||||
Bit16u timbreAMap; // 128 bytes
|
||||
Bit16u timbreAOffset;
|
||||
bool timbreACompressed;
|
||||
Bit16u timbreBMap; // 128 bytes
|
||||
Bit16u timbreBOffset;
|
||||
bool timbreBCompressed;
|
||||
Bit16u timbreRMap; // 2 * timbreRCount bytes
|
||||
Bit16u timbreRCount;
|
||||
Bit16u rhythmSettings; // 4 * rhythmSettingsCount bytes
|
||||
Bit16u rhythmSettingsCount;
|
||||
Bit16u reserveSettings; // 9 bytes
|
||||
Bit16u panSettings; // 8 bytes
|
||||
Bit16u programSettings; // 8 bytes
|
||||
Bit16u rhythmMaxTable; // 4 bytes
|
||||
Bit16u patchMaxTable; // 16 bytes
|
||||
Bit16u systemMaxTable; // 23 bytes
|
||||
Bit16u timbreMaxTable; // 72 bytes
|
||||
};
|
||||
|
||||
enum MemoryRegionType {
|
||||
MR_PatchTemp, MR_RhythmTemp, MR_TimbreTemp, MR_Patches, MR_Timbres, MR_System, MR_Display, MR_Reset
|
||||
};
|
||||
|
||||
enum ReverbMode {
|
||||
REVERB_MODE_ROOM,
|
||||
REVERB_MODE_HALL,
|
||||
REVERB_MODE_PLATE,
|
||||
REVERB_MODE_TAP_DELAY
|
||||
};
|
||||
|
||||
class MemoryRegion {
|
||||
private:
|
||||
Synth *synth;
|
||||
Bit8u *realMemory;
|
||||
Bit8u *maxTable;
|
||||
public:
|
||||
MemoryRegionType type;
|
||||
Bit32u startAddr, entrySize, entries;
|
||||
|
||||
MemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable, MemoryRegionType useType, Bit32u useStartAddr, Bit32u useEntrySize, Bit32u useEntries) {
|
||||
synth = useSynth;
|
||||
realMemory = useRealMemory;
|
||||
maxTable = useMaxTable;
|
||||
type = useType;
|
||||
startAddr = useStartAddr;
|
||||
entrySize = useEntrySize;
|
||||
entries = useEntries;
|
||||
}
|
||||
int lastTouched(Bit32u addr, Bit32u len) const {
|
||||
return (offset(addr) + len - 1) / entrySize;
|
||||
}
|
||||
int firstTouchedOffset(Bit32u addr) const {
|
||||
return offset(addr) % entrySize;
|
||||
}
|
||||
int firstTouched(Bit32u addr) const {
|
||||
return offset(addr) / entrySize;
|
||||
}
|
||||
Bit32u regionEnd() const {
|
||||
return startAddr + entrySize * entries;
|
||||
}
|
||||
bool contains(Bit32u addr) const {
|
||||
return addr >= startAddr && addr < regionEnd();
|
||||
}
|
||||
int offset(Bit32u addr) const {
|
||||
return addr - startAddr;
|
||||
}
|
||||
Bit32u getClampedLen(Bit32u addr, Bit32u len) const {
|
||||
if (addr + len > regionEnd())
|
||||
return regionEnd() - addr;
|
||||
return len;
|
||||
}
|
||||
Bit32u next(Bit32u addr, Bit32u len) const {
|
||||
if (addr + len > regionEnd()) {
|
||||
return regionEnd() - addr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Bit8u getMaxValue(int off) const {
|
||||
if (maxTable == NULL)
|
||||
return 0xFF;
|
||||
return maxTable[off % entrySize];
|
||||
}
|
||||
Bit8u *getRealMemory() const {
|
||||
return realMemory;
|
||||
}
|
||||
bool isReadable() const {
|
||||
return getRealMemory() != NULL;
|
||||
}
|
||||
void read(unsigned int entry, unsigned int off, Bit8u *dst, unsigned int len) const;
|
||||
void write(unsigned int entry, unsigned int off, const Bit8u *src, unsigned int len, bool init = false) const;
|
||||
};
|
||||
|
||||
class PatchTempMemoryRegion : public MemoryRegion {
|
||||
public:
|
||||
PatchTempMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_PatchTemp, MT32EMU_MEMADDR(0x030000), sizeof(MemParams::PatchTemp), 9) {}
|
||||
};
|
||||
class RhythmTempMemoryRegion : public MemoryRegion {
|
||||
public:
|
||||
RhythmTempMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_RhythmTemp, MT32EMU_MEMADDR(0x030110), sizeof(MemParams::RhythmTemp), 85) {}
|
||||
};
|
||||
class TimbreTempMemoryRegion : public MemoryRegion {
|
||||
public:
|
||||
TimbreTempMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_TimbreTemp, MT32EMU_MEMADDR(0x040000), sizeof(TimbreParam), 8) {}
|
||||
};
|
||||
class PatchesMemoryRegion : public MemoryRegion {
|
||||
public:
|
||||
PatchesMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_Patches, MT32EMU_MEMADDR(0x050000), sizeof(PatchParam), 128) {}
|
||||
};
|
||||
class TimbresMemoryRegion : public MemoryRegion {
|
||||
public:
|
||||
TimbresMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_Timbres, MT32EMU_MEMADDR(0x080000), sizeof(MemParams::PaddedTimbre), 64 + 64 + 64 + 64) {}
|
||||
};
|
||||
class SystemMemoryRegion : public MemoryRegion {
|
||||
public:
|
||||
SystemMemoryRegion(Synth *useSynth, Bit8u *useRealMemory, Bit8u *useMaxTable) : MemoryRegion(useSynth, useRealMemory, useMaxTable, MR_System, MT32EMU_MEMADDR(0x100000), sizeof(MemParams::System), 1) {}
|
||||
};
|
||||
class DisplayMemoryRegion : public MemoryRegion {
|
||||
public:
|
||||
DisplayMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Display, MT32EMU_MEMADDR(0x200000), MAX_SYSEX_SIZE - 1, 1) {}
|
||||
};
|
||||
class ResetMemoryRegion : public MemoryRegion {
|
||||
public:
|
||||
ResetMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Reset, MT32EMU_MEMADDR(0x7F0000), 0x3FFF, 1) {}
|
||||
};
|
||||
|
||||
class ReportHandler {
|
||||
friend class Synth;
|
||||
|
||||
@ -254,47 +161,6 @@ protected:
|
||||
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 {
|
||||
friend class Part;
|
||||
friend class RhythmPart;
|
||||
@ -335,7 +201,7 @@ private:
|
||||
volatile Bit32u lastReceivedMIDIEventTimestamp;
|
||||
volatile Bit32u renderedSampleCount;
|
||||
|
||||
MemParams mt32ram, mt32default;
|
||||
MemParams &mt32ram, &mt32default;
|
||||
|
||||
BReverbModel *reverbModels[4];
|
||||
BReverbModel *reverbModel;
|
||||
@ -346,12 +212,6 @@ private:
|
||||
|
||||
float outputGain;
|
||||
float reverbOutputGain;
|
||||
#if MT32EMU_USE_FLOAT_SAMPLES
|
||||
float effectiveReverbOutputGain;
|
||||
#else
|
||||
int effectiveOutputGain;
|
||||
int effectiveReverbOutputGain;
|
||||
#endif
|
||||
|
||||
bool reversedStereoEnabled;
|
||||
|
||||
@ -368,11 +228,12 @@ private:
|
||||
// We emulate this by delaying new MIDI events processing until abortion finishes.
|
||||
Poly *abortingPoly;
|
||||
|
||||
Bit32u getShortMessageLength(Bit32u msg);
|
||||
Analog *analog;
|
||||
|
||||
Bit32u addMIDIInterfaceDelay(Bit32u len, Bit32u timestamp);
|
||||
|
||||
void produceLA32Output(Sample *buffer, Bit32u len);
|
||||
void convertSamplesToOutput(Sample *buffer, Bit32u len, bool reverb);
|
||||
void convertSamplesToOutput(Sample *buffer, Bit32u len);
|
||||
bool isAbortingPoly() const;
|
||||
void doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len);
|
||||
|
||||
@ -404,13 +265,20 @@ private:
|
||||
void newTimbreSet(int partNum, Bit8u timbreGroup, const char patchName[]);
|
||||
void printDebug(const char *fmt, ...);
|
||||
|
||||
// partNum should be 0..7 for Part 1..8, or 8 for Rhythm
|
||||
const Part *getPart(unsigned int partNum) const;
|
||||
|
||||
public:
|
||||
static inline Bit16s clipBit16s(Bit32s sample) {
|
||||
static inline Sample clipSampleEx(SampleEx sampleEx) {
|
||||
#if MT32EMU_USE_FLOAT_SAMPLES
|
||||
return sampleEx;
|
||||
#else
|
||||
// Clamp values above 32767 to 32767, and values below -32768 to -32768
|
||||
// FIXME: Do we really need this stuff? I think these branches are very well predicted. Instead, this introduces a chain.
|
||||
// The version below is actually a bit faster on my system...
|
||||
//return ((sample + 0x8000) & ~0xFFFF) ? (sample >> 31) ^ 0x7FFF : (Bit16s)sample;
|
||||
return ((-0x8000 <= sample) && (sample <= 0x7FFF)) ? (Bit16s)sample : (sample >> 31) ^ 0x7FFF;
|
||||
//return ((sampleEx + 0x8000) & ~0xFFFF) ? (sampleEx >> 31) ^ 0x7FFF : (Sample)sampleEx;
|
||||
return ((-0x8000 <= sampleEx) && (sampleEx <= 0x7FFF)) ? (Sample)sampleEx : (sampleEx >> 31) ^ 0x7FFF;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void muteSampleBuffer(Sample *buffer, Bit32u len) {
|
||||
@ -426,7 +294,8 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
static Bit8u calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum);
|
||||
static Bit32u getShortMessageLength(Bit32u msg);
|
||||
static Bit8u calcSysexChecksum(const Bit8u *data, const Bit32u len, const Bit8u initChecksum = 0);
|
||||
|
||||
// Optionally sets callbacks for reporting various errors, information and debug messages
|
||||
Synth(ReportHandler *useReportHandler = NULL);
|
||||
@ -435,8 +304,12 @@ 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.
|
||||
// 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);
|
||||
// usePartialCount sets the maximum number of partials playing simultaneously for this session (optional).
|
||||
// analogOutputMode sets the mode for emulation of analogue circuitry of the hardware units (optional).
|
||||
bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount = DEFAULT_MAX_PARTIALS, AnalogOutputMode analogOutputMode = AnalogOutputMode_COARSE);
|
||||
|
||||
// Overloaded method which opens the synth with default partial count.
|
||||
bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, AnalogOutputMode analogOutputMode);
|
||||
|
||||
// Closes the MT-32 and deallocates any memory used by the synthesizer
|
||||
void close(bool forced = false);
|
||||
@ -444,29 +317,34 @@ public:
|
||||
// All the enqueued events are processed by the synth immediately.
|
||||
void flushMIDIQueue();
|
||||
|
||||
// Sets size of the internal MIDI event queue.
|
||||
// Sets size of the internal MIDI event queue. The queue size is set to the minimum power of 2 that is greater or equal to the size specified.
|
||||
// The queue is flushed before reallocation.
|
||||
void setMIDIEventQueueSize(Bit32u);
|
||||
// Returns the actual queue size being used.
|
||||
Bit32u 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.
|
||||
// The timestamp is measured as the global rendered sample count since the synth was created (at the native sample rate 32000 Hz).
|
||||
// The minimum delay involves emulation of 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 methods return false if the MIDI event queue is full and the message cannot be enqueued.
|
||||
|
||||
// Enqueues a single short MIDI message. The message must contain a status byte.
|
||||
bool playMsg(Bit32u msg, Bit32u timestamp);
|
||||
// Enqueues a single well formed System Exclusive MIDI message.
|
||||
bool playSysex(const Bit8u *sysex, Bit32u len, Bit32u timestamp);
|
||||
// The MIDI event will be processed ASAP.
|
||||
|
||||
// Overloaded methods for the MIDI events to 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.
|
||||
// A thread that invokes these methods must be explicitly synchronised with the thread performing sample rendering.
|
||||
|
||||
// Sends a 4-byte MIDI message to the MT-32 for immediate playback.
|
||||
// Sends a short MIDI message to the synth for immediate playback. The message must contain a status byte.
|
||||
void playMsgNow(Bit32u msg);
|
||||
void playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity);
|
||||
|
||||
@ -495,12 +373,17 @@ public:
|
||||
void setMIDIDelayMode(MIDIDelayMode mode);
|
||||
MIDIDelayMode getMIDIDelayMode() const;
|
||||
|
||||
// Sets output gain factor. Applied to all output samples and unrelated with the synth's Master volume.
|
||||
// Sets output gain factor for synth output channels. Applied to all output samples and unrelated with the synth's Master volume,
|
||||
// it rather corresponds to the gain of the output analog circuitry of the hardware units. However, together with setReverbOutputGain()
|
||||
// it offers to the user a capability to control the gain of reverb and non-reverb output channels independently.
|
||||
// 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.
|
||||
// Sets output gain factor for the reverb wet output channels. It rather corresponds to the gain of the output
|
||||
// analog circuitry of the hardware units. However, together with setOutputGain() it offers to the user a capability
|
||||
// to control the gain of reverb and non-reverb output channels independently.
|
||||
//
|
||||
// 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
|
||||
@ -512,12 +395,21 @@ public:
|
||||
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).
|
||||
// Returns actual sample rate used in emulation of stereo analog circuitry of hardware units.
|
||||
// See comment for render() below.
|
||||
unsigned int getStereoOutputSampleRate() const;
|
||||
|
||||
// Renders samples to the specified output stream as if they were sampled at the analog stereo output.
|
||||
// When AnalogOutputMode is set to ACCURATE, the output signal is upsampled to 48 kHz in order
|
||||
// to retain emulation accuracy in whole audible frequency spectra. Otherwise, native digital signal sample rate is retained.
|
||||
// getStereoOutputSampleRate() can be used to query actual sample rate of the output signal.
|
||||
// The length is in frames, not bytes (in 16-bit stereo, one frame is 4 bytes).
|
||||
void render(Sample *stream, Bit32u len);
|
||||
|
||||
// Renders samples to the specified output streams (any or all of which may be NULL).
|
||||
// Renders samples to the specified output streams as if they appeared at the DAC entrance.
|
||||
// No further processing performed in analog circuitry emulation is applied to the signal.
|
||||
// NULL may be specified in place of any or all of the stream buffers.
|
||||
// The length is in samples, not bytes.
|
||||
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.
|
||||
@ -526,15 +418,28 @@ public:
|
||||
// Returns true if hasActivePartials() returns true, or reverb is (somewhat unreliably) detected as being active.
|
||||
bool isActive() const;
|
||||
|
||||
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);
|
||||
// Fills in current states of all the parts into the array provided. The array must have at least 9 entries to fit values for all the parts.
|
||||
// If the value returned for a part is true, there is at least one active non-releasing partial playing on this part.
|
||||
// This info is useful in emulating behaviour of LCD display of the hardware units.
|
||||
void getPartStates(bool *partStates) const;
|
||||
|
||||
// partNum should be 0..7 for Part 1..8, or 8 for Rhythm
|
||||
const Part *getPart(unsigned int partNum) const;
|
||||
// Fills in current states of all the partials into the array provided. The array must be large enough to accommodate states of all the partials.
|
||||
void getPartialStates(PartialState *partialStates) const;
|
||||
|
||||
// Fills in information about currently playing notes on the specified part into the arrays provided. The arrays must be large enough
|
||||
// to accommodate data for all the playing notes. The maximum number of simultaneously playing notes cannot exceed the number of partials.
|
||||
// Argument partNumber should be 0..7 for Part 1..8, or 8 for Rhythm.
|
||||
// Returns the number of currently playing notes on the specified part.
|
||||
unsigned int getPlayingNotes(unsigned int partNumber, Bit8u *keys, Bit8u *velocities) const;
|
||||
|
||||
// Returns name of the patch set on the specified part.
|
||||
// Argument partNumber should be 0..7 for Part 1..8, or 8 for Rhythm.
|
||||
const char *getPatchName(unsigned int partNumber) const;
|
||||
|
||||
void readMemory(Bit32u addr, Bit32u len, Bit8u *data);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include "mt32emu.h"
|
||||
#include "mmath.h"
|
||||
#include "internals.h"
|
||||
|
||||
namespace MT32Emu {
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include "mt32emu.h"
|
||||
#include "mmath.h"
|
||||
#include "internals.h"
|
||||
|
||||
namespace MT32Emu {
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
//#include <cstdlib>
|
||||
|
||||
#include "mt32emu.h"
|
||||
#include "internals.h"
|
||||
|
||||
namespace MT32Emu {
|
||||
|
||||
|
@ -16,14 +16,15 @@
|
||||
*/
|
||||
|
||||
//#include <cmath>
|
||||
//#include <cstdlib>
|
||||
//#include <cstring>
|
||||
|
||||
#include "mt32emu.h"
|
||||
#include "mmath.h"
|
||||
#include "Tables.h"
|
||||
|
||||
namespace MT32Emu {
|
||||
|
||||
// UNUSED: const int MIDDLEC = 60;
|
||||
|
||||
const Tables &Tables::getInstance() {
|
||||
static const Tables instance;
|
||||
return instance;
|
||||
|
@ -20,24 +20,11 @@
|
||||
|
||||
namespace MT32Emu {
|
||||
|
||||
// Sample rate to use in mixing. With the progress of development, we've found way too many thing dependent.
|
||||
// In order to achieve further advance in emulation accuracy, sample rate made fixed throughout the emulator.
|
||||
// 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;
|
||||
|
||||
class Tables {
|
||||
private:
|
||||
Tables();
|
||||
Tables(Tables &);
|
||||
~Tables() {}
|
||||
|
||||
public:
|
||||
static const Tables &getInstance();
|
||||
|
40
audio/softsynth/mt32/Types.h
Normal file
40
audio/softsynth/mt32/Types.h
Normal file
@ -0,0 +1,40 @@
|
||||
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
|
||||
* Copyright (C) 2011, 2012, 2013, 2014 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_TYPES_H
|
||||
#define MT32EMU_TYPES_H
|
||||
|
||||
namespace MT32Emu {
|
||||
|
||||
typedef unsigned int Bit32u;
|
||||
typedef signed int Bit32s;
|
||||
typedef unsigned short int Bit16u;
|
||||
typedef signed short int Bit16s;
|
||||
typedef unsigned char Bit8u;
|
||||
typedef signed char Bit8s;
|
||||
|
||||
#if MT32EMU_USE_FLOAT_SAMPLES
|
||||
typedef float Sample;
|
||||
typedef float SampleEx;
|
||||
#else
|
||||
typedef Bit16s Sample;
|
||||
typedef Bit32s SampleEx;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif
|
83
audio/softsynth/mt32/internals.h
Normal file
83
audio/softsynth/mt32/internals.h
Normal file
@ -0,0 +1,83 @@
|
||||
/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher
|
||||
* Copyright (C) 2011, 2012, 2013, 2014 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_INTERNALS_H
|
||||
#define MT32EMU_INTERNALS_H
|
||||
|
||||
// Debugging
|
||||
|
||||
// 0: Standard debug output is not stamped with the rendered sample count
|
||||
// 1: Standard debug output is stamped with the rendered sample count
|
||||
// NOTE: The "samplestamp" corresponds to the end of the last completed rendering run.
|
||||
// This is important to bear in mind for debug output that occurs during a run.
|
||||
#define MT32EMU_DEBUG_SAMPLESTAMPS 0
|
||||
|
||||
// 0: No debug output for initialisation progress
|
||||
// 1: Debug output for initialisation progress
|
||||
#define MT32EMU_MONITOR_INIT 0
|
||||
|
||||
// 0: No debug output for MIDI events
|
||||
// 1: Debug output for weird MIDI events
|
||||
#define MT32EMU_MONITOR_MIDI 0
|
||||
|
||||
// 0: No debug output for note on/off
|
||||
// 1: Basic debug output for note on/off
|
||||
// 2: Comprehensive debug output for note on/off
|
||||
#define MT32EMU_MONITOR_INSTRUMENTS 0
|
||||
|
||||
// 0: No debug output for partial allocations
|
||||
// 1: Show partial stats when an allocation fails
|
||||
// 2: Show partial stats with every new poly
|
||||
// 3: Show individual partial allocations/deactivations
|
||||
#define MT32EMU_MONITOR_PARTIALS 0
|
||||
|
||||
// 0: No debug output for sysex
|
||||
// 1: Basic debug output for sysex
|
||||
#define MT32EMU_MONITOR_SYSEX 0
|
||||
|
||||
// 0: No debug output for sysex writes to the timbre areas
|
||||
// 1: Debug output with the name and location of newly-written timbres
|
||||
// 2: Complete dump of timbre parameters for newly-written timbres
|
||||
#define MT32EMU_MONITOR_TIMBRES 0
|
||||
|
||||
// 0: No TVA/TVF-related debug output.
|
||||
// 1: Shows changes to TVA/TVF target, increment and phase.
|
||||
#define MT32EMU_MONITOR_TVA 0
|
||||
#define MT32EMU_MONITOR_TVF 0
|
||||
|
||||
// Configuration
|
||||
|
||||
// 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: Maximum speed at the cost of a bit lower emulation accuracy.
|
||||
// 1: Maximum achievable emulation accuracy.
|
||||
#define MT32EMU_BOSS_REVERB_PRECISE_MODE 0
|
||||
|
||||
#include "Structures.h"
|
||||
#include "Tables.h"
|
||||
#include "Poly.h"
|
||||
#include "LA32Ramp.h"
|
||||
#include "LA32WaveGenerator.h"
|
||||
#include "TVA.h"
|
||||
#include "TVP.h"
|
||||
#include "TVF.h"
|
||||
#include "Partial.h"
|
||||
#include "Part.h"
|
||||
|
||||
#endif
|
@ -1,6 +1,7 @@
|
||||
MODULE := audio/softsynth/mt32
|
||||
|
||||
MODULE_OBJS := \
|
||||
Analog.o \
|
||||
BReverbModel.o \
|
||||
LA32Ramp.o \
|
||||
LA32WaveGenerator.o \
|
||||
|
@ -18,63 +18,20 @@
|
||||
#ifndef MT32EMU_MT32EMU_H
|
||||
#define MT32EMU_MT32EMU_H
|
||||
|
||||
// Debugging
|
||||
|
||||
// 0: Standard debug output is not stamped with the rendered sample count
|
||||
// 1: Standard debug output is stamped with the rendered sample count
|
||||
// NOTE: The "samplestamp" corresponds to the end of the last completed rendering run.
|
||||
// This is important to bear in mind for debug output that occurs during a run.
|
||||
#define MT32EMU_DEBUG_SAMPLESTAMPS 0
|
||||
|
||||
// 0: No debug output for initialisation progress
|
||||
// 1: Debug output for initialisation progress
|
||||
#define MT32EMU_MONITOR_INIT 0
|
||||
|
||||
// 0: No debug output for MIDI events
|
||||
// 1: Debug output for weird MIDI events
|
||||
#define MT32EMU_MONITOR_MIDI 0
|
||||
|
||||
// 0: No debug output for note on/off
|
||||
// 1: Basic debug output for note on/off
|
||||
// 2: Comprehensive debug output for note on/off
|
||||
#define MT32EMU_MONITOR_INSTRUMENTS 0
|
||||
|
||||
// 0: No debug output for partial allocations
|
||||
// 1: Show partial stats when an allocation fails
|
||||
// 2: Show partial stats with every new poly
|
||||
// 3: Show individual partial allocations/deactivations
|
||||
#define MT32EMU_MONITOR_PARTIALS 0
|
||||
|
||||
// 0: No debug output for sysex
|
||||
// 1: Basic debug output for sysex
|
||||
#define MT32EMU_MONITOR_SYSEX 0
|
||||
|
||||
// 0: No debug output for sysex writes to the timbre areas
|
||||
// 1: Debug output with the name and location of newly-written timbres
|
||||
// 2: Complete dump of timbre parameters for newly-written timbres
|
||||
#define MT32EMU_MONITOR_TIMBRES 0
|
||||
|
||||
// 0: No TVA/TVF-related debug output.
|
||||
// 1: Shows changes to TVA/TVF target, increment and phase.
|
||||
#define MT32EMU_MONITOR_TVA 0
|
||||
#define MT32EMU_MONITOR_TVF 0
|
||||
|
||||
// Configuration
|
||||
|
||||
// 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: 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 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
|
||||
{
|
||||
// Sample rate to use in mixing. With the progress of development, we've found way too many thing dependent.
|
||||
// In order to achieve further advance in emulation accuracy, sample rate made fixed throughout the emulator,
|
||||
// except the emulation of analogue path.
|
||||
// The output from the synth is supposed to be resampled externally in order to convert to the desired sample rate.
|
||||
const unsigned int SAMPLE_RATE = 32000;
|
||||
|
||||
// The default value for the maximum number of partials playing simultaneously.
|
||||
const unsigned int DEFAULT_MAX_PARTIALS = 32;
|
||||
|
||||
@ -97,17 +54,7 @@ const unsigned int MAX_SAMPLES_PER_RUN = 4096;
|
||||
const unsigned int DEFAULT_MIDI_EVENT_QUEUE_SIZE = 1024;
|
||||
}
|
||||
|
||||
#include "Structures.h"
|
||||
#include "common/file.h"
|
||||
#include "Tables.h"
|
||||
#include "Poly.h"
|
||||
#include "LA32Ramp.h"
|
||||
#include "LA32WaveGenerator.h"
|
||||
#include "TVA.h"
|
||||
#include "TVP.h"
|
||||
#include "TVF.h"
|
||||
#include "Partial.h"
|
||||
#include "Part.h"
|
||||
#include "Types.h"
|
||||
#include "ROMInfo.h"
|
||||
#include "Synth.h"
|
||||
|
||||
|
@ -39,12 +39,10 @@ Timestamp::Timestamp(uint ms, uint fr) {
|
||||
Timestamp::Timestamp(uint s, uint frames, uint fr) {
|
||||
assert(fr > 0);
|
||||
|
||||
_secs = s;
|
||||
_secs = s + (frames / fr);
|
||||
_framerateFactor = 1000 / Common::gcd<uint>(1000, fr);
|
||||
_framerate = fr * _framerateFactor;
|
||||
_numFrames = frames * _framerateFactor;
|
||||
|
||||
normalize();
|
||||
_numFrames = (frames % fr) * _framerateFactor;
|
||||
}
|
||||
|
||||
Timestamp Timestamp::convertToFramerate(uint newFramerate) const {
|
||||
|
@ -24,9 +24,12 @@
|
||||
|
||||
#if defined(SDL_BACKEND)
|
||||
|
||||
#include "common/textconsole.h"
|
||||
#include "backends/audiocd/sdl/sdl-audiocd.h"
|
||||
|
||||
#if !SDL_VERSION_ATLEAST(1, 3, 0)
|
||||
|
||||
#include "common/textconsole.h"
|
||||
|
||||
SdlAudioCDManager::SdlAudioCDManager()
|
||||
:
|
||||
_cdrom(0),
|
||||
@ -133,4 +136,6 @@ void SdlAudioCDManager::updateCD() {
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !SDL_VERSION_ATLEAST(1, 3, 0)
|
||||
|
||||
#endif
|
||||
|
@ -27,6 +27,8 @@
|
||||
|
||||
#include "backends/platform/sdl/sdl-sys.h"
|
||||
|
||||
#if !SDL_VERSION_ATLEAST(1, 3, 0)
|
||||
|
||||
/**
|
||||
* The SDL audio cd manager. Implements real audio cd playback.
|
||||
*/
|
||||
@ -47,4 +49,6 @@ protected:
|
||||
uint32 _cdEndTime, _cdStopTime;
|
||||
};
|
||||
|
||||
#endif // !SDL_VERSION_ATLEAST(1, 3, 0)
|
||||
|
||||
#endif
|
||||
|
@ -26,18 +26,48 @@
|
||||
|
||||
#include "backends/events/dinguxsdl/dinguxsdl-events.h"
|
||||
|
||||
#ifndef GCW0
|
||||
#define PAD_UP SDLK_UP
|
||||
#define PAD_DOWN SDLK_DOWN
|
||||
#define PAD_LEFT SDLK_LEFT
|
||||
#define PAD_RIGHT SDLK_RIGHT
|
||||
#define BUT_A SDLK_LCTRL
|
||||
#define BUT_B SDLK_LALT
|
||||
#define BUT_X SDLK_SPACE
|
||||
#define BUT_Y SDLK_LSHIFT
|
||||
#define BUT_X SDLK_SPACE // BUT_Y in GCW0
|
||||
#define BUT_Y SDLK_LSHIFT // BUT_X in GCW0
|
||||
#define BUT_SELECT SDLK_ESCAPE
|
||||
#define BUT_START SDLK_RETURN
|
||||
#define TRIG_L SDLK_TAB
|
||||
#define TRIG_R SDLK_BACKSPACE
|
||||
#else // GCW0
|
||||
|
||||
/******
|
||||
* GCW0 keymap
|
||||
* Dingoo button
|
||||
* A -> Left Button BUT_Y
|
||||
* B -> right button BUT_B
|
||||
* X -> ' ' BUT_A '0'
|
||||
* Y -> '.' BUT_X
|
||||
* Select -> ESC TRIG_R
|
||||
* Start -> F5 TRIG_L
|
||||
* L -> Shift BUT_START
|
||||
* R -> VK BUT_SELECT
|
||||
*/
|
||||
|
||||
#define PAD_UP SDLK_UP
|
||||
#define PAD_DOWN SDLK_DOWN
|
||||
#define PAD_LEFT SDLK_LEFT
|
||||
#define PAD_RIGHT SDLK_RIGHT
|
||||
#define BUT_A SDLK_LSHIFT
|
||||
#define BUT_B SDLK_LALT
|
||||
#define BUT_X SDLK_SPACE
|
||||
#define BUT_Y SDLK_LCTRL
|
||||
#define BUT_SELECT SDLK_BACKSPACE
|
||||
#define BUT_START SDLK_TAB
|
||||
#define TRIG_L SDLK_RETURN
|
||||
#define TRIG_R SDLK_ESCAPE
|
||||
|
||||
#endif
|
||||
|
||||
bool DINGUXSdlEventSource::remapKey(SDL_Event &ev, Common::Event &event) {
|
||||
if (ev.key.keysym.sym == PAD_UP) {
|
||||
|
@ -49,22 +49,55 @@
|
||||
#define JOY_BUT_SPACE 4
|
||||
#define JOY_BUT_F5 5
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
static uint32 convUTF8ToUTF32(const char *src) {
|
||||
uint32 utf32 = 0;
|
||||
|
||||
char *dst = SDL_iconv_string(
|
||||
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
||||
"UTF-32BE",
|
||||
#else
|
||||
"UTF-32LE",
|
||||
#endif
|
||||
"UTF-8", src, SDL_strlen(src) + 1);
|
||||
|
||||
if (dst) {
|
||||
utf32 = *((uint32 *)dst);
|
||||
SDL_free(dst);
|
||||
}
|
||||
|
||||
return utf32;
|
||||
}
|
||||
#endif
|
||||
|
||||
SdlEventSource::SdlEventSource()
|
||||
: EventSource(), _scrollLock(false), _joystick(0), _lastScreenID(0), _graphicsManager(0) {
|
||||
: EventSource(), _scrollLock(false), _joystick(0), _lastScreenID(0), _graphicsManager(0)
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
, _queuedFakeKeyUp(false), _fakeKeyUp()
|
||||
#endif
|
||||
{
|
||||
// Reset mouse state
|
||||
memset(&_km, 0, sizeof(_km));
|
||||
|
||||
int joystick_num = ConfMan.getInt("joystick_num");
|
||||
if (joystick_num > -1) {
|
||||
if (joystick_num >= 0) {
|
||||
// Initialize SDL joystick subsystem
|
||||
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) {
|
||||
error("Could not initialize SDL: %s", SDL_GetError());
|
||||
}
|
||||
|
||||
// Enable joystick
|
||||
if (SDL_NumJoysticks() > 0) {
|
||||
debug("Using joystick: %s", SDL_JoystickName(0));
|
||||
if (SDL_NumJoysticks() > joystick_num) {
|
||||
_joystick = SDL_JoystickOpen(joystick_num);
|
||||
debug("Using joystick: %s",
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
SDL_JoystickName(_joystick)
|
||||
#else
|
||||
SDL_JoystickName(joystick_num)
|
||||
#endif
|
||||
);
|
||||
} else {
|
||||
warning("Invalid joystick: %d", joystick_num);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -74,21 +107,24 @@ SdlEventSource::~SdlEventSource() {
|
||||
SDL_JoystickClose(_joystick);
|
||||
}
|
||||
|
||||
int SdlEventSource::mapKey(SDLKey key, SDLMod mod, Uint16 unicode) {
|
||||
if (key >= SDLK_F1 && key <= SDLK_F9) {
|
||||
int SdlEventSource::mapKey(SDLKey sdlKey, SDLMod mod, Uint16 unicode) {
|
||||
Common::KeyCode key = SDLToOSystemKeycode(sdlKey);
|
||||
|
||||
if (key >= Common::KEYCODE_F1 && key <= Common::KEYCODE_F9) {
|
||||
return key - SDLK_F1 + Common::ASCII_F1;
|
||||
} else if (key >= SDLK_KP0 && key <= SDLK_KP9) {
|
||||
return key - SDLK_KP0 + '0';
|
||||
} else if (key >= SDLK_UP && key <= SDLK_PAGEDOWN) {
|
||||
} else if (key >= Common::KEYCODE_KP0 && key <= Common::KEYCODE_KP9) {
|
||||
return key - Common::KEYCODE_KP0 + '0';
|
||||
} else if (key >= Common::KEYCODE_UP && key <= Common::KEYCODE_PAGEDOWN) {
|
||||
return key;
|
||||
} else if (unicode) {
|
||||
return unicode;
|
||||
} else if (key >= 'a' && key <= 'z' && (mod & KMOD_SHIFT)) {
|
||||
return key & ~0x20;
|
||||
} else if (key >= SDLK_NUMLOCK && key <= SDLK_EURO) {
|
||||
} else if (key >= Common::KEYCODE_NUMLOCK && key <= Common::KEYCODE_EURO) {
|
||||
return 0;
|
||||
} else {
|
||||
return key;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
void SdlEventSource::processMouseEvent(Common::Event &event, int x, int y) {
|
||||
@ -171,7 +207,9 @@ void SdlEventSource::handleKbdMouse() {
|
||||
_km.y_down_count = 1;
|
||||
}
|
||||
|
||||
SDL_WarpMouse((Uint16)_km.x, (Uint16)_km.y);
|
||||
if (_graphicsManager) {
|
||||
_graphicsManager->getWindow()->warpMouseInWindow((Uint16)_km.x, (Uint16)_km.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -338,7 +376,9 @@ Common::KeyCode SdlEventSource::SDLToOSystemKeycode(const SDLKey key) {
|
||||
case SDLK_HELP: return Common::KEYCODE_HELP;
|
||||
case SDLK_PRINT: return Common::KEYCODE_PRINT;
|
||||
case SDLK_SYSREQ: return Common::KEYCODE_SYSREQ;
|
||||
#if !SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
case SDLK_BREAK: return Common::KEYCODE_BREAK;
|
||||
#endif
|
||||
case SDLK_MENU: return Common::KEYCODE_MENU;
|
||||
case SDLK_POWER: return Common::KEYCODE_POWER;
|
||||
case SDLK_UNDO: return Common::KEYCODE_UNDO;
|
||||
@ -349,6 +389,16 @@ Common::KeyCode SdlEventSource::SDLToOSystemKeycode(const SDLKey key) {
|
||||
bool SdlEventSource::pollEvent(Common::Event &event) {
|
||||
handleKbdMouse();
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
// In case we still need to send a key up event for a key down from a
|
||||
// TEXTINPUT event we do this immediately.
|
||||
if (_queuedFakeKeyUp) {
|
||||
event = _fakeKeyUp;
|
||||
_queuedFakeKeyUp = false;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
// If the screen changed, send an Common::EVENT_SCREEN_CHANGED
|
||||
int screenID = ((OSystem_SDL *)g_system)->getGraphicsManager()->getScreenChangeID();
|
||||
if (screenID != _lastScreenID) {
|
||||
@ -385,24 +435,73 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) {
|
||||
case SDL_JOYAXISMOTION:
|
||||
return handleJoyAxisMotion(ev, event);
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
case SDL_MOUSEWHEEL: {
|
||||
Sint32 yDir = ev.wheel.y;
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 4)
|
||||
if (ev.wheel.direction == SDL_MOUSEWHEEL_FLIPPED) {
|
||||
yDir *= -1;
|
||||
}
|
||||
#endif
|
||||
// HACK: It seems we want the mouse coordinates supplied
|
||||
// with a mouse wheel event. However, SDL2 does not supply
|
||||
// these, thus we use whatever we got last time. It seems
|
||||
// these are always stored in _km.x, _km.y.
|
||||
processMouseEvent(event, _km.x, _km.y);
|
||||
if (yDir < 0) {
|
||||
event.type = Common::EVENT_WHEELDOWN;
|
||||
return true;
|
||||
} else if (yDir > 0) {
|
||||
event.type = Common::EVENT_WHEELUP;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
case SDL_TEXTINPUT: {
|
||||
// When we get a TEXTINPUT event it means we got some user input for
|
||||
// which no KEYDOWN exists. SDL 1.2 introduces a "fake" key down+up
|
||||
// in such cases. We will do the same to mimic it's behavior.
|
||||
event.type = Common::EVENT_KEYDOWN;
|
||||
|
||||
event.kbd = Common::KeyState(Common::KEYCODE_INVALID, convUTF8ToUTF32(ev.text.text), 0);
|
||||
|
||||
SDLModToOSystemKeyFlags(SDL_GetModState(), event);
|
||||
// Set the scroll lock sticky flag
|
||||
if (_scrollLock)
|
||||
event.kbd.flags |= Common::KBD_SCRL;
|
||||
|
||||
// Fake a key up when we have a proper ascii value.
|
||||
_queuedFakeKeyUp = (event.kbd.ascii != 0);
|
||||
_fakeKeyUp = event;
|
||||
_fakeKeyUp.type = Common::EVENT_KEYUP;
|
||||
|
||||
return _queuedFakeKeyUp;
|
||||
}
|
||||
|
||||
case SDL_WINDOWEVENT:
|
||||
switch (ev.window.event) {
|
||||
case SDL_WINDOWEVENT_EXPOSED:
|
||||
if (_graphicsManager)
|
||||
_graphicsManager->notifyVideoExpose();
|
||||
return false;
|
||||
|
||||
case SDL_WINDOWEVENT_RESIZED:
|
||||
return handleResizeEvent(event, ev.window.data1, ev.window.data2);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
case SDL_VIDEOEXPOSE:
|
||||
if (_graphicsManager)
|
||||
_graphicsManager->notifyVideoExpose();
|
||||
return false;
|
||||
|
||||
case SDL_VIDEORESIZE:
|
||||
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;
|
||||
return handleResizeEvent(event, ev.resize.w, ev.resize.h);
|
||||
#endif
|
||||
|
||||
case SDL_QUIT:
|
||||
event.type = Common::EVENT_QUIT;
|
||||
@ -427,7 +526,9 @@ bool SdlEventSource::handleKeyDown(SDL_Event &ev, Common::Event &event) {
|
||||
|
||||
// Ctrl-m toggles mouse capture
|
||||
if (event.kbd.hasFlags(Common::KBD_CTRL) && ev.key.keysym.sym == 'm') {
|
||||
toggleMouseGrab();
|
||||
if (_graphicsManager) {
|
||||
_graphicsManager->getWindow()->toggleMouseGrab();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -470,7 +571,7 @@ bool SdlEventSource::handleKeyDown(SDL_Event &ev, Common::Event &event) {
|
||||
|
||||
event.type = Common::EVENT_KEYDOWN;
|
||||
event.kbd.keycode = SDLToOSystemKeycode(ev.key.keysym.sym);
|
||||
event.kbd.ascii = mapKey(ev.key.keysym.sym, (SDLMod)ev.key.keysym.mod, (Uint16)ev.key.keysym.unicode);
|
||||
event.kbd.ascii = mapKey(ev.key.keysym.sym, (SDLMod)ev.key.keysym.mod, obtainUnicode(ev.key.keysym));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -514,7 +615,7 @@ bool SdlEventSource::handleKeyUp(SDL_Event &ev, Common::Event &event) {
|
||||
|
||||
event.type = Common::EVENT_KEYUP;
|
||||
event.kbd.keycode = SDLToOSystemKeycode(ev.key.keysym.sym);
|
||||
event.kbd.ascii = mapKey(ev.key.keysym.sym, (SDLMod)ev.key.keysym.mod, (Uint16)ev.key.keysym.unicode);
|
||||
event.kbd.ascii = mapKey(ev.key.keysym.sym, (SDLMod)ev.key.keysym.mod, 0);
|
||||
|
||||
// Ctrl-Alt-<key> will change the GFX mode
|
||||
SDLModToOSystemKeyFlags(mod, event);
|
||||
@ -750,13 +851,6 @@ bool SdlEventSource::remapKey(SDL_Event &ev, Common::Event &event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void SdlEventSource::toggleMouseGrab() {
|
||||
if (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_OFF)
|
||||
SDL_WM_GrabInput(SDL_GRAB_ON);
|
||||
else
|
||||
SDL_WM_GrabInput(SDL_GRAB_OFF);
|
||||
}
|
||||
|
||||
void SdlEventSource::resetKeyboadEmulation(int16 x_max, int16 y_max) {
|
||||
_km.x_max = x_max;
|
||||
_km.y_max = y_max;
|
||||
@ -764,4 +858,52 @@ void SdlEventSource::resetKeyboadEmulation(int16 x_max, int16 y_max) {
|
||||
_km.last_time = 0;
|
||||
}
|
||||
|
||||
bool SdlEventSource::handleResizeEvent(Common::Event &event, int w, int h) {
|
||||
if (_graphicsManager) {
|
||||
_graphicsManager->notifyResize(w, 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;
|
||||
}
|
||||
|
||||
uint32 SdlEventSource::obtainUnicode(const SDL_keysym keySym) {
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
SDL_Event events[2];
|
||||
|
||||
// In SDL2, the unicode field has been removed from the keysym struct.
|
||||
// Instead a SDL_TEXTINPUT event is generated on key combinations that
|
||||
// generates unicode.
|
||||
// Here we peek into the event queue for the event to see if it exists.
|
||||
int n = SDL_PeepEvents(events, 2, SDL_PEEKEVENT, SDL_KEYDOWN, SDL_TEXTINPUT);
|
||||
// Make sure that the TEXTINPUT event belongs to this KEYDOWN
|
||||
// event and not another pending one.
|
||||
if ((n > 0 && events[0].type == SDL_TEXTINPUT)
|
||||
|| (n > 1 && events[0].type != SDL_KEYDOWN && events[1].type == SDL_TEXTINPUT)) {
|
||||
// Remove the text input event we associate with the key press. This
|
||||
// makes sure we never get any SDL_TEXTINPUT events which do "belong"
|
||||
// to SDL_KEYDOWN events.
|
||||
n = SDL_PeepEvents(events, 1, SDL_GETEVENT, SDL_TEXTINPUT, SDL_TEXTINPUT);
|
||||
// This is basically a paranoia safety check because we know there
|
||||
// must be a text input event in the queue.
|
||||
if (n > 0) {
|
||||
return convUTF8ToUTF32(events[0].text.text);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
return keySym.unicode;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -49,11 +49,6 @@ public:
|
||||
*/
|
||||
virtual void resetKeyboadEmulation(int16 x_max, int16 y_max);
|
||||
|
||||
/**
|
||||
* Toggles mouse input grab
|
||||
*/
|
||||
virtual void toggleMouseGrab();
|
||||
|
||||
protected:
|
||||
/** @name Keyboard mouse emulation
|
||||
* Disabled by fingolfin 2004-12-18.
|
||||
@ -130,7 +125,7 @@ protected:
|
||||
/**
|
||||
* Maps the ASCII value of key
|
||||
*/
|
||||
virtual int mapKey(SDLKey key, SDLMod mod, Uint16 unicode);
|
||||
int mapKey(SDLKey key, SDLMod mod, Uint16 unicode);
|
||||
|
||||
/**
|
||||
* Configures the key modifiers flags status
|
||||
@ -141,6 +136,30 @@ protected:
|
||||
* Translates SDL key codes to OSystem key codes
|
||||
*/
|
||||
Common::KeyCode SDLToOSystemKeycode(const SDLKey key);
|
||||
|
||||
/**
|
||||
* Notify graphics manager of a resize request.
|
||||
*/
|
||||
bool handleResizeEvent(Common::Event &event, int w, int h);
|
||||
|
||||
/**
|
||||
* Extracts unicode information for the specific key sym.
|
||||
* May only be used for key down events.
|
||||
*/
|
||||
uint32 obtainUnicode(const SDL_keysym keySym);
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
/**
|
||||
* Whether _fakeKeyUp contains an event we need to send.
|
||||
*/
|
||||
bool _queuedFakeKeyUp;
|
||||
|
||||
/**
|
||||
* A fake key up event when we receive a TEXTINPUT without any previous
|
||||
* KEYDOWN event.
|
||||
*/
|
||||
Common::Event _fakeKeyUp;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -133,7 +133,9 @@ bool SymbianSdlEventSource::remapKey(SDL_Event &ev, Common::Event &event) {
|
||||
_currentZone = 0;
|
||||
event.type = Common::EVENT_MOUSEMOVE;
|
||||
processMouseEvent(event, _mouseXZone[_currentZone], _mouseYZone[_currentZone]);
|
||||
SDL_WarpMouse(event.mouse.x, event.mouse.y);
|
||||
if (_graphicsManager) {
|
||||
_graphicsManager->getWindow()->warpMouseInWindow(event.mouse.x, event.mouse.y);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -381,15 +381,17 @@ AbstractFSList AmigaOSFilesystemNode::listVolumes() const {
|
||||
dosList = IDOS->NextDosEntry(dosList, LDF_VOLUMES);
|
||||
while (dosList) {
|
||||
if (dosList->dol_Type == DLT_VOLUME &&
|
||||
dosList->dol_Name) {
|
||||
dosList->dol_Name &&
|
||||
dosList->dol_Port) {
|
||||
|
||||
// The original line was
|
||||
//if (dosList->dol_Type == DLT_VOLUME &&
|
||||
//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
|
||||
// The reason for that was that
|
||||
// 1) dol_Task wasn't a task pointer, it is a message port instead
|
||||
// 2) It was redefined to be dol_Port in dos/obsolete.h in afore mentioned SDK
|
||||
|
||||
// Copy name to buffer
|
||||
IDOS->CopyStringBSTRToC(dosList->dol_Name, buffer, MAXPATHLEN);
|
||||
|
@ -35,8 +35,8 @@ static const OSystem::GraphicsMode s_supportedGraphicsModes[] = {
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
DINGUXSdlGraphicsManager::DINGUXSdlGraphicsManager(SdlEventSource *boss)
|
||||
: SurfaceSdlGraphicsManager(boss) {
|
||||
DINGUXSdlGraphicsManager::DINGUXSdlGraphicsManager(SdlEventSource *boss, SdlWindow *window)
|
||||
: SurfaceSdlGraphicsManager(boss, window) {
|
||||
}
|
||||
|
||||
const OSystem::GraphicsMode *DINGUXSdlGraphicsManager::getSupportedGraphicsModes() const {
|
||||
@ -122,7 +122,7 @@ void DINGUXSdlGraphicsManager::initSize(uint w, uint h) {
|
||||
if (w > 320 || h > 240) {
|
||||
setGraphicsMode(GFX_HALF);
|
||||
setGraphicsModeIntern();
|
||||
_eventSource->toggleMouseGrab();
|
||||
_window->toggleMouseGrab();
|
||||
}
|
||||
|
||||
_transactionDetails.sizeChanged = true;
|
||||
|
@ -34,7 +34,7 @@ enum {
|
||||
|
||||
class DINGUXSdlGraphicsManager : public SurfaceSdlGraphicsManager {
|
||||
public:
|
||||
DINGUXSdlGraphicsManager(SdlEventSource *boss);
|
||||
DINGUXSdlGraphicsManager(SdlEventSource *boss, SdlWindow *window);
|
||||
|
||||
bool hasFeature(OSystem::Feature f);
|
||||
void setFeatureState(OSystem::Feature f, bool enable);
|
||||
|
@ -35,8 +35,8 @@ static const OSystem::GraphicsMode s_supportedGraphicsModes[] = {
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
GPHGraphicsManager::GPHGraphicsManager(SdlEventSource *sdlEventSource)
|
||||
: SurfaceSdlGraphicsManager(sdlEventSource) {
|
||||
GPHGraphicsManager::GPHGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window)
|
||||
: SurfaceSdlGraphicsManager(sdlEventSource, window) {
|
||||
}
|
||||
|
||||
const OSystem::GraphicsMode *GPHGraphicsManager::getSupportedGraphicsModes() const {
|
||||
@ -141,7 +141,7 @@ void GPHGraphicsManager::initSize(uint w, uint h, const Graphics::PixelFormat *f
|
||||
if (w > 320 || h > 240) {
|
||||
setGraphicsMode(GFX_HALF);
|
||||
setGraphicsModeIntern();
|
||||
_eventSource->toggleMouseGrab();
|
||||
_window->toggleMouseGrab();
|
||||
}
|
||||
|
||||
_videoMode.overlayWidth = 320;
|
||||
|
@ -33,7 +33,7 @@ enum {
|
||||
|
||||
class GPHGraphicsManager : public SurfaceSdlGraphicsManager {
|
||||
public:
|
||||
GPHGraphicsManager(SdlEventSource *boss);
|
||||
GPHGraphicsManager(SdlEventSource *boss, SdlWindow *window);
|
||||
|
||||
bool hasFeature(OSystem::Feature f);
|
||||
void setFeatureState(OSystem::Feature f, bool enable);
|
||||
|
@ -45,8 +45,8 @@ static const OSystem::GraphicsMode s_supportedGraphicsModes[] = {
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
LinuxmotoSdlGraphicsManager::LinuxmotoSdlGraphicsManager(SdlEventSource *sdlEventSource)
|
||||
: SurfaceSdlGraphicsManager(sdlEventSource) {
|
||||
LinuxmotoSdlGraphicsManager::LinuxmotoSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window)
|
||||
: SurfaceSdlGraphicsManager(sdlEventSource, window) {
|
||||
}
|
||||
|
||||
const OSystem::GraphicsMode *LinuxmotoSdlGraphicsManager::getSupportedGraphicsModes() const {
|
||||
@ -134,7 +134,7 @@ void LinuxmotoSdlGraphicsManager::initSize(uint w, uint h) {
|
||||
if (w > 320 || h > 240) {
|
||||
setGraphicsMode(GFX_HALF);
|
||||
setGraphicsModeIntern();
|
||||
_eventSource->toggleMouseGrab();
|
||||
_window->toggleMouseGrab();
|
||||
}
|
||||
|
||||
_transactionDetails.sizeChanged = true;
|
||||
|
@ -27,7 +27,7 @@
|
||||
|
||||
class LinuxmotoSdlGraphicsManager : public SurfaceSdlGraphicsManager {
|
||||
public:
|
||||
LinuxmotoSdlGraphicsManager(SdlEventSource *sdlEventSource);
|
||||
LinuxmotoSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window);
|
||||
|
||||
virtual void initSize(uint w, uint h);
|
||||
virtual void setGraphicsModeIntern();
|
||||
|
@ -21,16 +21,14 @@
|
||||
*/
|
||||
#if defined(MAEMO)
|
||||
|
||||
#include "SDL_syswm.h"
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
#include "backends/platform/maemo/maemo.h"
|
||||
#include "backends/events/maemosdl/maemosdl-events.h"
|
||||
#include "backends/graphics/maemosdl/maemosdl-graphics.h"
|
||||
|
||||
MaemoSdlGraphicsManager::MaemoSdlGraphicsManager(SdlEventSource *sdlEventSource)
|
||||
: SurfaceSdlGraphicsManager(sdlEventSource) {
|
||||
MaemoSdlGraphicsManager::MaemoSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window)
|
||||
: SurfaceSdlGraphicsManager(sdlEventSource, window) {
|
||||
}
|
||||
|
||||
bool MaemoSdlGraphicsManager::loadGFXMode() {
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
class MaemoSdlGraphicsManager : public SurfaceSdlGraphicsManager {
|
||||
public:
|
||||
MaemoSdlGraphicsManager(SdlEventSource *sdlEventSource);
|
||||
MaemoSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window);
|
||||
|
||||
protected:
|
||||
virtual bool loadGFXMode();
|
||||
|
@ -49,9 +49,9 @@ OpenGLGraphicsManager::OpenGLGraphicsManager()
|
||||
_displayWidth(0), _displayHeight(0), _defaultFormat(), _defaultFormatAlpha(),
|
||||
_gameScreen(nullptr), _gameScreenShakeOffset(0), _overlay(nullptr),
|
||||
_overlayVisible(false), _cursor(nullptr),
|
||||
_cursorX(0), _cursorY(0), _cursorHotspotX(0), _cursorHotspotY(0), _cursorHotspotXScaled(0),
|
||||
_cursorHotspotYScaled(0), _cursorWidthScaled(0), _cursorHeightScaled(0), _cursorKeyColor(0),
|
||||
_cursorVisible(false), _cursorDontScale(false), _cursorPaletteEnabled(false)
|
||||
_cursorX(0), _cursorY(0), _cursorDisplayX(0),_cursorDisplayY(0), _cursorHotspotX(0), _cursorHotspotY(0),
|
||||
_cursorHotspotXScaled(0), _cursorHotspotYScaled(0), _cursorWidthScaled(0), _cursorHeightScaled(0),
|
||||
_cursorKeyColor(0), _cursorVisible(false), _cursorDontScale(false), _cursorPaletteEnabled(false)
|
||||
#ifdef USE_OSD
|
||||
, _osdAlpha(0), _osdFadeStartTime(0), _osd(nullptr)
|
||||
#endif
|
||||
@ -351,7 +351,7 @@ void OpenGLGraphicsManager::updateScreen() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear the screen buffer
|
||||
// Clear the screen buffer.
|
||||
GLCALL(glClear(GL_COLOR_BUFFER_BIT));
|
||||
|
||||
const GLfloat shakeOffset = _gameScreenShakeOffset * (GLfloat)_displayHeight / _gameScreen->getHeight();
|
||||
@ -370,12 +370,42 @@ void OpenGLGraphicsManager::updateScreen() {
|
||||
// visible.
|
||||
const GLfloat cursorOffset = _overlayVisible ? 0 : shakeOffset;
|
||||
|
||||
_cursor->draw(_cursorX - _cursorHotspotXScaled, _cursorY - _cursorHotspotYScaled + cursorOffset,
|
||||
_cursor->draw(_cursorDisplayX - _cursorHotspotXScaled,
|
||||
_cursorDisplayY - _cursorHotspotYScaled + cursorOffset,
|
||||
_cursorWidthScaled, _cursorHeightScaled);
|
||||
}
|
||||
|
||||
// Fourth step: Draw black borders around the game screen when no overlay
|
||||
// is visible. This makes sure that the mouse cursor etc. is only drawn
|
||||
// in the actual game screen area in this case.
|
||||
if (!_overlayVisible) {
|
||||
GLCALL(glColor4f(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
|
||||
GLCALL(glDisable(GL_TEXTURE_2D));
|
||||
GLCALL(glDisableClientState(GL_TEXTURE_COORD_ARRAY));
|
||||
|
||||
// Top border.
|
||||
drawRect(0, 0, _outputScreenWidth, _displayY);
|
||||
|
||||
// Left border.
|
||||
drawRect(0, 0, _displayX, _outputScreenHeight);
|
||||
|
||||
// Bottom border.
|
||||
const int y = _displayY + _displayHeight;
|
||||
drawRect(0, y, _outputScreenWidth, _outputScreenHeight - y);
|
||||
|
||||
// Right border.
|
||||
const int x = _displayX + _displayWidth;
|
||||
drawRect(x, 0, _outputScreenWidth - x, _outputScreenHeight);
|
||||
|
||||
GLCALL(glEnableClientState(GL_TEXTURE_COORD_ARRAY));
|
||||
GLCALL(glEnable(GL_TEXTURE_2D));
|
||||
|
||||
GLCALL(glColor4f(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
}
|
||||
|
||||
#ifdef USE_OSD
|
||||
// Fourth step: Draw the OSD.
|
||||
// Fifth step: Draw the OSD.
|
||||
if (_osdAlpha > 0) {
|
||||
Common::StackLock lock(_osdMutex);
|
||||
|
||||
@ -435,10 +465,16 @@ int16 OpenGLGraphicsManager::getOverlayHeight() {
|
||||
|
||||
void OpenGLGraphicsManager::showOverlay() {
|
||||
_overlayVisible = true;
|
||||
|
||||
// Update cursor position.
|
||||
setMousePosition(_cursorX, _cursorY);
|
||||
}
|
||||
|
||||
void OpenGLGraphicsManager::hideOverlay() {
|
||||
_overlayVisible = false;
|
||||
|
||||
// Update cursor position.
|
||||
setMousePosition(_cursorX, _cursorY);
|
||||
}
|
||||
|
||||
Graphics::PixelFormat OpenGLGraphicsManager::getOverlayFormat() const {
|
||||
@ -489,7 +525,7 @@ void OpenGLGraphicsManager::warpMouse(int x, int y) {
|
||||
if (!_overlay) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// It might be confusing that we actually have to handle something
|
||||
// here when the overlay is visible. This is because for very small
|
||||
// resolutions we have a minimal overlay size and have to adjust
|
||||
@ -892,8 +928,8 @@ void OpenGLGraphicsManager::adjustMousePosition(int16 &x, int16 &y) {
|
||||
const int16 width = _gameScreen->getWidth();
|
||||
const int16 height = _gameScreen->getHeight();
|
||||
|
||||
x = (x * width) / _displayWidth;
|
||||
y = (y * height) / _displayHeight;
|
||||
x = (x * width) / (int)_displayWidth;
|
||||
y = (y * height) / (int)_displayHeight;
|
||||
|
||||
// Make sure we only supply valid coordinates.
|
||||
x = CLIP<int16>(x, 0, width - 1);
|
||||
@ -901,6 +937,19 @@ void OpenGLGraphicsManager::adjustMousePosition(int16 &x, int16 &y) {
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLGraphicsManager::setMousePosition(int x, int y) {
|
||||
_cursorX = x;
|
||||
_cursorY = y;
|
||||
|
||||
if (_overlayVisible) {
|
||||
_cursorDisplayX = x;
|
||||
_cursorDisplayY = y;
|
||||
} else {
|
||||
_cursorDisplayX = CLIP<int>(x, _displayX, _displayX + _displayWidth - 1);
|
||||
_cursorDisplayY = CLIP<int>(y, _displayY, _displayY + _displayHeight - 1);
|
||||
}
|
||||
}
|
||||
|
||||
Texture *OpenGLGraphicsManager::createTexture(const Graphics::PixelFormat &format, bool wantAlpha) {
|
||||
GLenum glIntFormat, glFormat, glType;
|
||||
if (format.bytesPerPixel == 1) {
|
||||
@ -1044,8 +1093,11 @@ void OpenGLGraphicsManager::recalculateDisplayArea() {
|
||||
}
|
||||
|
||||
// We center the screen in the middle for now.
|
||||
_displayX = (_outputScreenWidth - _displayWidth ) / 2;
|
||||
_displayY = (_outputScreenHeight - _displayHeight) / 2;
|
||||
_displayX = (_outputScreenWidth - _displayWidth ) / 2;
|
||||
_displayY = (_outputScreenHeight - _displayHeight) / 2;
|
||||
|
||||
// Update the cursor position to adjust for new display area.
|
||||
setMousePosition(_cursorX, _cursorY);
|
||||
}
|
||||
|
||||
void OpenGLGraphicsManager::updateCursorPalette() {
|
||||
@ -1163,4 +1215,20 @@ void OpenGLGraphicsManager::saveScreenshot(const Common::String &filename) const
|
||||
delete[] pixels;
|
||||
}
|
||||
|
||||
void OpenGLGraphicsManager::drawRect(GLfloat x, GLfloat y, GLfloat w, GLfloat h) {
|
||||
if (w < 0 || h < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const GLfloat vertices[4*2] = {
|
||||
x, y,
|
||||
x + w, y,
|
||||
x, y + h,
|
||||
x + w, y + h
|
||||
};
|
||||
GLCALL(glVertexPointer(2, GL_FLOAT, 0, vertices));
|
||||
|
||||
GLCALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
|
||||
}
|
||||
|
||||
} // End of namespace OpenGL
|
||||
|
@ -155,7 +155,7 @@ protected:
|
||||
* @param x X coordinate in physical coordinates.
|
||||
* @param y Y coordinate in physical coordinates.
|
||||
*/
|
||||
void setMousePosition(int x, int y) { _cursorX = x; _cursorY = y; }
|
||||
void setMousePosition(int x, int y);
|
||||
|
||||
/**
|
||||
* Query the mouse position in physical coordinates.
|
||||
@ -393,6 +393,16 @@ private:
|
||||
*/
|
||||
int _cursorY;
|
||||
|
||||
/**
|
||||
* X coordinate used for drawing the cursor.
|
||||
*/
|
||||
int _cursorDisplayX;
|
||||
|
||||
/**
|
||||
* Y coordinate used for drawing the cursor.
|
||||
*/
|
||||
int _cursorDisplayY;
|
||||
|
||||
/**
|
||||
* The X offset for the cursor hotspot in unscaled coordinates.
|
||||
*/
|
||||
@ -454,6 +464,11 @@ private:
|
||||
*/
|
||||
byte _cursorPalette[3 * 256];
|
||||
|
||||
/**
|
||||
* Draws a rectangle
|
||||
*/
|
||||
void drawRect(GLfloat x, GLfloat y, GLfloat w, GLfloat h);
|
||||
|
||||
#ifdef USE_OSD
|
||||
//
|
||||
// OSD
|
||||
|
@ -28,8 +28,13 @@
|
||||
#include "common/translation.h"
|
||||
#endif
|
||||
|
||||
OpenGLSdlGraphicsManager::OpenGLSdlGraphicsManager(uint desktopWidth, uint desktopHeight, SdlEventSource *eventSource)
|
||||
: SdlGraphicsManager(eventSource), _lastVideoModeLoad(0), _hwScreen(nullptr), _lastRequestedWidth(0), _lastRequestedHeight(0),
|
||||
OpenGLSdlGraphicsManager::OpenGLSdlGraphicsManager(uint desktopWidth, uint desktopHeight, SdlEventSource *eventSource, SdlWindow *window)
|
||||
: SdlGraphicsManager(eventSource, window), _lastRequestedHeight(0),
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
_glContext(),
|
||||
#else
|
||||
_lastVideoModeLoad(0), _hwScreen(nullptr),
|
||||
#endif
|
||||
_graphicsScale(2), _ignoreLoadVideoMode(false), _gotResize(false), _wantsFullScreen(false), _ignoreResizeEvents(0),
|
||||
_desiredFullscreenWidth(0), _desiredFullscreenHeight(0) {
|
||||
// Setup OpenGL attributes for SDL
|
||||
@ -40,16 +45,40 @@ OpenGLSdlGraphicsManager::OpenGLSdlGraphicsManager(uint desktopWidth, uint deskt
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
|
||||
// Retrieve a list of working fullscreen modes
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
const int numModes = SDL_GetNumDisplayModes(0);
|
||||
for (int i = 0; i < numModes; ++i) {
|
||||
SDL_DisplayMode mode;
|
||||
if (SDL_GetDisplayMode(0, i, &mode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
_fullscreenVideoModes.push_back(VideoMode(mode.w, mode.h));
|
||||
}
|
||||
#else
|
||||
const SDL_Rect *const *availableModes = SDL_ListModes(NULL, SDL_OPENGL | SDL_FULLSCREEN);
|
||||
if (availableModes != (void *)-1) {
|
||||
// TODO: NULL means that there are no fullscreen modes supported. We
|
||||
// should probably use this information and disable any fullscreen support
|
||||
// in this case.
|
||||
if (availableModes != NULL && availableModes != (void *)-1) {
|
||||
for (;*availableModes; ++availableModes) {
|
||||
const SDL_Rect *mode = *availableModes;
|
||||
|
||||
_fullscreenVideoModes.push_back(VideoMode(mode->w, mode->h));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Sort the modes in ascending order.
|
||||
Common::sort(_fullscreenVideoModes.begin(), _fullscreenVideoModes.end());
|
||||
// Sort the modes in ascending order.
|
||||
Common::sort(_fullscreenVideoModes.begin(), _fullscreenVideoModes.end());
|
||||
|
||||
// Strip duplicates in video modes.
|
||||
for (uint i = 0; i + 1 < _fullscreenVideoModes.size();) {
|
||||
if (_fullscreenVideoModes[i] == _fullscreenVideoModes[i + 1]) {
|
||||
_fullscreenVideoModes.remove_at(i);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
// In case SDL is fine with every mode we will force the desktop mode.
|
||||
@ -108,7 +137,7 @@ void OpenGLSdlGraphicsManager::setFeatureState(OSystem::Feature f, bool enable)
|
||||
|
||||
case OSystem::kFeatureIconifyWindow:
|
||||
if (enable) {
|
||||
SDL_WM_IconifyWindow();
|
||||
_window->iconifyWindow();
|
||||
}
|
||||
break;
|
||||
|
||||
@ -120,11 +149,19 @@ void OpenGLSdlGraphicsManager::setFeatureState(OSystem::Feature f, bool enable)
|
||||
bool OpenGLSdlGraphicsManager::getFeatureState(OSystem::Feature f) {
|
||||
switch (f) {
|
||||
case OSystem::kFeatureFullscreenMode:
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
if (_window) {
|
||||
return (SDL_GetWindowFlags(_window->getSDLWindow()) & SDL_WINDOW_FULLSCREEN) != 0;
|
||||
} else {
|
||||
return _wantsFullScreen;
|
||||
}
|
||||
#else
|
||||
if (_hwScreen) {
|
||||
return (_hwScreen->flags & SDL_FULLSCREEN) != 0;
|
||||
} else {
|
||||
return _wantsFullScreen;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
return OpenGLGraphicsManager::getFeatureState(f);
|
||||
@ -201,13 +238,20 @@ void OpenGLSdlGraphicsManager::updateScreen() {
|
||||
OpenGLGraphicsManager::updateScreen();
|
||||
|
||||
// Swap OpenGL buffers
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
SDL_GL_SwapWindow(_window->getSDLWindow());
|
||||
#else
|
||||
SDL_GL_SwapBuffers();
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenGLSdlGraphicsManager::notifyVideoExpose() {
|
||||
}
|
||||
|
||||
void OpenGLSdlGraphicsManager::notifyResize(const uint width, const uint height) {
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
setActualScreenSize(width, height);
|
||||
#else
|
||||
if (!_ignoreResizeEvents && _hwScreen && !(_hwScreen->flags & SDL_FULLSCREEN)) {
|
||||
// We save that we handled a resize event here. We need to know this
|
||||
// so we do not overwrite the users requested window size whenever we
|
||||
@ -218,6 +262,7 @@ void OpenGLSdlGraphicsManager::notifyResize(const uint width, const uint height)
|
||||
g_system->quit();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenGLSdlGraphicsManager::transformMouseCoordinates(Common::Point &point) {
|
||||
@ -229,7 +274,7 @@ void OpenGLSdlGraphicsManager::notifyMousePos(Common::Point mouse) {
|
||||
}
|
||||
|
||||
void OpenGLSdlGraphicsManager::setInternalMousePosition(int x, int y) {
|
||||
SDL_WarpMouse(x, y);
|
||||
_window->warpMouseInWindow(x, y);
|
||||
}
|
||||
|
||||
bool OpenGLSdlGraphicsManager::loadVideoMode(uint requestedWidth, uint requestedHeight, const Graphics::PixelFormat &format) {
|
||||
@ -282,7 +327,7 @@ bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) {
|
||||
if (!_fullscreenVideoModes.empty()) {
|
||||
VideoModeArray::const_iterator i = _fullscreenVideoModes.end();
|
||||
--i;
|
||||
|
||||
|
||||
_desiredFullscreenWidth = i->width;
|
||||
_desiredFullscreenHeight = i->height;
|
||||
} else {
|
||||
@ -300,6 +345,58 @@ bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) {
|
||||
height = _desiredFullscreenHeight;
|
||||
}
|
||||
|
||||
// This is pretty confusing since RGBA8888 talks about the memory
|
||||
// layout here. This is a different logical layout depending on
|
||||
// whether we run on little endian or big endian. However, we can
|
||||
// only safely assume that RGBA8888 in memory layout is supported.
|
||||
// Thus, we chose this one.
|
||||
const Graphics::PixelFormat rgba8888 =
|
||||
#ifdef SCUMM_LITTLE_ENDIAN
|
||||
Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24);
|
||||
#else
|
||||
Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0);
|
||||
#endif
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
if (_glContext) {
|
||||
notifyContextDestroy();
|
||||
|
||||
SDL_GL_DeleteContext(_glContext);
|
||||
_glContext = nullptr;
|
||||
}
|
||||
|
||||
_window->destroyWindow();
|
||||
|
||||
uint32 flags = SDL_WINDOW_OPENGL;
|
||||
if (_wantsFullScreen) {
|
||||
flags |= SDL_WINDOW_FULLSCREEN;
|
||||
} else {
|
||||
flags |= SDL_WINDOW_RESIZABLE;
|
||||
}
|
||||
|
||||
if (!_window->createWindow(width, height, flags)) {
|
||||
// We treat fullscreen requests as a "hint" for now. This means in
|
||||
// case it is not available we simply ignore it.
|
||||
if (_wantsFullScreen) {
|
||||
_window->createWindow(width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
|
||||
}
|
||||
|
||||
if (!_window->getSDLWindow()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_glContext = SDL_GL_CreateContext(_window->getSDLWindow());
|
||||
if (!_glContext) {
|
||||
return false;
|
||||
}
|
||||
|
||||
notifyContextCreate(rgba8888, rgba8888);
|
||||
int actualWidth, actualHeight;
|
||||
getWindowDimensions(&actualWidth, &actualHeight);
|
||||
setActualScreenSize(actualWidth, actualHeight);
|
||||
return true;
|
||||
#else
|
||||
// WORKAROUND: Working around infamous SDL bugs when switching
|
||||
// resolutions too fast. This might cause the event system to supply
|
||||
// incorrect mouse position events otherwise.
|
||||
@ -341,17 +438,6 @@ bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) {
|
||||
_lastVideoModeLoad = SDL_GetTicks();
|
||||
|
||||
if (_hwScreen) {
|
||||
// This is pretty confusing since RGBA8888 talks about the memory
|
||||
// layout here. This is a different logical layout depending on
|
||||
// whether we run on little endian or big endian. However, we can
|
||||
// only safely assume that RGBA8888 in memory layout is supported.
|
||||
// Thus, we chose this one.
|
||||
const Graphics::PixelFormat rgba8888 =
|
||||
#ifdef SCUMM_LITTLE_ENDIAN
|
||||
Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24);
|
||||
#else
|
||||
Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0);
|
||||
#endif
|
||||
notifyContextCreate(rgba8888, rgba8888);
|
||||
setActualScreenSize(_hwScreen->w, _hwScreen->h);
|
||||
}
|
||||
@ -363,6 +449,21 @@ bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) {
|
||||
_ignoreResizeEvents = 10;
|
||||
|
||||
return _hwScreen != nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenGLSdlGraphicsManager::getWindowDimensions(int *width, int *height) {
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
SDL_GetWindowSize(_window->getSDLWindow(), width, height);
|
||||
#else
|
||||
if (width) {
|
||||
*width = _hwScreen->w;
|
||||
}
|
||||
|
||||
if (height) {
|
||||
*height = _hwScreen->h;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool OpenGLSdlGraphicsManager::notifyEvent(const Common::Event &event) {
|
||||
@ -456,7 +557,9 @@ bool OpenGLSdlGraphicsManager::notifyEvent(const Common::Event &event) {
|
||||
// Calculate the next scaling setting. We approximate the
|
||||
// current scale setting in case the user resized the
|
||||
// window. Then we apply the direction change.
|
||||
_graphicsScale = MAX<int>(_hwScreen->w / _lastRequestedWidth, _hwScreen->h / _lastRequestedHeight);
|
||||
int windowWidth = 0, windowHeight = 0;
|
||||
getWindowDimensions(&windowWidth, &windowHeight);
|
||||
_graphicsScale = MAX<int>(windowWidth / _lastRequestedWidth, windowHeight / _lastRequestedHeight);
|
||||
_graphicsScale = MAX<int>(_graphicsScale + direction, 1);
|
||||
|
||||
// Since we overwrite a user resize here we reset its
|
||||
@ -472,7 +575,9 @@ bool OpenGLSdlGraphicsManager::notifyEvent(const Common::Event &event) {
|
||||
}
|
||||
|
||||
#ifdef USE_OSD
|
||||
const Common::String osdMsg = Common::String::format("Resolution: %dx%d", _hwScreen->w, _hwScreen->h);
|
||||
int windowWidth = 0, windowHeight = 0;
|
||||
getWindowDimensions(&windowWidth, &windowHeight);
|
||||
const Common::String osdMsg = Common::String::format("Resolution: %dx%d", windowWidth, windowHeight);
|
||||
displayMessageOnOSD(osdMsg.c_str());
|
||||
#endif
|
||||
|
||||
|
@ -32,7 +32,7 @@
|
||||
|
||||
class OpenGLSdlGraphicsManager : public OpenGL::OpenGLGraphicsManager, public SdlGraphicsManager, public Common::EventObserver {
|
||||
public:
|
||||
OpenGLSdlGraphicsManager(uint desktopWidth, uint desktopHeight, SdlEventSource *eventSource);
|
||||
OpenGLSdlGraphicsManager(uint desktopWidth, uint desktopHeight, SdlEventSource *eventSource, SdlWindow *window);
|
||||
virtual ~OpenGLSdlGraphicsManager();
|
||||
|
||||
// GraphicsManager API
|
||||
@ -68,8 +68,14 @@ protected:
|
||||
private:
|
||||
bool setupMode(uint width, uint height);
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
SDL_GLContext _glContext;
|
||||
#else
|
||||
uint32 _lastVideoModeLoad;
|
||||
SDL_Surface *_hwScreen;
|
||||
#endif
|
||||
|
||||
void getWindowDimensions(int *width, int *height);
|
||||
|
||||
uint _lastRequestedWidth;
|
||||
uint _lastRequestedHeight;
|
||||
|
@ -32,8 +32,8 @@
|
||||
|
||||
static SDL_Cursor *hiddenCursor;
|
||||
|
||||
OPGraphicsManager::OPGraphicsManager(SdlEventSource *sdlEventSource)
|
||||
: SurfaceSdlGraphicsManager(sdlEventSource) {
|
||||
OPGraphicsManager::OPGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window)
|
||||
: SurfaceSdlGraphicsManager(sdlEventSource, window) {
|
||||
}
|
||||
|
||||
bool OPGraphicsManager::loadGFXMode() {
|
||||
|
@ -32,7 +32,7 @@ enum {
|
||||
|
||||
class OPGraphicsManager : public SurfaceSdlGraphicsManager {
|
||||
public:
|
||||
OPGraphicsManager(SdlEventSource *sdlEventSource);
|
||||
OPGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window);
|
||||
|
||||
bool loadGFXMode();
|
||||
void unloadGFXMode();
|
||||
|
@ -28,8 +28,8 @@
|
||||
#include "backends/events/samsungtvsdl/samsungtvsdl-events.h"
|
||||
#include "backends/graphics/samsungtvsdl/samsungtvsdl-graphics.h"
|
||||
|
||||
SamsungTVSdlGraphicsManager::SamsungTVSdlGraphicsManager(SdlEventSource *sdlEventSource)
|
||||
: SurfaceSdlGraphicsManager(sdlEventSource) {
|
||||
SamsungTVSdlGraphicsManager::SamsungTVSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window)
|
||||
: SurfaceSdlGraphicsManager(sdlEventSource, window) {
|
||||
}
|
||||
|
||||
bool SamsungTVSdlGraphicsManager::hasFeature(OSystem::Feature f) {
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
class SamsungTVSdlGraphicsManager : public SurfaceSdlGraphicsManager {
|
||||
public:
|
||||
SamsungTVSdlGraphicsManager(SdlEventSource *sdlEventSource);
|
||||
SamsungTVSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window);
|
||||
|
||||
bool hasFeature(OSystem::Feature f);
|
||||
void setFeatureState(OSystem::Feature f, bool enable);
|
||||
|
@ -22,10 +22,12 @@
|
||||
|
||||
#include "backends/graphics/sdl/sdl-graphics.h"
|
||||
|
||||
#include "backends/platform/sdl/sdl-sys.h"
|
||||
#include "backends/events/sdl/sdl-events.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
SdlGraphicsManager::SdlGraphicsManager(SdlEventSource *source)
|
||||
: _eventSource(source) {
|
||||
SdlGraphicsManager::SdlGraphicsManager(SdlEventSource *source, SdlWindow *window)
|
||||
: _eventSource(source), _window(window) {
|
||||
}
|
||||
|
||||
SdlGraphicsManager::~SdlGraphicsManager() {
|
||||
@ -38,3 +40,36 @@ void SdlGraphicsManager::activateManager() {
|
||||
void SdlGraphicsManager::deactivateManager() {
|
||||
_eventSource->setGraphicsManager(0);
|
||||
}
|
||||
|
||||
SdlGraphicsManager::State SdlGraphicsManager::getState() {
|
||||
State state;
|
||||
|
||||
state.screenWidth = getWidth();
|
||||
state.screenHeight = getHeight();
|
||||
state.aspectRatio = getFeatureState(OSystem::kFeatureAspectRatioCorrection);
|
||||
state.fullscreen = getFeatureState(OSystem::kFeatureFullscreenMode);
|
||||
state.cursorPalette = getFeatureState(OSystem::kFeatureCursorPalette);
|
||||
#ifdef USE_RGB_COLOR
|
||||
state.pixelFormat = getScreenFormat();
|
||||
#endif
|
||||
return state;
|
||||
}
|
||||
|
||||
bool SdlGraphicsManager::setState(const State &state) {
|
||||
beginGFXTransaction();
|
||||
#ifdef USE_RGB_COLOR
|
||||
initSize(state.screenWidth, state.screenHeight, &state.pixelFormat);
|
||||
#else
|
||||
initSize(state.screenWidth, state.screenHeight, 0);
|
||||
#endif
|
||||
setFeatureState(OSystem::kFeatureAspectRatioCorrection, state.aspectRatio);
|
||||
setFeatureState(OSystem::kFeatureFullscreenMode, state.fullscreen);
|
||||
setFeatureState(OSystem::kFeatureCursorPalette, state.cursorPalette);
|
||||
|
||||
if (endGFXTransaction() != OSystem::kTransactionSuccess) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
#define BACKENDS_GRAPHICS_SDL_SDLGRAPHICS_H
|
||||
|
||||
#include "backends/graphics/graphics.h"
|
||||
#include "backends/platform/sdl/sdl-window.h"
|
||||
|
||||
#include "common/rect.h"
|
||||
|
||||
@ -36,7 +37,7 @@ class SdlEventSource;
|
||||
*/
|
||||
class SdlGraphicsManager : virtual public GraphicsManager {
|
||||
public:
|
||||
SdlGraphicsManager(SdlEventSource *source);
|
||||
SdlGraphicsManager(SdlEventSource *source, SdlWindow *window);
|
||||
virtual ~SdlGraphicsManager();
|
||||
|
||||
/**
|
||||
@ -91,8 +92,39 @@ public:
|
||||
*/
|
||||
virtual void notifyMousePos(Common::Point mouse) = 0;
|
||||
|
||||
/**
|
||||
* A (subset) of the graphic manager's state. This is used when switching
|
||||
* between different SDL graphic managers on runtime.
|
||||
*/
|
||||
struct State {
|
||||
int screenWidth, screenHeight;
|
||||
bool aspectRatio;
|
||||
bool fullscreen;
|
||||
bool cursorPalette;
|
||||
|
||||
#ifdef USE_RGB_COLOR
|
||||
Graphics::PixelFormat pixelFormat;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* Queries the current state of the graphic manager.
|
||||
*/
|
||||
State getState();
|
||||
|
||||
/**
|
||||
* Setup a basic state of the graphic manager.
|
||||
*/
|
||||
bool setState(const State &state);
|
||||
|
||||
/**
|
||||
* Queries the SDL window.
|
||||
*/
|
||||
SdlWindow *getWindow() const { return _window; }
|
||||
|
||||
protected:
|
||||
SdlEventSource *_eventSource;
|
||||
SdlWindow *_window;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -116,13 +116,19 @@ static AspectRatio getDesiredAspectRatio() {
|
||||
}
|
||||
#endif
|
||||
|
||||
SurfaceSdlGraphicsManager::SurfaceSdlGraphicsManager(SdlEventSource *sdlEventSource)
|
||||
SurfaceSdlGraphicsManager::SurfaceSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window)
|
||||
:
|
||||
SdlGraphicsManager(sdlEventSource),
|
||||
SdlGraphicsManager(sdlEventSource, window),
|
||||
#ifdef USE_OSD
|
||||
_osdSurface(0), _osdAlpha(SDL_ALPHA_TRANSPARENT), _osdFadeStartTime(0),
|
||||
#endif
|
||||
_hwscreen(0), _screen(0), _tmpscreen(0),
|
||||
_hwscreen(0),
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
_renderer(nullptr), _screenTexture(nullptr),
|
||||
#else
|
||||
_originalBitsPerPixel(0),
|
||||
#endif
|
||||
_screen(0), _tmpscreen(0),
|
||||
#ifdef USE_RGB_COLOR
|
||||
_screenFormat(Graphics::PixelFormat::createFormatCLUT8()),
|
||||
_cursorFormat(Graphics::PixelFormat::createFormatCLUT8()),
|
||||
@ -235,7 +241,7 @@ void SurfaceSdlGraphicsManager::setFeatureState(OSystem::Feature f, bool enable)
|
||||
break;
|
||||
case OSystem::kFeatureIconifyWindow:
|
||||
if (enable)
|
||||
SDL_WM_IconifyWindow();
|
||||
_window->iconifyWindow();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -681,12 +687,22 @@ static void fixupResolutionForAspectRatio(AspectRatio desiredAspectRatio, int &w
|
||||
const int w = width;
|
||||
const int h = height;
|
||||
|
||||
int bestW = 0, bestH = 0;
|
||||
uint bestMetric = (uint)-1; // Metric is wasted space
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
const int numModes = SDL_GetNumDisplayModes(0);
|
||||
SDL_DisplayMode modeData, *mode = &modeData;
|
||||
for (int i = 0; i < numModes; ++i) {
|
||||
if (SDL_GetDisplayMode(0, i, &modeData)) {
|
||||
continue;
|
||||
}
|
||||
#else
|
||||
SDL_Rect const* const*availableModes = SDL_ListModes(NULL, SDL_FULLSCREEN|SDL_SWSURFACE); //TODO : Maybe specify a pixel format
|
||||
assert(availableModes);
|
||||
|
||||
const SDL_Rect *bestMode = NULL;
|
||||
uint bestMetric = (uint)-1; // Metric is wasted space
|
||||
while (const SDL_Rect *mode = *availableModes++) {
|
||||
#endif
|
||||
if (mode->w < w)
|
||||
continue;
|
||||
if (mode->h < h)
|
||||
@ -699,15 +715,23 @@ static void fixupResolutionForAspectRatio(AspectRatio desiredAspectRatio, int &w
|
||||
continue;
|
||||
|
||||
bestMetric = metric;
|
||||
bestMode = mode;
|
||||
}
|
||||
bestW = mode->w;
|
||||
bestH = mode->h;
|
||||
|
||||
if (!bestMode) {
|
||||
// Make editors a bit more happy by having the same amount of closing as
|
||||
// opening curley braces.
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
}
|
||||
#else
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!bestW || !bestH) {
|
||||
warning("Unable to enforce the desired aspect ratio");
|
||||
return;
|
||||
}
|
||||
width = bestMode->w;
|
||||
height = bestMode->h;
|
||||
width = bestW;
|
||||
height = bestH;
|
||||
}
|
||||
|
||||
bool SurfaceSdlGraphicsManager::loadGFXMode() {
|
||||
@ -774,7 +798,15 @@ bool SurfaceSdlGraphicsManager::loadGFXMode() {
|
||||
_hwscreen = g_eventRec.getSurface(_videoMode.hardwareWidth, _videoMode.hardwareHeight);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
{
|
||||
// Save the original bpp to be able to restore the video mode on unload
|
||||
#if !SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
if (_originalBitsPerPixel == 0) {
|
||||
const SDL_VideoInfo *videoInfo = SDL_GetVideoInfo();
|
||||
_originalBitsPerPixel = videoInfo->vfmt->BitsPerPixel;
|
||||
}
|
||||
#endif
|
||||
|
||||
_hwscreen = SDL_SetVideoMode(_videoMode.hardwareWidth, _videoMode.hardwareHeight, 16,
|
||||
_videoMode.fullscreen ? (SDL_FULLSCREEN|SDL_SWSURFACE) : SDL_SWSURFACE
|
||||
);
|
||||
@ -876,6 +908,10 @@ void SurfaceSdlGraphicsManager::unloadGFXMode() {
|
||||
_screen = NULL;
|
||||
}
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
deinitializeRenderer();
|
||||
#endif
|
||||
|
||||
if (_hwscreen) {
|
||||
SDL_FreeSurface(_hwscreen);
|
||||
_hwscreen = NULL;
|
||||
@ -903,6 +939,13 @@ void SurfaceSdlGraphicsManager::unloadGFXMode() {
|
||||
}
|
||||
#endif
|
||||
DestroyScalers();
|
||||
|
||||
#if !SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
// Reset video mode to original
|
||||
// This will ensure that any new graphic manager will use the initial BPP when listing available modes
|
||||
if (_originalBitsPerPixel != 0)
|
||||
SDL_SetVideoMode(_videoMode.screenWidth, _videoMode.screenHeight, _originalBitsPerPixel, _videoMode.fullscreen ? (SDL_FULLSCREEN | SDL_SWSURFACE) : SDL_SWSURFACE);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SurfaceSdlGraphicsManager::hotswapGFXMode() {
|
||||
@ -1443,6 +1486,9 @@ void SurfaceSdlGraphicsManager::setPalette(const byte *colors, uint start, uint
|
||||
base[i].r = b[0];
|
||||
base[i].g = b[1];
|
||||
base[i].b = b[2];
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
base[i].a = 255;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (start < _paletteDirtyStart)
|
||||
@ -1481,6 +1527,9 @@ void SurfaceSdlGraphicsManager::setCursorPalette(const byte *colors, uint start,
|
||||
base[i].r = b[0];
|
||||
base[i].g = b[1];
|
||||
base[i].b = b[2];
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
base[i].a = 255;
|
||||
#endif
|
||||
}
|
||||
|
||||
_cursorPaletteDisabled = false;
|
||||
@ -1710,7 +1759,7 @@ void SurfaceSdlGraphicsManager::warpMouse(int x, int y) {
|
||||
int y1 = y;
|
||||
|
||||
// Don't change actual mouse position, when mouse is outside of our window (in case of windowed mode)
|
||||
if (!(SDL_GetAppState( ) & SDL_APPMOUSEFOCUS)) {
|
||||
if (!_window->hasMouseFocus()) {
|
||||
setMousePos(x, y); // but change game cursor position
|
||||
return;
|
||||
}
|
||||
@ -1720,9 +1769,9 @@ void SurfaceSdlGraphicsManager::warpMouse(int x, int y) {
|
||||
|
||||
if (_mouseCurState.x != x || _mouseCurState.y != y) {
|
||||
if (!_overlayVisible)
|
||||
SDL_WarpMouse(x * _videoMode.scaleFactor, y1 * _videoMode.scaleFactor);
|
||||
_window->warpMouseInWindow(x * _videoMode.scaleFactor, y1 * _videoMode.scaleFactor);
|
||||
else
|
||||
SDL_WarpMouse(x, y1);
|
||||
_window->warpMouseInWindow(x, y1);
|
||||
|
||||
// SDL_WarpMouse() generates a mouse movement event, so
|
||||
// setMousePos() would be called eventually. However, the
|
||||
@ -2317,4 +2366,52 @@ void SurfaceSdlGraphicsManager::notifyMousePos(Common::Point mouse) {
|
||||
setMousePos(mouse.x, mouse.y);
|
||||
}
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
void SurfaceSdlGraphicsManager::deinitializeRenderer() {
|
||||
SDL_DestroyTexture(_screenTexture);
|
||||
_screenTexture = nullptr;
|
||||
|
||||
SDL_DestroyRenderer(_renderer);
|
||||
_renderer = nullptr;
|
||||
|
||||
_window->destroyWindow();
|
||||
}
|
||||
|
||||
SDL_Surface *SurfaceSdlGraphicsManager::SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags) {
|
||||
deinitializeRenderer();
|
||||
|
||||
if (!_window->createWindow(width, height, (flags & SDL_FULLSCREEN) ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
_renderer = SDL_CreateRenderer(_window->getSDLWindow(), -1, 0);
|
||||
if (!_renderer) {
|
||||
deinitializeRenderer();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
_screenTexture = SDL_CreateTexture(_renderer, SDL_PIXELFORMAT_RGB565, SDL_TEXTUREACCESS_STREAMING, width, height);
|
||||
if (!_screenTexture) {
|
||||
deinitializeRenderer();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SDL_Surface *screen = SDL_CreateRGBSurface(0, width, height, 16, 0xF800, 0x7E0, 0x1F, 0);
|
||||
if (!screen) {
|
||||
deinitializeRenderer();
|
||||
return nullptr;
|
||||
} else {
|
||||
return screen;
|
||||
}
|
||||
}
|
||||
|
||||
void SurfaceSdlGraphicsManager::SDL_UpdateRects(SDL_Surface *screen, int numrects, SDL_Rect *rects) {
|
||||
SDL_UpdateTexture(_screenTexture, nullptr, screen->pixels, screen->pitch);
|
||||
|
||||
SDL_RenderClear(_renderer);
|
||||
SDL_RenderCopy(_renderer, _screenTexture, NULL, NULL);
|
||||
SDL_RenderPresent(_renderer);
|
||||
}
|
||||
#endif // SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
|
||||
#endif
|
||||
|
@ -77,7 +77,7 @@ public:
|
||||
*/
|
||||
class SurfaceSdlGraphicsManager : public SdlGraphicsManager, public Common::EventObserver {
|
||||
public:
|
||||
SurfaceSdlGraphicsManager(SdlEventSource *sdlEventSource);
|
||||
SurfaceSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window);
|
||||
virtual ~SurfaceSdlGraphicsManager();
|
||||
|
||||
virtual void activateManager();
|
||||
@ -166,6 +166,17 @@ protected:
|
||||
/** Hardware screen */
|
||||
SDL_Surface *_hwscreen;
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
/* SDL2 features a different API for 2D graphics. We create a wrapper
|
||||
* around this API to keep the code paths as close as possible. */
|
||||
SDL_Renderer *_renderer;
|
||||
SDL_Texture *_screenTexture;
|
||||
void deinitializeRenderer();
|
||||
|
||||
SDL_Surface *SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags);
|
||||
void SDL_UpdateRects(SDL_Surface *screen, int numrects, SDL_Rect *rects);
|
||||
#endif
|
||||
|
||||
/** Unseen game screen */
|
||||
SDL_Surface *_screen;
|
||||
#ifdef USE_RGB_COLOR
|
||||
@ -225,6 +236,9 @@ protected:
|
||||
};
|
||||
VideoState _videoMode, _oldVideoMode;
|
||||
|
||||
// Original BPP to restore the video mode on unload
|
||||
uint8 _originalBitsPerPixel;
|
||||
|
||||
/** Force full redraw on next updateScreen */
|
||||
bool _forceFull;
|
||||
|
||||
|
@ -27,8 +27,8 @@
|
||||
#include "backends/graphics/symbiansdl/symbiansdl-graphics.h"
|
||||
#include "backends/platform/symbian/src/SymbianActions.h"
|
||||
|
||||
SymbianSdlGraphicsManager::SymbianSdlGraphicsManager(SdlEventSource *sdlEventSource)
|
||||
: SurfaceSdlGraphicsManager(sdlEventSource) {
|
||||
SymbianSdlGraphicsManager::SymbianSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window)
|
||||
: SurfaceSdlGraphicsManager(sdlEventSource, window) {
|
||||
}
|
||||
|
||||
int SymbianSdlGraphicsManager::getDefaultGraphicsMode() const {
|
||||
|
@ -27,7 +27,7 @@
|
||||
|
||||
class SymbianSdlGraphicsManager : public SurfaceSdlGraphicsManager {
|
||||
public:
|
||||
SymbianSdlGraphicsManager(SdlEventSource *sdlEventSource);
|
||||
SymbianSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window);
|
||||
|
||||
public:
|
||||
virtual bool hasFeature(OSystem::Feature f);
|
||||
|
@ -42,8 +42,8 @@
|
||||
#include "backends/platform/wince/CEScaler.h"
|
||||
#include "backends/platform/wince/CEgui/ItemAction.h"
|
||||
|
||||
WINCESdlGraphicsManager::WINCESdlGraphicsManager(SdlEventSource *sdlEventSource)
|
||||
: SurfaceSdlGraphicsManager(sdlEventSource),
|
||||
WINCESdlGraphicsManager::WINCESdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window)
|
||||
: SurfaceSdlGraphicsManager(sdlEventSource, window),
|
||||
_panelInitialized(false), _noDoubleTapRMB(false), _noDoubleTapPT(false),
|
||||
_toolbarHighDrawn(false), _newOrientation(0), _orientationLandscape(0),
|
||||
_panelVisible(true), _saveActiveToolbar(NAME_MAIN_PANEL), _panelStateForced(false),
|
||||
|
@ -41,7 +41,7 @@ extern bool _hasSmartphoneResolution;
|
||||
|
||||
class WINCESdlGraphicsManager : public SurfaceSdlGraphicsManager {
|
||||
public:
|
||||
WINCESdlGraphicsManager(SdlEventSource *sdlEventSource);
|
||||
WINCESdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window);
|
||||
|
||||
const OSystem::GraphicsMode *getSupportedGraphicsModes() const;
|
||||
void initSize(uint w, uint h, const Graphics::PixelFormat *format = NULL);
|
||||
|
@ -37,7 +37,7 @@ namespace Common {
|
||||
struct HardwareInput;
|
||||
class Keymap;
|
||||
|
||||
#define ACTION_ID_SIZE (4)
|
||||
#define ACTION_ID_SIZE (5)
|
||||
|
||||
struct KeyActionEntry {
|
||||
const KeyState ks;
|
||||
|
@ -93,10 +93,12 @@ void Log::print(const char *message, const bool printTime) {
|
||||
|
||||
void Log::printTimeStamp() {
|
||||
TimeDate date;
|
||||
int curMonth;
|
||||
_system->getTimeAndDate(date);
|
||||
curMonth = date.tm_mon + 1; // month is base 0, we need base 1 (1 = january and so on)
|
||||
|
||||
_stream->writeString(Common::String::format("[%d-%02d-%02d %02d:%02d:%02d] ",
|
||||
date.tm_year + 1900, date.tm_mon, date.tm_mday,
|
||||
date.tm_year + 1900, curMonth, date.tm_mday,
|
||||
date.tm_hour, date.tm_min, date.tm_sec));
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,11 @@ void DoubleBufferSDLMixerManager::startAudio() {
|
||||
_soundThreadIsRunning = true;
|
||||
|
||||
// Finally start the thread
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
_soundThread = SDL_CreateThread(mixerProducerThreadEntry, "ScummVM Double Buffer Mixer", this);
|
||||
#else
|
||||
_soundThread = SDL_CreateThread(mixerProducerThreadEntry, this);
|
||||
#endif
|
||||
|
||||
SdlMixerManager::startAudio();
|
||||
}
|
||||
|
@ -57,10 +57,14 @@ void SdlMixerManager::init() {
|
||||
error("Could not initialize SDL: %s", SDL_GetError());
|
||||
}
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
const char *sdlDriverName = SDL_GetCurrentAudioDriver();
|
||||
#else
|
||||
const int maxNameLen = 20;
|
||||
char sdlDriverName[maxNameLen];
|
||||
sdlDriverName[0] = '\0';
|
||||
SDL_AudioDriverName(sdlDriverName, maxNameLen);
|
||||
#endif
|
||||
debug(1, "Using SDL Audio Driver \"%s\"", sdlDriverName);
|
||||
|
||||
// Get the desired audio specs
|
||||
|
@ -74,9 +74,11 @@ MODULE_OBJS += \
|
||||
|
||||
# SDL 1.3 removed audio CD support
|
||||
ifndef USE_SDL13
|
||||
ifndef USE_SDL2
|
||||
MODULE_OBJS += \
|
||||
audiocd/sdl/sdl-audiocd.o
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef USE_OPENGL
|
||||
MODULE_OBJS += \
|
||||
|
@ -396,12 +396,6 @@ void OSystem_Android::initBackend() {
|
||||
EventsBaseBackend::initBackend();
|
||||
}
|
||||
|
||||
void OSystem_Android::addPluginDirectories(Common::FSList &dirs) const {
|
||||
ENTER();
|
||||
|
||||
JNI::getPluginDirectories(dirs);
|
||||
}
|
||||
|
||||
bool OSystem_Android::hasFeature(Feature f) {
|
||||
return (f == kFeatureFullscreenMode ||
|
||||
f == kFeatureAspectRatioCorrection ||
|
||||
@ -600,10 +594,4 @@ Common::String OSystem_Android::getSystemProperty(const char *name) const {
|
||||
return Common::String(value, len);
|
||||
}
|
||||
|
||||
#ifdef DYNAMIC_MODULES
|
||||
void AndroidPluginProvider::addCustomDirectories(Common::FSList &dirs) const {
|
||||
((OSystem_Android *)g_system)->addPluginDirectories(dirs);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -96,13 +96,6 @@ extern void checkGlError(const char *expr, const char *file, int line);
|
||||
#define GLTHREADCHECK do { } while (false)
|
||||
#endif
|
||||
|
||||
#ifdef DYNAMIC_MODULES
|
||||
class AndroidPluginProvider : public POSIXPluginProvider {
|
||||
protected:
|
||||
virtual void addCustomDirectories(Common::FSList &dirs) const;
|
||||
};
|
||||
#endif
|
||||
|
||||
class OSystem_Android : public EventsBaseBackend, public PaletteManager {
|
||||
private:
|
||||
// passed from the dark side
|
||||
@ -177,7 +170,6 @@ public:
|
||||
virtual ~OSystem_Android();
|
||||
|
||||
virtual void initBackend();
|
||||
void addPluginDirectories(Common::FSList &dirs) const;
|
||||
void enableZoning(bool enable) { _enable_zoning = enable; }
|
||||
|
||||
virtual bool hasFeature(Feature f);
|
||||
|
@ -2,200 +2,106 @@
|
||||
|
||||
# These must be incremented for each market upload
|
||||
ANDROID_VERSIONCODE = 6
|
||||
ANDROID_PLUGIN_VERSIONCODE = 6
|
||||
|
||||
JAVA_FILES = \
|
||||
ScummVM.java \
|
||||
ScummVMEvents.java \
|
||||
ScummVMEventsHoneycomb.java \
|
||||
ScummVMApplication.java \
|
||||
ScummVMActivity.java \
|
||||
EditableSurfaceView.java \
|
||||
MouseHelper.java \
|
||||
Unpacker.java
|
||||
ANDROID_TARGET_VERSION = 14
|
||||
|
||||
JAVA_FILES_PLUGIN = \
|
||||
PluginProvider.java
|
||||
|
||||
JAVA_FILES_GEN = \
|
||||
Manifest.java \
|
||||
R.java
|
||||
NDK_BUILD = $(ANDROID_NDK)/ndk-build APP_ABI=$(ABI)
|
||||
SDK_ANDROID = $(ANDROID_SDK)/tools/android
|
||||
|
||||
PATH_DIST = $(srcdir)/dists/android
|
||||
PATH_RESOURCES = $(PATH_DIST)/res
|
||||
|
||||
PORT_DISTFILES = $(PATH_DIST)/README.Android
|
||||
DIST_JAVA_SRC_DIR = $(srcdir)/backends/platform/android/org
|
||||
|
||||
RESOURCES = \
|
||||
$(PATH_RESOURCES)/values/strings.xml \
|
||||
$(PATH_RESOURCES)/values/margins.xml \
|
||||
$(PATH_RESOURCES)/values-television/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-xhdpi/ouya_icon.png
|
||||
$(PATH_BUILD_RES)/values/strings.xml \
|
||||
$(PATH_BUILD_RES)/values-television/margins.xml \
|
||||
$(PATH_BUILD_RES)/layout/main.xml \
|
||||
$(PATH_BUILD_RES)/drawable/scummvm.png \
|
||||
$(PATH_BUILD_RES)/drawable/scummvm_big.png \
|
||||
$(PATH_BUILD_RES)/drawable-xhdpi/ouya_icon.png
|
||||
|
||||
PLUGIN_RESOURCES = \
|
||||
$(PATH_RESOURCES)/values/strings.xml \
|
||||
$(PATH_RESOURCES)/drawable/scummvm.png
|
||||
|
||||
# FIXME: find/mark plugin entry points and add all this back again:
|
||||
#LDFLAGS += -Wl,--gc-sections
|
||||
#CXXFLAGS += -ffunction-sections -fdata-sections -fvisibility=hidden -fvisibility-inlines-hidden
|
||||
|
||||
AAPT = $(ANDROID_SDK)/$(ANDROID_BTOOLS)/aapt
|
||||
ADB = $(ANDROID_SDK)/platform-tools/adb
|
||||
DX = $(ANDROID_SDK)/$(ANDROID_BTOOLS)/dx
|
||||
APKBUILDER = java -Xmx128M -classpath $(ANDROID_SDK)/tools/lib/sdklib.jar com.android.sdklib.build.ApkBuilderMain
|
||||
JAVAC ?= javac
|
||||
JAVACFLAGS = -source 1.5 -target 1.5
|
||||
|
||||
ANDROID_JAR = $(ANDROID_SDK)/platforms/android-14/android.jar
|
||||
DIST_ANDROID_MK = $(PATH_DIST)/jni/Android.mk
|
||||
DIST_BUILD_XML = $(PATH_DIST)/custom_rules.xml
|
||||
|
||||
PATH_BUILD = ./build.tmp
|
||||
PATH_BUILD_ASSETS = $(PATH_BUILD)/assets
|
||||
PATH_BUILD_CLASSES_MAIN_TOP = $(PATH_BUILD)/classes.main
|
||||
PATH_BUILD_CLASSES_PLUGIN_TOP = $(PATH_BUILD)/classes.plugin
|
||||
|
||||
PATH_STAGE_PREFIX = build.stage
|
||||
PATH_STAGE_MAIN = $(PATH_STAGE_PREFIX).main
|
||||
|
||||
PATH_REL = org/scummvm/scummvm
|
||||
PATH_SRC_TOP = $(srcdir)/backends/platform/android
|
||||
PATH_SRC = $(PATH_SRC_TOP)/$(PATH_REL)
|
||||
|
||||
PATH_GEN_TOP = $(PATH_BUILD)/java
|
||||
PATH_GEN = $(PATH_GEN_TOP)/$(PATH_REL)
|
||||
PATH_CLASSES_MAIN = $(PATH_BUILD_CLASSES_MAIN_TOP)/$(PATH_REL)
|
||||
PATH_CLASSES_PLUGIN = $(PATH_BUILD_CLASSES_PLUGIN_TOP)/$(PATH_REL)
|
||||
PATH_BUILD_RES = $(PATH_BUILD)/res
|
||||
PATH_BUILD_LIBSCUMMVM = $(PATH_BUILD)/lib/$(ABI)/libscummvm.so
|
||||
|
||||
FILE_MANIFEST_SRC = $(srcdir)/dists/android/AndroidManifest.xml
|
||||
FILE_MANIFEST = $(PATH_BUILD)/AndroidManifest.xml
|
||||
FILE_DEX = $(PATH_BUILD)/classes.dex
|
||||
FILE_DEX_PLUGIN = $(PATH_BUILD)/plugins/classes.dex
|
||||
FILE_RESOURCES = resources.ap_
|
||||
FILE_RESOURCES_MAIN = $(PATH_BUILD)/$(FILE_RESOURCES)
|
||||
|
||||
SRC_GEN = $(addprefix $(PATH_GEN)/, $(JAVA_FILES_GEN))
|
||||
APK_MAIN = ScummVM-debug.apk
|
||||
APK_MAIN_RELEASE = ScummVM-release-unsigned.apk
|
||||
|
||||
CLASSES_MAIN = $(addprefix $(PATH_CLASSES_MAIN)/, $(JAVA_FILES:%.java=%.class))
|
||||
CLASSES_GEN = $(addprefix $(PATH_CLASSES_MAIN)/, $(JAVA_FILES_GEN:%.java=%.class))
|
||||
CLASSES_PLUGIN = $(addprefix $(PATH_CLASSES_PLUGIN)/, $(JAVA_FILES_PLUGIN:%.java=%.class))
|
||||
|
||||
APK_MAIN = scummvm.apk
|
||||
APK_PLUGINS = $(patsubst plugins/lib%.so, scummvm-engine-%.apk, $(PLUGINS))
|
||||
|
||||
$(FILE_MANIFEST): $(FILE_MANIFEST_SRC)
|
||||
$(FILE_MANIFEST): $(FILE_MANIFEST_SRC) | $(PATH_BUILD)
|
||||
@$(MKDIR) -p $(@D)
|
||||
sed "s/@ANDROID_VERSIONCODE@/$(ANDROID_VERSIONCODE)/" < $< > $@
|
||||
|
||||
$(SRC_GEN): $(FILE_MANIFEST) $(filter %.xml,$(RESOURCES)) $(ANDROID_JAR)
|
||||
@$(MKDIR) -p $(PATH_GEN_TOP)
|
||||
$(AAPT) package -m -J $(PATH_GEN_TOP) -M $< -S $(PATH_RESOURCES) -I $(ANDROID_JAR)
|
||||
|
||||
$(PATH_CLASSES_MAIN)/%.class: $(PATH_GEN)/%.java $(SRC_GEN)
|
||||
@$(MKDIR) -p $(@D)
|
||||
$(JAVAC) $(JAVACFLAGS) -cp $(PATH_SRC_TOP) -d $(PATH_BUILD_CLASSES_MAIN_TOP) -bootclasspath $(ANDROID_JAR) $<
|
||||
|
||||
$(PATH_CLASSES_MAIN)/%.class: $(PATH_SRC)/%.java $(SRC_GEN)
|
||||
@$(MKDIR) -p $(@D)
|
||||
$(JAVAC) $(JAVACFLAGS) -cp $(PATH_SRC_TOP):$(PATH_GEN_TOP) -d $(PATH_BUILD_CLASSES_MAIN_TOP) -bootclasspath $(ANDROID_JAR) $<
|
||||
|
||||
$(PATH_CLASSES_PLUGIN)/%.class: $(PATH_SRC)/%.java
|
||||
@$(MKDIR) -p $(@D)
|
||||
$(JAVAC) $(JAVACFLAGS) -cp $(PATH_SRC_TOP) -d $(PATH_BUILD_CLASSES_PLUGIN_TOP) -bootclasspath $(ANDROID_JAR) $<
|
||||
|
||||
$(FILE_DEX): $(CLASSES_MAIN) $(CLASSES_GEN)
|
||||
$(DX) --dex --output=$@ $(PATH_BUILD_CLASSES_MAIN_TOP)
|
||||
|
||||
$(FILE_DEX_PLUGIN): $(CLASSES_PLUGIN)
|
||||
@$(MKDIR) -p $(@D)
|
||||
$(DX) --dex --output=$@ $(PATH_BUILD_CLASSES_PLUGIN_TOP)
|
||||
|
||||
$(PATH_BUILD)/%/AndroidManifest.xml: $(PATH_DIST)/mkplugin.sh $(srcdir)/configure $(PATH_DIST)/plugin-manifest.xml
|
||||
@$(MKDIR) -p $(@D)
|
||||
$(PATH_DIST)/mkplugin.sh $(srcdir)/configure $* $(PATH_DIST)/plugin-manifest.xml $(ANDROID_PLUGIN_VERSIONCODE) $@
|
||||
|
||||
$(PATH_STAGE_PREFIX).%/res/values/strings.xml: $(PATH_DIST)/mkplugin.sh $(srcdir)/configure $(PATH_DIST)/plugin-manifest.xml
|
||||
@$(MKDIR) -p $(@D)
|
||||
$(PATH_DIST)/mkplugin.sh $(srcdir)/configure $* $(PATH_DIST)/plugin-strings.xml $(ANDROID_PLUGIN_VERSIONCODE) $@
|
||||
|
||||
$(PATH_STAGE_PREFIX).%/res/drawable/scummvm.png: $(PATH_RESOURCES)/drawable/scummvm.png
|
||||
$(PATH_BUILD)/res/%: $(PATH_DIST)/res/% | $(PATH_BUILD)
|
||||
@$(MKDIR) -p $(@D)
|
||||
$(CP) $< $@
|
||||
|
||||
$(FILE_RESOURCES_MAIN): $(FILE_MANIFEST) $(RESOURCES) $(ANDROID_JAR) $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA)
|
||||
$(PATH_BUILD)/libs/%: $(PATH_DIST)/libs/% | $(PATH_BUILD)
|
||||
@$(MKDIR) -p $(@D)
|
||||
$(CP) $< $@
|
||||
|
||||
$(PATH_BUILD_ASSETS): $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) $(DIST_FILES_SHADERS) $(DIST_BUILD_XML) | $(PATH_BUILD)
|
||||
$(INSTALL) -d $(PATH_BUILD_ASSETS)
|
||||
$(INSTALL) -c -m 644 $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) $(PATH_BUILD_ASSETS)/
|
||||
work_dir=`pwd`; \
|
||||
for i in $(PATH_BUILD_ASSETS)/*.zip; do \
|
||||
echo "recompress $$i"; \
|
||||
cd "$$work_dir"; \
|
||||
$(RM) -rf $(PATH_BUILD_ASSETS)/tmp; \
|
||||
$(MKDIR) $(PATH_BUILD_ASSETS)/tmp; \
|
||||
unzip -q $$i -d $(PATH_BUILD_ASSETS)/tmp; \
|
||||
cd $(PATH_BUILD_ASSETS)/tmp; \
|
||||
zip -r ../`basename $$i` *; \
|
||||
done
|
||||
@$(RM) -rf $(PATH_BUILD_ASSETS)/tmp
|
||||
$(AAPT) package -f -0 zip -M $< -S $(PATH_RESOURCES) -A $(PATH_BUILD_ASSETS) -I $(ANDROID_JAR) -F $@
|
||||
$(INSTALL) -d $(PATH_BUILD)/jni
|
||||
$(INSTALL) -c -m 644 $(DIST_ANDROID_MK) $(PATH_BUILD)/jni
|
||||
$(INSTALL) -c -m 644 $(DIST_BUILD_XML) $(PATH_BUILD)
|
||||
|
||||
$(PATH_BUILD)/%/$(FILE_RESOURCES): $(PATH_BUILD)/%/AndroidManifest.xml $(PATH_STAGE_PREFIX).%/res/values/strings.xml $(PATH_STAGE_PREFIX).%/res/drawable/scummvm.png plugins/lib%.so $(ANDROID_JAR)
|
||||
$(AAPT) package -f -M $< -S $(PATH_STAGE_PREFIX).$*/res -I $(ANDROID_JAR) -F $@
|
||||
$(PATH_BUILD): $(DIST_ANDROID_MK)
|
||||
$(MKDIR) -p $(PATH_BUILD) $(PATH_BUILD)/res
|
||||
$(MKDIR) -p $(PATH_BUILD)/libs
|
||||
|
||||
# Package installer won't delete old libscummvm.so on upgrade so
|
||||
# replace it with a zero size file
|
||||
$(APK_MAIN): $(EXECUTABLE) $(FILE_RESOURCES_MAIN) $(FILE_DEX)
|
||||
$(INSTALL) -d $(PATH_STAGE_MAIN)/common/lib/armeabi
|
||||
touch $(PATH_STAGE_MAIN)/common/lib/armeabi/libscummvm.so
|
||||
$(INSTALL) -d $(PATH_STAGE_MAIN)/common/mylib/armeabi
|
||||
$(INSTALL) -c -m 644 libscummvm.so $(PATH_STAGE_MAIN)/common/mylib/armeabi/
|
||||
$(STRIP) $(PATH_STAGE_MAIN)/common/mylib/armeabi/libscummvm.so
|
||||
$(APKBUILDER) $@ -z $(FILE_RESOURCES_MAIN) -f $(FILE_DEX) -rf $(PATH_STAGE_MAIN)/common || { $(RM) $@; exit 1; }
|
||||
$(PATH_BUILD_LIBSCUMMVM): libscummvm.so | $(PATH_BUILD)
|
||||
$(INSTALL) -c -m 644 libscummvm.so $(PATH_BUILD)
|
||||
$(STRIP) $(PATH_BUILD)/libscummvm.so
|
||||
cd $(PATH_BUILD); $(NDK_BUILD)
|
||||
|
||||
scummvm-engine-%.apk: plugins/lib%.so $(PATH_BUILD)/%/$(FILE_RESOURCES) $(FILE_DEX_PLUGIN)
|
||||
$(INSTALL) -d $(PATH_STAGE_PREFIX).$*/apk/mylib/armeabi/
|
||||
$(INSTALL) -c -m 644 plugins/lib$*.so $(PATH_STAGE_PREFIX).$*/apk/mylib/armeabi/
|
||||
$(STRIP) $(PATH_STAGE_PREFIX).$*/apk/mylib/armeabi/lib$*.so
|
||||
$(APKBUILDER) $@ -z $(PATH_BUILD)/$*/$(FILE_RESOURCES) -f $(FILE_DEX_PLUGIN) -rf $(PATH_STAGE_PREFIX).$*/apk || { $(RM) $@; exit 1; }
|
||||
$(PATH_BUILD_RES): $(RESOURCES) | $(PATH_BUILD)
|
||||
|
||||
all: $(APK_MAIN) $(APK_PLUGINS)
|
||||
setupapk: $(FILE_MANIFEST) $(PATH_BUILD_RES) $(PATH_BUILD_ASSETS) $(PATH_BUILD_LIBSCUMMVM) | $(PATH_BUILD)
|
||||
$(SDK_ANDROID) update project -p $(PATH_BUILD) -t android-$(ANDROID_TARGET_VERSION) -n ScummVM
|
||||
|
||||
$(APK_MAIN): setupapk | $(PATH_BUILD)
|
||||
(cd $(PATH_BUILD); ant debug -Dsource.dir="$(realpath $(DIST_JAVA_SRC_DIR))")
|
||||
$(CP) $(PATH_BUILD)/bin/ScummVM-debug.apk $@
|
||||
|
||||
$(APK_MAIN_RELEASE): setupapk | $(PATH_BUILD)
|
||||
(cd $(PATH_BUILD); ant release -Dsource.dir="$(realpath $(DIST_JAVA_SRC_DIR))")
|
||||
$(CP) $(PATH_BUILD)/bin/ScummVM-release-unsigned.apk $@
|
||||
|
||||
all: $(APK_MAIN)
|
||||
|
||||
clean: androidclean
|
||||
|
||||
androidclean:
|
||||
@$(RM) -rf $(PATH_BUILD) $(PATH_STAGE_PREFIX).* *.apk release
|
||||
@$(RM) -rf $(PATH_BUILD) *.apk release
|
||||
|
||||
# remove debugging signature
|
||||
release/%.apk: %.apk
|
||||
@$(MKDIR) -p $(@D)
|
||||
@$(RM) $@
|
||||
$(CP) $< $@.tmp
|
||||
zip -d $@.tmp META-INF/\*
|
||||
jarsigner $(JARSIGNER_FLAGS) $@.tmp release
|
||||
zipalign 4 $@.tmp $@
|
||||
$(RM) $@.tmp
|
||||
|
||||
androidrelease: $(addprefix release/, $(APK_MAIN) $(APK_PLUGINS))
|
||||
androidrelease: $(APK_MAIN_RELEASE)
|
||||
|
||||
androidtestmain: $(APK_MAIN)
|
||||
$(ADB) install -r $(APK_MAIN)
|
||||
$(ADB) shell am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -n org.scummvm.scummvm/.Unpacker
|
||||
$(ADB) shell am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -n org.scummvm.scummvm/.ScummVMActivity
|
||||
|
||||
androidtest: $(APK_MAIN) $(APK_PLUGINS)
|
||||
androidtest: $(APK_MAIN)
|
||||
@set -e; for apk in $^; do \
|
||||
$(ADB) install -r $$apk; \
|
||||
done
|
||||
$(ADB) shell am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -n org.scummvm.scummvm/.Unpacker
|
||||
$(ADB) shell am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -n org.scummvm.scummvm/.ScummVMActivity
|
||||
|
||||
# used by buildbot!
|
||||
androiddistdebug: all
|
||||
$(MKDIR) debug
|
||||
$(CP) $(APK_MAIN) $(APK_PLUGINS) debug/
|
||||
$(CP) $(APK_MAIN) debug/
|
||||
for i in $(DIST_FILES_DOCS) $(PORT_DISTFILES); do \
|
||||
sed 's/$$/\r/' < $$i > debug/`basename $$i`.txt; \
|
||||
done
|
||||
|
||||
.PHONY: androidrelease androidtest
|
||||
.PHONY: androidrelease androidtest $(PATH_BUILD_SRC)
|
||||
|
@ -295,7 +295,6 @@ AssetFdReadStream::AssetFdReadStream(JNIEnv *env, jobject assetfd) :
|
||||
jclass cls = env->GetObjectClass(_assetfd);
|
||||
MID_close = env->GetMethodID(cls, "close", "()V");
|
||||
assert(MID_close);
|
||||
env->DeleteLocalRef(cls);
|
||||
|
||||
jmethodID MID_getStartOffset =
|
||||
env->GetMethodID(cls, "getStartOffset", "()J");
|
||||
@ -321,6 +320,8 @@ AssetFdReadStream::AssetFdReadStream(JNIEnv *env, jobject assetfd) :
|
||||
|
||||
_fd = env->GetIntField(javafd, FID_descriptor);
|
||||
env->DeleteLocalRef(javafd);
|
||||
|
||||
env->DeleteLocalRef(cls);
|
||||
}
|
||||
|
||||
AssetFdReadStream::~AssetFdReadStream() {
|
||||
|
@ -79,7 +79,6 @@ jmethodID JNI::_MID_displayMessageOnOSD = 0;
|
||||
jmethodID JNI::_MID_setWindowCaption = 0;
|
||||
jmethodID JNI::_MID_showVirtualKeyboard = 0;
|
||||
jmethodID JNI::_MID_getSysArchives = 0;
|
||||
jmethodID JNI::_MID_getPluginDirectories = 0;
|
||||
jmethodID JNI::_MID_initSurface = 0;
|
||||
jmethodID JNI::_MID_deinitSurface = 0;
|
||||
|
||||
@ -293,46 +292,6 @@ void JNI::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) {
|
||||
}
|
||||
}
|
||||
|
||||
void JNI::getPluginDirectories(Common::FSList &dirs) {
|
||||
JNIEnv *env = JNI::getEnv();
|
||||
|
||||
jobjectArray array =
|
||||
(jobjectArray)env->CallObjectMethod(_jobj, _MID_getPluginDirectories);
|
||||
|
||||
if (env->ExceptionCheck()) {
|
||||
LOGE("Error finding plugin directories");
|
||||
|
||||
env->ExceptionDescribe();
|
||||
env->ExceptionClear();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
jsize size = env->GetArrayLength(array);
|
||||
for (jsize i = 0; i < size; ++i) {
|
||||
jstring path_obj = (jstring)env->GetObjectArrayElement(array, i);
|
||||
|
||||
if (path_obj == 0)
|
||||
continue;
|
||||
|
||||
const char *path = env->GetStringUTFChars(path_obj, 0);
|
||||
|
||||
if (path == 0) {
|
||||
LOGE("Error getting string characters from plugin directory");
|
||||
|
||||
env->ExceptionClear();
|
||||
env->DeleteLocalRef(path_obj);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
dirs.push_back(Common::FSNode(path));
|
||||
|
||||
env->ReleaseStringUTFChars(path_obj, path);
|
||||
env->DeleteLocalRef(path_obj);
|
||||
}
|
||||
}
|
||||
|
||||
bool JNI::initSurface() {
|
||||
JNIEnv *env = JNI::getEnv();
|
||||
|
||||
@ -454,7 +413,6 @@ void JNI::create(JNIEnv *env, jobject self, jobject asset_manager,
|
||||
FIND_METHOD(, displayMessageOnOSD, "(Ljava/lang/String;)V");
|
||||
FIND_METHOD(, showVirtualKeyboard, "(Z)V");
|
||||
FIND_METHOD(, getSysArchives, "()[Ljava/lang/String;");
|
||||
FIND_METHOD(, getPluginDirectories, "()[Ljava/lang/String;");
|
||||
FIND_METHOD(, initSurface, "()Ljavax/microedition/khronos/egl/EGLSurface;");
|
||||
FIND_METHOD(, deinitSurface, "()V");
|
||||
|
||||
@ -543,10 +501,6 @@ jint JNI::main(JNIEnv *env, jobject self, jobjectArray args) {
|
||||
env->DeleteLocalRef(arg);
|
||||
}
|
||||
|
||||
#ifdef DYNAMIC_MODULES
|
||||
PluginManager::instance().addPluginProvider(new AndroidPluginProvider());
|
||||
#endif
|
||||
|
||||
LOGI("Entering scummvm_main with %d args", argc);
|
||||
|
||||
res = scummvm_main(argc, argv);
|
||||
|
@ -55,7 +55,6 @@ public:
|
||||
|
||||
static void setReadyForEvents(bool ready);
|
||||
|
||||
static void getPluginDirectories(Common::FSList &dirs);
|
||||
static void setWindowCaption(const char *caption);
|
||||
static void getDPI(float *values);
|
||||
static void displayMessageOnOSD(const char *msg);
|
||||
@ -93,7 +92,6 @@ private:
|
||||
static jmethodID _MID_setWindowCaption;
|
||||
static jmethodID _MID_showVirtualKeyboard;
|
||||
static jmethodID _MID_getSysArchives;
|
||||
static jmethodID _MID_getPluginDirectories;
|
||||
static jmethodID _MID_initSurface;
|
||||
static jmethodID _MID_deinitSurface;
|
||||
|
||||
|
@ -1,63 +0,0 @@
|
||||
package org.scummvm.scummvm;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class PluginProvider extends BroadcastReceiver {
|
||||
private final static String LOG_TAG = "ScummVM";
|
||||
|
||||
public final static String META_UNPACK_LIB =
|
||||
"org.scummvm.scummvm.meta.UNPACK_LIB";
|
||||
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (!intent.getAction().equals(ScummVMApplication.ACTION_PLUGIN_QUERY))
|
||||
return;
|
||||
|
||||
Bundle extras = getResultExtras(true);
|
||||
|
||||
final ActivityInfo info;
|
||||
final PackageInfo pinfo;
|
||||
try {
|
||||
info = context.getPackageManager()
|
||||
.getReceiverInfo(new ComponentName(context, this.getClass()),
|
||||
PackageManager.GET_META_DATA);
|
||||
pinfo = context.getPackageManager()
|
||||
.getPackageInfo(context.getPackageName(), 0);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.e(LOG_TAG, "Error finding my own info?", e);
|
||||
return;
|
||||
}
|
||||
|
||||
String host_version = extras.getString(ScummVMApplication.EXTRA_VERSION);
|
||||
if (!pinfo.versionName.equals(host_version)) {
|
||||
Log.e(LOG_TAG, "Plugin version " + pinfo.versionName + " is not equal to ScummVM version " + host_version);
|
||||
return;
|
||||
}
|
||||
|
||||
String mylib = info.metaData.getString(META_UNPACK_LIB);
|
||||
if (mylib != null) {
|
||||
ArrayList<String> all_libs =
|
||||
extras.getStringArrayList(ScummVMApplication.EXTRA_UNPACK_LIBS);
|
||||
all_libs.add(new Uri.Builder()
|
||||
.scheme("plugin")
|
||||
.authority(context.getPackageName())
|
||||
.path(mylib)
|
||||
.toString());
|
||||
|
||||
extras.putStringArrayList(ScummVMApplication.EXTRA_UNPACK_LIBS,
|
||||
all_libs);
|
||||
}
|
||||
|
||||
setResultExtras(extras);
|
||||
}
|
||||
}
|
@ -54,7 +54,6 @@ public abstract class ScummVM implements SurfaceHolder.Callback, Runnable {
|
||||
abstract protected void getDPI(float[] values);
|
||||
abstract protected void displayMessageOnOSD(String msg);
|
||||
abstract protected void setWindowCaption(String caption);
|
||||
abstract protected String[] getPluginDirectories();
|
||||
abstract protected void showVirtualKeyboard(boolean enable);
|
||||
abstract protected String[] getSysArchives();
|
||||
|
||||
@ -444,10 +443,6 @@ public abstract class ScummVM implements SurfaceHolder.Callback, Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
File cache_dir = ScummVMApplication.getLastCacheDir();
|
||||
String libname = System.mapLibraryName("scummvm");
|
||||
File libpath = new File(cache_dir, libname);
|
||||
|
||||
System.load(libpath.getPath());
|
||||
System.loadLibrary("scummvm");
|
||||
}
|
||||
}
|
||||
|
@ -83,13 +83,6 @@ public class ScummVMActivity extends Activity {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] getPluginDirectories() {
|
||||
String[] dirs = new String[1];
|
||||
dirs[0] = ScummVMApplication.getLastCacheDir().getPath();
|
||||
return dirs;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void showVirtualKeyboard(final boolean enable) {
|
||||
runOnUiThread(new Runnable() {
|
||||
|
@ -1,31 +0,0 @@
|
||||
package org.scummvm.scummvm;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class ScummVMApplication extends Application {
|
||||
public final static String ACTION_PLUGIN_QUERY = "org.scummvm.scummvm.action.PLUGIN_QUERY";
|
||||
public final static String EXTRA_UNPACK_LIBS = "org.scummvm.scummvm.extra.UNPACK_LIBS";
|
||||
public final static String EXTRA_VERSION = "org.scummvm.scummvm.extra.VERSION";
|
||||
|
||||
private static File _cache_dir;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
// This is still on /data :(
|
||||
_cache_dir = getCacheDir();
|
||||
// This is mounted noexec :(
|
||||
//cache_dir = new File(Environment.getExternalStorageDirectory(),
|
||||
// "/.ScummVM.tmp");
|
||||
// This is owned by download manager and requires special
|
||||
// permissions to access :(
|
||||
//cache_dir = Environment.getDownloadCacheDirectory();
|
||||
}
|
||||
|
||||
public static File getLastCacheDir() {
|
||||
return _cache_dir;
|
||||
}
|
||||
}
|
@ -1,388 +0,0 @@
|
||||
package org.scummvm.scummvm;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
public class Unpacker extends Activity {
|
||||
protected final static String LOG_TAG = "ScummVM";
|
||||
// TODO don't hardcode this
|
||||
private final static boolean PLUGINS_ENABLED = false;
|
||||
private final static String META_NEXT_ACTIVITY =
|
||||
"org.scummvm.unpacker.nextActivity";
|
||||
private ProgressBar mProgress;
|
||||
private File mUnpackDest; // location to unpack into
|
||||
private AsyncTask<String, Integer, Void> mUnpacker;
|
||||
private final static int REQUEST_MARKET = 1;
|
||||
|
||||
// Android 3.1+ only
|
||||
public static final int FLAG_INCLUDE_STOPPED_PACKAGES = 32;
|
||||
|
||||
private static class UnpackJob {
|
||||
public ZipFile zipfile;
|
||||
public Set<String> paths;
|
||||
|
||||
public UnpackJob(ZipFile zipfile, Set<String> paths) {
|
||||
this.zipfile = zipfile;
|
||||
this.paths = paths;
|
||||
}
|
||||
|
||||
public long UnpackSize() {
|
||||
long size = 0;
|
||||
for (String path: paths) {
|
||||
ZipEntry entry = zipfile.getEntry(path);
|
||||
if (entry != null) size += entry.getSize();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
private class UnpackTask extends AsyncTask<String, Integer, Void> {
|
||||
@Override
|
||||
protected void onProgressUpdate(Integer... progress) {
|
||||
mProgress.setIndeterminate(false);
|
||||
mProgress.setMax(progress[1]);
|
||||
mProgress.setProgress(progress[0]);
|
||||
mProgress.postInvalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void result) {
|
||||
Bundle md = getMetaData();
|
||||
String nextActivity = md.getString(META_NEXT_ACTIVITY);
|
||||
if (nextActivity != null) {
|
||||
final ComponentName cn =
|
||||
ComponentName.unflattenFromString(nextActivity);
|
||||
if (cn != null) {
|
||||
final Intent origIntent = getIntent();
|
||||
Intent intent = new Intent();
|
||||
intent.setComponent(cn);
|
||||
if (origIntent.getExtras() != null)
|
||||
intent.putExtras(origIntent.getExtras());
|
||||
intent.putExtra(Intent.EXTRA_INTENT, origIntent);
|
||||
intent.setDataAndType(origIntent.getData(),
|
||||
origIntent.getType());
|
||||
//intent.fillIn(getIntent(), 0);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
|
||||
Log.i(LOG_TAG,
|
||||
"Starting next activity with intent " + intent);
|
||||
startActivity(intent);
|
||||
} else {
|
||||
Log.w(LOG_TAG,
|
||||
"Unable to extract a component name from " + nextActivity);
|
||||
}
|
||||
}
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(String... all_libs) {
|
||||
// This will contain all unpack jobs
|
||||
Map<String, UnpackJob> unpack_jobs =
|
||||
new HashMap<String, UnpackJob>(all_libs.length);
|
||||
|
||||
// This will contain all unpack filenames (so we can
|
||||
// detect stale files in the unpack directory)
|
||||
Set<String> all_files = new HashSet<String>(all_libs.length);
|
||||
|
||||
for (String lib: all_libs) {
|
||||
final Uri uri = Uri.parse(lib);
|
||||
final String pkg = uri.getAuthority();
|
||||
final String path = uri.getPath().substring(1); // skip first /
|
||||
|
||||
all_files.add(new File(path).getName());
|
||||
|
||||
UnpackJob job = unpack_jobs.get(pkg);
|
||||
if (job == null) {
|
||||
try {
|
||||
// getPackageResourcePath is hidden in Context,
|
||||
// but exposed in ContextWrapper...
|
||||
ContextWrapper context =
|
||||
new ContextWrapper(createPackageContext(pkg, 0));
|
||||
ZipFile zipfile =
|
||||
new ZipFile(context.getPackageResourcePath());
|
||||
job = new UnpackJob(zipfile, new HashSet<String>(1));
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.e(LOG_TAG, "Package " + pkg +
|
||||
" not found", e);
|
||||
continue;
|
||||
} catch (IOException e) {
|
||||
// FIXME: show some sort of GUI error dialog
|
||||
Log.e(LOG_TAG,
|
||||
"Error opening ZIP for package " + pkg, e);
|
||||
continue;
|
||||
}
|
||||
unpack_jobs.put(pkg, job);
|
||||
}
|
||||
job.paths.add(path);
|
||||
}
|
||||
|
||||
// Delete stale filenames from mUnpackDest
|
||||
for (File file: mUnpackDest.listFiles()) {
|
||||
if (!all_files.contains(file.getName())) {
|
||||
Log.i(LOG_TAG,
|
||||
"Deleting stale cached file " + file);
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
int total_size = 0;
|
||||
for (UnpackJob job: unpack_jobs.values())
|
||||
total_size += job.UnpackSize();
|
||||
|
||||
publishProgress(0, total_size);
|
||||
|
||||
mUnpackDest.mkdirs();
|
||||
|
||||
int progress = 0;
|
||||
|
||||
for (UnpackJob job: unpack_jobs.values()) {
|
||||
try {
|
||||
ZipFile zipfile = job.zipfile;
|
||||
for (String path: job.paths) {
|
||||
ZipEntry zipentry = zipfile.getEntry(path);
|
||||
if (zipentry == null)
|
||||
throw new FileNotFoundException(
|
||||
"Couldn't find " + path + " in zip");
|
||||
File dest = new File(mUnpackDest, new File(path).getName());
|
||||
if (dest.exists() &&
|
||||
dest.lastModified() == zipentry.getTime() &&
|
||||
dest.length() == zipentry.getSize()) {
|
||||
// Already unpacked
|
||||
progress += zipentry.getSize();
|
||||
} else {
|
||||
if (dest.exists())
|
||||
Log.d(LOG_TAG,
|
||||
"Replacing " + dest.getPath() +
|
||||
" old.mtime=" + dest.lastModified() +
|
||||
" new.mtime=" + zipentry.getTime() +
|
||||
" old.size=" + dest.length() +
|
||||
" new.size=" + zipentry.getSize());
|
||||
else
|
||||
Log.i(LOG_TAG,
|
||||
"Extracting " + zipentry.getName() +
|
||||
" from " + zipfile.getName() +
|
||||
" to " + dest.getPath());
|
||||
|
||||
long next_update = progress;
|
||||
|
||||
InputStream in = zipfile.getInputStream(zipentry);
|
||||
OutputStream out = new FileOutputStream(dest);
|
||||
int len;
|
||||
byte[] buffer = new byte[4096];
|
||||
while ((len = in.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, len);
|
||||
progress += len;
|
||||
if (progress >= next_update) {
|
||||
publishProgress(progress, total_size);
|
||||
// Arbitrary limit of 2% update steps
|
||||
next_update += total_size / 50;
|
||||
}
|
||||
}
|
||||
|
||||
in.close();
|
||||
out.close();
|
||||
dest.setLastModified(zipentry.getTime());
|
||||
}
|
||||
publishProgress(progress, total_size);
|
||||
}
|
||||
|
||||
zipfile.close();
|
||||
} catch (IOException e) {
|
||||
// FIXME: show some sort of GUI error dialog
|
||||
Log.e(LOG_TAG, "Error unpacking plugin", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (progress != total_size)
|
||||
Log.d(LOG_TAG, "Ended with progress " + progress +
|
||||
" != total size " + total_size);
|
||||
|
||||
setResult(RESULT_OK);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private class PluginBroadcastReciever extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (!intent.getAction()
|
||||
.equals(ScummVMApplication.ACTION_PLUGIN_QUERY)) {
|
||||
Log.e(LOG_TAG,
|
||||
"Received unexpected action " + intent.getAction());
|
||||
return;
|
||||
}
|
||||
|
||||
Bundle extras = getResultExtras(false);
|
||||
if (extras == null) {
|
||||
// Nothing for us to do.
|
||||
Unpacker.this.setResult(RESULT_OK);
|
||||
finish();
|
||||
}
|
||||
|
||||
ArrayList<String> unpack_libs =
|
||||
extras.getStringArrayList(ScummVMApplication.EXTRA_UNPACK_LIBS);
|
||||
|
||||
if (unpack_libs != null && !unpack_libs.isEmpty()) {
|
||||
final String[] libs =
|
||||
unpack_libs.toArray(new String[unpack_libs.size()]);
|
||||
mUnpacker = new UnpackTask().execute(libs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initPlugins() {
|
||||
Bundle extras = new Bundle(1);
|
||||
|
||||
ArrayList<String> unpack_libs = new ArrayList<String>(1);
|
||||
// This is the common ScummVM code (not really a "plugin" as such)
|
||||
unpack_libs.add(new Uri.Builder()
|
||||
.scheme("plugin")
|
||||
.authority(getPackageName())
|
||||
.path("mylib/armeabi/libscummvm.so")
|
||||
.toString());
|
||||
extras.putStringArrayList(ScummVMApplication.EXTRA_UNPACK_LIBS,
|
||||
unpack_libs);
|
||||
|
||||
final PackageInfo info;
|
||||
try {
|
||||
info = getPackageManager().getPackageInfo(getPackageName(), 0);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.e(LOG_TAG, "Error finding my own info?", e);
|
||||
return;
|
||||
}
|
||||
extras.putString(ScummVMApplication.EXTRA_VERSION, info.versionName);
|
||||
|
||||
Intent intent = new Intent(ScummVMApplication.ACTION_PLUGIN_QUERY);
|
||||
// Android 3.1 defaults to FLAG_EXCLUDE_STOPPED_PACKAGES, and since
|
||||
// none of our plugins will ever be running, that is not helpful
|
||||
intent.setFlags(FLAG_INCLUDE_STOPPED_PACKAGES);
|
||||
sendOrderedBroadcast(intent, Manifest.permission.SCUMMVM_PLUGIN,
|
||||
new PluginBroadcastReciever(),
|
||||
null, RESULT_OK, null, extras);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle b) {
|
||||
super.onCreate(b);
|
||||
|
||||
mUnpackDest = ScummVMApplication.getLastCacheDir();
|
||||
|
||||
setContentView(R.layout.splash);
|
||||
mProgress = (ProgressBar)findViewById(R.id.progress);
|
||||
|
||||
setResult(RESULT_CANCELED);
|
||||
|
||||
tryUnpack();
|
||||
}
|
||||
|
||||
private void tryUnpack() {
|
||||
Intent intent = new Intent(ScummVMApplication.ACTION_PLUGIN_QUERY);
|
||||
List<ResolveInfo> plugins = getPackageManager()
|
||||
.queryBroadcastReceivers(intent, 0);
|
||||
if (PLUGINS_ENABLED && plugins.isEmpty()) {
|
||||
// No plugins installed
|
||||
AlertDialog.Builder alert = new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.no_plugins_title)
|
||||
.setMessage(R.string.no_plugins_found)
|
||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
finish();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.quit,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
|
||||
final Uri uri = Uri.parse("market://search?q=ScummVM plugin");
|
||||
final Intent market_intent = new Intent(Intent.ACTION_VIEW, uri);
|
||||
if (getPackageManager().resolveActivity(market_intent, 0) != null) {
|
||||
alert.setPositiveButton(R.string.to_market,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
try {
|
||||
startActivityForResult(market_intent,
|
||||
REQUEST_MARKET);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Log.e(LOG_TAG,
|
||||
"Error starting market", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
alert.show();
|
||||
|
||||
} else {
|
||||
// Already have at least one plugin installed
|
||||
initPlugins();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
if (mUnpacker != null)
|
||||
mUnpacker.cancel(true);
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode,
|
||||
Intent data) {
|
||||
switch (requestCode) {
|
||||
case REQUEST_MARKET:
|
||||
if (resultCode != RESULT_OK)
|
||||
Log.w(LOG_TAG, "Market returned " + resultCode);
|
||||
tryUnpack();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private Bundle getMetaData() {
|
||||
try {
|
||||
ActivityInfo ai = getPackageManager()
|
||||
.getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
|
||||
return ai.metaData;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.w(LOG_TAG, "Unable to find my own meta-data", e);
|
||||
return new Bundle();
|
||||
}
|
||||
}
|
||||
}
|
@ -33,7 +33,7 @@ void OSystem_SDL_Dingux::initBackend() {
|
||||
|
||||
// Create the graphics manager
|
||||
if (_graphicsManager == 0) {
|
||||
_graphicsManager = new DINGUXSdlGraphicsManager(_eventSource);
|
||||
_graphicsManager = new DINGUXSdlGraphicsManager(_eventSource, _window);
|
||||
}
|
||||
|
||||
// Call parent implementation of this method
|
||||
|
@ -1,6 +1,7 @@
|
||||
DINGUX_EXE_STRIPPED := scummvm_stripped$(EXEEXT)
|
||||
|
||||
bundle_name = dingux-dist/scummvm
|
||||
gcw0_bundle = gcw0-opk
|
||||
|
||||
all: $(DINGUX_EXE_STRIPPED)
|
||||
|
||||
@ -30,3 +31,37 @@ endif
|
||||
$(CP) $(srcdir)/backends/platform/dingux/scummvm.gpe $(bundle_name)/
|
||||
$(CP) $(srcdir)/backends/platform/dingux/README.DINGUX $(bundle_name)/
|
||||
$(CP) $(srcdir)/backends/platform/dingux/scummvm.png $(bundle_name)/
|
||||
|
||||
# Special target for generationg GCW-Zero OPK bundle
|
||||
$(gcw0_bundle): all
|
||||
$(MKDIR) $(gcw0_bundle)
|
||||
$(CP) $(DIST_FILES_DOCS) $(gcw0_bundle)/
|
||||
$(MKDIR) $(gcw0_bundle)/themes
|
||||
$(CP) $(DIST_FILES_THEMES) $(gcw0_bundle)/themes/
|
||||
ifdef DIST_FILES_ENGINEDATA
|
||||
$(MKDIR) $(gcw0_bundle)/engine-data
|
||||
$(CP) $(DIST_FILES_ENGINEDATA) $(gcw0_bundle)/engine-data/
|
||||
endif
|
||||
ifdef DYNAMIC_MODULES
|
||||
$(MKDIR) $(gcw0_bundle)/plugins
|
||||
$(CP) $(PLUGINS) $(gcw0_bundle)/plugins/
|
||||
endif
|
||||
$(CP) $(EXECUTABLE) $(gcw0_bundle)/scummvm
|
||||
|
||||
$(CP) $(srcdir)/backends/vkeybd/packs/vkeybd_default.zip $(gcw0_bundle)/
|
||||
$(CP) $(srcdir)/backends/vkeybd/packs/vkeybd_small.zip $(gcw0_bundle)/
|
||||
|
||||
$(CP) $(srcdir)/dists/gcw0/scummvm.png $(gcw0_bundle)/
|
||||
$(CP) $(srcdir)/dists/gcw0/default.gcw0.desktop $(gcw0_bundle)/
|
||||
$(CP) $(srcdir)/dists/gcw0/scummvmrc $(gcw0_bundle)/
|
||||
$(CP) $(srcdir)/dists/gcw0/scummvm.sh $(gcw0_bundle)/
|
||||
|
||||
gcw0-opk-unstripped: $(gcw0_bundle)
|
||||
$(CP) $(PLUGINS) $(gcw0_bundle)/plugins/
|
||||
$(CP) $(EXECUTABLE) $(gcw0_bundle)/scummvm
|
||||
./dists/gcw0/opk_make.sh -d $(gcw0_bundle) -o scummvm
|
||||
|
||||
gcw-opk: $(gcw0_bundle)
|
||||
$(STRIP) $(gcw0_bundle)/plugins/*
|
||||
$(STRIP) $(gcw0_bundle)/scummvm
|
||||
./dists/gcw0/opk_make.sh -d $(gcw0_bundle) -o scummvm
|
||||
|
@ -159,7 +159,7 @@ void OSystem_GPH::initBackend() {
|
||||
|
||||
// Create the graphics manager
|
||||
if (_graphicsManager == 0) {
|
||||
_graphicsManager = new GPHGraphicsManager(_eventSource);
|
||||
_graphicsManager = new GPHGraphicsManager(_eventSource, _window);
|
||||
}
|
||||
|
||||
/* Pass to POSIX method to do the heavy lifting */
|
||||
|
@ -31,7 +31,7 @@ void OSystem_LINUXMOTO::initBackend() {
|
||||
_eventSource = new LinuxmotoSdlEventSource();
|
||||
|
||||
if (_graphicsManager == 0)
|
||||
_graphicsManager = new LinuxmotoSdlGraphicsManager(_eventSource);
|
||||
_graphicsManager = new LinuxmotoSdlGraphicsManager(_eventSource, _window);
|
||||
|
||||
// Call parent implementation of this method
|
||||
OSystem_POSIX::initBackend();
|
||||
|
@ -35,10 +35,6 @@
|
||||
#include "common/textconsole.h"
|
||||
#include "common/translation.h"
|
||||
|
||||
|
||||
#include <SDL/SDL_syswm.h>
|
||||
#include <X11/Xutil.h>
|
||||
|
||||
namespace Maemo {
|
||||
|
||||
OSystem_SDL_Maemo::OSystem_SDL_Maemo()
|
||||
@ -84,6 +80,15 @@ static void registerDefaultKeyBindings(Common::KeymapperDefaultBindings *_keymap
|
||||
}
|
||||
#endif
|
||||
|
||||
void OSystem_SDL_Maemo::init() {
|
||||
// Use an iconless window for Maemo
|
||||
// also N900 is hit by SDL_WM_SetIcon bug (window cannot receive input)
|
||||
// http://bugzilla.libsdl.org/show_bug.cgi?id=586
|
||||
_window = new SdlIconlessWindow();
|
||||
|
||||
OSystem_POSIX::init();
|
||||
}
|
||||
|
||||
void OSystem_SDL_Maemo::initBackend() {
|
||||
ConfMan.registerDefault("fullscreen", true);
|
||||
ConfMan.registerDefault("aspect_ratio", true);
|
||||
@ -93,7 +98,7 @@ void OSystem_SDL_Maemo::initBackend() {
|
||||
_eventSource = new MaemoSdlEventSource();
|
||||
|
||||
if (_graphicsManager == 0)
|
||||
_graphicsManager = new MaemoSdlGraphicsManager(_eventSource);
|
||||
_graphicsManager = new MaemoSdlGraphicsManager(_eventSource, _window);
|
||||
|
||||
if (_eventObserver == 0)
|
||||
_eventObserver = new MaemoSdlEventObserver((MaemoSdlEventSource *)_eventSource);
|
||||
@ -178,12 +183,6 @@ const Maemo::Model OSystem_SDL_Maemo::detectModel() {
|
||||
return *model;
|
||||
}
|
||||
|
||||
void OSystem_SDL_Maemo::setupIcon() {
|
||||
// no Maemo version needs setupIcon
|
||||
// also N900 is hit by SDL_WM_SetIcon bug (window cannot receive input)
|
||||
// http://bugzilla.libsdl.org/show_bug.cgi?id=586
|
||||
}
|
||||
|
||||
#ifdef ENABLE_KEYMAPPER
|
||||
static const Common::KeyTableEntry maemoKeys[] = {
|
||||
// Function keys
|
||||
|
@ -36,11 +36,11 @@ public:
|
||||
OSystem_SDL_Maemo();
|
||||
~OSystem_SDL_Maemo();
|
||||
|
||||
virtual void init();
|
||||
virtual void initBackend();
|
||||
virtual void quit();
|
||||
virtual void fatalError();
|
||||
virtual void setWindowCaption(const char *caption);
|
||||
virtual void setupIcon();
|
||||
#ifdef ENABLE_KEYMAPPER
|
||||
virtual Common::HardwareInputSet *getHardwareInputSet();
|
||||
virtual Common::Keymap *getGlobalKeymap();
|
||||
|
@ -147,7 +147,7 @@ void OSystem_OP::initBackend() {
|
||||
|
||||
// Create the graphics manager
|
||||
if (_graphicsManager == 0) {
|
||||
_graphicsManager = new OPGraphicsManager(_eventSource);
|
||||
_graphicsManager = new OPGraphicsManager(_eventSource, _window);
|
||||
}
|
||||
|
||||
/* Pass to POSIX method to do the heavy lifting */
|
||||
|
@ -40,7 +40,7 @@ void OSystem_SDL_SamsungTV::initBackend() {
|
||||
_eventSource = new SamsungTVSdlEventSource();
|
||||
|
||||
if (_graphicsManager == 0)
|
||||
_graphicsManager = new SamsungTVSdlGraphicsManager(_eventSource);
|
||||
_graphicsManager = new SamsungTVSdlGraphicsManager(_eventSource, _window);
|
||||
|
||||
// Call parent implementation of this method
|
||||
OSystem_POSIX::initBackend();
|
||||
|
@ -47,6 +47,9 @@ OSystem_MacOSX::OSystem_MacOSX()
|
||||
}
|
||||
|
||||
void OSystem_MacOSX::init() {
|
||||
// Use an iconless window on OS X, as we use a nicer external icon there.
|
||||
_window = new SdlIconlessWindow();
|
||||
|
||||
#if defined(USE_TASKBAR)
|
||||
// Initialize taskbar manager
|
||||
_taskbarManager = new MacOSXTaskbarManager();
|
||||
@ -101,10 +104,6 @@ void OSystem_MacOSX::addSysArchivesToSearchSet(Common::SearchSet &s, int priorit
|
||||
}
|
||||
}
|
||||
|
||||
void OSystem_MacOSX::setupIcon() {
|
||||
// Don't set icon on OS X, as we use a nicer external icon there.
|
||||
}
|
||||
|
||||
bool OSystem_MacOSX::hasFeature(Feature f) {
|
||||
if (f == kFeatureDisplayLogFile)
|
||||
return true;
|
||||
|
@ -38,7 +38,6 @@ public:
|
||||
virtual void init();
|
||||
virtual void initBackend();
|
||||
virtual void addSysArchivesToSearchSet(Common::SearchSet &s, int priority = 0);
|
||||
virtual void setupIcon();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1,7 +1,8 @@
|
||||
MODULE := backends/platform/sdl
|
||||
|
||||
MODULE_OBJS := \
|
||||
sdl.o
|
||||
sdl.o \
|
||||
sdl-window.o
|
||||
|
||||
ifdef POSIX
|
||||
MODULE_OBJS += \
|
||||
@ -19,6 +20,7 @@ endif
|
||||
ifdef WIN32
|
||||
MODULE_OBJS += \
|
||||
win32/win32-main.o \
|
||||
win32/win32-window.o \
|
||||
win32/win32.o
|
||||
endif
|
||||
|
||||
|
@ -52,12 +52,84 @@ typedef struct { int FAKE; } FAKE_FILE;
|
||||
#define strncasecmp FAKE_strncasecmp
|
||||
#endif
|
||||
|
||||
// HACK: SDL might include windows.h which defines its own ARRAYSIZE.
|
||||
// However, we want to use the version from common/util.h. Thus, we make sure
|
||||
// that we actually have this definition after including the SDL headers.
|
||||
#if defined(ARRAYSIZE) && defined(COMMON_UTIL_H)
|
||||
#define HACK_REDEFINE_ARRAYSIZE
|
||||
#undef ARRAYSIZE
|
||||
#endif
|
||||
|
||||
// HACK to fix compilation with SDL 2.0 in MSVC.
|
||||
// In SDL 2.0, intrin.h is now included in SDL_cpuinfo.h, which includes
|
||||
// setjmp.h. SDL_cpuinfo.h is included from SDL.h and SDL_syswm.h.
|
||||
// Thus, we remove the exceptions for setjmp and longjmp before these two
|
||||
// includes. Unfortunately, we can't use SDL_VERSION_ATLEAST here, as SDL.h
|
||||
// hasn't been included yet at this point.
|
||||
#if !defined(FORBIDDEN_SYMBOL_ALLOW_ALL) && defined(_MSC_VER)
|
||||
// We unset any fake definitions of setjmp/longjmp here
|
||||
|
||||
#ifndef FORBIDDEN_SYMBOL_EXCEPTION_setjmp
|
||||
#undef setjmp
|
||||
#endif
|
||||
|
||||
#ifndef FORBIDDEN_SYMBOL_EXCEPTION_longjmp
|
||||
#undef longjmp
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(__SYMBIAN32__)
|
||||
#include <esdl\SDL.h>
|
||||
#else
|
||||
#include <SDL.h>
|
||||
#endif
|
||||
|
||||
#include <SDL_syswm.h>
|
||||
|
||||
// Restore the forbidden exceptions from the hack above
|
||||
#if !defined(FORBIDDEN_SYMBOL_ALLOW_ALL) && defined(_MSC_VER)
|
||||
|
||||
#ifndef FORBIDDEN_SYMBOL_EXCEPTION_setjmp
|
||||
#undef setjmp
|
||||
#define setjmp(a) FORBIDDEN_SYMBOL_REPLACEMENT
|
||||
#endif
|
||||
|
||||
#ifndef FORBIDDEN_SYMBOL_EXCEPTION_longjmp
|
||||
#undef longjmp
|
||||
#define longjmp(a,b) FORBIDDEN_SYMBOL_REPLACEMENT
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
// SDL_syswm.h will include windows.h on Win32. We need to undefine its
|
||||
// ARRAYSIZE definition because we supply our own.
|
||||
#undef ARRAYSIZE
|
||||
|
||||
#ifdef HACK_REDEFINE_ARRAYSIZE
|
||||
#undef HACK_REDEFINE_ARRAYSIZE
|
||||
#define ARRAYSIZE(x) ((int)(sizeof(x) / sizeof(x[0])))
|
||||
#endif
|
||||
|
||||
// In a moment of brilliance Xlib.h included by SDL_syswm.h #defines the
|
||||
// following names. In a moment of mental breakdown, which occured upon
|
||||
// gazing at Xlib.h, LordHoto decided to undefine them to prevent havoc.
|
||||
#ifdef Status
|
||||
#undef Status
|
||||
#endif
|
||||
|
||||
#ifdef Bool
|
||||
#undef Bool
|
||||
#endif
|
||||
|
||||
#ifdef True
|
||||
#undef True
|
||||
#endif
|
||||
|
||||
#ifdef False
|
||||
#undef False
|
||||
#endif
|
||||
|
||||
// Finally forbid FILE again (if it was forbidden to start with)
|
||||
#if !defined(FORBIDDEN_SYMBOL_ALLOW_ALL) && !defined(FORBIDDEN_SYMBOL_EXCEPTION_FILE)
|
||||
#undef FILE
|
||||
@ -74,5 +146,50 @@ typedef struct { int FAKE; } FAKE_FILE;
|
||||
#define strncasecmp FORBIDDEN_SYMBOL_REPLACEMENT
|
||||
#endif
|
||||
|
||||
// SDL 2 has major API changes. We redefine constants which got renamed to
|
||||
// ease the transition. This is sometimes dangerous because the values changed
|
||||
// too!
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
|
||||
// Type names which changed between SDL 1.2 and SDL 2.
|
||||
#define SDLKey SDL_Keycode
|
||||
#define SDLMod SDL_Keymod
|
||||
#define SDL_keysym SDL_Keysym
|
||||
|
||||
// Key code constants which got renamed.
|
||||
#define SDLK_SCROLLOCK SDLK_SCROLLLOCK
|
||||
#define SDLK_NUMLOCK SDLK_NUMLOCKCLEAR
|
||||
#define SDLK_LSUPER SDLK_LGUI
|
||||
#define SDLK_RSUPER SDLK_RGUI
|
||||
#define SDLK_PRINT SDLK_PRINTSCREEN
|
||||
#define SDLK_COMPOSE SDLK_APPLICATION
|
||||
#define SDLK_KP0 SDLK_KP_0
|
||||
#define SDLK_KP1 SDLK_KP_1
|
||||
#define SDLK_KP2 SDLK_KP_2
|
||||
#define SDLK_KP3 SDLK_KP_3
|
||||
#define SDLK_KP4 SDLK_KP_4
|
||||
#define SDLK_KP5 SDLK_KP_5
|
||||
#define SDLK_KP6 SDLK_KP_6
|
||||
#define SDLK_KP7 SDLK_KP_7
|
||||
#define SDLK_KP8 SDLK_KP_8
|
||||
#define SDLK_KP9 SDLK_KP_9
|
||||
|
||||
// Meta key constants which got renamed.
|
||||
#define KMOD_META KMOD_GUI
|
||||
|
||||
// SDL surface flags which got removed.
|
||||
#define SDL_SRCCOLORKEY 0
|
||||
#define SDL_SRCALPHA 0
|
||||
#define SDL_FULLSCREEN 0x40000000
|
||||
|
||||
// Compatibility implementations for removed functionality.
|
||||
int SDL_SetColors(SDL_Surface *surface, SDL_Color *colors, int firstcolor, int ncolors);
|
||||
int SDL_SetAlpha(SDL_Surface *surface, Uint32 flag, Uint8 alpha);
|
||||
|
||||
#define SDL_SetColorKey SDL_SetColorKey_replacement
|
||||
int SDL_SetColorKey_replacement(SDL_Surface *surface, Uint32 flag, Uint32 key);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
223
backends/platform/sdl/sdl-window.cpp
Normal file
223
backends/platform/sdl/sdl-window.cpp
Normal file
@ -0,0 +1,223 @@
|
||||
/* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#define FORBIDDEN_SYMBOL_ALLOW_ALL
|
||||
|
||||
#include "backends/platform/sdl/sdl-window.h"
|
||||
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "icons/scummvm.xpm"
|
||||
|
||||
SdlWindow::SdlWindow()
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
: _window(nullptr), _inputGrabState(false), _windowCaption("ScummVM")
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
SdlWindow::~SdlWindow() {
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
destroyWindow();
|
||||
#endif
|
||||
}
|
||||
|
||||
void SdlWindow::setupIcon() {
|
||||
int x, y, w, h, ncols, nbytes, i;
|
||||
unsigned int rgba[256];
|
||||
unsigned int *icon;
|
||||
|
||||
if (sscanf(scummvm_icon[0], "%d %d %d %d", &w, &h, &ncols, &nbytes) != 4) {
|
||||
warning("Wrong format of scummvm_icon[0] (%s)", scummvm_icon[0]);
|
||||
|
||||
return;
|
||||
}
|
||||
if ((w > 512) || (h > 512) || (ncols > 255) || (nbytes > 1)) {
|
||||
warning("Could not load the built-in icon (%d %d %d %d)", w, h, ncols, nbytes);
|
||||
return;
|
||||
}
|
||||
icon = (unsigned int*)malloc(w*h*sizeof(unsigned int));
|
||||
if (!icon) {
|
||||
warning("Could not allocate temp storage for the built-in icon");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < ncols; i++) {
|
||||
unsigned char code;
|
||||
char color[32];
|
||||
memset(color, 0, sizeof(color));
|
||||
unsigned int col;
|
||||
if (sscanf(scummvm_icon[1 + i], "%c c %s", &code, color) != 2) {
|
||||
warning("Wrong format of scummvm_icon[%d] (%s)", 1 + i, scummvm_icon[1 + i]);
|
||||
}
|
||||
if (!strcmp(color, "None"))
|
||||
col = 0x00000000;
|
||||
else if (!strcmp(color, "black"))
|
||||
col = 0xFF000000;
|
||||
else if (color[0] == '#') {
|
||||
if (sscanf(color + 1, "%06x", &col) != 1) {
|
||||
warning("Wrong format of color (%s)", color + 1);
|
||||
}
|
||||
col |= 0xFF000000;
|
||||
} else {
|
||||
warning("Could not load the built-in icon (%d %s - %s) ", code, color, scummvm_icon[1 + i]);
|
||||
free(icon);
|
||||
return;
|
||||
}
|
||||
|
||||
rgba[code] = col;
|
||||
}
|
||||
for (y = 0; y < h; y++) {
|
||||
const char *line = scummvm_icon[1 + ncols + y];
|
||||
for (x = 0; x < w; x++) {
|
||||
icon[x + w * y] = rgba[(int)line[x]];
|
||||
}
|
||||
}
|
||||
|
||||
SDL_Surface *sdl_surf = SDL_CreateRGBSurfaceFrom(icon, w, h, 32, w * 4, 0xFF0000, 0x00FF00, 0x0000FF, 0xFF000000);
|
||||
if (!sdl_surf) {
|
||||
warning("SDL_CreateRGBSurfaceFrom(icon) failed");
|
||||
}
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
if (_window) {
|
||||
SDL_SetWindowIcon(_window, sdl_surf);
|
||||
}
|
||||
#else
|
||||
SDL_WM_SetIcon(sdl_surf, NULL);
|
||||
#endif
|
||||
|
||||
SDL_FreeSurface(sdl_surf);
|
||||
free(icon);
|
||||
}
|
||||
|
||||
void SdlWindow::setWindowCaption(const Common::String &caption) {
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
_windowCaption = caption;
|
||||
if (_window) {
|
||||
SDL_SetWindowTitle(_window, caption.c_str());
|
||||
}
|
||||
#else
|
||||
SDL_WM_SetCaption(caption.c_str(), caption.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
void SdlWindow::toggleMouseGrab() {
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
if (_window) {
|
||||
_inputGrabState = !(SDL_GetWindowGrab(_window) == SDL_TRUE);
|
||||
SDL_SetWindowGrab(_window, _inputGrabState ? SDL_TRUE : SDL_FALSE);
|
||||
}
|
||||
#else
|
||||
if (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_OFF) {
|
||||
SDL_WM_GrabInput(SDL_GRAB_ON);
|
||||
} else {
|
||||
SDL_WM_GrabInput(SDL_GRAB_OFF);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SdlWindow::hasMouseFocus() const {
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
if (_window) {
|
||||
return (SDL_GetWindowFlags(_window) & SDL_WINDOW_MOUSE_FOCUS);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
return (SDL_GetAppState() & SDL_APPMOUSEFOCUS);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SdlWindow::warpMouseInWindow(uint x, uint y) {
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
if (_window) {
|
||||
SDL_WarpMouseInWindow(_window, x, y);
|
||||
}
|
||||
#else
|
||||
SDL_WarpMouse(x, y);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SdlWindow::iconifyWindow() {
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
if (_window) {
|
||||
SDL_MinimizeWindow(_window);
|
||||
}
|
||||
#else
|
||||
SDL_WM_IconifyWindow();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SdlWindow::getSDLWMInformation(SDL_SysWMinfo *info) const {
|
||||
SDL_VERSION(&info->version);
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
return SDL_GetWindowWMInfo(_window, info);
|
||||
#else
|
||||
return SDL_GetWMInfo(info);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
SDL_Surface *copySDLSurface(SDL_Surface *src) {
|
||||
const bool locked = SDL_MUSTLOCK(src) == SDL_TRUE;
|
||||
|
||||
if (locked) {
|
||||
if (SDL_LockSurface(src) != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_Surface *res = SDL_CreateRGBSurfaceFrom(src->pixels,
|
||||
src->w, src->h, src->format->BitsPerPixel,
|
||||
src->pitch, src->format->Rmask, src->format->Gmask,
|
||||
src->format->Bmask, src->format->Amask);
|
||||
|
||||
if (locked) {
|
||||
SDL_UnlockSurface(src);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool SdlWindow::createWindow(int width, int height, uint32 flags) {
|
||||
destroyWindow();
|
||||
|
||||
if (_inputGrabState) {
|
||||
flags |= SDL_WINDOW_INPUT_GRABBED;
|
||||
}
|
||||
|
||||
_window = SDL_CreateWindow(_windowCaption.c_str(), SDL_WINDOWPOS_UNDEFINED,
|
||||
SDL_WINDOWPOS_UNDEFINED, width, height, flags);
|
||||
if (!_window) {
|
||||
return false;
|
||||
}
|
||||
setupIcon();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SdlWindow::destroyWindow() {
|
||||
SDL_DestroyWindow(_window);
|
||||
_window = nullptr;
|
||||
}
|
||||
#endif
|
112
backends/platform/sdl/sdl-window.h
Normal file
112
backends/platform/sdl/sdl-window.h
Normal file
@ -0,0 +1,112 @@
|
||||
/* 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_PLATFORM_SDL_WINDOW_H
|
||||
#define BACKENDS_PLATFORM_SDL_WINDOW_H
|
||||
|
||||
#include "backends/platform/sdl/sdl-sys.h"
|
||||
|
||||
#include "common/str.h"
|
||||
|
||||
class SdlWindow {
|
||||
public:
|
||||
SdlWindow();
|
||||
virtual ~SdlWindow();
|
||||
|
||||
/**
|
||||
* Setup the window icon.
|
||||
*/
|
||||
virtual void setupIcon();
|
||||
|
||||
/**
|
||||
* Change the caption of the window.
|
||||
*
|
||||
* @param caption New window caption in UTF-8 encoding.
|
||||
*/
|
||||
void setWindowCaption(const Common::String &caption);
|
||||
|
||||
/**
|
||||
* Toggle mouse grab state. This decides whether the cursor can leave the
|
||||
* window or not.
|
||||
*/
|
||||
void toggleMouseGrab();
|
||||
|
||||
/**
|
||||
* Check whether the application has mouse focus.
|
||||
*/
|
||||
bool hasMouseFocus() const;
|
||||
|
||||
/**
|
||||
* Warp the mouse to the specified position in window coordinates.
|
||||
*/
|
||||
void warpMouseInWindow(uint x, uint y);
|
||||
|
||||
/**
|
||||
* Iconifies the window.
|
||||
*/
|
||||
void iconifyWindow();
|
||||
|
||||
/**
|
||||
* Query platform specific SDL window manager information.
|
||||
*
|
||||
* Since this is an SDL internal structure clients are responsible
|
||||
* for accessing it in a version safe manner.
|
||||
*/
|
||||
bool getSDLWMInformation(SDL_SysWMinfo *info) const;
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
public:
|
||||
/**
|
||||
* @return The window ScummVM has setup with SDL.
|
||||
*/
|
||||
SDL_Window *getSDLWindow() const { return _window; }
|
||||
|
||||
/**
|
||||
* Creates a new SDL window (and destroies the old one).
|
||||
*
|
||||
* @param width Width of the window.
|
||||
* @param height Height of the window.
|
||||
* @param flags SDL flags passed to SDL_CreateWindow
|
||||
* @return true on success, false otherwise
|
||||
*/
|
||||
bool createWindow(int width, int height, uint32 flags);
|
||||
|
||||
/**
|
||||
* Destroies the current SDL window.
|
||||
*/
|
||||
void destroyWindow();
|
||||
|
||||
protected:
|
||||
SDL_Window *_window;
|
||||
|
||||
private:
|
||||
bool _inputGrabState;
|
||||
Common::String _windowCaption;
|
||||
#endif
|
||||
};
|
||||
|
||||
class SdlIconlessWindow : public SdlWindow {
|
||||
public:
|
||||
virtual void setupIcon() {}
|
||||
};
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user