aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AUTHORS4
-rw-r--r--COPYING699
-rw-r--r--INSTALL59
-rw-r--r--README15
-rw-r--r--THANKS3
-rw-r--r--data/test.ttl32
-rw-r--r--machina.desktop.in9
-rw-r--r--src/client/ClientModel.cpp94
-rw-r--r--src/client/ClientModel.hpp67
-rw-r--r--src/client/ClientObject.cpp56
-rw-r--r--src/client/ClientObject.hpp71
-rw-r--r--src/client/wscript17
-rw-r--r--src/engine/Action.hpp60
-rw-r--r--src/engine/ActionFactory.cpp55
-rw-r--r--src/engine/ActionFactory.hpp34
-rw-r--r--src/engine/Controller.cpp228
-rw-r--r--src/engine/Edge.cpp64
-rw-r--r--src/engine/Edge.hpp60
-rw-r--r--src/engine/Engine.cpp126
-rw-r--r--src/engine/Evolver.cpp139
-rw-r--r--src/engine/JackDriver.cpp479
-rw-r--r--src/engine/JackDriver.hpp119
-rw-r--r--src/engine/LearnRequest.cpp56
-rw-r--r--src/engine/LearnRequest.hpp61
-rw-r--r--src/engine/Loader.cpp193
-rw-r--r--src/engine/MIDISink.hpp37
-rw-r--r--src/engine/Machine.cpp389
-rw-r--r--src/engine/MachineBuilder.cpp325
-rw-r--r--src/engine/MachineBuilder.hpp87
-rw-r--r--src/engine/MidiAction.cpp103
-rw-r--r--src/engine/MidiAction.hpp57
-rw-r--r--src/engine/Mutation.cpp190
-rw-r--r--src/engine/Node.cpp271
-rw-r--r--src/engine/Node.hpp116
-rw-r--r--src/engine/Problem.cpp383
-rw-r--r--src/engine/Problem.hpp135
-rw-r--r--src/engine/Recorder.cpp69
-rw-r--r--src/engine/Recorder.hpp68
-rw-r--r--src/engine/SMFDriver.cpp162
-rw-r--r--src/engine/SMFDriver.hpp67
-rw-r--r--src/engine/SMFReader.cpp323
-rw-r--r--src/engine/SMFReader.hpp87
-rw-r--r--src/engine/SMFWriter.cpp241
-rw-r--r--src/engine/SMFWriter.hpp73
-rw-r--r--src/engine/Schrodinbit.hpp42
-rw-r--r--src/engine/Slave.hpp73
-rw-r--r--src/engine/Stateful.cpp39
-rw-r--r--src/engine/Stateful.hpp70
-rw-r--r--src/engine/URIs.cpp23
-rw-r--r--src/engine/Updates.cpp66
-rw-r--r--src/engine/machina/Atom.hpp217
-rw-r--r--src/engine/machina/Context.hpp51
-rw-r--r--src/engine/machina/Controller.hpp80
-rw-r--r--src/engine/machina/Driver.hpp86
-rw-r--r--src/engine/machina/Engine.hpp68
-rw-r--r--src/engine/machina/Evolver.hpp72
-rw-r--r--src/engine/machina/Loader.hpp49
-rw-r--r--src/engine/machina/Machine.hpp132
-rw-r--r--src/engine/machina/Model.hpp44
-rw-r--r--src/engine/machina/Mutation.hpp60
-rw-r--r--src/engine/machina/URIs.hpp93
-rw-r--r--src/engine/machina/Updates.hpp46
-rw-r--r--src/engine/machina/types.hpp69
-rw-r--r--src/engine/quantize.hpp46
-rw-r--r--src/engine/quantize_test.cpp41
-rw-r--r--src/engine/smf_test.cpp84
-rw-r--r--src/engine/wscript45
-rw-r--r--src/gui/EdgeView.cpp142
-rw-r--r--src/gui/EdgeView.hpp59
-rw-r--r--src/gui/MachinaCanvas.cpp211
-rw-r--r--src/gui/MachinaCanvas.hpp66
-rw-r--r--src/gui/MachinaGUI.cpp750
-rw-r--r--src/gui/MachinaGUI.hpp190
-rw-r--r--src/gui/NodePropertiesWindow.cpp136
-rw-r--r--src/gui/NodePropertiesWindow.hpp67
-rw-r--r--src/gui/NodeView.cpp203
-rw-r--r--src/gui/NodeView.hpp76
-rw-r--r--src/gui/WidgetFactory.hpp65
-rw-r--r--src/gui/machina.gladep9
-rw-r--r--src/gui/machina.svg66
-rw-r--r--src/gui/machina.ui1128
-rw-r--r--src/gui/main.cpp100
-rw-r--r--src/gui/wscript43
-rw-r--r--src/machina.cpp86
-rw-r--r--src/midi2machina.cpp81
-rwxr-xr-xutil/machina2dot184
-rwxr-xr-xwaf171
-rw-r--r--waflib/.gitignore (renamed from .gitignore)0
-rw-r--r--waflib/Build.py (renamed from Build.py)0
-rw-r--r--waflib/COPYING25
-rw-r--r--waflib/ConfigSet.py (renamed from ConfigSet.py)0
-rw-r--r--waflib/Configure.py (renamed from Configure.py)0
-rw-r--r--waflib/Context.py (renamed from Context.py)0
-rw-r--r--waflib/Errors.py (renamed from Errors.py)0
-rw-r--r--waflib/Logs.py (renamed from Logs.py)0
-rw-r--r--waflib/Node.py (renamed from Node.py)0
-rw-r--r--waflib/Options.py (renamed from Options.py)0
-rw-r--r--waflib/README.md (renamed from README.md)0
-rw-r--r--waflib/Runner.py (renamed from Runner.py)0
-rw-r--r--waflib/Scripting.py (renamed from Scripting.py)0
-rw-r--r--waflib/Task.py (renamed from Task.py)0
-rw-r--r--waflib/TaskGen.py (renamed from TaskGen.py)0
-rw-r--r--waflib/Tools/__init__.py (renamed from Tools/__init__.py)0
-rw-r--r--waflib/Tools/ar.py (renamed from Tools/ar.py)0
-rw-r--r--waflib/Tools/asm.py (renamed from Tools/asm.py)0
-rw-r--r--waflib/Tools/bison.py (renamed from Tools/bison.py)0
-rw-r--r--waflib/Tools/c.py (renamed from Tools/c.py)0
-rw-r--r--waflib/Tools/c_aliases.py (renamed from Tools/c_aliases.py)0
-rw-r--r--waflib/Tools/c_config.py (renamed from Tools/c_config.py)0
-rw-r--r--waflib/Tools/c_osx.py (renamed from Tools/c_osx.py)0
-rw-r--r--waflib/Tools/c_preproc.py (renamed from Tools/c_preproc.py)0
-rw-r--r--waflib/Tools/c_tests.py (renamed from Tools/c_tests.py)0
-rw-r--r--waflib/Tools/ccroot.py (renamed from Tools/ccroot.py)0
-rw-r--r--waflib/Tools/clang.py (renamed from Tools/clang.py)0
-rw-r--r--waflib/Tools/clangxx.py (renamed from Tools/clangxx.py)0
-rw-r--r--waflib/Tools/compiler_c.py (renamed from Tools/compiler_c.py)0
-rw-r--r--waflib/Tools/compiler_cxx.py (renamed from Tools/compiler_cxx.py)0
-rw-r--r--waflib/Tools/compiler_d.py (renamed from Tools/compiler_d.py)0
-rw-r--r--waflib/Tools/compiler_fc.py (renamed from Tools/compiler_fc.py)0
-rw-r--r--waflib/Tools/cs.py (renamed from Tools/cs.py)0
-rw-r--r--waflib/Tools/cxx.py (renamed from Tools/cxx.py)0
-rw-r--r--waflib/Tools/d.py (renamed from Tools/d.py)0
-rw-r--r--waflib/Tools/d_config.py (renamed from Tools/d_config.py)0
-rw-r--r--waflib/Tools/d_scan.py (renamed from Tools/d_scan.py)0
-rw-r--r--waflib/Tools/dbus.py (renamed from Tools/dbus.py)0
-rw-r--r--waflib/Tools/dmd.py (renamed from Tools/dmd.py)0
-rw-r--r--waflib/Tools/errcheck.py (renamed from Tools/errcheck.py)0
-rw-r--r--waflib/Tools/fc.py (renamed from Tools/fc.py)0
-rw-r--r--waflib/Tools/fc_config.py (renamed from Tools/fc_config.py)0
-rw-r--r--waflib/Tools/fc_scan.py (renamed from Tools/fc_scan.py)0
-rw-r--r--waflib/Tools/flex.py (renamed from Tools/flex.py)0
-rw-r--r--waflib/Tools/g95.py (renamed from Tools/g95.py)0
-rw-r--r--waflib/Tools/gas.py (renamed from Tools/gas.py)0
-rw-r--r--waflib/Tools/gcc.py (renamed from Tools/gcc.py)0
-rw-r--r--waflib/Tools/gdc.py (renamed from Tools/gdc.py)0
-rw-r--r--waflib/Tools/gfortran.py (renamed from Tools/gfortran.py)0
-rw-r--r--waflib/Tools/glib2.py (renamed from Tools/glib2.py)0
-rw-r--r--waflib/Tools/gnu_dirs.py (renamed from Tools/gnu_dirs.py)0
-rw-r--r--waflib/Tools/gxx.py (renamed from Tools/gxx.py)0
-rw-r--r--waflib/Tools/icc.py (renamed from Tools/icc.py)0
-rw-r--r--waflib/Tools/icpc.py (renamed from Tools/icpc.py)0
-rw-r--r--waflib/Tools/ifort.py (renamed from Tools/ifort.py)0
-rw-r--r--waflib/Tools/intltool.py (renamed from Tools/intltool.py)0
-rw-r--r--waflib/Tools/irixcc.py (renamed from Tools/irixcc.py)0
-rw-r--r--waflib/Tools/javaw.py (renamed from Tools/javaw.py)0
-rw-r--r--waflib/Tools/ldc2.py (renamed from Tools/ldc2.py)0
-rw-r--r--waflib/Tools/lua.py (renamed from Tools/lua.py)0
-rw-r--r--waflib/Tools/md5_tstamp.py (renamed from Tools/md5_tstamp.py)0
-rw-r--r--waflib/Tools/msvc.py (renamed from Tools/msvc.py)0
-rw-r--r--waflib/Tools/nasm.py (renamed from Tools/nasm.py)0
-rw-r--r--waflib/Tools/nobuild.py (renamed from Tools/nobuild.py)0
-rw-r--r--waflib/Tools/perl.py (renamed from Tools/perl.py)0
-rw-r--r--waflib/Tools/python.py (renamed from Tools/python.py)0
-rw-r--r--waflib/Tools/qt5.py (renamed from Tools/qt5.py)0
-rw-r--r--waflib/Tools/ruby.py (renamed from Tools/ruby.py)0
-rw-r--r--waflib/Tools/suncc.py (renamed from Tools/suncc.py)0
-rw-r--r--waflib/Tools/suncxx.py (renamed from Tools/suncxx.py)0
-rw-r--r--waflib/Tools/tex.py (renamed from Tools/tex.py)0
-rw-r--r--waflib/Tools/vala.py (renamed from Tools/vala.py)0
-rw-r--r--waflib/Tools/waf_unit_test.py (renamed from Tools/waf_unit_test.py)0
-rw-r--r--waflib/Tools/winres.py (renamed from Tools/winres.py)0
-rw-r--r--waflib/Tools/xlc.py (renamed from Tools/xlc.py)0
-rw-r--r--waflib/Tools/xlcxx.py (renamed from Tools/xlcxx.py)0
-rw-r--r--waflib/Utils.py (renamed from Utils.py)0
-rw-r--r--waflib/__init__.py (renamed from __init__.py)0
-rw-r--r--waflib/ansiterm.py (renamed from ansiterm.py)0
-rw-r--r--waflib/extras/__init__.py (renamed from extras/__init__.py)0
-rw-r--r--waflib/extras/autowaf.py (renamed from extras/autowaf.py)0
-rw-r--r--waflib/extras/batched_cc.py (renamed from extras/batched_cc.py)0
-rw-r--r--waflib/extras/biber.py (renamed from extras/biber.py)0
-rw-r--r--waflib/extras/bjam.py (renamed from extras/bjam.py)0
-rw-r--r--waflib/extras/blender.py (renamed from extras/blender.py)0
-rw-r--r--waflib/extras/boo.py (renamed from extras/boo.py)0
-rw-r--r--waflib/extras/boost.py (renamed from extras/boost.py)0
-rw-r--r--waflib/extras/build_file_tracker.py (renamed from extras/build_file_tracker.py)0
-rw-r--r--waflib/extras/build_logs.py (renamed from extras/build_logs.py)0
-rw-r--r--waflib/extras/buildcopy.py (renamed from extras/buildcopy.py)0
-rw-r--r--waflib/extras/c_bgxlc.py (renamed from extras/c_bgxlc.py)0
-rw-r--r--waflib/extras/c_dumbpreproc.py (renamed from extras/c_dumbpreproc.py)0
-rw-r--r--waflib/extras/c_emscripten.py (renamed from extras/c_emscripten.py)0
-rw-r--r--waflib/extras/c_nec.py (renamed from extras/c_nec.py)0
-rw-r--r--waflib/extras/cabal.py (renamed from extras/cabal.py)0
-rw-r--r--waflib/extras/cfg_altoptions.py (renamed from extras/cfg_altoptions.py)0
-rw-r--r--waflib/extras/clang_compilation_database.py (renamed from extras/clang_compilation_database.py)0
-rw-r--r--waflib/extras/codelite.py (renamed from extras/codelite.py)0
-rw-r--r--waflib/extras/color_gcc.py (renamed from extras/color_gcc.py)0
-rw-r--r--waflib/extras/color_rvct.py (renamed from extras/color_rvct.py)0
-rw-r--r--waflib/extras/compat15.py (renamed from extras/compat15.py)0
-rw-r--r--waflib/extras/cppcheck.py (renamed from extras/cppcheck.py)0
-rw-r--r--waflib/extras/cpplint.py (renamed from extras/cpplint.py)0
-rw-r--r--waflib/extras/cross_gnu.py (renamed from extras/cross_gnu.py)0
-rw-r--r--waflib/extras/cython.py (renamed from extras/cython.py)0
-rw-r--r--waflib/extras/dcc.py (renamed from extras/dcc.py)0
-rw-r--r--waflib/extras/distnet.py (renamed from extras/distnet.py)0
-rw-r--r--waflib/extras/doxygen.py (renamed from extras/doxygen.py)0
-rw-r--r--waflib/extras/dpapi.py (renamed from extras/dpapi.py)0
-rw-r--r--waflib/extras/eclipse.py (renamed from extras/eclipse.py)0
-rw-r--r--waflib/extras/erlang.py (renamed from extras/erlang.py)0
-rw-r--r--waflib/extras/fast_partial.py (renamed from extras/fast_partial.py)0
-rw-r--r--waflib/extras/fc_bgxlf.py (renamed from extras/fc_bgxlf.py)0
-rw-r--r--waflib/extras/fc_cray.py (renamed from extras/fc_cray.py)0
-rw-r--r--waflib/extras/fc_nag.py (renamed from extras/fc_nag.py)0
-rw-r--r--waflib/extras/fc_nec.py (renamed from extras/fc_nec.py)0
-rw-r--r--waflib/extras/fc_open64.py (renamed from extras/fc_open64.py)0
-rw-r--r--waflib/extras/fc_pgfortran.py (renamed from extras/fc_pgfortran.py)0
-rw-r--r--waflib/extras/fc_solstudio.py (renamed from extras/fc_solstudio.py)0
-rw-r--r--waflib/extras/fc_xlf.py (renamed from extras/fc_xlf.py)0
-rw-r--r--waflib/extras/file_to_object.py (renamed from extras/file_to_object.py)0
-rw-r--r--waflib/extras/fluid.py (renamed from extras/fluid.py)0
-rw-r--r--waflib/extras/freeimage.py (renamed from extras/freeimage.py)0
-rw-r--r--waflib/extras/fsb.py (renamed from extras/fsb.py)0
-rw-r--r--waflib/extras/fsc.py (renamed from extras/fsc.py)0
-rw-r--r--waflib/extras/gccdeps.py (renamed from extras/gccdeps.py)0
-rw-r--r--waflib/extras/gdbus.py (renamed from extras/gdbus.py)0
-rw-r--r--waflib/extras/gob2.py (renamed from extras/gob2.py)0
-rw-r--r--waflib/extras/halide.py (renamed from extras/halide.py)0
-rwxr-xr-xwaflib/extras/javatest.py (renamed from extras/javatest.py)0
-rw-r--r--waflib/extras/kde4.py (renamed from extras/kde4.py)0
-rw-r--r--waflib/extras/local_rpath.py (renamed from extras/local_rpath.py)0
-rw-r--r--waflib/extras/lv2.py (renamed from extras/lv2.py)0
-rw-r--r--waflib/extras/make.py (renamed from extras/make.py)0
-rw-r--r--waflib/extras/midl.py (renamed from extras/midl.py)0
-rw-r--r--waflib/extras/msvcdeps.py (renamed from extras/msvcdeps.py)0
-rw-r--r--waflib/extras/msvs.py (renamed from extras/msvs.py)0
-rw-r--r--waflib/extras/netcache_client.py (renamed from extras/netcache_client.py)0
-rw-r--r--waflib/extras/objcopy.py (renamed from extras/objcopy.py)0
-rw-r--r--waflib/extras/ocaml.py (renamed from extras/ocaml.py)0
-rw-r--r--waflib/extras/package.py (renamed from extras/package.py)0
-rw-r--r--waflib/extras/parallel_debug.py (renamed from extras/parallel_debug.py)0
-rw-r--r--waflib/extras/pch.py (renamed from extras/pch.py)0
-rw-r--r--waflib/extras/pep8.py (renamed from extras/pep8.py)0
-rw-r--r--waflib/extras/pgicc.py (renamed from extras/pgicc.py)0
-rw-r--r--waflib/extras/pgicxx.py (renamed from extras/pgicxx.py)0
-rw-r--r--waflib/extras/proc.py (renamed from extras/proc.py)0
-rw-r--r--waflib/extras/protoc.py (renamed from extras/protoc.py)0
-rw-r--r--waflib/extras/pyqt5.py (renamed from extras/pyqt5.py)0
-rw-r--r--waflib/extras/pytest.py (renamed from extras/pytest.py)0
-rw-r--r--waflib/extras/qnxnto.py (renamed from extras/qnxnto.py)0
-rw-r--r--waflib/extras/qt4.py (renamed from extras/qt4.py)0
-rw-r--r--waflib/extras/relocation.py (renamed from extras/relocation.py)0
-rw-r--r--waflib/extras/remote.py (renamed from extras/remote.py)0
-rw-r--r--waflib/extras/resx.py (renamed from extras/resx.py)0
-rw-r--r--waflib/extras/review.py (renamed from extras/review.py)0
-rw-r--r--waflib/extras/rst.py (renamed from extras/rst.py)0
-rw-r--r--waflib/extras/run_do_script.py (renamed from extras/run_do_script.py)0
-rw-r--r--waflib/extras/run_m_script.py (renamed from extras/run_m_script.py)0
-rw-r--r--waflib/extras/run_py_script.py (renamed from extras/run_py_script.py)0
-rw-r--r--waflib/extras/run_r_script.py (renamed from extras/run_r_script.py)0
-rw-r--r--waflib/extras/sas.py (renamed from extras/sas.py)0
-rw-r--r--waflib/extras/satellite_assembly.py (renamed from extras/satellite_assembly.py)0
-rw-r--r--waflib/extras/scala.py (renamed from extras/scala.py)0
-rw-r--r--waflib/extras/slow_qt4.py (renamed from extras/slow_qt4.py)0
-rw-r--r--waflib/extras/softlink_libs.py (renamed from extras/softlink_libs.py)0
-rw-r--r--waflib/extras/stale.py (renamed from extras/stale.py)0
-rw-r--r--waflib/extras/stracedeps.py (renamed from extras/stracedeps.py)0
-rw-r--r--waflib/extras/swig.py (renamed from extras/swig.py)0
-rw-r--r--waflib/extras/syms.py (renamed from extras/syms.py)0
-rw-r--r--waflib/extras/ticgt.py (renamed from extras/ticgt.py)0
-rw-r--r--waflib/extras/unity.py (renamed from extras/unity.py)0
-rw-r--r--waflib/extras/use_config.py (renamed from extras/use_config.py)0
-rw-r--r--waflib/extras/valadoc.py (renamed from extras/valadoc.py)0
-rw-r--r--waflib/extras/waf_xattr.py (renamed from extras/waf_xattr.py)0
-rw-r--r--waflib/extras/why.py (renamed from extras/why.py)0
-rw-r--r--waflib/extras/win32_opts.py (renamed from extras/win32_opts.py)0
-rw-r--r--waflib/extras/wix.py (renamed from extras/wix.py)0
-rw-r--r--waflib/extras/xcode6.py (renamed from extras/xcode6.py)0
-rw-r--r--waflib/fixpy2.py (renamed from fixpy2.py)0
-rwxr-xr-xwaflib/processor.py (renamed from processor.py)0
-rwxr-xr-xwaflib/waf16
-rw-r--r--wscript78
270 files changed, 11497 insertions, 34 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..f1bb6b3
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,4 @@
+Author:
+
+David Robillard <d@drobilla.net>
+
diff --git a/COPYING b/COPYING
index a4147d2..94a9ed0 100644
--- a/COPYING
+++ b/COPYING
@@ -1,25 +1,674 @@
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
-2. 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.
-
-3. The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR "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 AUTHOR 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.
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..623cddd
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,59 @@
+Installation Instructions
+=========================
+
+Basic Installation
+------------------
+
+Building this software requires only Python. To install with default options:
+
+ ./waf configure
+ ./waf
+ ./waf install
+
+You may need to become root for the install stage, for example:
+
+ sudo ./waf install
+
+Configuration Options
+---------------------
+
+All supported options can be viewed using the command:
+
+ ./waf --help
+
+Most options only need to be passed during the configure stage, for example:
+
+ ./waf configure --prefix=/usr
+ ./waf
+ ./waf install
+
+Compiler Configuration
+----------------------
+
+Several standard environment variables can be used to control how compilers are
+invoked:
+
+ * CC: Path to C compiler
+ * CFLAGS: C compiler options
+ * CXX: Path to C++ compiler
+ * CXXFLAGS: C++ compiler options
+ * CPPFLAGS: C preprocessor options
+ * LINKFLAGS: Linker options
+
+Installation Directories
+------------------------
+
+The --prefix option (or the PREFIX environment variable) can be used to change
+the prefix which all files are installed under. There are also several options
+allowing for more fine-tuned control, see the --help output for details.
+
+Packaging
+---------
+
+Everything can be installed to a specific root directory by passing a --destdir
+option to the install stage (or setting the DESTDIR environment variable),
+which adds a prefix to all install paths. For example:
+
+ ./waf configure --prefix=/usr
+ ./waf
+ ./waf install --destdir=/tmp/package
diff --git a/README b/README
new file mode 100644
index 0000000..6f2073f
--- /dev/null
+++ b/README
@@ -0,0 +1,15 @@
+Machina
+=======
+
+Machina is a MIDI sequencer based on Finite State Automata.
+
+A machine can be constructed manually from the user interface, recorded from
+MIDI input (free-form or step), or loaded from a MIDI file. The probability of
+arcs can be manipulated to build a machine that produces structured but
+constantly changing output. This way, Machina can be used as a generative
+recording tool that plays back patterns similar to, but not identical to, the
+original input.
+
+For more information, see <http://drobilla.net/software/machina>.
+
+ -- David Robillard <d@drobilla.net>
diff --git a/THANKS b/THANKS
new file mode 100644
index 0000000..f880df5
--- /dev/null
+++ b/THANKS
@@ -0,0 +1,3 @@
+Professor Albert Chan
+Professor Frank Dehne
+Professor Franz Oppacher
diff --git a/data/test.ttl b/data/test.ttl
new file mode 100644
index 0000000..35e30e6
--- /dev/null
+++ b/data/test.ttl
@@ -0,0 +1,32 @@
+@prefix : <http://drobilla.net/ns/machina#> .
+
+<>
+ a :Machine;
+
+ :initialNode <#n1> ;
+ :node <#n2> ;
+ :node <#n3> ;
+
+ :edge [
+ :tail <#n1> ;
+ :head <#n2> ;
+ ] , [
+ :tail <#n1> ;
+ :head <#n3> ;
+ ] .
+
+
+<#n1>
+ a :Node ;
+ :midiNote 60 ;
+ :duration 20000 .
+
+<#n2>
+ a :Node ;
+ :midiNote 72 ;
+ :duration 10000 .
+
+<#n3>
+ a :Node ;
+ :midiNote 79 ;
+ :duration 10000 .
diff --git a/machina.desktop.in b/machina.desktop.in
new file mode 100644
index 0000000..b1598e7
--- /dev/null
+++ b/machina.desktop.in
@@ -0,0 +1,9 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=Machina
+Comment=MIDI sequencer based on probabilistic finite-state automata.
+Exec=machina_gui
+Terminal=false
+Icon=machina.svg
+Type=Application
+Categories=Application;AudioVideo;Sound;Audio
diff --git a/src/client/ClientModel.cpp b/src/client/ClientModel.cpp
new file mode 100644
index 0000000..3a8770f
--- /dev/null
+++ b/src/client/ClientModel.cpp
@@ -0,0 +1,94 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2014 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "ClientModel.hpp"
+
+namespace machina {
+namespace client {
+
+SPtr<ClientObject>
+ClientModel::find(uint64_t id)
+{
+ SPtr<ClientObject> key(new ClientObjectKey(id));
+ Objects::iterator i = _objects.find(key);
+ if (i != _objects.end()) {
+ return *i;
+ } else {
+ return SPtr<ClientObject>();
+ }
+}
+
+SPtr<const ClientObject>
+ClientModel::find(uint64_t id) const
+{
+ SPtr<ClientObject> key(new ClientObjectKey(id));
+ Objects::const_iterator i = _objects.find(key);
+ if (i != _objects.end()) {
+ return *i;
+ } else {
+ return SPtr<ClientObject>();
+ }
+}
+
+void
+ClientModel::new_object(uint64_t id, const Properties& properties)
+{
+ SPtr<ClientObject> key(new ClientObjectKey(id));
+ Objects::iterator i = _objects.find(key);
+ if (i == _objects.end()) {
+ SPtr<ClientObject> object(new ClientObject(id, properties));
+ _objects.insert(object);
+ signal_new_object.emit(object);
+ } else {
+ for (const auto& p : properties) {
+ (*i)->set(p.first, p.second);
+ }
+ }
+}
+
+void
+ClientModel::erase_object(uint64_t id)
+{
+ SPtr<ClientObject> key(new ClientObjectKey(id));
+ Objects::iterator i = _objects.find(key);
+ if (i == _objects.end()) {
+ return;
+ }
+
+ signal_erase_object.emit(*i);
+ (*i)->set_view(NULL);
+ _objects.erase(i);
+}
+
+void
+ClientModel::set(uint64_t id, URIInt key, const Atom& value)
+{
+ SPtr<ClientObject> object = find(id);
+ if (object) {
+ object->set(key, value);
+ }
+}
+
+const Atom&
+ClientModel::get(uint64_t id, URIInt key) const
+{
+ static const Atom null_atom;
+ SPtr<const ClientObject> object = find(id);
+ return object ? object->get(key) : null_atom;
+}
+
+}
+}
diff --git a/src/client/ClientModel.hpp b/src/client/ClientModel.hpp
new file mode 100644
index 0000000..e65fb93
--- /dev/null
+++ b/src/client/ClientModel.hpp
@@ -0,0 +1,67 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2014 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_CLIENTMODEL_HPP
+#define MACHINA_CLIENTMODEL_HPP
+
+#include <set>
+
+#include <sigc++/sigc++.h>
+
+#include "machina/Model.hpp"
+
+#include "ClientObject.hpp"
+
+namespace Raul {
+class Atom;
+}
+
+namespace machina {
+namespace client {
+
+class ClientModel : public Model
+{
+public:
+ void new_object(uint64_t id, const Properties& properties);
+
+ void erase_object(uint64_t id);
+
+ SPtr<ClientObject> find(uint64_t id);
+ SPtr<const ClientObject> find(uint64_t id) const;
+
+ void set(uint64_t id, URIInt key, const Atom& value);
+ const Atom& get(uint64_t id, URIInt key) const;
+
+ sigc::signal< void, SPtr<ClientObject> > signal_new_object;
+ sigc::signal< void, SPtr<ClientObject> > signal_erase_object;
+
+private:
+ struct ClientObjectComparator {
+ inline bool operator()(SPtr<ClientObject> lhs,
+ SPtr<ClientObject> rhs) const {
+ return lhs->id() < rhs->id();
+ }
+
+ };
+
+ typedef std::set<SPtr<ClientObject>, ClientObjectComparator> Objects;
+ Objects _objects;
+};
+
+}
+}
+
+#endif // MACHINA_CLIENTMODEL_HPP
diff --git a/src/client/ClientObject.cpp b/src/client/ClientObject.cpp
new file mode 100644
index 0000000..20b50a2
--- /dev/null
+++ b/src/client/ClientObject.cpp
@@ -0,0 +1,56 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2014 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <iostream>
+
+#include "ClientObject.hpp"
+
+namespace machina {
+namespace client {
+
+ClientObject::ClientObject(uint64_t id, const Properties& properties)
+ : _id(id)
+ , _view(NULL)
+ , _properties(properties)
+{}
+
+ClientObject::ClientObject(const ClientObject& copy, uint64_t id)
+ : _id(id)
+ , _view(NULL)
+ , _properties(copy._properties)
+{}
+
+void
+ClientObject::set(URIInt key, const Atom& value)
+{
+ _properties[key] = value;
+ signal_property.emit(key, value);
+}
+
+const Atom&
+ClientObject::get(URIInt key) const
+{
+ static const Atom null_atom;
+ Properties::const_iterator i = _properties.find(key);
+ if (i != _properties.end()) {
+ return i->second;
+ } else {
+ return null_atom;
+ }
+}
+
+}
+}
diff --git a/src/client/ClientObject.hpp b/src/client/ClientObject.hpp
new file mode 100644
index 0000000..6520b11
--- /dev/null
+++ b/src/client/ClientObject.hpp
@@ -0,0 +1,71 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2014 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_CLIENTOBJECT_HPP
+#define MACHINA_CLIENTOBJECT_HPP
+
+#include <map>
+
+#include <sigc++/sigc++.h>
+
+#include "machina/Atom.hpp"
+#include "machina/types.hpp"
+
+namespace machina {
+namespace client {
+
+class ClientObject
+{
+public:
+ explicit ClientObject(uint64_t id, const Properties& properties={});
+ ClientObject(const ClientObject& copy, uint64_t id);
+
+ inline uint64_t id() const { return _id; }
+
+ void set(URIInt key, const Atom& value);
+ const Atom& get(URIInt key) const;
+
+ sigc::signal<void, URIInt, Atom> signal_property;
+
+ class View
+ {
+ public:
+ virtual ~View() {}
+ };
+
+ typedef std::map<URIInt, Atom> Properties;
+ const Properties& properties() { return _properties; }
+
+ View* view() const { return _view; }
+ void set_view(View* view) { _view = view; }
+
+private:
+ uint64_t _id;
+ View* _view;
+
+ Properties _properties;
+};
+
+/** Stub client object to use as a search key. */
+struct ClientObjectKey : public ClientObject
+{
+ explicit ClientObjectKey(uint64_t id) : ClientObject(id) {}
+};
+
+}
+}
+
+#endif // MACHINA_CLIENTOBJECT_HPP
diff --git a/src/client/wscript b/src/client/wscript
new file mode 100644
index 0000000..8333b43
--- /dev/null
+++ b/src/client/wscript
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+from waflib.extras import autowaf as autowaf
+
+def build(bld):
+ obj = bld(features = 'cxx cxxshlib')
+ obj.source = '''
+ ClientModel.cpp
+ ClientObject.cpp
+ '''
+ obj.export_includes = ['.']
+ obj.includes = ['.', '..', '../..']
+ obj.name = 'libmachina_client'
+ obj.target = 'machina_client'
+ obj.use = 'libmachina_engine'
+ autowaf.use_lib(bld, obj, 'GLIBMM GTKMM RAUL LV2')
+
+ bld.add_post_fun(autowaf.run_ldconfig)
diff --git a/src/engine/Action.hpp b/src/engine/Action.hpp
new file mode 100644
index 0000000..2916099
--- /dev/null
+++ b/src/engine/Action.hpp
@@ -0,0 +1,60 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_ACTION_HPP
+#define MACHINA_ACTION_HPP
+
+#include <string>
+#include <iostream>
+
+#include "raul/Maid.hpp"
+#include "raul/TimeSlice.hpp"
+
+#include "machina/types.hpp"
+
+#include "MIDISink.hpp"
+#include "Stateful.hpp"
+
+namespace machina {
+
+/** An Action, executed on entering or exiting of a state.
+ */
+struct Action
+ : public Raul::Maid::Disposable
+ , public Stateful
+{
+ bool operator==(const Action& rhs) const { return false; }
+
+ virtual void execute(MIDISink* sink, Raul::TimeStamp time) = 0;
+
+ virtual void write_state(Sord::Model& model) {}
+};
+
+class PrintAction : public Action
+{
+public:
+ explicit PrintAction(const std::string& msg) : _msg(msg) {}
+
+ void execute(MIDISink* sink, Raul::TimeStamp time)
+ { std::cout << "t=" << time << ": " << _msg << std::endl; }
+
+private:
+ std::string _msg;
+};
+
+} // namespace machina
+
+#endif // MACHINA_ACTION_HPP
diff --git a/src/engine/ActionFactory.cpp b/src/engine/ActionFactory.cpp
new file mode 100644
index 0000000..f031149
--- /dev/null
+++ b/src/engine/ActionFactory.cpp
@@ -0,0 +1,55 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "ActionFactory.hpp"
+#include "MidiAction.hpp"
+
+namespace machina {
+
+SPtr<Action>
+ActionFactory::copy(SPtr<Action> copy)
+{
+ SPtr<MidiAction> ma = dynamic_ptr_cast<MidiAction>(copy);
+ if (ma) {
+ return SPtr<Action>(new MidiAction(ma->event_size(), ma->event()));
+ } else {
+ return SPtr<Action>();
+ }
+}
+
+SPtr<Action>
+ActionFactory::note_on(uint8_t note, uint8_t velocity)
+{
+ uint8_t buf[3];
+ buf[0] = 0x90;
+ buf[1] = note;
+ buf[2] = velocity;
+
+ return SPtr<Action>(new MidiAction(3, buf));
+}
+
+SPtr<Action>
+ActionFactory::note_off(uint8_t note, uint8_t velocity)
+{
+ uint8_t buf[3];
+ buf[0] = 0x80;
+ buf[1] = note;
+ buf[2] = velocity;
+
+ return SPtr<Action>(new MidiAction(3, buf));
+}
+
+} // namespace Machine
diff --git a/src/engine/ActionFactory.hpp b/src/engine/ActionFactory.hpp
new file mode 100644
index 0000000..db7aa68
--- /dev/null
+++ b/src/engine/ActionFactory.hpp
@@ -0,0 +1,34 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_ACTIONFACTORY_HPP
+#define MACHINA_ACTIONFACTORY_HPP
+
+#include "machina/types.hpp"
+
+namespace machina {
+
+struct Action;
+
+namespace ActionFactory {
+SPtr<Action> copy(SPtr<Action> copy);
+SPtr<Action> note_on(uint8_t note, uint8_t velocity=64);
+SPtr<Action> note_off(uint8_t note, uint8_t velocity=64);
+}
+
+} // namespace machina
+
+#endif // MACHINA_ACTIONFACTORY_HPP
diff --git a/src/engine/Controller.cpp b/src/engine/Controller.cpp
new file mode 100644
index 0000000..8f9e1a2
--- /dev/null
+++ b/src/engine/Controller.cpp
@@ -0,0 +1,228 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2014 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "machina/Controller.hpp"
+#include "machina/Engine.hpp"
+#include "machina/Machine.hpp"
+#include "machina/Updates.hpp"
+
+#include "Edge.hpp"
+#include "MidiAction.hpp"
+
+namespace machina {
+
+Controller::Controller(SPtr<Engine> engine,
+ Model& model)
+ : _engine(engine)
+ , _model(model)
+ , _updates(new Raul::RingBuffer(4096))
+{
+ _engine->driver()->set_update_sink(_updates);
+}
+
+uint64_t
+Controller::create(const Properties& properties)
+{
+ std::map<URIInt, Atom>::const_iterator d = properties.find(
+ URIs::instance().machina_duration);
+
+ double duration = 0.0;
+ if (d != properties.end()) {
+ duration = d->second.get<float>();
+ } else {
+ std::cerr << "warning: new object has no duration" << std::endl;
+ }
+
+ TimeDuration dur(_engine->machine()->time().unit(), duration);
+ SPtr<Node> node(new Node(dur));
+ _objects.insert(node);
+ _model.new_object(node->id(), properties);
+ _engine->machine()->add_node(node);
+ return node->id();
+}
+
+void
+Controller::announce(SPtr<Machine> machine)
+{
+ if (!machine) {
+ return;
+ }
+
+ Forge& forge = _engine->forge();
+
+ // Announce nodes
+ for (const auto& n : machine->nodes()) {
+ Properties properties {
+ { URIs::instance().rdf_type,
+ forge.make_urid(URIs::instance().machina_Node) },
+ { URIs::instance().machina_duration,
+ forge.make(float(n->duration().to_double())) } };
+ if (n->is_initial()) {
+ properties.insert({ URIs::instance().machina_initial,
+ forge.make(true) });
+ }
+
+ SPtr<MidiAction> midi_action = dynamic_ptr_cast<MidiAction>(
+ n->enter_action());
+ if (midi_action) {
+ Properties action_properties {
+ { URIs::instance().machina_note_number,
+ forge.make((int32_t)midi_action->event()[1]) } };
+
+ _model.new_object(midi_action->id(), action_properties);
+ properties.insert({ URIs::instance().machina_enter_action,
+ forge.make(int32_t(n->enter_action()->id())) });
+ }
+
+ _objects.insert(n);
+ _model.new_object(n->id(), properties);
+ }
+
+ // Announce edges
+ for (const auto& n : machine->nodes()) {
+ for (const auto& e : n->edges()) {
+ const Properties properties {
+ { URIs::instance().rdf_type,
+ forge.make_urid(URIs::instance().machina_Edge) },
+ { URIs::instance().machina_probability,
+ forge.make(e->probability()) },
+ { URIs::instance().machina_tail_id,
+ forge.make((int32_t)n->id()) },
+ { URIs::instance().machina_head_id,
+ forge.make((int32_t)e->head()->id()) } };
+
+ _objects.insert(e);
+ _model.new_object(e->id(), properties);
+ }
+ }
+}
+
+SPtr<Stateful>
+Controller::find(uint64_t id)
+{
+ SPtr<StatefulKey> key(new StatefulKey(id));
+ Objects::iterator i = _objects.find(key);
+ if (i != _objects.end()) {
+ return *i;
+ }
+ return SPtr<Stateful>();
+}
+
+void
+Controller::learn(SPtr<Raul::Maid> maid, uint64_t node_id)
+{
+ SPtr<Node> node = dynamic_ptr_cast<Node>(find(node_id));
+ if (node) {
+ _engine->machine()->learn(maid, node);
+ } else {
+ std::cerr << "Failed to find node " << node_id << " for learn"
+ << std::endl;
+ }
+}
+
+void
+Controller::set_property(uint64_t object_id,
+ URIInt key,
+ const Atom& value)
+{
+ SPtr<Stateful> object = find(object_id);
+ if (object) {
+ object->set(key, value);
+ _model.set(object_id, key, value);
+ }
+}
+
+uint64_t
+Controller::connect(uint64_t tail_id, uint64_t head_id)
+{
+ SPtr<Node> tail = dynamic_ptr_cast<Node>(find(tail_id));
+ SPtr<Node> head = dynamic_ptr_cast<Node>(find(head_id));
+
+ if (!tail) {
+ std::cerr << "error: tail node " << tail_id << " not found" << std::endl;
+ return 0;
+ } else if (!head) {
+ std::cerr << "error: head node " << head_id << " not found" << std::endl;
+ return 0;
+ }
+
+ SPtr<Edge> edge(new Edge(tail, head));
+ tail->add_edge(edge);
+ _objects.insert(edge);
+
+ Forge& forge = _engine->forge();
+
+ const Properties properties = {
+ { URIs::instance().rdf_type,
+ forge.make_urid(URIs::instance().machina_Edge) },
+ { URIs::instance().machina_probability, forge.make(1.0f) },
+ { URIs::instance().machina_tail_id,
+ forge.make((int32_t)tail->id()) },
+ { URIs::instance().machina_head_id,
+ forge.make((int32_t)head->id()) } };
+
+ _model.new_object(edge->id(), properties);
+
+ return edge->id();
+}
+
+void
+Controller::disconnect(uint64_t tail_id, uint64_t head_id)
+{
+ SPtr<Node> tail = dynamic_ptr_cast<Node>(find(tail_id));
+ SPtr<Node> head = dynamic_ptr_cast<Node>(find(head_id));
+
+ SPtr<Edge> edge = tail->remove_edge_to(head);
+ if (edge) {
+ _model.erase_object(edge->id());
+ } else {
+ std::cerr << "Edge not found" << std::endl;
+ }
+}
+
+void
+Controller::erase(uint64_t id)
+{
+ SPtr<StatefulKey> key(new StatefulKey(id));
+ Objects::iterator i = _objects.find(key);
+ if (i == _objects.end()) {
+ return;
+ }
+
+ SPtr<Node> node = dynamic_ptr_cast<Node>(*i);
+ if (node) {
+ _engine->machine()->remove_node(node);
+ }
+
+ _model.erase_object((*i)->id());
+ _objects.erase(i);
+}
+
+void
+Controller::process_updates()
+{
+ const uint32_t read_space = _updates->read_space();
+
+ uint64_t subject;
+ URIInt key;
+ Atom value;
+ for (uint32_t i = 0; i < read_space; ) {
+ i += read_set(_updates, &subject, &key, &value);
+ _model.set(subject, key, value);
+ }
+}
+
+}
diff --git a/src/engine/Edge.cpp b/src/engine/Edge.cpp
new file mode 100644
index 0000000..7c206f9
--- /dev/null
+++ b/src/engine/Edge.cpp
@@ -0,0 +1,64 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "machina/Atom.hpp"
+#include "machina/URIs.hpp"
+#include "sord/sordmm.hpp"
+
+#include "Edge.hpp"
+#include "Node.hpp"
+
+namespace machina {
+
+void
+Edge::set(URIInt key, const Atom& value)
+{
+ if (key == URIs::instance().machina_probability) {
+ _probability = value.get<float>();
+ }
+}
+
+void
+Edge::write_state(Sord::Model& model)
+{
+ using namespace Raul;
+
+ const Sord::Node& rdf_id = this->rdf_id(model.world());
+
+ SPtr<Node> tail = _tail.lock();
+ SPtr<Node> head = _head;
+
+ if (!tail || !head)
+ return;
+
+ assert(tail->rdf_id(model.world()).is_valid()
+ && head->rdf_id(model.world()).is_valid());
+
+ model.add_statement(rdf_id,
+ Sord::URI(model.world(), MACHINA_NS_tail),
+ tail->rdf_id(model.world()));
+
+ model.add_statement(rdf_id,
+ Sord::URI(model.world(), MACHINA_NS_head),
+ head->rdf_id(model.world()));
+
+ model.add_statement(
+ rdf_id,
+ Sord::URI(model.world(), MACHINA_NS_probability),
+ Sord::Literal::decimal(model.world(), _probability, 7));
+}
+
+} // namespace machina
diff --git a/src/engine/Edge.hpp b/src/engine/Edge.hpp
new file mode 100644
index 0000000..a04dd73
--- /dev/null
+++ b/src/engine/Edge.hpp
@@ -0,0 +1,60 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_EDGE_HPP
+#define MACHINA_EDGE_HPP
+
+#include <list>
+
+#include "machina/types.hpp"
+
+#include "Action.hpp"
+#include "Stateful.hpp"
+
+namespace machina {
+
+class Node;
+
+class Edge : public Stateful
+{
+public:
+ Edge(WPtr<Node> tail, SPtr<Node> head, float probability=1.0f)
+ : _tail(tail)
+ , _head(head)
+ , _probability(probability)
+ {}
+
+ void set(URIInt key, const Atom& value);
+ void write_state(Sord::Model& model);
+
+ WPtr<Node> tail() { return _tail; }
+ SPtr<Node> head() { return _head; }
+
+ void set_tail(WPtr<Node> tail) { _tail = tail; }
+ void set_head(SPtr<Node> head) { _head = head; }
+
+ inline float probability() const { return _probability; }
+ inline void set_probability(float p) { _probability = p; }
+
+private:
+ WPtr<Node> _tail;
+ SPtr<Node> _head;
+ float _probability;
+};
+
+} // namespace machina
+
+#endif // MACHINA_EDGE_HPP
diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp
new file mode 100644
index 0000000..1aed7b6
--- /dev/null
+++ b/src/engine/Engine.cpp
@@ -0,0 +1,126 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2014 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "machina_config.h"
+#include "machina/Engine.hpp"
+#include "machina/Loader.hpp"
+#include "machina/Machine.hpp"
+#include "SMFDriver.hpp"
+#ifdef HAVE_JACK
+#include "JackDriver.hpp"
+#endif
+
+namespace machina {
+
+Engine::Engine(Forge& forge,
+ SPtr<Driver> driver,
+ Sord::World& rdf_world)
+ : _driver(driver)
+ , _rdf_world(rdf_world)
+ , _loader(_forge, _rdf_world)
+ , _forge(forge)
+{}
+
+SPtr<Driver>
+Engine::new_driver(Forge& forge,
+ const std::string& name,
+ SPtr<Machine> machine)
+{
+ if (name == "jack") {
+#ifdef HAVE_JACK
+ JackDriver* driver = new JackDriver(forge, machine);
+ driver->attach("machina");
+ return SPtr<Driver>(driver);
+#else
+ return SPtr<Driver>();
+#endif
+ }
+ if (name == "smf") {
+ return SPtr<Driver>(new SMFDriver(forge, machine->time().unit()));
+ }
+
+ std::cerr << "Error: Unknown driver type `" << name << "'" << std::endl;
+ return SPtr<Driver>();
+}
+
+/** Load the machine at `uri`, and run it (replacing current machine).
+ * Safe to call while engine is processing.
+ */
+SPtr<Machine>
+Engine::load_machine(const std::string& uri)
+{
+ SPtr<Machine> machine = _loader.load(uri);
+ SPtr<Machine> old_machine = _driver->machine(); // Keep reference
+ if (machine) {
+ _driver->set_machine(machine); // Switch driver to new machine and wait
+ }
+
+ // Drop (possibly last) reference to old_machine in this thread
+ return machine;
+}
+
+/** Build a machine from the MIDI at `uri`, and run it (replacing current machine).
+ * Safe to call while engine is processing.
+ */
+SPtr<Machine>
+Engine::load_machine_midi(const std::string& uri,
+ double q,
+ Raul::TimeDuration dur)
+{
+ SPtr<Machine> machine = _loader.load_midi(uri, q, dur);
+ SPtr<Machine> old_machine = _driver->machine(); // Keep reference
+ if (machine) {
+ _driver->set_machine(machine); // Switch driver to new machine and wait
+ }
+
+ // Drop (possibly last) reference to old_machine in this thread
+ return machine;
+}
+
+void
+Engine::export_midi(const std::string& filename, Raul::TimeDuration dur)
+{
+ SPtr<Machine> machine = _driver->machine();
+ SPtr<machina::SMFDriver> file_driver(
+ new machina::SMFDriver(_forge, dur.unit()));
+
+ const bool activated = _driver->is_activated();
+ if (activated) {
+ _driver->deactivate(); // FIXME: disable instead
+ }
+ file_driver->writer()->start(filename, TimeStamp(dur.unit(), 0.0));
+ file_driver->run(machine, dur);
+ machine->reset(NULL, machine->time());
+ file_driver->writer()->finish();
+
+ if (activated) {
+ _driver->activate();
+ }
+}
+
+void
+Engine::set_bpm(double bpm)
+{
+ _driver->set_bpm(bpm);
+}
+
+void
+Engine::set_quantization(double q)
+{
+ _driver->set_quantization(q);
+}
+
+} // namespace machina
diff --git a/src/engine/Evolver.cpp b/src/engine/Evolver.cpp
new file mode 100644
index 0000000..973c9c8
--- /dev/null
+++ b/src/engine/Evolver.cpp
@@ -0,0 +1,139 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <iostream>
+
+#include "eugene/HybridMutation.hpp"
+#include "eugene/Mutation.hpp"
+#include "eugene/TournamentSelection.hpp"
+
+#include "machina/Evolver.hpp"
+#include "machina/Mutation.hpp"
+
+#include "Problem.hpp"
+
+using namespace std;
+using namespace eugene;
+
+namespace machina {
+
+Evolver::Evolver(TimeUnit unit,
+ const string& target_midi,
+ SPtr<Machine> seed)
+ : _rng(0)
+ , _problem(new Problem(unit, target_midi, seed))
+ , _seed_fitness(-FLT_MAX)
+ , _exit_flag(false)
+{
+ SPtr<eugene::HybridMutation<Machine> > m(new HybridMutation<Machine>());
+
+ m->append_mutation(1 / 6.0f, std::shared_ptr< eugene::Mutation<Machine> >(
+ new Mutation::Compress()));
+ m->append_mutation(1 / 6.0f, std::shared_ptr< eugene::Mutation<Machine> >(
+ new Mutation::AddNode()));
+ //m->append_mutation(1/6.0f, std::shared_ptr< eugene::Mutation<Machine> >(
+ // new Mutation::RemoveNode()));
+ //m->append_mutation(1/6.0f, std::shared_ptr< eugene::Mutation<Machine> >(
+ // new Mutation::AdjustNode()));
+ m->append_mutation(1 / 6.0f, std::shared_ptr< eugene::Mutation<Machine> >(
+ new Mutation::SwapNodes()));
+ m->append_mutation(1 / 6.0f, std::shared_ptr< eugene::Mutation<Machine> >(
+ new Mutation::AddEdge()));
+ m->append_mutation(1 / 6.0f, std::shared_ptr< eugene::Mutation<Machine> >(
+ new Mutation::RemoveEdge()));
+ m->append_mutation(1 / 6.0f, std::shared_ptr< eugene::Mutation<Machine> >(
+ new Mutation::AdjustEdge()));
+
+ std::shared_ptr< Selection<Machine> > s(
+ new TournamentSelection<Machine>(*_problem.get(), 3, 0.8));
+ std::shared_ptr< Crossover<Machine> > crossover;
+ size_t gene_length = 20; // FIXME
+ Problem::Population pop;
+ _ga = SPtr<MachinaGA>(
+ new MachinaGA(_rng,
+ _problem,
+ s,
+ crossover,
+ m,
+ pop,
+ gene_length,
+ 20,
+ 2,
+ 1.0,
+ 0.0));
+}
+
+void
+Evolver::start()
+{
+ if (!_thread) {
+ _thread = std::unique_ptr<std::thread>(
+ new std::thread(&Evolver::run, this));
+ }
+}
+
+void
+Evolver::join()
+{
+ if (_thread && _thread->joinable()) {
+ _exit_flag = true;
+ _thread->join();
+ }
+}
+
+void
+Evolver::seed(SPtr<Machine> parent)
+{
+ /*_best = SPtr<Machine>(new Machine(*parent.get()));
+ _best_fitness = _problem->fitness(*_best.get());*/
+ _problem->seed(parent);
+ _seed_fitness = _problem->evaluate(*parent.get());
+}
+
+void
+Evolver::run()
+{
+ float old_best = _ga->best_fitness();
+
+ //cout << "ORIGINAL BEST: " << _ga->best_fitness() << endl;
+
+ _improvement = true;
+
+ while (!_exit_flag) {
+ //cout << "{" << endl;
+ _problem->clear_fitness_cache();
+ _ga->iteration();
+
+ float new_best = _ga->best_fitness();
+
+ /*cout << _problem->fitness_less(old_best, *_ga->best().get()) << endl;
+ cout << "best: " << _ga->best().get() << endl;
+ cout << "best fitness: " << _problem->fitness(*_ga->best().get()) << endl;
+ cout << "old best: " << old_best << endl;
+ cout << "new best: " << new_best << endl;*/
+ cout << "generation best: " << new_best << endl;
+
+ if (_problem->fitness_less_than(old_best, new_best)) {
+ _improvement = true;
+ old_best = new_best;
+ cout << "*** NEW BEST: " << new_best << endl;
+ }
+
+ //cout << "}" << endl;
+ }
+}
+
+} // namespace machina
diff --git a/src/engine/JackDriver.cpp b/src/engine/JackDriver.cpp
new file mode 100644
index 0000000..e7f2e97
--- /dev/null
+++ b/src/engine/JackDriver.cpp
@@ -0,0 +1,479 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <iostream>
+
+#include "machina/Context.hpp"
+#include "machina/URIs.hpp"
+#include "machina/Updates.hpp"
+#include "machina_config.h"
+
+#include "Edge.hpp"
+#include "JackDriver.hpp"
+#include "LearnRequest.hpp"
+#include "MachineBuilder.hpp"
+#include "MidiAction.hpp"
+
+using namespace machina;
+using namespace std;
+
+namespace machina {
+
+JackDriver::JackDriver(Forge& forge, SPtr<Machine> machine)
+ : Driver(forge, machine)
+ , _client(NULL)
+ , _machine_changed(0)
+ , _input_port(NULL)
+ , _output_port(NULL)
+ , _context(forge, 48000, MACHINA_PPQN, 120.0)
+ , _frames_unit(TimeUnit::FRAMES, 48000)
+ , _beats_unit(TimeUnit::BEATS, 19200)
+ , _stop(0)
+ , _stop_flag(false)
+ , _record_dur(_frames_unit) // = 0
+ , _is_activated(false)
+{
+ _context.set_sink(this);
+}
+
+JackDriver::~JackDriver()
+{
+ detach();
+}
+
+void
+JackDriver::attach(const std::string& client_name)
+{
+ // Already connected
+ if (_client) {
+ return;
+ }
+
+ jack_set_error_function(jack_error_cb);
+
+ _client = jack_client_open(client_name.c_str(), JackNullOption, NULL, NULL);
+
+ if (_client == NULL) {
+ _is_activated = false;
+ } else {
+ jack_set_error_function(jack_error_cb);
+ jack_on_shutdown(_client, jack_shutdown_cb, this);
+ jack_set_process_callback(_client, jack_process_cb, this);
+ }
+
+ if (jack_client()) {
+
+ _context.time().set_tick_rate(sample_rate());
+
+ _input_port = jack_port_register(jack_client(),
+ "in",
+ JACK_DEFAULT_MIDI_TYPE,
+ JackPortIsInput,
+ 0);
+
+ if (!_input_port) {
+ std::cerr << "WARNING: Failed to create MIDI input port."
+ << std::endl;
+ }
+
+ _output_port = jack_port_register(jack_client(),
+ "out",
+ JACK_DEFAULT_MIDI_TYPE,
+ JackPortIsOutput,
+ 0);
+
+ if (!_output_port) {
+ std::cerr << "WARNING: Failed to create MIDI output port."
+ << std::endl;
+ }
+
+ if (!_machine) {
+ _machine = SPtr<Machine>(
+ new Machine(TimeUnit::frames(
+ jack_get_sample_rate(jack_client()))));
+ }
+ }
+}
+
+void
+JackDriver::detach()
+{
+ if (_is_activated) {
+ _is_activated = false;
+ _stop.timed_wait(std::chrono::seconds(1));
+ }
+
+ if (_input_port) {
+ jack_port_unregister(jack_client(), _input_port);
+ _input_port = NULL;
+ }
+
+ if (_output_port) {
+ jack_port_unregister(jack_client(), _output_port);
+ _output_port = NULL;
+ }
+
+ if (_client) {
+ deactivate();
+ jack_client_close(_client);
+ _client = NULL;
+ _is_activated = false;
+ }
+}
+
+void
+JackDriver::activate()
+{
+ if (!jack_activate(_client)) {
+ _is_activated = true;
+ } else {
+ _is_activated = false;
+ }
+}
+
+void
+JackDriver::deactivate()
+{
+ if (_client) {
+ jack_deactivate(_client);
+ }
+
+ _is_activated = false;
+}
+
+void
+JackDriver::set_machine(SPtr<Machine> machine)
+{
+ if (machine == _machine) {
+ return;
+ }
+
+ SPtr<Machine> last_machine = _last_machine; // Keep a reference
+ _machine_changed.reset(0);
+ assert(!last_machine.unique());
+ _machine = machine;
+ if (is_activated()) {
+ _machine_changed.wait();
+ }
+ assert(_machine == machine);
+ last_machine.reset();
+}
+
+void
+JackDriver::read_input_recording(SPtr<Machine> machine,
+ const Raul::TimeSlice& time)
+{
+ const jack_nframes_t nframes = time.length_ticks().ticks();
+ void* buf = jack_port_get_buffer(_input_port, nframes);
+ const jack_nframes_t n_events = jack_midi_get_event_count(buf);
+
+ for (jack_nframes_t i = 0; i < n_events; ++i) {
+ jack_midi_event_t ev;
+ jack_midi_event_get(&ev, buf, i);
+
+ const TimeStamp rel_time_frames = TimeStamp(_frames_unit, ev.time);
+ const TimeStamp time_frames = _record_dur + rel_time_frames;
+ _recorder->write(time.ticks_to_beats(time_frames), ev.size, ev.buffer);
+ }
+
+ if (n_events > 0) {
+ _recorder->whip();
+ }
+
+ _record_dur += time.length_ticks();
+}
+
+void
+JackDriver::read_input_playing(SPtr<Machine> machine,
+ const Raul::TimeSlice& time)
+{
+ const jack_nframes_t nframes = time.length_ticks().ticks();
+ void* buf = jack_port_get_buffer(_input_port, nframes);
+ const jack_nframes_t n_events = jack_midi_get_event_count(buf);
+
+ for (jack_nframes_t i = 0; i < n_events; ++i) {
+ jack_midi_event_t ev;
+ jack_midi_event_get(&ev, buf, i);
+
+ if (ev.buffer[0] == 0x90) {
+ const SPtr<LearnRequest> learn = machine->pending_learn();
+ if (learn) {
+ learn->enter_action()->set_event(ev.size, ev.buffer);
+ learn->start(_quantization,
+ TimeStamp(TimeUnit::frames(sample_rate()),
+ jack_last_frame_time(_client)
+ + ev.time, 0));
+ }
+
+ } else if (ev.buffer[0] == 0x80) {
+ const SPtr<LearnRequest> learn = machine->pending_learn();
+ if (learn && learn->started()) {
+ learn->exit_action()->set_event(ev.size, ev.buffer);
+ learn->finish(TimeStamp(TimeUnit::frames(sample_rate()),
+ jack_last_frame_time(_client) + ev.time,
+ 0));
+
+ const uint64_t id = Stateful::next_id();
+ write_set(_updates, id,
+ URIs::instance().rdf_type,
+ _forge.make_urid(URIs::instance().machina_MidiAction));
+ write_set(_updates, learn->node()->id(),
+ URIs::instance().machina_enter_action,
+ _forge.make((int32_t)id));
+ write_set(_updates, id,
+ URIs::instance().machina_note_number,
+ _forge.make((int32_t)ev.buffer[1]));
+
+ machine->clear_pending_learn();
+ }
+ }
+ }
+}
+
+void
+JackDriver::write_event(Raul::TimeStamp time,
+ size_t size,
+ const byte* event)
+{
+ if (!_output_port) {
+ return;
+ }
+
+ const Raul::TimeSlice& slice = _context.time();
+
+ if (slice.beats_to_ticks(time) + slice.offset_ticks() <
+ slice.start_ticks()) {
+ std::cerr << "ERROR: Missed event by "
+ << (slice.start_ticks()
+ - slice.beats_to_ticks(time)
+ + slice.offset_ticks())
+ << " ticks"
+ << "\n\tbpm: " << slice.bpm()
+ << "\n\tev time: " << slice.beats_to_ticks(time)
+ << "\n\tcycle_start: " << slice.start_ticks()
+ << "\n\tcycle_end: " << (slice.start_ticks()
+ + slice.length_ticks())
+ << "\n\tcycle_length: " << slice.length_ticks()
+ << std::endl << std::endl;
+ return;
+ }
+
+ const TimeDuration nframes = slice.length_ticks();
+ const TimeStamp offset = slice.beats_to_ticks(time)
+ + slice.offset_ticks() - slice.start_ticks();
+
+ if (!(offset < slice.offset_ticks() + nframes)) {
+ std::cerr << "ERROR: Event offset " << offset << " outside cycle "
+ << "\n\tbpm: " << slice.bpm()
+ << "\n\tev time: " << slice.beats_to_ticks(time)
+ << "\n\tcycle_start: " << slice.start_ticks()
+ << "\n\tcycle_end: " << slice.start_ticks()
+ + slice.length_ticks()
+ << "\n\tcycle_length: " << slice.length_ticks() << std::endl;
+ } else {
+#ifdef JACK_MIDI_NEEDS_NFRAMES
+ jack_midi_event_write(
+ jack_port_get_buffer(_output_port, nframes), offset,
+ event, size, nframes);
+#else
+ jack_midi_event_write(
+ jack_port_get_buffer(_output_port, nframes.ticks()), offset.ticks(),
+ event, size);
+#endif
+ }
+}
+
+void
+JackDriver::on_process(jack_nframes_t nframes)
+{
+ if (!_is_activated) {
+ _stop.post();
+ return;
+ }
+
+ _context.time().set_bpm(_bpm);
+
+ assert(_output_port);
+ jack_midi_clear_buffer(jack_port_get_buffer(_output_port, nframes));
+
+ TimeStamp length_ticks(TimeStamp(_context.time().ticks_unit(), nframes));
+
+ _context.time().set_length(length_ticks);
+ _context.time().set_offset(TimeStamp(_context.time().ticks_unit(), 0, 0));
+
+ /* Take a reference to machine here and use only it during the process
+ * cycle so _machine can be switched with set_machine during a cycle. */
+ SPtr<Machine> machine = _machine;
+
+ // Machine was switched since last cycle, finalize old machine.
+ if (machine != _last_machine) {
+ if (_last_machine) {
+ assert(!_last_machine.unique()); // Realtime, can't delete
+ _last_machine->reset(_context.sink(), _last_machine->time()); // Exit all active states
+ _last_machine.reset(); // Cut our reference
+ }
+ _machine_changed.post(); // Signal we're done with it
+ }
+
+ if (!machine) {
+ _last_machine = machine;
+ goto end;
+ }
+
+ if (_stop_flag) {
+ machine->reset(_context.sink(), _context.time().start_beats());
+ }
+
+ switch (_play_state) {
+ case PlayState::STOPPED:
+ break;
+ case PlayState::PLAYING:
+ read_input_playing(machine, _context.time());
+ break;
+ case PlayState::RECORDING:
+ case PlayState::STEP_RECORDING:
+ read_input_recording(machine, _context.time());
+ break;
+ }
+
+ if (machine->is_empty()) {
+ goto end;
+ }
+
+ while (true) {
+ const uint32_t run_dur_frames = machine->run(_context, _updates);
+
+ if (run_dur_frames == 0) {
+ // Machine didn't run at all (machine has no initial states)
+ machine->reset(_context.sink(), machine->time()); // Try again next cycle
+ _context.time().set_slice(TimeStamp(_frames_unit, 0, 0),
+ TimeStamp(_frames_unit, 0, 0));
+ break;
+
+ } else if (machine->is_finished()) {
+ // Machine ran for portion of cycle and is finished
+ machine->reset(_context.sink(), machine->time());
+
+ _context.time().set_slice(TimeStamp(_frames_unit, 0, 0),
+ TimeStamp(_frames_unit, nframes
+ - run_dur_frames, 0));
+ _context.time().set_offset(TimeStamp(_frames_unit, run_dur_frames,
+ 0));
+
+ } else {
+ // Machine ran for entire cycle
+ _context.time().set_slice(
+ _context.time().start_ticks() + _context.time().length_ticks(),
+ TimeStamp(_frames_unit, 0, 0));
+ break;
+ }
+ }
+
+end:
+ /* Remember the last machine run, in case a switch happens and
+ * we need to finalize it next cycle. */
+ _last_machine = machine;
+
+ if (_stop_flag) {
+ _context.time().set_slice(TimeStamp(_frames_unit, 0, 0),
+ TimeStamp(_frames_unit, 0, 0));
+ _stop_flag = false;
+ _stop.post();
+ }
+}
+
+void
+JackDriver::set_play_state(PlayState state)
+{
+ switch (state) {
+ case PlayState::STOPPED:
+ switch (_play_state) {
+ case PlayState::STOPPED:
+ break;
+ case PlayState::RECORDING:
+ case PlayState::STEP_RECORDING:
+ finish_record();
+ // fallthru
+ case PlayState::PLAYING:
+ _stop_flag = true;
+ _stop.wait();
+ }
+ break;
+ case PlayState::RECORDING:
+ start_record(false);
+ break;
+ case PlayState::STEP_RECORDING:
+ start_record(true);
+ break;
+ case PlayState::PLAYING:
+ if (_play_state == PlayState::RECORDING ||
+ _play_state == PlayState::STEP_RECORDING) {
+ finish_record();
+ }
+ }
+ Driver::set_play_state(state);
+}
+
+void
+JackDriver::start_record(bool step)
+{
+ const double q = (step || _quantize_record) ? _quantization : 0.0;
+ switch (_play_state) {
+ case PlayState::STOPPED:
+ case PlayState::PLAYING:
+ _recorder = SPtr<Recorder>(
+ new Recorder(_forge, 1024, _beats_unit, q, step));
+ _record_dur = 0;
+ break;
+ case PlayState::RECORDING:
+ case PlayState::STEP_RECORDING:
+ _recorder->builder()->set_step(true);
+ break;
+ }
+ _play_state = step ? PlayState::STEP_RECORDING : PlayState::RECORDING;
+}
+
+void
+JackDriver::finish_record()
+{
+ _play_state = PlayState::PLAYING;
+ SPtr<Machine> machine = _recorder->finish();
+ _recorder.reset();
+ _machine->merge(*machine.get());
+}
+
+int
+JackDriver::jack_process_cb(jack_nframes_t nframes, void* jack_driver)
+{
+ JackDriver* me = reinterpret_cast<JackDriver*>(jack_driver);
+ me->on_process(nframes);
+ return 0;
+}
+
+void
+JackDriver::jack_shutdown_cb(void* jack_driver)
+{
+ JackDriver* me = reinterpret_cast<JackDriver*>(jack_driver);
+ me->_client = NULL;
+}
+
+void
+JackDriver::jack_error_cb(const char* msg)
+{
+ cerr << "[JACK] Error: " << msg << endl;
+}
+
+} // namespace machina
diff --git a/src/engine/JackDriver.hpp b/src/engine/JackDriver.hpp
new file mode 100644
index 0000000..4ebfaa1
--- /dev/null
+++ b/src/engine/JackDriver.hpp
@@ -0,0 +1,119 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_JACKDRIVER_HPP
+#define MACHINA_JACKDRIVER_HPP
+
+#include <jack/jack.h>
+#include <jack/midiport.h>
+
+#include "raul/Semaphore.hpp"
+
+#include "machina/Context.hpp"
+#include "machina/Driver.hpp"
+#include "machina/Machine.hpp"
+#include "machina/types.hpp"
+
+#include "Recorder.hpp"
+
+namespace machina {
+
+class MidiAction;
+class Node;
+
+/** Realtime JACK Driver.
+ *
+ * "Ticks" are individual frames when running under this driver, and all code
+ * in the processing context must be realtime safe (non-blocking).
+ */
+class JackDriver : public machina::Driver
+{
+public:
+ JackDriver(Forge& forge,
+ SPtr<Machine> machine = SPtr<Machine>());
+ ~JackDriver();
+
+ void attach(const std::string& client_name);
+ void detach();
+
+ void activate();
+ void deactivate();
+
+ void set_machine(SPtr<Machine> machine);
+
+ void write_event(Raul::TimeStamp time,
+ size_t size,
+ const unsigned char* event);
+
+ void set_play_state(PlayState state);
+
+ void start_transport() { jack_transport_start(_client); }
+ void stop_transport() { jack_transport_stop(_client); }
+
+ void rewind_transport() {
+ jack_position_t zero;
+ zero.frame = 0;
+ zero.valid = (jack_position_bits_t)0;
+ jack_transport_reposition(_client, &zero);
+ }
+
+ bool is_activated() const { return _is_activated; }
+ bool is_attached() const { return _client != NULL; }
+ bool is_realtime() const { return _client && jack_is_realtime(_client); }
+
+ jack_nframes_t sample_rate() const { return jack_get_sample_rate(_client); }
+ jack_client_t* jack_client() const { return _client; }
+
+private:
+ void read_input_recording(SPtr<Machine> machine,
+ const Raul::TimeSlice& time);
+
+ void read_input_playing(SPtr<Machine> machine,
+ const Raul::TimeSlice& time);
+
+ static void jack_error_cb(const char* msg);
+ static int jack_process_cb(jack_nframes_t nframes, void* me);
+ static void jack_shutdown_cb(void* me);
+
+ void start_record(bool step);
+ void finish_record();
+
+ void on_process(jack_nframes_t nframes);
+
+ jack_client_t* _client;
+
+ Raul::Semaphore _machine_changed;
+ SPtr<Machine> _last_machine;
+
+ jack_port_t* _input_port;
+ jack_port_t* _output_port;
+
+ Context _context;
+
+ Raul::TimeUnit _frames_unit;
+ Raul::TimeUnit _beats_unit;
+
+ Raul::Semaphore _stop;
+ bool _stop_flag;
+
+ Raul::TimeDuration _record_dur;
+ SPtr<Recorder> _recorder;
+ bool _is_activated;
+};
+
+} // namespace machina
+
+#endif // MACHINA_JACKDRIVER_HPP
diff --git a/src/engine/LearnRequest.cpp b/src/engine/LearnRequest.cpp
new file mode 100644
index 0000000..5756dd1
--- /dev/null
+++ b/src/engine/LearnRequest.cpp
@@ -0,0 +1,56 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "LearnRequest.hpp"
+#include "quantize.hpp"
+
+namespace machina {
+
+LearnRequest::LearnRequest(SPtr<Raul::Maid> maid, SPtr<Node> node)
+ : _started(false)
+ , _start_time(TimeUnit(TimeUnit::BEATS, 19200), 0, 0) // irrelevant
+ , _quantization(0) // irrelevant
+ , _node(node)
+ , _enter_action(new MidiAction(4, NULL))
+ , _exit_action(new MidiAction(4, NULL))
+{
+}
+
+SPtr<LearnRequest>
+LearnRequest::create(SPtr<Raul::Maid> maid, SPtr<Node> node)
+{
+ return SPtr<LearnRequest>(new LearnRequest(maid, node));
+}
+
+void
+LearnRequest::start(double q, Raul::TimeStamp time)
+{
+ _started = true;
+ _start_time = time;
+ _quantization = q;
+}
+
+/** Add the learned actions to the node */
+void
+LearnRequest::finish(TimeStamp time)
+{
+ _node->set_enter_action(_enter_action);
+ _node->set_exit_action(_exit_action);
+
+ _node->set_duration(time - _start_time);
+}
+
+}
diff --git a/src/engine/LearnRequest.hpp b/src/engine/LearnRequest.hpp
new file mode 100644
index 0000000..9cdfc3c
--- /dev/null
+++ b/src/engine/LearnRequest.hpp
@@ -0,0 +1,61 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_LEARNREQUEST_HPP
+#define MACHINA_LEARNREQUEST_HPP
+
+#include "raul/Maid.hpp"
+
+#include "machina/types.hpp"
+
+#include "MidiAction.hpp"
+#include "Node.hpp"
+
+namespace machina {
+
+class Node;
+class MidiAction;
+
+/** A request to MIDI learn a certain node.
+ */
+class LearnRequest : public Raul::Maid::Disposable
+{
+public:
+ static SPtr<LearnRequest> create(SPtr<Raul::Maid> maid, SPtr<Node> node);
+
+ void start(double q, Raul::TimeStamp time);
+ void finish(TimeStamp time);
+
+ inline bool started() const { return _started; }
+
+ const SPtr<Node>& node() { return _node; }
+ const SPtr<MidiAction>& enter_action() { return _enter_action; }
+ const SPtr<MidiAction>& exit_action() { return _exit_action; }
+
+private:
+ LearnRequest(SPtr<Raul::Maid> maid, SPtr<Node> node);
+
+ bool _started;
+ TimeStamp _start_time;
+ double _quantization;
+ SPtr<Node> _node;
+ SPtr<MidiAction> _enter_action;
+ SPtr<MidiAction> _exit_action;
+};
+
+} // namespace machina
+
+#endif // MACHINA_LEARNREQUEST_HPP
diff --git a/src/engine/Loader.cpp b/src/engine/Loader.cpp
new file mode 100644
index 0000000..1c662d2
--- /dev/null
+++ b/src/engine/Loader.cpp
@@ -0,0 +1,193 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <cmath>
+#include <iostream>
+#include <map>
+
+#include "lv2/lv2plug.in/ns/ext/midi/midi.h"
+
+#include "machina/Loader.hpp"
+#include "machina/URIs.hpp"
+#include "machina/Machine.hpp"
+
+#include "Edge.hpp"
+#include "MidiAction.hpp"
+#include "Node.hpp"
+#include "SMFDriver.hpp"
+#include "machina_config.h"
+
+using namespace Raul;
+using namespace std;
+
+namespace machina {
+
+Loader::Loader(Forge& forge, Sord::World& rdf_world)
+ : _forge(forge)
+ , _rdf_world(rdf_world)
+{}
+
+static SPtr<Action>
+load_action(Sord::Model& model, Sord::Node node)
+{
+ if (!node.is_valid()) {
+ return SPtr<Action>();
+ }
+
+ Sord::URI rdf_type(model.world(), MACHINA_URI_RDF "type");
+ Sord::URI midi_NoteOn(model.world(), LV2_MIDI__NoteOn);
+ Sord::URI midi_NoteOff(model.world(), LV2_MIDI__NoteOff);
+ Sord::URI midi_noteNumber(model.world(), LV2_MIDI__noteNumber);
+ Sord::URI midi_velocity(model.world(), LV2_MIDI__velocity);
+
+ Sord::Node type = model.get(node, rdf_type, Sord::Node());
+ uint8_t status = 0;
+ if (type == midi_NoteOn) {
+ status = LV2_MIDI_MSG_NOTE_ON;
+ } else if (type == midi_NoteOff) {
+ status = LV2_MIDI_MSG_NOTE_OFF;
+ } else {
+ return SPtr<Action>();
+ }
+
+ Sord::Node num_node = model.get(node, midi_noteNumber, Sord::Node());
+ Sord::Node vel_node = model.get(node, midi_velocity, Sord::Node());
+
+ const uint8_t num = num_node.is_int() ? num_node.to_int() : 64;
+ const uint8_t vel = vel_node.is_int() ? vel_node.to_int() : 64;
+ const uint8_t event[3] = { status, num, vel };
+
+ return SPtr<Action>(new MidiAction(sizeof(event), event));
+}
+
+/** Load (create) all objects from RDF into the engine.
+ *
+ * @param uri URI of machine (resolvable URI to an RDF document).
+ * @return Loaded Machine.
+ */
+SPtr<Machine>
+Loader::load(const std::string& uri)
+{
+ std::string document_uri = uri;
+
+ // If "URI" doesn't contain a colon, try to resolve as a filename
+ if (uri.find(":") == string::npos) {
+ document_uri = "file://" + document_uri;
+ }
+
+ cout << "Loading " << document_uri << endl;
+
+ TimeUnit beats(TimeUnit::BEATS, MACHINA_PPQN);
+
+ SPtr<Machine> machine(new Machine(beats));
+
+ typedef std::map<Sord::Node, SPtr<Node> > Created;
+ Created created;
+
+ Sord::URI base_uri(_rdf_world, document_uri);
+ Sord::Model model(_rdf_world, document_uri);
+
+ SerdEnv* env = serd_env_new(base_uri.to_serd_node());
+ model.load_file(env, SERD_TURTLE, document_uri);
+ serd_env_free(env);
+
+ Sord::Node nil;
+
+ Sord::URI machina_SelectorNode(_rdf_world, MACHINA_NS_SelectorNode);
+ Sord::URI machina_duration(_rdf_world, MACHINA_NS_duration);
+ Sord::URI machina_edge(_rdf_world, MACHINA_NS_arc);
+ Sord::URI machina_head(_rdf_world, MACHINA_NS_head);
+ Sord::URI machina_node(_rdf_world, MACHINA_NS_node);
+ Sord::URI machina_onEnter(_rdf_world, MACHINA_NS_onEnter);
+ Sord::URI machina_onExit(_rdf_world, MACHINA_NS_onExit);
+ Sord::URI machina_probability(_rdf_world, MACHINA_NS_probability);
+ Sord::URI machina_start(_rdf_world, MACHINA_NS_start);
+ Sord::URI machina_tail(_rdf_world, MACHINA_NS_tail);
+ Sord::URI rdf_type(_rdf_world, MACHINA_URI_RDF "type");
+
+ Sord::Node subject = base_uri;
+
+ // Get start node ID (but re-use existing start node)
+ Sord::Iter i = model.find(subject, machina_start, nil);
+ if (i.end()) {
+ cerr << "error: Machine has no start node" << std::endl;
+ }
+ created[i.get_object()] = machine->initial_node();
+
+ // Get remaining nodes
+ for (Sord::Iter i = model.find(subject, machina_node, nil); !i.end(); ++i) {
+ const Sord::Node& id = i.get_object();
+ if (created.find(id) != created.end()) {
+ cerr << "warning: Machine lists the same node twice" << std::endl;
+ continue;
+ }
+
+ // Create node
+ Sord::Iter d = model.find(id, machina_duration, nil);
+ SPtr<Node> node(new Node(TimeStamp(beats, d.get_object().to_float())));
+ machine->add_node(node);
+ created[id] = node;
+
+ node->set_enter_action(
+ load_action(model, model.get(id, machina_onEnter, nil)));
+ node->set_exit_action(
+ load_action(model, model.get(id, machina_onExit, nil)));
+ }
+
+ // Get arcs
+ for (Sord::Iter i = model.find(subject, machina_edge, nil); !i.end(); ++i) {
+ Sord::Node edge = i.get_object();
+ Sord::Iter t = model.find(edge, machina_tail, nil);
+ Sord::Iter h = model.find(edge, machina_head, nil);
+ Sord::Iter p = model.find(edge, machina_probability, nil);
+
+ Sord::Node tail = t.get_object();
+ Sord::Node head = h.get_object();
+ Sord::Node probability = p.get_object();
+
+ float prob = probability.to_float();
+
+ Created::iterator tail_i = created.find(tail);
+ Created::iterator head_i = created.find(head);
+
+ if (tail_i != created.end() && head_i != created.end()) {
+ const SPtr<Node> tail = tail_i->second;
+ const SPtr<Node> head = head_i->second;
+ tail->add_edge(SPtr<Edge>(new Edge(tail, head, prob)));
+ } else {
+ cerr << "warning: Ignored edge between unknown nodes "
+ << tail << " -> " << head << endl;
+ }
+ }
+
+ if (machine && !machine->nodes().empty()) {
+ machine->reset(NULL, machine->time());
+ return machine;
+ } else {
+ return SPtr<Machine>();
+ }
+}
+
+SPtr<Machine>
+Loader::load_midi(const std::string& uri,
+ double q,
+ Raul::TimeDuration dur)
+{
+ SPtr<SMFDriver> file_driver(new SMFDriver(_forge, dur.unit()));
+ return file_driver->learn(uri, q, dur);
+}
+
+} // namespace machina
diff --git a/src/engine/MIDISink.hpp b/src/engine/MIDISink.hpp
new file mode 100644
index 0000000..983dbba
--- /dev/null
+++ b/src/engine/MIDISink.hpp
@@ -0,0 +1,37 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_MIDI_SINK_HPP
+#define MACHINA_MIDI_SINK_HPP
+
+#include "raul/Deletable.hpp"
+#include "raul/TimeStamp.hpp"
+
+namespace machina {
+
+/** Pure virtual base for anything you can write MIDI to. */
+class MIDISink
+ : public Raul::Deletable
+{
+public:
+ virtual void write_event(Raul::TimeStamp time,
+ size_t ev_size,
+ const uint8_t* ev) = 0;
+};
+
+} // namespace machina
+
+#endif // MACHINA_MIDI_SINK_HPP
diff --git a/src/engine/Machine.cpp b/src/engine/Machine.cpp
new file mode 100644
index 0000000..b144b6f
--- /dev/null
+++ b/src/engine/Machine.cpp
@@ -0,0 +1,389 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <cstdlib>
+#include <map>
+
+#include "sord/sordmm.hpp"
+
+#include "machina/Atom.hpp"
+#include "machina/Context.hpp"
+#include "machina/Machine.hpp"
+#include "machina/URIs.hpp"
+#include "machina/Updates.hpp"
+#include "machina/types.hpp"
+
+#include "Edge.hpp"
+#include "Node.hpp"
+#include "LearnRequest.hpp"
+#include "MidiAction.hpp"
+
+using namespace std;
+using namespace Raul;
+
+namespace machina {
+
+Machine::Machine(TimeUnit unit)
+ : fitness(0.0)
+ , _initial_node(new Node(TimeStamp(unit, 0, 0), true))
+ , _active_nodes(MAX_ACTIVE_NODES, SPtr<Node>())
+ , _time(unit, 0, 0)
+ , _is_finished(false)
+{
+ _nodes.insert(_initial_node);
+}
+
+void
+Machine::assign(const Machine& copy)
+{
+ std::map< SPtr<Node>, SPtr<Node> > replacements;
+
+ replacements[copy.initial_node()] = _initial_node;
+
+ for (const auto& n : copy._nodes) {
+ if (!n->is_initial()) {
+ SPtr<machina::Node> node(new machina::Node(*n.get()));
+ _nodes.insert(node);
+ replacements[n] = node;
+ }
+ }
+
+ for (const auto& n : _nodes) {
+ for (const auto& e : n->edges()) {
+ e->set_tail(n);
+ e->set_head(replacements[e->head()]);
+ }
+ }
+}
+
+Machine::Machine(const Machine& copy)
+ : Stateful() // don't copy RDF ID
+ , fitness(0.0)
+ , _initial_node(new Node(TimeStamp(copy.time().unit(), 0, 0), true))
+ , _active_nodes(MAX_ACTIVE_NODES, SPtr<Node>())
+ , _time(copy.time())
+ , _is_finished(false)
+{
+ _nodes.insert(_initial_node);
+ assign(copy);
+}
+
+Machine&
+Machine::operator=(const Machine& copy)
+{
+ if (&copy == this) {
+ return *this;
+ }
+
+ fitness = copy.fitness;
+
+ _active_nodes = std::vector< SPtr<Node> >(MAX_ACTIVE_NODES, SPtr<Node>());
+ _is_finished = false;
+ _time = copy._time;
+ _pending_learn = SPtr<LearnRequest>();
+
+ _nodes.clear();
+ _nodes.insert(_initial_node);
+ assign(copy);
+
+ return *this;
+}
+
+bool
+Machine::operator==(const Machine& rhs) const
+{
+ return false;
+}
+
+void
+Machine::merge(const Machine& machine)
+{
+ for (const auto& m : machine.nodes()) {
+ if (m->is_initial()) {
+ for (const auto& e : m->edges()) {
+ e->set_tail(_initial_node);
+ _initial_node->edges().insert(e);
+ }
+ } else {
+ _nodes.insert(m);
+ }
+ }
+}
+
+/** Always returns a node, unless there are none */
+SPtr<Node>
+Machine::random_node()
+{
+ if (_nodes.empty()) {
+ return SPtr<Node>();
+ }
+
+ size_t i = rand() % _nodes.size();
+
+ // FIXME: O(n) worst case :(
+ for (Nodes::const_iterator n = _nodes.begin(); n != _nodes.end(); ++n,
+ --i) {
+ if (i == 0) {
+ return *n;
+ }
+ }
+
+ return SPtr<Node>();
+}
+
+/** May return NULL even if edges exist (with low probability) */
+SPtr<Edge>
+Machine::random_edge()
+{
+ SPtr<Node> tail = random_node();
+
+ for (size_t i = 0; i < _nodes.size() && tail->edges().empty(); ++i) {
+ tail = random_node();
+ }
+
+ return tail ? tail->random_edge() : SPtr<Edge>();
+}
+
+void
+Machine::add_node(SPtr<Node> node)
+{
+ _nodes.insert(node);
+}
+
+void
+Machine::remove_node(SPtr<Node> node)
+{
+ _nodes.erase(node);
+
+ for (Nodes::const_iterator n = _nodes.begin(); n != _nodes.end(); ++n) {
+ (*n)->remove_edge_to(node);
+ }
+}
+
+void
+Machine::reset(MIDISink* sink, Raul::TimeStamp time)
+{
+ if (!_is_finished) {
+ for (auto& n : _active_nodes) {
+ if (n) {
+ n->exit(sink, time);
+ n.reset();
+ }
+ }
+ }
+
+ _time = TimeStamp(_time.unit(), 0, 0);
+ _is_finished = false;
+}
+
+SPtr<Node>
+Machine::earliest_node() const
+{
+ SPtr<Node> earliest;
+
+ for (const auto& n : _active_nodes) {
+ if (n && (!earliest || n->exit_time() < earliest->exit_time())) {
+ earliest = n;
+ }
+ }
+
+ return earliest;
+}
+
+bool
+Machine::enter_node(Context& context,
+ SPtr<Node> node,
+ SPtr<Raul::RingBuffer> updates)
+{
+ assert(!node->is_active());
+ assert(_active_nodes.size() == MAX_ACTIVE_NODES);
+
+ /* FIXME: Would be best to use the MIDI note here as a hash key, at least
+ * while all actions are still MIDI notes... */
+ size_t index = (rand() % MAX_ACTIVE_NODES);
+ for (size_t i = 0; i < MAX_ACTIVE_NODES; ++i) {
+ if (!_active_nodes[index]) {
+ node->enter(context.sink(), _time);
+ _active_nodes[index] = node;
+
+ write_set(updates,
+ node->id(),
+ URIs::instance().machina_active,
+ context.forge().make(true));
+ return true;
+ }
+ index = (index + 1) % MAX_ACTIVE_NODES;
+ }
+
+ // If we get here, ran out of active node spots. Don't enter node
+ return false;
+}
+
+void
+Machine::exit_node(Context& context,
+ SPtr<Node> node,
+ SPtr<Raul::RingBuffer> updates)
+{
+ // Exit node
+ node->exit(context.sink(), _time);
+
+ // Notify UI
+ write_set(updates,
+ node->id(),
+ URIs::instance().machina_active,
+ context.forge().make(false));
+
+ // Remove node from _active_nodes
+ for (auto& n : _active_nodes) {
+ if (n == node) {
+ n.reset();
+ }
+ }
+
+ // Activate successors
+ if (node->is_selector()) {
+ const double rand_normal = rand() / (double)RAND_MAX; // [0, 1]
+ double range_min = 0;
+
+ for (const auto& e : node->edges()) {
+ if (!e->head()->is_active()
+ && rand_normal > range_min
+ && rand_normal < range_min + e->probability()) {
+
+ enter_node(context, e->head(), updates);
+ break;
+
+ } else {
+ range_min += e->probability();
+ }
+ }
+ } else {
+ for (const auto& e : node->edges()) {
+ const double rand_normal = rand() / (double)RAND_MAX; // [0, 1]
+ if (rand_normal <= e->probability()) {
+ SPtr<Node> head = e->head();
+
+ if (!head->is_active()) {
+ enter_node(context, head, updates);
+ }
+ }
+ }
+ }
+}
+
+uint32_t
+Machine::run(Context& context, SPtr<Raul::RingBuffer> updates)
+{
+ if (_is_finished) {
+ return 0;
+ }
+
+ const Raul::TimeSlice& time = context.time();
+
+ const TimeStamp end_frames = (time.start_ticks() + time.length_ticks());
+ const TimeStamp end_beats = time.ticks_to_beats(end_frames);
+
+ if (_time.is_zero()) { // Initial run
+ // Exit any active nodes
+ for (auto& n : _active_nodes) {
+ if (n && n->is_active()) {
+ n->exit(context.sink(), _time);
+ write_set(updates,
+ n->id(),
+ URIs::instance().machina_active,
+ context.forge().make(false));
+ }
+ n.reset();
+ }
+
+ // Enter initial node
+ enter_node(context, _initial_node, updates);
+
+ if (_initial_node->edges().empty()) { // Nowhere to go, exit
+ _is_finished = true;
+ return 0;
+ }
+ }
+
+ while (true) {
+ SPtr<Node> earliest = earliest_node();
+ if (!earliest) {
+ // No more active states, machine is finished
+ _is_finished = true;
+ break;
+ }
+
+ const TimeStamp exit_time = earliest->exit_time();
+ if (time.beats_to_ticks(exit_time) < end_frames) {
+ // Earliest active state ends this cycle, exit it
+ _time = earliest->exit_time();
+ exit_node(context, earliest, updates);
+
+ } else {
+ // Earliest active state ends in the future, done this cycle
+ _time = end_beats;
+ break;
+ }
+
+ }
+
+ return time.beats_to_ticks(_time).ticks() - time.start_ticks().ticks();
+}
+
+void
+Machine::learn(SPtr<Raul::Maid> maid, SPtr<Node> node)
+{
+ _pending_learn = LearnRequest::create(maid, node);
+}
+
+void
+Machine::write_state(Sord::Model& model)
+{
+ using namespace Raul;
+
+ model.add_statement(model.base_uri(),
+ Sord::URI(model.world(), MACHINA_URI_RDF "type"),
+ Sord::URI(model.world(), MACHINA_NS_Machine));
+
+ for (Nodes::const_iterator n = _nodes.begin(); n != _nodes.end(); ++n) {
+ (*n)->write_state(model);
+
+ if ((*n)->is_initial()) {
+ model.add_statement(model.base_uri(),
+ Sord::URI(model.world(), MACHINA_NS_start),
+ (*n)->rdf_id(model.world()));
+ } else {
+ model.add_statement(model.base_uri(),
+ Sord::URI(model.world(), MACHINA_NS_node),
+ (*n)->rdf_id(model.world()));
+ }
+ }
+
+ for (Nodes::const_iterator n = _nodes.begin(); n != _nodes.end(); ++n) {
+ for (Node::Edges::const_iterator e = (*n)->edges().begin();
+ e != (*n)->edges().end(); ++e) {
+
+ (*e)->write_state(model);
+
+ model.add_statement(model.base_uri(),
+ Sord::URI(model.world(), MACHINA_NS_arc),
+ (*e)->rdf_id(model.world()));
+ }
+
+ }
+}
+
+} // namespace machina
diff --git a/src/engine/MachineBuilder.cpp b/src/engine/MachineBuilder.cpp
new file mode 100644
index 0000000..d812b06
--- /dev/null
+++ b/src/engine/MachineBuilder.cpp
@@ -0,0 +1,325 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "lv2/lv2plug.in/ns/ext/midi/midi.h"
+
+#include "machina/Machine.hpp"
+#include "machina/types.hpp"
+
+#include "Edge.hpp"
+#include "MachineBuilder.hpp"
+#include "MidiAction.hpp"
+#include "Node.hpp"
+#include "quantize.hpp"
+
+using namespace std;
+using namespace Raul;
+
+namespace machina {
+
+MachineBuilder::MachineBuilder(SPtr<Machine> machine, double q, bool step)
+ : _quantization(q)
+ , _time(machine->time().unit()) // = 0
+ , _machine(machine)
+ , _initial_node(machine->initial_node()) // duration 0
+ , _connect_node(_initial_node)
+ , _connect_node_end_time(_time) // = 0
+ , _step_duration(_time.unit(), q)
+ , _step(step)
+{}
+
+void
+MachineBuilder::reset()
+{
+ _time = TimeStamp(_machine->time().unit()); // = 0
+ _connect_node = _initial_node;
+ _connect_node_end_time = _time; // = 0
+}
+
+bool
+MachineBuilder::is_delay_node(SPtr<Node> node) const
+{
+ return node != _initial_node &&
+ !node->enter_action() &&
+ !node->exit_action();
+}
+
+/** Set the duration of a node, with quantization.
+ */
+void
+MachineBuilder::set_node_duration(SPtr<Node> node,
+ Raul::TimeDuration d) const
+{
+ if (_step) {
+ node->set_duration(_step_duration);
+ return;
+ }
+
+ Raul::TimeStamp q_dur = quantize(TimeStamp(d.unit(), _quantization), d);
+
+ // Never quantize a note to duration 0
+ if (q_dur.is_zero() && (node->enter_action() || node->exit_action())) {
+ q_dur = _quantization; // Round up
+ }
+ node->set_duration(q_dur);
+}
+
+/** Connect two nodes, inserting a delay node between them if necessary.
+ *
+ * If a delay node is added to the machine, it is returned.
+ */
+SPtr<Node>
+MachineBuilder::connect_nodes(SPtr<Machine> m,
+ SPtr<Node> tail,
+ Raul::TimeStamp tail_end_time,
+ SPtr<Node> head,
+ Raul::TimeStamp head_start_time)
+{
+ SPtr<Node> delay_node;
+ if (tail == head) {
+ return delay_node;
+ }
+
+ if (is_delay_node(tail) && tail->edges().empty()) {
+ // Tail is a delay node, just accumulate the time difference into it
+ set_node_duration(tail,
+ tail->duration() + head_start_time - tail_end_time);
+ tail->add_edge(SPtr<Edge>(new Edge(tail, head)));
+ } else if (_step || (head_start_time == tail_end_time)) {
+ // Connect directly
+ tail->add_edge(SPtr<Edge>(new Edge(tail, head)));
+ } else {
+ // Need to actually create a delay node
+ delay_node = SPtr<Node>(new Node(head_start_time - tail_end_time));
+ tail->add_edge(SPtr<Edge>(new Edge(tail, delay_node)));
+ delay_node->add_edge(SPtr<Edge>(new Edge(delay_node, head)));
+ m->add_node(delay_node);
+ }
+
+ return delay_node;
+}
+
+void
+MachineBuilder::note_on(Raul::TimeStamp t, size_t ev_size, uint8_t* buf)
+{
+ SPtr<Node> node;
+ if (_step && _poly_nodes.empty() && is_delay_node(_connect_node)) {
+ /* Stepping and the connect node is the merge node after a polyphonic
+ group. Re-use it to avoid creating delay nodes in step mode. */
+ node = _connect_node;
+ node->set_duration(_step_duration);
+ } else {
+ node = SPtr<Node>(new Node(default_duration()));
+ }
+
+ node->set_enter_action(SPtr<Action>(new MidiAction(ev_size, buf)));
+
+ if (_step && _poly_nodes.empty()) {
+ t = _time = _time + _step_duration; // Advance time one step
+ }
+
+ SPtr<Node> this_connect_node;
+ Raul::TimeStamp this_connect_node_end_time(t.unit());
+
+ /* If currently polyphonic, use a poly node with no successors as connect
+ node, for more sensible patterns like what a human would build. */
+ if (!_poly_nodes.empty()) {
+ for (PolyList::iterator j = _poly_nodes.begin();
+ j != _poly_nodes.end(); ++j) {
+ if (j->second->edges().empty()) {
+ this_connect_node = j->second;
+ this_connect_node_end_time = j->first + j->second->duration();
+ break;
+ }
+ }
+ }
+
+ /* Currently monophonic, or didn't find a poly node, so use _connect_node
+ which is maintained below on note off events. */
+ if (!this_connect_node) {
+ this_connect_node = _connect_node;
+ this_connect_node_end_time = _connect_node_end_time;
+ }
+
+ SPtr<Node> delay_node = connect_nodes(
+ _machine, this_connect_node, this_connect_node_end_time, node, t);
+
+ if (delay_node) {
+ _connect_node = delay_node;
+ _connect_node_end_time = t;
+ }
+
+ node->enter(NULL, t);
+ _active_nodes.push_back(node);
+}
+
+void
+MachineBuilder::resolve_note(Raul::TimeStamp time,
+ size_t ev_size,
+ uint8_t* buf,
+ SPtr<Node> resolved)
+{
+ resolved->set_exit_action(SPtr<Action>(new MidiAction(ev_size, buf)));
+
+ if (_active_nodes.size() == 1) {
+ if (_step) {
+ time = _time = _time + _step_duration;
+ }
+
+ // Last active note
+ _connect_node_end_time = time;
+
+ if (!_poly_nodes.empty()) {
+ // Finish a polyphonic section
+ _connect_node = SPtr<Node>(new Node(TimeStamp(_time.unit(), 0, 0)));
+ _machine->add_node(_connect_node);
+
+ connect_nodes(_machine, resolved, time, _connect_node, time);
+
+ for (PolyList::iterator j = _poly_nodes.begin();
+ j != _poly_nodes.end(); ++j) {
+ _machine->add_node(j->second);
+ if (j->second->edges().empty()) {
+ connect_nodes(_machine, j->second,
+ j->first + j->second->duration(),
+ _connect_node, time);
+ }
+ }
+ _poly_nodes.clear();
+
+ _machine->add_node(resolved);
+
+ } else {
+ // Just monophonic
+ if (is_delay_node(_connect_node)
+ && _connect_node->duration().is_zero()
+ && (_connect_node->edges().size() == 1)
+ && ((*_connect_node->edges().begin())->head() == resolved)) {
+ // Trim useless delay node if possible (after poly sections)
+
+ _connect_node->edges().clear();
+ _connect_node->set_enter_action(resolved->enter_action());
+ _connect_node->set_exit_action(resolved->exit_action());
+ resolved->set_enter_action(SPtr<Action>());
+ resolved->set_exit_action(SPtr<Action>());
+ set_node_duration(_connect_node, resolved->duration());
+ resolved = _connect_node;
+ _machine->add_node(_connect_node);
+
+ } else {
+ _connect_node = resolved;
+ _machine->add_node(resolved);
+ }
+ }
+
+ } else {
+ // Polyphonic, add this node to poly list
+ _poly_nodes.push_back(make_pair(resolved->enter_time(), resolved));
+ _connect_node = resolved;
+ _connect_node_end_time = _time;
+ }
+
+ if (resolved->is_active()) {
+ resolved->exit(NULL, _time);
+ }
+}
+
+void
+MachineBuilder::event(Raul::TimeStamp time,
+ size_t ev_size,
+ uint8_t* buf)
+{
+ if (ev_size == 0) {
+ return;
+ }
+
+ if (!_step) {
+ _time = time;
+ }
+
+ if ((buf[0] & 0xF0) == LV2_MIDI_MSG_NOTE_ON) {
+ note_on(time, ev_size, buf);
+ } else if ((buf[0] & 0xF0) == LV2_MIDI_MSG_NOTE_OFF) {
+ for (ActiveList::iterator i = _active_nodes.begin();
+ i != _active_nodes.end(); ++i) {
+ SPtr<MidiAction> action = dynamic_ptr_cast<MidiAction>(
+ (*i)->enter_action());
+ if (!action) {
+ continue;
+ }
+
+ const size_t ev_size = action->event_size();
+ const uint8_t* ev = action->event();
+
+ if ((ev[0] & 0xF0) == LV2_MIDI_MSG_NOTE_ON &&
+ (ev[0] & 0x0F) == (buf[0] & 0x0F) &&
+ ev[1] == buf[1]) {
+ // Same channel and note as on event
+ resolve_note(time, ev_size, buf, *i);
+ _active_nodes.erase(i);
+ break;
+ }
+ }
+ }
+}
+
+/** Finish the constructed machine and prepare it for use.
+ * Resolve any stuck notes, quantize, etc.
+ */
+void
+MachineBuilder::resolve()
+{
+ // Resolve stuck notes
+ if (!_active_nodes.empty()) {
+ for (list<SPtr<Node> >::iterator i = _active_nodes.begin();
+ i != _active_nodes.end(); ++i) {
+ cerr << "WARNING: Resolving stuck note." << endl;
+ SPtr<MidiAction> action = dynamic_ptr_cast<MidiAction>(
+ (*i)->enter_action());
+ if (!action) {
+ continue;
+ }
+
+ const size_t ev_size = action->event_size();
+ const uint8_t* ev = action->event();
+ if (ev_size == 3 && (ev[0] & 0xF0) == LV2_MIDI_MSG_NOTE_ON) {
+ uint8_t st((LV2_MIDI_MSG_NOTE_OFF & 0xF0) | (ev[0] & 0x0F));
+ const uint8_t note_off[3] = { st, ev[1], 0x40 };
+ (*i)->set_exit_action(
+ SPtr<Action>(new MidiAction(3, note_off)));
+ set_node_duration((*i), _time - (*i)->enter_time());
+ (*i)->exit(NULL, _time);
+ _machine->add_node((*i));
+ }
+ }
+ _active_nodes.clear();
+ }
+
+ // Add initial node if necessary
+ if (!_machine->nodes().empty()) {
+ _machine->add_node(_initial_node);
+ }
+}
+
+SPtr<Machine>
+MachineBuilder::finish()
+{
+ resolve();
+
+ return _machine;
+}
+
+} // namespace machina
diff --git a/src/engine/MachineBuilder.hpp b/src/engine/MachineBuilder.hpp
new file mode 100644
index 0000000..83baf78
--- /dev/null
+++ b/src/engine/MachineBuilder.hpp
@@ -0,0 +1,87 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_MACHINEBUILDER_HPP
+#define MACHINA_MACHINEBUILDER_HPP
+
+#include <stdint.h>
+
+#include <list>
+
+#include "machina/types.hpp"
+#include "raul/TimeStamp.hpp"
+
+namespace machina {
+
+class Machine;
+class Node;
+
+class MachineBuilder
+{
+public:
+ MachineBuilder(SPtr<Machine> machine,
+ double quantization,
+ bool step);
+
+ void event(Raul::TimeStamp time, size_t size, unsigned char* buf);
+
+ void set_step(bool step) { _step = step; }
+
+ void reset();
+ void resolve();
+
+ SPtr<Machine> finish();
+
+private:
+ bool is_delay_node(SPtr<Node> node) const;
+ void set_node_duration(SPtr<Node> node, Raul::TimeDuration d) const;
+
+ void note_on(Raul::TimeStamp t, size_t ev_size, uint8_t* buf);
+
+ void resolve_note(Raul::TimeStamp t,
+ size_t ev_size,
+ uint8_t* buf,
+ SPtr<Node> resolved);
+
+ SPtr<Node>connect_nodes(SPtr<Machine> m,
+ SPtr<Node> tail,
+ Raul::TimeStamp tail_end_time,
+ SPtr<Node> head,
+ Raul::TimeStamp head_start_time);
+
+ Raul::TimeStamp default_duration() {
+ return _step ? _step_duration : Raul::TimeStamp(_time.unit(), 0, 0);
+ }
+
+ typedef std::list<SPtr<Node> > ActiveList;
+ ActiveList _active_nodes;
+
+ typedef std::list<std::pair<Raul::TimeStamp, SPtr<Node> > > PolyList;
+ PolyList _poly_nodes;
+
+ double _quantization;
+ Raul::TimeStamp _time;
+ SPtr<Machine> _machine;
+ SPtr<Node> _initial_node;
+ SPtr<Node> _connect_node;
+ Raul::TimeStamp _connect_node_end_time;
+ Raul::TimeStamp _step_duration;
+ bool _step;
+};
+
+} // namespace machina
+
+#endif // MACHINA_MACHINEBUILDER_HPP
diff --git a/src/engine/MidiAction.cpp b/src/engine/MidiAction.cpp
new file mode 100644
index 0000000..b5df155
--- /dev/null
+++ b/src/engine/MidiAction.cpp
@@ -0,0 +1,103 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "lv2/lv2plug.in/ns/ext/midi/midi.h"
+#include "machina/Atom.hpp"
+#include "machina/URIs.hpp"
+#include "machina/types.hpp"
+
+#include "MIDISink.hpp"
+#include "MidiAction.hpp"
+
+namespace machina {
+
+/** Create a MIDI action.
+ *
+ * Creating a NULL MidiAction is okay, pass event=NULL and the action will
+ * simply do nothing until a set_event (for MIDI learning).
+ */
+MidiAction::MidiAction(size_t size,
+ const byte* event)
+ : _size(0)
+ , _max_size(size)
+{
+ _event = new byte[_max_size];
+ set_event(size, event);
+}
+
+MidiAction::~MidiAction()
+{
+ delete[] _event.load();
+}
+
+bool
+MidiAction::set_event(size_t size, const byte* new_event)
+{
+ byte* const event = _event.load();
+ if (size <= _max_size) {
+ _event = NULL;
+ if (size > 0 && new_event) {
+ memcpy(event, new_event, size);
+ }
+ _size = size;
+ _event = event;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void
+MidiAction::execute(MIDISink* sink, Raul::TimeStamp time)
+{
+ const byte* const ev = _event.load();
+ if (ev && sink) {
+ sink->write_event(time, _size, ev);
+ }
+}
+
+void
+MidiAction::write_state(Sord::Model& model)
+{
+ const uint8_t* ev = event();
+ const uint8_t type = (ev[0] & 0xF0);
+ if (type == LV2_MIDI_MSG_NOTE_ON) {
+ model.add_statement(
+ rdf_id(model.world()),
+ Sord::URI(model.world(), MACHINA_URI_RDF "type"),
+ Sord::URI(model.world(), LV2_MIDI__NoteOn));
+ } else if (type == LV2_MIDI_MSG_NOTE_OFF) {
+ model.add_statement(
+ rdf_id(model.world()),
+ Sord::URI(model.world(), MACHINA_URI_RDF "type"),
+ Sord::URI(model.world(), LV2_MIDI__NoteOff));
+ } else {
+ std::cerr << "warning: Unable to serialise MIDI event" << std::endl;
+ }
+
+ model.add_statement(
+ rdf_id(model.world()),
+ Sord::URI(model.world(), LV2_MIDI__noteNumber),
+ Sord::Literal::integer(model.world(), (int)ev[1]));
+ if (ev[2] != 64) {
+ model.add_statement(
+ rdf_id(model.world()),
+ Sord::URI(model.world(), LV2_MIDI__velocity),
+ Sord::Literal::integer(model.world(), (int)ev[2]));
+ }
+}
+
+} // namespace machina
diff --git a/src/engine/MidiAction.hpp b/src/engine/MidiAction.hpp
new file mode 100644
index 0000000..64a2c65
--- /dev/null
+++ b/src/engine/MidiAction.hpp
@@ -0,0 +1,57 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_MIDIACTION_HPP
+#define MACHINA_MIDIACTION_HPP
+
+#include <atomic>
+
+#include "raul/TimeSlice.hpp"
+
+#include "machina/types.hpp"
+
+#include "Action.hpp"
+
+namespace machina {
+
+class MIDISink;
+
+class MidiAction : public Action
+{
+public:
+ ~MidiAction();
+
+ MidiAction(size_t size,
+ const unsigned char* event);
+
+ size_t event_size() { return _size; }
+ byte* event() { return _event.load(); }
+
+ bool set_event(size_t size, const byte* event);
+
+ void execute(MIDISink* sink, Raul::TimeStamp time);
+
+ virtual void write_state(Sord::Model& model);
+
+private:
+ size_t _size;
+ const size_t _max_size;
+ std::atomic<byte*> _event;
+};
+
+} // namespace machina
+
+#endif // MACHINA_MIDIACTION_HPP
diff --git a/src/engine/Mutation.cpp b/src/engine/Mutation.cpp
new file mode 100644
index 0000000..776142d
--- /dev/null
+++ b/src/engine/Mutation.cpp
@@ -0,0 +1,190 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <iostream>
+#include <cstdlib>
+
+#include "machina/Machine.hpp"
+#include "machina/Mutation.hpp"
+
+#include "ActionFactory.hpp"
+#include "Edge.hpp"
+#include "MidiAction.hpp"
+
+using namespace std;
+
+namespace machina {
+namespace Mutation {
+
+void
+Compress::mutate(Random& rng, Machine& machine)
+{
+ //cout << "COMPRESS" << endl;
+
+ // Trim disconnected nodes
+ for (Machine::Nodes::iterator i = machine.nodes().begin();
+ i != machine.nodes().end(); ) {
+ Machine::Nodes::iterator next = i;
+ ++next;
+
+ if ((*i)->edges().empty()) {
+ machine.remove_node(*i);
+ }
+
+ i = next;
+ }
+}
+
+void
+AddNode::mutate(Random& rng, Machine& machine)
+{
+ //cout << "ADD NODE" << endl;
+
+ // Create random node
+ SPtr<Node> node(new Node(machine.time().unit()));
+ node->set_selector(true);
+
+ SPtr<Node> note_node = machine.random_node();
+ if (!note_node) {
+ return;
+ }
+
+ uint8_t note = rand() % 128;
+
+ SPtr<MidiAction> enter_action = dynamic_ptr_cast<MidiAction>(
+ note_node->enter_action());
+ if (enter_action) {
+ note = enter_action->event()[1];
+ }
+
+ node->set_enter_action(ActionFactory::note_on(note));
+ node->set_exit_action(ActionFactory::note_off(note));
+ machine.add_node(node);
+
+ // Insert after some node
+ SPtr<Node> tail = machine.random_node();
+ if (tail && (tail != node) /* && !node->connected_to(tail)*/) {
+ tail->add_edge(SPtr<Edge>(new Edge(tail, node)));
+ }
+
+ // Insert before some other node
+ SPtr<Node> head = machine.random_node();
+ if (head && (head != node) /* && !head->connected_to(node)*/) {
+ node->add_edge(SPtr<Edge>(new Edge(node, head)));
+ }
+}
+
+void
+RemoveNode::mutate(Random& rng, Machine& machine)
+{
+ //cout << "REMOVE NODE" << endl;
+
+ SPtr<Node> node = machine.random_node();
+ if (node && !node->is_initial()) {
+ machine.remove_node(node);
+ }
+}
+
+void
+AdjustNode::mutate(Random& rng, Machine& machine)
+{
+ //cout << "ADJUST NODE" << endl;
+
+ SPtr<Node> node = machine.random_node();
+ if (node) {
+ SPtr<MidiAction> enter_action = dynamic_ptr_cast<MidiAction>(
+ node->enter_action());
+ SPtr<MidiAction> exit_action = dynamic_ptr_cast<MidiAction>(
+ node->exit_action());
+ if (enter_action && exit_action) {
+ const uint8_t note = rand() % 128;
+ enter_action->event()[1] = note;
+ exit_action->event()[1] = note;
+ }
+ node->set_changed();
+ }
+}
+
+void
+SwapNodes::mutate(Random& rng, Machine& machine)
+{
+ //cout << "SWAP NODE" << endl;
+
+ if (machine.nodes().size() <= 1) {
+ return;
+ }
+
+ SPtr<Node> a = machine.random_node();
+ SPtr<Node> b = machine.random_node();
+ while (b == a) {
+ b = machine.random_node();
+ }
+
+ SPtr<MidiAction> a_enter = dynamic_ptr_cast<MidiAction>(a->enter_action());
+ SPtr<MidiAction> a_exit = dynamic_ptr_cast<MidiAction>(a->exit_action());
+ SPtr<MidiAction> b_enter = dynamic_ptr_cast<MidiAction>(b->enter_action());
+ SPtr<MidiAction> b_exit = dynamic_ptr_cast<MidiAction>(b->exit_action());
+
+ uint8_t note_a = a_enter->event()[1];
+ uint8_t note_b = b_enter->event()[1];
+
+ a_enter->event()[1] = note_b;
+ a_exit->event()[1] = note_b;
+ b_enter->event()[1] = note_a;
+ b_exit->event()[1] = note_a;
+}
+
+void
+AddEdge::mutate(Random& rng, Machine& machine)
+{
+ //cout << "ADJUST EDGE" << endl;
+
+ SPtr<Node> tail = machine.random_node();
+ SPtr<Node> head = machine.random_node();
+
+ if (tail && head && tail != head) {
+ // && !tail->connected_to(head) && !head->connected_to(tail)
+ SPtr<Edge> edge(new Edge(tail, head));
+ edge->set_probability(rand() / (float)RAND_MAX);
+ tail->add_edge(SPtr<Edge>(new Edge(tail, head)));
+ }
+}
+
+void
+RemoveEdge::mutate(Random& rng, Machine& machine)
+{
+ //cout << "REMOVE EDGE" << endl;
+
+ SPtr<Node> tail = machine.random_node();
+ if (tail && !(tail->is_initial() && tail->edges().size() == 1)) {
+ tail->remove_edge(tail->random_edge());
+ }
+}
+
+void
+AdjustEdge::mutate(Random& rng, Machine& machine)
+{
+ //cout << "ADJUST EDGE" << endl;
+
+ SPtr<Edge> edge = machine.random_edge();
+ if (edge) {
+ edge->set_probability(rand() / (float)RAND_MAX);
+ edge->tail().lock()->edges_changed();
+ }
+}
+
+} // namespace Mutation
+} // namespace machina
diff --git a/src/engine/Node.cpp b/src/engine/Node.cpp
new file mode 100644
index 0000000..7d8a870
--- /dev/null
+++ b/src/engine/Node.cpp
@@ -0,0 +1,271 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <cassert>
+#include <iostream>
+
+#include "sord/sordmm.hpp"
+
+#include "machina/Atom.hpp"
+#include "machina/URIs.hpp"
+
+#include "ActionFactory.hpp"
+#include "Edge.hpp"
+#include "Node.hpp"
+
+using namespace Raul;
+using namespace std;
+
+namespace machina {
+
+Node::Node(TimeDuration duration, bool initial)
+ : _enter_time(duration.unit())
+ , _duration(duration)
+ , _is_initial(initial)
+ , _is_selector(false)
+ , _is_active(false)
+{}
+
+Node::Node(const Node& copy)
+ : Stateful() // don't copy RDF ID
+ , _enter_time(copy._enter_time)
+ , _duration(copy._duration)
+ , _enter_action(ActionFactory::copy(copy._enter_action))
+ , _exit_action(ActionFactory::copy(copy._exit_action))
+ , _is_initial(copy._is_initial)
+ , _is_selector(copy._is_selector)
+ , _is_active(false)
+{
+ for (Edges::const_iterator i = copy._edges.begin(); i != copy._edges.end();
+ ++i) {
+ SPtr<Edge> edge(new Edge(*i->get()));
+ _edges.insert(edge);
+ }
+}
+
+static inline bool
+action_equals(SPtr<const Action> a, SPtr<const Action> b)
+{
+ return (a == b) || (a && b && *a.get() == *b.get());
+}
+
+bool
+Node::operator==(const Node& rhs) const
+{
+ return _duration == rhs._duration &&
+ _is_initial == rhs._is_initial &&
+ _is_selector == rhs._is_selector &&
+ _is_active == rhs._is_active &&
+ action_equals(_enter_action, rhs.enter_action()) &&
+ action_equals(_exit_action, rhs.exit_action());
+ // TODO: compare edges
+}
+
+/** Always returns an edge, unless there are none */
+SPtr<Edge>
+Node::random_edge()
+{
+ SPtr<Edge> ret;
+ if (_edges.empty()) {
+ return ret;
+ }
+
+ size_t i = rand() % _edges.size();
+
+ // FIXME: O(n) worst case :(
+ for (Edges::const_iterator e = _edges.begin(); e != _edges.end(); ++e,
+ --i) {
+ if (i == 0) {
+ ret = *e;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+void
+Node::edges_changed()
+{
+ if (!_is_selector) {
+ return;
+ }
+
+ // Normalize edge probabilities if we're a selector
+ double prob_sum = 0;
+
+ for (Edges::iterator i = _edges.begin(); i != _edges.end(); ++i) {
+ prob_sum += (*i)->probability();
+ }
+
+ for (Edges::iterator i = _edges.begin(); i != _edges.end(); ++i) {
+ (*i)->set_probability((*i)->probability() / prob_sum);
+ }
+
+ _changed = true;
+}
+
+void
+Node::set_selector(bool yn)
+{
+ _is_selector = yn;
+
+ if (yn) {
+ edges_changed();
+ }
+
+ _changed = true;
+}
+
+void
+Node::set_enter_action(SPtr<Action> action)
+{
+ _enter_action = action;
+ _changed = true;
+}
+
+void
+Node::set_exit_action(SPtr<Action> action)
+{
+ _exit_action = action;
+ _changed = true;
+}
+
+void
+Node::enter(MIDISink* sink, TimeStamp time)
+{
+ if (!_is_active) {
+ _changed = true;
+ _is_active = true;
+ _enter_time = time;
+
+ if (sink && _enter_action) {
+ _enter_action->execute(sink, time);
+ }
+ }
+}
+
+void
+Node::exit(MIDISink* sink, TimeStamp time)
+{
+ if (_is_active) {
+ if (sink && _exit_action) {
+ _exit_action->execute(sink, time);
+ }
+
+ _changed = true;
+ _is_active = false;
+ _enter_time = 0;
+ }
+}
+
+SPtr<Edge>
+Node::edge_to(SPtr<Node> head) const
+{
+ // TODO: Make logarithmic
+ for (Edges::const_iterator i = _edges.begin(); i != _edges.end(); ++i) {
+ if ((*i)->head() == head) {
+ return *i;
+ }
+ }
+ return SPtr<Edge>();
+}
+
+void
+Node::add_edge(SPtr<Edge> edge)
+{
+ assert(edge->tail().lock().get() == this);
+ if (edge_to(edge->head())) {
+ return;
+ }
+
+ _edges.insert(edge);
+ edges_changed();
+}
+
+void
+Node::remove_edge(SPtr<Edge> edge)
+{
+ _edges.erase(edge);
+ edges_changed();
+}
+
+bool
+Node::connected_to(SPtr<Node> node)
+{
+ return bool(edge_to(node));
+}
+
+SPtr<Edge>
+Node::remove_edge_to(SPtr<Node> node)
+{
+ SPtr<Edge> edge = edge_to(node);
+ if (edge) {
+ _edges.erase(edge); // TODO: avoid double search
+ edges_changed();
+ }
+ return edge;
+}
+
+void
+Node::set(URIInt key, const Atom& value)
+{
+ if (key == URIs::instance().machina_initial) {
+ std::cerr << "error: Attempt to change node initial state" << std::endl;
+ } else if (key == URIs::instance().machina_selector) {
+ set_selector(value.get<int32_t>());
+ }
+}
+
+void
+Node::write_state(Sord::Model& model)
+{
+ using namespace Raul;
+
+ const Sord::Node& rdf_id = this->rdf_id(model.world());
+
+ if (_is_selector)
+ model.add_statement(
+ rdf_id,
+ Sord::URI(model.world(), MACHINA_URI_RDF "type"),
+ Sord::URI(model.world(), MACHINA_NS_SelectorNode));
+ else
+ model.add_statement(
+ rdf_id,
+ Sord::URI(model.world(), MACHINA_URI_RDF "type"),
+ Sord::URI(model.world(), MACHINA_NS_Node));
+
+ model.add_statement(
+ rdf_id,
+ Sord::URI(model.world(), MACHINA_NS_duration),
+ Sord::Literal::decimal(model.world(), _duration.to_double(), 7));
+
+ if (_enter_action) {
+ _enter_action->write_state(model);
+ model.add_statement(rdf_id,
+ Sord::URI(model.world(), MACHINA_NS_onEnter),
+ _enter_action->rdf_id(model.world()));
+ }
+
+ if (_exit_action) {
+ _exit_action->write_state(model);
+ model.add_statement(rdf_id,
+ Sord::URI(model.world(), MACHINA_NS_onExit),
+ _exit_action->rdf_id(model.world()));
+ }
+}
+
+} // namespace machina
diff --git a/src/engine/Node.hpp b/src/engine/Node.hpp
new file mode 100644
index 0000000..cae75f5
--- /dev/null
+++ b/src/engine/Node.hpp
@@ -0,0 +1,116 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_NODE_HPP
+#define MACHINA_NODE_HPP
+
+#include <set>
+
+#include "machina/types.hpp"
+
+#include "Action.hpp"
+#include "MIDISink.hpp"
+#include "Schrodinbit.hpp"
+#include "Stateful.hpp"
+
+namespace machina {
+
+class Edge;
+using Raul::TimeDuration;
+using Raul::TimeStamp;
+using Raul::TimeUnit;
+
+/** A node is a state (as in a FSM diagram), or "note".
+ *
+ * It contains a action, as well as a duration and pointers to its
+ * successors (states/nodes that (may) follow it).
+ *
+ * Initial nodes do not have enter actions (since they are entered at
+ * an undefined point in time <= 0).
+ */
+class Node : public Stateful
+{
+public:
+ Node(TimeDuration duration, bool initial=false);
+ Node(const Node& copy);
+
+ bool operator==(const Node& rhs) const;
+
+ void set_enter_action(SPtr<Action> action);
+ void set_exit_action(SPtr<Action> action);
+
+ SPtr<const Action> enter_action() const { return _enter_action; }
+ SPtr<Action> enter_action() { return _enter_action; }
+ SPtr<const Action> exit_action() const { return _exit_action; }
+ SPtr<Action> exit_action() { return _exit_action; }
+
+ void enter(MIDISink* sink, TimeStamp time);
+ void exit(MIDISink* sink, TimeStamp time);
+
+ void edges_changed();
+
+ void add_edge(SPtr<Edge> edge);
+ void remove_edge(SPtr<Edge> edge);
+ SPtr<Edge> remove_edge_to(SPtr<Node> node);
+ bool connected_to(SPtr<Node> node);
+
+ void set(URIInt key, const Atom& value);
+ void write_state(Sord::Model& model);
+
+ bool is_initial() const { return _is_initial; }
+ bool is_active() const { return _is_active; }
+ TimeStamp enter_time() const { return _enter_time; }
+ TimeStamp exit_time() const { return _enter_time + _duration; }
+ TimeDuration duration() const { return _duration; }
+ void set_duration(TimeDuration d) { _duration = d; }
+ bool is_selector() const { return _is_selector; }
+ void set_selector(bool i);
+
+ inline bool changed() { return _changed; }
+ inline void set_changed() { _changed = true; }
+
+ struct EdgeHeadOrder {
+ inline bool operator()(const SPtr<const Edge>& a,
+ const SPtr<const Edge>& b) {
+ return a.get() < b.get();
+ }
+ };
+
+ SPtr<Edge> edge_to(SPtr<Node> head) const;
+
+ typedef std::set<SPtr<Edge>, EdgeHeadOrder> Edges;
+
+ Edges& edges() { return _edges; }
+
+ SPtr<Edge> random_edge();
+
+private:
+ Node& operator=(const Node& other); // undefined
+
+ TimeStamp _enter_time; ///< valid iff _is_active
+ TimeDuration _duration;
+ SPtr<Action> _enter_action;
+ SPtr<Action> _exit_action;
+ Edges _edges;
+ Schrodinbit _changed;
+ bool _is_initial;
+ bool _is_selector;
+ bool _is_active;
+};
+
+} // namespace machina
+
+#endif // MACHINA_NODE_HPP
diff --git a/src/engine/Problem.cpp b/src/engine/Problem.cpp
new file mode 100644
index 0000000..3b05fed
--- /dev/null
+++ b/src/engine/Problem.cpp
@@ -0,0 +1,383 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#define __STDC_LIMIT_MACROS 1
+
+#include <stdint.h>
+
+#include <set>
+#include <vector>
+#include <iostream>
+
+#include "lv2/lv2plug.in/ns/ext/midi/midi.h"
+
+#include "eugene/Problem.hpp"
+
+#include "machina/Machine.hpp"
+
+#include "ActionFactory.hpp"
+#include "Edge.hpp"
+#include "MidiAction.hpp"
+#include "Problem.hpp"
+#include "SMFReader.hpp"
+#include "machina_config.h"
+
+using namespace std;
+
+namespace machina {
+
+Problem::Problem(TimeUnit unit,
+ const std::string& target_midi,
+ SPtr<Machine> seed)
+ : _unit(unit)
+ , _target(*this)
+ , _seed(new Machine(*seed.get()))
+{
+ SMFReader smf;
+ const bool opened = smf.open(target_midi);
+ assert(opened);
+
+ smf.seek_to_track(2); // FIXME: ?
+
+ uint8_t buf[4];
+ uint32_t ev_size;
+ uint32_t delta_time;
+ while (smf.read_event(4, buf, &ev_size, &delta_time) >= 0) {
+ // time ignored
+ _target.write_event(TimeStamp(_unit, 0.0), ev_size, buf);
+#if 0
+ //_target._length += delta_time / (double)smf.ppqn();
+ if ((buf[0] & 0xF0) == MIDI_CMD_NOTE_ON) {
+ const uint8_t note = buf[1];
+ /*++_target._note_frequency[note];
+ ++_target.n_notes();*/
+ ++_target._counts[note];
+ _target._notes.push_back(note);
+ }
+#endif
+ }
+
+ cout << "Target notes: " << _target.n_notes() << endl;
+
+ _target.compute();
+}
+
+float
+Problem::evaluate(const Machine& const_machine) const
+{
+ #if 0
+ //cout << "(";
+
+ // kluuudge
+ Machine& machine = const_cast<Machine&>(const_machine);
+
+ map<Machine*, float>::const_iterator cached = _fitness.find(&machine);
+ if (cached != _fitness.end()) {
+ return cached->second;
+ }
+
+ SPtr<Evaluator> eval(new Evaluator(*this));
+
+ //machine.reset();
+ machine.set_sink(eval);
+
+ // FIXME: timing stuff here isn't right at all...
+
+ static const unsigned ppqn = MACHINA_PPQN;
+ Raul::TimeSlice time(ppqn, ppqn, 120.0);
+ time.set_slice(TimeStamp(_unit, 0, 0), TimeDuration(_unit, 2 * ppqn));
+
+ machine.run(time);
+ if (eval->n_notes() == 0) {
+ return 0.0f; // bad dog
+ }
+ TimeStamp end(_unit, time.start_ticks().ticks() + 2 * ppqn);
+ time.set_slice(end, TimeStamp(_unit, 0, 0));
+
+ while (eval->n_notes() < _target.n_notes()) {
+ machine.run(time);
+ if (machine.is_finished()) {
+ machine.reset(time.start_ticks());
+ }
+ time.set_slice(end, TimeStamp(end.unit(), 0, 0));
+ }
+
+ eval->compute();
+
+ // count
+#if 0
+ float f = 0;
+
+ for (uint8_t i = 0; i < 128; ++i) {
+ /*if (eval->_note_frequency[i] <= _target._note_frequency[i])
+ f += eval->_note_frequency[i];
+ else
+ f -= _target._note_frequency[i] - eval->_note_frequency[i];*/
+ //f -= fabs(eval->_note_frequency[i] - _target._note_frequency[i]);
+ }
+#endif
+ //cout << ")";
+
+ // distance
+ //float f = distance(eval->_notes, _target._notes);
+
+ float f = 0.0;
+
+ for (Evaluator::Patterns::const_iterator i = eval->_patterns.begin();
+ i != eval->_patterns.end(); ++i) {
+ // Reward for matching patterns
+ if (_target._patterns.find(i->first) != _target._patterns.end()) {
+ Evaluator::Patterns::const_iterator c = _target._patterns.find(
+ i->first);
+ const uint32_t cnt
+ = (c == _target._patterns.end()) ? 1 : c->second;
+ f += min(i->second, cnt) * (i->first.length());
+
+ } else {
+ // Punish for bad patterns
+ const uint32_t invlen = (eval->_order - i->first.length() + 1);
+ f -= (i->second / (float)eval->_patterns.size()
+ * (float)(invlen * invlen * invlen)) * 4;
+ }
+ }
+
+ // Punish for missing patterns
+ for (Evaluator::Patterns::const_iterator i = _target._patterns.begin();
+ i != _target._patterns.end(); ++i) {
+ if (eval->_patterns.find(i->first) == _target._patterns.end()) {
+ f -= i->second / (float)_target.n_notes()
+ * (float)(eval->_order - i->first.length() + 1);
+ }
+ }
+
+ //cout << "f = " << f << endl;
+
+ _fitness[&machine] = f;
+
+ return f;
+ #endif
+ return 0.0f;
+}
+
+void
+Problem::Evaluator::write_event(Raul::TimeStamp time,
+ size_t ev_size,
+ const uint8_t* ev) throw (std::logic_error)
+{
+ if ((ev[0] & 0xF0) == LV2_MIDI_MSG_NOTE_ON) {
+
+ const uint8_t note = ev[1];
+
+ if (_first_note == 0) {
+ _first_note = note;
+ }
+
+ /*++_note_frequency[note];
+ ++n_notes();*/
+ //_notes.push_back(note);
+ if (_read.length() == 0) {
+ _read = note;
+ return;
+ }
+ if (_read.length() == _order) {
+ _read = _read.substr(1);
+ }
+
+ _read = _read + (char)note;
+
+ for (size_t i = 0; i < _read.length(); ++i) {
+ const string pattern = _read.substr(i);
+ Patterns::iterator p = _patterns.find(pattern);
+ if (p != _patterns.end()) {
+ ++(p->second);
+ } else {
+ _patterns[pattern] = 1;
+ }
+ }
+
+ ++_counts[note];
+ ++_n_notes;
+
+ }
+}
+
+void
+Problem::Evaluator::compute()
+{
+ /*
+ for (uint8_t i=0; i < 128; ++i) {
+ if (_note_frequency[i] > 0) {
+ _note_frequency[i] /= (float)n_notes();
+ //cout << (int)i << ":\t" << _note_frequency[i] << endl;
+ }
+ }*/
+}
+
+void
+Problem::initial_population(eugene::Random& rng,
+ Population& pop,
+ size_t gene_size,
+ size_t pop_size) const
+{
+ // FIXME: ignores _seed and builds based on MIDI
+ // evolution of the visible machine would be nice..
+ SPtr<Machine> base = SPtr<Machine>(new Machine(_unit));
+ for (uint8_t i = 0; i < 128; ++i) {
+ if (_target._counts[i] > 0) {
+ //cout << "Initial note: " << (int)i << endl;
+ SPtr<Node> node(new Node(TimeDuration(_unit, 1 / 2.0)));
+ node->set_enter_action(ActionFactory::note_on(i));
+ node->set_exit_action(ActionFactory::note_off(i));
+ node->set_selector(true);
+ base->add_node(node);
+ }
+ }
+
+ for (size_t i = 0; i < pop_size; ++i) {
+ // FIXME: double copy
+ Machine m(*base.get());
+
+ set< SPtr<Node> > unreachable;
+
+ for (Machine::Nodes::iterator i = m.nodes().begin(); i != m.nodes().end();
+ ++i) {
+ if (dynamic_ptr_cast<MidiAction>((*i)->enter_action())->event()[1] ==
+ _target.first_note()) {
+ m.initial_node()->add_edge(
+ SPtr<Edge>(new Edge(m.initial_node(), *i)));
+ } else {
+ unreachable.insert(*i);
+ }
+ }
+
+ SPtr<Node> cur = m.initial_node();
+ unreachable.erase(cur);
+ SPtr<Node> head;
+
+ while (!unreachable.empty()) {
+ if (rand() % 2) {
+ head = m.random_node();
+ } else {
+ head = *unreachable.begin();
+ }
+
+ if (!head->connected_to(head) ) {
+ cur->add_edge(SPtr<Edge>(new Edge(cur, head)));
+ unreachable.erase(head);
+ cur = head;
+ }
+ }
+
+ pop.push_back(m);
+
+ /*cout << "initial # nodes: " << m.nodes().size();
+ cout << "initial fitness: " << fitness(m) << endl;*/
+ }
+}
+
+/** levenshtein distance (edit distance) */
+size_t
+Problem::distance(const std::vector<uint8_t>& source,
+ const std::vector<uint8_t>& target) const
+{
+ // Derived from http://www.merriampark.com/ldcpp.htm
+
+ assert(source.size() < UINT16_MAX);
+ assert(target.size() < UINT16_MAX);
+
+ // Step 1
+
+ const uint16_t n = source.size();
+ const uint16_t m = target.size();
+ if (n == 0) {
+ return m;
+ }
+ if (m == 0) {
+ return n;
+ }
+
+ _matrix.resize(n + 1);
+ for (uint16_t i = 0; i <= n; i++) {
+ _matrix[i].resize(m + 1);
+ }
+
+ // Step 2
+
+ for (uint16_t i = 0; i <= n; i++) {
+ _matrix[i][0] = i;
+ }
+
+ for (uint16_t j = 0; j <= m; j++) {
+ _matrix[0][j] = j;
+ }
+
+ // Step 3
+
+ for (uint16_t i = 1; i <= n; i++) {
+
+ const uint8_t s_i = source[i - 1];
+
+ // Step 4
+
+ for (uint16_t j = 1; j <= m; j++) {
+
+ const uint8_t t_j = target[j - 1];
+
+ // Step 5
+
+ uint16_t cost;
+ if (s_i == t_j) {
+ cost = 0;
+ } else {
+ cost = 1;
+ }
+
+ // Step 6
+
+ const uint16_t above = _matrix[i - 1][j];
+ const uint16_t left = _matrix[i][j - 1];
+ const uint16_t diag = _matrix[i - 1][j - 1];
+ uint16_t cell = min(above + 1, min(left + 1, diag + cost));
+
+ // Step 6A: Cover transposition, in addition to deletion,
+ // insertion and substitution. This step is taken from:
+ // Berghel, Hal ; Roach, David : "An Extension of Ukkonen's
+ // Enhanced Dynamic Programming ASM Algorithm"
+ // (http://www.acm.org/~hlb/publications/asm/asm.html)
+
+ if (i > 2 && j > 2) {
+ uint16_t trans = _matrix[i - 2][j - 2] + 1;
+ if (source[i - 2] != t_j) {
+ trans++;
+ }
+ if (s_i != target[j - 2]) {
+ trans++;
+ }
+ if (cell > trans) {
+ cell = trans;
+ }
+ }
+
+ _matrix[i][j] = cell;
+ }
+ }
+
+ // Step 7
+
+ return _matrix[n][m];
+}
+
+} // namespace machina
diff --git a/src/engine/Problem.hpp b/src/engine/Problem.hpp
new file mode 100644
index 0000000..81e67d7
--- /dev/null
+++ b/src/engine/Problem.hpp
@@ -0,0 +1,135 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_PROBLEM_HPP
+#define MACHINA_PROBLEM_HPP
+
+#include <stdint.h>
+
+#include <map>
+#include <stdexcept>
+
+#include "machina/Machine.hpp"
+#include "eugene/Problem.hpp"
+
+#include "MIDISink.hpp"
+
+namespace machina {
+
+class Problem : public eugene::Problem<Machine> {
+public:
+ Problem(TimeUnit unit,
+ const std::string& target_midi,
+ SPtr<Machine> seed = SPtr<Machine>());
+ virtual ~Problem() {}
+
+ void seed(SPtr<Machine> parent) { _seed = parent; }
+
+ float evaluate(const Machine& machine) const;
+
+ bool fitness_less_than(float a, float b) const { return a < b; }
+
+ void clear_fitness_cache() { _fitness.clear(); }
+
+ void initial_population(eugene::Random& rng,
+ Population& pop,
+ size_t gene_size,
+ size_t pop_size) const;
+
+private:
+ size_t distance(const std::vector<uint8_t>& source,
+ const std::vector<uint8_t>& target) const;
+
+ // count
+ /*struct FreqEvaluator : public Raul::MIDISink {
+ Evaluator(const Problem& problem)
+ : _problem(problem), _n_notes(0), _length(0) {
+ for (uint8_t i=0; i < 128; ++i)
+ _note_frequency[i] = 0;
+ }
+ void write_event(Raul::BeatTime time,
+ size_t ev_size,
+ const uint8_t* ev) throw (std::logic_error);
+ void compute();
+ const Problem& _problem;
+
+ size_t n_notes() const { return _n_notes; }
+
+ float _note_frequency[128];
+ size_t _n_notes;
+ double _length;
+ };*/
+
+ // distance
+ /*struct Evaluator : public Raul::MIDISink {
+ Evaluator(const Problem& problem) : _problem(problem) {}
+ void write_event(Raul::BeatTime time,
+ size_t ev_size,
+ const uint8_t* ev) throw (std::logic_error);
+ void compute();
+ const Problem& _problem;
+
+ size_t n_notes() const { return _notes.size(); }
+
+ std::vector<uint8_t> _notes;
+ float _counts[128];
+ };*/
+
+ struct Evaluator : public MIDISink {
+ explicit Evaluator(const Problem& problem)
+ : _problem(problem)
+ , _order(4)
+ , _n_notes(0)
+ , _first_note(0)
+ {
+ for (uint8_t i=0; i < 128; ++i)
+ _counts[i] = 0;
+ }
+ void write_event(TimeStamp time,
+ size_t ev_size,
+ const uint8_t* ev) throw (std::logic_error);
+ void compute();
+ const Problem& _problem;
+
+ size_t n_notes() const { return _n_notes; }
+ uint8_t first_note() const { return _first_note; }
+
+ const uint32_t _order;
+
+ std::string _read;
+
+ typedef std::map<std::string, uint32_t> Patterns;
+ Patterns _patterns;
+ uint32_t _counts[128];
+ size_t _n_notes;
+ uint8_t _first_note;
+ };
+
+ TimeUnit _unit;
+
+ Evaluator _target;
+ SPtr<Machine> _seed;
+
+ /// for levenshtein distance
+ mutable std::vector< std::vector<uint16_t> > _matrix;
+
+ /// memoization
+ mutable std::map<Machine*, float> _fitness;
+};
+
+} // namespace machina
+
+#endif // MACHINA_PROBLEM_HPP
diff --git a/src/engine/Recorder.cpp b/src/engine/Recorder.cpp
new file mode 100644
index 0000000..21dfd4b
--- /dev/null
+++ b/src/engine/Recorder.cpp
@@ -0,0 +1,69 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <ios>
+#include <iostream>
+
+#include "MachineBuilder.hpp"
+#include "Recorder.hpp"
+
+using namespace std;
+using namespace Raul;
+
+namespace machina {
+
+Recorder::Recorder(Forge& forge,
+ size_t buffer_size,
+ TimeUnit unit,
+ double q,
+ bool step)
+ : _unit(unit)
+ , _record_buffer(buffer_size)
+ , _builder(new MachineBuilder(SPtr<Machine>(new Machine(unit)), q, step))
+{}
+
+void
+Recorder::_whipped()
+{
+ TimeStamp t(_unit);
+ size_t size;
+ unsigned char buf[4];
+
+ while (true) {
+ bool success = _record_buffer.read(sizeof(TimeStamp), (uint8_t*)&t);
+ if (success) {
+ success = _record_buffer.read(sizeof(size_t), (uint8_t*)&size);
+ }
+ if (success) {
+ success = _record_buffer.read(size, buf);
+ }
+ if (success) {
+ _builder->event(t, size, buf);
+ } else {
+ break;
+ }
+ }
+}
+
+SPtr<Machine>
+Recorder::finish()
+{
+ SPtr<Machine> machine = _builder->finish();
+ _builder.reset();
+ return machine;
+}
+
+}
diff --git a/src/engine/Recorder.hpp b/src/engine/Recorder.hpp
new file mode 100644
index 0000000..c98c42b
--- /dev/null
+++ b/src/engine/Recorder.hpp
@@ -0,0 +1,68 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_RECORDER_HPP
+#define MACHINA_RECORDER_HPP
+
+#include "raul/RingBuffer.hpp"
+
+#include "machina/Machine.hpp"
+#include "machina/types.hpp"
+
+#include "Slave.hpp"
+
+namespace machina {
+
+class MachineBuilder;
+
+class Recorder
+ : public Slave
+{
+public:
+ Recorder(Forge& forge,
+ size_t buffer_size,
+ TimeUnit unit,
+ double q,
+ bool step);
+
+ inline void write(Raul::TimeStamp time, size_t size,
+ const unsigned char* buf) {
+ if (_record_buffer.write_space() <
+ (sizeof(TimeStamp) + sizeof(size_t) + size)) {
+ std::cerr << "Record buffer overflow" << std::endl;
+ return;
+ } else {
+ _record_buffer.write(sizeof(TimeStamp), (uint8_t*)&time);
+ _record_buffer.write(sizeof(size_t), (uint8_t*)&size);
+ _record_buffer.write(size, buf);
+ }
+ }
+
+ SPtr<MachineBuilder> builder() { return _builder; }
+
+ SPtr<Machine> finish();
+
+private:
+ virtual void _whipped();
+
+ TimeUnit _unit;
+ Raul::RingBuffer _record_buffer;
+ SPtr<MachineBuilder> _builder;
+};
+
+} // namespace machina
+
+#endif // MACHINA_RECORDER_HPP
diff --git a/src/engine/SMFDriver.cpp b/src/engine/SMFDriver.cpp
new file mode 100644
index 0000000..3dc51e2
--- /dev/null
+++ b/src/engine/SMFDriver.cpp
@@ -0,0 +1,162 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <iostream>
+#include <list>
+
+#include "machina/Context.hpp"
+#include "machina/Machine.hpp"
+#include "machina/types.hpp"
+
+#include "Edge.hpp"
+#include "SMFDriver.hpp"
+#include "SMFReader.hpp"
+#include "SMFWriter.hpp"
+#include "quantize.hpp"
+
+using namespace std;
+
+namespace machina {
+
+SMFDriver::SMFDriver(Forge& forge, Raul::TimeUnit unit)
+ : Driver(forge, SPtr<Machine>())
+{
+ _writer = SPtr<SMFWriter>(new SMFWriter(unit));
+}
+
+/** Learn a single track from the MIDI file at `uri`
+ *
+ * @param track The track of the MIDI file to import, starting from 1.
+ *
+ * Currently only file:// URIs are supported.
+ * @return the resulting machine.
+ */
+SPtr<Machine>
+SMFDriver::learn(const string& filename,
+ unsigned track,
+ double q,
+ Raul::TimeDuration max_duration)
+{
+ //assert(q.unit() == max_duration.unit());
+ SPtr<Machine> m(new Machine(max_duration.unit()));
+ SPtr<MachineBuilder> builder = SPtr<MachineBuilder>(
+ new MachineBuilder(m, q, false));
+ SMFReader reader;
+
+ if (!reader.open(filename)) {
+ cerr << "Unable to open MIDI file " << filename << endl;
+ return SPtr<Machine>();
+ }
+
+ if (track > reader.num_tracks()) {
+ return SPtr<Machine>();
+ } else {
+ learn_track(builder, reader, track, q, max_duration);
+ }
+
+ m->reset(NULL, m->time());
+
+ if (m->nodes().size() > 1) {
+ return m;
+ } else {
+ return SPtr<Machine>();
+ }
+}
+
+/** Learn all tracks from a MIDI file into a single machine.
+ *
+ * This will result in one disjoint subgraph in the machine for each track.
+ */
+SPtr<Machine>
+SMFDriver::learn(const string& filename, double q, Raul::TimeStamp max_duration)
+{
+ SPtr<Machine> m(new Machine(max_duration.unit()));
+ SPtr<MachineBuilder> builder = SPtr<MachineBuilder>(
+ new MachineBuilder(m, q, false));
+ SMFReader reader;
+
+ if (!reader.open(filename)) {
+ cerr << "Unable to open MIDI file " << filename << endl;
+ return SPtr<Machine>();
+ }
+
+ for (unsigned t = 1; t <= reader.num_tracks(); ++t) {
+ builder->reset();
+ learn_track(builder, reader, t, q, max_duration);
+ }
+
+ m->reset(NULL, m->time());
+
+ if (m->nodes().size() > 1) {
+ return m;
+ } else {
+ return SPtr<Machine>();
+ }
+}
+
+void
+SMFDriver::learn_track(SPtr<MachineBuilder> builder,
+ SMFReader& reader,
+ unsigned track,
+ double q,
+ Raul::TimeDuration max_duration)
+{
+ const bool found_track = reader.seek_to_track(track);
+ if (!found_track) {
+ return;
+ }
+
+ uint8_t buf[4];
+ uint32_t ev_size;
+ uint32_t ev_delta_time;
+
+ Raul::TimeUnit unit = Raul::TimeUnit(TimeUnit::BEATS, MACHINA_PPQN);
+
+ uint64_t t = 0;
+ while (reader.read_event(4, buf, &ev_size, &ev_delta_time) >= 0) {
+ t += ev_delta_time;
+
+ const uint32_t beats = t / (uint32_t)reader.ppqn();
+ const uint32_t smf_ticks = t % (uint32_t)reader.ppqn();
+ const double frac = smf_ticks / (double)reader.ppqn();
+ const uint32_t ticks = frac * MACHINA_PPQN;
+
+ if (!max_duration.is_zero() && t > max_duration.to_double()) {
+ break;
+ }
+
+ if (ev_size > 0) {
+ // TODO: quantize
+ builder->event(TimeStamp(unit, beats, ticks), ev_size, buf);
+ }
+ }
+
+ builder->resolve();
+}
+
+void
+SMFDriver::run(SPtr<Machine> machine, Raul::TimeStamp max_time)
+{
+ // FIXME: unit kludge (tempo only)
+ Context context(_forge, machine->time().unit().ppt(),
+ _writer->unit().ppt(), 120.0);
+ context.set_sink(this);
+ context.time().set_slice(TimeStamp(max_time.unit(), 0, 0),
+ context.time().beats_to_ticks(max_time));
+ machine->run(context, SPtr<Raul::RingBuffer>());
+}
+
+} // namespace machina
diff --git a/src/engine/SMFDriver.hpp b/src/engine/SMFDriver.hpp
new file mode 100644
index 0000000..e8b9a4b
--- /dev/null
+++ b/src/engine/SMFDriver.hpp
@@ -0,0 +1,67 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_SMFDRIVER_HPP
+#define MACHINA_SMFDRIVER_HPP
+
+#include "machina/Driver.hpp"
+#include "machina/types.hpp"
+
+#include "MachineBuilder.hpp"
+#include "SMFReader.hpp"
+#include "SMFWriter.hpp"
+
+namespace machina {
+
+class Node;
+class Machine;
+
+class SMFDriver : public Driver
+{
+public:
+ SMFDriver(Forge& forge, Raul::TimeUnit unit);
+
+ SPtr<Machine> learn(const std::string& filename,
+ double q,
+ Raul::TimeDuration max_duration);
+
+ SPtr<Machine> learn(const std::string& filename,
+ unsigned track,
+ double q,
+ Raul::TimeDuration max_duration);
+
+ void run(SPtr<Machine> machine, Raul::TimeStamp max_time);
+
+ void write_event(Raul::TimeStamp time,
+ size_t ev_size,
+ const unsigned char* ev)
+ { _writer->write_event(time, ev_size, ev); }
+
+ SPtr<SMFWriter> writer() { return _writer; }
+
+private:
+ SPtr<SMFWriter> _writer;
+
+ void learn_track(SPtr<MachineBuilder> builder,
+ SMFReader& reader,
+ unsigned track,
+ double q,
+ Raul::TimeDuration max_duration);
+};
+
+} // namespace machina
+
+#endif // MACHINA_SMFDRIVER_HPP
diff --git a/src/engine/SMFReader.cpp b/src/engine/SMFReader.cpp
new file mode 100644
index 0000000..5976f75
--- /dev/null
+++ b/src/engine/SMFReader.cpp
@@ -0,0 +1,323 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Raul is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Raul is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Raul. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <arpa/inet.h>
+#include <cassert>
+#include <cstdio>
+#include <cstring>
+#include <string>
+
+#include "lv2/lv2plug.in/ns/ext/midi/midi.h"
+
+#include "SMFReader.hpp"
+
+using std::endl;
+
+namespace machina {
+
+/** Return the size of the given event NOT including the status byte,
+ * or -1 if unknown (eg sysex)
+ */
+static int
+midi_event_size(unsigned char status)
+{
+ if (status >= 0x80 && status <= 0xE0) {
+ status &= 0xF0; // mask off the channel
+ }
+
+ switch (status) {
+ case LV2_MIDI_MSG_NOTE_OFF:
+ case LV2_MIDI_MSG_NOTE_ON:
+ case LV2_MIDI_MSG_NOTE_PRESSURE:
+ case LV2_MIDI_MSG_CONTROLLER:
+ case LV2_MIDI_MSG_BENDER:
+ case LV2_MIDI_MSG_SONG_POS:
+ return 2;
+
+ case LV2_MIDI_MSG_PGM_CHANGE:
+ case LV2_MIDI_MSG_CHANNEL_PRESSURE:
+ case LV2_MIDI_MSG_MTC_QUARTER:
+ case LV2_MIDI_MSG_SONG_SELECT:
+ return 1;
+
+ case LV2_MIDI_MSG_TUNE_REQUEST:
+ case LV2_MIDI_MSG_CLOCK:
+ case LV2_MIDI_MSG_START:
+ case LV2_MIDI_MSG_CONTINUE:
+ case LV2_MIDI_MSG_STOP:
+ case LV2_MIDI_MSG_ACTIVE_SENSE:
+ case LV2_MIDI_MSG_RESET:
+ return 0;
+
+ case LV2_MIDI_MSG_SYSTEM_EXCLUSIVE:
+ return -1;
+ }
+
+ return -1;
+}
+
+SMFReader::SMFReader(const std::string filename)
+ : _fd(NULL)
+ , _ppqn(0)
+ , _track(0)
+ , _track_size(0)
+{
+ if (filename.length() > 0) {
+ open(filename);
+ }
+}
+
+SMFReader::~SMFReader()
+{
+ if (_fd) {
+ close();
+ }
+}
+
+bool
+SMFReader::open(const std::string& filename)
+{
+ if (_fd) {
+ throw std::logic_error(
+ "Attempt to start new read while write in progress.");
+ }
+
+ std::cout << "Opening SMF file " << filename << " for reading." << endl;
+
+ _fd = fopen(filename.c_str(), "r+");
+
+ if (_fd) {
+ // Read type (bytes 8..9)
+ fseek(_fd, 0, SEEK_SET);
+ char mthd[5];
+ mthd[4] = '\0';
+ fread(mthd, 1, 4, _fd);
+ if (strcmp(mthd, "MThd")) {
+ std::cerr << filename << " is not an SMF file, aborting." << endl;
+ fclose(_fd);
+ _fd = NULL;
+ return false;
+ }
+
+ // Read type (bytes 8..9)
+ fseek(_fd, 8, SEEK_SET);
+ uint16_t type_be = 0;
+ fread(&type_be, 2, 1, _fd);
+ _type = ntohs(type_be);
+
+ // Read number of tracks (bytes 10..11)
+ uint16_t num_tracks_be = 0;
+ fread(&num_tracks_be, 2, 1, _fd);
+ _num_tracks = ntohs(num_tracks_be);
+
+ // Read PPQN (bytes 12..13)
+ uint16_t ppqn_be = 0;
+ fread(&ppqn_be, 2, 1, _fd);
+ _ppqn = ntohs(ppqn_be);
+
+ // TODO: Absolute (SMPTE seconds) time support
+ if ((_ppqn & 0x8000) != 0) {
+ throw UnsupportedTime();
+ }
+
+ seek_to_track(1);
+
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/** Seek to the start of a given track, starting from 1.
+ * Returns true if specified track was found.
+ */
+bool
+SMFReader::seek_to_track(unsigned track)
+{
+ if (track == 0) {
+ throw std::logic_error("Seek to track 0 out of range (must be >= 1)");
+ }
+
+ if (!_fd) {
+ throw std::logic_error("Attempt to seek to track on unopened SMF file.");
+ }
+
+ unsigned track_pos = 0;
+
+ fseek(_fd, 14, SEEK_SET);
+ char id[5];
+ id[4] = '\0';
+ uint32_t chunk_size = 0;
+
+ while (!feof(_fd)) {
+ fread(id, 1, 4, _fd);
+
+ if (!strcmp(id, "MTrk")) {
+ ++track_pos;
+ } else {
+ std::cerr << "Unknown chunk ID " << id << endl;
+ }
+
+ uint32_t chunk_size_be;
+ fread(&chunk_size_be, 4, 1, _fd);
+ chunk_size = ntohl(chunk_size_be);
+
+ if (track_pos == track) {
+ break;
+ }
+
+ fseek(_fd, chunk_size, SEEK_CUR);
+ }
+
+ if (!feof(_fd) && track_pos == track) {
+ _track = track;
+ _track_size = chunk_size;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/** Read an event from the current position in file.
+ *
+ * File position MUST be at the beginning of a delta time, or this will die very messily.
+ * ev.buffer must be of size ev.size, and large enough for the event. The returned event
+ * will have it's time field set to it's delta time (so it's the caller's responsibility
+ * to keep track of delta time, even for ignored events).
+ *
+ * Returns event length (including status byte) on success, 0 if event was
+ * skipped (eg a meta event), or -1 on EOF (or end of track).
+ *
+ * If `buf` is not large enough to hold the event, 0 will be returned, but ev_size
+ * set to the actual size of the event.
+ */
+int
+SMFReader::read_event(size_t buf_len,
+ uint8_t* buf,
+ uint32_t* ev_size,
+ uint32_t* delta_time)
+{
+ if (_track == 0) {
+ throw std::logic_error("Attempt to read from unopened SMF file");
+ }
+
+ if (!_fd || feof(_fd)) {
+ return -1;
+ }
+
+ assert(buf_len > 0);
+ assert(buf);
+ assert(ev_size);
+ assert(delta_time);
+
+ // Running status state
+ static uint8_t last_status = 0;
+ static uint32_t last_size = 0;
+
+ *delta_time = read_var_len(_fd);
+ int status = fgetc(_fd);
+ if (status == EOF) {
+ throw PrematureEOF();
+ } else if (status > 0xFF) {
+ throw CorruptFile();
+ }
+
+ if (status < 0x80) {
+ if (last_status == 0) {
+ throw CorruptFile();
+ }
+ status = last_status;
+ *ev_size = last_size;
+ fseek(_fd, -1, SEEK_CUR);
+ } else {
+ last_status = status;
+ *ev_size = midi_event_size(status) + 1;
+ last_size = *ev_size;
+ }
+
+ buf[0] = static_cast<uint8_t>(status);
+
+ if (status == 0xFF) {
+ *ev_size = 0;
+ if (feof(_fd)) {
+ throw PrematureEOF();
+ }
+ uint8_t type = fgetc(_fd);
+ const uint32_t size = read_var_len(_fd);
+
+ if (type == 0x2F) {
+ return -1; // we hit the logical EOF anyway...
+ } else {
+ fseek(_fd, size, SEEK_CUR);
+ return 0;
+ }
+ }
+
+ if ((*ev_size > buf_len) || (*ev_size == 0) || feof(_fd)) {
+ // Skip event, return 0
+ fseek(_fd, *ev_size - 1, SEEK_CUR);
+ return 0;
+ } else {
+ // Read event, return size
+ if (ferror(_fd)) {
+ throw CorruptFile();
+ }
+
+ fread(buf + 1, 1, *ev_size - 1, _fd);
+
+ if (((buf[0] & 0xF0) == 0x90) && (buf[2] == 0) ) {
+ buf[0] = (0x80 | (buf[0] & 0x0F));
+ buf[2] = 0x40;
+ }
+
+ return *ev_size;
+ }
+}
+
+void
+SMFReader::close()
+{
+ if (_fd) {
+ fclose(_fd);
+ }
+
+ _fd = NULL;
+}
+
+uint32_t
+SMFReader::read_var_len(FILE* fd)
+{
+ if (feof(fd)) {
+ throw PrematureEOF();
+ }
+
+ uint32_t value;
+ uint8_t c;
+
+ if ((value = getc(fd)) & 0x80) {
+ value &= 0x7F;
+ do {
+ if (feof(fd)) {
+ throw PrematureEOF();
+ }
+ value = (value << 7) + ((c = getc(fd)) & 0x7F);
+ } while (c & 0x80);
+ }
+
+ return value;
+}
+
+} // namespace machina
diff --git a/src/engine/SMFReader.hpp b/src/engine/SMFReader.hpp
new file mode 100644
index 0000000..7147f38
--- /dev/null
+++ b/src/engine/SMFReader.hpp
@@ -0,0 +1,87 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_SMF_READER_HPP
+#define MACHINA_SMF_READER_HPP
+
+#include <stdexcept>
+#include <string>
+#include <inttypes.h>
+#include "raul/TimeStamp.hpp"
+
+namespace machina {
+
+/** Standard Midi File (Type 0) Reader
+ *
+ * Currently this only reads SMF files with tempo-based timing.
+ * \ingroup raul
+ */
+class SMFReader
+{
+public:
+ class PrematureEOF
+ : public std::exception
+ {
+ const char* what() const throw() { return "Unexpected end of file"; }
+ };
+ class CorruptFile
+ : public std::exception
+ {
+ const char* what() const throw() { return "Corrupted file"; }
+ };
+ class UnsupportedTime
+ : public std::exception
+ {
+ const char* what() const throw() { return
+ "Unsupported time stamp type (SMPTE)"; }
+ };
+
+ explicit SMFReader(const std::string filename = "");
+ ~SMFReader();
+
+ bool open(const std::string& filename);
+
+ bool seek_to_track(unsigned track);
+
+ uint16_t type() const { return _type; }
+ uint16_t ppqn() const { return _ppqn; }
+ size_t num_tracks() { return _num_tracks; }
+
+ int read_event(size_t buf_len,
+ uint8_t* buf,
+ uint32_t* ev_size,
+ uint32_t* ev_delta_time);
+
+ void close();
+
+ static uint32_t read_var_len(FILE* fd);
+
+protected:
+ /** size of SMF header, including MTrk chunk header */
+ static const uint32_t HEADER_SIZE = 22;
+
+ std::string _filename;
+ FILE* _fd;
+ uint16_t _type;
+ uint16_t _ppqn;
+ uint16_t _num_tracks;
+ uint32_t _track;
+ uint32_t _track_size;
+};
+
+} // namespace machina
+
+#endif // MACHINA_SMF_READER_HPP
diff --git a/src/engine/SMFWriter.cpp b/src/engine/SMFWriter.cpp
new file mode 100644
index 0000000..25d25c9
--- /dev/null
+++ b/src/engine/SMFWriter.cpp
@@ -0,0 +1,241 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <arpa/inet.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <cassert>
+#include <cstdio>
+#include <cstring>
+#include <limits>
+#include <string>
+
+#include "SMFWriter.hpp"
+
+using std::endl;
+
+namespace machina {
+
+/** Create a new SMF writer.
+ *
+ * @param unit Must match the time stamp of ALL events passed to write, or
+ * terrible things will happen.
+ *
+ * *** NOTE: Only beat time is implemented currently.
+ */
+SMFWriter::SMFWriter(Raul::TimeUnit unit)
+ : _fd(NULL)
+ , _unit(unit)
+ , _start_time(unit, 0, 0)
+ , _last_ev_time(unit, 0, 0)
+ , _track_size(0)
+ , _header_size(0)
+{
+ if (unit.type() == Raul::TimeUnit::BEATS) {
+ assert(unit.ppt() < std::numeric_limits<uint16_t>::max());
+ }
+}
+
+SMFWriter::~SMFWriter()
+{
+ if (_fd) {
+ finish();
+ }
+}
+
+/** Start a write to an SMF file.
+ *
+ * @param filename Filename to write to.
+ * @param start_time Beat time corresponding to t=0 in the file (timestamps
+ * passed to write_event will have this value subtracted before writing).
+ */
+bool
+SMFWriter::start(const std::string& filename,
+ Raul::TimeStamp start_time)
+{
+ if (_fd) {
+ throw std::logic_error(
+ "Attempt to start new write while write in progress.");
+ }
+
+ std::cout << "Opening SMF file " << filename << " for writing." << endl;
+
+ _fd = fopen(filename.c_str(), "w+");
+
+ if (_fd) {
+ _track_size = 0;
+ _filename = filename;
+ _start_time = start_time;
+ _last_ev_time = 0;
+ // write a tentative header to pad file out so writing starts at the right offset
+ write_header();
+ }
+
+ return (_fd == 0) ? false : true;
+}
+
+/** Write an event at the end of the file.
+ *
+ * @param time The absolute time of the event, relative to the start of the
+ * file (the start_time parameter to start). Must be monotonically increasing
+ * on successive calls to this method.
+ */
+void
+SMFWriter::write_event(Raul::TimeStamp time,
+ size_t ev_size,
+ const unsigned char* ev)
+{
+ if (time < _start_time) {
+ throw std::logic_error("Event time is before file start time");
+ } else if (time < _last_ev_time) {
+ throw std::logic_error("Event time not monotonically increasing");
+ } else if (time.unit() != _unit) {
+ throw std::logic_error("Event has unexpected time unit");
+ }
+
+ Raul::TimeStamp delta_time = time;
+ delta_time -= _start_time;
+
+ fseek(_fd, 0, SEEK_END);
+
+ uint64_t delta_ticks = delta_time.ticks() * _unit.ppt()
+ + delta_time.subticks();
+ size_t stamp_size = 0;
+
+ /* If delta time is too long (i.e. overflows), write out empty
+ * "proprietary" events to reach the desired time.
+ * Any SMF reading application should interpret this correctly
+ * (by accumulating the delta time and ignoring the event) */
+ while (delta_ticks > VAR_LEN_MAX) {
+ static unsigned char null_event[] = { 0xFF, 0x7F, 0x0 };
+ stamp_size = write_var_len(VAR_LEN_MAX);
+ fwrite(null_event, 1, 3, _fd);
+ _track_size += stamp_size + 3;
+ delta_ticks -= VAR_LEN_MAX;
+ }
+
+ assert(delta_ticks <= VAR_LEN_MAX);
+ stamp_size = write_var_len(static_cast<uint32_t>(delta_ticks));
+ fwrite(ev, 1, ev_size, _fd);
+
+ _last_ev_time = time;
+ _track_size += stamp_size + ev_size;
+}
+
+void
+SMFWriter::flush()
+{
+ if (_fd) {
+ fflush(_fd);
+ }
+}
+
+void
+SMFWriter::finish()
+{
+ if (!_fd) {
+ throw std::logic_error(
+ "Attempt to finish write with no write in progress.");
+ }
+
+ write_footer();
+ fclose(_fd);
+ _fd = NULL;
+}
+
+void
+SMFWriter::write_header()
+{
+ std::cout << "SMF Flushing header\n";
+
+ const uint16_t type = htons(0); // SMF Type 0 (single track)
+ const uint16_t ntracks = htons(1); // Number of tracks (always 1 for Type 0)
+ const uint16_t division = htons(_unit.ppt()); // PPQN
+
+ char data[6];
+ memcpy(data, &type, 2);
+ memcpy(data + 2, &ntracks, 2);
+ memcpy(data + 4, &division, 2);
+ //data[4] = _ppqn & 0xF0;
+ //data[5] = _ppqn & 0x0F;
+
+ _fd = freopen(_filename.c_str(), "r+", _fd);
+ assert(_fd);
+ fseek(_fd, 0, 0);
+ write_chunk("MThd", 6, data);
+ write_chunk_header("MTrk", _track_size);
+}
+
+void
+SMFWriter::write_footer()
+{
+ std::cout << "Writing EOT\n";
+
+ fseek(_fd, 0, SEEK_END);
+ write_var_len(1); // whatever...
+ static const unsigned char eot[4] = { 0xFF, 0x2F, 0x00 }; // end-of-track meta-event
+ fwrite(eot, 1, 4, _fd);
+}
+
+void
+SMFWriter::write_chunk_header(const char id[4], uint32_t length)
+{
+ const uint32_t length_be = htonl(length);
+
+ fwrite(id, 1, 4, _fd);
+ fwrite(&length_be, 4, 1, _fd);
+}
+
+void
+SMFWriter::write_chunk(const char id[4], uint32_t length, void* data)
+{
+ write_chunk_header(id, length);
+
+ fwrite(data, 1, length, _fd);
+}
+
+/** Write an SMF variable length value.
+ *
+ * @return size (in bytes) of the value written.
+ */
+size_t
+SMFWriter::write_var_len(uint32_t value)
+{
+ size_t ret = 0;
+
+ uint32_t buffer = value & 0x7F;
+
+ while ((value >>= 7)) {
+ buffer <<= 8;
+ buffer |= ((value & 0x7F) | 0x80);
+ }
+
+ while (true) {
+ //printf("Writing var len byte %X\n", (unsigned char)buffer);
+ ++ret;
+ fputc(buffer, _fd);
+ if (buffer & 0x80) {
+ buffer >>= 8;
+ } else {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+} // namespace machina
diff --git a/src/engine/SMFWriter.hpp b/src/engine/SMFWriter.hpp
new file mode 100644
index 0000000..18bf3ec
--- /dev/null
+++ b/src/engine/SMFWriter.hpp
@@ -0,0 +1,73 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_SMF_WRITER_HPP
+#define MACHINA_SMF_WRITER_HPP
+
+#include <stdexcept>
+#include <string>
+
+#include "raul/TimeStamp.hpp"
+
+#include "MIDISink.hpp"
+
+namespace machina {
+
+/** Standard Midi File (Type 0) Writer
+ * \ingroup raul
+ */
+class SMFWriter
+ : public MIDISink
+{
+public:
+ explicit SMFWriter(Raul::TimeUnit unit);
+ ~SMFWriter();
+
+ bool start(const std::string& filename,
+ Raul::TimeStamp start_time);
+
+ Raul::TimeUnit unit() const { return _unit; }
+
+ void write_event(Raul::TimeStamp time,
+ size_t ev_size,
+ const unsigned char* ev);
+
+ void flush();
+
+ void finish();
+
+protected:
+ static const uint32_t VAR_LEN_MAX = 0x0FFFFFFF;
+
+ void write_header();
+ void write_footer();
+
+ void write_chunk_header(const char id[4], uint32_t length);
+ void write_chunk(const char id[4], uint32_t length, void* data);
+ size_t write_var_len(uint32_t val);
+
+ std::string _filename;
+ FILE* _fd;
+ Raul::TimeUnit _unit;
+ Raul::TimeStamp _start_time;
+ Raul::TimeStamp _last_ev_time; ///< Time last event was written relative to _start_time
+ uint32_t _track_size;
+ uint32_t _header_size; ///< size of SMF header, including MTrk chunk header
+};
+
+} // namespace machina
+
+#endif // MACHINA_SMF_WRITER_HPP
diff --git a/src/engine/Schrodinbit.hpp b/src/engine/Schrodinbit.hpp
new file mode 100644
index 0000000..0721eb9
--- /dev/null
+++ b/src/engine/Schrodinbit.hpp
@@ -0,0 +1,42 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SCHRODINBIT_HPP
+#define SCHRODINBIT_HPP
+
+/** A flag which becomes false when its value is observed
+ */
+class Schrodinbit
+{
+public:
+ Schrodinbit() : _flag(false) {}
+
+ inline operator bool() {
+ const bool ret = _flag;
+ _flag = false;
+ return ret;
+ }
+
+ inline bool operator=(bool flag) {
+ _flag = flag;
+ return flag;
+ }
+
+private:
+ bool _flag;
+};
+
+#endif // SCHRODINBIT_HPP
diff --git a/src/engine/Slave.hpp b/src/engine/Slave.hpp
new file mode 100644
index 0000000..74ec06b
--- /dev/null
+++ b/src/engine/Slave.hpp
@@ -0,0 +1,73 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_SLAVE_HPP
+#define MACHINA_SLAVE_HPP
+
+#include <thread>
+
+#include "raul/Semaphore.hpp"
+
+namespace machina {
+
+/** Thread driven by (realtime safe) signals.
+ *
+ * Use this to perform some task in a separate thread you want to 'drive'
+ * from a realtime (or otherwise) thread.
+ */
+class Slave
+{
+public:
+ Slave()
+ : _whip(0)
+ , _exit_flag(false)
+ , _thread(&Slave::_run, this)
+ {}
+
+ virtual ~Slave() {
+ _exit_flag = true;
+ _whip.post();
+ _thread.join();
+ }
+
+ /** Tell the slave to do whatever work it does. Realtime safe. */
+ inline void whip() { _whip.post(); }
+
+protected:
+ /** Worker method.
+ *
+ * This is called once from this thread every time whip() is called.
+ * Implementations likely want to put a single (non loop) chunk of code
+ * here, e.g. to process an event.
+ */
+ virtual void _whipped() = 0;
+
+ Raul::Semaphore _whip;
+
+private:
+ inline void _run() {
+ while (_whip.wait() && !_exit_flag) {
+ _whipped();
+ }
+ }
+
+ bool _exit_flag;
+ std::thread _thread;
+};
+
+} // namespace machina
+
+#endif // MACHINA_SLAVE_HPP
diff --git a/src/engine/Stateful.cpp b/src/engine/Stateful.cpp
new file mode 100644
index 0000000..7608a06
--- /dev/null
+++ b/src/engine/Stateful.cpp
@@ -0,0 +1,39 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "Stateful.hpp"
+
+namespace machina {
+
+uint64_t Stateful::_next_id = 1;
+
+Stateful::Stateful()
+ : _id(next_id())
+{}
+
+const Sord::Node&
+Stateful::rdf_id(Sord::World& world) const
+{
+ if (!_rdf_id.is_valid()) {
+ std::ostringstream ss;
+ ss << "b" << _id;
+ _rdf_id = Sord::Node(world, Sord::Node::BLANK, ss.str());
+ }
+
+ return _rdf_id;
+}
+
+} // namespace machina
diff --git a/src/engine/Stateful.hpp b/src/engine/Stateful.hpp
new file mode 100644
index 0000000..b15d863
--- /dev/null
+++ b/src/engine/Stateful.hpp
@@ -0,0 +1,70 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_STATEFUL_HPP
+#define MACHINA_STATEFUL_HPP
+
+#include <stdint.h>
+
+#include "sord/sordmm.hpp"
+
+#include "machina/Atom.hpp"
+#include "machina/types.hpp"
+
+namespace Raul {
+class Atom;
+}
+
+namespace machina {
+
+class URIs;
+
+class Stateful
+{
+public:
+ Stateful();
+
+ virtual ~Stateful() {}
+
+ virtual void set(URIInt key, const Atom& value) {}
+ virtual void write_state(Sord::Model& model) = 0;
+
+ uint64_t id() const { return _id; }
+ const Sord::Node& rdf_id(Sord::World& world) const;
+
+ static uint64_t next_id() { return _next_id++; }
+
+protected:
+ explicit Stateful(uint64_t id) : _id(id) {}
+
+private:
+ static uint64_t _next_id;
+
+ uint64_t _id;
+ mutable Sord::Node _rdf_id;
+};
+
+class StatefulKey : public Stateful
+{
+public:
+ explicit StatefulKey(uint64_t id) : Stateful(id) {}
+
+ void write_state(Sord::Model& model) {}
+};
+
+} // namespace machina
+
+#endif // MACHINA_STATEFUL_HPP
diff --git a/src/engine/URIs.cpp b/src/engine/URIs.cpp
new file mode 100644
index 0000000..2b43b81
--- /dev/null
+++ b/src/engine/URIs.cpp
@@ -0,0 +1,23 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "machina/URIs.hpp"
+
+namespace machina {
+
+URIs* URIs::_instance = NULL;
+
+} // namespace machina
diff --git a/src/engine/Updates.cpp b/src/engine/Updates.cpp
new file mode 100644
index 0000000..ce655e4
--- /dev/null
+++ b/src/engine/Updates.cpp
@@ -0,0 +1,66 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2014 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
+
+#include "machina/Atom.hpp"
+#include "machina/Updates.hpp"
+#include "machina/types.hpp"
+#include "raul/RingBuffer.hpp"
+
+namespace machina {
+
+void
+write_set(SPtr<Raul::RingBuffer> buf,
+ uint64_t subject,
+ URIInt key,
+ const Atom& value)
+{
+ const uint32_t update_type = UPDATE_SET;
+ buf->write(sizeof(update_type), &update_type);
+ buf->write(sizeof(subject), &subject);
+ buf->write(sizeof(key), &key);
+
+ const LV2_Atom atom = { value.size(), value.type() };
+ buf->write(sizeof(LV2_Atom), &atom);
+ buf->write(value.size(), value.get_body());
+}
+
+uint32_t
+read_set(SPtr<Raul::RingBuffer> buf,
+ uint64_t* subject,
+ URIInt* key,
+ Atom* value)
+{
+ uint32_t update_type = 0;
+ buf->read(sizeof(update_type), &update_type);
+ if (update_type != UPDATE_SET) {
+ return 0;
+ }
+
+ buf->read(sizeof(*subject), subject);
+ buf->read(sizeof(*key), key);
+
+ LV2_Atom atom = { 0, 0 };
+ buf->read(sizeof(LV2_Atom), &atom);
+ *value = Atom(atom.size, atom.type, NULL);
+ buf->read(atom.size, value->get_body());
+
+ return sizeof(update_type) + sizeof(*subject) + sizeof(*key)
+ + sizeof(LV2_Atom) + atom.size;
+}
+
+}
diff --git a/src/engine/machina/Atom.hpp b/src/engine/machina/Atom.hpp
new file mode 100644
index 0000000..710cc26
--- /dev/null
+++ b/src/engine/machina/Atom.hpp
@@ -0,0 +1,217 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Raul is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Raul is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Raul. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_ATOM_HPP
+#define MACHINA_ATOM_HPP
+
+#include <stdint.h>
+
+#include <cassert>
+#include <cstdlib>
+#include <cstring>
+#include <string>
+
+namespace machina {
+
+class Forge;
+
+/**
+ A generic typed data container.
+
+ An Atom holds a value with some type and size, both specified by a uint32_t.
+ Values with size less than sizeof(void*) are stored inline: no dynamic
+ allocation occurs so Atoms may be created in hard real-time threads.
+ Otherwise, if the size is larger than sizeof(void*), the value will be
+ dynamically allocated in a separate chunk of memory.
+*/
+class Atom {
+public:
+ Atom() : _size(0), _type(0) { _val._blob = NULL; }
+ ~Atom() { dealloc(); }
+
+ typedef uint32_t TypeID;
+
+ /** Contruct a raw atom.
+ *
+ * Typically this is not used directly, use Forge methods to make atoms.
+ */
+ Atom(uint32_t size, TypeID type, const void* body)
+ : _size(size)
+ , _type(type)
+ {
+ if (is_reference()) {
+ _val._blob = malloc(size);
+ }
+ if (body) {
+ memcpy(get_body(), body, size);
+ }
+ }
+
+ Atom(const Atom& copy)
+ : _size(copy._size)
+ , _type(copy._type)
+ {
+ if (is_reference()) {
+ _val._blob = malloc(_size);
+ memcpy(_val._blob, copy._val._blob, _size);
+ } else {
+ memcpy(&_val, &copy._val, _size);
+ }
+ }
+
+ Atom& operator=(const Atom& other) {
+ if (&other == this) {
+ return *this;
+ }
+ dealloc();
+ _size = other._size;
+ _type = other._type;
+ if (is_reference()) {
+ _val._blob = malloc(_size);
+ memcpy(_val._blob, other._val._blob, _size);
+ } else {
+ memcpy(&_val, &other._val, _size);
+ }
+ return *this;
+ }
+
+ inline bool operator==(const Atom& other) const {
+ if (_type == other.type() && _size == other.size()) {
+ if (is_reference()) {
+ return !memcmp(_val._blob, other._val._blob, _size);
+ } else {
+ return !memcmp(&_val, &other._val, _size);
+ }
+ }
+ return false;
+ }
+
+ inline bool operator!=(const Atom& other) const {
+ return !operator==(other);
+ }
+
+ inline bool operator<(const Atom& other) const {
+ if (_type == other.type()) {
+ if (is_reference()) {
+ return memcmp(_val._blob, other._val._blob, _size) < 0;
+ } else {
+ return memcmp(&_val, &other._val, _size) < 0;
+ }
+ }
+ return _type < other.type();
+ }
+
+ inline uint32_t size() const { return _size; }
+ inline bool is_valid() const { return _type; }
+ inline TypeID type() const { return _type; }
+
+ inline const void* get_body() const {
+ return is_reference() ? _val._blob : &_val;
+ }
+
+ inline void* get_body() {
+ return is_reference() ? _val._blob : &_val;
+ }
+
+ template <typename T> const T& get() const {
+ assert(size() == sizeof(T));
+ return *static_cast<const T*>(get_body());
+ }
+
+ template <typename T> const T* ptr() const {
+ return static_cast<const T*>(get_body());
+ }
+
+private:
+ friend class Forge;
+
+ /** Free dynamically allocated value, if applicable. */
+ inline void dealloc() {
+ if (is_reference()) {
+ free(_val._blob);
+ }
+ }
+
+ /** Return true iff this value is dynamically allocated. */
+ inline bool is_reference() const {
+ return _size > sizeof(_val);
+ }
+
+ uint32_t _size;
+ TypeID _type;
+
+ union {
+ intptr_t _val;
+ void* _blob;
+ } _val;
+};
+
+class Forge {
+public:
+ Forge()
+ : Int(1)
+ , Float(2)
+ , Bool(3)
+ , URI(4)
+ , URID(5)
+ , String(6)
+ {}
+
+ virtual ~Forge() {}
+
+ Atom make() { return Atom(); }
+ Atom make(int32_t v) { return Atom(sizeof(v), Int, &v); }
+ Atom make(float v) { return Atom(sizeof(v), Float, &v); }
+ Atom make(bool v) {
+ const int32_t iv = v ? 1 : 0;
+ return Atom(sizeof(int32_t), Bool, &iv);
+ }
+
+ Atom make_urid(int32_t v) { return Atom(sizeof(int32_t), URID, &v); }
+
+ Atom alloc(uint32_t size, uint32_t type, const void* val) {
+ return Atom(size, type, val);
+ }
+
+ Atom alloc(const char* v) {
+ const size_t len = strlen(v);
+ return Atom(len + 1, String, v);
+ }
+
+ Atom alloc(const std::string& v) {
+ return Atom(v.length() + 1, String, v.c_str());
+ }
+
+ Atom alloc_uri(const char* v) {
+ const size_t len = strlen(v);
+ return Atom(len + 1, URI, v);
+ }
+
+ Atom alloc_uri(const std::string& v) {
+ return Atom(v.length() + 1, URI, v.c_str());
+ }
+
+ Atom::TypeID Int;
+ Atom::TypeID Float;
+ Atom::TypeID Bool;
+ Atom::TypeID URI;
+ Atom::TypeID URID;
+ Atom::TypeID String;
+};
+
+} // namespace machina
+
+#endif // MACHINA_ATOM_HPP
diff --git a/src/engine/machina/Context.hpp b/src/engine/machina/Context.hpp
new file mode 100644
index 0000000..766975b
--- /dev/null
+++ b/src/engine/machina/Context.hpp
@@ -0,0 +1,51 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_CONTEXT_HPP
+#define MACHINA_CONTEXT_HPP
+
+#include "machina/Atom.hpp"
+#include "machina/types.hpp"
+#include "raul/TimeSlice.hpp"
+
+namespace machina {
+
+class MIDISink;
+
+class Context
+{
+public:
+ Context(Forge& forge, uint32_t rate, uint32_t ppqn, double bpm)
+ : _forge(forge)
+ , _time(rate, ppqn, bpm)
+ {}
+
+ void set_sink(MIDISink* sink) { _sink = sink; }
+
+ Forge& forge() { return _forge; }
+ const Raul::TimeSlice& time() const { return _time; }
+ Raul::TimeSlice& time() { return _time; }
+ MIDISink* sink() { return _sink; }
+
+private:
+ Forge& _forge;
+ Raul::TimeSlice _time;
+ MIDISink* _sink;
+};
+
+} // namespace machina
+
+#endif // MACHINA_CONTEXT_HPP
diff --git a/src/engine/machina/Controller.hpp b/src/engine/machina/Controller.hpp
new file mode 100644
index 0000000..833b5b2
--- /dev/null
+++ b/src/engine/machina/Controller.hpp
@@ -0,0 +1,80 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2014 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_CONTROLLER_HPP
+#define MACHINA_CONTROLLER_HPP
+
+#include <stdint.h>
+
+#include <set>
+
+#include "raul/RingBuffer.hpp"
+#include "raul/Maid.hpp"
+
+#include "machina/Model.hpp"
+#include "machina/URIs.hpp"
+#include "machina/types.hpp"
+
+#include "Stateful.hpp"
+
+namespace Raul {
+class Atom;
+}
+
+namespace machina {
+
+class Engine;
+class Machine;
+
+class Controller
+{
+public:
+ Controller(SPtr<Engine> engine, Model& model);
+
+ uint64_t create(const Properties& properties);
+ uint64_t connect(uint64_t tail_id, uint64_t head_id);
+
+ void set_property(uint64_t object_id, URIInt key, const Atom& value);
+
+ void learn(SPtr<Raul::Maid> maid, uint64_t node_id);
+ void disconnect(uint64_t tail_id, uint64_t head_id);
+ void erase(uint64_t id);
+
+ void announce(SPtr<Machine> machine);
+
+ void process_updates();
+
+private:
+ SPtr<Stateful> find(uint64_t id);
+
+ struct StatefulComparator {
+ inline bool operator()(SPtr<Stateful> a, SPtr<Stateful> b) const {
+ return a->id() < b->id();
+ }
+ };
+
+ typedef std::set<SPtr<Stateful>, StatefulComparator> Objects;
+ Objects _objects;
+
+ SPtr<Engine> _engine;
+ Model& _model;
+
+ SPtr<Raul::RingBuffer> _updates;
+};
+
+}
+
+#endif // MACHINA_CONTROLLER_HPP
diff --git a/src/engine/machina/Driver.hpp b/src/engine/machina/Driver.hpp
new file mode 100644
index 0000000..546c4d6
--- /dev/null
+++ b/src/engine/machina/Driver.hpp
@@ -0,0 +1,86 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_DRIVER_HPP
+#define MACHINA_DRIVER_HPP
+
+#include "raul/RingBuffer.hpp"
+
+#include "machina/types.hpp"
+
+#include "MIDISink.hpp"
+
+namespace machina {
+
+class Machine;
+
+class Driver : public MIDISink
+{
+public:
+ Driver(Forge& forge, SPtr<Machine> machine)
+ : _forge(forge)
+ , _machine(machine)
+ , _play_state(PlayState::STOPPED)
+ , _bpm(120.0)
+ , _quantization(0.125)
+ , _quantize_record(0)
+ {}
+
+ enum class PlayState {
+ STOPPED,
+ PLAYING,
+ RECORDING,
+ STEP_RECORDING
+ };
+
+ virtual ~Driver() {}
+
+ SPtr<Machine> machine() { return _machine; }
+
+ virtual void set_machine(SPtr<Machine> machine) {
+ _machine = machine;
+ }
+
+ SPtr<Raul::RingBuffer> update_sink() { return _updates; }
+
+ void set_update_sink(SPtr<Raul::RingBuffer> b) {
+ _updates = b;
+ }
+
+ virtual void set_bpm(double bpm) { _bpm = bpm; }
+ virtual void set_quantization(double q) { _quantization = q; }
+ virtual void set_quantize_record(bool q) { _quantize_record = q; }
+ virtual void set_play_state(PlayState state) { _play_state = state; }
+
+ virtual bool is_activated() const { return false; }
+ virtual void activate() {}
+ virtual void deactivate() {}
+
+ PlayState play_state() const { return _play_state; }
+
+protected:
+ Forge& _forge;
+ SPtr<Machine> _machine;
+ SPtr<Raul::RingBuffer> _updates;
+ PlayState _play_state;
+ double _bpm;
+ double _quantization;
+ bool _quantize_record;
+};
+
+} // namespace machina
+
+#endif // MACHINA_JACKDRIVER_HPP
diff --git a/src/engine/machina/Engine.hpp b/src/engine/machina/Engine.hpp
new file mode 100644
index 0000000..dd40f42
--- /dev/null
+++ b/src/engine/machina/Engine.hpp
@@ -0,0 +1,68 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_ENGINE_HPP
+#define MACHINA_ENGINE_HPP
+
+#include <string>
+
+#include "machina/Atom.hpp"
+#include "machina/Driver.hpp"
+#include "machina/Loader.hpp"
+#include "machina/types.hpp"
+
+namespace machina {
+
+class Machine;
+
+class Engine
+{
+public:
+ Engine(Forge& forge,
+ SPtr<Driver> driver,
+ Sord::World& rdf_world);
+
+ Sord::World& rdf_world() { return _rdf_world; }
+
+ static SPtr<Driver> new_driver(Forge& forge,
+ const std::string& name,
+ SPtr<Machine> machine);
+
+ SPtr<Driver> driver() { return _driver; }
+ SPtr<Machine> machine() { return _driver->machine(); }
+ Forge& forge() { return _forge; }
+
+ SPtr<Machine> load_machine(const std::string& uri);
+ SPtr<Machine> load_machine_midi(const std::string& uri,
+ double q,
+ Raul::TimeDuration dur);
+
+ void export_midi(const std::string& filename,
+ Raul::TimeDuration dur);
+
+ void set_bpm(double bpm);
+ void set_quantization(double beat_fraction);
+
+private:
+ SPtr<Driver> _driver;
+ Sord::World& _rdf_world;
+ Loader _loader;
+ Forge _forge;
+};
+
+} // namespace machina
+
+#endif // MACHINA_ENGINE_HPP
diff --git a/src/engine/machina/Evolver.hpp b/src/engine/machina/Evolver.hpp
new file mode 100644
index 0000000..400c177
--- /dev/null
+++ b/src/engine/machina/Evolver.hpp
@@ -0,0 +1,72 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2017 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_EVOLVER_HPP
+#define MACHINA_EVOLVER_HPP
+
+#include <atomic>
+#include <memory>
+#include <thread>
+
+#include "eugene/GA.hpp"
+#include "eugene/Random.hpp"
+#include "machina/types.hpp"
+#include "raul/TimeStamp.hpp"
+
+#include "Machine.hpp"
+#include "Schrodinbit.hpp"
+
+namespace eugene {
+template<typename G> class HybridMutation;
+}
+
+namespace machina {
+
+class Problem;
+
+class Evolver
+{
+public:
+ Evolver(Raul::TimeUnit unit,
+ const std::string& target_midi,
+ SPtr<Machine> seed);
+
+ void seed(SPtr<Machine> parent);
+ bool improvement() { return _improvement; }
+
+ void start();
+ void join();
+
+ const Machine& best() { return _ga->best(); }
+
+ typedef eugene::GA<Machine> MachinaGA;
+
+private:
+ void run();
+
+ eugene::Random _rng;
+ SPtr<MachinaGA> _ga;
+ SPtr<Problem> _problem;
+ float _seed_fitness;
+ Schrodinbit _improvement;
+ std::atomic<bool> _exit_flag;
+
+ std::unique_ptr<std::thread> _thread;
+};
+
+} // namespace machina
+
+#endif // MACHINA_EVOLVER_HPP
diff --git a/src/engine/machina/Loader.hpp b/src/engine/machina/Loader.hpp
new file mode 100644
index 0000000..3fa66ff
--- /dev/null
+++ b/src/engine/machina/Loader.hpp
@@ -0,0 +1,49 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_LOADER_HPP
+#define MACHINA_LOADER_HPP
+
+#include "machina/Atom.hpp"
+#include "machina/types.hpp"
+#include "raul/TimeStamp.hpp"
+#include "sord/sordmm.hpp"
+
+using Sord::Namespaces;
+
+namespace machina {
+
+class Machine;
+
+class Loader
+{
+public:
+ Loader(Forge& forge, Sord::World& rdf_world);
+
+ SPtr<Machine> load(const std::string& filename);
+
+ SPtr<Machine> load_midi(const std::string& filename,
+ double q,
+ Raul::TimeDuration dur);
+
+private:
+ Forge& _forge;
+ Sord::World& _rdf_world;
+};
+
+} // namespace machina
+
+#endif // MACHINA_LOADER_HPP
diff --git a/src/engine/machina/Machine.hpp b/src/engine/machina/Machine.hpp
new file mode 100644
index 0000000..7c9855d
--- /dev/null
+++ b/src/engine/machina/Machine.hpp
@@ -0,0 +1,132 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_MACHINE_HPP
+#define MACHINA_MACHINE_HPP
+
+#include <vector>
+#include <set>
+
+#include "machina/types.hpp"
+#include "machina/Atom.hpp"
+#include "raul/RingBuffer.hpp"
+#include "raul/TimeSlice.hpp"
+#include "sord/sordmm.hpp"
+
+#include "types.hpp"
+#include "Node.hpp"
+
+namespace machina {
+
+class Context;
+class LearnRequest;
+
+/** A (Finite State) Machine.
+ */
+class Machine : public Stateful
+{
+public:
+ explicit Machine(TimeUnit unit);
+
+ /** Copy a Machine.
+ *
+ * Creates a deep copy which is the 'same' machine, but with fresh state,
+ * i.e. all nodes are inactive and time is at zero.
+ */
+ Machine(const Machine& copy);
+
+ /** Completely replace this machine's contents with a deep copy. */
+ Machine& operator=(const Machine& copy);
+
+ bool operator==(const Machine& rhs) const;
+
+ /** Merge another machine into this machine. */
+ void merge(const Machine& machine);
+
+ bool is_empty() { return _nodes.empty(); }
+ bool is_finished() { return _is_finished; }
+
+ void add_node(SPtr<Node> node);
+ void remove_node(SPtr<Node> node);
+ void learn(SPtr<Raul::Maid> maid, SPtr<Node> node);
+
+ void write_state(Sord::Model& model);
+
+ /** Exit all active nodes and reset time to 0. */
+ void reset(MIDISink* sink, Raul::TimeStamp time);
+
+ /** Run the machine for a (real) time slice.
+ *
+ * Returns the duration of time the machine actually ran in frames.
+ *
+ * Caller can check is_finished() to determine if the machine still has any
+ * active nodes. If not, time() will return the exact time stamp the
+ * machine actually finished on (so it can be restarted immediately
+ * with sample accuracy if necessary).
+ */
+ uint32_t run(Context& context, SPtr<Raul::RingBuffer> updates);
+
+ // Any context
+ inline Raul::TimeStamp time() const { return _time; }
+
+ SPtr<LearnRequest> pending_learn() { return _pending_learn; }
+ void clear_pending_learn() { _pending_learn.reset(); }
+
+ typedef std::set< SPtr<Node> > Nodes;
+ Nodes& nodes() { return _nodes; }
+ const Nodes& nodes() const { return _nodes; }
+
+ SPtr<Node> initial_node() const { return _initial_node; }
+
+ SPtr<Node> random_node();
+ SPtr<Edge> random_edge();
+
+ float fitness; // For GA
+
+private:
+ /** Return the active Node with the earliest exit time. */
+ SPtr<Node> earliest_node() const;
+
+ void assign(const Machine& other);
+
+ /** Enter a node at the current time (called by run()).
+ *
+ * @return true if node was entered, otherwise voics are exhausted.
+ */
+ bool enter_node(Context& context,
+ SPtr<Node> node,
+ SPtr<Raul::RingBuffer> updates);
+
+ /** Exit a node at the current time (called by run()). */
+ void exit_node(Context& context,
+ SPtr<Node> node,
+ SPtr<Raul::RingBuffer> updates);
+
+ static const size_t MAX_ACTIVE_NODES = 128;
+
+ SPtr<Node> _initial_node;
+ std::vector< SPtr<Node> > _active_nodes;
+
+ SPtr<LearnRequest> _pending_learn;
+ Nodes _nodes;
+ Raul::TimeStamp _time;
+
+ bool _is_finished;
+};
+
+} // namespace machina
+
+#endif // MACHINA_MACHINE_HPP
diff --git a/src/engine/machina/Model.hpp b/src/engine/machina/Model.hpp
new file mode 100644
index 0000000..32a332e
--- /dev/null
+++ b/src/engine/machina/Model.hpp
@@ -0,0 +1,44 @@
+/*
+ This file is part of Machina.
+ Copyright 2014 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_MODEL_HPP
+#define MACHINA_MODEL_HPP
+
+#include <stdint.h>
+
+#include <map>
+
+#include "machina/Atom.hpp"
+#include "machina/types.hpp"
+
+namespace machina {
+
+class Model
+{
+public:
+ virtual ~Model() {}
+
+ virtual void new_object(uint64_t id, const Properties& properties) = 0;
+
+ virtual void erase_object(uint64_t id) = 0;
+
+ virtual void set(uint64_t id, URIInt key, const Atom& value) = 0;
+ virtual const Atom& get(uint64_t id, URIInt key) const = 0;
+};
+
+} // namespace machina
+
+#endif // MACHINA_MODEL_HPP
diff --git a/src/engine/machina/Mutation.hpp b/src/engine/machina/Mutation.hpp
new file mode 100644
index 0000000..69f5ee4
--- /dev/null
+++ b/src/engine/machina/Mutation.hpp
@@ -0,0 +1,60 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_MACHINE_MUTATION_HPP
+#define MACHINA_MACHINE_MUTATION_HPP
+
+#include "machina_config.h"
+
+#ifdef HAVE_EUGENE
+# include "eugene/Mutation.hpp"
+# define SUPER : public eugene::Mutation<Machine>
+#else
+# define SUPER : public Mutation
+#endif
+
+namespace machina {
+
+#ifdef HAVE_EUGENE
+typedef eugene::Random Random;
+#else
+struct Random {};
+#endif
+
+class Machine;
+
+namespace Mutation {
+
+struct Mutation {
+ virtual ~Mutation() {}
+
+ virtual void mutate(Random& rng, Machine& machine) = 0;
+};
+
+struct Compress SUPER { void mutate(Random& rng, Machine& machine); };
+struct AddNode SUPER { void mutate(Random& rng, Machine& machine); };
+struct RemoveNode SUPER { void mutate(Random& rng, Machine& machine); };
+struct AdjustNode SUPER { void mutate(Random& rng, Machine& machine); };
+struct SwapNodes SUPER { void mutate(Random& rng, Machine& machine); };
+struct AddEdge SUPER { void mutate(Random& rng, Machine& machine); };
+struct RemoveEdge SUPER { void mutate(Random& rng, Machine& machine); };
+struct AdjustEdge SUPER { void mutate(Random& rng, Machine& machine); };
+
+} // namespace Mutation
+
+} // namespace machina
+
+#endif // MACHINA_MACHINE_MUTATION_HPP
diff --git a/src/engine/machina/URIs.hpp b/src/engine/machina/URIs.hpp
new file mode 100644
index 0000000..f770723
--- /dev/null
+++ b/src/engine/machina/URIs.hpp
@@ -0,0 +1,93 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_URIS_HPP
+#define MACHINA_URIS_HPP
+
+#include <stdint.h>
+
+#include "machina/Atom.hpp"
+#include "machina/types.hpp"
+
+#define MACHINA_URI_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+
+#define MACHINA_NS "http://drobilla.net/ns/machina#"
+
+#define MACHINA_NS_Machine MACHINA_NS "Machine"
+#define MACHINA_NS_Node MACHINA_NS "Node"
+#define MACHINA_NS_SelectorNode MACHINA_NS "SelectorNode"
+#define MACHINA_NS_arc MACHINA_NS "arc"
+#define MACHINA_NS_duration MACHINA_NS "duration"
+#define MACHINA_NS_head MACHINA_NS "head"
+#define MACHINA_NS_node MACHINA_NS "node"
+#define MACHINA_NS_onEnter MACHINA_NS "onEnter"
+#define MACHINA_NS_onExit MACHINA_NS "onExit"
+#define MACHINA_NS_probability MACHINA_NS "probability"
+#define MACHINA_NS_start MACHINA_NS "start"
+#define MACHINA_NS_tail MACHINA_NS "tail"
+
+namespace machina {
+
+class URIs
+{
+public:
+ static void init() { _instance = new URIs(); }
+
+ static inline const URIs& instance() { assert(_instance); return *_instance; }
+
+ URIInt machina_Edge;
+ URIInt machina_MidiAction;
+ URIInt machina_Node;
+ URIInt machina_active;
+ URIInt machina_canvas_x;
+ URIInt machina_canvas_y;
+ URIInt machina_duration;
+ URIInt machina_enter_action;
+ URIInt machina_exit_action;
+ URIInt machina_head_id;
+ URIInt machina_initial;
+ URIInt machina_note_number;
+ URIInt machina_probability;
+ URIInt machina_selector;
+ URIInt machina_tail_id;
+ URIInt rdf_type;
+
+private:
+ URIs()
+ : machina_Edge(100)
+ , machina_MidiAction(101)
+ , machina_Node(102)
+ , machina_active(1)
+ , machina_canvas_x(2)
+ , machina_canvas_y(3)
+ , machina_duration(4)
+ , machina_enter_action(11)
+ , machina_exit_action(12)
+ , machina_head_id(5)
+ , machina_initial(6)
+ , machina_note_number(13)
+ , machina_probability(7)
+ , machina_selector(8)
+ , machina_tail_id(9)
+ , rdf_type(10)
+ {}
+
+ static URIs* _instance;
+};
+
+} // namespace machina
+
+#endif // MACHINA_URIS_HPP
diff --git a/src/engine/machina/Updates.hpp b/src/engine/machina/Updates.hpp
new file mode 100644
index 0000000..ff09af9
--- /dev/null
+++ b/src/engine/machina/Updates.hpp
@@ -0,0 +1,46 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_UPDATES_HPP
+#define MACHINA_UPDATES_HPP
+
+#include <stdint.h>
+
+#include "machina/Atom.hpp"
+#include "machina/types.hpp"
+#include "raul/RingBuffer.hpp"
+
+namespace machina {
+
+enum UpdateType {
+ UPDATE_SET = 1
+};
+
+void
+write_set(SPtr<Raul::RingBuffer> buf,
+ uint64_t subject,
+ URIInt key,
+ const Atom& value);
+
+uint32_t
+read_set(SPtr<Raul::RingBuffer> buf,
+ uint64_t* subject,
+ URIInt* key,
+ Atom* value);
+
+} // namespace machina
+
+#endif // MACHINA_UPDATES_HPP
diff --git a/src/engine/machina/types.hpp b/src/engine/machina/types.hpp
new file mode 100644
index 0000000..61137b7
--- /dev/null
+++ b/src/engine/machina/types.hpp
@@ -0,0 +1,69 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2014 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_TYPES_HPP
+#define MACHINA_TYPES_HPP
+
+#include <map>
+#include <memory>
+
+#include "raul/Maid.hpp"
+
+namespace machina {
+
+typedef unsigned char byte;
+
+typedef uint32_t URIInt;
+
+class Atom;
+typedef std::map<URIInt, Atom> Properties;
+
+#if __cplusplus >= 201103L
+template <class T>
+using SPtr = std::shared_ptr<T>;
+
+template <class T>
+using WPtr = std::weak_ptr<T>;
+
+template <class T>
+using MPtr = Raul::managed_ptr<T>;
+#else
+#define SPtr std::shared_ptr
+#define WPtr std::weak_ptr
+#define MPtr Raul::managed_ptr
+#endif
+
+template <class T>
+void NullDeleter(T* ptr) {}
+
+template<class T, class U>
+SPtr<T> static_ptr_cast(const SPtr<U>& r) {
+ return std::static_pointer_cast<T>(r);
+}
+
+template<class T, class U>
+SPtr<T> dynamic_ptr_cast(const SPtr<U>& r) {
+ return std::dynamic_pointer_cast<T>(r);
+}
+
+template<class T, class U>
+SPtr<T> const_ptr_cast(const SPtr<U>& r) {
+ return std::const_pointer_cast<T>(r);
+}
+
+} // namespace machina
+
+#endif // MACHINA_TYPES_HPP
diff --git a/src/engine/quantize.hpp b/src/engine/quantize.hpp
new file mode 100644
index 0000000..9cf939a
--- /dev/null
+++ b/src/engine/quantize.hpp
@@ -0,0 +1,46 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_QUANTIZE_HPP
+#define MACHINA_QUANTIZE_HPP
+
+#include <cmath>
+
+#include "raul/TimeStamp.hpp"
+
+namespace machina {
+
+inline TimeStamp
+quantize(TimeStamp q, TimeStamp t)
+{
+ assert(q.unit() == t.unit());
+ // FIXME: Precision problem? Should probably stay in discrete domain
+ const double qd = q.to_double();
+ const double td = t.to_double();
+ return TimeStamp(t.unit(), (qd > 0) ? lrint(td / qd) * qd : td);
+}
+
+inline double
+quantize(double q, double t)
+{
+ return (q > 0)
+ ? lrint(t / q) * q
+ : t;
+}
+
+} // namespace machina
+
+#endif // MACHINA_QUANTIZE_HPP
diff --git a/src/engine/quantize_test.cpp b/src/engine/quantize_test.cpp
new file mode 100644
index 0000000..5410489
--- /dev/null
+++ b/src/engine/quantize_test.cpp
@@ -0,0 +1,41 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <iostream>
+
+#include "quantize.hpp"
+
+using namespace std;
+using namespace machina;
+
+int
+main()
+{
+ TimeStamp q(TimeUnit(TimeUnit::BEATS, 19200), 0.25);
+
+ for (double in = 0.0; in < 32; in += 0.23) {
+ TimeStamp beats(TimeUnit(TimeUnit::BEATS, 19200), in);
+
+ /*cout << "Q(" << in << ", 1/4) = "
+ << quantize(q, beats) << endl;*/
+
+ if (quantize(q, beats).subticks() % (19200 / 4) != 0) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/engine/smf_test.cpp b/src/engine/smf_test.cpp
new file mode 100644
index 0000000..d98a696
--- /dev/null
+++ b/src/engine/smf_test.cpp
@@ -0,0 +1,84 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <iostream>
+#include <string>
+#include "raul/log.hpp"
+#include "raul/SMFReader.hpp"
+#include "raul/SMFWriter.hpp"
+
+using namespace std;
+using namespace machina;
+
+int
+main(int argc, char** argv)
+{
+#define CHECK(cond) \
+ do { if (!(cond)) { \
+ error << "Test at " << __FILE__ << ":" << __LINE__ \
+ << " failed: " << __STRING(cond) << endl; \
+ return 1; \
+ } \
+ } \
+ while (0)
+
+ static const uint16_t ppqn = 19200;
+
+ const char* filename = NULL;
+
+ if (argc < 2) {
+ filename = "./test.mid";
+ SMFWriter writer(TimeUnit(TimeUnit::BEATS, ppqn));
+ writer.start(string(filename), TimeStamp(writer.unit(), 0, 0));
+ writer.finish();
+ } else {
+ filename = argv[1];
+ }
+
+ SMFReader reader;
+ bool opened = reader.open(filename);
+
+ if (!opened) {
+ cerr << "Unable to open SMF file " << filename << endl;
+ return -1;
+ }
+
+ CHECK(reader.type() == 0);
+ CHECK(reader.num_tracks() == 1);
+ CHECK(reader.ppqn() == ppqn);
+
+ for (unsigned t = 1; t <= reader.num_tracks(); ++t) {
+ reader.seek_to_track(t);
+
+ unsigned char buf[4];
+ uint32_t ev_size;
+ uint32_t ev_delta_time;
+ while (reader.read_event(4, buf, &ev_size, &ev_delta_time) >= 0) {
+
+ cout << t << ": Event, size = " << ev_size << ", time = "
+ << ev_delta_time;
+ cout << ":\t";
+ cout.flags(ios::hex);
+ for (uint32_t i = 0; i < ev_size; ++i) {
+ cout << "0x" << static_cast<int>(buf[i]) << " ";
+ }
+ cout.flags(ios::dec);
+ cout << endl;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/engine/wscript b/src/engine/wscript
new file mode 100644
index 0000000..1ec63a2
--- /dev/null
+++ b/src/engine/wscript
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+from waflib.extras import autowaf as autowaf
+
+def build(bld):
+ core_source = '''
+ ActionFactory.cpp
+ Controller.cpp
+ Edge.cpp
+ Engine.cpp
+ JackDriver.cpp
+ LearnRequest.cpp
+ Loader.cpp
+ Machine.cpp
+ MachineBuilder.cpp
+ MidiAction.cpp
+ Mutation.cpp
+ Node.cpp
+ Recorder.cpp
+ SMFDriver.cpp
+ SMFReader.cpp
+ SMFWriter.cpp
+ Stateful.cpp
+ Updates.cpp
+ URIs.cpp
+ '''
+ if bld.env.HAVE_EUGENE:
+ core_source += '''
+ Evolver.cpp
+ Problem.cpp
+ '''
+ obj = bld(features = 'cxx cxxshlib')
+ obj.source = core_source
+ obj.export_includes = ['.']
+ obj.includes = ['.', '..', '../..']
+ obj.name = 'libmachina_engine'
+ obj.target = 'machina_engine'
+ obj.cxxflags = '-pthread'
+ if bld.env.CXX_NAME != 'clang':
+ obj.linkflags = '-pthread'
+ core_libs = 'RAUL SERD SORD JACK LV2'
+ if bld.env.HAVE_EUGENE:
+ core_libs += ' EUGENE '
+ autowaf.use_lib(bld, obj, core_libs)
+
+ bld.add_post_fun(autowaf.run_ldconfig)
diff --git a/src/gui/EdgeView.cpp b/src/gui/EdgeView.cpp
new file mode 100644
index 0000000..b4c6099
--- /dev/null
+++ b/src/gui/EdgeView.cpp
@@ -0,0 +1,142 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "ganv/Canvas.hpp"
+
+#include "machina/Controller.hpp"
+#include "machina/types.hpp"
+
+#include "EdgeView.hpp"
+#include "MachinaCanvas.hpp"
+#include "MachinaGUI.hpp"
+#include "NodeView.hpp"
+
+namespace machina {
+namespace gui {
+
+/* probability colour stuff */
+
+#define RGB_TO_UINT(r, g, b) ((((guint)(r)) << 16) | (((guint)(g)) << 8) | ((guint)(b)))
+#define RGB_TO_RGBA(x, a) (((x) << 8) | ((((guint)a) & 0xff)))
+#define RGBA_TO_UINT(r, g, b, a) RGB_TO_RGBA(RGB_TO_UINT(r, g, b), a)
+
+#define UINT_RGBA_R(x) (((uint32_t)(x)) >> 24)
+#define UINT_RGBA_G(x) ((((uint32_t)(x)) >> 16) & 0xff)
+#define UINT_RGBA_B(x) ((((uint32_t)(x)) >> 8) & 0xff)
+#define UINT_RGBA_A(x) (((uint32_t)(x)) & 0xff)
+
+#define MONO_INTERPOLATE(v1, v2, t) ((int)rint((v2) * (t) + (v1) * (1 - (t))))
+
+#define UINT_INTERPOLATE(c1, c2, t) \
+ RGBA_TO_UINT(MONO_INTERPOLATE(UINT_RGBA_R(c1), UINT_RGBA_R(c2), t), \
+ MONO_INTERPOLATE(UINT_RGBA_G(c1), UINT_RGBA_G(c2), t), \
+ MONO_INTERPOLATE(UINT_RGBA_B(c1), UINT_RGBA_B(c2), t), \
+ MONO_INTERPOLATE(UINT_RGBA_A(c1), UINT_RGBA_A(c2), t) )
+
+inline static uint32_t edge_color(float prob)
+{
+ static const uint32_t min = 0xFF4444FF;
+ static const uint32_t mid = 0xFFFF44FF;
+ static const uint32_t max = 0x44FF44FF;
+
+ if (prob <= 0.5) {
+ return UINT_INTERPOLATE(min, mid, prob * 2.0);
+ } else {
+ return UINT_INTERPOLATE(mid, max, (prob - 0.5) * 2.0);
+ }
+}
+
+/* end probability colour stuff */
+
+using namespace Ganv;
+
+EdgeView::EdgeView(Canvas& canvas,
+ NodeView* src,
+ NodeView* dst,
+ SPtr<machina::client::ClientObject> edge)
+ : Ganv::Edge(canvas, src, dst, 0x9FA0A0FF, true, false)
+ , _edge(edge)
+{
+ set_color(edge_color(probability()));
+
+ edge->signal_property.connect(
+ sigc::mem_fun(this, &EdgeView::on_property));
+
+ signal_event().connect(
+ sigc::mem_fun(this, &EdgeView::on_event));
+}
+
+EdgeView::~EdgeView()
+{
+ _edge->set_view(NULL);
+}
+
+float
+EdgeView::probability() const
+{
+ return _edge->get(URIs::instance().machina_probability).get<float>();
+}
+
+double
+EdgeView::length_hint() const
+{
+ NodeView* tail = dynamic_cast<NodeView*>(get_tail());
+ return tail->node()->get(URIs::instance().machina_duration).get<float>()
+ * 10.0;
+}
+
+void
+EdgeView::show_label(bool show)
+{
+ set_color(edge_color(probability()));
+}
+
+bool
+EdgeView::on_event(GdkEvent* ev)
+{
+ MachinaCanvas* canvas = dynamic_cast<MachinaCanvas*>(this->canvas());
+ Forge& forge = canvas->app()->forge();
+
+ if (ev->type == GDK_BUTTON_PRESS) {
+ if (ev->button.state & GDK_CONTROL_MASK) {
+ if (ev->button.button == 1) {
+ canvas->app()->controller()->set_property(
+ _edge->id(),
+ URIs::instance().machina_probability,
+ forge.make(float(probability() - 0.1f)));
+ return true;
+ } else if (ev->button.button == 3) {
+ canvas->app()->controller()->set_property(
+ _edge->id(),
+ URIs::instance().machina_probability,
+ forge.make(float(probability() + 0.1f)));
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void
+EdgeView::on_property(machina::URIInt key, const Atom& value)
+{
+ if (key == URIs::instance().machina_probability) {
+ set_color(edge_color(value.get<float>()));
+ }
+}
+
+} // namespace machina
+} // namespace gui
diff --git a/src/gui/EdgeView.hpp b/src/gui/EdgeView.hpp
new file mode 100644
index 0000000..1ad2dd0
--- /dev/null
+++ b/src/gui/EdgeView.hpp
@@ -0,0 +1,59 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_EDGEVIEW_HPP
+#define MACHINA_EDGEVIEW_HPP
+
+#include "ganv/Edge.hpp"
+
+#include "client/ClientObject.hpp"
+
+#include "machina/types.hpp"
+
+namespace machina {
+namespace gui {
+
+class NodeView;
+
+class EdgeView
+ : public Ganv::Edge
+ , public machina::client::ClientObject::View
+{
+public:
+ EdgeView(Ganv::Canvas& canvas,
+ NodeView* src,
+ NodeView* dst,
+ SPtr<machina::client::ClientObject> edge);
+
+ ~EdgeView();
+
+ void show_label(bool show);
+
+ virtual double length_hint() const;
+
+private:
+ bool on_event(GdkEvent* ev);
+ void on_property(machina::URIInt key, const Atom& value);
+
+ float probability() const;
+
+ SPtr<machina::client::ClientObject> _edge;
+};
+
+} // namespace machina
+} // namespace gui
+
+#endif // MACHINA_EDGEVIEW_HPP
diff --git a/src/gui/MachinaCanvas.cpp b/src/gui/MachinaCanvas.cpp
new file mode 100644
index 0000000..a9e51c4
--- /dev/null
+++ b/src/gui/MachinaCanvas.cpp
@@ -0,0 +1,211 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2014 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <map>
+
+#include "raul/TimeStamp.hpp"
+
+#include "client/ClientModel.hpp"
+#include "client/ClientObject.hpp"
+#include "machina/Controller.hpp"
+#include "machina/Engine.hpp"
+#include "machina/types.hpp"
+
+#include "EdgeView.hpp"
+#include "MachinaCanvas.hpp"
+#include "MachinaGUI.hpp"
+#include "NodeView.hpp"
+
+using namespace Raul;
+using namespace Ganv;
+
+namespace machina {
+namespace gui {
+
+MachinaCanvas::MachinaCanvas(MachinaGUI* app, int width, int height)
+ : Canvas(width, height)
+ , _app(app)
+ , _connect_node(NULL)
+ , _did_connect(false)
+{
+ widget().grab_focus();
+
+ signal_event.connect(sigc::mem_fun(this, &MachinaCanvas::on_event));
+}
+
+void
+MachinaCanvas::connect_nodes(GanvNode* node, void* data)
+{
+ MachinaCanvas* canvas = (MachinaCanvas*)data;
+ NodeView* view = dynamic_cast<NodeView*>(Glib::wrap(node));
+ if (!view || view == canvas->_connect_node) {
+ return;
+ }
+ if (canvas->get_edge(view, canvas->_connect_node)) {
+ canvas->action_disconnect(view, canvas->_connect_node);
+ canvas->_did_connect = true;
+ } else if (!canvas->get_edge(canvas->_connect_node, view)) {
+ canvas->action_connect(view, canvas->_connect_node);
+ canvas->_did_connect = true;
+ }
+}
+
+bool
+MachinaCanvas::node_clicked(NodeView* node, GdkEventButton* event)
+{
+ if (event->state & (GDK_CONTROL_MASK|GDK_SHIFT_MASK)) {
+ return false;
+
+ } else if (event->button == 2) {
+ // Middle click: learn
+ _app->controller()->learn(_app->maid(), node->node()->id());
+ return false;
+
+ } else if (event->button == 1) {
+ // Left click: connect/disconnect
+ _connect_node = node;
+ for_each_selected_node(connect_nodes, this);
+ const bool handled = _did_connect;
+ _connect_node = NULL;
+ _did_connect = false;
+ if (_app->chain_mode()) {
+ return false; // Cause Ganv to select as usual
+ } else {
+ return handled; // If we did something, stop event here
+ }
+ }
+
+ return false;
+}
+
+bool
+MachinaCanvas::on_event(GdkEvent* event)
+{
+ if (event->type == GDK_BUTTON_RELEASE
+ && event->button.button == 3
+ && !(event->button.state & (GDK_CONTROL_MASK))) {
+
+ action_create_node(event->button.x, event->button.y);
+ return true;
+ }
+ return false;
+}
+
+void
+MachinaCanvas::on_new_object(SPtr<client::ClientObject> object)
+{
+ const machina::URIs& uris = URIs::instance();
+ const Atom& type = object->get(uris.rdf_type);
+ if (!type.is_valid()) {
+ return;
+ }
+
+ if (type.get<URIInt>() == uris.machina_Node) {
+ const Atom& node_x = object->get(uris.machina_canvas_x);
+ const Atom& node_y = object->get(uris.machina_canvas_y);
+ float x, y;
+ if (node_x.type() == _app->forge().Float &&
+ node_y.type() == _app->forge().Float) {
+ x = node_x.get<float>();
+ y = node_y.get<float>();
+ } else {
+ int scroll_x, scroll_y;
+ get_scroll_offsets(scroll_x, scroll_y);
+ x = scroll_x + 128.0;
+ y = scroll_y + 128.0;
+ }
+
+ NodeView* view = new NodeView(_app->window(), *this, object, x, y);
+
+ //if ( ! node->enter_action() && ! node->exit_action() )
+ // view->set_base_color(0x101010FF);
+
+ view->signal_clicked().connect(
+ sigc::bind<0>(sigc::mem_fun(this, &MachinaCanvas::node_clicked),
+ view));
+
+ object->set_view(view);
+
+ } else if (type.get<URIInt>() == uris.machina_Edge) {
+ SPtr<machina::client::ClientObject> tail = _app->client_model()->find(
+ object->get(uris.machina_tail_id).get<int32_t>());
+ SPtr<machina::client::ClientObject> head = _app->client_model()->find(
+ object->get(uris.machina_head_id).get<int32_t>());
+
+ if (!tail || !head) {
+ std::cerr << "Invalid arc "
+ << object->get(uris.machina_tail_id).get<int32_t>()
+ << " => "
+ << object->get(uris.machina_head_id).get<int32_t>()
+ << std::endl;
+ return;
+ }
+
+ NodeView* tail_view = dynamic_cast<NodeView*>(tail->view());
+ NodeView* head_view = dynamic_cast<NodeView*>(head->view());
+
+ object->set_view(new EdgeView(*this, tail_view, head_view, object));
+
+ } else {
+ std::cerr << "Unknown object type " << type.get<URIInt>() << std::endl;
+ }
+}
+
+void
+MachinaCanvas::on_erase_object(SPtr<client::ClientObject> object)
+{
+ const Atom& type = object->get(URIs::instance().rdf_type);
+ if (type.get<URIInt>() == URIs::instance().machina_Node) {
+ delete object->view();
+ object->set_view(NULL);
+ } else if (type.get<URIInt>() == URIs::instance().machina_Edge) {
+ remove_edge(dynamic_cast<Ganv::Edge*>(object->view()));
+ object->set_view(NULL);
+ } else {
+ std::cerr << "Unknown object type" << std::endl;
+ }
+}
+
+void
+MachinaCanvas::action_create_node(double x, double y)
+{
+ const Properties props = {
+ { URIs::instance().rdf_type,
+ _app->forge().make_urid(URIs::instance().machina_Node) },
+ { URIs::instance().machina_canvas_x,
+ _app->forge().make((float)x) },
+ { URIs::instance().machina_canvas_y,
+ _app->forge().make((float)y) },
+ { URIs::instance().machina_duration,
+ _app->forge().make((float)_app->default_length()) } };
+
+ _app->controller()->create(props);
+}
+
+void
+MachinaCanvas::action_connect(NodeView* tail, NodeView* head)
+{
+ _app->controller()->connect(tail->node()->id(), head->node()->id());
+}
+
+void
+MachinaCanvas::action_disconnect(NodeView* tail, NodeView* head)
+{
+ _app->controller()->disconnect(tail->node()->id(), head->node()->id());
+}
+
+} // namespace machina
+} // namespace gui
diff --git a/src/gui/MachinaCanvas.hpp b/src/gui/MachinaCanvas.hpp
new file mode 100644
index 0000000..f32e6cb
--- /dev/null
+++ b/src/gui/MachinaCanvas.hpp
@@ -0,0 +1,66 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_CANVAS_HPP_HPP
+#define MACHINA_CANVAS_HPP_HPP
+
+#include <string>
+
+#include "ganv/Canvas.hpp"
+#include "machina/types.hpp"
+
+using namespace Ganv;
+
+namespace machina {
+
+namespace client { class ClientObject; }
+
+namespace gui {
+
+class MachinaGUI;
+class NodeView;
+
+class MachinaCanvas : public Canvas
+{
+public:
+ MachinaCanvas(MachinaGUI* app, int width, int height);
+
+ void on_new_object(SPtr<machina::client::ClientObject> object);
+ void on_erase_object(SPtr<machina::client::ClientObject> object);
+
+ MachinaGUI* app() { return _app; }
+
+protected:
+ bool on_event(GdkEvent* event);
+
+ bool node_clicked(NodeView* node, GdkEventButton* ev);
+
+private:
+ void action_create_node(double x, double y);
+ void action_connect(NodeView* tail, NodeView* head);
+ void action_disconnect(NodeView* tail, NodeView* head);
+
+ static void connect_nodes(GanvNode* node, void* data);
+
+ MachinaGUI* _app;
+ NodeView* _connect_node;
+ bool _did_connect;
+};
+
+} // namespace machina
+} // namespace gui
+
+#endif // MACHINA_CANVAS_HPP_HPP
diff --git a/src/gui/MachinaGUI.cpp b/src/gui/MachinaGUI.cpp
new file mode 100644
index 0000000..ff71ca7
--- /dev/null
+++ b/src/gui/MachinaGUI.cpp
@@ -0,0 +1,750 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "machina_config.h"
+
+#include <cmath>
+#include <fstream>
+#include <limits.h>
+#include <pthread.h>
+#include "sord/sordmm.hpp"
+#include "machina/Controller.hpp"
+#include "machina/Engine.hpp"
+#include "machina/Machine.hpp"
+#include "machina/Mutation.hpp"
+#include "machina/Updates.hpp"
+#include "client/ClientModel.hpp"
+#include "WidgetFactory.hpp"
+#include "MachinaGUI.hpp"
+#include "MachinaCanvas.hpp"
+#include "NodeView.hpp"
+#include "EdgeView.hpp"
+
+#ifdef HAVE_EUGENE
+#include "machina/Evolver.hpp"
+#endif
+
+namespace machina {
+namespace gui {
+
+MachinaGUI::MachinaGUI(SPtr<machina::Engine> engine)
+ : _unit(TimeUnit::BEATS, 19200)
+ , _engine(engine)
+ , _client_model(new machina::client::ClientModel())
+ , _controller(new machina::Controller(_engine, *_client_model.get()))
+ , _maid(new Raul::Maid())
+ , _refresh(false)
+ , _evolve(false)
+ , _chain_mode(true)
+{
+ _canvas = SPtr<MachinaCanvas>(new MachinaCanvas(this, 1600*2, 1200*2));
+
+ Glib::RefPtr<Gtk::Builder> xml = WidgetFactory::create();
+
+ xml->get_widget("machina_win", _main_window);
+ xml->get_widget("about_win", _about_window);
+ xml->get_widget("help_dialog", _help_dialog);
+ xml->get_widget("toolbar", _toolbar);
+ xml->get_widget("open_menuitem", _menu_file_open);
+ xml->get_widget("save_menuitem", _menu_file_save);
+ xml->get_widget("save_as_menuitem", _menu_file_save_as);
+ xml->get_widget("quit_menuitem", _menu_file_quit);
+ xml->get_widget("zoom_in_menuitem", _menu_zoom_in);
+ xml->get_widget("zoom_out_menuitem", _menu_zoom_out);
+ xml->get_widget("zoom_normal_menuitem", _menu_zoom_normal);
+ xml->get_widget("arrange_menuitem", _menu_view_arrange);
+ xml->get_widget("import_midi_menuitem", _menu_import_midi);
+ xml->get_widget("export_midi_menuitem", _menu_export_midi);
+ xml->get_widget("export_graphviz_menuitem", _menu_export_graphviz);
+ xml->get_widget("view_toolbar_menuitem", _menu_view_toolbar);
+ xml->get_widget("view_labels_menuitem", _menu_view_labels);
+ xml->get_widget("help_about_menuitem", _menu_help_about);
+ xml->get_widget("help_help_menuitem", _menu_help_help);
+ xml->get_widget("canvas_scrolledwindow", _canvas_scrolledwindow);
+ xml->get_widget("bpm_spinbutton", _bpm_spinbutton);
+ xml->get_widget("quantize_checkbutton", _quantize_checkbutton);
+ xml->get_widget("quantize_spinbutton", _quantize_spinbutton);
+ xml->get_widget("stop_but", _stop_button);
+ xml->get_widget("play_but", _play_button);
+ xml->get_widget("record_but", _record_button);
+ xml->get_widget("step_record_but", _step_record_button);
+ xml->get_widget("chain_but", _chain_button);
+ xml->get_widget("fan_but", _fan_button);
+ xml->get_widget("load_target_but", _load_target_button);
+ xml->get_widget("evolve_toolbar", _evolve_toolbar);
+ xml->get_widget("evolve_but", _evolve_button);
+ xml->get_widget("mutate_but", _mutate_button);
+ xml->get_widget("compress_but", _compress_button);
+ xml->get_widget("add_node_but", _add_node_button);
+ xml->get_widget("remove_node_but", _remove_node_button);
+ xml->get_widget("adjust_node_but", _adjust_node_button);
+ xml->get_widget("add_edge_but", _add_edge_button);
+ xml->get_widget("remove_edge_but", _remove_edge_button);
+ xml->get_widget("adjust_edge_but", _adjust_edge_button);
+
+ _canvas_scrolledwindow->add(_canvas->widget());
+ _canvas_scrolledwindow->signal_event().connect(sigc::mem_fun(this,
+ &MachinaGUI::scrolled_window_event));
+
+ _canvas_scrolledwindow->property_hadjustment().get_value()->set_step_increment(10);
+ _canvas_scrolledwindow->property_vadjustment().get_value()->set_step_increment(10);
+
+ _stop_button->signal_toggled().connect(
+ sigc::mem_fun(this, &MachinaGUI::stop_toggled));
+ _play_button->signal_toggled().connect(
+ sigc::mem_fun(this, &MachinaGUI::play_toggled));
+ _record_button->signal_toggled().connect(
+ sigc::mem_fun(this, &MachinaGUI::record_toggled));
+ _step_record_button->signal_toggled().connect(
+ sigc::mem_fun(this, &MachinaGUI::step_record_toggled));
+
+ _chain_button->signal_toggled().connect(
+ sigc::mem_fun(this, &MachinaGUI::chain_toggled));
+ _fan_button->signal_toggled().connect(
+ sigc::mem_fun(this, &MachinaGUI::fan_toggled));
+
+ _menu_file_open->signal_activate().connect(
+ sigc::mem_fun(this, &MachinaGUI::menu_file_open));
+ _menu_file_save->signal_activate().connect(
+ sigc::mem_fun(this, &MachinaGUI::menu_file_save));
+ _menu_file_save_as->signal_activate().connect(
+ sigc::mem_fun(this, &MachinaGUI::menu_file_save_as));
+ _menu_file_quit->signal_activate().connect(
+ sigc::mem_fun(this, &MachinaGUI::menu_file_quit));
+ _menu_zoom_in->signal_activate().connect(
+ sigc::mem_fun(this, &MachinaGUI::on_zoom_in));
+ _menu_zoom_out->signal_activate().connect(
+ sigc::mem_fun(this, &MachinaGUI::on_zoom_out));
+ _menu_zoom_normal->signal_activate().connect(
+ sigc::mem_fun(this, &MachinaGUI::on_zoom_normal));
+ _menu_view_arrange->signal_activate().connect(
+ sigc::mem_fun(this, &MachinaGUI::arrange));
+ _menu_import_midi->signal_activate().connect(
+ sigc::mem_fun(this, &MachinaGUI::menu_import_midi));
+ _menu_export_midi->signal_activate().connect(
+ sigc::mem_fun(this, &MachinaGUI::menu_export_midi));
+ _menu_export_graphviz->signal_activate().connect(
+ sigc::mem_fun(this, &MachinaGUI::menu_export_graphviz));
+ _menu_view_toolbar->signal_toggled().connect(
+ sigc::mem_fun(this, &MachinaGUI::show_toolbar_toggled));
+ _menu_view_labels->signal_toggled().connect(
+ sigc::mem_fun(this, &MachinaGUI::show_labels_toggled));
+ _menu_help_about->signal_activate().connect(
+ sigc::mem_fun(this, &MachinaGUI::menu_help_about));
+ _menu_help_help->signal_activate().connect(
+ sigc::mem_fun(this, &MachinaGUI::menu_help_help));
+ _bpm_spinbutton->signal_changed().connect(
+ sigc::mem_fun(this, &MachinaGUI::tempo_changed));
+ _quantize_checkbutton->signal_toggled().connect(
+ sigc::mem_fun(this, &MachinaGUI::quantize_record_changed));
+ _quantize_spinbutton->signal_changed().connect(
+ sigc::mem_fun(this, &MachinaGUI::quantize_changed));
+
+ _mutate_button->signal_clicked().connect(
+ sigc::bind(sigc::mem_fun(this, &MachinaGUI::random_mutation),
+ SPtr<Machine>()));
+ _compress_button->signal_clicked().connect(
+ sigc::hide_return(sigc::bind(sigc::mem_fun(this, &MachinaGUI::mutate),
+ SPtr<Machine>(), 0)));
+ _add_node_button->signal_clicked().connect(
+ sigc::bind(sigc::mem_fun(this, &MachinaGUI::mutate),
+ SPtr<Machine>(), 1));
+ _remove_node_button->signal_clicked().connect(
+ sigc::bind(sigc::mem_fun(this, &MachinaGUI::mutate),
+ SPtr<Machine>(), 2));
+ _adjust_node_button->signal_clicked().connect(
+ sigc::bind(sigc::mem_fun(this, &MachinaGUI::mutate),
+ SPtr<Machine>(), 3));
+ _add_edge_button->signal_clicked().connect(
+ sigc::bind(sigc::mem_fun(this, &MachinaGUI::mutate),
+ SPtr<Machine>(), 4));
+ _remove_edge_button->signal_clicked().connect(
+ sigc::bind(sigc::mem_fun(this, &MachinaGUI::mutate),
+ SPtr<Machine>(), 5));
+ _adjust_edge_button->signal_clicked().connect(
+ sigc::bind(sigc::mem_fun(this, &MachinaGUI::mutate),
+ SPtr<Machine>(), 6));
+
+ _canvas->widget().show();
+
+ _main_window->present();
+
+ _quantize_checkbutton->set_active(false);
+ update_toolbar();
+
+ // Idle callback to drive the maid (collect garbage)
+ Glib::signal_timeout().connect(
+ sigc::bind_return(sigc::mem_fun(_maid.get(), &Raul::Maid::cleanup),
+ true),
+ 1000);
+
+ // Idle callback to update node states
+ Glib::signal_timeout().connect(
+ sigc::mem_fun(this, &MachinaGUI::idle_callback), 100);
+
+#ifdef HAVE_EUGENE
+ _load_target_button->signal_clicked().connect(
+ sigc::mem_fun(this, &MachinaGUI::load_target_clicked));
+ _evolve_button->signal_clicked().connect(
+ sigc::mem_fun(this, &MachinaGUI::evolve_toggled));
+ Glib::signal_timeout().connect(
+ sigc::mem_fun(this, &MachinaGUI::evolve_callback), 1000);
+#else
+ _evolve_toolbar->hide();
+#endif
+
+ _client_model->signal_new_object.connect(
+ sigc::mem_fun(this, &MachinaGUI::on_new_object));
+ _client_model->signal_erase_object.connect(
+ sigc::mem_fun(this, &MachinaGUI::on_erase_object));
+
+ rebuild_canvas();
+}
+
+MachinaGUI::~MachinaGUI()
+{
+}
+
+#ifdef HAVE_EUGENE
+bool
+MachinaGUI::evolve_callback()
+{
+ if (_evolve && _evolver->improvement()) {
+ _engine->driver()->set_machine(
+ SPtr<Machine>(new Machine(_evolver->best())));
+ _controller->announce(_engine->machine());
+ }
+
+ return true;
+}
+#endif
+
+bool
+MachinaGUI::idle_callback()
+{
+ _controller->process_updates();
+ return true;
+}
+
+static void
+destroy_edge(GanvEdge* edge, void* data)
+{
+ MachinaGUI* gui = (MachinaGUI*)data;
+ EdgeView* view = dynamic_cast<EdgeView*>(Glib::wrap(edge));
+ if (view) {
+ NodeView* tail = dynamic_cast<NodeView*>(view->get_tail());
+ NodeView* head = dynamic_cast<NodeView*>(view->get_head());
+ gui->controller()->disconnect(tail->node()->id(), head->node()->id());
+ }
+}
+
+static void
+destroy_node(GanvNode* node, void* data)
+{
+ MachinaGUI* gui = (MachinaGUI*)data;
+ NodeView* view = dynamic_cast<NodeView*>(Glib::wrap(GANV_NODE(node)));
+ if (view) {
+ const SPtr<client::ClientObject> node = view->node();
+ gui->canvas()->for_each_edge_on(
+ GANV_NODE(view->gobj()), destroy_edge, gui);
+ gui->controller()->erase(node->id());
+ }
+}
+
+bool
+MachinaGUI::scrolled_window_event(GdkEvent* event)
+{
+ if (event->type == GDK_KEY_PRESS) {
+ if (event->key.keyval == GDK_Delete) {
+ _canvas->for_each_selected_node(destroy_node, this);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void
+MachinaGUI::arrange()
+{
+ _canvas->arrange();
+}
+
+void
+MachinaGUI::load_target_clicked()
+{
+ Gtk::FileChooserDialog dialog(*_main_window,
+ "Load MIDI file for evolution", Gtk::FILE_CHOOSER_ACTION_OPEN);
+ dialog.set_local_only(false);
+ dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
+
+ Gtk::FileFilter filt;
+ filt.add_pattern("*.mid");
+ filt.set_name("MIDI Files");
+ dialog.set_filter(filt);
+
+ const int result = dialog.run();
+
+ if (result == Gtk::RESPONSE_OK)
+ _target_filename = dialog.get_filename();
+}
+
+#ifdef HAVE_EUGENE
+void
+MachinaGUI::evolve_toggled()
+{
+ if (_evolve_button->get_active()) {
+ _evolver = SPtr<Evolver>(
+ new Evolver(_unit, _target_filename, _engine->machine()));
+ _evolve = true;
+ stop_toggled();
+ _engine->driver()->set_machine(SPtr<Machine>());
+ _evolver->start();
+ } else {
+ _evolver->join();
+ _evolve = false;
+ SPtr<Machine> new_machine = SPtr<Machine>(
+ new Machine(_evolver->best()));
+ _engine->driver()->set_machine(new_machine);
+ _controller->announce(_engine->machine());
+ _engine->driver()->activate();
+ }
+}
+#endif
+
+void
+MachinaGUI::random_mutation(SPtr<Machine> machine)
+{
+ if (!machine)
+ machine = _engine->machine();
+
+ mutate(machine, machine->nodes().size() < 2 ? 1 : rand() % 7);
+}
+
+void
+MachinaGUI::mutate(SPtr<Machine> machine, unsigned mutation)
+{
+ #if 0
+ if (!machine)
+ machine = _engine->machine();
+
+ using namespace Mutation;
+
+ switch (mutation) {
+ case 0:
+ Compress().mutate(*machine.get());
+ _canvas->build(machine, _menu_view_labels->get_active());
+ break;
+ case 1:
+ AddNode().mutate(*machine.get());
+ _canvas->build(machine, _menu_view_labels->get_active());
+ break;
+ case 2:
+ RemoveNode().mutate(*machine.get());
+ _canvas->build(machine, _menu_view_labels->get_active());
+ break;
+ case 3:
+ AdjustNode().mutate(*machine.get());
+ idle_callback(); // update nodes
+ break;
+ case 4:
+ AddEdge().mutate(*machine.get());
+ _canvas->build(machine, _menu_view_labels->get_active());
+ break;
+ case 5:
+ RemoveEdge().mutate(*machine.get());
+ _canvas->build(machine, _menu_view_labels->get_active());
+ break;
+ case 6:
+ AdjustEdge().mutate(*machine.get());
+ _canvas->update_edges();
+ break;
+ default: throw;
+ }
+ #endif
+}
+
+void
+MachinaGUI::update_toolbar()
+{
+ const Driver::PlayState state = _engine->driver()->play_state();
+ _record_button->set_active(state == Driver::PlayState::RECORDING);
+ _step_record_button->set_active(state == Driver::PlayState::STEP_RECORDING);
+ _play_button->set_active(state == Driver::PlayState::PLAYING);
+}
+
+void
+MachinaGUI::rebuild_canvas()
+{
+ _controller->announce(_engine->machine());
+ _canvas->arrange();
+}
+
+void
+MachinaGUI::quantize_record_changed()
+{
+ _engine->driver()->set_quantize_record(_quantize_checkbutton->get_active());
+}
+
+void
+MachinaGUI::quantize_changed()
+{
+ _engine->set_quantization(1.0 / _quantize_spinbutton->get_value());
+}
+
+void
+MachinaGUI::tempo_changed()
+{
+ _engine->set_bpm(_bpm_spinbutton->get_value_as_int());
+}
+
+void
+MachinaGUI::menu_file_quit()
+{
+ _main_window->hide();
+}
+
+void
+MachinaGUI::menu_file_open()
+{
+ Gtk::FileChooserDialog dialog(*_main_window, "Open Machine", Gtk::FILE_CHOOSER_ACTION_OPEN);
+ dialog.set_local_only(false);
+ dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
+
+ Gtk::FileFilter filt;
+ filt.add_pattern("*.machina.ttl");
+ filt.set_name("Machina Machines (Turtle/RDF)");
+ dialog.set_filter(filt);
+
+ const int result = dialog.run();
+
+ if (result == Gtk::RESPONSE_OK) {
+ SPtr<machina::Machine> new_machine = _engine->load_machine(dialog.get_uri());
+ if (new_machine) {
+ rebuild_canvas();
+ _save_uri = dialog.get_uri();
+ }
+ }
+}
+
+void
+MachinaGUI::menu_file_save()
+{
+ if (_save_uri == "" || _save_uri.substr(0, 5) != "file:") {
+ menu_file_save_as();
+ } else {
+ if (_save_uri.substr(0, 5) != "file:")
+ menu_file_save_as();
+
+ Sord::Model model(_engine->rdf_world(), _save_uri);
+ _engine->machine()->write_state(model);
+ model.write_to_file(_save_uri, SERD_TURTLE);
+ }
+}
+
+void
+MachinaGUI::menu_file_save_as()
+{
+ Gtk::FileChooserDialog dialog(*_main_window, "Save Machine",
+ Gtk::FILE_CHOOSER_ACTION_SAVE);
+
+ dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
+
+ if (_save_uri.length() > 0)
+ dialog.set_uri(_save_uri);
+
+ const int result = dialog.run();
+
+ assert(result == Gtk::RESPONSE_OK
+ || result == Gtk::RESPONSE_CANCEL
+ || result == Gtk::RESPONSE_NONE);
+
+ if (result == Gtk::RESPONSE_OK) {
+ string filename = dialog.get_filename();
+
+ if (filename.length() < 13 || filename.substr(filename.length()-12) != ".machina.ttl")
+ filename += ".machina.ttl";
+
+ const std::string uri = Glib::filename_to_uri(filename);
+
+ bool confirm = false;
+ std::fstream fin;
+ fin.open(filename.c_str(), std::ios::in);
+ if (fin.is_open()) { // File exists
+ string msg = "A file named \"";
+ msg += filename + "\" already exists.\n\nDo you want to replace it?";
+ Gtk::MessageDialog confirm_dialog(dialog,
+ msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_YES_NO, true);
+ if (confirm_dialog.run() == Gtk::RESPONSE_YES)
+ confirm = true;
+ else
+ confirm = false;
+ } else { // File doesn't exist
+ confirm = true;
+ }
+ fin.close();
+
+ if (confirm) {
+ _save_uri = uri;
+ Sord::Model model(_engine->rdf_world(), _save_uri);
+ _engine->machine()->write_state(model);
+ model.write_to_file(_save_uri, SERD_TURTLE);
+ }
+ }
+}
+
+void
+MachinaGUI::menu_import_midi()
+{
+ Gtk::FileChooserDialog dialog(*_main_window, "Learn from MIDI file",
+ Gtk::FILE_CHOOSER_ACTION_OPEN);
+ dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
+
+ Gtk::FileFilter filt;
+ filt.add_pattern("*.mid");
+ filt.set_name("MIDI Files");
+ dialog.set_filter(filt);
+
+ Gtk::HBox* extra_widget = Gtk::manage(new Gtk::HBox());
+ Gtk::SpinButton* length_sb = Gtk::manage(new Gtk::SpinButton());
+ length_sb->set_increments(1, 10);
+ length_sb->set_range(0, INT_MAX);
+ length_sb->set_value(0);
+ extra_widget->pack_start(*Gtk::manage(new Gtk::Label("")), true, true);
+ extra_widget->pack_start(*Gtk::manage(new Gtk::Label("Maximum Length (0 = unlimited): ")), false, false);
+ extra_widget->pack_start(*length_sb, false, false);
+ dialog.set_extra_widget(*extra_widget);
+ extra_widget->show_all();
+
+ const int result = dialog.run();
+
+ if (result == Gtk::RESPONSE_OK) {
+ const double length_dbl = length_sb->get_value_as_int();
+ const Raul::TimeStamp length(_unit, length_dbl);
+
+ SPtr<machina::Machine> machine = _engine->load_machine_midi(
+ dialog.get_filename(), 0.0, length);
+
+ if (machine) {
+ dialog.hide();
+ machine->reset(NULL, machine->time());
+ _engine->driver()->set_machine(machine);
+ _canvas->clear();
+ rebuild_canvas();
+ } else {
+ Gtk::MessageDialog msg_dialog(dialog, "Error loading MIDI file",
+ false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
+ msg_dialog.run();
+ }
+ }
+}
+
+void
+MachinaGUI::menu_export_midi()
+{
+ Gtk::FileChooserDialog dialog(*_main_window, "Export to a MIDI file",
+ Gtk::FILE_CHOOSER_ACTION_SAVE);
+ dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
+
+ Gtk::FileFilter filt;
+ filt.add_pattern("*.mid");
+ filt.set_name("MIDI Files");
+ dialog.set_filter(filt);
+
+ Gtk::HBox* extra_widget = Gtk::manage(new Gtk::HBox());
+ Gtk::SpinButton* dur_sb = Gtk::manage(new Gtk::SpinButton());
+ dur_sb->set_increments(1, 10);
+ dur_sb->set_range(0, INT_MAX);
+ dur_sb->set_value(0);
+ extra_widget->pack_start(*Gtk::manage(new Gtk::Label("")), true, true);
+ extra_widget->pack_start(*Gtk::manage(new Gtk::Label("Duration (beats): ")), false, false);
+ extra_widget->pack_start(*dur_sb, false, false);
+ dialog.set_extra_widget(*extra_widget);
+ extra_widget->show_all();
+
+ const int result = dialog.run();
+
+ if (result == Gtk::RESPONSE_OK) {
+ const double dur_dbl = dur_sb->get_value_as_int();
+ const Raul::TimeStamp dur(_unit, dur_dbl);
+ _engine->export_midi(dialog.get_filename(), dur);
+ }
+}
+
+void
+MachinaGUI::menu_export_graphviz()
+{
+ Gtk::FileChooserDialog dialog(*_main_window, "Export to a GraphViz DOT file",
+ Gtk::FILE_CHOOSER_ACTION_SAVE);
+ dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
+
+ const int result = dialog.run();
+
+ if (result == Gtk::RESPONSE_OK)
+ _canvas->export_dot(dialog.get_filename().c_str());
+}
+
+void
+MachinaGUI::show_toolbar_toggled()
+{
+ if (_menu_view_toolbar->get_active())
+ _toolbar->show();
+ else
+ _toolbar->hide();
+}
+
+void
+MachinaGUI::on_zoom_in()
+{
+ _canvas->set_font_size(_canvas->get_font_size() + 1.0);
+}
+
+void
+MachinaGUI::on_zoom_out()
+{
+ _canvas->set_font_size(_canvas->get_font_size() - 1.0);
+}
+
+void
+MachinaGUI::on_zoom_normal()
+{
+ _canvas->set_zoom(1.0);
+}
+
+static void
+show_node_label(GanvNode* node, void* data)
+{
+ bool show = *(bool*)data;
+ Ganv::Node* nodemm = Glib::wrap(node);
+ NodeView* const nv = dynamic_cast<NodeView*>(nodemm);
+ if (nv) {
+ nv->show_label(show);
+ }
+}
+
+static void
+show_edge_label(GanvEdge* edge, void* data)
+{
+ bool show = *(bool*)data;
+ Ganv::Edge* edgemm = Glib::wrap(edge);
+ EdgeView* const ev = dynamic_cast<EdgeView*>(edgemm);
+ if (ev) {
+ ev->show_label(show);
+ }
+}
+
+void
+MachinaGUI::show_labels_toggled()
+{
+ bool show = _menu_view_labels->get_active();
+
+ _canvas->for_each_node(show_node_label, &show);
+ _canvas->for_each_edge(show_edge_label, &show);
+}
+
+void
+MachinaGUI::menu_help_about()
+{
+ _about_window->set_transient_for(*_main_window);
+ _about_window->show();
+}
+
+void
+MachinaGUI::menu_help_help()
+{
+ _help_dialog->set_transient_for(*_main_window);
+ _help_dialog->run();
+ _help_dialog->hide();
+}
+
+void
+MachinaGUI::stop_toggled()
+{
+ if (_stop_button->get_active()) {
+ const Driver::PlayState old_state = _engine->driver()->play_state();
+ _engine->driver()->set_play_state(Driver::PlayState::STOPPED);
+ if (old_state == Driver::PlayState::RECORDING ||
+ old_state == Driver::PlayState::STEP_RECORDING) {
+ rebuild_canvas();
+ }
+ }
+}
+
+void
+MachinaGUI::play_toggled()
+{
+ if (_play_button->get_active()) {
+ const Driver::PlayState old_state = _engine->driver()->play_state();
+ _engine->driver()->set_play_state(Driver::PlayState::PLAYING);
+ if (old_state == Driver::PlayState::RECORDING ||
+ old_state == Driver::PlayState::STEP_RECORDING) {
+ rebuild_canvas();
+ }
+ }
+}
+
+void
+MachinaGUI::record_toggled()
+{
+ if (_record_button->get_active()) {
+ _engine->driver()->set_play_state(Driver::PlayState::RECORDING);
+ }
+}
+
+void
+MachinaGUI::step_record_toggled()
+{
+ if (_step_record_button->get_active()) {
+ _engine->driver()->set_play_state(Driver::PlayState::STEP_RECORDING);
+ }
+}
+
+void
+MachinaGUI::chain_toggled()
+{
+ if (_chain_button->get_active()) {
+ _chain_mode = true;
+ }
+}
+
+void
+MachinaGUI::fan_toggled()
+{
+ if (_fan_button->get_active()) {
+ _chain_mode = false;
+ }
+}
+
+void
+MachinaGUI::on_new_object(SPtr<client::ClientObject> object)
+{
+ _canvas->on_new_object(object);
+}
+
+void
+MachinaGUI::on_erase_object(SPtr<client::ClientObject> object)
+{
+ _canvas->on_erase_object(object);
+}
+
+} // namespace machina
+} // namespace gui
diff --git a/src/gui/MachinaGUI.hpp b/src/gui/MachinaGUI.hpp
new file mode 100644
index 0000000..bb111a2
--- /dev/null
+++ b/src/gui/MachinaGUI.hpp
@@ -0,0 +1,190 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_GUI_HPP
+#define MACHINA_GUI_HPP
+
+#include <string>
+
+#include <gtkmm.h>
+
+#include "raul/Maid.hpp"
+#include "raul/TimeStamp.hpp"
+
+#include "machina/types.hpp"
+#include "machina_config.h"
+
+using namespace std;
+
+namespace machina {
+
+class Machine;
+class Engine;
+class Evolver;
+class Controller;
+
+namespace client {
+class ClientModel;
+class ClientObject;
+}
+
+namespace gui {
+
+class MachinaCanvas;
+
+class MachinaGUI
+{
+public:
+ explicit MachinaGUI(SPtr<machina::Engine> engine);
+ ~MachinaGUI();
+
+ SPtr<MachinaCanvas> canvas() { return _canvas; }
+ SPtr<machina::Engine> engine() { return _engine; }
+ SPtr<machina::Controller> controller() { return _controller; }
+ Forge& forge() { return _forge; }
+ SPtr<Raul::Maid> maid() { return _maid; }
+ Gtk::Window* window() { return _main_window; }
+
+ void attach();
+ void quit() { _main_window->hide(); }
+
+ bool chain_mode() const { return _chain_mode; }
+
+ double default_length() const {
+ return 1 / (double)_quantize_spinbutton->get_value();
+ }
+
+ inline void queue_refresh() { _refresh = true; }
+
+ void on_new_object(SPtr<machina::client::ClientObject> object);
+ void on_erase_object(SPtr<machina::client::ClientObject> object);
+
+ SPtr<machina::client::ClientModel> client_model() {
+ return _client_model;
+ }
+
+protected:
+ void menu_file_quit();
+ void menu_file_open();
+ void menu_file_save();
+ void menu_file_save_as();
+ void menu_import_midi();
+ void menu_export_midi();
+ void menu_export_graphviz();
+ void on_zoom_in();
+ void on_zoom_out();
+ void on_zoom_normal();
+ void show_toolbar_toggled();
+ void show_labels_toggled();
+ void menu_help_about();
+ void menu_help_help();
+ void arrange();
+ void load_target_clicked();
+
+ void random_mutation(SPtr<machina::Machine> machine);
+ void mutate(SPtr<machina::Machine> machine, unsigned mutation);
+ void update_toolbar();
+ void rebuild_canvas();
+
+ bool scrolled_window_event(GdkEvent* ev);
+ bool idle_callback();
+
+#ifdef HAVE_EUGENE
+ void evolve_toggled();
+ bool evolve_callback();
+#endif
+
+ void stop_toggled();
+ void play_toggled();
+ void record_toggled();
+ void step_record_toggled();
+
+ void chain_toggled();
+ void fan_toggled();
+
+ void quantize_record_changed();
+ void quantize_changed();
+ void tempo_changed();
+
+ string _save_uri;
+ string _target_filename;
+
+ Raul::TimeUnit _unit;
+
+ SPtr<MachinaCanvas> _canvas;
+ SPtr<machina::Engine> _engine;
+ SPtr<machina::client::ClientModel> _client_model;
+ SPtr<machina::Controller> _controller;
+
+ SPtr<Raul::Maid> _maid;
+ SPtr<machina::Evolver> _evolver;
+
+ Forge _forge;
+
+ Gtk::Main* _gtk_main;
+
+ Gtk::Window* _main_window;
+ Gtk::Dialog* _help_dialog;
+ Gtk::AboutDialog* _about_window;
+ Gtk::Toolbar* _toolbar;
+ Gtk::MenuItem* _menu_file_open;
+ Gtk::MenuItem* _menu_file_save;
+ Gtk::MenuItem* _menu_file_save_as;
+ Gtk::MenuItem* _menu_file_quit;
+ Gtk::MenuItem* _menu_zoom_in;
+ Gtk::MenuItem* _menu_zoom_out;
+ Gtk::MenuItem* _menu_zoom_normal;
+ Gtk::MenuItem* _menu_view_arrange;
+ Gtk::MenuItem* _menu_import_midi;
+ Gtk::MenuItem* _menu_export_midi;
+ Gtk::MenuItem* _menu_export_graphviz;
+ Gtk::MenuItem* _menu_help_about;
+ Gtk::CheckMenuItem* _menu_view_labels;
+ Gtk::CheckMenuItem* _menu_view_toolbar;
+ Gtk::MenuItem* _menu_help_help;
+ Gtk::ScrolledWindow* _canvas_scrolledwindow;
+ Gtk::TextView* _status_text;
+ Gtk::Expander* _messages_expander;
+ Gtk::SpinButton* _bpm_spinbutton;
+ Gtk::CheckButton* _quantize_checkbutton;
+ Gtk::SpinButton* _quantize_spinbutton;
+ Gtk::ToggleToolButton* _stop_button;
+ Gtk::ToggleToolButton* _play_button;
+ Gtk::ToggleToolButton* _record_button;
+ Gtk::ToggleToolButton* _step_record_button;
+ Gtk::RadioButton* _chain_button;
+ Gtk::RadioButton* _fan_button;
+ Gtk::ToolButton* _load_target_button;
+ Gtk::Toolbar* _evolve_toolbar;
+ Gtk::ToggleToolButton* _evolve_button;
+ Gtk::ToolButton* _mutate_button;
+ Gtk::ToolButton* _compress_button;
+ Gtk::ToolButton* _add_node_button;
+ Gtk::ToolButton* _remove_node_button;
+ Gtk::ToolButton* _adjust_node_button;
+ Gtk::ToolButton* _add_edge_button;
+ Gtk::ToolButton* _remove_edge_button;
+ Gtk::ToolButton* _adjust_edge_button;
+
+ bool _refresh;
+ bool _evolve;
+ bool _chain_mode;
+};
+
+} // namespace machina
+} // namespace gui
+
+#endif // MACHINA_GUI_HPP
diff --git a/src/gui/NodePropertiesWindow.cpp b/src/gui/NodePropertiesWindow.cpp
new file mode 100644
index 0000000..f22eb57
--- /dev/null
+++ b/src/gui/NodePropertiesWindow.cpp
@@ -0,0 +1,136 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <string>
+
+#include "client/ClientObject.hpp"
+#include "machina/URIs.hpp"
+#include "raul/TimeStamp.hpp"
+
+#include "MachinaGUI.hpp"
+#include "NodePropertiesWindow.hpp"
+#include "WidgetFactory.hpp"
+
+using namespace std;
+
+namespace machina {
+namespace gui {
+
+NodePropertiesWindow* NodePropertiesWindow::_instance = NULL;
+
+NodePropertiesWindow::NodePropertiesWindow(
+ BaseObjectType* cobject,
+ const Glib::RefPtr<Gtk::Builder>& xml)
+ : Gtk::Dialog(cobject)
+ , _gui(NULL)
+{
+ property_visible() = false;
+
+ xml->get_widget("node_properties_note_spinbutton", _note_spinbutton);
+ xml->get_widget("node_properties_duration_spinbutton", _duration_spinbutton);
+ xml->get_widget("node_properties_apply_button", _apply_button);
+ xml->get_widget("node_properties_cancel_button", _cancel_button);
+ xml->get_widget("node_properties_ok_button", _ok_button);
+
+ _apply_button->signal_clicked().connect(
+ sigc::mem_fun(this, &NodePropertiesWindow::apply_clicked));
+ _cancel_button->signal_clicked().connect(
+ sigc::mem_fun(this, &NodePropertiesWindow::cancel_clicked));
+ _ok_button->signal_clicked().connect(
+ sigc::mem_fun(this, &NodePropertiesWindow::ok_clicked));
+}
+
+NodePropertiesWindow::~NodePropertiesWindow()
+{}
+
+void
+NodePropertiesWindow::apply_clicked()
+{
+#if 0
+ const uint8_t note = _note_spinbutton->get_value();
+ if (!_node->enter_action()) {
+ _node->set_enter_action(ActionFactory::note_on(note));
+ _node->set_exit_action(ActionFactory::note_off(note));
+ } else {
+ SPtr<MidiAction> action = dynamic_ptr_cast<MidiAction>(_node->enter_action());
+ action->event()[1] = note;
+ action = dynamic_ptr_cast<MidiAction>(_node->exit_action());
+ action->event()[1] = note;
+ }
+#endif
+ _node->set(URIs::instance().machina_duration,
+ _gui->forge().make(float(_duration_spinbutton->get_value())));
+}
+
+void
+NodePropertiesWindow::cancel_clicked()
+{
+ assert(this == _instance);
+ delete _instance;
+ _instance = NULL;
+}
+
+void
+NodePropertiesWindow::ok_clicked()
+{
+ apply_clicked();
+ cancel_clicked();
+}
+
+void
+NodePropertiesWindow::set_node(MachinaGUI* gui,
+ SPtr<machina::client::ClientObject> node)
+{
+ _gui = gui;
+ _node = node;
+ #if 0
+ SPtr<MidiAction> enter_action = dynamic_ptr_cast<MidiAction>(node->enter_action());
+ if (enter_action && ( enter_action->event_size() > 1)
+ && ( (enter_action->event()[0] & 0xF0) == 0x90) ) {
+ _note_spinbutton->set_value(enter_action->event()[1]);
+ _note_spinbutton->show();
+ } else if (!enter_action) {
+ _note_spinbutton->set_value(60);
+ _note_spinbutton->show();
+ } else {
+ _note_spinbutton->hide();
+ }
+ #endif
+ _duration_spinbutton->set_value(
+ node->get(URIs::instance().machina_duration).get<float>());
+}
+
+void
+NodePropertiesWindow::present(MachinaGUI* gui,
+ Gtk::Window* parent,
+ SPtr<machina::client::ClientObject> node)
+{
+ if (!_instance) {
+ Glib::RefPtr<Gtk::Builder> xml = WidgetFactory::create();
+
+ xml->get_widget_derived("node_properties_dialog", _instance);
+
+ if (parent) {
+ _instance->set_transient_for(*parent);
+ }
+ }
+
+ _instance->set_node(gui, node);
+ _instance->show();
+}
+
+} // namespace machina
+} // namespace gui
diff --git a/src/gui/NodePropertiesWindow.hpp b/src/gui/NodePropertiesWindow.hpp
new file mode 100644
index 0000000..a999004
--- /dev/null
+++ b/src/gui/NodePropertiesWindow.hpp
@@ -0,0 +1,67 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef NODEPROPERTIESWINDOW_HPP
+#define NODEPROPERTIESWINDOW_HPP
+
+#include <gtkmm.h>
+
+#include "machina/types.hpp"
+
+namespace machina {
+
+namespace client { class ClientObject; }
+
+namespace gui {
+
+class MachinaGUI;
+
+class NodePropertiesWindow : public Gtk::Dialog
+{
+public:
+ NodePropertiesWindow(BaseObjectType* cobject,
+ const Glib::RefPtr<Gtk::Builder>& xml);
+
+ ~NodePropertiesWindow();
+
+ static void present(MachinaGUI* gui,
+ Gtk::Window* parent,
+ SPtr<machina::client::ClientObject> node);
+
+private:
+ void set_node(MachinaGUI* gui,
+ SPtr<machina::client::ClientObject> node);
+
+ void apply_clicked();
+ void cancel_clicked();
+ void ok_clicked();
+
+ static NodePropertiesWindow* _instance;
+
+ MachinaGUI* _gui;
+ SPtr<machina::client::ClientObject> _node;
+
+ Gtk::SpinButton* _note_spinbutton;
+ Gtk::SpinButton* _duration_spinbutton;
+ Gtk::Button* _apply_button;
+ Gtk::Button* _cancel_button;
+ Gtk::Button* _ok_button;
+};
+
+} // namespace machina
+} // namespace gui
+
+#endif // NODEPROPERTIESWINDOW_HPP
diff --git a/src/gui/NodeView.cpp b/src/gui/NodeView.cpp
new file mode 100644
index 0000000..a1b96e4
--- /dev/null
+++ b/src/gui/NodeView.cpp
@@ -0,0 +1,203 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "machina/Controller.hpp"
+#include "machina/URIs.hpp"
+#include "machina/types.hpp"
+
+#include "client/ClientModel.hpp"
+
+#include "MachinaCanvas.hpp"
+#include "MachinaGUI.hpp"
+#include "NodePropertiesWindow.hpp"
+#include "NodeView.hpp"
+
+using namespace std;
+
+namespace machina {
+namespace gui {
+
+NodeView::NodeView(Gtk::Window* window,
+ Ganv::Canvas& canvas,
+ SPtr<machina::client::ClientObject> node,
+ double x,
+ double y)
+ : Ganv::Circle(canvas, "", x, y)
+ , _window(window)
+ , _node(node)
+ , _default_border_color(get_border_color())
+ , _default_fill_color(get_fill_color())
+{
+ set_fit_label(false);
+ set_radius_ems(1.25);
+
+ signal_event().connect(sigc::mem_fun(this, &NodeView::on_event));
+
+ MachinaCanvas* mcanvas = dynamic_cast<MachinaCanvas*>(&canvas);
+ if (is(mcanvas->app()->forge(), URIs::instance().machina_initial)) {
+ set_border_width(4.0);
+ set_is_source(true);
+ const uint8_t alpha[] = { 0xCE, 0xB1, 0 };
+ set_label((const char*)alpha);
+ }
+
+ node->signal_property.connect(sigc::mem_fun(this, &NodeView::on_property));
+
+ for (const auto& p : node->properties()) {
+ on_property(p.first, p.second);
+ }
+}
+
+NodeView::~NodeView()
+{
+ _node->set_view(NULL);
+}
+
+bool
+NodeView::on_double_click(GdkEventButton*)
+{
+ MachinaCanvas* canvas = dynamic_cast<MachinaCanvas*>(this->canvas());
+ NodePropertiesWindow::present(canvas->app(), _window, _node);
+ return true;
+}
+
+bool
+NodeView::is(Forge& forge, machina::URIInt key)
+{
+ const Atom& value = _node->get(key);
+ return value.type() == forge.Bool && value.get<int32_t>();
+}
+
+bool
+NodeView::on_event(GdkEvent* event)
+{
+ MachinaCanvas* canvas = dynamic_cast<MachinaCanvas*>(this->canvas());
+ Forge& forge = canvas->app()->forge();
+ if (event->type == GDK_BUTTON_PRESS) {
+ if (event->button.state & GDK_CONTROL_MASK) {
+ if (event->button.button == 1) {
+ canvas->app()->controller()->set_property(
+ _node->id(),
+ URIs::instance().machina_selector,
+ forge.make(!is(forge, URIs::instance().machina_selector)));
+ return true;
+ }
+ } else {
+ return _signal_clicked.emit(&event->button);
+ }
+ } else if (event->type == GDK_2BUTTON_PRESS) {
+ return on_double_click(&event->button);
+ }
+ return false;
+}
+
+static void
+midi_note_name(uint8_t num, uint8_t buf[8])
+{
+ static const char* notes = "CCDDEFFGGAAB";
+ static const bool is_sharp[] = { 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0 };
+
+ const uint8_t octave = num / 12;
+ const uint8_t id = num - octave * 12;
+ const uint8_t sub[] = { 0xE2, 0x82, uint8_t(0x80 + octave) };
+ const uint8_t sharp[] = { 0xE2, 0x99, 0xAF };
+
+ int b = 0;
+ buf[b++] = notes[id];
+ if (is_sharp[id]) {
+ for (unsigned s = 0; s < sizeof(sharp); ++s) {
+ buf[b++] = sharp[s];
+ }
+ }
+ for (unsigned s = 0; s < sizeof(sub); ++s) {
+ buf[b++] = sub[s];
+ }
+ buf[b++] = 0;
+}
+
+void
+NodeView::show_label(bool show)
+{
+ if (show && _enter_action) {
+ Atom note_number = _enter_action->get(
+ URIs::instance().machina_note_number);
+ if (note_number.is_valid()) {
+ uint8_t buf[8];
+ midi_note_name(note_number.get<int32_t>(), buf);
+ set_label((const char*)buf);
+ return;
+ }
+ }
+
+ set_label("");
+}
+
+void
+NodeView::on_property(machina::URIInt key, const Atom& value)
+{
+ static const uint32_t active_color = 0x408040FF;
+ static const uint32_t active_border_color = 0x00FF00FF;
+
+ if (key == URIs::instance().machina_selector) {
+ if (value.get<int32_t>()) {
+ set_dash_length(4.0);
+ } else {
+ set_dash_length(0.0);
+ }
+ } else if (key == URIs::instance().machina_initial) {
+ set_border_width(value.get<int32_t>() ? 4.0 : 1.0);
+ set_is_source(value.get<int32_t>());
+ } else if (key == URIs::instance().machina_active) {
+ if (value.get<int32_t>()) {
+ if (get_fill_color() != active_color) {
+ set_fill_color(active_color);
+ set_border_color(active_border_color);
+ }
+ } else if (get_fill_color() == active_color) {
+ set_default_colors();
+ }
+ } else if (key == URIs::instance().machina_enter_action) {
+ const uint64_t action_id = value.get<int32_t>();
+ MachinaCanvas* canvas = dynamic_cast<MachinaCanvas*>(this->canvas());
+ _enter_action_connection.disconnect();
+ _enter_action = canvas->app()->client_model()->find(action_id);
+ if (_enter_action) {
+ _enter_action_connection = _enter_action->signal_property.connect(
+ sigc::mem_fun(this, &NodeView::on_action_property));
+ for (auto i : _enter_action->properties()) {
+ on_action_property(i.first, i.second);
+ }
+ }
+ }
+}
+
+void
+NodeView::on_action_property(machina::URIInt key, const Atom& value)
+{
+ if (key == URIs::instance().machina_note_number) {
+ show_label(true);
+ }
+}
+
+void
+NodeView::set_default_colors()
+{
+ set_fill_color(_default_fill_color);
+ set_border_color(_default_border_color);
+}
+
+} // namespace machina
+} // namespace gui
diff --git a/src/gui/NodeView.hpp b/src/gui/NodeView.hpp
new file mode 100644
index 0000000..6e0681c
--- /dev/null
+++ b/src/gui/NodeView.hpp
@@ -0,0 +1,76 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MACHINA_NODEVIEW_HPP
+#define MACHINA_NODEVIEW_HPP
+
+#include "ganv/Circle.hpp"
+
+#include "client/ClientObject.hpp"
+
+#include "machina/types.hpp"
+
+namespace machina {
+namespace gui {
+
+class NodeView
+ : public Ganv::Circle
+ , public machina::client::ClientObject::View
+{
+public:
+ NodeView(Gtk::Window* window,
+ Canvas& canvas,
+ SPtr<machina::client::ClientObject> node,
+ double x,
+ double y);
+
+ ~NodeView();
+
+ SPtr<machina::client::ClientObject> node() { return _node; }
+
+ void show_label(bool show);
+
+ void update_state(bool show_labels);
+
+ void set_default_colors();
+
+ sigc::signal<bool, GdkEventButton*>& signal_clicked() {
+ return _signal_clicked;
+ }
+
+private:
+ bool on_event(GdkEvent* ev);
+ bool on_double_click(GdkEventButton* ev);
+ void on_property(machina::URIInt key, const Atom& value);
+ void on_action_property(machina::URIInt key, const Atom& value);
+
+ bool is(Forge& forge, machina::URIInt key);
+
+ Gtk::Window* _window;
+ SPtr<machina::client::ClientObject> _node;
+ uint32_t _default_border_color;
+ uint32_t _default_fill_color;
+
+ SPtr<machina::client::ClientObject> _enter_action;
+ sigc::connection _enter_action_connection;
+
+ sigc::signal<bool, GdkEventButton*> _signal_clicked;
+};
+
+} // namespace machina
+} // namespace gui
+
+#endif // MACHINA_NODEVIEW_HPP
diff --git a/src/gui/WidgetFactory.hpp b/src/gui/WidgetFactory.hpp
new file mode 100644
index 0000000..08118dc
--- /dev/null
+++ b/src/gui/WidgetFactory.hpp
@@ -0,0 +1,65 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include <gtkmm.h>
+
+#include "machina_config.h"
+
+namespace machina {
+namespace gui {
+
+class WidgetFactory
+{
+public:
+ static Glib::RefPtr<Gtk::Builder> create() {
+ Glib::RefPtr<Gtk::Builder> xml;
+
+ // Check for the .ui file in current directory
+ std::string ui_filename = "./machina.ui";
+ std::ifstream fs(ui_filename.c_str());
+ if (fs.fail()) {
+ // didn't find it, check MACHINA_DATA_DIR
+ fs.clear();
+ ui_filename = MACHINA_DATA_DIR;
+ ui_filename += "/machina.ui";
+
+ fs.open(ui_filename.c_str());
+ if (fs.fail()) {
+ std::cerr << "No machina.ui in current directory or "
+ << MACHINA_DATA_DIR << "." << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ fs.close();
+ }
+
+ try {
+ xml = Gtk::Builder::create_from_file(ui_filename);
+ } catch (const Gtk::BuilderError& ex) {
+ std::cerr << ex.what() << std::endl;
+ throw ex;
+ }
+
+ return xml;
+ }
+
+};
+
+} // namespace machina
+} // namespace gui
diff --git a/src/gui/machina.gladep b/src/gui/machina.gladep
new file mode 100644
index 0000000..c27832f
--- /dev/null
+++ b/src/gui/machina.gladep
@@ -0,0 +1,9 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-project SYSTEM "http://glade.gnome.org/glade-project-2.0.dtd">
+
+<glade-project>
+ <name>Machina</name>
+ <program_name>machina</program_name>
+ <language>C++</language>
+ <gnome_support>FALSE</gnome_support>
+</glade-project>
diff --git a/src/gui/machina.svg b/src/gui/machina.svg
new file mode 100644
index 0000000..39f7be0
--- /dev/null
+++ b/src/gui/machina.svg
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.45pre1"
+ width="256"
+ height="256"
+ version="1.0"
+ sodipodi:docbase="/home/dave/code/lad/machina/src/gui"
+ sodipodi:docname="machina-icon.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ sodipodi:modified="true">
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs5" />
+ <sodipodi:namedview
+ inkscape:window-height="771"
+ inkscape:window-width="1183"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ guidetolerance="10.0"
+ gridtolerance="10.0"
+ objecttolerance="10.0"
+ borderopacity="1.0"
+ bordercolor="#666666"
+ pagecolor="#ffffff"
+ id="base"
+ inkscape:zoom="1.4142136"
+ inkscape:cx="310.87907"
+ inkscape:cy="40.263308"
+ inkscape:window-x="293"
+ inkscape:window-y="52"
+ inkscape:current-layer="svg2"
+ width="256px"
+ height="256px" />
+ <path
+ style="fill:#000000"
+ id="path2189"
+ d="M 9.0278551,221.40628 C 22.572047,206.65805 31.269455,188.04472 39.259152,169.8919 C 51.644822,140.58208 56.041288,109.13092 58.849953,77.700978 C 59.962909,56.867589 61.443535,35.864275 59.495794,15.031194 C 63.699947,13.959444 67.938586,10.617016 72.108252,11.815945 C 77.429885,13.346107 71.833885,22.901101 72.179608,28.42755 C 73.082013,42.852664 75.819667,56.648771 78.944682,70.727057 C 85.857753,99.135712 98.546656,125.08352 122.5541,142.3916 C 149.02904,155.16169 159.76491,132.36389 173.41824,112.97967 C 189.89026,87.286307 201.67487,59.107084 212.79124,30.80732 C 238.81023,-0.42720544 213.47911,64.070958 211.76716,70.33904 C 202.71922,108.04829 200.75405,146.89462 201.60034,185.52025 C 202.59785,202.30805 203.03716,219.51338 208.40658,235.60545 C 209.82998,239.98783 211.97855,244.0493 214.19967,248.06447 L 201.80123,255.17231 C 199.63995,251.03059 197.23968,246.9206 196.00991,242.38503 C 191.48082,225.58243 192.01187,207.89788 191.55783,190.64995 C 191.61755,152.05096 193.33386,113.23415 201.28376,75.349455 C 205.86472,56.717664 207.3011,28.642568 227.34088,20.082503 C 229.12544,19.320222 225.62336,23.562881 224.76461,25.303071 C 211.8352,53.436232 199.91298,82.147117 183.57547,108.55509 C 167.9424,131.6754 143.08989,165.0319 111.95557,149.10157 C 88.367319,131.04024 76.047527,104.6256 68.72284,76.231983 C 66.537387,67.180737 52.831088,26.99333 59.36089,18.303914 C 62.551525,14.058035 67.78841,11.838043 72.002169,8.6051067 C 71.542368,29.947073 70.679719,51.279977 69.135855,72.573834 C 66.386515,103.98497 62.160375,135.44301 50.178851,164.87632 C 42.654028,182.74324 34.458191,200.83743 22.235303,216.04826 L 9.0278551,221.40628 z " />
+ <path
+ style="fill:#000000"
+ id="path2191"
+ d="M 177.4044,227.49356 C 186.73664,226.83386 194.85497,230.46984 202.63542,235.17331 C 208.34182,239.12994 205.55279,237.07748 211.00405,241.32859 L 199.24569,248.73751 C 193.86468,244.57093 196.63478,246.5644 190.93236,242.7613 C 183.22589,238.37114 175.24676,235.06127 166.16497,235.78874 L 177.4044,227.49356 z " />
+ <path
+ style="fill:#000000"
+ id="path2193"
+ d="M 202.904,253.28805 C 206.25526,244.218 211.55226,236.16787 216.96397,228.23361 C 217.73013,227.17184 218.49629,226.11007 219.26244,225.0483 L 232.24972,219.44712 C 231.4278,220.51566 230.60588,221.58419 229.78396,222.65273 C 224.30649,230.41756 219.07262,238.3307 215.72758,247.29568 L 202.904,253.28805 z " />
+</svg>
diff --git a/src/gui/machina.ui b/src/gui/machina.ui
new file mode 100644
index 0000000..89404fc
--- /dev/null
+++ b/src/gui/machina.ui
@@ -0,0 +1,1128 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy toplevel-contextual -->
+ <object class="GtkRadioAction" id="play_action">
+ <property name="stock_id">gtk-media-play</property>
+ <property name="draw_as_radio">True</property>
+ <property name="group">record_action</property>
+ </object>
+ <object class="GtkRadioAction" id="step_record_action">
+ <property name="label" translatable="yes">Step record</property>
+ <property name="stock_id">gtk-add</property>
+ <property name="draw_as_radio">True</property>
+ <property name="group">record_action</property>
+ </object>
+ <object class="GtkRadioAction" id="stop_action">
+ <property name="stock_id">gtk-media-stop</property>
+ <property name="draw_as_radio">True</property>
+ <property name="group">record_action</property>
+ </object>
+ <object class="GtkAboutDialog" id="about_win">
+ <property name="can_focus">False</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="type_hint">normal</property>
+ <property name="program_name">Machina</property>
+ <property name="copyright" translatable="yes">© 2013 David Robillard &lt;http://drobilla.net&gt;</property>
+ <property name="comments" translatable="yes">A MIDI sequencer based on
+ probabilistic finite-state automata</property>
+ <property name="website">http://drobilla.net/software/machina</property>
+ <property name="license" translatable="yes">Machina is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+Machina is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Machina; if not, write to the Free Software Foundation, Inc.,
+51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+</property>
+ <property name="authors">David Robillard &lt;d@drobilla.net&gt;</property>
+ <property name="translator_credits" translatable="yes" comments="TRANSLATORS: Replace this string with your names, one name per line.">translator-credits</property>
+ <property name="logo">machina.svg</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="GtkAdjustment" id="bpm_adjustment">
+ <property name="lower">1</property>
+ <property name="upper">480</property>
+ <property name="value">120</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="duration_adjustment">
+ <property name="upper">64</property>
+ <property name="value">0.25</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">4</property>
+ </object>
+ <object class="GtkDialog" id="help_dialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">8</property>
+ <property name="title" translatable="yes">Machina Help</property>
+ <property name="resizable">False</property>
+ <property name="window_position">center-on-parent</property>
+ <property name="icon_name">gtk-help</property>
+ <property name="type_hint">dialog</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="closebutton1">
+ <property name="label">gtk-close</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="label" translatable="yes">&lt;big&gt;&lt;b&gt;Nodes&lt;/b&gt;&lt;/big&gt;
+Nodes represent notes, which have a pitch and duration.
+When a node is highlighted green, it is playing.
+
+Play begins at the initial node α. Whenever all nodes become
+inactive or stop is pressed, play returns to the initial node.
+
+Nodes with dashed borders are selectors. Only one successor is
+played after a selector, i.e. only one outgoing arc is traversed.
+
+ • Right click the canvas to create a new node
+ • Middle click a node to learn a MIDI note
+ • Double click a node to edit its properties
+ • Ctrl+Left click a node to make it a selector
+
+&lt;big&gt;&lt;b&gt;Arcs&lt;/b&gt;&lt;/big&gt;
+When a node is finished playing, play travels along outgoing
+arcs, depending on their probability. The colour of an arc
+indicates its probability, where green is high and red is low.
+
+ • Ctrl+Left click an arc to decrease its probability
+ • Ctrl+Right click an arc to increase its probability
+
+&lt;big&gt;&lt;b&gt;Recording&lt;/b&gt;&lt;/big&gt;
+A machine can be built by recording MIDI input. To record, press
+the record button and play some MIDI notes. To finish recording,
+press stop or play and the new nodes will be added to the machine.
+
+Normal recording inserts delay nodes to reproduce the timing of
+the input. To avoid this, use step recording which directly connects
+nodes to their successors with no delays in-between.
+
+&lt;big&gt;&lt;b&gt;Connecting&lt;/b&gt;&lt;/big&gt;
+Connecting nodes is based on the selection. If there is a selection,
+clicking a node will connect the selection to that node.
+
+There are two modes: chain and fan. In chain mode, the selection is
+moved to the clicked node for quickly connecting long chains of nodes.
+In fan mode, the selection is unchanged for quickly connecting the
+selection to many nodes.</property>
+ <property name="use_markup">True</property>
+ <property name="justify">fill</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="padding">8</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-7">closebutton1</action-widget>
+ </action-widgets>
+ </object>
+ <object class="GtkAdjustment" id="length_adjustment">
+ <property name="lower">1</property>
+ <property name="upper">256</property>
+ <property name="value">8</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">8</property>
+ </object>
+ <object class="GtkWindow" id="machina_win">
+ <property name="can_focus">False</property>
+ <property name="border_width">1</property>
+ <property name="title" translatable="yes">Machina</property>
+ <property name="default_width">640</property>
+ <property name="default_height">480</property>
+ <property name="icon">machina.svg</property>
+ <child>
+ <object class="GtkVBox" id="main_vbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuBar" id="menubar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="file_menu">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="file_menu_menu">
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkImageMenuItem" id="open_menuitem">
+ <property name="label">gtk-open</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_open_session_menuitem_activate" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="save_menuitem">
+ <property name="label">gtk-save</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_save_session_menuitem_activate" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="save_as_menuitem">
+ <property name="label">gtk-save-as</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_save_session_as_menuitem_activate" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="separator5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="import_midi_menuitem">
+ <property name="label">_Import MIDI...</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="I" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ <signal name="activate" handler="on_learn_midi_menuitem_activate" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="export_midi_menuitem">
+ <property name="label">_Export MIDI...</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="E" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ <signal name="activate" handler="on_export_midi_menuitem_activate" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="separator7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="export_graphviz_menuitem">
+ <property name="label">Export _GraphViz...</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="d" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ <signal name="activate" handler="on_export_graphviz_menuitem_activate" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="separator6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="quit_menuitem">
+ <property name="label">gtk-quit</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_quit1_activate" swapped="no"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="view_menu">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_View</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="view_menu_menu">
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkCheckMenuItem" id="view_labels_menuitem">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Labels</property>
+ <property name="use_underline">True</property>
+ <accelerator key="L" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ <signal name="activate" handler="on_view_edge_labels_menuitem_activate" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckMenuItem" id="view_toolbar_menuitem">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Toolbar</property>
+ <property name="use_underline">True</property>
+ <property name="active">True</property>
+ <accelerator key="T" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ <signal name="activate" handler="on_toolbar2_activate" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="separatormenuitem3">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="zoom_in_menuitem">
+ <property name="label">gtk-zoom-in</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="plus" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ <accelerator key="equal" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="zoom_out_menuitem">
+ <property name="label">gtk-zoom-out</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="minus" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="zoom_normal_menuitem">
+ <property name="label">gtk-zoom-100</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="0" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="separatormenuitem1">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="arrange_menuitem">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Arrange</property>
+ <property name="use_underline">True</property>
+ <accelerator key="g" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="help_menu">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Help</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="help_menu_menu">
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkImageMenuItem" id="help_help_menuitem">
+ <property name="label">gtk-help</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_help_about_menuitem_activate" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="help_about_menuitem">
+ <property name="label">gtk-about</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_about1_activate" swapped="no"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <child>
+ <object class="GtkToolbar" id="toolbar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="toolbar_style">icons</property>
+ <property name="show_arrow">False</property>
+ <property name="icon_size">2</property>
+ <property name="icon_size_set">True</property>
+ <child>
+ <object class="GtkToggleToolButton" id="stop_but">
+ <property name="related_action">stop_action</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">Stop</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleToolButton" id="play_but">
+ <property name="related_action">play_action</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">Play</property>
+ <property name="active">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleToolButton" id="record_but">
+ <property name="related_action">record_action</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">Record</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleToolButton" id="step_record_but">
+ <property name="related_action">step_record_action</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="tooltip_text" translatable="yes">Step record</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparatorToolItem" id="separatortoolitem1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="toolitem1">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkHBox" id="hbox3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkSpinButton" id="bpm_spinbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip_text" translatable="yes">Playback tempo</property>
+ <property name="max_length">3</property>
+ <property name="invisible_char">•</property>
+ <property name="width_chars">3</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">bpm_adjustment</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes"> BPM</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparatorToolItem" id="separatortoolitem2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="toolitem3">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkHBox" id="hbox4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">4</property>
+ <child>
+ <object class="GtkCheckButton" id="quantize_checkbutton">
+ <property name="label" translatable="yes">Quantize 1/</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="tooltip_text" translatable="yes">Quantize recorded notes</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="quantize_spinbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip_text" translatable="yes">Note type for quantization</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">quantize_adjustment</property>
+ <property name="climb_rate">1</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparatorToolItem" id="separatortoolitem3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="toolitem2">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">4</property>
+ <child>
+ <object class="GtkRadioButton" id="chain_but">
+ <property name="label" translatable="yes">Chain</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="tooltip_text" translatable="yes">Move selection to head node after connection</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="fan_but">
+ <property name="label" translatable="yes">Fan</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="tooltip_text" translatable="yes">Keep selection on tail node after connection</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">chain_but</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolbar" id="evolve_toolbar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="toolbar_style">icons</property>
+ <property name="show_arrow">False</property>
+ <property name="icon_size">2</property>
+ <property name="icon_size_set">True</property>
+ <child>
+ <object class="GtkSeparatorToolItem" id="toolbutton7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="load_target_but">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="stock_id">gtk-open</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparatorToolItem" id="toolbutton5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleToolButton" id="evolve_but">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Evolve machine (towards target MIDI)</property>
+ <property name="stock_id">gtk-execute</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparatorToolItem" id="toolbutton4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="mutate_but">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="stock_id">gtk-dialog-warning</property>
+ <accelerator key="m" signal="clicked" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparatorToolItem" id="toolbutton3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="compress_but">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="stock_id">gtk-convert</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparatorToolItem" id="toolbutton2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="add_node_but">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Add Node</property>
+ <property name="stock_id">gtk-new</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="remove_node_but">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Delete Node</property>
+ <property name="stock_id">gtk-delete</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="adjust_node_but">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Adjust Node</property>
+ <property name="stock_id">gtk-edit</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparatorToolItem" id="toolbutton1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="add_edge_but">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Add Edge</property>
+ <property name="stock_id">gtk-connect</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="remove_edge_but">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Remove Edge</property>
+ <property name="stock_id">gtk-disconnect</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="adjust_edge_but">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Adjust Edge</property>
+ <property name="stock_id">gtk-select-color</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="canvas_scrolledwindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="padding">2</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="GtkAdjustment" id="note_num_adjustment">
+ <property name="upper">127</property>
+ <property name="value">64</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkRadioAction" id="record_action">
+ <property name="stock_id">gtk-media-record</property>
+ <property name="draw_as_radio">True</property>
+ </object>
+ <object class="GtkDialog" id="node_properties_dialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">8</property>
+ <property name="title" translatable="yes">dialog1</property>
+ <property name="resizable">False</property>
+ <property name="icon">machina.svg</property>
+ <property name="type_hint">dialog</property>
+ <property name="skip_taskbar_hint">True</property>
+ <property name="skip_pager_hint">True</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">8</property>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="node_properties_apply_button">
+ <property name="label">gtk-apply</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="node_properties_cancel_button">
+ <property name="label">gtk-cancel</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="node_properties_ok_button">
+ <property name="label">gtk-ok</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">4</property>
+ <property name="row_spacing">8</property>
+ <child>
+ <object class="GtkSpinButton" id="node_properties_duration_spinbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">duration_adjustment</property>
+ <property name="digits">3</property>
+ <property name="numeric">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Duration: </property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Note: </property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="node_properties_note_spinbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">note_num_adjustment</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-10">node_properties_apply_button</action-widget>
+ <action-widget response="-6">node_properties_cancel_button</action-widget>
+ <action-widget response="-5">node_properties_ok_button</action-widget>
+ </action-widgets>
+ </object>
+ <object class="GtkAdjustment" id="quantize_adjustment">
+ <property name="lower">1</property>
+ <property name="upper">256</property>
+ <property name="value">1</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">8</property>
+ </object>
+</interface>
diff --git a/src/gui/main.cpp b/src/gui/main.cpp
new file mode 100644
index 0000000..547966f
--- /dev/null
+++ b/src/gui/main.cpp
@@ -0,0 +1,100 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2014 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <signal.h>
+
+#include <iostream>
+#include <string>
+
+#include "lv2/lv2plug.in/ns/ext/midi/midi.h"
+#include "machina/Engine.hpp"
+#include "machina/Loader.hpp"
+#include "machina/Machine.hpp"
+#include "machina/URIs.hpp"
+#include "machina_config.h"
+#include "sord/sordmm.hpp"
+
+#include "MachinaGUI.hpp"
+
+using namespace std;
+using namespace machina;
+
+int
+main(int argc, char** argv)
+{
+ Glib::thread_init();
+ machina::URIs::init();
+
+ Sord::World rdf_world;
+ rdf_world.add_prefix("", MACHINA_NS);
+ rdf_world.add_prefix("midi", LV2_MIDI_PREFIX);
+
+ Forge forge;
+ SPtr<machina::Machine> machine;
+
+ Raul::TimeUnit beats(TimeUnit::BEATS, MACHINA_PPQN);
+
+ // Load machine, if given
+ if (argc >= 2) {
+ const string filename = argv[1];
+ const string ext = filename.substr(filename.length() - 4);
+
+ if (ext == ".ttl") {
+ cout << "Loading machine from " << filename << endl;
+ machine = Loader(forge, rdf_world).load(filename);
+
+ } else if (ext == ".mid") {
+ cout << "Building machine from MIDI file " << filename << endl;
+
+ double q = 0.0;
+ if (argc >= 3) {
+ q = strtod(argv[2], NULL);
+ cout << "Quantization: " << q << endl;
+ }
+
+ machine = Loader(forge, rdf_world).load_midi(
+ filename, q, Raul::TimeDuration(beats, 0, 0));
+ }
+
+ if (!machine) {
+ cerr << "Failed to load machine, exiting" << std::endl;
+ return 1;
+ }
+ }
+
+
+ if (!machine) {
+ machine = SPtr<Machine>(new Machine(beats));
+ }
+
+ // Create driver
+ SPtr<Driver> driver(Engine::new_driver(forge, "jack", machine));
+ if (!driver) {
+ cerr << "warning: Failed to create Jack driver, using SMF" << endl;
+ driver = SPtr<Driver>(Engine::new_driver(forge, "smf", machine));
+ }
+
+ SPtr<Engine> engine(new Engine(forge, driver, rdf_world));
+
+ Gtk::Main app(argc, argv);
+
+ driver->activate();
+ gui::MachinaGUI gui(engine);
+
+ app.run(*gui.window());
+
+ return 0;
+}
diff --git a/src/gui/wscript b/src/gui/wscript
new file mode 100644
index 0000000..a5a04be
--- /dev/null
+++ b/src/gui/wscript
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+from waflib.extras import autowaf as autowaf
+
+def build(bld):
+ obj = bld(features = 'cxx cxxshlib')
+ obj.source = '''
+ EdgeView.cpp
+ MachinaCanvas.cpp
+ MachinaGUI.cpp
+ NodePropertiesWindow.cpp
+ NodeView.cpp
+ '''
+
+ obj.includes = ['.', '..', '../..', '../engine']
+ obj.export_includes = ['.']
+ obj.name = 'libmachina_gui'
+ obj.target = 'machina_gui'
+ obj.use = 'libmachina_engine libmachina_client'
+ autowaf.use_lib(bld, obj, '''
+ GANV
+ GLADEMM
+ GLIBMM
+ GNOMECANVAS
+ GTKMM
+ RAUL
+ SORD
+ SIGCPP
+ EUGENE
+ LV2
+ ''')
+
+ # GUI runtime files
+ bld.install_files('${DATADIR}/machina', 'machina.ui')
+ bld.install_files('${DATADIR}/machina', 'machina.svg')
+
+ # Executable
+ obj = bld(features = 'cxx cxxprogram')
+ obj.target = 'machina_gui'
+ obj.source = 'main.cpp'
+ obj.includes = ['.', '../..', '../engine']
+ obj.defines = 'VERSION="' + bld.env.MACHINA_VERSION + '"'
+ obj.use = 'libmachina_engine libmachina_gui'
+ autowaf.use_lib(bld, obj, 'GTHREAD GLIBMM GTKMM SORD RAUL MACHINA EUGENE GANV LV2')
diff --git a/src/machina.cpp b/src/machina.cpp
new file mode 100644
index 0000000..32eab05
--- /dev/null
+++ b/src/machina.cpp
@@ -0,0 +1,86 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "machina_config.h"
+
+#include <iostream>
+#include <signal.h>
+#include "machina/Action.hpp"
+#include "machina/Edge.hpp"
+#include "machina/Engine.hpp"
+#include "machina/Machine.hpp"
+#include "machina/MidiAction.hpp"
+#ifdef HAVE_EUGENE
+# include "machina/Problem.hpp"
+#endif
+
+#include "JackDriver.hpp"
+
+using namespace std;
+using namespace machina;
+
+bool quit = false;
+
+void
+catch_int(int)
+{
+ signal(SIGINT, catch_int);
+ signal(SIGTERM, catch_int);
+
+ std::cout << "Interrupted" << std::endl;
+
+ quit = true;
+}
+
+int
+main(int argc, char** argv)
+{
+ if (argc != 2) {
+ cout << "Usage: " << argv[0] << " FILE" << endl;
+ return -1;
+ }
+
+ if (!Glib::thread_supported()) {
+ Glib::thread_init();
+ }
+
+ SPtr<JackDriver> driver(new JackDriver());
+
+ Sord::World rdf_world;
+ Engine engine(driver, rdf_world);
+
+ /* FIXME: Would be nice if this could take URIs on the cmd line
+ char* uri = (char*)calloc(6 + strlen(argv[1]), sizeof(char));
+ strcpy(uri, "file:");
+ strcat(uri, argv[1]);
+ engine.load_machine(uri);
+ free(uri);
+ */
+ engine.load_machine(argv[1]);
+
+ driver->attach("machina");
+
+ signal(SIGINT, catch_int);
+ signal(SIGTERM, catch_int);
+
+ while (!quit) {
+ sleep(1);
+ }
+
+ driver->detach();
+
+ return 0;
+}
diff --git a/src/midi2machina.cpp b/src/midi2machina.cpp
new file mode 100644
index 0000000..fdff2f7
--- /dev/null
+++ b/src/midi2machina.cpp
@@ -0,0 +1,81 @@
+/*
+ This file is part of Machina.
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Machina is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation, either version 3 of the License, or any later version.
+
+ Machina is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Machina. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <iostream>
+#include <signal.h>
+#include "sord/sordmm.hpp"
+#include "machina/Engine.hpp"
+#include "machina/Machine.hpp"
+#include "machina/Action.hpp"
+#include "machina/Edge.hpp"
+#include "machina/SMFDriver.hpp"
+#include "machina/MidiAction.hpp"
+
+using namespace std;
+using namespace machina;
+
+bool quit = false;
+
+void
+catch_int(int)
+{
+ signal(SIGINT, catch_int);
+ signal(SIGTERM, catch_int);
+
+ std::cout << "Interrupted" << std::endl;
+
+ quit = true;
+}
+
+int
+main(int argc, char** argv)
+{
+ if (argc != 3) {
+ cout << "Usage: midi2machina QUANTIZATION FILE" << endl;
+ cout << "Specify quantization in beats, e.g. 1.0, or 0 for none"
+ << endl;
+ return -1;
+ }
+
+ SPtr<SMFDriver> driver(new SMFDriver());
+
+ SPtr<Machine> machine = driver->learn(argv[2], strtof(argv[1], NULL));
+
+ if (!machine) {
+ cout << "Failed to load MIDI file." << endl;
+ return -1;
+ }
+
+ /*
+ string out_filename = argv[1];
+ if (out_filename.find_last_of("/") != string::npos)
+ out_filename = out_filename.substr(out_filename.find_last_of("/")+1);
+
+ const string::size_type last_dot = out_filename.find_last_of(".");
+ if (last_dot != string::npos && last_dot != 0)
+ out_filename = out_filename.substr(0, last_dot);
+ out_filename += ".machina";
+
+ cout << "Writing output to " << out_filename << endl;
+ */
+
+ Sord::World world;
+ Sord::Model model(world);
+ machine->write_state(model);
+ model.serialise_to_file_handle(stdout);
+
+ return 0;
+}
diff --git a/util/machina2dot b/util/machina2dot
new file mode 100755
index 0000000..22cfbf0
--- /dev/null
+++ b/util/machina2dot
@@ -0,0 +1,184 @@
+#!/usr/bin/python
+
+import sys
+import RDF
+
+model = RDF.Model()
+parser = RDF.Parser(name="guess")
+
+if len(sys.argv) != 2:
+ print "Usage: machina2dot FILE"
+ sys.exit(-1)
+
+parser.parse_into_model(model, "file:" + sys.argv[1])
+
+print """
+digraph finite_state_machine {
+ rankdir=LR;
+ {
+ node [ shape = doublecircle ];
+""",
+
+written_nodes = {}
+node_durations = {}
+invis_id = 0;
+
+
+# Initial selectors
+
+initial_selectors_query = RDF.SPARQLQuery("""
+PREFIX machina: <http://drobilla.net/ns/machina#>
+SELECT DISTINCT ?n ?dur ?note WHERE {
+ ?m machina:initialNode ?n .
+ ?n a machina:SelectorNode ;
+ machina:duration ?dur .
+ OPTIONAL { ?n machina:enterAction ?a .
+ ?a machina:midiNote ?note }
+}
+""")
+
+for result in initial_selectors_query.execute(model):
+ node_id = result['n'].blank_identifier
+ duration = float(result['dur'].literal_value['string'])
+ written_nodes[node_id] = True;
+ node_durations[node_id] = duration
+ print "\t{ node [ style = invis ] ",
+ print "invis%d" % invis_id, " }"
+
+ label = "d=%.1f" % duration
+ if result['note']:
+ label += "\\nn=%s" % result['note'].literal_value['string']
+
+ print '\t', node_id, "[ label=\"%s\" ]" % label
+ print '\t', "invis%d" % invis_id, " -> ", node_id
+ invis_id += 1
+
+print """ } {
+ node [ shape = circle ];
+"""
+
+
+# Initial non-selectors
+
+initial_nodes_query = RDF.SPARQLQuery("""
+PREFIX machina: <http://drobilla.net/ns/machina#>
+SELECT DISTINCT ?n ?dur ?note WHERE {
+ ?m machina:initialNode ?n .
+ ?n a machina:Node ;
+ machina:duration ?dur .
+ OPTIONAL { ?n machina:enterAction ?a .
+ ?a machina:midiNote ?note }
+}
+""")
+
+for result in initial_nodes_query.execute(model):
+ node_id = result['n'].blank_identifier
+ duration = float(result['dur'].literal_value['string'])
+
+ if written_nodes.has_key(node_id):
+ continue
+
+ written_nodes[node_id] = True;
+ node_durations[node_id] = duration
+ print "\t{ node [ style = invis ] ",
+ print "invis%d" % invis_id, " }"
+
+ label = "d=%.1f" % duration
+ if result['note']:
+ label += "\\nn=%s" % result['note'].literal_value['string']
+
+ print '\t', node_id, "[ label=\"%s\" ]" % label
+ print '\t', "invis%d" % invis_id, " -> ", node_id
+ invis_id += 1
+
+
+# Non-initial selectors
+
+print """ } {
+ node [ shape = doublecircle ];
+"""
+
+selectors_query = RDF.SPARQLQuery("""
+PREFIX machina: <http://drobilla.net/ns/machina#>
+SELECT DISTINCT ?n ?dur ?note WHERE {
+ ?m machina:node ?n .
+ ?n a machina:SelectorNode ;
+ machina:duration ?dur .
+ OPTIONAL { ?n machina:enterAction ?a .
+ ?a machina:midiNote ?note }
+}
+""")
+
+
+for result in selectors_query.execute(model):
+ node_id = result['n'].blank_identifier
+ duration = float(result['dur'].literal_value['string'])
+
+ if written_nodes.has_key(node_id):
+ continue
+
+ node_durations[node_id] = duration
+
+ label = "d=%.1f" % duration
+ if result['note']:
+ label += "\\nn=%s" % result['note'].literal_value['string']
+
+ print '\t', node_id, "[ label=\"%s\" ]" % label
+
+
+
+# Non-initial non-selectors
+
+print """ } {
+ node [ shape = circle ];
+"""
+
+nodes_query = RDF.SPARQLQuery("""
+PREFIX machina: <http://drobilla.net/ns/machina#>
+SELECT DISTINCT ?n ?dur ?note WHERE {
+ ?m machina:node ?n .
+ ?n a machina:Node ;
+ machina:duration ?dur .
+ OPTIONAL { ?n machina:enterAction ?a .
+ ?a machina:midiNote ?note }
+}
+""")
+
+
+for result in nodes_query.execute(model):
+ node_id = result['n'].blank_identifier
+ duration = float(result['dur'].literal_value['string'])
+
+ if written_nodes.has_key(node_id):
+ continue
+
+ node_durations[node_id] = duration
+
+ label = "d=%.1f" % duration
+ if result['note']:
+ label += "\\nn=%s" % result['note'].literal_value['string']
+
+ print '\t', node_id, "[ label=\"%s\" ]" % label
+
+
+# Edges
+
+edge_query = RDF.SPARQLQuery("""
+PREFIX machina: <http://drobilla.net/ns/machina#>
+SELECT DISTINCT ?tail ?head ?prob WHERE {
+ ?e a machina:Edge ;
+ machina:tail ?tail ;
+ machina:head ?head ;
+ machina:probability ?prob .
+}
+""")
+
+for edge in edge_query.execute(model):
+ print '\t', edge['tail'].blank_identifier, ' -> ',
+ print edge['head'].blank_identifier, ' ',
+ print "[ label = \"%1.2f\"" % float(edge['prob'].literal_value['string']),
+ print "minlen = ", node_durations[edge['tail'].blank_identifier]+0.1, " ];"
+
+print """
+ }
+}"""
diff --git a/waf b/waf
index e22930a..483dea5 100755
--- a/waf
+++ b/waf
@@ -1,16 +1,169 @@
#!/usr/bin/env python
+# encoding: latin-1
+# Thomas Nagy, 2005-2017
+#
+"""
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
-# Minimal waf script for projects that include waflib directly
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
-from waflib import Context, Scripting
+2. 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.
-import inspect
-import os
+3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
-def main():
- script_path = os.path.abspath(inspect.getfile(inspect.getmodule(main)))
- project_path = os.path.dirname(script_path)
- Scripting.waf_entry_point(os.getcwd(), Context.WAFVERSION, project_path)
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR "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 AUTHOR 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.
+"""
+
+import os, sys, inspect
+
+VERSION="2.0.4"
+REVISION="0466fbd730f20d94d655ebe324c8478e"
+GIT="x"
+INSTALL=''
+C1='#1'
+C2='#/'
+C3='#.'
+cwd = os.getcwd()
+join = os.path.join
+
+
+WAF='waf'
+def b(x):
+ return x
+if sys.hexversion>0x300000f:
+ WAF='waf3'
+ def b(x):
+ return x.encode()
+
+def err(m):
+ print(('\033[91mError: %s\033[0m' % m))
+ sys.exit(1)
+
+def unpack_wafdir(dir, src):
+ f = open(src,'rb')
+ c = 'corrupt archive (%d)'
+ while 1:
+ line = f.readline()
+ if not line: err('run waf-light from a folder containing waflib')
+ if line == b('#==>\n'):
+ txt = f.readline()
+ if not txt: err(c % 1)
+ if f.readline() != b('#<==\n'): err(c % 2)
+ break
+ if not txt: err(c % 3)
+ txt = txt[1:-1].replace(b(C1), b('\n')).replace(b(C2), b('\r')).replace(b(C3), b('\x00'))
+
+ import shutil, tarfile
+ try: shutil.rmtree(dir)
+ except OSError: pass
+ try:
+ for x in ('Tools', 'extras'):
+ os.makedirs(join(dir, 'waflib', x))
+ except OSError:
+ err("Cannot unpack waf lib into %s\nMove waf in a writable directory" % dir)
+
+ os.chdir(dir)
+ tmp = 't.bz2'
+ t = open(tmp,'wb')
+ try: t.write(txt)
+ finally: t.close()
+
+ try:
+ t = tarfile.open(tmp)
+ except:
+ try:
+ os.system('bunzip2 t.bz2')
+ t = tarfile.open('t')
+ tmp = 't'
+ except:
+ os.chdir(cwd)
+ try: shutil.rmtree(dir)
+ except OSError: pass
+ err("Waf cannot be unpacked, check that bzip2 support is present")
+
+ try:
+ for x in t: t.extract(x)
+ finally:
+ t.close()
+
+ for x in ('Tools', 'extras'):
+ os.chmod(join('waflib',x), 493)
+
+ if sys.hexversion<0x300000f:
+ sys.path = [join(dir, 'waflib')] + sys.path
+ import fixpy2
+ fixpy2.fixdir(dir)
+
+ os.remove(tmp)
+ os.chdir(cwd)
+
+ try: dir = unicode(dir, 'mbcs')
+ except: pass
+ try:
+ from ctypes import windll
+ windll.kernel32.SetFileAttributesW(dir, 2)
+ except:
+ pass
+
+def test(dir):
+ try:
+ os.stat(join(dir, 'waflib'))
+ return os.path.abspath(dir)
+ except OSError:
+ pass
+
+def find_lib():
+ src = os.path.abspath(inspect.getfile(inspect.getmodule(err)))
+ base, name = os.path.split(src)
+
+ #devs use $WAFDIR
+ w=test(os.environ.get('WAFDIR', ''))
+ if w: return w
+
+ #waf-light
+ if name.endswith('waf-light'):
+ w = test(base)
+ if w: return w
+ err('waf-light requires waflib -> export WAFDIR=/folder')
+
+ dirname = '%s-%s-%s' % (WAF, VERSION, REVISION)
+ for i in (INSTALL,'/usr','/usr/local','/opt'):
+ w = test(i + '/lib/' + dirname)
+ if w: return w
+
+ #waf-local
+ dir = join(base, (sys.platform != 'win32' and '.' or '') + dirname)
+ w = test(dir)
+ if w: return w
+
+ #unpack
+ unpack_wafdir(dir, src)
+ return dir
+
+wafdir = find_lib()
+sys.path.insert(0, wafdir)
if __name__ == '__main__':
- main()
+
+ from waflib import Scripting
+ Scripting.waf_entry_point(cwd, VERSION, wafdir)
+
+#==>
+#BZh91AY&SYhë+÷¥Õÿÿÿ°ÐÿÿÿÿÿÿÿÿÿÿÿE ‚„ 0Á#.ˆ¨bJ¼wzÛ¢#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.Ož¦ÕµfXj™NìRíP¥mªd¾ÝØ©´ÞíÛ`¥<¶µS¶o.ws²šÖå}íJæÍMlonƧׅï. s6fÓ裈®µ½Í»¯XVfo7£ÕQ­ØoZ¹èÅ•Þû1qlõ«ïnß]wÃîóÞÝÍ7[E†º{;·z÷ß>ùðîÀ}»Þ¾>+j{½í}Oî÷;SVÖ{íà#.#.#.½€ôØÐ€t¯³p>€Ÿo#. ››0í }Þƒßes}ìwvtP=#.hÖq·{¶Ù¨»A@kÐh(#1ÐÐÙÜURUR”¡J/{ƒD’K¬AE#.²’¡èÖöàBÏ­]ëîÝ{„}<óíÙÝ9;¾¼Ú²•“»F·c Ù:dS“4Íñmæè×M sKåU«ß.®Æª\ôïM:ï{I™—{n›w¶;}|Ž™U·Ÿg¯3>ã¹÷»Êíê÷ŽÜgk];±C]ížÊÐaQU$%ZkM%í»3¬…±Ý]Öò{¯rÇmìié¡wvÆvàôn›mB¥Ï{Ùo4#.P Hìez`(ô={#.‘§B”÷½¹ïXâQ{nî5vkO¼5ï¾úû¾fÝ}/cÝ€]öÓæûŒ‹[­}uÛ4úã#. ozîlÆÀ7ÇkÎ÷ ž÷kÐ#1ûuݱ77+ëë×¥=΃£Ž½WqÝh°öÇ@#f}ÜpZ{»wruÎí®e¶|6órùöúñJæ§s¹>ï<÷,íNãkO·igŸyÝæ·w.ëŽío¼{ǦßéÝó•ÛQ¶•(ž¹»vû…³ZÆÞOZˆËÛ:ÓÁžAM¯\y;ÙðÔ>ÜíÃ\Ù|û©î{§±Û7xì¾o®³^Ñ`ûÏyov_o«Ùæ-¾µ¶=µpöWvØ× mum®îž{X#.o]M½ç½==÷»®áî÷g{k¦ïH(¡@%RDITu›ÍÛvŽšë˜k£QmiÛ})î—F›°à›™¶îÖöÝ–‘§Q+®Ì¯7ŽôÕEwW{¶Q ”äw¹ß{{yÏ€#.}÷ÏQò#.#.S|ã×6\rdzÞy¤]ª=Û}ÏTôezcw>÷®ÙgmÝKÒ39™uè|wsÓèç³uJOnیعÉß^:ö¹iòwî½7s®Ôð•hÉ¢œ¶öºUâf¥;kp˾î£äûàÓÜgkª>ÛÄûµ·Ö¹µfd3bñÚ'#/¾ÝϨ¹¥Î]|]Ùäì»”ÆíÇ3Ö†#/˜fš ½Ü+™˜Km»·Ønìw@­Ø•Nx7KÕÛ¯^wËÞÝ{Ý¥Í×»X#.€U[Û]EU;c¥Þ÷žÏCMÀÃ.·zíiÜ^¼p­çÓËÞ}ç£Ï\ûíFûîÜÝwÜ+M—°œ¾ûë¼ú³5èÙ õ zav5O^ÝaYÝ;ÎK`Áà°.Ü©Õ`çCœì÷ãÇß>﮳<ÛCOAÐVC×»[î¥ØtØ{%[ÓnñóÌûÞöð¶Ð#.#.(mÞ˸ï{‰¶õ¥Å¼Ngwvëc*öéÊ]eËïrŽš{²œíÒU#1í® ]7l&Ý×Þg-‰žéÜvªÅ¼®õëɺî×kǾŸ÷>³Ý÷´õñ4}é.æ@ÚßGÎŒTlÈ-™ï»Ã±­âàвæ÷gs¶ë=iÞ;¨m¾O}÷;ö8mw}¸zøºöó¯ñyC-O™¾†š   #.Ð#.4#.FM†¦Šz“OF§”LŒ†OSÔò ”Ð!  &@L)¢~š§èÕ?T<šF@ÃPdh#.#.#.#.‚D! ÓA¡IOdÐÁOJhô‡©é”zG¦Ñê#.#.#.#.OT¤ˆAôž jh†i5=MSOP2#.j €2yG¨z€#.€#.IM4Ð#.C@  A4Âz™=MLÔÄj4È#/#.@#.MDA#.&24#.§ˆž¤ý'ª~£QêzM ö©Ð#.#. #.ÿûæ?Ù«Zî%üŽ]æ¶®æ_¯ZÒêÄF7Ƶ®éÂ2ŶS/}kmsVµFÚÈú’ÔHB?AùÿOèºýYŸ¤xé7f)Ii¼ÈDR›¯Îe<Ó¼V!<N.JlT,UÜL$FÕ‘ý²&*­Þ«òCrƒ¤#.(Ducu rï|˜ˆ\¼´ÓÅÄÕ:§NªmæŠx³‰œq--W3|’m¼Þ¸âšÍöSðß[¾D(á AHÅ`F¨(QmdÛVe­¤µ«¥¶¹KQF ˆ4‘Z‚Ôd+œç,!"E4+)Š BG"(¡`(6"(Bº Ñ#.l#1ºjÕÝ©ÑlV-íwmI[jªÞú¶ÕjffBÉFf SLM†–((’DšMLÄÉa5L± Ò”iE6Òc„„Í’PFCT´[ƒ hÍ%“K#†ш@¢£‘6”£H¬jXЦ,bc(LØÒE&ÉhÚˆ¡,²Ò˜ÊÑTD,´†ÍÁ²“5ÖEhÀ*#/&ÈŠ¤Ø¦2P&f™iÀŠR±©J6Úm[+kAb!5C6$Ñ™’d&ˆ Ë&Úm´ÓhRQ²’¦¶2Ô¶Ì©–“)¤¢4FÀ©aÍY¢’RÆ‚–E1L„ÆÐ‰2¢Í"±I„±Q …ŠMH”f•‚¥1Š6!PHŒ¦0- ˆÉ#jL²ÄA 3BQ"„C)%#F•”fŒˆÙE,šÉEѱ -"Ä2YHH±¬¥$&(’“#(ˆÉ¡D3#1“F‘(Æ“ k !Q‹E€¢fRÓLÀ66É Å&3M3a3BÄmI° 6VJMŠÄD–ŒPRBX€‘4h"Q(,0ÊÉ)Åš(”•LÔlVÒjH’b iJ$ ‘d¶ $‹,Ê)dš&̤FÆ"M‰‘£6DÁ3lÓ!RÁeŒT†ÉšCF"5+%’6#/•!4H¥’BA5"E¦š,hµI!,Êl£(1h¦f‘´F‚)¥Ú˜,Pi,ÆB1d¤²$˜‹Ó0¢”Q£0Á¨¦…&*#/‚$H$0ŠLÆ’Fˆ‘2Âb‚6I•35f4U)TJ"fCR4Ä4ÍŠ”6ÊhÙ#Eb’I6H£S*l‹LÑcb)¤ÊLÊb–FVm2%b#ILÈSSI¦H) e¤54ZmŠ#f¥Jd˜˜Ù#&IJÉ¢ÆJÄc(ÊH&b(ØÛ’Mi(Êj6ME’H¡¤l ˆÆ –Í&&ÐhÑSY„ÒeE3!³"IM’™PLÙ2Vl¬ZÂX ‰‰4Ë$IbÚ5A¬E2ØR$(ÆÅ"A„e’Å£L2™¦”Á²VSJ`¤Ö’#/ &)IŒÂ2ɲ#)‚E6Ti–¦• ‘3F‰¶ÔªÚ2†JÓ,e34¢’S1Z#/J[–h³šc(K,©²É±Z™l-š10Å6#)@d°Ò–¿™×m‘ ©ªAc´QŠl$ѤÔmd‘K)£¡£Z&–¨ØØEI5Y–FY1”d‹)€•&1´ˆ0ÄÌÒ–ÌÕŒI¥BØÚŠBÑ“L"Xʤ“23XXÚ#/+4S&L´›A±eXÛ6P¬‰–ESÍE0¦ÍŒ¤ÌÛ%´ÒÂcFÖ‹DkdÓ5’J™e6²kE6ÊRšbÓÔ$d¤mEŠVX“I©*#hªÁZ‚ÆŠÚ2TEŠØ¶“Y(¶£h¨©5bh ’*‹Q*ѰËF¬Xƒi4”4BkPÂJ)Ú TLD-)&)Zk±£i‰lj’Á¢ÚH+mS[lÓJ¨HL¦jjÉ¢M‘‰”Ô"‹$VÔµ›c¦Ól³J˜Í4Ú¦D[iT²I™ªTŒY¦‰´$™&[,hM2ÒY6ÊÊ´kD²ŒaF*,H`‰˜Ðb“, , D‰Ú ŒCe´ÍS0!*(dÖ’²M,l™4Q²!²Å"›)FÈŠÍH²!bS2K#1J,Å3P¢Ñ¬ÉŒ¬Si2QÖ@¤#4È™$"50XÊ&‚SPY HAJh±Dҙřf”#/°˜Ä  ²cÂRÉ2É ¨´dˆ"2ÔQF̤’Œ‚2 E”¬d0Ú*LjJ2lŒlI–(ÉXÒ$ 4i ÆM&“fSfRRÌÚR’±L,‘¨†5$­FÍhÍXh©M&ÃLl±¬Ê6£@‘Œ„LR#/16$©6“(QRšb¤‚Š[%2£h*™Sd²E“(LTI¤(iFJÌ’Ú+dF„S3 dJ‚ˆ0Ùf(ˆd؃Y¤lQ¢³I²ˆ›5$$-„¨¨ÔQX̶L1BJ#1F–LŒRˆ#b5e–K!¢ ŒÂÉM”%Im¢¡"±ª+$ѰJU(6("2Fɪ-“ D0°ƒT¥#/Li†)TjˆÒšid(¢Ä™6Ì‹D‹Qc”ª¥¦Š¢ØÒ`ˆ²X’)¦R²…bÊ À ‰’TØ”DÍ%ZˆÚÁ&*f¢¤´X”Y¥¨ÉXµ´˜¤¬Ò©mDÐ6%2”Ô–$’ÅIFÉ¢HŒIT”†¨¤Ì‚1k2‚‹)S*b)¶¤¬–É!M$Ôh"0Q6kI´[)%Š“LhÁ1`)$V¥£ie¢Ñµ‰6´U£)FÒUH‚£m’‹ŠhL’™1¦¤IA©›$ͨÔU%‹T•,Õ“Z´[¢¢‰"ÛͳKj-‹FµÚLÉ›(„²ÙX›hª,ʨÚl¨¥-J)IA‘”š™(,&bi3XÛ™±$X‚ ¤­‹&¥4ÖZŠÆ±šFÑ’Kµ¬R&m¦$£¨a b(¨¦’V™É*ÆØ¶Å¦jÚ1­e¤©¡–¶RÉmMM²Ôi«FQDEŒÑY%#/f¤ÔmŒÍ›*"„ƒQ’L¤™‘Dd#1¤¶ŠelÇñÿ±õ§ú~jsüø³B Oëý ÌdBD‰V0ë_ýÅšÅ\¬4›=]Wñ<[Ç7ðÿoKze7q˺þO>vóðþ‹ˆhó%qÿ޵ĔÀA%‚VÆ,º&©%UPÕTte#ØöG¥êö×ú.Óþß™uþ¯íò¼ÈÛi¯,€Üò%„ŒEõsÕ£þ‹n¶;m"‚‡Ã××N-ÁÖZX%'uÔäšu­;brxy‘J1°á‹³\wz(ÓòÎ]ióŒŸì¤kÛ ÕdÔHí/ ±½:ô×F{+‡““G]“¼êQ™ª™¤Í™@À…°¤ŠµUI޼ûfsW"2l=û\Åowƒ4fS®4¹–%!L@P‘#%Í®Åö1¤gÀ®È¶™|[›#7¦æ’ÁdŒ”^¼ÊŒfH‡:¡SÓ÷éCþ éãÚ0#FŸ'|»ªæºhñ\Kò.h"»ÐJE‚Å}ÿökD;^/LV¢eÙ±SShR,ürV?ÉÉ"˜Ò¼Z°Yâ$£Çöî\P?ìŒ=XQ÷}0­ ÐÖXnÙð>Ïõk×£•=Nª+9P® îãF€ÑÖè æî]P ¬sãËÌ”råÑwÊæÝ,†Š*æÝ<î‹êºùKol[îB¼[\Šf¾.EmâæñÒ'ñ·j¼s›…ñùâ aãÆa#/›O,¡¶cÓ+ˆm·Š}~uovô÷U\ÒmÝrޏÓk¼ç“,oRŒ­F¶Â46€8šë•Þwc”t®\ÉE.»¤‘Îd+ìU¼¼íþýý+Ëà½mªR)BŠ‘•‚ÐûleÓ'–¬#1üè¤OJš~¶¦#/+j~OLHeàÒ]ü]¹_ßO¿«À¨Tž»_Ö_ˆ†A†bI>ʘ¹—óKH(#lg}}1Žhøæ¹k›ÿ pš8¢*–ËahGb­ódlAîTi¯M¾›’(3Éë8(™ŽÍÙs‰ñÉù5} ”|±ÉÃUíâaðøkÕš08 ²Kç^—\–f5ŠÛË[Ãܯ&¶¯ƒ â„-”ƒÊª)OݯÛ~ŽyßC¹;7ktä×ΡLciœ…’2ÆTè—u»3¼”ROÅÆ-¹dÂ6d#g£ %C°%⇆K-2Å”ÂØZsiP;¨¥ç•ãî~x©Ñ\¤]VYU@‚Ÿf.àÒA&ñ’HàÊ}á4ùu…‘69×·yçõUö·“ôºf]Ú“œh£ÊŠª‚Ä7Ím‰Z¾)t4=]n‚^î{«Ï]=c f{:_&föÒ±<ÙZøWVcJh `²" UãrɨÞ9j*ßíþÏo&4úIµì®Y0Ÿ´ú¿«ï{uŠwVæÒ„Ñðþ‡›^”l‰ŒbI5%'ÁÍÊïwZ·K§-âvâ‚`MS•ÖR’.•ˆÁJIME¶ó¿õëx@Ò’[IP÷»–-ûrÝ{»o\clP›•k‚ÐSÜ%kUBÑH,ŠAdD/¡øUãƒIo­Ð#E_’ìaõäøïš°ÌâȈÀŽ48‰QãPøóêXeˆ®•µ›3ðI0‚ #ítü½mÙ„Û&¿2ä—áîÕòwÉѦ×……á¨$`²¡)!³iè¹9tÂçZú²üN­õi«à/™ :”y¦7J)”"’ê­)ìÞ­doåW<*ÊrrËfõ`Å#.T`)À„9hàaZáÆ!ŒR'8^SÉhTþˆ£G†ˆÒÛKM[ͽá…⢠lõ…O£#1×ÉlV–ŠEFRpg«0"<™% kTt©@'…SuðoÙALáE*}øÃ®4•g“á‡^$Þüb«‚m/?øiî®)bÙˆn›8@„»šQRA‡t_¥Ê»aØöˆ!£#/:óÁ‡ƒB‡Q$)#1JLÂŒÊ4¦~WO¾¯³}ØügqÔ5„žUá¢öÙå|è¾½À1ÙAMï\Ãʪ™s)KU#1DD|Z’ãT©µt{n…ó³á~pª9\•”®ž9¿†Jj!(#/X +sé_[Sn5¯ša%ȃ‘Y¦‰g{·-6A„8ܤ3óÅ»¾p™©òÓzúf²&™Ñ Ê=SZÏÞû£eQ´ÎëC‹þÈã˜Ý,8:(œÚ>ÖP§z~¾·×âòu5Á1`èÙ÷ûlË^¯œÔ/žx®KåU S¨<g“çïtŸNtI¢Cµ?F,…«Ï‚@8!<Xj'x¿÷3,Ú!½ÃY>‡un’3T·å]¶EèÊ´Lc1×™£&Uí“ΗÕ:PâyË‹âð‰¯_·‰½ã«—¦FêâFpˆVÜòÖvÞò™`ÜŸb¤blÇdzï†s4çíßz`s¨UZ|¦YI×jµÙPÝ(Ib¨Œ1Gù}—ÿvM(I³ Îçb©vŒc8À¬Ø1 )#1†°`¼Î§%±qæ‰ÓÓÀÕÐùdE:´!`ÝËÂmáG6gitü™×lðH`Ò¡ö¥¤nÈcÂ2¸œŠ=}ùLiÈ,­†NŠÚΡ?DØGÑß­íuR6)ãÏ«~k(úw~4LU«¾íëÛdÑéU6ΞN5ÞA6ÖÙÛ Ö(Æúëš¾ÖÕoqëZ®#/ÿToRi¢ǨÅÞhx×ÁÛº£sui×î:…8gLj±±Ï׺} 5Ì==°+~žWÉã²IeT׿¶–“çÚ”Ò£o¨#/_Ï“F÷0Èu4ìí}¦´jU$üQe7EGŸ/Ho¡á¬¯Zóß)®r~RÅ|#1|zˆÞwº¤hõhõêßÚãâB鑆œÜiµ·ßÊÉßu…PÖ´ñH§ Ý‘Ôп—ΧÉ2¾ù<Æ8w³™«óSq®ÿn/«!²©¤ü’2¿¯Äöú'k8~ŸOF#.dn"G“H´”’T¿x·Ÿ¨èƒ}îä7+¯]vKq›ðöº•d%~•Ìp×jWËŸÓPnÈÜ‹/—û°¢m¢Â(O#1©¯ÿ]×äþ_𺖟_çãxèÓøÕE~¿ixÄú.ÛôuÞ"ˆ,F¡ùx¦}lðóíÍãåöa\ñºéQåw7kw!IøÑ•¯¹ýÜç¶(²nÆIüím«é›¸IMº“TìºTèªÍD#/pJf{ñs54³ó2Ðí¼å mk4µ“O»-nõ#/3?K?s°¸ÒÊ.©P`/:…ºýivS<Ë_¥ž¯m¶¶â~trÆ[J~iJ¼E|OS’.NýÔ#ë1þ|èýÚ]“ø ·#Ä8G¨àDL4wwÄ9½ºÛ9ô¬ñùÀn˜:°(m)UER`gìÜ}”T^&j¬64Ø›IÖŸÖÒ;¹Œ‹‰OŽá£÷öuO»~ýð=´B(üU^;÷Ń8™/Þô!Ó]7¸{ZÜo×MËk»#1RNîzߣâ^äV5«œä7ಂñÔ|sÁ’âÄ¡AÉ¥Q×›œ±Ô)#/€]$:Æç€Æ›¼£ŠÂvòRG”tÚN⺄CÆñªb‘HaÙ5zÏäc)—Ûüh†9U/&mrSÝç½Ì>‡ÙF7̹–ÀAAïÚ­€»Ó]ÜkxÌÄES#1{%¶ÑÛwL<S|ÂR:§~˜¶RŸT¦rJRi]/fÇzãuŠ£êKZϾª$yóÚ#/tõdsŒæ£Ãé§j>Ûáuš(æ”"#1ô¨p:{óg¥Tè…Äj¤¤Z+of/Ÿ;/DågùkG#1€¦ƒlÂ%§ƒ#1\²”‰»:¬ì˜‚ÓÎsÛÏaxÛ(WnN¦I0呦RN/ð¾}þ6FPg<DÕ‹õ£üë{Ïéh\t»&öÿ#.€ÊÙ´eŠò¤ÜŒ†#11Á†&… 뢈S+fv†Æ¥k)lNO"s|®û=+8Ƶˆ¬KJ\95ˆ±%®£±ÐOÿuؽ;®wyÑÌqÌâGag,¼¦2:LçÑeEÄãçN,ã¤Éy¤³ëñ-¶Þšú)¨N^Ó29#/3lD3[ÿÏõhrÁB(ªÆ¡º²,é8^Õ s5¯°a0†ÂÁ‹cD°‹Î(44#/!GŒLiþ¨‹`»’É]Üêùd”?k^~e€ˆ«-ꬾ¹®rKyh‡ 0¨EаšR9~Hi}Ô¾Ô=‰ŸÝ}dÛ¦çOº?ÁL>ë8ùª‰„"ºêÓo7ãFŽpžì}”~ç²e}ËóÜfH WÍÇ.âM)™ü<¦„¼§Zª-Ç_:ÌËQ°ãw· ÏyÏZÀ)5r„ˆ5)×UHö«ÌKÁÞE;iëgú(Fx¾¾é‰±ÿ <÷½eïë—7Õ·eŽý98…ð†%¡#…üÓ‚{&Ì/"–(å6š"º[QôÉF>¼í;qíô}|9ƒº,Sæí± ÓŽáxpR?DâØûÔ¢"ôÇÇ>üÊOèþ:ëìÿ­ŽYÖx¡ôy¼ø·áˆ,Üõ¯©Ñ8ÿeNÖX•­“7Wu-ÉSüaØ«|)tç˜îc}³ºÂl¦XÎ¥¼ØÇºµ¡Bʪ8ybäùŽ +¥Vü(<vï9oÌ|fXi¶R_/‡Kñ!œ¢îûèìg£õŒØë9*¨‹1kƒÎ÷y)T:ë0´Ãz¡U/#/WJ¼ü™úŠâ«ÜãêÖ®²–ýõO:Áðí¬óÙžæ (fìû“œN£7ºõªz¿s&اZiÛaǶ‰ix?gÆ\Ó³¹i½];¥ž_ †õ–—¿¥^¹ }¢Á¾³¬!ÄÆüô÷_Ò‹;«K߆-8lN#/³£ÛTLò¬ª=VïÛ]äˆðIœíqiz æ¸ˆÓ BYMirÊ?’´[â»sIå¸dGÚUê o»P‚Oi‹;?±¼µxæßkñ«”ب7S·•x¦`‰.»›Mwq® sfF#1öVRöþ¿ÌÒŽÖkF("Ïéé›{©ÙLåa$5 %q¹õ Ê _}aàšEz*A*2ž#/*rCùwÖìÔñÖ‹gÜ…Þ·üqŽ<¸É2ûåðDZj¦vBI~¿=*#1M„ÿ…³ªß$áüÇaÅ6úÅþžU—Ú-ÈtQ¨jQþê(¼ø ÆF>"?É0Çñþì88ýþz ù¦©m¾›ãµéhæ·ÔÆPEû¡ŽQ¢:U/åSe¿Ã8õ*š¥ph¡œ'çú¯˜Î¦#/ñÓÌÆ³ëÝ1˜2:ÔÔ…Ï ÛiŒmtÒ2Á}[ ã&5Ÿ'=~ˆ+dýl#1Áµ®ypò1(ÃÅï'Òÿ‹´¯ÞÿJ$ÙÍ. ÕùÄ„~Hî™ÎKKLjÊÕÔ‰fÉQ`¨*EI°[ ?ãEý¢Gíí#.|ý]î>µ+öšsQF/F&”µËHêš²ÙÏj9WëíÛnýwèñ8¦¥´_­R*dL!@ˆÅŠÆؘÁŠ•u.ì[а7]J=Ù©ìJf*ó‚tufj þMvÍÓ³6y°ã­L oUQ80±…«|;ò-B&Zj H³â6U’´ODßDX[¿w?~¸\®èã.j8Ú…Ù<‹>Pä#/u2¹ëg|ºÃê¹E$‡K+?ã¬ùµs6¾È´ú›¾^E®Á±DÍ@Ÿ,¾2Ñ´mðöÛí>4;rzØ Šî¡(ü‚¬}øáõa™•4˜[ù7~úª<M`¨¨šþ4ÁyÒÁ¨2£Yª‡•;ŒâÑ%ÝCÝuªR#3RP¥Q(X£/LRâ€ÉQT VTÕã·væñ;\Š\u]Á¢ˆ—ÒEV(Â}µìÎcÒß“§S‚ð:»ÝË¿9?mŠÀ½hÝýŒI8•$’ä™d°½`R·ƒonUÿ®P@ÊÇëÓbX÷Ô}cé €ˆvïÍxÚ/¾!Õ¬|Y(‘‚+”( 9TYß×õÿ»äüñÒå)äQ¬øµ­¾Yòã‹üµ_ØžöœnØ`O‚O“ £>ô8¼Yvö‡1PG•½Õ×Ê2¥1²lÛà“B#/Ÿe#1¬Á籘5ËxF±‘§&­_¡¶¼XµdoTƒAì×óéÎÛ‹o¦&Å 7ºV`åÜÂóÅÀ¶}•ÖÐÙ@–îõã#/²68¯¡‘`¢X(´£♈y)e‹8·ÆçívË}Â#1>˜yb¾KÑØÑPb°y‰xUP >þŒïÏŠ‹jÛèÐêhDQ‚³Ô&L)ô¶œ¡Xظøg¤@y3YgTA¥±mQg•:óaÎÿî6JŽ%µWä2/"Ïb-_w–¬+Ëw½™<ÚY-ìU#1¶b-U<×·ùe¯û}Ó;Øà$\»‹hpåݸ¸€û¾¸fï{ÃóÍ5>-}ìøÊ|ùÇN^˜ É#1]ðirºzXtM¦Õôu4¤Š*ÀÊM´7á(XÎ54oj L c‡#a;BK/á‚hwv§úpé.øˆïÉ${–``7çˆn¶àZgL6ÎûöpÊf¢1øMs4ÑXTFP‰Hý!{ø¥]ü·M²0Ê›×Ù˽Õ{hjªá&wÿníÂ¥`¬Í‰Mü³W—½à†©– §aãéQM"Šw??” Cºžéó:€•›Ãì@øùz~ÀÁ`øóßTâø×Gvƒé™Âá?ÃήyüFÅ/_Kp$=Ì&9½*Ë?“ãiΈýä2ˇ)ÑEÎgÀ‘ðƒsQ´Õ({Ö)q—j6ÓÅ.3´ˆ X0`ÔÆYèL™%•á¸ÆW‰é·Vbµ[ÌÜ”#Cœ§‚¥‚™†$9•>rŠÁH¡„Õ’PmST±~÷™û+©_ž´rîºìÓstf½°ú¯VzÔºýöÇGŽÅíïŒõÜ #Ê­%ÐQ&\RdPévÊC¸pͪªñÌÚô_:Œb«'ƒêAäâ¸ÓIè z­ßÕ[ûuãcéR ñQò#1/`"#1" ˜»]ŠÞÝìâì¶8Á RQ @媱¨xWEX8ÿ’§gl3Ï·S»¸û˜'¦iŽl¢?±Kòmàö‰Ù¯þªë¤§Û™E w¹áB Î@F@QFÍ@—ÿu=yÙS®:$ÿ·7s¥ã(LÎv9ø÷ð×N¸’¯vñ#D>ïé·€6:Úâá»z#/Hv(À1À)5ªjx²:ýsðh ¼z+8,(v¿–ŽšÆTrÒûJ&ùæs6fYκ/ÿ|[/Jó£Œï´¿î ‚zvN½3z»gG(ûDl°så2¼Ÿn*ÓÆZÁ.õèûp„ì[¦”F+Ë|×F~írŽÚ¾¨ÄíBðç©Ñï‡:|¸ð"¶Ž0%«ëçÎ_ŽÆ¾YÎ/ýQÐu%‘V»@7bÐÍ .å^]T…»cžåCW=ÈüHÊ4#}jž{D,ìN/ðu°Õ%A¬bB‚£S9J¹Yâ¿%:"Mƒ@h:TuòÐ޳Ôêl8U@ä¼XÏeTFîá«nNLüÝü·Y+u&/ÏbUÈòš+';ªgÌ¥t6܆öäe¼‹,#1J"\ÇoÔõÁÓŸˆ:îMêàïbé#1ñ{p]úsær]pè"³¹”8ò”ãœkv~u±ÊIÿ=Ms‹÷¨Øê+úrã£ÒK¼€Ûñ[›èá›À‰›r#X>sèÓôå#ÇÿXNHr0àÚ_Æÿ-ßëy¹§ ÏL‰TE©G°À_9õfìý—EÇ_¤Æß·»$SoŠä“À%—ÒïÀØaÞ#r\/*¯ò…Ž7!÷r0êêëà~_`"nßú–ÈI€€ñùšá" ‘ —ú÷MIþ+–÷²ã˜®Ï5uç‡UÅÉ,Ê–mY øw+h8ÐC‹ŸáÔ;€r_/O8Ù¦Ê$[“;³—«O£ÃMC¹ý™#/a^¯æÈá.nU^ß]˜D|%:¾ÅÕ®¾|NÓQéT²ŽDJ9×öY“îú;ŸßÀò&emAÞ@„$ Œü¿Ã¿—³ú­xDÆ›½#/öãÿVwÏF+Æp-¦ƒpÞùÈP Ô"²BÖˆ§šeÍŶíãXþÜ­ëÈÚd·èTD…ðþ÷ž½šÿÇÛ© põjæÈE“SÏ¡3Œm!þáâ`¯Æuf>ê§T¤³(Ï8 ÏçèÚ\â]~>­ýtËå‹wZ:£M%¶²AÀg©þ‹]"V÷úf/æN°Ì68Q4`Xè D.j›tW6#]M$<¾ZìÿE¾ˆŒbôªâô1†¤Mªñí-ßñÍd¾ ‰lúk¯m=ÙQ€ñ>× .„ìÓêœF‚ ýþºÂt¨›å›þŸÿgû¦4šÔEõŒ @^ŒcÂÝóüÒÁs¹æYÄÌÔì3*|¾TË—Å<T™/øÖ…~dõæ‚qŸ¾nÿ¨Ó…ÎÃG;ϯ÷áú^µ‡íË0‚ϲä—5í9CôÖø8.C¡²£Þß6áôü^—ŒÜn®û¯áè¸Ø¦våßbJ܉Hj]ÓýT’>ë'_è}?Ã8£;è2ÕGk²=CGGÇ¿5»|/°?ä¤_†=JÍq·ÕÑ”NC2GÅü}qŒHHø ³Uù„E¢³äÈ=Ñ÷tôÒ|]qM1H(<šï*°Î^»ï]ò³˜#/‚ŒË*uU_Îj• bP¢L…H$‹¸#/Ënõ¬í)¯møÌY|GísîÇð°u&á—Ó-”øBó"!'t$óþÎ2`SÿþØunp…¾×éמ›#§mÍ1"0î–b8&DGòS2ø‰9 ‘þrâ®ÎåˆÀ½wêÛÛ£©\ëûÏã¹Ó=åºèª—\Kn˜-­îí´#/N‘W6KŸÖ˜[‘uðÅ<=Åÿ‡ÃÜh`a#/è®Î¾ìEj·Œ°Îš/Pýë¼G#.©F!)qIÑfâNÑ£z¦ãlÂ,Áš…µœ-rg÷EžP€PÕ­‘+D«i%#/伪¡#ö|½']ü¢3ÙâxÙŠß¼¤Œ×”ÿ½æyìVòÈͽK±þÂå¡D9ÁNc­8pMµZíõåöºLÈ¢4û(¦ÅÚ©XѬ”heC-ª ËùtUw¬Á|>ˆ®7íÄÀ‡»€‘~&,ïØ>çKÚuÑŽJ[aµíLjôÓ¯½±ÆÔaœ¿‡•yÛ¡_÷­Ko•<ø8n…Í«V–Ë;ðÏyµÀÿL ¨gU=HbPm9`,d’§>—ÒR0yâ&¡žq'½6“bÂ0y*óëöäámåÞ·ƒ¢„©:gðï!A”ù#,¤eøÑÞK rE üÍøôÚ)"æ#1«­œhª(ª„ä¶ÿ;&>‹ÎÈ'~9š]qÕ§ê‹UÀGøºž}Mñÿ3AäÜXrJ’Þ›¢~­”®f )#fuå{°ûjá\ ¼³²xȹàÒ軯†¹¿3S§#dû—ÏÛJÜä°¿5Û>IÍYüþûþ íæÀ-¡Sé#/w²\KZÅQPC6^}±d²@æ0m˜¤][Wî{µIé:i†&v’mþµýVÿšÆ„NÄÅ;Ç¡¿lSvLöYjÑLì»âXi&»Ì0º§ý?^ ÎÏÆCS{STª¦šÛ‹#o*Š….×p†e-ÜÆ;?Ê âÛ˜~¢#/ˆp™Ñ«Ú ’O$DG$”#.Z°TLö>7"ëã¾w¦È©lz¿8ëÆÊ2ë<8¨D&Q¬Tiò÷5 ýEt–AÄìÙ ã K{£´âÊÿ‡¸=oÖe4™õï±Le`ttGd”-Ǻ§˜1d·âú߇È#/<ßQõþ»¡†Ç½„e$€R D[Üž‚òH.Óì…#/]_#1uV8‘MFƒL6Èa ‡÷Ǫ^GèáuÛ÷¾Ü+kìlß70,Oˆ(‡)2CP°"éÓ2cÜþÓ~»TžknœçÜ”h°;½–JÅl8w)Æ– tåÁ#/ :ëhôŒ_á÷œbÛ£v « SÜ0¦¢chsB]Êc!Q2–Õã;:ÚLm¬„iTô©à㫲‘`¥+ Å´Ù0Àצíþò†¼´óðª†ÐéÍ®ÿ•0WsÊ=ß#/¶ðºØn%yJŒeE` ÄUô/7¯*·‡y?TNý3¿©9Øùt;?óƒ dã0-vèd¦° @@;µh`$éü7M<UÙðººFsåþª%µFmœÁºë©`WŽЂ½ÃfÙÄŽ° Pb¹„ЭªL‚êù«óff¨!¶’EkÓ¨@º#‘·ihQgbæ£á‹ ž\4ˆA‚”HÊö‘(Û‹ð¢çP°AûÖÕh¤i¼Ô‹tS5Uá÷ªQˆZÚ¡ÊóÎÁUG@¸3±È#1LK:@ ÞîúôKSë­L^ÒíäüG(/.$„¹F<vàÜß|ñ¦ˆ*I×Nt§n‹ë/K†ö~)Ñ}eªÇ6·›ö®¾6¨#/3ïÝøï¢SÉ ¦¨ ¡Ë=ÙCÁ“FWè­ÂÈG=ŠàE…™å[”x‰éyÓ§~;W yóž:&ã‡nÁè.܇ ëg룡Žmð增'¯[Œà‡F»/*ž\ÖÅðóáJèІ¸99ptbÔd*GN©.ï« ‡F]0í„u49™ºËÜ%0æËB¡(yßO«v)×?G-K·*mü8q¬bºlkÖÛ)gUësÑŽ"”„Ž)XåØt;SA D^á#1ŠèF¶.%3°8`±D³=û¦3Ÿ\t¶éwL^ÜŽ·UžqVY6ú²‹'ŸìtÔmu}=²gmÅ—œ‚f?îæ­#/ˆ«¾zĉ‘²¡3{aÂÇXôP ýá–€Áè´WÎÕ:ðw¶Âÿ\éÝ·¶ÙJ9>6ëÇÝêYiäú½ØôÒžî“û¨ù¸Ç½†¼f,-Þ.è²ö‰„Õ­+ Æ66Óc2DIhÈŽ-<vFc.n‘pЦAAAmv¿-s©æíetò2^uøñ¼û½NµïÓ/¦½“§Å憲¨(E –H6†©ŠáLc ó)LOLbkëãá‚Ŷa¸Š™ÁÇb´€>!܆DÉÜ5R,ú½<÷Ìœb<\2ÀÂ4­MØf¨*©Cñ¹ª™ö^֊‹O(Dþ²§,v<WGÇó€ÊÉøW´0u¹VO)r™ n($›ÜÒKPºÐf¹0ì`Xïmö¸Šç="N8ÉÁÃp½¦¨ýêR˱¤[ìçš,0p¨¨S"Ðoœ*ñ“!WP#ìà=ÐCKI¨š#/çgÍfoצ´4(eL$Æ ;¤,zæ,M:×OoXHÆ@ëh7ŸbhŸg†;šQ¿ÆMíSêè#1Û2Òùˆ*#. Rãw¾!ù¯9é@±ø[ k†3&kϧÂÕœ?hÇ­ZG÷¶rqÕ›JÄòÐ<Êlú®¶DuKZmÿ¿ùõy4¢r=¢ÆPÞ{>è•cº.uYÁ8cÆ áh¨nÔÆ—Di#/î]&7]Ì£m n øjhì1ÉaœF’4‘#F¶±zZhµC­ØÁHWñ¢à&0¶M’aÈŒ™²…ÖXòFTWZ£AƒNè÷?ê·Ø»{?ZvՈ߯r)xb0e#.¦ p… 2ê ¡( E±¦F(°Mh“(‰#/-‚ȰËê¤Õ &]Ï=²fèÁdåÔ"±Š0­6##1ÓB"(È)5ãS|t­}“§ÇSŒÎá$oéãáź¯?Y™#17Ú!`—”ï·s+9˜¼é=†Š)X,_C¤À[a2¥Ük{’wŽ‚ï]8+q›f²ôÖB]rÀ#."bȘóŽÄv ¦È›ûÆn;4þYÆ‹3YÖôá¶À::ýµˆ0t 6“ìxŒD%eñïϨ~ãÞæ¼ö :ô~†K2ˆÓ/¢#1<ÏòôÔW0ðÒz±ËϺ‹GæÌ®žMëÆãM'Á½è¸v‚eÁqʉhS-¡RÑJc.©cJX†ìƒ’£[ÔÁ¯ŸaésËÓõÔ¹mhœ9 ûC¶K‹3ŒXU Zg䯳sÒñÚŠl*o¿fqÜ•¬ë8,rŸ»ìA°Í„$;~xÜ(¡¥aÊÒ_g %(¬3àgŒï]Ÿ¬šâ1ßÂñ²øàŒQˆöÅ»1ñh5z8x™7ïuÛLàÎ7ƒÂb(KþùAÇñ¶õý3>N11°æ?}¼v¹ÙÛw§~t+ù jý:Ìø™I$ âk£¥´\ÒªÓzžwaS‡Ýƒ£ôv›Óð9З2hI ‡ 4 K'‰Ä³â¤»v0zGïÎ˦ +oà;~ÔÁe9y:­8øM âd>7$…´ö%ˆ#@´ÿGÀgöwg¸@@EV¤*i·Â?PkH,´Š‚ú@ÈJ™eriŠ#.ÒÀí‚“E8zÜ2›ˆâŒéå`Ü{îcO‘zQ:"çø|rX ¸:a&C š#Hz³Ä™dEdlåÒ™©ˆ‹#1°ÂRM"ƒk$¡‘fDIÓ­ÒN¬Ž¸#/Ši¨R(ÇH¢Ž +È¡#BÉtÇ/u°9…b¨dRÑhŒ9e®0"!.$Óçß0‚l6ãm6Ù fÝG2h‹bd+"Zq¶àëØÄ;ŒHÔF=3”’AB j­™s/GfxÏmuéMð z* iI¾þøÊŠkùOI+§j!—.tòç ÃÓž}9ãËQLi7™pPÒ¸GÙ³)Ùž?”õ€ióMÃuY§ÃÅIßÎϬ3tANÜÞ•«àÇqî§ÏtäcyH'õ9R-Ñì± ÿ–œUPÍ_™í©;ÇòyÍ{¶„øgq¤F7i’Ëd4îsh(±šF²Ñdt¹‹(ªÁå^ìýúçÚYŽ0ƒˆÎ3]ã6ò‡®˜ïFø}ç9c©Äȯ0Á$íÚå™­ñsæ};Åî[À#œÍ.ÆÃ˜NeFeB†Ã1ËĬ¢¹`í…fKL«Ú¢¬±•ï‚/HíQšîåáÝSSk‡Ðïe/ŒºTù 6ÙQµíîÕvãË÷ùÑçDtÁöø#1Øã³í)É+£ë)ÜïŸ÷ù¶?r4Jtf*¥Ïƒ}Ù]«¿óÇáÛt™1Üy’@Ê`Ÿæ™]¸‹,.%’?Üœ)ǘ~æ÷jAôr.R1¡þbŒ!Ú‚aèçJRä€TGÍKå1gEJªÏ7,fʧUZ©ÞEü÷9D}Õª­w:Æë6Ê”É4Ðî1TOË1õ^yßá§äcõøÎÀöìêç‚„„Ã"„âŸK£Kn\™V’#P¬ÕCPÅììªùáU¿êü׎SQŸ;á•MTÂ'\;u½íþW¼ç(xð£›>Ãe:{±F=Ü>g¾`¤×ÛF3 €(à ±.PB¡¤™úuÿ#/ò2÷#.ŽèªÆø|m~UÆqÙÅý\±»<½YÐøš5¹fD‘·s¯Ô¯$äØÜÈu=¾Hƒ‘j€uêuÖ¾VÜÓRk:Lb” ²,Dš0ÝÛµÓb5s\“Z!F$ Ð&†)û=b!c¬]¥²V#0[ìiÄZ0l(dµ¬ýÊè×nÛjÂÞ#.ã§€Ä-õJ 쪾i–Êã2úΨé8Ôçð#1HÃâ|CàÓ¤OÁ(7úüºËþÂÎUÕòè¢õ[õÏÅû¡¿rCçzì„XEEÇ«è›MÑ»ÚùËÖÀ4sG#u.Î2PᨋÚÁ*Žv%,#/Yù°ü6ßü&agë~½]=‰ „—#1‰Aî `G2x¢;†#1¶l°fAWòòsòG/va“¸ä b)êÄ#/Ò@ÿ$÷&öX«55þríuôE§é¡ÄE>y<·óÎØDùÿEÆòÜí¨½‰{ÖØoï¿(K2Lñ–4¾±,NDóH(sJ T‹®1°W®‚¦‘Hžénì“­õ<|Kø‚4v®r.Ñ: XöÁrïûÿsuüûÒ¾L8#.äEP-F³'2²1N¸#1{†ùrw¿m,a6n_Dïéç¶—žÎ;ú@RpT‹qºZ³ú¨‡““Ó†ÉlóQkjÉ#/äÅÃÏíó;Oåãû«í¾ÙðÈ]ôø¡˜DztìÜ&8žf`<™§[$–bâ :2Uæ›Ð¤‰oöon#6‹©˜JZgàs& vaüe•Ù¬áÓôiDÊÕÉõ8.Âã´…UG3ƒ)@Ä«5°±Üµ…»vîwðn`íÖàšE@û·û<8ýC§7ê7’eÜw”àíå±R2¯¤Ç|y´,=íÉ™$òj6¨C)ÐFiÒæ™A‰V?Ý¿ö ‰¶š;¿õ‚&/‡õÚ²úÿwÏ€ÊMÞ£êù9mç ©~,ˆU*ªS?W“UøBÍ4ípÎ!ûOÛ(ü:¾Çí××ï3 {$;ìvCÓ^ïñoK!#1-ˆÏS¶ÏÔëpŠfÔ–M1›*XÊ/Îã14i±WôW±Ûv£E·œK¶Š#1ÂÕ{)É…ï_éî8ß¿î9ñϧúΡþàšo6ç¬ÞpL¸~Þ8mE0USIN0­ë ”#Š…4ÏÆúÜˈ°bå]×ku,ÎrÁh>m¶‹Õ5çú+òþy¯?æØú?„¬_!²bQEï™â“êªm!Cßýwë®GÕMò~UhL“]{>|Ëä·é\´)¯ð•nH5JH“ BÛ*Aûâ¹Æ¢Ü4›çÕËéV¿õ:½”mשWÛzI·Éµ^JÚ½oWuÝtH>Æ46‘þ÷ñhý²cî#/ž”®Cš·f¤ii8DYo÷Ue£ä+P7HR#1L2ª&Ad÷» óË`Ö•ÿ"¸8/#.®dÎßÅÉ| –Ëøµa÷FYОŸÅ«/žœÿ—`ìÆ D3%Ü‹Œå®<¨ä‡û!ù_¦t<ë¿ñ¨}#/~¹m34¬ïm}“dµ_#/ß¿Û\çTCõœh½29©Èõü'a;ôô™ÏôÁ´7$AÞ=`¸*¨æÎ4aÈE&=²¸–¸h`¬§]9a«mðûë›É>´|BP?|¨PÁFáriƪb¤Qx¡AM„É;¹Фn#ÖÍ@ö—üÁÃ5ƒ£×L¿ž$jŽ3ßø$º°èðiG§#1#úD?kI8&î”Víýv¢,QVÉüÉI&Rx|³.%AÖÁIÏÒƒãFøáÇÙÉ6Õúü˜üÇ—ä·õ·âïÂ>®Á/›æùÅžžuñqIä}–¯¨·ˆ-çoîõlè'f1[»íßö¦¶zóÒÿغüÿyL?SÑ,ÄløÀ»à+õà¶¹‚ë-t#ñÙÕ½^nnëÙϨ¦{hÜò»¸R€N$UÙÅÜ«²ñU–Òœ¦RÆw¼©_#/Í/{~Q÷ÇoWM‰j5Ýï_ˆSûè¸ñûbÅ—Ôåîÿþ(ïÂÛ4ÖW?÷éŸÁÏÒM—r9+¥‡&þפi^4†i8ʨ´Å>þß§v Ý@T_ÌÆ˜1á@{<2<ÈÍâ‹J_âÄ«K’5¿­ÀZ pp</Í y—Ä‘ŠcåÙ,é› ³ÛÊ?ÏÆøs¹´Ñþî»øÕqnã~2ø¥âϯÑ@h£ÑÞùåp:i¦í[3]ã(ÚB€ª+ …ƒ6²ÇtÅÝË–‘ôülYd}C¸zBý*GÀ§àϽðêUCOÔ…àŠzX=ણ”¡†ö÷þÛÖ¾¡ŒTiå+mÄ#1*éuOGöçë5?Y¡Ð7Œk–_~8wôìô©6's06ÀSRØmt.±DÛNÂ#.áú¼ƒ3ÝßK[â×ÏóÜL(O¢A‘ÇÇŒ±Òø‚"T‘xf=VÜÒÊOIRÚÈÚÔpê‡Ïful³«–žŒ50müÑ®y îïÆGŠk¬rø†Àé frG•gL¦°þLÅÂA% ŽÆVw‚Œ4ýÌv3¸Ta%rX‡ÿ’E8_,Þ(?¡SÉ5ã#/&Sƒ‘þÚTôÈ.´4òÿOs¤E& #!ŠÁcÀ¸s?ŠcÅ_¼ª~ª6ºPž„¢bʳ<~™ÑÊjÕg#/iŒPM0p…8²÷êmëñ ¼D‘_#.•9ÏW—k<ŒTsŸ)âg–‰Wög`3˨¾úŠÊÜþ9[}þn ¼' /iÄŸvžQŸáéäùþ}ªº!É'‹)#1[ª€Ý%AJˆA ÝB[ê*¨ö’¤ÿztæ{½†3ôíïþþKýÀëúlm¸4òð£h6þ¾}ˆkBJé #/¦¹xµÓnm^•¼¾µõí=Ri„òHTV²n7ûöþ&úé#/„?Ñ\s¼#.(÷"H ¨ €yº…áäf,vnìÇ£wé×áóý~Nj{~¿­Çà6mè킪‹×ÏðÊfÒ”×ô}–ÛgÙg±ÙOÕkzìuãG¢cAKV¹ïÊ‹›Unö2-‚o÷wr;»Ø#/ûp µù]£¯_ÙÙûtß»â§/ÏÆñ W¦1|Ùù³Í…6Úöþ_Õù§ÚÕ.ùÏÞ·¯›š¿åáóç¥õ;œœûõz°rîäŠú•¥7ù´x·äÃŒ¿NQîÙãû{>¡§Ò[ôä!ÃF¿ÝøöýO<u°]BÔ¸ øx½ºßNÙÖZuôz58h¥£Ûö]ÔüûFX#/Ó&ÞÉ%7'»ùËãôH/ÈBžtŽ$l>O³Ñ ¤º<[Ý9tj”ø˜‹qÅÜ£ìÔÉËŸåòÿ…ß„ÇR–¨¦ÚÁ—³#N6ëí¬øáÒ¼˜vßê²mñÖø ØBýó9Ýe5ü£ÂÛ<Û£wMï~¿¼µ§ŸF)Ó>¿ð±Œ4U´îxéÕ3Û^m»N˜_sæ5,z_ÎîO$2bò9Η±-nÛ<'}¦ñ[;;ŽNtHµ´óþý_#`#/ÖY²VÙ²&Û/`6óAì1¬uðÊÙLKÏo\l;|»|–õ š¦Xã°[³B.`±s6€¹ÃV03/{ž®iúþ8xÙ£]rw'¯›“Öù8óaåÝ¿Ôví&éc¹Þr%ð{wÆ»Ÿ;õ_‡ŠØïâÙnÙsCÈ¡‡1‹K&ë¸ó­î˜ßƾë×Átõ¿£S¬(âC¼Ý to…¤xQ\RÃÛʢ®ÿÞç›ø{|úÝÌ|l±®½ÓëöðÖ0vhéW~/þ°š†^o?:ö~&—Æ=®ÌÃ߈Ë]ôœGq‡êÜ+¬g¶C¾ýÒ¿àŒNêåà–õœ–ö‡äú~½•ÛšÞ­¿#/‚ë>| óU_ì_0•uázsodú§x`g,»êÐÌØð]Ùþ‘œÙû*+H#/OF6è³ËõMâý#/¯“F2áZK‘½£¬G=?fîÙ\}aî8ejŽŸæ¢„zŠiÀÍ:ô/Ùó7Û·Ÿ?ÏþÕÛœ{³;‰Æ¼©ú½Ÿ×æ×*i»õìÜ]«>1ÃþhþÖÞêû|W÷Ã_Ê’ÿ¸ÇgÈù#/XA—³˜r|ݦܡAæq´w|®ùô"Ä r•ÚvæïúmýnÌ6ée÷ìþN¸}[yõt[¶å£ÒÎvˆýQ9Õþ[þvùŽ2Ž#.ËwÌßåÙÿ»èhjý¹gN°ñ…49èäÃ^ñÖGƒÅ×ôù'n¼~OwøÙuÞ»m<üÜÚÞŸYûÎ~¯°ýå¿Õß§w qåŠgî~'…kgåi*4úL?ÔRóý ¼ró6f—g™¿¯îÓóº¿Ë³ó%¹Pª*#.Àçp#1å¡hX(Œ˜«³Óåôùc  /úOÕç’Ó³8ììEÊëèªÑþ,®¾Øj#1ÏêS”\œus|­øÇå 1û.QpH#1./E€wÏQ›³Sùßø¯ m*Ås$ü>ßw°'çà:môvŠ};~ï’þ|y´Ÿ«ß–Œ;rñx~Bæf×áÖ;pËÑ=n~³»ð#¶ü¶…²½õé]{>MXsyMH]z9?úàïAÞPií#¯Ã»ÓŸœnð÷Ó¿“·óàÿ!«õûΣâxéüoÊY­ñCÆ? £Ðñä™\~ië¦KÊ<(þ­›K­ÿÈf“öfÓ`Ã÷*#ýìà ?S-ét½ƒpú±Nü{5mî{ÿ~­ºmû~JêpÚœ{ùZ·}ýv#/¢Á»áN{£Ãî˪{¢™£·]<Ó¬z1õxrjOúØlQÌ, Ô?I”HjP'ã×ëêçäçâê>ĵ<CçróAÊ­ñÈ0ú 5<‘xà§ó ØÏÛÈÔø!P„Õñí¾b¹ä¼›ôÞŽª%¡"‰À7ø§/6nwWã ;¾ï^%ån¢Þ¼üõ2=?¥‡¦Tù ûÛ‘ÙæÃ¿•tó;øO…fL—á>¶žY¢”äþŸž°Ð87$)çUèÃÉ1}ßN°n¾œ:¾NT–¨oÖÁ&ºXØ)힉ƒžÕdCåBr°I†}àP¢ËtóÉù:²Ër–’é;ROÇ`È9üÖïô&-'øœ$œŽ‚»%c:è)ƒèšŸ'Yc+ÇÃOuÅîË*W¢þn¼pB†;ˆŽ¦`4¾»#€ƒ×äQ.¾?w%ЪeÉGfõÞìH¸{&gI`C¨ —æÜ AÐ7ŠkBBnýÜuY瞟žß`N#=ðöoý£‡¶°âÂh;¶¿Â#.· ]<xÛ"½€ËÆéÍÖpl­Q\9¹N3çËŒõ®#/Ñ·ÝZû+O¹³¹Fƶ0ø¸©<Ÿi›yÜ·r×+0íkb¥æ¥­Ldö¼ÝãÇ—eÅ_ZëÚ`ëaÐ'Ÿ@Ã!¿‰ü‡TÌ^DvC¦èÔÙ-–ñ¬Kª:7×êÚ( húóçÈ#1é6âOÝëxÓ–Ô•mÑ¥†á³”±¦ímZg,î´ÊÚ ò» óÞôЏhóc^/~!G ë̾wøïv8>ïxzgA );H¡‘שñïÇYèØ!ÒȺA ¤Ä-ofó^ÿ¦M°Ô´øÙèù2ãAšxç¾#.‚«¿Ûûí·èáZû‡-ÑÝîÀXsϬø¤#Âõ­‘]íâ²ú˜X1È1#.NÄ7#G¤2}q£¨é³ÔtÝMDZaoRN5; s"ŽÝ:@­ú¬ÑÑžÎó-;˜îÊ÷ã÷~Qï†/_Ÿï#/:ËG‰Ú'¾£^Î¥KrDÍ™ù‹á*3c¼õkD„žü~/§8ú¾&LÞ/PÚ♟žÜF/=Ài?¿×&ÖÃÎyåϱv8Ñå‡c_•GÍ™4]¢ý¾‹üñ‰é¥LRI˱ôÁŠMòmŒ¤öF/ulu³ÜYLpåü#õõc„Ýúe#/>zú†pï'ˆ€6”*½2`cÎÄ’-sŒûOÝ꘨þ¿É’©Nlã"<Nôq¿úˆ½’rÍYy>HØüQÌ#.?4µàÔË㨠Ê7»ö|~nßíþ¤Yýd>²Åû¢ÅcÿS!þ‚ÔfeÆžFÑ[r7+,$M¨ÁFA„aIåí€V6j”A¶ÅHâæ#1+EJ$Ó¶ #D49ŒŒcIàâUV”nmM¶‡önÔò$1¶¸}=3†´Ö¤Olƒ&D­#u„ c±GMQ5†5i’SjÅCCt!Ij²4±[a™F6ÛŽF#@âŠìôɦW† „Y‡ÉIR#·êÒ»ÕuvÜ9.„¹ÙFÝE¡+÷Ä["É´ë+?«øo…­2"¹ÃŒäˆD#1WóßýÝèz(dÉvẰ\`±³øÿIhf ä·²a—û5®{¹ ¼ÁÊ E5z*,mM@¼.KÞ¾®}ÛËúoÓ/‡ë¿²|¹9…N©ßØPefeÝ‚%PU67.&q‰Ë$Ì\Ð"JL#/ÉbRTc˜BÕ¨,M±ÄÈÙ…¤’© ÅUÌ •t™·×à:þÍižq»æÏòøó\³µEz=é£ÁOÙlºŽ27õêÍKáÖDW;ÅÃU;#/¶ê–h Îw+VºÆn]¶y}ÏÁþK5ù:}ö5üþ×îÝùøzø zʽ~Lt/Ûöz#Þ§/ϳ³«o°4”O$ñ~_ÓÛ•ŸF–iÓÓ íœDuf6naŸõOJʃû%Wú^"êU`×Âö×u¸|µÙã#1ßõççÌî¾¾1Qx¿yØHð€pÎSÈá¯/gH$‰ ìÁÞ2íV¬z{þ/Ëð_¯“áÓ«ÛàQ-B<ÅxŽŽ.3šª›|Ûܶ>¿bïìeòÊî<:;óã2óG_±ys ÂÂw~w»ñr8¹Îg¹]kša†!úûï³óÿ]™è6©—éÙãû¶4ütÙäm¿Ò휺çÊM¸¢nºßß=Ú;¦-’È …ýñ}¡oîóa×çVF‘˜0Wݨ\p?ª)âpÃû}ÜÝÁ‰£U¯3¹¸‚˜bÅJ’+q‰ÿuš¤xÆ)Nù/³¶ 15¥#/ÒÄÖ¬3GžysmÜÞy“Ç5™J“dScêŠÛI˜ºÖxjMC eUkÝHÙŠ<µŠ#h 2(ì+²¡‘Æ•re+-ŸÙª-"8?%%mëQÖ…_”&6ÉËM¨1ƒ°µB²’`£$éÞN3‹™…#ñÈJ”¿·#/#1ãB742#/. DH+F"•‹Ë/SK¶ÉtØ7ƒQ&Ñ%ZaQ1N-N³µQiˆ#M\eqŠL©msZ$…¡Ã|ñImh  £MFÁšZ†<ê6ˆøÉÓ{5‘F…8Ô”E‘,3_ìÁ`·nœÊ‹ÎáÒ„AêÐGEšT#1ñ¨œ”uV‚(3Ê¢:#/ÖŠ¼S*1¸#/•ÖJF! à ¥ÕšÎ ä|ªÃAI*Í[0‰VXWk(t¬­‚R;:¥áCbˆ3Ë[³põó}¼8º‚µÚiöŒù[eŸïnNx\%~8—ŠÂ#/[…σyBÖ°té²_sCãö'qañm—̨¤ôj±tvåóý¶'²?Îì¬Ðnôßæ¦pH–|%SÈyÿÅCòÃJ-®>kÕÙYñf¾‘A0«““¤bGaëÔɼç(¥H®N‡vMÜñ Bx{<es²¬zÂ÷\¿!~™qÑd$)^_õO]8°Íµ=k²ÓNž½˜‡æùú¾ ‰‘³‡ökjÉØn”ƒµ:ÖØõþ+šÛt*-áÐëUÄ/q¼€»ˆé$#+ùÊvA#.c´ôAà ³8;bû¯ƒÙHèßÍA1£ÍóÏ y†"çºaE©ÅËíô2ïPzÙ‰$¹ÊªàÎ\ø0êŠSÈ(P@d䳜 ‚phô­¤Fôô3ÓMB‰‡yIÉÏý§!Òú­ý0m?3Þ$€Õú6莕 rÎw]¶rÁµGn1gŽÙ³·#.M¤’P˜páÛýØ:A,ïû“—8Ο¹mA/?/cß¾íDî42`×¢ä#_Ãl‡ÚÜ‘®Î…Á¥Uí=rGKø°GŒ.“-1UÁÏ$˜4†‰‡áÕ·gXÒ/÷àÃO-rê©‚8vÓ‡0Þ"´ëë¥þÿ颼ՔË@þ"Äu#1©‚в¿ª­Š]#/6Õ#c$#ˆÉb‰;° ý9tÞm¢^ŠFA»²È•SÇÝû¹ÿ_·ô3ãòYñuŽŸ¿ÆÒ#1·â1ŸooÉ+Æè©B5¬Ø H,X"SS×lX{$EsÊêÁuêâh&7pÄ`É24U.Ä`™ª3уh¦ebàd55P£d\b£ðËA„BzÇŽjæ<ù¨« 0d(Ôl+É5ƒÁÁ˜e”F LØŒ€Uô=\Ú^%ïÀˆÄÓ#1÷mºo˜ò8´èë§o³#1ш;ü¹A³`ØÙ7cUPßÙ†hjÁFâ~ S,ÄT®íÎ#/#1¶-¡;<fG*‘ÍZ¨®dÛÈvÐk .«¬ƒ"Ž(ÊV8Y"f÷Q,eP)*(˜-†Ó‘2ņ a¶P1íŃFÅ£Áv#/viN¡»"‹„ÆÚN¶‡]²Á°d;Ót0KÊ-™vÂ5LQÇiýïóhÑ ñvøÃ:Ãxæ×žÝtôïRáuÝ ï<òõä¶%TlIíxYVÞjaå¹[;Âh\c3Ç<G‘„…¥N*+&ýºüOÇ‘ëóJP¡A·¢íœx¾;!Píô(VÒ^<ùõ«ŽIŸ¿F‡%RÓ)’nj#1|ݯÝgå2œL6Ãûzƒ„ËŸçûÿ/HüÃ=ÿYú¸×ÝŽ‰ºIèäÞ2/ózÁ>“ñ‰f×£›OŸÒ(OH=€§éÉ’vË Òǃ4FYd¶bÅ…¥UO×Jª `Ó#ò]áv,üx´`q#Ñ7]8iF4ˆTéøLTQTHò,ޤG#"D•0†ó~»­éê*#/öÍDÌ}º†={9ÌïcÂÃÛ¶i¬KËÊâi¾‚‡Uv<„WFÞ`i¤´JÛÒшŒx Ô`ê£Û£cxµ`:¼¬²6ÛnÄ vã>OÏöYß:ˆP'ÖÕ)›·9TL¿Vº(â ͧwöÉçòÊý~Ÿo7ñã×ÓáçÍ™Þî­"üµw “ãæ}ßíús׿ÖÄ,5ë}”ÛÜ]«öÐØ—=–ÁVEËJÐdn-Ð‰ŽŽÒÞg³ÆiéR_rÅE%ÓÝz›§]Ä`ŸDŒuv¦`ÈÐûÈën6ûUFRw…u—£a‰¸á#qGƒ‡ê0+v<£C¨¤j#/´â+£qÈÇ/G$F7Ž%dFãY×33¼\Qlà†fæRªl‘A‘XP„E“FƒL3€L1Tî¾$”,B7ÕG0Åe ˆÝ9È0;-äèÈb×,‹oÎ0TccæCB;#/ʺs|6­l]é7w.²I‘QÒCë”ÛÙ¡Vi#.Ôœna‘6bés“*:­HT'‹Ý ÒÔ4ã#1Òcìyb5¨ÍIü0…my¹‘IÅÆàVÆíŠÀÔ `éC,‘e²ÉÂ2™ BMZþâkYþ+Ë¿¥+ðIS<!pÈDÈfó0ûñÐ]ú¹"(TÕr{­^s™¬3 ù³ÂßLnÌ> ež6W´û…?gôy1¤Ú;¸½»‚]ïóëï²OÑæš ƒpzT“˜ûŸ&›hz£^ø|\&d³ªwÒÞ:üö©«w_¥ÙtÙkIJm7Ù¾±O²ô#/+IÈ%<’*Å#.èTÙLP,òÌÙ¤_c¸5ÅÌÓU×®ÿ•›mížp"9KŒ—[á™rnH­muKqŒz±µZÆ[H·0d"m½h®IÜçܵᭈ‡Ùæ(×Åy4o/}-u©ë† «é 0Y3½b2%æAì5‹°J‹FlÞbX  §×XóE^Ú³7¶œ3R¥WÆa! ý à[àhÆ›V ~„“{Ž‹Œ ‘•Àtx@ÃèQülfœÉµ¡¤ºà‘µÚ#Á¸pûuæh‚<²ø„E§+9‡÷?y$†/j ‘}\hV8Æ%d¾·öE*Éà/§KÛÖœÉ,êÓíFIîŒÞµƒÆã8o™–.ÞàýPÖa;Zì&X4¶jmG”í†~Tq°ƒL®®æý°G#/Á ´ÌÚLÇ‹nø¢…öõÎ×Ú1Ö5­ôK^Añ‚F$Ø]õá©u#/–æü>4æýn²;Ú¨I$²ÍI$ wÜç,ôìÎd|¶|]‚žvË.#/ü¬ëÍŸ©†ÞoÄ;›@G#-Ô Tm)E¼[ç º¶µÚ¹Q”—VŒÈkq¥¸".óâd±1ÕœÐrRÍÇŽ›Nô:î(Ûj;º' ¬›ÌK%c¸o§8Ø(æòÙÝ5o†¥Ó#1°Ÿ±a°¶µ½Üì:#àºEjÇhDèÅ7]]qœÏ&vl©Ÿ1Ʀ¾ƒ`@…-šÝŽ5nuÇWfp–m>(mÃÌß96£7ÙÉ3ÎB(K¦f´WgßÃʤêɹ˛îdäO$ÚÔša´ó6f *Ñ`Õ1 sf¸#à#/n…(”±‡Cæ1n&@Ýäî€÷ßp”¥‘åÛ?³ÒJôv)2€í:ꨩx‡…Pâ“ÞcÆÇ»å¶[S–6}Q½äËݑƌb^L—q¿¥Ò¦«lô4•ÿ¯q‘'‘Š&#/ö‰\àÙ?Se7ޏâsq°ª§ß—è ÄMc–ã)äEÌþÔðø¹¥Ë¨‹XÄANMn£ e7:3FL@Ðð)Œ†TÜîøïZÊÊY7š{’&‹;¸õ´Ji£ŒÂœÊ£å½Ì»æž)ï›jrŠÝP‘»Ú7Ô&HL“nüÝYþïKnÒW¾|êN“CM6 }ï‚Ã~ŸÚ{—kíøÂXä‡Üë#1øòzí>£¢#/51c‹½Àfòø†1ÔÇeÚtuZ[NÁ ðž`5Bi¹g%l¹Î2Æ%$õ¶Mb3¼¢!ôûÌM*KNó:Á·‘yÁËm-ý÷9žš""+š³dÉ+¢û­²Å\$Z²(ã¬ê/xdîØX§¥…âÝ*BMê¦6‚ˆuc圵tZôMºW½³mW¾UÛáÉÃcœ#/È×/ί®]ánÃÊ|µÎ¢;Uiõ=²nFQL¨ÅÆ”[-št˜,DÍùèLg³–a©¾Ç4›¦Í6%!l)Š¥\cÚfŽ.£‚º´Ó)F¾êß'w\y&êÄmù¯.‚XfùIs‰=¦xnýåÙ’b!H˜w¼¬p+Zl‹K·]p·Â háÕÏ^ÛúÄÐñ€[>¬#.½¨(ê6Ò÷dü[àC-|#1½›y©©äüÐýý¿7Õxz¾®Z®³¡„‡Èøçtì¼*8zÑ °ã¦áå0”N²…$¥cµÏ™.ãQ…ú>%í= F‘®ç#ÒY80]Iáåú¥÷{#.„ÜáFÚ~ÓSwSÙÌ#I]Å¿rU†áåè2¸1²ÃSqŒ7ƒ|¸ IΚ)>ŸµD€¿”ryþ¼eeÓ`DFz‡ì/‚ }M |¾®Óý\)zŽìœuø>ªáê\›#ã>x†|nÕs Ó¿Ï|<‡Ûe“Iöo™Eˆ×ñå˜H??Ã-™·ýO;¥NGy™g“pañ‰x,ž³(i›M"ÏC1‹mi7ŽéçåðaJ¬°îÍü—ÀW»Å½Ö؃8!EÚK1#1ƒðìä–@ð<Tà ÏxËü4í® m؆G(ÖŒ-Ï[žcÛž  õßã’Ö?_ 鿼W¾xç5øó®ŒúŠ`ŠJØ· Á‡¨9×Ú³^9y¹Û¦âj¦HƒÓƒý£³ø¯É7XFX¦ôû“õ%úÙƒÈR~ß÷Ã{Ù7¥z”V8KØîÇÞ^×c§Ôêçõ ¶ `u·t‘ï'å–œJÎÓ“55‡°:F.„ B Ép1–èȰŸ±ÉN¡Î]¿ëâvNZ@ßë'€wc«õ<%‰:jñ w!{šESå1ºùåpÞYàlŠWÂâ†KIÁÍ9‰#1Q¼Á*Ãú‰nÈÁÆ1†¤v(™}©/—úÐk8`|˜aݲœxÇ;÷ݵʹØãsª4¬æ´¨#1–£‚ªÃbi²B.z˜— ÄKÁœbæ#.} ¶8ÎØKQdBLIªÙNæµEªÇU竇v¼õ’…KÒ ‹de²%ýðT'Õ±À§ÇZÔ¾#1çÈìáÁc7ûÿö÷ø©­÷7èÏ·êó¢;¬hÓžíVÅÓæ#/3êÈTî¿x­½‘×M¦tGYáRƦŸ(Ê,HÙz¡lßíÁQñ~ø}âÛ¹£sà Ýè][Þ·wvêΪ®Ç0°ÆMÞ_³´ú¼ëùqË0ŽqZâ0äc$ƒœÀx¼¦K»µ·ž@ÂÓ²äꚉ9Èùt~¼šõ¦JIëªõAfÅÙÅ…·dÄuv 0É£}zÓyZëÒváûà᩺u¡»‘ÓU“Ö\?™Zº{¥Õ‹/ö‹¬ó5ÉÆ6«ú™½²<Šs4òìÌÔ”£î¨žœ¿¹7£béCgøýº++ÒZz#/'ém®O{vGýº3fòëòkRé&[iØPúÍý÷sæÆk ìèød!ˆd6î×Ô¦™8‚¼Ž‡O>s¶·7ñ=ü§?ú¾ˆÓ䂸÷vhLº’CàŽ£§Aå‚zW*A?Áršâ å»N±9Ë&ŸžÚZLHí¶šQ„Ç¢‚Œb‰ êÂõÖú`xf¤-0è´%ûµ%h<Ž#Žœürß#/²ÝzltXØ·,–(e\תØÏûh…3–ÃOÕ¥ÃáX^˜ŒÈ[Bìrh®{ˆPÃ0Ó­0öØ,’F4o'™Ì-ކä"Cð¹ÆU3ïð£÷öñð$\ôÖ}ðÅôh é-D­·nüég#Ý=;øS‘ì6ÈÛBæé-¢•J)éÖÛáÊôØ—Ìs)Cå$ÿk»t‚ݧºêÐæ#/öeݦAä¨àëñ¢…«ø!½ó·Àã©ç^[{£¼#1[Ž_IŒêR€sEÍKvµ%RÝ;ÅMñ+Àã’ @•ÌÐ…?ã“0œ}[ˆìäåúž®ÞÄŽÙH¸x‹ºh™æÌ;AÏ­ã ?Ä,­P¸]rª”÷¬ BÛÎ+õÅà²W’›™úç3¼)TC$ýžM[˜]°û™? ¾×@!½ÿäÜw6ìî«ËÖg^•¯>½öÐ$q[4up¥ÔMíÖ„[´÷¨& j鑦¿…t‹Îw§ùú>º3Ö0ÆÕ±Ò¬¼ßÉpú^¸§8ÙF½‘…ó0¶dÆÞ­ž#/°|XŽ«C£”œ½V¨RP"Hfj¿­MŸ±5cã£ÛÝè/ò_8)ã9Ô!>be”<c­¥îÅßY×ZkXB”êÓµ)òÂ*tž+ÄêÉ}¶b^js·<Å…ÙõMFÖðÉ7Ûy{ØímñLë Ÿ/ÐÙò¶€»YÌÔïpYÕp°¦ˆèºeò¸è>–·D?«’‚©Ùeòº+P¢¿N>}æyõúm[ßs×Y¹9wΡ4ˆ®·®°W\Ú]Å+HõõÚÉ>}ýÝ»#.à@ ÆD¬Ñ­!ÉɨŒ”+adzױƶç4„#/-\[2kÕVT·1¥T^a'ŒÓyײĤõ·hb¶Ò‡òˆiÎ}¦¾¶"m¶pÝtZ¹w¨w™‡nýŒŽé­d÷&_DlzkA6â}Çß¿= äòdC¸L¿çåúϧœzU®Dç|ù)¤8Õáà7Õ¿Gëp2ñeìî#$ƒŽ4ØX¤Ñ©<ž<ç\–zLd،ɶÝ÷æKtÏ‹Ú8¸‰m­¶Ï¡×¾2&áö4öíkª÷»/&ûDQ«ǃ™è¸­›Ñ§q}#/̘m¶Iö0ŠRûÖOÞúNÝÆÉͱ)! ºÒ˜ß‘ݘkz¢òâq„VPKiæBÅ•/´]‚ç¬?ÒSeÎŽ?Ûíç¿O*®ý¨ˆîå:K}½ïB®w(Íä»eUÁ"ËÖÎfcÎÚñV—Ëx'>ýsŒÛp$oGÆ€]ª Z‹{f‰(žÓôÅ@ìR5û¦v66Ìg_¿8µGå>%à}~;QÕÈ$0ÝC“CVárPÒ0΢ïøp9erþØáˆw¶j¨Á³}±æ«²‘͈§ó3tN>{HÛØ¡Ì#:–E¦çvÓ6ͧn'jVËÞ±èˆ%ÝØ‹´”Ùâ{‘fž&14p;9Ý4øo_°’KÑâûÎ|±Ü…Õ£©ÊÚ\´oõ]«·D»îï´<§ïÂl)BÕß¡ÙCùyúNØßi‘4²ÄL=ö²%ÈÛÏ4Ôl2>3Mâ«Ç·ÇUÆÍ‘öØšSÓ¥Kè–dl³Áµ¾6‡^XhRÞ¶:íúaÇÁ6GG!ÿjˆ'qÛ°X¢#/(ñDÔL|Hy €~ª‡)ÞšB-/Asrø+„CàˆÄ/ 1;DXÎÎÛ4ÎA`^cA†[3Š1Ò£¡¹œªÍ|…žÏju#.¨{hpˆfZ9xÞâ<A‚ò½^ÄbO¾'ç×:;Ǭ€~ÜwÐÆÌÏi¦Ò»ÍÅžtí²âÒçñIâ³ì0æ[#1][\çü% ¤üUYŸ$ é!/y#1A@¡]<xÿ ´_©gIÀ£˜X玿ÈkÅï!%a®«:{çKË•nrÚQA¾ªÖ>½F”ˆå tÞZY›;BY®5ÂX#/öK¢ÖºÎ5àÀPY(îºÍ˃>ÞIè¾Ä¹¬.ÌÀ­W•¿p.x´Im³…b‰â7«óˆ´]“–ÄÂÁåÖ!#/%f-Y:æÓÉsEWÄéEÔôtElƒSW#1øž°Ò¹ç±”Eÿ?u‰ëµØKÑÍd9t蔲ʹqÂéXüi‰àüÔg ë8«î®‰•º<¿²¯Ã(X¶ˆß |qöøÉñY¾þSˆ¥Åñª»éÑú)£JòN)+ƒ¬±¬>6ròNûeñW@x¨ØoµÔs[Ë6ÔjÇ)äau×–#/š¯™'"‡&°ó÷áú>zg›Û¥óYnŒ¹ÂcÁ¯mm|Æ8cb¤_i.|4ëWçìEÈD)yW¹{V~ürK·WÖ¾=Çj¦ÁÛƒì¾ÞZÀo+ƒƒÓ›WÎÃŽöMt¤idÅ—A\”Okéât\(#1²:¨ÝLjõ®¾‡—Zãú³šÄ]zH•¿‹t¦’ j¨¤8Š\ñ%{_úñökñòÉ÷3ŒÌü{EÊÉ}ë<_TùØŒ!#.P¼nU_•’|ŠéV:ºiï¹lQ<×? ú^ÍyYÊE¸H=Ífž~°ðù*[G5šï¾Û1tøë´çËm—nu#ÅÕž¾|pžcÅfŒÛÅý¹˜;¥ôãâ×k0Ã5áCME… Ce~­ù„ÄŒŒ•ö`ª×ž6ⵑý$šÀŸNþªc¿x‰^œŸí ®•ÑJž5F¬zË܆õÕžŸ0ÐÒ'ƒžèRáÏ/TÂÕ¥µã)O\ݼŒ«²;¶ÅÑÈNz*LE"·âEàè‹ ,gží6O+D#1_s!?šÁ“Ne#/Žêo9àŽ“2A§qna>/r#¢=Øn™}2yÞé'zˆ·,Ƹ™0üe³Á³`°÷]ãõõÄÎmTb®¨ÕvÊ-¯‹›1SGÒÇ<=pv>,Ñ7]JŠWè8w3¢âlÆ.V2 yË ÑÐpÏ1Îs…±«*Cqû,®ó¼,ü•D3­º¾xãAŒž½_õhé±’B3\‹Ž6Žw‹ûåk´›ÉiÝ.çÀwdCD×h“iáhev°¿¢•ˆq4”]WÅ/ÕXìºSp•icÜ D@ïç|NÑK›¦½ù;×HápÜLÝÑòsOàÂØöÙ„5ôZüaâV©à£÷q³¾yÊG™s­°vJ³3]Öæ/j´…:ÆÈè¥^’ÎBæp[%d]#1Ç™ÀHû§—Xc„öûʃ‰¤ËÉóéž17e Zy‰ðÿ •ñ£%ÄSe–2#/E0V0¾_›KÜófkñ¦Ù=ÕlN{ÖYü&âég¥…¯Â¯Ù —Š„,©ê¡É‚EùíœzŸr3”JÌí^~’`ºª—RöœvŽM½ñþpX2-x‹§¢?‡XÔx›Úp£¶á(£DEZ™bövh—ÜÁhè¹icõ˜¾ªDÈ?•Cs/’9Žì¥'¿¡ÐÅF\“è•x5`Ncç @§.L5jŠTRi%Žë×fnhÎÅ^hŠú&—$·ÈŠ ä‘Åø/-ó·¼fm'ÕÈi¹úü±¯·9ÚüòbœÄ Q-Û$`Û³(‚å*Åã­{bhöeeo²Áˆ«lŽI;ø#1¾Žœã[¹´oÞ',ÂÁ€aZÒ±âÎswîð/Bîþçß{æ"àÏÔÙÌã£&Á•ô·7ÐZîª]:kp¾K%+Y9‡lêëˆÍ#10¾kCÂÕ¶UQ®*ïÆn‹–íí9RNa b g54Ž ¼‰õL-¶êXÁÕß×£@HâÍP¬šÊáµ®§.ÛŸ¦gŒQa‰†«YÄ©y5­UÑíu’ã)îh‰wòÆÈÍÛ1X–~xô¦åÝ4¬®5 #.¨Êúk #/ДÁ‰É#15'n·áY¯ ×¥è}w cÈ]B–˜¡Q(G!× ïhÍx6 þß `%h[LV´,#u2Þø>åÞnã#/Ñ—Eïۅϲ™Ë>{•Þ¢¡œ¤AÛÚwAÌìl¨Ûe5ÏLUùgsëd*ι ¦uµü§,óÒZS«­.t·àñrÞðæp§CVª%Y‰Ò1Œ¢í y¸Àßdí£Ô­ 2v‘Ùµ#.²ˆ÷0A¥ëó©#/Þ®`òP"J“G_–>vYÃc=ñ¾+סë‘ÅÎøGëTbrįúíÉöŽRÁËóãØ)ý¯lùöœ‰Ûß3âpoðøN N¼±ÏØ¢Ç< Ô-ß—ÙeFË J¸½v1ÛïËî6¼Vf»óD~+6s«c_óÖ;èáeKiÝAOž[ÍÌfßd›gØ‚?ÒöùÿNoK îîååÜ‚oã]p”Œ<ÎlîX=e~h$ƒÓc(°ó^Væ[NÚZ­±ÓÏ$"Ä…ñ¿2k¡çòÚã‹ØûÒ(B5úÿÛ©&ÂHüEm?k™eSr5åÛ³æºæÚUö‡:§3ú± ‚q¿ryº~NKøéð³¼ÌZxäWºohëØdŽ#/uËQOG˜`úYk°¥«”<+-Š},ÈŽ9€“ôÿMÜ™â÷,ò‹Š€õG¹©~µsž u~p[ݬɈ·zLJýîä2ò`æ#/òâpËK løª™&eÀëòèq‹¾r/÷ðâ’™éH‡(eÑkp¥pp¦Ö¶ðçºQ²|FRš‹+ºÛªªlŒ´T¨n¥[TJ`MsHd_„–¬¸gP:b¸mC¤BiBûþݬûöÃgÚ‹#/ÿ›áW#U/Ö-/a4¥Vðî»(°õ-áMœùÄyú<ïX:OÇ8“¢0~‹÷Eñœ³0n¶LA®1“w[¡K0¬V*„߀¾*”òØÎŒTÎþ˜5ïÍÈó$.d—Xônîyîro"úÁïS»¹|ã¤ùâ¥,.duß u÷ÊUã÷ëHmÙ¦y˜´gmw›¹8¹³©jJ#fع¾”Ò,ÆZ^òø6ó˜‘öR÷ÎXKKRªa%Û¼wØýXêe3*ä¾#1þAd3<?O[RqGóÈDDÚC˜àÓd·´J#.ÿaÖ\3á…ë¦çe‚¾{yaa3lέÅqܱ·Dyáqå"‹zV/pôR9±@¤ á6¶g0!°Xrj¤³.ËXÁHS ®çZ‚O/ð#Qó¾a^MlZž-v’[CD”ïµS#Ìûë~¨g ;I¹ ™C>]]Šr»=»iPÆœU=Ìê—' ¹ Ü ¥JCž­5sÂQw…ôÐ4ÂÚò­³n$X Z÷µ3Á¢ðº >ÏȾÿbŽñì{gÏ1´#.ÊÄ¥ƒvç÷È_UTï_0,S ©¢.±vÝÙÆ'íÉÃêóßÞ¬´áë÷Éé.UÆQ³cÀ©®aÖ­=ðšêç–¯Q¬Æ[ªUŸÙæ%Ø`étÿ/v®‚ü×ÖF­Vœ¿iô8bµÔÇ&Õ–{íz8…èVÌvj¢%ŽÛÍ{#.á¸ã%¢®¸1éW ã^ng $œ7Ì`Î\¯W3àï˜ô0²˜ã+„šv-‘ž›[kIÅÓ%4?¨ê'IÙ3OMΙ£‡?Cî´Z!'bMØÙ¡”n!M5¨S_N¹UåÓ†ÆûIÞ<¹Î©¯¦ÅéåxÌòª#1z\Ê0€“ýÅßnS[F6ç-4ä?Gcï†wŒv p• éÍï3^%_“³Dëº|˜cíÑd¤¯‚$n.„-rV#1`©Ê¤@ÙïV“Ú¶#|Ü÷¼æfóaðFB¾ï.nðXJøUC#]c$^æëÒUNÓý•öAtŽ8󈦩¥xWPx£Çæ½Í,“ÖÍ“”Jt9n~‰DJµt].X+«ìXÖÊ`Ô¡~õf6[–Ê{óbê¢Z°UêU ßsFyhçÈü1s.3•<sæ¾³pR9‹Ç°îŸGwLI+Ì 2‹.‚)3Ôk("UUHSͰGgæ5_3)¢iU‹÷6`Ïm^§‘Óˆò[$™B„f‹1ç—Y}ý>9wûÆ~ƒßp¢VÖ+9,=ì9Û—¶\O}– ÔP.eAú2>5‘-!#.TZyÐѶ%ö É‹õf¬!ÖaO?[µ—b#1,UBKݽ¨ ÆeÔåÌëü¢*Ç?&§\hèxíkK»ªÂ˺ãÖº3Ši¶ˆB‚¨L#1g_Ž®®k¬…Ël³<µd&É7¦Í?#êCgŠ%sYöÃ#.ê:h9#»:Ûï˜Ô3ß.ž\ä´„Po#1#.©ÃÍÎdþ“òKÄS¦ôm¿|ö¢Ì'vŸ·»´ë¼½¡Î+JqÜ®ŒÙ–aUÜ$L¿Ù'±y‘Úöèvh®EÀ¯¤j&݃0sÙÍiý0ìlþù‰iLæýs\»÷ØÁ_{ö8íÆÕÔ"iƒW¼“]㊮òú¡.yå(Ú*¥9ŽŒø–Âl©Äæ)<Ì.“ß6›ÆÊmÊ8’F#xEqˆëNz%o·Cצ-¯§M7¼£œìDwï0¸ uh\^Ñýÿk—ÛA¢y.ƒì•œ’ʈøû@µ³-dâý/y#/¾²h’VœiÇ#/Õ®œ«Qy¾..ˆ¾0‹ã¥Ò0 µˆX½d>x'dw¸M3ñeù=4ûìéçGºoù.»£ïR)F=væêÝWI¬ø›·k‡§X§L}o9o_–¾½ý[„-¶ë5šéZ:š žÏ/5\ûyu_MŽªrÎG®8Ø¢‹¯ÊgN汨ž.+Iåϯ«õqå|!l§0õ8ìø^9b¹~ËÂ8£…êb 00ºÚv,´`O—ãg åßekì%¬óÑŒV1‰‰`’=“b‹úbc³¼ë²&l/§¶Y¬Ùó¸Ÿ!}¯† :¯Ÿ&Á"Bf8ÂVñ~†ÍڱȹæÞNtÖª_Ï´é–Ý0Ó‡åB¯\ì^0”¨|N‹yÂ6É„D ¤Ý­ÊŸõ qwAÐ'óÌCcùï»V¯×¬tþØ›Ìæƒå϶!}ݯóëæä*É/h”•.9Á„×îõÌþ'M iqÏvîq>°ãUì^Ū(.<¢¬ñ¿Í<xï㎉c®û»ˆûç×çæûHà‡ÏIñÖüÔŸ9PŒêç¢sµt’)¼k—ö#1µx¹…#.èth‹¯¿ß8ò83Îß,8¥ãD<¹îÙå’˵ØéJõ„öjâÙ¦Ú„öb<x5-‘/sšhHý®Ð-ÿo­úÜs?³!xïóÏk ‰ïÃK–¹ÃÁ¼„[y:E°É ÑŠbS ƒZ+ çO9Fä_óÖDä>T1ãO&…œd·–°ûYƒa“ˆÙ0ûå½Ö™„-‘ß­íQÒñT½u’›“¿…#×òÚH;ÜŸa›'\^ DõÅú5W5¥¨å@Y†W·B…aF"eUÕƒ—•µ³´Èˆj™ƒK•9׳Í»{“\˹Ÿš7 {¹˜œ¼Ôªø¿/|ù/ž¯|Ïe}¡×V,zºÛ'/‘Û> Ò#n¾{:é @qÐà†òéÛ¤»Yôñ“Ñú{O§$Îá€#.h@8ëh.G¯²à*­;Pnþ›ut\ÝõwYñê¸'g.Ëí¾äÑÖ¨& Ú] ˆÔB—²)!Ÿ©Ö5|^7ËŠö[ûÔ<M ú‹'luÍï2ÑQÒõ<½—zO*§]Òm«Fø½Û°Í!³R{ÃZèrúÄ5b–›™CLè®^…!nÿ’ðžÞ¥àµgïx#ç°8vV[GVvÌ9±f#ªå#/¡OèX|PŒªýjcÔ?AZ@¤Ã;Jþ+ ‹O»œ©ÊÊø(ï.²ü ï´gšbÙa(˜xÄ~¥áOæýM¦0ï3f•}ðöcò‹–ËÈ#12Žƒ xé»jCé¿ð<û×ä»——IBPŽyhK¢iV’Òn~g—ñŸ$#1ïÛ†"ŒÎ$ÁËŸkgK3´ÌR³jUö‡J“†ˆÇ×±#.’„-vÃ+5@Ù㙆¨›2…Ÿ¯ÎÊùF¼Œp×úxAüä€x.oË}wozwDôñèû§ÆRнܜJ#.ôª|Þ•#.hýéû÷}@xúâ„‚ZƒoÖo³”}ó„ªÂÛã«Rõܨéøý˜Ý5Ì$LOL=|ª$‚·‰QÙëx0h–L—ù@÷qª—ûççp>…´U$Š##1aCÏã ’‚ˆ±)¤Û&Æ5´kW\ ßÒ¾ÞS¥]ªzFóQÂ@¸ÃŽ´5Uþß]Žq#88Ou÷å§Kšz£ºéã\O#ð¹wHpUH²FE (1X ¡óÎëâ[9ɩ妅H’5#1j±ðµ9 Â#1CD•/ÑJ±Šˆ‘ä*áPæÚ¨V£”•¹0ÏÔÛ§òo¬1f3üƒû³¯èñä4n#1)`ª÷^ÑóTœJ:D‰}6½áùÐýã—@ˆaíXÙ!Ì´—¿Ëö/ð¯åö)[Y<â#l#eU fœô¾îÞz±ÇÊn°Õ…êßʦ,v9®€ßM•G¦a²­½ÁX{À4¶œUNh—>¡õ0Qïºeòú é»ç#ï¨0×™ˆ„{¾’ûu‡´…îaR"\wëÞ9a¡5Ü¥A#Uö|€ßÁËÈ6ŸàÂß¿ÊÉú¾`4ìt,SÓcp&9d6é©ZK·)­v4‡/š×<<£*€,ô#/årJ>ø©rI#1|¤‡r#/nšn=Vm’R”DЉl(¬hÏÃѦ†l8‰ú¿ZšÕéCXmN’[&ù–bÏvl†ù:—äPz0àÍD§öïËÞž¦“›ç\+4Uf x Lú°Õó¯Ù§Ù÷’®g‚âU¡ç±,#\ëhrsr¹B9~—fÊ#Õ¶.0Ø/óv¶w-¢€qz&MpÌ/yŸ7 u‚Aå¦BÐß áúÿÑÖw¿ŒÕÆÀ¾¸áZëÐ#.v`L6ÑáãCBÞ…h߯rt „B¤´ŒŠhn”]Ȱ´#/þ•:½ŒPËõ¿j‚’e)Î膵AI7C§åþ«âÀ¦#.†Ôs$”„Roº¥ª*‹hênQQIcb-ü¿¿ÖÚ÷~F­¢R¸N…ÍX÷°Ë5^wXtͪ´óe ntÅÀ20Ý×.–±R]Ò#Øt€2Ïae§eD#/–Ø–Œ]°<â/º!”loîç®Ü;\íe:¢–ÐïŠq×åþ‹“ɧ| U ¸rÐ~KÚ…_“'¹Ú2„‡ÇÁíŒnáݦâ¾Å úÜpWñ¿tÐ}×>^ø!r1h‰s/‘#걚ÑtÌ:ð »ÝGšÌ®Gúzo»—*ªƒå¶P·›;(ç ~ãùvîh°2‘Ef·}ÂboTY‹#.´:½VäkLbŒ[%ï<´¦“v#/Щ}Í·{~ôúá+Òõð˜Öu(«Q#/¾¾T¬pÎÿ¶±R|±€¿ƒ×ô,IÍÇ68yfßo×=ñ®úÙ¨¹“ËJê—¼9ØËyrÆãÙôÇÖzã¶œ™yºH‡}‰¥3øL?‘ï¼PC2P„!D7ÓʶçÍŠe§~u4ÉóSä¡÷ÓTOiRî™àUM‹õnz¼#5Á¢(ܣ܊z7½9ú-µŒ»¡aØŠôULÆ °¤.{x¾«| Ë‘%Zê­“¸á;oöÌ¡¬ÛpïhÓH< -$kZ™^òwulôªÆZqe‰±4Äù€lÖž¼4Ž‘Lƒ`<H£Â)Ï<ùß~§ú#/Ú^ò+Êx|EA”¨C±x0Εº…{býKCÛ0Ô»µë¢3dŒÄ#.è`:¡cˆCš2‘ ¹ jŠÊúsv÷6arÿcÜ ç¸#./À®<÷»ñ€<e‹Ÿmß;º¤àÂî.#1oz½Øó  ]º/N:<Œ#/Å×»ù• Ê¯[ëŠY!ñHB’B˜fÎ×[TŠàT0LÕäA@êúL§oVŒRØ b ™ÏçôXSÍqûå4ˆšR)7hfÃÐ?ï~ñÑïŠ=ŰÂ@É—àT˜G¸¯hT¾´TU&«Ö©PY8 ªJ:×=ÜÁH3‚ý#–ù€õñß²¥tó…Sİ·0èÉ(û¦º“Á„-.Ь°Dyõªóùëi©'.©ª¡ %,¢Á;½E¿„\3¢i¢6e¸å¦`“¸˜Á~R§dãÑÀŠrqõ&2¾ÌñC?÷3ªMë/Åõ¨Ç#øäo»öU‡Y9zŽ(éŸÎ-@&œ9[HxK«¾Žâ®W”tº„”9ÇXé‹ìÎÓ :=®Bé.m¼±ÞrûÞLôx^O<³t ¬N«ñüqåÜ)g”Ër:õ÷“û’`i‚'(ÒEÚÔ%ú4:d]ùS±‚ë3ÐZ”ôã«ë‰]3{¶Ðoà·ñ‰d®’Á(fhLÚ²– Ù8:–üʬrû/37›‘få4D(ÊIQKL_Œ5¯E¯Ô…ÎÁçÉõh·nK}IìŒsK]€ÐŹ{z¦7«„ƒ)iƒU$»pí#/™¯s¼ÎæÈ¯¿ôÛ‘¯Ž{]×á²Ëñ»E¼ ž^¯ïfÜò:ùÝ!çÉnX‰¸#/Ï¡0|Ùý80¢at¥Ë¯Êaò%=øÒúۨط8#/óVò GÛÀÜ “ÇÞü²±hñ?O+˜•»wF(N²‰&QàUœv¸AÁº®rï†ØÉ†#1'†hí)¬¼Ál~ú[Þä tîa€rsæá¼XídRÁ*éÐà+Áh {m7Éîƒg²¬Y¡TÅ(!éˆWƳî®zz½´êÂQe^GµùöˆÂ7K,·Wa"á•»ÄÖæ¼ŽÉ¼y PB õ.Ýæ½"‡÷/:>6vCYé_‡‹àRß½9çÃBæÿí¨,›2•Xiêõ9ï+1™òƒd傤còóF¨›7##/ÖZ8ƒ{TˆC`&€(LÚ¶Åüs³R” )‚(¡>ñ¡ÌG[€ŒŸ@aj!*,n<Šs®a}Ù‚ë$–ºÛž1õ:Á}\Ã|–QèhÓª<ž³Ï#1Ç÷]ãç$_%¦žv,¦M=RZ"B_DW‡,/òoîGSdz´waÍqå|aÎýÍ[ÝôMTÄáóÝúñÒ) Ñ•ïÝÍÑiŠfG(2ºYÇM£¶qí×\IœšÏD²lá‚l#.¡#1Ø‹ÛË6Dp™{f¸òÇwØa=ƒëÇM³h×±r†{Ôª™XY­DQŽ*"äu£žÉ[d¨¡Ä´œÏD.Fâ99—Ô;8b¾ZÛŠt»7·´ ‘ëŠüŠäo-ôÄõí4ô¬’;M>JR ÇH!ù”ZäO„qaSfÎ3H ®*sØ‹ ê 0)O‘تè¯W#/½"ØÁÒME{\²–ׄBB£êúî÷ÀêŸ1l7>¥“ŒJƒêé[æÖ=£ƒ•«;ïË£zLI.m,ܫƻæ•ìŒЦY8‘ò0à6QJ¹o¥´ˆvuÍs䉕"„ªcròzÎÜÅöÄeZüÝÏ+tÈHù£qëÒùo—tø~ÎyÔÑh“j iŸ²!ý«g „&Û\°”*¦Óiï¤9Fàî£ÌòX¤€Äá¾:ˆRŽ ¡‘««nN*ª€È? ’rF–qç7$]܈⫉"ädEÛQ#¼xIÔíJœ(0]zrÆ]*g#o62W {["S><½er ¾êäÄ@5\î‚÷¬L1i½ðzMÚ#I>mŒ* aççÃ=¼ì¿Ø©¹ ©Ð[sƒŠGQqÛ<mБ#.Sªv íAc°Ãj•&­Ì¬S_e‘¯gIå6uÈÎÞ&ŸÛY4š8ò‹J”"gÃÝÓÅ4ûbK,…ÖÓRÌ(%n”«éfg‚ QÛJs*_TxšràöL%’×)Å4zîÈ B ²Åè3ºÁ!sÁCjȽvÜuvµíÕ#/âMß¿#2û¬>Gl¾›34™Ý“|6e5©wêîB4ÇQ_j1³ØRPÆ*lÒŤ‰ò«p0¤Âuñ–Æâ¾YÓM׃\™ÜE§‚ôHžÍ}ⲎÚØàpYb{@üt4™ŒÒ„~^ïÇlg¦õ©]dL•J²@ÜÃ%#/CÖYg('œD®‡#.û\udb#z›; nŒi …S6óg[%{a䯾çî‹þBJª¡ªO?[£œþéAóçA¦È#>M^ÎÿEþíüê?¤Üü­ÃË8FŲ3r¹vî§;u¯<ò¼¦)\Ûp5ù_ç~Ãñ××Üæü˶¯‡ð{Ë.¸¿ªªá?ÕÈT£B¾£ó>¶—ÚÜV Ç‚€ª©¾@ Aþež¤O#.sò`8Àߘz¶þZô¯á[ò õý¿'©@Z½-ü#1ûÀ´ÚB˜”й0ýÐ3þcøËŠpú#/Óù? `ËÌwQ‹<ƒÕ®¾Z¸7òl(Sv2ƒˆÞ¾›no„Štw«¯vàP›ékœh8ÝKsb€Rèlꣳl§tÉš}q)mÍ·{à%ð[¥Mñ¨’ŒÎ5À†Ì]7­Ó0üý]npšöï6꓇ץˆ~UóC±0ýÿ—“f>#/0Ñެ¹D;¯OIÜUàÔŸŒ#/Ì8|œb›‘)#.ñg•|,ä…ò¥•¨Ñ‡vn‡×³ÅQM³=ÛÙqÝɺ•Œ•% üœ¥ÑR^9Nž-ð蟙û{wäròvž¢r Ý’Ë Î½>!›Ûßžè\rÍNøÛ*âû;ú^NœVm´xŽíÆ“ÞÃSC›L´¡W‡eŽô™øÉÊÈä,u‹Ü/·UÒ~ˆÑõ蠣ǫÇûì€ÄÔvpß×R§'’VjODþßèÁi;ºTðá^;Ù¢°ÑP`«ù‡Ì¹[‡ç™ñ?ö¥2·c³îÃw¿³¿lâÀ„=Å0¢ÜƒìƒÓ?Ã1d náûHþë˜w¹³Uì]è‚À„#/ÚúJ`#.:åþgxYsÌ£°o›F†Z‚8Uð(4@»œBÌ=ŒEMº#“}[9¥#1²)]@E u…”ŽÆèzp#,Ä"ml#/†Cùã³×(ºCTÚP™¥ÈEI#.¸0Ö周£vÊ(º¿+`PšÎ6êp#.°"!«dÕ(Ó€¼EÒÆmc0G|À°á"YDÆ‹4‰FD”! ±õèˆ*g©#1Ùñ4¡è$»Õâe?ßvÍ>ÐRÌ8² ÐC“Fqy(ç!UþŸÝP9ëð‡V_Ï¥_ïàÐãß4ÌVù1A·÷¡r²lìÓü9x$–#1òPh ±â#.á8³ãÓãhö†¡Ó[¿í~¦U¦%…p¸ s|%ÄÅJ’* N¤—c§«Ð·òÖs^{GˆšL•Þ ¤üÝKÚÿ‹›g:É+Mn_îå(°j@áÏ!ô}4:gŸoc’Þ9—bÚMËôAwB#H!6ˆÔ"„Q$diAIF#/¦Ëp0íÔÆ"nûv»“VAH[”Õ;7Å*;JíͳQüÊ{{vñqR¸ëŒ«üRp¾dSÆù\aGï>fÉ]RÎ*8 šµá8ÑMŒTL°ú¯bŠvpøEŽO'ºÔF25$ÓÓÛVTœ;Ù£5F«,=j¿ÎãÙTù4Å5A#/Ñ>•ếCÚa°xl<Z vTT¶ï¾^`WÍ×mîž®iHø á&¼_³Þô£ØZºq|‰ÁÿElÆÈ,9ñ¢4 v—¿¬7?\1xh_ÚmJéé!! ƒõÄ=ÙsäÜbYð,WÛù}-¼½îá654Y±´S4©f´Ì”Å©çà¾÷¨ÏT¢ªª£ IQ…Rxug囄qØ|_gC¹7vy4(å5œBQÌÒÁdå¾Òö黿G(dHA –È£D‰$5N°Nic((¬¼Kª—¡å`ZÒ'ìž5·¸óÖ‚‘“Ö#I Ûq3"#1B+Öm7Ñ`LúWH›5‚ë#/Ý@†ëR%¡5qtLD’CJt6_"´óÚ`^I¡Ñ2C`ò2zdVºR£4 3G¯fVJÁƒå¢]VRnÓ#.¤q&¬b¥¬å¦¶9jQ@NÝgo§ê‡Ö›9p¿³ÄÏ@œ9¯dè‹Ý˜m­u¨å>NKƒ©"zÞ¨HzE껑ïˆg ¸3zn€ìœd¡õ°˜;‡OÛ¸{’àøíµá߯½á’¢‰äVº€›HµœÓÁ<¼hªæVwn€sŒÎDˆT*{­âÛ%m[ÇY«%µy+xªì·kÆÚ®U“j¹ªéµF$`šmä왈˜ÓhØÇ©›šÄn½vó·[sm}2l”š0W¶tRl¬ ¹®&Dšà9‰sVjî°Ë%#/ÓZj^ôÀÜ‹k:E‚ Žl¹CG<÷ÞXèžg‹†|Y0nwô8swt/#/ƒ±"Ũkä'Ë|‹*âA:WÙr„;Ê_¾u“#1}%gʦ†~ü¯¯Í"ûÛo¥i4%“hÖÆÁ¡8MƒbXx!qð/;ä¬ÑÁÙÑž¾;Mo^Y‹'‘íÜ!sÌC*þ ¤WBåÙŠ8=Pì®#/VØ ØDÌ#.o U$¦ZíP§$é‘ÆÆ•òÎú‚}•øØ ‰<¡3ìͦog5‰@“òí‹fsQ!«Š¢#.T ‚¤ˆI±S»k{ox–Qk×ô/ Â B ª*D‡uîÿãáZ‹ÇÝDãoâ“·¨<Qª¥V¼;0VMªz(lë±RdaâŠhðÁEă“M—CåÇ3Žvö¾#.bü·Â1‹ wÄeÃBn– ˆn$4I÷¯ØŸ^ý©Ç^uÕ>^sÙp•ã¼ìC€„©×—bÙäŘÔJ:ó«(R…—I#1ƒl6#.³üÙíÝŽ{ú¶x|ÇáÀ0§ –š(Æ$:†»Þ”7ÞPk¢Es:^ñBéSaS·\¯,Ðg+ϺŠ”B¤bÓ#1E‚È(:ñõ¡£lN•‡B „ÜÕ##/Ý|ôº"wõVgóÖ°Y#/ãÜÁâ Å-yTj@žÃ³´aJçå41þ ù9ì¿\Sʸðø{, ÑSUWŸ’CãK"Äy!AV_°¢°Æ¤ƒT75Ô¸g¯//ǜݚ®î”Ù,At.cgÚKåð¿øÒwuꨩß2¿*8§Ê³ŒÅæ}¨n)òÉ72hQ2¯ÉßruùíKÜÚ{œÇ„Á*7 ñåS•%D=㸄°gGO7Ñš¤”ÀiDÆtÒ· È2);ž£ ' ƒ?ê&ƒŽª459œ¬UR‰è…òÌᲞ¦-M úQ¶Û°' ßÕŒ2-†ÖáÐø´–m·yçE®ùMV¼ìÆžæ¨I&pžæ5AÑ™=¡þoN3 uuÍqӯźqàìN;N—FøyãƒÃ³¥‡>’ Ó¡¦sú}|MŽd‘ÉhMª‘P0™•7ïÆ•Tìß7Ø9ûjÏgݱ§ÛPîß;d–q×ÄQ^÷!­ùE(ûûUÓ­átd;#/.”u«ója¶û£seÓ¤2àϳ+¢ý»ƒ€‘$nÕ¡¿!( Î½4í}rÛ2Ü®ò¦ö<#1HtE.œÙüO¯O WH¼Õìå“­ÒFärG#cÐÅê•}ØlÑQ©¥”îוâú¼ìÄ.‚—Ѧ"Þn›0xT%ëAÀô>|Oñœ/VzöQI§!¸âgŸ™i$ü·{C[Iô}<\Û)çTì‹Íé Æ'÷>¼ÚîÊIê·î¿W>íãlk—í&¤LF8 h\DKE”ÐÙIiJ²A“Òã*§…¹1ƒw¹×hOT†žÂ’;J#/:ˆ(PQ“ž×mP¢T»º‡r‹×>é P0‚AÛsV­”æºö§ÉwÚÈh@;e¼žAØt»gœLdq= q¢Ž#1PP`ª‹ÈÀt’bZÑ)PDC¾tÈé\|û¤¸šv]sí,ü ¿¥_K®ƒ+¹øUïŽòù9lÇ„<ß}! žÍu¢,عŸ{æ÷ÞXÙ—ŽQuü9·ÚöGZf)¨õ bÎgCoÇ{(2,37ဧÉlbJ§RNˆv·ÙBü õ®ø°öè`Øt6p<?Ò!ÁÛäÿKލøÐÿ®¨žµÌ3£¢Ù,~s‚ËÄæeÓ¿Ý7“Óf+øg•#1[¼+Ì.C½ZNlƧ€ÊňÄ4]3‰Ô©ÀL‘ï©T+R–‡â]ÙB2Ý9(( ²ÉI·Y†PvÞðœMôèNpMf\kèù}vuòÒÝ{¶†éBµDNê;аbö¤5OŽ´>&;ñ!ƒâ±ñUTãç4kÀ·`Å÷itbÍ=×¶ÞFË(±¢ú1ýÌûTO'Pø)÷¸è”»o‘7îüdÙrDµç×v%šØÈwž9lboŠN¢•AÌàÙAÁúŸ¹#1ªÆFivLà7{V1ÑR„˜yq?YæN›}bÓý36öt6ð&ò;]óZ ? «læbÜ•%W!FÂÙ¿%¨HM¦ç>gdÕæ2ÞcgV-~ÎÖé#/æÔ9oÔ{Ž«¡È(îCV 'zIâÀRÙ 9¾'pˆ– Àûc8·¶ m £¹tØñ0ÄÄ3"ïµÉWC‹’IÝ ¬m±Ñ4Tž›ª&±”ØÍmUŠBÐ7aÁì4—V*T*”ft5»K<=³ŒúÊê*C®Gæ AïhœmÀä¥,Ì`=z~]©û¬-ƒ4ôôa¦Û]qO£÷§{hûǬ|jBrdk‡¥‹C¥=$R\€xCšW5*¾’lÏaoK)b"ï/D§#XyDù;SÛ\¶–WoŸG9fäîtäp#.öØt’¥I¡7Z"}#1ð5‰¡c&q:ùöû8l6½ xì✄:‡äÝðýß=#1MJüDùp•»î»ÓÙ‰¢}lö0ÕÕ‚Ò°”<Å£#1¿N=Ù®Ûkäƒ`!×(@4üØ8N#.DýÞ«,=»>k7¦¾ïèâ#ŸÆÇ¦Çúnx#.e#1È ?\÷ℱ*3Ͱ¶¤ôe{*¾¢Yz¿“õè9™Qζܓîã·òÔö‚'oíÏ@Dâ÷Ù|c²&ø˜º Ûô[³ðøEñàm;èËœ&1³¶öBG[žñ¿íæðܱò~¦Š<¶ƒöÀðÂ}ê#/–Žþß8ð*¶< y­~xj׫¨ò~&œÊJ¿>Œíyÿ³êÙŒ2ÓŒ„ ½s°ì‚ì^Ô@h8%•Pe¿Üz«o¹&¨‰2(Œ”lN¿‡èªþ0ð…íW‚ÿœÇõ`ÂàúÓ8€û_ç‡ÝH*$‘He\¦èaÑÙŸ£ûç­ß<8î³É9ùoïÊçúÌ1âôeðæ½?Ȉ÷¬F6ã?‚TèuÖ^çÏÑÏåç€ùé=ÿ’9¯Iì†'î?ÒOí•ßËON¸ÅMÐóòÒÊ ë¶U#..¿¨ÿ?åçþüy¿ñüËæÁßþ¬adZ¾[KجÔZÉœøEE]?¾0ü'M4#/ ­êå#/Z¹ÏR{YלiFAý`EÿÄUL\ËÆÇ‰œÒÊöʽ_¦h€ 2J99ôJW»Uˆã@fÒ±­(ã6‹GY®Âp÷ŸÕ„s·~߯ó¤ÿ‚8I äc+¨œMgù¿É÷õk>}\âˆ?yò׿ÏÿŠ,©N’i ¥ÏÖoÞ¾~=q}õåéU~iÈÿwĨ È‹D$Y$Ûö§øK[ˆ´Ê/öîûÇ þâñËömƒö08ƒ6ª'ûW,’Ÿõ‚I‡éö#jã¡'j$¾`!d|Š,¢%‹‹›vW¶á·ÜM)`ꉉß}Áž(!m]ç  L¹GßÐ5 ìQ$ª #1‡qÍÇžüÚ6sSK΢>ßö/û\/Ϲ22樧OEv­™­§HÑ„-Ù {À@‚ÃGŒ8ïê7]Ö ¬] ¸–€b7}‡,öáB•¡‹[ÉF–ÿ¯/Ê|ä`ciNq´Áþ–æ—Ûþ{O°ý>³´Yßï²ÄRÏ…cÂòb1ÉY…-cÙu|oBzÈMS© ¢Ï ‹>ïÇ—Ûu.ˆüÊ¥-Æ?Gæ'^H }ÉLŒ8² (!Ä#1dJ^üÅú¿~ðëÿ›Ý³O_²»egj½­Ûnç—ô×Õ|ú參®PÔjH?ô‘[ÎÃ߯ÁD†áx¶~k ЂÉ×1âý°<¥äyj ^aôg7#1Æ"$X¡?ÄåÙßôÓõËïj«Õ¾hVˆ”–·ÞÊ Ë·ladÀ§î“ƒ®ôîæüã]ÞŽK|_ÌK‘×ÖÑì§·£Ü¿Œözþ¿ixT6{k_^GÃÞzÏg§˜ô·fñ×àèeçôgÑì$TQð>*Éç´µŒ wÕ⊹r$]î_ÍNßoø—2 ­ß¿úJ=â&+`º¯¹s,Ïu¬ÊWø33‹ms6¶@¥=(ÙÖýà€~:ȃ”ˆs­ë üGŽO/ìô ðôLý¤¶1{ZQ0‰Ê¬À×îíÁùO‡õ|1<ƒHþ†=«küß?º|ÿ©@ù¢x#/^Ùð£/iÓÏѯxò“ÑCÏäSè jrèÄz­ýUï”F@£ÇòÆñ¨éΞ„kßärdD·?Ã…ÑÞxü×ê(Hèÿ>W£¿Mâ÷4W³Ž4ôVØù á#Ô¸¾)â–‡'¬5L\€@ [(iÇóx‚'¤ó¯Ó ¿V¼ìv}¾k¿Ýë7î2·ºán’_ÉÊñÎ8V$-H_$ëð¢Âuµ¨Ï×ðC;}FÈ Óåxêo0òy¼iS4Pû¼ùþMÎѤ«Õˆpzñ†²¦- Få‰ÕšÚŽB&P#/$ƒJ£Š­l¡Hz'òú6m×!î¬O/*ŽWÓpçéÝ+ˆyùÕ¡Fõ›~˜†#1Y“çü~ZC;Î#.¶Øé_z.µUëB,ÑIÊ­“ó|3NþWâñ¸S'FŒPioƒ«©» ×£›÷=#Ò•Kʨ¨—‡šûüׂ}Ërý[aý¤ª&¿cí§ý6ãNÞ_áûý0êß„…l!!‘ðYôÜÀÊ­Òù«,Œt‡S¥z/YuŸt'ˆ߈Ù'ó§K#1hœ`+²ÂÃÌZ~˜™„ÙæûÿwËèmí9! LÄBìRŽ­HåÃw#1Ç#/ñƯ–w/Û}4õùý]jºh¢º¡:øûnt¨Û±[nk+)û#'PD4Ï  Ò+ùºõ öTÚž‘æöhÄD9=tši–°š®ø4pPOì½ÿ2så×Ö9Cow…xrï ‚xg*NQ )dÚ{WU‚‰Ñ r Yj­ËjÚ¡î-'îd‰ð×ðmþ­ÈñÇ6m!Ž!+DJý<ö$»Ìaq|¶JŒ£õ{!KuqâñŒ"¶3{毨ðÕ–•ôì•Ù»€@‚#1ôàG„~mÏæ¾?·o=¥/Mû&b°ùË#;BexÒ‰×e·yT[–kvès£®Yµp#1F¼,»Ãï (Dƒ[a¿Z×uû29²  s;ù~¢‘ô¨ÅÛ›ú[í <¸2;È/Oa¨öT|e$|а€{Ï?P\=¯«„¤’`Šî0•ŸS ©)à´¢»ÓxÔ.5ž™zúÝv ×ŽßÓèW\ƒøëÀ;#.CÔdTQB³7‹ù)ÌÚÕrFê™Ç+D±³Ý¿ç±ñö2:ùd÷9‰ý›¶K[f… àµs`à à£H†Ãã‚IÂ΃ðoQgJx·…ßwû1¾'+qfx£;ã¾jª¼´êv1þ:Æn<cšæ­)”—žÐlb»[×ÄùA·ÛD$¶]Pþ¼¾ò贑获µçTŸ¿½éÑ[ÞÞêÎ_ª|o™WÚZ; ÀTáoBfaÇE"(Ð’¤Œ‚¼J÷ìjzœ¡)5)ÁTÑj]\¯š©»›LÑ«ñ 8©Çªxz¤<=ˤ¥0““fàð×È Ïý6Ež+PÊvqä€yG‹£ÁV÷†ƒè°ÇðÁ·Øùt[À:¤1µYƪ„‘Ê'¥º³¬µ%vR•{ù0›pšäD©<ê®…‚ôQ‹V#ÒÁ;wLf?ÙsìÚ”—\4ò>>—Ä97¡ÇL/wåFÇ,°ù­‰÷ðôæÍ‚år¦²Å_7€¦ÓW¿’*°vt`y>È1o$u“±éf·ñ«û£¤3‡•Dlú<{‘›£—°6Ø«°ÔRÌ‹`ÖÆã“—Ô°*» ámœ´ÿ¿Ö%2Ã¥¥ñRÓ—Êü±qIœHå\ÏîÞ£‹#·.Ev‡m„âõèìˆuÏ« ‹¶y71¤$aµ’¶åpÆ«^&°6X×ÊãHÛ0’guPBȇWªn—ï›Ô°µ%ÊcµbÊ ,f"MßP¶qÈ®ü®Ú½6ž¹qAŒbT>è …P¯šo.£»íͼ Ç8Á8ÁÇ”Q¹ù¹1;¾!ëð¬Ç_’œ)ßÊ(ëÓ\w¬ëµÎ¨zƒè?\f³5`‚«ÔW“¿o<ÀhÅ¿”úg­íÝé?<PP 0ŠÅo{“Œ]ƒƒ&öX”7;dõÊ5ÁƒI$ÌâZ®‚½ÊAÁV"ç%¯yRÎTåÑô;\gKŸW¯…mîGÉæo6r‰—<!óñhm;ª*ô»Öq™\¡}Ë©î•AtZ-ƒ4[¯ßã1á´´¹ç¯µWŒºHý!‡QÎm >)ËC"U{?û5tºÕÛÆä4Ë›TíŒpŠò?yÒ‰Þ>lÇÃhvÙØLsp#—8·_Ñ4r‰£þ-¡¦QNm(I~äE¿hu÷5ªNþšyTB’•êÒ&ˆ¢Wì*ž—шTSãaÌΕ”LÎ$‡7íöYøXé/ß Ó»eüøàÙß«‰Do3c&¤ÆþŸ­™}:æ"Ÿ9elQÙÅÚ¿DÛá ž8Öümžó¢ØëN°ìbÇ…Êzž’OƒÆÅ#‹¸¦Ú>¦r]éÝ;¢œñ„®pÓ¤¢õE Xª DJ‡æzgýPó=\”[J #1”$Á Ǭ޺üè9Ó˜Xuþ%$§çÏíÚ!çx÷eµv©wD@‡&~&;×x)Ç´‡šr{ô,¥%Â#/$pW¤¶¹ët‰Œ{¢Ð#1ü8g€SBv,4®7#.ýw,!sÖS6º|N,”AÑ7·‘±|)û¾ŸnúÕõÍæÊ®Ý¡ëSXE2h0¯tÅÔP÷þtS‹64k½/ísξêȺ qÙEÛ¯çØîFÊXX…Ü_i«<ʹQJˆ”'˜è© Q×ð¨"G “·–ÅÑöãȬóöÃÆmÝó1ï÷ÊlŽŸ(Åšyí†åÉ<¡îxãˆðc eˆØ´:Üèj2Œx•èU­ÙÃ4Šã#.ኬ ñºptb›¢Ï“€¡“‘_c=Åuã’0û,¬SF{kÅé~Ü0F1ÒêöŠ]ó~+Gå"½ÉÚ\äØKk·컉¤¹m¡+ø<yû¢‚Ï1Ùá—w û™±rÄ©˜5Ü%<' 5e Ïëï¯]cßò°uX¤Ñ¶˜ ä^P¾²ôð|á‰Ï»R«JÇ?Š '7æÕìÑû'F¹ôµÊúÚçEFè`Î3(¾ #1‡÷ùäƒ}mës”W-•dŠX¨$#1.¬ËViIâÉ K[üGŸÐõ:w÷vòFØ})rO¨ìMù‰×Ê]½Óó›ÓȈMKc±öÉ<?ê‹Dzæqìod”ãð½còmSQ³#²Qfÿ;'È1a-‰ÏßUð}¾¯##1œÔ„#ñç˜ÇÚsF#1É[­@Si޳0XôÄ"vk‹AR(-#vž-ou6’˜;Öö¦Žƒ8³µŠnØa!þõ¤Bò·s˜å:øbѳÊßìý æ\4ìïÔ‡â¨é§m”»ŠÝÐeæ#1Ý_YŽ]¶Çg#.U¤¹~#åú§¦tt%¡výlùâ#/Üs?`ë#1|ÓÏõχiÇûò?Õ0êCLG†Q‹ÂvÄ”(°UÛÞê÷mhI(#1æ^G<8ƒ6Vy¡öpƒÑäïb> „Ê\:Pðä»Ìôû8ñŠí“\x‹—²Ÿæ‘Æ Ã—MëY´Ú`ž™Ž‰Ðß“¹Ë³å2~“zƒ\fÄþ¹ =½»qüޝÑ9vÆ£‹mÁ|D7Ǥ¢©¼]Ò÷¥ù@Œ<1IŠpq%ä†Òï³ðÿ”á ´ó!òAƒÛ©qÙ4@ˆ»‡s¢vƒ!¢ÎãýgØÈÓ&&\ÉàÉ(8Ž‹yÏËçã$Æñ‘´«”=WA ‘Ö‚+ìÀök«&~´u†{§9Õ#1Í4FF›?:?dxÙÅøŽÌvÖ !}pä&=“FÖŒ!ÄaWZ2"z³ óaàå4d-'ªŠ)ýNAÄV) %&ÿ‡[/†—¥WOÇŸ‹k-kïsï<n ƒÌ‹ ƒ4ïËG ¹Hû½*+€^œQ S‹CŒ3‹_T `© Ajô#1Æ#.1¦©%•´# c ?îFÀÒ.ʬDLNÚ¤í€Z•ôvÖùð,ž“ªÛ-Pz|r£ìäñf°p}±øªµ JàèL îŒ\15‡Ï_lxã9Fï;| ÄÇG0ˆže ‹Â<¦H[ÅS9-?†æ{Q<‡z?¯Käé/¼i|Ø ¸\/ üñÏ–€¡#.#ðûG¡<c 5+‡¼—mÏ·ÐâO…Ž%¢„çE§Mß ÚÞYä¡ÔDq#._³÷˜7~_wó²`™ž§©#ïæï#/„ɲ0$<£ù®é,çþ<“ŽE"ljIüäÉDНÝóL¯ì<£„×ì&~Ѹ§éŠk%0'Ö¬wŸæÂŸHøt PcÎxö(ÈJŽo#.RÞtrÖM†\OVJQ/ô)úËîÿ Ým!oúLY@õÀÉÈÕîûÏ÷}Áý?ä\ÜküÒ‹i OòÊ)ò8š?œk(¼wgÄ#ˆòyóäNP9’`t; ÇghóÙñßÀî¯//×="C~$e,zFîµsíú¿WÅ–¬éÖR@cÐHçè Ž(ßiÒú¹ó‚Ó)D§ïSÎFÖ/yN-º‡IG/©>?©=ŸçøÏ@¤ãA™žû°áAÖ]j(ÛU ¹¼Ã”|Ñ $’þ’Œb5÷Ÿ««ï hû´"!ðX¼ü;ÆÎ0Š0Gº§€œ}0©ðʱ!áôÑíœÜõ {È1:’QnöÈÚ FÁõˆ¾§%ù ‰`(IO# d/†¿†#1Îû¤#/ŸV›H3B?dô?IKõ»Ë²)ñH “ã@ç*1"ÚëÙ£½ñ{¹»zù× ÏÔòI3ïÉÅÂ".«4½UêÎ\3˜0}÷ípµ²Q˜±e‰zÜÍ`tÀBqªäôy¦r>#.øýØ+u·+²J#Šp ;™4ª±y‚ ;°X2£ž ¦iÒ´×Hn‰Ì¡JŽb& v ß .À Ð@y ·uƒŽ|+·†òÙ§íÿ_éÛËŒÈ×ON~£(K¡"ˆ'S.[ ¡ÙÙòÂë˜\¤=0r㩳óO¬_ |YhñóÌQ¯/Ë;¡Šî?Þçhv+Á8ŽÄø×6i («ëR|‡Ëv#/È4³3aAr #.Ž!4‚+1‡ŸŒÄ—r¦Úïƒw:Ã13M¤ØX"æŒùâ·rÑwPÅÊÜ€l0œMñž%µùhŃÒm­Y Œô²kôw4 ™÷öòº­wÝçNXiÿ #ü+îZ09þi¨Î#16Å×%³#‘ZBÜ_«–j(±ÅËüNŒD «·¿jçÃxçᮑåäųÀ{ÎÌí^ áDoÊU©Iaß~­A.[ åbÞ‘8ruoëç&ûÖ2o‰ã Ç~Z›™¦m–W\©<#/û³#/5Üœç®Võ<e$GŒ_G^eGßäGNgüêÔ笮ic„04Ÿ}Ò)Äz%MJ¯€&n‡•Vß7šÛ]nŠèÌ¡˜#8;“šÓRŒ" ‰Rªst3µ-rŒƒ’³»ož a$J䳓3’â ßmÁªQ€võ¹Ë[íÇ£ÄlöiÆü|Bè¦á¢8pñê€~Œ:î¤ÛÏdÇ­m·ˆìà,Í ;dŠ#/™¾yñÐ.äÚhÄ:øE²k4˜H‰ÎÔð¤ßVnh#|ôcž1ì¦{/ý~fÕ0H&=‹ñ*f`(#í‹*T'ÛÕ`¯„þï<bZ9U:r¤ïˆòã´ë£ ]x`’)0`ð½o)W(æõ®ڴAUÓ1ÇÕÈ@Éfƒ±ucXaû¯ˆw:ÿ‡ZtŒùÞ/).1lû­h~ÏÚÃÊåÏQæÞú=3ÃÛèø‡·gñ‘o[r9c´%_Ù4#="P„Â15E{ñdRl/e¸õŒ–rÛ`øêÙOæÔÕÉݦÖÅ•øuÅTãGð„æuÆ4.΄|ß[(Ñz‹¦ƒØGòV}—ú‹¬ûVAÔ<›ŸÒOÝ:ãŒÙÈç~dêÚë¹FrS0Í¢¡•¶ç¥Þ²-¤ˆ®2Äa)38Ò¦»m㮺:´¬eRåÝÛÄá–nz´tá‰n‹)M:gAÖVª]¡#/ËAü·:×Lõ¦01_>ˆ'Ònò9r­bVÍþžÑæI´åÓJ>ÐoR‚G3}.N^N¦ß)Xõ©fœsEUQîúâ<--çÛ󯕥¶ïÂuW_f¸—³nϹÀÒ [ šiKQêok‹œQŸj¹sU, §`ù({§êÉ0Š¢ÖùÃa7Y~¨ ß•ë_ìã0gÀ]ê‡ ºšàÔÒÖ#1%±¡ìå¹—Tâ Ž½h4Fa¦\´Ï¯#1:È¢Ù…Ê»›yλͅÔ~–AV–ΟžÊum¯EºÑht– €ÎœDiûç£Os„£ ¼>ìö—½ºDWâ‹Çª†yRAûz›žجó{Îyê¬åܶ>•UQU¬i$€&%P„1FÊzÛ„È!F­V¸=ñ}Å4ðk^!†ÓWe˜0¨‹ÃäaHIñ‚9ož=¥WY4׺µŽekª,ä¸Á®b°·Ỉûu.g’;ežŒ÷X„ûkÎ!ܦ]‹rÄ’äçtâ[<ŠL¦|m@Ýq±”££Å¥JŽF±t¹-¾B¾µà8|íºhÜC-â!ôsáDJ;¢Ókùb›pÝŒ,;#.‘ßD§ëŠ5Túµqvà“Õƒ ÀØ8#.=f±•aÀ³+‘³‹oÇæ«%È_³óÛM§ôpüòÅß'rsëÖƒ¾#/[;q¢B»i¿èÒœ:zŒ¨ËBÕÕhƒìοé|®_Ë-Ë^Ÿ>0¿‡èµ¾ªqo…Rn5J>]•8g‹jTÙž_:Ñà†“;±6—G%Lõƒˆs®ÜË?á«”!ä{g—l§¹ÆË'PãðÄ[ïœöø£Áî‚ðœv?¸„ÅÃÖL‹ÑÛ<Íü˜v}Ÿ”Šõ¢ÂHæF ÝõïÑeOø.u÷q†.™ÓâVé8/~™Ænxu©Ä̩וÎÙeb#1/6¸ŒÈí«ÚZgç³k}ß(tµ¼5ÂÔ#/ŠÀG–á\ãL…¡¦ÛÚ¾šÞ=;ªpyÃTXí´Váø¼ùú1º”Ä9@µ“¤Ç‘ÛùÌ‹™>Â+Gí¢5üî-ÉÁ-:<û˜Q!1ŒQ4ì#1¾·[OBÐ%„d M÷<œƒK2ylJúkÜž?.»õõ5”K`1¶AØþ0DÙ—ìÖR€Üsúûü*<V‚``#1QÀ*1äós)š¨æÓA#.ÍØá™QÄJI<¶^#.N¾zÆê'“«Å·v<‚˜]‹œÚHAȪ6#/ù9œ)Wá«s–Á¬4x UaKáyÒÜîÔw3i´-ªò/ 1V€¸XšD¹-»ßÃSC0J‹G@q™*ý±Ñ9É@5µ<šXF{æW}¬ÛKÀ˜¾6ÌdøC#/ô£å -ÀOG&Çwø«”ÄÐ2QÅP6@h[~a#/Y¦üÜY<Nɹ€}½À]ËxHv 9gŸ{ndë—ŠIPÔ$#Ùwh»n<n}í7Tx:ïÉÃ|(*f7gsT‘$ p4ìªÆÁÔg bø¼vtËgš&|8þ½°z*®øÍ$¾þ^;××ã5á´#–[³? ±š‹PIGe“´ç)|'*w_vs…3vØ{A0ÌŒ[†kûÃt{”kžÐ‚€ê,Ù>ygˆç(f0”¡Ë—”ŒõKCµÚ±×xû_´Ã/sh•ùhÜÒXŒ2ÄIÖde²WÓ%½r6g(ÖåÎoe)%ò-›Æ®eó4à6é;tÁQ¿7´rüšÕržSµ4ët#/¡¢<OŽç<¸‰òÞô:#1T6Ûhhî*"SAˆÍ¿°S€Þ‘muo˜¢ì#x“^ùœxªãâÍ:æ@ëÊ݇`ÉàQ´ ޏ4’NÖó:.¬oÅѲŦ :Ø[ƒàt lŸ¼ž×ï©]vc¾Óélânˆ=c˜c¯³É;ý¨Úz–Å®Üð.Ö÷%Î "/˜Ò9º„ ÅPH6®ž*,}P@„OÁx÷å~âMz˜«w!ž•'zës]býÊ#G¦f§:ãjÓKV»jàWM°ñX®5è™N^gíA<Åzd§“(€½Ç~îL+EMÀŒ3ª(]xT=ñäm§QÞÂ1£#1‡#¡éÙ¤š˜‹ƒ(šH¦"Ý"¢ÀI+ö¶v®˜ÄN¹—«8–Õk:qtÇGZÁpÒ óõ»G@²ÙÖ³ró))Ý2PÏäYtÂÁ¸ºÌf#1vÍd ÆëZwBªä=]âlð¾hö3•Í"z%ª´t2¯¼[¢tÙÖ<,„:—+ð9ÜHaqVª1•1}£ i«’ëEˆ(+|ùº{l¢>‹v-„œçè("­‚:$ZÑjˆ€øž hÓM×çÁ9Ú¡ˆêe¾¦SyIíZó-ó7; r9±XouѶ*œÓ+„sîîÌÌìÊ9@vžŽ0²SËgzø"#1»‡Û.wgó`vE¬6ܶô#.d ÅŒˆèé}ˆÚÖ½¶J"ÃX¢ž¬vh{X£‰¶ñ„<A\—Æ­(Éθò½\ ½NÙ(ÉûV.!´`¤[zwBè³ñ¿ÃUCBóÃ’$þ#.Êeµûñàëa ÉFNÎÞ"¼4ø†t¡hñü:\î³&Øo-‚ÁàJÜ3ˆÕ)Zäó(˜ w*Ú¬p×\3Žlû³wçÈè Ãòƒ„ ®«Î=˜(çß6m·ç¶wͨãmœõ¼|,[osÅ5œÓáëuÉà–FR-Î>M?(&_¼±n!.OYZ&Ÿ :†/§6_LŽ|ŒŽ~íxž5ëPÿ±q5¯J¯C·~†©ûwx¹9]H-›% F‚zŒBQlÀ\ÀºŒl#/«8·#.à—€›ûœäJ#/“Í nE¼–²åg™«bez-áÛo*,þû¡kêšÄT_³TÂý(Â5Õ«SߤH\(Ž€Î-9≛QAt†êJÙP,Z[^—¼»¨‡Æ‹ÃÙé/ëïN`…"¶®HÞ1ù$ŽâÇLxê#.¼"¼xò߀ˆšŒAéTs%ÀDÄ'p[@h…¹ù¹cÕ î .ȃ2:»¢B›é}c©žÍÝï²ý_Öð¹r{¹è›Ôhzeyóæo4D çü©Ðwß¼INüQÊ#.<#/0pEÁYW3šâ–(6OÛ³—í÷zÇ»È=¿Fqs·Ìåùûiò‚ÕGÑ¡@IõYè~oJ¯EQ÷Yk=‹ùEè)"•f¾6]€#V…‚11Æ¥Š4;}½~îÒvò>½ãAÁ8Žñ0+/wXGÁøçä¥¢Ø _aÞ8€IýÞúÚžsݰpå`á’}|ˆ»õ˜ݧ+FŒO ÊuÌ®Ëø/Ou½}Y·þáð,ÿwPð:$1­ÍÌ ö‡˜¼Ü>ØZжºY`ÏAAb'â3 Aøœ0ÍŠéÄA 5 XŸÍÖÎÍn_'Ì'*¬íü¹pþà~¯uí÷Àm U;J¤PE#1 ñT1ýãå+Ô¾Ïð@;Ÿüñºa=€ÏœÔûÐÀ/æ@T†ké²3†ÄæÉ´0N*Oó5U#1Ÿê,“ù‰JXC¿üæªúb<ÐÐHѸ^AÀ%AÀj™#.±PŠð-`a d;ZÏú˜ÿamÇ£M¯uŒúi#1n÷èEÒKÿÂq²QªÀÿ[Á°\ý!›äš⎃gÓj,/óÄâ*lTLÁÝ'{p"#µeÍÛÈV~[Í•}„¹ž¯n£"v]¢I vu£{êU®È¿:*MÛ¬+vøQ€˜ã  gPfd#.;û@ÙäÕëíðxh09?cŸ¼;‡m0ù­ÿØç¡©}ÿq¨ˆêm††eþï‚< ·€Ÿáýð¸/ˆyÚ‚îO¬žn¨[#“ ܹýu ÔÔÍ=(Ô,~÷ßø”†°$d¸ìý;[¹'¬iî^ch!¹ù¬*&_“ü«ß¸ðñµ®§¦ñ¿œ;<ÆwU~Ém½aŸ $d3iüS»ñSЭ#/X€L¨W¬õœÍË2ä’ÄÓJ«Õì‚ù—÷æ©qVF€#.Õˆg Q+Èì׳ú°ìõüß)_¶Û‰9þ6-®û%ºî ^’FŽÿ_g§ç –ìþ|_¹ÛöZ´â}Åi}þ‰×Ö&’¬–JªÖK~t.dÀü l`¥ó¥¨>Óh[\·gxßÎLüÀD_C0fAt*A‚‹Ô¿«ô‡Ïå.îÃZ@¨<¡hZÈ]‚Rå:±¿–m¤néÖ-#1þ†âT#/Ž–íádØóEBD9sܘ4ï QX¢ìA:ex%vIdÏúœ¶°)áÕ%]å}ä*#ú¢vziªßfÐ=EÉ=ß/#.ïn v :ƒ±î*=Ç@0¼ž#/»7-ŸåðeM†ÑÛ¹ToâŽg­–ãCÖk}ë£ ˆ7RÈK#81`¡"]‚u'=}H¬ø³ÅiJ0%Âtíâ[6FS˜s¹œ€ì6æHéR¬k¥³&L#/ª®88¶#AhUðý >¨?[ì{pEd¦+Ñzö#/¶I÷ý¡{“Ïü<ÇtÄ}¡Ua7o·–ÅáùÃLÃ$¨#ˈ`+ó¹,ÀqfÛÚ`¬dü#.@>9òy“A²Kpß ÿü‡ÀûX` !•R¨‹ Ö!Á»â•„ã¡ÛYu{T2 ¨õ(Èè±å*ÿ†Îýñ†Ã> õ3]“cÈLD#.’LŸ|ùy÷'aãIJûáç@{%WÂß SPæW÷Ñ»m»'Ï>ë< Éžq†èÓ#/µ‹&2.úº¡¢CñÆ–uÇȇPD`ÌÇíîAB#.ð14O’à1õ™*#ö!Úžk#.›D§‰úŽB ‚#ÚiÞ/¦ÒÎÓh$£o<‹h,#/­ä´ƒ$ø'ÃÌãÖì9ˆD4öÿ)UHqÈò‘Ëó"žA%S5n‡UΆæ÷dívƒ¤M#.èS‡þY¦cœÐ6ëü«˜Ë»;ôŸxøÝef¢3wý‹)&ÅÚWñÑLBÐdtšÔéîÇ)eÇH6-ÑŽqŠÈ"$²ZÝSºI$±yˆ0•Šùwí\#Óf1Ë{Î!MˆúKžêÛÇniù#/ž³^/^VÖQ§¦aÏ€mz‹‡a;Þz#.zØtÁ„v:›èd#/!»·OÇ[2¦áˆh¶ÁE›”Sލv?߬ùb|Žw2?ƒVû˨[û9þÈI‘Âb$ÇýŸf¯Gâs;HTŒ_ÙÞw÷— À’6§ùdý¯Ò¦Q CLxÚ÷0yi¦#/}'ÜCkVŸo%W¨ Üxótvxëöµ£Ó‡îÆR~'c Ãd(k‡ýoéþñÿ`tsóCõµÊ0ꯑáfD‘ährÉ1 IÇgB—fÊ‚ˆ˜<•cj'Â͸ÐÌí!`'_ñ–4T?q#û2õ6­2¥(‹#H‘„2æ²Ëœæ‡6™çžH°[[Ò@Pôb#/‘Aµ ÑúøíÜÐ)#ƒ›À£åè “B! ŽÃ 5z¸±YE°uìû¾ïùóCwÜë÷öjA ÑCLŠõö)¼àšÿ#/@ž$CS„‡˜‰Ñ “½n­aŽP ²„!Éã´2@Èv=\ZL”CsQ á 1=LÃŒŠq/²Êƒ¿Ò‹TötÁ_\H~1usN* Ö˜ôÐÇb„$îR¥.Ò:jl $ AÎ)#¦Òš6)†=`D Ìͪ†e›º‘ž <<{E<ñ÷æÑFÞO…@Xn5¤I®vÓ¶I<C7a¬:»Ñy…Ë#|N¾åÒ¤6š,ôаÉ#/gsMêz–p“D%AɈCPÚI"75,A6üîìþGñûŽáÿn y~j?$#.Þ…ÃøW\”£#/·\2mâñû>q¯_¿ÃtõëªMèÁƒ2(H¿‚¥61°l¯êY»þÑs¹á’ÒïQ¸EŒ¦h•ÖFÖhÖiëL‰ê#/íÇŽH—X7Ž²Ì®R‘ VMÚëÉ$’LÕFšÅ&*fµ­Iikm§™«š²×¬Ç¦V`ax˨c¯X@ÁÑàÛÞðšÞ‘íIw#/áÅ,zÖ:²ñb4leQ ŒÛm5þF•oãñø©LdBBB pîüsRÈõÏ'ù¬C+4ŸÜLnG·©Üys=þ¯U½‡»fcò:¯úä÷×·µ/Þà̾–6"…@ûÁWÕ+¨µîõêjéŒó˼ÉI~]~åÔÚï^äûöý¨s§— ÍW‰h}Ä9øÙП•NÚéòµU] À#.;P Gï7ƒ¶ø¹|ƒÈÜ=" ’€¼€3d1š<mA0 Ý2d㻂¢Ù¡RWz»5ƒ”¡.õüXc«©Å8•¶Ûuv>Úv6ggÛn5ŒäвV× $“lCÆÃ ÁÕ””Övè™^ù™x”OƒæŒ\#1™¬%5AI^]#/¡vS!¼¡AG1DZS÷k—†N±|Í#1ÁóË—­ðÉMîT45=aOî 6,Øâ{C±õ£¡tëß@—;øqÞFÌt6dm0žu÷_­]8ÍÖ”®X£K›¥2›F‡vµ;¾eï œw ìíï—xð#.HŒâ¾ ¨V)ÄU#1jrüŸ‰–G-ÿ_…"{||ZDIUãö¶ß¬¹Ð’œ, ÐbÊR=#À¾ñ!uûväѹ°>_Jep*ûL \©Câ0·Èê=øæí¹ßñ*š*JR‘DS=N@Û#.|Ýã~$በy€ø*»5K*Šƒ”¥,é@)‘ú¶ƒõs+N´® àÒ»öú[o8q'Ñ…”¸4U˜~“¸¯{$õ,BÄkâ¼Ê£WÝ«×W‚þžmxÅ(o@dï! Y„byñ—ó ã5¤¡y†ÀØéqm¼<O®àÉ€»ëÜRýÄÊÏ×», b)·Ýe»¡BÐ@#. »@=ÝžP\›œ}Žò¡|wqÇò=ºïÀFiå?Ÿ6qdº¿MЫús„•óƒ)æ%¾4#1´OŒTSÍ螢ÖüIR¤Âöûîܘ.þQ=}Ÿƒý>÷>|Èð:×õ¡Í³ïYaÓçÐ<gY¿&EƒŠÀQØû û­ï™Ä?9ñ‡d"Æ4äë~£¸ö„FâÖt‹¹"@ÿUú#.÷»·y§âqPëO0øŠw‘R‚$n#.7>ŽõD˜™¥äE‚)@öðçÒ¼<ÞHrw´d#/Yâlêܵu ¤xÏÆxdSi*w ô“r„8[ÛÜLPy ‚Š‚1ØÂj6&LK~Õõëzýzó|Ûoïî°º€#1ûŒŠE6ùì¹àœŸ/ÃC{·‡ 4 ÓÊSè^¸‹t€ëA›Õ×ÜsTùÉýUk}ßh£Ë‹´ƒéò‰Ï°=#._ O±G¨@ OaŒP7ä‹ñ“ÑXå1!X•¾4>ý9]ôl>@ù~__Ýv#1¾ËX9ƒ#1j•à¥Ô ± v#$’O™àíâûÑ6ïýú¨UÊ3Ø6Ö#.‰#.}¾¯B¿"’}Ý¢CÉšq ÜßÓÀK~òÚîýžGÆP„;¥cÃszL;[•mb"_ãˆÀÞñ‹‰3ùuNP¾'ˆ€&ÒÞ²èí Œ e_í©+€Øÿ0Û¡“Ñ ƒ8K³í*¿¡qìÅ_ÕR|!ÂK#/à,+ï9Ûœ#1nrbM©azËPçÌÊï—âu:tøU†,¤˜w¾Ft³gÏĬ!¢Q&V;Än¦1ÇùfU>t·O; €Â¯î¡”đƉǎư«¢bF„¦_ù©Q9lìkP¨ÉêÑÀüÿŸïí{l«ÖûBLÚ[#1RÆÜç[~ù¥Ó)FLÅ®±nFÙ†Z-Î &K‘âö•Ñ6>Þ>ÔÝ×ÔæE3€q¼óÙ3ÆAdV@ùú %¢Ô¨ÛÝÉ÷§‰èt—bXÑ!¶Æ ý]§yˆŸ\F«•Ø£eR°k—~ó§| õ$>TÔù w§™ }9½¡Üƒ»‡Ö‘Póï7ï6xzÊS“á“/ΖÞàÚH(S̰Y•R‹ÜÔøy|îçü¯#1“䟪:|{‚¦4 ú1$5HÀºWè·öûÊ€~M$¨Áª¯öè±eü7+¼¼Gñ;h¯+ÇžœQ3Ô×-”v|1rFã6¥iJ#1¡Þæ_¯·»Æ{O<'ªÖÁøŸE}“´‡HH%9¬/œûЉëQ-ý_)%é#/m†¦!ŸèG¡y à[³<fø#/ÛvüÖ¸©ð‡rÇßyÛ¼åù~gIø<¤#.SØ"‚,i %1j3éSÍ9p}ùª„Eßp;#/#1ˆ4àb£³Hœ‚î›™êðܘ g_¥Éç®ywç+Òñ<€²0)óÔáÕ8ös,Á0|ÁsÔ\¡JP‡°P¨Z6ú„ÁŒû%hÑVe’À~·–P9½Üx~—8èýzÇÜr%IG¹žV`fR`³à¬l¤}õ0…1ÔWræd‚ÃsUoX£I~„bTÇr’c`•jdÎ¥Ä"fëðµQ˜£ÂY$Að'÷¹€{ÇV€ÂÔ P@9QöË9FL¸ ¡E"Ô3†, ‡®"÷P’‚£“ФŽÅ⨚°ý9 ¡Ðƒ¢m–¿©ò¦Ü?i÷ýLþldAgo­9þÊúà¬5¤”3N7‡YR¤*C}J w0¤‘N—Þ†íÉâš%<8ÌÔ(úµ- ê‹¥BŒîF‚Ä1þ¿æ>ñ?_Pv§Øwu+{¿HÕ~Ú¤*H¤W²‹·Ÿ} 1ájùÑ´“„4Àü3†È;QH€­£FĦa•ów›o™£\ªô×7HÚ·d6T%k-KnëË̺¬òvñ¯#/°7äèû}Þ¯ÃI°#19'1ëƒ.ü}“áì‹ïà]‚‡‚úîº|™È_¼ kÕˆ"#ƒÓÒõ£#/®cƒ@,;M¯å5Öb´K@²ƒ£’ ·ªRF1RDDh€Ì‚`@³‚çá˜ä:ào6!D@¾Ê1È‹móRòz%Û^<ÝÜ{Ï]&¬$!.ztí8ÞwçO`½C„-þUB¥ëHŽ»:æW~ñý“ãE$çZÅ ÇÄ31ÙCÎ#.Ó’C‰Gq¬ íÜQ¸ñˆVñÞÛ'_1w:ßtï¾¶ü¶Úg€‰~(u#1rC0hÆ$¸ÐÝ…´…@Ø0ù½[Ì»Ýù(öÿÏfÝð‚ú<l–Z/ºÛ/<—Aí×"‰ªŠQ““îù{Ï­€¿iR¼ç¡ßeOwÚû®Ÿ\r‰#1ÖkìÇ×¹£Á½ê•ÊBø„lþ„MŸZiiᆙªÍNBð°¥Ý &PÞ¥ž¥’U’Iwß ˜>ÌlÐÍÒÖ-¼#/#/-`˜h3*Z1¶Bƒ}€ Ïöûžwm5ILYðªERêž?°¾óå×E/ÔE™{¨ìÉ#¼¢9ä(dä…óî¡{£ Bh‰·j‘ © ´Ù€!‹õÓºËé;#.ììù}ÃÚT–›Ê+ˆm¿#/¢ß˜w²“œ¹ök”ðIhN¨zYG *#:Aþ„üÃõUÁý)ç-hMÙüüööHó—ò !Whï>0#´FN¡Ò ‘YÅ%ž ìC \Izú6pxÎDœÆSêhŸîû¤/ö«»ðÕö›@_n™Còçe[’¯#.ÐíÀÙ9XÓd¦'í!O÷užÿÀÎ0õÊk‘QDAô)Uhª=£˜Æp ÈKœOM"È¿#þ¦ÐTソ¡‚ V+#1’"¤D¨ø¡d2PöN+ ¤8²œ8á-@[sHb!ƒùÂ$Só¤T&‘$¥ö¹} õÐ{Ìþ ®þçË·KeÏPa#1Nh…"”°Y.-“CŠÄáwÆçXpðØQ¤ †ç>µ‰±Ø?›í ×#/³öBéχy·Š=PTágÁ^àüjd*D[#.vý}’à0¨3ÑØz{N£5zO«vÁìS tÀàõÀårß$…”éú샂ñP jìx‡¦Å/K›ìë…ÕS¦ÜF ™{ŽA<C=eÔEdÁ‘‚¨ ŠùÐ[âD#1ž#ÓÃâH#/üêqóF^Îhøî_(â*Îöå¡#.T%P"këȼ³—‘~‡†œXMÝT‚w1d†E!Y#s‘è36ù}°A#.ç=à„°$~ÒR1^¯—ãÚìI6Ø7û([ä… q3'm±8M]Ûb%ã=¹*ùS³3VJ.ÉݱÇ"¨ÝÁñ?½®Oâ!Òq–qF œg×wpÅãh²EC•J¤æMÓsEnÔÅ->œÔ°:S5-aB#/KåÜ#v/AÈÑ{P uϸ9ryA¿h Â,á†;ûðwœ d÷|é#½€¼N•Úž-œ‰r(…:An¹7GCØáÄFi6¥Ôò&B¿ƒÔºô˯“´¤VLà.ÔÈ4>Í,H#/y[Š$öÓþO,qæœ#.œv€@ä<ƒ‰ÁFÝvcoV&Ä{·fÐô•#/h~Ò#.îaâg&ãHÄ„t$cÔA«ÀFÑ,1@)X'3Ê+‘ò„õ¡êCª!<<z‰d,óÜ›80H%VEíkò-œƒ{ádz‘\¹NyïÜv—Fa¼Œ CòßZ ×Iæ€÷bsÒ¼ô®þ#1™ï£üôwŒC‡˜ùØÈ ¾NÏ´j%sø#[ °QK—¶xÝ»°àg¾(ìûÓìýÏÜwÎuü#.#1“`<£¸CžÎ¯ì((ÄA#»ú‰è9e˜Q/‡øj MDΉö‡‡zÜæp‡m½ïÛÈS˜“ÒWÒ§š(Ãñ…Öm#/£øá®a˜Åøó³_½Ö~¬pýf(9œÕo£9<ÿÛÇá“wgYÜ~ÍõŒà7†êÕ+ŒË&«Ó}w}8×9oä[”þ”b&þÑä\0˜ÀM$—Ç3#/Ú$MÐ#/@ÚQ²ÎZ—ظޗê#/rW—»ÇŸØä{>žÿÖÕ*«žp5Ÿ°äBÏ8Pv½#1TiŽ@æÀïpj:]K¡è#.s.?„zs3*÷'Á˜ÓÔ†Ä=pYùEHÁdþGÐ \4Ü=IÑêóL”$€dA0%¢¯x°lö=.î{;4lŽÿÕÃÔƒªì>ëï\tÞ\¡µbþŠôˤ^Öä+0fXÙ#/à¢EÿvFDý(ò+Ó½P­â‡6±!uS~HP–r0Glín7ê#1/@qßí¦@#/Qõ†àp6†êÅUE„22 CP¯ßíýi¡ó|~P!2?¢­³Ø¥âÈ%w¯U%"^ú]×o;v‡‹xÕ(?‰óÍ9M2 _ÖÍYšÖ¥$>NÚËBŽkœšàôÈs².yãƒUÉ4ò(Åé>Sê tŠvFm”ÊëŠU¦ÆKµoèµÔ °(­›5*æ ˜#XÅ Ѿ\üs-Í2&Ý…‹#1OÍ€`;?p¤ýõ¶G¿ãì÷¯¾é]ö°úïCíÓçüÖ„O`Ð÷ Ä>ät!õ%Â#.eòOD±ó:r9#(#.ÚLdÍ~^?£Äôœ“9‹¿!³7¶Ž¿E"‹Î!PR¼=¸âvŒò<ÉRùoÏ@O #/¤/—è¿#üŽî#ÜmZ²Ô>Â"B!cï;]wwe~žÎ#.F¿¶~ÞLUŒrØòÿY ZÆáúñJöÆæ§EˆbZ ѣú5ˆÛODJG((]Ô¸ª8Yu_‘w1Uc‹ÈRH-ŒÓè9öâWøecÍ\r<éÓÓP”ÐxSÙû0Ñ «ô¥Çé<ó'TŸ½êýÏÐãCT#1z'Lš·#1ké@ZI©#/MîðuÎoÀÓÕ9¼~®|ñ'×(”ë·Ùå=à¢å¹=ær ÛY” øœ8ŠÂÝ„¼êío¢úQ܈™Ÿêéµsd1ü‘‡Üøª>EÁïíú´wR»¼ÿ†ÇcðH|#.À‘œŒ;2üÿ ñvN¾ØäˆëújË52¥¯Œ>d×ö½v#/4Ðûüõc 8Å…#1¢_ž£FHF˜ÐS*4þTû¶wõU9­wøÞd¯xÀÔГ¿ÜQlE„@`DØÁ qº.0ã„@ô¨//ZÿLKóÿ4<ÀàbX‹Ïêåã|ÉÌø~wëúïù´õtÎkF$-¡Ö;ß¿…ë€|B¶#/zBB,¶ Çt`àø#.ƒ×ƒç8…¡ °‹žëï³@ÒÙ…\ÚþšùçîÛØö'3çüéG¡ÆÁ W䇨£ûuõãìÕ\¹bÿC• v 1ù¤žäb3ßýâç÷¿–„~›š*cD7b#1ÊK(šLR+@ÓV8A¦˜“ZrÊImȨ²Qiú#1¼P“EKd02dbliŠƒ´ÈFà]På¦ãõ0W2ËÄâN8Ë1`E¥Š``­E#0ÐÒ)"Òj°ŠiH¡šÒ&Ôÿ/ÖÎCä(nÂ?ö2zHY›2Jñ-FYÀ¿¾{ÞÖƒþ½b(¿÷åaδڰ‹Û1j»uL¡‹k¥Ç w›lî£`ok“´äèÅ_¨aÒ ;B16c9KZ Cjnb5ˆþû€ÌCŒGöV{ài}ÔHÛ1¤Ú|¨N#Š=ÁPüðjnï—Ðõד#/#.H‚ æXHXl^{~„ýÚ@©–§aú5³ Ë‹9‘P#1Ÿ×?Î!aÙ_R±2Å&êó~Åb¿[Ræg3éoîxMfùgdþQ NÞ&Ùzuìé“)!üêT–‹€1#.ÆGàÙ ~!q<­sOKŠs~D‚\ë%ƒî=<@Kü¾ÃZ^Úçzmï1ˆ‰ùÐM0â¨n†.múÚˤÙl(Økls®Ög5‰‘½Ê˜¨„B7üβvÇùSêÇè¼oúIäG¥q9´æÆZA"-$,-E‚‡’C˜ÿ À¡A¥YýßÖfï•ôÑ,°ýU$,b¶ å‡be! )ßQøð‡“,Ù$%¥«`§ìàã#.º¯wãúM#.ýß«kíéÁCñùÞæhãûj\Xß¾}ï«%"†óÞfÏÑRkÞÿžÊ÷úî?ýZ¨’Ø_~è5ÀC¯&­N¿b0c€u°¤Ž|¢ô/ˆp ‚}¾¥@ˆâ Ù9#.±ÅÃ[Ä·Ôbk7DË%„~‘Ñ@ÚwÏ–=J¦MõNl‘»´pT àwøý×7ô£N´&Þ?xzì|Jν&´iü òM›¬äo„YÆ hg¶ŸLœ5:×¼ÛÙÕüòÏ,é¯0‡¢Q5½TÄS1Ã9Ôïš ,áÙÆ>:ƒH1ë‰0ßQ“X’þ'™X7³0èzq¥ÑÙ#1,É<ž¯ƒ:õQF Ãd™rƃûJsœä]‹žÏõžx¸ÔÑr±²!îä«Û–JÃ(»(Žà‰Øþv»‡u¤Ê¡šÒÑhôNîM„/,¾¢p‚"À<0G{‡ Q¾/$¼&þ—zN¦ ˜ø¡7³ü†%!¦¡³ùí"/ÕéTCFzñH~‚s€Ïß÷ÛÃîñl.ŸëôÕ_¥Îøy#Tw‰#/´þ÷Öù¿'”_#.hçG‡êU¸v³ xÜ&=Ñðš@¢.`‰šÔL#1seþ¡‹;w/PÒ2‹Ý¨%‰¶Ç*…V^ÀØeÏ!‹µ”‡¬wíÿRPj"b…y7j{Ðÿ~Ÿ¿45‚wØÇ hÕW#1÷»Rø,ÿ™Èñ'7Ëúɸø³ŒèîÂl#1šú xÇ™´GD&>#1(ëèoáôZ9‡OÊü,ñ·5›±í¸t>A:;ÜÁê€1FUO~C¤X8Ùuˆƒ{;ØxÇ#ê.ƒ¨=þ/ʶ¨ÆïIÝ&^~²Èó?–åQ@Îê}/Úèžéý#/üdŽŽkÔHNõwê)Èû¿õr `®l×°^!#.m”*f½âxÃEÔÔ0{}RF’½ê‡1ªçX^l|'-9–:ºõMÕ¹ÿCNù@KE_~i#KC˜">Õ͆D+× µŽÝ€¬1’üïÝcÜËžár!Ö f1!6ÙF@ã•4±rŒê‹¤¹ÊÚv¬ªcåÝQÏ–Þš)1&Ý/ÁWtÎ z“ÅmÙþ¾:˜5ìùd¼çœA&üÊÂnët„&aÐ/s¶pýŒEãw¾Êxg#/ tòà¹yÏM²ûªæjúó­ßNšë‡^¸ÌD7k—£b鵂KüÖÀT®i;Câõ´Þq)|.`il‚ª]Z¨HÅó,›f¶6Å%¢K•Ö°¶*#1d ÀîÌA¥Êi]]ÉvÙby@þRúŽçL¿Lå2Í6eå¨Ðò¸Ûã9”öS»|r”8˜@…RŒM3F‚×Ë«¦Ù©œ«Y䜵宋Ù[&¸’˜UÝœnL È¡j¨-õ­´#1¬g™ïü•ç[Ha¿>·Åàdu\L#1BéiÕ¤œGGú@ @#.P¯ñþVy=Ãê/ý“ô›/GA‘ˆ°h¹²ÝÚHhGƒön^pÂ#/-ªá4væzÁçŽåvª/Ç~]î²+Â?*ï[ô׉¸Ø¯×µYpÇ„jNüN¥¶Ö£…ñR¶ÙËú¯Ç|))ý½³_ƇÁÜ_.“yý—‰Î¸r‡[B£=¦ŒÃÒð¥}ø~Öì—ûÓÞ§\¾'eÂt€~Ñ~UwÚ¦ÝÞW¦Q!™7ä‘…‘ØÂ3³÷@g0úäééÓq}z£å1¼­ðk´Ñ‚@¤! ÖÆJ ¸æ?w7ÂŽFœÞ˜íEÝ•ìßËz ŸÆ‚® ³DAFS«s0çUÎÁ¡û÷#.>ß¿Kó'Óö'_|.»ÁQ߇˜]ñ‰soºñ±m ¼Ž[üÜ÷Þˆþ¡76ÞuW¡Œ@ ÙJ¿’=g×H`<rh±…´·U´2»²íÔ4ŸÃ£àïÚ:#)’ ¬ÛÚgßKâ”àdŸŽ¥‰Ô8!HB›>®Ÿ½9ÿSŽ2tå‘ÝêÚ¢qç+f|lmíв-àÞ\)—Ü¡œ*%W Sh>{2mu>Ê9sKNŠOŒ×›¯µ9MûS:%w¤Æ<~ÉnçG”ìd”ÀšxßÇO<ß#/† ¾MëÜb¦M4ÐXɲ+Þ…ª~l§uôÀôF6c¸÷KÛßíìïÂ[¤ÑNg¦p—pöᤆî€Óæ&ôÙèG 'C[Ý}r³ŒO_ÙF›wšÚ馔ô…²§…켊jò “·³>/9wGC‹Zð·w¶Ûý»÷[{ý|CÓX’c@}è®$¨NG#βy¢;#/ *‘a‘’ê²G“˜F(^¼üó ‰»RÄ`å©.€—ºæ#/1É ¾MDô¤•ºþ¾Ú?©úgBYš;K(¸žÃÐD$dSê`ÿŸÕþèø¾sèü|ô{?‡øVÎAˆF @ ëgóî@Ðÿ‹{‡Þ>lÕ”"’#.Øçz¦âQÈOÓE"ƒÆ!Ûƒû7ögïð;-°Øj}–Aˆþrñ“ó|«ÁÆP 2e$OQbä×òø`‹ü|‡Ö‡ðþ—Ìn!ý'’l#/-ëIpÏþL³-몪°ÿÖÄn¦ ûÁ÷è0ÿqÅ&î+m#.êÞñ2 Heqê&a·¡ý@víÜ=gph6‡K½¡û‚ŒÛò%¤Ø\4„”‰¡û^iü,½A¿YÄÕØp*UÛÞg¥‰$ÆG|ƒRp;ÌÚAŽ xf›íØínÜx‡ŠMSÁLúC@8ì"ÑÞõ gÈ–.,}aÇ}(Z«8:KƒÍÜ=Íï!¬<0z€{Ư2{#/à7#.ó¹ëÒ\ÄÆÊ”hó$†'±D7ªdJhfy…™ÔÊÙŸàlwœ~Ó`/H‰ER±ƒÒ©ZªU ÜuÙ›ÔíA¨o#94‚p[#/˜ÁÖï‡ « òyŸÈ}ßÌ•÷×ó¡N fÃP(e¢<ƒÀï9tŠŒCË¡å2uXéÈ^l㦄ó-'B„ à\Ë»èq¾Vá„û&±›éÒ£Õ1Â#/7¯':îu*„MTâÔŽ³§íËór 0Á‡1†ƒPÖƒbhF# .£Pf–€‹Â<8ò”vŒßTnÂ>ÐÎ!b+Q™aZ–ã™:„ÝÁÀ-‹X¢ˆ©œ´»îY}>¸ÉÙúaª_ $è;#·1J%5»!O¤ô¦áµÀûè,€dúѽ[üþN^ì$)Û§·™¬‹£ï÷u^épOáÃÕɲ#Xåùã¹Ý¾š‹2Ïg^Ð|Ž&#.ñíhà (¨~¡=M=…Œ'$‚1‹ƒ–”ZÉ aÿ-Æ…š1V˜Û&­IPDƒHŒ`ÃØV ¸Óö„›–PùÄ#1 *’Rˆ„?ÒÍÉ‘%åOúßÃíoi­-™£F±¬oÞÞ÷X^kÙÈi»£uÕúÒ¨–FËûÍ©ñ#1WÚ‡T#ê„# *Ö¶ìÉæ;ÎðÐoƒýTD‡³yAƒ'µíðk#.’lÕ"oS¿°€ûz‹¯S#1±Xˆ<£ý“³´³Ü˜T’àk'T¹0è_Ä3ºÑÁØè9¢6ÐLXr²8´S„aï_²Ú¹8ï£!è>3gù—“Ã$€Ú=Ìá æ‡™t“Y¸'?8A`ê¯xÍHDì7€šK‡ÒŠq!<-5ÁÉÜM‡³ÈskûH¤‚Õ^b§»´r#¼iccPh$‹ùˆ©¿a£”3aîÄæÄ6åå}öÝîžNÄ7áÛή¯‘' Íd¦`[Œrlní·`¹øœ#Ô™ˆWXÜÈ’ni¹×$¸v¤+‘[ï#°Ô0i‡ë9<‹oÕœ-PÖ–NáËòÞ¤J«ÇnI‚‰¸tqÀæo¬‹°›žÒÈl\ÆdìºÈÔÉêKõ£ÅÊD°¯_u 'q5³Ë¿{›:2«ßV®1ûáƒYÒsž%ç¬QÆ'I!¿mûº¯º£±§´Ä[¿,Ý7frQJÔš=Ö·“w~ýÜ ñ‘ÆyqM=5w0²#†ìÂmÞ3¬ÂHÝŽ’ºìÕ·t»ÑÚ9áÛ’ù›¨6œ›^:ÍÎçÍÌã}IÉåbP¦³FÓÒM4—îý½Uîã!ÈÓ~ãÐÍìTP¼ÔRžÌ[w<g–äaó%ìt8†ã<‚`†§“uy9›ØXM¤Öm2v#.Ÿ?öÕ4Vïo¾æÂwpuN¢á,Ï#.{LÒÆ€ÏÁà @W S9$S>ÇnÉn¶—P¢`ÀÁ]Gµc;’hhÃUÔé îðŒù3ÌÚ¶ÚàNåntæûbïvè ÖT¢iAŒÎÊpU¨ÛFѶ˜ñhPžŒ&S‰Ç´"i/0bQ_ ”ƒ ‡À6›Øk¤#/kM4ªšë[u8,A"–¼؃Р4 ’YvõGP‰‚ž{S°ˆ¦÷cžÈ5гÐ-Κñ å¢k,SŽqåiS”{y Puhž‡BÌ]rØþ"àƒ4ºƒÊ«™°=Þó½ëó h&¦ÃkÎ `"q´hÌÎÈN¬ÒkY¼zVc8Îr©R¦!5;uX(ƒ¨liÇi¤(Öa%L„<›¹Ð:kÒÝv‰•Ë•yx¶|—·´Ü™mJ )nîþúœ‘™Y-½òŽÄÁGxkÖæ4º@ø`À¨ðvä]ÈòÝŒ²¸…gZÜ·d(‰‡qAUÑÔxðâ„fV†äTÛ6g#.Ëgï­­©UÁ§cÜo†JPü>ÜÁJ{$|E^y„DW€âÈ€Õ39Ã*×¶w̘ÙWYCÝËС6†J ³oif)¡›N\EŒg¼ÔÉžeŽÂ4ZQ±cË×€Õr›0YË¿ºà5,Þvi†Ë÷ʽФ‘P"BA¨£¬PËÃ–Îø¬$1Ž<*ÍSKoTÝ¥ÞéÈÌb,B.aÍëà1!-àq7Üæ…A’#PT³‹EPªˆäÞ)Ÿ! ¨$ˆrØèÔMt““:Žr‹¦§ôš¥Œ²5¹3¢@î(|##/¯f#17xMQ‘¨—;‘2±’š#/JI'/¥…Z©“©GCÙ4£³”9j»›tr<þ?:̦2Ø«ÌË32eÌÌ̱¶cYVe™’J±Û3Ñó^Í·žíz¡´ƒ» =!Ôð¤Bi°lA°h ùK4Þt‰`Ö-?—¦ý¦¬¯6x‚.#.B÷lžqZyå–ä™àÓÓï*ã}®Ži …eQS‡œÁ–WžÕœÔ¨ñNZÝK3¯ ƒ¤ñ2(Æa¨lÉ\]Œ£AŽº¶¤˜R&d!«H€–Öœ|àÄëaõzƒlŽ¢3²BœÍDÆœÓj¬¥Ùè¶ýs&®ÎÈø#/@éõö %ÜbÁ`ŒJ¢„—T²Å¢ ˆƒ‰g-&ÞÃêçz!X˸&Ý2(øíÒö¡±‡˜L ÍÄ,٩ٷÜÉÔšã®Ë¿yÏnd|ñc°Ûê|íT*’j:™`wuàôõ†²iÉ!àf#”Ñe„¨jÐ<#/-l¯õ„!D:MûÑQVúæ'-HªtB˜°v#1OËÌéƒþc`v¶ z˜’‡›áe¼zЍb2 Ó‡djžId#/ˆDõqöË™l&‹Tï¾Ê‰g*ø$ƒFÞBÁ1ÛE¤v‘S6§1nùÞâbÌ<rúQÐݵG(tÕ˜wàn˜âpzK&d?Š5m²Þµ#/uó¸pÔèm… ç\ÈÔÅHl"YÕfíY5n—¸õ‹—"&ºÚ¥e–\¹X³°þ~vÆÀûW-?ˆaEäûJc£»cžÈ~!+Ž+ÀлhfÍ­V5WHž~;Coz.„â#. C©/3$96†³4öhÅ (BÎÉøáž@sïn"‚ðŒ„#1i¦Ãµ.lP;pL„¤æÉí ¶ e‹ÊûŠ$êuU&PV{%†é³Pzp8›•àb=Áh††Ò"У ž''#.X‘ñKº\òLÓà3]%†7ô7q‡¢7 N"[]záÝ$1·²/z5ÀjÌiš.‘ý=#1‹ó&”Œè­,’£>6¨ê1V0¦Æ§ /xy#.˜2²¬ênj™ïQnä9Ch¥I솭…î/b=‡¯Óp:7]¡ÇœMÙ»Rɿ׃ ƒÔîc¡Ü5ÇÒ0¢­@⃠½RލÈ?p¾ÐƒC3Ò.vÓ´ÚŽL3‡k5²nŠppä6f„Ö£©pH’ÆDr𬠒i™…UTnÙbÖV'Yê¼KB%Ç6Â2ן±Ì^Ÿ œè½dÍÂ*5Ñ ûWJwxÒ’½À´!ë|ƒÄ•Ì ‰S°!÷dÀì A"&6‰¦ËS(¤Þõï/…ÒmÐ.ò.‡¨æ:ŒVpv˜äò#/©´ø•‰ÒüÊlýžÚÊÖ°_·6m3Æ€ÓÚ÷ÑÛäEÍLJ[W°ˆÐäì~$àpø¯P‹ÑvHö 80-Á‡<çrüĶËDƒÓ}ú½  ‡ú&Ä’|bŠ÷àIŒ¤sÿ^{#M¼7R¾<=û/(´x°HA$aç—€wÃÒèlÍÍ0‚¡Ò7ÜÞ7ÒÄDš¢0Óë”á+ ^7µðë›Åç.4Ì`Açˆcph%»ñŠy&éeY$–-Ãp%A=e5±€M€³ööœd}ÅfËØQÏ@B4‹¾ÐllÙú:/Ö}$o“0›¹”˜I-§Óý”iƒ}16xf‰Á“Q"îqÔ&Q A,t;™åmèh ãFö*P›ß:Ö[Õá™<૆€þäteéj€A/ÙM67øúIL´8`ÊKZû_­?†Äÿd,¢fåGZ•U¬ä7ü¹g8—#{ðòñÏj†¡ ´!F…QEô}ÞZØÿ ê¼Ì̳ó#ì°ZìBãß0gJ—# ·SÓHß·rz”D¢%Ží¿Ù9å*Š%%Nè‚ÅHX2 ÐO¼ù|žî‰ëÍÀW^ä#1G“‹˜9éÿAßõCþÏtË0ÿgûg#/KQY-ÜþQ§WMµ–i,¿• Vß]ù¾ÛôZµwëT÷—R%ÌÙ%r‡÷EYG‡‡¯AñÊCw¬.Û?¡‹‘ÖŸ#1m~Àè‡Ú!€1„Bi[Ûº‰5ü#/·Y™JDêxíÈ% P^R‚)j !#D«ù` (>Þ• D}¿¸@جõKñ]"Aån¾%íåƒ}[â£Î \ð„„”^á~|´Å%‹*)ùÑþ2E»T#1™@¤-j@[än ÝÖõ§ýì¾tt°ž8I´÷ä­¤&ˆkj¨›ð:àÏ)…@82*#˜zտ˪¿9T´4@¹NmÍ àÅRQ)$oVƒ"¶Ð_ÏCä"€ˆž¯ø°`’MêHŸ¡úÜ(4ƒè‹(]5GrEJAÚdèDO}!¼Œ#.À„’CR2H‡»[t ÐÀüh,e×¥ôÓ=8Ú¨­ŒÛ¯“>>Vã3º±tRaÝ¥`™Ò±1.C@À0Š,D…´nXZkpqA„¤Ê0?î7D[ÃÏÔêžö´šQîÓð³÷‡i58°„¾'.ª/qÌyU¸|n¤ÀÃäÄlFO×DªóøÑ´B ¤=¦‡hK*?m>èIœiü¦f‡ŸA¢Bn¸·¦»Þñ @c'rJþh¥n}úWK¨ÀwçÚ#n}][_ÜŠú¤ˆËæ1ÀªyÄ€±@69x:Nà„A 4"ÈP$ Å „Qöï#…@îˆ#.ž#1‹"†" QAD‘#.!$[ȸ”(7HUŠœM;'à„>=°_÷YÒáq˜*5—Ñ£:_ÕP±4P R|@BÒÕ™¨-‰Ö 0k#/b¦‘ƒIŒÐ0D8BHMë4¡ÿUG’°bûн1x¯E¹u$‘oWvñéUÊõ7/Sx×bhÝÎ× ——\#/£,ÏbU‘¨0lŒƒr%Œ0L$Cy`ȈuGƒ#/¸eb’A$™•ÙŽÄæc¤Ðî&lƒ@› ×m·é×Ùömµ}ý^ÀCìdÒoô5%€jY|Ï ÄET$ŒäS´7•äö{_Õƒ«ç+tµ¬Z+¦Z´cPq–BŽ>¿ ä{:÷+ò¡ì?½¿‚V›‹Ž(Áf>¾gŽnÍù“]2¶ã¹ùú)ôõ×—5è/ºøž±Ï ™ƒõ°é$N$¬±î÷–¾åÂlÝþÊ{Ž-â¦lQSdJŒ DVD—^¤l¶½V#"ü¼oÕÂÉ"?µzkaÖÇøÄè=ß>6 oD÷#/TGúËPmªoÔÛ#/i‰ì¯à`Ÿ¾á,çÙ}\fɇ©¦²© $ΤB˜ó®‘%̆MuÜ>)i2E@b†4¶†›èGƒsBÒ°¶KÀºasýLD 2!—t#/£¸xriõ¦ípð7‡?å${ÖþË-ýßjç·Jzj~….SxiN\=~£u»â~_8Ÿ0XmÜðÝ#.>꺫 ™Æ ‰ 'PÁä>]-RYêïmaúƒWEO‘$Aõè{²w÷Ó$XðõçQ&'§˜!<ĹuU4ÁɃòû?É'D’³t},ÃÏ#/GŸšç¦K%Y!·ðƒQš6ÎËöîø›c–P[3ÐçwL;º> ÒÖæ@Óõ÷‚QB5MSECÓÏóÞ Øy´QA(í6§²“e/o9érM„\›UúªŠÛ“fÕ0-^T®ÕWì“[é¬ÞgÊØ¦VÓK ëæ2G)%}'\l\ª’UIQ)†ò¶ÓÄÝz¨[Ý—¢˜…±\Ò³þY*#lMƒ?%ü+<·‰f3$}¢h« ë 03ˆ>:q­ËO£âwÆJª”Ò ‡Neç»Îi;v5—ô{×®¶¢üh* Žå¥*+ù¡pøà !"McmFª’µdƒf¬i&Ê$ÔRmËM¶±4ªKM¬V™ªÌiQIŒýLhT"Æ 'C’IÊ}3ªß\©9ÃŽàSЇ ŠDõA¨ƒ"ŒŠ½-oXAòB”¡T>”òw¦¾Fç½kËdFìe¢#/s`M¢~Ùs!Cᡱ×G5j¢Ñ£UºXÌðYdRæè€˜Vj„í›c8~ÙzñhA7¤/¾\BI„"p1†ªÄrųDÛÄ1°Sn•ò¿Ã‡ÈÈ¥m 6¡‘0°AWÖ¬´.Ã#.KH¹-#.]s·‚ÝAx lAm`ÚÅUVùþµÛmy+D.äW«Õš†{˜p—T¬ZŠ"ÎÖ`·\S&eÐ 2b%#2¡bbS_öãx|1úˆö0 zúè#/2‰¶TŒPª”à€íáʘ2€¨@!Ÿ ßƒÛæÓ_}­b*3Ccešß{5sX&ÑXÖ®mËnVæ™P¥Šf‹f`ÔT›h  M"‚Ágß™ùO&°h!Ù²v¥æi™±Cx¡"#.DŠÈŠ9*'ŠŠD9—דêÞ?…ï¿Ú~?>-ÕFËg Fç i#/ Ü\"oúU\È'´æAâW¨ñ½O °*† 'ÍP úHFãiôH|Íáù¿7Ýüÿ‡Ÿü?ã´Û³R¡è‰#èœr¢Uô± #.,îô³Á/#1 „bª*¤0ÀÝ¡$–©'®µ '蛀 *1… mt\PÂzURj}§+ÃŽg™ááÝf’n!±Ñ¥ ÀM³þh¨ºçK”ÔOŸ€2ûò×2¢*}Œþ~ÙXš´È–ŠýP7N¬4—ŒÞ^N*îŽQŒÔ/þuC»½ôèP:åP'ì¡tDш˜i¥¨£ŸqÔ0 ¤œ±á¿0O#Á:6ì!Naúå J©ÀÛÞÿ6Oiھ͡±Q›Áï–n +Ð<Ì€†þãm#¶l–A¸Xà4BŠ(ba5K¶0œ:=iØ|ݧO²Žð"»wæIavÀx‚ÿ² C Îiý#/gÍ׿Kî»öéA”6.PC;0kèv=³#¼><_Í™èxdƒ³cƒ“ï¡ãlQô]°öáâvr‰Sº^vÍu®Z¾_,¦IUBMRmŸ©°"dJ,„Çä£f±Ìü]cÕ¨y莳HiŸ/ÒÒéó£ÆÊ$#/9—#/MÛ~ïIz1—?ô\Ü0ǹEÿi]½§`DLòê7l\N$[—án#1M3EˆYƒSª¢nˆUÌ´®I¿$%.!Ì}ÃÂŒ2ý÷‹%³ƒÎbo;x­ª$ÙÕÃ`Þ`ÓU5s‡-d¥*œkú³{Úãž7­.F‰Äh0dE +UzPTCšbÚšíXY5°ÚÂ#/²Ë+IÜ!é ˆ„`Œ#1>æ“e©P÷¤=4y‚&Óyìò~u¨8nGè¥-#.éÈê¬Å wþÿê¢$!gúÏìñöW¿Jêø7:î»N'd£O̼¿O޾ʾ¯¦Š“cñÕÝÚîR‹Ö¢ H½~ÏØÑ"©˜#”V"'·4 ÙØžžß<¹ÃåˆÞÙÛô©/Å)ìÇ÷¸F›’CR{:XÁÃÎDwL'õ)[ŽïÓ#/¥$–˜€€º«VqêöIûœµ£]åƧíÔî` ŽFǰàÑB€~¥¤îXA4ŽdH @×h8ü,1 M*˜iè]Å—½qÌæ 0߭λ<b 6E»ô_ñÜ¡öÅá™CÏ‚À‹ñËN…²å}Á—7iÏÐgT´Þ.RäÃWÈáÂ:y“ê-t‹@ ˜š…Ðý¿!di¡ƒ¢`²lÆM#1qf®ÉÊg]ž=Ž¢I>æ:çö4ã­“äqÑ0ÜÁâµÑ’¼_i‚YvÀÛvV³a;m6(²Ë#„à#p€¬L™VÍCn«0ªSa\Ó9Û‡Û\tŽBwÚy¬ª:mñf‡£¢åF#/†Ãté}E×·<{ñ•Ønyždƒ–‹$äA-éPŒœ˜½Î¶uäÖv Ñ«‡ Îé°W³¸)Ù±ƒwé‹cSnùö‹Sµ™ PV;VìÝ3[û5îôgw›¥yÚ|µ_NG‘Dþi(í: Á¿‰ˆKã˳Suº#`¾V!G©£ãñÄæÃ¯MçÛŽ#.»d<&hìÞ”K&66.C{ðo×îæ}ù©Ãŧ9¿²Š! Ï«[%ãÛß< !Ǩt޽$*{Œ/#.„¾”F}"(ReÕõ^/´?<åßÝ£ìm+g½¹¦³àW®}d1àiŽßs¾ý`w ‰â~õéÓ_lM´…ZpJ$"¯F;¸Ï8`Ŷ~˜!ùÁS–‰Êù4è»ADDšT nÒJ-gjŠ${°³•ë›b§i•~p‹8zºƒÂ2§ˆÅ]]¨j¹6Ÿ7²³*€7k¡©Á³Êž÷ŽùÇNV±âï3Üa0påsR@Û¸&½Œíh]É®«¡Ëo*öq@7< ;7ë°¨\Õ:s×q´À˜ÝÎ{(9lsÜä#¡ì8’ìáFŽ:î*©¦Ä@„L..·ßÃ\†»†æ´R)õÝx¬ˆÁìÓEd²©º¡=õŒO‡Ãs‡±(õá½Éœˆ ‚`˜÷“0#/ƒÒ¬ ¯œuy $:EÚ$’k¯¤lAbCw._Ç3¤';Cîâg®ÜÍØ>æ%Ø`»#1<ç§ÕáIý®Þ‹!"B3Iøô;·HüO†ÚG&jkë$’/ƒ;±H³Ü9D9KuHu©£y©©GîòödG™®¤‰8¡¾Ý/„•?R:q1ê+ ]™±Ûl²w¢™±`M¦Ó“ ŸÛ·p„FAmíéªóÝP ¾aÒ=ù5Ù¹@ç¡5N ï“¾•¨'XëG¹¡…YÂáeÕH ƒ#.S, Ù+].l‰êê]-Ü‹’Æ«)mvû<ñãšvµI‚ƒ#6ÐW FBÅY!UHQ‚-%4$]ÆíÍ܈Ž!PJ€Nä—}•@­ŒCI0C#.èÝGX&#/É"D"2c$b! ¢HàÜpßÏòcµƒ ù`s†C´5™ôbôù‚—å¹Ã}üýÙãÚ~7í_‰7[„§âûn¼R*`IrºX6º]¶ûnº¾ÕuÝß«ç6A;ì(—#.¨(œÃÆ@<Uó[º/)ú Ê£è§àö¦nõuFÔæ ± ©Ñ‰Ž4:(,”À1g}`ê^óC&¿©!”ÎêµÃÙaÍl¦1ÁPÖl‰ƒmF®àùìb¡Ã Ão‚Š{ÿdë$”Û»#.¯€«M#1nÚ‡ª:@g#/¦¡2@û´¬°* ¤7#Q-ʳ{ÀªÇGqïÓM[Fá<P‚sB}X:¯WG×2¤#%og†xnëFµÍuÖíunÖº%J[P¡HÓ¶·hF‚¤ÐŠX€@F4¤E,) ãYPá$´C!ÈøPÇct½úTç¬ 75®¾5êú¥5Ú‹s¦Ú£ZRâA2‹ 8fü›Ð5Iy*Ÿ~M}ÜÙ=s€¹wqåGàf·„ 4 ³äÓû¾fÄE56Æç]7€dÁå†àt‰ð·nÉ#.M4Pßrt‰®nóDT±¶×3jªªv=A;H¤‹ v8…pØI}ñíþ5‚b»% ònä쌶Ÿ1޹[U°E€í*l0D´#1`x #1‚AÜÆ›æ×lðhd5ÂÕè oÁ q¡µØÍ¹‚–ˆŠzÛÙQ "´gÉ UFkûywZÓzw”@¦†Ì*EŠ£œxµêL¡˜*ñ#.]œ\ RlȽ‚ÈSéÆ.èpd4MìX‹H¡·m#m u=šõƒM¾_¤±ÛåU’5æÍ•N·ëؤ‡¢QÌ¢pÌã· ‡ÃŒÔæÉó,8›ÑuEèù•tº[Òªuq5'eŽ0Þãí½íxN…¦ì)j¡Hδ²—<NžÏ©¶Þ@ûùœ»—8G¥ üCóóÙ&·Ù”hŽÐ"2ƒð^7ÖMHñŸ—[DvH†³füÙšdr5ÈeÎ>T {É$‘}AëíqöÍY‘#rì‚ [†‚`lsŠÝÇqÄPXW4ðÎPã(€ºf”E# “° к˜dG(pD#/ÂY¨ŒƒÐ€¡¯5iЗ9xT Y"q‹›Ý•ì>añ¢ ³<Ô ‰®±#üÆÁ#1=€+ EסY¨ƒ¨<X-‰ @‡’„©¸š`œm¤ˆs`2F0S#.­CÍÀØÓDÃB„ ¥sÔ‹Z""ÜKÃ.BqÂn]ÙPÜ0ª„¾ÄƒÆÑŒói±# Ð`”  6Æ1°"wœµ¥s…X#/,#1KD2–çVß5ÕÛo=uªõeŒ2 u)a‡}Öì‚/ÛÎ\áçœvþ>!l?gÒ°üô„¶KËRöER§35­F” áš¡™pãX¹+ËýÅ'É Ú(ucíÄ)(‘›¯@%A-¥y>ÃH®–0›#.šüv»ðdBn‹ÒÖcµøÆ@óè¹ $Á«o†î£h{Ûõ_BK%ýi¡Çɘ¼û8ês?¤:#.³Á';8\KX6Uv)ǸüqohÀÙM-ÍŸ´žÑ}LŒ>†V–´»·»,-,tÔ ›Ó–^ÿ$}Â侎ŽÓa¹ë?4ìté=µåwa"9u6%HrܱýlöDë'oáB& I±EÄRª$ƒ&ï‘ùR#"²,ˆÆl^„Hv#1 (j;¹¶U.wSZ‚)tÇ–Ïf2V.Gɨߏðõ7âþ™¹Rª«Ù,‰Í¥Å)qY’ Ï€):Íôû^OEíåLs×Spïð籩TÁ¦­”"!M-¢×ÔÞëã7•ã¹Û¹ÉŸ¹‘(”¶‹n¥1¡˜B7ùFf°,×Be"³&"Š(‘„”¥#1’è ®“0“2söXYâRŠÉI"‰?àÇD½Æ}ÖÆ ”ÌSY"1bD¬èçÐêº BeÑ«RVuk@õ¦ëô´÷+a ,»›Ð&jƒ¼Y~Î Áõ¤°ŒU¤gD„mâ1õ£ "yÚcý1î!¦%¼šŠÒ=0ÑPŒVYZXMŠ<)6BO33#.Jôê•U7M#/ °Œ?RŒ“¯98ͬ¢µ³RVFV© <Ì{åŒcWõD(ÑˆÄΆ>ÈS†Òn±é_dp6¶k(ã׌’5‹>|7† Œy³h¦6*äIT)GB£ÝÊz SY2h#/P’a'±ç]ߺ­ƒç³æ‰wawBóx¼ºÖ¡ØèbXÀá¬ÀýÙSáßG¤™´&LظD„“[]…±E4iÈîÄÈÅ%k«Iˆaɨ`ÊÚ)!5¬t?/––ÇÖ¸ª3†Úâ½Bzµ¶LÍaJ8&½{š‚P÷/6[$ ;P@[kK#w]iìîóµêiR5²Äˆ¨1D@@FQo6ÏʲQÙte<˜pÖ4 ³otˆ+“e8¢Æ$ŽG‹ •B†Ó؆ª Q©¢<·ËZï­mŽCÖâ}œ2>/xy%W«]Û¡D¦ñ#1¢©ÕKÀÓëÅ Xh×ùÏí9¯½¸ßR@jëÚâ•*$¸òäxåÒ#1‹‰;_Db¯l˜Öû^Ž0»Ã;#Ä„©ÝøxF3cjŒuMþt6C¡7Iñ¡”CKV¬7ŠZ)´ .P7‘CQd TS„™$ !‡aÙ8jùÚSS}2ä®ÑÉj+ÁE€ƒ`7™YÄÈØ[»µ¤I¶“(&¤G(™‡"圞HtÅMö§ 3lÓQÁ‘©·VÆ£&¢S#/Øh’’MFCd ›6ÍËL²Ýœ(ÉÁ‹LÙJ1åö¢pNSY’´›„""lˆ|,j°ÐÂ4•3IU-#/ÐÑàÀÑ £"#1€°ÐݨY«d ¯òˆFÿ'<·Ä3¹ñ‹ç«©RfyR‡]5ˆ),¹¾¢ÍF0Å#¹‹Ýê਑Ñ4){GÅg©ˆ6ã<œÛXnÄiÀÚÿrN ä+Øpy¿# oÓ!_“.:`”!°››#1@ƒRçƒOÖ¨fݱ#/œá®”¸YJb7 œ¾[·tÞÉcyTM ™r¹A&-DhˆtŠ:‘ wùÐ!‘»È°X‹Ëy¸,ŽØQ/Ä ÖÿkàC1â\#/¢A„Þf[9½ÝŽT‡W¿à¿ŽÆÑ3ˆIÒl'µÞžRdMr§6oYÙEØ”Œzœö6vÁ­ðçŧ/ ÁAD„Z2&\Bf I¹¶°PîUFÙ„°>*ªÌM¿M›ê&)·1¯Ê[qrÁlútI÷F\æá®2õ'5£\Üh=›ò}ö¥‡4·®b[îše¶M€ëðlÉÝÊ U(ÑÍí“Ø| f„ÇYìÝàs`Ç!ì›ú‘è*œØ†:~®‡Lù—ˆ=¼†v ,œP Xï6ü}„âžÃi­âY‡hm)‚ÄX#16`—c‘#1“!’ì …âØU··#.ÃzºX±X[ÜÝP ŽmÍ[VZ"HX& ¹1£¬’G1$j˜"%Ž;,˜™”µ¾«c ÒÂg—4Ølë€Å¬Dc†K)ŠQÀ7,ûÄá#1:°L#1¹Åy¹iö‚ðËh[LÍ ñè üÛß™|*$^£ù¾ö©š¼…Ý,ú{ñÛžEýƒn”Î#/FuÀM ™µmÏukãYo L᪶G²?‚76MÚ—ÂheðkµXѨξ®nŽeñ<Õ‹Åù\FZ)™—¿cAži7ùYVÃMG†~ù¢šU6ÔÊîµtÇ›±ùûâ ñ`0#.’`ô|¬NŒ~5<HCò`ˆˆ*B©M¤”XQ­¨Û^5\­U¶´j¿‘ª¯ÛÆÛZ  #.e$(Jqõ}BðôÛÀžº0'žéY¡—ã¥JO5ÁÙ|=Ð[YvPõÀ'¬÷u¡°è†fGÈD{ã1“ˆ%)2S1š¥,Ì•”¦˜¤Ú¤ËIûn¤¦´„ Љ¥L ›4DÀ¢Úh×ÑÛ˜d©1d”JR¶hS&&i™)Ti‰¥KåÝ@FÅ‘¡ALi%%– Ìš5TÔQF¦FÆ2kJ6U!)‚RTe6 ¥Fb 4ÊÀ¢ˆÆDQY¡£Ì;Òk¿mNÑ;€¯~•%þŒÄ30:I°VùÀ&Aò~V6˜®¥Ú%ðq®Ëñýûìî”'ôJ}c8C8£µG)V¶~ó$¾3š ]¼·. šæqu¹wbI›÷òjbÅ#/Þ’Ãû ò†#.r1¾§>ÃPîàÞ¶Š$F~j=ŒKñ7nn1pð´ßD¹cpäæag-Šm[iÙIvmLñ³(%3ß?¼3ƒ=m%OOÖMÓ1ëÛ#/Zb¤þr¦~_¤¶‹ ¾üBñ†›ª YCJÞVÈàd\ ‹¤·ì~«V÷½í¯5õ¶$cM´F¦²m‹DTÍfYˆJaå·>\k­¹Ð; ¸qE¼ª—0¢"¹N‰ÙÛógg³>â=•Sy¹Æzõa äaк´ÞÒ`ØÃBî5Ùá»3/¶¸æß9{È«ÖôúD€÷´Ê‘®çœ÷”P#,&¢»öŸ~\¤ý0OÝ­îvˆ…EĪ!ÝTXWÎ-™]Á“`Òf[7õüÖl"¦ôÓDéѹb„Æ\]ÙC}E飭‡mUYU„íj‰ D@ ؉¦Ãä–Ц{‘Þ]g#.HØdQÇYñûdË8ö0Y>ý.€½žå#1Œ¼~¾ª«Ï‚"b8(ܶ⟋wåÞz‹gV4½gÆÛfºë)kwœþ)ˆ†ØÈd]&L]/ŒZkZ5 ñ˜Qå™m6íN/Üö3ÛƈeÕC]”ÖúõVÛÕßQ3Å»oÏ*ò`é›KÚÉ´/7vܱÆvßa:lr†­$m£ƒ ×@#1êãtöÐ@ÅÐI´±Ùš,\ÛÎu·×äüÎ#/Ÿvܵ¡Ü2…óàSËê;xì¾Srß±ñ‰Þ!õ³‡¥1Fàv-ÍšxÞi[šóK«@Û&Öƒ\“XM¶ðšö)9tûžuóF•jàv­(¨y <6îY¶CèÎ3ëÁ_ ê{ÐæùÝ´yš< B!MáÞ4ÏRyÈ4»¦°35œøªM‘:Èt—Ôe 7NU#a%cKG90L¢øtˆ‰¹'Èî@ñê™ä*~¿tÖÊòtÛ¬öoãäÙçÌ™¶’(ç¿è'iVúö;øW‡À<|¬ý0°¶zƒ×õ~šìÆ?¤ïµð‚R¼ش倳b¤™”œ$i‚‰YÈåˆDÅ×y¥õЗÈÞeî¶·~Í´’'Ô2´ù€[s”Ü”3¥ œµ)º‹(¯»ÁõÁ&t~­ö™kêMkw¨©.ÝÞ‹ûbì[ÝHÔú2¤´n‡L¡Ð· cqÆëØ]FêúzÔŸ^Úªj¨äF_ëâœîdÁzISW†Ã(K:iªfDØiš“ÉÄáƒÊz½„ôT4† WPœ¾:tEt;?€@dD†¨,!ØWY!S”•ÔïÜÙna•òvß0ÔI  éUwkœáÄ’ÀpÕDÓ 'ØÑŒQãZ‘Ž7•—3.ÍF6Ìš¨ƒÔ `ò¸Q‘­q§F÷5˜#/§íÆf-åsî˜cmÍFéÌÇÌÓjþMd’6mΡY#1Άaeí1Qûà¶òÁ{f2`Ãå:“ æTCKÓ\ëX‰ÅZÈ´]o2fFs­û²MáM««&¡yÚÛçN×x&ÞO ¹„ZRÞ'ìÍhÂ\æóE#/z§’u… øàSt¸Xðꆨ“Xqô×½Ò'$LÍDiS‹IíTbæe –•Df]¨²”!‰M).IqF\F¢%‰ÚšÖÈ©ÄXÜ|éÇúÝ'Hy{—µ&¢0´d»ÍoyÚö²²o¹,5¥’F:g lI;uˆë eó¡ûêú듟Nw•Ó‚DòänÛÑÕÝ„„..Ì)9w”@˜©~Ìei¸†%_WhmŸ\Í„ ÒGÛXÿ8t‡£v°DnuáÚësuF‘ÆM §—‡æwH#/³:jî•c« r xå` pL LšêñTÒ;#sc{hÄÉÛ²D:Ðú†œ•,wÞnÖ2ù߬°¼>7'9ÎÜ­ÕW1µ£ZTÊÓôÌe­ØÒfiO–›Š,ZÝËÆ —KG&üfZñRÿ›‡•Ê# v–xˆöæšišºÈS‹†»>M”§RŒ`Ì᪖(ƒ%F› 7üœçÝAéþÁ¿dbõÆ x‹@ìIÑEç*4˦öaè‰Z»ÆøÖ+y3­cs7ÕõSÆûÙ¸©™ÝÒgóB‡C¶ó»¬S‘q\GØ.5€ç1Ç>Ùá^u¼Hºt17Ѷâc“Ñ͸(„ýñ’tŸ“ 9šÞ#/Tç|o ùÆØqÁ$ˆïÓkÕo|×'fÚѼ1)šûÝTµðž\r+|¾5Îu-ÑçNB÷=BT;g$éö¶ææs°Ù;Û½ª0PŸ1ʬîæû-\5ÃÃUäwN~&ƙّÖóa#1Áæ7#/x2f£å®k1˜,}N¨xÊA™†¶Œ…¾Í/aAc˜CÐc;´ ÎÓ0ÀæÅ6ª@‰¡ÆÄµ®2LGÍ–ˆrÒë ‚ƒ*~âÄŠÂ ¦¡:f?j ¦²Mi# 'PÕÓø¼ªr`iYwoœHVÚ¸Š²\Ëc˜˜•³¹{CjbM ™2̬5’66#1…ñ‚dî¸Ô<@?&D[©.Úiz»š¦ …ª1‘å*eÌÖìK ™€á#/Lßdµ&ºC¡æ‰)7lõƒ­TÉ“sÄD­õU³Éc¤[$Ñ&¨­x0Ü"Îâ,Á/´l·LRà߬¶Z:qNKC’šõÎîÎrÉšœ @âA–ƪcwhCF„—C¦„TâÓSéJ~°Î ¹¸„IZ"Ñ iš×mšÔM­ÚüÜÞ¸Ò2§ÚÝñáÓ9ïi³½nó‚/6vƒÐ^¤Æ^¦y0äË+]µWCås§£ʱjÞH§¸iëV_.l…ºbJ©ˆ½¡PtXô¸ºwгC5ä+Þ&SeLŠ®xjh›¯È…ñ6ô>±¹×Ky_6 F„=[9[l;cm‹Ä.!\2±‡WE°a(Á!óYšw¿º¨¼a]#1 Œ\D&“.œüù.2Ýô™tjFÛ­pó Ü#5&©_éãFäÙò¾'sdÌ}ÁÃ|GÃXܳ[#/c®#/rœâ×Iöjã­ºÊKlrè¹I<ì¡4È$b‰É}è·u¼“­êLMQ3(dÌ’™ÞžV©ñ‡„„AÔ ®ž§EI#1×I!ž\À˜sÍ5›-›y%%O6;­hÐÆãça7dt3zݳ4æÌÊÍVð­êÙŒÆ_[JÍÍQ»Ï5#1j°Rðÿ,ñ¼iL(s™~¤Ñnt¨™¨ºª§šUcŠ<Y~8zØÄFÉš}‰µ¾_ƒRáÆøÆd[áãJÛ’#/¦7ep6üÞ5’ I…tmš²jâ2Vqñ¸7.µ3Üf´ðdÄÉ­ÁÚ4qqÓSu`ñ¨Ç$|8-<8— 5A°¬Mé–k33S£I&ó[sn³2nØá©˜Ìºuê3+c³TÁ‰ä˜ì”Nš%Í:k¶³–FÙs–"ºöû)®Ó†&ÄaúCÀn‡ÄC |&Þ‰ËNÉ¡—å ÙmdíÞ sî† ]¤Þ0gh 8;YJjç =Jb™ìrÓŠÖ+z2ÕŠ Î)Ì iÇ'…caዜYL89NœðqjâÎõ`þãœ(ÑÀÌ#1œÂ©tï¥î`r&*3`ÈÑB(±‰)aXÍm0ŠDÊÀ8ãDÄ=|›¸`î{c3R¿¥VˆM #Ëâ{ VŒ’­ Wa£¨tزÕTÓnhLp&%#/8x!Ë÷¡¼6hà«[On0«¶{ñ#153b¹áŒY°‹P‹!¼\=–h +²•$Ï&Ö£²6&íÆzõ\Ëq«¨¡H-¤¤'U)%jÅᙽO/œàAÄë–Í0B-¹ÑìÆå¬,2ÆÇ$0÷¥WbìÒzÁäâ6v€#‚ˆe„.Ü­Ê)‚9$ȧe‹—ãq³’ÈŒ@„!»˜Bs ŽphŒ‡aõ™ •]S§ öVw‡U{÷(<x"ˆ‹Æ©²/"ÛK¢¶áIP$V›¤ÓJ”Ó2É–e†,L桲V<sˆZMÀÙ^•NÉ2†¢˜SÁ–š´Žê#/»8KdÑ6([ ¤õ„Kc3˜…ZQ®Ò6&öÍz͵¬¨P±ÐR¸aoF’"†-„Ý5tÍM™)ÂÅAã­îÎI-"Ú´D×*vC9¶9Á8»ã¦ÚvÂð\ãlT¦™4@ËF7 \«ƒ7`.V`=6Žˆç`¿.”kgzÄc—éÂ#¤šSnÔ¥(æ…/.Ù9–N&Ô²qADg1º.»ãõ"e'Q$teo=9\Ë—‡®ºE±„Æ–ÌM§B7“P>·#yụӇ©Ý¯$ÆÐ8èNr‡‡ÝwºÚX£”,ÛpP´3qµ^q³1­(ÝÌs;-ÐæçäÙHBÍ‘œÑ Œ âÔÍÄ$-5-³VL»ËF6¥‚`—¶VÒläq€ÍdÃeø\uÚäÓ Xð†à †ªƒ‹ÊhClíBl· lb7µsšËÊbdÑÙHäLøà#ÓUû²`ã13— ÄIÛ+LE@Ê{W{á=`èr^L2b xN— Ë‡*‡g€0ÜìÓJi ¡Êàô,4®^F!7x[Ù6B˜E ¥ ¡*Ð4— b£ƒŒV$krï#1”ä Y1ìw¨Îñ©˜ºÃSaO Tlïä2e‘Œmµ$`1³„¼x¼~­s †\¶:ñÖ5Œí6ëÉÄÿZ¤c<@‹Âݨhö:ÐV0`¹öÀ¢<lTVµ´ë¥€°5:ƒÔ2lcšÁ è~f*¡˜ò7Rár–Á@!¥¾úyØsˆ*§-w‘C!D8šŒVdÌ­&5cp²iLöèÔ±•À*”DAÎQ`jÄHÌÊÊÑdœ¨ã9š…‘ߺò*8#ŽYÑ/†4x#„ʪlJ“¨Ð )¢„#/ÌBÀÑØèp£;ñêÎ((¤É.fZCHÝÒÒeÌ1|c±6íJ܉°‹#.\´à]0œeØPìÎ.È6f{ñÓXä„ꃖSS#.æ9™r“3#$) Ñ¹˜ê`ܹCQÑ0mÈL‹l90ºlJGDB­vÊG-lq„¸0Åš¼Žð0Í»RI„¶ZÙÁÜØi2¥¤‘ѧ…#1’«˜\7'Æ©: ¥P@" „#Ée´ÌÚÕýI´›ª·÷¿MÚÿ&H¤"¢m‚&Àx³;À(x&¥å= H£¯ñ5:~¾!ðæáQmFù«íûv˵ÀÑV7w3»’B#.‘ATp>‘þc Ì`“˜*°ÄàÌtn‘OæGœm„Òj$º…àTXªÆDaœd#.È#.Ê»‚“#1<à´øe¼Áùkæ4°lÙ ë[ã¯G•Õ]n<hœ¨NÒ¿{_+’G¦p~©Ým©:„ï;=̼8¶™ÍÁ¬ÊS5£ŒdÝû i‘iJ°ækO¶–ÜR¬7W£ž[ÂŽsiÁ³f°Áð2vªß®‘ñx3™‡’MÄÛ7©÷ƒ“× DÈC!ÌFÐÞÓ†‘–xÜr ð8™§#1NYe¬òc!#/Øw~¾@„uë€Sîx"}ãt#1XFVL^Ç›/S# „I ŽðxŸóò¸n“vK@JÙ·IÓ‹ÄòçuÇwžW.ÉÈ1‚£—é#1#1"–€ Pª\ÑP(l¬‹!œ|à›l7T ‹|WÓšñ3¤2ìê|ø¤LÄÓ0Þ¾”lcí+PZUýÄÙï3pH0¼3¬Û­áï÷ìò@sCÄZ;`&:|Ù/j¯È *ˆï›p1$€‡²A~ê…wwvݘÖë»2æë¦íA«š"õ%ÀK?lªˆ°&ÈR¬àxúÐzˆ¼»G™,ª”?Èu¯úˆï”Çr‚ñ#.xq‘úpˆ!ÍgÌ䇟>€|/8áÛC™#.«R&FpE>Â?j»Ê4Ð}cV6—)2.þ'÷ˆÐw9?[eCߣ¿ü§Þ{.<’à¦Á/º|8mÚ£5ÓVŠôT¢*LCŒŒŒ±ALvƒi´‘Äú#/̸·Ê÷£_ͳe"iÍ&;#.‘¡M1(·¨ø!Šá°QèÕj,¨Ñ„æ#ˆ„¸0ZÖØˆ€`pýÏ–CœXv…"%CQ¥{ÈÄ3@ýäGPùWçùÂÅ›•ÀzbbI‚Úâ|Ï•}õ$ ‚¢ Œ$RE˜´khµ“ŒJFi™hÔر¯øÕ®ˆPhZ€@ò¨Ÿ2ðö˜òÙ[{Àñ ‚–íSvëöx­&A’ñ.ާ|6›;Ä=k<JeJ4jââèDLW…ÄDvôµWUÓQ®W;+_7ou– ÇM½+ ÍÉ<ÝL•¤ñ(#bÍ4¾–i‘H{P±Ÿ¨ý\‹Š<!’GÂßL™qÒt" 1“Û=(µÕÚOê»ÌÀ„{¦áÝ ÕŠ~ÛQhˆü°hYo#ö!ã®àÆIØP()Èé¶ÁiI‚îÚÈò"5Åi]ÂGGØÀSÞqQ-0Û$.ªRG5´˜@…0”$›²`L00¨¤!-’^…afR–@lÓH­#.Üb­.š!ä`Ø´ÀdsàfR&lcƒ8®åB+ç7Æ#àv¥£>Ò!¦!¦&4›m´H•Dm€Á€U~Ú[ãÆcEÓ¬¹2¥ß~ó'kŽj„#/QŒU ›Ý4]XÊD—T`ëÇ~Л¨2”2†HTŽ­R—#ÖˆVº¤m#°Ö2†™}„ÖÈÑ×Bç3€`ÙW˜‚i\Ê^K:n0Ñq› º8¨cZšêÌ8äa#.XMN,,šœV;釿7§ŸãéОpï#AMÊ.É ¬„!f‚R#1¦ @ÄE€°Tþ#.é¼KF Š~RH!k#.¥›4!xüö]Ñ#.3UA‚@CCšË1Ë?MUª³¾^–Ó}a˜þ½w®$mc¥¿³1¢´4iŸ³q#18ƒ[éÕÈÂ-Íìà­¢20™vg]²gÞRšr@˜¤ÅÂv®ïØ›#/°¢;çÚÀ¥ÝDY£Q$ÙÓ‰u Ø4i†>¸à9hØ-ÙñÂÒÒ¶³QAö¯åîg Œ‡=pD«Ô*&vÙ¤@…/…‹Ÿc0ø˜J#/LÉ#1±!ÆŒt+ØÛ«c{”ÆSX à>…&º'.ç+#.¶ÄC§ß|A¥±o„AQý:N¦ÕÖÈÎRLr;v:Q0îÎsœ>L8f[ A¦s8ÂÂD!éXšëM/ ±ŽžÍ@Úò•öÂ%H¦SR.Œ…mC§5——já7ši¾¯šß7R8Ác çÎK»Ö=‰©ÖóH³Œ³µoLhBÙˆc6#/7,ôCC!¥P¤ÓA6Ö,IÚÃ0ÐH`#/K-#Èn„##1L‘ fÈ K’ÉÁ L XI~’0,H*„AK'\‡(Œ#/A7#.m͹£¨¦­ŒØH¹Ký#.#1"#¼ˆñÛ¼,=añŸS_Y +ú³&~UršqH¢ÄöP0a¨þõn”Lü£¾P*ÃìóñIú¡ðTAšáŽÖ¾­Íc}¨tþ¡ÀoЉˆQm¼˜ÍuÚ˜šIDd=‘è¾¶¤_U •ôîînçf›ïÖ¼küõ´ÔlmE¬›F­bÛ`µImµ~q$BD„‡­òS4>¥ ÏVËÉR±5•ª ÏÉÌË·Þ²Ÿi´ö'T+½[;Çטš¨ß=,¥¥÷o•ýÀÝ&°VâÚÊÄš¸Ž¡‡*ÒJäPÙ7¾ºpÖw‰FÊg¡ia^e…8òíÖ8ûs7Î×fŽXºa¥:ËF¥¯e(Åt¥444“lÃM ˆ…´]²«ÄÓ†EÃÅHT·„–3y&Æ ŒÊdz˜œ9ݱîÈ5Y&®++oz¾HÞV)§"$Ï3Ã6d/l"qS&_[":úkz$LãÄç›h4G·F-³¼L³Ê[”¡žƒ¨ÊV³Mf2±5\øë6pîDuá–ž<aãNCÀæù,+•ÃÓrèÛ^NAxg•Bª“yº¶j‡#.:ÖÝ%³±Q#/³šÚ¡êi÷¹‘Nî:”,’г'Xt+|:£,ÎϨ̬Ö'#1v¹$›Ùºå¢pýêÞ6E¦I5j+¥n³ƒµòLe .ÐÚ²I²Ç’AÜP“åP÷CíLŒ6MLŒ‡7¡—q§Å£l¸`4Lð­Ã5DbÃyF81¥lÝ)Ad‰L#.€j-0#’ Q¹e‡×·Ùq‰iVRÓ e%0âEoª&‡d˜X%ƒ1à`ÀJ^tQ¯^UâÛFòËjÙ±°,æ7#1#1Q ‚$H#.^#1‡,h]~r&ÿuÍè/¹ðPzê쒪מûœ›»­tÚšîÑv[¥FÅÝÕé'=yx¼9yÒ¶2FáAÙGeU¶2‡kwœji¯t5yæ·[Ëf77dÞ™wzî±]ÚæfÓ´óuÞf¯27+’…n¥nO<¼Æñ]ˆH¤‰lÊŽ™)8pÚ6Ü•í·¸ÚÛÔ´e+LÛ2B”¢l¬m‹YRe)eåu®–±M¦Mia›6ÚŸ_Fß?¯FZ-P`±©‰F¶’Ú£mP•¨RlÂ-ïõúŠÑÉ:Äv(ý$nk‚Q¬K‰{v±Tkg ™ÀªKAœNMl¦ÑEmë5Ó]ZÛWÖÕk¹<€8Á}Q ö'¾ÁíÏÔB0¸ë§ëa@ÿåË Bêhø¾ÌÑ<ö¨|£ { "IÄé`±JŸPOš³,¤ÌÎ^C‹üôÌœ[14 ¶ª)”ò±Èƒ…§2ÄNÙU–™mÅFIמ6;£pvX^¤Úb•9'Üþ[*c÷¥Jª¢ "A²‘ ? ¦ÁfPFV$¤»v<BwîG§-â&·äq´™’dÙfsûö5­AÇ#/‘»~h7Òոȑ7L7|7Çês,Ø #1zVÅ­L¦cã½W£¬P‰.n!¿Ž¾ßåz­¯{SsÅ„ð+s±Ì3äc8‡HX2%lÏ$ °{¡9{žŽu^ º(<d\ˆ«7.8ÞªV¿w–›,o˜€Þ•ñd/ÐPꈎL†®[)C±h"dý¬²!+íŒîϹB\Ñ<Fˆºäœ®Hg3‘.W±‰#/Qªÿo¦#1 ^ƒš¡<}€ï‡öêK^<Z„üîêJ´ë¡Rlë“o´û ذ Q "Úú}¿Y#1í>'ßÐÚÞ@ã ¶ªô^â7ú鹌Ê5ðý†Eˆ_½4jõzÛ[zeéÙ5£ZCø;ª*É&ó®šhͤöë\U!)7Ím2,ÂjI¬“Ùå+k’Íe±”¶“a¤ÒO¦™¤yÛ¥šdÓj)ŠYZH‰}­u…#U”Ì6[41,Ú(µ©ª¦‰éÅHÕJ\ºmB_N×5×Í^Ýv4TÄ%ÌÕY‰ªõ:•i5±ic*¿u¶Ýµó;"ljõݲM‘E¬YY6ÕT‰&•M­nnÚZ,™m쮓Sm½zï#1h›M¦Ê)…KXTZ–ͤÞmªV£¨ñ·EfͼêëÎäÊmiÐÕü/óiªMèWlÕãk¬e(•XHÕxMrb-K[L&%JA\þå/ íŽr‡ùÖ‡‹Ù7^{(>uŸH}¥&S ßß­ytÏ#1†Jª&ªàÄ4èGð¸¥É&C¯ºi‹Q9 ý—ÞȈ_áAïm4ÃV‚à€{Ò’edë-K»­¢ˆá5B;ó €a$­òVÛ⥶„£àéš–›JÁ# ##.¡=üwÌÄbsT…¶Í+fš·»V®–¶*ø»5š^n×)µÊ¾~ë^SZé¨ÛnÙUÝÛZKH,Š¡P8Å1[’) /˜v¦”î0£®ò”GE ©´H­E’Ûe³f%˜¡Œdd;¥tP”jRj”Ö¶›f̱¶“mCJh¥Jmô-Ê` Lµ•D´bRR4¤…M²”²&ÒFI†f2ÆÁQR‰MlÔR“i2%¤hXÙ*5”SE”²›¥-ˆ¨©M&T¥2&ÍŒZ(¬i’ɤ©%1IT†,‘jQ6£k6šT… )1`¤ÊL&™&™*Yj›c¢5dLVŠX™µ&IVÚ–k&MS%¥6Ùf¶¤Ö­|u­wj“iY«Md²Ù#/%¾jÞ6Ûɳf«•UÑJ…Arˆ]drdYB•¯lÛX¶µóµ¹ªRØ5¨µ´‰­PÙ[_&Ú«š×¼"¸zÄ5˜ á‘z¬”ÈuÐ YÙ’’>Û‘·³¸RÇ a¸éŒ™ÔWËL7ö3ƒÝÒëí¸Ëþ]DÚÌ£ágAþ¤È˜¥»sÞ[P4©Rfy:ž~!k ÎõTHÔ-Õ7Õ^¢HCçÈ2ó¿ˆ*Ì·€!ñ‚xú1„ôCŒvDz³ÅÜaž>noRlâ鳎ÔÉhsµ³A™~lŒõ]*›ò9)Fí%AZ´µ‹®¯’ãµíÛ˜Xÿ^a”OF-ÊúÎ\p@èSø›#1!"„—†,ª4Å:fÕµgU1ˆ‚lk‚”¨QQTAÂYô€{$™ìùPçó/Ü÷AŠ£è6ì¹¾Ž¹¨m‚uu*b‚¥~ÏùõÝQ<´Ü&øšCädS‚lë–&ÊÓe¨¹‹áçò ö8ðlblÈ®(ÌÍ3¡ |!l"cÐŽhq﹊M*†ÃiØØ§´‘¥0à¹Lä#1¹Åp³ò0Dl£±ÇÊ=Åá·Çœž<Ocƒ9|@tP& wêtûSqóÁÑF~ÏÙ¦¼G¿Hç*^“9#1“Lª#1‹­Cè©r0xU:%-?táxv 𡟆øØ8íb,’3³z­bœ%©èXA„¸wvÂÑô:›mŸ*6õÖŽ&EŠgk˜}<FÆÞí”q„s»PÕµEi!º}atgY÷o„Ãg6œÁ~EèD•Iy EíWã²-_W/¥ïëmíÚpcR` yw#.#. cm‹jŒožjî»nCÔáìàw‚!X"ÝõÈ@*ŠT‘D¬‰Û`ë²é%•[ßivt-a~¼ëH ÷Oòê;P²+ȼv¶ƒš Ón‰uháټŅóK—¶µ»áiÓ³FÜéôüÌÚvý<oÅ~ 0É2H'«@äa8y¾rêqìô†Ñ¼çîlz+³w)ƒ#,{ÇPؽ¨ÊÊÓ0>ïw;ß|N“½éyb\ÿ_÷‚Ør® v7*Å#š71~Ãד÷Þ{;9ÔWªXÙLjñ®¥â<my4_¾çÁ¼kZ•–ÅÛC+,‰‚ÐçQ4#/5§Œ) DË`V؈Ðê’LÕ…ÛEbT@X(Á+&i(ÆÓ€8Ù¦ìâšÖ8«@j©ÐùÅUcg5&Ó•Ú¤#9°eA@Ä@¦KKq‹,qKnˆc#/B¨ Š•Ût2`©jÐóH¡ú.Œªg¤5Å#sÙRh‚0k°#.Øéwó@‡²Öü‡NZH×ꃮ–hâQ‘N)­¡caìØ‚x 9èù!Ã}PhE¡2Öã¥J¦²­ÀY©ë“Ñraq‹@;;àtЩ]ÞÝæzœ\;œÂ±i¼E@u4NãTF99QôØ×WÊyÎx“‰Š3ðÄ*ÅâÚ–Em ¬n#1nŠ'~\4ëÕ[{ŠC¿V»/E䉘ß\úC,w*ÆÅÎÛ÷¸¿¿§êõì+ºÆ“¶i{äj\GÆÔ†Äb#1V¸ç:k#1òVÜZ|]j¬[97Éfó_#/‘©ç-%̯ͺQ©ôÇ-‰¥!I¡ž6é‚A^íIfȨJÊ™P!+~¿*8÷HV3 or¾Å†ÔA áwIy#.¼½¤oÉõ“‚õôåAø½vœOƤtÑ\iƒCúìÓûHá€ò ?~4„sŽoã¦=`†a™ kµ€v ¡I i#/¬ôOàZv~­‘ûÚ3»ûõžÊg@acÊKPàb XÜ`-­ap ÈC$i š””ž-DVCCpš€P±Èi“(£å3€Ü;NçÏÏ_Dì—1™Ó²­ØNvZ x…áX.’e"gšxQȵãxǘdTnÇ©‘º ö÷Ã5ªA–J;©Ùó¢èÂ’B>uÄ.1µ‘ÄÆq¢^=&ˉëp§ÌÓ°î¿„UA´öq ;2¡UKÑ” ÅâÔÄ©ÑFœñ»£¶1õHÐt"#.`41¹9(½#11ÜP«ÚQ48ÙX•&@ÅÌÄ ”#w#~½ÆOOgwrCÏóíTšPåœoEÂê¨4x,y§o4©’u„!€²ƒ$_Ô@A’¤A))Ë‘«jžýj`TÃáÒŠÛŽ’²¡t:ñÕf¢15øSsDÁ¶úÑ!´GN#.ˆÙôeéhî—èâùó]Šp´UwM#kd1xXuÖ¨ÅÀš1yã"×a˜öAx\#/¡#/¹U›$VÀ‚#1q´£ho4\a#/`E>‡#ˆuöR»x*±}”R¯"ORõä{;ÎÝÖo+fw*wI3ïq=”d8`í:‡QE2ýJ1uµÊB¯~¼¶§–×]j^çÚxaØbÆÏ’žt©¡÷ÌK‰ݨx¼ɘŸgӂ¨q.õLeÀ†öA$B'-(Ôë7‚…ˆ¤Œ 2=l#1U^ ˆ”^o.XµŒ†7µÕêÛ˾©Õ”y/ç{8ÙËVÚîeë»Î=ÂFšmYõfÕmX£NêGôõÑÀ½|·Æº@A¾ v_Þò´'J¤áTí©7C,`¬áEK¡DùkЉŽ9 !lM°aZ#FÓÃ&ë»—G›§kt–isÏ:Ðxžyæwë7eç\c©]wW¤¤lŒá­`Ü1ÄÄÛkÖFùÔŠ#*ªdÒ€ƒ-8 ÞLYôöw‡¯h%{ÃÆN'¯8Nåº2ù°*2A;õUÖî3Þ'p}·Cf|øqëí­›Öƒ.-‰#.Ú]ˆ×v·ã#a.4W÷[fG+Xl`¶x*#%}ójØÁÈ=Eœ'1.~Ý¡ÝÜ™ENž¼ õõYóÇDéÕËÙ³¼J¨7¾Aî¼ìöå‰íä¯7¼Sp(üZtÊA;}zæë9gViKø'Õ{²` ,É=ý´‹–£Ò¦ÎÑ™EÃô¢‘-¼3‹&á‘T—TÁVFSµ…ofN/w#Ab€À³ºå–&"bÁh84ƒÉ""ï"I66$†2´‘·HÉWWŽÎmõ7”‰”É 'yå\4cRDÕI$7#.8 …‰ÅTUWž§’‹½»«Ç\ë;·2\±ruØëÙ+ΚˆCQQŠØéHI¦À}û{çs!ÌQá<K©¿¿L#.m\mÑê–?·w!Ñy(ƒrèxjkrO.ÇTzä…™RׯiØÈ§k]´F Œuib0>Ba<`×꽯ٻ՘ Ù¦m¿éó.MöÀC#1èÖ:´jɽVFaÿš„,Z9Ý2h3p€[ ¡4@`Å„&Q÷wjµx°¶i¤ÓVW]7É^KÅ1%‘chÕ×åm\Æ*#V7Òµ|ô© CÁK@¨#/õeY€¤"*Eb#.7‚4uþKØ—$¢2–M\ šÂx>˜¨æ…™OÓ.C.»YœÊBÙ"ÂZ%D(@oSÞñHüX]¡Œ"ázk#/Š/˜Ù¶Æ`‡ö¾õZà?S‚£c"4Ѫ!`¦ °¯ý™šò†“–ÃQ¸Aaœ,‘¼Ðàí½rO„”T6 Ÿì‚#–¨¥boÛR„8ª ¦s?Ö(*ÈÅã°©új•H̪§¶Š…],#.Ýì Ë“ EA½ݹÀéçë¦Ó\Ðÿ&ê—75r³J»ºMLF”RD ’‘V&Ðßðá+Ô;õ2nìºqQp‘ûÐa˜!y#[ì\Ã1 51ñ’ÒÆNDM#1mµðå,q{¢Œˆ„E!ÜIb•ß û¯ôO¯›ïrÁamYÛª‹ Ühh0‹ #/ÄÒÝMåîöiïßÔ‰œ(¦~›ýÿ÷0‚áEÞétÚBЀ“¯ãò7c#1#/#.ZH–ç¶%Çu³W#uŸ;z·&Á¯ˆãÂ_ªFdñN!Þ(‹$Õ$†„@ëëî×h#1xÂ¥O›eÆ#1&…I#.ÎH/ÝÈœ •úýÅê#1OĽ@ˆÄ#iD½‚ŠE0 Ä7\¦ßæ?k£]xI‡XãÞ£°ÄiÄ;×^`…k D2§åâøé#1¢ ¶¿*ùlH‰!^9òïTâü½þä3ú”wÃ1¯>È^mÙ¿ù å×µ®êìCaŸ˜> vº›ÁÏ”)ª)±ÌôÐüT>¯X{/ö+ÿZ'¨‚)ÆÄ¡T´‘#.OX¹µÊ«]5U¶JæµQ@Hh/lY€ v PD ‚È.j#.SPÆ<m̦["7̸ÿ oÝ7c¬©]+ÚKƳ!bB‹dzLn[9„LŽó­:ϞǓڧèü»‡«h1wÉö4ƒrzSÜ”tOAyYÁXOànñ‡T’@AXT¡ª ·âY7åS¸Ñ{%ªE aH®ƒKÌ ÷¤{~[¾GÏH]â•F$½}yø/líÊ·>ú #/Ÿ?¶F•ªfpÁÈâc›Ðö…Ú#1Ýçzù¶‡DòÝÃê‰æ:¶½·–úf:è‘î#/9eß—§ÄüP‰ÞE@ÁÝ#"#1X"á$#/a·`ÑòÚÀ¡¢~¸P‘GZÄ7©þxi£ëkL[Øÿ‰íƨhæ0j¤**…PS!ŸíŸ§2 #18Ò+葘ÐV«h–&èL_ÊËÞ‚CFa#1°0‚QÚ?èÔÆÿÛrÝ•#1@))$Fêļd#.mfA¸ErΘvÈ’ÓX°u½ð@[I#/I1°R±@Zl˜sK@a R!v'ÌšciB8¤ÄÇH64ÕYbX<f1´61™Y+5 ¹éÅ‹Nê®m¢Ñ^ÍÍWÁµkRJB#1±a".sa-5d§ ÂbIœ,` q¥Pqj%D¡F½›ÅI­Ëx±·»jï:ôªÒ™L6ŵtè„&5e¤Ñʈ¶Ö ™Õm#/Ò6«HTº&¥éá„Ùh¬1‡-¢ÄÞª²¬ »¬ƒ6Ñß!›‚#1ÛM¢ŽNÑÍr×.m£i]Ñ¢¢ÑrÜ·1&+Îï]tȪ²S%¸Ö¤Ñ3Y¥Ù½Q5LjÓ´ŒQÆÒ+Iш»ÿ§Y†™a#Mðâ\0zFÖ?i¼_ mÜ‘c,q‰º5@€áCIîÅæË4³®-‘b¦ÔBRDBÆäÕgzážÛ'÷ç¬F± u‘P2rüMÔ>€>š[m“ÓR`B扪~ŸD9;Eíi_žüíºgó•ÛS)–©+1¼uUÞuÚl.Ù¦ºÍÙkÖ±>1ü))„éHd#/³õtÊÜN\½ï_ÎÞ:*uoPG£™/6…POpqà~áú§ß‰y“i\ûjû-ðµflke,šÛKJÔ›Mmôª¯R­§q¨Ü¼Ûx¶ìÔ´ÙLXÓjòµÇW';%Ët–ÆÔîºUµÔ¬kQ£j¹f͹eT–›M¦ÞÛt¶“-eå„GXÜ@X b2Ô© © (B"~%“‹ö!üÁäXwl) &"B#/#._‰÷½|˰lAbä꫱ËËíóïøùåiîZ,`àå÷z$4=[ Ó2!‚JQ¸ÿ®*¦€?;%mˆ%"ˆ(°eÁ¢6q›’(7]u|U“ã)ÄïåÚ8ò OYD¡ÈmBƒÈf¬˜CTÀX(a!?Û(•&AmËrù7•ãu’ÖHÚM¹Ò™R)µsnj¼VûíkË/Nˆ¢Ù5¬¤5QÓX±´@#DÂŒÈ(šÒÍU¼myœ£F,Ĥ’­„"E! eBP‘T˜¥¨ |"2<ã ù÷qŽócFêh‡ùÊq”ÊŒ#.Bã}¥—IùÈ„ŠFQY‚UÁ4$3$ù:€2C#"™# ” Á±”e qV„…¶æBF„ªªªô¨x´;dp¨›BÙ>eÂ1ˆÁަíü €îŒaìó» óì`âwBXæ¹”ÞG!=ëð’ð#/Ê—Níþ‡§‡½" W«x½›M>ÓîÕ€=2\'wFèQR¯ÝÏ»·X»>°}5‹åÜG%õ‰(Zw B&Úm³Šj–L@Ø,eÏ:íO®/tT¨‚i_@b⎢ŽÛe'0ùoTN®d=€‰”–°Ý˦Õ] y„Œõ%Å<Šö‰ h*D¨®4ÖE¥)j5Ñ&Ù¯y]TiZÍ6ËîÒïi]FÐF&h¬2DÛvW¯}ÎGiÀÀÎþ~6L@K£Uß¾7¶ýCT ! WA˜{ö—ìoãÞ0ü’w]QÆÇ»î/Ã&b~'¥ÌãR´£ið+wôàj“1è°úúM¦“Gk¡†´ÌãuÏÞµyL¹Î*/#`OnÏ„$ µs"ëcÉ9d˜˜ÐÊFÑ% 0ÆG{Š Åchi¼”dÙÓ;«Tî0B6`k3!‹)V\Õ*STÂÛ""˜ŽCçì`ñô#1'¡=Ox*@î$’IlG¤TöFåÈdäm='™kz·õi‘—·ð¾Î¤o®³QÈÚ&0kºäç·|–2&·Œrˆ[©»-…¨´Ô!-™?ÇíäB-ÎI&ÎŽ‚?©z™à ê:J¹6sLGî±À¢…¤wbM£éõÅÓEu«k‘Œ$ˆPþ“¬F#Rš!1o:àŽM-tÛ^#/°ƒ@H¥]ŸDiB é"8@F#/ƒˆ09ÄÁ(Ê¢ã}a‘GÔžð’¿©Î#1ß­Ÿz«÷w«bdˆ›Í ÈLòä‡*}’ÆýcTkÿL,0>üò¹F3³LþžßèÜÑ]¦Å r›F‹ 1ź»ë±ökc:“ë†ÆNÞÁ>´õª±÷¦Íª¸ê(|¾pÙ(‰-µ—B@ŠÈ’$‰“"¨Æ²V˜bÌ©¦b¨Ú¥U-M6$Ö#13¥5ù¥v! ±™´†ÒÏÒÚܪ2V-™•M"ÑBÊj’[RЦF,UM¶Ê²Ñ[52Û*UMmb¥TÖ™DÙªÍMRÑUhÖ€Œ´„4Ùï[å?uµ7‚?•úŸ¤²udð£6ÄŽê¶{0*àòÌíˆu~䢢¬‚£A*ÛE¶Æ×SZ×-»3Î*ó#1Œ¿¹ÔצÚ/§yib'à¶Opö=œ´Dä t6ÄjR¡ 6,dê_–׫ªö«_Y¥¯çËm±_Ì^ëŠûwPBpÄ¢¢æJ„ƒ#M0ÁT{P¤K>©@†–0|&GðÐukPŠB|Þ®GK£¾ ‘Gwí¢7‰#16ö¡çM|Óø†IP@J 5#.OÊ€<#/‡½9«¸?s2>á÷tÚD«§ߪßÛ Iâ¶ÔXÛoh†ÞK¦¯Wt^eW&D Ú–É !Ê•=ÈLŸ@bí¯bØÎ<Çõ|9lÓ†¼#1ßsnYºeEÁn2vº&Ä|ÐXy{*“Š#/µ!³Ž#.¾AAtch–1Þ\ÖizÌÌÁè8Ç/Ëúk)¤s#1ÂØXPµÈ…q>›ÛÜ}8‘<@ªbDfÞoP{½œíëë¹^Íì}°š€r@áRP”rãÕÝÚŒ›°Ó¤>ƒ‘„ëDC wï<†"#.AȲ~¨lÊ« ¬#/ˆß-•`as¿Ã#/VäŠZÈÁ#’àüQÓ†3F¬'¤EkCQ‚Cp‰µ‰‡ïÖIÃ8æm•EX¸°72µz^_§Ìä|ð§ÍgºïþF$Ñ‘ŠiµkñßC9#1bgU•}Ü}‘OÉ'ÝÑtpü†[Q±M=È88Ê'Qýø’À½[FAD0tűŒGD"È(ˆ¨Hƒ|qÃùmÇ–y]¿©ó^=(„„øsY[ò;·äèùæ{T*Î,Y¦ß¼åêÀêÀÍñ )“T…SB`»!`ŒVRT¤¡-#1*"ƒâ|¼M1×m­ñ¯Ø]òo^]ÕìFe,¬C ,R,<‹ÃMO†¸ÆŸ†ÖkG":iV"[~Wþ¡¥›5ˆ>-ÁÉHP$AHÆO(†´RUÝUÆ!qº#1¡–U+|kËÛöÜaq c|E=8 å…°M¤ÞH¤Ó— …¥²#.Á„ (^µCÀÁ2BJî(ÆÐšÕ§Íqo'cßsˆa¢#fL|€³…° P]Þ†Éb@/ÓJµ 2)Ë:KÂ1¨vÚ™ü¥:ÇícFgM$LPû`®˜(@óæšéR; Ué*<ýÙÏß¡sÇ2õI`ŽD§³¬Mo7#/©É˜!&Õ#ƒÌ7/$Æ 4“©Õ¡/\7on†lyo9i&s#1bZ~Ñ @ïxž!(H QɹB–UXÂQ#1J d.,Q€’§d°³‚‘ÅO4ÎÍ«ÓåKÕ. ÛµQØ;@0~O2” ûz#ëw÷ù<j¨$BMйLDRQE2QlêïY¤õ²}pþPÇ‚žÛ*Q¡®í®‚Ñ“@­4Ág£-ا0úÕá£Ûæ½æÄS¯CRôvü­®¶üö÷Û{x·¤m®&=ú舌ˆ†–UB1 jâR#/!¶Iß-ïÉí»ìyᨅD–ª|hŠÙX—@‚Ȱ‹!ãaqÄÐ§Ž’¼¹L”…Þyæ~Iy"m²°BŒàÕ·”Y>-q"!‡Èª‚€È‡YÐÆ]Új m3­˜ y '/KG š»”VA¨_ÒaƒÑÅ4PÛ¤$mŠq£IÎ×I2œw•P3íòæ™ØË@ìm÷‹‘c‹¹E}ÓÁ¬3@ñÄ©ñ R¥´ä1Z”¦@æK|]^Xµƒ„Ëžô²q›Ö®V•à¡T§y•ƒ)"_ãjý°ã ͤ&)(¶Aó+b}Õ¶b˜Uh'‹ìe¸I/’£#/q\™!!l$V Zê—ƒ)(²ŠKS*‡K+¨Ç¼Ž’AäjYGï’¢Z'±+5žuÄü*½rT“‚sª“#]ø;oÁˆ˜4#Ï^]J¢‰•œÕ:£h|¢®ÅádôaÒ(Ñse¼D€ïÇÕôY{ß`;G²Õ­ˆÞÒ–%?fxòQ€ŒÓçêš(vŒÜlSõ•wE&‹†Âa°©4!¾0Ö› NHÁ#.à‡(,YFã+%5a9í*Šq†­!gM·÷Óͳ£æöíƒ%N¸>T(ªÆF%U<ÌR@D¤QµW¬ÜË©¥Úñ·›-±Á.Y,Š@"'aŒa[èñ(;vS §¸€»—\nªˆoåß;~Á#/ÝeÂjdr8r(2SÔeýZš]ášG†¥8Ò+^Ížìô™»ùgÞ7;X¨duq5îö\@YYDêN­;PîâŠC–…çǼùz“rÊ:–[Â@–’ vAï#.›Ÿ¢)³··Õµ\öï<4Ì›7ÜwáœM¤Eõ¾ÿ[³Y½FòÄ.¯Ží©è#.ñAŒO{CQ$|c$¡C†C È–Ó2b¯ç7^y·‹Îv×X••J©-V“U–‘Üê-§[måÚëÉ¢ém/:‚ÁOÏòÞo8P[2Ÿñ¸ŽŠ¹Qhz ¹(6P7#/ÞUª¢¢"Gü ßÝ%þ¯’À­#pˆ#1Í*aD5ùI⽞-zrÜÞ›‘RTonégÈL“äœoçí#1nK½·¦²#/å.™pÌskm@…°ÃÁL ‰q-‚ƒ`¥Jâ1rÌ ´ÈÊÄ6`™rÈ5­ÂØf¨Ê²S„4ÛXE¤âŒbið!Àd`ÅQu‚XÓΞu6©7ºbÀ¬é&qB5¢#’e1œÙÓáÖ›M­²;w‹#/<Co1cÆm‘µ$ #f¥à@Æ…ýÉ´è$DF ðÒðá’Ãaí1a ‹êcÒ#/.È€oíM Û#f)„B%›”lÞ¾è€O#uR'prW?S±d&&Û)DwHÈ Ža4êÏzž<¾E†ÉU¿ÌG Ÿoš––.±G£Î ‹”ø–G™zâ&Éþ]îþÛ7ħS‰¢|òN ƒí¡)NÈ$€È逽±O2³IeF²VÄËFŤÖÔj”µ¾åÊ¿"ç6¤¥}{Õåj~O+ÆÑoU͹\5dH__Ûø¤FN[ÓóqÃìhéǦ唢çÆÕ0u}AÏz( =}ö³VB£~KAØvc¿©¿ŽÝÕŒc‡ÉýÂüðïꞟ⒠7¹&Ƙ;©PXÈŸ:›k]®÷Ž™giÓ)ŸûòuÆ3\âb³lþ©Á½9-ªÚ[s3oËl݇½oŽÆkq´ÂI·±«€Æý™”˜Â÷ýVøð+ž>‡F³¦ùo¹‚Ùa”ÇËp:#.­„Å#1&ÂF䣳'–)1{n.8×M:ÍŒ$TcÇm±å×€Q®º ÞÝ\»ºŒ´ÙI©Š@XrÏI¼8o¹at‰r÷10áøäµÏjoàóØ-¢™nKAWèRù˹o®˜l½>'‘Q½Býþzðq½¶ÝˆÜíGž~ j» "íäq­¿äK&fG±àÉÍtïš;¯'NícésÇçY¢~ìYõ°»ÎË»ZQFV¦´àxú=ݠ/¤óá‚1×c 7o=ß¿· ñ‹Î#/#æÀ¨ÈeͦӘ©¡¥„1^”n.¾mG¼Ìtí¯¾/ÔšÎ#M}$æÈÚ&ùfomŠj6WCÁ•œ‰£ v}©O®…f55véí/x|ÝÔZ1dG,¿”¾¼^‹’Æù×ݨœOæO°dÃà š„~ ÌB}ðRÛh††÷þ]tÔg„Ì/w~·¸iÅôÓ8›4•ú]]„cL>·0‰pÓêAk €[»µ„X#1UL J`¢2Ë%Ã5DôÅ#1°¸ ", $ÐHe$—ñ)h sCHpóÒ÷ì(Å»8lC€e@–òw.i=ÄOñ‹u•ë-årÐ U%ˆ@$`ÇØmÞ,E2¢-&#1@beyeb*, #/ÖØ@NÀ$pR8`Jb)#.´Äð¢(§ÐAXÂBñpDDlFã(›*QÆãSé½s.y½½G<ºÏbu—'fXÃõUÒ󩘵‹[rµ×.œòÅÇ’eégÓÇN˜NãfçÇ'‰ëGo²!¯Ü>­#/:4ÝÔ˜!¶µ‚3$)2ÔÁ‘:WFÏ\PÃÓkˆhèt§ÝÅÈò­fÖ¤·£ôÀãa”ú`@kž\ó\ÑíG<ð>®fÐX¿¸>}Á"ŠK×¶%Gx‘ÌÐÛÖNºriˆQMCdlÔ„äæP­I&Ú#q „h‘F‰ºÍ³MiM­;v«©6Uªfµ]VÆŠæeâååÝ¥o5­óM”l*mU5V±Z×áþ^#/Îäà}SËȬ°3íôø¥+M{JPS9Z±šnÅ¡ãÁ¶ â#1mÏÕÐFMñî¾#)79Ôú4ŸFn Úëô»)À©uKoÂP˜ý9IÈ65#/b˜ârڤߛDPj‚¶©>Ô ÀܶáýW cƃa±+HÖ¢F†p“d˜FYÀ{rç³7“¹¤ŠY )AŒiôçËAòþÿ˜¾ð^Po3ˆmys·nÌ£#v€åzO5μ_3zL¬+â#/ ñ9Î5wµ$€"þ'ò[Öä}\÷¸öü±™žìo-bÓ70éðâ–õo•û¶zðdd#‹<—õøM¦£ì®5Öïîûy߇ü[nj«"³-#.ó•8oPŒþ)®60s'oßÊÚ8M!­p<ˆ£FͯYDdPÀ<IöÃ_Éø~ÿòÿ?ä߯Ð.ÐVsŸ*—×显ۤïM{|sx&øÚÖÆCèð¤µHRœuÏu][º´4’„†Æ¨ ÆØ˜^r#1ýf²°Xn`ëìÇ™YøÜµ „+ÉoA½´”V#1›ÀEÚ—l8ê .°)`‹ùI‡nÄðå\Rœ:¥;ž«ÌÒØÚ`õ¬h#/ÀïÝÅÇëYt'¡;”Ó¦Øá²'Ý“69lÔ@~=ÓþÈ`òŽ÷îo Œ㲟Ùû»ÕnåRI%æÍ¶x<fƒLËm÷¨Êq ¯-ú±#EÛ\&¶dkuCˆ¢Üaƒ'`í 1šx #/„d.xÐ5,C§‹ÆÎ;@M‹Ûhoób0ˆÃ¼;àÎåéÒÛo·I¤"n¡æ] ˆõÀ²R`QŸ Í•Yt¨G»°…º㫎@Í»÷“P»ç?~É àŞ͙7¡F#.,ƒÅ22ÈðJ®°XSoÏWîk¥"STƒô0y3ci„›rçLàÈO‡æßª®†›!¬-0ß<jcaÓn?W4%·ýu¹5ƒQÎé#/ÅŽ#1h Â۶Ѳü-nµÏ¤µÝ£†Ð´"¼V£GÙ#.h(Ñë4Ë£åÒòÙÃM(ôð³¨^dT*¯×žAØ2`u Y';õý[_GfßQ)²˜‘‰#.à„!H¢&"`0yqS$/Ÿf·QBF@’¢:ÝMlktü*‹ÆÑZÌ´V¢-m‹V-FÔVÚY•²[clšŠYRÐ@*H)QPh†¬2–»^§Ñû÷Ýq“Ø0i;ULŒD¤é$2`ªº.A€@¥HÇ/.›Nsm÷ ÷ášqCyܻ婿°Á`ºwU½æ`†“j3Ó°#.¸}§QÆmƒ_ÑGhV:þÙíõ¸AõS#.ºjèy„FCvòÛ¦Û$†ŠØÓAÝ`d#1ÿÄ=±ß”^²ˆ~š{ƒâv¢i7¯iû’ F@$jzU¶~œº­7)•#.¢ x mhø|80>(ïÆ/›z€{rÃùØ&~Ñi6?›³aƒs¶ûÜæ'²Ù ý‡#.ã—~š)|ÉA¨zh{­Ç¢„œT„™—\æØ#/׫{°lÊ„Õ5kf•oñÚõ×L„Á³5+‡@>3Û,a&ndÁx>¸TMb•ªý²UT”y¹CÂHøwÕëÅœnæ^‘3΄£e5HL¡líI?BZ@´ÖYA#/0|@ kpƒHŠ1]Žd” ‚'üˆ‘F‚¥m5J ÄÏ£MI1ë«ß1Â0È’ : Ç‘œCxbFH!³*¤EV¨TÙú>¯ƒ¾ö}9>??cúìcšò0Ä5‰‚ÆÂG!"Ár#/¶,FBj’E êžÊöç/–.Çl·%Ð{ì®Ê™%‘ÜXNWÌÕ°¬5nç*"a,•´˜ôž²pÑÚžEʨ´:ö¼†Ní¬ããœõȱh˜¾O×`v®X,@&(*¨äÇ“¾“<ún ¾ÉþËlé]ºž__¹¯â¾Ñðax\æî<ÌB‰ì=Ëpvû6—SŸ¨ìtÈõæ_ïiºôe²]PªŠÍhÒÍRâµtKVB™#1HUP¢0Bû“`×6›8>ˆÐp´óc”orLo#1@©¦Áƽ!P€hj*(A'#Cf·uÚRJ²ŒÚ*-’hÓ@:ÕÏÀ|f[k²†`À`¦„c$Æ "ŒdA¤†‡XôÀšƒ‰B7ƒhdq¸4jÚ'¡@NQ­¦…S"DÂØˆ˜Áê$JÅü!±‚lïã!¿›pÉꙢ~×™jÑoçzï}—Wvd–ŠÕ¾iÙ¯UÊùåtȳdÛRms¤˜Ï7v“tê¥&Ó]ëÏ'˜õ5w¿^¼h•¦#1õšìÐyuš€¨U5¨ŠÄš`Ólu™“¬Æ©™]D±´D†!ˆi¦6&Ú¥ªSPЦQ±RHiJË!%•LÄÈ­”Ô½uvòí\–îw8mÉ“}#/ÛË®k4v^órÞo;Èš4*Z†Få¾]îöfÃ2#1Ù…,£ZšKÌ—YaämˆÍÆÒ#/V·Kˆ‰Rc„Ù«V@DVCqØî)«Î2iŒmæEN.©­½I³ZŠ´ØÁÇôó¬æ!¦>Ú«XÂE‚Ü™l$m©´ÈW—O‡1È6ØLøÔq“i±DC‰ Lâ—áÃK ƒÆSho¾”u{·^˜çLú¢òõãJíµÐHW­®–5ðng]«­jƬ´Â9†-9²¯0¢aV.ÎʲSB1”Ž+6Q&(b#‹Yÿ $KÈG…ÃVC05’ÂÁ›².£¤V+#/#/‹µ*7r[ ÇU„aFÒbò7E´±€3-²2´(ÐM%Z#1HñÝíîÉwY4—¯]Þyã fûeÎ8Day,CV̤„–6\0ÌD8¯8¹¤Öæ¸ò•©a ‘l†"Ö+ãÆìQfjŒU½)Ê|ìF™&„ºx¾%§Yº´Ý“sJXyÀç•3[©«™ÍmÍîyª"ã#1|v´–5Œ<ý&1dô¡wÜc‰ò±,xj“skhÁÇ ÌeíÒá¤Ø4Á\6k1‹lÆöõB J¦ÃÓÏHW P@9ó$q„|ŠÕÈÌÅ,Î6Ãyªþƒìe'ÕêX©v)Ðâ84gí_43 h9ûþÇeÁsćm$”¸Óñ<¬/ÉÏ2|Ïnž t´›ÑÎ$ÞH¨Ìm»9a]ÈWÓq˜ö³F4Ãä0n²4°xÌi¦#/­V’©µY¾2évéÇ!1)¾/Ô‹bL;g\"ѳk#/*µ˜ÈªtñÊ9§£ HF [†â#ÓÝ×ZC]ó4m1Cd8Zéü¸ôX@ÉãTè 66 É̪H¤Y(*#/–ác(3*­ÛlÒÔÜ_D£óÓ|ë†B›B )DT 9ˆÌ0DcŠ$4¶‡dqi#/ 0)6è5Z*e#/#.Qd¥²‹!€À“DaQµшƒtD[F¿ÞPzkÁëì}ÍíönO}ÿ³K2Î):'zgöD–ýÐQ ‹¿ÊAí[+åø;C Üû܈ýŽKSX+åöKÓ:2Í{ëÃuŠ=­uiF#1lŸµ,.–"S#1P>¯ Û(M²9X±5"{ëÒë.|%-{ö ±´ß‰­ÍJJW»#{º-—!Ë‹®^%gËÑ™¡bÔâ‰jY!L5MZDÖÎâaáá¦^3Y‡W4`PyãCÐDãÞ¿å?ï“<y›t8ëÔÃÇ_àý¯°”.[/;I’C“˜6£›ÑZ§æ-¨¯U~5¼õ7Î5̦¼ î8ÓÈE3£‘àe<^0ž²w›£ë¡µVCdÃÍé"Ìì[žÚÛw¼ð¼8Ûa$b²ž§kÏsÞo“zÖÄÅ»ks}þF¥Ý<y÷FÍÍ÷ïx!d#U;¾5¸k'I¡—û„2òd*hüNáºx>e1¯{Ð=çÙz2…ìí+Ñ7<p”BÎ\0\¿5XܹoÍÈÃÓÉ#1öoe]4B²–Ë#/wI‚…H&Ó¡YÔÏÞvS}åF¡3ÎuØÑÎkpéqcg0Pö#/-Ãn1LU‹³=Zo÷¬>@ÈŠ0Š|¿[ŸŒwßB|Hu'Ä ãAW"ÒU *š¾zý¯eb·5k…ŠÖ–å2é¼¶ë^­^jðCå@Ú–ƒôЊȌ²›B0F£ªQÈÙ¤4;U¼t¨Þ*»-nWMjñÒÕãÎè±[¼ë›¦®Õ]6îÝ2—jñµðÍ%ˆ©‘Š•-Î;¶îí´Z“U"i E-DR¨£KE¬Ù‹ ¤#.¶@RLø8pØú²êˆ0úx7~,¬TcÔmÉ%øâO–‹Ó’-$P´A"##1`*ÀŠ…DŒ!H…1 –,¨Ø¯S „)FèRxÆË#/¡(ÈA«Ä%Ò+]¯m¯ –%²0À#10]ø¼÷Òe†±P”Cu¬×Zöm¨ŠŠÔ¤’eõû¥°h¦Í¨©Í)X¶5EF³(¨±¨­Ŷ$ÖCFŒišÒl‘LÐÌ™ZhѦ•šf3V¦¯;UÑ¿)#1‘ü(Ðûý4üõ(?¢*1Þk¾}­µëÈ4›Tm ©$²üÇ^Ûügß鿇¡ëy-ëÓî±cøÞÞw(ß;ʃJéGs–;âËo†Œhá‡7öCÐ÷‹km¥¯ÌÖÑ©¦Ö’™F£}Žo£zëòJâÍÕÒ«ìVÛ†«O»ŠìAµoY]ŠÆ“Nî·K]Ú+®Ö±­£Y#/&¨Öñ¼òÝM¥)VÓVÊõÛ³YZ5-µÊU,húìÔÝ!¼Ñw✈ęfò(ò}áA9†‚у#I!Ίl\I¼œ¹é,$2ª€²"ØÆA¡â•BÂÎÞz©¾Ô²ŽTÊðr›Â%` 1j`<YæÇ¸°¼d5[šOR{~{?5>aÚú¹µëòß PðÉgÉöf?{(¨…Ì]hÊŒPŸLÓp$?ÄF͇¥TK8kF4hAt”Ɇ27?E–8ØÆ2ºfI¦¡0AŒ0¹HmJÝi†d™Ã.M½(‡1PDBŒIˆˆª’]ž^'öâòyøíY„›m<&6”цLVN›>r [ú§“`”îë¤ò ѲÉø•Ò]Œ¯±Þñ¼hÀðšÅ’óa™1%äO28Ùß%µ$çÉ0ãÌBvs™ª-6݈!Bsº´@„ ó¸kaŒ&S¼É“Ž ²äÃH—w»ÞñM‚Éú×F0dáãx¥Î®«óØVOÚ8¯; Ø€£½MÞ6Z»OåËÊÇûê³áEÉÍŠŒZÒ–SªVÕK¡§¶IGª¼Í”QêGíPèâ Éï#/BÈ=PO¦6Ù5¤¶¶5¢Ô–ß6êmbˆ # F÷1þò!•é#.2Š-‰š­¼“Ó¨Û%µ±«|Í·6׌3‚B çç@'ãÝ/#ëú™›þ§pí|ù¹#>ÊD 1-~voò*/?(^ÞéŒw$†DX‰" FQ+J¢(ƒP#1Œ`bô@ D¹ÚxuÞϦƒ'D(Ò+T‘.a§SbÐÚŠ©À\0Wº²0…ÿHd"æá{-¾e¶¶Û2LcdÕ­y¬›Ïl•^wUÛö]⮲$ˆÂ%0²Ê”ÉåŠp•c #.Œd# ivµ¬Ý±á¤èV›dÈ:„-3ØÛÕ©JFAÀU¤D=XªÓ ’£cbX˜P©#1 *ˆP¨P¬V1b û‡± GL¤Í!^ fçå(#1KVDA¤r.¨ ^Ù†~ûqxŽ”„XÄA#7•$<ßæâëí• Îo#.8QòÐöÒ]~g×rþ)NMŒæf1G¼õÛZLeœÓ±bí˜<q±Âú”ÍÈ!­2dʶ9Çüúcœ°SÁ^kkVü2Öª5m_eá0„¸‡`AF@Sêí<´Ïî>’(w…WÈŸæ£[ Ðømþ_ÔÔ½ k™Fè~»§SGÚÊ<XQ=ç÷‚$„dÜop~1Mõ $hú$,dâžß“}ÏÏ“OŽ­Çá€Xó|ù­]k>nÍú”³’ñ[}èÎVv:fT8ô²îË—Œ„*,Û}Îkƒ£èDÔzb«Û¶³µ‚Ñéž™º òÅ8d9pLGD*$–‡Øu5¸¢zÙ“‘¼ÝA9{C‰5s<êÎ8u¸`Ý–„9ÙE³#1É#/jŠ­áƒIAµœaÒ)#1ƒ”$ê `ÄI#.¥‚¼ª‘†Ä¥2L.ÆÁ‚Á°óú#/á€?(\!4B6+áßÇÑõôúy‚áØ_å[wvU\#VßuTøFÆrÞæ»žåx¢åI½O ¯DÒ´°o_‰üÐ]µCÏÂ`ðˆ*ŠÑNµv¥µNk-ÕÝŒÕõk­Jl¢%Ÿõ]ñ¤¡#1‹PC#’f@ìþ«žÜè8¸F”@ª–¤$TX ¦S =¡fÂ<Óchû#"Ȥ׳ßwžy§yÜi¹jâiG]Þ/!±)F^W%‹,,‰LÍBl¥hòUÍKJ¤Ú))60^7Á„¹«•ËnS‡tÞo.Üu×jcåwvéݶëŹäÛ¤RÞO%º¹w5™cC®*³[FGœËTÔ¬îÜÚé·M¶ºm²–6ºÅ6îÛ´•%ot›§59™ÒÜ‘MNíÐsWYs¶V5­]wZê?Ò B•´ H[X¡UàÐ#1Ybzàp²M ¤TìzøƒJâ'¸;\BÄ=#.x#ýžã ÄUƒ¨B#1‚P(ìD°¶ ¨¼~äO1x'_nŠŸòÎSÞ#.=Â<.¬²)нZ>ÈøyB{CP×é«g–H|ý0d‡·p‡«wˆ¤ûè@ ý?Iú¼Nvò¾\Ex#1’#É! Fff›è×·5W´­mµÌ´È™©kßøüb#.ˆ&“³ßÊÀ*bRTkdÕŠÕ¡KC5oš"<lo©K ö‚$J…$ŒŒ•Í®ÜêôµwŽåÎRÍ¢Å+˜K’ÄgË‹ÀÊIPO˜!»’ÉV€=>ò$’9ŒIFÝ)ç[|$ëºRRL‘EE#/ 2„6J(d µê»ÍÎÚöÏJ4‘kÃxˆ–#"ÑHÒ±f±lÁ#1º¶m·fD‹l©o~®×ªù]'ôo\ RŸ:ªVuèáE‚ÿ ÐˬR#.8ÃHŒÑ,bíŠøŸÍû¦ÃÝX#/‹¾Eî¹4‹Ä; +"À©«eM ¥VAfÕ5U$@k~>Fj;Ò#1è#˜ªÙ4›6ÈÓi“+FÚØ‡ßT‡g¤ô0Hj‘Þ|º²pD¨‘té©-37õZ´­FkI1ˆC,¸¤ñ…@*-[C[Ó<(êÛAÒ;ÐöAI#1#/% ë”#.lWCÖÜ¿Aæ»EUJªÞK¾<¯¨;^‡OeÏ|ÐÛºÿª*{¾Kù'P03ý…™3ß¹ü™ð»LHÅ4ƒ”aË4kÁ±´1ƒ!#/nL¬ãˆ#/RóZ—f…¦…Ó夤$nIÁ¥FŽºîlg}É©ÖLP2"EþsX)üÕUÓÞ=‚Â`Á;Žèl9÷«h/uËPþ§Å…v3"$#.‚’%Œ+$•Š1ƒ‰Ç„…"mD¹#.l(DÓpŒc†õ!¬#1Õ²YþÅæºVæ®’Zå^ï#/‰ëu#1Öµ!*!4…XAÆc1•[¶PcCo1Ъ°¢Uˆ^*ç?Uã¦Ü©-ËÞÊÛ^$„–2E¬Ë€-µÙÀª¯ ²ë2äâVªù8“Ûíx¡Ý•VCúBÚÄ€º€ç#.Ê.f¤¹l®…3¹d•ë÷…h4Ö #/õÕ¬±1éƒj4J#/kR³#/taF B”EŠÔkl[ZÞ¶­ÍÓrŠW³@üÿ3M‘a©»4Ó NÄiIÇ#.Ú1£DЛ®Ò¡8t*$}ƒ P>c»S„¯ÊZQ»p`ÀÇÒ XZ5r¨4J(‘ÀÕ*] ò…îW‡ª¥ª˜} íýðÑ…EøÙPy1„7#.Dš§nø4TVU#.#.± Ñ*@sUS¨ øÅ)TF•ú±¦i]z´í›kšÛÆ#/‘¨/ÎÚÁ!é.íbF"´0ŒØ(—;”öÁà§¥:80‰#!î*ykÇœéü|#1Âf®¨jË-"M!#/`Ë¥—ŒýÐãa¿®¾Ö¸]?è?osXÝ>ìÌ_Ýà–CCš´H7c˜Ñ8$Z:ÓzdM·–L„™(¡ $jmWGQ—+mÙ!+¤j¬bÿnN¶ml«C;//(äGË/ÎU#1²ðªÛl+ݦµ>óyÖ½o*ši„{CD=Áîž‚­[Ãq¡à”ðTöèÛ¨ÝD¨!·u¦ QØD#.‡#.¤ÞsÌN#.²?¢(#.©ˆ5 E{¨i€  ˆý©0(ö#.)’€% y‚>NâÁî0lMî`9ÏÞ„#//ö™HtzÇtà>Çì·öNyxø!ÿ''ó2<^öþÞ”“F#/!°TÍ~Þ“åsg¶9‚‹°>SöAÀ/úÃ>ê?õéâÝ–cЯ£tåt©A?”iDÆÛÕÒ¼~ÝÆ·¸K.C6鵿¨¡5!!9B4Ùûªôaƒ€e¤+Ž—WØ}¸4™¶'Þ’ÏäPé-¼Ø”j^#/ja)†"Vê¦%Ž¥fšFБ7OÔ™Ä#18åÇsBÅÝÌtðŽìÊ·¦ çÉ"dL¡†‰ÕuÌŽ+f4 Öü#/.¸ÍJNš#.Ù¸F#.è*5҆ƊÙ_ £¼GÅ<TC‰`DG‰Õù:œËA³³×UT¿&‹‹p¤>!ôÑ¢92J PsC?t¡âR¨ÈŸÚ“ï÷ûû/ê<¸`Ééï{Õû^$Hû®øô»Ì¾{Í`Åü±Å§-à EU,u–…r‹}ܪ›±Ìù»”ø-ÐÓ³ÒÂ%þ—¥h-ëúããÅ#/ù²Nõ·)ÒZ/Ñ:lV„ºÎ+ëe¿ÍÝ-†ôi4RšXÅ4®ù^•ØO>N ª•M^nÅ)Š¥2ƒ«ˆE¤6ôÖ ­,-Z&ñØcÔ!¤ÑŠšçß<8dY9¤—? žÙ.÷~|UÓ¨Fw›â(ÊÛ¥â¯<ñÕ¼®Imšws{O-;YÒži{êzÒy°0 y:sbõžl†;ìÅcØÁ`‰âÚ‰áƒG?„³^]¶NÔ-îë5íÒœ hâžÕzâ8ïÞWržMüVa7yyQÔj‚jÆl$ìÀ饥٬K¤“ÝHovÐö÷Êk#1§U×1/Õô‰6bòÙæ®_¬¿‹…MåüìƒR+‚x½îjoËL¶Â˜n´e @¢ Š^$°X Òn,·.á>#/Ë‘ pvÓš™à™÷ó<‡´÷žD®0Äíµ«ãŒôQß{I‚«BkŽH‰ez#1ª(6åŸ=²ˆ#.•?ÎØEuBåœ=G¢Ý^Ýf_ˆ×‘Í;Ìô¹è7¦ß#.ÆÊJ;ˆp€ßu#.5ÈÃÏ#/>7µ³‰PkÞ Ô$q vÏø½9(Þn0Qƒ8v?ZÄh8GÜ}y+Qº%D¨Âx5$4r¸âQ××£·16 ³¡1[ÎM—›¶H…Þa?õ¨ú],F/‚¬Ö¨GÉ÷³*ÜeUKRª«6»{«I%€Ï½ HFظa•€m·’ɶÉsW;Õߊ_9o8Ãc+”pNæ®ÞÖ®ñ_ÍìBzOdÛ­ºD³0ðÕõ#/o¾WÏSŽ&–»Ëz¤c¯f~ú¹ˆ±—ê,¿Lpå· q½4ã©°'•T(;ľw®þ·&p(Aí ƒ3ÁXûü‘ÜcÙÏ!°™Åb¬„–0H±i¤F/‰¥ JDÐÆ3˜a?Nßvgkl RÓ=1U‚áP#‡3âXÙ‡tRï$øR”i@@͈`úq·j£(èÂWž½£”઄̛˜éE±{IœIv[#Ÿ~i;õËMÝ<­šÞ°* —ÞÜû´IŠ*I&§l{øì<º±d룟=ØHDz}¤W{˾tÆ–¨Ñ&.ÛfYeÂbý=ª/ìL(Î|X Æq‡GÝÛðHyªž¢ .ôï•4Ú*þlÚ×¥µŒZ·äü’h‰¡±N˜ù¡ò?¬V"#1#1HÅ£F¬Q¦¶u¾÷Þ½wªÛèÙ ýyà©êÞ~Jõ¤Èffú)Ƕ!"›¨âÆ<ßÌ;ŽÚ5N®©ìôY°e·ì>dð—Ú@Wäl9°¸[¬<âd[¿2Ý`Š:ÿ´\sÆ” UãAM†áô‹#.p¥ôfRý`2‰ªŠˆÓx#.÷}«»@#½I_'ÎÀ SðÕµmóˆ44bÎi¢Šò(Xu’rPø#.u­PLáâÄBâuÑ!"éÑ_$*¹sæq9Bc§PžÞaGVã¨ð£×d~b*fjûzèç«Y¢ ïå,s&—CYVŸZ³®µW'Ñ=x€©ˆŠ0ê(#1"y@(0*!¸{ûÍîSέ㮡Žé×^ÑfNаìßµgÔ9(ø U[ŽóènñcµÛC²ƒL ÊÀ†'>;Z;©ÂדÇö¿Éõ'ŸÅö–àµ$GÓMŽ˜Ñ,'hØH>Ô¶[½Ò¡äO»ŸSF,d! Pˆ1%TˆšB$AK(lh¼!SÝP2q¢{ýò0íþ³¡ž'+Í…•MGšKó”TÌüT4̜žŸÙ%.[¢¤Kñô²»u¿|}{ß u|ª&#/ü 5ÄÖ#/X|bݸ]åÔ‡/uâî®3;h^È_t}˜A™ZþË[öOîµõ"ÔéõÛ^ÃzÖw±h®´¿gKÇl„à:w>ì8L²zìfÚŠ«Ù°Å^æD߀ßpeìEBòÄ ç*6u¹cÂåZa„Že#/$>cåéû7Öô5~Zíã|ªÜ.×–àÊÁ#ÃQØ|Ñ?ÑŸ£?GäLÈHH d_Ã8ÉG‘r€ZË—X”ÇrzoÐwMëUN80X¼üû0 ïß3]CœÏ7²)²Ìàý¬.#/}| —ËÄÛj×ÐïýÈ¡$ƒ³ÀÃñyŸ[1©å¢‘γè]yÛ¦>ÍkN󺋺6Ꚙ> ƒá¿«Ç”6’†Á"vµd–DÁ#/3†PØÙûø®±Ò¸Ãµ8–1j·„¼VÄ(}ÉdÆt¯CÃÉÌ4~ŠÈXòºªºy9ŽW*®Ú¤Å±ª¥³mšZ–•-m2H(FwðÝax<ëbI8!Ö¹¬“OMpm÷ù~bFè¡Pg«$Šb£i¿o÷>ç£öÿ?‡™ŒÉ3FHŠ˜CKKA1#1 ˆIF"4¦Èɱ4’"2*#1ˆ±H~rqàüS^p„4ê~7'KCä&ìŸZ÷ÑÇå¦y…åÁ‡eÝsŒ;œqÂáS£0àå6"´Æ¢3…bæñÏ"0F!Ó#.ñ#.û™Ö,¯ð—1Ëè~f9½#1ax4ÓÍý'D­ï¼ù°¨^Ê‘HÔªÍEÔ`Ä»bÈyó9ªñyB„0C [2†&6ÛÆq3Òá~xlµÓIª©ÈZ´Ë1+FóØ Èƒ! ŽC±j:Ýqµé,ýY1Øß:9Ãd¶Ðš·K²yçpû·×¤ÍÑœ·ŽA/οXc ÷8ÖŒ¹ÁšA7ÀõµˆçRH?ãd ™ÉÏùuãG{ÃæB âG©Ë#3øQ@Ñk-‚©Žï/7ƒ ͹ռW«7“aoFm!–dÞ’AŒÌ©4V@îãExÙÝrR¹ª»]ÓsuIu\"Ö…ÕŠ„Œd¥3h#7͆3y… T‘Xa(4!0É#.ÂÔ0ZaŽ´‘|š"2¨bb!Q…FÝ8Ô(Ñ’$£Eý³m´-Ť֙·¸rér΂ŒÓ#.Tk‡/Ôö'ɋɬ¨ËLGÛ.X)D´] …,ÌȺB! aÊ?tpµ¹ój&¦«.¤&‹™¦¥„_ó°Â¿ƒù‹Øágˆ>5U°Í66+鬓3• QFÒažÉÊ%!ÆsˆmB6ò-´´JÛÉÀèˆó;µ³I°y­ìÖÍš*-œF¢!RD‹’lAŒ`‚0e\™ª ¥MSDQ[J¹·rÒ"t·Šß€zsv»«w—à÷Œ 0´I0ÛT¬ Dañ-1‚åÔStT6œdŽH2Œ²îî1ee¨ÄÃB‚c9 Q #1á¢Zª28,Œ¨lXF6•Aª¢±7*­”—(ìÂ6Êëx„!3l"®- ¢ GTŠ7"¡\޲Z1Žƒbl®2&R†Êæ”hÔpÇâ‰CP0lY¬³@ÞΜ縕,’R¬QwØ9QW7UrDÃZRhj Xðz{ÌÚÊò-¬Þ£òDJDLe»Ú‹ì†ÎAQ&—³N¦’WGFè'bÌ¥+H­;#UˆË HVI†DJL£‹Œn—‚¾°ÃÓÒÂH-÷>¢ÔÎ; ëUƒçãkX²%ÈÈÓCk³JØ“àí \dhz oPÕ4ën–bȤ$iÃj­O;ƒ6Çf+AŒ– ñ&jƒpc¥FŸÌKH'…p¬ƒJ¼M)æ«c¸J—ˆ@î)ÙVX»F†Æ ¬31¤,@È•FŸ#/@dWe#/D'Ú±¥5T’“#:Dδe#/# ”j’€bhóúG¾ÃžEŒË°´F5#iV€1!c L#/êŒâ­’™bR¹Ö¡30¬ŠnFg7!X¡„J#MbG(Hˆâ±V#1ƒ@äA(4ŒhÞàƒÆŠ¢£ŸtÓÐp[|¡má‚mó&œå$0LiTÐD˜• ˆ˜ÄÀÇc–o£m銉½æÛ5"ˆsuºF²¢éEÜ©¼]䘛v”Rò‚Q U6QR;S¡ äÄM3E"‚!«s(¬O+pkt‡FÈ(ø“ÖF¶Ðµx#1JùžÆYÊŒM’ï‹ìu¦÷Â…}`N'u¤`JÛêÉÔ¥Ð6†Òª"¬u©P#ó¦"¯I飖W!~^äšìk\šÆ(ˆ­–ÈMbT+(ÓÕ”P@Rtlå¥C#/D‘ˆFèÑ;¦ˆˆ$i-ÅA‰sŒ 0ÊAVñ¬m’ˆÉ¬²õ¶õnµê]©0hCHؘ Ñ•º–*ÒA’ …¦¹(WbR¥‘üœC¯¨$!Uš5Þ;œ7w^ ýßåÓ£B±ABOÙE(à¤Z¨5HÈ-njo›_‹i(*Ѫ¢…©³i¢Á„au*þæUˆ!uü£Þ…)ûPÿfKÊÈz̳3¨ì…ODý\hÇh¥E¢P‚”6pD€bç†JÃú3žûÒTQù±@â:ÂzèÒÆß®J­ #1îH­IµRV6´Ó5•¢Ûl#.á.[n—²s’'Â¥%D9UêBsuÓﳌ 4¨, ’¡O§áÇ=œûɃŒîMöf¬Ä®¢Æ r†·ÑŠ|’#/„#²Å#1?æbÌ‘¸Ã—wlD[]]\­·¶ºé¡wpX0 œ¥Qc(PŘR6DÅj ¥PÖªTÙ‹)ˆ<’C÷ªÙr@ìH›šîͰb0Ù¼nÔfÚ‰(lô_ËèÖ #/€õ™²ƒ—gy,k„¼©Î_mšëÎÞßvaˆ}“’ ,F{©šnNcËFMžf«úC €o÷›¶#/ÜAòmd½êGv˜¾‘ˆàYô¶ær~;<´Jö"%nb³ìþX2$ìÜH“'sõg´ÉPì2à¼0Õ!äDÄÒl-kÔö#.#.ÞeŸÃÇå=6ž©.“Ô°³D`à¦åUãzZ[MÓ…5ÚuTú»?—¸Ýþsë>²Ì~¼Ð5ß߇¨?PÂH$}Pù“ÇO0Té×Gö~3`úç8_vê¿>ùË­„ÆÓ&–)¸:¿7ꢣø#;fG­ö“åöùÝÉ8G=êBůÏXvÌU½µáôÂ$bˆï¹ŸZ¸é$‚‰Q ¡\~ÔîS'=š§”…Ñ]ئ!ʨœ`v¥¶ wªÀÖÂ\‰‰îŽÉbM{Ê^¸¨Tw°#/õ·pkYn±Ä¡xõ}eïĪuÎÚÈÅ<Ñ`ò&ã´°ÖQ‚T'°JÜ‚eb‚#1HŠ©ì`}©ÖZvÒ•J!µTÎß‚\Ì*{Zài–†‰q†Ÿn&,ÊCW5T†%ÛQa?cYäoƒs¤NGX&nlZ’p@)!Bj€«ýt4n© Ô…Kë¥ãUÒ.í\cRQE¯­’Ö-ãnÄ·1­\Ö*hÑÊæ·M­ºjÈkº[\¬šææ£Z¹S»Ù¹6¤Ö¿k¸h¯¥ÚÊþ‘%¸a¢C¹»¤¡»Ç„šªêÄGqFEiÇN8É™‰F©m zN[#1=)žšzÃÈØN^€àD8‘r1Hä ‚ELD_°ˆš€qB>Çè£ ’.d E””Ä7qáÊ«ãÕüt|Ýhy¼J¨*}´@÷´*}®‘@>²#1lb2 sD²ügòÒK:˜TÐ"Í'4‡{CL’î²6<½µzÓ#1FBThb$m% #qcBp’ô}Fý‘´º¤ŒP<lFª †Z6Ô–©ekm%¶‹ºÕUºJ‚ÒÙ5ø¾}·£^·¬0¤ÈK·º<ʈp‹U넊2ÍFѤÖHÑZ©-TˆFóœ|ϤTBá±Pu£ÿx= ‰¯åˆvt ì·YF-PÅ¡€}D—¥2DõÑPi†Ÿ¥8»ì«¤ö’ްB3¨~lóÍûO_º(²1ŠA€¨2*$‹C×ât…h,Éó"¾ª;({v¡›¡@{;ZPȲ#1–×Å]¾íZëjÿšU2›I!,ÐÁc–H2ѤسB´Í£e)2JI´hSmh¶¢Úƶ£kSiZeR™SmbØHcH@Яä3è­Öä‚rJÃ2#1‘i6䯂#/•Èçapت…8¹‹(¤¥¤0… 7#/‹”‘;¨4•:ѶÆ£Ê! X(#/lÈË €5X›C#Èå«LfJTØÂLh01@Á°t`¶ÀÄ&‚@Ô"- ¨ŠR PEBª×y!Eò@IF PÆ }Ÿ³;ëk{~^Úð©x²è·MMÝÓ»1píO×Wi½uº½wfµãUÔ¨HÁK–Ù–ž€và’$ˆ»;”g„dùYüO "xTöv6ûüqåYYíÊBB¨©' >#1‰$Tˆ)äÓ_äÐÊ´YóNëñ\(#1aMvÞ:œlúZGã§UPÄüpËÊož#1˜‘&˜Øeå#.Ä0#@ðŒòã¢#/SƒöùøÖ]ÖcØÒ>-#3fˆu§a#1e-/ya¨€8þœ™9'z5}GHQ;šêgvó˼ë·tv–ò˲¦Ò™ú:áé–ÕÍFÚÅ¢ÑmmmrÖÉB¨…ª#1ÀJ%‰Ëß3 <D›?½?Ò¬’ÌTöb©ž#/ºˆe +#1Jú?§M«& »8g\R;9O«hikÀe¤Uón¤A;äùâéª$ ö¤Ù>v}ÒêºrÐ4&`ZÄ3¦ë³Üÿo~×¶5Ñ ó¤•¶~Þ•m.Òº¾ù’Ö{<aÛh䂞ýnxµh—ÝL²ÖG*c–K;UG˜M×éÔ­÷5ëÆÊçO-Îç[tDNÝÌ;W\•&6RÚÔ›ã¡\yPW-œï»:u•†‰9nÝg4÷^ÈTÎ éã)‹ñ£ ÷°‚I†8 –$Î0ž y)ÅGU*ŸN³Òܬû;R'nîgqßT®ü¾°þ7z¶{EܼT8«áÆÝ"òšÑ4më¯3ZÃLÛmU˜"‡©fÌKâHq­·ff‚Ó†òîwÍêøÖ}µVøÅ.atƒô9˜óO×#3÷yÃ?'78¥”#/$Ž'c£¶‘<iì'[lüÇ>ÿyÁ[bàñTÙ¥[¹^¹‰çGIf‘WJV<SX„›*öÆ¥0 Œ¥AµOWõ‹†µË>Z„$^<ñ^å`—\±¼Äs°K@ÑìÑÝ2€ÐM\{|âgXƒŸÐyU¬^C‰××z[&×R’…,¨I}ÑIL‡1#/R(d )‘L É’s@Â@—tþ¿ìÇ´œ…#”[^•Ôðí®=<ü76»¯x…̺mZ–£ã=ÊÏ÷öp·Ù¿Ñ™ÿ(%UÛ5`B›x+¶ÅÕx:q`è4z/•…6»2]J DãVk ËJA†1qš^8È 5]*P…l˶2U¥¡)è”&Ó©Z\éæ™-vÓºÌ]ZÔÙ©fOuFs©fÊg—:¼ã‘xÄb±odÔ¤ðh1 XKvVNV`ÊH¢´,Š)-!(PLµM\ñŽ’[EªÚΓÈXàÞt´2’Y‹Ù};íž%å͵´ä@¦â¹©\_Ù¾ÐðbÙ¬dÂH:&f#mø«ŽðôÖúDäŽÝ‚DÏ:~̺<­ê#1-¥s‹QÍZ*©UBiÈÛñÄÎs›yÐ!g5ã|(ÀÝ#.P‹ 63¢Àø‚üG&Ô#/LKèl°v6Üކ͙_5¢'O2Ã1¬zÝ3Æ“b0U£™¹YrïcÅtÛË,1¾k#.û;8‡êãÖ¼isc˜Q—³.Á´¾ØZòÎŬaÝmIݶž8s<ïñÚm°·ž:ðw”åòI= Í4qÑ'|KÒW¶k˹â]cÊ…Ë!mi9žè­c0bÛ#¸è¬Îw6U<×»)ËVæ+UÞ+F"…'ªÖ‡AïP`Ò((óG#¤½ö¼³ôŲ¿ªû–º~ .ÜÞ. YŽd¥ß³›;íYv…Ó_„dE,yÌÈîâs÷.þ¸µ«ê¤^Tª&pŸ½z#/Ý L Îë1Î#/ñ®7ŒžéxŽ q[*év£ƒ¦YöÉ5§zI4îôþS-“³éL;»¡o]^UW|]y¾6Λ”ë+žz=˜¤ÇvæW lˆ~N×0³ÃÛ•iô#.ŒF*q(á%hªª‰ +Ïaq†ÅÏhÁL1{¬ d.½f1â/×ÛÞ¹Z5ãÀW•°‰g"$Eðf¼ç7rŽWcA‘KØëN(Ýlá21«K»¹Tùç¯MõÀvçL4F´ämK4&\«¾Æìj›ØZM×w‰w¦¼t'Í®Ú ÊÊf–Û;sX¿ûßBs¤ 3%·R\ »Õ2OŽ›í±¬èã뢣p|ç| )˜'Ç›ösÚ.§(jÆ#1r5ÄX3½†åÚLû6¦‹AÐÓ#/¶·tngîFèo&<c¹Æ<ð`×8M’φ¡IµúàÜÎIjŒRl2ЃíÚ:(à74¡V…„uÃ^=P™´M15œ:É1ÔŽW…ÙM—Ê\w‰f»9lÒጳE'^ÀwX)…bŠ\{½Û#.Ãõ#1ÜC'nž:9ʺy~Û¬.7Ø4b ÙÁ³l6/ n©ÄBÙ#.Óõ°cÓ·¼=ç–”úæ"ßàòæ#/Õ$Vž…Z{¡F Sâ¸Øi{"_7(¡ZJ`Æãd#.„DR0XEL^ÊôÞq‡^NÕ\Âý{뻆™e¿KÙýd´³t€ó°­‘Ó‰–fþR¥ÓD¿iªš#1š=y"\d6ݲŠDˆÝ=,6ãŽÍŽVQ…héqæxU{Ó™ïÈͳÎzd›ºèÙOù–fúM m#ÃâZ›ˆZÆV.ìRÀ-ÙTØ4 [ûI»eÃ5„(’J›v½œ$Ê4 d•—ciï‹bËá<ˆ&Sìa4À‰WÉqs¢Si˜ž´4‰Q@ÎK° ­­ôJ_F«œIeuLíé>Óͼ™[GHêˆ;¿<÷ŽªéöT{ܧap¡ûGJŠETÚQSSNGsã7®ÓÍ 1YÙl\oŠiš‡ƒi¸ÚìÍ‘ÊJ.rÞ²krŽ6àjÆMÆ47éÞ:Gé׸–""\¥ã3Í,žVW48ÕÇO³#/—sMÓ8†¥J^0˜¢b[ DðYš#‡"YŽ'™œuÊú_OØŽÛbvš¬ÏYiö…Ó-¢›½áÝÔ>º‘ã íÆc ürçã‘ÅdŒuòÔ¥!À9es¼ï\wâ>‰2lu)!wñ@¹#SæìºÒ«­D“#›á÷—l[˜âtq[b0ʬkêí‚)èÀøŠì¥Î™hé­oZ k¿\B®ÍË;HqÛשë™Ç(l±\ñµ>þ æŸ.v§¯'马¼sl&‡Bîºë-ðAÊ#1}Í´N׬Ý57‡>~0xÇ(tAÜß÷êjÑï;#Öû7c&×wò·=%÷ðçb_¦j6©½]͹¯9t¸ƒ,±MzWNóè_“ê#/зè wªŸ7#/t¸„¼¤w­±ã[’œÂ÷±Âg…¿GHtý:7à3\9÷ùý>בxL<üŸ‰,a*ˆ¬Œ÷¦Í¾G- J¥¬PŽÈ¸µˆ+G¬¾Ì3#.Ÿh*ˆio<Å› Ù0ažG—çÓ¡l<!ÜÁ"댬«gp}eP³×$DjXX:¶ênYÑQné½hËzú 7ކ‹œŸ¨ï€/*ð.“ØxÙÒ®ðÃ#ÙÝ‹E¨è›6u«í«×ù˜^œ¬üà' íØ(C½|iĹ`C;–l\¹äm.ð åKŽ-ÞæÉ®ãó¹³d#/…9±76à¹s’cÊ\F3„Ñ´U==ÝËeR5WP¨jmÅG¡m;sFšÂò7*{íV’Í6¡]d¤%4bYL™¼ ÍÁH\q9N(W¬”ñ˜²NUÈÁÐá$ÖïXr:`³‘ªíS˜@u®uÐ÷6Pšßö õ*íNüÃ#/˜a†á˜+uP)åOø”êë7r+3E.)ñ-ÄB Ô]»²câvÙ6#ËóyypÞéÂëã‘:„ðA'›ð^wƒ¾!š8­¿±1ìUT4úÿ|ëš¡ˆ—‡Z>ð÷¯qlHgÒzÂí©ßjµHJšé¿>A`'–uu#.£È€fǘ ÇJ ‘q(t§sípqÚ}emQŒY‡ÖÒ¡iåldHŒDŸ|ÃÝwšÝ`í*{2hzmÝ'ê’ hq*l¶%ôš¡ £‚&ÐÁ’FÐõÕÞoWzësoMnhÚ5%&ÑnmxÞIÝrL•™ª/¯^d¦ìJ•¹x€6€D¤Ä,@Y#.[ÅIZ‚-Em‘b‡je¬ˆ¢HÁci¬ Ç…PȨˆ*Š2(#/@ìÞæ`òæs=g7ñ? Óó¸ëwÃj—’`FÜÛû9ÒcÙvÒ_ë$ΓaBüs‹zVñ[Å\ŽKÓhDõAID*(„‚#.$œ¬Ñ4Î_Uªfqò áš%Œ²é]Ož¦`ö}·xõ5fYjtªþïX«Æ6†–(Vê É”5Óë…>!›nÚçfàkF7Fiþ#©³‰ØQƒ+‰_|ÐTø³y6ÅNµ·`*ÌC<²º¸`@¥/íy#/°J1R¤… Å]R2•A¤WË>&÷ zÖ®©¼k0‰Á2Ì¡LZLÝ#/I·Ù (šŒÓicRE,R±4Ó!„W-ä ÊÎC&Ü{·tÊGd’`¼¾Ùl»…¦ÓTm´FhÍ–É…AH %Èa“hLÓƒS;ĸ1LÖ\n°È›€Q§cuìdmNˆ)˜Î36/rê'54\E&LA!¸«ZzmkZ¦A¸Ck.<“µ†µux9ųo,­ã&ÛI l°Tv›‘M(6¦”#/Ú6›OFhŒ˜bm¨5g#1Ûa’gR×é¾5©×9;·kL…d39Nñ+;rA0lǺµ£vJ2 &8ãcF=ØñWXū֪CD9-­´È¡ÀšYŠo.e|3UŽLŽ•¬$ÍëcÆ7 —R‰ŒÔ#/7`7ºCmuñZæÒ‘'`œa§#/vÔqë»ã†•&pÈÇœ¶;É„Qšƒ0úǘ›œA•'#/[ªˆkHÐÓ9:C%i¹Œ!Ѹe)¥#1°È4n#1hpÒM615Z6óqmäÉc®»zça›[ ¹tøÐC :Ô6EJŠ×gƒCW Wki5a”j¬æË¶î‚£›¹Œc1 ìs§?ã<·Û·˜î0røê°¡´ÑMSúr²Ä7$~’^U*|ÜtëS#1˜¬Ócgi‡LÇvO#7Ûö}sj´2I Èø³qÉ æ´Ü…¦Ô°Œ!‚ÇZ1šKZ&Æc\˜O&g6Õ3s0tðáˆ((”,)†mq-R‚G¢æ˜Plô›mºÒ æ(­¬þ¼¥ ­GÅy†0á•(øD#1101¥^äTÂ(ënº`¢ë¼#/4Œz°7`>I+Ç-4<iàæ‰"pôÛÅ…³)L8…Ù 4Ö¬ÓoLq'ÙŠXÁ–B1ØÓ<;„Œ‡>ä³gCÝ5#1êpnŽ5I€EBÌœ«s2±5»Æ‰­í„áé­ˆ‚Ø»%Cƒ5©Î”4ÒÓÆbLd#/ F¤çYˆk,à/# F°I‰2,¹J“]^æÙlÛú0 ÞsÐ4øZ`üš©LŒ"0RŽÒ˺MK…Ý3Ûƒø<sÿ9½”H0#A¬Ù7OºÕûvRoÀñ®³¬D¤eR0>2mùŽhv½@>ÇÖé‰Öc¦¦«‰)ð,;ÕGƒc5ØÅ%¹¼yFÀ2ßG­_í»~fÜüáØ]Üÿ¿s{z¦ìjh‡mœœ¤›8‚Lé>pI'Äï#1øŠwO#1†Éöø‡×§d$7Vw¥¨ r:ûº~ÏP:ìaÕéo«’zlf±ë'/EV:»§†ŒI#1—†mºå\À†Áq·ÙÆT>­#/US7,¶‡)†}[ÍîzíÀÆð‘¡©9$cM¢"§'vìÙ»¹íôW¶üŒ’Ç„{}uÓ{ÕÔ=cë”{#.ê;ÂÀ5‰¤Mö£I#{J*U$)!÷]Sí:yväc:;¦ù覠T$xEÍä#1†êBäHÚñjæÞe^+%u×[¥^j™M¶ñ¶LmQu×W+Nä„6’Æ¡ R#.…R+æ.ÌÓ÷PÖiæ#Ôh”êÛÛ<;jÜOÞ†T“l)-­†Æ4„dŒDÉF‘*&’…ªe¶XÛ&Á¶Ö)(ª$¬‘†3eF±HËB–l“JM’”S(„ÊˆÐÆ–‚¢SÑš)M¦‘J¥k$Ãl>µÆÌ"iBŒ‚©TQ$ˆ‰ûŽ]‡¨¯LÂôÁï9#/èd/¾'T‡\ïg=çä›Å÷í¹ ?#./Òu;#/8uBAœ|D9âg»ÆÚðËÚrùä¨?–jGÛŠ€*µ >ƒõª8e’à«,Fœ`Ãß&Ûò#.ä9F×µ› ´Ò´â@-Bj{(L®'ÏSð½6ݶ!ûÒïÈïHA‰„×võò2&ò»ÎÍÝÊáѹG(Á¸ÃãØ°ª¦!ã…߉òú½úÀ„ûwñ'9ßèÄ1TÊ©J­©Ð¦¿Nl[zm“,‹ÚVd^ÙÌñ¥„ “oÄH#Û$@‰6Ø+HÊ#jKZ(ûí«\¬¿Ou¹WP¤ÔhÆÛÆ}WUÌey6·Z—«©©rX´‘bƒ0à0'}ââ%B@aT¡’ÙgáîÐ[4±E”žL­Ê]q¿¨Í¡­‹>–ŠÆb#ÈT¶ m˜Œ8Ÿ†]­±¥\]<jTQÁ’´¦¥*¶6Qâ"³$6ÆÇ)#1ÍM„P°@ ²@´€¤#/G@L0-&#.¼ù2رhf‰s"Lìå{yÚòçhÆRº3^Ò¼¤¼nY&×]»WLu4eû09j›‚r@º.1„·”}àÓÎ"€´Ò#1Ò"µÊ ‚ddV$FS°ì£nBg3Íèz¨4%CÛ/3‰Q#/ñõÌ2 º˜¸µR«#1h¨, :ëÈzÓ¹M£o™­ØBf%sÃå84agö^p$F^œ²lF:6­æ©%\ª50w••'wËqš“ F@êˆs4„%á(Ž‚a#/@²us$¤!eÕP³D.¨Rd-1l#1Ä4 ”ŰH»#1…±ÉǤËÔÉ"I$‘9Ý£Pöõ_„˜S¬4Ñ0¥+–mÑ-5XH#.ìïl§­ü˜ÝŽò€û#Ãèzàgû“[T>#/ðÅÀ`ŒS¿8ýYÌ]ÚFyA#/{ûij;‰¾\Ý”žÏq'æsˆîe+UP"MšÜ¸]c°Ù)äRÉ)i.Ã<¨!wH"mî#/uøB2ôLž<6•dØfEbïû(´özñÇAú“$œ÷ÄLÑ/_oŸ¬ÒHHQåˆ0PõE*m®^‹tõŸ0²4çL‰ f`ÒÂ: Äãam©"z†ÃË⟹zèiu=ÆåßÏ'ßy2VBú˜2ÓH¶ÊÃkvÒ‰JæèM `ÛL™PØ4PPSD”ÔZ ±QøÄ së]kŠ<í9†[-¾ßaî°l‰¼Øg]ó8n ü>ù}·03ïV=©Ñ6¼jÍþ'ÝTÁd‚VLO5í¢›ìžÒ,yÐ’ JA”BAjE¾ƒ¯aaß§-Äó–ìñ“Cë÷}Rp]ÀnÙ˜YÍáíS#%è•EÒ|ª[MË(ø;@ÿTÍŠzž&¼Ð`ÜX©i`„ËТÈ>-aa;° Ò-š4#1Žñ¯‡×â˜ùûæ–$à<*Çž#A¶ðs!%Wãðn›&ʪ“ŸÚÓã(ž]fèùäR#$ !#.‚"#1±0iöG¡{üv¢Îòf_kø}[àÀ¢#/AÁ´#ãaódé…³êü?…æÎ¿sšb\Ÿ[¦p}ÿtl×ι¨2]4XŒd©1†ÖêÁœž°´íNg½'q¬#!Пhêà>3½““pvÐx’œ½l„€fïh¬ª¤ÂA.f%~Á^-.}µºšw{Ú&_.¢ˆ‡½•áÜÖzxÃÁƱ×äŒé°lkRÇ£œ”ˆm²Yàö†ÞÆÙâD_ey\#1¨Ð‰JwcL]ÖÒ4¨¦g…§ÖuUÃíT6UWhv…°ÓAI  „Úß̉ƒ‰A#.H†§í7Ui,¿Ý**-¬‰0ß#å4x.äÖ2i¾Pc1ƒŒ‚Ê„˜y3]É‘E¤€i.âwp+X=0銰l|(C<ª/x…¡®6oœÐÁçQ œÍ¦sP3FvvÏê@ úÙ¹ÑâÆcd& ØŽ 56‹ïéçOCºˆ=f¾ž¹Ø¦0­c©>¤ãÓÆFxb:añe‚î¡ 1XIJia#.5#1#/ò™  H% 13T#€Íö)A4hUå"}qx6¶M†Â#.¢È°ÛZm:È׿ž3b‚M$i ŒˆdæH$×mÛßr°r¢‚").¢!Ï:™í®üœPºíR­¦„Æ9—–¡ÔYLÔŒL+Ðã{™ÛXhcA“HzM½¯g æwÝC¶ã¦#/n1ÉÚ†}jáÚL¶”­âàÂ-™ äÔÔꙉi"¢erL<.Ë;bñøy¼^Ë{æ%µµLO\q2™œè9ÕÇFÙaÁ&\knÊLp²f½;†ü’1Ë)¼@.Õ"T³[•”C|÷É<#/½ùp"± Ž Ñ#/Ê#1² @/®ÃY«€Ä&ˆ4…èJÂVLAÈ+DB¶RŽ ¶¼Î½Ý@¯K3)[ïºÃ5,†<£¢*"š‚-\’®®híˆD`ŠN<ƒÄÊRÊ4ÇgR£–’;ëU ¤z,do¶b9έ• 5‹º» ®Þq< ¡¨GóÍ>\¼2×;¾Xy”H˜Â0¦ÙFÓ(@“Y.l€c‚Ús&´… !ßH’Út˜m*XeaLà>‡ªKK{;¤ÌI©0¶Ûm,ÅÉR=s§“™iIeó»K'Ðôå±ç¶#/Œ’úŒ‰Í.IˆfáÛpÞQ{HhM)ï‡ €ïjºlCl‚oOÁ¸»v4b²†o…ÌZÅÙæ£Î)²$;KÙ.žõeÖψ[m$KYq(tS¼®0»È×÷ºÂí1úEǶ[ÏR¾Ž ØR¯žÑ"¥‚ž6·8ÄÓ#1¨Þ2!äz"¦ÛìïõƒÑŇ>:»4ŽK'k…܈z%ˆ’_ ùdÒ-ŸlÉpte ûúÑáabõĵ UÕL¡ç]z18k¶ðŸ]`:«ÅÌ›‰¤OIÓ¹*•®:Ô*ÔE•@Œ¡®^­ÊçUUmxÁX!VÐ3iÞêÜŒ•*8°€q u>®f°»žCë´#/ó†¹Ó6ÓQ•kpQO°ÒÒÂ’ÑÑww.¼,+ëEïÐDdà×&:U D TúTÊ7މ„̓ oNÛ˜éÁž €ÂãgL#/*\\Îù˜lްî`ÉPŸ«ÖÜÙ7N,èe;˜kŦ"B6gá.áÀàv¾Ð*¸ull`Ä…G–²…µä“rLó0…1C:i¡5 m-€¬E…TP hᆷÎ@·DË`á‚Á‹ÒEÐ"I„œùß#1:µf†Ò¼–áãbʘ¸S±®cLÎt&5õ@7Ë‹ªpÁçãÇä¹ç iÍžuÈÂik#ž³Eu³¼¡Ù¦¤f†ÕLu±¬V]2L]®%¶KãLC¥ ï²8ks ºŽ™´Ž'ç×ù~>NiÁÓhRä6pþy«i??Mö­÷VQ$ À3Mˆ9LþŽxðæË̬y¬<uÓÈ‚›¥†§ý1éiugHÇ8Ü\³2.z˜b›ÔìùríŽIª›øŒáä˜N Ùðúii:V}œ3†äÁbdtBC$µ‡(¨LP}].ÜöŸ#/ô±Î—W5(õ®^B–(h{ ó×7ìßK#1¡Q±„¶BÁQcÈltÐÍ#1ƒ¤ôf·êÒÚûDMµÛÐ;rù{xç9R¤™#/ɼ‹Ò,ô›u†Ì%…NÞV9ª~¸NÍ“~y°Ó,àœÍ5 E,o¦É±¨˜ˆk»v[=F¯gMPçÌCm¸™¬Ò«fˆ[ÓË`>WRâ`—s eœS±›yš‡ž.wdZd 8q¢â—&GND®–årõ‰†æ#.~5šiS·^ÓŽÚ¾ ›ÓRÚ–vA=u¹8LÉS.®Ï& ݨÞ"Ó%Í9cˆç¬¡š`µÚ) b´i{UÝùkŠoº#g{|`ß.gtÇÏ5шDr.¢Epn<.GQ$$¢;nèa*ÙRèjìÚš1E Ôî5¦’*bf~‰#‹O‡l6ÔP‰y©]ßhÛ¬‚+š¥ mT(ïß»œ®‚Ÿ$Ö##.VpÀæ˜(²9Ø)1·WÚæ Yˆä\ÔnÒØŠ4!€×FÅ(p7Ë‘g¦’ÛÄläXÄ4ë"Ñ¢äêfa7g`¨FO."ì·Loî‡yão{uêøT8›†ŒpfeЍ”`ã¦JÂ…ÇÙ™¨Ø d ÆA!R¥)R1"o¶—y¡c2–Us¢!{‰˜–7!‘[¢jBeHš,*#1w6k#/&›ÙP3 Ä¨oÇ’¬p„Š5ahHʹm³“6ŽPw#1¹qÔ 1250›š* TCWd)ˆQƒa‘ÊÀ(\“v ‚Cs8£i3€¦ˆ¼aNyã·ÇÁ‰09p¼QtÄ»ÄÊQº ±"*Ð6òÎæÕFXD-±3DT3¼4TY¨#LJ ¡…?@dß/;Ùä9ö¸PtEA”QQ@c^š¿~º*j»òÙWlžÑ¸h9üy¬Hw…DÚ¨°‰ #.-@ÕÐ#.N² sÏøÏ¦ñ@à ϑ?Î>×ô¹³éUP$O·Û:æn-Œt_½ô©†ºKi}çlo¬…*ŽH„n߬}#/e1¥"l£5l‚¨ƒf^óaµw9Ÿ,ÌÏ:h )1M©R't螢à­4îŸEA0l–æÞìÏYœás5Ú¤f°fšmª€Ü»!åBÈŽF¿JY¯—EU×=j䪧Ûj"þ˜&øÈË™E#1÷Ò«"/Q8T‡Q5÷VõL”R0ÒR'#.õ‹¿§4Ûû9÷³z;³ƒw)S÷UJ=åÊ‘8r°2ÅœDÈÒ*µSð²É4ÇtõщZEõ‘4É“Ç'{¶?#š´ÑçÓ|r~¶´®¤C€ˆœã h©_°. ÚG…SeõÕNźyÎÜa·4}¯{n¸Ýg1Y7zg ÷Õ™CÄ'×Û_Ù` ¤TO…2ãÅùÏ¥N¾© ¸H9s*Ú¶»¢w‰ñˆdgÝw|n‡ ÜâuŸ>Ì:Â(?¡¤u„ªVC²m%j4R5Q¡*BD8xå1åZåMÌÈÖ;n®S´•£’¦0Ä…Úõ<ç.í¯$ÈL’™¯Ó¼nMy»´f¤WyÛל%cƒÂ #!&8å ÔrK0Ä$)oVÝ36òZ-åMzžzÝs$¨SZ4(Ðü´™#.JcÅ‘Ö$eĺ=ÁŠ˜—+¸a”Ä…4ÍűF-E-Ýh233Gb¬ÌdXâA‚¤PDü#1„Жu~¯?GÙ>ž¿#.OÁ3î=»ðæD)©éNiÖ Iã#.„3>‘ÍB2H²rS'Óµ¶ŠªŒ@ˆ4:HñÜÂÛ ‚­#/‰]’&ÉL-.wÌ´È4Hä*Y®Ò&‹`£§!(8³¬&Ñ9\‰AàÄv£É–ž/|œÄ;ŸmžŸ‚u׺D¢“µQ6öT»Íé}×&½¾æeDÎ3úåíÃø›l –¦Ç¡œ?@°ó ­bÎ&Ý–UŠó“SŽlá°MýþìX‡ŠZ`?¤iɺ)f!PÄÄy8–å Ϫ4átâÅÙ1`x…ÇÍ#.ñAåçA7’äÈIE=½ÃmÝ´v‚¦ÎÌlÁæ¼RBP]03aƒƒÀâËS*ý ?9j‹.ZDcŸ—›m›]«ß¡¼ÜV*‘ŠÄOZ#1Gì0ÛÊn”ê[”UŽQq X˰3{€ý©œQ(ý•n´kîq­~•¶ê¦[M$£XÛ6ÚÒ”dÛdÖ™_‚k%µìÖ¹k'+\ÚÛÞ@€b,ÀÒ#1%È¥EKØÁeZ‹"-’#1Ý"8`¡Áš*½‘à“ß2×*(ZT…‡/õà#.xš|²­ÞÆœô¹¼ÛF ˆÈ ,`¢ Mñw··ðžð©ïSb… ©C~7q˜ì Y@DHD#.QýÄëñZîW§u%;±Q„kÁô)#/ûìtèшÍáµCÜ<WÚ•ÈìɯBN•s¹ŒYÃa E,pÎ*‚æ#/”cB#Gd¤H#1Ô¹HÕb‰#.Q‘²¸› áQ"d²:šbt-•cUe¦\¢A3LmWget««n^n×Z*žn%’E0„‚‚ŠOλ#.ò;NÞ‡o^m€q»¶„ĨÛ#.d®j¯%,-#.r¹‘f™¥­>õðÇ‘ìäõ%†"§ƒññøþNË#/m‚©¾0‡O*0íEë-¡sñ,˜ËUí€ã<ŽÁÖ÷FÉà;Ý䘱@2|I(ÄÛ¯C —¦¡.ù”~M|•o¡€œ°º"È£­I)‚€ÄØÇ¦’ ÖûÅZ#/Ûnè¤ÿ!7w´ð›,@#.-„n¨Y“:KËð飿IŽmcïù ÇÃFs|0V!;ÄÇ9D¹ã+0:ÕwlàQ½ÑÔÔ ÞU¦ÝÔÛV¾“o‡#/ב˜,tü·âì_,`kjËÇ;8˜þ‚›dÁï†Ìè4+»ÓV6§ÛíC뇾(‰Äã¡aŠH$ SILTª  ß¬4k·´,³³ éŽº̧U0~soœ>óàHz—Ä'Æ"Y.€.^w/$‰бLÚMIJ¥•L˜Û”3k[³#/a4#FÊÌj´•~ëEµqœ×LhF,R½PHqy`›ç¥=‘ÅŠ®¼#.ÛèÚ‡Šˆr;zN©±ÌM¬ú#.†Ã~]}šÄ€è@âÛ—ng=#.ˆ$  0røÓ£>’9'Ù¢¥¼´èîÞ%I9ó´U¼Oqó]A1UN°#1Tw1~|õé $:Ïf~|9E½õòï-î¯t•YfϯY„e.¦5J´Uæü¯_\RëåÖd5„‹Dí:)éó¸ãºF&øj†¦ô ±­ÅܲYw%ƒçDÊ#/\ ¡ÃAäÅÇQgP8ÅNIVl‘‹ª±†L*ff¡4È8„Ь(­ƒèvã†á#/¤Í°&-8RhIKVø£k û…-àê­òø»4ü ­µ[£'kÎn¶Ñ, mµ:²pNÔ9,#1 2Ì-ÊÊ®XzjLÓT am‘Àý[þ¸©Äñ@=!"Ä!'xZÔàÑ}CD-d2r”kHßâ­h`|ïF´·XaÉÂ_˜°ülÖÉŒ².šŒ)Éû Â`„Š`;{ýob–K¾u¬ðëF´ªøÛÇêL (þÆ0$E㔄Ïa=á$<¹™'`xtéÓ,dtba3¼(‰Ð7¡è‚0 °!Ø1sb¢oxÇ®écç<½ÄùýE ^žg˜—…£RJ$ˆs©;Á3ŠÍÊ%|º½¾`·ÞØb%%@F¡”ƒ ê˜’Þý³·Óó<û{£'URgxgÍ*B”ì6иw¥ßÕÏ듟s¦¬ØÑ»n!kÉCÁ…6ê¹gŒàþå)8üÛþ¯/†úQ¨Eˆ…ÀˆÝÍá™yAIî#.üÍÛwø.òR:ö}½í».ݯ®l€0.>–l×Ììw1Í|=!s§àHX´.•>Q¸þ‹X€#.lÑ@¥^*=)µ–'nýR¶kû)½_N(ÀÉFfz먓b4i7:î¹# ˆ°cÒ‹ŠŠÒϬJnË’ñT¶Ó„””m)ei#/£D¼›¶‹y&Å¡+I´ˆcDË›¤’Å€±ˆ¥HÂ/é„!Qï?jE7 #.C$¡¼¶ý¿_Äòó”PQUò+èøXô/f{ƒÈ’³Âz>«é#.Å=}•õ~&"Ém&©VciK-¢ ˆ! ÜÈÞ>ŽÑϲD$ Ä…F¥#chÒ³%SoÍÚý•ðê¾›û<4ˆ£F&ÆÃTͱV+)J”Òóöxc0b"(†Á£Ð #.´D×í„¥®\'¼H™˜Y/2)é`·Îò©#"uA ÈHÕ6cMdVÊØ¢ØÙ‘‹c-¦”[4ž›Ç‹m"¡ "@‚ —€„^"ù å~Š©´ÿù:jíì_ÕþRÌÐuM>+ì=_^Ú˜û3*l¹ ¾í´»ƒêdÁ¼;‘)1ï#/˜(µ¡nbŽšLvè%Eó=¨>­Ãñ¦«Îߦ÷°XŽÚå¶(Ûð9_ÌŸßyÑ$TßeÞÀžÄ˜ò#/ì…Å„A€Ú~Ho^¼½ø E Dm°6½ª›A ~¬s²&H'Dø­@!BAzÅ[ÖÞ|ªóz×Ö¤ÕÍÑ­Q6´–QjgSe{îÞSÚñdI š·Óo¢q¶` öi ¡´"„‚4+±©ò£O"!"0åAhŒ²÷êeÙ¯_ºƒ¤>2¡^xÐb3·8õZŒç¸ÃXezsKn&3C°?’…³»8–”¥¶Î4Í}4#1éóâCyù#`š°¢ow%olÃV“4àÃþZ>¹oUðc¯j{ó¹Ü줎xdC×s3D>°ØKÐGé~p}t)üA¯Ö—ݾee/»Ù(5K4Ò¤„¤Te±ŠŠ#/DmRmP¥£_¶ûš¼a¡EhÚ“LÐlJšÉ¿Õ}ïËø|;eš¨²#1"E…a‚…Œ.ÄŒ%®¯Î7DXÂ/:)bdÂEÑ46î(Ô,”#/@=oœû#Ÿݶô;î;Hˆÿp²±ZŒkAZPÕ¬Ì dU F)í;oÃɳâÎíí¥VY) ~ã_v?sIö¡c yøœãm~FšÆmïIRR™G¸ó« ü  Ðvú /¼Üî?§"­"´¥·ó¼<ÒÈÝÛ2Ä6”pXY: F13ž—h¦ª¥d¹å¡Dà(¢ÆZyb¼ë]7Ž·Ì·Y¤ÔÑJ–õ›#1í§vmwvWy¼­uM¢Émé­ÉQo7ušnÌÊ®¹»j*çjl™e«Ë»i“k»«»µ²l©*dJl!YT4XXH€£%"ÂC+˵Ы•yy-³½7¯."ªSKdÔ™R¯J×kyÕמv­xÖ6ÊL̪øK]l¥¯]Ü·k»u–3[.šé+–"gl6<Ù0 (Ñ·h†÷™ Á«#1+ù *üª«JQhX²è_2ȨÅPLÉàHü´DsèÞס¸c†á_±‹m4ÂI͵ÁrùižxPÓŒ#1x&îUh¯{¹Ô‡d›.h·#]ßÐåû®#.y`=°\Ä~‹âTöüøÃëÌ=Þ4.(XÙÆ0(Û3|#/a¸L©M¸Cª5Vãq.$-ݯ/#íýe¦F$Ç©CUR§WÊtÎÑo5ši¦(Æû|ŒÌ=rì0û¶<â "Š8®QHÄN%ápo¯Rc Ns÷õ1´Š.Ö‘ í3`Eƒ"ÕIt3°ÝÒbz|÷˜º²"îü‹îÊIq#.ˆ;r¿yÕô:}²V]×J“×[Ø7èš°íå¸'vnxƒaZ#‘ÃL#ÞDš}ÿ q3ÚM^O-˜DPèE-ÑJ¾kU;`g2ã¶qºÉe²÷'± ]#1CJ>h_ ýΞi&êÀŸ–LëW¬ÍxÓ2ap5šõbQ8³ÀÕMÊ#/å›()ZN¦[-ˆL÷ä[e¸j™žôíj hìaÛ»V`¢úC#©lc¿Ð5Þ›wt«¼´Ê“h«Å¸¥(´(Œ(€QjÀ$4®?Ë9=A§<òÿJ=®Ó:9$v*µ³œ:È%ÝÎ6 ãós#/²+¤3¦KD”êl3æhè@¡]Þ3œÕ—[þ!D¹kšw©Ï2 -“=–må6ÛlÊa5HÐäþ (-ˆˆ¬Ïu@«Ù·ò–ÉæëacÚ(4¿®#/¬jþJªò›Q‘hõ+l­âqUÛÊœ:;ëBˆqÁ­Žp•fNä¤ý¤ò m60ƒL8é‡b¶ž0ÖæÁ0(ô/–XŠÒȤ¦Qh¢äu‰ ‡É‡-v1å& áô3d`%0ÆÇâhß»’ÛSòž°…“ñIÕ`‘‘ð6ÜÀèr^„ Š`øBÎgyèøhd@àæe­À­Ç¾‘ý–üÞ$<?…¶`4ÔJÖ“î³¼óŠRè¢$û³€Z @Tü»?yä}‚nš‘¿– H\²‘°¯›€úÞ,ž™#ˆDà b%Eà¥_Àcð»í¾à,ŽÃ¶=ZˆlT&ÏÛeVÑg÷ÿçÿåÿgý_ùvñÿÓÿ‡þ¿æþ¿ýüËÿÛfÍÝÿýÿðÿ‡þ¿òþ=¿oÑìùpÿ‡Ëðÿùø)þ~¨ÿËÿ(çâÿùxÿü?þÿáýÿãÿû¿ãÿ?‡ÿÏÍ®øáÿ.ùÿéÍà=yÛÿOýÓ»£çúþšûÔ>•ä0>Ñöæ}¿ð?:&J^ƒ¶ ’fUÃûÿŒŠ¤#À…AÞD¢áÁ²`ô¿Ôb%lȸŸàFÓ@ŸèH%QP††5étCýgùüýÎî±$&f}:ß>ÔÛ_¨ %Ë;d$ì×xAwi´C\z³#1k1eƒûlñå{+€ `DíñáÃrd:n(.l‡÷‹n·o·!ÏïS'Â/ûÒ3D ŒqŠ<\Ìv×o{Ü¿[›°T_ç#ðå÷ÚcYï̧h÷ê"¸5b·ûp)žÍqÿ óó±?âéÏ‚ÏñßÍ8ß›®éN1ïÐß>Ž—B%rŸëGG²âˆÑëßÕµ~5WºÕµék$>#/}eýP'ƒCŽ oúÚ‚EÏ‹ÿ¥ð°Œ(ßQZc˜é[U‘žElÌþÞ.ÒÀ7µ*jH†þÖF¾[©šcc[³A#ù¯2¼€Ù­?D®„#/ŸÍƒFÈ8žõ‹wb髇^ÜÌ |NÑàÚR©92Ý8hix2ËÙl`…ÕÁÊ[OG,ÞŠŠ;V©°Û«N‹šË,Ѧh5r¢f¥ ºÞ¦4Ä-Òi–#1,…~On°²]É`Ë«vL„“‘•MŰ×¾!Øßƒ¼%Ó;ÉŸE*Û8ï˜Ù2#/øFò÷µ’Ñ[¨|Ðiõ×1”z¦l&ÏS€c»»#/”+ˆ¥1dƒÈÄK’$dŒN-ró¶/#e™†2á ý/’#/¸™Ÿ–ZâYV~¶¬y”ë³y.‚u§HØÑ®Á’î#.±¤z.)~¹‚÷Ï)¿­í?Þ¬ïöÐ4ÃÕ‰%!‰<Ž;¾Œ,vêÖj¦+½%“èÅŠF>1'‚"aèñÂ]ÂŽ3d0UÊðÉQr΢Ö)P"‚y‡½âÆà*ß&Œ¶OðWuÌUñ3ç°XÎÃ4DT@öb€FH• 黟Šf>µ.9s2„Œ‚k¹ß.\ÔÓþ¥[,ó@B*\ððõúOwp™›<,ØÖH#1$` ìHty…#/ù浩b×Q#/!"i“¼Îf%‰™ÃÿæB{ed€ŸH+m¹1Þ[žf#`DÂ'>{¬(èlÐÚ[Ç¥j'F›šKm¥"&6(^7QM#/¨6ÆÚ–LÛh¶JÆHÒB,(ƒ0UqJª¹çB\O->™?æŸI«r.MÒLöž5MÑVô‚‚()l‘xže‰v¥#.Åb{À›CéEþ1ÉœH²kÖ¡Ä"y ··k8#.Jy«På0 !þçjg'ÈÚ¾¿*ÕõâURDQä%:˜³={);?â9“—Ë%hº&zQ0‡fÙ9.ê !ö˜FUÿîŸã!”|ÄO—öû  áÇZBwnP.7ÅPLÝjIÅ­S€tÕ\pßÂàu¢^hšà² 3³¥«Œa¡å‚??-µ>c<As£3áÕ.ä¿^^ÚusPrʼn£×[òx› 6ÍGY…ûh´j~#16mÞÙôæé[Ý&v#/#/—LüM¬Õwg®Èå*žl °R|ß;vXÖñi·Ô×5%cäÕtÔZŒoÛÓF¯e«zš6Ô›ƒZ½óZåªöW—tuîÓAL¶•ÆT¥gJÓ!Ñ®±Há¡6EqMŒþZÓ«pkÿ1@.wÕœ€Úfí#1 o<]±sÌp]îÂïHf&0M‹ñéÐ\IAÓ]áy˜å~¥óX¬÷RÑÈ(.A<?ë:®þ`TÉ@$gà:4!-ÿWÛ<|¾ÙGMEx(l~Ì_D.§÷°#1-ÉCÔ§ÊlGÿlUc#.’D@!‹#/®Ôrqá®Û±Ûk›-«%¶™šámÛUÙjù›Ù©5í{kZý(Ú‘BPrGÄ÷÷}Ÿ#@ôÙøÏ¤M‘HA'»¯Á>¾¾Ö»ëtӸ̾VZ²q 14:£¬Ô’)í‰ôKd/¿ñÍÿ`¨‡çÝÿŸðBÞ+AáxŸ4’ dFü* F¿ñ›pó]?ŸþyTŸþ»ôh(ÔýÂZ»íûäß—'þ-Ÿþй‡÷ᛜá¬Yà ÿËÆÿþím^uù¼¾ï¥ØhåΜõëŽnRt;¶<ïxðò„|—xì<å…Þ¾ï?¼Ñyšÿå¸û='oý†^TŸ®?yñÊ)›þõ/Ÿ„¼ÍÒ,†±4ŽÐo‹Ÿ»G?ü•¤ØÊ2Ó¥íÅŽ×ËÿÏï5öYuAœþ›µþ>b_Fœ*Ú&Ê?…J?ñQk8+·‰ÅQÎ ¾ÍW Œ³˜50ï‘Ä>>»N´@΃++Çœ7„n yl%ððЉkÒÚs sÇãßëј:˜Ó$Éçw>ÕÞÅ$F±.>ÿ/—±%ÿrü(·MycèÈõeïÿÕX&Ò§B;bÞ©4>ckIBæÓZó×äÿ´JÔõŸÞ&%íùÞAÿü]ÉáBA£¬¯Ü
+#<==
diff --git a/.gitignore b/waflib/.gitignore
index 8d35cb3..8d35cb3 100644
--- a/.gitignore
+++ b/waflib/.gitignore
diff --git a/Build.py b/waflib/Build.py
index 1afcba6..1afcba6 100644
--- a/Build.py
+++ b/waflib/Build.py
diff --git a/waflib/COPYING b/waflib/COPYING
new file mode 100644
index 0000000..a4147d2
--- /dev/null
+++ b/waflib/COPYING
@@ -0,0 +1,25 @@
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. 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.
+
+3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR "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 AUTHOR 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.
diff --git a/ConfigSet.py b/waflib/ConfigSet.py
index b300bb5..b300bb5 100644
--- a/ConfigSet.py
+++ b/waflib/ConfigSet.py
diff --git a/Configure.py b/waflib/Configure.py
index d0a4793..d0a4793 100644
--- a/Configure.py
+++ b/waflib/Configure.py
diff --git a/Context.py b/waflib/Context.py
index bb47c92..bb47c92 100644
--- a/Context.py
+++ b/waflib/Context.py
diff --git a/Errors.py b/waflib/Errors.py
index bf75c1b..bf75c1b 100644
--- a/Errors.py
+++ b/waflib/Errors.py
diff --git a/Logs.py b/waflib/Logs.py
index 2a47516..2a47516 100644
--- a/Logs.py
+++ b/waflib/Logs.py
diff --git a/Node.py b/waflib/Node.py
index 4ac1ea8..4ac1ea8 100644
--- a/Node.py
+++ b/waflib/Node.py
diff --git a/Options.py b/waflib/Options.py
index ad802d4..ad802d4 100644
--- a/Options.py
+++ b/waflib/Options.py
diff --git a/README.md b/waflib/README.md
index c5361b9..c5361b9 100644
--- a/README.md
+++ b/waflib/README.md
diff --git a/Runner.py b/waflib/Runner.py
index 261084d..261084d 100644
--- a/Runner.py
+++ b/waflib/Runner.py
diff --git a/Scripting.py b/waflib/Scripting.py
index 749d4f2..749d4f2 100644
--- a/Scripting.py
+++ b/waflib/Scripting.py
diff --git a/Task.py b/waflib/Task.py
index 0fc449d..0fc449d 100644
--- a/Task.py
+++ b/waflib/Task.py
diff --git a/TaskGen.py b/waflib/TaskGen.py
index a74e643..a74e643 100644
--- a/TaskGen.py
+++ b/waflib/TaskGen.py
diff --git a/Tools/__init__.py b/waflib/Tools/__init__.py
index 079df35..079df35 100644
--- a/Tools/__init__.py
+++ b/waflib/Tools/__init__.py
diff --git a/Tools/ar.py b/waflib/Tools/ar.py
index b39b645..b39b645 100644
--- a/Tools/ar.py
+++ b/waflib/Tools/ar.py
diff --git a/Tools/asm.py b/waflib/Tools/asm.py
index b6f26fb..b6f26fb 100644
--- a/Tools/asm.py
+++ b/waflib/Tools/asm.py
diff --git a/Tools/bison.py b/waflib/Tools/bison.py
index eef56dc..eef56dc 100644
--- a/Tools/bison.py
+++ b/waflib/Tools/bison.py
diff --git a/Tools/c.py b/waflib/Tools/c.py
index effd6b6..effd6b6 100644
--- a/Tools/c.py
+++ b/waflib/Tools/c.py
diff --git a/Tools/c_aliases.py b/waflib/Tools/c_aliases.py
index c9d5369..c9d5369 100644
--- a/Tools/c_aliases.py
+++ b/waflib/Tools/c_aliases.py
diff --git a/Tools/c_config.py b/waflib/Tools/c_config.py
index d2b3c0d..d2b3c0d 100644
--- a/Tools/c_config.py
+++ b/waflib/Tools/c_config.py
diff --git a/Tools/c_osx.py b/waflib/Tools/c_osx.py
index f70b128..f70b128 100644
--- a/Tools/c_osx.py
+++ b/waflib/Tools/c_osx.py
diff --git a/Tools/c_preproc.py b/waflib/Tools/c_preproc.py
index 7e04b4a..7e04b4a 100644
--- a/Tools/c_preproc.py
+++ b/waflib/Tools/c_preproc.py
diff --git a/Tools/c_tests.py b/waflib/Tools/c_tests.py
index f858df5..f858df5 100644
--- a/Tools/c_tests.py
+++ b/waflib/Tools/c_tests.py
diff --git a/Tools/ccroot.py b/waflib/Tools/ccroot.py
index cfef8bf..cfef8bf 100644
--- a/Tools/ccroot.py
+++ b/waflib/Tools/ccroot.py
diff --git a/Tools/clang.py b/waflib/Tools/clang.py
index 3828e39..3828e39 100644
--- a/Tools/clang.py
+++ b/waflib/Tools/clang.py
diff --git a/Tools/clangxx.py b/waflib/Tools/clangxx.py
index 152013c..152013c 100644
--- a/Tools/clangxx.py
+++ b/waflib/Tools/clangxx.py
diff --git a/Tools/compiler_c.py b/waflib/Tools/compiler_c.py
index 2dba3f8..2dba3f8 100644
--- a/Tools/compiler_c.py
+++ b/waflib/Tools/compiler_c.py
diff --git a/Tools/compiler_cxx.py b/waflib/Tools/compiler_cxx.py
index 1af65a2..1af65a2 100644
--- a/Tools/compiler_cxx.py
+++ b/waflib/Tools/compiler_cxx.py
diff --git a/Tools/compiler_d.py b/waflib/Tools/compiler_d.py
index 43bb1f6..43bb1f6 100644
--- a/Tools/compiler_d.py
+++ b/waflib/Tools/compiler_d.py
diff --git a/Tools/compiler_fc.py b/waflib/Tools/compiler_fc.py
index 96b58e7..96b58e7 100644
--- a/Tools/compiler_fc.py
+++ b/waflib/Tools/compiler_fc.py
diff --git a/Tools/cs.py b/waflib/Tools/cs.py
index aecca6d..aecca6d 100644
--- a/Tools/cs.py
+++ b/waflib/Tools/cs.py
diff --git a/Tools/cxx.py b/waflib/Tools/cxx.py
index 194fad7..194fad7 100644
--- a/Tools/cxx.py
+++ b/waflib/Tools/cxx.py
diff --git a/Tools/d.py b/waflib/Tools/d.py
index e4cf73b..e4cf73b 100644
--- a/Tools/d.py
+++ b/waflib/Tools/d.py
diff --git a/Tools/d_config.py b/waflib/Tools/d_config.py
index 6637556..6637556 100644
--- a/Tools/d_config.py
+++ b/waflib/Tools/d_config.py
diff --git a/Tools/d_scan.py b/waflib/Tools/d_scan.py
index 14c6c31..14c6c31 100644
--- a/Tools/d_scan.py
+++ b/waflib/Tools/d_scan.py
diff --git a/Tools/dbus.py b/waflib/Tools/dbus.py
index d520f1c..d520f1c 100644
--- a/Tools/dbus.py
+++ b/waflib/Tools/dbus.py
diff --git a/Tools/dmd.py b/waflib/Tools/dmd.py
index 8917ca1..8917ca1 100644
--- a/Tools/dmd.py
+++ b/waflib/Tools/dmd.py
diff --git a/Tools/errcheck.py b/waflib/Tools/errcheck.py
index de8d75a..de8d75a 100644
--- a/Tools/errcheck.py
+++ b/waflib/Tools/errcheck.py
diff --git a/Tools/fc.py b/waflib/Tools/fc.py
index d9e8d8c..d9e8d8c 100644
--- a/Tools/fc.py
+++ b/waflib/Tools/fc.py
diff --git a/Tools/fc_config.py b/waflib/Tools/fc_config.py
index 222f3a5..222f3a5 100644
--- a/Tools/fc_config.py
+++ b/waflib/Tools/fc_config.py
diff --git a/Tools/fc_scan.py b/waflib/Tools/fc_scan.py
index 12cb0fc..12cb0fc 100644
--- a/Tools/fc_scan.py
+++ b/waflib/Tools/fc_scan.py
diff --git a/Tools/flex.py b/waflib/Tools/flex.py
index 2256657..2256657 100644
--- a/Tools/flex.py
+++ b/waflib/Tools/flex.py
diff --git a/Tools/g95.py b/waflib/Tools/g95.py
index f69ba4f..f69ba4f 100644
--- a/Tools/g95.py
+++ b/waflib/Tools/g95.py
diff --git a/Tools/gas.py b/waflib/Tools/gas.py
index 77afed7..77afed7 100644
--- a/Tools/gas.py
+++ b/waflib/Tools/gas.py
diff --git a/Tools/gcc.py b/waflib/Tools/gcc.py
index acdd473..acdd473 100644
--- a/Tools/gcc.py
+++ b/waflib/Tools/gcc.py
diff --git a/Tools/gdc.py b/waflib/Tools/gdc.py
index d89a66d..d89a66d 100644
--- a/Tools/gdc.py
+++ b/waflib/Tools/gdc.py
diff --git a/Tools/gfortran.py b/waflib/Tools/gfortran.py
index 1050667..1050667 100644
--- a/Tools/gfortran.py
+++ b/waflib/Tools/gfortran.py
diff --git a/Tools/glib2.py b/waflib/Tools/glib2.py
index 949fe37..949fe37 100644
--- a/Tools/glib2.py
+++ b/waflib/Tools/glib2.py
diff --git a/Tools/gnu_dirs.py b/waflib/Tools/gnu_dirs.py
index 2847071..2847071 100644
--- a/Tools/gnu_dirs.py
+++ b/waflib/Tools/gnu_dirs.py
diff --git a/Tools/gxx.py b/waflib/Tools/gxx.py
index 22c5d26..22c5d26 100644
--- a/Tools/gxx.py
+++ b/waflib/Tools/gxx.py
diff --git a/Tools/icc.py b/waflib/Tools/icc.py
index b6492c8..b6492c8 100644
--- a/Tools/icc.py
+++ b/waflib/Tools/icc.py
diff --git a/Tools/icpc.py b/waflib/Tools/icpc.py
index 8a6cc6c..8a6cc6c 100644
--- a/Tools/icpc.py
+++ b/waflib/Tools/icpc.py
diff --git a/Tools/ifort.py b/waflib/Tools/ifort.py
index 74934f3..74934f3 100644
--- a/Tools/ifort.py
+++ b/waflib/Tools/ifort.py
diff --git a/Tools/intltool.py b/waflib/Tools/intltool.py
index af95ba8..af95ba8 100644
--- a/Tools/intltool.py
+++ b/waflib/Tools/intltool.py
diff --git a/Tools/irixcc.py b/waflib/Tools/irixcc.py
index c3ae1ac..c3ae1ac 100644
--- a/Tools/irixcc.py
+++ b/waflib/Tools/irixcc.py
diff --git a/Tools/javaw.py b/waflib/Tools/javaw.py
index f6fd20c..f6fd20c 100644
--- a/Tools/javaw.py
+++ b/waflib/Tools/javaw.py
diff --git a/Tools/ldc2.py b/waflib/Tools/ldc2.py
index a51c344..a51c344 100644
--- a/Tools/ldc2.py
+++ b/waflib/Tools/ldc2.py
diff --git a/Tools/lua.py b/waflib/Tools/lua.py
index 15a333a..15a333a 100644
--- a/Tools/lua.py
+++ b/waflib/Tools/lua.py
diff --git a/Tools/md5_tstamp.py b/waflib/Tools/md5_tstamp.py
index 6428e46..6428e46 100644
--- a/Tools/md5_tstamp.py
+++ b/waflib/Tools/md5_tstamp.py
diff --git a/Tools/msvc.py b/waflib/Tools/msvc.py
index 17b347d..17b347d 100644
--- a/Tools/msvc.py
+++ b/waflib/Tools/msvc.py
diff --git a/Tools/nasm.py b/waflib/Tools/nasm.py
index 411d582..411d582 100644
--- a/Tools/nasm.py
+++ b/waflib/Tools/nasm.py
diff --git a/Tools/nobuild.py b/waflib/Tools/nobuild.py
index 2e4b055..2e4b055 100644
--- a/Tools/nobuild.py
+++ b/waflib/Tools/nobuild.py
diff --git a/Tools/perl.py b/waflib/Tools/perl.py
index 32b03fb..32b03fb 100644
--- a/Tools/perl.py
+++ b/waflib/Tools/perl.py
diff --git a/Tools/python.py b/waflib/Tools/python.py
index 25841d0..25841d0 100644
--- a/Tools/python.py
+++ b/waflib/Tools/python.py
diff --git a/Tools/qt5.py b/waflib/Tools/qt5.py
index 4f9c690..4f9c690 100644
--- a/Tools/qt5.py
+++ b/waflib/Tools/qt5.py
diff --git a/Tools/ruby.py b/waflib/Tools/ruby.py
index 8d92a79..8d92a79 100644
--- a/Tools/ruby.py
+++ b/waflib/Tools/ruby.py
diff --git a/Tools/suncc.py b/waflib/Tools/suncc.py
index 33d34fc..33d34fc 100644
--- a/Tools/suncc.py
+++ b/waflib/Tools/suncc.py
diff --git a/Tools/suncxx.py b/waflib/Tools/suncxx.py
index 3b384f6..3b384f6 100644
--- a/Tools/suncxx.py
+++ b/waflib/Tools/suncxx.py
diff --git a/Tools/tex.py b/waflib/Tools/tex.py
index eaf9fdb..eaf9fdb 100644
--- a/Tools/tex.py
+++ b/waflib/Tools/tex.py
diff --git a/Tools/vala.py b/waflib/Tools/vala.py
index 822ec50..822ec50 100644
--- a/Tools/vala.py
+++ b/waflib/Tools/vala.py
diff --git a/Tools/waf_unit_test.py b/waflib/Tools/waf_unit_test.py
index a71ed1c..a71ed1c 100644
--- a/Tools/waf_unit_test.py
+++ b/waflib/Tools/waf_unit_test.py
diff --git a/Tools/winres.py b/waflib/Tools/winres.py
index 586c596..586c596 100644
--- a/Tools/winres.py
+++ b/waflib/Tools/winres.py
diff --git a/Tools/xlc.py b/waflib/Tools/xlc.py
index 134dd41..134dd41 100644
--- a/Tools/xlc.py
+++ b/waflib/Tools/xlc.py
diff --git a/Tools/xlcxx.py b/waflib/Tools/xlcxx.py
index 76aa59b..76aa59b 100644
--- a/Tools/xlcxx.py
+++ b/waflib/Tools/xlcxx.py
diff --git a/Utils.py b/waflib/Utils.py
index b4665c4..b4665c4 100644
--- a/Utils.py
+++ b/waflib/Utils.py
diff --git a/__init__.py b/waflib/__init__.py
index 079df35..079df35 100644
--- a/__init__.py
+++ b/waflib/__init__.py
diff --git a/ansiterm.py b/waflib/ansiterm.py
index 0d20c63..0d20c63 100644
--- a/ansiterm.py
+++ b/waflib/ansiterm.py
diff --git a/extras/__init__.py b/waflib/extras/__init__.py
index c8a3c34..c8a3c34 100644
--- a/extras/__init__.py
+++ b/waflib/extras/__init__.py
diff --git a/extras/autowaf.py b/waflib/extras/autowaf.py
index a4a06ba..a4a06ba 100644
--- a/extras/autowaf.py
+++ b/waflib/extras/autowaf.py
diff --git a/extras/batched_cc.py b/waflib/extras/batched_cc.py
index aad2872..aad2872 100644
--- a/extras/batched_cc.py
+++ b/waflib/extras/batched_cc.py
diff --git a/extras/biber.py b/waflib/extras/biber.py
index fd9db4e..fd9db4e 100644
--- a/extras/biber.py
+++ b/waflib/extras/biber.py
diff --git a/extras/bjam.py b/waflib/extras/bjam.py
index 8e04d3a..8e04d3a 100644
--- a/extras/bjam.py
+++ b/waflib/extras/bjam.py
diff --git a/extras/blender.py b/waflib/extras/blender.py
index e5efc28..e5efc28 100644
--- a/extras/blender.py
+++ b/waflib/extras/blender.py
diff --git a/extras/boo.py b/waflib/extras/boo.py
index 06623d4..06623d4 100644
--- a/extras/boo.py
+++ b/waflib/extras/boo.py
diff --git a/extras/boost.py b/waflib/extras/boost.py
index c2aaaa9..c2aaaa9 100644
--- a/extras/boost.py
+++ b/waflib/extras/boost.py
diff --git a/extras/build_file_tracker.py b/waflib/extras/build_file_tracker.py
index c4f26fd..c4f26fd 100644
--- a/extras/build_file_tracker.py
+++ b/waflib/extras/build_file_tracker.py
diff --git a/extras/build_logs.py b/waflib/extras/build_logs.py
index cdf8ed0..cdf8ed0 100644
--- a/extras/build_logs.py
+++ b/waflib/extras/build_logs.py
diff --git a/extras/buildcopy.py b/waflib/extras/buildcopy.py
index a6d9ac8..a6d9ac8 100644
--- a/extras/buildcopy.py
+++ b/waflib/extras/buildcopy.py
diff --git a/extras/c_bgxlc.py b/waflib/extras/c_bgxlc.py
index 6e3eaf7..6e3eaf7 100644
--- a/extras/c_bgxlc.py
+++ b/waflib/extras/c_bgxlc.py
diff --git a/extras/c_dumbpreproc.py b/waflib/extras/c_dumbpreproc.py
index ce9e1a4..ce9e1a4 100644
--- a/extras/c_dumbpreproc.py
+++ b/waflib/extras/c_dumbpreproc.py
diff --git a/extras/c_emscripten.py b/waflib/extras/c_emscripten.py
index e1ac494..e1ac494 100644
--- a/extras/c_emscripten.py
+++ b/waflib/extras/c_emscripten.py
diff --git a/extras/c_nec.py b/waflib/extras/c_nec.py
index 96bfae4..96bfae4 100644
--- a/extras/c_nec.py
+++ b/waflib/extras/c_nec.py
diff --git a/extras/cabal.py b/waflib/extras/cabal.py
index e10a0d1..e10a0d1 100644
--- a/extras/cabal.py
+++ b/waflib/extras/cabal.py
diff --git a/extras/cfg_altoptions.py b/waflib/extras/cfg_altoptions.py
index 47b1189..47b1189 100644
--- a/extras/cfg_altoptions.py
+++ b/waflib/extras/cfg_altoptions.py
diff --git a/extras/clang_compilation_database.py b/waflib/extras/clang_compilation_database.py
index 4d9b5e2..4d9b5e2 100644
--- a/extras/clang_compilation_database.py
+++ b/waflib/extras/clang_compilation_database.py
diff --git a/extras/codelite.py b/waflib/extras/codelite.py
index 523302c..523302c 100644
--- a/extras/codelite.py
+++ b/waflib/extras/codelite.py
diff --git a/extras/color_gcc.py b/waflib/extras/color_gcc.py
index b68c5eb..b68c5eb 100644
--- a/extras/color_gcc.py
+++ b/waflib/extras/color_gcc.py
diff --git a/extras/color_rvct.py b/waflib/extras/color_rvct.py
index f89ccbd..f89ccbd 100644
--- a/extras/color_rvct.py
+++ b/waflib/extras/color_rvct.py
diff --git a/extras/compat15.py b/waflib/extras/compat15.py
index 0e74df8..0e74df8 100644
--- a/extras/compat15.py
+++ b/waflib/extras/compat15.py
diff --git a/extras/cppcheck.py b/waflib/extras/cppcheck.py
index 13ff424..13ff424 100644
--- a/extras/cppcheck.py
+++ b/waflib/extras/cppcheck.py
diff --git a/extras/cpplint.py b/waflib/extras/cpplint.py
index e3302e5..e3302e5 100644
--- a/extras/cpplint.py
+++ b/waflib/extras/cpplint.py
diff --git a/extras/cross_gnu.py b/waflib/extras/cross_gnu.py
index 309f53b..309f53b 100644
--- a/extras/cross_gnu.py
+++ b/waflib/extras/cross_gnu.py
diff --git a/extras/cython.py b/waflib/extras/cython.py
index 481d6f4..481d6f4 100644
--- a/extras/cython.py
+++ b/waflib/extras/cython.py
diff --git a/extras/dcc.py b/waflib/extras/dcc.py
index c1a57c0..c1a57c0 100644
--- a/extras/dcc.py
+++ b/waflib/extras/dcc.py
diff --git a/extras/distnet.py b/waflib/extras/distnet.py
index 09a31a6..09a31a6 100644
--- a/extras/distnet.py
+++ b/waflib/extras/distnet.py
diff --git a/extras/doxygen.py b/waflib/extras/doxygen.py
index 28f56e9..28f56e9 100644
--- a/extras/doxygen.py
+++ b/waflib/extras/doxygen.py
diff --git a/extras/dpapi.py b/waflib/extras/dpapi.py
index b94d482..b94d482 100644
--- a/extras/dpapi.py
+++ b/waflib/extras/dpapi.py
diff --git a/extras/eclipse.py b/waflib/extras/eclipse.py
index bb78741..bb78741 100644
--- a/extras/eclipse.py
+++ b/waflib/extras/eclipse.py
diff --git a/extras/erlang.py b/waflib/extras/erlang.py
index 49f6d5b..49f6d5b 100644
--- a/extras/erlang.py
+++ b/waflib/extras/erlang.py
diff --git a/extras/fast_partial.py b/waflib/extras/fast_partial.py
index b3af513..b3af513 100644
--- a/extras/fast_partial.py
+++ b/waflib/extras/fast_partial.py
diff --git a/extras/fc_bgxlf.py b/waflib/extras/fc_bgxlf.py
index cca1810..cca1810 100644
--- a/extras/fc_bgxlf.py
+++ b/waflib/extras/fc_bgxlf.py
diff --git a/extras/fc_cray.py b/waflib/extras/fc_cray.py
index da733fa..da733fa 100644
--- a/extras/fc_cray.py
+++ b/waflib/extras/fc_cray.py
diff --git a/extras/fc_nag.py b/waflib/extras/fc_nag.py
index edcb218..edcb218 100644
--- a/extras/fc_nag.py
+++ b/waflib/extras/fc_nag.py
diff --git a/extras/fc_nec.py b/waflib/extras/fc_nec.py
index 67c8680..67c8680 100644
--- a/extras/fc_nec.py
+++ b/waflib/extras/fc_nec.py
diff --git a/extras/fc_open64.py b/waflib/extras/fc_open64.py
index 413719f..413719f 100644
--- a/extras/fc_open64.py
+++ b/waflib/extras/fc_open64.py
diff --git a/extras/fc_pgfortran.py b/waflib/extras/fc_pgfortran.py
index afb2817..afb2817 100644
--- a/extras/fc_pgfortran.py
+++ b/waflib/extras/fc_pgfortran.py
diff --git a/extras/fc_solstudio.py b/waflib/extras/fc_solstudio.py
index 53766df..53766df 100644
--- a/extras/fc_solstudio.py
+++ b/waflib/extras/fc_solstudio.py
diff --git a/extras/fc_xlf.py b/waflib/extras/fc_xlf.py
index 5a3da03..5a3da03 100644
--- a/extras/fc_xlf.py
+++ b/waflib/extras/fc_xlf.py
diff --git a/extras/file_to_object.py b/waflib/extras/file_to_object.py
index 1393b51..1393b51 100644
--- a/extras/file_to_object.py
+++ b/waflib/extras/file_to_object.py
diff --git a/extras/fluid.py b/waflib/extras/fluid.py
index 4814a35..4814a35 100644
--- a/extras/fluid.py
+++ b/waflib/extras/fluid.py
diff --git a/extras/freeimage.py b/waflib/extras/freeimage.py
index f27e525..f27e525 100644
--- a/extras/freeimage.py
+++ b/waflib/extras/freeimage.py
diff --git a/extras/fsb.py b/waflib/extras/fsb.py
index 1b8f398..1b8f398 100644
--- a/extras/fsb.py
+++ b/waflib/extras/fsb.py
diff --git a/extras/fsc.py b/waflib/extras/fsc.py
index c67e70b..c67e70b 100644
--- a/extras/fsc.py
+++ b/waflib/extras/fsc.py
diff --git a/extras/gccdeps.py b/waflib/extras/gccdeps.py
index d9758ab..d9758ab 100644
--- a/extras/gccdeps.py
+++ b/waflib/extras/gccdeps.py
diff --git a/extras/gdbus.py b/waflib/extras/gdbus.py
index 0e0476e..0e0476e 100644
--- a/extras/gdbus.py
+++ b/waflib/extras/gdbus.py
diff --git a/extras/gob2.py b/waflib/extras/gob2.py
index b4fa3b9..b4fa3b9 100644
--- a/extras/gob2.py
+++ b/waflib/extras/gob2.py
diff --git a/extras/halide.py b/waflib/extras/halide.py
index 6078e38..6078e38 100644
--- a/extras/halide.py
+++ b/waflib/extras/halide.py
diff --git a/extras/javatest.py b/waflib/extras/javatest.py
index 979b8d8..979b8d8 100755
--- a/extras/javatest.py
+++ b/waflib/extras/javatest.py
diff --git a/extras/kde4.py b/waflib/extras/kde4.py
index e49a9ec..e49a9ec 100644
--- a/extras/kde4.py
+++ b/waflib/extras/kde4.py
diff --git a/extras/local_rpath.py b/waflib/extras/local_rpath.py
index b2507e1..b2507e1 100644
--- a/extras/local_rpath.py
+++ b/waflib/extras/local_rpath.py
diff --git a/extras/lv2.py b/waflib/extras/lv2.py
index 815987f..815987f 100644
--- a/extras/lv2.py
+++ b/waflib/extras/lv2.py
diff --git a/extras/make.py b/waflib/extras/make.py
index 933d9ca..933d9ca 100644
--- a/extras/make.py
+++ b/waflib/extras/make.py
diff --git a/extras/midl.py b/waflib/extras/midl.py
index 43e6cf9..43e6cf9 100644
--- a/extras/midl.py
+++ b/waflib/extras/midl.py
diff --git a/extras/msvcdeps.py b/waflib/extras/msvcdeps.py
index fc1ecd4..fc1ecd4 100644
--- a/extras/msvcdeps.py
+++ b/waflib/extras/msvcdeps.py
diff --git a/extras/msvs.py b/waflib/extras/msvs.py
index 8aa2db0..8aa2db0 100644
--- a/extras/msvs.py
+++ b/waflib/extras/msvs.py
diff --git a/extras/netcache_client.py b/waflib/extras/netcache_client.py
index dc49048..dc49048 100644
--- a/extras/netcache_client.py
+++ b/waflib/extras/netcache_client.py
diff --git a/extras/objcopy.py b/waflib/extras/objcopy.py
index 82d8359..82d8359 100644
--- a/extras/objcopy.py
+++ b/waflib/extras/objcopy.py
diff --git a/extras/ocaml.py b/waflib/extras/ocaml.py
index afe73c0..afe73c0 100644
--- a/extras/ocaml.py
+++ b/waflib/extras/ocaml.py
diff --git a/extras/package.py b/waflib/extras/package.py
index c06498e..c06498e 100644
--- a/extras/package.py
+++ b/waflib/extras/package.py
diff --git a/extras/parallel_debug.py b/waflib/extras/parallel_debug.py
index 35883a3..35883a3 100644
--- a/extras/parallel_debug.py
+++ b/waflib/extras/parallel_debug.py
diff --git a/extras/pch.py b/waflib/extras/pch.py
index 103e752..103e752 100644
--- a/extras/pch.py
+++ b/waflib/extras/pch.py
diff --git a/extras/pep8.py b/waflib/extras/pep8.py
index 676beed..676beed 100644
--- a/extras/pep8.py
+++ b/waflib/extras/pep8.py
diff --git a/extras/pgicc.py b/waflib/extras/pgicc.py
index 9790b9c..9790b9c 100644
--- a/extras/pgicc.py
+++ b/waflib/extras/pgicc.py
diff --git a/extras/pgicxx.py b/waflib/extras/pgicxx.py
index eae121c..eae121c 100644
--- a/extras/pgicxx.py
+++ b/waflib/extras/pgicxx.py
diff --git a/extras/proc.py b/waflib/extras/proc.py
index 764abec..764abec 100644
--- a/extras/proc.py
+++ b/waflib/extras/proc.py
diff --git a/extras/protoc.py b/waflib/extras/protoc.py
index f3cb4d8..f3cb4d8 100644
--- a/extras/protoc.py
+++ b/waflib/extras/protoc.py
diff --git a/extras/pyqt5.py b/waflib/extras/pyqt5.py
index c21dfa7..c21dfa7 100644
--- a/extras/pyqt5.py
+++ b/waflib/extras/pyqt5.py
diff --git a/extras/pytest.py b/waflib/extras/pytest.py
index 7dd5a1a..7dd5a1a 100644
--- a/extras/pytest.py
+++ b/waflib/extras/pytest.py
diff --git a/extras/qnxnto.py b/waflib/extras/qnxnto.py
index 1158124..1158124 100644
--- a/extras/qnxnto.py
+++ b/waflib/extras/qnxnto.py
diff --git a/extras/qt4.py b/waflib/extras/qt4.py
index 90cae7e..90cae7e 100644
--- a/extras/qt4.py
+++ b/waflib/extras/qt4.py
diff --git a/extras/relocation.py b/waflib/extras/relocation.py
index 7e821f4..7e821f4 100644
--- a/extras/relocation.py
+++ b/waflib/extras/relocation.py
diff --git a/extras/remote.py b/waflib/extras/remote.py
index 3b038f7..3b038f7 100644
--- a/extras/remote.py
+++ b/waflib/extras/remote.py
diff --git a/extras/resx.py b/waflib/extras/resx.py
index caf4d31..caf4d31 100644
--- a/extras/resx.py
+++ b/waflib/extras/resx.py
diff --git a/extras/review.py b/waflib/extras/review.py
index 561e062..561e062 100644
--- a/extras/review.py
+++ b/waflib/extras/review.py
diff --git a/extras/rst.py b/waflib/extras/rst.py
index f3c3a5e..f3c3a5e 100644
--- a/extras/rst.py
+++ b/waflib/extras/rst.py
diff --git a/extras/run_do_script.py b/waflib/extras/run_do_script.py
index f3c5812..f3c5812 100644
--- a/extras/run_do_script.py
+++ b/waflib/extras/run_do_script.py
diff --git a/extras/run_m_script.py b/waflib/extras/run_m_script.py
index b5f27eb..b5f27eb 100644
--- a/extras/run_m_script.py
+++ b/waflib/extras/run_m_script.py
diff --git a/extras/run_py_script.py b/waflib/extras/run_py_script.py
index 3670381..3670381 100644
--- a/extras/run_py_script.py
+++ b/waflib/extras/run_py_script.py
diff --git a/extras/run_r_script.py b/waflib/extras/run_r_script.py
index b0d8f2b..b0d8f2b 100644
--- a/extras/run_r_script.py
+++ b/waflib/extras/run_r_script.py
diff --git a/extras/sas.py b/waflib/extras/sas.py
index 754c614..754c614 100644
--- a/extras/sas.py
+++ b/waflib/extras/sas.py
diff --git a/extras/satellite_assembly.py b/waflib/extras/satellite_assembly.py
index 005eb07..005eb07 100644
--- a/extras/satellite_assembly.py
+++ b/waflib/extras/satellite_assembly.py
diff --git a/extras/scala.py b/waflib/extras/scala.py
index a9880f0..a9880f0 100644
--- a/extras/scala.py
+++ b/waflib/extras/scala.py
diff --git a/extras/slow_qt4.py b/waflib/extras/slow_qt4.py
index ec7880b..ec7880b 100644
--- a/extras/slow_qt4.py
+++ b/waflib/extras/slow_qt4.py
diff --git a/extras/softlink_libs.py b/waflib/extras/softlink_libs.py
index 50c777f..50c777f 100644
--- a/extras/softlink_libs.py
+++ b/waflib/extras/softlink_libs.py
diff --git a/extras/stale.py b/waflib/extras/stale.py
index cac3f46..cac3f46 100644
--- a/extras/stale.py
+++ b/waflib/extras/stale.py
diff --git a/extras/stracedeps.py b/waflib/extras/stracedeps.py
index 37d82cb..37d82cb 100644
--- a/extras/stracedeps.py
+++ b/waflib/extras/stracedeps.py
diff --git a/extras/swig.py b/waflib/extras/swig.py
index fd3d6d2..fd3d6d2 100644
--- a/extras/swig.py
+++ b/waflib/extras/swig.py
diff --git a/extras/syms.py b/waflib/extras/syms.py
index dfa0059..dfa0059 100644
--- a/extras/syms.py
+++ b/waflib/extras/syms.py
diff --git a/extras/ticgt.py b/waflib/extras/ticgt.py
index f43a7ea..f43a7ea 100644
--- a/extras/ticgt.py
+++ b/waflib/extras/ticgt.py
diff --git a/extras/unity.py b/waflib/extras/unity.py
index 78128ed..78128ed 100644
--- a/extras/unity.py
+++ b/waflib/extras/unity.py
diff --git a/extras/use_config.py b/waflib/extras/use_config.py
index ef5129f..ef5129f 100644
--- a/extras/use_config.py
+++ b/waflib/extras/use_config.py
diff --git a/extras/valadoc.py b/waflib/extras/valadoc.py
index c50f69e..c50f69e 100644
--- a/extras/valadoc.py
+++ b/waflib/extras/valadoc.py
diff --git a/extras/waf_xattr.py b/waflib/extras/waf_xattr.py
index 351dd63..351dd63 100644
--- a/extras/waf_xattr.py
+++ b/waflib/extras/waf_xattr.py
diff --git a/extras/why.py b/waflib/extras/why.py
index 1bb941f..1bb941f 100644
--- a/extras/why.py
+++ b/waflib/extras/why.py
diff --git a/extras/win32_opts.py b/waflib/extras/win32_opts.py
index 9f7443c..9f7443c 100644
--- a/extras/win32_opts.py
+++ b/waflib/extras/win32_opts.py
diff --git a/extras/wix.py b/waflib/extras/wix.py
index d87bfbb..d87bfbb 100644
--- a/extras/wix.py
+++ b/waflib/extras/wix.py
diff --git a/extras/xcode6.py b/waflib/extras/xcode6.py
index 91bbff1..91bbff1 100644
--- a/extras/xcode6.py
+++ b/waflib/extras/xcode6.py
diff --git a/fixpy2.py b/waflib/fixpy2.py
index 24176e0..24176e0 100644
--- a/fixpy2.py
+++ b/waflib/fixpy2.py
diff --git a/processor.py b/waflib/processor.py
index 2eecf3b..2eecf3b 100755
--- a/processor.py
+++ b/waflib/processor.py
diff --git a/waflib/waf b/waflib/waf
new file mode 100755
index 0000000..e22930a
--- /dev/null
+++ b/waflib/waf
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+
+# Minimal waf script for projects that include waflib directly
+
+from waflib import Context, Scripting
+
+import inspect
+import os
+
+def main():
+ script_path = os.path.abspath(inspect.getfile(inspect.getmodule(main)))
+ project_path = os.path.dirname(script_path)
+ Scripting.waf_entry_point(os.getcwd(), Context.WAFVERSION, project_path)
+
+if __name__ == '__main__':
+ main()
diff --git a/wscript b/wscript
new file mode 100644
index 0000000..c85a8d7
--- /dev/null
+++ b/wscript
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+import os
+import subprocess
+
+from waflib.extras import autowaf as autowaf
+
+# Version of this package (even if built as a child)
+MACHINA_VERSION = '0.5.0'
+
+# Variables for 'waf dist'
+APPNAME = 'machina'
+VERSION = MACHINA_VERSION
+
+# Mandatory variables
+top = '.'
+out = 'build'
+
+def options(opt):
+ opt.load('compiler_cxx')
+ autowaf.set_options(opt)
+
+def configure(conf):
+ autowaf.display_header('Machina Configuration')
+ conf.load('compiler_cxx', cache=True)
+ conf.load('autowaf', cache=True)
+ autowaf.set_cxx_lang(conf, 'c++11')
+
+ autowaf.check_pkg(conf, 'lv2', uselib_store='LV2',
+ atleast_version='1.2.0', mandatory=True)
+ autowaf.check_pkg(conf, 'glibmm-2.4', uselib_store='GLIBMM',
+ atleast_version='2.14.0', mandatory=True)
+ autowaf.check_pkg(conf, 'gthread-2.0', uselib_store='GTHREAD',
+ atleast_version='2.14.0', mandatory=True)
+ autowaf.check_pkg(conf, 'gtkmm-2.4', uselib_store='GTKMM',
+ atleast_version='2.12.0', mandatory=False)
+ autowaf.check_pkg(conf, 'jack', uselib_store='JACK',
+ atleast_version='0.120.0', mandatory=True)
+ autowaf.check_pkg(conf, 'raul', uselib_store='RAUL',
+ atleast_version='0.8.9', mandatory=True)
+ autowaf.check_pkg(conf, 'ganv-1', uselib_store='GANV',
+ atleast_version='1.2.1', mandatory=False)
+ autowaf.check_pkg(conf, 'serd-0', uselib_store='SERD',
+ atleast_version='0.4.0', mandatory=False)
+ autowaf.check_pkg(conf, 'sord-0', uselib_store='SORD',
+ atleast_version='0.4.0', mandatory=False)
+ autowaf.check_pkg(conf, 'eugene', uselib_store='EUGENE',
+ atleast_version='0.0.0', mandatory=False)
+
+ # Check for posix_memalign (OSX, amazingly, doesn't have it)
+ autowaf.check_function(conf, 'cxx', 'posix_memalign',
+ header_name = 'stdlib.h',
+ define_name = 'HAVE_POSIX_MEMALIGN',
+ mandatory = False)
+
+ if conf.env.HAVE_GTKMM and conf.env.HAVE_GANV:
+ autowaf.define(conf, 'MACHINA_BUILD_GUI', 1)
+
+ autowaf.define(conf, 'MACHINA_PPQN', 19200)
+ autowaf.define(conf, 'MACHINA_VERSION', MACHINA_VERSION)
+ autowaf.define(conf, 'MACHINA_DATA_DIR',
+ os.path.join(conf.env.DATADIR, 'machina'))
+
+ conf.write_config_header('machina_config.h', remove=False)
+
+ autowaf.display_summary(conf)
+ autowaf.display_msg(conf, "Jack", bool(conf.env.HAVE_JACK))
+ autowaf.display_msg(conf, "GUI", bool(conf.env.MACHINA_BUILD_GUI))
+ print('')
+
+def build(bld):
+ bld.recurse('src/engine')
+ bld.recurse('src/client')
+
+ if bld.env.MACHINA_BUILD_GUI:
+ bld.recurse('src/gui')
+
+def lint(ctx):
+ subprocess.call('cpplint.py --filter=-whitespace/comments,-whitespace/tab,-whitespace/braces,-whitespace/labels,-build/header_guard,-readability/casting,-readability/todo,-build/namespaces,-whitespace/line_length,-runtime/rtti,-runtime/references,-whitespace/blank_line,-runtime/sizeof,-readability/streams,-whitespace/operators,-whitespace/parens,-build/include,-build/storage_class `find -name *.cpp -or -name *.hpp`', shell=True)