Compare commits
116 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f5653d3c4d | |||
| 4026d7818b | |||
| a0fda2f058 | |||
| 93546e2d5c | |||
| 85a562713a | |||
| 4e9beb1a5d | |||
| 522333e0f5 | |||
| b4c4d0264c | |||
| 21c3187fa3 | |||
| b377cfcdef | |||
| 5d384e531d | |||
| 2b00c64973 | |||
| 7c777dea22 | |||
| 9d0c77917d | |||
| 6d40ced435 | |||
| 0675ee7f79 | |||
| 0aec15a848 | |||
| 2ff01a99ac | |||
| 0596ed7739 | |||
| d8beb99964 | |||
| 648dfd8d75 | |||
| 279a7a3fc8 | |||
| a62aede62f | |||
| b07bbbc1c9 | |||
| 547ce87eaa | |||
| c206456d00 | |||
| 590dd13f8f | |||
| afd0e70479 | |||
| b9166b35b3 | |||
| 333fa51862 | |||
| ec6d256707 | |||
| 86764d92a6 | |||
| cf1c9b3e5e | |||
| b15d7179b6 | |||
| 72312f1249 | |||
| 373198f628 | |||
| e96d8c774b | |||
| 8e221e183d | |||
| 2249c49303 | |||
| 388362d318 | |||
| a554ea8e8b | |||
| 090719deee | |||
| e29a162a85 | |||
| e17fa9edc2 | |||
| e57529257e | |||
| 866f9228ef | |||
| 3a38a8b89b | |||
| 0509a23881 | |||
| cbb0741ab9 | |||
| 716bb0fac1 | |||
| acb193bd7b | |||
| 0a09beb731 | |||
| 93dca3df73 | |||
| 008143a3da | |||
| 30a4db432e | |||
| 65546baf01 | |||
| 8be5069490 | |||
| 802410644b | |||
| e74b172372 | |||
| 23ac59588e | |||
| 7f6e9de5e5 | |||
| 143906ae9d | |||
| b003371456 | |||
| 1e6fa37a10 | |||
| 935786a161 | |||
| 23c4d07432 | |||
| 77107ef1eb | |||
| cac4238b12 | |||
| 8c402c2dc3 | |||
| 9bff95826a | |||
| fc0f7cecc4 | |||
| 0b5c5ce17e | |||
| 9aa61532ff | |||
| 3271557f8a | |||
| 9587342129 | |||
| 9b4821308d | |||
| 663978906a | |||
| 60f3308b8b | |||
| a305d6e725 | |||
| 1feff344b8 | |||
| 4fa4ffae54 | |||
| 4ecd72de6e | |||
| 3ec495c3fc | |||
| 5efc437384 | |||
| a777f4d3b1 | |||
| ff139808ff | |||
| 07136f9be8 | |||
| 35fc2295e9 | |||
| 2b262ccca3 | |||
| d42aab3dff | |||
| 612c347498 | |||
| 3c4540131a | |||
| 43317f6e02 | |||
| 523742d8de | |||
| 9516ee910e | |||
| 3ea31a3862 | |||
| 649411a4ef | |||
| a0e60e5453 | |||
| e3f1f40a78 | |||
| d0177f4c52 | |||
| 84a3a55a03 | |||
| 048075b47a | |||
| fab097c92a | |||
| 9d26bd8c04 | |||
| 664d243c23 | |||
| 9a0906655d | |||
| 13d60f2741 | |||
| 7d4de64d0b | |||
| 9ada2d9884 | |||
| a5f1951cfb | |||
| 98850e4c5c | |||
| 709e1f925d | |||
| b11ae331a5 | |||
| 3ba4b26b99 | |||
| b864077017 | |||
| d411dff1c0 |
@@ -28,3 +28,29 @@ launchertrans.rpy
|
||||
jcc
|
||||
scite/scite.zip
|
||||
screenshot*
|
||||
.ropeproject
|
||||
release_cardgame.sh
|
||||
cardgame
|
||||
dse.split
|
||||
empty
|
||||
empty-linux-x86.tar.bz2
|
||||
empty-mac.zip
|
||||
empty.zip
|
||||
errors.txt
|
||||
icons
|
||||
iliad-icon.png
|
||||
iliad-work
|
||||
kuroi
|
||||
lsprofcalltree.py
|
||||
manifest.xml
|
||||
mediatest
|
||||
musicroom
|
||||
nobmtest
|
||||
renpy-iliad.sh
|
||||
renpy.kcg
|
||||
renpy.stats
|
||||
renpygame-1.2.zip
|
||||
speedtest
|
||||
styles.txt
|
||||
renpygame-demo/game/saves
|
||||
./lib
|
||||
|
||||
@@ -1,3 +1,257 @@
|
||||
New in Ren'Py 6.9.1
|
||||
-------------------
|
||||
|
||||
The music and video playback code has been completely rewritten. The
|
||||
new implementation uses the ffmpeg library. It includes the following
|
||||
features:
|
||||
|
||||
* Support for ogg, mp3, mp2, wav, and flac audio.
|
||||
|
||||
* Support for theora, mpeg4 part 2 (including DivX and Xvid), mpeg2,
|
||||
and mpeg1 video.
|
||||
|
||||
* Support for an arbitrary number of named channels, replacing the
|
||||
previous eight numbered channels.
|
||||
|
||||
* Audio files are now automatically converted to the output sampling
|
||||
rate (by default, 44100hz). This prevents the problems that would
|
||||
previously crop up when playing audio sampled at other rates.
|
||||
|
||||
* The play, queue, and stop statements now take a channel name as the
|
||||
second argument, which makes using user-defined channels easier.
|
||||
|
||||
As part of this rewrite, we have eliminated support for mod formats,
|
||||
as they are not supported by ffmpeg.
|
||||
|
||||
Please read the reference manual documentation for Audio and Movies to
|
||||
learn the new interface.
|
||||
|
||||
The new code requires some changes in the saved data. As a result,
|
||||
loading a file saved with a prior version of Ren'Py may lead to a lack
|
||||
of music until the next track is played.
|
||||
|
||||
|
||||
|
||||
The new window statement allows one to control if an empty window is
|
||||
shown when no character is speaking. Specifically, the
|
||||
|
||||
window show [transition]
|
||||
|
||||
statement shows the window, and the
|
||||
|
||||
window hide [transition]
|
||||
|
||||
statement hides it. Both can take an optional
|
||||
transition. config.empty_window is responsible for displaying the
|
||||
window, while config.window_show_transition and
|
||||
config.window_hide_transition are default transitions. The state of
|
||||
the window can be determined by testing _window. The
|
||||
renpy.shown_window function can be called to suppress displaying the
|
||||
empty window, and is called automatically when characters speak.
|
||||
|
||||
|
||||
Hotspots for the game menu buttons can be defined as part of
|
||||
layout.imagemap_load_save, layout.imagemap_preferences, and
|
||||
layout.imagemap_yesno_prompt. Doing so prevents layout.navigation from
|
||||
being called for that screen. It also allows opaque imagemaps to be
|
||||
used, rather than forcing them to be transparent to allow the
|
||||
navigation to show through.
|
||||
|
||||
|
||||
There is a new config.windows_icon variable, that lets one set the
|
||||
window icon for Windows separately from Mac OS X and Linux.
|
||||
|
||||
|
||||
Ren'Py now automatically scales itself down when it detects that the
|
||||
game window is too large for the screen.
|
||||
|
||||
|
||||
The define.move_transitions function can now pass keyword arguments to
|
||||
the moves it creates. The most useful thing with this is to pass
|
||||
subpixel=True, in order to create subpixel move transitions.
|
||||
|
||||
|
||||
The Motion (including Move and Pan), Zoom, RotoZoom, and FactorZoom
|
||||
displayables now use the style.motion style, which now has xanchor and
|
||||
yanchor both set to 0. This gives these movements the same semantics
|
||||
when used inside an image clause as they have outside one.
|
||||
|
||||
|
||||
im.Grayscale and im.Sepia now take properties, so they can be
|
||||
positioned.
|
||||
|
||||
|
||||
The new renpy.get_placement function returns information about the
|
||||
placement of displayables.
|
||||
|
||||
|
||||
Improved the render management code, so there should no longer be any
|
||||
leaks reported during shutdown.
|
||||
|
||||
|
||||
Deal with the case where a bmfont has a negative offset for the first
|
||||
character in a string.
|
||||
|
||||
|
||||
Added more verbose diagnostics when illegal statements are run in the
|
||||
init phase.
|
||||
|
||||
|
||||
Fixed a bug that caused layout.imagemap_main_menu to call a label even
|
||||
though there was a button with the same name.
|
||||
|
||||
Fixed a bug that prevented Zoom from working in non-bilinear mode.
|
||||
|
||||
Fixed a bug that could cause buttons not not consume events when using
|
||||
an imagemap main menu.
|
||||
|
||||
Fixed a bug that caused Transform to produce an incorrectly-sized
|
||||
render.
|
||||
|
||||
Fixed a bug that could cause crashing when rolling back to a Zoom with
|
||||
an after_child.
|
||||
|
||||
Fixed a bug that could cause segfaults when saving overly-complicated
|
||||
images.
|
||||
|
||||
|
||||
New in Ren'Py 6.9.0
|
||||
-------------------
|
||||
|
||||
This release features a rewritten rendering system. The rendering
|
||||
subsystem is what manages drawing to the screen. There have been
|
||||
several major improvements:
|
||||
|
||||
* The render system has been simplified. Although still complex, it's
|
||||
now easier to understand than the old system, while also being
|
||||
naturally a bit faster.
|
||||
|
||||
* That being said, the render system now focuses more on framerate
|
||||
consistency than raw speed.
|
||||
|
||||
* The render system now supports subpixel drawing, drawing images at
|
||||
locations that are not integer pixel boundaries. This is enabled by
|
||||
setting the subpixel style property to True for a displayable. (It
|
||||
makes the most sense to do this for Move and Pan, and less sense to
|
||||
do this for non-moving displayables.)
|
||||
|
||||
* The render system supports specifying subpixel placement of
|
||||
displayables. This is done using the new absolute type. (A
|
||||
floating-point position is still interpreted as a fraction of the
|
||||
area in which the displayable is placed.)
|
||||
|
||||
* The render system supports a rotate-zoom-alpha mode, in which allows
|
||||
a displayable to be rotated, zoomed, and have its alpha
|
||||
changed. When this mode is used, the cost remains constant, even
|
||||
when multiple transformations are composed. Currently, this is only
|
||||
used by Transform and ui.transform.
|
||||
|
||||
* The render system supports rotating and scaling focusable objects.
|
||||
|
||||
Improved the transform routine used by RotoZoom and Transform. It's
|
||||
now both more accurate and faster. On Intel computers, it is now
|
||||
accelerated using MMX instructions.
|
||||
|
||||
|
||||
The image statement now implicitly places itself inside an init block
|
||||
if it's not inside an init block to begin with. The code:
|
||||
|
||||
image bg world = "world.jpg"
|
||||
|
||||
is now equivalent to:
|
||||
|
||||
init 990:
|
||||
image bg world = "world.jpg"
|
||||
|
||||
|
||||
When compiling python blocks containing strings with a unicode
|
||||
character in them, Ren'Py will automatically prepend a u to any string
|
||||
that's missing it. This should make life easier for users of non-ASCII
|
||||
languages.
|
||||
|
||||
|
||||
The new ui.autobar function makes it easy to make a bar that
|
||||
varies between two values over time.
|
||||
|
||||
|
||||
The new renpy.Container class can be subclassed to create a user-defined
|
||||
displayable thatr contains other displayables, like Transforms.
|
||||
|
||||
|
||||
The new im.FactorScale image manipulator allows images to be scaled by
|
||||
a specified factor at preload time. This is generally faster than doing the
|
||||
equivalent scaling at display time with FactorZoom.
|
||||
|
||||
|
||||
The new renpy.sound.set_pan function allows sound and music to be
|
||||
smoothly panned over time.
|
||||
|
||||
|
||||
The new config.label_callback variable allows a function to be called
|
||||
whenever control reaches a label. It can distinguish between jumps to
|
||||
that label and control falling through.
|
||||
|
||||
|
||||
There have been a couple of places where properly documented functions
|
||||
have superseded semi-documented calls into the Ren'Py internals:
|
||||
|
||||
The renpy.game_menu and ui.gamemenus functions are now the preferred ways
|
||||
of getting access to the game menu.
|
||||
|
||||
The ui.jumps function now takes a transition argument, allowing it to subsume
|
||||
the undocumented _intra_jumps.
|
||||
|
||||
|
||||
Ren'Py now looks for the file environment.txt in the directory
|
||||
containing Ren'Py itself. If it finds it, it loads the file as a
|
||||
python file, and uses it to set the default values of environment
|
||||
variables. This allows things like the editor or the scale factor to
|
||||
be set even on platforms that don't make it easy to change environment
|
||||
variables globally.
|
||||
|
||||
|
||||
The reporting of parse errors involving missing quotes has been
|
||||
improved.
|
||||
|
||||
|
||||
Changed the behavior of overlays during transitions. Now, both the old
|
||||
and new screens will use the new overlay. Previously, the overlay was
|
||||
cleared out during a transition, which didn't work well, as (for
|
||||
example) a dissolve would cause the overlay to blink out and then
|
||||
dissolve back in again.
|
||||
|
||||
|
||||
MoveTransition and define.move_transitions have been changed to take
|
||||
a layers argument, giving the list of layers the transition applies
|
||||
to. By default, this is only the master layer, which is a change in
|
||||
their behavior.
|
||||
|
||||
|
||||
Calling theme.roundrect() no longer implies layout.button_menu().
|
||||
|
||||
|
||||
Ren'Py now autosaves when entering the quit prompt, and will not actually
|
||||
quit until this autosave is complete.
|
||||
|
||||
|
||||
The default framerate limit (config.framerate) has been raised to
|
||||
100fps. This make tearing less noticable than on a 60hz screen.
|
||||
|
||||
|
||||
Added renpy.partial as a version of renpy.curry that requires two
|
||||
function calls, rather than three.
|
||||
|
||||
|
||||
Fixed a divide by zero error that occured when Alpha was used for 0
|
||||
seconds.
|
||||
|
||||
|
||||
Libvorbis has been upgraded to version 1.2.0.
|
||||
|
||||
|
||||
To reduce distribution size, OGG Speex support was dropped.
|
||||
|
||||
|
||||
New in Ren'Py 6.8.1
|
||||
-------------------
|
||||
|
||||
@@ -4137,4 +4391,3 @@ controls which transitions are performed.
|
||||
|
||||
Added a Pan function which can be used in an at clause. This allows us
|
||||
to pan over a background image.
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
Copyright 2004-2007 PyTom <pytom@bishoujo.us>
|
||||
Ren'Py itself is covered by the terms of the following (MIT) license:
|
||||
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation files
|
||||
@@ -19,9 +20,6 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
(The above is the MIT License)
|
||||
|
||||
Please see each individual source file for a list of copyright
|
||||
holders. The pictures of Eileen in the demo are Copyright 2006
|
||||
@@ -34,25 +32,20 @@ launchers may contain code taken from the following projects:
|
||||
* Pygame (LGPL)
|
||||
* SDL (LGPL)
|
||||
* SDL_image (LGPL)
|
||||
* SDL_sound (LGPL)
|
||||
* SDL_ttf (LGPL)
|
||||
* SDL_mixer (LGPL)
|
||||
* Freetype (LGPL)
|
||||
* FFmpeg (LGPL)
|
||||
* jpeg (JPEG License)
|
||||
* libmodplug (Public Domain)
|
||||
* libogg (Xiph.org License)
|
||||
* libpng (PNG license)
|
||||
* libvorbis (Xiph.org License)
|
||||
* smpeg (LGPL)
|
||||
* speex (Xiph.org License)
|
||||
* zlib (Zlib License)
|
||||
* zlib (Zlib License)
|
||||
* bzip2 (Bzip2 License)
|
||||
* pyobjc (MIT License)
|
||||
|
||||
The Linux launcher may also include code from:
|
||||
The Linux build may also include code from:
|
||||
|
||||
* directfb (LGPL)
|
||||
* svgalib (LGPL)
|
||||
* svgalib (LGPL)
|
||||
|
||||
For the purpose of LGPL compliance, the source code to all LGPL
|
||||
software we depend on is either in the Ren'Py package (available from
|
||||
@@ -61,9 +54,13 @@ http://www.renpy.org/), or in the renpy-deps package
|
||||
by including a copy of this license with every copy of Ren'Py you
|
||||
distribute, and referring to it in your project's README file.
|
||||
|
||||
The default Ren'Py build distributes Ren'Py alongside jEdit, which is
|
||||
licensed under the GNU GPL. The source code to jEdit is distributed as
|
||||
part of the renpy-deps package (http://www.renpy.org/dl/lgpl/).
|
||||
|
||||
|
||||
GNU Lesser General Public License
|
||||
---------------------------------
|
||||
=================================
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
@@ -575,7 +572,7 @@ That's all there is to it!
|
||||
|
||||
|
||||
Python License
|
||||
--------------
|
||||
==============
|
||||
|
||||
1. This LICENSE AGREEMENT is between the Python Software Foundation
|
||||
("PSF"), and the Individual or Organization ("Licensee") accessing and
|
||||
@@ -625,7 +622,7 @@ Agreement.
|
||||
|
||||
|
||||
Jpeg License
|
||||
------------
|
||||
============
|
||||
|
||||
In plain English:
|
||||
|
||||
@@ -674,39 +671,8 @@ commercial products, provided that all warranty or liability claims are
|
||||
assumed by the product vendor.
|
||||
|
||||
|
||||
Xiph.org License
|
||||
----------------
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
- Neither the name of the Xiph.org Foundation nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
PNG License
|
||||
-----------
|
||||
===========
|
||||
|
||||
The PNG Reference Library is supplied "AS IS". The Contributing Authors
|
||||
and Group 42, Inc. disclaim all warranties, expressed or implied,
|
||||
@@ -734,9 +700,8 @@ supporting the PNG file format in commercial products. If you use this
|
||||
source code in a product, acknowledgment is not required but would be
|
||||
appreciated.
|
||||
|
||||
|
||||
Zlib License
|
||||
------------
|
||||
============
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
@@ -754,8 +719,9 @@ Zlib License
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
|
||||
Bzip2 License
|
||||
-------------
|
||||
=============
|
||||
|
||||
This program, "bzip2", the associated library "libbzip2", and all
|
||||
documentation, are copyright (C) 1996-2005 Julian R Seward. All
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
build_exe.py run_game
|
||||
@@ -31,6 +31,7 @@ def main():
|
||||
'pygame.surfarray',
|
||||
'pygame.mixer',
|
||||
'pygame.mixer_music',
|
||||
'pygame.font',
|
||||
'_ssl',
|
||||
'_hashlib',
|
||||
'win32con',
|
||||
@@ -59,6 +60,9 @@ def main():
|
||||
if fn in seen:
|
||||
continue
|
||||
|
||||
if fn == "SDL_mixer.dll":
|
||||
continue
|
||||
|
||||
seen[fn] = True
|
||||
|
||||
zfnew.writestr(fn, zfold.read(fn))
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import glob
|
||||
import os
|
||||
|
||||
files = glob.glob("common/*.rpy")
|
||||
files.sort()
|
||||
|
||||
styles = [ ]
|
||||
|
||||
for fn in files:
|
||||
f = file(fn)
|
||||
|
||||
fi = iter(f)
|
||||
|
||||
for l in iter(fi):
|
||||
|
||||
l = l.strip()
|
||||
|
||||
if l.startswith("### "):
|
||||
|
||||
prefix, style, parent = l.split()
|
||||
|
||||
desc = ""
|
||||
|
||||
for l in fi:
|
||||
|
||||
l = l.strip()
|
||||
if not l.startswith("# "):
|
||||
break
|
||||
|
||||
desc = desc + " " + l[2:]
|
||||
|
||||
styles.append(' style.create("%s", "%s", "%s")' % (style, parent, desc[1:]))
|
||||
|
||||
f.close()
|
||||
|
||||
f = file("common/style.rpy")
|
||||
of = file("common/style.rpy.new", "w")
|
||||
|
||||
fi = iter(f)
|
||||
|
||||
for l in fi:
|
||||
|
||||
of.write(l)
|
||||
|
||||
if l.startswith("# AUTOMATICALLY GENERATED"):
|
||||
|
||||
for l in styles:
|
||||
of.write(l + "\n")
|
||||
|
||||
for l in fi:
|
||||
if l.startswith("# END AUTOMATICALLY GENERATED"):
|
||||
break
|
||||
|
||||
of.write(l)
|
||||
|
||||
f.close()
|
||||
of.close()
|
||||
|
||||
os.rename("common/style.rpy", "common/style.rpy.bak")
|
||||
os.rename("common/style.rpy.new", "common/style.rpy")
|
||||
@@ -54,6 +54,9 @@ init 1210 python hide::
|
||||
if compat(6, 2, 0):
|
||||
config.reject_backslash = False
|
||||
|
||||
if compat(6, 9, 0):
|
||||
style.motion.clear()
|
||||
|
||||
if "Fullscreen" in config.translations:
|
||||
fs = _("Fullscreen")
|
||||
config.translations.setdefault("Fullscreen 4:3", fs + " 4:3")
|
||||
|
||||
@@ -62,60 +62,69 @@ init -1110 python:
|
||||
return .5 - math.cos(math.pi * x) / 2.0
|
||||
|
||||
# This defines a family of move transitions.
|
||||
def move_transitions(prefix, delay, time_warp=None, in_time_warp=None, out_time_warp=None, old=False):
|
||||
def move_transitions(prefix, delay, time_warp=None, in_time_warp=None, out_time_warp=None, old=False, layers=[ 'master' ], **kwargs):
|
||||
moves = {
|
||||
"" : MoveTransition(
|
||||
delay,
|
||||
old=old,
|
||||
factory=MoveFactory(time_warp=time_warp)),
|
||||
layers=layers,
|
||||
factory=MoveFactory(time_warp=time_warp, **kwargs)),
|
||||
|
||||
"inright" : MoveTransition(
|
||||
delay,
|
||||
old=old,
|
||||
factory=MoveFactory(time_warp=time_warp),
|
||||
enter_factory=MoveIn((1.0, None, 0.0, None), time_warp=in_time_warp)),
|
||||
layers=layers,
|
||||
factory=MoveFactory(time_warp=time_warp, **kwargs),
|
||||
enter_factory=MoveIn((1.0, None, 0.0, None), time_warp=in_time_warp, **kwargs)),
|
||||
|
||||
"inleft" : MoveTransition(
|
||||
delay,
|
||||
old=old,
|
||||
factory=MoveFactory(time_warp=time_warp),
|
||||
enter_factory=MoveIn((0.0, None, 1.0, None), time_warp=in_time_warp)),
|
||||
layers=layers,
|
||||
factory=MoveFactory(time_warp=time_warp, **kwargs),
|
||||
enter_factory=MoveIn((0.0, None, 1.0, None), time_warp=in_time_warp, **kwargs)),
|
||||
|
||||
"intop" : MoveTransition(
|
||||
delay,
|
||||
old=old,
|
||||
factory=MoveFactory(time_warp=time_warp),
|
||||
enter_factory=MoveIn((None, 0.0, None, 1.0), time_warp=in_time_warp)),
|
||||
layers=layers,
|
||||
factory=MoveFactory(time_warp=time_warp, **kwargs),
|
||||
enter_factory=MoveIn((None, 0.0, None, 1.0), time_warp=in_time_warp, **kwargs)),
|
||||
|
||||
"inbottom" : MoveTransition(
|
||||
delay,
|
||||
old=old,
|
||||
factory=MoveFactory(time_warp=time_warp),
|
||||
enter_factory=MoveIn((None, 1.0, None, 0.0), time_warp=in_time_warp)),
|
||||
layers=layers,
|
||||
factory=MoveFactory(time_warp=time_warp, **kwargs),
|
||||
enter_factory=MoveIn((None, 1.0, None, 0.0), time_warp=in_time_warp, **kwargs)),
|
||||
|
||||
"outright" : MoveTransition(
|
||||
delay,
|
||||
old=old,
|
||||
factory=MoveFactory(time_warp=time_warp),
|
||||
leave_factory=MoveOut((1.0, None, 0.0, None), time_warp=out_time_warp)),
|
||||
layers=layers,
|
||||
factory=MoveFactory(time_warp=time_warp, **kwargs),
|
||||
leave_factory=MoveOut((1.0, None, 0.0, None), time_warp=out_time_warp, **kwargs)),
|
||||
|
||||
"outleft" : MoveTransition(
|
||||
delay,
|
||||
old=old,
|
||||
factory=MoveFactory(time_warp=time_warp),
|
||||
leave_factory=MoveOut((0.0, None, 1.0, None), time_warp=out_time_warp)),
|
||||
layers=layers,
|
||||
factory=MoveFactory(time_warp=time_warp, **kwargs),
|
||||
leave_factory=MoveOut((0.0, None, 1.0, None), time_warp=out_time_warp, **kwargs)),
|
||||
|
||||
"outtop" : MoveTransition(
|
||||
delay,
|
||||
old=old,
|
||||
factory=MoveFactory(time_warp=time_warp),
|
||||
leave_factory=MoveOut((None, 0.0, None, 1.0), time_warp=out_time_warp)),
|
||||
layers=layers,
|
||||
factory=MoveFactory(time_warp=time_warp, **kwargs),
|
||||
leave_factory=MoveOut((None, 0.0, None, 1.0), time_warp=out_time_warp, **kwargs)),
|
||||
|
||||
"outbottom" : MoveTransition(
|
||||
delay,
|
||||
old=old,
|
||||
factory=MoveFactory(time_warp=time_warp),
|
||||
leave_factory=MoveOut((None, 1.0, None, 0.0), time_warp=time_warp)),
|
||||
layers=layers,
|
||||
factory=MoveFactory(time_warp=time_warp, **kwargs),
|
||||
leave_factory=MoveOut((None, 1.0, None, 0.0), time_warp=time_warp, **kwargs)),
|
||||
}
|
||||
|
||||
for k, v in moves.iteritems():
|
||||
|
||||
@@ -18,7 +18,7 @@ init -1105 python:
|
||||
|
||||
# This takes care of actually displaying button menus.
|
||||
def _display_button_menu(menuitems):
|
||||
|
||||
|
||||
narration = [ s for s, i in menuitems if i is None and s ]
|
||||
menuitems = [ (s, i) for s, i in menuitems if i is not None or not s ]
|
||||
|
||||
|
||||
@@ -69,6 +69,12 @@ init -1180 python:
|
||||
|
||||
config.with_callback = _default_with_callback
|
||||
|
||||
|
||||
def _default_empty_window():
|
||||
store.narrator("", interact=False)
|
||||
|
||||
config.empty_window = _default_empty_window
|
||||
|
||||
style.skip_indicator = Style(style.default, heavy=True, help='The skip indicator.')
|
||||
style.skip_indicator.xpos = 10
|
||||
style.skip_indicator.ypos = 10
|
||||
@@ -284,7 +290,7 @@ label _hide_windows:
|
||||
python:
|
||||
_windows_hidden = True
|
||||
ui.saybehavior(dismiss=['dismiss', 'hide_windows'])
|
||||
ui.interact(suppress_overlay=True)
|
||||
ui.interact(suppress_overlay=True, suppress_window=True)
|
||||
_windows_hidden = False
|
||||
|
||||
return
|
||||
@@ -509,7 +515,7 @@ init 1180 python:
|
||||
|
||||
label _enter_menu:
|
||||
python hide:
|
||||
renpy.movie_stop()
|
||||
renpy.movie_stop(only_fullscreen=True)
|
||||
renpy.take_screenshot((config.thumbnail_width, config.thumbnail_height))
|
||||
|
||||
renpy.context()._menu = True
|
||||
@@ -518,9 +524,11 @@ label _enter_menu:
|
||||
renpy.context().main_menu = False
|
||||
renpy.context_dynamic("main_menu")
|
||||
renpy.context_dynamic("_window_subtitle")
|
||||
|
||||
renpy.context_dynamic("_window")
|
||||
|
||||
store.main_menu = False
|
||||
store._window_subtitle = config.menu_window_subtitle
|
||||
store._window = False
|
||||
|
||||
store.mouse_visible = True
|
||||
store.suppress_overlay = True
|
||||
@@ -599,7 +607,7 @@ label _return:
|
||||
|
||||
if renpy.context().main_menu:
|
||||
$ renpy.transition(config.game_main_transition)
|
||||
jump _main_menu
|
||||
jump _main_menu_screen
|
||||
|
||||
$ renpy.transition(config.exit_transition)
|
||||
|
||||
@@ -696,14 +704,14 @@ label _start:
|
||||
|
||||
label _invoke_main_menu:
|
||||
|
||||
$ renpy.call_in_new_context("_enter_main_menu")
|
||||
$ renpy.call_in_new_context("_main_menu")
|
||||
|
||||
# If the main menu returns, then start the game.
|
||||
jump start
|
||||
|
||||
# At this point, we've been switched into a new context. So we
|
||||
# initialize it.
|
||||
label _enter_main_menu:
|
||||
label _main_menu(_main_menu_screen="_main_menu_screen"):
|
||||
|
||||
call _enter_menu from _call__enter_menu_1
|
||||
|
||||
@@ -713,9 +721,11 @@ label _enter_main_menu:
|
||||
|
||||
renpy.context().main_menu = True
|
||||
store.main_menu = True
|
||||
|
||||
jump expression _main_menu_screen
|
||||
|
||||
# This is called to show the main menu to the user.
|
||||
label _main_menu:
|
||||
label _main_menu_screen:
|
||||
|
||||
# Let the user completely override the main menu. (But please note
|
||||
# it still lives in the menu context, rather than the game context.)
|
||||
|
||||
@@ -29,6 +29,8 @@ init -1150 python:
|
||||
]
|
||||
|
||||
label _quit_prompt:
|
||||
$ renpy.loadsave.force_autosave()
|
||||
|
||||
if layout.yesno_prompt(None, u"Are you sure you want to quit?"):
|
||||
jump _quit
|
||||
else:
|
||||
|
||||
@@ -1,17 +1,29 @@
|
||||
# This file contains code that sets up the various mixers, based on how
|
||||
# the user sets config.has_music, .has_sound, and .has_voice.
|
||||
|
||||
init -1130:
|
||||
|
||||
init -1130 python hide:
|
||||
|
||||
# Set to true in the very unlikely event you want to manually init
|
||||
# the sound system.
|
||||
$ config.force_sound = False
|
||||
config.force_sound = False
|
||||
|
||||
# basics: True if the game will have music.
|
||||
$ config.has_music = True
|
||||
config.has_music = True
|
||||
|
||||
# basics: True if the game will have sound effects.
|
||||
$ config.has_sound = True
|
||||
config.has_sound = True
|
||||
|
||||
# Register 8 channels by default, for compatiblity with older version
|
||||
# of Ren'Py.
|
||||
for i in xrange(0, 8):
|
||||
renpy.music.register_channel(i)
|
||||
|
||||
renpy.music.register_channel("movie", "music", False, stop_on_mute=False)
|
||||
|
||||
# Set up default names for some of the channels.
|
||||
renpy.music.alias_channel(0, "sound")
|
||||
renpy.music.alias_channel(7, "music")
|
||||
renpy.music.alias_channel(2, "voice")
|
||||
|
||||
init 1130:
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ init -1100 python:
|
||||
return s
|
||||
|
||||
def nvl_show_core():
|
||||
|
||||
|
||||
if renpy.in_rollback():
|
||||
nvl_window = __s(style.nvl_window)['rollback']
|
||||
nvl_vbox = __s(style.nvl_vbox)['rollback']
|
||||
@@ -99,8 +99,14 @@ init -1100 python:
|
||||
rv = config.nvl_show_display_say(who, what, variant=nvl_variant, **kw)
|
||||
|
||||
ui.close()
|
||||
|
||||
renpy.shown_window()
|
||||
|
||||
return rv
|
||||
|
||||
def nvl_window():
|
||||
nvl_show_core()
|
||||
|
||||
def nvl_show(with_):
|
||||
nvl_show_core()
|
||||
renpy.with_statement(with_)
|
||||
@@ -208,7 +214,6 @@ init -1100 python:
|
||||
kind=adv)
|
||||
|
||||
def nvl_clear():
|
||||
|
||||
store.nvl_list = [ ]
|
||||
|
||||
# Run clear at the start of the game.
|
||||
@@ -219,6 +224,10 @@ init -1100 python:
|
||||
if nvl_list is None:
|
||||
store.nvl_list = [ ]
|
||||
|
||||
ui.layer("transient")
|
||||
ui.clear()
|
||||
ui.close()
|
||||
|
||||
ui.window(style=__s(style.nvl_window))
|
||||
ui.vbox(style=__s(style.nvl_vbox))
|
||||
|
||||
|
||||
@@ -164,7 +164,6 @@ python early hide:
|
||||
|
||||
renpy.music.queue(eval(p["file"]), channel=channel)
|
||||
|
||||
|
||||
renpy.statements.register('queue music',
|
||||
parse=parse_queue_music,
|
||||
execute=execute_queue_music,
|
||||
@@ -254,10 +253,131 @@ python early hide:
|
||||
parse=parse_stop_music,
|
||||
execute=execute_stop_sound)
|
||||
|
||||
|
||||
# Generic play/queue/stop statements. These take a channel name as
|
||||
# the second thing.
|
||||
|
||||
def parse_play_generic(l, parse_play_music=parse_play_music):
|
||||
channel = l.name()
|
||||
|
||||
if channel is None:
|
||||
renpy.error('play requires a channel')
|
||||
|
||||
rv = parse_play_music(l)
|
||||
if rv["channel"] is None:
|
||||
rv["channel"] = repr(channel)
|
||||
|
||||
return rv
|
||||
|
||||
def parse_queue_generic(l, parse_queue_music=parse_queue_music):
|
||||
channel = l.name()
|
||||
|
||||
if channel is None:
|
||||
renpy.error('queue requires a channel')
|
||||
|
||||
rv = parse_queue_music(l)
|
||||
if rv["channel"] is None:
|
||||
rv["channel"] = repr(channel)
|
||||
|
||||
return rv
|
||||
|
||||
def parse_stop_generic(l, parse_stop_music=parse_stop_music):
|
||||
channel = l.name()
|
||||
|
||||
if channel is None:
|
||||
renpy.error('stop requires a channel')
|
||||
|
||||
rv = parse_stop_music(l)
|
||||
if rv["channel"] is None:
|
||||
rv["channel"] = repr(channel)
|
||||
|
||||
return rv
|
||||
|
||||
def lint_play_generic(p, lint_play_music=lint_play_music):
|
||||
channel = eval(p["channel"])
|
||||
|
||||
if not renpy.music.channel_defined(channel):
|
||||
renpy.error("channel %r is not defined" % channel)
|
||||
|
||||
lint_play_music(p)
|
||||
|
||||
def lint_stop_generic(p):
|
||||
channel = eval(p["channel"])
|
||||
|
||||
if not renpy.music.channel_defined(channel):
|
||||
renpy.error("channel %r is not defined" % channel)
|
||||
|
||||
renpy.statements.register('play',
|
||||
parse=parse_play_generic,
|
||||
execute=execute_play_music,
|
||||
predict=predict_play_music,
|
||||
lint=lint_play_generic)
|
||||
|
||||
renpy.statements.register('queue',
|
||||
parse=parse_queue_generic,
|
||||
execute=execute_queue_music,
|
||||
lint=lint_play_generic)
|
||||
|
||||
renpy.statements.register('stop',
|
||||
parse=parse_stop_generic,
|
||||
execute=execute_stop_music,
|
||||
lint=lint_stop_generic)
|
||||
|
||||
|
||||
# The "window show" and "window hide" statements.
|
||||
def parse_window(l):
|
||||
p = l.simple_expression()
|
||||
if not l.eol():
|
||||
renpy.error('expected end of line')
|
||||
|
||||
return p
|
||||
|
||||
def lint_window(p):
|
||||
if p is not None:
|
||||
_try_eval(p, 'window transition')
|
||||
|
||||
def execute_window_show(p):
|
||||
if store._window:
|
||||
return
|
||||
|
||||
if p is not None:
|
||||
trans = eval(p)
|
||||
else:
|
||||
trans = config.window_show_transition
|
||||
|
||||
renpy.with_statement(None)
|
||||
store._window = True
|
||||
renpy.with_statement(trans)
|
||||
|
||||
def execute_window_hide(p):
|
||||
if not _window:
|
||||
return
|
||||
|
||||
if p is not None:
|
||||
trans = eval(p)
|
||||
else:
|
||||
trans = config.window_hide_transition
|
||||
|
||||
renpy.with_statement(None)
|
||||
store._window = False
|
||||
renpy.with_statement(trans)
|
||||
|
||||
renpy.statements.register('window show',
|
||||
parse=parse_window,
|
||||
execute=execute_window_show,
|
||||
lint=lint_window)
|
||||
|
||||
renpy.statements.register('window hide',
|
||||
parse=parse_window,
|
||||
execute=execute_window_hide,
|
||||
lint=lint_window)
|
||||
|
||||
|
||||
init -1200 python:
|
||||
|
||||
config.window_show_transition = None
|
||||
config.window_hide_transition = None
|
||||
|
||||
def _try_eval(e, what):
|
||||
try:
|
||||
return eval(e)
|
||||
|
||||
@@ -33,6 +33,7 @@ init -1200 python hide:
|
||||
style.image_placement = Style(style.default, help='default placement of images on the screen')
|
||||
style.image = Style(style.default, help="default style of images")
|
||||
|
||||
style.motion = Style(style.default, help="default style of motions and zooms.")
|
||||
style.animation = Style(style.default, help="default style of animations.")
|
||||
|
||||
style.say_label = Style(style.default, help='the name of the character speaking dialogue.')
|
||||
@@ -68,6 +69,7 @@ init -1200 python hide:
|
||||
style.hyperlink_text = Style(style.default, help='hyperlinked text')
|
||||
|
||||
style.viewport = Style(style.default, help='default style of viewports')
|
||||
style.transform = Style(style.motion, help='default style of transforms')
|
||||
|
||||
style.list = Style(style.default)
|
||||
style.list_box = Style(style.vbox)
|
||||
@@ -166,7 +168,8 @@ init -1090 python:
|
||||
style.default.ymaximum = None
|
||||
style.default.xoffset = 0
|
||||
style.default.yoffset = 0
|
||||
|
||||
style.default.subpixel = False
|
||||
|
||||
# Sound properties.
|
||||
style.default.sound = None
|
||||
|
||||
@@ -197,6 +200,10 @@ init -1090 python:
|
||||
style.hbox.box_layout = 'horizontal'
|
||||
style.vbox.box_layout = 'vertical'
|
||||
|
||||
# Motions, zooms, rotozooms, and transforms.
|
||||
style.motion.xanchor = 0
|
||||
style.motion.yanchor = 0
|
||||
|
||||
# Windows.
|
||||
style.window.background = Solid((0, 0, 0, 192))
|
||||
style.window.xpadding = 6
|
||||
@@ -263,6 +270,9 @@ init -1090 python:
|
||||
# Viewport
|
||||
style.viewport.clipping = True
|
||||
|
||||
# Transform
|
||||
style.transform.subpixel = True
|
||||
|
||||
# Menu windows.
|
||||
style.mm_root.background = "#000"
|
||||
style.mm_root.xfill = True
|
||||
|
||||
@@ -298,13 +298,19 @@ init -1110 python hide:
|
||||
|
||||
# Compat for the old roundrect theme.
|
||||
window = None,
|
||||
button_menu = True,
|
||||
button_menu = None,
|
||||
rounded_window = True,
|
||||
outline_bars = False,
|
||||
|
||||
mm_root = None,
|
||||
gm_root = None):
|
||||
|
||||
if button_menu is None:
|
||||
if (config.script_version is not None) and (config.script_version < (6, 9, 0)):
|
||||
button_menu = True
|
||||
else:
|
||||
button_menu = False
|
||||
|
||||
layout.defaults()
|
||||
|
||||
if config.screen_width <= 640:
|
||||
|
||||
@@ -52,7 +52,7 @@ init -1120:
|
||||
# This is called on each interaction, to ensure that the
|
||||
# appropriate voice file is played for the user.
|
||||
def voice_interact():
|
||||
|
||||
|
||||
if not config.has_voice:
|
||||
return
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ init python:
|
||||
|
||||
class _ImageMapper(object):
|
||||
|
||||
def __init__(self, ground, idle, hover, selected_idle, selected_hover, hotspots):
|
||||
def __init__(self, screen, ground, idle, hover, selected_idle, selected_hover, hotspots):
|
||||
|
||||
self.idle = idle
|
||||
self.hover = hover
|
||||
@@ -13,10 +13,29 @@ init python:
|
||||
for (x1, y1, x2, y2, name) in hotspots:
|
||||
self.hotspots[name] = (x1, y1, x2, y2)
|
||||
|
||||
# Display the layout navigation only if there are no
|
||||
# game menu buttons defined.
|
||||
for i in config.game_menu:
|
||||
if i[1] in self.hotspots:
|
||||
break
|
||||
else:
|
||||
layout.navigation(screen)
|
||||
|
||||
ui.fixed(style='imagemap')
|
||||
ui.image(ground)
|
||||
|
||||
# Display any navigation buttons that exist.
|
||||
for e in config.game_menu:
|
||||
screen_ = e[0]
|
||||
name = e[1]
|
||||
act = e[2]
|
||||
enable = e[3]
|
||||
|
||||
if not eval(enable):
|
||||
continue
|
||||
|
||||
self.button(name, act, screen == screen_)
|
||||
|
||||
def button(self, name, clicked, selected):
|
||||
|
||||
if name not in self.hotspots:
|
||||
@@ -32,8 +51,8 @@ init python:
|
||||
hover = self.selected_hover
|
||||
else:
|
||||
idle = self.idle
|
||||
hover = self.selected_hover
|
||||
|
||||
hover = self.hover
|
||||
|
||||
ui.imagebutton(
|
||||
LiveCrop((x1, y1, (x2 - x1), (y2 - y1)), idle),
|
||||
LiveCrop((x1, y1, (x2 - x1), (y2 - y1)), hover),
|
||||
|
||||
@@ -233,10 +233,8 @@ init python:
|
||||
|
||||
__scratch.file_picker_page = fpp
|
||||
|
||||
# Show navigation
|
||||
layout.navigation(screen)
|
||||
|
||||
ime = _ImageMapper(
|
||||
screen,
|
||||
config.load_save_ground,
|
||||
config.load_save_idle,
|
||||
config.load_save_hover,
|
||||
|
||||
@@ -44,9 +44,12 @@ label main_menu_screen:
|
||||
if isinstance(act, basestring):
|
||||
act = ui.jumpsoutofcontext(act)
|
||||
act()
|
||||
|
||||
break
|
||||
else:
|
||||
ui.jumpsoutofcontext(rv)()
|
||||
|
||||
|
||||
jump main_menu_screen
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -39,9 +39,8 @@ init python:
|
||||
|
||||
def __show_preferences():
|
||||
|
||||
layout.navigation("prefs")
|
||||
|
||||
ime = _ImageMapper(
|
||||
"prefs",
|
||||
config.preferences_ground,
|
||||
config.preferences_idle,
|
||||
config.preferences_hover,
|
||||
|
||||
@@ -17,9 +17,8 @@ init python hide:
|
||||
def yesno_prompt(screen, message):
|
||||
renpy.transition(config.intra_transition)
|
||||
|
||||
layout.navigation(screen)
|
||||
|
||||
ime = _ImageMapper(
|
||||
screen,
|
||||
config.yesno_prompt_ground,
|
||||
config.yesno_prompt_idle,
|
||||
config.yesno_prompt_hover,
|
||||
|
||||
@@ -6,6 +6,8 @@ init:
|
||||
image logo crop = im.Crop("logo.png", 0, 0, 100, 307)
|
||||
|
||||
image logo scale = im.Scale("logo.png", 100, 150)
|
||||
|
||||
image logo factorscale = im.FactorScale("logo.png", 1.5, 1.5)
|
||||
|
||||
image logo composite = im.Composite((200, 407),
|
||||
(0, 0), "logo.png",
|
||||
@@ -101,8 +103,13 @@ label demo_imageops:
|
||||
show logo scale at logopos
|
||||
with dissolve
|
||||
|
||||
e "The im.Scale operation lets us change the size of images."
|
||||
e "The im.Scale operation lets us scale an image to a particular size."
|
||||
|
||||
show logo factorscale at logopos
|
||||
with dissolve
|
||||
|
||||
e "im.FactorScale lets us do the same thing, except to a factor of the original size."
|
||||
|
||||
show logo green at logopos
|
||||
with dissolve
|
||||
|
||||
|
||||
@@ -223,6 +223,8 @@ label demo_minigame:
|
||||
|
||||
e "Now, why don't we play some pong?"
|
||||
|
||||
window hide None
|
||||
|
||||
label demo_minigame_pong:
|
||||
|
||||
# Put up the pong background, in the usual fashion.
|
||||
@@ -232,11 +234,13 @@ label demo_minigame_pong:
|
||||
python:
|
||||
ui.add(PongDisplayable())
|
||||
winner = ui.interact(suppress_overlay=True, suppress_underlay=True)
|
||||
|
||||
|
||||
scene bg washington
|
||||
show eileen vhappy
|
||||
|
||||
|
||||
window show None
|
||||
|
||||
|
||||
if winner == "eileen":
|
||||
|
||||
e "I win!"
|
||||
|
||||
@@ -62,6 +62,15 @@ label demo_movement:
|
||||
|
||||
e "Move can repeat a movement, and even have it bounce back and forth, like I'm doing now."
|
||||
|
||||
|
||||
show eileen happy at Move((.25, 1.0, 0.0, 1.0),
|
||||
(.75, 1.0, 1.0, 1.0),
|
||||
10.0, repeat=True, bounce=True, subpixel=True)
|
||||
|
||||
with dissolve
|
||||
|
||||
e "Our subpixel mode allows slow moves, of less than a few pixels a second, to be very accurate."
|
||||
|
||||
scene bg onememorial at Pan((0, 800), (0, 0), 10.0)
|
||||
with dissolve
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# support. Right now, this is just showing off sound and music, but
|
||||
# Ren'Py does support movies, and we'll add them sometime later.
|
||||
|
||||
image movie = Movie(size=(320, 240), xpos=475, ypos=50, xanchor=0, yanchor=0)
|
||||
|
||||
label demo_multimedia:
|
||||
|
||||
@@ -22,18 +23,35 @@ label demo_multimedia:
|
||||
# This plays a sound effect.
|
||||
play sound "18005551212.ogg"
|
||||
|
||||
e "We can also play up to eight channels of sound effects on top of the music."
|
||||
e "We can play sound effects on top of the music."
|
||||
|
||||
$ renpy.music.set_pan(-1, 0)
|
||||
$ renpy.music.set_pan( 1, 2)
|
||||
|
||||
e "We can pan the music back and forth."
|
||||
|
||||
$ renpy.music.set_pan(0, .5)
|
||||
|
||||
e "Voice support is included as part of Ren'Py, although we don't yet have a demonstration."
|
||||
|
||||
e "We also support playing mpeg movies, but we don't have one to show you right now."
|
||||
|
||||
show eileen concerned
|
||||
|
||||
e "Sorry."
|
||||
|
||||
show eileen happy
|
||||
$ renpy.music.set_volume(0, .5)
|
||||
|
||||
e "Finally, we support playing movie files."
|
||||
|
||||
show movie behind eileen
|
||||
play movie "shuttle.ogv"
|
||||
|
||||
e "We can put a movie on the screen with text and other images."
|
||||
|
||||
stop movie
|
||||
hide movie
|
||||
|
||||
e "We can also play the movie in fullscreen... but with this tiny movie we're using as an example, it's going to look bad."
|
||||
|
||||
$ renpy.movie_cutscene("shuttle.ogv", stop_music=False)
|
||||
|
||||
$ renpy.music.set_volume(1, 1.0)
|
||||
|
||||
e "That's about it for multimedia."
|
||||
|
||||
return
|
||||
|
||||
@@ -10,6 +10,8 @@ init:
|
||||
|
||||
label demo_nvlmode:
|
||||
|
||||
window hide
|
||||
|
||||
nvl clear
|
||||
nvl show dissolve
|
||||
|
||||
@@ -72,6 +74,7 @@ label demo_nvlmode:
|
||||
$ menu = renpy.display_menu
|
||||
|
||||
nvl hide dissolve
|
||||
window show
|
||||
|
||||
return
|
||||
|
||||
|
||||
@@ -92,6 +92,8 @@ label demo_text:
|
||||
|
||||
eoutline "You can have more than one outline, and each has its own color and offset."
|
||||
|
||||
window hide
|
||||
|
||||
esubtitle "Here, we have two outlines around the white text."
|
||||
|
||||
esubtitle "The bottom one is a translucent black that's offset a little, while the top one is green."
|
||||
@@ -99,7 +101,9 @@ label demo_text:
|
||||
esubtitle "By hiding the window and adjusting the layout method, we are able to create reasonable subtitles."
|
||||
|
||||
esubtitle "This might be an interesting look for a game."
|
||||
|
||||
|
||||
window show
|
||||
|
||||
esfont "For even more control, Ren'Py supports SFonts, image files containing font information."
|
||||
|
||||
esfont "SFonts let you use fonts you otherwise couldn't, and apply special effects to fonts using your favorite image editor."
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
# This is the code for the logo example.
|
||||
init python:
|
||||
|
||||
# This spins the logo, while at the same time zooming it and decreasing the
|
||||
# alpha.
|
||||
def logo_transform(t, st, at):
|
||||
|
||||
# Repeat every 10 seconds.
|
||||
st = st % 7.0
|
||||
|
||||
# The move takes 5 seconds.
|
||||
done = min(st / 5.0, 1.0)
|
||||
|
||||
t.xpos = done
|
||||
t.xanchor = 1.0 - done
|
||||
t.ypos = .5
|
||||
t.yanchor = .5
|
||||
t.rotate = 360 * done
|
||||
t.alpha = 1.0 - done
|
||||
t.zoom = 1.0 + done
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
# This is the code for the balls example. It's a bit complicated, but most of
|
||||
# this is the code for ball movement and so on. Only a very little bit of this
|
||||
# actually deals with Ren'Py.
|
||||
init python:
|
||||
import math
|
||||
|
||||
class Ball(object):
|
||||
def __init__(self, filename, x, y, function=None):
|
||||
|
||||
self.transform = Transform(child=filename, xpos=x, ypos=y, xanchor=0.5, yanchor=0.5, rotate=0, function=function)
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
||||
MAX_SPEED = 150
|
||||
|
||||
self.dx = renpy.random.uniform(-MAX_SPEED, MAX_SPEED)
|
||||
self.dy = renpy.random.uniform(-MAX_SPEED, MAX_SPEED)
|
||||
|
||||
# Rotation speed.
|
||||
self.drotate = renpy.random.uniform(0, 180)
|
||||
|
||||
# This is called
|
||||
def balls_collide(p1, p2):
|
||||
"""
|
||||
Check to see if any of the balls are colliding. If they are,
|
||||
then handle the collision.
|
||||
"""
|
||||
|
||||
DOUBLE_RADIUS = 75
|
||||
|
||||
x21 = p2.x - p1.x
|
||||
y21 = p2.y - p1.y
|
||||
|
||||
d = math.hypot(x21, y21)
|
||||
|
||||
# Return if too far.
|
||||
if d > DOUBLE_RADIUS:
|
||||
return
|
||||
|
||||
vx21 = p2.dx - p1.dx
|
||||
vy21 = p2.dy - p1.dy
|
||||
|
||||
# Return if not approaching.
|
||||
if (vx21 * x21 + vy21 * y21) > 0:
|
||||
return
|
||||
|
||||
# Fix divide by zero.
|
||||
if x21 == 0:
|
||||
x21 = .00001
|
||||
|
||||
# Compute the collision.
|
||||
a = y21 / x21
|
||||
dvx2 = -(vx21 + a * vy21) / (1 + a * a)
|
||||
|
||||
p2.dx += dvx2
|
||||
p2.dy += a * dvx2
|
||||
|
||||
p1.dx -= dvx2
|
||||
p2.dy -= a * dvx2
|
||||
|
||||
# This is called by the first transform. It updates all of the
|
||||
# transforms.
|
||||
def balls_update(pilot, st, at):
|
||||
|
||||
global last_time
|
||||
|
||||
RADIUS = 75 / 2
|
||||
LEFT = RADIUS
|
||||
RIGHT = 800 - RADIUS
|
||||
TOP = RADIUS
|
||||
BOTTOM = 600 - RADIUS
|
||||
|
||||
# The pilot is the first ball in our list, and he's the one
|
||||
# that gets last_time updated.
|
||||
if last_time is None:
|
||||
dt = 0
|
||||
else:
|
||||
dt = st - last_time
|
||||
|
||||
last_time = st
|
||||
|
||||
# Handle current collisions.
|
||||
for i in xrange(0, len(balls)):
|
||||
for j in xrange(i + 1, len(balls)):
|
||||
balls_collide(balls[i], balls[j])
|
||||
|
||||
# Basic movement, and bouncing off the walls.
|
||||
for i in balls:
|
||||
|
||||
i.x += i.dx * dt
|
||||
i.y += i.dy * dt
|
||||
|
||||
if i.x < LEFT:
|
||||
i.x = LEFT
|
||||
i.dx = abs(i.dx)
|
||||
|
||||
if i.x > RIGHT:
|
||||
i.x = RIGHT
|
||||
i.dx = -abs(i.dx)
|
||||
|
||||
if i.y < TOP:
|
||||
i.y = TOP
|
||||
i.dy = abs(i.dy)
|
||||
|
||||
if i.y > BOTTOM:
|
||||
i.y = BOTTOM
|
||||
i.dy = -abs(i.dy)
|
||||
|
||||
|
||||
# Update the transforms.
|
||||
for i in balls:
|
||||
|
||||
# This is the code that deals with Ren'Py to update the
|
||||
# various transforms. Note that we use absolute coordinates
|
||||
# to position ourselves with subpixel accuracy.
|
||||
i.transform.xpos = absolute(i.x)
|
||||
i.transform.ypos = absolute(i.y)
|
||||
i.transform.rotate = (i.drotate * st) % 360.0
|
||||
i.transform.update()
|
||||
|
||||
return 0
|
||||
|
||||
# These are used in the button example:
|
||||
init python:
|
||||
def button_transform(t, st, at):
|
||||
t.rotate = (90 * st) % 360.0
|
||||
return 0
|
||||
|
||||
label demo_transform:
|
||||
|
||||
e "The Transform function allows you to rotate, zoom, move, and adjust the alpha of a displayable."
|
||||
|
||||
e "It does this under the control of a Python function, making it incredibly flexible at the cost of some complexity."
|
||||
|
||||
hide eileen
|
||||
with dissolve
|
||||
|
||||
show logo base at Transform(function=logo_transform)
|
||||
|
||||
e "Here's a simple example, showing how we can change an image as it moves around the screen."
|
||||
|
||||
e "A nice thing about Transform is that it's \"one price\"."
|
||||
|
||||
e "If you use it to do a rotation, you can zoom or adjust alpha at no additional cost."
|
||||
|
||||
hide logo base
|
||||
with dissolve
|
||||
|
||||
python:
|
||||
|
||||
last_time = None
|
||||
|
||||
# Define a list of ball objects.
|
||||
balls = [
|
||||
Ball("eileen_orb.png", 200, 150, function=balls_update),
|
||||
Ball("lucy_orb.png", 400, 150),
|
||||
Ball("eileen_orb.png", 600, 150),
|
||||
|
||||
Ball("lucy_orb.png", 200, 300),
|
||||
Ball("lucy_orb.png", 600, 300),
|
||||
|
||||
Ball("eileen_orb.png", 200, 450),
|
||||
Ball("lucy_orb.png", 400, 450),
|
||||
Ball("eileen_orb.png", 600, 450),
|
||||
]
|
||||
|
||||
# Add each ball's transform to the screen.
|
||||
for i, b in enumerate(balls):
|
||||
renpy.show("ball%d" % i, what=b.transform)
|
||||
|
||||
with dissolve
|
||||
|
||||
e "As the python functions get more complicated, more advanced behavior is possible."
|
||||
|
||||
e "This can include coordinating more than one Transform."
|
||||
|
||||
python:
|
||||
for i, b in enumerate(balls):
|
||||
renpy.hide("ball%d" % i)
|
||||
|
||||
|
||||
with dissolve
|
||||
|
||||
python hide:
|
||||
ui.transform(function=button_transform, xalign=0.5, yalign=0.5)
|
||||
ui.textbutton("A Working Button", clicked=ui.returns(True))
|
||||
|
||||
e "Finally, transforms can be applied to buttons, and work even while the button is zoomed."
|
||||
|
||||
show eileen happy
|
||||
with dissolve
|
||||
|
||||
e "With a little Python code, transforms let you do a lot of things."
|
||||
|
||||
|
||||
|
||||
return
|
||||
|
||||
|
||||
@@ -90,7 +90,9 @@ init:
|
||||
button(face, editing == i, ("edit", i), xminimum=250)
|
||||
|
||||
ui.null(height=20)
|
||||
ui.textbutton("Continue", clicked=ui.returns(("done", True)), xminimum=250)
|
||||
ui.textbutton("Continue",
|
||||
clicked=ui.returns(("done", True)),
|
||||
xminimum=250)
|
||||
ui.null(height=20)
|
||||
ui.close()
|
||||
|
||||
@@ -98,28 +100,30 @@ init:
|
||||
# Choice window.
|
||||
if editing:
|
||||
ui.frame(xpos=300,
|
||||
ypos=200,
|
||||
xanchor='left',
|
||||
yanchor='top',
|
||||
xfill=False,
|
||||
xminimum=500
|
||||
)
|
||||
ypos=200,
|
||||
xanchor='left',
|
||||
yanchor='top',
|
||||
xfill=False,
|
||||
xminimum=500,
|
||||
xmargin = 10
|
||||
)
|
||||
|
||||
ui.vbox()
|
||||
ui.text("What will you do in the %s?" % editing.lower())
|
||||
ui.null(height=20)
|
||||
|
||||
for i in choices:
|
||||
button(i, plan[editing] == i, ("set", i),
|
||||
xpos=0, xanchor='left')
|
||||
button(i,
|
||||
plan[editing] == i,
|
||||
("set", i),
|
||||
xpos=0,
|
||||
xanchor='left',
|
||||
xminimum=250)
|
||||
|
||||
ui.close()
|
||||
|
||||
# Window at the bottom.
|
||||
ui.window()
|
||||
ui.vbox()
|
||||
ui.text("To get to the next screen, click the 'Continue' button.")
|
||||
ui.close()
|
||||
e("To get to the next screen, click the 'Continue' button.", interact=False)
|
||||
|
||||
type, value = ui.interact()
|
||||
|
||||
@@ -199,6 +203,8 @@ label demo_ui:
|
||||
|
||||
e "Imagemaps let the user click on an image to make a choice. For example, the following screen lets you pick what to do after school:"
|
||||
|
||||
window hide None
|
||||
|
||||
# This is an imagemap. It consists of two images, and a list of
|
||||
# hotspots. For each hotspot we give the coordinates of the left,
|
||||
# top, right, and bottom sides, and the value to return if it is
|
||||
@@ -215,6 +221,8 @@ label demo_ui:
|
||||
# result variable. We can use an if statement to vary what
|
||||
# happens based on the user's choice.
|
||||
|
||||
window show None
|
||||
|
||||
if result == "swimming":
|
||||
|
||||
e "You chose swimming."
|
||||
@@ -242,6 +250,8 @@ label demo_ui:
|
||||
e "We also support viewports, that allow us to display things that are bigger then the screen."
|
||||
|
||||
e "This viewport can be adjusted by dragging, by the mouse wheel, and by the scrollbars."
|
||||
|
||||
window hide
|
||||
|
||||
show eileen happy at right
|
||||
with move
|
||||
@@ -262,15 +272,23 @@ label demo_ui:
|
||||
|
||||
show eileen happy at center
|
||||
with move
|
||||
|
||||
|
||||
window show
|
||||
|
||||
e "While these constructs are probably enough for most visual novels, dating simulations may be more complicated."
|
||||
|
||||
e "The ui functions allow you to create quite complicated interfaces."
|
||||
|
||||
e "For example, try the following scheduling and stats screen, which could be used by a stat-based dating simulation."
|
||||
|
||||
hide eileen
|
||||
with dissolve
|
||||
|
||||
$ day_planner()
|
||||
|
||||
show eileen happy
|
||||
with dissolve
|
||||
|
||||
e "For a better implementation of this, take a look at the dating sim engine (DSE) that ships with Ren'Py."
|
||||
|
||||
call fight("Eileen", 10, 99, pname=povname) from _call_fight_1
|
||||
|
||||
@@ -8,14 +8,14 @@ init:
|
||||
("demo_basics", "Basic Scripting", "5.6.3"),
|
||||
("demo_experience", "User Experience", "5.6.3"),
|
||||
("demo_transitions", "Transitions", "5.6.6"),
|
||||
("demo_movement", "Positions and Movement", "6.7.0"),
|
||||
("demo_movement", "Positions and Movement", "6.9.0"),
|
||||
("demo_animation", "Animation", "6.2.0"),
|
||||
("demo_multimedia", "Music and Sound", "5.6.3"),
|
||||
("demo_multimedia", "Music, Sound, and Video", "6.9.1"),
|
||||
("demo_imageops", "Image Operations", "6.5.0"),
|
||||
("demo_ui", "User Interaction", "6.5.0"),
|
||||
("demo_text", "Fonts and Text Tags", "6.8.0"),
|
||||
("demo_character", "Character Objects", "6.2.0"),
|
||||
("demo_advanced", "Advanced Features", "6.7.0"),
|
||||
("demo_advanced", "Advanced Features", "6.9.0"),
|
||||
]
|
||||
|
||||
# Update above with this!
|
||||
@@ -25,6 +25,7 @@ init:
|
||||
("demo_dynamic", "Dynamic Displayables", "5.6.3"),
|
||||
("demo_minigame", "Minigames", "6.3.2"),
|
||||
("demo_persistent", "Persistent Data", "6.7.0"),
|
||||
("demo_transform", "Transform", "6.9.0"),
|
||||
]
|
||||
|
||||
def demos_show(demos_info, nevermind):
|
||||
@@ -63,7 +64,7 @@ label demos:
|
||||
|
||||
# Is this the first time through the demo?
|
||||
$ demo_first_time = True
|
||||
|
||||
|
||||
show eileen happy at left
|
||||
with move
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 11 KiB |
@@ -235,6 +235,12 @@ init -1 python hide:
|
||||
## Used when a game is loaded.
|
||||
config.after_load_transition = dissolve
|
||||
|
||||
## Used when the window is shown.
|
||||
config.window_show_transition = Dissolve(.25)
|
||||
|
||||
## Used when the window is hidden.
|
||||
config.window_hide_transition = Dissolve(.25)
|
||||
|
||||
#########################################
|
||||
## This is the name of the directory where the game's data is
|
||||
## stored. (It needs to be set early, before any other init code
|
||||
@@ -256,7 +262,9 @@ init -1 python hide:
|
||||
## The default text speed in characters per second. 0 is infinite.
|
||||
config.default_text_cps = 0
|
||||
|
||||
|
||||
config.window_icon = "logo.png"
|
||||
config.windows_icon = "logo32.png"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ label start:
|
||||
|
||||
# Start the background music playing.
|
||||
play music "mozart.ogg"
|
||||
|
||||
window show
|
||||
|
||||
e "Hi, and welcome to the Ren'Py demo game."
|
||||
|
||||
|
||||
@@ -87,11 +87,7 @@ def tree(root):
|
||||
|
||||
if ".svn" in dirs:
|
||||
dirs.remove(".svn")
|
||||
else:
|
||||
if not root == "lib":
|
||||
print "Note:", dirname, "not in subversion."
|
||||
|
||||
|
||||
|
||||
for f in filenames:
|
||||
if f[-1] == '~' or f[0] == '.':
|
||||
continue
|
||||
@@ -155,32 +151,27 @@ def main():
|
||||
module_files = [
|
||||
"lib/pysdlsound/linmixer.py",
|
||||
"lib/pysdlsound/__init__.py",
|
||||
"README.txt",
|
||||
"_renpy.pyx",
|
||||
"_renpy.c",
|
||||
"core.c",
|
||||
"mmx.h",
|
||||
"native_midi.h",
|
||||
"native_midi_common.c",
|
||||
"native_midi_common.h",
|
||||
"native_midi_mac.c",
|
||||
"native_midi_win32.c",
|
||||
"nativemidi.c",
|
||||
"nativemidi.pyx",
|
||||
"pss.c",
|
||||
"pss.h",
|
||||
"sound.c",
|
||||
"sound.pyx",
|
||||
"rwobject.c",
|
||||
"renpy.h",
|
||||
"setup.py",
|
||||
"winmixer.c",
|
||||
"winmixer.pyx",
|
||||
"ffdecode.c",
|
||||
"IMG_savepng.c",
|
||||
"IMG_savepng.h",
|
||||
"mmx.h",
|
||||
"pss.c",
|
||||
"pss.h",
|
||||
"README.txt",
|
||||
"_renpy.c",
|
||||
"renpy_font.c",
|
||||
"renpy.h",
|
||||
"_renpy.pyx",
|
||||
"renpy_ttf.c",
|
||||
"renpy_ttf.h",
|
||||
"rwobject.c",
|
||||
"setup.py",
|
||||
"sound.c",
|
||||
"sound.pyx",
|
||||
"subpixel.c",
|
||||
"winmixer.c",
|
||||
"winmixer.pyx",
|
||||
]
|
||||
|
||||
for i in module_files:
|
||||
@@ -235,8 +226,9 @@ def main():
|
||||
|
||||
print
|
||||
print "Did you remember to rebuild the exe after the last change?"
|
||||
print "Did you run me with renpython -O?"
|
||||
print "Did you run me with renpython -OO?"
|
||||
print "Was ming using the right crt?"
|
||||
print "Did you update renpy.py and launcher/script_version.rpy?"
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -63,7 +63,7 @@ init -100 python:
|
||||
if name:
|
||||
layout.label(s.name, "stat")
|
||||
else:
|
||||
ui.nul()
|
||||
ui.null()
|
||||
|
||||
if value and max:
|
||||
layout.label("%d/%d" % (v, s.max), "stat_value")
|
||||
|
||||
|
Before Width: | Height: | Size: 4.3 KiB |
@@ -1,9 +0,0 @@
|
||||
init:
|
||||
image black = Solid((0, 0, 0, 255))
|
||||
|
||||
label main_menu:
|
||||
|
||||
$ renpy.renpy.style.write_docs("doc/styles.xml")
|
||||
$ renpy.renpy.style.write_hierarchy("doc/style_heirarchy.xml")
|
||||
$ raise "foo"
|
||||
|
||||
@@ -29,9 +29,10 @@ margin-left: -2.5em;
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="renpy_help"></div>
|
||||
<p><a name="Basic_Help" id="Basic_Help"></a></p>
|
||||
<h3><span class="mw-headline">Basic Help</span></h3>
|
||||
<h2><span class="mw-headline">Basic Help</span></h2>
|
||||
<p>To advance through the game, <tt>left-click</tt> or press the <tt>space</tt> or <tt>enter</tt> keys. When at a menu, <tt>left-click</tt> to make a choice, or use the arrow keys to select a choice and <tt>enter</tt> to activate it.</p>
|
||||
<p><a name="Game_Menu" id="Game_Menu"></a></p>
|
||||
<h3><span class="mw-headline">Game Menu</span></h3>
|
||||
@@ -105,5 +106,8 @@ margin-left: -2.5em;
|
||||
<dt>Alt-F4, Command-Q</dt>
|
||||
<dd>Quits the game.</dd>
|
||||
</dl>
|
||||
<p><a name="Legal_Notice" id="Legal_Notice"></a></p>
|
||||
<h2><span class="mw-headline">Legal Notice</span></h2>
|
||||
<p>This game uses source code from a number of open source projects. For a list, and a location where the source code can be downloaded from, please view the <a href="./renpy/LICENSE.txt">LICENSE.txt</a> file in the renpy directory.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -36,7 +36,7 @@ init python:
|
||||
"manifest.xml"
|
||||
)
|
||||
|
||||
def ignored(fn, dir, dest):
|
||||
def ignored(fn, dir, dest, root):
|
||||
if fn[0] == ".":
|
||||
return True
|
||||
|
||||
@@ -53,7 +53,7 @@ init python:
|
||||
if fn == "archived":
|
||||
return True
|
||||
|
||||
if dir == dest:
|
||||
if root and dir == dest:
|
||||
if fn.endswith(".py") or fn.endswith(".sh") or fn.endswith(".app"):
|
||||
return True
|
||||
|
||||
@@ -66,7 +66,8 @@ init python:
|
||||
exclude_suffix=[ ".pyc", "~", ".bak" ],
|
||||
exclude_prefix=[ ".", '#' ],
|
||||
exclude=ignored_files,
|
||||
exclude_func=None,
|
||||
exclude_func=ignored,
|
||||
root=False,
|
||||
):
|
||||
|
||||
# Get rid of trailing slashes.
|
||||
@@ -90,7 +91,7 @@ init python:
|
||||
if i == fn.lower():
|
||||
return False
|
||||
|
||||
if exclude_func and exclude_func(fn, destdir, dest):
|
||||
if exclude_func and exclude_func(fn, destdir, dest, root):
|
||||
return False
|
||||
|
||||
return True
|
||||
@@ -232,9 +233,7 @@ label distribute:
|
||||
if not interact():
|
||||
renpy.jump("tools")
|
||||
|
||||
default_name = project.name
|
||||
if persistent.build_project == project.name:
|
||||
default_name = persistent.build_name
|
||||
default_name = project.info.get('distribution_base', project.name)
|
||||
|
||||
name = prompt(u"Building Distributions",
|
||||
u"Please enter a base name for the directories making up this distribution.",
|
||||
@@ -252,14 +251,14 @@ label distribute:
|
||||
except:
|
||||
store.error(u"Error", u"Distribution names must be ASCII. This is because archive file formats do not support non-ASCII characters in a uniform way.", "tools_menu")
|
||||
|
||||
persistent.build_project = project.name
|
||||
persistent.build_name = name
|
||||
project.info['distribution_base'] = name
|
||||
|
||||
ignore_extensions = persistent.ignore_extensions or "~ .bak"
|
||||
ignore_extensions = project.info.get('ignore_extensions', "~ .bak")
|
||||
ignore_extensions = prompt(u"Building Distributions", u"Please enter a space separated list of the file extensions you do not want included in the distribution.", "tools", ignore_extensions)
|
||||
persistent.ignore_extensions = ignore_extensions
|
||||
store.ignore_extensions = [ i.strip() for i in ignore_extensions.strip().split() ]
|
||||
project.info['ignore_extensions'] = ignore_extensions
|
||||
|
||||
project.save()
|
||||
|
||||
# Figure out the files that will make up the distribution.
|
||||
|
||||
@@ -279,7 +278,7 @@ label distribute:
|
||||
exclude_suffix = [ ],
|
||||
exclude_prefix = [ ],
|
||||
exclude=[ ],
|
||||
exclude_func = project.info["ignored"]))
|
||||
root=True))
|
||||
|
||||
shortgamedir = project.gamedir[len(project.path)+1:]
|
||||
|
||||
@@ -360,7 +359,6 @@ label distribute:
|
||||
|
||||
linux_files.extend(tree(config.renpy_base + "/lib/linux-x86", "lib/linux-x86"))
|
||||
|
||||
|
||||
tf = tarfile.open(name + "-linux-x86.tar.bz2", "w:bz2")
|
||||
tf.dereference = True
|
||||
|
||||
|
||||
@@ -10,14 +10,17 @@ init:
|
||||
if not config.editor:
|
||||
|
||||
editor = os.path.normpath(config.renpy_base + "/jedit/jedit.jar")
|
||||
editor = renpy.shell_escape(editor)
|
||||
|
||||
if sys.platform == 'win32':
|
||||
config.editor = 'javaw.exe -jar "' + editor + '" -reuseview "%(filename)s" +line:%(line)d "%(otherfiles)s"'
|
||||
config.editor_transient = 'javaw.exe -jar "' + editor + '" -newplainview "%(filename)s" +line:%(line)d "%(otherfiles)s"'
|
||||
else:
|
||||
config.editor = 'java -jar "' + editor + '" -reuseview "%(filename)s" +line:%(line)d "%(otherfiles)s"'
|
||||
config.editor_transient = 'java -jar "' + editor + '" -newplainview "%(filename)s" +line:%(line)d "%(otherfiles)s"'
|
||||
if os.path.exists(editor):
|
||||
|
||||
editor = renpy.shell_escape(editor)
|
||||
|
||||
if sys.platform == 'win32':
|
||||
config.editor = 'javaw.exe -jar "' + editor + '" -reuseview "%(filename)s" +line:%(line)d "%(otherfiles)s"'
|
||||
config.editor_transient = 'javaw.exe -jar "' + editor + '" -newplainview "%(filename)s" +line:%(line)d "%(otherfiles)s"'
|
||||
else:
|
||||
config.editor = 'java -jar "' + editor + '" -reuseview "%(filename)s" +line:%(line)d "%(otherfiles)s"'
|
||||
config.editor_transient = 'java -jar "' + editor + '" -newplainview "%(filename)s" +line:%(line)d "%(otherfiles)s"'
|
||||
|
||||
if config.editor:
|
||||
os.environ['RENPY_EDITOR'] = config.editor
|
||||
|
||||
@@ -231,7 +231,21 @@ init:
|
||||
title = re.sub(r'\s+\S', upper, title)
|
||||
self.title = title
|
||||
|
||||
def save(self):
|
||||
import os
|
||||
|
||||
f = file(self.path + "/" + "launcherinfo.py.new", "w")
|
||||
for k, v in self.info.iteritems():
|
||||
f.write("%s = %r\n" % (k, v))
|
||||
f.close()
|
||||
|
||||
try:
|
||||
os.rename(self.path + "/launcherinfo.py.new", self.path + "/launcherinfo.py")
|
||||
except:
|
||||
os.unlink(self.path + "/launcherinfo.py")
|
||||
os.rename(self.path + "/launcherinfo.py.new", self.path + "/launcherinfo.py")
|
||||
|
||||
|
||||
def load_project(dir, name):
|
||||
import os.path
|
||||
|
||||
@@ -251,7 +265,6 @@ init:
|
||||
|
||||
info = dict()
|
||||
info["description"] = desc
|
||||
info["ignored"] = ignored
|
||||
|
||||
if os.path.exists(dir + "/launcherinfo.py"):
|
||||
source = file(dir + "/launcherinfo.py", "rU").read().decode("utf8")
|
||||
@@ -261,6 +274,8 @@ init:
|
||||
|
||||
exec source in info
|
||||
|
||||
del info["__builtins__"]
|
||||
|
||||
store.projects.append(Project(name, dir, gamedir, info))
|
||||
|
||||
def load_projects():
|
||||
@@ -516,10 +531,11 @@ label game_directory:
|
||||
import renpy.subprocess as subprocess
|
||||
subprocess.Popen([ "open", gamedir ])
|
||||
else:
|
||||
store.message = _(u"Opening the game directory is not supported on this platform.\n%s") % gamedir
|
||||
import renpy.subprocess as subprocess
|
||||
subprocess.Popen([ "xdg-open", gamedir ])
|
||||
|
||||
|
||||
gamedir = "".join(quote(i) for i in gamedir)
|
||||
|
||||
gamedir = "".join(quote(i) for i in gamedir)
|
||||
store.message = _(u"Opening game directory:\n%s") % gamedir
|
||||
|
||||
jump top_menu
|
||||
@@ -599,21 +615,23 @@ label archive_files:
|
||||
|
||||
import renpy.tools.archiver as archiver
|
||||
|
||||
extensions = persistent.archive_extensions or "*.png *.gif *.jpg"
|
||||
extensions = project.info.get('archive_extensions', "*.png *.gif *.jpg")
|
||||
extensions = prompt(u"Archiving Files", u"Please enter a space separated list of the file patterns you want archived.", "tools", extensions)
|
||||
if not extensions.strip():
|
||||
renpy.jumps("tools_menu")
|
||||
persistent.archive_extensions = extensions
|
||||
project.info['archive_extensions'] = extensions
|
||||
extensions = [ i.strip() for i in extensions.strip().split() ]
|
||||
|
||||
|
||||
base = persistent.archive_base or "data"
|
||||
base = project.info.get('archive_base', "data")
|
||||
base = prompt(u"Archiving Files", u"Please enter the name of the archive file, without the .rpa extension.", "tools", base)
|
||||
base = base.strip()
|
||||
if not base:
|
||||
renpy.jumps("tools_menu")
|
||||
persistent.archive_base = base
|
||||
project.info['archive_base'] = base
|
||||
|
||||
project.save()
|
||||
|
||||
# Tell the user we're archiving images.
|
||||
title(u"Archiving Files")
|
||||
store.message = u"Please wait while we archive files."
|
||||
|
||||
@@ -57,7 +57,7 @@ label new:
|
||||
if d.startswith("."):
|
||||
dirs.remove(d)
|
||||
dn = dir + "/" + d
|
||||
shutil.rmtree(dn)
|
||||
shutil.rmtree(dn, True)
|
||||
|
||||
# Change the save directory.
|
||||
options = file(name + "/game/options.rpy").read()
|
||||
|
||||
@@ -7,31 +7,38 @@ import sys
|
||||
import array
|
||||
|
||||
# The starting point of the resource segment in the file.
|
||||
RESOURCE_BASE=0x3400
|
||||
RESOURCE_BASE=0x3600
|
||||
|
||||
# The virtual memory location the resource segment is loaded
|
||||
# to.
|
||||
RESOURCE_VIRTUAL=0x6000
|
||||
|
||||
# The offset of the field that tells us with the alignment need be.
|
||||
ALIGNMENT_FIELD = 0x138
|
||||
ALIGNMENT_FIELD = 0x130
|
||||
|
||||
# The start of the .rsrc segment header.
|
||||
RSRC_HEADER = 0x268
|
||||
|
||||
# Locations in the file where we need to patch in the resource
|
||||
# segment length.
|
||||
RESOURCE_LENGTH_PATCHES = [ 0x018c, 0x278 ]
|
||||
RESOURCE_LENGTH_PATCHES = [ 0x0184, RSRC_HEADER + 0x8 ]
|
||||
|
||||
# Locations in the file where we need to patch in the padded
|
||||
# resource segment length.
|
||||
RESOURCE_PADDED_PATCHES = [ 0x280 ]
|
||||
RESOURCE_PADDED_PATCHES = [ RSRC_HEADER + 0x10 ]
|
||||
|
||||
# Locations in the file where we need to patch in the change in the
|
||||
# size of the resource segement (and hence the change in the file's
|
||||
# total size.)
|
||||
SIZE_DELTA_PATCHES = [ 0x150 ]
|
||||
SIZE_DELTA_PATCHES = [ 0x148 ]
|
||||
|
||||
# A location in the file that will be checked to be sure it matches
|
||||
# RESOURCE_BASE.
|
||||
CHECK_ADDRESS = 0x284
|
||||
CHECK_ADDRESS = RSRC_HEADER + 0x14
|
||||
|
||||
# A location in the file that will be checked to make sure it matches
|
||||
# RESOURCE_VIRTUAL
|
||||
CHECK_ADDRESS2 = RSRC_HEADER + 0xc
|
||||
|
||||
# This class performs various operations on memory-loaded binary files,
|
||||
# including modifications.
|
||||
@@ -285,10 +292,14 @@ def change_icons(oldexe, icofn):
|
||||
global pe
|
||||
pe = BinFile(oldexe)
|
||||
|
||||
# Check that RESOURCE_BASE is still valid.
|
||||
# Check that RESOURCE_BASE and RESOURCE_VIRTUAL are still valid.
|
||||
pe.seek(CHECK_ADDRESS)
|
||||
if pe.u32() != RESOURCE_BASE:
|
||||
raise Exception("RESOURCE_BASE is no longer correct. Please check all relocations.")
|
||||
raise Exception("RESOURCE_BASE is no longer correct. Please check all relocations.")
|
||||
|
||||
pe.seek(CHECK_ADDRESS2)
|
||||
if pe.u32() != RESOURCE_VIRTUAL:
|
||||
raise Exception("RESOURCE_VIRTUAL is no longer correct. Please check all relocations.")
|
||||
|
||||
resources = parse_directory(0)
|
||||
# show_resources(resources, "")
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
init -999:
|
||||
$ config.script_version = (6, 8, 0)
|
||||
$ config.script_version = (6, 9, 1)
|
||||
|
||||
|
||||
@@ -69,7 +69,8 @@ cdef extern from "renpy.h":
|
||||
float, float, float, float)
|
||||
|
||||
void transform32_core(object, object,
|
||||
float, float, float, float, float, float)
|
||||
float, float, float, float, float, float,
|
||||
int, float)
|
||||
|
||||
void blend32_core(object, object, object, int)
|
||||
|
||||
@@ -84,6 +85,8 @@ cdef extern from "renpy.h":
|
||||
void staticgray_core(object, object,
|
||||
int, int, int, int, int, char *)
|
||||
|
||||
int subpixel32(object, object, float, float, int)
|
||||
|
||||
void PyErr_Clear()
|
||||
|
||||
|
||||
@@ -93,7 +96,7 @@ PygameSurface = pygame.Surface
|
||||
|
||||
# Update this in 00library.rpy as well!
|
||||
def version():
|
||||
return 6007001
|
||||
return 6009000
|
||||
|
||||
def save_png(surf, file, compress=-1):
|
||||
|
||||
@@ -102,7 +105,7 @@ def save_png(surf, file, compress=-1):
|
||||
|
||||
save_png_core(surf, RWopsFromPython(file), compress)
|
||||
|
||||
|
||||
|
||||
def pixellate(pysrc, pydst, avgwidth, avgheight, outwidth, outheight):
|
||||
|
||||
if not isinstance(pysrc, PygameSurface):
|
||||
@@ -329,7 +332,7 @@ def check(surf):
|
||||
|
||||
def transform(pysrc, pydst,
|
||||
corner_x, corner_y,
|
||||
xdx, ydx, xdy, ydy):
|
||||
xdx, ydx, xdy, ydy, a=1.0):
|
||||
|
||||
check(pysrc)
|
||||
check(pydst)
|
||||
@@ -340,7 +343,8 @@ def transform(pysrc, pydst,
|
||||
transform32_core(pysrc, pydst,
|
||||
corner_x, corner_y,
|
||||
xdx, ydx,
|
||||
xdy, ydy)
|
||||
xdy, ydy,
|
||||
pysrc.get_shifts()[3], a)
|
||||
|
||||
# pydst.unlock()
|
||||
# pysrc.unlock()
|
||||
@@ -405,6 +409,14 @@ def colormatrix(pysrc, pydst,
|
||||
def staticgray(pysrc, pydst, rmul, gmul, bmul, amul, shift, vmap):
|
||||
PyErr_Clear()
|
||||
staticgray_core(pysrc, pydst, rmul, gmul, bmul, amul, shift, vmap)
|
||||
|
||||
|
||||
def subpixel(pysrc, pydst, xoffset, yoffset, shift):
|
||||
|
||||
if subpixel32(pysrc, pydst, xoffset, yoffset, shift):
|
||||
return
|
||||
|
||||
pydst.blit(pysrc, (int(xoffset), int(yoffset)))
|
||||
|
||||
|
||||
# Be sure to update scale.py when adding something new here!
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "renpy.h"
|
||||
#include <pygame/pygame.h>
|
||||
#include "IMG_savepng.h"
|
||||
#include <pygame/pygame.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
@@ -30,7 +30,6 @@ void save_png_core(PyObject *pysurf, SDL_RWops *rw, int compress) {
|
||||
IMG_SavePNG_RW(rw, surf, compress);
|
||||
}
|
||||
|
||||
|
||||
/* This pixellates a 32-bit RGBA pygame surface to a destination
|
||||
* surface of a given size.
|
||||
*
|
||||
@@ -960,13 +959,16 @@ void scale24_core(PyObject *pysrc, PyObject *pydst,
|
||||
Py_END_ALLOW_THREADS
|
||||
}
|
||||
|
||||
#define I(a, b, mul) ((((((b - a) * mul)) >> 8) + a) & 0xff00ff)
|
||||
|
||||
/****************************************************************************/
|
||||
/* A similar concept to rotozoom, but implemented differently, so we
|
||||
can limit the target area. */
|
||||
void transform32_core(PyObject *pysrc, PyObject *pydst,
|
||||
float corner_x, float corner_y,
|
||||
float xdx, float ydx,
|
||||
float xdy, float ydy) {
|
||||
void transform32_std(PyObject *pysrc, PyObject *pydst,
|
||||
float corner_x, float corner_y,
|
||||
float xdx, float ydx,
|
||||
float xdy, float ydy,
|
||||
int ashift, float a) {
|
||||
|
||||
SDL_Surface *src;
|
||||
SDL_Surface *dst;
|
||||
@@ -986,7 +988,7 @@ void transform32_core(PyObject *pysrc, PyObject *pydst,
|
||||
dst = PySurface_AsSurface(pydst);
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
|
||||
|
||||
srcpixels = (unsigned char *) src->pixels;
|
||||
dstpixels = (unsigned char *) dst->pixels;
|
||||
srcpitch = src->pitch;
|
||||
@@ -996,6 +998,8 @@ void transform32_core(PyObject *pysrc, PyObject *pydst,
|
||||
srch = src->h;
|
||||
dsth = dst->h;
|
||||
|
||||
unsigned int amul = (int) (a * 256);
|
||||
|
||||
lsx = corner_x * 256;
|
||||
lsy = corner_y * 256;
|
||||
|
||||
@@ -1008,8 +1012,7 @@ void transform32_core(PyObject *pysrc, PyObject *pydst,
|
||||
// Scaled subtracted srcw and srch.
|
||||
float fsw = (srcw - 2) * 256;
|
||||
float fsh = (srch - 2) * 256;
|
||||
|
||||
|
||||
|
||||
for (y = 0; y < dsth; y++, lsx += xdy, lsy += ydy) {
|
||||
|
||||
sx = lsx;
|
||||
@@ -1028,7 +1031,6 @@ void transform32_core(PyObject *pysrc, PyObject *pydst,
|
||||
minx = fmaxf(minx, fminf(d1, d2));
|
||||
maxx = fminf(maxx, fmaxf(d1, d2));
|
||||
|
||||
// printf("ZZZ1 %f %f\n", d1, d2);
|
||||
} else if ( lsx < 0 || lsx >= fsw) {
|
||||
continue;
|
||||
}
|
||||
@@ -1039,7 +1041,6 @@ void transform32_core(PyObject *pysrc, PyObject *pydst,
|
||||
|
||||
minx = fmaxf(minx, fminf(d1, d2));
|
||||
maxx = fminf(maxx, fmaxf(d1, d2));
|
||||
// printf("ZZZ2 %f %f\n", d1, d2);
|
||||
} else if ( lsy < 0 || lsy >= fsh) {
|
||||
continue;
|
||||
}
|
||||
@@ -1048,108 +1049,316 @@ void transform32_core(PyObject *pysrc, PyObject *pydst,
|
||||
continue;
|
||||
}
|
||||
|
||||
// printf("CCC %f %f\n", minx, maxx);
|
||||
|
||||
minx = ceil(minx);
|
||||
maxx = floor(maxx);
|
||||
|
||||
// printf("CCC2 %f %f\n", minx, maxx);
|
||||
// printf("XXX %f %f %f %f\n", lsx / 256, xdx / 256, lsy / 256, ydx/256);
|
||||
|
||||
|
||||
// printf("%f %f\n", minx, maxx);
|
||||
|
||||
// printf("Minx at: %f %f\n", (lsx + xdx * minx) / 256, (lsy + ydx * minx) / 256);
|
||||
|
||||
|
||||
unsigned char *d = dstpixels + dstpitch * y;
|
||||
unsigned char *dend = d + 4 * (int) maxx;
|
||||
d += 4 * (int) minx;
|
||||
|
||||
sx += minx * xdx;
|
||||
sy += minx * ydx;
|
||||
sx = lsx + minx * xdx;
|
||||
sy = lsy + minx * ydx;
|
||||
|
||||
|
||||
while (d <= dend) {
|
||||
int sxi = (int) sx;
|
||||
int syi = (int) sy;
|
||||
int xdxi = (int) xdx;
|
||||
int ydxi = (int) ydx;
|
||||
|
||||
int px, py, sxi, syi;
|
||||
sxi = ((int) sx);
|
||||
syi = ((int) sy);
|
||||
while (d <= dend) {
|
||||
int px, py;
|
||||
px = sxi >> 8;
|
||||
py = syi >> 8;
|
||||
|
||||
unsigned char *sp = srcpixels + py * srcpitch + px * 4;
|
||||
|
||||
int yfrac = syi & 0xff; // ((short) sy) & 0xff;
|
||||
int xfrac = sxi & 0xff; // ((short) sx) & 0xff;
|
||||
|
||||
/* These bounds are checked analytically, hence the if (1)
|
||||
* { */
|
||||
unsigned int pal = *(unsigned int *) sp;
|
||||
unsigned int pbl = *(unsigned int *) (sp + 4);
|
||||
sp += srcpitch;
|
||||
unsigned int pcl = *(unsigned int *) sp;
|
||||
unsigned int pdl = *(unsigned int *) (sp + 4);
|
||||
|
||||
unsigned int pah = (pal >> 8) & 0xff00ff;
|
||||
unsigned int pbh = (pbl >> 8) & 0xff00ff;
|
||||
unsigned int pch = (pcl >> 8) & 0xff00ff;
|
||||
unsigned int pdh = (pdl >> 8) & 0xff00ff;
|
||||
|
||||
pal &= 0xff00ff;
|
||||
pbl &= 0xff00ff;
|
||||
pcl &= 0xff00ff;
|
||||
pdl &= 0xff00ff;
|
||||
|
||||
unsigned int rh = I(I(pah, pch, yfrac), I(pbh, pdh, yfrac), xfrac);
|
||||
unsigned int rl = I(I(pal, pcl, yfrac), I(pbl, pdl, yfrac), xfrac);
|
||||
|
||||
unsigned int alpha = (((rh << 8) | rl) >> ashift) & 0xff;
|
||||
alpha = (alpha * amul) >> 8;
|
||||
|
||||
// if (0 <= px && px < srcw - 1 && 0 <= py && py < srch - 1) {
|
||||
if (1) {
|
||||
|
||||
unsigned char *sp = srcpixels + py * srcpitch + px * 4;
|
||||
unsigned int dl = * (unsigned int *) d;
|
||||
unsigned int dh = (dl >> 8) & 0xff00ff;
|
||||
dl &= 0xff00ff;
|
||||
|
||||
// unsigned char *s1p = s0p + srcpitch;
|
||||
|
||||
int s1frac = syi & 0xff; // ((short) sy) & 0xff;
|
||||
int xfrac = sxi & 0xff; // ((short) sx) & 0xff;
|
||||
|
||||
#define I(a, b, mul) ((((((b - a) * mul)) >> 8) + a) & 0xff00ff)
|
||||
|
||||
unsigned int rl, rh;
|
||||
|
||||
unsigned int pal = *(unsigned int *) sp;
|
||||
unsigned int pbl = *(unsigned int *) (sp + 4);
|
||||
sp += srcpitch;
|
||||
unsigned int pcl = *(unsigned int *) sp;
|
||||
unsigned int pdl = *(unsigned int *) (sp + 4);
|
||||
|
||||
unsigned int pah = (pal >> 8) & 0xff00ff;
|
||||
unsigned int pbh = (pbl >> 8) & 0xff00ff;
|
||||
unsigned int pch = (pcl >> 8) & 0xff00ff;
|
||||
unsigned int pdh = (pdl >> 8) & 0xff00ff;
|
||||
|
||||
pal &= 0xff00ff;
|
||||
pbl &= 0xff00ff;
|
||||
pcl &= 0xff00ff;
|
||||
pdl &= 0xff00ff;
|
||||
|
||||
// if (y == 10)
|
||||
// printf("%x %x %x %x\n", pah, pbh, pch, pdh);
|
||||
|
||||
|
||||
rh = I(I(pah, pch, s1frac), I(pbh, pdh, s1frac), xfrac);
|
||||
rl = I(I(pal, pcl, s1frac), I(pbl, pdl, s1frac), xfrac);
|
||||
// rl = 0;
|
||||
|
||||
// if (y == 10)
|
||||
// printf("%x\n", rh);
|
||||
|
||||
* (unsigned int *) d = (rh << 8) | rl;
|
||||
|
||||
d += 4;
|
||||
|
||||
|
||||
} else {
|
||||
// This code can't execute.
|
||||
|
||||
printf("Failed x %f (%d, %d)\n", minx, px, py);
|
||||
|
||||
*d++ = 0;
|
||||
*d++ = 0;
|
||||
*d++ = 0;
|
||||
*d++ = 0;
|
||||
|
||||
}
|
||||
dl = I(dl, rl, alpha);
|
||||
dh = I(dh, rh, alpha);
|
||||
|
||||
sx += xdx;
|
||||
sy += ydx;
|
||||
// minx++;
|
||||
* (unsigned int *) d = (dh << 8) | dl;
|
||||
|
||||
d += 4;
|
||||
sxi += xdxi;
|
||||
syi += ydxi;
|
||||
}
|
||||
}
|
||||
|
||||
Py_END_ALLOW_THREADS
|
||||
}
|
||||
|
||||
#ifdef GCC_MMX
|
||||
|
||||
/****************************************************************************/
|
||||
/* A similar concept to rotozoom, but implemented differently, so we
|
||||
can limit the target area. */
|
||||
int transform32_mmx(PyObject *pysrc, PyObject *pydst,
|
||||
float corner_x, float corner_y,
|
||||
float xdx, float ydx,
|
||||
float xdy, float ydy,
|
||||
int ashift,
|
||||
float a) {
|
||||
|
||||
SDL_Surface *src;
|
||||
SDL_Surface *dst;
|
||||
|
||||
int y;
|
||||
int srcpitch, dstpitch;
|
||||
|
||||
int srcw, srch;
|
||||
int dstw, dsth;
|
||||
|
||||
unsigned char *srcpixels;
|
||||
unsigned char *dstpixels;
|
||||
|
||||
float lsx, lsy; // The position of the current line in the source.
|
||||
float sx, sy; // The position of the current pixel in the source.
|
||||
unsigned int px, py;
|
||||
unsigned int sxi, syi;
|
||||
unsigned int xdxi, ydxi;
|
||||
|
||||
src = PySurface_AsSurface(pysrc);
|
||||
dst = PySurface_AsSurface(pydst);
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
|
||||
srcpixels = (unsigned char *) src->pixels;
|
||||
dstpixels = (unsigned char *) dst->pixels;
|
||||
srcpitch = src->pitch;
|
||||
dstpitch = dst->pitch;
|
||||
srcw = src->w;
|
||||
dstw = dst->w;
|
||||
srch = src->h;
|
||||
dsth = dst->h;
|
||||
|
||||
// Due to mmx.
|
||||
ashift *= 2;
|
||||
|
||||
// Compute the coloring multiplier.
|
||||
unsigned int amul = (unsigned int) (a * 256);
|
||||
|
||||
lsx = corner_x * 256;
|
||||
lsy = corner_y * 256;
|
||||
|
||||
xdx *= 256;
|
||||
ydx *= 256;
|
||||
xdy *= 256;
|
||||
ydy *= 256;
|
||||
|
||||
// Scaled subtracted srcw and srch.
|
||||
float fsw = (srcw - 2) * 256;
|
||||
float fsh = (srch - 2) * 256;
|
||||
|
||||
for (y = 0; y < dsth; y++, lsx += xdy, lsy += ydy) {
|
||||
|
||||
float minx = 0;
|
||||
float maxx = dstw - 1;
|
||||
|
||||
if (xdx != 0) {
|
||||
float d1 = -lsx / xdx;
|
||||
float d2 = (fsw - lsx) / xdx;
|
||||
|
||||
minx = fmaxf(minx, fminf(d1, d2));
|
||||
maxx = fminf(maxx, fmaxf(d1, d2));
|
||||
|
||||
} else if ( lsx < 0 || lsx >= fsw) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ydx != 0) {
|
||||
float d1 = -lsy / ydx;
|
||||
float d2 = (fsh - lsy) / ydx;
|
||||
|
||||
minx = fmaxf(minx, fminf(d1, d2));
|
||||
maxx = fminf(maxx, fmaxf(d1, d2));
|
||||
} else if ( lsy < 0 || lsy >= fsh) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (minx > maxx) {
|
||||
continue;
|
||||
}
|
||||
|
||||
minx = ceil(minx);
|
||||
maxx = floor(maxx);
|
||||
|
||||
unsigned char *d = dstpixels + dstpitch * y;
|
||||
unsigned char *dend = d + 4 * (int) maxx;
|
||||
d += 4 * (int) minx;
|
||||
|
||||
sx = (lsx + minx * xdx);
|
||||
sy = (lsy + minx * ydx);
|
||||
|
||||
sxi = (unsigned int) sx;
|
||||
syi = (unsigned int) sy;
|
||||
xdxi = (int) xdx;
|
||||
ydxi = (int) ydx;
|
||||
|
||||
// No floating point allowed between here and the end of the
|
||||
// while loop.
|
||||
|
||||
// 0 -> mm7
|
||||
pxor_r2r(mm7, mm7);
|
||||
|
||||
// ashift -> mm0
|
||||
movd_m2r(ashift, mm0);
|
||||
|
||||
// amul -> mm4
|
||||
movd_m2r(amul, mm4);
|
||||
|
||||
while (d <= dend) {
|
||||
|
||||
px = sxi >> 8;
|
||||
py = syi >> 8;
|
||||
|
||||
unsigned char *sp = srcpixels + py * srcpitch + px * 4;
|
||||
|
||||
unsigned int yfrac = syi & 0xff; // ((short) sy) & 0xff;
|
||||
unsigned int xfrac = sxi & 0xff; // ((short) sx) & 0xff;
|
||||
|
||||
// Put xfrac in mm5, yfrac in m6
|
||||
pxor_r2r(mm5, mm5);
|
||||
pxor_r2r(mm6, mm6);
|
||||
movd_m2r(xfrac, mm5);
|
||||
movd_m2r(yfrac, mm6);
|
||||
punpcklwd_r2r(mm5, mm5);
|
||||
punpcklwd_r2r(mm6, mm6);
|
||||
punpckldq_r2r(mm5, mm5); /* 0X0X0X0X -> mm5 */
|
||||
punpckldq_r2r(mm6, mm6); /* 0Y0Y0Y0Y -> mm6 */
|
||||
|
||||
// Load in the 4 bytes.
|
||||
movd_m2r(*(unsigned int *) sp, mm1);
|
||||
movd_m2r(*(unsigned int *) (sp + 4), mm2);
|
||||
punpcklbw_r2r(mm7, mm1);
|
||||
punpcklbw_r2r(mm7, mm2);
|
||||
|
||||
// Interpolate between a and b.
|
||||
psubw_r2r(mm1, mm2);
|
||||
pmullw_r2r(mm5, mm2);
|
||||
psrlw_i2r(8, mm2);
|
||||
paddb_r2r(mm2, mm1); /* mm1 contains I(a, b, xfrac); */
|
||||
|
||||
sp += srcpitch;
|
||||
movd_m2r(*(unsigned int *) sp, mm3);
|
||||
movd_m2r(*(unsigned int *) (sp + 4), mm2);
|
||||
|
||||
punpcklbw_r2r(mm7, mm3);
|
||||
punpcklbw_r2r(mm7, mm2);
|
||||
|
||||
// Interpolate between c and d.
|
||||
psubw_r2r(mm3, mm2);
|
||||
pmullw_r2r(mm5, mm2);
|
||||
psrlw_i2r(8, mm2);
|
||||
paddb_r2r(mm2, mm3); /* mm3 contains I(c, d, xfrac); */
|
||||
|
||||
// Interpolate between ab and cd.
|
||||
psubw_r2r(mm1, mm3);
|
||||
pmullw_r2r(mm6, mm3);
|
||||
psrlw_i2r(8, mm3);
|
||||
paddb_r2r(mm3, mm1); /* mm1 contains I(ab, cd, yfrac) */
|
||||
|
||||
|
||||
// Store the result.
|
||||
// packuswb_r2r(mm7, mm1);
|
||||
// movd_r2m(mm1, *(unsigned int *)d);
|
||||
|
||||
// Alpha blend with dest.
|
||||
movq_r2r(mm1, mm3);
|
||||
psrlq_r2r(mm0, mm3); /* 000000AA -> m3 */
|
||||
|
||||
pmullw_r2r(mm4, mm3); // Alpha adjustment.
|
||||
psrlw_i2r(8, mm3);
|
||||
|
||||
punpcklwd_r2r(mm3, mm3); /* 0000AAAA -> m3 */
|
||||
movd_m2r(*(unsigned int *)d, mm2);
|
||||
punpcklwd_r2r(mm3, mm3); /* AAAAAAAA -> m3 */
|
||||
punpcklbw_r2r(mm7, mm2); /* a -> m2 */
|
||||
psubw_r2r(mm2, mm1); /* b - a -> m1 */
|
||||
pmullw_r2r(mm3, mm1);
|
||||
psrlw_i2r(8, mm1); /* alpha * (b-a) -> m1 */
|
||||
paddb_r2r(mm2, mm1); /* a + alpha*(b-a) -> mm1 */
|
||||
|
||||
// Store the result.
|
||||
packuswb_r2r(mm7, mm1);
|
||||
movd_r2m(mm1, *(unsigned int *)d);
|
||||
|
||||
d += 4;
|
||||
sxi += xdxi;
|
||||
syi += ydxi;
|
||||
}
|
||||
|
||||
emms();
|
||||
}
|
||||
|
||||
Py_END_ALLOW_THREADS
|
||||
|
||||
|
||||
// This is bogus, and only serves to ensure that the FPU
|
||||
// computes these variables at the right times.
|
||||
return sxi + syi + xdxi + ydxi;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void transform32_core(PyObject *pysrc, PyObject *pydst,
|
||||
float corner_x, float corner_y,
|
||||
float xdx, float ydx,
|
||||
float xdy, float ydy,
|
||||
int ashift,
|
||||
float a) {
|
||||
|
||||
#ifdef GCC_MMX
|
||||
static int checked_mmx = 0;
|
||||
static int has_mmx = 0;
|
||||
|
||||
if (! checked_mmx) {
|
||||
has_mmx = SDL_HasMMX();
|
||||
checked_mmx = 1;
|
||||
}
|
||||
|
||||
if (has_mmx) {
|
||||
transform32_mmx(pysrc, pydst, corner_x, corner_y,
|
||||
xdx, ydx, xdy, ydy, ashift, a);
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
transform32_std(pysrc, pydst, corner_x, corner_y,
|
||||
xdx, ydx, xdy, ydy, ashift, a);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void blend32_core_std(PyObject *pysrca, PyObject *pysrcb, PyObject *pydst,
|
||||
int alpha) {
|
||||
|
||||
|
||||
@@ -2,12 +2,6 @@ import sys
|
||||
|
||||
from sound import *
|
||||
|
||||
try:
|
||||
import nativemidi
|
||||
sys.modules['nativemidi'] = sys.modules['pysdlsound.nativemidi']
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
import winmixer
|
||||
sys.modules['winmixer'] = sys.modules['pysdlsound.winmixer']
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
native_midi: Hardware Midi support for the SDL_mixer library
|
||||
Copyright (C) 2000 Florian 'Proff' Schulze
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Florian 'Proff' Schulze
|
||||
florian.proff.schulze@gmx.net
|
||||
*/
|
||||
|
||||
#ifndef _NATIVE_MIDI_H_
|
||||
#define _NATIVE_MIDI_H_
|
||||
|
||||
#include <SDL/SDL_rwops.h>
|
||||
|
||||
typedef struct _NativeMidiSong NativeMidiSong;
|
||||
|
||||
int native_midi_detect();
|
||||
NativeMidiSong *native_midi_loadsong(char *midifile);
|
||||
NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw);
|
||||
void native_midi_freesong(NativeMidiSong *song);
|
||||
void native_midi_start(NativeMidiSong *song);
|
||||
void native_midi_stop();
|
||||
int native_midi_active();
|
||||
void native_midi_setvolume(int volume);
|
||||
char *native_midi_error();
|
||||
|
||||
#endif /* _NATIVE_MIDI_H_ */
|
||||
@@ -1,404 +0,0 @@
|
||||
/*
|
||||
native_midi: Hardware Midi support for the SDL_mixer library
|
||||
Copyright (C) 2000,2001 Florian 'Proff' Schulze
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Florian 'Proff' Schulze
|
||||
florian.proff.schulze@gmx.net
|
||||
*/
|
||||
|
||||
|
||||
#include "native_midi_common.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
|
||||
/* The maximum number of midi tracks that we can handle
|
||||
#define MIDI_TRACKS 32 */
|
||||
|
||||
|
||||
/* A single midi track as read from the midi file */
|
||||
typedef struct
|
||||
{
|
||||
Uint8 *data; /* MIDI message stream */
|
||||
int len; /* length of the track data */
|
||||
} MIDITrack;
|
||||
|
||||
/* A midi file, stripped down to the absolute minimum - divison & track data */
|
||||
typedef struct
|
||||
{
|
||||
int division; /* number of pulses per quarter note (ppqn) */
|
||||
int nTracks; /* number of tracks */
|
||||
MIDITrack *track; /* tracks */
|
||||
} MIDIFile;
|
||||
|
||||
|
||||
/* Some macros that help us stay endianess-independant */
|
||||
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
||||
#define BE_SHORT(x) (x)
|
||||
#define BE_LONG(x) (x)
|
||||
#else
|
||||
#define BE_SHORT(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF))
|
||||
#define BE_LONG(x) ((((x)&0x0000FF)<<24) | \
|
||||
(((x)&0x00FF00)<<8) | \
|
||||
(((x)&0xFF0000)>>8) | \
|
||||
(((x)>>24)&0xFF))
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Get Variable Length Quantity */
|
||||
static int GetVLQ(MIDITrack *track, int *currentPos)
|
||||
{
|
||||
int l = 0;
|
||||
Uint8 c;
|
||||
while(1)
|
||||
{
|
||||
c = track->data[*currentPos];
|
||||
(*currentPos)++;
|
||||
l += (c & 0x7f);
|
||||
if (!(c & 0x80))
|
||||
return l;
|
||||
l <<= 7;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a single MIDIEvent */
|
||||
static MIDIEvent *CreateEvent(Uint32 time, Uint8 event, Uint8 a, Uint8 b)
|
||||
{
|
||||
MIDIEvent *newEvent;
|
||||
|
||||
newEvent = calloc(1, sizeof(MIDIEvent));
|
||||
|
||||
if (newEvent)
|
||||
{
|
||||
newEvent->time = time;
|
||||
newEvent->status = event;
|
||||
newEvent->data[0] = a;
|
||||
newEvent->data[1] = b;
|
||||
}
|
||||
|
||||
return newEvent;
|
||||
}
|
||||
|
||||
/* Convert a single midi track to a list of MIDIEvents */
|
||||
static MIDIEvent *MIDITracktoStream(MIDITrack *track)
|
||||
{
|
||||
Uint32 atime = 0;
|
||||
Uint32 len = 0;
|
||||
Uint8 event,type,a,b;
|
||||
Uint8 laststatus = 0;
|
||||
Uint8 lastchan = 0;
|
||||
int currentPos = 0;
|
||||
int end = 0;
|
||||
MIDIEvent *head = CreateEvent(0,0,0,0); /* dummy event to make handling the list easier */
|
||||
MIDIEvent *currentEvent = head;
|
||||
|
||||
while (!end)
|
||||
{
|
||||
if (currentPos >= track->len)
|
||||
break; /* End of data stream reached */
|
||||
|
||||
atime += GetVLQ(track, ¤tPos);
|
||||
event = track->data[currentPos++];
|
||||
|
||||
/* Handle SysEx seperatly */
|
||||
if (((event>>4) & 0x0F) == MIDI_STATUS_SYSEX)
|
||||
{
|
||||
if (event == 0xFF)
|
||||
{
|
||||
type = track->data[currentPos];
|
||||
currentPos++;
|
||||
switch(type)
|
||||
{
|
||||
case 0x2f: /* End of data marker */
|
||||
end = 1;
|
||||
case 0x51: /* Tempo change */
|
||||
/*
|
||||
a=track->data[currentPos];
|
||||
b=track->data[currentPos+1];
|
||||
c=track->data[currentPos+2];
|
||||
AddEvent(song, atime, MEVT_TEMPO, c, b, a);
|
||||
*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
type = 0;
|
||||
|
||||
len = GetVLQ(track, ¤tPos);
|
||||
|
||||
/* Create an event and attach the extra data, if any */
|
||||
currentEvent->next = CreateEvent(atime, event, type, 0);
|
||||
currentEvent = currentEvent->next;
|
||||
if (NULL == currentEvent)
|
||||
{
|
||||
FreeMIDIEventList(head);
|
||||
return NULL;
|
||||
}
|
||||
if (len)
|
||||
{
|
||||
currentEvent->extraLen = len;
|
||||
currentEvent->extraData = malloc(len);
|
||||
memcpy(currentEvent->extraData, &(track->data[currentPos]), len);
|
||||
currentPos += len;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
a = event;
|
||||
if (a & 0x80) /* It's a status byte */
|
||||
{
|
||||
/* Extract channel and status information */
|
||||
lastchan = a & 0x0F;
|
||||
laststatus = (a>>4) & 0x0F;
|
||||
|
||||
/* Read the next byte which should always be a data byte */
|
||||
a = track->data[currentPos++] & 0x7F;
|
||||
}
|
||||
switch(laststatus)
|
||||
{
|
||||
case MIDI_STATUS_NOTE_OFF:
|
||||
case MIDI_STATUS_NOTE_ON: /* Note on */
|
||||
case MIDI_STATUS_AFTERTOUCH: /* Key Pressure */
|
||||
case MIDI_STATUS_CONTROLLER: /* Control change */
|
||||
case MIDI_STATUS_PITCH_WHEEL: /* Pitch wheel */
|
||||
b = track->data[currentPos++] & 0x7F;
|
||||
currentEvent->next = CreateEvent(atime, (Uint8)((laststatus<<4)+lastchan), a, b);
|
||||
currentEvent = currentEvent->next;
|
||||
if (NULL == currentEvent)
|
||||
{
|
||||
FreeMIDIEventList(head);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
case MIDI_STATUS_PROG_CHANGE: /* Program change */
|
||||
case MIDI_STATUS_PRESSURE: /* Channel pressure */
|
||||
a &= 0x7f;
|
||||
currentEvent->next = CreateEvent(atime, (Uint8)((laststatus<<4)+lastchan), a, 0);
|
||||
currentEvent = currentEvent->next;
|
||||
if (NULL == currentEvent)
|
||||
{
|
||||
FreeMIDIEventList(head);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
default: /* Sysex already handled above */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentEvent = head->next;
|
||||
free(head); /* release the dummy head event */
|
||||
return currentEvent;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a midi song, consisting of up to 32 tracks, to a list of MIDIEvents.
|
||||
* To do so, first convert the tracks seperatly, then interweave the resulting
|
||||
* MIDIEvent-Lists to one big list.
|
||||
*/
|
||||
static MIDIEvent *MIDItoStream(MIDIFile *mididata)
|
||||
{
|
||||
MIDIEvent **track;
|
||||
MIDIEvent *head = CreateEvent(0,0,0,0); /* dummy event to make handling the list easier */
|
||||
MIDIEvent *currentEvent = head;
|
||||
int trackID;
|
||||
|
||||
if (NULL == head)
|
||||
return NULL;
|
||||
|
||||
track = (MIDIEvent**) calloc(1, sizeof(MIDIEvent*) * mididata->nTracks);
|
||||
if (NULL == head)
|
||||
return NULL;
|
||||
|
||||
/* First, convert all tracks to MIDIEvent lists */
|
||||
for (trackID = 0; trackID < mididata->nTracks; trackID++)
|
||||
track[trackID] = MIDITracktoStream(&mididata->track[trackID]);
|
||||
|
||||
/* Now, merge the lists. */
|
||||
/* TODO */
|
||||
while(1)
|
||||
{
|
||||
Uint32 lowestTime = INT_MAX;
|
||||
int currentTrackID = -1;
|
||||
|
||||
/* Find the next event */
|
||||
for (trackID = 0; trackID < mididata->nTracks; trackID++)
|
||||
{
|
||||
if (track[trackID] && (track[trackID]->time < lowestTime))
|
||||
{
|
||||
currentTrackID = trackID;
|
||||
lowestTime = track[currentTrackID]->time;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if we processes all events */
|
||||
if (currentTrackID == -1)
|
||||
break;
|
||||
|
||||
currentEvent->next = track[currentTrackID];
|
||||
track[currentTrackID] = track[currentTrackID]->next;
|
||||
|
||||
currentEvent = currentEvent->next;
|
||||
|
||||
|
||||
lowestTime = 0;
|
||||
}
|
||||
|
||||
/* Make sure the list is properly terminated */
|
||||
currentEvent->next = 0;
|
||||
|
||||
currentEvent = head->next;
|
||||
free(track);
|
||||
free(head); /* release the dummy head event */
|
||||
return currentEvent;
|
||||
}
|
||||
|
||||
static int ReadMIDIFile(MIDIFile *mididata, SDL_RWops *rw)
|
||||
{
|
||||
int i = 0;
|
||||
Uint32 ID;
|
||||
Uint32 size;
|
||||
Uint16 format;
|
||||
Uint16 tracks;
|
||||
Uint16 division;
|
||||
|
||||
if (!mididata)
|
||||
return 0;
|
||||
if (!rw)
|
||||
return 0;
|
||||
|
||||
/* Make sure this is really a MIDI file */
|
||||
SDL_RWread(rw, &ID, 1, 4);
|
||||
if (BE_LONG(ID) != 'MThd')
|
||||
return 0;
|
||||
|
||||
/* Header size must be 6 */
|
||||
SDL_RWread(rw, &size, 1, 4);
|
||||
size = BE_LONG(size);
|
||||
if (size != 6)
|
||||
return 0;
|
||||
|
||||
/* We only support format 0 and 1, but not 2 */
|
||||
SDL_RWread(rw, &format, 1, 2);
|
||||
format = BE_SHORT(format);
|
||||
if (format != 0 && format != 1)
|
||||
return 0;
|
||||
|
||||
SDL_RWread(rw, &tracks, 1, 2);
|
||||
tracks = BE_SHORT(tracks);
|
||||
mididata->nTracks = tracks;
|
||||
|
||||
/* Allocate tracks */
|
||||
mididata->track = (MIDITrack*) calloc(1, sizeof(MIDITrack) * mididata->nTracks);
|
||||
if (NULL == mididata->track)
|
||||
{
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* Retrieve the PPQN value, needed for playback */
|
||||
SDL_RWread(rw, &division, 1, 2);
|
||||
mididata->division = BE_SHORT(division);
|
||||
|
||||
|
||||
for (i=0; i<tracks; i++)
|
||||
{
|
||||
SDL_RWread(rw, &ID, 1, 4); /* We might want to verify this is MTrk... */
|
||||
SDL_RWread(rw, &size, 1, 4);
|
||||
size = BE_LONG(size);
|
||||
mididata->track[i].len = size;
|
||||
mididata->track[i].data = malloc(size);
|
||||
if (NULL == mididata->track[i].data)
|
||||
{
|
||||
goto bail;
|
||||
}
|
||||
SDL_RWread(rw, mididata->track[i].data, 1, size);
|
||||
}
|
||||
return 1;
|
||||
|
||||
bail:
|
||||
for(;i >= 0; i--)
|
||||
{
|
||||
if (mididata->track[i].data)
|
||||
free(mididata->track[i].data);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MIDIEvent *CreateMIDIEventList(SDL_RWops *rw, Uint16 *division)
|
||||
{
|
||||
MIDIFile *mididata = NULL;
|
||||
MIDIEvent *eventList;
|
||||
int trackID;
|
||||
|
||||
mididata = calloc(1, sizeof(MIDIFile));
|
||||
if (!mididata)
|
||||
return NULL;
|
||||
|
||||
/* Open the file */
|
||||
if ( rw != NULL )
|
||||
{
|
||||
/* Read in the data */
|
||||
if ( ! ReadMIDIFile(mididata, rw))
|
||||
{
|
||||
free(mididata);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
free(mididata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (division)
|
||||
*division = mididata->division;
|
||||
|
||||
eventList = MIDItoStream(mididata);
|
||||
|
||||
for(trackID = 0; trackID < mididata->nTracks; trackID++)
|
||||
{
|
||||
if (mididata->track[trackID].data)
|
||||
free(mididata->track[trackID].data);
|
||||
}
|
||||
free(mididata->track);
|
||||
free(mididata);
|
||||
|
||||
return eventList;
|
||||
}
|
||||
|
||||
void FreeMIDIEventList(MIDIEvent *head)
|
||||
{
|
||||
MIDIEvent *cur, *next;
|
||||
|
||||
cur = head;
|
||||
|
||||
while (cur)
|
||||
{
|
||||
next = cur->next;
|
||||
if (cur->extraData)
|
||||
free (cur->extraData);
|
||||
free (cur);
|
||||
cur = next;
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
native_midi: Hardware Midi support for the SDL_mixer library
|
||||
Copyright (C) 2000,2001 Florian 'Proff' Schulze & Max Horn
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Florian 'Proff' Schulze
|
||||
florian.proff.schulze@gmx.net
|
||||
|
||||
Max Horn
|
||||
max@quendi.de
|
||||
*/
|
||||
|
||||
#ifndef _NATIVE_MIDI_COMMON_H_
|
||||
#define _NATIVE_MIDI_COMMON_H_
|
||||
|
||||
#include <SDL/SDL.h>
|
||||
|
||||
/* Midi Status Bytes */
|
||||
#define MIDI_STATUS_NOTE_OFF 0x8
|
||||
#define MIDI_STATUS_NOTE_ON 0x9
|
||||
#define MIDI_STATUS_AFTERTOUCH 0xA
|
||||
#define MIDI_STATUS_CONTROLLER 0xB
|
||||
#define MIDI_STATUS_PROG_CHANGE 0xC
|
||||
#define MIDI_STATUS_PRESSURE 0xD
|
||||
#define MIDI_STATUS_PITCH_WHEEL 0xE
|
||||
#define MIDI_STATUS_SYSEX 0xF
|
||||
|
||||
/* We store the midi events in a linked list; this way it is
|
||||
easy to shuffle the tracks together later on; and we are
|
||||
flexible in the size of each elemnt.
|
||||
*/
|
||||
typedef struct MIDIEvent
|
||||
{
|
||||
Uint32 time; /* Time at which this midi events occurs */
|
||||
Uint8 status; /* Status byte */
|
||||
Uint8 data[2]; /* 1 or 2 bytes additional data for most events */
|
||||
|
||||
Uint32 extraLen; /* For some SysEx events, we need additional storage */
|
||||
Uint8 *extraData;
|
||||
|
||||
struct MIDIEvent *next;
|
||||
} MIDIEvent;
|
||||
|
||||
|
||||
/* Load a midifile to memory, converting it to a list of MIDIEvents.
|
||||
This function returns a linked lists of MIDIEvents, 0 if an error occured.
|
||||
*/
|
||||
MIDIEvent *CreateMIDIEventList(SDL_RWops *rw, Uint16 *division);
|
||||
|
||||
/* Release a MIDIEvent list after usage. */
|
||||
void FreeMIDIEventList(MIDIEvent *head);
|
||||
|
||||
|
||||
#endif /* _NATIVE_MIDI_COMMON_H_ */
|
||||
@@ -1,707 +0,0 @@
|
||||
/*
|
||||
native_midi_mac: Native Midi support on MacOS for the SDL_mixer library
|
||||
Copyright (C) 2001 Max Horn
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Max Horn
|
||||
max@quendi.de
|
||||
*/
|
||||
|
||||
#ifndef MACOSX
|
||||
#define MACOSX
|
||||
#endif
|
||||
|
||||
#if defined(macintosh) || defined(MACOSX)
|
||||
|
||||
#include "native_midi.h"
|
||||
#include "native_midi_common.h"
|
||||
|
||||
#ifdef MACOSX
|
||||
#include <QuickTime/QuickTimeMusic.h>
|
||||
#else
|
||||
#include <QuickTimeMusic.h>
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/* Native Midi song */
|
||||
struct _NativeMidiSong
|
||||
{
|
||||
Uint32 *tuneSequence;
|
||||
Uint32 *tuneHeader;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
/* number of (32-bit) long words in a note request event */
|
||||
kNoteRequestEventLength = ((sizeof(NoteRequest)/sizeof(long)) + 2),
|
||||
|
||||
/* number of (32-bit) long words in a marker event */
|
||||
kMarkerEventLength = 1,
|
||||
|
||||
/* number of (32-bit) long words in a general event, minus its data */
|
||||
kGeneralEventLength = 2
|
||||
};
|
||||
|
||||
#define ERROR_BUF_SIZE 256
|
||||
#define BUFFER_INCREMENT 5000
|
||||
|
||||
#define REST_IF_NECESSARY() do {\
|
||||
int timeDiff = eventPos->time - lastEventTime; \
|
||||
if(timeDiff) \
|
||||
{ \
|
||||
timeDiff = (int)(timeDiff*tick); \
|
||||
qtma_StuffRestEvent(*tunePos, timeDiff); \
|
||||
tunePos++; \
|
||||
lastEventTime = eventPos->time; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
||||
static Uint32 *BuildTuneSequence(MIDIEvent *evntlist, int ppqn, int part_poly_max[32], int part_to_inst[32], int *numParts);
|
||||
static Uint32 *BuildTuneHeader(int part_poly_max[32], int part_to_inst[32], int numParts);
|
||||
|
||||
/* The global TunePlayer instance */
|
||||
static TunePlayer gTunePlayer = NULL;
|
||||
static int gInstaceCount = 0;
|
||||
static Uint32 *gCurrentTuneSequence = NULL;
|
||||
static char gErrorBuffer[ERROR_BUF_SIZE] = "";
|
||||
|
||||
|
||||
/* Check whether QuickTime is available */
|
||||
int native_midi_detect()
|
||||
{
|
||||
/* TODO */
|
||||
return 1;
|
||||
}
|
||||
|
||||
NativeMidiSong *native_midi_loadsong(char *midifile)
|
||||
{
|
||||
NativeMidiSong *song = NULL;
|
||||
MIDIEvent *evntlist = NULL;
|
||||
int part_to_inst[32];
|
||||
int part_poly_max[32];
|
||||
int numParts = 0;
|
||||
Uint16 ppqn;
|
||||
SDL_RWops *rw;
|
||||
|
||||
/* Init the arrays */
|
||||
memset(part_poly_max,0,sizeof(part_poly_max));
|
||||
memset(part_to_inst,-1,sizeof(part_to_inst));
|
||||
|
||||
/* Attempt to load the midi file */
|
||||
rw = SDL_RWFromFile(midifile, "rb");
|
||||
if (rw) {
|
||||
evntlist = CreateMIDIEventList(rw, &ppqn);
|
||||
SDL_RWclose(rw);
|
||||
if (!evntlist)
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* Allocate memory for the song struct */
|
||||
song = malloc(sizeof(NativeMidiSong));
|
||||
if (!song)
|
||||
goto bail;
|
||||
|
||||
/* Build a tune sequence from the event list */
|
||||
song->tuneSequence = BuildTuneSequence(evntlist, ppqn, part_poly_max, part_to_inst, &numParts);
|
||||
if(!song->tuneSequence)
|
||||
goto bail;
|
||||
|
||||
/* Now build a tune header from the data we collect above, create
|
||||
all parts as needed and assign them the correct instrument.
|
||||
*/
|
||||
song->tuneHeader = BuildTuneHeader(part_poly_max, part_to_inst, numParts);
|
||||
if(!song->tuneHeader)
|
||||
goto bail;
|
||||
|
||||
/* Increment the instance count */
|
||||
gInstaceCount++;
|
||||
if (gTunePlayer == NULL)
|
||||
gTunePlayer = OpenDefaultComponent(kTunePlayerComponentType, 0);
|
||||
|
||||
/* Finally, free the event list */
|
||||
FreeMIDIEventList(evntlist);
|
||||
|
||||
return song;
|
||||
|
||||
bail:
|
||||
if (evntlist)
|
||||
FreeMIDIEventList(evntlist);
|
||||
|
||||
if (song)
|
||||
{
|
||||
if(song->tuneSequence)
|
||||
free(song->tuneSequence);
|
||||
|
||||
if(song->tuneHeader)
|
||||
DisposePtr((Ptr)song->tuneHeader);
|
||||
|
||||
free(song);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw)
|
||||
{
|
||||
NativeMidiSong *song = NULL;
|
||||
MIDIEvent *evntlist = NULL;
|
||||
int part_to_inst[32];
|
||||
int part_poly_max[32];
|
||||
int numParts = 0;
|
||||
Uint16 ppqn;
|
||||
|
||||
/* Init the arrays */
|
||||
memset(part_poly_max,0,sizeof(part_poly_max));
|
||||
memset(part_to_inst,-1,sizeof(part_to_inst));
|
||||
|
||||
/* Attempt to load the midi file */
|
||||
evntlist = CreateMIDIEventList(rw, &ppqn);
|
||||
if (!evntlist)
|
||||
goto bail;
|
||||
|
||||
/* Allocate memory for the song struct */
|
||||
song = malloc(sizeof(NativeMidiSong));
|
||||
if (!song)
|
||||
goto bail;
|
||||
|
||||
/* Build a tune sequence from the event list */
|
||||
song->tuneSequence = BuildTuneSequence(evntlist, ppqn, part_poly_max, part_to_inst, &numParts);
|
||||
if(!song->tuneSequence)
|
||||
goto bail;
|
||||
|
||||
/* Now build a tune header from the data we collect above, create
|
||||
all parts as needed and assign them the correct instrument.
|
||||
*/
|
||||
song->tuneHeader = BuildTuneHeader(part_poly_max, part_to_inst, numParts);
|
||||
if(!song->tuneHeader)
|
||||
goto bail;
|
||||
|
||||
/* Increment the instance count */
|
||||
gInstaceCount++;
|
||||
if (gTunePlayer == NULL)
|
||||
gTunePlayer = OpenDefaultComponent(kTunePlayerComponentType, 0);
|
||||
|
||||
/* Finally, free the event list */
|
||||
FreeMIDIEventList(evntlist);
|
||||
|
||||
return song;
|
||||
|
||||
bail:
|
||||
if (evntlist)
|
||||
FreeMIDIEventList(evntlist);
|
||||
|
||||
if (song)
|
||||
{
|
||||
if(song->tuneSequence)
|
||||
free(song->tuneSequence);
|
||||
|
||||
if(song->tuneHeader)
|
||||
DisposePtr((Ptr)song->tuneHeader);
|
||||
|
||||
free(song);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void native_midi_freesong(NativeMidiSong *song)
|
||||
{
|
||||
if(!song || !song->tuneSequence)
|
||||
return;
|
||||
|
||||
/* If this is the currently playing song, stop it now */
|
||||
if (song->tuneSequence == gCurrentTuneSequence)
|
||||
native_midi_stop();
|
||||
|
||||
/* Finally, free the data storage */
|
||||
free(song->tuneSequence);
|
||||
DisposePtr((Ptr)song->tuneHeader);
|
||||
free(song);
|
||||
|
||||
/* Increment the instance count */
|
||||
gInstaceCount--;
|
||||
if ((gTunePlayer != NULL) && (gInstaceCount == 0))
|
||||
{
|
||||
CloseComponent(gTunePlayer);
|
||||
gTunePlayer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void native_midi_start(NativeMidiSong *song)
|
||||
{
|
||||
UInt32 queueFlags = 0;
|
||||
ComponentResult tpError;
|
||||
|
||||
assert (gTunePlayer != NULL);
|
||||
|
||||
SDL_PauseAudio(1);
|
||||
// SDL_UnlockAudio();
|
||||
|
||||
/* First, stop the currently playing music */
|
||||
native_midi_stop();
|
||||
|
||||
/* Set up the queue flags */
|
||||
queueFlags = kTuneStartNow;
|
||||
|
||||
/* Set the time scale (units per second), we want milliseconds */
|
||||
tpError = TuneSetTimeScale(gTunePlayer, 1000);
|
||||
if (tpError != noErr)
|
||||
{
|
||||
strncpy (gErrorBuffer, "MIDI error during TuneSetTimeScale", ERROR_BUF_SIZE);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Set the header, to tell what instruments are used */
|
||||
tpError = TuneSetHeader(gTunePlayer, (UInt32 *)song->tuneHeader);
|
||||
if (tpError != noErr)
|
||||
{
|
||||
strncpy (gErrorBuffer, "MIDI error during TuneSetHeader", ERROR_BUF_SIZE);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Have it allocate whatever resources are needed */
|
||||
tpError = TunePreroll(gTunePlayer);
|
||||
if (tpError != noErr)
|
||||
{
|
||||
strncpy (gErrorBuffer, "MIDI error during TunePreroll", ERROR_BUF_SIZE);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* We want to play at normal volume */
|
||||
tpError = TuneSetVolume(gTunePlayer, 0x00010000);
|
||||
if (tpError != noErr)
|
||||
{
|
||||
strncpy (gErrorBuffer, "MIDI error during TuneSetVolume", ERROR_BUF_SIZE);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Finally, start playing the full song */
|
||||
gCurrentTuneSequence = song->tuneSequence;
|
||||
tpError = TuneQueue(gTunePlayer, (UInt32 *)song->tuneSequence, 0x00010000, 0, 0xFFFFFFFF, queueFlags, NULL, 0);
|
||||
if (tpError != noErr)
|
||||
{
|
||||
strncpy (gErrorBuffer, "MIDI error during TuneQueue", ERROR_BUF_SIZE);
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
1;
|
||||
// SDL_LockAudio();
|
||||
SDL_PauseAudio(0);
|
||||
}
|
||||
|
||||
void native_midi_stop()
|
||||
{
|
||||
if (gTunePlayer == NULL)
|
||||
return;
|
||||
|
||||
/* Stop music */
|
||||
TuneStop(gTunePlayer, 0);
|
||||
|
||||
/* Deallocate all instruments */
|
||||
TuneUnroll(gTunePlayer);
|
||||
}
|
||||
|
||||
int native_midi_active()
|
||||
{
|
||||
if (gTunePlayer != NULL)
|
||||
{
|
||||
TuneStatus ts;
|
||||
|
||||
TuneGetStatus(gTunePlayer,&ts);
|
||||
return ts.queueTime != 0;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void native_midi_setvolume(int volume)
|
||||
{
|
||||
if (gTunePlayer == NULL)
|
||||
return;
|
||||
|
||||
/* QTMA olume may range from 0.0 to 1.0 (in 16.16 fixed point encoding) */
|
||||
TuneSetVolume(gTunePlayer, (0x00010000 * volume)/SDL_MIX_MAXVOLUME);
|
||||
}
|
||||
|
||||
char *native_midi_error()
|
||||
{
|
||||
return gErrorBuffer;
|
||||
}
|
||||
|
||||
Uint32 *BuildTuneSequence(MIDIEvent *evntlist, int ppqn, int part_poly_max[32], int part_to_inst[32], int *numParts)
|
||||
{
|
||||
int part_poly[32];
|
||||
int channel_to_part[16];
|
||||
|
||||
int channel_pan[16];
|
||||
int channel_vol[16];
|
||||
int channel_pitch_bend[16];
|
||||
|
||||
int lastEventTime = 0;
|
||||
int tempo = 500000;
|
||||
double Ippqn = 1.0 / (1000*ppqn);
|
||||
double tick = tempo * Ippqn;
|
||||
MIDIEvent *eventPos = evntlist;
|
||||
MIDIEvent *noteOffPos;
|
||||
Uint32 *tunePos, *endPos;
|
||||
Uint32 *tuneSequence;
|
||||
size_t tuneSize;
|
||||
|
||||
/* allocate space for the tune header */
|
||||
tuneSize = 5000;
|
||||
tuneSequence = (Uint32 *)malloc(tuneSize * sizeof(Uint32));
|
||||
if (tuneSequence == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Set starting position in our tune memory */
|
||||
tunePos = tuneSequence;
|
||||
endPos = tuneSequence + tuneSize;
|
||||
|
||||
/* Initialise the arrays */
|
||||
memset(part_poly,0,sizeof(part_poly));
|
||||
|
||||
memset(channel_to_part,-1,sizeof(channel_to_part));
|
||||
memset(channel_pan,-1,sizeof(channel_pan));
|
||||
memset(channel_vol,-1,sizeof(channel_vol));
|
||||
memset(channel_pitch_bend,-1,sizeof(channel_pitch_bend));
|
||||
|
||||
*numParts = 0;
|
||||
|
||||
/*
|
||||
* Now the major work - iterate over all GM events,
|
||||
* and turn them into QuickTime Music format.
|
||||
* At the same time, calculate the max. polyphony for each part,
|
||||
* and also the part->instrument mapping.
|
||||
*/
|
||||
while(eventPos)
|
||||
{
|
||||
int status = (eventPos->status&0xF0)>>4;
|
||||
int channel = eventPos->status&0x0F;
|
||||
int part = channel_to_part[channel];
|
||||
int velocity, pitch;
|
||||
int value, controller;
|
||||
int bend;
|
||||
int newInst;
|
||||
|
||||
/* Check if we are running low on space... */
|
||||
if((tunePos+16) > endPos)
|
||||
{
|
||||
/* Resize our data storage. */
|
||||
Uint32 *oldTuneSequence = tuneSequence;
|
||||
|
||||
tuneSize += BUFFER_INCREMENT;
|
||||
tuneSequence = (Uint32 *)realloc(tuneSequence, tuneSize * sizeof(Uint32));
|
||||
if(oldTuneSequence != tuneSequence)
|
||||
tunePos += tuneSequence - oldTuneSequence;
|
||||
endPos = tuneSequence + tuneSize;
|
||||
}
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case MIDI_STATUS_NOTE_OFF:
|
||||
assert(part>=0 && part<=31);
|
||||
|
||||
/* Keep track of the polyphony of the current part */
|
||||
part_poly[part]--;
|
||||
break;
|
||||
case MIDI_STATUS_NOTE_ON:
|
||||
if (part < 0)
|
||||
{
|
||||
/* If no part is specified yet, we default to the first instrument, which
|
||||
is piano (or the first drum kit if we are on the drum channel)
|
||||
*/
|
||||
int newInst;
|
||||
|
||||
if (channel == 9)
|
||||
newInst = kFirstDrumkit + 1; /* the first drum kit is the "no drum" kit! */
|
||||
else
|
||||
newInst = kFirstGMInstrument;
|
||||
part = channel_to_part[channel] = *numParts;
|
||||
part_to_inst[(*numParts)++] = newInst;
|
||||
}
|
||||
/* TODO - add support for more than 32 parts using eXtended QTMA events */
|
||||
assert(part<=31);
|
||||
|
||||
/* Decode pitch & velocity */
|
||||
pitch = eventPos->data[0];
|
||||
velocity = eventPos->data[1];
|
||||
|
||||
if (velocity == 0)
|
||||
{
|
||||
/* was a NOTE OFF in disguise, so we decrement the polyphony */
|
||||
part_poly[part]--;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Keep track of the polyphony of the current part */
|
||||
int foo = ++part_poly[part];
|
||||
if (part_poly_max[part] < foo)
|
||||
part_poly_max[part] = foo;
|
||||
|
||||
/* Now scan forward to find the matching NOTE OFF event */
|
||||
for(noteOffPos = eventPos; noteOffPos; noteOffPos = noteOffPos->next)
|
||||
{
|
||||
if ((noteOffPos->status&0xF0)>>4 == MIDI_STATUS_NOTE_OFF
|
||||
&& channel == (eventPos->status&0x0F)
|
||||
&& pitch == noteOffPos->data[0])
|
||||
break;
|
||||
/* NOTE ON with velocity == 0 is the same as a NOTE OFF */
|
||||
if ((noteOffPos->status&0xF0)>>4 == MIDI_STATUS_NOTE_ON
|
||||
&& channel == (eventPos->status&0x0F)
|
||||
&& pitch == noteOffPos->data[0]
|
||||
&& 0 == noteOffPos->data[1])
|
||||
break;
|
||||
}
|
||||
|
||||
/* Did we find a note off? Should always be the case, but who knows... */
|
||||
if (noteOffPos)
|
||||
{
|
||||
/* We found a NOTE OFF, now calculate the note duration */
|
||||
int duration = (int)((noteOffPos->time - eventPos->time)*tick);
|
||||
|
||||
REST_IF_NECESSARY();
|
||||
/* Now we need to check if we get along with a normal Note Event, or if we need an extended one... */
|
||||
if (duration < 2048 && pitch>=32 && pitch<=95 && velocity>=0 && velocity<=127)
|
||||
{
|
||||
qtma_StuffNoteEvent(*tunePos, part, pitch, velocity, duration);
|
||||
tunePos++;
|
||||
}
|
||||
else
|
||||
{
|
||||
qtma_StuffXNoteEvent(*tunePos, *(tunePos+1), part, pitch, velocity, duration);
|
||||
tunePos+=2;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MIDI_STATUS_AFTERTOUCH:
|
||||
/* NYI - use kControllerAfterTouch. But how are the parameters to be mapped? */
|
||||
break;
|
||||
case MIDI_STATUS_CONTROLLER:
|
||||
controller = eventPos->data[0];
|
||||
value = eventPos->data[1];
|
||||
|
||||
switch(controller)
|
||||
{
|
||||
case 0: /* bank change - igore for now */
|
||||
break;
|
||||
case kControllerVolume:
|
||||
if(channel_vol[channel] != value<<8)
|
||||
{
|
||||
channel_vol[channel] = value<<8;
|
||||
if(part>=0 && part<=31)
|
||||
{
|
||||
REST_IF_NECESSARY();
|
||||
qtma_StuffControlEvent(*tunePos, part, kControllerVolume, channel_vol[channel]);
|
||||
tunePos++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kControllerPan:
|
||||
if(channel_pan[channel] != (value << 1) + 256)
|
||||
{
|
||||
channel_pan[channel] = (value << 1) + 256;
|
||||
if(part>=0 && part<=31)
|
||||
{
|
||||
REST_IF_NECESSARY();
|
||||
qtma_StuffControlEvent(*tunePos, part, kControllerPan, channel_pan[channel]);
|
||||
tunePos++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* No other controllers implemented yet */;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case MIDI_STATUS_PROG_CHANGE:
|
||||
/* Instrument changed */
|
||||
newInst = eventPos->data[0];
|
||||
|
||||
/* Channel 9 (the 10th channel) is different, it indicates a drum kit */
|
||||
if (channel == 9)
|
||||
newInst += kFirstDrumkit;
|
||||
else
|
||||
newInst += kFirstGMInstrument;
|
||||
/* Only if the instrument for this channel *really* changed, add a new part. */
|
||||
if(newInst != part_to_inst[part])
|
||||
{
|
||||
/* TODO maybe make use of kGeneralEventPartChange here,
|
||||
to help QT reuse note channels?
|
||||
*/
|
||||
part = channel_to_part[channel] = *numParts;
|
||||
part_to_inst[(*numParts)++] = newInst;
|
||||
|
||||
if(channel_vol[channel] >= 0)
|
||||
{
|
||||
REST_IF_NECESSARY();
|
||||
qtma_StuffControlEvent(*tunePos, part, kControllerVolume, channel_vol[channel]);
|
||||
tunePos++;
|
||||
}
|
||||
if(channel_pan[channel] >= 0)
|
||||
{
|
||||
REST_IF_NECESSARY();
|
||||
qtma_StuffControlEvent(*tunePos, part, kControllerPan, channel_pan[channel]);
|
||||
tunePos++;
|
||||
}
|
||||
if(channel_pitch_bend[channel] >= 0)
|
||||
{
|
||||
REST_IF_NECESSARY();
|
||||
qtma_StuffControlEvent(*tunePos, part, kControllerPitchBend, channel_pitch_bend[channel]);
|
||||
tunePos++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MIDI_STATUS_PRESSURE:
|
||||
/* NYI */
|
||||
break;
|
||||
case MIDI_STATUS_PITCH_WHEEL:
|
||||
/* In the midi spec, 0x2000 = center, 0x0000 = - 2 semitones, 0x3FFF = +2 semitones
|
||||
but for QTMA, we specify it as a 8.8 fixed point of semitones
|
||||
TODO: detect "pitch bend range changes" & honor them!
|
||||
*/
|
||||
bend = (eventPos->data[0] & 0x7f) | ((eventPos->data[1] & 0x7f) << 7);
|
||||
|
||||
/* "Center" the bend */
|
||||
bend -= 0x2000;
|
||||
|
||||
/* Move it to our format: */
|
||||
bend <<= 4;
|
||||
|
||||
/* If it turns out the pitch bend didn't change, stop here */
|
||||
if(channel_pitch_bend[channel] == bend)
|
||||
break;
|
||||
|
||||
channel_pitch_bend[channel] = bend;
|
||||
if(part>=0 && part<=31)
|
||||
{
|
||||
/* Stuff a control event */
|
||||
REST_IF_NECESSARY();
|
||||
qtma_StuffControlEvent(*tunePos, part, kControllerPitchBend, bend);
|
||||
tunePos++;
|
||||
}
|
||||
break;
|
||||
case MIDI_STATUS_SYSEX:
|
||||
if (eventPos->status == 0xFF && eventPos->data[0] == 0x51) /* Tempo change */
|
||||
{
|
||||
tempo = (eventPos->extraData[0] << 16) +
|
||||
(eventPos->extraData[1] << 8) +
|
||||
eventPos->extraData[2];
|
||||
|
||||
tick = tempo * Ippqn;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* on to the next event */
|
||||
eventPos = eventPos->next;
|
||||
}
|
||||
|
||||
/* Finally, place an end marker */
|
||||
*tunePos = kEndMarkerValue;
|
||||
|
||||
return tuneSequence;
|
||||
}
|
||||
|
||||
Uint32 *BuildTuneHeader(int part_poly_max[32], int part_to_inst[32], int numParts)
|
||||
{
|
||||
Uint32 *myHeader;
|
||||
Uint32 *myPos1, *myPos2; /* pointers to the head and tail long words of a music event */
|
||||
NoteRequest *myNoteRequest;
|
||||
NoteAllocator myNoteAllocator; /* for the NAStuffToneDescription call */
|
||||
ComponentResult myErr = noErr;
|
||||
int part;
|
||||
|
||||
myHeader = NULL;
|
||||
myNoteAllocator = NULL;
|
||||
|
||||
/*
|
||||
* Open up the Note Allocator
|
||||
*/
|
||||
myNoteAllocator = OpenDefaultComponent(kNoteAllocatorComponentType,0);
|
||||
if (myNoteAllocator == NULL)
|
||||
goto bail;
|
||||
|
||||
/*
|
||||
* Allocate space for the tune header
|
||||
*/
|
||||
myHeader = (Uint32 *)
|
||||
NewPtrClear((numParts * kNoteRequestEventLength + kMarkerEventLength) * sizeof(Uint32));
|
||||
if (myHeader == NULL)
|
||||
goto bail;
|
||||
|
||||
myPos1 = myHeader;
|
||||
|
||||
/*
|
||||
* Loop over all parts
|
||||
*/
|
||||
for(part = 0; part < numParts; ++part)
|
||||
{
|
||||
/*
|
||||
* Stuff request for the instrument with the given polyphony
|
||||
*/
|
||||
myPos2 = myPos1 + (kNoteRequestEventLength - 1); /* last longword of general event */
|
||||
qtma_StuffGeneralEvent(*myPos1, *myPos2, part, kGeneralEventNoteRequest, kNoteRequestEventLength);
|
||||
myNoteRequest = (NoteRequest *)(myPos1 + 1);
|
||||
myNoteRequest->info.flags = 0;
|
||||
/* I'm told by the Apple people that the Quicktime types were poorly designed and it was
|
||||
* too late to change them. On little endian, the BigEndian(Short|Fixed) types are structs
|
||||
* while on big endian they are primitive types. Furthermore, Quicktime failed to
|
||||
* provide setter and getter functions. To get this to work, we need to case the
|
||||
* code for the two possible situations.
|
||||
* My assumption is that the right-side value was always expected to be BigEndian
|
||||
* as it was written way before the Universal Binary transition. So in the little endian
|
||||
* case, OSSwap is used.
|
||||
*/
|
||||
#if __LITTLE_ENDIAN__
|
||||
myNoteRequest->info.polyphony.bigEndianValue = OSSwapHostToBigInt16(part_poly_max[part]);
|
||||
myNoteRequest->info.typicalPolyphony.bigEndianValue = OSSwapHostToBigInt32(0x00010000);
|
||||
#else
|
||||
myNoteRequest->info.polyphony = part_poly_max[part];
|
||||
myNoteRequest->info.typicalPolyphony = 0x00010000;
|
||||
#endif
|
||||
myErr = NAStuffToneDescription(myNoteAllocator,part_to_inst[part],&myNoteRequest->tone);
|
||||
if (myErr != noErr)
|
||||
goto bail;
|
||||
|
||||
/* move pointer to beginning of next event */
|
||||
myPos1 += kNoteRequestEventLength;
|
||||
}
|
||||
|
||||
*myPos1 = kEndMarkerValue; /* end of sequence marker */
|
||||
|
||||
|
||||
bail:
|
||||
if(myNoteAllocator)
|
||||
CloseComponent(myNoteAllocator);
|
||||
|
||||
/* if we encountered an error, dispose of the storage we allocated and return NULL */
|
||||
if (myErr != noErr) {
|
||||
DisposePtr((Ptr)myHeader);
|
||||
myHeader = NULL;
|
||||
}
|
||||
|
||||
return myHeader;
|
||||
}
|
||||
|
||||
#endif /* MacOS native MIDI support */
|
||||
@@ -1,321 +0,0 @@
|
||||
/*
|
||||
native_midi: Hardware Midi support for the SDL_mixer library
|
||||
Copyright (C) 2000,2001 Florian 'Proff' Schulze
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Florian 'Proff' Schulze
|
||||
florian.proff.schulze@gmx.net
|
||||
*/
|
||||
|
||||
/* everything below is currently one very big bad hack ;) Proff */
|
||||
|
||||
#ifdef WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <windowsx.h>
|
||||
#include <mmsystem.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include "native_midi.h"
|
||||
#include "native_midi_common.h"
|
||||
|
||||
struct _NativeMidiSong {
|
||||
int MusicLoaded;
|
||||
int MusicPlaying;
|
||||
MIDIHDR MidiStreamHdr;
|
||||
MIDIEVENT *NewEvents;
|
||||
Uint16 ppqn;
|
||||
int Size;
|
||||
int NewPos;
|
||||
};
|
||||
|
||||
static UINT MidiDevice=MIDI_MAPPER;
|
||||
static HMIDISTRM hMidiStream;
|
||||
static NativeMidiSong *currentsong;
|
||||
|
||||
static int BlockOut(NativeMidiSong *song)
|
||||
{
|
||||
MMRESULT err;
|
||||
int BlockSize;
|
||||
|
||||
if ((song->MusicLoaded) && (song->NewEvents))
|
||||
{
|
||||
// proff 12/8/98: Added for savety
|
||||
midiOutUnprepareHeader((HMIDIOUT)hMidiStream,&song->MidiStreamHdr,sizeof(MIDIHDR));
|
||||
if (song->NewPos>=song->Size)
|
||||
return 0;
|
||||
BlockSize=(song->Size-song->NewPos);
|
||||
if (BlockSize<=0)
|
||||
return 0;
|
||||
if (BlockSize>36000)
|
||||
BlockSize=36000;
|
||||
song->MidiStreamHdr.lpData=(void *)((unsigned char *)song->NewEvents+song->NewPos);
|
||||
song->NewPos+=BlockSize;
|
||||
song->MidiStreamHdr.dwBufferLength=BlockSize;
|
||||
song->MidiStreamHdr.dwBytesRecorded=BlockSize;
|
||||
song->MidiStreamHdr.dwFlags=0;
|
||||
err=midiOutPrepareHeader((HMIDIOUT)hMidiStream,&song->MidiStreamHdr,sizeof(MIDIHDR));
|
||||
if (err!=MMSYSERR_NOERROR)
|
||||
return 0;
|
||||
err=midiStreamOut(hMidiStream,&song->MidiStreamHdr,sizeof(MIDIHDR));
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void MIDItoStream(NativeMidiSong *song, MIDIEvent *evntlist)
|
||||
{
|
||||
int eventcount;
|
||||
MIDIEvent *event;
|
||||
MIDIEVENT *newevent;
|
||||
|
||||
eventcount=0;
|
||||
event=evntlist;
|
||||
while (event)
|
||||
{
|
||||
eventcount++;
|
||||
event=event->next;
|
||||
}
|
||||
song->NewEvents=malloc(eventcount*3*sizeof(DWORD));
|
||||
if (!song->NewEvents)
|
||||
return;
|
||||
memset(song->NewEvents,0,(eventcount*3*sizeof(DWORD)));
|
||||
|
||||
eventcount=0;
|
||||
event=evntlist;
|
||||
newevent=song->NewEvents;
|
||||
while (event)
|
||||
{
|
||||
int status = (event->status&0xF0)>>4;
|
||||
switch (status)
|
||||
{
|
||||
case MIDI_STATUS_NOTE_OFF:
|
||||
case MIDI_STATUS_NOTE_ON:
|
||||
case MIDI_STATUS_AFTERTOUCH:
|
||||
case MIDI_STATUS_CONTROLLER:
|
||||
case MIDI_STATUS_PROG_CHANGE:
|
||||
case MIDI_STATUS_PRESSURE:
|
||||
case MIDI_STATUS_PITCH_WHEEL:
|
||||
newevent->dwDeltaTime=event->time;
|
||||
newevent->dwEvent=(event->status|0x80)|(event->data[0]<<8)|(event->data[1]<<16)|(MEVT_SHORTMSG<<24);
|
||||
newevent=(MIDIEVENT*)((char*)newevent+(3*sizeof(DWORD)));
|
||||
eventcount++;
|
||||
break;
|
||||
|
||||
case MIDI_STATUS_SYSEX:
|
||||
if (event->status == 0xFF && event->data[0] == 0x51) /* Tempo change */
|
||||
{
|
||||
int tempo = (event->extraData[0] << 16) |
|
||||
(event->extraData[1] << 8) |
|
||||
event->extraData[2];
|
||||
newevent->dwDeltaTime=event->time;
|
||||
newevent->dwEvent=(MEVT_TEMPO<<24) | tempo;
|
||||
newevent=(MIDIEVENT*)((char*)newevent+(3*sizeof(DWORD)));
|
||||
eventcount++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
event=event->next;
|
||||
}
|
||||
|
||||
song->Size=eventcount*3*sizeof(DWORD);
|
||||
|
||||
{
|
||||
int time;
|
||||
int temptime;
|
||||
|
||||
song->NewPos=0;
|
||||
time=0;
|
||||
newevent=song->NewEvents;
|
||||
while (song->NewPos<song->Size)
|
||||
{
|
||||
temptime=newevent->dwDeltaTime;
|
||||
newevent->dwDeltaTime-=time;
|
||||
time=temptime;
|
||||
if ((song->NewPos+12)>=song->Size)
|
||||
newevent->dwEvent |= MEVT_F_CALLBACK;
|
||||
newevent=(MIDIEVENT*)((char*)newevent+(3*sizeof(DWORD)));
|
||||
song->NewPos+=12;
|
||||
}
|
||||
}
|
||||
song->NewPos=0;
|
||||
song->MusicLoaded=1;
|
||||
}
|
||||
|
||||
void CALLBACK MidiProc( HMIDIIN hMidi, UINT uMsg, DWORD dwInstance,
|
||||
DWORD dwParam1, DWORD dwParam2 )
|
||||
{
|
||||
switch( uMsg )
|
||||
{
|
||||
case MOM_DONE:
|
||||
if ((currentsong->MusicLoaded) && ((DWORD)dwParam1 == (DWORD)¤tsong->MidiStreamHdr))
|
||||
BlockOut(currentsong);
|
||||
break;
|
||||
case MOM_POSITIONCB:
|
||||
if ((currentsong->MusicLoaded) && ((DWORD)dwParam1 == (DWORD)¤tsong->MidiStreamHdr))
|
||||
currentsong->MusicPlaying=0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int native_midi_detect()
|
||||
{
|
||||
MMRESULT merr;
|
||||
HMIDISTRM MidiStream;
|
||||
|
||||
merr=midiStreamOpen(&MidiStream,&MidiDevice,1,(DWORD)&MidiProc,0,CALLBACK_FUNCTION);
|
||||
if (merr!=MMSYSERR_NOERROR)
|
||||
MidiStream=0;
|
||||
midiStreamClose(MidiStream);
|
||||
if (!MidiStream)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
NativeMidiSong *native_midi_loadsong(char *midifile)
|
||||
{
|
||||
NativeMidiSong *newsong;
|
||||
MIDIEvent *evntlist = NULL;
|
||||
SDL_RWops *rw;
|
||||
|
||||
newsong=malloc(sizeof(NativeMidiSong));
|
||||
if (!newsong)
|
||||
return NULL;
|
||||
memset(newsong,0,sizeof(NativeMidiSong));
|
||||
|
||||
/* Attempt to load the midi file */
|
||||
rw = SDL_RWFromFile(midifile, "rb");
|
||||
if (rw) {
|
||||
evntlist = CreateMIDIEventList(rw, &newsong->ppqn);
|
||||
SDL_RWclose(rw);
|
||||
if (!evntlist)
|
||||
{
|
||||
free(newsong);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
MIDItoStream(newsong, evntlist);
|
||||
|
||||
FreeMIDIEventList(evntlist);
|
||||
|
||||
return newsong;
|
||||
}
|
||||
|
||||
NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw)
|
||||
{
|
||||
NativeMidiSong *newsong;
|
||||
MIDIEvent *evntlist = NULL;
|
||||
|
||||
newsong=malloc(sizeof(NativeMidiSong));
|
||||
if (!newsong)
|
||||
return NULL;
|
||||
memset(newsong,0,sizeof(NativeMidiSong));
|
||||
|
||||
/* Attempt to load the midi file */
|
||||
evntlist = CreateMIDIEventList(rw, &newsong->ppqn);
|
||||
if (!evntlist)
|
||||
{
|
||||
free(newsong);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MIDItoStream(newsong, evntlist);
|
||||
|
||||
FreeMIDIEventList(evntlist);
|
||||
|
||||
return newsong;
|
||||
}
|
||||
|
||||
void native_midi_freesong(NativeMidiSong *song)
|
||||
{
|
||||
if (hMidiStream)
|
||||
{
|
||||
midiStreamStop(hMidiStream);
|
||||
midiStreamClose(hMidiStream);
|
||||
}
|
||||
if (song)
|
||||
{
|
||||
if (song->NewEvents)
|
||||
free(song->NewEvents);
|
||||
free(song);
|
||||
}
|
||||
}
|
||||
|
||||
void native_midi_start(NativeMidiSong *song)
|
||||
{
|
||||
MMRESULT merr;
|
||||
MIDIPROPTIMEDIV mptd;
|
||||
|
||||
native_midi_stop();
|
||||
if (!hMidiStream)
|
||||
{
|
||||
merr=midiStreamOpen(&hMidiStream,&MidiDevice,1,(DWORD)&MidiProc,0,CALLBACK_FUNCTION);
|
||||
if (merr!=MMSYSERR_NOERROR)
|
||||
{
|
||||
hMidiStream=0;
|
||||
return;
|
||||
}
|
||||
//midiStreamStop(hMidiStream);
|
||||
currentsong=song;
|
||||
currentsong->NewPos=0;
|
||||
currentsong->MusicPlaying=1;
|
||||
mptd.cbStruct=sizeof(MIDIPROPTIMEDIV);
|
||||
mptd.dwTimeDiv=currentsong->ppqn;
|
||||
merr=midiStreamProperty(hMidiStream,(LPBYTE)&mptd,MIDIPROP_SET | MIDIPROP_TIMEDIV);
|
||||
BlockOut(song);
|
||||
merr=midiStreamRestart(hMidiStream);
|
||||
}
|
||||
}
|
||||
|
||||
void native_midi_stop()
|
||||
{
|
||||
if (!hMidiStream)
|
||||
return;
|
||||
midiStreamStop(hMidiStream);
|
||||
midiStreamClose(hMidiStream);
|
||||
currentsong=NULL;
|
||||
hMidiStream = 0;
|
||||
}
|
||||
|
||||
int native_midi_active()
|
||||
{
|
||||
return currentsong->MusicPlaying;
|
||||
}
|
||||
|
||||
void native_midi_setvolume(int volume)
|
||||
{
|
||||
int calcVolume;
|
||||
if (volume > 128)
|
||||
volume = 128;
|
||||
if (volume < 0)
|
||||
volume = 0;
|
||||
calcVolume = (65535 * volume / 128);
|
||||
|
||||
midiOutSetVolume((HMIDIOUT)hMidiStream, MAKELONG(calcVolume , calcVolume));
|
||||
}
|
||||
|
||||
char *native_midi_error()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
#endif /* Windows native MIDI support */
|
||||
@@ -1,104 +0,0 @@
|
||||
# Copyright 2005 PyTom <pytom@bishoujo.us>
|
||||
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation files
|
||||
# (the "Software"), to deal in the Software without restriction,
|
||||
# including without limitation the rights to use, copy, modify, merge,
|
||||
# publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
# and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
cdef extern from "pss.h":
|
||||
|
||||
cdef struct SDL_RWops:
|
||||
int (* close)(SDL_RWops *)
|
||||
|
||||
SDL_RWops* RWopsFromPython(object obj)
|
||||
|
||||
cdef int SDL_GetTicks()
|
||||
|
||||
cdef extern from "native_midi.h":
|
||||
|
||||
cdef struct _NativeMidiSong:
|
||||
pass
|
||||
|
||||
ctypedef _NativeMidiSong NativeMidiSong
|
||||
|
||||
int native_midi_detect()
|
||||
NativeMidiSong *native_midi_loadsong(char *midifile)
|
||||
NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw)
|
||||
void native_midi_freesong(NativeMidiSong *song)
|
||||
void native_midi_start(NativeMidiSong *song)
|
||||
void native_midi_stop()
|
||||
int native_midi_active()
|
||||
void native_midi_setvolume(int volume)
|
||||
char *native_midi_error()
|
||||
|
||||
cdef NativeMidiSong *current
|
||||
cdef int start
|
||||
|
||||
current = NULL
|
||||
|
||||
def init():
|
||||
if not native_midi_detect():
|
||||
raise Exception("Could not initialize native midi support.")
|
||||
|
||||
def play(file):
|
||||
cdef SDL_RWops *rw
|
||||
|
||||
if current:
|
||||
stop()
|
||||
|
||||
rw = RWopsFromPython(file)
|
||||
|
||||
if rw == NULL:
|
||||
raise Exception, "Could not create RWops."
|
||||
|
||||
global current
|
||||
current = native_midi_loadsong_RW(rw)
|
||||
|
||||
rw.close(rw)
|
||||
|
||||
if current == NULL:
|
||||
raise Exception, "Could not load midi file."
|
||||
|
||||
global start
|
||||
start = SDL_GetTicks()
|
||||
native_midi_start(current)
|
||||
|
||||
|
||||
def stop():
|
||||
|
||||
global current
|
||||
|
||||
if not current:
|
||||
return
|
||||
|
||||
native_midi_stop()
|
||||
native_midi_freesong(current)
|
||||
current = NULL
|
||||
|
||||
def busy():
|
||||
return native_midi_active()
|
||||
|
||||
def get_pos():
|
||||
|
||||
if not busy():
|
||||
return -1
|
||||
|
||||
return SDL_GetTicks() - start
|
||||
|
||||
def set_volume(volume):
|
||||
native_midi_setvolume(int(volume * 128))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2005 PyTom <pytom@bishoujo.us>
|
||||
Copyright 2005-2009 PyTom <pytom@bishoujo.us>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation files
|
||||
@@ -24,9 +24,19 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#include "pss.h"
|
||||
#include <Python.h>
|
||||
#include <SDL/SDL.h>
|
||||
#include <SDL/SDL_sound.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* Declarations of ffdecode functions. */
|
||||
struct VideoState;
|
||||
|
||||
struct VideoState *ffpy_stream_open(SDL_RWops *, const char *);
|
||||
void ffpy_stream_close(struct VideoState *is);
|
||||
void ffpy_alloc_event(struct VideoState *vs, PyObject *surface);
|
||||
void ffpy_refresh_event(struct VideoState *vs);
|
||||
void ffpy_init(int rate, int status);
|
||||
int ffpy_audio_decode(struct VideoState *is, Uint8 *stream, int len);
|
||||
|
||||
|
||||
/* The current Python. */
|
||||
PyInterpreterState* interp;
|
||||
PyThreadState* thread = NULL;
|
||||
@@ -52,6 +62,10 @@ static void decref(PyObject *ref) {
|
||||
}
|
||||
|
||||
/* Locking on entry from python... */
|
||||
// #define BEGIN() PyThreadState *_save;
|
||||
// #define ENTER() { printf("Locking by %s.\n", __FUNCTION__); _save = PyEval_SaveThread(); SDL_LockAudio(); printf("Lock by %s\n", __FUNCTION__); }
|
||||
// #define EXIT() { SDL_UnlockAudio(); PyEval_RestoreThread(_save); printf("Release by %s\n", __FUNCTION__); }
|
||||
|
||||
#define BEGIN() PyThreadState *_save;
|
||||
#define ENTER() { _save = PyEval_SaveThread(); SDL_LockAudio(); }
|
||||
#define EXIT() { SDL_UnlockAudio(); PyEval_RestoreThread(_save); }
|
||||
@@ -60,9 +74,6 @@ static void decref(PyObject *ref) {
|
||||
#define min(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#define max(a, b) (((a) > (b)) ? (a) : (b))
|
||||
|
||||
/* The number of channels we support. */
|
||||
#define NUM_CHANNELS 8
|
||||
|
||||
/* Various error codes. */
|
||||
#define SUCCESS 0
|
||||
#define SDL_ERROR -1
|
||||
@@ -86,7 +97,7 @@ struct Channel {
|
||||
|
||||
/* The currently playing sample, NULL if this sample isn't playing
|
||||
anything. */
|
||||
Sound_Sample *playing;
|
||||
struct VideoState *playing;
|
||||
|
||||
/* The name of the playing music. */
|
||||
PyObject *playing_name;
|
||||
@@ -98,8 +109,11 @@ struct Channel {
|
||||
int playing_tight;
|
||||
|
||||
/* The queued up sample. */
|
||||
Sound_Sample *queued;
|
||||
struct VideoState *queued;
|
||||
|
||||
/* A sample that's dying, and needs to be deallocated. */
|
||||
struct VideoState *dying;
|
||||
|
||||
/* The name of the queued up sample. */
|
||||
PyObject *queued_name;
|
||||
|
||||
@@ -115,12 +129,6 @@ struct Channel {
|
||||
/* The volume of the channel. */
|
||||
int volume;
|
||||
|
||||
/* The number of decoded bytes in the buffer. */
|
||||
int decoded;
|
||||
|
||||
/* The offset into the buffer that we last stopped at. */
|
||||
int last;
|
||||
|
||||
/* The position (in bytes) that this channel has queued to. */
|
||||
int pos;
|
||||
|
||||
@@ -145,12 +153,34 @@ struct Channel {
|
||||
/* The event posted to the queue when we finish a track. */
|
||||
int event;
|
||||
|
||||
/* The pan being applied to the current channel. */
|
||||
float pan_start;
|
||||
float pan_end;
|
||||
|
||||
/* The length of the current pan, in samples. */
|
||||
unsigned int pan_length;
|
||||
|
||||
/* The number of samples we've finished in the current pan. */
|
||||
unsigned int pan_done;
|
||||
|
||||
|
||||
/* These are used like in pan, above. Unlike the volume parameter,
|
||||
the voulme set here is persisted between sessions. */
|
||||
float vol2_start;
|
||||
float vol2_end;
|
||||
unsigned int vol2_length;
|
||||
unsigned int vol2_done;
|
||||
};
|
||||
|
||||
/*
|
||||
* The number of channels the system knows about.
|
||||
*/
|
||||
int num_channels = 0;
|
||||
|
||||
/*
|
||||
* All of the channels that the system knows about.
|
||||
*/
|
||||
struct Channel channels[NUM_CHANNELS];
|
||||
struct Channel *channels = NULL;
|
||||
|
||||
/*
|
||||
* The spec of the audio that is playing.
|
||||
@@ -158,6 +188,39 @@ struct Channel channels[NUM_CHANNELS];
|
||||
SDL_AudioSpec audio_spec;
|
||||
|
||||
|
||||
static float interpolate_pan(struct Channel *c) {
|
||||
float done;
|
||||
|
||||
if (c->pan_done > c->pan_length) {
|
||||
c->pan_length = 0;
|
||||
}
|
||||
|
||||
if (c->pan_length == 0) {
|
||||
return c->pan_end;
|
||||
}
|
||||
|
||||
done = 1.0 * c->pan_done / c->pan_length;
|
||||
|
||||
return c->pan_start + done * (c->pan_end - c->pan_start);
|
||||
|
||||
}
|
||||
|
||||
static float interpolate_vol2(struct Channel *c) {
|
||||
float done;
|
||||
|
||||
if (c->vol2_done > c->vol2_length) {
|
||||
c->vol2_length = 0;
|
||||
}
|
||||
|
||||
if (c->vol2_length == 0) {
|
||||
return c->vol2_end;
|
||||
}
|
||||
|
||||
done = 1.0 * c->vol2_done / c->vol2_length;
|
||||
|
||||
return c->vol2_start + done * (c->vol2_end - c->vol2_start);
|
||||
}
|
||||
|
||||
static int ms_to_bytes(int ms) {
|
||||
return ((long long) ms) * audio_spec.freq * audio_spec.channels * 2 / 1000;
|
||||
}
|
||||
@@ -171,8 +234,6 @@ static void start_sample(struct Channel* c, int reset_fade) {
|
||||
|
||||
if (!c) return;
|
||||
|
||||
c->decoded = 0;
|
||||
c->last = 0;
|
||||
c->pos = 0;
|
||||
|
||||
if (reset_fade) {
|
||||
@@ -185,62 +246,52 @@ static void start_sample(struct Channel* c, int reset_fade) {
|
||||
c->fade_off = 0;
|
||||
c->fade_vol = 0;
|
||||
|
||||
c->fade_step_len = ms_to_bytes(c->playing_fadein) / fade_steps;
|
||||
c->fade_step_len &= ~0x7; // Even sample.
|
||||
|
||||
if (fade_steps) {
|
||||
c->fade_step_len = ms_to_bytes(c->playing_fadein) / fade_steps;
|
||||
c->fade_step_len &= ~0x7; // Even sample.
|
||||
} else {
|
||||
c->fade_step_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
c->stop_bytes = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* static void update_pause(void) { */
|
||||
/* int i; */
|
||||
/* int pause = 1; */
|
||||
|
||||
/* for (i = 0; i < NUM_CHANNELS; i++) { */
|
||||
/* if (channels[i].playing) { */
|
||||
/* pause = 0; */
|
||||
/* break; */
|
||||
/* } */
|
||||
/* } */
|
||||
|
||||
/* SDL_PauseAudio(pause); */
|
||||
/* } */
|
||||
|
||||
static void free_sample(Sound_Sample *ss) {
|
||||
Sound_FreeSample(ss);
|
||||
static void free_sample(struct VideoState *ss) {
|
||||
ffpy_stream_close(ss);
|
||||
}
|
||||
|
||||
#define MAX_SHORT (32767)
|
||||
#define MIN_SHORT (-32768)
|
||||
|
||||
// Actually mixes the audio.
|
||||
static void mixaudio(Uint8 *dst, Uint8 *src, int length, int volume) {
|
||||
|
||||
// SDL_MixAudio may not work when length % 16 != 0.
|
||||
if ((length & 0x0f) == 0) {
|
||||
SDL_MixAudio(dst, src, length, volume);
|
||||
} else {
|
||||
int i;
|
||||
short *sdst = (short *) dst;
|
||||
short *ssrc = (short *) src;
|
||||
|
||||
int newlength = length + 16 - (length & 0xf);
|
||||
Uint8 newsrc[newlength];
|
||||
Uint8 newdst[newlength];
|
||||
|
||||
memcpy(newsrc, src, length);
|
||||
memcpy(newdst, dst, length);
|
||||
for (i = 0; i < length / 2; i++) {
|
||||
int sound = *sdst + (volume * *ssrc) / SDL_MIX_MAXVOLUME;
|
||||
if (sound > MAX_SHORT) {
|
||||
sound = MAX_SHORT;
|
||||
}
|
||||
if (sound < MIN_SHORT) {
|
||||
sound = MIN_SHORT;
|
||||
}
|
||||
|
||||
// Mix the audio once.
|
||||
SDL_MixAudio(newdst, newsrc, newlength, volume);
|
||||
|
||||
memcpy(dst, newdst, length);
|
||||
}
|
||||
*sdst++ = (short) sound;
|
||||
ssrc++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Mixes the audio, while performing fading.
|
||||
static void fade_mixaudio(struct Channel *c,
|
||||
Uint8 *dst, Uint8 *src, int length) {
|
||||
|
||||
while (length) {
|
||||
|
||||
|
||||
// No fade case.
|
||||
if (c->fade_step_len == 0) {
|
||||
mixaudio(dst, src, length, c->volume);
|
||||
@@ -250,8 +301,9 @@ static void fade_mixaudio(struct Channel *c,
|
||||
// Fading, but we have some space left in the current step.
|
||||
if (c->fade_off < c->fade_step_len) {
|
||||
int l = min(c->fade_step_len - c->fade_off, length);
|
||||
mixaudio(dst, src, l, c->fade_vol);
|
||||
|
||||
mixaudio(dst, src, l, c->fade_vol);
|
||||
|
||||
length -= l;
|
||||
dst += l;
|
||||
src += l;
|
||||
@@ -275,7 +327,7 @@ static void fade_mixaudio(struct Channel *c,
|
||||
c->fade_step_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -290,11 +342,62 @@ static void post_event(struct Channel *c) {
|
||||
SDL_PushEvent(&e);
|
||||
}
|
||||
|
||||
/* This handels panning and vol2 manipulations. */
|
||||
static void pan_audio(struct Channel *c, Uint8 *stream, int length) {
|
||||
int i;
|
||||
short *sample = (short *) stream;
|
||||
length /= 4;
|
||||
|
||||
float pan;
|
||||
float vol2;
|
||||
int left = 256;
|
||||
int right = 256;
|
||||
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
|
||||
if ((i & 0x1f) == 0) {
|
||||
pan = interpolate_pan(c);
|
||||
vol2 = interpolate_vol2(c);
|
||||
|
||||
// If nothing to do, skip 32 samples.
|
||||
if (pan == 0.0 && vol2 == 1.0) {
|
||||
i += 31;
|
||||
c->pan_done += 32;
|
||||
c->vol2_done += 32;
|
||||
sample += 32 * 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
vol2 *= 256.0;
|
||||
|
||||
if (pan < 0) {
|
||||
left = (int) vol2;
|
||||
right = (int) (vol2 * (1.0 + pan));
|
||||
} else {
|
||||
left = (int) (vol2 * (1.0 - pan));
|
||||
right = (int) vol2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
*sample = (short) ((*sample * left) >> 8);
|
||||
sample++;
|
||||
*sample = (short) ((*sample * right) >> 8);
|
||||
sample++;
|
||||
|
||||
c->pan_done += 1;
|
||||
c->vol2_done += 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void callback(void *userdata, Uint8 *stream, int length) {
|
||||
int channel = 0;
|
||||
|
||||
for (channel = 0; channel < NUM_CHANNELS; channel++) {
|
||||
|
||||
for (channel = 0; channel < num_channels; channel++) {
|
||||
|
||||
|
||||
int mixed = 0;
|
||||
struct Channel *c = &channels[channel];
|
||||
|
||||
@@ -308,21 +411,22 @@ static void callback(void *userdata, Uint8 *stream, int length) {
|
||||
|
||||
while (mixed < length && c->playing) {
|
||||
int mixleft = length - mixed;
|
||||
int bufleft = c->decoded - c->last;
|
||||
Uint8 buffer[mixleft];
|
||||
|
||||
// Decode some amount of data.
|
||||
|
||||
int bytes = ffpy_audio_decode(c->playing, buffer, mixleft);
|
||||
|
||||
// We have some data in the buffer.
|
||||
if (c->stop_bytes && bufleft) {
|
||||
int bytes = min(bufleft, mixleft);
|
||||
|
||||
if (c->stop_bytes && bytes) {
|
||||
|
||||
if (c->stop_bytes != -1)
|
||||
bytes = min(c->stop_bytes, bytes);
|
||||
|
||||
fade_mixaudio(c, &stream[mixed],
|
||||
&( ((Uint8*) c->playing->buffer) [c->last]),
|
||||
bytes);
|
||||
pan_audio(c, buffer, bytes);
|
||||
fade_mixaudio(c, &stream[mixed], buffer, bytes);
|
||||
|
||||
mixed += bytes;
|
||||
c->last += bytes;
|
||||
|
||||
if (c->stop_bytes != -1)
|
||||
c->stop_bytes -= bytes;
|
||||
@@ -335,13 +439,14 @@ static void callback(void *userdata, Uint8 *stream, int length) {
|
||||
// Otherwise, no data is left in the buffer. Check why,
|
||||
// and act accordingly.
|
||||
|
||||
// EOF or ERROR -- Skip to the next sample.
|
||||
if (c->stop_bytes == 0 || c->playing->flags & (SOUND_SAMPLEFLAG_ERROR | SOUND_SAMPLEFLAG_EOF)) {
|
||||
// Skip to the next sample.
|
||||
if (c->stop_bytes == 0 || bytes == 0) {
|
||||
|
||||
int old_tight = c->playing_tight;
|
||||
|
||||
post_event(c);
|
||||
|
||||
free_sample(c->playing);
|
||||
c->dying = c->playing;
|
||||
decref(c->playing_name);
|
||||
|
||||
c->playing = c->queued;
|
||||
@@ -355,31 +460,54 @@ static void callback(void *userdata, Uint8 *stream, int length) {
|
||||
c->queued_tight = 0;
|
||||
|
||||
start_sample(c, ! old_tight);
|
||||
/* update_pause(); */
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we're here, we're simply out of data in the
|
||||
// buffer. Decode some.
|
||||
|
||||
c->decoded = Sound_Decode(c->playing);
|
||||
c->last = 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks that the given channel is in range. Returns 0 if it is,
|
||||
* sets an error and returns -1 if it is not.
|
||||
* sets an error and returns -1 if it is not. Allocates channels
|
||||
* that don't already exist.
|
||||
*/
|
||||
static int check_channel(int c) {
|
||||
if (c < 0 || c >= NUM_CHANNELS) {
|
||||
int i;
|
||||
|
||||
if (c < 0) {
|
||||
error(PSS_ERROR);
|
||||
error_msg = "Channel number out of range.";
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (c >= num_channels) {
|
||||
channels = realloc(channels, sizeof(struct Channel) * (c + 1));
|
||||
|
||||
for (i = num_channels; i <= c; i++) {
|
||||
|
||||
channels[i].playing = NULL;
|
||||
channels[i].queued = NULL;
|
||||
channels[i].playing_name = NULL;
|
||||
channels[i].queued_name = NULL;
|
||||
channels[i].dying = NULL;
|
||||
channels[i].volume = SDL_MIX_MAXVOLUME;
|
||||
channels[i].paused = 1;
|
||||
channels[i].event = 0;
|
||||
channels[i].pan_start = 0.0;
|
||||
channels[i].pan_end = 0.0;
|
||||
channels[i].pan_length = 0;
|
||||
channels[i].pan_done = 0;
|
||||
channels[i].vol2_start = 1.0;
|
||||
channels[i].vol2_end = 1.0;
|
||||
channels[i].vol2_length = 0;
|
||||
channels[i].vol2_done = 0;
|
||||
}
|
||||
|
||||
num_channels = c + 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -388,15 +516,9 @@ static int check_channel(int c) {
|
||||
* Loads the provided sample. Returns the sample on success, NULL on
|
||||
* failure.
|
||||
*/
|
||||
static Sound_Sample *load_sample(SDL_RWops *rw, const char *ext) {
|
||||
Sound_AudioInfo ai;
|
||||
Sound_Sample *rv;
|
||||
|
||||
ai.format = audio_spec.format;
|
||||
ai.channels = audio_spec.channels;
|
||||
ai.rate = audio_spec.freq;
|
||||
|
||||
rv = Sound_NewSample(rw, ext, &ai, audio_spec.samples * audio_spec.channels * 2);
|
||||
struct VideoState *load_sample(SDL_RWops *rw, const char *ext) {
|
||||
struct VideoState *rv;
|
||||
rv = ffpy_stream_open(rw, ext);
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -667,14 +789,20 @@ void PSS_fadeout(int channel, int ms) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
fade_steps = c->volume;
|
||||
c->fade_delta = -1;
|
||||
c->fade_off = 0;
|
||||
c->fade_vol = c->volume;
|
||||
|
||||
c->fade_step_len = ms_to_bytes(ms) / fade_steps;
|
||||
c->fade_step_len &= ~0x7; // Even sample.
|
||||
|
||||
if (fade_steps) {
|
||||
c->fade_step_len = ms_to_bytes(ms) / fade_steps;
|
||||
c->fade_step_len &= ~0x7; // Even sample.
|
||||
} else {
|
||||
c->fade_step_len = 0;
|
||||
}
|
||||
|
||||
c->stop_bytes = ms_to_bytes(ms);
|
||||
c->queued_tight = 0;
|
||||
|
||||
@@ -719,7 +847,7 @@ void PSS_unpause_all(void) {
|
||||
|
||||
ENTER();
|
||||
|
||||
for (i = 0; i < NUM_CHANNELS; i++) {
|
||||
for (i = 0; i < num_channels; i++) {
|
||||
channels[i].paused = 0;
|
||||
}
|
||||
|
||||
@@ -805,6 +933,7 @@ void PSS_set_volume(int channel, float volume) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
float PSS_get_volume(int channel) {
|
||||
|
||||
float rv;
|
||||
@@ -828,13 +957,63 @@ float PSS_get_volume(int channel) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* This sets the pan of the channel... independent volumes for the
|
||||
* left and right channels.
|
||||
*/
|
||||
void PSS_set_pan(int channel, float pan, float delay) {
|
||||
struct Channel *c;
|
||||
BEGIN();
|
||||
|
||||
if (check_channel(channel)) {
|
||||
return;
|
||||
}
|
||||
|
||||
c = &channels[channel];
|
||||
|
||||
ENTER();
|
||||
|
||||
c->pan_start = interpolate_pan(c);
|
||||
c->pan_end = pan;
|
||||
c->pan_length = (int) (audio_spec.freq * delay);
|
||||
c->pan_done = 0;
|
||||
|
||||
EXIT();
|
||||
|
||||
error(SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* This sets the secondary volume of the channel.
|
||||
*/
|
||||
void PSS_set_secondary_volume(int channel, float vol2, float delay) {
|
||||
struct Channel *c;
|
||||
BEGIN();
|
||||
|
||||
if (check_channel(channel)) {
|
||||
return;
|
||||
}
|
||||
|
||||
c = &channels[channel];
|
||||
|
||||
ENTER();
|
||||
|
||||
c->vol2_start = interpolate_vol2(c);
|
||||
c->vol2_end = vol2;
|
||||
c->vol2_length = (int) (audio_spec.freq * delay);
|
||||
c->vol2_done = 0;
|
||||
|
||||
EXIT();
|
||||
|
||||
error(SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initializes the sound to the given frequencies, channels, and
|
||||
* sample buffer size.
|
||||
*/
|
||||
void PSS_init(int freq, int stereo, int samples) {
|
||||
int i;
|
||||
void PSS_init(int freq, int stereo, int samples, int status) {
|
||||
|
||||
if (initialized) {
|
||||
return;
|
||||
@@ -865,31 +1044,15 @@ void PSS_init(int freq, int stereo, int samples) {
|
||||
audio_spec.callback = callback;
|
||||
audio_spec.userdata = NULL;
|
||||
|
||||
if (SDL_OpenAudio(&audio_spec, NULL)) {
|
||||
if (SDL_OpenAudio(&audio_spec, &audio_spec)) {
|
||||
error(SDL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (! Sound_Init()) {
|
||||
SDL_CloseAudio();
|
||||
error(SOUND_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the channels.
|
||||
*/
|
||||
|
||||
for (i = 0; i < NUM_CHANNELS; i++) {
|
||||
channels[i].playing = NULL;
|
||||
channels[i].queued = NULL;
|
||||
channels[i].volume = SDL_MIX_MAXVOLUME;
|
||||
channels[i].paused = 1;
|
||||
channels[i].event = 0;
|
||||
}
|
||||
|
||||
SDL_PauseAudio(0);
|
||||
|
||||
ffpy_init(audio_spec.freq, status);
|
||||
|
||||
initialized = 1;
|
||||
|
||||
error(SUCCESS);
|
||||
@@ -908,17 +1071,54 @@ void PSS_quit() {
|
||||
SDL_PauseAudio(1);
|
||||
EXIT();
|
||||
|
||||
for (i = 0; i < NUM_CHANNELS; i++) {
|
||||
for (i = 0; i < num_channels; i++) {
|
||||
PSS_stop(i);
|
||||
}
|
||||
|
||||
Sound_Quit();
|
||||
SDL_CloseAudio();
|
||||
|
||||
num_channels = 0;
|
||||
initialized = 0;
|
||||
error(SUCCESS);
|
||||
}
|
||||
|
||||
/* This must be called frequently, to take care of deallocating dead
|
||||
* streams. */
|
||||
void PSS_periodic() {
|
||||
BEGIN();
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_channels; i++) {
|
||||
if (channels[i].dying) {
|
||||
ENTER();
|
||||
ffpy_stream_close(channels[i].dying);
|
||||
channels[i].dying = NULL;
|
||||
EXIT();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This should be called in response to an FF_ALLOC_EVENT, with a pygame
|
||||
* surface to display the movie on. */
|
||||
void PSS_alloc_event(PyObject *surface) {
|
||||
int i;
|
||||
for (i = 0; i < num_channels; i++) {
|
||||
if (channels[i].playing) {
|
||||
ffpy_alloc_event(channels[i].playing, surface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This should be called in response to a FF_REFRESH_EVENT */
|
||||
void PSS_refresh_event(void) {
|
||||
int i;
|
||||
for (i = 0; i < num_channels; i++) {
|
||||
if (channels[i].playing) {
|
||||
ffpy_refresh_event(channels[i].playing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the error message string if an error has occured, or
|
||||
@@ -931,7 +1131,7 @@ const char *PSS_get_error() {
|
||||
case SDL_ERROR:
|
||||
return SDL_GetError();
|
||||
case SOUND_ERROR:
|
||||
return Sound_GetError();
|
||||
return "Some sort of ffmpeg error.";
|
||||
case PSS_ERROR:
|
||||
return error_msg;
|
||||
default:
|
||||
|
||||
@@ -29,7 +29,6 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
SDL_RWops* RWopsFromPythonThreaded(PyObject* obj);
|
||||
|
||||
|
||||
void PSS_play(int channel, SDL_RWops *rw, const char *ext, PyObject *name, int fadeout, int tight, int paused);
|
||||
void PSS_queue(int channel, SDL_RWops *rw, const char *ext, PyObject *name, int fadeout, int tight);
|
||||
void PSS_stop(int channel);
|
||||
@@ -43,8 +42,15 @@ void PSS_set_endevent(int channel, int event);
|
||||
int PSS_get_pos(int channel);
|
||||
void PSS_set_volume(int channel, float volume);
|
||||
float PSS_get_volume(int channel);
|
||||
void PSS_init(int freq, int stereo, int samples);
|
||||
void PSS_set_pan(int channel, float pan, float delay);
|
||||
void PSS_set_secondary_volume(int channel, float vol2, float delay);
|
||||
void PSS_init(int freq, int stereo, int samples, int status);
|
||||
void PSS_quit(void);
|
||||
|
||||
void PSS_periodic(void);
|
||||
void PSS_alloc_event(PyObject *surface);
|
||||
void PSS_refresh_event(void);
|
||||
|
||||
const char *PSS_get_error(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -86,7 +86,8 @@ void transform32_core(PyObject *pysrc,
|
||||
PyObject *pydst,
|
||||
float, float,
|
||||
float, float,
|
||||
float, float);
|
||||
float, float,
|
||||
int, float);
|
||||
|
||||
void blend32_core(PyObject *pysrca,
|
||||
PyObject *pysrcb,
|
||||
@@ -109,4 +110,9 @@ void staticgray_core(
|
||||
int rmul, int gmul, int bmul, int amul, int shift,
|
||||
char *vmap);
|
||||
|
||||
int subpixel32(
|
||||
PyObject *pysrc, PyObject *pydst,
|
||||
float xoffset, float yoffset, int ashift);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
# -*- python -*-
|
||||
# Copyright 2005 PyTom <pytom@bishoujo.us>
|
||||
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation files
|
||||
# (the "Software"), to deal in the Software without restriction,
|
||||
# including without limitation the rights to use, copy, modify, merge,
|
||||
# publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
# and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
cdef extern void ffplay_init()
|
||||
cdef extern int ffplay_open(char *filename)
|
||||
cdef extern void ffplay_close()
|
||||
cdef extern int ffplay_event_handler(int)
|
||||
|
||||
def version():
|
||||
return 1
|
||||
|
||||
def init():
|
||||
ffplay_init()
|
||||
|
||||
def play(fn):
|
||||
if ffplay_open(fn):
|
||||
raise Exception("ffplay could not open: " + fn)
|
||||
|
||||
def stop():
|
||||
ffplay_close()
|
||||
|
||||
def event_handler(type):
|
||||
return ffplay_event_handler(type)
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include <Python.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "pygame.h"
|
||||
#include <pygame/pygame.h>
|
||||
#include "structmember.h"
|
||||
#include "renpy_ttf.h"
|
||||
|
||||
|
||||
@@ -310,9 +310,10 @@ static int rw_seek_th(SDL_RWops* context, int offset, int whence)
|
||||
{
|
||||
result = PyObject_CallFunction(helper->seek, "ii", offset, whence);
|
||||
if(!result) {
|
||||
PyThreadState_Swap(oldstate);
|
||||
PyEval_ReleaseLock();
|
||||
return -1;
|
||||
PyErr_Clear();
|
||||
PyThreadState_Swap(oldstate);
|
||||
PyEval_ReleaseLock();
|
||||
return -1;
|
||||
}
|
||||
|
||||
Py_DECREF(result);
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
|
||||
import distutils.core
|
||||
import os
|
||||
import os.path
|
||||
@@ -12,47 +10,134 @@ try:
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
# These control the level of optimization versus debugging.
|
||||
extra_compile_args = [ "-O3", "-funroll-loops" ]
|
||||
# extra_compile_args = [ "-O0", "-ggdb" ]
|
||||
|
||||
|
||||
# This environment variable should have the full path to the installed
|
||||
# Ren'Py dependencies.
|
||||
install = os.environ.get("RENPY_DEPS_INSTALL", "/home/tom/ab/deps/install")
|
||||
install = os.environ.get("RENPY_DEPS_INSTALL", None)
|
||||
|
||||
# Check to see if that's the case.
|
||||
if not os.path.isdir(install):
|
||||
print "The Ren'Py dependencies install directory:"
|
||||
print
|
||||
print install
|
||||
print
|
||||
print "does not exist. Please set RENPY_DEPS_INSTALL to the correct"
|
||||
print "location of the Ren'Py dependencies install directory, and "
|
||||
print "re-run this script."
|
||||
if install is None:
|
||||
print """
|
||||
The RENPY_DEPS_INSTALL environment variable has not been set. This
|
||||
should be set to a double-colon-delimited list of places where the
|
||||
Ren'Py dependencies can be found. (To use system libraries, this can
|
||||
be set to a system directory, like /usr.)
|
||||
"""
|
||||
sys.exit(-1)
|
||||
|
||||
# Default compile arguements for everybody.
|
||||
include_dirs = [ install + "/include", install + "/include/SDL", install + "/include/freetype2", install + "/include/pygame"]
|
||||
library_dirs = [ install + "/lib" ]
|
||||
extra_compile_args = [ "-O3", "-funroll-loops", "-ffast-math" ]
|
||||
install = install.split("::")
|
||||
|
||||
include_dirs = [ ]
|
||||
library_dirs = [ ]
|
||||
|
||||
def add_include(prefix, file):
|
||||
"""
|
||||
Search for prefix/file underneath <i> and <i>/include, for each of the
|
||||
directories <i> in install. When found, puts the directory it was found
|
||||
in into include_dirs.
|
||||
"""
|
||||
|
||||
checked = [ ]
|
||||
for i in install:
|
||||
|
||||
dir = os.path.join(i, prefix)
|
||||
fn = os.path.join(dir, file)
|
||||
fn = os.path.normpath(fn)
|
||||
checked.append(fn)
|
||||
if os.path.exists(fn):
|
||||
break
|
||||
|
||||
dir = os.path.join(i, "include", prefix)
|
||||
fn = os.path.join(dir, file)
|
||||
fn = os.path.normpath(fn)
|
||||
checked.append(fn)
|
||||
|
||||
if os.path.exists(fn):
|
||||
break
|
||||
|
||||
else:
|
||||
|
||||
print "Could not find include %s." % file
|
||||
print "The paths searched were:"
|
||||
for i in checked:
|
||||
print "-", i
|
||||
sys.exit(-1)
|
||||
|
||||
dir = os.path.normpath(dir)
|
||||
|
||||
print "Found %s in %s." % (file, dir)
|
||||
|
||||
if dir not in include_dirs:
|
||||
include_dirs.append(dir)
|
||||
|
||||
def add_library(name):
|
||||
"""
|
||||
This looks for a library named name in the <i> and <i>/lib, for all
|
||||
<i> in install. When found, it adds it to library_dirs.
|
||||
"""
|
||||
|
||||
checked = [ ]
|
||||
|
||||
for i in install:
|
||||
for d in ('', 'lib'):
|
||||
for suffix in (".so", ".dylib", ".a"):
|
||||
|
||||
dir = os.path.join(i, d)
|
||||
fn = os.path.join(dir, name + suffix)
|
||||
checked.append(fn)
|
||||
|
||||
if os.path.exists(fn):
|
||||
print "Found %s." % fn
|
||||
|
||||
if dir not in library_dirs:
|
||||
library_dirs.append(dir)
|
||||
return
|
||||
|
||||
print "Couldn't find library %s." % name
|
||||
print "The paths searched were:"
|
||||
for i in checked:
|
||||
print "-", i
|
||||
sys.exit(-1)
|
||||
|
||||
add_include("", "zlib.h")
|
||||
add_include("", "png.h")
|
||||
add_include("SDL", "SDL.h")
|
||||
add_include("", "ft2build.h")
|
||||
add_include("freetype2", "freetype/freetype.h")
|
||||
add_include("", "libavutil/avstring.h")
|
||||
add_include("", "libavformat/avformat.h")
|
||||
add_include("", "libavcodec/avcodec.h")
|
||||
add_include("", "libswscale/swscale.h")
|
||||
|
||||
add_library("libSDL")
|
||||
add_library("libz")
|
||||
add_library("libpng")
|
||||
add_library("libavformat")
|
||||
add_library("libavcodec")
|
||||
add_library("libavutil")
|
||||
add_library("libfreetype")
|
||||
|
||||
|
||||
|
||||
extra_link_args = [ ]
|
||||
png_libraries = [ 'png', "z" ]
|
||||
|
||||
sdl_libraries = [ 'SDL' ]
|
||||
sound_libraries = [ 'SDL_sound', 'smpeg', 'vorbisfile', 'vorbis', 'ogg', 'modplug', 'speex', 'stdc++', ]
|
||||
sound_libraries = [ "avformat", "avcodec", "avutil", "z" ]
|
||||
png_libraries = [ 'png', "z" ]
|
||||
|
||||
# The following turn on optional modules.
|
||||
nativemidi = None
|
||||
winmixer = None
|
||||
linmixer = None
|
||||
|
||||
# Detect win32.
|
||||
if platform.win32_ver()[0]:
|
||||
nativemidi = [ 'nativemidi.c', 'native_midi_win32.c', 'native_midi_common.c', 'rwobject.c' ]
|
||||
nativemidi_libs = [ 'winmm', 'SDL' ]
|
||||
extra_compile_args.append("-fno-strict-aliasing")
|
||||
winmixer = True
|
||||
|
||||
# Detect mac.
|
||||
if platform.mac_ver()[0]:
|
||||
nativemidi = [ 'nativemidi.c', 'native_midi_mac.c', 'native_midi_common.c', 'rwobject.c' ]
|
||||
nativemidi_libs = [ 'SDL' ]
|
||||
|
||||
# Detect OSS.
|
||||
try:
|
||||
import ossaudiodev
|
||||
@@ -65,7 +150,7 @@ py_modules = [ 'pysdlsound.__init__' ]
|
||||
|
||||
rpe = distutils.core.Extension(
|
||||
"_renpy",
|
||||
[ "IMG_savepng.c", "core.c", "rwobject.c", "_renpy.c" ],
|
||||
[ "IMG_savepng.c", "core.c", "rwobject.c", "_renpy.c", "subpixel.c" ],
|
||||
include_dirs=include_dirs,
|
||||
library_dirs=library_dirs,
|
||||
extra_compile_args=extra_compile_args,
|
||||
@@ -82,14 +167,14 @@ renpy_font = distutils.core.Extension(
|
||||
library_dirs=library_dirs,
|
||||
extra_compile_args=extra_compile_args,
|
||||
extra_link_args=extra_link_args,
|
||||
libraries=sdl_libraries + ['freetype'],
|
||||
libraries=sdl_libraries + [ 'freetype' ],
|
||||
)
|
||||
|
||||
extensions.append(renpy_font)
|
||||
|
||||
psse = distutils.core.Extension(
|
||||
"pysdlsound.sound",
|
||||
[ "pss.c", "rwobject.c", "sound.c" ],
|
||||
[ "pss.c", "rwobject.c", "sound.c", "ffdecode.c" ],
|
||||
include_dirs=include_dirs,
|
||||
library_dirs=library_dirs,
|
||||
extra_compile_args=extra_compile_args,
|
||||
@@ -99,19 +184,6 @@ psse = distutils.core.Extension(
|
||||
|
||||
extensions.append(psse)
|
||||
|
||||
if nativemidi:
|
||||
nme = distutils.core.Extension(
|
||||
"pysdlsound.nativemidi",
|
||||
nativemidi,
|
||||
include_dirs=include_dirs,
|
||||
library_dirs=library_dirs,
|
||||
extra_compile_args=extra_compile_args,
|
||||
libraries=nativemidi_libs,
|
||||
extra_link_args=extra_link_args,
|
||||
)
|
||||
|
||||
extensions.append(nme)
|
||||
|
||||
if winmixer:
|
||||
wme = distutils.core.Extension(
|
||||
"pysdlsound.winmixer",
|
||||
@@ -124,20 +196,9 @@ if winmixer:
|
||||
if linmixer:
|
||||
py_modules.append('pysdlsound.linmixer')
|
||||
|
||||
|
||||
# ffplay = distutils.core.Extension(
|
||||
# "renpy_ffplay",
|
||||
# ["renpy_ffplay.c", "ffplay_module.c"],
|
||||
# include_dirs = [ install + '/include/SDL', install + '/include/ffmpeg', install + '/include' ],
|
||||
# library_dirs = [ install + '/lib' ],
|
||||
# libraries = [ 'avcodec', 'avformat', 'avcodec', 'avutil', 'vorbis', 'vorbisenc', 'ogg', 'SDL', 'z', 'm', ],
|
||||
# )
|
||||
|
||||
# extensions.append(ffplay)
|
||||
|
||||
distutils.core.setup(
|
||||
name = "renpy_module",
|
||||
version = "5.5.0",
|
||||
version = "6.9.1",
|
||||
ext_modules = extensions,
|
||||
py_modules = py_modules,
|
||||
package_dir = { '' : 'lib' },
|
||||
|
||||
@@ -39,16 +39,17 @@ cdef extern from "pss.h":
|
||||
void PSS_set_endevent(int channel, int event)
|
||||
void PSS_set_volume(int channel, float volume)
|
||||
float PSS_get_volume(int channel)
|
||||
void PSS_init(int freq, int stereo, int samples)
|
||||
void PSS_set_pan(int channel, float pan, float delay)
|
||||
void PSS_set_secondary_volume(int channel, float vol2, float delay)
|
||||
void PSS_init(int freq, int stereo, int samples, int status)
|
||||
void PSS_quit()
|
||||
|
||||
void PSS_periodic()
|
||||
void PSS_alloc_event(object)
|
||||
void PSS_refresh_event()
|
||||
|
||||
char *PSS_get_error()
|
||||
|
||||
def _extension(s):
|
||||
i = s.rfind('.')
|
||||
if i == -1:
|
||||
return s
|
||||
return s[i+1:]
|
||||
|
||||
def check_error():
|
||||
e = PSS_get_error();
|
||||
if e:
|
||||
@@ -72,9 +73,7 @@ def play(channel, file, name, paused=False, fadein=0, tight=False):
|
||||
else:
|
||||
tight = 0
|
||||
|
||||
ext = _extension(name)
|
||||
|
||||
PSS_play(channel, rw, ext, name, fadein, tight, pause)
|
||||
PSS_play(channel, rw, name, name, fadein, tight, pause)
|
||||
check_error()
|
||||
|
||||
def queue(channel, file, name, fadein=0, tight=False):
|
||||
@@ -87,9 +86,7 @@ def queue(channel, file, name, fadein=0, tight=False):
|
||||
else:
|
||||
tight = 0
|
||||
|
||||
ext = _extension(name)
|
||||
|
||||
PSS_queue(channel, rw, ext, name, fadein, tight)
|
||||
PSS_queue(channel, rw, name, name, fadein, tight)
|
||||
check_error()
|
||||
|
||||
def stop(channel):
|
||||
@@ -127,7 +124,19 @@ def get_pos(channel):
|
||||
return PSS_get_pos(channel)
|
||||
|
||||
def set_volume(channel, volume):
|
||||
PSS_set_volume(channel, 10 ** volume / 10 )
|
||||
if volume == 0:
|
||||
PSS_set_volume(channel, 0)
|
||||
else:
|
||||
PSS_set_volume(channel, 10 ** volume / 10 )
|
||||
|
||||
check_error()
|
||||
|
||||
def set_pan(channel, pan, delay):
|
||||
PSS_set_pan(channel, pan, delay)
|
||||
check_error()
|
||||
|
||||
def set_secondary_volume(channel, volume, delay):
|
||||
PSS_set_secondary_volume(channel, volume, delay)
|
||||
check_error()
|
||||
|
||||
def set_end_event(channel, event):
|
||||
@@ -137,13 +146,27 @@ def set_end_event(channel, event):
|
||||
def get_volume(channel):
|
||||
return PSS_get_volume(channel)
|
||||
|
||||
def init(freq, stereo, samples):
|
||||
PSS_init(freq, stereo, samples)
|
||||
def init(freq, stereo, samples, status=False):
|
||||
if status:
|
||||
status = 1
|
||||
else:
|
||||
status = 0
|
||||
|
||||
PSS_init(freq, stereo, samples, status)
|
||||
check_error()
|
||||
|
||||
def quit():
|
||||
PSS_quit()
|
||||
|
||||
def periodic():
|
||||
PSS_periodic()
|
||||
|
||||
def alloc_event(surf):
|
||||
PSS_alloc_event(surf)
|
||||
|
||||
def refresh_event():
|
||||
PSS_refresh_event()
|
||||
|
||||
def check_version(version):
|
||||
if version < 2 or version > 3:
|
||||
if version < 2 or version > 4:
|
||||
raise Exception("pysdlsound version mismatch.")
|
||||
|
||||
@@ -0,0 +1,420 @@
|
||||
/* subpixel.c - Ren'Py subpixel blitter code.
|
||||
* Copyright 2009 Tom Rothamel <pytom@bishoujo.us>
|
||||
*
|
||||
* This allows one to blit an image at a fractional pixel position. It
|
||||
* requires MMX to operate. On non-mmx platforms, a traditional blit
|
||||
* is used instead.
|
||||
*/
|
||||
|
||||
#include "renpy.h"
|
||||
#include <pygame/pygame.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
|
||||
#define GCC_MMX 1
|
||||
#include "mmx.h"
|
||||
#endif
|
||||
|
||||
/* MMX Register assignments (between applications of the blitter core).
|
||||
*
|
||||
* mm0 - new row 0
|
||||
* mm1 - new row 1
|
||||
* mm2 - destination
|
||||
* mm3 - alpha shift.
|
||||
* mm4 - old row 0
|
||||
* mm5 - old row 1
|
||||
* mm6 - (256 - xfrac)
|
||||
* mm7 - (256 - yfrac) (or alpha-multiply)
|
||||
*/
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
* This is the basic algorithm that does the subpixel blit
|
||||
* interpolation.
|
||||
*************************************************************************/
|
||||
|
||||
/* mm2 = destination; */
|
||||
|
||||
/* unpack mm0; */
|
||||
/* unpack mm1; */
|
||||
/* unpack mm2; */
|
||||
|
||||
/* // Horizontal */
|
||||
/* mm4 -= mm0; */
|
||||
/* mm5 -= mm1; */
|
||||
/* mm4 *= xfrac; */
|
||||
/* mm5 *= xfrac; */
|
||||
/* mm4 >>= 8; */
|
||||
/* mm5 >>= 8; */
|
||||
/* mm4 += mm0; */
|
||||
/* mm5 += mm1; */
|
||||
|
||||
/* // Vertical */
|
||||
/* mm4 -= mm5; */
|
||||
/* mm4 *= yfrac; */
|
||||
/* mm4 >>= 8; */
|
||||
/* mm4 += mm5; */
|
||||
|
||||
/* // Alpha blend, */
|
||||
/* mm5 = mm4; */
|
||||
/* mm5 >>= mm3; */
|
||||
/* unpack mm5; */
|
||||
/* mm4 -= mm2; */
|
||||
/* unpack mm5; */
|
||||
/* mm4 *= mm5; */
|
||||
/* mm4 >>= 8; */
|
||||
/* mm4 += mm2; */
|
||||
|
||||
/* // Store and repeat */
|
||||
/* pack mm4 */
|
||||
/* destination = mm4; */
|
||||
/* mm4 = mm0; */
|
||||
/* mm5 = mm1; */
|
||||
|
||||
#ifdef GCC_MMX
|
||||
|
||||
|
||||
#define dp(s, r) \
|
||||
movq_r2m(r, scratch); \
|
||||
printf(s, scratch);
|
||||
|
||||
// This expands registers 4 and 5, which are initialized below.
|
||||
#define MMX_EXPAND() \
|
||||
pxor_r2r(mm2, mm2); \
|
||||
punpcklbw_r2r(mm2, mm4); \
|
||||
punpcklbw_r2r(mm2, mm5);
|
||||
|
||||
|
||||
// This expects the two old pixels to be arranged like:
|
||||
// mm4 mm0
|
||||
// mm5 mm1
|
||||
// alpha shift in mm3
|
||||
// (256 - xfrac) in mm6
|
||||
// (256 - yfrac) in mm7
|
||||
// It does the bilinear interpolation, and leaves the result
|
||||
// in mm4.
|
||||
#define MMX_INTERP(dest) \
|
||||
pxor_r2r(mm2, mm2); \
|
||||
punpcklbw_r2r(mm2, mm0); \
|
||||
punpcklbw_r2r(mm2, mm1); \
|
||||
\
|
||||
psubw_r2r(mm0, mm4); \
|
||||
psubw_r2r(mm1, mm5); \
|
||||
pmullw_r2r(mm6, mm4); \
|
||||
pmullw_r2r(mm6, mm5); \
|
||||
psrlw_i2r(8, mm4); \
|
||||
psrlw_i2r(8, mm5); \
|
||||
paddb_r2r(mm0, mm4); \
|
||||
paddb_r2r(mm1, mm5); \
|
||||
/* p0 in mm4, p1 in mm5 */ \
|
||||
\
|
||||
psubw_r2r(mm5, mm4); \
|
||||
pmullw_r2r(mm7, mm4); \
|
||||
psrlw_i2r(8, mm4); \
|
||||
paddb_r2r(mm5, mm4); \
|
||||
/* p in mm4 */ \
|
||||
\
|
||||
pxor_r2r(mm5, mm5); \
|
||||
movd_m2r((dest), mm2); \
|
||||
punpcklbw_r2r(mm5, mm2); \
|
||||
\
|
||||
movq_r2r(mm4, mm5); \
|
||||
psrlq_r2r(mm3, mm5); \
|
||||
punpcklwd_r2r(mm5, mm5); \
|
||||
punpcklwd_r2r(mm5, mm5); \
|
||||
psubw_r2r(mm2, mm4); \
|
||||
pmullw_r2r(mm5, mm4); \
|
||||
psrlw_i2r(8, mm4); \
|
||||
paddb_r2r(mm2, mm4); \
|
||||
packuswb_r2r(mm4, mm4); \
|
||||
movd_r2m(mm4, (dest)); \
|
||||
movq_r2r(mm0, mm4); \
|
||||
movq_r2r(mm1, mm5);
|
||||
|
||||
#define min(x, y) ( ((x) < (y)) ? (x) : (y) )
|
||||
|
||||
/* This blits pysrc into pydst such that the upper-right corner of
|
||||
pysrc is at xo, yo relative to pydst. */
|
||||
int subpixel32(PyObject *pysrc, PyObject *pydst,
|
||||
float xoffset, float yoffset, int ashift) {
|
||||
SDL_Surface *src;
|
||||
SDL_Surface *dst;
|
||||
|
||||
int srcpitch, dstpitch;
|
||||
int srcw, srch;
|
||||
int dstw, dsth;
|
||||
unsigned char *srcpixels;
|
||||
unsigned char *dstpixels;
|
||||
|
||||
int xfrac, yfrac;
|
||||
int xo, yo;
|
||||
int sx, sy;
|
||||
|
||||
int draw_finalx;
|
||||
int normal_pixels;
|
||||
|
||||
int inverted_alpha_mask;
|
||||
|
||||
unsigned int pixel;
|
||||
unsigned int blankpixel;
|
||||
|
||||
unsigned char *s0;
|
||||
unsigned char *s1;
|
||||
unsigned char *d;
|
||||
unsigned char *dend;
|
||||
|
||||
|
||||
long long scratch;
|
||||
|
||||
if (!SDL_HasMMX()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
src = PySurface_AsSurface(pysrc);
|
||||
dst = PySurface_AsSurface(pydst);
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
|
||||
srcpixels = (unsigned char *) src->pixels;
|
||||
dstpixels = (unsigned char *) dst->pixels;
|
||||
srcpitch = src->pitch;
|
||||
dstpitch = dst->pitch;
|
||||
srcw = src->w;
|
||||
dstw = dst->w;
|
||||
srch = src->h;
|
||||
dsth = dst->h;
|
||||
|
||||
inverted_alpha_mask = ~(0xff << ashift);
|
||||
|
||||
// Due to mmx.
|
||||
ashift *= 2;
|
||||
|
||||
xo = (int) floor(xoffset);
|
||||
yo = (int) floor(yoffset);
|
||||
xfrac = (int) ((xoffset - xo) * 255);
|
||||
yfrac = (int) ((yoffset - yo) * 255);
|
||||
|
||||
// Due to the way the interpolator works.
|
||||
// xfrac = 256 - xfrac;
|
||||
// yfrac = 256 - yfrac;
|
||||
|
||||
if (xo < 0) {
|
||||
sx = -xo - 1;
|
||||
xo = 0;
|
||||
} else {
|
||||
sx = -1;
|
||||
}
|
||||
|
||||
if (yo < 0) {
|
||||
sy = -yo - 1;
|
||||
yo = 0;
|
||||
} else {
|
||||
sy = -1;
|
||||
}
|
||||
|
||||
if (sx >= srcw - 1 || sy >= srch - 1) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
// Figure out how many pixels we need to draw on each line.
|
||||
normal_pixels = min(srcw - sx - 1, dstw - xo);
|
||||
|
||||
if (normal_pixels < dstw - xo) {
|
||||
draw_finalx = 1;
|
||||
} else {
|
||||
draw_finalx = 0;
|
||||
}
|
||||
|
||||
// Load up the mmx registers.
|
||||
movd_m2r(xfrac, mm6);
|
||||
punpcklwd_r2r(mm6, mm6);
|
||||
punpckldq_r2r(mm6, mm6);
|
||||
|
||||
movd_m2r(yfrac, mm7);
|
||||
punpcklwd_r2r(mm7, mm7);
|
||||
punpckldq_r2r(mm7, mm7);
|
||||
|
||||
movd_m2r(ashift, mm3);
|
||||
|
||||
if (xo >= dstw) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
// Draw the first line, when sy == -1.
|
||||
|
||||
if (sy == -1) {
|
||||
|
||||
if (yo >= dsth) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
s1 = srcpixels + sx * 4;
|
||||
|
||||
if (sx < 0) {
|
||||
pixel = (* (unsigned int *) (s1 + 4)) & inverted_alpha_mask;
|
||||
} else {
|
||||
pixel = * (unsigned int *) s1;
|
||||
}
|
||||
|
||||
blankpixel = pixel & inverted_alpha_mask;
|
||||
|
||||
movd_m2r(blankpixel, mm4);
|
||||
movd_m2r(pixel, mm5);
|
||||
|
||||
MMX_EXPAND();
|
||||
s1 += 4;
|
||||
|
||||
d = dstpixels + xo * 4 + yo * dstpitch;
|
||||
dend = d + normal_pixels * 4;
|
||||
|
||||
while (d != dend) {
|
||||
pixel = * (unsigned int *) s1;
|
||||
blankpixel = pixel & inverted_alpha_mask;
|
||||
movd_m2r(blankpixel, mm0);
|
||||
movd_m2r(pixel, mm1);
|
||||
MMX_INTERP(* (unsigned int *) d);
|
||||
d += 4;
|
||||
s1 += 4;
|
||||
|
||||
}
|
||||
|
||||
if (draw_finalx) {
|
||||
s1 -= 4;
|
||||
movd_m2r(blankpixel, mm0);
|
||||
movd_m2r(blankpixel, mm1);
|
||||
MMX_INTERP(* (unsigned int *) d);
|
||||
}
|
||||
|
||||
sy += 1;
|
||||
yo += 1;
|
||||
}
|
||||
|
||||
// Draw the second and later lines..
|
||||
|
||||
while (sy < srch - 1) {
|
||||
|
||||
if (yo >= dsth) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
s0 = srcpixels + sx * 4 + (srcpitch * sy);
|
||||
s1 = srcpixels + sx * 4 + (srcpitch * (sy + 1));
|
||||
|
||||
if (sx < 0) {
|
||||
blankpixel = (* (unsigned int *) (s0 + 4)) & inverted_alpha_mask;
|
||||
movd_m2r(blankpixel, mm4);
|
||||
|
||||
blankpixel = (* (unsigned int *) (s1 + 4)) & inverted_alpha_mask;
|
||||
movd_m2r(blankpixel, mm5);
|
||||
|
||||
} else {
|
||||
movd_m2r(* (unsigned int *) s0, mm4);
|
||||
movd_m2r(* (unsigned int *) s1, mm5);
|
||||
}
|
||||
|
||||
MMX_EXPAND();
|
||||
|
||||
s0 += 4;
|
||||
s1 += 4;
|
||||
|
||||
d = dstpixels + xo * 4 + yo * dstpitch;
|
||||
dend = d + normal_pixels * 4;
|
||||
|
||||
unsigned char *dp = d;
|
||||
|
||||
while (d != dend) {
|
||||
movd_m2r(* (unsigned int *) s0, mm0);
|
||||
movd_m2r(* (unsigned int *) s1, mm1);
|
||||
MMX_INTERP(* (unsigned int *) d);
|
||||
|
||||
d += 4;
|
||||
s0 += 4;
|
||||
s1 += 4;
|
||||
}
|
||||
|
||||
if (draw_finalx) {
|
||||
s0 -= 4;
|
||||
s1 -= 4;
|
||||
|
||||
blankpixel = (* (unsigned int *) s0) & inverted_alpha_mask;
|
||||
movd_m2r(blankpixel, mm0);
|
||||
blankpixel = (* (unsigned int *) s1) & inverted_alpha_mask;
|
||||
movd_m2r(blankpixel, mm1);
|
||||
MMX_INTERP(* (unsigned int *) d);
|
||||
}
|
||||
|
||||
yo += 1;
|
||||
sy += 1;
|
||||
|
||||
}
|
||||
|
||||
// The final part, where we handle the bottom line of the source surface.
|
||||
|
||||
if (yo >= dsth) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
s0 = srcpixels + sx * 4 + (srcpitch * sy);
|
||||
|
||||
if (sx < 0) {
|
||||
pixel = * (unsigned int *) (s0 + 4);
|
||||
blankpixel = pixel & inverted_alpha_mask;
|
||||
|
||||
movd_m2r(blankpixel, mm4);
|
||||
movd_m2r(blankpixel, mm5);
|
||||
} else {
|
||||
pixel = * (unsigned int *) s0;
|
||||
blankpixel = pixel & inverted_alpha_mask;
|
||||
|
||||
movd_m2r(pixel, mm4);
|
||||
movd_m2r(blankpixel, mm5);
|
||||
}
|
||||
|
||||
MMX_EXPAND();
|
||||
|
||||
s0 += 4;
|
||||
|
||||
d = dstpixels + xo * 4 + yo * dstpitch;
|
||||
dend = d + normal_pixels * 4;
|
||||
|
||||
while (d != dend) {
|
||||
pixel = * (unsigned int *) s0;
|
||||
blankpixel = pixel & inverted_alpha_mask;
|
||||
|
||||
movd_m2r(pixel, mm0);
|
||||
movd_m2r(blankpixel, mm1);
|
||||
MMX_INTERP(* (unsigned int *) d);
|
||||
d += 4;
|
||||
s0 += 4;
|
||||
}
|
||||
|
||||
if (draw_finalx) {
|
||||
movd_m2r(blankpixel, mm0);
|
||||
movd_m2r(blankpixel, mm1);
|
||||
MMX_INTERP(* (unsigned int *) d);
|
||||
}
|
||||
|
||||
|
||||
done:
|
||||
|
||||
// Reset the MMX unit and call it a night.
|
||||
emms();
|
||||
|
||||
Py_END_ALLOW_THREADS
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
|
||||
/* On a non-mmx platform, return 0 to let the pyrex code handle it. */
|
||||
|
||||
int subpixel32(PyObject *pysrc, PyObject *pydst,
|
||||
float xoffset, float yoffset, int ashift) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
Before Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 162 KiB |
|
Before Width: | Height: | Size: 162 KiB |
|
Before Width: | Height: | Size: 164 KiB |
|
Before Width: | Height: | Size: 505 B |
|
Before Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 737 B |
|
Before Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 106 KiB |
@@ -1,219 +0,0 @@
|
||||
# This is an implementation of Kana mode, a mode in which Ren'Py
|
||||
# emulates the NVL-style games. Multiple lines of dialogue are shown
|
||||
# on the screen at once, whenever a line of dialogue is said by a
|
||||
# KanaCharacter. Calling the clear function clears the screen,
|
||||
# ensuring that the the next line will appear at the top of the
|
||||
# screen.
|
||||
|
||||
|
||||
##############################################################################
|
||||
# This is some sample code, demonstrating how to use Kana mode.
|
||||
|
||||
init:
|
||||
python:
|
||||
|
||||
# First, we want to declare a character to be a
|
||||
# KanaCharacter. This character will always speak in
|
||||
# Kana mode.
|
||||
ke = KanaCharacter("Eileen", color="#cfc")
|
||||
|
||||
# We re-declare the narrator as a Kana character, so narration
|
||||
# will be spoken in Kana mode.
|
||||
narrator = KanaCharacter(" ", who_suffix="")
|
||||
|
||||
|
||||
# style.kana_window:
|
||||
# The window containing the Kana mode.
|
||||
style.kana_window.background = "#0008"
|
||||
style.kana_window.yfill = True
|
||||
style.kana_window.xfill = True
|
||||
style.kana_window.xpadding = 20
|
||||
style.kana_window.ypadding = 20
|
||||
|
||||
# style.kana_vbox:
|
||||
# The vbox containing all of the entries.
|
||||
style.kana_vbox.box_spacing = 10
|
||||
|
||||
# style.kana_entry:
|
||||
# The windows containing each entry, individually.
|
||||
|
||||
# style.kana_label
|
||||
# The label giving who is speaking.
|
||||
style.kana_label.minwidth = 150
|
||||
style.kana_label.text_align = 1.0
|
||||
|
||||
# style.kana_dialogue
|
||||
# Dialogue being spoken by a KanaCharacter.
|
||||
|
||||
|
||||
|
||||
# We can also display menus in Kana mode. When this is done,
|
||||
# the menu will be displayed at the bottom of the current
|
||||
# screen of text.
|
||||
|
||||
# Redefine the menu display function.
|
||||
menu = kana_menu
|
||||
|
||||
# NOTE: These styles will not be used until 5.6.3 comes out.
|
||||
# Use style.menu_choice, style.menu_choice_button, etc. until then.
|
||||
|
||||
# style.kana_menu_window:
|
||||
|
||||
# style.kana_menu_choice:
|
||||
style.kana_menu_choice.idle_color = "#0ff"
|
||||
style.kana_menu_choice.hover_color = "#ff0"
|
||||
|
||||
# style.kana_menu_choice_chosen:
|
||||
|
||||
# style.kana_menu_choice_button:
|
||||
style.kana_menu_choice_button.left_margin = 160
|
||||
style.kana_menu_choice_button.right_margin = 20
|
||||
style.kana_menu_choice_button.xfill = True
|
||||
style.kana_menu_choice_button.hover_background = "#F0F2"
|
||||
|
||||
# style.kana_menu_choice_button_chosen:
|
||||
|
||||
|
||||
label kana_mode:
|
||||
|
||||
show bg whitehouse
|
||||
|
||||
$ kana_clear()
|
||||
|
||||
ke "Kana mode is a mode in which we display more then one line
|
||||
of dialogue on the screen at once."
|
||||
|
||||
"We can also display lines of narration."
|
||||
|
||||
ke "This mode attempts to emulate the NVL style games."
|
||||
|
||||
ke "It's named after the game {i}Kana: Little Sister{/i}, which was
|
||||
the first game that we played using the style."
|
||||
|
||||
$ kana_clear()
|
||||
|
||||
ke "To use this mode, you need to declare KanaCharacters rather then Characters."
|
||||
|
||||
ke "You can also use the kana_clear function to clear the screen."
|
||||
|
||||
menu:
|
||||
|
||||
ke "Kana mode also supports menus. Do you understand?"
|
||||
|
||||
"I understand.":
|
||||
return
|
||||
|
||||
"Tell me again.":
|
||||
jump splashscreen
|
||||
|
||||
|
||||
|
||||
##############################################################################
|
||||
# The implementation of Kana mode lives below this line.
|
||||
|
||||
init -100:
|
||||
|
||||
python:
|
||||
|
||||
# Styles that are used by kana mode.
|
||||
style.create('kana_window', 'default')
|
||||
style.create('kana_vbox', 'vbox')
|
||||
style.create('kana_label', 'say_label')
|
||||
style.create('kana_dialogue', 'say_dialogue')
|
||||
style.create('kana_entry', 'default')
|
||||
|
||||
style.create('kana_menu_window', 'default')
|
||||
style.create('kana_menu_choice', 'default')
|
||||
style.create('kana_menu_choice_chosen', 'kana_menu_choice')
|
||||
style.create('kana_menu_choice_button', 'default')
|
||||
style.create('kana_menu_choice_chosen_button', 'kana_menu_choice_button')
|
||||
|
||||
|
||||
# A list of arguments that have been passed to kana_record_show.
|
||||
kana_list = None
|
||||
|
||||
def kana_show(*args, **kwargs):
|
||||
|
||||
kana_list[-1] = (args, kwargs)
|
||||
|
||||
ui.window(style='kana_window')
|
||||
ui.vbox(style='kana_vbox')
|
||||
|
||||
for i in kana_list:
|
||||
if not i:
|
||||
continue
|
||||
|
||||
a, kw = i
|
||||
rv = renpy.show_display_say(*a, **kw)
|
||||
|
||||
ui.close()
|
||||
|
||||
return rv
|
||||
|
||||
class KanaCharacter(Character):
|
||||
|
||||
def __init__(self, who,
|
||||
show_say_vbox_properties={ 'box_layout' : 'horizontal' },
|
||||
who_style='kana_label',
|
||||
what_style='kana_dialogue',
|
||||
window_style='kana_entry',
|
||||
**kwargs):
|
||||
|
||||
Character.__init__(self, who,
|
||||
show_say_vbox_properties=show_say_vbox_properties,
|
||||
who_style=who_style,
|
||||
what_style=what_style,
|
||||
window_style=window_style,
|
||||
show_function=kana_show,
|
||||
**kwargs)
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
if kana_list is None:
|
||||
store.kana_list = [ ]
|
||||
|
||||
kana_list.append(None)
|
||||
rv = Character.__call__(self, *args, **kwargs)
|
||||
|
||||
# Prevent this from being slow on later displays.
|
||||
if kana_list[-1]:
|
||||
kana_list[-1][1]["what_args"]["slow"] = False
|
||||
|
||||
|
||||
def kana_clear():
|
||||
global kana_list
|
||||
kana_list = [ ]
|
||||
|
||||
def kana_menu(items):
|
||||
|
||||
# Clear out the previous scene list, as we will need to redraw
|
||||
# it.
|
||||
renpy.with(None)
|
||||
|
||||
if kana_list is None:
|
||||
store.kana_list = [ ]
|
||||
|
||||
ui.window(style='kana_window')
|
||||
ui.vbox(style='kana_vbox')
|
||||
|
||||
for i in kana_list:
|
||||
if not i:
|
||||
continue
|
||||
|
||||
a, kw = i
|
||||
rv = renpy.show_display_say(*a, **kw)
|
||||
|
||||
renpy.display_menu(items, interact=False,
|
||||
window_style='kana_menu_window',
|
||||
choice_style='kana_menu_choice',
|
||||
choice_chosen_style='kana_menu_choice_chosen',
|
||||
choice_button_style='kana_menu_choice_button',
|
||||
choice_chosen_button_style='kana_menu_choice_chosen_button',
|
||||
)
|
||||
|
||||
ui.close()
|
||||
|
||||
rv = ui.interact()
|
||||
|
||||
renpy.checkpoint()
|
||||
|
||||
return rv
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 559 B |
|
Before Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
@@ -1,2 +0,0 @@
|
||||
description="A game that demonstrates many of the features of Ren'Py."
|
||||
ro=True
|
||||
@@ -1,13 +0,0 @@
|
||||
all:: reference.html example.html
|
||||
|
||||
reference.html: reference.xml paragraph.py preprocess.py stylesheet.xslt style.css styles.xml
|
||||
python paragraph.py < reference.xml > reference.par.xml
|
||||
python preprocess.py reference.par.xml > reference.hi.xml
|
||||
xsltproc stylesheet.xslt reference.hi.xml > reference.html
|
||||
cp reference.html style.css ~/ab/website/renpy/devel/doc
|
||||
|
||||
|
||||
example.html: example.xml preprocess.py stylesheet.xslt style.css styles.xml ../demo2/script.rpy
|
||||
python preprocess.py example.xml > example.hi.xml
|
||||
xsltproc stylesheet.xslt example.hi.xml > example.html
|
||||
cp example.html style.css ~/ab/website/renpy/devel/doc
|
||||
@@ -1,44 +0,0 @@
|
||||
import xml.dom.minidom
|
||||
import sys
|
||||
|
||||
def escape(s):
|
||||
return s.replace("&", "&").replace("<", "<").replace('"', """)
|
||||
|
||||
def transform_children(node):
|
||||
|
||||
rv = [ ]
|
||||
|
||||
for n in node.childNodes:
|
||||
rv.append(transform(n))
|
||||
|
||||
return ''.join(rv)
|
||||
|
||||
|
||||
def transformElement(node):
|
||||
|
||||
attributes = ' '.join(['%s="%s"' % (k, escape(v)) for k,v in node.attributes.items()])
|
||||
|
||||
tag = node.tagName
|
||||
|
||||
|
||||
# Otherwise, the default.
|
||||
return "<%s %s>%s</%s>" % (node.tagName, attributes,
|
||||
transform_children(node), node.tagName)
|
||||
|
||||
|
||||
|
||||
def transform(node):
|
||||
|
||||
if node.nodeType == node.ELEMENT_NODE:
|
||||
return transformElement(node)
|
||||
else:
|
||||
return escape(str(node))
|
||||
|
||||
def main():
|
||||
|
||||
dom = xml.dom.minidom.parse(sys.argv[1])
|
||||
|
||||
print transform(dom.documentElement)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<doc>
|
||||
<title>Demo Script Example</title>
|
||||
|
||||
<p><a href="tutorial.html">Return to the tutorial.</a></p>
|
||||
|
||||
<example>
|
||||
<!-- include ../demo2/script.rpy -->
|
||||
</example>
|
||||
|
||||
<p><a href="tutorial.html">Return to the tutorial.</a></p>
|
||||
|
||||
</doc>
|
||||
@@ -1,163 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<renpydoc>
|
||||
|
||||
<!-- (define-key xml-mode-map '(control return) 'tompy-xml-ctrlret) -->
|
||||
|
||||
<section title="Say Statement">
|
||||
|
||||
<rule name="statement">
|
||||
<alt>string</alt>
|
||||
<alt>string string</alt>
|
||||
<alt>identifier string</alt>
|
||||
</rule>
|
||||
|
||||
<formal>
|
||||
These three productions are the various forms of the say
|
||||
statement. In order, they are the 1-string form, the 2-string
|
||||
form, and the name-string form. We'll discuss individually
|
||||
what each one does.
|
||||
</formal><formal>
|
||||
The 1-string form calls the <f>say</f> function (found in the
|
||||
execution context) with the string.
|
||||
</formal><formal>
|
||||
The 2-string form also calls the <f>say</f> function, but this
|
||||
time it passes it the two strings. The strings are passed in the
|
||||
order in which they appear.
|
||||
</formal><formal>
|
||||
When a identifierstring say statement is executed, the first thing
|
||||
that happens is that the identifier is looked up in the execution
|
||||
context. It's an error if the name is not defined. If the name
|
||||
is bound to a string, both strings are passed to the <f>say</f>
|
||||
function, as if the 2-string form was used. Otherwise, the
|
||||
<f>say</f> method is called on the object that the identifier was
|
||||
bound to, with the string as the only argument.
|
||||
</formal>
|
||||
|
||||
<user>
|
||||
While the various say statements are very flexible, there are
|
||||
some conventions that we suspect users will use when writing
|
||||
games.
|
||||
</user><user>
|
||||
The 1-string form of the say statment is rendered to the user in
|
||||
a text box without any name associated with it. It's used to
|
||||
indicate the thoughts of the POV character, or to narrate
|
||||
actions that occur in the game.
|
||||
</user><user>
|
||||
The 2-string form is displayed with the first string as a name,
|
||||
and the second string as the line of dialogue said by the
|
||||
character with that name. It's used to indicate spoken dialogue,
|
||||
usually from characters that are minor enough to not have a
|
||||
character object associated with them. The 2-string form is
|
||||
rarely used directly, as normally the name-string or 1-string
|
||||
forms are preferred for dialogue from main characters.
|
||||
</user><user>
|
||||
Generally, when the name-string form is used, the first name
|
||||
refers to a character object. The name field of that character
|
||||
object is looked up, and used to display dialogue as if the
|
||||
2-string form was used. So the name-string form is what is used
|
||||
when we want a main character to say something. This form allows
|
||||
the direct use of the character's name before the dialogue,
|
||||
without requiring the quoting needed by the 2-string form.
|
||||
</user>
|
||||
|
||||
<example>
|
||||
"I was walking down the street one day, when I came across the postman."
|
||||
|
||||
me "Hey, any mail for me today?"
|
||||
|
||||
"Postman" "Yeah, a package came for you. But there's no return address."
|
||||
</example>
|
||||
|
||||
</section>
|
||||
|
||||
<subsection title="Image Display">
|
||||
|
||||
<formal>
|
||||
In Ren'Py, image display is controlled by a pair of image
|
||||
lists. The two image lists are the are called the master image
|
||||
list and the temporary image list. These lists contain python
|
||||
objects that are capable of drawing themselves to the
|
||||
screen. When the time comes to show something to the user, we
|
||||
iterate through the temporary image list, drawing things on
|
||||
the screen in the order in which they appear in the list. So
|
||||
the first thing in the list will be the background, and the
|
||||
thing that's drawn closest to the user will be the last thing
|
||||
in the list.
|
||||
</formal><formal>
|
||||
We can divide the execution of a Ren'Py program into periods
|
||||
where we are showing a screen to the user (for example, during
|
||||
the execution of a say statement) and periods where we are
|
||||
not. After a period of showing the screen to the user, the master image
|
||||
list is copied over the temporary image list.
|
||||
</formal>
|
||||
|
||||
<user>
|
||||
We have two image lists for two reasons. The first is the idea
|
||||
that only the temporary image list will include transitions,
|
||||
and once we're done performing the transition, we want it to
|
||||
be remove from the list. The second reason is an idea that
|
||||
(behind the scenes) the temporary list will also include
|
||||
interface elements (like boxes containing character
|
||||
dialog). We, in general, want these boxes to be shown to the
|
||||
user once and then removed from the screen.
|
||||
</user>
|
||||
|
||||
<rule name="image_name">
|
||||
<alt>identifier+</alt>
|
||||
</rule>
|
||||
|
||||
<formal>
|
||||
An image name consists of one or more identifiers. The first
|
||||
identifier is called the primary identifier of this image
|
||||
name, while the second and later identifier are called
|
||||
secondary identifers. An image name may not include a Ren'Py
|
||||
keyword.
|
||||
</formal><formal>
|
||||
Ren'Py maintains an image dictionary, which maps between image
|
||||
names and objects implementing images that can be show to the
|
||||
user.
|
||||
</formal>
|
||||
|
||||
<user>
|
||||
The idea behind primary identifiers is that we will normally
|
||||
want to display only one image of a character at a time. So we
|
||||
use an image's primary identifier to find other images in the
|
||||
master display list that have the same primary identifier.
|
||||
</user>
|
||||
|
||||
|
||||
<rule name="statement">
|
||||
<alt>"image" image_name "=" python_expression</alt>
|
||||
</rule>
|
||||
|
||||
<formal>
|
||||
This adds a new entry to the image dictionary. Specifically, the
|
||||
python_expression is evaluated to get something that's drawable,
|
||||
and then that object is stored in a tuple in the image
|
||||
dictionary.
|
||||
</formal>
|
||||
|
||||
<user>
|
||||
In general, the python expression will be a call to the image
|
||||
constructor, which loads in a new image from disk (or
|
||||
somewhere... read the documentation for the image constructor.)
|
||||
It's also possible to use another constructor, like the one for
|
||||
animation, but that will probably be more rare.
|
||||
</user><user>
|
||||
It's important to note that this doesn't actually load the image
|
||||
into ram, until close to the time when the image is actually
|
||||
needed.
|
||||
</user><user>
|
||||
In general, it makes sense to ensure that all image statements
|
||||
execure during game startup, before the call to
|
||||
startup_complete().
|
||||
</user>
|
||||
|
||||
<example>
|
||||
image woods = image("backgrounds/woods.jpg")
|
||||
image eileen red upset = image("eileen/red_upset.png")
|
||||
image eileen red happy = image("eileen/red_happy.png")
|
||||
</example>
|
||||
|
||||
</subsection>
|
||||
</renpydoc>
|
||||
@@ -1,116 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import sys
|
||||
import re
|
||||
|
||||
nopar = [
|
||||
"<pre",
|
||||
"<ul",
|
||||
"<li",
|
||||
"<example",
|
||||
"<ol",
|
||||
"<h",
|
||||
"<var",
|
||||
"</var",
|
||||
"<label",
|
||||
"<!--",
|
||||
"<func",
|
||||
"</func",
|
||||
"<label",
|
||||
"</label",
|
||||
]
|
||||
|
||||
noclose = [
|
||||
"</pre",
|
||||
"</ul",
|
||||
"</li",
|
||||
"</example",
|
||||
"</ol",
|
||||
"</h",
|
||||
"</var",
|
||||
"<var",
|
||||
"<!--",
|
||||
"</func",
|
||||
"<func",
|
||||
"</label",
|
||||
"<label",
|
||||
]
|
||||
|
||||
|
||||
|
||||
while True:
|
||||
|
||||
# Read until # START NOPAR or EOF.
|
||||
for l in sys.stdin:
|
||||
if l.startswith("# START NOPAR"):
|
||||
break
|
||||
sys.stdout.write(l)
|
||||
else:
|
||||
break
|
||||
|
||||
# Read until # END NOPAR.
|
||||
data = ""
|
||||
|
||||
for l in sys.stdin:
|
||||
if l.startswith("# END NOPAR"):
|
||||
break
|
||||
|
||||
data += l
|
||||
|
||||
# Tokenize.
|
||||
tokens = list(re.split(r'(?s)(<[^>]+>)', data))
|
||||
moretokens = [ ]
|
||||
|
||||
for i in tokens:
|
||||
moretokens.extend([j for j in re.split(r'(?s)(\s*\n\s*\n\s*)', i) if j])
|
||||
|
||||
nt = [ moretokens[0] ]
|
||||
|
||||
# Insert paragraphs as appropriate.
|
||||
i = 1
|
||||
while i < len(moretokens) - 1:
|
||||
|
||||
prev = moretokens[i - 1]
|
||||
cur = moretokens[i]
|
||||
next = moretokens[i + 1]
|
||||
|
||||
if cur == "<example>":
|
||||
|
||||
nt.append(cur)
|
||||
while True:
|
||||
i += 1
|
||||
cur = moretokens[i]
|
||||
nt.append(cur)
|
||||
if cur == "</example>":
|
||||
break
|
||||
|
||||
i += 1
|
||||
continue
|
||||
|
||||
if not re.search(r'\n\s*\n', cur):
|
||||
nt.append(cur)
|
||||
i += 1
|
||||
continue
|
||||
|
||||
nt.append("\n")
|
||||
|
||||
for j in noclose:
|
||||
if prev.startswith(j):
|
||||
break
|
||||
else:
|
||||
nt.append("</p>")
|
||||
|
||||
for j in nopar:
|
||||
if next.startswith(j):
|
||||
break
|
||||
else:
|
||||
nt.append("<p>")
|
||||
|
||||
nt.append("\n")
|
||||
|
||||
i += 1
|
||||
|
||||
nt.append(moretokens[-1])
|
||||
nt.append("\n</p>\n")
|
||||
|
||||
print ''.join(nt)
|
||||