pax_global_header00006660000000000000000000000064145101737240014516gustar00rootroot0000000000000052 comment=bfe7511d386557ed5f15ceab739503b6d5ab571c geonkick-2.10.2/000077500000000000000000000000001451017372400133725ustar00rootroot00000000000000geonkick-2.10.2/.gitignore000066400000000000000000000001021451017372400153530ustar00rootroot00000000000000*.o *.a moc_* .qmake.stash gui/Makefile gui/geonkick build doxigengeonkick-2.10.2/AUTHORS000066400000000000000000000000751451017372400144440ustar00rootroot00000000000000Author and Maintainer: Iurie Nistor Email: iuriehn@gmail.com geonkick-2.10.2/CHANGELOG.md000066400000000000000000000211101451017372400151760ustar00rootroot00000000000000## [2.10.2] ### Fixes - Added lock memory for standalone. - Added external sub directory path to VST3 SDK for building. - Error check on parsing JSON. - Fixed info about vst plugin. ### Features - Added Geonkick Bells presets. ### Other - Cleanups, adding new info ## [2.10.1] ### Fixes - Use OpenSSL for base64 encoding/decoding and eliminated third-party code related to base64. - Generate PNG resources during the build process and removed the generated C files from the repository. - Sanitized all SVG images by removing sensitive data. - Relocated the 'redkite,' 'dsp,' 'plugin,' and 'standalone' folders to the 'src' directory. - Eliminated the '3rdparty' folder and excluded 'vst3sdk' from the repository. - Introduced a CMake option to specify the path to the VST3 SDK. - Additional clean-up to remove sensitive data and unnecessary files. - Added licensing (CC0) information for all images, documentation, and other non-code materials. ## [2.10.0] ### Fixes - Fix linking VST3 against wrong Redkite library path. - Fix conversion from frequency to note (more fixes). - Fix shortcut for hide/show envelope. ### Features - Set manually the exact value of envelope points. - Display the current envelope point coordinates (value, time). ### Improvements - Improve the precision for saving into presets. ## [2.9.2] ### Fixes - Fix conversion from frequency to note in the frequency envelope graph. ### Other - Moved redkite to 3rdparty folder. ## [2.9.1] ### Fixes - Fix crash in Reaper for LV2 (if the event frame is given wrong by the host). ## [2.9.0] ### Features - Bring back VST3 plugin. - Support the session sample rate for VST3 plugin. - Integrate VST3 SDK into Geonkick build by providing the library into the 3rdparty dir. ### Fixes - export ModuleEntry/ModuleExit symbols for VST3 SDK - don't try to build standalone if there is no libjack dev library installed - fix a potential crash if the GUI graph thread was not created - for LV2 plugin tell the host that there is required ui:parent and ui:resize (manifest file changes) ## [2.8.1] ### Fixes - Remove redundant lock in the DSP worker. ## [2.8.0] ### Fixes - Fix crash when opening multiple GUI for LV2 Find rapidjson when. - Configuring at build add musl compatibility patch (Teldra / @teldra@likeable.space). ### Features - Add limiter to samples preview. ### Improvements - Integrate Redkite into Geonkick code. ## [2.7.3] ### Fixes - Fix selection of the last MIDI column in the MIDI key popup. ### Improvements - Move Redkite GUI toolkit into the Geonkick source and integrate it in the build system (no need to install Redkite now to build Geonkick). ## [2.7.2] ### Improvements - Show Geonkick version in the UI ### Fixes - Fix crash for standalone when jack server is not running. ## [2.7.1] ### Features - Add scale factor 1.5x for UI. ## [2.7.0] ### Features - Sample rate agnostic. ## [2.6.1] ### Fixes - Fix potential race condition. - Remove lib prefix for plugin shared library. ## [2.6.0] ### Features - Set MDI key popup for instruments. - Export instrument kit to SFZ. - 16 stereo channels for standalone. - Autoconnect ports to system playback for standalone. ## [2.5.1] ### Fixes - Fix build for some distros (include iomanip). ## [2.5.0] ### Features - "Single" version of LV2 plugin (single instrument and single stereo channel). ## [2.4.1] ### Improvement - Shared worker between plugin instances/ ## [2.4.0] ### Features - Sample browser with sample preview. - Save/restore the last visited path, also save UI visual state. - Loaded sample is amplified. - Pitch shift (+- 48 semitones) envelope for oscillator when it uses a sample. - Added sound level indication for kit mixer. - UI updates, switch between envelopes is more easy, removed the envelopes menu. - Option to scale the UI by factor of 2 for so-called HiDPI cases. ## [2.3.8] ### Fixes: - Fixed the looped continuous sound that is generated on some hosts (LV2 and VST3). ## [2.3.7] - Added manpage for geonkick executable. ## [2.3.6] ### Fixes: - Fix updating kit UI when selecting a preset from host. - Fix some compile warnings in the vst plugin. ### Other - Remove old presets. ## [2.3.5] ### Fixes: - Fixes delays in the process loop and improve the process loop. ### Other - remove experimental file system support. ## [2.3.4] ### Fixes - Fixes name label for output channels in LV2, fixes "Fanout" for channels (by Robin Gareus). ## [2.3.3] ### Fixes: - Fix build on ARM architecture (by Andreas Müller) - Fix showing limiter sound level - Fix typos in documentation ## [2.3.2] ### Fixes: - Add cmake architecture variable for compiler optimisation flags. ## [2.3.1] ### Fixes: - Add lv2:requiredFeature urid:map. ### [2.3.0] ### Features - Reset to default button. - Add stereo output channels for LV2, VST and standalone. ### Improvements - Added compiler optimization flags and fast math. - Process audio routines for LV2, VST and standalone was made more efficient, now DSP should take less CPU. ### Fixes: - Fix loading other audio format subtypes for samples. - Reset to default shortcut. - FHS friendly LV2 and VST3 plugin installation paths. - Don't copy mute and solo settings when copy/paste percussion. - Reverse sawtooth. ### [2.2.3] ### Fixes - Fix compilers flags ### [2.2.2] ### Improvements - Follow the standard https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables. - Search for presets in the $XDG_DATA_DIRS and $XDG_CONFIG_HOME. - Use GEONKICK_DATA_DIR when geonkick is installed in non a nonstandard path. ### [2.2.1] ### Fixes - Fix crash when .local/geonkick path can't be created. - Fix loading presets from .local/geonkick/presets. ### [2.2.0] ### Features - Kit mixer. - Preset browser. - Factory presets (installed under /usr/share/geonkick/presets or /usr/local/share/geonkick/presets). - Distortion volume envelope. ### Improvements - Range channels from 1 - 16 instead of 0 - 15. - Added visual effect for play button and some other buttons. ### Fixes - Fix loading WAVEFORMATEX samples - Other small fixes ### Changes from 2.1.0 to 2.1.1 ### Fixes - Fix install of *.desktop file. - Fix finding Redkite `lib64` in `FindRedkite.cmake`. - Add loading WAVEX samples. ### [2.1.0] ### Fixes - Fix loading stereo samples. ### Improvements - Better quality of tuning. ### Changes from [2.0.2] ### Fixes - Fix saving kit state for standalone. ### [2.0.1] ### Fixes - Fixed the crash when opening a directory as kit preset. ### Other - Moved presets into doc/examples. ### [2.0.0] ### Features - VST3 plugin support for GNU/Linux. - Seed for noise generator to have different noise patterns. - Add copy/paste between percussions. - Add shortcut keys to navigate and change the kit list order. ### Improvements - Noise pattern does not change when synthesizer parameters are changed. Load/save preset will preserve the noise pattern too. - Keep the order of percussion when saving/opening state or preset. - Add new percussion only at the the end. - When starting to edit a percussion name move cursor by default to the end. - Reduce unnecessary updates of the UI when changing parameters. ### Fixes - The shortcuts and key 'k'. - A bug regarding "grabbing" window manager keys. - Set "any" key. - Updating the name in the toolbar. - Not updating the current percussion envelopes when adding a new default. - Reset to default percussion. - Limiter leveler for plugin. - Related to loading the preset from the host list of states (for LV2). - Deebug level when debugging. - ttl LV2 validation errors. - Fixed wrong linking to experimental version of filesystem for GCC version >= 8. ### [1.10.0] ### Freatures - Percussion kit - supports up the 16 percussions in the kit. - Multichannel support - supports up to 16 audio output channels. - Distortion drive envelope. - Distortion effect input limiter. - Play percussion button. - For build system check if Redkite is installed. - Enabled back the shortcut "k" key for triggering sound. - Documentation. - Backward compatibility, i.e. will load older presets. ### Improvements - Fmproved the checkbox for effects. - Resonance knob was reversed. ### Fixes - Fixed skipping notes for some hosts when notes are in a raw. - Fixed the crash for file dialog when going to top file system '/' directory. - Fixed the label of FM checkbox. geonkick-2.10.2/CMakeLists.txt000066400000000000000000000257541451017372400161470ustar00rootroot00000000000000cmake_minimum_required (VERSION 3.7) project(geonkick VERSION 2.10.2) include(GNUInstallDirs) option(GKICK_STANDALONE "Enable build for standalone" ON) option(GKICK_PLUGIN "Enable build for plugin" ON) if (GKICK_PLUGIN) option(GKICK_PLUGIN_LV2 "Enable build LV2 plugin" ON) if (VST3_SDK_PATH) option(GKICK_PLUGIN_VST "Enable build VST plugin" ON) endif(VST3_SDK_PATH) endif (GKICK_PLUGIN) if (NOT CMAKE_BUILD_TYPE) message(STATUS "no build type selected, set default to Release") set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type Release" FORCE) endif() if (NOT GKICK_ARCHITECTURE) message(STATUS "GKICK_ARCHITECTURE variable was not set use default ${CMAKE_HOST_SYSTEM_PROCESSOR}") set(GKICK_ARCHITECTURE ${CMAKE_HOST_SYSTEM_PROCESSOR}) endif () if (GKICK_ARCHITECTURE MATCHES x86_64) message(STATUS "set optimisation compiler flags for ${GKICK_ARCHITECTURE}") set(GKICK_OPTIMISATION_FLAGS "-O3 -msse -msse2 -mfpmath=sse -ffast-math -fomit-frame-pointer") else () message(STATUS "set optimisation compiler flags for ${GKICK_ARCHITECTURE}") set(GKICK_OPTIMISATION_FLAGS "-O3 -ffast-math -fomit-frame-pointer") endif () set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_FLAGS "-Wall ${GKICK_OPTIMISATION_FLAGS} ${CMAKE_CXX_FLAGS} -fvisibility=hidden") set(CMAKE_C_FLAGS "-Wall ${GKICK_OPTIMISATION_FLAGS} ${CMAKE_C_FLAGS} -fvisibility=hidden") if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) set (CMAKE_INSTALL_PREFIX "/usr/local" CACHE PATH "default install path" FORCE) endif() if (NOT CMAKE_INSTALL_FULL_DATADIR) message(STATUS "CMAKE_INSTALL_FULL_DATADIR is not set, use default ${CMAKE_INSTALL_PREFIX}/share") set(CMAKE_INSTALL_FULL_DATADIR "${CMAKE_INSTALL_PREFIX}/share" CACHE PATH "default data dir path" FORCE) endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGEONKICK_DATA_DIR=\\\"${CMAKE_INSTALL_FULL_DATADIR}\\\"") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DGEONKICK_DATA_DIR=\\\"${CMAKE_INSTALL_FULL_DATADIR}\\\"") set(GKICK_DOC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/doc) set(GKICK_COMMON_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) set(GKICK_API_DIR ${GKICK_COMMON_DIR}/dsp) set(GKICK_RC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/resources) set(GKICK_STANDALONE_DIR ${GKICK_COMMON_DIR}/standalone) set(GKICK_REDKITE_DIR ${GKICK_COMMON_DIR}/redkite) if (GKICK_PLUGIN) if (GKICK_PLUGIN_VST) set(GKICK_VST_DIR ${GKICK_COMMON_DIR}/plugin/vst) set(GKICK_VST_SDK_PATH ${VST3_SDK_PATH}) endif (GKICK_PLUGIN_VST) if (GKICK_PLUGIN_LV2) set(GKICK_LV2_DIR ${GKICK_COMMON_DIR}/plugin/lv2) endif (GKICK_PLUGIN_LV2) endif(GKICK_PLUGIN) set(GKICK_DATA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/data) set(GKICK_PRESETS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/presets) set(GKICK_COMMON_HEADERS ${GKICK_COMMON_DIR}/Base64EncoderDecoder.h ${GKICK_COMMON_DIR}/GeonkickConfig.h ${GKICK_COMMON_DIR}/UiSettings.h ${GKICK_COMMON_DIR}/ViewState.h ${GKICK_COMMON_DIR}/geonkick_slider.h ${GKICK_COMMON_DIR}/geonkick_button.h ${GKICK_COMMON_DIR}/PathListModel.h ${GKICK_COMMON_DIR}/file_dialog.h ${GKICK_COMMON_DIR}/SampleBrowser.h ${GKICK_COMMON_DIR}/filter.h ${GKICK_COMMON_DIR}/geonkick_groupbox.h ${GKICK_COMMON_DIR}/top_bar.h ${GKICK_COMMON_DIR}/control_area.h ${GKICK_COMMON_DIR}/controls_widget.h ${GKICK_COMMON_DIR}/percussion_model.h ${GKICK_COMMON_DIR}/kit_model.h ${GKICK_COMMON_DIR}/percussion_view.h ${GKICK_COMMON_DIR}/kit_widget.h ${GKICK_COMMON_DIR}/envelope_draw_area.h ${GKICK_COMMON_DIR}/limiter.h ${GKICK_COMMON_DIR}/general_envelope.h ${GKICK_COMMON_DIR}/effects_group_box.h ${GKICK_COMMON_DIR}/compressor_group_box.h ${GKICK_COMMON_DIR}/distortion_group_box.h ${GKICK_COMMON_DIR}/layers_group_box.h ${GKICK_COMMON_DIR}/general_group_box.h ${GKICK_COMMON_DIR}/envelope.h ${GKICK_COMMON_DIR}/BufferView.h ${GKICK_COMMON_DIR}/kick_graph.h ${GKICK_COMMON_DIR}/envelope_widget.h ${GKICK_COMMON_DIR}/knob.h ${GKICK_COMMON_DIR}/oscillator.h ${GKICK_COMMON_DIR}/geonkick_api.h ${GKICK_COMMON_DIR}/percussion_state.h ${GKICK_COMMON_DIR}/kit_state.h ${GKICK_COMMON_DIR}/oscillator_envelope.h ${GKICK_COMMON_DIR}/oscillator_group_box.h ${GKICK_COMMON_DIR}/ExportAbstract.h ${GKICK_COMMON_DIR}/ExportSoundData.h ${GKICK_COMMON_DIR}/ExportToSfz.h ${GKICK_COMMON_DIR}/export_widget.h ${GKICK_COMMON_DIR}/preset.h ${GKICK_COMMON_DIR}/preset_folder.h ${GKICK_COMMON_DIR}/preset_browser_model.h ${GKICK_COMMON_DIR}/preset_browser_view.h ${GKICK_COMMON_DIR}/MidiKeyWidget.h ${GKICK_COMMON_DIR}/EnvelopePointContextWidget.h ${GKICK_COMMON_DIR}/MainWindow.h) set(GKICK_COMMON_SOURCES ${GKICK_COMMON_DIR}/Base64EncoderDecoder.cpp ${GKICK_COMMON_DIR}/GeonkickConfig.cpp ${GKICK_COMMON_DIR}/UiSettings.cpp ${GKICK_COMMON_DIR}/ViewState.cpp ${GKICK_COMMON_DIR}/geonkick_widget.cpp ${GKICK_COMMON_DIR}/geonkick_slider.cpp ${GKICK_COMMON_DIR}/geonkick_button.cpp ${GKICK_COMMON_DIR}/PathListModel.cpp ${GKICK_COMMON_DIR}/file_dialog.cpp ${GKICK_COMMON_DIR}/SampleBrowser.cpp ${GKICK_COMMON_DIR}/filter.cpp ${GKICK_COMMON_DIR}/geonkick_groupbox.cpp ${GKICK_COMMON_DIR}/top_bar.cpp ${GKICK_COMMON_DIR}/control_area.cpp ${GKICK_COMMON_DIR}/controls_widget.cpp ${GKICK_COMMON_DIR}/percussion_model.cpp ${GKICK_COMMON_DIR}/kit_model.cpp ${GKICK_COMMON_DIR}/percussion_view.cpp ${GKICK_COMMON_DIR}/kit_widget.cpp ${GKICK_COMMON_DIR}/envelope_draw_area.cpp ${GKICK_COMMON_DIR}/limiter.cpp ${GKICK_COMMON_DIR}/general_envelope.cpp ${GKICK_COMMON_DIR}/effects_group_box.cpp ${GKICK_COMMON_DIR}/compressor_group_box.cpp ${GKICK_COMMON_DIR}/distortion_group_box.cpp ${GKICK_COMMON_DIR}/layers_group_box.cpp ${GKICK_COMMON_DIR}/general_group_box.cpp ${GKICK_COMMON_DIR}/envelope.cpp ${GKICK_COMMON_DIR}/BufferView.cpp ${GKICK_COMMON_DIR}/kick_graph.cpp ${GKICK_COMMON_DIR}/envelope_widget.cpp ${GKICK_COMMON_DIR}/knob.cpp ${GKICK_COMMON_DIR}/oscillator.cpp ${GKICK_COMMON_DIR}/geonkick_api.cpp ${GKICK_COMMON_DIR}/percussion_state.cpp ${GKICK_COMMON_DIR}/kit_state.cpp ${GKICK_COMMON_DIR}/oscillator_envelope.cpp ${GKICK_COMMON_DIR}/oscillator_group_box.cpp ${GKICK_COMMON_DIR}/export_widget.cpp ${GKICK_COMMON_DIR}/ExportAbstract.cpp ${GKICK_COMMON_DIR}/ExportSoundData.cpp ${GKICK_COMMON_DIR}/ExportToSfz.cpp ${GKICK_COMMON_DIR}/preset.cpp ${GKICK_COMMON_DIR}/preset_folder.cpp ${GKICK_COMMON_DIR}/preset_browser_model.cpp ${GKICK_COMMON_DIR}/preset_browser_view.cpp ${GKICK_COMMON_DIR}/MidiKeyWidget.cpp ${GKICK_COMMON_DIR}/EnvelopePointContextWidget.cpp ${GKICK_COMMON_DIR}/MainWindow.cpp) if (CMAKE_BUILD_TYPE STREQUAL "Debug") message(STATUS "Enable application logging") option(ENABLE_LOGGING "Enable logging for API and GUI" ON) endif() if (ENABLE_LOGGING) message(STATUS "set debug level") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DRK_LOG_DEBUG_LEVEL") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DRK_LOG_DEBUG_LEVEL") list(APPEND GKICK_API_FLAGS "-DGKICK_LIB_LOG_LEVEL_DEBUG") list(APPEND GKICK_API_FLAGS "-DGKICK_LIB_LOG_FUNCTION") list(APPEND GKICK_API_PLUGIN_FLAGS "-DGKICK_LIB_LOG_LEVEL_DEBUG") list(APPEND GKICK_API_PLUGIN_FLAGS "-DGKICK_LIB_LOG_FUNCTION") list(APPEND GKICK_API_PLUGIN_SINGLE_FLAGS "-DGKICK_LIB_LOG_LEVEL_DEBUG") list(APPEND GKICK_API_PLUGIN_SINGLE_FLAGS "-DGKICK_LIB_LOG_FUNCTION") endif() find_package(PkgConfig REQUIRED) find_package(RapidJSON REQUIRED) find_package(OpenSSL REQUIRED) pkg_check_modules(SNDFILE sndfile) if (NOT SNDFILE_FOUND) message(FATAL_ERROR "can't find sndfile library") endif(NOT SNDFILE_FOUND) if (GKICK_STANDALONE) pkg_check_modules(JACK jack) if (NOT JACK_FOUND) message(STATUS "can't find jack library") set(GKICK_STANDALONE OFF) set(GKICK_JACK_NOT_FOUND ON) endif(NOT JACK_FOUND) list(APPEND GKICK_API_FLAGS "-DGEONKICK_AUDIO_JACK") endif() if (GKICK_PLUGIN) list(APPEND GKICK_API_PLUGIN_SINGLE_FLAGS "-DGEONKICK_SINGLE") if(CMAKE_SYSTEM_NAME MATCHES Windows) message(STATUS "checking for VST SDK...") else() message(STATUS "checking for LV2 library...") pkg_check_modules(LV2 lv2) if (NOT LV2_FOUND) message(FATAL_ERROR "can't find LV2 library") endif(NOT LV2_FOUND) endif() endif(GKICK_PLUGIN) if (CMAKE_SYSTEM_NAME MATCHES Windows) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DRK_OS_WIN") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DRK_OS_WIN") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DRK_FOR_SHARED") else () set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DRK_OS_GNU") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DRK_OS_GNU") endif () include_directories(${GKICK_API_DIR}/src) include_directories(${GKICK_REDKITE_DIR}/include) link_directories(${GKICK_REDKITE_DIR}) set(CMAKE_INCLUDE_CURRENT_DIR ON) add_definitions(-fPIC) add_subdirectory(${GKICK_API_DIR}) add_subdirectory(${GKICK_REDKITE_DIR}) include(${GKICK_RC_DIR}/resources.rc) foreach(pngFile ${gkick_rc_png_files}) message(STATUS "convert file ${pngFile}") get_filename_component(filename ${GKICK_RC_DIR}/${pngFile} NAME) string(REPLACE "." "_" filename ${filename}) string(REPLACE " " "_" filename ${filename}) add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/resources/${filename}.c COMMAND mkdir -p ${CMAKE_BINARY_DIR}/resources && ${CMAKE_BINARY_DIR}/src/redkite/tools/rkpng2c "${GKICK_RC_DIR}/${pngFile}" "${CMAKE_BINARY_DIR}/resources/${filename}.c" "rk__${filename}") set(GKICK_RC_OUTPUT ${GKICK_RC_OUTPUT} ${CMAKE_BINARY_DIR}/resources/${filename}.c) endforeach() add_custom_target(gkick_resources ALL DEPENDS ${GKICK_RC_OUTPUT}) add_dependencies(gkick_resources redkite rkpng2c) include_directories(${GKICK_COMMON_DIR}) link_directories(${REDKITE_LIBRARY_DIR}) add_library(geonkick_common STATIC ${GKICK_COMMON_HEADERS} ${GKICK_COMMON_SOURCES} ${GKICK_RC_OUTPUT}) add_dependencies(geonkick_common gkick_resources) if (GKICK_PLUGIN) add_library(geonkick_common_single STATIC ${GKICK_COMMON_HEADERS} ${GKICK_COMMON_SOURCES} ${GKICK_RC_OUTPUT}) add_dependencies(geonkick_common_single gkick_resources) target_compile_options(geonkick_common_single PUBLIC ${GKICK_API_PLUGIN_SINGLE_FLAGS}) endif(GKICK_PLUGIN) message(STATUS "------------ Summary ---------") if (GKICK_STANDALONE) message(STATUS "Standalone: yes" ) else(GKICK_STANDALONE) if (GKICK_JACK_NOT_FOUND) message(STATUS "Standalone: no (install jack development library)") else () message(STATUS "Standalone: no") endif() endif(GKICK_STANDALONE) if (GKICK_PLUGIN_LV2) message(STATUS "LV2 plugin: yes" ) else(GKICK_PLUGIN_LV2) message(STATUS "LV2 plugin: no") endif(GKICK_PLUGIN_LV2) if (GKICK_PLUGIN_VST) message(STATUS "VST plugin: yes" ) else(GKICK_PLUGIN_VST) message(STATUS "VST plugin: no") endif(GKICK_PLUGIN_VST) if (ENABLE_LOGGING) message(STATUS "Debug enabled: yes" ) else(ENABLE_LOGGING) message(STATUS "Debug enabled: no" ) endif(ENABLE_LOGGING) message(STATUS "------------------------------") if (GKICK_STANDALONE) add_subdirectory(${GKICK_STANDALONE_DIR}) endif (GKICK_STANDALONE) if (GKICK_PLUGIN) add_subdirectory(${GKICK_COMMON_DIR}/plugin) endif (GKICK_PLUGIN) add_subdirectory(data) add_subdirectory(presets) geonkick-2.10.2/LICENSE000066400000000000000000001045151451017372400144050ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. 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. Copyright (C) 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 . 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: Copyright (C) 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 . 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 . geonkick-2.10.2/README.md000066400000000000000000000111231451017372400146470ustar00rootroot00000000000000# Geonkick **Geonkick** - a [free software](https://www.gnu.org/philosophy/free-sw.en.html) percussion synthesizer. **Website:** [https://geonkick.org](https://geonkick.org) Geonkick is a free software synthesizer capable of generating a wide range of percussive sounds, including kicks, snares, claps, hi-hats, shakers, and unique effect sounds. **License:** GNU General Public License, Version 3 **Author and Maintainer:** Iurie Nistor **Version:** 2.10.2 If you have an idea about a feature or found an issue, please, submit it: - [on GitLab](https://gitlab.com/Geonkick-Synthesizer/geonkick/issues) - [on Codeberg](https://codeberg.org/Geonkick-Synthesizer/geonkick/issues) - [on GitHub](https://github.com/Geonkick-Synthesizer/geonkick/issues) If you want your created presets to be added to the list of Geonkick presets, please, create a pull request with the preset archive. See the folder "presets" for instructions on creating a preset archive. ![Screenshot](data/screenshot.png) ![Screenshot](data/screenshot_kit.png) ## Download Source code repository: - https://gitlab.com/Geonkick-Synthesizer/geonkick - https://codeberg.org/Geonkick-Synthesizer/geonkick - https://github.com/Geonkick-Synthesizer/geonkick #### Features * Kit - Supports up to 16 instruments. - Save / Load kit in JSON format - Export instrument kit to SFZ * Multitibral - 16 midi key range - user configurable key mapping * Multichannel - 16 stereo audio output channels for plugin (1 stereo for standalone) - user configurable mapping of kit instrument to output channels * 3 layers * Layers Mixer * 2 oscillators per layer - sine, square, triangle, saw-tooth, sample (wav, ogg, flac) - initial phase control - amplitude, frequency and sample pitch shift envelope - low, band and high pass filter, cutoff envelope * FM synthesis - OSC1->OSC2 * One noise generator per layer - white & brownian - random seed - amplitude envelope - low, band and high pass filter, cutoff envelope * General - amplitude envelope & kick length - low & high pass filter, cutoff envelope - limiter - compression - distortion - distortion drive envelope - distortion volume envelope - tune percussion option (relative to key A4) * Key velocity sensitive for instrument total output amplitude * Jack support * Export - mono, stereo - WAV: 16, 24, 32 bit - FLAC: 16, 24 bit - Ogg * Open & Save presets in JSON format * Preset browser * Standalone * Pitch to note * LV2 & VST3 Plugin - "Single" version that supports one instrument and one stereo channel per instance. - The default version that supports kits of 16 instruments and 16 output stereo channels per instance. * Operating System: - GNU/Linux * Manually scale the UI by factor of 2 (useful for HiDPI cases) #### Requirements In order Geonkick to run and operate correctly there is a need: for standalone: * GNU/Linux operating system * Jack server running for plugin: * GNU/Linux operating system. * A host that supports LV2 plugin format. #### Install ##### Install dependencies In order to build Geonkick there is a need to install the following development packages: * libsndfile * OpenSSL * RapidJSON * JACK Audio Connection Kit * LV2 development library On Debian, Ubuntu, Ubuntu Studio install: apt-get install build-essential apt-get install cmake apt-get install libjack-dev libsndfile-dev apt-get install libssl-dev apt-get install rapidjson-dev apt-get install lv2-dev apt-get install libcairo2-dev ##### Build & install Geonkick Clone the Geonkick code repository, compile and install. copy the source code of the latest release mkdir geonkick/build cd geonkick/build cmake ../ make make install ##### Build Geonkick with VST3 Plugin Install additional dependences: apt-get install libx11-xcb-dev libxcb-util-dev libxcb-cursor-dev libxcb-xkb-dev libxkbcommon-dev libxkbcommon-x11-dev libfontconfig1-dev libcairo2-dev libgtkmm-3.0-dev libsqlite3-dev libxcb-keysyms1-dev Get VST3 SDK than build Geonkick VST3 plugin: mkdir geonkick/build cd geonkick/build cmake -DVST3_SDK_PATH= ../ make make install #### Packages Geonkick can be found in the repository of ArchLinux, OpenSUSE, Fedora , Manjaro, FreeBSD, KXStudio and Ubuntu Studio. ##### Packaging Please use only release versions ([see repository releases](https://gitlab.com/Geonkick-Synthesizer/geonkick/-/releases)). #### Documentation [doc/Documentation.md](doc/Documentation.md) geonkick-2.10.2/data/000077500000000000000000000000001451017372400143035ustar00rootroot00000000000000geonkick-2.10.2/data/CMakeLists.txt000066400000000000000000000005051451017372400170430ustar00rootroot00000000000000install(DIRECTORY ${GKICK_DATA_DIR}/icons DESTINATION ${CMAKE_INSTALL_PREFIX}/share) if (GKICK_STANDALONE) install(FILES ${GKICK_DATA_DIR}/geonkick.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications) endif() install(FILES ${GKICK_DATA_DIR}/geonkick.xml DESTINATION ${CMAKE_INSTALL_PREFIX}/share/mime/packages) geonkick-2.10.2/data/README000066400000000000000000000003001451017372400151540ustar00rootroot00000000000000All images under this directory (including icons and design) are released under CC0 1.0 Universal (CC0 1.0) Public Domain Dedication https://creativecommons.org/publicdomain/zero/1.0/deed.en .geonkick-2.10.2/data/design/000077500000000000000000000000001451017372400155545ustar00rootroot00000000000000geonkick-2.10.2/data/design/geonkick.svg000066400000000000000000046360761451017372400201150ustar00rootroot00000000000000 image/svg+xml Background for MainWindow Frequency Amplitude Oscillator 1 Noise Oscillator 2 General Geonkick Oscillator 1 General Wave Function Sine Noise Square Triangle Sawtooth Noise Noise 3 Noise 2 Noise 1 Sine Noise Square Triangle Sawtooth Sawtooth Sawtooth Q Filter Frequency Q High Pass Q Filter Frequency Q High Pass Amplitude Amplitude Wave Function Noise Square Triangle Sawtooth Q Filter Frequency Q High Pass Frequency Amplitude Filter Envelope Wave Function Sine Square Square Triangle Triangle High Pass Low Pass White White Pink Pink Brownian Brownian Amplitude Frequency Frequency Frequency Sine Square Triangle Sawtooth Envelope Amplitude Frequency Filter Frequency Frequency Wave Function Sine Square Triangle Sawtooth Oscillator 2 Envelope Amplitude Frequency Filter Frequency Frequency Envelope Amplitude Frequency Filter Frequency Frequency Envelope Amplitude Pitch Shift Envelope Amplitude Frequency Envelope Amplitude Length Filter Frequency Frequency Effects Attack Release Makeup Threshold Compressor Knee Ratio Layers Mixer Attack Release Makeup Threshold Knee Ratio Ratio Compressor -10 -20 0 20 10 0 -10 -20 -30 -40 -50 -15 -6 -3 3 6 15 Frequency, 1000 Hz 10k 10k 10k 0.05 0.05 0.05 0.05 Length, 825 ms Low Pass Sine Triangle Square Sawtooth Sine Triangle Square Sawtooth Wave Function Triangle Sawtooth L1 L1 L1 Sine Square Sine Sine Sine Triangle L2 L3 L3 L2 L1 Filter Envelope Amplitude Frequency Frequency Filter Cutoff Filter Cutoff LP BP HP LP BP HP L1 L2 L3 Tune Tune Sample Sample ... Hit-Hat Snare II Macara Crash II Snare I Snare Cymbal Tom I Tom II Crash I Hit-Hat Long Kick D#4 D#4 D#4 D#4 D#4 D#4 D#4 D#4 D#4 D#4 D#4 D#4 D#4 D#4 D#4 D#4 D#4 Distortion Distortion Compressor Distortion Compressor Distortion Compressor Distortion Compressor Distortion Geonkick v2.2 Geonkick - a free software percussion synthesizerCopyright (C) 2020 Iurie NistorLicense: GNU General Public License Version 3 Cancel Export Error on exporting kick Browse Location Location FLAC 24 WAV 16 WAV 24 WAV 32 OGG FLAC 16 Stereo Mono File Name Location FLAC 24 WAV 16 WAV 32 WAV 24 OGG FLAC 16 Stereo Mono File Name -10 -20 0 20 10 0 -10 -20 -30 -40 -50 -15 -6 -3 3 6 15 Frequency Amplitude Oscillator 1 Noise Oscillator 2 General Oscillator 1 General Wave Function Sine Noise Square Triangle Sawtooth Noise Noise 3 Noise 2 Noise 1 Amplitude Sine Noise Square Triangle Sawtooth Q Filter Frequency Q High Pass Sawtooth Sawtooth Q Filter Frequency Q High Pass Q Filter Frequency Q High Pass High Pass Filter Envelope Wave Function Sine Noise Square Triangle Sawtooth Q Filter Frequency Q High Pass Frequency Amplitude Filter Envelope Wave Function Sine Sine Square Square Triangle Triangle High Pass Low Pass White White Pink Pink Brownian Brownian Effects Attack Release Makeup Release Compressor Attack Release Makeup Threshold Compressor Knee Ratio Distortion Ratio Low Pass Oscillator 2 Oscillator 2 Oscillator 1 Oscillator 1 Noise Noise Osc1 -> Osc 2 Osc1 -> Osc 2 Oscillator 1 Oscillator 1 Noise Noise Osc1->Osc2 Osc1->Osc2 Oscillator 2 Oscillator 2 M M M S S S Presets Presets E E E e e e Open Save Export Snare Reset Play L1 L2 L3 Frequency Amplitude Oscillator 1 Noise Oscillator 2 General Geonkick Oscillator 1 General Wave Function Sine Noise Square Triangle Sawtooth Noise Noise 2 Noise 1 Wave Function Noise Square Triangle Sawtooth Attack Release Makeup Threshold Compressor Knee Ratio -10 -20 0 Frequency, 1000 Hz 10k 10k 10k 0.05 0.05 0.05 0.05 Length, 825 ms Square Sine Sample Sample ... Snare Reset Play L1 L2 L3 Open Save Export geonkick-2.10.2/data/design/geonkick3.svg000066400000000000000000000254351451017372400201630ustar00rootroot00000000000000 image/svg+xml geonkick-2.10.2/data/design/geonkick_2.svg000066400000000000000000007573371451017372400203360ustar00rootroot00000000000000 image/svg+xml Frequency Amplitude Oscillator 1 Noise Oscillator 2 General Geonkick Frequency, 1000 Hz 10k 10k 10k 0.05 0.05 0.05 0.05 Length, 825 ms Open Save Export Snare Reset Play L1 L2 L3 Play Play L1 L2 L3 L1 L2 L3 L1 L2 L3 Tune Tune Tune Tune Oscillator 1 Oscillator 2 Noise General L2 L1 L3 Amplitude Frequency Cutoff Amplitude Frequency Cutoff Frequency Cutoff Amplitude Amplitude Frequency Cutoff Oscillator 1 Oscillator 2 Noise General Oscillator 1 Oscillator 2 Noise General L1 L1 L2 L2 L3 L3 Amplitude General Oscillator 1 Oscillator 1 Oscillator 2 Noise Oscillator 2 Noise Osc1 -> Osc2 Osc1 -> Osc2 Open Save Export Reset Reset Reset Samples Samples Samples Kit Presets Controls Samples Kit Presets Controls Samples Kit Presets Controls Play Play Play Load Load Load Osc1 Osc1 Osc1 Osc2 Osc2 Osc2 Sample Sample Sample Wave Function Oscillator 1 Oscillator 1 Oscillator 1 Oscillator 2 Oscillator 2 Oscillator 2 Osc1-> Osc2 Osc1-> Osc2 Osc1-> Osc2 Noise Noise Noise Amplitude Pitch Shift Amplitude Amplitude Amplitude Frequency Frequency Frequency Pitch Shift Pitch Shift Pitch Shift Cutoff Cutoff Cutoff Q LP BP HP LP BP HP LP BP HP Open Save Export Filter Filter Filter Envelope Length Envelope Distortion Distortion Distortion Volume Volume Volume Drive Drive Drive Input Compression Compression Compression Attack Threshold Ratio Makeup Layers Mixer L1 L2 L3 geonkick-2.10.2/data/design/geonkick_new.svg000066400000000000000000010013341451017372400207420ustar00rootroot00000000000000 image/svg+xml Frequency Amplitude Oscillator 1 Noise Oscillator 2 General Geonkick Frequency, 1000 Hz 10k 10k 10k 0.05 0.05 0.05 0.05 Length, 825 ms Open Save Export Snare Reset Play L1 L2 L3 Play Play L1 L2 L3 L1 L2 L3 L1 L2 L3 Tune Tune Tune Tune Oscillator 1 Oscillator 2 Noise General L2 L1 L3 Amplitude Frequency Cutoff Amplitude Frequency Cutoff Frequency Cutoff Amplitude Amplitude Frequency Cutoff Oscillator 1 Oscillator 2 Noise General Oscillator 1 Oscillator 2 Noise General L1 L1 L2 L2 L3 L3 Amplitude General Oscillator 1 Oscillator 1 Oscillator 2 Noise Oscillator 2 Noise Osc1 -> Osc2 Osc1 -> Osc2 Open Save Export Reset Reset Reset Samples Samples Samples Kit Presets Controls Samples Kit Presets Controls Samples Kit Presets Controls Play Play Play Load Load Load Osc1 Osc1 Osc1 Osc2 Osc2 Osc2 Sample Sample Sample Wave Function Oscillator 1 Oscillator 1 Oscillator 1 Oscillator 2 Oscillator 2 Oscillator 2 Osc1-> Osc2 Osc1-> Osc2 Osc1-> Osc2 Noise Noise Noise Amplitude Pitch Shift Amplitude Amplitude Amplitude Frequency Frequency Frequency Pitch Shift Pitch Shift Pitch Shift Cutoff Cutoff Cutoff Q LP BP HP LP BP HP LP BP HP Open Save Export Filter Filter Filter Envelope Length Envelope Distortion Distortion Distortion Volume Volume Volume Drive Drive Drive Input Compression Compression Compression Attack Threshold Ratio Makeup Layers Mixer L1 L2 L3 geonkick-2.10.2/data/design/knob_bk_image.xcf000066400000000000000000000071751451017372400210370ustar00rootroot00000000000000gimp xcf filePPBBgimp-image-grid(style solid) (fgcolor (color-rgba 0.000000 0.000000 0.000000 1.000000)) (bgcolor (color-rgba 1.000000 1.000000 1.000000 1.000000)) (xspacing 10.000000) (yspacing 10.000000) (spacing-unit inches) (xoffset 0.000000) (yoffset 0.000000) (offset-unit inches)  OPPknob_bk_image.png     IPPa CPP}  &2())#  =<<<<, =<<<<989:9;.&3(()"  <<<<<- =<<<<888:9;.&3((*"  <<<<<- =<<<<988:9;.&{2(`BC^()1}1 ""%3 0 r } <D<Be<aj<aj<b, =1#<</|R<?6<${8z%8@68Y0r:91#;.  Eڿ r~w d 3   E߿ r~w d 3    Eۿ r~w  d 3 J bK ah `h c E$ ~ /r_C~{~{~w  C _d / ~ #3 , ::;G  !" )(23 , ;;;H   " )(23 , ::;H   # ))33 f, dh:dh:Fa;B"=$        ` bg bg cH K((PPLayer      PPqPP1AQaDDFDDFDDFDDF((geonkick-2.10.2/data/geonkick.desktop000066400000000000000000000006401451017372400174700ustar00rootroot00000000000000[Desktop Entry] Type=Application Version=1.1 MimeType=application/x-geonkick-preset; Name=Geonkick GenericName=Percussion Synthesizer GenericName[fr]=Synthétiseur de kick de batterie Comment=Synthesizes elements of percussion Comment[fr]=Synthétise les éléments de percussion Icon=geonkick Categories=AudioVideo;Audio;AudioVideoEditing; Exec=env UBUNTU_MENUPROXY=0 geonkick %F Terminal=false StartupNotify=false geonkick-2.10.2/data/geonkick.xml000066400000000000000000000004611451017372400166200ustar00rootroot00000000000000 Geonkick preset geonkick-2.10.2/data/geonkick_512x512.png000066400000000000000000001502651451017372400176230ustar00rootroot00000000000000PNG  IHDRxsBIT|d pHYs2Q~QtEXtSoftwarewww.inkscape.org< IDATxylWu[5yzxooГI 1qcNvlwv:Nw'8Oq&vc'16f^$$A`uk:?NSUs?l=g0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0Cazˁm/hBDl AHA0TI!U$(ZH((]5+ CqwwR/~6ux0̈AKKH\C\ N@\q&}[JyM yMQou͏!aaaƆ\6]a_p q+ 9/4rի;^ L+0!^M'!I6TJIȯJ\y ;1D zȌd2) < f1# Y]!OK+?7($X`8uҲp݋|~}H+_ya/aVsę_-I|As؋rIcay_f\`aZp#rD$($f&%A3~/~qo bQɓb<̰YH|$}Υ>/(X`swk9›z@xkbפW {M 3LX`_x߅iK |x^MN_p\zưÌ Ryk^0 n_[d_zfPfL.] ~@^z[~_{A OX`&s?>Hwp:DQ"" (D7]J+%$ǭ#Yt]H ,~ DCؗ=P1X 3֬/B{${~@(B@"i4U({+RT *!DHׅ[uu{ZU>E.;12oGu8͌qfg7.= !@J>AU((+B4hUU_B/8Wa;lۆm%8( (8.\ǁeeaLɑ9<3^ t +ذ >ЏS \@Ea  ]׋'җ'[ dO*-[oO , B\ۆ㺰l㎗[%9VX`F.(> {= ^U) LCiœ}I૪Z%H+6cTO-t)%)Z yd9 ؎vc Q(o3,7.?N0F*5U0 (u|/DxMomY rrd2(,ۆe;8&Agyv؋aFtv\?A0bϨ@WU`0]a:th,<,ufd/X(X6,nc  S._ʕ+_b\Μ9XM) ]i0MF3N}ZffLq%+(!!? /pʧX`p᭐熽*tUe6nHf~/O@BkxWޣ bia$⥔z^_3Zkq\ "_RF(~)@|! 34\&)#\ZQ鄯#  "`P+}%AQ/JJ9~VS?YRA HuPY/=jve! N#L!o(XѨI<@3O.ͅdVC̽a,u D8F0l/P+x[o5WwNhfM(ɼh(8S7=\S N#N#J!Wp_9_B8uhdXk%uLß|(U-}wG %lۆU*d@M-p L"J##gvt_O]"fx.x0OD5MC(D(B0,;K3( ~~oq&[ ]h1m؎T SF>wlT HKY >&I W|cX`VqCa%I?!+B5MHpX] Ph Uӊ:hAk͑ "*6[$X!@*= hn˶a ٶ HY'{ @rHR8H$+X{>ASOmc w|I/_IʟxNACG8B8F (mJI{'|E{UjdED~~QmPnHDTvx} K ˲aV}n.2 ҹ< CpH )_zsfa+g7.\}UEA,C>\V%_b 0^uPT~}4K ˲_N3lF2D<G2E._F7ď=wz7f&Vp…cB/xaWUU H/\_״y(uʳ,k8 y?TU-+Sj`H?}qP: uR)q$R)dsÏC}K_zpoL"0=q #4;RC MC4 Fj*0 P(u#]׭|gYnB+> crA3~oD.G<G\Iʟs_~ uSf`ͩ '>JW!0DDf^BQ`&Bh/=_'l °&! ÀiP5  M.5g*F w!mA"(Zd *2$0` 5w?@~I.6q{GCG8@4E(Xt5!`: ӄVܻ}n\??2 AdH c `UVQr=A"H,T^ra܌oX`rzD 8_4t!OCס& uQZ \.w~|Y:esX4S !Pj줗D-J)zR)!L!D?•ݠoČ70MY___tWz|!Az_? i0͖&a SUU   4[r% PZ:.իWwz#flaiȩ@A'`!X kW`Ct؆׶mdف S4 n ԗ$7" ! Bi޶e!wh H`k{t6%_xg7bV*6664#uUE8hb~np'Lz։.͖t?~+D0 n9Bu.vvw/XmF>n/ FX Sƫ`}PPX 3XXSj@\7]˲fdn~e8P1°RB0M /6A~kYvd6 WC)/=5ț0+ _ b~"B1?? ?,y\t&~/p@Ӵr? Ő4S+yd(+c{{{sA~g'K(3ΰ0lll,^wX(\Tn%sm hZuwt>?Q߬DHD3ע`>G.okhD*rAt4%0ݰ0Ŝݸ$;b~UQ bXr7Miv$-B:n')*^@$A0hQvRb777|73jKBw=wʕ܀uDs¥cf=7g`0&\#d @4iLCJr888@*_)?ޔɟ2`62 ,E7AmmK Q,5N,&"AĢQu @[Z=Rغq 3`8q| UpG" UMC(n+D6E&/BW(R5SN+{+VhD"7o ATa?stfa`8sQ8 /{n"B8`b6\K_S BŎo.,dϦyɁ|SKY tq8f:]uloocs{l[ /\ =XΝ? Xܺ"aqanCl\$DQ7iSdJz. |.Ô5^@h4h4r9-lk~:ƍ7H0k@J M0>G~+ ֩})/BS:_Y/ ǧ}UrH& Z" tL{͎ffbPXp:Z>r4y&L0'N03s[S"X\Xp PȊ~P]dU4L~gtI/)`&(F|\C$I\ydzPրIg?a`B9s%'\!,=f Qjy>~(L?=iZ+%)Vݫd6nnnbkwLO:smga`9wҝ#?W3 iti%`0XըV[d2F? }fP9hZD|.lqf'SW&|ۗ\y3Å %?R?5t (76Pah{kH&f|,¯"@DB˒=񻥀WP, ׯ_>2K KokN"B,"7&r2LH"5^d<Gł~ B9P0[BmGRplR\/@SU y ]+kG67_ω p…&7SUbQ,/#4ܨ}_~(Wrd25aS+]QnRaZ>%+C$]ȣ`pW3@x{kRfx0 7HE_о`V\LP Z*sqppp+_BB,".R4lǁa do>OQhW@:fb1HFPw\+kGbׯ~N>0ƜrT6¡&yމ%JJDYĢѦO>ukey|嗱 IWsRf0f;wnU?B ٙbEavG"4mcY̕eL*aT6+Dd9ۗ_.>nZ{_`aOt_sf9ů2Oō(P׈t:~2am8HP8;* -bP|6T%)ܷmr_21a}}}Qic>"L8X%z_p8\zuL&zL 3މ(  wɭ( t]8UJwìpP#o}?GmmOܼuOt "DX[YdWS(-ż.l! J;g6t5Jon5C/^r_2-#kRs~̧(hҾib eo"*ЏO̴MPk6@z^x'R!gYpݾ xoܸѝ)9TXaN]p p1*Xj]?X@4mM#c(Rʎ[ dsK;(JpP+(md>YhYB<6{w]aBf0tiYq ُtM"悟D` fϧ~iL7qJkZRgy[!:lǁl"Խݢ(ЏʁEᣫ+{ = rҥ@}1  XX\,- EA4mھ3w:\Š ]U^@E6W5Wn;v׮]c%```ĸ|r 㸟~2 ,cnvJ4MC4m.vnՓR>n2b\@P()ŋ+OBhZgJiLd3(DYY=yӽO+C&ch(#ÝD4 4a0G;keYd2 ( -܈@ 0dP")7.{n_}2/ ~ @:] qduX&ku~@˲q~?p4KP9{im)1E%@G&G`Gdu2 S^:#6AY\RiuH..؏SfxtV- ]G:F@@;7]u2{X RЧ,:W$XwjWh.U(0#@qt0a@) IDAT ,L_xⱣyڵt1 !s C ^R: #ru?E3t|o 3:HtK @04]8&`)& N7+%$]wv֛i+;N::O4X,:.@ @2YngѤS% ͂ j^``P0]Sd ku"sX8Dn\D +KmymN'+2:vrÌ6fYH) ~:mT-Pb|dm{+=Mt +$~=Fy,,,}(x Rgc0J\e! 5PT/@$t3>7.Y׿DLpx)*簸Xr7T`"HC:P Biz/ f,U#u{:$(z+l;K2a`>Ο<`i~KK_u@4Hw,])!Y3x҉`YEK@E&Qѳ FQzmGA4LKz@I!0`ii BQ6i$ױ;g1FBvT#Jass@ %Pjj"w~;fc i}$L;0@n\A{Afa9r ]F=ODG.辜0Dm )PtXvD(F"HSڑׯ]u"1 3^=g"!T"C״*MDwg0H"{UL EtHtQ۟?L+L6 CסWRB([ L&L.kzkؼ~4>Q@@f"aY[mح57"D}Kx<=Y3̔CrT: 4˙E!( 8-!"R$B/+oavooos}>1}B_p9¡VW|k5! B4mWgiFBBvP'͛(XVCS4WY &n{q=Cj0^'a>|/sbr_4jVǶQb àm@)%4"pCפjmkRݿ|dׯIVzf}}}Q ,W"87S y`(T_ݽkY3 SE%u]d2D"JB_EbP`&KG֍7:of.o,OD\JѰ(>,} n0L#:)\(p HAB  ocE#h H ?eh(Ѷƿ #DG~X3 !۶.`ԫY((`0"ADg_n^IV|+O#`Xhd: a:0堪*UU:bZ߼y&|.8QPWU03:k3a־B\N"}a΂o'`4cRbuesmkÂh ''"EX[[kWzӄeh "88ןa.m-LH2 (4KHAήk$+]R4$huc"V k$Rm!DRbÈ EBH4-. bBrprmD")5;m{4i]](/ЁӾ1b~vѴDPN$u6Rg'ڹsv+yPL 4tannKΣ3S-i-]p…G ~M@EXVI+vaF\ZI{,HU|E#did9Gk|;Ŵ9qAU/s& h{/M45=mw^㟅?0}B}傉P0xIEQpر^K ߞd:ׯpՕ޸qqӂ)-MpVL¿?/Ȭ?4D.0! A)zSDZG [*mdSIr…;H_gڟk8n4R:z坸W2.YCd2sҷ_ _ 660a!H떖0;;[..d]ƍKBà O &*.L*$/wL4ґB4#b.Qk_{\DP(P(y}:L[ҕg_3ܸ8؈aAJrF(^WMd[:-//cgwά Х~O2l܅ \IO& 976iccS({xo@8n? _VL XԹ*)eAޜP} ߥݱvOo^%LBU? M<@GvB b7ynFOݽFQ~):y /dC?0͕i8)_]* -ܟ¶m$S)8>I~ 7.?wL$Օf BGm~AY;Źy\<Ͻ\GnaG+W@P(;uzض}5VP(AKG>u7N0Ip"D$c~nu/6P4u&G{OˈwFf= 3-smmo7uRZamu,I[޸Dl4RJa6hL&;2Sz[{3~>԰01 '_j|^}`f8EQ}Q,˪񭹂H8_; 0 ͕[Brp_-NF;:i4L[qQ3e0TJ!nà?MӠQV+8 H?cFffB0M5~D[S٨|2CO{ 3U[r;;;u?'"L֡rf°q}ۼO SH^l z-F&iNe{wx; "|!`1z#+OV޾YLmue'+OSݸ h8Tůa:o+Ǜ}Ӱ0SE7͂*1 dՠ*a6Hݸz'Vz:F +ȵ}1_}{x-o. sdTR޼; M_.ɟ=xZ܅ VVW4|[H;xܙJql ) ?DT5Պ4R躎7?a/avݽR0͖'Yti1g*N0th.T̢U2lcTy#x2ƣ=V9dZ)eAUT"+ZY^%!ǙSN8a@%"D!D*M TMì;E+x%>2ixo Q"c7Yvvw! L(2fgg{)F#/_l: nH,k蘝7zfIIcyCi{߿=q_ڧV)_L( Pz{XX+T& U)dCq)@QQTU-5UcTGJ1HRJ$0;7 &{,¡_lNq`@(]SD J'*_ Q"H 1[ގphc]r]w(4ZY~JQPAӴR0]* )ek?luèκ0?x"ew*VX`bcN{=3uD[´_iUA(Ba(|XZ\£kW.ᔂC$7obwwD\0X4\.lj]@ X`qqfpɍ~:wD;tv$"Dáb^O?n;NS~N;5'apb[FH)Q(QΥGQMӠ:TUk|(R= l_;88@(Yb-ngĐd Z@*&V'AOgxm(P* e^4ZNq6UGxA\ݽ8l6D"ln>2VN'f@:$񲕬Uh 5$]|$36k+ODl&޿}3AiM$)s!)%rm Ae3 \E2*]^ZUL5.e&Rp-a 7(@ӝ0S{\<|}kR "̕:p ۶dͭ-"NwoX`MR+Z4 3h9KuDH@@k3Yԡ, |4n@ѿ;|UFxQ!H$*)TЕV~Ү?@ee@fEۼZ 3Ib"7͉SΝ;7#Akz]O{QeLNO/ӌh̍2Bd[[[A*m2Z u7@m, ya&Np4-+ "V{Q]N1h5}[2$0W*22 @%^?-!Ͷ n0ДH$\q͖B1Pp6GBUUv8@gFM(UZfPhy34"sE@'Ŝƍ|>x<PA"0]BU_  >Qo' I4C_*2Q&2E"؛~qƋrC ; f3Rԡ""(Bc&s4 nӧ@B>ׇ0L=D@u\h$ l IDATPӴ*4AX?hl6ϗOLc,`8N9^ bϽn#Z9CYl]L4ڴP+fIW][E%V=8euT0KYTLvww|>wsbkY #ΤP d]dف Lgt) GD3$e=gu 'B$ ZF6x{e,-. 쾓B &Bx<-R遺v 0 P(u'JWA,ki%h k/?Lp9g ԾFNĠuJDSjZbmAb 0hּq(+  S<%=urGQ}=|Up?Ug:H$B2lI?vNҋq0%R&]R. l,|iwgff 87m$}5UA(l<%n+u7(#" ]dm?Pk&e5PYJ}"ALiQUAֺb婢R k~`}5ww;BPbp^#]虔Z!֦UְКZE`1 khuiKSu](!tʼn6 X4UE0*n/-0MӪes9_EJd5MÛą @gT*Tj 0UvcZIŠtRk1o4+ jNѹ\"c{{˷!.m7)%@cDA0ʹzST}U@Euk06AH| 1&Cx0afjJض=_}щ Tl۶DZw7`jѦ E0 aLZ\`(dqp|h@Qn=Dem)%RvvvzF7?73ie +, }0SF eY @Zfb1e jό5c ׽gmʏqF2ч=> i#͖U NHJrJB H+8c`쥄tq8MUIkD Md_k.?ط7 H)H$w]G#@(\a/. B}J h$ r\d2u G$ׁvTk@BvmMu[{PR)JJ#a-ƩKgEweDp$R_5Ŝ7p47/0QPUh$c:(V-B˲Nqa AFXC0$]"Bɫ](h~9,z0m[݀bC!0I6"J{c/hğHcthoVJ<:TyOIeq>byV&n_ K\X3p[Q{SU>sC9MSbLffK Ө3`h0z- %R!:S*D2JٲlJF_ߨkj08}u pd@sV5`[%ZV *0:P<,vwwJkx0%Џ ')eVU70k m $VX&HDPPWz RE? ƨ.}O iG| ] `Gg* Wu;+CUUksRr?eܽ< 3VxY(iMWQ&@*  6І{|ɱL˧}}}B҉RSr&}? F?pV&FA޾^)+ &T~G 6c۪/@e @Y{~ mRU*ŠP(C5ײ1%L weZDs0r4*4 H!3퀅{lB0 o "ĕ\x|`M|a/cb``HӰm33 i\@I +{;T }!ܚ _CmT{].?4E{TV>to>~ù SwP|-E+ B_ X[uhd5F`,v U¿3!rk8lN<5%Ll_,nϱ9L1"T@D"qe~bA .+O>u԰smL&SeoTh|~D3GJKs0TZ}Q5J`z˒͡zh걏]_Қu G&F(@Dxk2&A8]qm'0h4h8X,t:T:T:kׯ᯾WKj(*bz}UP(l7 o1 R5?)DtMȆ*t20(nԧOⰗ1q+ x^ղh-mㅯ~<>[kT*[e$Iwz_iFUtpPt\FoFEFqA@ADhn:4I%tΞZR{~$=y^ܺܓTy_r92l63AA*#} F#s* iW@i3eh DDk^/+8]Δ,kA\q]Jrjؼa6o؄OƟz]=\dJ)J(-y+@R 1I& ֮P7S$JY3FVMdB*?fd·--g,!nێ mǙ3ůk_yN'!iYA$q%H^>A~u:k9`)3 9x!*-ZP_We%џ 7\>1ɥeY 7w^|Gal|,[jy1\ P~-! $,1R,:@u!e^F h O8תX~ؿkօ8۶l?9|9{Rv5  nHNvH5 ,T SjTH !W]_ ~WXޑK>O1pc$ 5xq\Ncrp PB+Sq1`u)gEz<ϻ w0 ^JQ1;; NK"'[qC7V˪q@֡P\͜vsLSS]Z[>|/A6 Ň?|/% /wPH }6nx+B`0[2,+\\XpȺ7L/&@Ufw:xa ^lp[/2mV|擟2PtzP;P FV p@!,J H3Zq 6d2RnV:b> 5JGr2A,x{  4fn*7ROd2!،>hnjƲe^lDQX۱eũG)4v;Y c8;|A̠5|Z p$ip~ t:Ɔ@@Pe/7THT(fDtP/|/A;_kgښZ|~ݜ<۷^7b BPUY*,o[¾/ݯ2133RHFhbIPTPU Hyb)4/R%5omxꙿ#YW \uXӹ&)oKq%bdtO=Z9C $B] C 0QIN$!jkjё4,<򽌢O}S̝gRn_(++ؼho_qӇ/ |Ipj}xB cBk $B kP4#yOFYh>o^pkJ7+;q[.;\za2OVr.e8=p=Gm7A7MGچ;>w].볧,+TXF 5. B$fEpPwB6nؘe( -lŋ*_w|_EMua0[oC?{W UI033* d I@$d  ?_|VX5/CUqd߅H3z[.ex' /Ҫ2KmM-/1tsEQnђS$h4l6ZښZ+P[SRf%/+"jwUT@$ԥ!HG]:2m\sƾ|/cIЈky/)>k6+<+;{_ x8sP @1VX*`PVV*k%jklhnjBU*R؝v8M9LLczv. .N N RvEcC#PW?p{xBQ> `ú,=2V\}UxO}}˦-8JUe>v,J)fggQ]]A3luش~#.zV(ȄŲ-I ל ~݁D X,0`4+Vl2e=5HRGrKB+@U - ̟&X+^l(x#"T3QjqENk[7ow/zfY`3X|6ۀks5*, &`2?A֞޸QXR@b PzK&xx5, ]׾ }ӪܳPgw_xqd X޶+WoD[k[=$KH1md|)(W(k.T~RB!Dۮ]6KPğk#+Y\wKB z7oێ|^gRnW\M6aݚueQ~(lK[!`{WTno[%(⏏1\eTZ+~:.NcC#^|ccƵ}* vL*:. 2.)5(a΂ @-:My]P쉋+E|U]$B|oŮwa/a9& Wb x%eYKWa{".5v 5)?SA%B45/SU~4fjdttt}*ǫp8j>;(z=yK!vOr—, P |TpP-J47JyVV^Õp8bXaflޘ bBW-lA$y'+@ЧJz]G`eʼ=_z1u^īr8WT 3h1pa#Jƣ9%JJ&R+Eh>֭*KG;tp8ŅbY??0aWD`pAIb5:0"(R"@ּ=[)Zuu5V @,BHL:5I~d\0!{ U)IiJ)$K P۲ּ<^aQB/S\. L4 Ο)"{~(2--e6O R!Cnʇ@UULhZ3ӹ\Q)y7@P3;h6'CZ=ӓY@U Ν^QInyPUU|O=58LESebUgTT:D h/Q ja3pdPQq>Xk^}b PP@ 4}O SOaxd8l^_yh.pTJLvOEPB@@\P֭YgVkCgsr-% G "P@U -`P؟D^S\ٙgC׭?`p8D[2,-Z(B/Lu|dS$ 3c炆u{=\Kq&YDdUd}JF:S* >=^75IHc-ϪP9rH̵h?qFq1+|7S9@mQӗ>hà˱0^Җ8&b sii̵9Rc9NLLmH> >, IDAT~y˄@bGT Pc'Ib@"ae !;fƆƜ=+]Kͱ=%Ip8QDH!P2!|>9UgP@:2.(I|It(a `}]}Ξ.r8J B@!f┄>_L8h)lC}0D%@t9(ħP\0r8NqhHf*/} zdzzzX)s,(C?DP@x"^0dptۻ)+ ^ 0D0oNXAIZ4C!reP?s-:/+'9J,J !!) |> r/P@P:Vԗr8 0L LNMesIj0/ QD @1E (_>DMHlU4JiA$ # H ڲLdJ^-9 BRpIƘ6 @xGXB@(Wr$&@$(Bܸ£_ D%1j 88*&"~H=;D ОJG+ !Q !Чa"0<ЉW(QiX 9z65tGN*a-n7S` :x`J'qAIgJ @xBr,q8ř3WabrYߺͯ,ʐ *kQ+Kj$r1&AQ*A },W'CdY0L98ӤRN'ffU$sd`ކӡ73>ORDf ,1#$+K(ֵPCQ2`0@R% YUfpR@)GnoOK4n('pkhN:.+1퇿v`lX&A @$2N  RJf͝i&&b01pf +om<Iӽ/܌qkHp8!"ZRfs&@qܿ9MTXF@R @ Kzx|%26'Nr8yg{n7>sgpۯdz293x&8;2 &-?rATP#&$oo)Lvwmض}"yP &U}@fso&(WI LO*e28u"ݸ7"t~z;qJ?RLLN```g084#JJJҊ&466fH2ۋ.ۿ=zptd׸Fs؁JiJk -pge`!Z`^* QәN:\qM26g h2,N9y${u jS{_w}CWXA?YpP@@("<^V!@Bg%Y-)0Y cZ[Z3*N.q:k3RgphmW ?pќnCPJq^v fggӚє \ՉƆF47555Cvva8:r]]L1@\ Zmĉ?Yxn=kHt@ v*>^f9+ @yYYBl p@r- #2ٿ= ]_ǭ5g=83x镗xobdt$okx<8t>4Ph4RɄ2`h4X( gp8IZJ#e,B)S 4@P@%rr(177Ӿ6#d2af&jFE)T.7yBPn>:;;;p޽Ya<xmkص{*]^/y:y\yb,чV#p X8H^c SE_#|#)R69`^. !NzU|K}?!k֬A]}=pQYs wkw 7p#E^3^z6f:)h @P wGኄng;ݭ&VN8۰P:vnnT^%%AV_i%$ ntgrKF>;|vk׬R2QhXumm6V+ɶ_> W˯{ox/.Rhݺ9={K/V9F:κeeeq Pe< w&0cs[oTMGtuwC![)UUU8}U Z%%%0X,ZV svv177ϧ$s!Qb2jJTXJl0Op{Y]=d'( qQsA7< ZFFoT~D n68/+Duu5s5 VGse娴ZQU]}HףJ|~^^>^n'snxXerK9l6jm6*CҔ  xm`aQ bD8!2y+SI 084XFcÿ~[6og>^#ؽgdehhlL&6oقQ;zC[RV^*XVX,}1~:&eߒ`0ݎ9n7<71@EA`0`2 l6`0uO|$,&/L655Մe`Q mȞN)B@J8 dsQ9 $I8|0nޚe Nw~N׾3+7}&E`XѡzTUUSQ[l6VJEC^WQ2BB Rh4De2PJ N^ A29QR0JqQIQ(xܨtK`^1ky nMRYW:LNM60eXfMVƎ Am`Nsn7@ &+d2 0VBRzQpmˎfv}{t:0 ظqcm4i=.lJSҵXH%CE‘cg (@J#z]PILޫҸy(K m @nd07\ݎuU+/l%Xہ~LŸNM7DQjVVFI{Xb,d6v2P)*Yzq$>KYHxp7&Qᅎt:V\pķ6Z:Zp՗O+m>%c}1V8 ,HU@Lj2hf O˘⡸\@ '["fggQ[[T<ڔn6xw?N>J)-Ƕ-rlKh^ރf`5g$Kdtd[]#\H/ΗwnŎvಷ\5נzeee0 0`2x0k >_zY'[]UU`jMLHNxL*+hDIT%xyc'L S毒<^$Nv#Ez>Yy]p0Kpûou:I1Wq?ߔ7z Nh 3heX ,c~?f!Q< D*!ZWymMz}F١OA7n,تj!***zj. IqP}AAuUU{V Dëߏ[lSt P,|333 ?Ea43ǫy O^5reykd0؞]+贑?Dc֭X,lܴiIF*,%Jy)++nBc8(D`$ +F dtZj=3xv,XWbd>b\{7#i!L&i2qӦɫ/++SERDEP}CHfFkEE}(?Q!C;7 _4E, ?0_eA #|>~cUg>ԃE1itܹb>㖑$YYY^l-MU_fFA]ꮯ199)7Kd2ѱreN lؼe 6mڔN(@@˭c)$)p8053O) >2P ufT>RMM0 (nO+h>?.ˮP4g~\׼#4LI(//Gyy9V\ Ӊ98M) ]$IVNee}E(E!7O{Nt(s>y{*}wJ胄  bf(V[ &Mp80>6qx`pۯ5W_{lX!嘹9z}WĵlRg pG P\@w&”\P|0>> ՓB(j?P.߂E0x|~LOO+z}V̬h_[f1 g{/܌>vS/bL kJM*=̝-~?N*8!NEVrNV(W"xqё4r1%y\e^cM߯$V.{{B֭_w@eLfAI/u+qJ=w.暐?鏳2w80=k$Ci Pqr0u6[b!?żrhx~-ֿ(z Yp8o bYiiNRF#V\K.+V`j!x| W/=uOinnO<SVŏѬU?[&\͝8 K*`"o2P Gqi*7KDݷu,c>? (-Js&Zmmq%dƗ%ݼ<(xȐE 6&~1Y1'7J(Ԃ@rU__3(F144"P%`$Lv/L+}VkF pcUL䩓x߇އ^Vޓͪq?S`;qUy IDATZSeÑs7 ZX?ٹp`|r2 T!KF%j8 i?=,LA܌w@{G~՗}q_fU@FɄ;pG֞)Y*8LQ nYC$Yg9E!jc(G/D H B>)`vu>s5 ZZZKf*8ۖsaO#ʃ%Fݿ{wv KF s^'&F~8/ב DV< c##LbjeIY< @y_@Lѥy*X,ضm6l o]Z w}.sS|+ap$=(Rat%=D\Epڏ5p sF'*f)wPBe,(N'fg'@VLZ颋3Re&MMM7[>qK[?F{8HSnxI-n#R,4ь`zf~ӣ ħ60>Fϝ+.H+@Bphkk1`4QQQ5/|>FgG>2 7d n0&Gǜ~?$IJzM)S笃̒Tz{w-&IL+@AlUh4,_;._Pnn.i[|!%: >l,61'Ӱ'ccc!/~snl ӳv8Bo64X,I`^L[>?Ξ : +:+W…ozӢP˃cZQVO܂O|Y;؇7]&|o`kjZ4P GL>Ÿ{h8ӧ1Q {X%{ ϲ$ bO5Ղlƍu۶48NJ+b--t1zۭ-Iilh:8xHAp=;|n:' o2. 64lض},cFt)Z B &?W ޵+ɠp?.Dž\I7D_~)I9{a@ݻ*EWo~=!`A]u%6n 9P4g`||Kɉ 8 X`e`4a0x0k&&wVb .\N*t:be3 \_[[S4@J@(ؽ{7΍1NE’W`]`ڽSY*8vlѺ&Z5 MrȩhazREQL\4l!=K;` ݬc>?ǘҰ@ӡKlR?'r 7𡛪!B1<. E'N?A0."`O_@:XJ)fN- xjkQRR% !7l[FB8+;: #Y/D鿤$2 u:?iEW(:XϏgVH&!˚YdjؼeK^V6A[ 9|#' !xpz` Oؚ\X=xsn8,! Trb Cޔш[2~(A$_SS/ß"?dY=q$s3Cq1 A(*X(b\.gE ZZ[y9 nۖӘʪ*lr#1t:4Y< }Iݎ#)dH+#^0cÃKAJ1 -IB@%1FA]]A^<څ+W'fYB7D69Q yO)7Nx165͜Gv;"+Q46 7B Pj6d2-^׻:BL&\.|u,TXl|p[477c yc*)cg"X,X#:&'pt|FώNPp ![Cb QgARkxBJK1>1Qp=`Pk!(IKe--Xv-jm9|a2}&i BV\ ],(I-%; 7ǴɼN }={~?9/{FN8L?`` H;YSJ1ta``@ѮPL/5pP"D\NC[[[D(Ba';i+l(J>5sgZb+)~b#03;d)0z+V'\r{0K=;-Jqu_1<2 T !PT/9Sd>Gբ$l EG;wC&࠵d¥LN8#np{pɈ9C>PBp"!ŷCpy2Sm;wLKXJ ( =;\XM Y}$*}\f(n;pBVQOA؈ڈk!z0<>`S/={_c 1#H-- p8'ۮ@"[XBdg14<@zi|1 "\PHND:sy}8v.9?f4q8%_n%A:@2(H xpȑ !Ok%W~:99?~ۗY@g/Vvt@/a$-S!Q}}Iā Y0kAI¹i 2'JK|r%p8 @'`( wER _|h;ѱj4LAL{jG sAxXPRR"kLt4dACj<&w(|_}}}+O$zӍ@>׽;I,}=]!SLFŒ28wrB䋂J455MM 8ɇbn78sqՅ ޽{˴s6O!RL:pѸ1RBZW p$C.hAvUa|7B@ @LHS[=b5. j56}}PB? 9U 1:>3 xVիYpTBbdzZAX2rX `bPkn'd_XF LLNUp  VhLt *޲3"VI$x}eѴS@pޮ7 }=1ȱ1}Sb2p~v;oG#fy4BG h?77޾>8iS%3I8p CA8әgGEE/]yy9:۹)2 d$2Η3'@E۷3ιG1A&FN'@.9(Ϗ`Ꚛ_D' A`Yp APZZh|M~¿5'Iolgz$aFVX:D)c'O 'uTgQdAoMca7"\N,0ĩ%KMjE 9"h4*nDWWW5¿#%&&&o~*}:݉8p K 7NtpN\ χtpLII^i*+~R?N]{.wh DZp,26:V1rFqo8\ pTK 9TUU}A~<>twcbzRŸwoҚd2ss:cURޜǍZU}tx_L_\ǎPޞ=_I"\cL+;3iDz7%Q[,tMgI'˔@Ass3#Q]N:nw?tM/zفqHȗA@t8PW%TVVӼ6S37 /K)|~?D?ʪ aA<^DqZ߶o>1\g)E8]..si)JK155vY4&@!Z-:WŅ~xQ ?`߁pyŬ$ WL&&(+yt}cN i9/wII V+o '/ F(4 Xz5FFGѳ?쮌{ٕ8 @9722R_x܈43٧D>z=p8g ᤍɤ<d2a(1p =72&)(>~W̹㵍 ;ӝᰣl)Ap΁P FZx^^0)Rh슊 ^:o@Pz'1Gsfpoӝ?|GW }yssvJY<6Tp46 Є A}T{遁L {{vݝ8p@y `Ssgf ,+TTTd2azzr8y@,õG+狸OΡ8heޮOgj2NzpV<- X\~Q@***ѓ l2btl*r* ]&B(z?q$z•<\C @ @111vI.,bTPYYZ6Z . >o ˏb`ugg$x=PIR$#8x2$AtîD`N @111UW=Zҝ/ t"*& wh4!䄲2h4k͆)1S,%IBo_;A믻22!'cp 4Q"•ҝO Jv6%ĢX,bff8,B[PXF^(GPW "zzzpb | X|hettW_[*h2% ι9x=nTUVB*aB`2PSSp8YDd2E\+5fBʠ{^9y qoc$Iz{33Y733/a3a $,,cYHj lae3?$9-<|ͱ33zfꬬztWuGjUefddVWe~_DFW޾ګ 5Efuuxaawl!>bl[?8 25=vPC,c~~^O+T2E2jW^%L $D&|/狯f/Goo~+P{pVVV}^_v[mK Evwm,T] 0>6FvwW!`<+#]wRJJC_},ݛG|˦]Ľ;w{Roh0e.}^w lIP*%H01>ޑ{GF8{,BBgh4 Μ9õkm\sjuH8?~۷ﰝkpPވc}ۗ͞ը kk.?߰w4rl۶qfv)٬ tA,og!wD\{o~xͨ7m̥~ӰzQgZ%qOFa,[[{Q9pRazz5X-7x<)'zYi:g,׮]#NP!d **<`uuU7 hN=\vdq9ޜ+T*q] rχg3 :pX{ΝUU&&apۋܛY9 /W"~M^}6{o {Ts۷ǖB$ Jvt"ܨ$Ν0 t6@s*\z^xRoƗﱽ5u7ݹTs|&Ƈ>eDsgykV佉Ue<|ʊ9Tsrx"W\ KP~>wd}R6;_uŚCȇ^z麔ӣb(W<˹&jO[[[]F3XpOXXy_2{`56?yK5Nj6'7nd*H czR+{nNW9VFFFz*s"rܿ{_b_}r{s= DFzWhNbLOիNOЭm5>| t< Ο:j{omS~.W^yEOqBK/)wB0NrE}ƈe?jA˲x1OyTg~ .C#JPTÇO2R{^W,8%KD΁PdfsL&@C˶YYYѣG:#($O?4/^_a{wR9l?pk mN/~w0`l$͹9\B2llVuX*um4J<ҥK\tX,ֶ|_.>~{ݼbZ/쳌EX O<у iB199YXX{n6WUV7/* O߻/AM?7bui2>fan|SthX[[cuuU@i" h[ѾR_{|e-ݏ۶mW 7nLV ~'1>:.ԥKm#0~ض+++!\pss}}~[m<|y\r$B|:HZh|y#/@ Nęk AR(A >cccsܹ3[L-ǏYz]wv)de!Wo>(qFB<d4/_fٞ\yY__faKӋ}i5_Zb{7GV;F6 ?M dy0>~@ff˗.177Q{[ᗲm>lll:',-2{|~۸4hv|\ {}a0L03=KjSv9 tc?88`m}mvwwui?s gϞ$^:~)%;ld9o~˂|TQjh>ҏC+`ԍ@"4/@Ub{{ t:M&!pff&x^z**_{=6(JRݯ6mN7}nܘaPuDiK7¯S & L,cff3gΐdB УWjGvr{G)ϖrwQ6mN~-}#;' H01:x")8!+XUr L2==LNNbt2TR>$ߧp4m_~^UlD 顓:pEg?>RvrbJI%9ŋC ҎNEláRˑfՍ4drrII}{ rk>>v&}(2>1A::ѿX]_P,{؏?sCׯKkhj6by͐ $,fccm旀g"CW8&'P=Lsp"H)) ( E"š3aJHRӤiFGctdN{HA#/?> }H^7 &GGY5;;H}208YD>;}T**[?.`29ud,F:ls 6w,=N=>FTP(P,(JJ%* jJB\Rb<K%iҩDAuض*?z&zPŠ0ߘ}O#ܺ%*iiN~+R,##3O":;L 31:勞@E;NRR.S,jImfD۶Aʦ~i6 aOXab1bx;>\.2PT:AOeb J~f@!Gkfh|RILMM] %λpwA<#qffHi"6B;sNBUVVVܤPP_GM!O}T_L&[>!bB4wj0h0t,Ê~_/.\h*W,~C*B{_H&qfϜ!3;ٳgkP{&Z#Q.X][ɓ'lnmS,)+ȦW S,N^N%K5+2Qf 8+ E$+~~Nn岨HR }T"N<czr9\Q5wDoQ,)UWf gMYB"h9wjf@X\\Tj]3`h0|:~J˕JES d $18gf8{,LCAfÓ"=6XYYak{br쑾D%VX妈?7 A/@@ z"DI\P([w,iy(JFqbⓘHKv!b$1FG833 Ld"C:>n?iJ[[[RRTB1ѽbrõ4ͦl%Բ@#l @ /]Ê~? Tr6pYSoE?[X̆!̙30=5<ݾ/%=67fumrJRŲY|IHō?tb'I2AWfGF`|֨ӹ/HR݈+}-0g|g8SSLMM1::I$u~3R\.lmoSTk Y XB|N`Jka4>.Bd 0fg#h0h0بf M重e[޶c eY?,N4bMt33ӌ1>6?.}_JA=r{{lo?8RRZT.}nnu.W[` T@Շ@eYhn"mZ  .mu߉}(_*^m['' ے0! fƈLbAx3::J:> X$WɹrmSm58aۿ*-0J%.00à_Vm 8h0D[ն.{Am(e~G%|8a{!14 L 166J:"HNIӌOC}Rrpp@PT>r-,ecK9{!S\/j"5Jtb6M/s6G{# +01ѵ7<ώŦUoJ)#_j𛆁aA0!LH&I$$Xx4k# GjCk)*CCkW,Skmec5,$22﷘Gubš>Q_6@D; -턿/FC}WsER !ʞB @:a5!Q4 x>) UJնZ]ګ+BEĖ6-0|$ l[sU)D1^3fY#Pr@6E`HOM 8{?W"mR}~ Fr@VZZe Ŷ0 !(̀23I5  ` !OJ"(wz^~7bo۶ߖR;N'J.oHпyM;$KB{B'KU !Z231 fkn#6MoCp;~'ߩ ~X,t` Tm>)CĚC+%'?aH m cЩ)jZ̀+3W4l@:KKKmM[ܟAE t꿝~wI^~XF0fR|\w 9H$ a݂R>k :5*C~ڠPeMlI`yyYM > RQſ!ժz-h/otW ~ho#aE>tDbTZmķKķ hfwB8ONÖ3JtHS4eEvkM^#np: z^)@D=(S6ף(}wߍGB\:\H ҈[:`9a/O2%۲AOavd f4x^nMÉ6O?~w-AJ# ~bKoGӱLs,b/K&iˏ!)%_hYܰ ͫ0aA7)hg:1@S6 JtT< 4AT<Ŀ D?RnE>J%!fXmB,R7iB"yWH~ݬTv[i'&!l TCd,>Zp; fJ~@6Ymq@y{NM j ˲[ߝo;گ䦌QW3uڞ[ Y8YB+7$k+hzO! +BrǴ#hZS,:HoH%4鷏+kWNv0L6S*5ަ5#b0d5Yt١Űi2xjjl6 >&X*.PZ^nt :ptSAmM?:;7W~ {oNoQD>@dM)懤/J亀n;#{x:fBg+W{W)o_v +woz'߉^w$m[qC\W!MVig<%%W .pxUJ ;HaĐg>坠#|3n@{s[Je : 'O&}ꙁHa4>ـZ&4eɠ)Y޲m.t?@h0X@ܓ\-IH? .l?Q|'QMy^d3B+0 ɂDeQ`IJ@o<Rؚm۵IuAaڥ<@è2BbBii0 f hd bO-Pq:nllYH`3ˉdtc_U~d2Tņ[4OC]m^o~O߉w% Tg-pF\$k5˲ ![a R0oKyN gbږL`G8灄-ؑ; vl )m)wR.زQ;;&# n{O91aLx3NhO1 ,k\նY7o4wtDŽ6{7%E?$`MLe5w>¯[f~(At.i6X#/+o"mYƸ3Q ! qȦ`\J Cʪ-ľBҶ0!lۖrOXV4x};vd_x8D2!LA!( vT@!I2fY=n8>BZѿ+ߔoo'?M~!,Ub= [Ǽi# 7gB]ڇˆ14SFb "?3LiB\&p h: &YFQbqD@9lpXD7  H4q+$~m~Z8?I~'0TCgeـm6}mc`= И_smph=ڀM=ȏWvS{4Ou7Hkr4nQzs_RJvTԴNJR=|߻MFѦòAG/G  (pzOl~*nN _!{UVk 6@F]=tNyma՛mWP.&oTQ받NkCPį. 6FќD8a!;=DVJu[ 5J М Ð=7]W> N%ozউBc=F3\h0hDA}T.kf/[n@&`zuh^ 4㠻\P=nL / (QpLg}4t*.SJQM@rІ@37+7J%aH;S{ @\Ckmd2)L&2H^TZw_S0d# P5^$fov>FWf{lm 4GoWkŹwRw^0dMFvu}z(cYJdX,- qF4 y8{zo~h- (cP!)J=7.0&,D( e1VL+ K }׏ O|ˑǥiүQE#G88>${Z Lc پL/F˲.0˵ a y=wx pcyw9M!P mNǙ-͂>o{3.z٬4MSqH$d2eL&w"  " ׯ_L$1&LM5:@K- C a@(#̀{&<(0h0 jO'b?O 1 Ve>I'2d8:h0,.͛JgNQk3nJiSï206X4X1 Z6:j&ׄy@E£ DA({LUD~~Q?$lYK3M8h0,hdsbq6 xj !pBDck0wV|Pw,!{hW;uteS-yKupLh0X4Z3CfL@ m ?ИCf^CM&nA hODy/y}ewC7PPo3(_ 9/e3@X,6@Lz5 5zu 8h0~CYv|hN;u'G-YFm:]~]޺Ոu?@h0, JGMS=Yl(w3_ӂW@U1} ykaǑBtTS2ZuQ#K$gsXtG!ӆ`8luK޿ySW馋R `6n`Yțki v[%Z@iyJ{^3`6#B6qG5p8[a'{@" ITİ^+AM_B9z/fP3%>:Ҭ/?pG:?mԡ6I `Plfwgۜsre7@ Xؐ W3n$Ѝ,C{ȃgT[C>E}wS=7Py [n?eo.'`38P |;++c +S1hdH2!ϡSNJZ_?j:?CCt{*oz"~8i+ˮ]zoFPh&>@p2L.Whx-[g DD݁Is^E/iІ,|ډ{\":0bt.@H:~w_ai3:+!̀";]Q'@.h4ʄMwa@Kzm&vP;v e؃:WU)?,-1?-'}b@k6jO "+`eQ 42gA7ف ev>q ~x! +-royO!ltߴՖۈ#M=Q?t%A5 (m'e:+fZ e~@Uֽ4dF-+ևݮ*,;A!wU0ݦU|waM?>D~R7+j#np̀}!,j@G5zl\};1'#u{CgB߲. -p(/N->ꇖ` g8|DС>:_4sXCYVu~b<(8td:)>~<X.MUuF{}Tߴ܅a]*of4}Ge @{C0lSJ[LЉ1es0 AqT&9'"! <|z4߾eU4 wP"oE^E 8+ fgĽcn[;lgB'? DF%V/WgPt >E߻f97!" F͙g{9!To296'aBqUF4CKŠ Awhg*T#^N)В-OmvWOtg !S~uy{tf.D aۏYv®,F[@C4`ч#05‰ٜP|gtn$D7@8cZ&4b݋n~aļmm #К+^6}h|h}~Y17 g#x {!VSQ"~?(0D9fTQg tOu :ve]y7?C{+өC`] p5xpk !5C.gD7c-d6F锞 GzÞOr~UU +ЙCw}ZO0 :6 @w/K.1 t(c(`L7Hؕ=Ezrc}T>h?hp!Jv 7nT f jY/Ǖ%݈~;1o['E'z^!CC4AƗ(kOD707%@Az+^qWn@?Qz\͢jaFD%, 4>t.]!SOɅ,IENDB`geonkick-2.10.2/data/icons/000077500000000000000000000000001451017372400154165ustar00rootroot00000000000000geonkick-2.10.2/data/icons/hicolor/000077500000000000000000000000001451017372400170555ustar00rootroot00000000000000geonkick-2.10.2/data/icons/hicolor/128x128/000077500000000000000000000000001451017372400200125ustar00rootroot00000000000000geonkick-2.10.2/data/icons/hicolor/128x128/apps/000077500000000000000000000000001451017372400207555ustar00rootroot00000000000000geonkick-2.10.2/data/icons/hicolor/128x128/apps/geonkick.png000066400000000000000000000256571451017372400232740ustar00rootroot00000000000000PNG  IHDR>asBIT|d pHYs88tEXtSoftwarewww.inkscape.org< IDATx}yxu篪́`'7(%[>#vl'NJbpثx?}r9vlgmfxJ"Ő")$(RM`f0 \%z{x\\'͛V'$cP=xpz7z ]vE%]@7`X0BZ\p'&9Çg%oD8Bp"Tc8Ɂ)Eƞ;v"ԳaMlҿ a#"}'/n(5x?09t{(!E~E,ˠ Dv M&(j Eaq^I@@UsĉtpC0@wwwNπONWEF(D0"ː$ ˻=vHi(,2, LMCIJfv[,K   r 1 @PJA[i`{$<2$ct}rk E˒v  _PH-¡!9(%"Qps 1WSB@)(N4M0ksd2&SHe`Tq_N,0w~pWYBSs UU0LҕkS E;Eue;w &6JPEc<YFM JrLϘ97+< P`h|p4?0I:Gl1;z(ÏtzGA4CVjj@(E>G6-E%Lp RAaPJ(*8(82RcCN Kً,I Aa  !X&p +Ɔ/e B >EaHL&q #cru@>iB$4c (ݙڡ'Kdsώ X=:V@)$YF8iH&%Ӫ~:r<|>u|{뮦PK%a-sMhil>`\Lhs!`1/\@LӴl2^hliP߳ݾEg=;~ef46ĭG*FP0^jHU-B4aȲ ]Q[[ YLX45&,f6wx1\zpW쒩4MpJam Pa  B(鱉njm0bڴh { W؋966!!`,c4an Eo>p4My|$C:'iOX( e "ιX=Z[5H+0\>P(EQi"LCGf< ݱ&]+,t[f䜈Ԇ&^-m-h/m] x"Xn\l{SP@IG@ ϣ(uD#$FF'55f-d vJ9~G ՝)kM֊Ǜ 4MC>BA &omliסᅪ={w(ι-M!BrWۯδ7!JA "@跰C1@2M[H(x @i"7Topdҝ"RJѶuቂwnᅪwA`;ɟ:h_ BdI,H&7Y(ŃC֮z(|c;Nq"(]m%T*\=Y{'6ނX,B c$9^vి 'hinX99OϷy%[zz$l,KزqGEJEn|Cd&7M?~~>RBAV~dY膁C/E:4k{2= hl]0[Um+ M&'ރS^kCY'e B:pY14J?*t>Kضmw 8~9} rn%=x)=r߅msmSi)>PJhͪlvlO]b& @v[I8g l؅+Mt嗷,L YQ@)Eg o1?a3go:NJ,#x}>dsp(OVtN~˭EA)@ݨ HR4Y%+ wO`V̙pZZKC" @%}?p(El2 (zhjl˗188a N#͢P(&z.3,SFpd2,ʊ떝;WL1'C>bQXs;_Doۍyj,0MӍ+N72s]Q(4 475c`x)Ba_j=' )qC$QDm(E=\⋢~޹T_hK8c( d2H$CJg'$- "!˞yʧPTs\_B IK Hޞm=&EWh42]uLCH&PUrcG"e2HA-k׮4U3; W$QD;"FFFL&wڪ,PSaWUULbtt]ve 9L8DI!uAf\ϭ8cFjE"Dt:FGGJ:znYP+^?hT*Cف0^Oi lRau>B'DTܪ@ W(e tC&4FoQM0G(v[x|: ŐdvR2BUIZH8TlIDUUH$&? n3b1Q(H Oj8P ((EK6*FUosp0QQtC-e%icۡ(ʤsiipϻ8CjS.sRabU\ k>M$nv">OrEPhLBMUSSSZtC5Ϭ (*7,fH$(/$I­[US岁a-s$4 n(8V;%Q@SU ϺeӖ 1sRREP4WTdJ9Q*n@h\~U4M;cפ p 2~ݿox":}$|9cn%rQs%&!sӳ:p',/^i/ѳ⪘Wg6=?ߠZks ص~e/.r||rz ؒtc&t15~-Uy W;J<~PR:kW^O";W᯿Pj4+2L˲,N|sb=ʱ @SdyTcغykEe˵P}!WlҵmXչ ("3K܁gv!Nm(!`,;,S}E ] U0Vxwb{v8 ㋟܏uk׹- c>񑏻 G1PJ!c "E=: *<7=Av}6Vb-* O<Cc/ _Gg'wn|[^8PrsT:tmnلmJUU1A6E*B*F:F>G6(ԡ}E֭]Hmg_?|d2z9bYB\@vMSb0eY9Aj+~;;\y6=K/-}ߋMl܏}׫E_3>w x[߆[nC{[(hmnh +\>ТNs$TTULoLXneG٧so}?~.U r JePAb`ڵ(iJlӵ6VZ ?jG72\sEq U1:5]w3XBRd-ǙgSƧƮ[-@&P&gpnY),e9ʼn 0Up>44T]{n:џ\B$0S09^S<6U|b/ܰfm׮"d_v-FFGpŹpσ>kx#V)04>G/ sԵ skgq9zdktpM+nNli #RxWW@  vr7Lbm&G)ϽmU1}cxa Xnv(9/ᛏ|DZfe@<ǙW_E:c? "6i2 r<4M+1:&ՆÈcFY! { <3~`R#fC3`)qBA .###%/T5R$}߽xgg?}_,uuu4{zF__n*ȲH]"uuw 8Kmy'pka7LloO@zjN{J~Hm-{+Q$|1OVGvޱ_ӯ쓧NK_Z%Iزuq`&P(͡d5/'dmtZ[Dֺᒔ X| pر-=;^jDkW`gވD"%R?kzJ$"&GqlX_EAGG?|>w]b0 'NSNJK+Itqi%D@>E xRcc㌎" s*ܾ{7V\ Jk@g>⥋>i0V`QIo˦F++WP(6~СCsMl^a,&\+W썓-_o!2֭_]vMdƞMM=wpD|2bx"DQD}liE dVVˋ_}ߚO=ns[6"q'OVl ΝKX9tR5kע}A[h~D"VB6p|>l޴}.& É|4@6jJng_UXbtӣyahkSUU-!>!mmmP]UUJof_ͷM: `;`:OABP_o-PJ!"55Xa[Zv_)Apc-kp4h4&ˀDNp[:vxN/d ^D|h 9:;:mO(C8xyb*7ѧ_@fl /q / |sl2gκ$Yƚ5ku\ θkVcֲ1>v_ڊ:@WqJ{G| o!p΅߆G!s :yȼv +ǂG` GcˊͰ x6f+RGo&|R>l.0/h3H~zؑ?X6-FBN .:'GF{UpM]]7LЅ4(|`Æ 0mK8G?4Á>EȊ(1؃ņG`O5ә ,([U$  zeFNB >~nقUV{B04VJ(tMwqJC%z/8NQjHRQ(‹rBk(\eV((,Ͳp8l& V[[VZhKdϞ=4Hp8,($b(ȺNƘ28(Aa9G QTB\WU^$SJ(aIifP0Y4jd233,Xػw/yGP($$ Q$QeA,UM\)#D;{{TAD8m+9G;YI(Vpx? ɂ!/. 8S:+(L:2SU 0J)UbiDt@EEq,18ҵo>$Ibg*"tEapcĐe;J !i'w EzI/B<ǢG ,;75M3u]gx\g@Oi'Ţ|Tu+ jLlR1l٦oܱ 83IU*ILei*M56ߐ@h(\N&~:#5~$(f0PBTk{Dv3ˠ$I̱ * C=K.:=D"xU ""@A)r8(@a08l,r)xo[T,b$.\b*IT4:&=ki c0c*a`fPX.ާB B.dzfYҒ\eDؕ+WUAoŠȃ>H$ܦ`/ ; a&qVˎ`ڄ&1=T=3%C\w&RuntE +׮+`ot%m e1t per8׼-C\$Q O9%8k>G_7Ϡ䴩 IENDB`geonkick-2.10.2/data/icons/hicolor/22x22/000077500000000000000000000000001451017372400176345ustar00rootroot00000000000000geonkick-2.10.2/data/icons/hicolor/22x22/apps/000077500000000000000000000000001451017372400205775ustar00rootroot00000000000000geonkick-2.10.2/data/icons/hicolor/22x22/apps/geonkick.png000066400000000000000000000023201451017372400230740ustar00rootroot00000000000000PNG  IHDRĴl;sBIT|d pHYsbbtEXtSoftwarewww.inkscape.org<MIDAT8]lTEwݝ{{weZ\ҰƏ!@5LH|RM<4H£A_Z%)v޻{PXK1qmΙ9?g?-i٬i'ʇToGаfeva5~ 9O ~a0GFF6_MJ)l7n.WGs_3vht$3۱maR׆hHeP=<}Xc`gj6R֦D4;vP}`&-*vM) `Z"`0d2ZXִCdDz,$I|8:|hx4X@R'`PX;R1v J)P,QT#!r)?޷oE*Y5^, R juu^# lVi'o J%l IP\,>>~ٽ/g^BRjH;t;`Gr'("J$IB>ahFp˯(;=Cm,1I-pwwn5,J)8K;PQ\]@\vbjz &D8 L˪QlϜ9^|տ7%ӳҿקa1޼0MjauPJ: S3M`͗w:B)=TU3M. ^PPUa`i47w?'w Y뭅LH׵jX$IH&X1M~veP(dJ'!TO*!D3|>b #foݘX9K_KR FPȖe'ԭS cX}c۠O.Bٶeι$(cY59 Ab;|~"*MQ8璛1MY9wvV3gff3 >#IENDB`geonkick-2.10.2/data/icons/hicolor/24x24/000077500000000000000000000000001451017372400176405ustar00rootroot00000000000000geonkick-2.10.2/data/icons/hicolor/24x24/apps/000077500000000000000000000000001451017372400206035ustar00rootroot00000000000000geonkick-2.10.2/data/icons/hicolor/24x24/apps/geonkick.png000066400000000000000000000024211451017372400231020ustar00rootroot00000000000000PNG  IHDRw=sBIT|d pHYsjjtEXtSoftwarewww.inkscape.org<IDATHՕoTUǿ{} 3tfڙeKR)!Ht&,$04;7>9(И)[X$$IB{6 ?}6=aa[Αza"$I Gw%43S9֛X2͢?h#`l~xgg' FFVƹiZLK$[3Vsi󨚵[o6RV:XPOos]m˂!4 Àz֝MvtPV48p@3Msu]W$Ƣvi(p˪IˎM(N4]zY%O\5($m(w,P% !$$ ˲BJ0UeY~P~ %eses6Z*EM]AIENDB`geonkick-2.10.2/data/icons/hicolor/32x32/000077500000000000000000000000001451017372400176365ustar00rootroot00000000000000geonkick-2.10.2/data/icons/hicolor/32x32/apps/000077500000000000000000000000001451017372400206015ustar00rootroot00000000000000geonkick-2.10.2/data/icons/hicolor/32x32/apps/geonkick.png000066400000000000000000000036351451017372400231100ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYsktEXtSoftwarewww.inkscape.org<IDATXIl3(RDY$kwlAX6 nij8Q=.@Kchы-zh\0Yz%MdEQ$3Ӄ#Z=}o-yf߅|t:ztKtLul6GU.䫪F ]RH <WMKRBאLk %< BO X_`o_4647ua;k34a&M8_ܛ\/sxx~oh۶ xoU(B}n.8+C<6>]7?%uu#~! 0McŠ>wshht: m|OwUQ8*^ЉXtOدRi o \~" ˲C[KsQIql+eHHtutg?}^G?z"m;w*g:;jߺ >Y> UQ?ivH!y|R];R3XG+f6ؗÎqcqq\|e|=LJ24"d2*QUQ`Yrl192>CjU Ua #dMJ4 Y.x,zvoru:5#q݇Bp hY]mU"euϓ3ςi3z9 #x x008<H(֖VE"(H$ٹY)8brB?BŢ1;0 ЛK/@ H8irR"8*8+'TUff1qob˔_Va:lL& e[4M@2|P*9<cc8Q8v@.ÅE<G}BccE\bw|L߾(u1pbs3;~8^>2,±Pߐ_o:B",%hpgܹs|ZXȿo=m~mPTpӋ8 (`6C]]ꪮ!  brz%!mű[VfOg. BMM  q/gqu$ D"P8 HiN ^R &':Oh3 com(T*|qpw| XlˠkEJ`0BR;31xok6z|ߟՆן~DxR BB4C[rFNm000`k7 --- WcD"H&O 'ou?d[OMM޳TUrBގp8 _,V;ׯ n DXdsX)cHRػw/EIw-dlHT9VVV6@L@0SιW\~70Of a4hKKKBSx Ei!%,G%ؖ)U nRF";ͺRo dbX(|o]?ShR8@'3HDk >X]v}BJ_ڒ 24rcd~ dB|34P,+e(V[v|*, oA}/l.>7=80 =])= X,qR!DRAs,z_3і87Hu ~W:[E<CX{#? RT*@# ≶SsWkFǣX*}˥8ՙB9[KgMiIH{[RT'">޿]$2J%w !֒_5'H8|Pi~)=v  ЈsF*eRm/MOO[˭GhBͱǩqȧk)}~A˿rJu@ӄQxj5U XSzau~^g^L0D"@< V+a\@Ι L_5MCcc#v܉P_ʳ7d?VxJ04<XWʚD DJk:|t5U74XtR $qd2!z @|Q,}G}mmmrvx^^M!!4t137Wd2WV%pJs"Y ".rE5\t}}f C@"8Yq?^MoU^RqJGׇޚc>B}}=(~TJ#kI`||̒63YݑfT^/R)\|2^#ؕ5- N9Lv`,D5]|A&33g&]OoÓ̩dcޖhoGP9S8vvw#`;ϞHw3 h3)忝9Km۬ݍW'oJw{SnWo݃C_c[-o^̘B:X'ԉxwΩߛx`sG[~{ՠt-NNd$CDBE'Є˗/uCw'd+l)httT %&FhM"@4M+i1+g >zq"otqSX~:|d22}0tǣ1s Ya]NS$Ln%%J2r2:~qq"ٶ-C"uѨsĉD"s~nۚuRzK=$hz@HÀFY~Őu]C![D,`J㑺iر<>>nM8|699YW__p]kH)CLXH@D,lMSB;`'Nl8lp |]+ C:4QdY-|5M[u8!R.9Ooo jem{4MJEs^Ắ)E׻nDnqaY,"뺮 R'.' cccMzQn4u*J9r٩w,.|3B]ͯi69ۺmۖaXuuuiFRdwuu'OTXdioPIENDB`geonkick-2.10.2/data/icons/hicolor/64x64/000077500000000000000000000000001451017372400176505ustar00rootroot00000000000000geonkick-2.10.2/data/icons/hicolor/64x64/apps/000077500000000000000000000000001451017372400206135ustar00rootroot00000000000000geonkick-2.10.2/data/icons/hicolor/64x64/apps/geonkick.png000066400000000000000000000112751451017372400231210ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs߳tEXtSoftwarewww.inkscape.org<:IDATx[{pWu=]^DۉƎI !q)-a%)6S3CCG'LJ'44a@ dIR!e˶dK^i߻?vXJ7~{;;{ w.c;ƍMz5]zP\b!$yXB :o>NuM ؼm[?3&g.(LR}^yԵq O3 yiBqfua4Mx,FTTK,$fH|#W' 9F6>xLm\ jI#jzY3iӶmA"MX݅)%+`Q"~? 3 ] U5#3x4t:=oiEf|Vs\_u>t:I 8S[¡b&b`g͖ƘVc1Rj 0^Mp( ZfY"2 &&&f8NCVfyΎR|t6M A0P(PT\UMkefT*d2d28=UVBa6E< L7!v|>2M^WU)Ex<*teh۶d053lR.##/'hJR( i,B>ԌAw KQVNaYL03J2t]G,T|} WJ녦ip"J p( /Z@)Ie0 4!XhlTQ:ya;,˚m>?]; |D# @@<۶AB ѸEZhou.49 _~2چGiر}]r8pҗjݭB!!~5^71ת hj4!is%"ܼi9>, s)b̳x٧w\>|>ǃ7HRfM!òFJv8{P'4i‹&4 2 uwRxO?45ăA4{]qb]:;qÍ|'Ng@-\O"[pCaۋNt7ܒ34E{?hyӘiv~@_2,HkF /-d&j#5 [#@r7$JM}ǎC bqPit.k/^5-(U+@ =X4:g' 1>1>b8vٍ~ G۶qQR(k A6$7C)Z%W(Oe ZJ/pn_\B9C^8OH8x 3Y Y(B[kJ0va J)}! B*KS` #'MАy{T*вm B\"r&''a.{Gcu]~o>MlٺPl_y+r3! 8ZڡiJ]ES b|+(JLKfR7 σpT6vqpam' f0JҬgHRx`v#[s f޲u+\E\ 7 tavHmYǃ~yY )c?wSSSJ_BCy\k֬s|ⓟK^¦͛2:B0vw7T G/ ] M[ m߶&pQH)Q.16: ˲/~ O~Iut#k!e/.KA @8ӟ$4MQ,g |Çv'kኆ83ӹځd<M p_tjfi< nkm$Z[k뺐R/Ek0LZ=J2.O&җFcccNKKT{k, otTK55i^W+Jjի4>> xC8B(aԮƃq%/e/3pX3MӪT**j*[s9YI&<]&4˲ ۶MMt*u "-涊-@(301SSu]jm]We)])kxA%zrw&q image/svg+xml geonkick-2.10.2/data/screenshot.png000066400000000000000000005326751451017372400172100ustar00rootroot00000000000000PNG  IHDRsBIT|dtEXtSoftwaregnome-screenshot> IDATxw|TT͌zCBBB D`q`cxdS$ْ&ԍ[lbzMB Nӌ`hs{͜|!B!B!B!B!B!B!B!B!B!B!B!B!B gh ЌYB!B| m#MU@FddڼLZVRh-B!B\(x^zl{Z@md* //7W'v\n|>2 !B!ASTh4z=O+ΟL麜 -V+CB!B!x<7h4N<Gz1EFFMLHXm٤W!B!M( Vĵ蟧F:sܹEIW!B!ĭ@RJUWT\i-:Y_!B! EQi4l?8̫(B!B1>yk _:B!BqKQE('xQ WH!B!P Y#V۟cv{Uz4_*䪣NB!B5^4C9ܫc@'t%B!BR[b\|)jx]lrbwͣ4IB!B3Nvj[Jk Nu~~aH,B*iDsIGYB! z$5JGhyo[G; B!BMÃ|]EN?s?wcSsze !!SR\'Utk_#Q;7pX!]MŚ'bydbTzjѝ{묂NgaN 9,1gBqw9RX/[d̏xL+|Ĺ!F0˟ҟuiWhIBܵq+ί3 \o2 Srod=Mp"7]$Bk%KoZK=CTA~ZO^pD~؞A <ó1͓X!I=_l4XKy`KtzHȜ{mi3S3ʉ (ozhԽ͔#up,B!rIZ#t_@ (^7Q_H< !+VXԊS}_Ft29<1BK s}EΜomDEd|o9ĭ WpXĦ1''FQ,(|MT8]uF ~\q,’Tlx$BBqk}*"Cl.W׎],b!+i2IөWOUL+`V>ը'/d/oⱭ?F*%Q -6>?[ٸ~c_"K#8yM(rY2ݐ筙hJG~w?_,!n -B!N*qWޤ~36y SLrTT3+SHRy r*=x^)"VD0c:sO&:> )NdὉෲ?~ȻOA[t 0o^z}>BU]+vrLպcn+ BfB.syEUj47b9Cntf8Ɛv/>"Yro6B:,!!q3f_z4ty}|1cN;jƨ3H,.8ǧ?͞KQ⦵juZʻUA3hO+~P_,>,UI! ҪPT]o?TTPׯQ}Vڋx703f)M&興NYWM)"4n7=_p*5e!_2^ݘz BKf+&EQR_΀MG[) ~#Q.>&~T /8Ep.ɣzf?=qo]5?#^8!{KƌSlg%$BH ccZ{_ǹ!oid,!w-oN/O_?HETޮ*}:/(f7~HI!07Uy;~?gwSDkl*_ |U<5oP3`U㩔ng>•>U)k\! C!k/vt/G&tGB!B!xHp)&I߳㨷/.)YvZ!B!Dg/]} nE ޳mA !B!wabYf9bFOݾ ӑX!B!DuzȳFtI]Kl" B!Bq5chB!B! !B!+H,B! B!B$B!B! !B!+H,B!0u{zBY!B!"Fge焲B!B! vZ!B!]A`!B!wIB!B$g===jܰ۱cN:%15=?V$$$QO%B!Blzh4c0A Itl|y<!Ɛ$Bz-Q1Ѩ5]ky}X-f|\D7#) q8+?l zbhbri=qqhuxtws;/b1I/N#))jIHh!`z j YyycLFfLDǍ} jpDNKt\lʿM[!t#wR;N<'!v q E n]&Ӹ'6z7+A||97!BHh!hnCJǣ1JZEj7$tZxG!nO11̘1JE}}l6MMͣ'55.\\o ݨT*zl6hood2a2hkk5Dtt4^d2c^'%e"V~ȏ^#Ǻyeeed"R'JJJur ^K/2쵄6oLYiǎnY+W,gZn.o&ek5+1lܸܜ|>|Ǹc7+T7yb6RSS)?ww}wPϞz q/l򃡎tؾc_W}sQ^"7x[evñVQ%7@B~`hZT_Q7ARhxcVPhԗ<,X0[ҥ :^ܼa*++ywGŋ? f6m^'IOO Sf|^RR&ݷOϬY.M¤Iؽ{OH-Vʕ1Nq䩐7==8x̭^++twus1193go} c51*:ԔƱ[F+O?Mxx8'Oa,ZD^x[v$W"!!#Gz*j)>}fW3EEEp8g FC\\ܐ[>R'*ܚ57 8ϙͮ{ocrb"f Y~A>:s8ihd"\kSdKcC]+nײ`|bcc~EOO׿1 #m$پ};VdlLrr2u[X,ߦ:::xWBKWw7{ҥK?+Vb T*EMǟ|†ylڴ y5k.pwPMM 1ߢ/}&WΞ=KwO+Vn3m;455a4xfS^^}GJJ F'׿mHg5)x.*tSҌ~ETz4Ա6 Цt`uR7.tUT*Ԫu,/?ǤTTzV1.߈㞕+8t0a0x555\c;JbJZkH!krGkgmdNJ`z.O@Qc 3`w8la2MhHRՆht[,2/?zj4(O.4R Fw @Ra2AGL\fdFhh+=Źfw D{ȎS08~j??["p[?oN'mv jk_g]ObIVZCA]t17?VEQϮ]`B֬Y(L&\.>{gClljkXl)FOذaׯCRٱc IR}~^~UJJܔ`&nࢢb)›Y͠YV:;Fff&iitwX͛ABqiXr9aaan:dΜYBx<HK@AA>UUU,X0Zlȑ= TT -ZHTT .9z)SMXX* 0RrsݿNCQ*+/RYyqȱcbbX`^'Pլ\Ʉa=_owc/^}/8N^{!۪*nJs+WC۷OٲeH+%yaŁD:V_e̙ͦMQ^VNqQ1)))<-?ؼy3)));NxLsN:ӦM$d,և&&K?5MJ':%˭Cmpg0}nz |{---l{Ck[ǎc2Yp!qm5EQ}Q$%:{77( )OAVtr f1!!ɡ"y$\]HYe%'K $7C__}}zVz,ϟOgO7/U˒9s`~]Mdx8~SgKi$!6x^ii4TSK}fLbZ1gU a+ ?0L]"S)K{ow^^zfp|;_ZZ/VA7'NLGp)8HA,o=F#;wŋWfŊe|̝;JKKYl)׷3mZ66OP\|e˖q놘h;0_@nP||<cCnvHJs77.''фhl6c2=[etttrB%&y8z))1 $''̩SlV={D1eJ&Ns$%%ǭٳ2% :;;>=p:Nر vga8p 鈈ΝS2mZjy`9~$:s2u, &'N`zR&NhŽJŋ7=c9xZֲ|r.VÖ-47?smȖ͛yOB董:|n͛î]h6lXOOw`x ZٲNkzzeM<~_UkO=;3gٍ~kˍb ~\555<ÏQwy9sf3cdfjn-di†xq8|@IFF>fə墮Dz})@upRRUUU(B~L.]Dwwx<HLL!cXZŋUqQ233:u g$&&P_HV+XbY`?Ngg*|ZZZZ'KOO ]f%pçDDc2ddLY"M~fȣfHb^c{8n6gW,[ۅZ"?&mF>]Ș2$$$;r>JNn"#.WIfMt:4b\` IDATb9Zxʁ ҳ>fkʕLG}$殞΄"9ȴ#oK{efnV':fΜAxxI% Z~:n;p-x7].7#'ӃZ`Z]_vV1UjJcۃ~Urߺ7Ђ8h-M8'&4u1+/q@%l uU_\f ݏW^SFkWK,*> a*py_JK]_.~ݜ*Óo}s "gnRA̙׾{o'ŋz*>BI6n\Ϛ5kطo?3IKKȑB"#*ceE/^… (*:͊ˈ…J C333uNF#)))!8WVVPPω5V`6']#^'+k*=LJjj*%%g-kG `˖ͼ;|00ٕ`hll$77^oHIHLl,--ŝ;g6(~pqݴ/77!o{m0[qss3~?qǃ``ɒC&Nع}}}d ?] ^|GlmPں:oˢ ٰ~=f?ξ>^{5{1-Zf^YQwx/~񋴴pA\.iiilݺcUEŋSY֭<(BQQ534JvVY_: _ÇsAfΜɦM8{ ^L,Yor[o/|o|x^>#ox&a\uӽEx[OkҘI 4<n^Eo@ddvQZZdggx0-l}l6ȘNKkO8ׇJbFv6eeDGEL7Eeu5SA{g'JV6Z.&+DGExlJ_VhnoglZ2b[Ą=LI!VÁb w߸Rixݴ^R=Yk2)"Sg>S=x#sY6k~{ŏW9cya 0Mos$9J͙+?3|=|y9vþ} lܸJٳ:U+n֙3g)**'g۶PT>|/6wJ? %Źs̚UI7ur(--c֬&ONGRŋ|~a:r,Bbb"+V,GQT*.TpԩSp\tvzj55L='ܹ,Y5k?2@p}}]]݁=>3g2w6n܀JEuu 8֭[pۭ=cWUUw>V^_1c%9iz.v'|Jff&_|).VVj?fϚ75 <<;v Nx8y xwnZٿ ޻o~4442)m119|t]vul{b?еttts\=f׿ QQQl@7K=0` 5ukjjHVk|&е{О{ٳwo/`(ơo;|Fo~Yzl28Ͽہr:::h*7*ey wu*h;jt-x{B0Y"cAQbccX@Wc~8qD`^~R1#;#hjkc^~>~ Qׇ&0#{JNsYf095rhjmcfnf|~?Z\ER]=5zɕD0of>]x^EWsYfMgAA;j-ᰌE,KiXa-? DqP 9!*%u+! ]ӓǥKd4=-py נ&i_tro9sKM`0HMMm;̙R'NU%Ec97+q ill2cǎSVVNoϤdw )N;X0>ӧΎ;Sywuuf@Ss 4흝d71őCgg'M-جzbb2ObǻKX-[x#eD&gfdb;Pq5~nvpޢ]+Mh&/m|gs;= ƏڅS؃:ʁ6[;xϞ}#;1~xzu{%fv39J>-nxaqQO];B*+{\9g* ydWGve]Nvn{X;çN N=ME\ܗ>GgY1 bGCR_ VdK] f6Qrş IY&`pM~S6e噞3)%`){{{9yvkjoo2 O@A`ȸđ(VU]Ґ皚=zl'p] *Hb|46p85X6֡;PƑU7]n.k.??&񗟿<vbbXzn7 >Fҵk iM===dMJWOX۱$L]fX~]F+Z2YuK0>ݽ\3qn# }uGC>]}{]mk4Ktgq'BqX;b罵'y}BiEŅur̈uKW0zXoZnv'_WSz3i4NN<'!vZ;c{,=r9墽)`MGWѸM,M[=Ldi!NZ?C[%77K!B!mm`Ih!B!wIB!B$B!BqWX!B!]ag.))"B!6{|~L7iRw=YYY,\PhkkgϞdza:</j?zdP+B!wQm|19ÇǤ`,_?%~?֭e,Y;waىeݺ!tB!B2jC{1 h4l6;=L%$-B!B!n/ׇ<>|Z{.*jr>t~?.UKNN6/ ?)\$B!B!ӟ򸣣ΟP%IB!B=דϛoEoo*4 >/oI,B!"d |ӧK;w).>ͶmqErr9y(uX!B!Dx<^z!v:;;ijj$>>;v]XX!B!D|>G|bbv+2HB!B! !B!+H,B! B!BLytӢ5TZD ?fɪνJ'ꄹ/>>R'n69 2۾OL4,@mk(_+>@{M7jI#k!B!-GC59$ԝ]ƼyZIro><ނ?_3f6O3$÷0s&q>i=[5$;>B!Bqg83f̠<Bo4v:S_ EqIWբe0/`8T1b+z=UW'AI7|vi΢"گ)-葉  V3 :Jx1;]cZ=CԘ ˉsB>b,eOa)Hj=^^JhB!B[$!>hWmv?:Z7h8Js=IDT4QD.bOZ<%'^=Xe"+5eܗ V3oek:B!B;$6q:c|X؄4^*FN~AEĴ,ԝ@y)d Nt>:jX(F}'Pŵ{TH~9UM=m!B!(6no 뉍ĨOK]$U8d1 Ňi:>520D`T6iZ;g?=Jm}4 l?| 3H #QWr-0M$N@IƇ_eZqpv]WM\b4涞 B!BX,V7x'Hyxp.\|W)wC+gU*%gta-^!&.v5QOGS ;^-Nt@CD&tr-B!=JETT֭pp)G$yh9se$uFOR~JQZd `!B!zQQQ^ 2?v6=Ju/K(++q۫F)**&**m_AQ9sH|'諧pO 覭h/B!vsR5ܮfήQpfƌ$$y=??IGIիd_6p`Xhhh~ܚB!bQVVfv܈%Kͅ  Fξ./q9mLѐ֭[IMM%--(Z-Z(Xt)>(h4][FBBͮmGboe~?1ydinn:H| &xшj+Wr!Y,, $%%tRyٵk]wvFbOO[[FիWݡH$fOb̛7n[``KHOb $P(/Hyss A!LLL`hmmnpy={$ IDATe$WmmmhmmE4]0CCCCCCCCCJlF4( ipW8H$۷oߏmdKkZKaD" VP;;;;;;;;;Yyt:lև8Wy&޻P*c,\.qhZvf( IСChhhݻw6n   Dс)\%kLMMa%BE8{,;ݎGҥKYuFf:{ECCΝ;$Q;;;;;;;;;kE78]*ȑ)'`&n p_>{vgQ(71-5C0ӧׇ~^'O`xxw]_   VW{{~_mYh4JU' cxx~!099 ׋X,X,Y,,,t?۷VE4c4c4c4c4c4c4c4c4TXh\u4&pD"n;w.{V Z3>a ҌҌҌҌҌҌҌҌL$'N'~h4T̷fXJYnǵAAA/&ch x$f!!!!!!!!!VdK,nx$cd̓ҌҌҌҌҌҌҌҌl@!b@z8AAAdK@"477x4E!!!!!!!!!VdKAC"fffffffff2%7)AAAw CCCCCCCCC.À+q0AAAA2+ypvH3vH3vH3vH3vH3vH3vH3vH3vHe"=77x<V[QɇZ(4rBl!!!!!!!!GP`]xWPWWWq\&bЉD.WrsssD0 0ZF0AAA&^}b1?ol+ €֭۰I%slgCZغGUpA8Ƹ7/hiinԔ$}D2E}nG+~^ݦdmÀ* a4c4c4c4c4c4c4c4+GӧP* uFl>ŵqMhPJl6/,kwlz+*[wCΏ?~@ G>>~ [U!Ѐ~7{0rk q Ǡ.9uD$r7~Kann@u+4c%zgeAAD-OСC8~-\/ V: u)e}3ycK7iRH Dٵr`hϔ@L,+0pT>wY/ȲCCCCCCCC'}OlVh_Fc+þ}oo ǃVܻ{B˽_*ja6XT(uwuz{k`eBCCCCCCCVQ, ŎEzp _:fggaɎRn\&.*͜σ}u[2]╝JG)3AAAVX吊YlpeIΝ>\Dm Mɿm!I/D`(=29R*z« %CCCCCCCVK/kZI pKKKv[Nlaiٓ%22E.'6z%?x+E C$vH3vH3vH3vH3vH3vH3vHO~ kwСhmLpq݈D"00 9ەcϗ .=kUfM0K^*iiiiiiZѬ?Gk+ӧizBe{!BՖ9 p /4=Cvi5Z!QTRQm6psq 426ֽϬn\'OO`z~ loB?w޽{5 U Hr͘mz`ܸrtN|ng1>܌`Ҍ/vs43&ZT@ZHu` C!PAqi\p۶kMb^xhiini'1m}# hA]AᶳLJF5q(PlEjkSYH zGbfX\ڳr9޻/cĪ  X9?à$Xբz.O>'<5Wn*M=سыCЍBP V@kË1C܋A @ihnP\ `@t8r)e( .R%4-L<{:rV ;;;;;; l٠RJv~-j.>j@ wCWF>)a nCs 78Ҍ ,#u@Ҡ 4"׸\zXC),ҌҌҌҌҌҌLZ~?Ν;ÁH$zf)%sCq<7ÚYt}.wLXx=04B>qSSˆ["TLp@36s>:ʂ5XI,,,o7ѣ^En jx]o@VCAV`0 d2AgF>vލ#ØQ8N@ܯ)ZH%u:jQTm=yfvxl!1z᫱,n3SSX;;;;;;;kI3Z&tvP(|Xj֯_H$ƍ7pUl޼ @5uF}+B^5, }}D`z} gΜӧѨoűcǠ˳\H]T$VJjAAD8u O~cLOOW \&Eà^sq?HegfY?c8'qT=_+bq^h)gU婪M^+3RBCCCCCCIK0'|qC|q  9l&XP.vH3vH3vH3vH3vH3vH bìc 8vR`2\'m)fsVqImfJ}Z{t=^@dʘ]1٨GUhx tv1f5Ò}naz0 j6yP.iiiiiiVvؑ*T-.hdYֱ]vAVݻ$>Z`V{ރ,fKƞܒ$ޭ D۹Ps-Z:*?gJAAԊflݺU=o|W0 hS{2ks8m3GW\ 8Ҏ 5cŒeI%cr@hY/M[yTU|^jMT1#s]$PkV";;;;;(J?ݻꞻg[ep9 פv2R'X3jla,\LXѓ󘔡Ի-36bï6pT`P]-yz ;W#rCCCCCCI޽/aaa1eoUsWE, V0U ,dNصkՅ-[Uw%/{!B~q%'ihhg`ZD=ޫi VfmeX+Pqe9WA8AihǾ}###th#GEĎ}Ϻ:twwffӍ_q{K{!9 p9'ۺ-YǮ^X,.[X,5P)4X,Y|$SBw%[+Ej@C %&[RجİLx}kFXR1+i-0rCCCCC4☟|^GNK8``hHDhN0ނzUNk?|-kP:01iٟ 5*S4i B^Q!AAR5 tr\3N7ߣ[6^b 8uv?s n|GL*Z kO ;{ g >>H@o0*q-$P')Cl5+]*>|uӼgto6tXpr1cFQ%e2Fah[&f Cofffff"ff'syar ÀX,!`_> ®5e6JhØ}<+,V.s=T_l6[_1[lbjd.)|?ϺZ)4˅0\X=󕹪6j[ ϧ78zRk .ȁJgҌҌҌҌlP;XنDk 4L'$XĹ& Fcr?D$RWMgΜ#Gr<47>.Ds?>wŌRTrLBVR>Kv|Ckx*& a|6y_ϨXd9AYs뽮fjkqr8#<ę!U(=EMvH3vH3vH3vH3vH3驫NBTբ1m* #0d`UCp٭2RB,%K9zY*1WIԶP\z  q߇?'?1wg?)+Gp9TJWחݯR+Fe.=OEʕ2RK7+VBlS}f+C̨ ?6!شC5 .U+=V>RBCCCCIٳ011k|Y|*rjQFDQO'4yPFi;e{}3-{ HeX~MTTJ3lr66,\B^m-U2ߕd|64c4c4c4c4, x N',DXξTu|ݪ^c*H M/e01VkXn_㑉zL.DgzT.1VZ9r5,XX؂%bNF ޜ.Vp),5W H\_8n%|`6o*V%>0R8 %4c Vrjm Ă&c8 m?0BH˭H}|JuqpaUE}&wH3vH3vH3vH3v֊fòc… -=g>:۷onhhC7oxZ{T.P(HTa1.QL%]Sft9B!|5;I _UCu¹씫q;;;;;kE3o?wTt~'NFRCR!no]xf)?QH$)- T>;tBQVՎ]u[}6*ݏ&UD@l+פ=-0B#"  \t ?O[pa\x"u)SxYkyO5%}l 1e%43(-_cN]־f|s\YZb}59,~gl lEg5lACCCU?ᏩG16PTibݺu[̓%*XJd ʘi%CMCY͖R<\<9Z FQcIYp̀/&F <^ \4[Iffff!WdV@۾ G1>B}>o- [zE\_@Bـ ._dJ~|9A g|R]o8vuH9>^D;W5 (WQg4-?K҈: ж/dL0_CV(uT|/g/ϕ|%iDFQ6iDlư'  259FO㗣8Z2%-c;FoԗwБ^4O\ءg'!dڎ7^Eu⢷ijѶcb&ڇM|%CzMÉW=}k^˘.=WQ^L\нM+~Mf)uJ_-,)2QmbM- gej6@Ӛ(fGS>zijg Mnou\P7]:XrIxry !!!Va6ⵟÁ`YSv^)Uzl/kw*޽{t QQ(h72|vh_tNX`qh‘ N_G?̊hI]ny ƛx3(jmÃgkWpsG`+n#X 1 (n5s2ub-\lIm)XCV,\y})T>ӮOFٮƯa)/l+ǐh}6W;;;n_1{V||O1661폲XU@`Ҳ pg6v^G(@'Up렲7ҪM8e..D0nco!U`rba$r*XzzP?y{cP6:RR[5+83(-./|<\,p"vngpz<~ J"SU*51n+&dMEW潕Y+(9viތ0v.`3څưA!'jÅz< a|elԃW9oFaܑ"<tB܋XCGssp)Ѩyqg cyW"Z]mDoN@؃^}-3W1/c}/A5^5|J3/0L;ʠC"X6mqXxa|qi|V;Ԇ[ Ӌ{նb35N,+ĦLw קNW̰\r4K.$ƾBT Ѵ@~vf8)jhi0o X !!!V7(z44`Q {0'>sqA hԈ".HT:2~؞A08zQO< `pml;>B(8NgڶA`\"o)k۝+µk+l6ꫯӢƐ٦isOjQl ;X^ e'_Ѳfۜ*wu׈K笫ضԟ͵-DY lmlJ$V.CCEo#۶ÂCpmw:\ 7-4@޽X缄s Z@{C|g 8奶1 M"SxڊmZM C:Nu2óm'^-90jlaG#=Qke9 |-^ `\p$Xa%3E( _,^vr\ǹef7 a;;o(Dg24c4c4[=Focofٻ ":zgHi^ط;;`ٴnCHx`;Şz8Fr/|`x.Ai!az o{5~a`A5_9O `$gY:;[`S"ǭ&Fja\ ҍjz#Yg sX,|G,d8#ݎ.bΝٳg fg ^3eR~p|J4l| ڈfJo1](> tE(5"##H _ 933?nq-K>KAPeB9{Fgf(tCeRb{rNnZ o~ ӆtћ3 ˆw\ 6YBfi(.VpH.u\Zp\/ _KlNj'NY.d+Ƹ.L*}LXϗ}.48P@Q DQLN>śo-YBd`<0t_@D΋k].>74Joꫯ駟 6 'gD"tZr^MIW:D*UTY& #,d&_])=MByX5+Жpm)cY}PdRVmpULm[~`2&( !kZ.4m5YWHUWr=rjc 4UXX[xLi @IjC}RKWv.EMdXnd;[fNLsof~gjVL9Bl|Ō?*QO 9nҬ2\|9yz—ukG){9F]m7"ͦve0~p85:ǎA[b,_/T4ڊήv1RKbBJ&CU6aV*aDgдH7C K}dN?LCW3Jy(>mPHv!fk:|buU%5z1@Iq%9ߛRcgF}v L^ IDAT^7BʤD*a1^|z9/e%4J2g smCJ+Q5nѶ/^M 7I>fLLf 03yQsF[޻}Ϣ?yF, ׂ#(%]mCm JuR4ޯH#4 -AR㋹tP[C}0#u=K xrKdb ǷܗKZ[~[Xl/l\55Žڱ텿`4pLR]kyWC^OK$;v Fӎىu?Mk||xnzW.pJR5G;~?RgBoTDg@u X~lf4uUH%BE&S b r31#Q!+t[zMGjdl 7]O`)9!DIJOllw#0bIi_G0Xi]l.x}p1FdXy'R9vB!|>D"D"1O@?088sΥ|Z㵔1,%Jly>@bSz18ȴẌ́5Ri|T2rdXk˹xIH53gz$'5*ҬLؕԇ*uΡEpT0q<~a=x-W"JVjH=P7|_FnEm1egZap 2'#ThZ [&}B7_|yƵ6 ̧q%33os#x }?9!x? )' QS9'վǗ:TBw&z|i5Z;k"㸾g9r1xϼ܏kIYx08zm 0d/ Wȍ Wl$~?+ҥK]'2/ Yy:+TtR&BxP jSd>} &mH>U# :xvu盇_P%*x'eDL,/ Y=C:k2Þdf.'R,P4v=s#BAn#t u=Xucg.8(Sibե ego"x Oz f|`:@!+MahC3|I7 °әYV7,Lddðݩw mnήKF©cBvb뻏;M[ xغEnؠzAwz˅4XRV#8bAt=ό}nزy|\H.o*})OKms7s\' Mx3ʤ0i#؏34м|/py*(Ҍ%C-FaB—cO~cb/e Id;xOfzM/ :NIjY&l?'_?a$bbt]#^[> ۾};.O$v;, :;;/&f͘g+™Rz}ʸ ,ٕA ⵀEbKHAbA[dd6 ^[Y||[ɟ H θg6-֬sɿ#Y1zjo=\ Tߘ7.Yehg"2eL33 [Rc]ΦmIzٶ(W1 'T2KĘԺb?j2YcAjVТ{kϰΥ31%Vgza2`۳2;_zmmm֍P̄9DQx^lݺ===x!`ۓ}F*p+_+迚J%:Ru@ud3#9b,ȕ8"Wod]J Ƭ,%}nh/B=D 'w/} [MCFľR=NNŴ5Q:(u&4Ō,~_"Nyj?z2AD>jM-"c]ӂ -X+X$:::aXBVe? 2,rHƒLV29hV ʙ(G3) HJ`C }&˼D2 M\T -*Fȧ%ѝ7sR~dB"NCDu9ks y bwMnǷVʁ4c4[=-1W'oF{C;v}Gxz r"&&&Fښ |8x^\p'Oē'O`6i&tuuI 6.RJ ղ^Sr(ڕP f?6Ig9^kV*YKH(_X W}V,᫙:(3m-J4RҼꎞ62`3+kE&&Y}Z82?sP8w5gՂ4c4c4[=׿9x&oكyR(V 8lVTcLb٘d+U%/T t[С0HR3!βp%-Kz3੻׏4@)!!!V7Ys JM[ WAt`0SNl8r1>>Zhuj댰Bkk+o88aÆ Zؽ{70<<1QCx-šGJxYL|Rp>R HS1G00mWؑ $AZXFTv$[*[\eݷ31s_0Ļw::nݾɒeU*T*mDRE$Hb_K2!, /E07<99PX_GV̕%Gm|>އ&hwGsI.%0c]RADqhjj©Son\ VJ%]I08lp&=7=\,a\)Ь~\z===b\S~aD`( FFFDd9<#~;vGso4xǏ._c12h/e]H$A|y&t:!x#n8NLOO͛044/+MOOҥK/r`4qq;hoo/K LuC")E/ٹҬPiυfMDH^_)~A$1X3q֦fFm^~3r ױa=fsɦ~*% !!rÇb٣Ffs=>\|`#rk9#WM)grynQ7n< `bbk=}u#.^]'ާ(x`ZqTWW7ހ :LM*̪}[GIM$\ii]nIBOWI$<+H-'b[F___Q cbӒTAD04pI|H&_%oSEanTkLfޜro dWX.]D2صk<o}?088'960<6&_fsf۟YSo`ߚ(QI0 1?fc+g!v˓ՄkHp%, LU%&kB h۪m}}W|j6՗)IwMn(͒ҌҌJ5/~/҄i]cp)GNO~^n'e_UJ˯VhA/k8 Xrf#"oOyhǮ]p NelȦO;j*;;;;o /--w}<%tn|r1`.^ kW@<[>,_? dd_Z") SUn}'Q|/s3 @CCΞ=%. VKu9oiRL3*^`nS߳r#q9"nr-_!EJZ/wznu1&Io4o B̶,Z`affo{hooӧY?adxF DkѺ»e YbU SWc@*,3eh###Ǟ={~444ܹsl>׿xE~BaX]z7'Tk]Dž` wC?_z(`{'-f/TBͣ ˙`PȞݒ'ML%4c4c4c4-~!x)Wp_F`jN^|[v<~kBs+Qt=l#a5<ňG0²뷣X^tR Xy<Cs|2hάn3j֒]9;bCUrJekT&C6jښLט)%Ҡh ;;;;YP`svkW BD{{;~ĉ uB]y+Ǜ hj'&{aKl %CԿnQZO,`rj02>Ue/f%E+#,ZЃ˿)a}P&N׾\H.~V-F3̖Zxni%L(nWr1* ҌҌҌҬ|(~oiOVz(Lhdj}Tb)Es^?)|]Fww7v܉ ʭYF^D[q,Caڍ׺t6_fV`0v#"VJaF߅cV c(x=8l.^<ԝ)55+js0v;>*-96D:%ë -:؄ס%8Q ε [3D*2iiiiV>?O3a铨y9a?1tu?dB8N|Wx"^xZ{wE__4”Rxe˷qwNb.,3~OT|w'<0&wya1a+hК9;wJj,Q݀: U*̬.e<&({-{kt޹p:P(X$`*9Wz.z Y5q=?cõ=/zARR!!!!ʇ(f4<)"5bLnp8_b``@{{;z{{{n`xxPt-( H)?# ճ?od80e' IDATEh1>Uc#`]L}/o_ 0eSFnfι++ע:ֆos(M>ڵRĞAAS腏kBKmW,hLEJB4#]duu_~%\FwEoo/Jeå5Ų9B4 -jдBEI.6,xJxY욣WAvH3vH3vH3vHES\z j{w`uJ+iS}أ`\O)`u|3[1id|WNG6,\M*ڌ(6}N9Hl;a.&2iiiiV>?"ؼY/Lqk% (o# 1Τhp-k!!!!!ʇ-bRX| V.}U+X^^^Y@(u)nf7/kM[ u?Pj1i6Jݠˠz_IP9f>H4@h裏p].BenBܥhMJffӃK"ZMAnvH]_V;WgIY(2*p8DQX,hZt:3X^^dB__˕Vffb i-ͮ QO6 ^eg S >rCCCCCC呗VD4)ХKUvϫG_,҂\xEX,8<~m{ZttZpS599 ZK >(q|LQ*;;;;;;YPTA QBAQW!mX+Wo P2Ÿ =B8Foo/vVW=&''Rח/j9.lfmjR&$sx)OAAl'/5;q;t;2??W0^me$.( "[E܆)Ѐ|ᠭj0ϯUP"~!i`>z5 1?2I'[d6[vm *p3l˿o~A2d2AC>nCCE\r]]]ؿ?~?~?8VEss3A{gh4 3j5:,XĸT(mS2ҹ#2JQ.o  R)%9S* JB\MQӉ]CđV,ޛsY@gU7Gn:tݮM`֙Ђ-5N& MMMH.u=477P(gϞp0 y:CmmmFTUU*Oݎ.`pK ۽f~?n7< MJЅƏNk}{X#dzzf4c4c4c4c4c4+*C"Ѥh3g|KXVtV= C;B橇/ 5]$lesFyczzmmmL1n.]vv͆7obbb !fz=j([w@ [,stFO8n@>/v\nX14"7F)ҌҌҌҌҌҬ|PBk:8?Ƈ1aAU!^% Bjh ȻڅʵR۾Xtݞr[?2Nn& b<*o'El?kg˸O"eXn8m|xioO9/"қtZFgg'BjXYYY~֌oAy$z=A@kk+ DQDCCZ- hhh^fۑH+++E^sssP*|xL&VyphnmM\y>Asфk3œ[6 8O(؈fc .D 8|Ȁ[#>- -Ehww+o}\WX.|nK>;{FtmtQԽ,V?}߿Fc?5uӾ5 Ο?*chhhs67$"M&juG"Xxr ύu|kRbF-X/  nǡCm4㙙¯~%Z[nvtU(hiaNo]u_o]ED#Ex.|F-`֨V-&62HBHQb(aXV]W?Um5 N>*vl~~z+Dp̴y\]] J1Z!|P\pE kmYp ͕XCk !!!!!ʇo=gx4醨Eڇ`Uh;`l@}xٞ7g+)dӛF/D"\~}N?oƭ[`2>bĉÍ7䦩f̦ #A qQFA}}=_1mpmBXseYq >8U*Ż&  C`iz{qƆD$Ƽqx13ʊhYeOgrߏϟf3Ξ= ͆I,,,Z<&tttĢv7n܀߿0梥 X^^ TZQe5J*/ e-&4cɟ+"ɿ^//ǙdCBAffffffC` &oa",ǭ"`jLzB9(t@؃WX,PaEn.zKd[xwj 0Mhn AN:x 2Xgr nTʔ͵Y@H3vH3vH3vH3vH3vH2 p%dBe V!QߓjQ_|>%e h?'Bffffffe^D"WOxcP*䓋~>ࣜ pHW9_MoZЃ;< )Kpf3DC〷?}KȪ`;Or>U}i?h4VZ Kc^SYKz}x XejMXD)kffffffID"D"QpNh4 ZJ pe] \=$`׸7r rKK DQU s*uxv>Sd۝U:AA9n7{?"DQBH(> \X{jvx` fyȭIf,݄~XUv;ILkEEr5BkAM웬T*|BXm2V3@d].BPA^g>YlvH3vH3vH3vH3vH3v*ESބF2pxxn}-ٳdZYbee)JBus4VOX|M%]$czx̝lZt5Zb)XUY$Ah4){ T9urb˵YJfffffTf6 JKt@ͥ~ǒHn)"HwgOقH$ R+|zd n =H=|R+T2:9ۦ(0(ZrFÑ K8(WWWgI2Np~ڑAAl'  mzkd6 سgfggqQ\~`O7o#o5[8^_-M-2֔\e@o1CA(f:,Sk@œT:[,z _E9\4c4c4c4c4c4-/^±cGkN|M,..aqq 3a/ҥy96 Agj *KC8Y3i xlAK/f,,m0dsBCCCCCCH$[I``y=6 <ו}Gj RI)¹䎩65pST3Jdј כ8  !\@OjbkSo!-2} 8SVfʊQJšd>{V4faR(ҵfd3dœR-g9{ n|> l?JҌҌҌҌҌҬ| \@-`/«7L"c㭠(1Ex;l<_O#0eS -zq0lq-H|J5t%,"l6[X@ Q&)qV(H3vH3vH3vH3vH 6Bǁ{MJW͵ VV6 q<_7ɄҞ)[$@N& *ZXed2@v/  "/Ff Y??QDeun'Ӛ͔!tECLh~q!iqefܡٗldJ%x5& j:c9A.=;;;;;Y@l5)[;۵alEӶUR-Š_sk vfiLE^Dvj1::FI}YRe4 umwLҌҌҌҌҬ| \``1SN?n|ڶUb aivxcsB JA l9uQζdN'p_"AA m#~6Vg$3*pae4am.{(oDfɑ𐍌4#NeIhA~{^Dh 3vH3vH3vH3vH3vH p!5"]D߳ MKa3_3l%=rPZ;l+= Ö r{ 8c4c4c4c4c4+YJ2NE:kY >7APX#N`+h4f4Ȣ(=.A& !(eHLcΔkNZ"RfbRe2R')Z K?rH=%Sr&JUR4c4c4c4c4+oc&:TA:}z3eHz )^껛JKbg;jIz<ϧTNW: K?@>ZviVl8+6;;;Y@xncK()_-Ӭ aRx`9T:Ah4XYYv!B!^&AA[ pBb%S̔m6:i|UȲSmdJ%x5&5 G"~Y{ gK%BCCCJ|bfB:L-(]9~?(gC5ێffff`""]D5l)R)Ja4c4c5o$Grd^SY`l@ (Qʚ*;;;Y@(yU@& @N6񿥊@h4p\/  6 0WhҌҌҌrl#T*A6LPi 'r-<iiiV>& CCCSɚE"YѤ>BSST*T*Ur|B]f+ye iiiV>& bKAp8 1uQζdN'p^*A!L)c4c4c4c4cG,OREl "5gfff`"P, IDAT;;;;;l+= Ö r@qiiiV>& يA6 (z)kK A[ 0WhҌҌҌҌBi K-j9,ȑ'\BҌҌҬ| LJa4c4c4c4c4o$Grd^S=`l@ (P m7H3vH3vH 0AAF= rou& xA%Kww7&'' `Xn r<2D^2vH3vH3vH3vH3vA Rd2AV3dP=Y!!ݻ_Zwo߁jEgg>ZΏI+.iiiiN%hD2dF8JJ0z~? Ʌ Q ,אffG׎a``p!\|>SS/s^K  "OAp8ꦦ&3ٴxD8K$ȊsڵqD"y9.`"L;;;;;$WIx\œT:[,zFy;;پ}{TSSp: S(|>[S2g[`Fõ>mCCCCCVz  -;;YGG4Mo`XvۮNp'ñ`5 ^y0;;3gNҥhii󨭭E$R[W 0# AAf+h4f4Ȣ(=n sʟv]ixo _@{hjj`zzb|@+0SkH3vH3vH3vH3vH3vrY&,xR #Grdߟ` 3vH3vHp* cc8s4~"pr|2D^t\BCCCCSH[<ɑY׃jZ<%d'"ʹ;;YuO?,#LAAQd[j9>z,B!^&A`"L;;;;;;I Rd2AVgm{ o'JҌYU8:6k9=v6Z5"^ u#~\~0]!g-K `"P;;;;;;Y$h5M,RlR2d5,--%7D*4 fqP7>yi?νuXm؏æq|w|7f?}¬'6 0AAQpA p8i rrJuA*Xz*r-Np/(cwqu!熗k \Lh#gڃܽ%"\E^i?>@MC6|% p@+biiiiiiN>4o$Grd^SY`l@ (gifj߉j-N|u~z SCxmy{O 4*,d  z 'REl{ nPIls2~3(Hsa?C\,/ZN 0WhvҌҌҌҌҌҌRl#T*A6LPY@.EJҬ| LAA$d4&q|dJ WN.ԵL `  (!A p8i rrJuAΦœdN'*FՊ6TWWyi6==)JO&r1@^i܁{yЛB e^هQ!19N }|Of c(NjX}68}Y U4څ\Ci`>z5 1?2Ig\u7N5" }lCET6zJOJ fհ^G֋1[Iә8IU]^FhGX=ϣz@й "F5!xVV zg砦ڊso};z+N }u=u aVWֆGbhhÈD"{DF9DN!x m{ՇbZ j5 Պׯ&r]9<_iCtgtcе#DT"@Qp}wWBDA?j(}$J`{TZ-8 V3em=/?~[Aȏ'|m KFz*]TLUr)i%X:~Ž{O^'f נnZۦJ ù`ω6xox8rkQzSAN~y:"B^,_G8k k{pmNe1_DI{ ʎ x/,:-ŀԊC==gFZV )q\zhmWաFOLb-xy:N K?r$@| F$o)J:u z 39N14i>fpe:u MMM8u^J%`"pеB8D}:u.GFqEu8p`'j>cݰ.bS,7&NϪ;q]p]׵᠀QwԵ]_5Ǔ"햢{^BЁÇvc+;Nf?c+L}󠺥[Ec}OgղHl t8&jбVx}QD3Lķx#^)լUN6O EsKV}7n= 7n0l`H&A}}x~k:#4!V NT*J#oEOO&oIYc>cwkkP`Fa*=f&=kPc64Ioeڎf!P^`Ѥ5jfY.$3*L1J`B?ҽ̨eN3AW[YsO1 +JXqii; {{$ceb<;$Ƽqx13~QCX0-߫Ċ4~  &%: gj8ܽ{ Fhii$޽ 8z(:::0==7n$쫺?rg/vLt"]7AAA! "CVCVCC@@VJ";w"B;wnrT^c-_  ( BL&>c\N>N,..ٳgX]]Ů]RҦ@kڔťB 0[{   * Ʉz]X,ȑ#0Sp8RŃ= io([Rh  H)n\t SSS¥KL  $J (AAQpQX,455~ԲfX,B!2%F`r X[c>'   J A044Ç_˗ hpq Z̔MAA ш7|fh48}4`1223% 0AAA:Hׯ_GSSΟ?Ų, Ο?F|>ܸqHdU"ǡ.T*jMMMjy;v@AAAlߏK.ԩS08{,l6&''yMMM舙dݎ7n%4uuuQl}L&Y<{mmxsA'LAAիWӃ^X,P(APw,&ܽ{ ‡~ _g2Ŧ%ç`m$SXR6Vm3\˥qL  (5"Sjd2`0^/\.0==M6bA[[vލeܸxI2 >mϹcu<=F8&AAA* `bb>!/ӸuAP(͡VWWq?~< Q3} ZkU @~_ 0w1ZJn`7KxiѼpս@/ge1i}?a wE:OÙ1^C |;=hmv /xADnWmqu䓋ſ v؁%Kljp\~.CDR8R%AyAфvsfz*{v;K؜w;gڰ_6M~sޅ^C^ 2ڵOG J‡~^Gx0_dsȅohۃW_#.}x^kQd#lMP(h734]T굼~ZPydzS E!|ׯ=?K'|٨: p˯0AXs#` +aw4AU@ `MO P8|ګUӃ p礨; o*r vd.GĆ{Wƌ.Ն?]Õ(}k8KKa+t >M TI)"~oĸ+ E #SzXM0.ϼ9=ZWj1>B(A:nm?} o@T ˫ƾ&UnXGR1uqx":,8ۛhU;>/Vf'ZUc{Z{]ZN밣% ASM-7pi8@Q "2;;_ߠT*ckAZP(Tds) =x[t4O_ bYAcͰtnCUJu~KاiQ&PȑZ\Ԟ\wqL :7$*XDR"),;.n͓ϳdfo&N։$v|mI%KI; A91 V}@Ĝ9s9:zYP93yej#! x X3` jT1/ ,}J"Ub=G dƖq;c4޳|rUϸ@E'0Q˥O/|_ö8dyitL_qP 1Obޭƥi(q5qcV-l)FlYk?Cct}N¹tFⵟb+~Wq$׹ql[gNyCwقȘhNdP;ĐHLPR~+9 ;΍#wBgTPp51WOW"]D !X|3 5/4am=Xcl.8{Ђn˼h ,t`''?-.:g5Bpe )b v(1* }|> )d緍mc=zAn>PKMp)Kq4J"~"qY?(X3pvUS ^/){ihs!ƭP''LӸA.T\S 5IR{i w_V;%KfŃ 9A- o4n$4b2]nJ0`(hle%`W\4 !X¤>0BdBpO c.\dmN#vbRxaz@#kq$:6pJDt$#$E)蝗9߷O1AGkmx4{nC(4>E|UdYjyl텸T\ qSZ,__7LŨjȇ RAQPPXp~ 6]Q3cك?ۭ>[/ `ld0>SӮid ++Yr/41|8}~^B,M߇Ƈ"躔JR~7FL }ļ. ]u)$,qoH$*8tcq>TҖmቢ[C^ rRq2:mKMr IDAT5O2w}GAxps;(jTxo.Zx$+)4׸(B]t<:.Sfc40`cUZ/ZLɮdrnxkCIc4~z]Qu48 <{g:х7?!bixE9RP]a<{>^ٮ18FH+JtЦ}'_n.Oo9ϾCl{wXe\b?:Oq1zkFc&v|ybϾ%e6|%%'g3骂jPؤuXK_;0y4ź5:ug۾|ӄx{8pTjB,]!]HpiAt}Fbn /5i< @:sb*kIl6A#mF4XwQp/լ`V6}'Opna&gbRHt 9PnN}v5[xhjX 㜌z-O.G@t=epRf3OA3|gzKyvt9`M|7vу '\9siַ>ͺϯY?ZzEÁЀujf'j{97Źs-_o7㯽v%MUYWw㝟'+ʕ ᷛ9|ݽ !KͶڰ}C73HH=mj9,u-pjUrvkǜMb0=) Mq iy [-3vOy'fRp74323 !QQ봵sro|Oof# _;ȭHMM̕{lJ =LQt S1,SZ!>Vl V*m@}y>B!|9|P!Xh>z}/xM!B"&mc5:ǜmߋqL!B!o߂nl}3x"a^Fרc !B!|&зI_cY9L!B!oR,B!bIX!B!Ē B!B%A`!B!KB!B!yo>v+B!K7N=osK!B!X3QDFF-q4B!B$H,B!bIX!B!Ē B!B%a^FiA|^~>sO0̘-VT2] vѴ /B~l6jX$^q)byOq/; L0kz&(y܄f}Ǎhl=qva\$( $HB1',/o^&$8 f.Lfc6ЂN}CgȍAgO!sm<,/I;ɹ[?C<]yB1ѿB!B%a^ !BEGGa32:]7Щf.rJXXwEHVV^&FGGoy&Z[[AUṲ 7~o+VL?h0000m+h426:n>N'߀{f3mtw^ftt?@b?CCCmWQrڏ4Meeer] qOs8h]O=$HLt+?ehh3B*W`0:R[SSn$qݺuDEGsYΟ-_rTUEBWWUqܹ[WyE+JJpX,8p;J3=3d2 1 :t_[Wɓo~O>g}>v+WVi&>×$7obll O~^} qW_&66˿H^yY{go୷^ qoX,xv; p#<‡~(B,E |q֬Y/~*'Od2SO/HOO-PPŋu455Cyy)e||_ukx)-]룶"466h$%%N)**d|ٳgq$%%remiiooNOzFk֮ up\="FGGimiAuF#vzz{Rk޻򥧟q iokrͺN^~>?wښ ˖r*i'22lhkmh2j^ ˗s)j.\MXQZ*"Z_|C{Xz/CCݻ֬YM?OAuL&DGGD]e>#z{HKKx6++٬={n5G&%**2F#.\ªU |ؼyo6_|q|[l۶U`Η?^󞪪TVVGkkk8ٳ{qqTUU ZpyٷKEɊDDDp['111gefFWWN󑕙I~~>z9{\jeʕ8xTUUoIII|GDEEhB> q[x͚89Ǐ  oRP|ߣ;wL>t`Pg?9~!^/=(vW_}X ''{"ٹs?W=77^N<^V2 p;v4v;VƆv܉fc|lrj.\7}hV^MkK PK{[۴ښ#;wz)ؑ#tvvc.F#. Μ>Z<liUo?~L6n\ɑ#?"##Gw駟c5Ws8L&zzzٺu+~<, ۃB[[;/<~_p:?A w%'OQVVJYBF=RRb׮;v_;vl޷f?HKKw *v??tvv.bdff2444-蠣#:/7 60<<(DGGOp8HMM%11>N'7mo&&fZ?I---\N@JJ ۷m#%99ޑGijjd *++!9%>#CO;w젬={(/+b6Oioo`0أgBע㌍ΩSWj'E!55fٷS^|y<?t:>}vLSjjjٰau0LffM ~CQi}.cbbvs!**+)\ǎ 6ڮ{k`@ y׿`llvnOpc`㔕Q\\̑Çd?< YDIYgx[L̍nӯKMMexx~_䓏azxMN']]UUTUUX 񴶶rRR"& pB ׯf3v7?Ow B,e}vuֱyeIItw4:ǎ#55} $$$reexKիV]V8޽{{qqݾ&ǖX͛6X,Ӿu`&*** qOZs6oĚ5PUs=С#x?S, +V2L&ruwwSZZhDQʮYիPHim~9_._D|B+Wh2*ysE"aGCN^'$@n^׬`0z7^=fegOu7߼&t1Fŋp8&+;AN *˗z~{8UUtF:r())4&ٺ{ ..kkk ٌZ~nϬ~m%^{W|gw2yimmU+QeڽNsͥZc/ox3 q9Qy}ػoBu(| [Z֢ͦrG4L&Ӵ]yPbQ"#yQUk xѝ:;;9q8,+*B4E%.b2#/?ĩ'\WGJJ =Ιӧ7̛ƅj7ѫ47s:"rV:PUUEcCvB1\x6x⩧XV~{Dhक+Xv-׮eddygNΣu/kgvq4M`0p ڵkXV+fb7{i> J1::`Wêj'?Y?K/K/@[[;坝!gϞ< Ŋ_ G tZO?m۶xowߢ( `B`ۓ'pbsRu5.!L&gϞett" by&?0@uu5QQQB,JKJ8_UELL 'l ۷NcNZZuuuM866\n7 DGG/n#==;zGd5GGu@yMF#}}}ӆϷZ$vƒH)31GD ]5\bb".kNl$8T5<RRR έ( Im6FUU7LuQ"\p8j2ᠸpPSWw3.ɟ^Ɇ 03ۓ'u#۷c04 UUimmX|9V_o~:::ضu+~?{>PSSS\w||^{u>0 ޽{6o]/ [M$N'S+tB-"""&22Gkk+WGFFjFt]'!!$j/^$ ;;fPUܜN'^KuuJ+kR$T\HTT}FMm-Y466/blMB!ߌSU} GGG9}5-5Ʈڗi\US-S}ղA&UoH ! !B!X=:,Rv o%3D΃e!si^`D__uFӌ |~Qe9#3FS 3DB6}+-M_4'LfYmvn7^w?FQTՀ6HzS ɟB!Ҽbٱy{G8;b)K&PJR!B!KB!B! B!B, sXG iNB9@U1޻~O!םϩ n AH) :`QۛYBu~MdS!@h[قhBQoyK zM&"lyB,5/_?qV+[JN"Kbθ˅@s`{b`0H>bN0zZKh-b1rcb)lnx7O!םO˅B#BDQp]7]0fb0O#`4lجVqk`]zvT)1TU ?d ɟB,[͟`M bʜB,Պݍ躎=}"BшnqK9~?fIX b6y7\W V""Bb!E\7}  ~X F@ }}`2zB0 7R f'ҜR(M 2Sf2|s߹$& IDATi.ST4+JblTd$Y!|-j-h'3U. j7fO!M=e^ƥBЍ#`]B,4EQtdX lڼEQxg\Wǹgغm1󞞒Rï r,³?Ϲg:;v`4xB*ɟ?B4H@:;4$Z[jJIp:iin^t]-$d&qW9?Bqx~zzzHxNLL .>@\\FsrXb#;'Gnz|nl6㏉OHXǹ\WǥgMWͅ vŅj.Z/͑ÇyjnzAOo/G( E`ZS ifq\b0hhhxrZsIHKOgմC0>\#'\)S*Z*71BB X, tuv*q$%'idaF4]]))-pۋ(EJj*DİG0 TW348ȪիɝXw&Y8fs}{D6 ANn.}457JIi)eeT\/^$2*;vC?\.66{hv\%:*;wp808t EIi)Ns^X$Jb躎>#_-_yp&ڽ;8ݬ(){/vVZEbb"IIT?b!)1dz{zr ݷ>cHNJ".>UUYbMM:x5LBu5˗FWgg`0P|x⩧(*.3NH{[;}{Bɟ?k&A4BĴ;RX)*Qt]st?lX,:;;߫BdtIFFF%/?FOOQQ$sPv9,eJJK&;;nFGFpDFb0ؼe˴jSTo^4-/wݘfv;k\LkXf<:ayll Edddx~&'I|9O!dmA`MיzW K3ti㤫**`$y<~e躎d">!aΟ;`2.kQǏ㈌(5.כJQ -#-=Ύ.֢iDEE1<]]]űuv~ S 6lHVv6]NEe%IIS w}2LTTVbǏC[244@?Iص}۽P" XvwWۋ댏3<< $nݶ>JWg'qqNOݥK=0'0Zu<4|>nztuv22 `2ƒKG{;ϝc||DWg'5U jkkw((( 6&fΟ;vى@ 융K;%Swj7xe`8UU h=5ݙH{k@L#@xx;nll UU1\#'7Դ4addXf3O=mgB멭Z$&&n::ͤ#2EQx7zb[qٻȆwFh'X8ccD8O!Q" "14ԉ~^HQDg%2;˗fPw%]uд+B4Mclta,VkaA'yuF~nl6}iw܉j0DmpYE%%%k~{ TU%**_|AKs3oFbb"`0l&K/塇8vhx¬l6> _|9);"\f ˖oO9q8h4vey0<2Bd.ݻg#pFI B!u4WU+AܔX_D뀦F-[FcccQwWOEEex^Μ>MmM 066C4>ۿʕ+GQZ9}Tx_F娊BcCNB݃EŤ36:ʡS94>!M{IajhKlB6EQ0 ĿDD_= }EM7M iZx|`&E@v:yW;)ih!B4Իgްqq(JﯢfPTEUT!OTl,/~[Ӗ}[R22¯eNyӯGRZO( < n+-7U@U7ʿB1פ B!nK\\,##׌X~hTAh$**{5齪vwr,Ek}GHp:1 A$&%lr(.-ԱcZbh^~x gΠfFi(wJ 訠+A泐N^rNʬzN83wg Bq[ R9_^A1?zlNH g%4΢2sh#x9>Cb,yuV_ƭ۰9"-twvkÊJX~=b96mNQi >;FowP>d_+*DjoPݞ`0Ez1?woqB!j5j)Z^tz=E\8I+L {;.VUl S} b2Ot>~_o?*E%lg8zs>CJb+~˛?x_3o1Ŀ9_^As?q}r]_/bfY.Wv\K-v\CNJHQr(mE 35Rh*:=8t2GLRUuxݺ 5/VW-뙧,6iitN4w lٺeZhs/qcrnQ_]OOeE AZZ[9{,g6/y`ZRRRرc;wsEU+Wt immôN 'OɟSIedzGu{mǯyO鷦 3/d>\j?cp|;iL fL)D 't,uj([8_gu);F쏃@U ]f, H%6S՝uF]FGFPrw=+ݻٶu+21~ꫜT&@ pD- ַ(-)<99ʊ ?^7^ffƴ3Ghk#0222MNJddd=MDDPxUS\l'{f@dٲBihlZ(Ee^c-ɟ!?yغco-Fg ]?{=tL6Daxh0lrWUqÏOy\4:l{WWAtEA'4^#MRU= ikjK/f|ltt<-ӟI`c;N>:DJnjlx͊m-ؿob WO/|p0:2:f0N^K.W2*`>_#r";of͚kSU3Oo};_%67yT+V֌%۶>46 ̘NW^VJ|B{&W^y嚇IGx'y֐ECcoDNv|'EQٰaݼg\?9Oɟ~fy?~z=(<}7&fb~Omu5Z0сT_ӧ ¯=n7#CCx}>Ё1FЁ:Ξ8ypv~񓟠7s2MK\mmD0/d<;33B'Oɻ\;\c rf),w]w^sTU6 O'{>]fvt]_\VVƺpf|_| _255N?}jk_2uu<8}~d8k& #%%%zgϞ%ԤpP @jj .3uϨ( Y$9ETdd=fRjKDGE3-]GLL4mmtL4ÚK7n$7'ܱcǎ=e\;n>p1@PXXHR~[ڈ̌tLf3΄x.^cphR jj$"N\\~<]v_7!!\ 5f-M͟Q@kHʤÁ EQFFyTUEB^#ɩxSC 됖妷wN =lj躂kh:fJu۝|}}lڲw^}uO?pzC}&].9Å3gn?aS' L&`MM7zP_& x `zuJafu;Z+?{Te7Y<4YqTu Q[gJ:\02h3/wctd}fVX;HtϿNϟR__i~r%F&@}}@B7'xs׾BfFee%`-~/U*ٱ}+^UI_oc3q.=Ժ^ӴteeN>2:;;IOMfs>fL6nX-Nww/_сnˈ8{f7]=x<^6o~R.ԒFo_?._z e$'r[b /7^Q]..]2x@LL4EEtw084 ҋ/-$''Mf{׿JgG=ؾv(v bbTw܂륢gQb9 1( V}\E<ΕUPXG\R栩6 4L-@RT+tWõ6.ˍd[PZMo?}^V׹Ο.<\.7C\ɟWsAhd2aX0XVVلQU5]aj=}ຩQHKO%>!3榰dj>_~a>.W1>^( рld2aY1XL&F͊I=f F]x^/mhG5YYTWWumm\yr蜻F ()1-Ӳ,ό^ygX73{vs}ĢQ?7<ӳk=+᭷r9>{HAݵkLW^}'OcK3s|}{O᯿{棣ȇ:u?7immaqqWj-Y{v/iܯg333O rC7==ûm{r3bR;Fs4³?EpG<} 133>̏~uy)ٔ uVu>:rm,i*"I2:BDZ)u @ /䯻V%\UeE P(D((!,ˡZ5zq [)o4(Dn$I rmv}ӞkK${xQ6uuyW/{.&Ɩ1z\4-u;Xovq k((2!I2,cab&i,ZR{o_//=ƅ<ܯ=إ(^߆B!ELHe T"drceY8eXߓ\4uhmk_kq3 `M(.a8 oϹ/}fUג㬠IwHXxc8۷zu% k%=]sJ&WU,]Ou91~|];GxG LLN5\759ZeR#:r…mEQR$7|WqY}\ y)ٔϛh,F{Gf47F|C!p8m r\ ]/c&a:ea6a"2e(2TTU% ĢpD"eZiZA׏X.ϬeVffnj $I" 8?)In1 @>i}?,ߚsYj‘4dfH$B$BJ^a~~jC9@lEX4F$! {kPTCoWXW*>úEz 869h IDATmS,1MJ[F=YJO x<$)h1Mjj?K`<#; 5 oavvrweێmDRղ,TU4-.^ffVviVȞv=dpped8SSWh 2 7 R< m,,>pNn yWԅ +1њ73.S}ݜ>sAN:\7e580)fn2ur9ٟKkk =Kq#ϕ)WGS>VhhO!2PS L_!4H$$]) 2Q0L**bۂRhE" !bXx./UUq8, _ᬊL&H$D"aLӢ\.cYvT젺ux^dӨ^*0h"N\ZRT,K8S>SBz)TUDS1N"'D$Za}uk(Ko.O ~-=-J>o$DVԓȑxG8v8jzIO[䳟슮Wh ~|oyr~lvo_ o8H0Ϡ땺48t|[D(bvvݻFQ5?:z})؎pСr,_~>9q{モÇpqݶ{s`۶L_[Ґ}>8x{ヒgy8G}P$gW= |M:ٔϵ"ߏ,^3D"GbS*)rrZ׫ CC*"2Pji E\.!2ժEtHEەBT*E<'zc蕯]oM5~ADmbvzNϑrZX.>]1*-QJBN>HiQevv3|J%R93I|>#Qh4B<XL H&i4aR*HjczpPh,hֳl˲L,Ce J6[T*S,)uU[sZu]%>)jEc'"A9HqR+rÞn[$X|Fl۶~?SxwW^ixvs]ʫ9~d,d!ǹ}ܯ^Xr~X\\%fh_|KqT,f, ôr:}1D-Cyz |\.OTjH?'NCgG;}oʡާZRpctvv8yy#ٙdt.ڮBsH@Wg'ȊBOW' ~ l| 3-l6G(. `q1+| 4峆|^&X(Ganvb,"!4MRYXXd~~9uQk6mE__/mm=Joo/] J%ٺut:[ikkc˖!8-۷g&JJi^*PNۿ^{^`Hh*a[̂ cc[Vv2M,o{7`iR5 R9l!6dWXo$X\bR֙ e k7x(TDZgvv\.kDSsY.;vDN~I'~͘i,..(\2,MSQ.p8E쀜E(| 4rݠ/ضid,.昝% ˊ ˿~kHy$d2.,r,,d2 @6H$INWWT UU.+'bY6|Yf_H$ioo%F:4m bC$`(O}˼꫟A{~{ٟr:"A`cHD>P(f NZq`STt:M<GQa* xrg/ Ib .RT迢~"GS)ZZZ0MRIoa\}k7er17S)<E$.\CԿBOwwu!Mcbb#!(|\Iq߷os3qSz˺X04)54峆b)J߬PUp8Lww7CCD"Q*A-2(@?? eQ,d2ͳ8(B,Z[[DXAH߻`Z V UN[[%r~ ꗔ.][T"u<"ĶLff( d2dYfJ& 200@, jG5,IB6HP.LLL211AXDV;iH$twu!+ |!q].ZZ[9uMsP]>m0*LMNzUAa|L&C>'I{{w輿w= [oo_`jjL&e BRI#LҒ&n!NV /G{2>6wK`7~ z4@S>nTƶ2Hp(DZe~~lR8hFoo/[cT*ULӺ\t} 8z#fgPZI$Ƹx"bpŢ$I::: LP.^z8}nV>\kkb$I \ `?;J%(Q(Dr:fpJE3g,KLM]ɓ dYF"HI$P(bf\T׊i“#977G.#YcHE{{;xjvV(JLMMqQ~aYhZD"F:6EW:c#9`kY%\Xiz7Mlb)1^Z/X ,,dFGwNT ۷6܀VTsLLLP*B-amL&er'ӏOt5:ϺE&jod2&ϑ͊Z_0m Me۶XI>_XL F,twwǙ3g9q)X'&Y`]]R ժQw~)pU2E2Etut{m\ uji;w0<}èˉza%`J:BQd*ƞ&^YpM4DM4zo +E#c&H u ICCC$"fYd2`!t(;vl'rEeG 'Iy8!#eM41 '' 5晝 |UGG{FPa<P,ZmۆLO`YK˳ ԕ .?%XAV&JUUt:eYT OB ##;3LM]\ֱYL +ĢQT)羺Yhf@7D+b5.L]z6ri*d]xQ_\BFW堦zJ;زeK,l?۶QvBP x< x˲ tvv?*=n t|3zeX&z9lfȡߒLUUHwe~~,K9=܍iZJ5)Cr]j@U5oߊeف..aRd]Yov9lh߱X[$%yL&1fف 7dIfYDto5G tnMD7>uG|={pl۪c# #8M&, @UeFFv222(_/Eapp!Bus/>_qN&b1".j(v<CQ H{ṟy)n7u]^!X 3"Ke,RU+V%MTfffF{ p]w22Bۮ%DhiǥZiϑHxeY/xI&r`nZH'_> % '#ϟRxj1/eVE Ν#{57Bҏ+@?qtⱋkȲBggD|DE i< n2I(jp=,hd*^zoƏhَC!\:M#єϦ|.ES>k.paj.MUe2le-[˷JVD"4E4MBj]E+tuu( G~LTB tjuW^<T+!Ni"R|5{:is9dE!/nF#wPq{xF… ))wy}du=8l~l+vvvPHcvVX,Wy|ՂżeYH VMb([ ޶f)Wmvx===ɩSTd2YTU#& QTփ=ȑGx͝5b>$I;3:11oZx\rΝ;a i*#/n059D`jJ(͊>\3~|^ `||]d=0DY6į+: l$`lO<?}}}0=p'N,ѝܶw7Wm\7K,c:ogd>|Y(^Q\Nt$hMk׻5S!^?Q໮KHB!+RF7)M\|.F7x>U(F,,,d1*x={x5k_[$9333r9*H$؇9V$өeلpFv>jj_˲q Η,y_:wDB! (-IҦ^T4fzZ-3b'gff]cWQ$4i_^F$KLCUǮ\EU5Zu|>(D(" E/QwK`Ɋ__cǎe_$۷#+%JN:ͣ|@6[b1 --oNwO%{<ܹAN:*_;|>vrO\0MZF# 1BM [EQPVIBƱMAT2jR,T*8,7Mlp FVda`D""R q톑ضMR!͒d#9t]g~~~kTjՠ\.cY&DŢBaL"d5Fa5I\4MRp?%pHܐ3bx9%S)(Z3775M%zS9L@QzAl۾)W+Ȃ14-b19J0 %"ww`hY(2Ţ`}^X+w}'Kׇm vH$l@tiHh4ض`a>d֭Wyw ۱mu_E-" I0:2BgW'LIҩs r3vq' v$p)(3`y5SG|$Ν;`vvάr;d9s&xNeNq]@Ow3޼%"0z\ P4M JՊ?>FE~s9#z{7*رc?dYv.mmmM)Дϛz7YX,QjT;|#QTT*y+*},..2?@P$ϓJ%PMSV /Rq:7jZ𝾱S*'da\YƠvMZۣŨV ,ZٳH$L.Wþ** tѝ; %H$x_ZjBD]oKr9L eR.W=om۴zar9b(X U-$ɗ፵~Wt,|3ᗿe>ou$7fE}8q]w6(}?AOO7_yC,..#_s##;8x7Hķ`||-~^y+:wy殻jPEt9H-O ']HB|_x_, EQhoogvV(wu0mmtvv͟~A,cSTŢOo.<Ôe;;K_m z>"j5@V!uq\WEǴo~+/BRFt)7哦|.|xmrDXZ5pN$I4?}:YL$IT4͠0HiiukG;Isol q +}2귩fAi*33T*:b DZbl۶b b;vpjbftuu( m{rn( IDATj9WzB!OXĶmR9}q˲ B]P*(J^쵨E7 ֽ{k{{~o;?W{ g.gfyOpo8z쓫~=33>> gΞoMXbxxˊٹvvt4koO GT'_"e n{!ss|?\WO ~τC!ݾh4SO>Ώ~ ?ɳ?}ض_="G_=19ſ -54)S 2ciѷOmD"۷5/_>?LYٔˡ);#ȨA(\,(2ժAXX,8--zE׿rVJJoopv( t]#"v%?Ղp~jWYFk%޼@eœw[Z}ًmb zeQ(X\̊ZlPxGF Z\S(,HDo{N=j}ř4 u(Jryl&#WXF4󽫫KU qjY&Z]K}F_ c<1~|];GxG LN]hnbbݣ;u/Qh,v=XK,zgg[*ms%V˶Ye2<ʼnSٺu<?\8H|k_ػ{P,aZvGOw=:~z'xR3A ¡C}sKzgϝ!w9T MlJДGX łH5e/Jz4/I&J|iZ$q:;;ZfRDP$JRA"SONv1ˉON4a OBOgl߾FR`||-C[8}qE +A4kȸ K$բ w}Ξ']!jC,.f1MRm;alpo((up),ˤX,bU"Tb~ןtK]wqI2ś7%?h<Ϫ"I"3X,mh4JwwZu]#LJ<ZmH%Xԅ +8== ~̳w{7NepSu3[+DȤIZL{{ 5ƈ [nm/+ovyݻkϝkTZST)Ξ=ǡd[b6PH0RzJ,R$ٍaRRXUU, 0-x-U8hʍ)1uFNU^W0(}C3gK6Ls׮$ { |6rhӜ:)d(`[,G8C7 Ғ6TU8{PT*:rPH ιi+Tթ_ B9ޓ>${u'"+9sqre4~񱸘HYpqbmjb ~>pfDPl088:Ş@?gϞŶ- Dt0뽿O%AuX`|)D UbB:B--ibs`XE2l6axe"Ѩ.Ha/}ˤZ䲹u>ֽvC5|Ǐs=<`hp#;|#IW^~k:[*=ރ ػg7ᡠ0Nqc+o"[K8_|I`&C>|r}q??sm[9:#/sY.^f!Io%;??O*d c>o?$=p@䥗_[o|OX۶-~qciT $W00^ٓɎfQ3Lo $X###|; 4_|6)7{/iJeժG&w|ҝ % jW#Du\W \fz׷w}FTy{%+X "I 1s0)uf'5hJ[[;l(¶l[8n[nHm$TUarrRD[k{Яt۱Ttr<,niAU$qsWIu_1? ַVW_*s 0eAw oRYBRe˖!Μ9874sF/7{ȗs&H8KX\.cۢ5[ww7Ƽ 4kkkA堾Yu\"M߲m T7Y]ؾm{r/*;wlaE~̳l߾nfg8~ѝ#<_kt'8>,33?b:;;xW 4<>9NVuyٟmVzz{e /GrA.{ ?D+ͧmSg26.%[CdEa~!koh>}}}rSgz^g/,xY{a|sH.>!g9gyI0EB.fsNQuU0-eda j0!H=zܳg_LLLF ֶ)M|5F|̲ *Jq7oY6mm\8MZ0Ll¶-TU0L$I^9ߠs3}s>84$$IΝ{!U⅋Ez{{1 a&qlӧ߇LO#mF9s(ei۷mȊ8mhZ'O.b] $FOOSSWʮs4v/ {ޒ*^Ʋ쀝=HL&dWu 0hkk!ݶ-tB4TATZCէp0DUTRb0D"l˲D077aèHݴ*!o=W J<ɺ4 qkt]ŗ^g?٧Q24^|@$կO^ty~r}%$I޽V+-ҳXY8"_BHbllP(@?=07;q]O6/]kŕ ʲZZrŻž=a$F8s gΜEUU痢RYLUyx^u۷{Ύlf|b\xu,dz?筷; ۲磏>Z3⋍u&T7TmVe 7ДϦ|M\[4aiiXE'\WeC1^i֊eYey&`~ڵ?ur1ٶasu l%äIg쀆9nN  ]ݼMNUU\~0jH0M.q]jBZq쀫b#C4 gi=ȾS/J*RTT+^cC ȑ#7$R,/!YX\\_pduT:LhuRmB!OyAMlbhw0`!(ׅX,m;(OYF4$l.p`85^c,j=g. ɏF׮爇iD"a Zd3155뺤i-ttv26vJ BR>kw;/bm %.}foׯեcڼPUDZ,3K*IJ.h0MT*0PU(T]N䷘Iu5`6DPHC׫^yaeQqMlTLzy}} g{3̡ES>*~tOdLӪST\,"R.ogmMS#FU uN#I8']f_3* [eI[.j5x]]ر0UΏ^a`&}YqTjPmw ! 1luqdT*W7]}kҰFˤQnfȲ R(EQbrIZ`t2r펻|q֪K ZmZZқ"/2aoT(F7h&hO׭ʲ,uE{J @$MY4-6ຒW*,.e?ܹ 333r9r\p9Xwym.ԕd,_nw1S}q~}b:gßɫ~eY0| :a0E"M!PsB ]C> >ak>~NJ I#E)p&ZI8u7i7DM4&O2)Hz\E=R3%UQG*2,a&Xl]\+dYFuHPWi޶nietQX\7eEI/si/ABr%D7;\GDO: |"CAdgG˖lta Y#%\O`&=F JLD8 i^4 DZl"7֧zx>-pM4DMl,ѯT,(2#A D(u=ƎRC,ReşE$&0o`ͳ T@7~}%яʋ_ }7|~-*_ߒ>+IWt/ېwP8A`ngxN2PŶP(CH^=CKf_"V P( !eg\xN:( ^FӴ͜,+hozC 5M&h&6% _ԴJ,aYViCQdT*4e9A$?WUUlbvz.Қw`o^EU:i~>"TjJr `ǎs {iv%4;vGZ%Q. C4-$I8}^uՍ!M&h&6%.۶Z;EQ$g۶-Ea~Β9 !B)ӏ}P$I"PT'N4/2".cY ^Oq B.$˄# Pg"8 獦EkΫ j+M~F!$ g9eъNb1yST֥Cw@ z*F& u? S.*2w''w7{7^M>D5Q ˲D„B!EAE!)12;;M /M4b];mQ*pK`|Z,;wI pYQ{D,h,K(DN7*s)+Ywgqض6[pQuoo/VUl6GB8BUU/]Fuoy? ~q\㸄a"0R.A$b6jYVG4*X1˥> F(˗U''`MjChgY>_Ȱ,h$B4E@uG QkٶMST„aHP_+,!!TU "nu*X"!zGx3m; (I򴴴lxXU53P(S[E"GQd/F4M H$ضm;v\i$LfrU(e$I"XDZJĵ?e 1- ͻ1hIYXXW^%JrǾ@Bb~~0?Ck4&n7{ׅ|6Z**p8iZd2졮 ep8D8ҠK"*kP,#:R}k {,$$MTak+$ZIRNB333aPoK\!$AU(D$(v($lv]E%O$s)DC꽱Z{XUUz{{Btvv`T\7|t*> CCwW^e~!C*+_z|(¿y(nGTx??qs'DMlX,iX鵔54M ÙvoHL(X,=CH6M3``eH$k:D΃͋zcu],!F>ssЮ}WUUXX hx 3u"Tޞn; "} s跿ctdc/wnΜ>ˣ|!nThbhgM/HvRif8ժA*'k8LNNcōXD!TigDĐ$QG+骪SF~1'̩3Y?Q&?8JKӗ0M3ELӤT*yt%7<,..^PH#S(4}"J`@բx|2!/E,Ŷ-ѫZ}#\fsz]$IeJȲ,"Q.b&TH$ PHcq19# H1 qSCVXpbp#BjZR"+2,Ii,..λ'_@TsX8K._5f4峉&Vϖl>HFٴH&q ,sw_H$'GEZw"' i!t_k~R qP]/_f!+/a^o\P(D,#P(q,d9~8HH$a$*\_ IDAT:s0ϟhbD/EUy|wv{6q OrYA':{G$E (]3:&omLLեHSFo$W!]>tU9o^²~ge/<ɿoX]]^oɤy٧Y\X>y.]̋/}_`=Gx+{LH<} ېfdu,4MٿRM2Z YVsd2Y%:}}es_7h ;k TW44-t\?ֱ1Jz}PnٹL>#}{  T/aTi!NͤdBkJ6$IkE1Vf!hեVc6"ϑia>i+lZElz~+HxmY&Pƛo_l.e\p ݲpǏ?ϋǏq%VWZ-| _DoOoO߃OIgB'd B۶ (hZb@B VVVصk EQi">KK+aHe MȆ;Nro;*?(akT*\۶1M&''74Ma|||@8r_='MsLDQrBkMznFzyA1t&M>`ں 56q-T+Wy MPUnom݌V F+SE|>m,/033K@ejm\`A)2e$IX,099 tZtXNww.v1:Bq٫NX JT*233XE:cZm\WGFFx֭^yV%IBrλa{2={cllz뺱j3=D1dj ,--vGF-e#,^*SSLLLiyl4x}Q{"FIPcy筧?MLl~ ۝d~&$\(/[Kvw\NHHHHHH،n҂V"RJH&|_"֓qBBBBBBfj$<(Qed)JJ%ru˗YZZ$l*1*:3;;ۍT(JdYlj+ sǞcbrMHhgvY(T*e2,$S8} !s^g+W_ ٻw/(k:uZ$=+2J%,NHM~DT veB!f2A2/^M $IgnnIHb1mn>pv+Z[a$$lUj*}pi[)U\'-5SS&Lq<00 0EB)گezu8EFFFT*x19O?r e{}"^P< O?(ȃuXjĪ^OG0_3' 1A:uכS'c c,t vH*,yClj=|ЙckHTUΞ=mۤR)5\."a{ p𓘝1^}\ץdqh6Kuͽܬ) 2aB@RfxxTJ8-U }ƽ! ۭ y>4H>o?o߷>(>bV -l[ݻ kdueo^BIjQO*cöCՎC5oYT*3::$)jg˴y77tؐcYO~ "~\ܫduPvuӧ8{ux#aUU}W瘝u]RU\fxhEUh;}j,?-'N%{BT9ep2? ?v~ޭ[HH*B]eY P(e9hݍ(rY6Naff B*B!e}U+Wa)IJ7ZxUH& Ožڻ΁x\<|UwճvYo~6M.ПK)}ArRj7 Jp:"rYΞ=z~e* k\f*OLn˩oN]1uԦM$$I|lu]Znl6(z>334e#ˁ T ӴWBg۲`;,!!LH;l\^^MӴG64nȈuX^^!JQ(bia`Vm[qOdsyIgB$q]sEL&0,JVv;4-Z6Z2ö0?ڃ!fQ$Rs,qQl6CT\.S.I2v~*Tp W_cO}6]0|1"y  ȓ(eju( Ȳ (*e{ϑ#G8p`w(ddYܹJ s~)ʌʯ by~\1yoDqm1yjkիWطofQqɲzpYXXܹ8(JdA˭ Jl6z~́ȚZ[;O$zBB! &{2).=aw' J) ZJѸH8a~q dY n EqP2˸8R\,)˱,2n7.Lo02Wi.KUi"5} S99s6 .ۈ'ܜ~%aƮ]b%jii]qo+Wp\ üoGU(h4Hd2iF\9ր(b7Z QA,]q]\.G* d4 mq LN399A>anyjeY #k,-Bو ue2Y*2RPU?Rv ǎZVEpP{F ҭ*LH{yyg??{oo >NEQ"ɢ)6z00MzN usq5rydY,"tдNImK`ii9(IyR)?Y*dҸKݎC<}?aL:7lc |z:_37q _}.QRix1 p#VArѢP3552bmFVǶm6wvBr8e8{A Y,2,c/,V+, rJAjfr-PGF(\3ݷs:7ض(p+^YEi:X\2r u#9Au݁|cQ $Q1MNU+ R|>,K1Pkbjt:˥a`gUw—=!^p7R2? [i.=#|].=}Hm;r,jX4nwtڡŽeJB7E C' BۣgӴEI 6ڒ$e( y\.n\Luͻ ^؉v,z ?9o0ka@XW9rJW<#8zEQ55"t39sr## ydYz6-\YaeeZW9V%14T!Q)8KV4P,U5w1s玿 6g2&&&rhZFz۸K0 d5F>a\u=ݮ XDZt:4M! J6PS,(  E<ϣH|G^6_)!!!!!ag^8gO^'g ".ݮWEmNnI͗-|ߧ E^Z*BӲ4M oʮ%<{i5|={ w~Uh4fcժ)tݠVnwq]j5Ν *5i2L\ jӁcsUo$hZrH:P(0T)#+ }z.^vjwtITr,PhQ,i6[4-ZQ/MdY&Jqi Brɤi6[)]=6GU#B[Cr ,a&iẽ\6K2LSJLB6?(2,3TP(1MnKW7J"Rq0 NGDzX'(DrY>an1׉ƺ@usA0,Z Y-T5E&fhhNeYxeaum ˠq}UUIU!cZdj)~_'$$$$$$Kpȓۿv] 1 +SueAPU% }8nb]j?9L޾ o )wA~iN*azF>_} nyQ(BX۶,~zz =) Iƣ#(d,3hS焭DQ[E!AN*(JXX}mZva'$$$$$$\=o3`x=_N-[A;/~vEx24,7^GE~Oĥ9}~Bv$ ;S!L6^ fڏ=3='|C\̳L;X/Vp'TuwLz\{HK$ !ɐJXXXAʝD4TUkOHn$3!apSu_'{`z&su*t>@BY$ 0 >裁}1|GQi6|74υsX{uG>3|yBi=s&>AgaayP(h4ػo$177ǹg㜯9j6oh6wY%$k<  "LA6z8 jB$]8*ccc ШiZ>r~籼LPÜ;{|>ϯ^~˲X\\\pqym֪Ձ{>ݷŅ|gdt>w0?(٣>6e!ǎ瑟?({㛯ԷC}}eAСC|L KJ9/JaXF#!${Co!.tv(H ۿI2fRӿ x].ߛrRiqh^$R -Go;pbxnBv" {<^N*|6@poH-,EvolB^瑟B7_ uM骪pO Uf?0 fg8s4v⡇fnvJž{9 lV1 p8y8Ȟ={eZϝ;_=8tO?$n]HgvX,ryu8ϟz I9 2>1/G](t^S4GF]R),ccc?:'$lfᓏ?^irΞ9,?[\Xu66M3k\tuOH$3apa|g|ud?{7vQ6 LLNXXXlJcZerr۲zu 's]>_iyDQdll 4d2fbrNC^GQ8,..WUUvFԀHdp<_~ط?|u}!T'_떯>8)S Cض ?wiRj}9%!+ KK #"O=4W\CK9}z>d)&vJ(|o87QHh݌D?o"Fn)O⡇fgjQ(i4dY7@UUyYL0 ,:of|Qٻ7e}fI^}'\É^"Nl4xky^cxA -.{nrZ\Eȴ1|]xY]YkS7JLAk)7,S(Y[ݩ!nJsRmoLۿYT.s".]Σ=C?kJ?LNMn+.NOկ͡Çp/qI|1w L&O=19saOwCds9:yɰW'OrqzJK/ȎuC9́;tp]kO䷽I{{$s^C!d||rHf yѨ<EQ(VR(n ? bzW'z5q/J [~_ Wr~E^{UΜ>MTbek*t*u;@[o Exufgy' Acxy嗙$l']vQfTWWj⺮ ]n ۙ =s_%]#{#|;˭}nxocs|-eΝ=+=0￟`WJm=L_@.243g-""I,d@޻oBfɞ={k5u|㣏>s]v+!a;oEYz* TQq;|>?kf@ IJObT.c~*tBE<#ɐ!\sBzRY `AH7\+?}?^#nҿ96Z|ߧj)|!q"{K6a s !Y_ɓVUU~?O>ÇisyxsunyFXΟg}t;T*ɓ'y8~8d:2j{ܱc⫓'O= 111SnzsgѣxZ DQqݻ1nHgҁQ|޻.%qGr9V斴[NVQ$Iw tDZ{RHku[@H#N={ ^ ӧǝKt]GUU}mJ227;cPV\O>*$i4OLP o$Lٙ:(|ƗH Tۓvb_qu?>vz_~ELc_ב@;<>Ah]|2Swci,.,}gqaᖢ SǏAYx>q__k ITlۺ$/VzeٱbF[4-[a[{*¶-<_冊N ;F(m;kh6nɰg42 ey^,Ӟ"z) i!x3NpN8/gAe˲ Xw+jPo:܈Q8*>V -_1?p.j5~OUUv*!v;hS.ǡ㱁*۶)Jr9beeIS(~`jHt]dJ'X,rYVٽg?)No;{vwψwyiO7;n{_W:? 6ffw\+ې0 Fߏ7m׻nWzRf\O~J e3GgQBc0 ?}";õ W Cȹ޶P^^xPN1u{أpgXeYmm.]]]V^skF׊h6撺;pNX-￿L#\t_><^x!~oqa8"nmV1M=2Coz$Y$)EAm+^|<3UU<I,;_D,$,/EBUe|Qq\,uŪ[yGFGx7f:_#G+WR*H*$mض((@:^jm;ض4ք]!1P a曁yxER)TJ=Vq1L+VCz}DQ#8TUAUTT UUdR@7^^qdo'בK_hN7_}F_DkȲ,˨ϸ` Mu}|ߋoGPfiRtX4T}˲p7|u=vة;!!!!li824<\_ADk_Ef^j1Muu0,lj6X^GIe)R+2T|^C4v(v?{geiiS\~>0$24 `YNNض*vX  uXIeI ,Iqy|>$ɔJt=Pljsmnƍ Q;jQ2iVuZ6cN(c p]76PFLRd2irj39i_'k{=Q?V|!(RJl61tccnq<Fu* I2(Sf3d24lUM2Bc}"~4~(IXWl ;PS?wƱ!, R(e,//Iӡ`F<߀?I \.G*FXX,bY6N{(Võxax~{]zyDa=Hr, `Y&nj5uNضi}~ }awC xUUX,Fvs*m*TB nvnۛK,"oo+jufav5UWz>A^(EQ9frhW =ѼU-,{-)A}*Nл]N٤uv'yAB/!(f'뫦efshZ\N#˄qg'zYLa;"/,Id2ٍJ^(i*!ް๙Y{-8(|WohUdMR0tjN'ܐkNgdq8%u4^QmVWhF4 T*ݮNyA 6gޜ]+o:|y34"cYSvPaN,4jhZMB#mz"ȹF#!!aUB½aK+gO_4MΟ=3,,u5tKF Y/l!P.bttB,K׵4MY]RqQtmV2i U( (BՎWC܃'D;vvK6Q5(鲶VVGuLӌ34bMpE œ]#[sbWf3lС^~|t:EբJسz޻c'N݌H;F}JMZzq*sѴ@'ˆY|'N.mȲBq5XȲaZ-:\0EX0?H[}1zùX%I$*SVFv9- JMQ.d2@>00;iX*OXaw0 v;nP\4n ϞxرM-KHHؾ${ÖW|{ ˴h6jh,S(qkk5jc9c|={v355E6+upQ \.ÇyAnWgvvYvXP.qN(rf}F[ ;ftiڭ9- w*xn=cmz~Vtt CSFrT*e$I0A',zwPE9kk]q]ðtXi*Jaج*J2 w\=S1MIc̔OբVZ=,^yݤR*qhrn#"n\ 055i̠( fE@Y{yl=9 8 k|vED0!wu]5jk_5lR)3> QPU\Z{ $R^ujkzl2 +1) nƆ.AIgRk|w?SV>uOoVNPy<(/-R~}'R)4:.++Z-VnX٣X,r!&&&@ѱ,G+N!W.pZ(ś25!΄ pJ7BĻAw*K8++U5Z uccR.f3}BF!At:Vi4;w!VdY"022LRR=g[?E~f|tc6f~ %*mQX[[cu8޽4eubfEx<3tݸ 24-lFQ;22L>gxx(qBt cؚӋHȲaqdMѠ 槢(d9MT"ˆycE KKKQVcs&Rbh0̸B曟VB>HHH=Zxc1x睷t㶼x`R3ah zPI 6g?qlV_FCEFҥ˜?<Wu]FGG(4ZN}&)mvzp6'A~_^hPReJQFd2aXչ=s Ȍ2>sγm۴] c>,Y Jc;hC+tl6C`u{}>F;F e揺={ػw/kkkFcxqh(ìrҥ ñ㱱T5C&5 R%m[b=%j6[TVi4qqT`||]@p8?i6`Dm222J.cnnٰ(MbDR 1aqvm/WI[kk5*m>RGyl6m[eT\,dYbxxE /(S(vؘfCHHH:?kZ%$6Rٲ p&P~f3P~WWWV.@ {p]FE:gԩ3\t 2*DVk9ܾEQz~ϖNqX jFmdYFGGbukz"gmFFF(;wjeY4͸UZS(Q D#oiu% 3 jaPPU} Bb(PsrW`U Ş&^'hnRU56jk՞-"N366Ɓ$)lc̅ fzz:.4w'h(R.T* W0 ZWRxb]6OJQHHH=F$g͂Ȯ3w="D EVDq()jMҌfec=f~mz^DCAK9$d(KU*Ēkp="@BfF$7+Dfd{9??Y8---ejz '3=vlmmQD'~YzI.`Tʇ8!'i[z=.ۄJ`zz/14jN셢(`gN렪*Ofuuu@`g){= Yy; aƅz#N]t]EUtTX:1AlUU2{{mAM(O?}i,3334M̓ܲ$IԩXYYP(y~|I^J^b0 XVDQ;,--l ,>lp]7^ϚGa_8ak{}>u)._{OĉFaH|fg1 =gu0\Tڤs}Wξw7T6 įba[%"59Z_\2 'O`~m*ˇ$IJ$vܼVe&cej +騪"~~WϽ6hpq6ӯ _]ZZ۶1 W9vܶ>LΉ'vu:zDގ^I$IקWx/c1s1t@xu$JFzƭ[ضC'ki6[Lz*j-vڤ>'N̙'-weYfqqcǎ{sM=P򎘜<,u]0LLD58^vܰv ibYju܉YZZ$ZSU*QF$rbFCC&xG>=IvE'Zt}^|Vk{19ËHGb8A_O}8G*hye>J>CyK80Ϸ r ^\eE41/+쎪mX$ \Y곦eU?q (,//#]FWma} C駟jh4d2x`1c1^o{fYǥnDaSOaƁYضMTOsܫLZ4 2\Vz@[X#Z\.G>CQE/[Q׮(2yNfeلa&4Nvamʕ+ܺu+Odnn^zZml6<;c1c!HQ5Y.ѝh`N0 bZq:!_cJN˲h4躖ωѧъd N':LJ; ,昞" XGg!I"H ޾;@PK.V+ E?8Cy׳Y^^$Ī6ffS]sǣ>T+A3$N۶Àr hBꫯo˗/{ #cvvl6G~;]e0c1c1#'4 |vM.DN0ԩI:u( }zE}LS.lp~;"2^eX {.y]Q8TU0,.A PU| rf0l&ˡiZj;g:/R(m8E69p(Y=l@csM/^ 03&] M}7&+)laee˗/!N۶ |ALl]< kdg:@QBuz==s'8Qv2kX(1 czws0 sF,Ϗ,;{ àR)n.bJ:!~!*\?l / Zizƒ[ׅ8ݨ3a\Q4BH1<C"1~a>xX, Xȶm0$S(Fjp/\ץT*q H麖£(&mQa[v+B+ C q\(T*RGJxً;>.¶x-k׺OB|u]0"ɤ~ף ]t]^OP VotDP!ERKދ !,3==5kF(L\& ElŶt;)bؖ51c1 s.m#SS#I ڵ!ԡQޣFi4 ."IR wY0BU [8ظK& I= ?um'.|DJE<t]cvvF i{CϑF|EBMT*(H3A@&ceƞ͊= D8wx0^R?!RU(qc<;;~y33fD( !0 vuG YRӴ+AƖH=/Db,8$5%IG{Js7NxC4M\puS]c:( *a_aJU81SS#MNERI w8>N~c/>OO~/c1Ƹ1r*KHЊHy\׍EDw1rL\Xz0)J#MLE\L&e'l_(F֪# :s4} Ut( <IjxP;ITχnľ¢$APU5f؈,+ԽcS ǎXvnM""c#ٻ>{K/rҥ߹\N5?wpiZ4 N?8KKlsWÐg}]) |ߧl'gBliSP]3F"<\KDDqBV5%]R0 ~*#Ɖ*ض'ɠްQ(bu탻$ɱ ! I k~F o$^*' G\]=H$Ei*:Or(De¨_}4Z%l4Cދ}4EHc/N>.|[ϼzΏ~5pnp]9~Sɓ'w4W\*?3׾%ԍC Eqt099]~Y,bsc#}M7˒O]?ڵkASgΐ /PT8 ] &ϐWJ~P5Rcw̻(>߿ X&/yChUQԈ /w}O/|瀯t7EN/ʼn|2[L&sWL&CP駟&pMfj4ll6&VBx%<6\! Cms?~V90, `dcA`q?y>X(y3ajqyZe8(A0TmQyg.ifw̮0o~c|>G_O)}{ۿ[<_x~|K_^8OVOv38HcZ Ebg%Q?o'Od~~55MCDBN߄R(Jz0cbx'(I9 wqZ30H%4--Z%jX? iضe٘AbȞ IDAT;+}83ѵus@|,uj,JҽUU) juׯ/}?ar?>^ы|_8Hn'NSչڅN:IT{߽ vt:|bc}}tno~[lnmoo / ٳg)'>Imsɳ DWjh8O14B_EQ)D\.)  |?/ž//bOIc?t]5:qG77wVOq j5r;wsgaƁ'>4LuIk:4MZ6:8D>;럹DXtM "rB@>_ ǽm}__&F"A{,kiR%\764Ev{1RAU8aTPHGpݧ>%^躊(XBDH<6aw d1>$(P5 vR mB%uT8!膌(zI'TP5M탒IՑ o}yO6|Q=Rs|$( c}jxѸƫ^|C;`?|kP~[w~;"_8">||w|S|ⓟ$ C>6':(kxKVcgJHܲ,Db8nJ}jF٠llE&&*Lz!=9ݥaQ]No} ~}k/?RqWU9+c:`Y=Z6;;U6v'Mr$|r,belp^GѠl311rl6~o֮k<x$ Zz0k6[OUU^ ,(z֟%$I_$R<'*臖en| q0 S ib&oM)uz5TUG}(XuL@={l!\rMSLG0S(=`|#fIRX$np{Ӡ_p(VJ4G Oۜ}%"NLHu:AP.P(]$>­!D,nh4ilnnnh͒QQ/rygOz\d: OgN ,x}><=~7󯞣21IDuIVŲlTUerrB!*$,Ha"RWE[[4 :ju榙%ͥc s 2~}H$,#I!eYfkk鴊?u47M0eyddAkDPܗ&vct:DQĥKus155E!I>o2jYQXc^ɜQyIJtAKqy j u#Iҹ0*{c#߸$躎)q|SSS|oSM`]1L:&:~&"jǓIגy0z)-oP7`d2Y;\ϵ=ꈢ/|%[llls,--R*әdOC? E7xaaY;)g~~ LS'<_{W^~9~8٨1Lg!|Wpcj6fvm:(şfGۤdYǘ"#ǂua(,RQU\nN +I֭ ǎcffԑ$y}" hᕤY$"|?$͐ɘN:2lxUUVk)5/1̨!t$&1t$I" f+677V={^{9a*fSX:lL&'Go3I}[DS"p;/&"yBl_q/~TfP;b||N,x͛vm `uu%4Ms!{ =119}1vv\vͭ'iӘfp)fJq]Ik$k.l`[8Z_Xa=Zwx}1D<&f~\ammZȼmgX^^X,bfEfQ7nr-rzb(Mӽرc~gx^4y.0L 14~ǎ}/,3o| _oZJcYBNjo;;UaiekW_?c{NXդy~NV{w,.-}@\X_ߠl111Q+‰'QD7g^ND㏟fccM0BU5J* W>ks(`r׿u]lIsܸqz*NFW%xqqw2;;K\x@R)~F," Q$I$I%xeΓ0ﱵeJfJ < ̈x|~fw/ Kft)f,][caa!N n!4tB!3{'l6a4c%IaJ*5*P٩agSkϳG6~(xs~m-Ii›$v`"a:!a(|3Ď,e5 ö ڞ{:>95W}_U5{(!y&]Wi4ZI&c3oCQJe?>s%a(bv4?<8댜@+j'0 gYVU}M Ð\.CA E)E0=33^o*A-ʲ1*J{/Ȳ"Ӝ5㸔J0YVY__0 BWG85 pE|{?H=HHa.BjU'3㰶&ٝN ~b~~~W''5vZjCu|!!&D<00MLIJl:zz={LbBTTU&!:"a<Jxk5JӨJQY{{Ϥ1tŜ{ZT۶mu}\!01O> dYAur9Qd?A]BYRsQ\X [a4<03C$.^6b|>ҧݻ7,=v%jb18QS#=UJbKrvM>T/r)) 2y{*#2+++\ېT*`YNL>}gqB\nq]7fi\OlFEt:'PȧqpT͇(;|AlB@q\z.,//Ρ ^MQGӴJ"ǝjbf195ɯ7wﳭ]94-٩bY=:mum^RHȲQ5 Ub4 Qbmq0PT}~s~>% "V6kn q^bffRy:urLף R͛{E1c`bBbg6\BBTݸ w1$^BIl{= JsC> 53;.,DTU"q2ll 2FOGu}& TiTuǥR)\MX2Ex1Ȟ<++KllldYN4F_O'Nyؘ0wSZ6AXvLD% jKhz](ަP(L&˗? ebbR+o|Y7XM>6M}A]# ;Pָv:nЙbffl6ˈ`2FJo%~㤖.w~O$^aBnĜI SSd2Ԫ+];!]L.-]]+ιLNNbг4LӠZv=m29YRX,8{Wڝ^;y!=^E|˲1 x.P,T*C_yfY[[# CJvo˰͍kΨ,6 &z;;5X+YJ\Fu&&&eJ? |0ɺ!+@&G{} AZƍ[DQr>{E??Kij5G mzwz>Lp\FTrp]I.J<?sKg ZeN>.rZ-7MN\"199hSg.$.L|>G.q67obnnIrU@5|ׯ qRHRX,~hϬ 6\C52 rnNNիw< - #dYq5\E4*2岈o g;*H ,\.GX׳i6= ? avEQ( J%B̮ISG} }CXVϧD@'E!FVIբ^oaY| 2!p(D TUq\t]q\rz$666<۶ 0tz=*b9MEUYZYկrz8bt_כضM\\.AಾyEPT( ضMdvvESj?LKWVbbN<٠ e 9JR[4MИu]Dz,Z4Vj5|?`{{vp]˲i۴Z-*2RRi1uZ(U>?SzFq`aa^>KrƆP0t,Ncx02 2$='ˤ z] `qq9ʶmn޼ɥKb۟l~|Ξ}[QUl6 dtpO!+dokO͂ $1QU( |vt=*r<'4\`Yb_H+f\9P%, F$VG&bҮy*9^}kRQ &''0 c!}Lćѥp(R^a@b󱶶udY!0334Ųzʣ>m61LZu'ad2<ϣjSekkZAdrqN<Srx=&Ny^.SL9u14MX,Q:y>N/1 b=r*)Z$sk\x7;y"щSvlllb3T*tiji~~>n&\p}]˲,~L( ><aϭMTU^b6M4}fj*^7-ږJe8~8KKLOOz*jܢ++"6R4bUTUZ FM "n/IW@k IDAT_ySQ]g夰Ica9h 1|aqgzg\'Xu.MUB0Lfo:(A@ZҥKlll>)WkGQƱce]8=s{WYX\`{k{G4 HDCXAC,Kd2&׮q-lFtfggD${^(ڿ |qi67EQ*4 UUe9*{93Ik\x5f8ybbbs~aCP|x'_u1[v 4kPUIykwm.cbbIJAτ(: DfZu'M$33 agG[ I\ӧOs JUUcUʨ?Qjw#ŴX(h6zXSgBQ8URfXTwvG. ,a;;U6bOI|q &(INw 8oRXq,+4K@4, _TR-Pi6 w=Fu}dY0h:N=0(Rp (PH2]FOd}&8r p6c1x/첕diY,ʬ y<˲ڤ_| Y4i s!t:] 7x{Ov]iJA+~ | u= ۶pF+}wU !Izs^i33q ˨N۵almm03P0 3~ a7abbBt.a"}j5nܸARfrrJDɯ8|߼y=߇{oiiA3BSgā(Qm4FGxhKo,fX[Ε+WR51= 8t7n={]w}.5Y9ժ85u:.A J(d.M_{Jr%\MBNJKt JG倝|VIBecczA.ر;nzj_k,N ʏۿK `fm;;;T5n`1y8qbbKAͮOQ`bX?'vڴ+Yrlࣲ>} f͉RaB} aeY"21QIY g/e}&8R "_k_?63(Ieu4)r[na&ss3)0bя^Dd4MefFtfu3UN(&/^fG|g'{W~T04L$Q M(o#ju^y\/̤-h.2 !.\? "`@$Xt$fuTTMoqܫxLNN0==Ig"h0KpAi:PEWΥliB!4,Kop…R.dvv񘄦;^ߑ9)f&z#=\Tm( Y^^fuuY(5#9p-,,`!ۋŶdJb@ソ9d_)BXٶ6vcq,j6  SV1Mr?Nᗭ`Y[[TUm(2ǎĉͦ؏PlMXXXN觬)/< s:粵MףX,211q!#.!ηBS H]D_N|)@l|HT"MؘE zirYΝ{W_}-xP$\V=Fz=z=ξ|AAO/:3oY]B;"YjZ4MrA&Jх ~F 3H~l?wX;w'4^92l6$sre@"1`zzJ lIGi}Z`FM3|BT2vQrOԁZ(իB>aP.abbl6m{}:Ao9ܻaNXydY9uq)Da㸔%,+Z Zf;*#r|ϔ{}/ns_aV lb{Jcvv|px( 0 &&XwVo\,;j5i6i,,,𶷝}dQ>,4 <Ƕ'@QT`⃣>Ici*A6 d2;p-Yɘ>G7 ;yU}s`.)$H$e҆-E~rd86P w,ɶ@ NA`33=GOWGV̪9pLwW11}TWeo3l Wp= $ZjHad'''H$bavB0&q]^{9EPQ(3htou0H(-XWW׈FuV$IcZy1n4B!O>_`xX\r݇M?썤g Tت'I-mVWWq]]FQ;٢rdyyw=E\D:!ϓav7wgyږL&}"m hBu"?I"iZ<ضU0D<߹'X_ݒ F;" uYYYfuU{a߾}XhCΚeȲž=e ӱX̯@*2Nt U;}溟Aq8kXM6#NoK质 }Àh8w>N ;ѨJD8v(#aeNgxJF&x+B,*x,"m -;>9bHbJ+2zjPf FtW&Lhle{o3VXYY4M"?G@k:hreΝ[*5wl6dZEm%޲GNMHͻI2FQTTUCU5VWױm}9"GAQ0ohT_|be L&M6^o΍FI2)H9WQ %*[P*~`r~SHRPt:jB'1nExD"*Rf:e{.HLح9@Tƶ-^"rWd8MS7vnB_ů|4_dE&b0~=ر$ɰq بm1U aR4 !LʝN -ؗH6婿Ζ|T׿~CVpNODԇ,+l~nnh,FPǾD._&ǍS\AwCMQ,)*ضM&fbb1\rxnE4&&&m鋴mJ"C$qo/c ϵ^}%VW|.`q1 翹]7G<5ڃgDyl.z;7N'Ce"˫~ ͩSȑÌNoqȲ"SST*>`4FHT*Wdvg@2CT$t]gbb ]׈Dth6[FyV8r.am =W0i4MX^^aeeBT*-B-"=$ ) {>:Uyzp6xuuLSSY\\b||1oqw#eKUUAQTl"ՙWBUUBV|>PUUi4hC!NZ)B&HDD4dYAdZ&R5صk1E+}&MQ?133Wi4꾖F* oƯFW`v}6MLѻ8;¹A谋1b")F<:H.n7CC$\2,njUt]#iްg~odqaqO ] ΁*Ȗ/Lŝ}xk4!H~[Ra,~ BOu3f(4Mb&p]HDD[t˗?{s~g]__a)ˤR)&&,T*d}H.*f;Ù3gr  t:kJ^fV5ffVY]]T*|E!LP(92 P*C"fysO}y{;#=S]jQ)t]X,N:"ՉF%**aY[[T5ld2I:"LF=>yAYV~I֭6m*uajViM CT*E6+Z_JLCu $bRiQ.}Y"+KfR3iR lm܎'՗^1L GZ-ldCl[* `q]w8. 4MXh4J$NWAp/:6 WY,AU;A:CDk4 n\mQ.W(J$ yG4Uqt=߇ l6T5DHVk۝S*魅$碭]QԐO-4,If+܋ԲS  { in)Z * xL&C2TZ;K? 2RsvQbXVf2d2A.!N)sFN{h0W8}I޿Wn f]~UbXL"PO4E^T*l@dmx$I#ݟ%LN<C{ZFHig1~hb zC#$)Ѩ \p٤6i64 VW4ş!+pxIz,ᾋzx,i#[z]-B2H$[&F!dY :΢2lFIRT`Y6tn[2 HeYT*b_TȠ ّg>-'Er}}|>lv7$ILs/R%)ug^yQΞ~7v׀+ H&(b1,ˤVkP1 #R:jO,a~ :Z ,D":eQ*v9P'"IgOFcԪU^rgz167f۶m7T JfEmPvd2q'$qY__=q:2Z2-±X:&@噂qw* ;ew#p`+u6B>#zVˠ[>XE C*hRتi}]dC8Nd¿1 4gcW|wsO Qvا~5\.fZM/|y*I`WA+D}V$/1"16Kudpv;eY`"+4E[q0#TM>#x>n9wnv۠\.L&I&Se~: J*H (` u|8ӷ4u@"HFND7Q~ͣ^S*q] |>GZģ`pALwt8zXI&~>}Q]* \Plq躎j8U韽 ?cj/u'p՚OvgttB!OV'ngqq} C(B{PU5t] [eE[;"6MBYߵi++y77?IR$D34q;\Q~l:] ,V(y8n(8+$U$A#>bPб |=ZolT:`ӴB{d2$ LӤlj$M˲9h|N"iDnpMc ~WV8Zi*ɤCh>yAIXw`h4*D1t]8hnBBW3c#vm$)$[YYՅo<aZ[ 1&Fi Z-ZN*UP(`Yn4nf%CQlj=ةߍ UVKt4jb\/Ͳkפ&CCfgghS2Y#NAj gn :ljD"qans+j5C^]P7ruj\ tg\ .J:UllVFF;hT<:' 1g̢%qQU fXMN{L}c\h+f q\\5wb"CJi쟁m[|7sp`NT#~PT{-˦h5CX_/ ~v5+p<$LƲp~;Oqlia&4m¶pvulz ("4lC0כx{Mƪ~W2Y'Ԅ䀚?n`0P!`e)jW#!h$#&idc2^7BaFV*'߿B=vH&}0A$:NqD*$)hF6upWl[Tp5M jF^}|;Ŷ0l fl tρkRڋaP]<×EQDjaehtϙ$T >6˺+l-AؖnK}V!.EQ$ ay/WRm;ڨ7?K6R,s/PV|91-I066%M`6e߾}\pOD tZt'[|ǿL~@TfԖw:!L&zRD4۴9`Y(+u(\&AiZʳzF7Ohɾq?zK_ {K\+ ӴBr9IRƲB:cwE_YvnC͵;/m^؎4`nnd݄qpݎ@޿%/?AE;:{ׄR20-A"`88tlX; i.ZzT !av. Y+ /+X"Дi?x9:ҔJض۔*Sl6|gmi6SuS$6iCIسgYnrL4R;f<*ai4ZyZ>YHnڎ m؉r3teCö,EVVVYYYaϞ=lRnHDjYV(Uy|Q0PT(o|o {2ݳhǍ;Ɍuwm`'̆uu}jVEBg7IregwKl#HvmNUN} v'Ƚ6I%uW 0Z-?y%!G0>>e+-!a^ϖedba1 vM4 ]~WH]3y&Q( Q7(iFFFnX]k$qtF ូjsWgY? ڇEbj$ǏY$ QUrL^Ƕ-VVVHS~e[اeYD] פ߮ 't]~ZYGVeq1223G;bz""5\:Τ&˲y/,dY@$!eDd2x$2::eYja~-ܙLf{8?\`6XL&E>4M.^}݇,+͋m HV*,366B&.;A_رFllh7ˎC};lCuNkkwPoEtI'MT ZOwBw;Qqcykk`81aYUeLsAwH0eqq:#߹`y.,J0<ϣjP8sUJLri 5؟Cw'JmlQ*xӷ>%IT9w\H866B<4@/  l@+Jo ,*eYLS)4?<<뺌D\duu Y9Xzn/ Dl6eR*8w<>i$I4jV\.T~ } b4M \ٟXâhP{`AaocY&$CW- q-D"8c,Kx\I#3K&abbh4AݱOmKh$ EH~kvhMkcmABMӈѰ1zOhӯ0yRA+u]Z yf$T*MZ0DX¥~Eob5ힶJ›o܇j>Y筱uQiX[[cvvFA:a||B:v K BL&SZ-",|]KTb޽=.SSS,--]Nq@ö_.O Fv$fqVW(+<k+>Dbaauccc yq#[>hD4D<هr`&ĝ06{Pk ykk\B4>7?P'uNu7$Ij5i WWWX,s_A*xG".`]qD$  ߩT*[:tl6 ;EXdiif M311Ȱ/fv9`O=f*86Lfڧo|9Af<dMRI 'B$e_BS؛i \O$:Nb||'AD 9jƕ+W,l68ضay-da[Gvsu>ڶf[ҥ>;}no _ё, B뱹wCjJPjfyyb{D"_6 Y!\7@@y.d oAUU4MT*35u! ! N6ЮT*γH addqx>J6òl I"d}6qU6lp42n , 56Ѩsyy5"J庯 ؟Ѩ-pEgd9v9}K.n1XeWX_/RVXYY!022KfuW`ќ\$lVcۢ b(i$c &w_7qU \< 0|l]X}Bk9UY|VqJrs .bUk",DZX<k~dX]St:M2$ZX,SV)+J%\!NqYD"D"LӤhh4jZP51<\ ϡiM#Vs _:on2Yl-zh Fi&V7fff>4M0 ?]zڈeY})Ӎ>AwCn(+嗯i[Qйe?@pv aݤrzA^T*S*qGQfgomg=z_~>~ɓw& ^|$˸a ]m-u=@sH&Z-_RCUd{Cկ3nc8M" 0 zA[ϭ0yDYo4%H$Hz۶iMq :r*oNv:A,a"Sv$/$I"SUyZTH9Z?o~ڧe)DqR \fI٤haϰ-tR  b8dܟwp]v w}e|x8ڽFzŅ&&'I$,..RVuQי2M籭N 8ש=v Y 3[LLNh4hD",--o$aH⎮?-pM8s7pTOСLLLpU@mJegϞn311N*bj<{f߾}J%{ :hQ"##Ü;w@d /_Y4gz"zd2ɡC|2J/6>/;qL˗D"Lڅ,,..Z}C7a1^ 4Mt$M<O䆔AU. l0j]N5guwwNGfP$UU|ߺ/XExM*|og4TmȲB.P(`Y/-ނs2ѨJl9C!:Dd;>.N{a\?OPHg2T*8mG$ӟ v0j~(O|^}=ّGVLLNS<|W._\e(JwW*/~I,~^x! vb1A8u} 9rE\7~@Qd 'ƿ7/~G+|+_ƶmN|7x_տoNĉ9r_ç??gf/>q{Rc_N?0/"+<n ?2s W._ӟ ַ,3>1O=<@Z%Ƌ?ρ9|)Q2 FE 9y O}ӧNq8|G//6{ ɟFKiĐaEx;#*  ]3$]ތCvtW,QvDbe38{Zi_;C,gنJJ"w5Ntĩk+Z7n&lI8R ZuAЩ6*r߯}*.\ qȑ8=? ?wsSԪU~]XyJ$<u5e6'~O^1Ƀ>~̭Fi*"b^3::moo[TPIr`MT*MCCP 8A$I4Fn*o}0Pk ڝzmJDZ4M?4?}' MBQ$MzUAg"IV)^} [ { I~5lfuu l븮K\luBF:tZ/wmpg2r-χL6K*~ÿ 2<;_?"'N<ʊY1<e(Οл/5/--.X͑#q<{'?y\.Ǯ]!,Ag'?yd2C8{,'N pEx~ p+ !Hl6SX]]\.,,,NAJ'Z\,&(i@ I2x IDATedY l|y k~z4X[΄vD#z$n4ض(ۖHeLӤnN*Doªe}b|po`#AZw7B_lrC'5pF?{=>|(955űcn7sss|',qѝb}|+_h~m/裏 XW?7_گ}LM&X^^^|>β2;;ǯߥZe}Qu_#˱+z׸J%FFFx믿/{w# qGŲ,b?|U*gϜ$G'HYX__ӟ4- HPVz~K2Rp~)&w≯~FA:S>^'8Ym,KM;KDQt]0j5,"n} "i"I2꘦5,n[a6?$kv,,>#~GG **ZD"m`1,bQ@ht%207AWՙΞL_@VCe^}%ºα曾Z}l.8b[W^e}}qxY_[CQ}* {i'&(Jx3qxgrrh,իX[mbbb-aqaz~m`zz'<WWWm?F4ej췿wIfgxɟD6~_syڜg)GR̙ضoTWٟ^Ķmտ}?FgvvK.emmgwa0e^y垽;sthZzINo<|:> @zb_?}4  /]Bx ?j8s>$dzUfV!x?C$u{9N:amu{+vtbdz8%fG`9u} ` OH&h;ӢJ#,vyu!//a}nA+q4Fhj5&*n[[:xcN&frYdYٟE *7JgҥK\rػw/䳟>̖> }ndD"I֠6p!mX#%W}m\T*"Y8Npln7 a[]0M}tN TEauuv:x -Cc`oeٴ&Tf3C,LduuBAHmy`Mp+WRP\.C.%HlK]w3:63gxҢ:v8&e!۶C4 LVa2ȲeeEA#:uV d2~W=igoq[< $Ji4ZF qXZZw(崁\P8|L&,t~}~H BVeP>9LJ}p3 c8wvO8s4 fx\u-&ĖrE!јҒ߭344D&Au\멌}rqs7y!D4j 4pp\{vfxOӴPt:eXm4-)LLL,[>UUA%׋\pH$y,Ѩ㸘f>:w~` 8}mC`> ͫ`OLD'~ vÏ~#_P~$ ׶ 4-&Td2% # V<:B h42|bL ͯoq&w"ˡ*#hiٻ+.]Ǿ]s׮]q5mOi۬-L&(nGcvvΗs˥) 8ONا>?$yc\t+3``b]| .kk~+~'IZD"px1 :8t ͦu5H( W\A$t!HL lٶSehx'9IeNr{-ܹs,s1>$Ijq>m'tdǡ^o<ǡT*am4!E"mE,FTr ||HXVoZ}~ cP`><[UaV{]&-NhIh*~uuǥT*Q.%e]*YXXlDf3~eiU`YNtooH50u.KK޳Cq1FFFKEQH&^Gzk3R$$Y$|@m4-mbqYQUX,E\h4B.att|>O&^o8N ;1cUo7 19u1 IJep <*jC111Nպ큰zuVWW$@PȓϋDV@5 k\x,}}e4yIE薶tUA*p\ @S ȲGI*uk EEQ\0 Ξ=KZd2zq]CUU4 0ҥ˘,+dJCnd\+\x/.{tDۃ ^,W)V7,IQ4MZ6'Nd``ݻwY+P%IRJu]n/a&fbǷRl6XT6GYX\ ~[O`و OKG#rdp{iZΎF:Sc&m355,epp42~(+Ic3;;Wt:A@*"[~%i8Rί`#IrFZ bm)qBJj  Z1<<ݻH3ضd{3]։poU~ׅ> @۽~e~k9i;s)KatH54-E'&Q!2i ض;f i6uΰ(JT˨ir,t|>rsE<1P(P,6iۘm;:Ij!a IPׁEmJij UIz\.ZrȲL8흧;$ISa@ یY~ȍkK(vGcJB?Bu1 NGlL$BlF$0-IX۳ @ ]vStTӻh /BHUF_༜ X|M`YR}v%ڝlmۺ.uFG8[ /-36+gQ٧@lޮC do{a(%Qfvѳ9H+mvys ,{p_|R\Xo2?;8FҬ[^,0u#iQސYta".B8[ 2f2k~C 4P5ݯ@l^K1G☖ޥ56eσw}ہ;4mUNk1̕K=Z.@oo_|8b4xс]Tlni򤌥m{;It=rOX/Ya+")J,*]߮j]۷ӳ,nzvKX^1t.b=rOX/,`E|+XVۡFv]m  ۫Q& %lˈ1L$ E3Y$YY>O`m`7?H} iTrܱۨm, +<,/s 2>(A@ԩSZ? 2?_wu˿6.y_OOO?4NRSO=[o' CL… ̬Yn<>7{~AWl֋m.\$I<#+zWg߾=㓓9LL[ u9s4 dY&ϓRU*@Siw" {7 4ڵkk6[:u0I=UN>7>\xYn|p6.]²,%AVҥKj5 ~ 8u d_aMm.^sv{k{q<fF΁ FU)N)AhFP>h[ O@0dYVkŝ C._<>44HOO~Ν<~8AR{RT}4WtJbhh ϒ+ BrիWWt_PU#G244yt:Ɗw56q~:0j?<0 y=>εI|#y=z{{ٱs'z}EjVśҹ$q?~K`#))< LL̙3+v;y; yӳ?iEi&JHT1O?]F`5Ym}v:j:t?`j\vmEii) |򸪪۷v6q7 ط?r0 IDAT8qQΝ=KٻqRͲ,9‰O>m^6@r:L 䥳$I"avkݿnd~0kЧf%_(0008b>'077eJ$%?oлQV裏Vmz>:A''"+ɹs5bYos">[gϞnsDgϞ]^I>dk`?yܲl~WWepNn LL,{6:Ƞ^($AUU0|$aDV OO<>~A(s'a||G~k w ojR.QUN&sLLg||V[88ϵgfٽ{7;w|P6O>Ȳ}oI/V/]sy4juUApI$@7iit]CUv:yR 'O5t f_rb l>ׇgOOB!  r/f. KvTUX,P-!)f˗.g^4M}Ƙ⅕mxyi.h]W$,t ˊ:m:۶q 0EQUTJCur,\z{{p4Fh[6}.GSs9p W\II>|W^x}Fu~Ooߏ./}kXE;~7~F=>HqNؾXkkϷ޺B?OVeFc9+WpʕUVDsmY}-__j ?0}~~yΞ=*]M 8xZm+ݿw+q]F7n`aaaN'4mZQOSΒN<{[bv#KfdtSS7h6[t:Q~IDj)tLYPU\.KXT*Q.ۋiZ7 @II6} } }n>`SVŹg=|oėoɁ y-\c[q*CS ? 9}_zvpYv䷿~ciۼ}no}s˥<ʻÐ??$]cEX,`YW\e~JVöxLTLP &u 0 Z6jzN@bvvr7)/Kq1C À뿉S0%]ZD!\>'Al6uV==B ĶUܰm֕^}zvCsYv֋-p{ L؍"HW|!)M#J֞-73,zVQض;l6Cd4Merri*R*pQen߽ _x O=N'NR7v0 Lbdd$>s_nO*xpGanHiLdZ 9sdX<m'Wք>WDžd2<<Ȳy0:._>w/K_˿ }~߬il(nޜfǎa;__˲M^|zEÐ:oy{jzvCsYv֋s_y~^j)Vl.{|*V8Cx营şo,u_# t+Vc;hfWtK0{9GmAUe]΍h4 Ð#G3::Df~O$JFFv2:1 0 }ȬHFI7͢ORom:y~.}o.%+I2M )>8Vj|JI4JBibp'^6g4J184,LOM8R:?OTs/t+yނЧЧ`r?={m|㛯cMW [$lJ_}e^\86&j +D+g54r$׮]jJx晧д4V{E>VA5F꘦,߹U]]F颣8munӥ iu5u0\԰lOsni3??t]w]׽myLOM%/Y ?B|q5kFɘj TJg ϋj0Hx@נ^}فiέ i&] B$$@O+0DrY&''hZs=XbbY6_wߧlqMEa׮1\׍R\ΓUPUYPE-GΥȱ.$].CWNN{mSS &e;/ػa/s~o*"I!RʲȲm[Ȳ(t Q5vݸK҈2qH~ڪDTlfEfgh*=,O8Xu >$ _[4 52,jB0_޳NaR9ipNkqWSdJe\7( ( i1 n+iF}e~_ʹ }B@ mXS.I\IuYE,,;nzc F*R()4M#QڦyZ'>^l-/skjz&4 fffVAG|;v).]v)}Ix/2?_%͒gfq%HgOMAUt= DSFMu|ǶdiV ѧ(h躞l#ONęwޮ}n~][خv=oL6K_k(OӪ*Nc8NNzXmx8a$hD4-E:&JNkd9t=28HT7vgnf&6K14WL2;;>|tZDzl&''9v,sNvI̙3\reŎ}Y >1EQ5֔iZl@t v^\E$|OfJR&(:tt:ҥGz:3I^R>W'V YQp._"eީ@p\>jinS;}>տɓi,kNkhZ Y<L,6a>m$qwto`Y$F`dlq48M>A׳d<٬N.u],Nң3nz^z+g,ntYU:|]cۑaYSSSܹ3BO>]?< e366ʵk0 *rL&KYѨVnI".I4TJ%Rp,4M&bV<"]dn]ĩniԔF׵P,f#BHcu:BBATf㣪Y׳f+ΠD_ t_l|S垯\y>~ljƵxWe|r[}ZF7L&7mFE@4M:vF9Q#TJMFT UUQӴH5,}rt <ϋ CNid2:==E b)vsض8I]bd\I ᣑqƯ~ kt:Mj#1 u.\Ð{oE.i#GxY3)Iтb -t'jV^nw,VqFLd\ףX,>L ta IҲ 0PVE:nfsJ%,\ t :E' BCJKت j.j6J:O`_}>(8sS^zZW._^iNxضCш c%,+N65PQUJELDUU .gF)VVBqq]7qu]"[B3rY:\QIyWxW''tYM^Ƕ<$evvfIX$ C~sƍU9.)z(I^gxxt:m:Jpw}vMKd}ێ>Zmfe9X7S Lޤ̠ی}4-iZIvDZ1 FAݦhb6aF'rT*2|>mqݵ{Ч窡) U5ADTgt } k˃Aߘ={б-tafU1;=C&5e]4 hT5fffDzL\}a߿L&COOUR˲ CaQ( S.)E,¶m"WfllBH69LlN̞fQU5Iysӓ'>>ѧ$I(iqsQ'a&l&)./_zݖX /aҘ*z,,ԨV0L<Ϗo{)Awv4ҭX4B?zm;HDXrV7jq٦ib6Rh@ 9wl2t#,t:a0<=\S XVB˦vG-694|ss]:i wߏaD+\QaXjd2:ܼ9iI:u|CG$lہVJ"ۅŎJ#M} }*$JiRg@Y67^\m[ˡ,ޅ8Sǧd2ٳgl~D6Mr@Va6B6J)%T6"APL:fo|ߧP!Ezֆb(.t^ogΝ:gEQd2(.=F:Ǐb 8#CBU4-oagBn$>@ l6E!ۿ_WuwާDh4XXh41MIؽ{G05=~p8xv%]T&jjJj M}˒ĢO[.jDs8%mz>l0 IK#KwdNR"LFA^Պ0 O<$bѣG)ci>QIccLLL( ihDsn#}R>g} } @ Ž4OIF,u>%I"Q,럻 vHRJ=;v'OR*JooR}JBB@ 60:?cI4Ofu<#ϱw "u]E'| c37W%NJi2N')IOq|vu>c#fo$G`-Yω<ȕeL&Jퟛ2?_c~z{ۋalnyr2S]3Pv~ի@ ܍Mwg|vFb|)L:j5i4M,B$* JA4zX|ǶCObn.;??O&AӤii-_o[+0eUUPEf#GCSx$JQdd9c6A`nR}vkEŲ:&Bx\XYVPIUt:}ǚ\Mj 0,1qu=֧3 CyxBЧ@ cSwY.2cYZVٹs'Ml] 4M@ףaWi4@1;^DJꁷ.]%JEQ$ 0Wmà(*a I2)g0,=eY&y.jZF}2ǎ%J}a}a9}rrzϧ$)ԻiCqIjzI"FШFqWO@ [MQc5ܤA4shhv6q\ ٳXE^'͒d(lV:}>͆$Z&CCl ȴZec6R"ؘ.Ne Iա6Hw²̸q;w]IRd2 b﫫Xmz3gTBQTkwl@4pwP}5hZ 0h4j XVd䎌ׇim0LvVrhf3r9TUgt` ;h|>ejuÇSI:zJ"R1]ەCd9jL86FFe@HG҇aX+olj;3 FIh4rYrwCcp.PЧ` 4, $IȲ&v`"q ւMHbTRR)&,0dpprTg^a211 $ѦF$FV2d@PiZzO IDAT\.Du&֋`0MN4 |ߧR022iZvĔag83VCբY`o'e*xַP@\ۋVls%TUŲ,ڝF+iFcp<ru eY躞O7¶YZy\|>$ILMM=P j*SSHGq Ka;Q]O8.ff3r%I/ʼ9<|ۋG4M4,]ǪoE>HizƦAK6k{q V QzJ=ERIt)PӴ6 QfK@܀':/eH"ry$zJ=<Ӕ˽v+eٔJ% "p$t]cr*P,).K.[5{(8eYض˽ iju7>=ϣP(0< hژyHK@ >ϭsl {9r!XM.t^|EJ%Z&|헴[ؔ! - 4gdd'lӴ0RHe9ml.|aH:Mz% uΝ9Gy#^6:b6|RYZ u2̲Z( ulRD:Y;a K\hkn7$9nwh[XVQ*sZ>&B4MX,PUO@ ÆM!FH:J)qNk74׮]jdT({0 c[4&abf2n,(`AR(4e o u1eQfJ4OƧٻo\n#ͮa68} cccR_[.388xǿf=-C$ ";w/'6$AY#dtm#1r2<<,.}z=*TU,²L,Nvm2֑aeEV۶8} JUU,RUdߪbpp[j%]d9jeYma6aP(gq9>|EQ}/?n28XX6v;3w!)ȲS<Þ={p(#CQ>#Ξ9އw_t0'B9`xi0>>NOmW^xɕo7901g}b"5YYV IR ;w$JQ.QL&lŋhD7 4E! h6~<4r(ңVQtTapN;^|yU( *@e*>, 8%tUaSЭQe9iՍjFoo ܳ  |4!$ף$m] } 6*fݼ\~Y9|ǙQS*۵ vFl6q:LMMQVٷ?,seڭJ|m i|21:6E>=ul.8r (ڽ&^E$8p:?fDYk{E4nLGPVq.?dsssꗿX, /7j3:6׮]cٳ=iCt:dYN"{Zrܹޱ۶r2e[.W..Jٙnܸ/0$< pҥe ?_2M>h /5"#;8yzz<8AГ}DNr#4J-6q\xR~|a=kۚv0 $Vi8zwy{Ib"MJ{KOO!i4\9<(aƑQq<0 Htt]g+tqW :eqa{]nk_:F(0 vO~c?v 7DM1>̉O>Sxq0dzjwW庽#:{=2;3K/LVCP!vo>fCsIN8Ǐ#INX!EÜ?wGLO9t0* O|KAC\vm=ߪ MP`YrBr={$?ƆƆw#%j@(*_QëT*,+LOϠZ."at6*86鴆n䬻K*&=}>QMt:###xG4M|myit= 9o6P(att'\{u? ҈ꉺss}ߋ Hӛ:$E]'A}9s[VOFCe2 8rz'xI^/?})^ze |5"oS.^ګngIDV_?Ð}cQ'J1;3ÇVA'Pח,=i?}&SN| O fIsD:FQn?N'}/oq՟801OͩSh6|x901D /=(~ϝCeg#ǎQ3~Xɷ(#G24<8\x}S~8s42 oJÏ4c8ru\G%>$Se)mS,Z*cc Q],EҗtWr;ȊѣGy/\H2u_^u{5yo7f7[ڒ(.JLFgvv3g'ah6H)JhZz<ѥ)9 FMϢ#J%<7bjjB!dã( B)|-\ץTall]Fq/NC ]N=r~)awN7PҠnn:")(t^>nE$c333:6eъnm*4 p'щkvrVI/0Cd2LLtT*+R)Ej*E.gdtJ۲x@Hi_y;iβknvSq ]smo\wb0`8 ݑdj#i8u{?9 W[OFڽǎi\g}?wԪUK*B4\K킻ER;v`||MӨU^SjO_yxw) ګR92LOMoK__߲!#I'&-oT;Fv(BXddt/?N%2`;l.ss S($ Ij?V^mY'H aIV$%򆪒-jzg'bn܇y؇ݘ3&Fއ~舽;w\w˫J U2"E@6@fC RJ@> D:I=]eP(DSSSɦߟ^}e-˗v*w wzގi<1߻X333\}&| <~LKk+$s5>nݼ,˜:sIӰituv222~ٻoNut#Ǐ Br2IOw'hIӜnrMpŒmdT*E]]Յ1>q%R[ay0,K&ܺr I=LONg0M|>ߞO&\tH$\.Ow\\2`Ytt rR__G]m=]頻>j_Z嫳} >^/o&x>\vd"kx)ya&?:}= hw9~I\xT*Ŗnۆ,蠺'Ob3 w}555|_:m^r!/ @4+N4nk[m["2OzKܿc{K^JP-)( <yԂף86Bcߣ##vZsK uu L& ~y NFK&4Mk>̻_}ŢkCC9z[ _斖E[4fȈ ֝,ˬp5?4].lLn!$,lQ쪫t5QIӎckvd땝sVϚ>??߀n娮!  av6J<$IPL*A2$33%av2SUZT\< 4?_zX+ ̳OINiH}}_@Az}Z[⳨hv4|=^aȣyOUU$155 b1t5XTA='{|$ *++4O>Zҩ{Mi)jEafzڙx?G#3:2設;6eY|$I555x<ffffzORɉ >cy`q.aZ&x.D">,d/e_KЂO?ulb||~aIGDu,bpppЍE}+H$0M[8M7;I_o/v;Z~>_fAy55\t͛9p ~yG48r(\:-ljldjrzY޹CoO$Ԅknvڵ[JY2S͂#lgps @rZݻwQUZXz+Nsroids_ i$`&>TWG 98dxy)GX5Msk<-bhM]ןyS3Oo1a{U?}.v=i$M]^UiIp-k%W_CS__9/Zbq. g?#DH&  ,xo>c_{;v2MNf|>=W_|!O>7`xUϥa9YmpK._4>{S,6صzxesQř?` t>Z[[fb1R4t Γ++2nEQPU/~@ @ee%^l6G4w]r_dom\v;,$nt=W_d"tber"Sf> @X җJ&effMruVGpYz_*şϝcaFGF"33=, ]Oؾci8 i%-9E^n7\n<)ɣiRnY&Vs +wOq偀4`xxĖz8~ zWeKMM&uX1a>?D‡>Í}}_)׋$IBlҖt\.$-<. :'a>˃"x) ˉeBz"c&7 WWg;Eab\.zzx8kYi֎(vtSɘ=X녔RM-~)\kWZG"櫱v&RK-lgQg^'JҼמe> @.PHs\v~2,kt#㜋k;obY} Iv_@Pf{[ X9|WI|(1vP7n$ɤR)&''4.7nthtjk'˲ [ Xm~^{@~ȲD4%Lk,199E<@lvNj*Ԭ d|>p@  k2,Ivl}/ b1xfsvk G*0 bei$oj*"r tV>+$bq=꤯v'/:# *e+OOO>}>Oд,ǃn2R5ţ>*RE|vNا@ bM:auXqlA@[ IDAT,n4  PxNE~~yΞ=K}C<,ZZZغmgc> oo))TNI54Mc|| ּ}ʲL4ezzPRQC:)'ږvJ"S +O,W<Ѭ.[VtVtkߗ.]Z Łe) Q&N13Euz{DQrsPsss(B ' sNZ.37oGrmgg^6o“U<bF|MEEtL&M>#N300H8Qi^Eq ssq myӴ0 sp~_aYRe(N8xё&''HRܻ{w PIj ve?nEE%~`+*wvv}H ]]݌vBTWGDUU4-aصxvdMWg^+.khhXcZHT55 \.iTVVR]]]pM8}}>dy=M.swDq\A"*"ndgm")뵂|۷o_򣩭?)ʬ5`(o,Ӽ̩c<_@fhw8&I  rUdYdY:;|TVVtKY[DQ4EqSYYYriie7y9Y? ԗ`vn=kn8;vtwuFV gԶ<ԙ!NӃz²XrYUFGx!esj,"*)?[\rg9#%8p=^{Z% {03MӸz:C,6ݻȲ/!oC;;U>;`S͜}m>_.,)d2<H5٬];4 nmmiUkEWyxG:o$RE]]-.Rtj =/ 55d2ڪG080@{{;Cgg'##޳oQh=ǫ}eid L$IzDj<|99w;| 0^lV_x~ I2>G""*$ 4t|P"3}">p:+\:w Je`0H*˼F;ou?>ʦM|}y睷sqԒxd0Vf]FY8CC g'C](jG<ʲ+OH$4)ˍ0MӶL&KWW7uu޽ Ӵ[ == >t=͛)(>iϕ}LFsH |y}.eTĢBz:U?[t=~~pԩU>a%f_Ç% ra744LG=~ jkk1vdZa 81 ) yHeX]ב$T#I.,XE}}= әy ˃Wb` x] 5a#TWWSUUI2Bӊ}/3BY0?6jjkdh^*hV fu$IFUUjk . NAӲRQ&vM0 KEE55WJ>>%UUßs}gϞAu\ʡC_B!~pL9/F;3gNPii8q7\z+WFV\._03qJ89( [6:۶ogܺqL@$eCqmԡmUhӴl1IBQ$IbvvM%ɤD"B!4-,Hprd{"LbYH$R\WI$R^*<,@>/aLjУW6%'*bfQ ̌=I522iظ $).G ⡮jjjzv 1%Tٗa϶m[QU={v-pD].3eEQVOr$iA >SP:,n M맾'3;sjE0 v۷53(t]ueJSS#oh}Jan.Nss77(Q}3˞::ʕ+쳮>xk׮#j d2ŃL~ ܼyvȑCTTT:cc44s1 \\՘`uH&SNwd2iU\d2777Gc&,.\ q=0Ob||X,V' [D{Ue]8$چ/࣫5({Z^~EqJeд xᄏO(`0H0t@^@1MY\.nz=LMMJ299 ^/a$ "I2\މڳM2п_֭m>G0MR7uuu\r>R$8SLLLTQUUEMM5s[Zaɶki{.iZ(ۙ㌏ND].^ S%r<٬LNܽLZ}|Tݻcddht+\۷a#…hѣG;HGpav[G|"eظ,"q[!kǏ`||)n==z˿Ξ=/^#GJ%Xq.\xF9J駟-Ϻp#iQg:\@OeeH$ ),d.n#x x<?R(#x#3==M:&"JnTU #{M&y-S:ᗿ5_%[ *,!EiNDXl;QU/5E! 077G&&ՙbzzUU ÄBA*++ \-z˅A6evV#%+0PUU bW}VUUJg1d >v4Mv؁,K466 I$躎e!LblHWW ݺuIhjjΝG`:p$Iڵs6Lo03eӦn߾|&>} M /R˵M\.\a̖-[3D"w>Z/׉k L^B`0XO37/8yzzi,r`Iz=r9TU%70 \.Wpv ~P%dY&|"*`9/UTgAo)xlVCt,xPӴl C[vpmGIÖl؃J0 [ڴH$XNn$AaU'@2:dBܹ[n?}t$cY&i󚪪%d2ISS#HpVsMe;YaqyQ>o8 Tک^IECRse#_Ƣ--{|g<˳KMDӲNr$Rd2tt:M:vz-,=JamwQz(ϧ:kOEE.**JY|G#o>x'{!e`1i^IAɮi I|TVVdv+[!]G׳dY' 33ڼDVl0e cgdx<>G8BQ&|I.  6;w4M>3cb۶Nb gn]P(ᘦCrM?~ >!NB /xuysBMHY;S|mb?> I|CQ#N6%Nf ?9<0,t=4-\.QU7nAUUM (n~}{{ea"94'N159ܗQ XgSɴSzdYR4%mIsr:A>oP2pl;x܅{; iv6QU?lLF/z1 H`Y] ,SUUÇuN4@:vd;:G:ٺ 0lV[{J4%qΞ=rgHؓ ssqn߾ۈD"\~lVgVyuk[A3Cx<^-g_pC*/tԤKM;shn޲B!,/p>2+&1* 5CҶZ'x8֎ px< 7G6 fA4mx$<ۅAUmE[WuZHd2BZeIvR+\7./co ?woە?eҥK47of}~C:ujdY?3 ^eL"OE]Jˎ?/L{)Q2ZU")vD7`˲0 [0iv,x0M[,dVgfNbaXH*mXwk_rB-K"K*ύZðkMö;$Qi/x^Ye-̷S] f Wgh}SҥgՈ7:egdj~v&W]t\:<םٳw/MM|pYT!.]h+Ypimo;%, 뢊jϲ,r$(NthfH(ّbh/=#fFfצ-Ees9;[KYv~K a]܆0MquJσ>!SZm[^M`Y&F6E)''d,<~ӟmϺwPEUlLr)2=8QŁ gDU^T-u I2{u{rlR9B6 Vrw`}}=BFUUs`A2 ҖS0p#@a@]{:;?jl0r>6lԀ]mg:cd3k8{ /{ufgc|',_(whmWۻ^A. [Tۓhe0gY>ege_ȼئm»D<O~! vl㴶cWDJ>RwEY[0j}}~Ã}(r|[oq-ZC4~LOMq.{ܼqy_]}=}4銊 \.Z&CKk+W\SOo;Ml۾w*?~|drYsed^s,CudY,`4jgژW$I%˨>?,H).V!P/{>Or7IqYywq]|s},}|{Yr)._orm>__`rrrIҲc?Q77sq~rn?l=9/Dp\rUxat(RK>4M8p 'OȲL(bnnP(aٓa 'imSIDAT"0W_uKAe(htNXJkt-8k׋*_K2ddd&۹rʒn?cbb۷}V=`ppd2Ů]ٵk;t`K?\.G?555߿t>ssqں#GtDil͛y1`ϦiZ[[immuRdeC6oL0ѣN4Yzrg8,DyGA I[lgͮ$얁 H$8-$IB![Ie<֭mHDOO/--[p$ӧO(nΟP(ѣG!NqAnmc߾;g9zs.##lAeuʪo0{{ٷo]]]\,FOw7>hzܾGGF8p mm280R{;vD,/ә ~[W64kyc#U.eY/u}ʉL& agϞ%ވܿM(JvHrŁ Xcǎˮ.GYJ%sЛKd2I__uuu%F V&4M+ W $I&gsZ$I tcbǎ_A($JqbsTVVr "* àw;g 3D"UX ^bhhݻwTH$innѣNo`߾ڵw;H$;w'|wvZE"NgtdD( `cH$K<577܈eY8ꩨSWWKW8p0Iks\l۶ ,~& ^/hhh4MFFF555B!"/xY>cY.^đ#9rCCC}W^7ظ8&'Nҥر;wv BNi``QΞ=SĹʏ|T*'|F8chVP7|)aXkRzQڈFgd4fff׷lBc>|@&ӟ~ hnn;DQLOϰy>tZ[[ǟՅ߫ׯdttpخk J-rOkȟ^w3N/gylIkf |>r9=zO&S9|͛{i+3 Lla(lٲy Rk`0֭έ[K/J"ϳggyMM D.~I7frr-Ş=y7zldO2LMM޾xi4#Anܸ4d˖{dE}}="IcϿף(SSӜ ⨪d2iܻ.!I=x'i{Lmm-{ajj/Gr) <`jjIb1Y=,<~{xu[8rIV ޽vqU?vkogzjʪ*R3x<01>Na&l޲ !HPWWG}}=J޽ ?h.֖H >W[[_UTT `!T N_5 h4J.gߧ٬N4Ų,b9f|>xFGGs0{m۶NgqÎ@Qܟ²,Οd׮d:.]ɓ!6mD44-nݺM.]q[Vba?;߼y{bb?n֭Eѱo-uGvmWϲ,n޼L]o Io:"'ë}kVްݻx؉&7}&Μatdi[ǎq%jkkql޼\.;ヒilbϘ*$w6f&}ي[*[-8X,cŋ\YL+>|˲OWԦi>a Kr,ˎ>)q iyOu_6s{c:tڅYkI#22eYY\N/IPh`Xn{444p,|>s ;wx~=uJoHXe (H$줭ׯO~Ii=ʥ عk/^$ϳ}.] SqY_NdzK.-zam~Lf$ʋ??Q,LʑbD6)݋Noy6)_C'67G?z,X=6l&x]XvsfFi>~eYzyf\<[)r'\^|N'~%OcM1::Nc&ޥoiYƟY8{ UUK?˲q³q aKFa`-SQQ" ry&''D%J)x}/~A}CWF\RrlXز,:ޥ$Ibb|Q'`||p8L"q6xA$>7ǟϝYCun7_}%x˲/I$Vjͤx׻?_Wtq aKFa`c&)$qI:~?;3g#(WUyws(n>xPW_O2Ν;$> ?`}iClnQвg` ^ I&$E_fzztom.[~.c>| /O@ W, 7403=4'NÇl۶۱;qǽx(tD>eY s عs'H~H$L`ex28ȓDwp@ b1Bi ѣ:|>31<4[Q<7[Jv-Vt7jzނbާ@jH_ߠ| 4I_o/ nߺűzTTT09\:cxĒ;$-{IBJǯG9N9a˃O6'ʇ /^Ĵ,̌F\tOdzzxhZ24s2A,۽|eUͤв S$dم' 0 d}.=>/eS dxɢmN6etddsssK.YQu2AP VO@P(/MCUU20- MP%28nB:u-lP8P`,_ۍ IvP<\,D@%, Du r|}O`)`yYrshie&"KǃW*S X>@ ,˚(IDا@ `!W @ `C `@ @ l,@  p@ @!@ @ 6@ @8@ @ X @  @ `C z'(@ @ a| @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ 5_iIENDB`geonkick-2.10.2/data/screenshot_kit.png000066400000000000000000002750001451017372400200410ustar00rootroot00000000000000PNG  IHDRsBIT|dtEXtSoftwaregnome-screenshot> IDATxwxT}4꽀BB ,``6{NĉMrN8\7]VvYЋ`BPm4}i <<̜9s9o|A!B!B!B!B!B!B!B!B!B!B!B!B!(7>3$1vJ$B!B\.x;nt7+JKJ.ltE8Z!B!>UU aٿq:f@6n$rUҒdes{|> : !B!7LQZ-HGgug1P\PZRuvO( !B!7* x<$''LYDޯ~s8+B!bʨ #-T]D@,^p !B!(rr-4F)B!Bۅ, q"O4 %B!BL@0'<=uM4 \GB!B!n+*L06zB!B!4 d$1`@ۯdXuB!Bqt!L*>@v lGB!B!jk/x0(WnMхx,˃0mBCzoB!B!inINh]T7?3MzY$B!ntl.#>l ]=b[B!Mz<;wt5=--Bh׿OrKt÷f\zB!,Ud5Iiɭ`!4%n&?<[.(' :@{UgMZK.`im71S!ĽPE5_oŊIӉc*xnǽ75Ub5(*O$y'+B!Y \7i!}BǷwNa.jynywB!nՂ7CXjw|AK?Mg ~GBiҮy !7)|O?6Qgx߿ǶǐZ0)mTҦ-Qe _!/8kIa3_KfvWW5{/US3Wģy'%gTa+? -ɿ("yAT%}L/a7PUijű*!w,IyϿE9KQ%|}`{_7u.9 = %ud:c$BEɺzvB 󅿼KX) %+B{6<V*OV-ǼeLKРIl-@'_SOn淾}a%U[֓Mg>'ypC|GpfJ>yVƍٱqIc՚RJgoO<;]/=SIrߞ>ăLp[B!Ľ@ZBܛ߱Ժ9w{zKq/cV ef۬9B#кN2V3g/^BҤt}oV+lBv77V0ApcB!&`@kcv;&RkX!=)Fg@%C…iE Z@gh^|I'7#E1/HjF }.^~Hwj><6ʏdvu P=u-l?+[GB%kĕ<@8-þlĜB!v pjTఞ*mlUW2AO !7zy37_Y5*(p}^$|$5߼U;!8/;l3\#!/4E^$O:\?d~g?/jɯv=?n`_?a<-z !Ľc*׭1]GI,B!"޲ppHϓI^zIvCCz^00 B!BD]@OLg"CL402df{ B!B2*=NB!BqOX!B!=A`!B! B!B$B!BqOX!B!=A`!B! ~? 9(BL :, hک.UAvA_tn(S|7rr9f`4V7?@[{K3 F ĭeZ 67Lwc&@דM}.B@ q݃_N%11j/-l6Q:gVyBZVgMUHHNcV/wS;n<&qXX4hEXcm6GG3ҍUmOgQM! ]kǯPnLQT0CUotM7%zb4DQV+6Sm/==ǃ{, 111}}j3f hjjFQ)KAtΞ=GrrIIMuq'l6@ `pp0c1MFme0M(>0 wMz͜9˗@'+Oqԩ[ᩧh0K/_Zjj*[nL5G֬^Ŭ^{5vה(50L< cMޘhkg!''ZzIG̿sdG$''/|mxG&ES{"σ{o>{+ tKvɦQV^# F}?7Bӡ!Uk(Zmfh4Q}BiWVv̰NNONc͚UL!Z=;v<#99}$t֭c͚UC*=^23y䑇;wͫQ`_*=5ir@ZB jZ~K_"66c_eKJ֭]Kjj*fڵ456RyT, .Ʀ& 0Z-nZ%;vLulT)))ɼe< p]>MRrB"KСH`6s4 T;GgOϧOb"-Z#<6rBvAI,_PλwOcfZ΍<3SV!RbBcZEiiCC >łb4h4bb"~Fceh4b>Ki4RRRp ݺ)$$$8~UUg祗~'>>F!--k᫨8Cy| ܌VErt:z=8^ύn8dfjĄ?7qqq kl638xkgdd`28z))Ld( VqA||n̛WF\l}T"0r9c ===ΞM yz{/W^Eee%cAL&#ba``ښګ HLJށOuޮf$%%>Vښ0{l&bbͥm۶p8d-dff믿nɟMqq1}}}QkLLJb`pݻws…ȗkլ^EQ8QYI|lڸ~Sؼy3fm۶C1|^/{ѣQ)都&> ܢ/|ΎvSUUŠիx,y7d4cQRRdPSSg?Y1s=~D_3/{P؉<>M@[:TEŽg!gd 2Hwە(he%7'E. *̱̘ uzϞM@wm(Ba^HMJd<*kj'uFw=}1ov ?V)Ȑ5n83C07ῥNZD 8sEee4ۋNE%\h: F7Mq`6.;;8=| iY:pe-feR#/K]Κ;5pJJ\֬/DRZUEcJŤk| 1G_)?;=u"f̘Qp9.46Ygi̝;F444Pw,yy‰ +*l/VӦٳY5k*\hlOQ/_NjJ r1]m.wy ˯t|r+?O7p E׳g>XhA$Wgg'a͚U8pn 擓9q@ H^^.%h4lv:|gcb ={@fٲX, RSS8|BAQz{0cX,RUuY$$J}yϏwbb"K,h 7q 4 k֬l69|ⲞwI/_o ~n7q*'|%ek֬fٳǏo:wWZYby/?<--ݷeK3OJRR.׶mdռԩS<ɟ vٳgqFOoO6n$)1&bcc)--裏x5kof3?\hh~n̢"Ñ {9F#?q%%LϧW_{+V߾]lBvv6o&9|g~DL~^۷oG3k,N:Q.6Վ6Jk6ӺJ Z2ї1$a\xϑRƠ݂5~s򻽏5;on3c4fl70m"TUCőB6 ]vv*Jׁlfhv9v4C.K'=5Rs PEeHM gnnoɊ "Txcc B:C@?I,,+#Fo PYSCOdЦaa  E,Xcîe3ӎ1I>A% 9w?ȴa'S%&% ~G60X)++ .MI,G}z=5u|2 9{,l6:;;>mriRS)..wtP0}:L&RSRAa0ݎ 0 'uYLoo/'Ox۷޼"L&3& ͆l`0PUu@ …s\=fEDcbb8|YLF233JN3gΤ|>4jjj),,vSSSKFFᖵvvwwp8(/Oaa55u3{v &O>9lθz9n+d߾ ⨭3f0kV=czi4-Z9ѣu,\3 f>8iiƻ.,]V\xZ+VP:#G~?:SͪU%66[ xŗַÎv-["t|rǻj,Z`\شi#A+wN[0 o}7| O>>.KF-(oLXK +F|Ho6رB HJLr ??.ǏsΑ駞bѢE̞=>#NWUsN \CW]]Mii)3gDqya$bnS6ka4őCVv6gϝe˖GRy^~԰m6>}:s;8]UŎ;f޼Ʀ&NTV8R][{șVU)uǽf,m&03>sq\hײ{9z9pYB!\W4 HXp8oϣF#egcw:]Qfg~i))g`H: ' 3-pee)>{VӧjX0^vWTLyiiT3GƜ=MeS)(xcYn1qcoOt$eei2ך?@ S.cE,^9+Y;/9sgy$)YgVgΒex`sf̠t2?90XYWϛWhcN.o#Xn]~[&J \cOZz`ӧp:ןt~x &an;288HfVÜ=ΙjY}z<^<3gE^O@ (<Hq,>}:)ttt.륥ł`j*, 33GFFNUU)+fcpJzz:~T h4FL&nP(np`6)(NGG- 9x0uug1,]HZZ*mp8lhh;Btuup8DQHkf…F!ΜFUURRR"ۉl6 >}999#, sP(Ĝ9 kм[LƟ^Go۶7(//' RCwJ>/BY\zz0Lx'ǎ`za!KHMM:4> IDATl <NbgX$EÖ-l{+~HJNpGnTLcVYCvV;w~pY+dkmkcp7Hwx8}4fi\w9H0>E$)99 Luuo~zyQ/~w(9UUuM;.ɓ|_q9\.:DRRxˎN rIvtpW)CL iIl3zcz]Hv˼@K$Bmh.fT $GZuB,8d&З~Z7!w !@w!.JNLdN,#]x܅F|>gΤ'v7"+= CTT JJR)IIt: T].,fN@0"-9/VZ[}n=8gzN q&3P^Ob|O9wJ(Zemc s( rpW2&5`]H rz*Ajf<>0]]+g9W*d4.QT49sJs} ԤG>ƚĉȿ1]1ch5\[}dѢEM&#ƥxF>1?G۱2u:223xÉ1bSil=&9`Y_aǣs8p!Zmͷ M" dP{^yha 4$|sorp7~FeHII!66%%ɗp'h-E3gR43 8x gܹl޼ӧ b oۆ_s_Ws',(/jߐiION0m\c)ӊjϛބx -rLMF0dfYy9nR;6uO^V@E0j,ȾonFJf̠~ԙEZC0`3gh:{{ގ^ ?RSY)̻C.F^2RIXo_&Οo@NJ%6rty99@#3ͮ֫Ŧ$ P0x݌&ow,)0_*(1:EeHO\e{45Iϛ}+WFwP[wP(HCC%%WuuuΌ7ή.2::XP^N(BSS}}}dtr:3k֠( u%5<,XP~Wf轔̙jϟǴi( (-ͦMpr,NZZWBUUEܹ˲<\.JJ1 of2gJA+gYbn@ŋE~W[[${dh萃 PKccZyٰ~aC&bvúukVa231 }{8s݇~LAAyףi5Ï~c:}W;66wَjΞcYd1m~xs{~ZNn^.I:xxܗfO̳'z] x?vi*s~C, N3 Bc##G/7y~DoNݦ&ϷE||<#R7|;vj#]Gڽ]wGn~⋘FB>2Ggdo^S?!"~hh(9l?&>>YoD__EX&J )>=jp|9udyHט&aCY#]=!y2nUUIJJdu8HRC^aT^Z Vb b <~$%ST'aQYJչI4} P6әS4#NrLpPUS㡣%Z;: Bt:JJPU - 4s"$&c2J '4|B*;]Eyl̛T]?g3ױxCb3S7vfgqz'U<] Ԝ3Q /# ɳ[ QPA4CW61x*hH[ˁn -੿ >G+xS zlv;{ERb"Ρ!PUn]{557GW>\XUU*++imi!bfbikiiϾvssÏ#%Ǐrᥗ~0OWzgˮMҶv tuuxd&==XlNÕ7`ى5}Kgb߾ddFUՑVHOOGUC9ą 㓿nl+GBFMM0Vذf74EwNooK/fz{{yT`Kߏq$G~DbBOGgNs;쬾>f.nEqttv%;+iX{GTs9 c m_4lRWZܷi-=0~Cf/n|y¸b<'ghC]iV4;l]\q7|kaٹo_yHU9<%o2Av_fQS_O`AE#s Sޓ5sdWowز.9rٱ:\yAKg8-kǏ_MYyKտv_{5糖rieyvveW].7cƾ|Z{jef ? wb2_͍KjcyŸ48_i]}s7\G{Țt\ύ]c~Ttv^wٗU~ ?+Һy3'3UL˧ߔpܪL6jc^ĭu:rO|lJLLnMyoVKBBMs4 dggS[9bH,!B!w&.B!B!  !B!'H,B! B!B{¤g>ur7)B!w|uR?ۻ3gt@]v¦Mh4'#4HجB!Bu'ުU&e܈U㗿|P(Ć 0m4VXtĆ oDZ!B!Dh48@(`hhшV`pЊlzY|%Sr)&U2X{cC9 V0׀Ajpr\fpR>BG|\~9y/'3Ncy'hgrLBo(daSg\L//dt-Δ<@!'@$ijH'jP!B$u؅js!B!d~?/eCCCNJJ UNO{'֦Q4+EMn"!=Wp:Moh:ft1 #7CU{h,vXOes`U;ߠJIbރj)\Y iD)I(=IѮVݹX B!έ `+f;ۣ;X4HAΟs OG%,.L@{5\''k $ŸptbŬ&1,7=-v15Y,+Sy+tyИ(@2ZFj24SsH h*VUOEE%vIB h̙dGF+f}B!&6iVWco,ƙj;Wzm7S-8*iCrt61!+3BU'Y5q[BZ yP91Td'+^3Hִ&U5S'n hbk̤dd&653rg7t\8%B 3fd6)[xXcy,֓ @o npuJ0Lv dTI+B!+;]ƹf#s9xMxZ:sb72B!nus0%̳xޑU_M,L!|HHHlO rڊ'#ׅB! -Xu4qp/Tqx]'}ZJQVVN7$''388b!//%KP]]Mmm-Rg !B!DZ+Eb;=OlfݺuEss3===\.x vIFFӧO'33 ݻqS|B!Bp 66Vb;#f3?0& AEE==GĆB!v;vz222Xr%)))<#AB!7$Y wmXn&vqzzzxd2n:Z-(B!"$6I `Ϟ=74xgN'FB!Bh`%$Xx٧ye6mggfٲ,|l0(++2|>***;w.äN+B!$6*f),%%_U~W9z[V{~ p~~>:+vmX,,\tYv-8quwwMff&yyy\pVB!bHl+MP† r8v-BhnnxyHsNG~~>ټ{q777I~~|ȅB!HlܕbaݺTWWo+y(y~k& 'NTbXx晧y饗QU8!!5</F̱cزe [t[ߔ,ɖ_8ņBHB($ڲ[iWk:e3m=zg FWm-%Baq  'q.ݒo%Y4CE%K~1295ز؈]vᣏ>s<ٓuc`0[ݺu{}6|>^{a<6\r'ǜ;Nzr8{,~?\.1<< XncB!\xq{%>_}u?(--Eii &&&011*CII  }vcÆ ؽ{7Μ9)x< u򅅅(//ŋWX%%%za OxjS3Ԍf<5ttڵkGp{|>ގ1޽qC0yroQظq#۷j౱1z ٳgQ[[F466Ξ?ݛvեSQQ 6v}.\}{ߏ0==/zjhw|q֭U߇ݻw6mZ1׋Ǐ @ ~qs Bf<5OxjS3,Xc˵hl46XrpWWكTWW/:8N88zjkkQSSP(/r5,Ec̕G{189v;tҪ]y-~YԌf<5OxjSb fd@[[<pA׵8|0vqڣ)6<5OxjS3ԌfK66O`{~ߏj<쳨Yv>yK%ؠj46Xr=3~??Crȑ#Ggg'`ZQZZj466n7>#]O- MOxjS3Ԍf<5[±={aÆycޥ46X[4w҂yGl2ozt(¥KpUu'˥YԌf<5OxjS̜kY /q%\~6l@CCJJJPXXH$1 w՛EDDDD񠠠`&''56Xc4! =zǎ_}h+O"OxjS3Ԍf<5 dmIVӓ]x: OxjS3Ԍf<5OͲ+T}$ݻ """"""qxtxibS3Ԍf<5OxjSZ@qqca0 cE`I)MOxjS3Ԍf<5?|O"u XVgh$>$"`Æ O?=l޼iUEgtԌf<5OxjS3^492 p٨9L̩˭s;%%%?<<#R.S3Ԍf<5Oxj˕fˎ,Z~Ԓ1 4uXDDDDDDp^x2l6}Ŋp81tXR*W$Ԍf<5OxjS3% goPiTLI&5OxjS3Ԍf<5KIvx|p2i o'etXRJExjS3Ԍf<5Oxj=4tԌf<5OxjS3e EDDDDD$'h,)"<5OxjS3Ԍf<5Yu, va0G;pJ?BTiL?^F4qI˥ Ԍf<5OxjS3e[_ƗWl:{q^z D >3mHvMp!04)౔4{_xjS3Ԍf<5Oxj=Ś{fz47ô7|o!3`qF?<000;r_ٳ<ܞ+g-v:ʘY 2f2am}o{s5nϕ n{Sߛq{705L8;G>g GBY ֢u- ```F<"ҼuӸ<`G_?ц}Q?[Gk6{ϫ[W[\._f<5OxjS3Ԍfn;wZr.|v  |>_ZC`I\.,jS3Ԍf<5Oxjr8vt@KjiOxjS3Ԍf<5OͲ""""""4N#ޅtԌf<5OxjS3nǾ}'@uuuCr\:X@ExjS3Ԍf<5Oxjr?ɏ ʕ6>|55kb: ,"""""^vga۶mi:Ŝ;=𷕧{7FExjS3Ԍf<5OxҬe[:066ײsuwwct f|~mb4r\9,jS3Ԍf<5Ox<'O-=uM0 #FGG 3"""""":^xe|󭸟7 n޷lB&,) )[kԌf<5OxjS3lePU%0 v=;we_4xjS3Ԍf<5Oxj2i⭷SO=ytwwVZESEDDDDD$x?{7tXRKExjS3Ԍf<5Oxj=4tԌf<5OxjS3%_SV<ķPVVXDDDDDDVMcc#~?|),)"<5OxjS3Ԍf<5K.׋>:χ`0łp8*`I)MOxjS3Ԍf<5Yry<\/#""""""򵂂<u.^u={vϻ?wF]j0>> 7|͛p$~i,)"<5OxjS3Ԍf\jgEEE?r޻;>>s__DhjjW_}i9s[nXʩdP3Ԍf<5Oxj˥f^}oԾ޿SSA9sv8ߏpwơCQTTvXR,WRLjS3Ԍf<5Oxj\~oNZ@KJy<5OxjS3Ԍf<5HNXRJExjS3Ԍf<5Oxj=4tԌf<5OxjS3C=4{ɥբ*}^j,)"<5OxjS3Ԍf<5K.ł]nMrԌf<5OxjS3,߇فMm]8E v|*..mmm8}Tya7.p8Gss^y70MW۷;Ġp,@X#Q?$BxjS3Ԍf<5/MOgߗb.7222珹i=W(i{ "s '\Dx[]uta,3 "^Gc/9W\zH5OxjS3Ԍf\jqY;se qkZZիװw^ܹs'&=:y'Oeэ24= UR+O~8meK.3~vFDDDDDdmx(--8yUv^g-bKl&ҽS˱F ,/4PJ;r/eɤf<5OxjS3ԌfDg[j? 1Xw/=]6LpI1f<5OxjS3Ԍf##c <ܷQltd#}Fg~ ?tmFKsFaeKg+MOxjS3Ԍf<5YHhsL-N…/oÜDpfx?˿wR-Wn߿Dc׃X'Qۗ8SttԌf<5OxjS3e⑿م~Um{7ΞCȹ ܀vLmVg/ދs^xܗģ2?ׅI4ۃe,F[[;c(b_zy 4L 2n;҄rE}`>H pʓi"<5OxjS3Ԍf<5Gy7-QR+qq"Cf$`,(G`;Lekh -{L=W&Zt:Օ1n744esejܞ+g-7W\ 8)nGaa!FGGgK6cE~H\W'p|/eYɟ='Bŕ.L-ضuӸ<`G_?ц}Q?[ѷQkʗn.4X.Zr@R3Ԍf<5OxjKu3ۍ;w-Lww^y啤l_F}}òy[|>#Ŋ+ڇÇw׿g`׏㭷cdd${Ņk[R5.LDpj6{ `8Fb5B7އ뗺030 `)B0Fc=Ou3`g 3Xm qjS3Ԍf<5OxjZa v )}G8ɺ~}3xfq0PX]x7M7e"H{Mk6`"0CəM>sbXf>9kdf<5OxjS3Ԍ+zc.y^/{seODOLAg2WExjS3Ԍf<5Oxj=?nƓ?8}+erͶjJ"<5OxjS3Ԍf<5? Vi8t ݸy; eܑd$*UJVtHFH؜%xn_6a˖f|VduVh{z5XDDDDD$u?bSu7N{{>\|3VoBmpUd꥕і;]$S4ņf<5OxjS3,| JKK;_=}"k oӵ3.y-4w[WZezD]N55=W"LjS3Ԍf<5R34P(r3$C(ݻw;wg#_Mϕ@#\Šgο ؛~#~˝1/P3gNdY8(p(ot D6`fV}U=l%gl<5OxjS3Ɨ_~ Y[[/f믿l3G>4a[}!ܝZ?k.=ZӤwxQ&hm?=nG;L[vzja9ʽpZcWxJ3-\w#-ns%LI9j~(1ڕ]wN'""چYAOOjQƍx_bWȏ9|H f'pJ,5 |T/c`t *>&zhr*_Z~_~lD-Jۯ_np/ܣMc_h ?f|E6]upku g~5+Yoܺϗ<.\.Mq]wTρRF[?n<Ż>LO v嶹uS͕l +X-|]${~M[zȥɢf#`s53\g8.K7G ufㅃh1/ 2~-7Ѧ2X\.ܯ.,y `~%K ^%ں3gܖ߹_gw޾pkY-}-7^(]MAR.|׌E=pߘl3z֍v-^<хP&խ%]/m,oy"2^Ԍf<5Oxj=?.6]DhXmXPju29PK&"_*D֍“F&3^N"1dIjԕϟm):L`2K?$u9cio(;GYnJsgel%-]N"'aZQD>t-SDDDDD'`"4ދ{ӽ#+gX^Mv\)6<5OxjS3Ԍf#`Kx 4Dmŧwh\Y}$c.rD|׼{\.^dIjS3Ԍf<5OͲG .~>n9۳%-f*$X32mjơC^8y]8y^ 硔0/qgyC0e ciBƢ> _pOz 9QT  }O?}xp|/8Al,38܉sI$W8`AŶs;w96ܚ/GŇ>{ufOEޢe^u>YhC%Y૽\.ff<5OxjS3Rvc]K.݅W^y%){Q_߰r^8F=nXQW>ciwwl<<ӟb{`G V!|8z9~Nݎ +'a{\7?=~dAj]aDmf"MOxjS3Ԍf<5YHZ;@ٝ-!tFc;P= 0l^8K~Zf𛉗KtԌf<5OxjS3e/-.˿sz19k"4p=2KB}NO.\νQ*Ψ4D9 Q-%5X Lf`W .ޣ"<5OxjS3Ԍf<5GqZ~3{{P_\^m`ⳍS"S[oe9[rtԌf<5OxjS3e 8 jaJ-a.yC޹E̐u:+"""""y?k_˽ޛ5V8wzҽhOxjS3Ԍf<5OͲG@נiS)lv«&{ O#0kmMMM `xx ηܴb=Ji Ԍf<5OxjS3rY?zzzg\o޲sy˚ ߇p8gyzcׯ@UUVu:,""""""^6;s˗/`fCOO݃?SSS8| >4zݏKJtdR3Ԍf<5OxjSz㨨(ǓO>'?Ξ=!),c_xDw8B&{ʉ"Ap-@Ԍf<5OxjS3,{@ACxe6&" QU|Urz#ZbȴkԌf<5OxjS3ex ]`_`! _' h܁7It]_EDDDDDR q4:s8ݞ}.S3Ԍf<5OxjSS%4]f<5OxjS3ԌfC` KJiOxjS3Ԍf<5OͲo+Oӎd>MOxjS3Ԍf<5Y8y}*Ӵ'""""""/ o+K..S3Ԍf<5OxjSp"<ލ5AExjS3Ԍf<5Oxj=4Nĩu!hH /ݻ&hOxjS3Ԍf<5OͲR.S3Ԍf<5OxjS %4]f<5OxjS3ԌfC`I)MOxjS3Ԍf<5Y8ImYIP]H.S3Ԍf<5OxjSԌf<5OxjS3,{h,""""""9A`I)MOxjS3Ԍf<5YXRJExjS3Ԍf<5Oxj=4Ԍf<5OxjS3,{h,)"<5OxjS3Ԍf<5HZ455fjjj{u֔mO`I)MOxjS3Ԍf<5Yj477?}زe3ׇj<)٦R.S3Ԍf<5OxjS+((Ek%Ν;񗿼8y3__j,""""""oD"Y7p8YE"<5OxjS3Ԍf\imZ"vuubll4---1߬gXPVV|ظ^}}}˘99O %\.Wμ`$Ԍf<5OxjS3^4kllnE]vM0 #FGG v;x`;O=uǏuuup:(//G8jMّy{1}ͷ~@ O?= Ǚ3gQ]] (**Dyy޽|_}TXR*RljS3Ԍf<5Oxj<@gΜxx q)پN%)3Ԍf<5OxjS3,uNxO7:4Ԍf<5OxjS3,{h,)"<5OxjS3Ԍf<5HNXRJExjS3Ԍf<5Oxj=4tԌf<5OxjS3e EDDDDD$'h,)"<5OxjS3Ԍf<5KJiOxjS3Ԍf<5OͲ""""""4tԌf<5OxjS3e EDDDDD$'h,""""""9!/; kh=?twN\w熇c'ϣ'dA4Uӏ96O`bmX_diܺ>Ķ};` .EDk[&U # +!v\oplC˖rEio w؆: b{ aN6W`1Cp߼냁o.sA6͢~1^cuZ0PUBނ%V <+=YG&fy3Ogk6,~FI=6Ge %q*45;C&|w/{,ś&?|:{q^z /[zGawգr*hC<1je7d:6-Պpe|yu~jf͍&: yuؾO6azǮQ6p ުba IDATS}97aP<Ԏ̋f,_^6e))ka!_5Mhv)7"%[ ݈Y쟡yX?[Qf~ҿd#Ͳ@K"hxCX?}T€ !L@7U _1ap1s,R܈^e瀼< Y|#V5O3Ћ+W" N!g<,7pJ)? $VY6rYد:-Zu*g>|#c{/(Gf@^?[Qa#(L,#83p؈auA`ݑioulp cK} No)NLmͲ4k囱|~ lC{ f69̀ <Ey!xn]E{ NܮߍC`ĽXPT_vطe6y"9, .)ka6^L&|(\W G_?+P` jDi`5ό5hg(>{l we  u"øqc|raUlE@~VT Do֝h><2'%΀ESu \vy+*-j+6o8u. l[@ n!޽_,~1:-z-L>fMtcQg/̏5 k~˼%{E)(FU٩ &SӰٿt`8ȩ%P^`01=>OTVY{݋Pռ|At#dA(rMl&)0p:,p`jxA1ja0_ۂQva"a1fs-SB}_c,.i 0fn5O?[j#ˉؐM %5%(6'Rh7<GU RTa[%e ض6 |S~S_|/Wz'0t:sb>r ?a`8QfabJm2=:  -a/C_Ĕ %ZZ"[ ù}~ Eo_j6WXDc3J`׶u}]JƆ0Q3 vyي~e{lhJ֠xp0ĺl(ل]-(;a7Q:wAp{{od; <+ u}`;>v`0|KT`?x--WfC n3׍;+1q oÞ=D ` Cau(/G}8ۆPz?c" 3_6@QNkZFVa 3KnLĴ?Fy,VWfQ_L9~]?G>g r`/o] lepvL݇M\Y),cN-\NL~]GDDDDD$藫0PF^ghhG] W\Inrb|7a:HO8US_A%91~]GDDDDD$%.Wә9>cAxtj-""""""GEDDDDD$Scccðl(Rm EDDDDDdTTT?޽ OQRRonݺ qE]M5USSS3gǾo߀a/Vt C(*K&l8. /X+Cp'w> ׿paܓPSS 6}tN^`&2ϒawPTO~y;Qpp٦WAAgl]n?պvz{{q؛x?blliοiF9-#²Pʞp3ߐ !||M.ӟ&7~Xz 6Xjkk#::jz=R߆TUAQNz^!Mc'STXD׿2e4V f5i_C'aLĢ0g0jbcJԴdޑzahy<Bi;w~ʕ+Zt:֭??_>fFc {Hn)hY+D w2s86[4S=QF#:FbrBܜ3G=$:J":;sYݚ@]|4غ̜7SLk+AcY8Ğ}\; [֢NZ*2, 6=g0%)KNkB~YPMP<CQNpy}o&4ew^Yymȼ}U|49mip ыʦrL|6qKbS& q{iUڹfș<<@~1AlfoAUcj0 .>A ð$P#DOYQu&699Ӎ,^h#<3Y}l"u']iťB4)%6&!> r?wbE0aҢ}( v}Cܩ".\Ujl;zv2؍EFxw33CˡkUCGVipja&zN+ bD@KW|膑>UDw!"WMQ}4SMD9K85~Dc a IG~A4A7s$aSD2i$|O|"Ni ¦C8kB_TfaiUv+ CɩBBlRۆ<"(YYNB&.yژB Fx}<>hܱm\6g!LAB96;rlxto5oGS)΢˫J}= T%k\`M'hTmk>!hp #*q-q]uPBZ6'ɜ̺6Lci:G^TNy($μH=Bdh+9FSKWrZ} 15ZZ}<7SE4t()JvIEeH!69dqZ9F 7*ѡSjOSmHOeұN3G?e{n ~ԆsJ}*b,q ] M`hkW'֥;, .J_p~6H+6'#f/ݭT|m^PAk?!1^Nup`e-M(F*:,! 8KŎx] 7Vrn bXuXTM}(VJ<}Uy\.MSb^H|X6?KWG)O̸@N'cfkliz1TE~ьt0@pfz;Ge0l7Jprp^ɡR 8d'#z {)jrBq6@ՙD%W3x׋LBwPu6 Ihk5':* JoD81qC5elg>~~Mڈܵ|)˨X 4Y,+N"X@n&&X{4 e%f']">|K.Ej(-g+?Mxmvtz=ZhUZ/5WQB;e >Ⱥ^ !7)o3;g $gif (j#[ Ԛt1|ф04_ЬBX&4z Z6Keg(;[¸;0<ܦf|S#rRL;untum{cq՝`g_aYFKDʕky ͣwRBO#XK:Q9'*=*ZFw"[f VF%T7(>DDr1bY¤s1+mvu gڊsmv/ Gv f{_ !),8Yı6VFIN"_;QYWvP4*Pyz(K4h\nGs\׶6++NS6)*5R|?|jQO̸Rv-'`C+VgS!LYÁMصFaBaݰMxU&3Gh=}ͶrJS;N|$7ӿǿKƕj+6/,1u@[-G*n=83k(_. pYAә{4.PV*Т5sgT;%9ɩ6}mSOϠc'uy'iLl\(21m¡}޹'\a;榳{YwvWW]^9w-\ZN3tH7ɽ硼bT[JuOS[N'ë *eٰp^jG֬baVjECK8gB!n+=4kѢ%{cN1ûv߿y4'>uNUUp9NU4.TWDž5:J7+3Yӻ]q5huj)ަr)@N@˪]ޞyQI1/ٳOBq#-mr[o%ߓO>I\ӻdj髋j4ZuK[e-LQT>Sf-r0$|>Y@Q]g !_%Mt@:*f&wsoB!n"hXls-&*@B!BO?ܤ'>}4#EŤ'B!BxLJ_v#Ϋ9|9)B!&#B!B!)B!B R !B!B!B1(H,B!bP*uuu+B!֓O>9M幽NKB!B1{=g``A^I2Z!B!Ġ B!BAA `!B!B!B!-n "X,W_z;W!DdX!B!Ġ B!BAA `!B!B!B!)B!B R !B!3g u|JJB!Bv^}@oH/X@Bmm-VnB!B j^-#-Vt%^Pp0CPQQIsSSc̓=b:)B!by%ʶ[o„ QS] @S/.!B!sKV,Eg'{0祼WtN?U;8o@_uCg8@R3yx *|SN7ܔ']]׮.kW#׮kW7K!'ݲx-^zEؾ>ǜ<%{/o}(*M``܄ L6;vv͚sZʌԚB.أ6l&+_gPԻssy F,w,n7u0wFkiB2Ӵ?l5^Iy9qV.#[/5'1M9Zނ 2y&l- !@=|{p&E:Vb12?Ҿo<.p!L})jtDvlCHWyE|7{+io߻>yM=zOvuvvu_O^\BxG ii3W 7~+~r<'9ZaV7޷F> @SS>jgmjɼ3YvlF<~ `8JZpPr4SB Qރiv}ceTiF@^ee8c)< @ŧ6/pS&t+А>n%xZ/74 i,\åO 4/`<Ń11s_" .,do?Ȣ]nklIk3; $UTJ8DH6xOvu_^g]ܵzuokBeGdqGVkk+}o_X{zgd2Sqk_p[!49CTyPq֝O&`P j#3wt1ZNrh|M CWg9P ڨ"/22esY5\CԻT.x~Yqٌ .G%[t ץVڬ槀;G]-TU7(Tk=mCQmkW𵫛>yM׮nkW7kBwHs<'x+w:v#ƍλW֣l۲'O*ݻYd1Z=w:G7p7!p2ֿadhI||oA8]1m$= Wky'(޷Ui_MTb=+~~oOPm^M,wBxv-l9oKdXy=YԗN IDATi*g0oI>-72o:rǢ`iw\ͦڱ; j@k' kBeF}|} fszmƆ.w~my#=}J:>Zׯoo:&?ȓu}k5Yau??7u2|m#(o]HE<ć_}}ɟIJBqGΊYu#L8U9z̹$kp"uç8_˼s.~ ocӫ?E]Tf+Pwpy%2u{B[,^biVuvLms+.O.!wQQDGG̳~SΘGGGէWu^$fw~۟q;[S^YyZD\fhbB6SE_$8Fho@a4]p,MU/}xU8s44 (py󣆡FY=89yҿe㩓>Ck=UVCÏ*z0e*5uWaRA "PϹ۵k\BJn`$hd̙=?o4AYw/}PQC'rq=!ldF+9bB?}%?x1H|szgT}<6ຐI1euVf|ݞ9_B*UT[sZ*nQ1aj-':y;iE0f=TM\޻9#N  =".v !(7Y-ߓϜ)rە{{.III4fxx8I `|bPUUEMuMV,f54n=P%x6ähZ .YGt/d%`xe ʵE`XzuB՟aWR{~|u##3aT˾duݜɾ3V.fl[)v~Qu4,w'ǥ{_M=UplG8ܫ>$,x'$HP|%M?|dh4K8ɫopI8r1?`*>5;(4ܾQ>s;#?)׮^׮~ѿ׮._~^Bk;wn ~^-tOwe_y;fTt4sͣb=f(ݥ_a?@rwHC_J,ثS+|o'UU|7Q!B!׸(`)B!=4!B!B!B1(H,B!bPX!B!Ġ B!BAA `!B!bŃ^:t)D/$%%H^< yFz;!ouuM<ܒH^!h2,B!bPpuY}TckտV?|[;-B!7EF:zɹ|kXҸZIelFYo9""+CPKBmYYY^IOOh,18۷Oedd|=9H<9N c tTc6!B18TU`ƍX[Ym[p8p8tj_Qu@ˮX»nX^oxD1Ig=*!BLY~/+Vd_)z=LLKCӳe.cz|Ng?[~Iyyc1LJ/rpEv3220L&߫$&&h ;;UUo|eFt۫x3ikq DU1fEO!Bxצ 5jyy>Rs21-|m[b>X,W1s&wfzobx xƍǩS8~xӉ f1}tt:Gq O?… yw9}t ϻ@X2?< J4VFD7>Xoo}o}<#cB J0Fal]_}/c#MxWg㗰X,Mԯwʕ #$$py~߹ gX((( 99<E!&&cǎxEQ wymLKKc̘1ر 6B7+uaY8';@ ̽`ׇGU)jg|:LՄ2% /֜z ]5S{oj yObCw(n!տ_̰G_R&j&+>l*/CB6ԓu1)D>/rUv}VsױB!9e,%o>i%mݬꚷw~3idΞ9ӯ1cPQQɉ O<nq0L 1cسgO |d„ rQFҥK5kk׮rMĻS$5&1gQ _ BJ ˜io\ͪ6q'B2lRxU*MEE4%J.Ԡlaզ4wwN.h|Jez]Ӈpn֯?H \xB14͞c9rtGyv<Ⱦ}rJcEq"7;vٽk[rOuu5DGGTWW)Vmm-ׯg466df͚%5_+!7h?߮';4]2sؔsr٥!xb͊;Wۢp:e^s l ݷJAMnnB!˼T¿'Ht\b`d_|#3dXH[:[pK/h*7O~SWܾJJJ2e F[9ٳeƍh0ͽ~rrrXj}!pjjz aaiB!3w.!^9qSn4ۿ3'V1.=pHKȽ'_j{нva+EvP^^… ijj/墬ɓ'#p8 a˖-:^II<1@h^%/gDvC b׾ @uXi)-|Jj*$\"3[ \T#΍ӆ9|㝁s {QB!W 뷮ww;oJqt:6n)v~*3mVڮy_.wq\\|ck׮;o.l.̞={|>sl4kR!Bx[zzMK {µiaXLB!%55u7e,18ܬ矜˷6*B!bPpɓy^Z@\Ӗ ^%Ioo[uuM pK:gc !B!`1ାKo -~DUEk !Sddx^Iwzɹ|kX 㗚v!*8Ξ?SP!n1zUrK q-)qj`v?UQl"5/CNY!›riFN,\¶-[|gttbv(J: e=s+]%K 1 nX^oxD1Ig=*!BtbJƍ+/"XĴ4:=[6o2Nͷt۩<~嗔=軸?vspE>a[FFF{755D4 dggh4޽{{u8R 3ikq DU1deq!›6mȨQ /Z^{ii.(mzǟbe3ɼ3#x뭷<Vox-W_43n8N:ϿPONtt4Ṳ$/_N@@6ӧ8rHc ~i.\Ȼӧ{}^^ZA:鋖CЊ9Lg`&t%,eK'IЉ,b[orK}yGpY4@!`8GcOX4̼G{ͧ9%`Snm~ű,FK/Nerz: :Bb!11 f:^BBlذ>>?,!!^xgy>`Mfpj7@1n2gNe [?m@QPUzFi8Ɏ`On- ^n}>cB[D.)̜mmb0Nm2j]Waԃ1r4J19=IUXaq3~};wvA, $''(p1RRRzp( !!!;iii3;vaB&XH &*s1v^c(k*_b c_Uފ)FrdQbvMʚ#I;ǎϠObƄ84*Ns9s(5zٞq,rpq!zl%qZLTUi0&tm$gL#' N2u)%hdfLèubѳ4^~MW.һ]߻uBqP2{IVcKTɋ%DI1l@;b9⣜v])L<gkw1TTTr"7=O=~[>L&DGG0fӧ8??2330a=z~`0tRf͚ڵk9p|h{ e5!31# ӢZͭCՅ(vn3ccT@IFJvmd'9|1qh\*Fs>sHfrf֯`&OnoԞk(CRIOq|F6zjH/c72s)l8ugW*#>їYg5cFjՙÊ1H2gԭSWB[Dbz(ɟ}ϬU;̯ _nZvy,Z Ns?wvKq&::hQU>Ūe_F,Y¬YdۜwG})Q$'OaJΝDi3.@m X ~(`j-dEp.j/MC@*VT|2{/E\?(9mk% ןB X(-6n4ǵ|bi8Oi hM!Zg2>/`jB]SӱͭB!n2Jg'TfFe}5D)BH/BG{r׺]wɜ9}} /Q`JMσ-< :t(sܹ^h4߿l~RSSٻw/---}nf)71l1*C1$n4SCgLp9Z\^/I@I1Q]`05W^k;k0EM.oԠU4(_\S:6{{=]S?&c>;O,/+~Fb!7%rFԫ P|D6"zK%c52#xd"вa |߰a?^x_O'Zh8ztj?#g@L]Krh%ΫwťQɰah7^<00mu!wHܹ=WBѝMQ '++ w!SSSXbp g߾}}:6##c矜7;h?/%eXkéoo⏿şȪ(|S !oXbpp[,9H,n>m> +IB!Ɲ#U2%zf=\xNB!BqpܳEwl;M{{n666H^߀'[! !B!`1hY}kտV?|[;-B!Vddx~\wzɹ|kX :=~Yo9""+CGT!7<œП ]OeRAƩuR06KTE&sԼT"bpr8>Z;O׳8s ۶lptpvӉ1(]9zϝwɧz1'ݰBb&{UB!bJƍ+/"XĴ4:=[6o2Nͷt۩<~嗔=軸?rpE>a[FFF{755D4 dggh4޽{{u8RAh!57Ueh2zUB!oӆ5⋼?o|g>ݶG1z ,_JϘ9;3=~zcouoruJs||<ƍԩS?~ eDGGs^LJJb`٘>}::#G8`駟f…>}WmXʌIE\#̀|g3~SFh[35E'9r6QI1+QԹGAgԅI,IO?܋oZmEl/XsŒ&y%hhgRҥ"ts29=텱^w#ĠgXHLL$))< ӣ? eڵ477AHHHږ /@NNVOqyF`>ǷF>g`S ZVTŴiwPϑ=iMP43pb 7ѨD:e;4[Lsps5X!?{?:Bg䧴a7_һx؎I:bJBxD88SG\(?Tt`NZ -ufJ4@qc | 3wp5.9Lա92GÉ_pyrS6 9HO 63v=?6O]9V!p->F_LU5XUի3y&M 3g\;JN涿g0x⩧vۇd"55hƌÞ={T瓐@ff&&L 77Gԯ6 .]ʬYXv-w `[%E3$}y.Ts|i5Eu %Ӓ9Q$3r56xb)SKu fVkq,Fȩs2NHf[2k"bh)pC3U{ .`_I1QX) JG =S Bv?s7)lO=hj[ޮǩuxNl7F!g*)?{y/0]AQ :/hL4ZƬ6Ѷ}{I̥ɷI4i$&ǜ6IOEM#APE0 sۿ?PfZk9|g8wm$/)?d_`grtb=s9UV3;V=8t%,FbN>ĉ rh4ϧիWcXˣ.!SG_N HTzۉTQ{ݢ03FѴWUv otT!HHA$T\.!Luč%b88~ s6B!Tlg)r=Όh ŕȮt2#?fx ;3|ja}wݼʯ{>?7o&;wgǓMGG"X#ߐ8SQ}l4Nqtn AS_KE,IhbipSV}mwh!ٳvUVVgN'---z=mE|| (g{=gz7;Ȧb-?E|YtrhUy|xkgwpݵ M]\!c鋾A8=jjNd{U Y~ bjT.aj %̔&%B]&?;^,䭧䑽uZ;t:y_q$((h_ r:pBAkϞ==vW|ؽ{1=je޽>Cov^UGU}n.Gg[%T<Šv~~Jj'_|Ǝ/>Esޡ#%)F.Gj'R_IQaҹIhܚ!R!9Z-NA羨]h4~jW\~߹j[} t,11h<jaB=6 fh3SnLBᗑ1"c!++o/ Xa.*eJBџK #I_N!B!cGM&Y-w$14ްNaHnrV_?fs3) !&B!B1&;B 1{{N:6;C!.`{0g(BtTJ3Fj|cX!x~M-ƸbH8@{{( !RPPNs\cþ}|jҗH,Đp)Q-vTEaB=qX-䯨BrPUz;}s rsv=CCC M[w!,y]>rx套YE3EfIȠ0tOBy6$).. %%ٌ:^bb"dlذ"lBccOqD`-i˸-"EwَC,Ua'hŨ6f !7w?>?)O~E~@ 6s(~5$LJl6c9ڿƟ np6RRRR(..FQ&LѣG6m\.`6tiiiGm6Ns<b(`;O9=_GE]0gTw*f-f~ NJH"3SC|yöZJ1Ek=63>csX89uޭ诶B!n|9w}WJ ;t EO=t3bAE.vAHp3ְ(ؿ O+.]Axx8rwqcFG֮uR__ҥKh4*>jlld֭TTT0m4VXN#77W Ql `%wfF>Q꺫O4~P.ࡩAI!Ԯzuv+v}EKwZڣZ!H#ago \7G=TPKa! Ft9u>;~F!čz]|āgpN&Ox|~8tb=s9UV3;V=8t%,FbN>ĉ rh4ϧիWcXˣLV/P'ӽv1&>hB9'ШЛ~uW JNpryUu:q\UNFuuU&MCT^}fg' i v9z(X&O IDu6q2@O^ōd5IKTW"#1Ye $!yfz~`0xlT_yj+ͶHȠ6mbr_ɧbNZ+ﺋ;{@yG~ 8gϞ{fee%{t@aaӖ p\ǣ( 555ypo' FXb*r<߂/s~ja׹[zJL|9VW5R}E=bfƝ ӣ/R_QgP{/u|A6s1j99-SqYt~53SB[~~-_?vwgnCղ=;{Uŝ+W~/{˞ C ={v_`^x<RXXs^V{^ -]UU:J?K;<{QKj g[Nlc>d?yvNSG>rGtrAq '-N6UwQ< 1L:7 {PB!Ft:|݅FV{(KVo,!WH@CK_bh<jaB=6 {58͈f$.0̙ !_ؐXҗH,Đ3tT)SB| M#2Fj|cx*-B!bD{ll14ǰ\wMwtF^xqX6,B&!B!d c=޽VH'N!w[K=x3B1D }w%K #I_I,(x~M-ƸbH8@{{7 ! p:^t}XblطoOZ=Z)AnJg` ]b۶m@hX1~P/( IDAT3^>=Y'ZO_Ȓ^lj}8]$3,avEQ?A/htmjCT{@ &>c9K,l?tv/Q|c)k)Qo%%\G VZ"obI:rd~NcSWB18{YRSC~_hL97oM"5<g #{'p~'N j7uF*558~gzG֮巯/yѣB\\ݻקDV\ٳ9~8GupCrz{wE~y?xX=*lWL6'}.ͼGsN,[N8kF;=2iBJr_>z?g#bKQ™iKCDݿݧwλ1!m[.;7=See=S7^B^^>p8d 6ޅ;$} ˭CWAvJ#mrnZQgKq/rBzj^yqvu9(C-+?Z+ϼUhT #1= ahT!M{ ѿ|:!U:RA)!S?7o&;wgǓMGGe}!kH `Oc-$W2{~,GN|{WZC0Z.1u.Z|'g>G1D2s:.r & gb!XѨMihJ{L *nUN,E,IhbipSV׹Ecl+AB\_Wvb~Nn~0gs?O^A!-S9_ -~/ƺ\ݾ@b Y|9>x<Ν;Gzz:?0. LNN]]]^ǫmnCZ=9/c!j; |3.!9[cU:kђLrNAxr21s"Swmr݉F*obڸJNz0DdM8δ)$Pi$xߘǃGѡׁEg[w:l9UC (4ںszb 3YMD4GUHLVX!Mȏ~>T}&nwF&bZ> ku@ZCI<3%I !.gM$׿~|| yu${6Z-۳w^=tҋΕ+IPPЀc=t `4:&:^{3ݻx(,,缬V+{z;VI7~x3ZVp4<##~=kAG(D0$4nuc ߡ!;4~F}}OYCt^LL8mȠ) ž}|j'}yd݋;oGCM_OTc m،66#FX v0g*b$ؐXҗH,Ęa20R !8TɨHҗolן(B!B~w|D 55,l`4r&; `Lf֎B1:B!B1AB=޽VH'M)!w[K=x3BџAʹ%Ɔ/ؤBĵ_j9i o1%B!`{ d,16 f,@ `!Dܔ(jy0v,\+I!F˅liR !f%v-Q-Y1JB1lߖI`ƍl/zFj:?9ii+7w@1~l.Xw H7oX믿>duJɓ9s&'Oرc=/""LؿW1Yj8233j>|x1z=֭c;yzC .&2️-=Kod4wh=H'zwMdCwb1jLA)z׵7vB@_iE!ǫz7SǡA7}׶~{~.W^zLXFtb=^Q o6${~lpx/11m{GEE[%&&a{1}!Ўk"8=l;ӆ -t-Oǎ >C4i"I=sg+-jsvmbry.UCMǟF kl"{<.| 6l6is}p&%`6{~[78Fii))))( &LѣL6x. EQ0TVVo:Ǵ4RSS裏ضm<ֆv\* kQ>"t{wSܦ G> zsʁc4gZ|,QMe>XF #jm25cyTwmmDrg'Ѥ#,Sn6s<b(`;O: :#S݊j+/o_/Bt EO=t%%%$&&rJfϞ9rQsh"}/ C[;809KnAw,5uv贺'uM!DP\DĄ֪5^CEȨP*[0'>~&VN'!$`bD9-NPoܭpoTΟ׾Z)?rĘ(JwJT皨ͦ=[f8 :#*!kT.WxC /E-w^oR?dtRzFY/K}}=K.%..ш[RQQiXb:\)XG-?ۅ-y:))H Em"NmQ(D LCi4mH}zȔ-T]hՀ2TgUV<: .Hfߵ~QN{׉v*<45^ ()]_MIrV9!ZPqW/6q?{2-wҗΩ)8Q`LX \b)..L8PJJJ(//:FyW7??BV^b!//st82zUԞ:J(A!O[/EkAACɑۉV9Z87"~_w}!zbf,~ s>.z=yc1VM(/cŬhr# &sy  k7.c}x8?g#)8>b՗쮺(j')|>5U|ׂW.nW_ugY@WUUd2sN,^x@`Z߷"lBcc1D` i fRxjJL& 4jg3ApK9V…E_}x8Gяg̟G^Ք]v:{Pи7Y|9gPPq;PN,Ô 2.}4;UtS:ɛ\hm~%h"#B iw3D|5Ts /]{0"m&Q]]immip9yq\frrr~nfUUlt`}3O$`>[qB1%NTNCY7^aƒDwZN`6j &:\(AUt赠:{?I^X]*>mZWl_]d5IKTW"#1Ye`!:okwzO~*;kБCvwgI&q_H@rCcoyxOv(DQPP6u: K 11Q۷ϧYYYҗG?޽~,!Ęh'1 ،6l6#FX yH#b`222Fd,16dee-eR !e`RL=B?GdKxk?7@.%B!B~Z,)=&;n#~/^8, wbB!B1ABJ`{[!t;0vBl],aP!FAʹ%Ɔ/ؤB*~M-ƸbH8@{x B`{ d,16 f,@ `!(rS:[穊JÄzڱ[rɯB!BUUVggcyNΕ+ى7"44n rFϟВo}BQ2oo6ʩ$wo5̜56mtR !Fk۵D[gn @VB1:lߖI`ƍl/zFj:?9ii+7w@1~lZ.Xw H7oX믿>duJɓ9s&'Oرc=/""LؿW1Yj8233j>|x1z=֭c;yzC .?![[K~ݐf$%ǙEMQbJ^9ezx|{K~3+=d Y7[֖-ٖ.W^zLXFtb=^Q o6${~liD"""ضmfrKLLdÆ AzF 6sU>f 0DEG{o-fQ &pQMu<˅(f*++ycZZ|Gl۶M@C_o%9AO*8 ki țX6`Eٳm Mmd‚tT#o_ ͮ(z*/Nb,et~"d c (BW ~Ac5BS_BVW~E9Z=!1}kq-8;rSO27=/OoTjjj9qxz=]o__G{{;8RSSٻwOpII \ٳgsq9Bkkr{,Z#:S&c8]2193 Bz 2(]jzSq3X2?;m,65|"c;R7sO?%//*ņ)2)Z,h%sB#SQoDZ_S~.pbȸ!&| D:~Zv鲥8ٕ38;;q'qӤF;~F!LE3Qq -Ow;cC<94.;7=See=S7^B^^>p8d 6<@;k8s~!'S_gΠ03FѴW`0p;=O4U@PxmTz3əQ \~7ΨekE$)Ecl+Ab4N?)]L<l>y}-GxO帣2{|5/\^rv6#Gf崶4 x8w<ø\.f3999tuuyJAA >r-ZFgʧ,:l9UC (4:Xk2&n2WPLo0e9ks+D^I(;ڀabn%j"9fE6GbBE{wyx-9[{.SՂGI6Cgp'9_r؏ӟ>32())Mu\Wx駘ʻ"g^9P}р_Cٳg={t:iiii\.Qp8x7˓oV㩧\ڑXZ~)ChnI{ ku{5Ӯ͇gz_?,.im<xoՋ:eo݆Ve{v6Ϋ8;N^zWܹr9; pB络Nt:y_0'mϞ==vW|ؽ{1=je޽>Coh `g9ߺ=*swh=G^5Jr}RKo&бВƣ!:(&c3ڰmmF6#1uaT!n #2K)J&4"ca?76'B!bL$$ؚ6) )Mwttlp&};e"!$#B!B!yX!l^|+Nc&;B%< !D }w%K #I_I,~x~M-ƸbH8@{{d !p*((tzFJ?ca߾}>khBAs)Q-vTEaB=qX-Wbs\o=l>tܹr9;򞿡}wWWep7 %s??CK} ! U&U\fQ9/SBk0s,^ش"XO1'- VGΎt>,.g̻UW{LỄ)\l .pС+eeea2hoo'??߫$4 555^b2 //ϫvbH,` ub묘e|!ȷ}[6ӧO'=# 7t"xOf6wů\I fxl=^ fQZZJJJ („ 8z(ӦM:BQf3ۃ1--T>#m XCZ+!LҏKCmcri=bH )ON<4 S*㙖9KEGSꈚyLdp՝WGF;(N~/a-멬7N' j?m*aahY7A aY0iJ;)DE9{뗋R›b9uޭwUw?k_C$m!*H%jmS9w^Lg:iVmq8b/)! I+'$;;;~"H읽C.땝.|zxX@V-ei<Ԅ6i g)QM?$/azt5].PL b}ST?uŴPm3ũꈟV5%2C^>.װlQ} r0*@1'! IYΗFctg}kf  hDqs~bӅ1w-_%VF_ lW\$""={8t1}ʊ+HLLd2*~joogԐիJ:a5:\hmYp{/ЦW51Yӻm馱P]- 7`TI>*}̝Iw,3ԟjAul~Z>!F@K?_OĆ*t]F-zsapb2f|X=4}f??vp9\3s9OɧE׳~zegsjh gG2 z:x9 !KXL:={ӧFEE>h4pJKKyᇱX,wNS@ccXx.)FfHU(UQx/{^~.pz׿6!#ѿ?n=F1yNy콴OAʺKR§AZR{Wnn?z^ Jb򪫫cŘfvw˗D~~>}}}h4l6^Nt"dRbPwb,g$PQ!{OVtןn~L##| (Nbg )9zWW4օukBL5P#*cgaɌj{_1LX,l|yBBB466fk@BB>(֭bPQQs1شioܘ>qL+IROFd'Π~+6b!=%E׆a3|ցGoŇ~8Z@vʏPmŘH4#-'~:&S(63ѝ1tv.3M@Bܘnη/&?E~wVj%[~α Ͷ|ɡ7mbv^eaVyO]>ſ|`y'"xin)Y[[˾}p\tuuPZZ咒n7III(BSSeee>p:l޼"ynxmܴ~|p֚R>*OzYy! vk+==gNӵt _yX^gY>=nE5.u}jVxSjޔ )+;6z5^7ӜԄUkgF 4OW rcCwlTWy!plXJJJp|۶MדC[[GPb!>>O>įAҗǧ>ڻ(yc<<12;3'BWCRcMknc70M&[0:nM)!WNNθ%܀Œ,@ ~iWq}#3jebrHz _'}yb!B!'^l#[F ySwMwrvwwߔ 9d !6 !B!7LBI\|+~S?PBBIhI r3BLDjŕsKL I_ؤB z/]FҞF\K< h=7g!5^KL 鋤/ X!&,C a랧**mZEZ[кWbn77|aܗ‚ݸkr݄ {p\_Q]]V d,1SBLP57,~/g7٩]Kڙ f%ZUYyMWzg{YYuz vf,^混 {Ty9/}f>>1}.pʕF.\Ç8ljrZ,RSSh4455QZZ|SLNNEEE>7B1mttܮ+f9Y !&;3g99l|y6GC#:7gaVU),Qu=MwIyA`˖-A=ѽ[c^Ni9s&ԩS?~|EGGd)..)fZZk֬!,, ɒ%Kt9rd1 O>$Vwߥʧ';y<g!k7W(#w}A|!θs&UGlBL ڴu?n;s.6~j=T}y' 믾g6 / n֓CUePav;Q^^@zz:QQQ~mBtt4۶m\-%%7RVV֭[ioo+1-+ 6S Ioq;)(q0 ů&5B]㲏Mlwߖ3/ v-#kג<#(bbyjC~g۩$==rEaڴi;v nE!**ZyQ瘕Eff&~!;vgB8#\ΚF@_=Gs׋{;߸!LO"Iɡb23@z>$Pf.}{f݂I?eΝܝIQ:Z8'pIGr_+^w72P= {p#vqӍ$' C{)%"m ,EƋZN_1~M[!8jBg,_*7< 8s/@L9y [Ǜx3 yjbXHLL 33UWTTB^^ ,ĉ=zQh0xXl۶mXGM%T}NH:.Jf^Ջx;aos; 8f_'DT~w{4-bT9_b h1E.VAWSnI"vq}/Jd qϲv/Q+V'_qcqY,P G'^*"튕+p:DDDp:f2x|ݺ\Z[[YbL&TUկXl߾222Xz5zB)X'qS}z^IkK'9ap^G3m.@kH P TP9]cC쵵f`-mU|zzmwf2= 6jjP;LM7^a* ĴWspy3gʙ4}%g}DA!wh=7>q!!M8?{rWxO{xvN-~ᎿyCACg,tUKdXz9t s,BBCt9{,ӧO',, }hcX(**NLM6%,HGzQhh8N1k1q -w>NkR=.{<xCҋAWL^uuu,^ݻ|rϧFfy"X8)Bҗqwrw~DSŐG(։10!!t +v(p8p5#$Ëm=Ui JNB![ۼj}͜8VBݽ?U4靾YKUQ1^P8{ P9VYʪSkkȴhi<Uw+ꭤP$ܿ,*d,!D̮8-6laܤ W_.1!t:;q_=r+?ᾼFՎ8+?~])>1}߶oʕF.\Ç8ljrZ,RSSh4455QZZ7nxl&''"ډG `!#f s.ZlS̙36><~Z^ֳ0+J(nf;$e˖ŞLz뭠wJ̙37oNC/::%KHqqO1Xf aaa8N,YNȑ##a0x'Yj.UUU> J,HO"lӔ9&JBr'*8N.ou׻vu>B1tu3:b;bk?2~.mX>|Gxw]yo;-i_w(pUX4 vyX̆""~zsr*z}`XvRSSIKKtp:>KII!::m۶Knn[qFغu+~7ܓM١=t1ǓVޥ!z#{nkS=H7# Ѧ6p Jh(?ZoO%v;Y]QꑵkILTTqL-X?!)ۨyNӭ,͹}(%b={lTܛBR?BVNx݄Jܵl|5Xm[..@+ٮXIDD{ pc&׭ ɥ+VdBUUZ[[۩!##իW),,uXkУu: }D":Q{qT[I0Svh~bo̊ Gi7Mk@uP`G~G UMQޚZ.̟AB Nzn|0 NCbr? ~"R.zo#Qk[!*Q§?}|(֥nU c:/PWWŋ1޽8˗/'))|h4l6,5il+6Rb>㲎ŒK*l.'Z k\Fn.ӜtUc)\Mb\;^o*cкv 8*P@!&X|_ Z$ĔgcL?5_f*= {~_'v>QMn{nuhǿ"BDcc#Vǯ_KCC<裸n(((```xuuu 1_3X~'Kns<wdKLϼY2sb{Z(svif* x:ji?1(DRi98=N8[5o6s3YvN~B`+ӧv11mUYq#TSP{(sp=HvNin:<, Ȼ~ v)+?O<S__Oss3۷EW>O[.))v(455QVVS ͛)**'1M}g^}7:i)* g(7SvH^:lD:TRyqγ`ML:vYA25.p0vB1bg4` 0[)f7)!&Ooxū,y8ojIM"t:;q_]Y\.^}'ܗ‚h~/_|=g:75޽{}z)--l6{c\Gk1Κ"ޯ;֊3:U0uUK]YyB!jsBfP5ET qhƣ,p\z=UU[Fsst:Udn\tkW v,1BimmZ+via݄n"%x3BVNNθ%܀Œ,@ `!`02V 1YuXbjO&s҄B!BL F(NTr\&;)~SW!n6B!B1%3B!&Gcp~MC %%GMP!>>vT/V20^M `!WKz'OrC2Z"B S^?*%O>įví-}YB!&C a랧**mZEZ[кOvQUuvw {^羼澾 2(!.ޞrXblɧ!Bmj Mvjgגv&=Y 15<ŵX,hhjjUUo"LNNEEE>7B!=[.uvbl5!+!;3g99l|y6GC#:7gaVU),Qu=MwIyA`˖-A=ѽ[c^Ni9s&ԩS?~|EGGd)..)fZZk֬!,, ɒ%Kt9rd1 O>$VwߥʧU qqCk'of5!@AXW~tt_lHHO!jFI^'BtuXYlDLl{/ooǿcdG~Z;l[ϭ$2/n7JJc A׳~zsr0"v; }/==(LII!::;v-%%7SOW 16vXOν0/ཪn\舘u'_^=T~ڀq0G+j,M:=A bl!z#{nk7]BO_D2]Bq|2}S|[wl ;'v;Y}dZg$El\,Oz/~@^v;S^^(L6cǎs<ۍ(DEEQ[[;3?dǎrChXA\*ڃwqU@KDY}:aaaTTTP]]sF3(--bPTTD__9:N=*+C(:) Y~,IK棷_(n( 9^]]l4A1Efh}/nU)a.&i;ޏ[ɈTcO[.))v(455QVVS ͛)**'1-e,y׈zqY[9wp/zPP$ܿ,*H>SZka5Dɧ͗*ꓜM_75fN+[>Ml!fWina0R nRBwAA w{Ct:vzW~}y),؍VqW~5w\#y䙣!n} }xx w^cz^JKK)--;/n/H'-җVɉ3f$"hYf@3oH\ؐz/ӛiNjB wT*ĵ3a1\hmmy>k3^HX΋˷z=999u%cOj'}y|製ˁ7` !~x5$5&K۴V&;v݄n"%x3bԐXҗH,b2Q+S@Tɨxҗ' B!B!&߬g|Ʃ5 0<|d4Mr\&;ਨ g2< BIFB!BL B1B[7[5JBK!;_\97P0^M `!REoD/mĵēܐ3=PFK0ca4 }eR !z[=~TEmZ+H+r ZFUUٙ1yzVSX}Xna{<++2 K%ƖuB!6&;kI;Ĭ[*]˼yyӦ`^3=¬,:=v]3Ng[=vb3XɾocrJF#.\Wl6cZ9pOq- h4(--EU7l6CQQO#Bq p]1]>"ņ*;#a|̙CvNM?HN YEUe%{ Gscc~siww^PزeKbO&oVP;y̛̙7SNq͒%KHLLاiiiY0N'K,Aqȑ0 <䓬Zw}*rcol `] w#]QitD!̲Z<1Y_Kѝh^V~%w~ZU$?GN/(8 6k=82_,d-ZtEG֮%yF2QQQg?% nSYYIzz:(´i8v>s(BTT;1++L>CvτqW+0F-cɭx42mʧJPλgGcک=RDIc?#Ddn"! mT3( j[$FnɒfnɘMLx82t !Ĥgڊ-djƆCm8TO\ KKbgtq-ײSf /}E9} sn&MM͜.װlQ} r0ᲩB!Tn 9 ɉt^y]u2pr_?ʉy{лCmx/y؅db N')áL&_. #&VVXAbb"& UUimm+V{{;۷o V^^P Il `%?5≘b0| R]P]- 7`TaHD9jmSȹS9=h (ج p (}XB !ޥǣ481h.tgR Qk[!*Q§}Sp};<&./^ Y骪)З ('{ IDATrAX,.A1镗sYONXXTWWG(--bPTTD__9:N=}^b_g/Q”v_T^~;neϸok <cx x.u{+uQBL8lॳ}}}h4l6^N4c^\/~Q*ۍVJR!Uˉh.ֈQ;@C!_GUi x>bj8?~u+0#2c1spٿe{/dnMQJ|sl, 6v>QM4vu8C+)a'$$$ )ĵ466fk@BB>(֭bPQQ@]]6m7ސww]iI"HLjZ":\;Z7YzNBlO ewBLyfb;}j&[ ?9xof#&M%ɿqxQ٘Ri?X@?k+-?Xf_>ЃdPQQ˛6]1vksϲ0+`._s<A<477P[[˾}p\tu ^ZZ咒n7III(BSSeee>p:l޼"ynx0x+8֘ba_uu_=͡p|j3eB!.]=q[}rnVpS{S昳݋?ojI0mF+t:;q_]U\.^}'ܗ‚h~/_|=g:75޽{}z)--l6{c[(z T+/,cMC; %Zfyꅓ,^_α_sG;̠z$Ct//p;B!&&WÜS7ӜԄUkgF 4Ap\z=UU[Fsst:Udn\tkW v,1%,y3ih?V- )P>6GB!D hmmZ+vip5G݄n"%x3"rrre,15,eVj),~L[oC !btFfTJ1uuXbjO6 `pu7;!B!] `mdt:oN~ ;Nnrx!a\UT!&YC!B!Ĕ0Χ@ !\|+~S?PBBIhI !׋U˟ d,15'}ybX!5_ջ=xzF#URR^v@S'|WV,@ `!b\h=Tέfy6[K[rݨ:;q;=Os_j v_s_K{ {p\_Q]]okV d,1䯤B1զܰdvv-ig҃ZUYyMWzg{YYuz vf,^混 {Ty9/}f>>1}NnʕF.\Ç8ljrZ,RSSh4455QZZ7nxl&''"ډG `!b]Wl -6VsS̙36><~Z^ֳ0+J(n&;$e˖Şz뭀wJ̙37oNC/::%KHqqO1Xf aaa8N,YNȑ##a0x'Yj.UUU> 4V?~/gI:iRu} 7R ?]~L osw0>y)B*厌B3)!l}&}Yvu ~Z;ompw>zv E EadPUY9T'!~)-/ v-#kג<#(bbyjC~g۩$==rEaڴi;voΣ_Q b֒@Bh1ڈ,Ep8[N2ݝL,vlgDNģncEm ,Š@ M;EQ$,Y`S*@s{~%Pwﻗ \.E!::Z|1++L8Ν;}5DҒ8P̹i#l8%.{W_&Dr~ZnUYS~J*/231(}'%G !*]1@B1A:˙i+&N+c?9o_%.]<@}r&ggpY_455s|5^o^Kbb`6>ǢE(//^Wz+V}v)D3gTܴ\h!>}6@435MF-dαwf\ AY+ p{{*udZDh3ٹ#ݳқ.ÎG鞳M!čArHؕks/vr?ŒD-6Gnv~v|/ɩr?ȿ ?9ڊll6;vczzzXn+Vٚ/Qq\)6١ IMh1ʟlD3mTPi<qC/Mm9TkjhLbf7S\li 84ꇷ-BzޏI#wM2%؇NQx8Uyk6Qq[X`tlظٜ=sRng&4t|T#3baE8i1TTTpyfΜIRRTWW{GѐHXXEEEl۶r, Frt8޽'|"b&ghg5p:{* h 7:C.?qyYCkhx'<~kw9@U{8☷MvNGyE!"VaapRj6)1akW;K*'k !hwBfP5U< j=9O6Mt:ywGG*h~ۍ~u|EQ qg9=?KZ !bDm-~d3ڨ[Kڹf%&=>Ȃ yvk`NǦ'֬,tZ{v3VctE?OzKN~Y5k`0ѣWd2OQQWq- h4(--EUoxd"''CyNL)B5JWlHlbꗕ ];7o99q8,]V˱cC裏vZz-Μ9UbMlMa2h>U!QBI[s/KͣǢE(//^Wz+V}v)dJ1 ݤBظp:jY~y>WDţ/)ϥ0k}X< )" +7|[!AFQiJf4~/??n|_m9"lU ixaۉ|W45q8DFF~>x_.~r]31 vݼ#?Ouuu,Y޽{}rJg``Fjz}+$XSߔ)B z Ā!d^v|f8jU9>wgݸB!8ۼ۫Bl{l@r >+Vu`3r6~;,O;O0q~_`oet yOmN-dF7Qr;nxG F֮]KooOy衇p\DGGgWWW' Hboiw9kSJ q-UP{h4fn< XRCh/9B!j"3NvbH~Ɋz'\9T+SUQi#{_?ՀKĺɡg7or/ǹ5+;d^ſr@yG~8477P[[Kaa!NKKK*fII .$E2b8l¡Cd b `%{hYElXP);r>CGT=nMWJ>:[H<2Y!D[=aku\LJ[Iؤ?=bCM< [ 1~#ԾSlF`؉VeW~>k+k =yطg/!!!ܯx5g_w 1 GvWmxPZZJiiyYVsjnj%F&бĒB!hHjL"=،6lFFHBK", rrrd,rssKeR !b !jeh0kk똒Dpן\76$B!"(jߤbb8I9o]~7yӛmߞI9ohxĤW!& !B! B!{}xA#%P{$g<⮫J3Sk&B!3K}:'}}ۈoI !ך+))tzFӍ:K?cn٣Z B!wmyhՏBK>BUUWٕ}>q:;ֱo1oxxn7vE7 %XƺKL,!BIWzs+ٌ6j֒v.=Y^xA,\ȳ7_St:6=8feس{:[x}}+*O~̇56=]r˫Y@ww7Gj_nn.&~kXHMMEDii)~~KL&999:tȫvbH,BIeu.[IDATMJ\i|͛GvNO=4ˑZNc7pkVgؿo߸blX|9yw$_=`otڄ!ͳgf>}'O\111,]LqqW1ҸptRZ-ǎw ^ϣ>ڵky뭷8sW97>²e I E¹c|ToeX鹰*vMQ窗/ rG!qd`G\Ǵ.CoKh! ]`?m0sc wws?w^s\‹lشq~fvlظTU:.Y`gHMM%-- Ӊiϔbbbؾ};}}}Sn)))nxldo| g٨"== Eaƌ8q \.E!::Z|1++L8Ν;}56(c/8U}4ഩ(q_km #9DD84)ֆ[rY5PEzsσ_ڞXU}Rr9Dh<8~(UH\^.#ffa-xpP"p~!"8uͤ꯶7 P[ڰ6*|3\o||8w/@L9U^>^y7%o1}cX0dffrA JRRRcѢEsqz{{+G^]wŊ+ؾ};|2 ˸m]..msaAMQ=(7%y|'{JT]9ƛY4wQޫ%)|iq6Q=31\+bi:cnX!5/v/n]$3f㰛gϜNK~3 C⧙lWY 22s}Fׯyի1FTUէXر .ut۷O ilb `ʙ1p|ne%YL=)=FIMn@7sɎ 84@@{p҉v^8z 6.4* : sʙ ]?x&e]c Lxim*0H %('&Rqi_UUɹmx_7B!Ωޡs6Sr+?4ژ?;| 3;:~ʞHG\ju:6llΞ9323V=><а%, Ο?̙3 jh4(**¡C9G!COGhH\ʭ,NT UQ %{)#ШC$ƑÎR;!]2.UQ{h4(t#G{X!F=q1t0^14mB ۇ(?g3j(s=xݴTUOGu!9Z-\^9fbvy套G$_1}ձdL&{9ʕ+IJJ"??4 V}$`M}Zk"̍slWDEmhH,B qS5[3SXTXqG(˅+?}t:wcߞc{yQn+r}o@K>k鱌u3X[!SjS/|n{%FZΥ0d…(k׮孷̙3^ &ބi_zQq7slA|D %r+ғ5OGYPAPE䆍BT:;qVf,}eB(NZo?a(ނCyxHrxٰipprٰq99)u:]fJZZ:^JJ 111l߾>rss}^,%%z2nJ{{OqDMh\?P 'e(Ż)lZp{ReGsӎƔ@XE?O%ml.E! B!4shzE ѷ ~}lVIͶ'''CWEG0.3l6/>Hd?߂l6HOOEQ1c'N ##x. EQ7|s"33sN~&D`M!!DtPԡ6}ĹnJ̭|6=qD9UMs@Q]YĆ ?ʹKEv_3E20G[c"}mtKVnEGC_aռU B y7zC,opqSC+ĖUE|.lΝ=s 9?fNyxz^ͫ~[LX,f34ϼRœG"0ڱذq9{ eܜS$EEERLO1Sؠr0[&}**n[Pq;CM!קB"z"}lFBW}3J,2Sh:\i-̌R(i ̇^\k&nW^zyd'?i@W]]K,d2w^\$@`Z^J2 7Eza Ġ"1`bhȷxVBITòB^B!Y\{> R!=Q^_;_EY,$=hn`n9@ZVgO0b'X_566j@bb"=ׯbPYYɐH]]7oW^w"=*fzaV0B]~u:;j>xmt:y#o%$$dܱճcr:pr!&Waanmcz<JKK)--9/}n/&vNok7}5(p{FW)'ݔ.jq|8w9{i7oB!7:Güg6ӜԄG(ķ&0aylBhZN'hTU`~4͘hkz\.ט\j1c Ѯ t,1 BhHjL"=،6lFFHBK"{Xrrrd,rssKeR !B`20V^cJa^r-؂k B!B*B!Nd AEzB!B)B!B)B!B)B!B)B!B)B!B)B!B)B!B)B!B)B!B)B!B)B!B)B!B)B!B)B!B)B!B)B!B)B!B)B!BeǭhB!B!)B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!BLq|_fIENDB`geonkick-2.10.2/doc/000077500000000000000000000000001451017372400141375ustar00rootroot00000000000000geonkick-2.10.2/doc/Documentation.md000066400000000000000000000465661451017372400173130ustar00rootroot00000000000000# Geonkick User Guide **Author:** Iurie Nistor **License:** This work (including all images it uses) is released under [CC0 1.0 Universal (CC0 1.0) Public Domain Dedication](https://creativecommons.org/publicdomain/zero/1.0/deed.en). - [Introduction](#introduction) - [How it works](#how-it-works) - [Block diagram](#block-diagram) - [Percussion](#percussion) * [Percussion layers](#percussion-layers) * [Playing percussion](#playing-percussion) * [Percussion UI](#percussion-ui) * [Top bar](#top-bar) * [Shortcut Keys](#shortcut-keys) * [Percussion envelope area](#percussion-envelope-area) * [Envelope points](#envelope-points) * [Envelope point label](#envelope-point-label) * [Manually input envelope point value](#manually-input-envelope-point-value) * [Display Current Selected Coordinates of Envelope Point](#display-current-selected-coordinates-of-envelope-point) * [Percussion graph](#percussion-graph) * [Percussion preset](#percussion-preset) * [Oscillator](#oscillator) * [Noise generator](#noise-generator) * [General controls](#general-controls) * [Limiter](#limiter) * [Filter](#filter) * [Distortion](#distortion) * [Compressor](#compressor) * [Tune](#tune) * [Export](#export) - [Kit](#kit) * [Default kit state](#default-kit-state) * [Kit UI](#kit-ui) * [Current percussion](#current-percussion) * [Kit output channels](#kit-output-channels) * [Kit preset](#kit-preset) * [Kit preset vs percussion preset](#kit-preset-vs-percussion-preset) * [Preset browser](#preset-browser) * [Percussion vs channel](#percussion-vs-channel) * [Sample browser](#sample-browser) - [LV2 Plugin](#lv2-plugin) - [Standalone](#standalone) - [Install](#install) - [Tutorials](#tutorials) - [Documentation license](#documentation-license) ### Introduction Geonkick is a [free software](https://www.gnu.org/philosophy/free-sw.en.html) percussion synthesizer, currently available only for GNU/Linux. It can synthesize elements of percussion. The most basic examples are: kicks, snares, hit-hats, shakers, claps. Geonkick is released under The GNU General Public License version 3. Developed in C and C++ and uses CMake build system. Can be used as standalone and LV2. ### How it works Geonkick synthesizes the full percussion length in a buffer in the memory every time the user changes the controls or the envelopes. The generated percussion then is "moved" to a module that plays the percussion. There is no synthesis happening when the user presses a key, i.e if no controls are changed Geonkick acts just like a sampler. There is also a list of synthesized percussion that make Geonkick to act like a percussion kit with key mappings. Percussion UI (main window) actually is like an "editor" for the selected percussion from the kit list. Every percussion from the kit list is mono. The percussion can be redirected to audio output to one of 16 stereo audio output channels. Since percussion is mono, left and right of a stereo channel are the same. ### Block diagram ![Screenshot](diagram.png) ### Percussion Percussion is a percussive sound generated by the Geonkick synthesizer. Its maximum length can be 4 seconds. This sound is played by Geonkick when an associated MIDI key is pressed. Geonkick can handle up to 16 percussion in parallel, thus, making it a multi-timbral synthesizer. There is always a current selected percussion that can be modified by the percussion UI. The user can select every percussion from the kit list and modify it. #### Percussion layers A percussion consists of 3 layers that can be turned on/off with the buttons "L1", "L2" and "L3" from the top bar menu. Every layer contains the 2 oscillators and the noise generator. The sum of these layers are passed to general controls. The contribution of every layer to the output can be controlled by the "Layers Mixer". There is only one controllable layer by the UI but the user can switch the layer by pressing the button "L1", "L2", or "L3" from the envelope area. #### Playing percussion Every time the user presses the percussion associated key Geonkick will play the percussion until the end or until the key was released. If the key was released before the end of the sound, Geonkick will apply a 30 ms linear decay of the sound. If there are changes in controls during the percussion play, changes will be played only at the next press of the key. The percussion graph instead will show the changes instantly. #### Percussion UI The UI of the percussion is actually the main window of Geonkick. It shows the percussion graph, envelops and controls. Also, there is a menu for open and save percussion preset, export percussion. #### Top bar - "Open" and "Save" buttons - opens a dialog for opening or saving the percussion preset - "Export" button - shows a dialog for exporting the current percussion to an audio file format - Play button - triggers Geonkick to play the current percussion - "L1", "L2" and "L3" - buttons to turn on/off the layer 1, 2, and 3. - "Reset" - reset the current percussion to default - "Tune" - checkbox will turn on/off tuning of the current percussion. - Preset label - it shows the name of the current selected percussion, by default shows "Default". - "Controls", "Kit", "Presets" and "Samples" is submenu that will switch to controls, kit, preset and sample browser UI #### Shortcut Keys * 'Ctrl + k' - play the percussion sound * 'Ctrl + r' - reset the current percussion to default * 'Ctrl + h' - hide envelope, only the graph is shown * 'Ctrl + o' - open file browser dialog * 'Ctrl + s' - open save dialog * 'Ctrl + e' - open export dialog * 'Ctrl + c' - copy current selected percussion * 'Ctrl + v' - paste the copied percussion into the current selected percussion * 'Left Ctrl + f' - scale up the UI by factor of x1.5, 2x or restore the original * 'Up/Down' - to navigate into the kit list * 'Ctrl + Up/Down' - to move up/down the position of the current selected percussion from the kit list * 'Left Ctrl + left mouse button double-click on an envelope point' - open the envelope point context input for its value. Note: - when running as plugin be sure the focus to be on the main Geonkick window. #### Percussion envelope area Percussion envelopes area will show: - Percussion graph, it is shown in real time the synthesized percussion graph - Envelopes, various types of envelopes shown on top of the percussion graph - Envelopes points - can be added, removed and moved by the user. Show the values of the envelopes. - Envelope path label, shows the current selected envelope type and category. For example, "L1/OSC1/AMPL" means the amplitude envelope of the "Layer 1" of "Oscillator 1". - Buttons "L1", "L2", "L3" will set envelope area to display only the envelopes for the layer 1, 2, or 3 - When hovering over/selecting the envelope point, it will display the point's coordinates (value, time) #### Envelope points The user can add, remove or move an envelope point. The user can't remove the default first and list points, just move them. - "left click" to select and move the point - "left double click" to add a new point - "right click" on the point to remove the point #### Envelope point label Envelope points have labels that show the current value related to vertical axes: amplitude, frequency etc. For frequency envelope or cutoff there is also an additional point label that maps the current frequency to a midi key. Note: Point keys labels in reality are shown not only for an exact frequency but for a frequency region that are close or exact to the particular key. #### Manually input envelope point value You can set the value of an envelope point manually with a maximum precision of 4 digits after the floating point. To do this: 1. Press `Left Ctrl` and double-click on the envelope point. An input context line edit will be displayed. 2. Enter the desired value and press `Enter`. 3. To cancel, press `Esc` or click outside the line edit widget, which will close this widget. #### Display Current Selected Coordinates of Envelope Point The coordinates of the currently hovered or selected envelope point are shown in the bottom-right area of the envelope graph as both value and time in milliseconds. #### Percussion graph Percussion graph will show in real time the synthesized percussion. Every time the user changes the percussion parameters, Geonkick will generate a new graph and update it. If the sound exceeds the range -1.0 - 1.0, the sound is hard limited to value 1.0 or -1.0. This can be seen in the percussion graph. Issues: For strictly periodic signals that have high frequency and makes the graph to be dense the graph may suffer from aliasing for some specific frequencies. This will not be reflected to sound or exported sound. #### Percussion preset The percussion preset is a JSON format, and its file extension is "*.gkick". Can be opened and saved by the percussion UI by using the buttons "Open" and "Save" from the top bar. #### Oscillator Every layer has two oscillators the output of which are mixed with noise generator, thus, the result makes the output of the layer. All controls related to oscillator are grouped and arranged vertically. - "Oscillator1" and "Oscillator 2" - checkbox that tun on/off the oscillator - "Osc1->Osc2" - checkbox that makes the output of Oscillator1 to FM modulate "Oscillator2" - "Sine", "Square", "Triangle", "Sawtooth" - types of periodic wave function of oscillator - "Sample" - an additional oscillator function type that represents a sampler of maximum 4 seconds length - "..." - button to browse and load a sample (WAV, FLAC or OGG) - "Phase" - changes the initial phase of the oscillator. For sample this will move the start of the sample up to 1/2 of the percussion length - Amplitude knob - sets the maximum current amplitude of the oscillator - Frequency knob - sets the maximum current frequency of the oscillator - Pitch kbob - will set the pitch shift range when the wave function of the oscillator is set as sample - pitch buttons will show the evelopes. - "Filter" button will enable/disable filter for the oscillator - Oscillator envelopes for amplitude, frequency, pitch shift, filter cutoff can be accessed with buttons "Amplitude", "Frequency", "Pitch", and "Cutoff" #### Noise generator Noise generator generates noise of two types: white and brownian. - "Amplitude" - maximum noise amplitude, and the amplitude envelope - "White" and "Brownian" - noise type - "Seed" - the slider that can set the different seeds for noise generator - Filter - the same controls as for oscillators Loading/saving presets will preserve the noise pattern and seed. #### General controls General controls are applied after layers mixer. Arranged vertically, and applied from top to bottom. The compressor is applied the last one. - Amplitude knob controls the maximum amplitude after the layers mixer. - "Amplitude" buttons shows the general amplitude envelope - Filter (the sample controls as for oscillator) - Distortion - Distortion drive and volume envelopes can be accessed with buttons "Drive" and "Volume" - Compressor #### Limiter The limiter control is not part of the synthesis module, but is applied to the audio output of the percussion. Its UI controls only the current selected percussion. Issues: - When playing the percussion and at the same time the user changes the limiter fast, there may be sound artifacts due to abrupt changes of the sound. To decrease the total output amplitude better to use "General" controls for the amplitude. #### Filter Geonkick implements digital state variable filter that is applied for oscillators, noise generator and after layers mixer, i.e. general filter. Here are the parameters: - "Filter" - buttons that enable/disable filter - Cutoff knob - controls the cutoff frequency of the filter, the range is logarithmic (20Hz - 20kHz). - "Cutoff" - the button the will show the cutoff envelope - Resonance "Q". The knob is logarithmic ranging from 0.01 to 10. - Filter type "LP", "BP", and "HP". Buttons for low-pass, band-pass, and high-pass. Issues: - The filter becomes unstable for some extremal combination of cutoff frequency and Q parameter. If the filter becomes unstable the percussion graph will be hard limited. There is a need to work with Q factor in order to use the cutoff in the desired range. #### Distortion The distortion effect is applied after general filter. Has the following controls: - Input - the limiter for the input - Volume - the output volume - Volume envelope (accessed with "Volume" button) - Drive - controls the amount of distortion - Drive envelope (accessed with "Drive" button) #### Compressor The compressor is applied after distortion, and has the following controls: - "Attack" - "Threshold" - "Ratio" - "Drive" Note: - Compressor is not a standard compressor, it only compresses the amplitudes signals with a zero release time. It is useful more for creating distortion. It is recommended better to use a dedicated compressor for standard sound compression. #### Tune In the top bar the "Tune" checkbox tun on/off tuning for the current selected percussion. When enabled Geonkick will stop mapping percussion to a particular key and start to tune the sound relative (in number of semitones) to the un-tuned key A4. Notes: - "Tune" will not make Geonkick to be polyphonic. #### Export Export is a dialog that can be opened by pushing on "Export" button from the top bar. I has the following inputs: - file format: "FLAC16", "FLAC24", "WAV16", "WAV24", "WAV32", "Ogg" - "Path" - the path where to export the file - "File name" - "Stereo" or "Mono", exports into a file with one or two identical channels. Geonkick will export only the current selected percussion. ### Kit Geonkick is multitimbral by default. Every synthesized percussion Geonkick will put in a list that makes a kit of up to 16 percussion. To access the kit UI the user needs to push the button "Kit" on the right side of main UI. #### Default kit state When the user opens a new Geonkick instance it will just have single a default percussion in the kit UI list, and the key mapping for this percussion will be set to any key. The channel of the default percussion will be directed to 0 channel number. #### Kit UI The percussion list size has maximum limit of 16 percussion, and shows the name and the MIDI key mapping of the percussion, thus forming a grid. The percussion UI will show the current selected percussion from the list that can be controllable. In the kit list the current percussion will be emphasized too. - "left click" on the percussion name to select the current percussion - "left double click" on the percussion name to edit its name - "add" button (an icon with "plus") to add a new default percussion - "open" button (an icon with an up arrow) opens an kit preset - "save" button (an icon with an down arrow) save the kit preset - "remove" button (an icon with cross) will remove the percussion - "copy" button will copy a percussion and add it into the list - the button and the and of percussion line will show a popup for selecting the MIDI key - the columns labels shows the MIDI key a percussion can be mapped. "Any" label tells that the percussion will be played by pressing on any key. - "green circle" - which percussion to which output channel is mapped, and can be changed by "left click" on the grid cell. - slider for limiter of the current percussion - "M" button - mute the current percussion - "S" - solo the current percussion #### Current percussion When the user selects a percussion from the kit list, it becomes the current controllable percussion by the percussion UI.The percussion UI will show the graph and envelopes of the current percussion. Changing controls will make the Geonkick to synthesize the current percussion only. When the user opens a percussion preset ("Open" button from the top bar) than the current selected percussion is updated to the opened preset. Also, when the user saves a percussion it will save only the preset of the current selected percussion. The same is for export. #### Kit output channels There are maximum 16 stereo audio output channels to which the user can map the percussion. When adding new percussion by default it will be mapped to channel 1. Note: Standalone supports only 1 stereo channel. Issues: Changing the percussion channel may cause abrupt interruption of sound during playing, and sharp start of playing thought the next selected channel. This may cause unpleasant sound. #### Kit preset Kit preset is a JSON format with file extension "*.gkit", and includes the list of all percussion presets from the kit. The user can save and open this preset from the kit UI. #### Kit preset vs percussion preset Percussion preset is a preset for one percussion and can be loaded by percussion UI. Kit preset contains a list of percussion presets and can be loaded by kit UI. #### Preset browser Preset browser can be opened by pushing button "Presets" on the top bar. Preset browser will list in the first column the name of the presets folders. A preset folder may contain percussion and kit presets. If the folder is selected the browser will list all the presets in selected folder in the next columns. Kit presets will be emphasized with prefix "KIT:". Note: for now the browser doesn't support search and can't add or remove presets from the UI. Note2: In order to add new presets in the browser there is a need to save in one of the following paths the presets (of both types, kit or percussion), than reopen the program or reload the plugin: /usr/share/geonkick/presets// /usr/local/share/geonkick/presets// /home//.local/geonkick/presets// Note3: default Geonkick presets are installed under /usr/share/geonkick/presets/ or /usr/local/share/geonkick/presets #### Percussion vs channel Percussion and channels from the Kit UI are different things. A percussion from the kit UI can be redirected to any audio output channel (from 1 - 16 channel number). Thus, multiple percussions can be redirected to a single channel. Multiple channels for a single percussion is not possible from Kit UI (the host may provide this feature). #### Sample browser This is a file dialog browser for samples files (wav, flac, ogg). The user can select and load a sample into osc1 or osc2. There is a preview side where the selected file can be played and previewed. The preview sound goes thought the first stereo channel (it must be connected in the host for plugin case). ### LV2 Plugin There are two version of the plugin the default one and "Single" version. - The default plugin version supports kits of 16 instruments and will created 16 stereo audio output channels per plugin instance. One instance of this plugin will take about 35MB from the heap. - The "Single" plugin version doesn't support kits and has only one instrument and one stereo audio output channel. The UI will not have kit or anything related to kits. One instance of this version will take about 2BM from the heap. If only one instrument per track is used, it is recommended to use the "Single" version of the plugin. ### Standalone The standalone version of Geonkick has the same features as LV2 plugin but has only one audio output channel and one MIDI input channel. ### Build & Install Please, see README file how to build and install Geonkick. geonkick-2.10.2/doc/diagram.png000066400000000000000000012357351451017372400162710ustar00rootroot00000000000000PNG  IHDR G Q`sBIT|d pHYs22 1qtEXtSoftwarewww.inkscape.org< IDATxytM+$Bd4F !1VB|5L)!ҢZCKT*mUj!攘 ?{9MRp_kegg8V/c-<;vU` .O>#]<]@~" , H`QGB8X‘p$(#E! , H`QGB8X[sܽ7wx?s=‘ƍUr9s(++%E`[m`QGB8X‘p$(#E! , H`QGB8X‘p$(#E! , H`QGB8X‘p$(#E! , H`QGB8X‘p$(#E! , H`QGB8X‘p$(#E! , H`QGB8X‘p$(#E! , H`QGB8X‘p$(#E! , H`QGB8X‘cɒ%jӦjԨs$(#E! , H`QGB8X[ss۫y.#JJJ2wX,‘{7U^=s32dƏo28ggg-^e^z)!!e`VX‘p$(#E! , H`QGB8X‘9sF7n4w UZexG jӦL 8p$`[m`QG¶xԩHs'LRR̙c2H>^0aYu=z8q㆒㬬,:tHӧu…Q&c# O?T˖-_M6unFF>W?{>Ō ?~\yPB:w-c,%%E'NPvvvۡׯZx'Ȝ9sT^=U\Y]vԩSs UdIMCեK%Jg>sA4qDyyyRJ*^wZObT^=]pAnnnjРsy~ʕ+RJ_M۷Fڶmk_rE"E(**JǏp$}HwիWȑ#&.] jݺu:{vޭ/BիWΝ;s)>>^;v4NKKܹstR_^/^ԥKԹsgEDDڥr޽Zr/R:rݻF˗/ҥKzo*11Xڵkv~\b>>"EhĉSƎh5whС5k@.?:uJ( (`tattt4\[L|ҥb7o\nnn9ݹsgk:uTj޺ulmmլYcYfKLLLcΤ%_#3HKK]moiǎj߾:dwvv-ZnݺmmMzMummmezY[[kr2ThQ㵵233sIOO7Yƍ&6><G >>^/s͞=[񊋋}-[$___>} Prr9"___k׮i֭Q$ӤmG1^?3ʵs-[t{ .‘#Drpp0wѣGa2˙3gg~ucǎ:t[=uQ'Nԫj;u.O.]Zz饗|r3ezzcS^z%;wѣO$I֭*V>H:t0? /O?ѭ` ֮],___޽[ VR$I{쑝h}ot -ZTUVU``Z… Un\ÇrIw^? B8x|hST)΃9s~uQ]vԩS;٩S'-X@K.Uݺu+V(**JZsΜ9j׮*V.\X*YZha S:uh]rEWVZ++VUV?WǏk͚57n4hp_#..Nrww7yyy)88XjԨW_}UcƌQJ4l0Iɓ驑#G>5|p%$$f͚*_~*Qϟ+iݻw}:vu駟Կk:v옚6mk׮)>>p$HpjR6mqΫ^ٚ0a*Ud2^lY͙3'uݻw7zZX1m۶M_|vޭoUժU9˗׌3RJiƱ/^,'''IVZ~A6mRZZF͛k۶mF7I+++-YDk֬я?(}rpp0n;88X˵~h̘1*Y={%iٲeegg^Zpq￯85iDIII7!!ACSꫯJ/_)S䘳sN 0@YYYZd6lؠk׮%ĉ^WܹsShh.]$V8?0͛d_~ђ%K|!t޽{eھ}q윣m?yuM9r$?K/˗+44Ԙ?q*_{9IsNX͛Ըq|{Zx6oެviŒlEGGrʚ7o ( I=z5k>}?ԴiԧO,XPǏ7}g5p@0<GE ]>}-Zd׭[W'Oڵ3 FJs=^ziڴiZ`$e˖#%ʕ+ʒsEOڵk֬Yc#%H"JHHPժU5i$-ZB8iɊ5Z~}k׮8:...rttNʞ={TR%)R$XŊUpa۷Ok~u9H"֭t4{G:::*---K.uNNN:{lcJKK=+ks<"""n:L2&?G֦My#F>ʕ+svBBBԯ_?ImWN<~!.WWWU\H@|t\LJ &???R7/lmmզM%''KRSSս{wOjѢ=UZ5͞=[m۶uIIҍ7vZխ[W;v$)<<\.\ѣuMeddO?|o^ZJOOג%KԫW/M0A4`IKKʕ+?*--MO=7oJ*}~7]pA 4Ν;uj:y7n5guA Zo[bRRR$I*Qϓ.++KRVVV㩩jҤhra 9s8߬Y3h5j_wyGo씞_]#FP…P9"Ely}B x]`Ac$;;;%&&jȐ! 233M6ɸuSN\~m-[V||||ԣG 0.,XPVR׮]5rHIR5j(͝;XV&2d<<<Էo_8qBTdI%$$(&&ƘW`u;99_ <G믿V\\J.09;;?TzԴiS%$$[gڷo4hkj q<#Խ{w5m#ۧj*[ѣGemm%K O:ׯt jpuuU2eq_%OOOYYYN8Ly{{&벳u1eeeՕ+WbŊܜ9sF.]]ѣrvvge\A#Ghܹ4hy{{{]rEƍo47N#F2224n8 0pd_iii2dȿy6ܶ΋g6{r/\Oɓ'̙3*^x &smZ|ui9;;WzuܹSTb$IqqqƜ5kj9UllN>,/^<ǜ8q{~/b[mXBm۶U||{1B}ь3~iӦyfܯaÆ&] ҬYPɣ*]bccgϖ ___-_\5k$u8pׯ/URE/Mֺxbcc`yyyaÆڷowUf͌Ξ?_}*T˫ZjѴiӔmr… UT)= ,X^xASL$kNƍqǫm۶sԱcG+$$D2I?^TxqnntM 6LQTJYFtaѣ&egg駟飪~[N+Wۺ...2dOPP\\\4k,G:tŋUvmլYS&Lŋ]ggg*P)5 ;bcc5o< 9矫f͚z4x`I˵xbyzz^qͱc4w\ڵK_ڶmklyu]z5|>|XnnnQppYtqIҘ1cQFI*[h֯_WgQ\\|Mo^ʔ)_|=G4|XBʕSƍշo_-Z֞8q&Ns*<<\ڰav4lҘ;}t-Z<-\Pm۶eƍ +___]vM&MRӦM5k֨_~Zpj*uQsUVdeem۶SN:sz-IҬY4b͙3G͚5nݪ.]ٳ_=pu=P;&]rE}v*>>^} (+WcǎWHH[cǎi Pff>SmV ,PհaC͛7OoQS'u IDAT7|/uLE#GTLLl٢SB ѣo7okr2B N*Ip~zeeeS>}+V=zgϞ4i5j;{폪իL2__i۶mJHHPrԽ{wh%ڵkWnl޽:rʗ//gggu^ 'H1'lٲjժ]888護R߾}5h }Ә1ce5o\۶mtk{lR͚5Ӎ74m4뫯l٢}kș3gjzղeK8qBqqqTBBFqkϝ;ƍ+++K:uR"En:UXQ+W4gSL~Z 4OeffСC!!!ZlΞ=gٳN:[ny^z7rJ5l8߰aC}']6mژ^z%^Z ,o ŋT`AIF˗/E͛gtMnuo$Ieʔф deel 0@SN5zZrT={IC … լY3c^Z_jժyz^͓.\ht,\&OdM4gϞƵ͛7W׮]5o<(11Q6mҟ)OOOI:t www|};;fee)99YZ~J(!???3VnݺuZn0f͚p$P=zT)))Zn7o8kN#Gԍ7tyh̙j߾qm\\ջwo}gTΝ;կ_?]֤k_޽UV-O>5tP,YҘ+V@.]wU۶muA9::Jr .%쓒ڵKƍSdddfF79OÇKm#ԥK G B tk餤$%''6n(OOOYYY\UF֭['7779 ,uȻ=EI֮]&M;),,LҭJ޽fEDDZny^l'Fn˖-.\YkuJKKS޽թS'cnݺqƚ1c1I&Vڵk$sگ_??ϵɓ'mr=l?ls<4#\zzullljժW^Qxx4vXIG}U#[˗穮yu93.RF]xQK.ۍ`m Д)SfEEEI?vOZ~Ƿ~^{MYYY9www(QB111ׯ^GؐeggkƍF&Mƻ?j֬/k=,AAAھ}q\dI.]Z:uRǎ?iK{HRZD3F=zȷGSN駟Vdd\\\vZy{{>3͘1C:t0oU\9#(I͚5|*Ue˖pႾcd˗/WLLfΜ@%''k͚9sW~*TH__+$$DZ|BCC5i$ݺuSTT-ZtOwG5j1chĉ&c_~^y%%%inK`ekk+www%K|͛7k#-ٳgYa29& ' HL4Izg4tPI{̙3a\r1bfΜ۷̙3 Tb4|pjر UFF̙ӧ+11kjѢ6m:uh]N<纭%$$~~bccUhQmذA [okGIK Ӗ-[Yf)44T7o6s׬Y3ծ]8S8UV?TR4j(hѢ*TPut^on=3gW^JLLTFFO > ./R[nզM5lPQQQ&-_\駟SFFΟ?o̫W8/RR˖-5c yzzs:wkȤVZ=zq\BڵKk֬ѯ*WWWu=- ,ŋ+:::gd)`m3gTHHN>>19gkk iW^y u`-[[ dcch988}}}}vM0A RVV/wVVVVh"5jH;vкuf͚F={СCq?ׯߧW^g}ݻwUsgirqqQDD{*))IZrnݪ,+222G>33;88($$D9:0ap$#$# X\Cꪏ?-ZP-:ޥK/hn۶ڶmk[[[W^իW5~yw Hڰa?ݲe4xX7w)ʕ++>>>1WWW-^Xҭגdgggr|ƍq度M1d͞=[7oThhN> jذa>|ծ];9rDuҥF)Iڹs"##U\9̙3 S6moC;mܸQZRPPj֬'N(44T۷eee͛7?ɓu+##Cѣj*yxxH/EDD(==]qz2ehҥO? xp$曏6Βࠍ7x.;v0 *Ik6:/0dqqq[:u$___5lP| `\a]|YJKKSկ_?Ɯ7xCuՔ)SLӧOe˖?~uf6l=zH6oެݻk͚5FѣG}ܹ+IRTreFGɑ#G***J}|!‘xXm,ʲe˴i&s~ҥIxzZl٢z~ܿc޴i+F .lEjzWr#?C#%TRz75e#{FJԳgϖ٣LmٲE)))&[mi֬Y*[&N(wwq{G;p$d#`yEFFu{nIRffs]3##C$mܸ%p$d#D{pLlmmaS SBBZnm UF ͚539vvvVjjjs/\B u>}={VNNN֭[w׹^^^wCWbccչsgsbp$d.xTuY6mҞ={W^XDD~ڪL2&?+W̙3M&Mh:sL{} 5wءm۶嘷w5_ّ P{'f]T"ZQRWhѢG-MQ5j@V-Z!r~P!r]}sϽnUV-ڵk:y G}k޽:}ǰB8p$؁eϞk֭}?X-[|`Ξ=hWBBBzx{{+44T!!!*ZTb9Vtiu]M6Վ;k׮wյkW~7VڵմiSݻW)))՘1ck񖵹rRf%&&*!!At颩S*s￯:hJLLTrr>SuYݻwOsL‘gV+W+00TŭVu~]KGEEiӦ?===vZ ק~ѣG[>>9rnQL9p$/nbwz*U$MSNʕ+}v5h@zٳMٳgCʖ-o߮;vѣճgO?^Ç /[LgΜѦM)Iruuհat1EFFj咤k׮/ԴiSSvԥK]vM'NԾ}TLu4iEo߾jӦ2 [ܿÑ=! vbҤIV̑#%J0svvV%tII+ʟ?h"mݺUe͛Wsεv\\lbc֭[l FީCرqEqruuw}8eΜY֭ӺuLRSSu9*Ydv=# v"o޼qWWW^*-C۶m+22RQQQ*]VىrppHVΊ͛7M$S@Nٲe-3g|ٽuu%&&C׭vYE8p$`{Vpp@:z W5b͘1CÇW_}~*qA:u*YbE\2k޽[e˖<t5]&cbb+ڵkrF,Y֝:uJ[nU…p7_~)b1=# @%na < Upa_/^T6m,ZjH1Bƍ$ ф uE'N;CZBƏ3fX^xQ{.IU.] J6m.]hҥ$]rEQƣyavƍJNNVG;1deϞT{>}UXX|Mn...ZnZj5k(((HڸqFP^ٳgզM}駪^Ξ=77Ћ/hYۨQ#]vMKVzM6VZ={eݢE%KQFrppڵkզM4<6mhȑ#`GxuUz$ u;!C(O]^^^O\8G8z@{o <.#m`ϸ62\Μ9ծ];[/_] .Hz? ‘g#J.e˖z m=BGq[mx5j… m=‘g#!>> H3‘3fG8oc@:v쨑#Gjńp$왣d<‘g#D8jS&88XsxB+V#daÆ)>>c< ;# {F82Wm=#}r劭GHHس'o{xm=‘g#!̛7OVGgH3‘ " {lab cǎV/]d3TPaQNS=""HIIfcVWÇ7ժTbaԨQT2dqu6l0KzRR1p@S-000 è\>j(#..Ϊ7|cZʪnkj7 0ʖ-k78Uݻw}faToҤasY}O8uU{K,1ղdbatToѢaQpaS3z:tL50 #44To׮a7o^S}ofȑ#ٳM|am۶5;udadϞT_pqAƴiL5___0 Ezn 0̙3K.5kƤILŋa76{ea}k..\0ƍg+W0 èWޯ_?0 _|uVz\\1rHS0  4 d$&&ZX~W_Yoܸa w1aSt҆aF L}anl۶ͪwll1zhSRJaFڵM7o޴n:cƍVcСZj 0UÆ 3]fcƍƓߟ?]{ĈV!?X$*j';Gx(ӦMӥK`K-s̒>}iӦs='I>}iW@zHySKԯ_?lR/Y$i٦ݺU&///z#GK-[l}zҥ%I3L2YT-?~sA),,R/WzTPAVu777nZEr%I6lΝ;gbգlٲ2 êvکDZS(|ҥcȑ#5a„߸q)D rrz_̏zG<<m= s$Hx=sn‘g#URŪ⢤$}^˧ܵWHHV^iӦ)""g Ə@7w\\c 5i$U\cd;ŋ㱘>}J*e1֭={>x4s$`O9x̙3GqכoEӧOWΝ5k0%'n {Α Ս7zj>}Zk֬Q޽m=‘g ؑL2k׮1c~G| H3‘ݻNhѢEo…ȀGq[m^P,YԲeˇwe]r%&@F" {ΑS\r:v옜<^bE;vLҥK(S}ʕʜ9sf4hJ.Koxuʹu֚4i$)88X5j~'UPR/Q o^}vmٲEuԱҥZl~)**uM&OW_}U,2x`kΝruu$hBZRZԬY3IҾ}4~x}*]PU^] .T<Gl`ڵڽ{иql=#ѣGz6c nZ$uY3f̰ Gn@yyyY*U?Zhauz-URE觥7o***J6l#oZ:ud-%KԲeK͛k?$ xf=# -[z u{gSt>cYf:>>^;vPPP%KOOOeʔɪnܸay|nw*]uy&.]իWzkǎ'OjƍZn]kϟa`k#`m=TfM]|_~>}z\رci/IR\U:v%IRlnNw˧W_}55L#9s_~iڵkuĉ֪Uta ԴiSe˖-=/={jٳg%InnnjӦ"##v:v옖,Yby>HN2yjժwy'JIIх dQbH3n <Nrz !,,LΝerqqQf<&Mh֬Y}{˫~5jj׮/jܹs9` 0@...5j(ȑòfԩ RÆ 5`*TH}FjժY{Y"##UjU}Z [zʕK?NѣGҥK?%$$觟~{UjU͙3GU6mԧO999 wM}xbU^]4m4-_\f7r#`G3nرVZy󬎗(QB˖-0VfM^*S>K+VX.\X֭:SNԩƌ1cXߪU+xɓG~~~V˗?P˗/̙3ʟ?e˖0"EX;p5k֘BjذԩMjԨQ:uo?bŊY7nܨ6mڨyZUf C8ۈϠvw5,X -X@&L.\(I:{Zn|)88XS-t)ҥ*Vh >i„ ѣ]h͖`oPu_J*$988v:uTNIBp'OV\jժ@ZhƍA͛76oެ]O‘g#"00PժU@z!0ԣG1BaW^իWU^=󊉉ŋsϩVZ|GTTz#GX7 C_2lfWWW;w@2 C۷o$ >\ ,u]|Y`IRttA0 ]VarZr Pn2l^<~#`G2DǎզM[3 IDAT=zPѢE-_=zHwsG͓$SSLQ [>ݻܬիin;:uJӦMӲeTzuIBr5k7o>9`{#`Ϝm=( 2Dk׶SS}j߾2et߽vիA_Lǒ4a 6L!!!ڵkk޽.\М9sy *(((Hݻw׵k,XK]vUl{n<Gs$ILLԯxPB~7n(%%E?'&&͛w[&_~ENNN*]\]]u]bb&M!Cڰa=ƍo?ê^`Ae˖MRʕRJrttTjj8IWP!^Z:t̙3UbE;vLG}rw/GGG-[LGVUzu)SFϟݻ-[6޽[%K$i THHqF5h@?=vکdɒ_ ЦMiӦ=kH3‘3ћoŋ+_|Աc秷zKM4ɰkߵtRM>]g}sj۶mZjU({:y (as_K.ʙ3RRRte͟?_͛7kլYSC xݻWM4QMQFW@@u(>>^3gVbbtYΝF?}>s/_^Ŋ(w5eʔ ٳKg5vϞ=fZGj:~֭ *k֬5}l]\\okڼyN8%K*<<\jղZ?zhh֭JMMU;wnO5%KMG[nݻ?0 K5k4֭LI&qmO=‘3̙3Wʖ-H"7oO?UXX 7x#C-[6̙3Cz=jgϞU`I’:tUhQO.\C_Wٲe3ӧ5}tٳGҭ+*((HS"Et }駺q5x`mݺU-ѣG#GnZZz6mڤhyyyi…Z~\]]3gj˖-JNNVժUշo_?wO]v)55UUV_D_>,XjʯZ'NP=Lŋ+&&F PhhZnm9|r!'''-\P*W!CX:3g_~Q֬YU^=S^^^:yz-M6M&Mjժnݺ>:u꤀u]Ydɰ ѣG.]RÆ δ(&&F/VZW^矿5jX\Z514TvڳrG:t˗WM;!GqTUaÆ5| {FJxvکnݺZb%)ڝsڶmƍ/jժUںuL:uꤡC|?VXoFIIIJIIQttRSSuiEGGKN8Ǐa}ibbb?C:b U^iܸ4hksx\r̩(((H+Wְat… o>/_^111߿x ?^UVՇ~~Mҭbn_wފЏ?5j(!!oرjժJ,HS۶mxCiر;vBBBӧO_?5l0%&&J*ڹsÇB Aƍ |Yenk#`9xmذAڴiVlY 6LcƌQuiUVMEUtt:tw}Wҭ v;w* @ :rFJfΜi _ *̙3ל9sԼysǫwޖPcǎ)S&9rD4yd?^Ŋ߯ hŖ*U6m?[o2eʘf(Qk5G3/R;Z~SN رc-M2z[[O[VʕdKvĉ?v|饗T~gϞ=4iqLԯ_?IRBB:d٥W^W,X`YߴiS(44TrvvVXX,cIjѢU_~Y/}^|͛}YvlРڷoU***Jaaan+K˖-լY3͘1Coꫯj͚5jԨZRϞ=5l0EEEGZ`$͙3GZJ4h/^KvܩHkwt5jDS}…p̙3z)%%E Ї~ 2ߒ%K5kVQFjٲݫӧ{;mݺU'O|h۶mʙ3ʕ+f͚xB</^4=޹sc{;v ~1^;V.rׯWVqu>|\EzZb=f͚魷ޒ^xĉtkG`9;;+""抋SÆ ռysڵrkת[nuk֬QJJ{n(P@ҭӵuVH;vTǎW_YzOQ\3vӧvZ^˝,JBB*I:yN<[*22R˗OwXә3g4j(c&Ol5nuttTӦMu!IvB &Nڵks7n(668))UZ.]W_}o`vrtt֭['O>|XwﶺK/d bޏKyJMMnXX,Yb 1֯_*ڬY3_^'(((}[wUo9%K*!!A .Ժu5$G^^^>q3|u>|Xg}ҥKSNjzoIv вe˔;wnIeo[BB:w_~Q\ӈ#'Erro?^{8#GhͶfۧ}z Ig}flbȑ#‘SP+Pt99;;㊏WbK-[ .heyfؽ,X@s֌3Lm޼x{Zcjܹ0b9GXbUZ5IBEi#?EzUN~>,t^Nʕ+5kĉ*Z\\\9::xZ,Yرc~iN%Y&R~}?>cw^̙3JMMU˖-ٳJJJR…S{g+漷Xv:qJ.ϕ+Zl>@>cFy/^\Oy[tt_n9::[ŋWÆ {參_Ej{^nܸ)Shƍ ):|vC}`$XB*Uz>S~S-O<ʗ/:u2eʨI&62gάWz H*W .m۶zgH);wn9s?ם?^ʙ3u֩hѢѣ  ڵK 4H3dVxt٣`S0{#u%5nXܹUreS8Kll7nbŊi֬YDy>|֮]+Iʖ-+ ʓ'$i7o^;vLrv67 Cppp˗fS]"_|rtt9sZ9sf.gϞUP!,YRݻwW˖-ާH":zU~Gիi* I8p̗/o痟_z;`QAq!PQRD0wqsAH50풚Zj▩&j,LrDp~_V si溮zƜ<ХTRX^x :]{nsF'OxGO9 HՓ$)>>^qqq2euaY[[u/rrr\VV=]j1-vϫu| ^eddh͚5^4jԨGIQF*Y~m;h.22RCllvv&NQmڴ;m޼Y򊒓ʕ+}իW/IB6m2Z[CҭCBBB^ҭ`dÆ 5p@=3tMӦM_&MM6M/_֌3$ioo;wN]v}{< 5h@4h][]ZmllkUIrww7~hرPpp^uթS%yݻxb;A޷jAQvڻϘ1wKƌcmH)׾}{M8Q'N4tIM?5j+,,L.]Rxx^j߸q}>ɓ5l0۷0~E[ 4P SNyŋl0RuvKبyJHH~?T||w*UHj}\?U^] 641cVZJ!!!1bʕ+~ЭVZ@ 3/ 'ҭ;C0mݺ0~1wP%TRFJ˗h"?~a OmUpi@H R;1BSuwQ.] LmݺU'NPNTvmIg}0UXQ^^^y~'3FFzsNSΪSΜ9t}С=0`^mU:x|}}uL2EIIIԧ~j47d >zUVMK,1Q#Fڀ{uu@@@qtt5}^+I*T… Fp͚5]שS'uw;aÆ&!r_~Y˗Z<>:uRvv”)9sFo"""]BbTd{߹ ztrppPŊ蚿K*QQNJJJt+H~#322/`rssVxGDm8vիTj׮mhذ*[||| sJG}%&&D򒽽aͫ IB7nܐ$Gݺu3sssӾ}ta;vLjԨlmmuq)RDdccǏɨ߯[Vњ3HOcƌu~Iz>Cm޼ܥ<5k\眜t,XPpႡcBt8Iz饗ԩS'=zTYYYQQ 6pvv֠Ah",XPϟ֬Y#ܹS бcLK_F7nhȑZ|y]pAZp5k/ 2D֭hѢEjѢ$iٲeU׮]5j(.\Xy&LaÆܹs[o^ճg!"ϣs$,H qttT֭h]v:_BUP!9\_zLR\=VX{z\eʔQ2e[֭s=*_bРAj֬L (""0W^=EGGkĈ5cuBB=B ׭[WK.UvvݫZj8p@.]2}ocǎnݺŋ+t&6l WW9DHX2/QfMyzzW_ӧչsg\vdooÇuLNN6ZO!!!?5qDegg>I&//_W^yE醱4KݺuSʕUBuM!!!ta_`>}$ A8p$X%K(--M%K4HСCy`y|M۷yWb _vvvq}'JHH飗_~Y>>>2dBBBL*^nݪXyxxo߾ PÆ 5x`6o\ŊSգGuE*S"## -Z$U^]ݻwWϞ=Zjip6X]etRIbbbTfM &hC/mY|٣lٲ7o\\\ kZlU?s:|vܩGhѢZ`*Vh>C8p@|lll4n8yzz+Rbbb{p7nA!_s$,HBCC5tPEGG+..Nvvv ҫ j׮]ڽ{&MdrŋUD Cj:zu[Ν;tR믲WB Imۦ~A={̙3wI4iÇh'NhΜ9:pԼys :T5hM6uv&c [YYW;99h@jӦڴis:%G>>>]ie# KF8~)Re .Ochڴi:{z-}w:vP=쳆]뫯$\R+?C .TTTvܩE{9sx Ӛ3g֭[;wH"_w}W;w֤ItuƪN:ڱc!@hԩq֮]u*..N*Uz2*̆p$%,[L;v0{7 KSN5r:r<<<ԥKEFFaMTT<<ȋډxPt%6w轓<==uaI|rݸqðf} ࡽ1b>3mٲE܄ ~5h0nݺU7گ[njܸ9"k̙c">|bccեK=f̘ʕ+[YYYӊ+jR\\G@G8p$X={ŋFcM6Q`A_ 4쬅 m۶z˗/SZZVZe_ݺu5n85mTƍS hܹJOOכoiX[V- >\P6muO?՗_~)kkkY[[+**JJLLTǎemm5khڳgϿ! m۶?WVMNNNرcM7nXnnnFcaaa5b!!!rww׼ygyFm۶Ր!C oWTIkƏ+22R+Vڵk^{ME5P6oެiӦ^|}}(gggú ={"""TX15mTx 9p$X;ӊ+r3fɘbccﺗ֬Ys͛yFcgj]WbE-XGYGΑ<%ԡs$,H@עE heT! KF8݆ t!s sEsLGKMMUjj, #`ɬ]# KF8+ GqƺpႹ*T%D8p$WG@>DHX2‘%# ‘9p$WG@>DHX2‘%5wy.x9!:G|p$,jSE.yT%]#SV-+We<E5w +#)sD8p$WG@>DHX2‘%# ‘9p$C#`G|p$Ct%# HX2‘ _! #`l]۷O2wȣڷo/gggsHDEEʕ+. Rٲe]|p$,Hx|G;w@_p1ct)sDԮ]p$j@>DHX2‘%Xmxʍ1BժU3w0?X۷o7w]5{lsȬ]Vw6w[#)סC -"Ypa 0e<2#D8p$WG@>DHX2[s'))Is1w.lG#wY-YeF/nqH+++BH $, Hȧ Gp6=z(""e8p@.ì, HűT.Dsdkk+OOOs9p$`s]Ο?o2jRq6hҤIڲeY1c(==]9993f/>}䞇Ҽyt!(QBm۶UHG1xDVX!%I6mRլY3}g0#mۦe>pɓ'/۷8UVMShhCy)-Z_#wyyyYf $޽[AAA֭Zl{"oXmX*‘`uUKVhh&N#Gðf݊Un4c hĈozK=m&___yxxIZ~L‘qqq\ڷo,uU =zjԨÇkȐ!\u7nThh`]tIK,ѪUGK y饗`2RJ=z.]$nZϗG}_WV_|ԴzjիWO111k3fO#`IGʕ+'Ѵݺu{ァŋҥK  /h $*%%E/֬Y$IGђ%KԿõ;vԩS~uA4l0uŨS,^X}`{Sa]ZZi0ީS'uU#GԲeg߾}ZWn۹s"## ;;;%%%)<<9qÆ ZrN>-K.-[&;;;;wN_>Κ5Kzdmmm2_V-}W:[.SRR4l0uZrf͚=zJKKS.]TF M}fԨQyfCNQQQ1bD۞{95o\tQɚsi۶m|||TP!kIꫯ-kkkM:UcǎӧUN9rDԸqc9;;N:SѢE ˗/p.s$rrr`]zUFsZlFi+Vњ"EH *d4^pa]~@rssSjjӳܖmxo<2ԩSJII1 nsrrҥKHjmذ$777Kuww8ej?\ׯWBB!(ؤIuQ?bbbԽ{wIRɒ%sNorRRRT^G|0`kEGGjvBBB4c 4Ȱ>00PC+{oTpaõ)'v7:Gە'N@Fs=z"""dccsر\dzl0teMҭnq?L=zUk_m.33SW^CIOO7n{YFAAA~Q[I޽ Ȗ-[7oJ=F>*qqqRxx܄ TL8qB*Uz8Vc{i&HIԩn޼O??_ժUL͛ҥKQFk4vcHHf͚K.|fΜixGjǎ&0` xz3f~z{9sFM~֮]k6s?Ix{[YYf_̙3RJAҥKQNz:tXmX*:Gرcʕ+s/PSN^;v,mذA111ڲe (` F|j׮RSS5i$)SưfРASݺu5~xyxx())ISLQ͚5?JJ*e˖s6lu5-ZHIIIڳgz~yZhI&]vMW.]4k,m={V 4g{qqqǕcrK.\0BHX:G~0 i*S㣚5k[YYs*\Ѹ4ibxի:tF!kkkay]^d;wh6n{7NIII5T/~% ~Ѻu"##Mf͚%///URӍΑTtUΝﻦL2$ 8d0O[V֭\\\^ 4}LW6lpgV޽s=rzj׮??Zn=z^z?~<==uq͘1C 6TppUlY}ر5p@yxx(--M;vPddƏ>}HrtssSǎ5rHY[[k:xlmB i O?@hݺu㏵s)EHX:GЦMﯯ:YfiG}zQw'&&F|||K.GW^$ݸqC>>>p႙rg'üuw=:aÆ^$~Qd< IDATa[=ٳgkڼy﯏>Ho>CW>///ժUuyxx Pӵk_k̘1VVVںu<==5zhEDD\rZfڴicXWtiݧe˖JHHPffF#G`eXڵk]'> I8Α}O>JLLTѢE'CAСC|/==])))0w)O\ՍF駟EFGW~wmڴhUJHHPNNn޼Ñن\XXԣG 8P%K4wI&̙s׹CͅzMǎձcǻٯ_PB Wxx}:886_ᵻ{]wVznj26sօjR9[4zG_Ϟ=5cƌG֭1c?0wIOLƍk jڴi3g5ǎSΝU\9M2E$iźr劾Iʖ-/B>>>Vppc~$‘YYY&l]pAɓUNZn111'-εkt)}'q͜9SNNNwqq1]Bw5sL|jڴzŋKf̘m>twٳʗ/yj2^X1y7o*.%%E))).l KB8HժUK/IgӦMҥ!Dt^}U7$ٽ{wpԨQ#^ 4޽{MjhҤ6mڔ‘J=-[ݳJ*ӰaT\9EDD<tR8pdSNjӦԩS={6{ظq]=edd]'N$Y[[Y5jPXX:v(]-YFm4VbŇ:>99Y?nܸ%K̗*UJvR``$=AԠAGד?Q*TP6m4tP+WNw*%! ,XFs/^w׹ WժU5{l͘1CWx%%%_Z|@llN<5j[Fl@E8@v?XCajժJLL#GrʹεjJZҺu޽{zرcM'''PBalmm@|}}M:~iȑX1RJ(P@^\ff>yyyܹsRw?M<9`ADkoQF=T~o'Ζk <#… UV-٩o߾޽{nݺڳg5kfX{%M>]Æ 3ڵkruuաC3ϨQFqㆲ4p@uEݺu3 U&%%aÆڲeʗ/d6([va2ԩSU^=%&&jÆ P-4vX.]Z0a/_nضm4n8I5{l۷OE^{5-[5w\}S-a8=))Isٳ5{l}JOOW:u4|pխ[h4;ڱcn޼)\rnjhѢ Sxx6oެEƍ{꥗^RVV4;GF5}R~W}'#{{EҞp$*]/^ X͚5m۶իy9sFz5x`= .M*88X}QTTUpalRFRݺu&ZbN?Gƿg-X@2e2335|5kLԳ>} 4vʔ) $+00P}ܹsխ[WvRժUMqFSC ܹsu}޽{UJioUF ;VEю;ԴiSXB]v$Yfjذ&O… kjܸ?P݌3ԭ[7լYS]tnݪk׮O?Kz饗`2f@8|||doo\ǎ{F}QFZj>s(QB:vh =zg$IQQQ?l٢M^35j(iTjU}WYc|bSNIZ$?puQo~ ^xa|Ɍ;6lMz董>:[.?g]V馛rgdРA9S2o޼ȭޚ &,@6Æ 9眓AeΜ9swd„ SNf̘n-W_}u9\veYxqdv}ݗ]v%Iw,^8wqGɻHkvYgڵkW_~̙$UV훫:_~y/ɔ)S$s… {.uM?LIIIػロ7|7tӔwM4kl'i&fܹsdɒws˜mݶlH.]2dȐ\~I)So.;2Islf9#ꫯfɒ%9#r%+[{dȐ!/~Qv$D9բRJYdI'pBvu\|Źҽ{l2IҢEԭ[7oF٣ٿW_eW^^xᅲ͛gڴiKYRZZƍg5kV>쳬־}ӴiӔdĉK} kZjU8K}yWҡCp رc.䒲ȑ#s'Im۶iҤIg:~ʑ~{zY|}~p e֭[+F~3%\z*_}U>t֭B<0C͛oM6d@1QѺut!G5\oln]wM:ur5/w݅^0aB+|nvanݺ~Iŋ .˭Hݺu~eȐ!LwygӧJ?uڢW^ٳ2VxnРA>$[l[oM~g9蠃|}kݻ/u 70~isf6[֭[wMiiiUVK]WJl香5kVdѢEkI&#uSOeԨQvilv+<͚5+wb[ʕs-dʋ/g)))-ܒz(>hԩѣGO>}2x z7n\~r_}U ޽{*q饗iӦy睳N;e6UW]UaE]}ݷ[u#VzjN=eΟs9K7*Wni{5k,#FX|˖-+\V[U8rijժ . \p` rs]Ϝ9sʍiٲ Qrwq9ꨣңG4k֬laÆҫW 80ݺu˂ ?!ύ7Xa-2^ziu_ԩS\uUO}j׮].<3ٳg/^/{srI[o5?OK/L*Urme0aJkA9ӟ4f{Wn|vH˖-ӳg4hРukڴiSncɳ>3CfȐ!)))IΝ /I&ev<1bD.T^=:u+ 6໽k9HZӦM3pB~`]vٷ_{K?3*5l0wq2j۶mew9;w.7֩St[3&IFrW,w]ƍ3lذXW:N-ZȯBHX{챩UVcn#vmSRBXk)G`ԩAvq㎅Xm(GEc\qP H(GEE9(*ʑ@QQJBK5jTc:uj# j7~?1XGx6PT#r[ε^[P6lSD9VЇ~z+իWO۶m g5gΜ7n&M CƍӿB`ڰt!z*tڨQҡC F*t4H89AիW/nis… (6N~PrJO^ѭ[BGr$PT#R`]geܸq?~|fϞ>,5V[mC9$[N뮻$KIIIƍYfYfg}V~>hNFe[{/w}w&N9sdɒ%iѢEu떃:(իW_ӦM?l9Cp~yG뮻[wQȒ%KrWgȐ!yG+"3fȀ$/RRRSNKZ2gΜԮ][_O>3gNZhѢwy9rd> tM9rYg3HJ*4iR M74;CziӦ%I6lB  ?/2}͟$I&Mr!}SN̙箻ʍ7ޘ{.Æ O߾}3x|駹ҿowޙ9sFׯ_{/={ɓ$[8lꫯk;ɓ3hР<裹[,5r!6mZ5j={r$Nd٩~93f̘\wu-G92IҧO4hРl?ɓ'g7Θ1cҽ{ <8wqG=،;6{νޛ kg͚YfWU.TVmUb Xx5*I2|\z|#d]vɛo5"'Og}v}3q$_3e]'x"lA~#M!jժs/sG@A)Gjv&Iw38cW[o5UV]|ΝӶm$u]}95K.n.\Ç'I.lVԩS 4(I&K]BXmXΝI&%+jM6[ne{lN=z~_G}[o5IrI'{衇2ԭ[7[L~z3ggMשSgz]vYo=O>d# > 01VoNVHXz꩔fϏk IDAT㕺?2ˑ}`~/~QnϢEҼyp2%Iƍs7TM7s=WY^TTicո{ VEeԨQ%#a5;wnU-Z,s^z9s7+WꫯrW'I? }$w>mΜ9JJJ^r$F-Z.oO<^z)[oudر9sfWc=u5jHn:JJm;]CPըiӦI>A>J V)S||NҮ]5jT$ȑ#$vX6p }s"efr׮p~z~:kVZ:*s?Oc bBG QǎSZ|;z]c=6rJn 6,3gC=$9餓z{$׿?pʥyw3q$[ock֬Y5kV}s".*)t(fkξ$⋳`G&MuGqDjԨ?83r,Y$;wokvl/rk߿z믿^1X)GjvEeo;,-SNGB{׫W/{N\y3fLeK/4*Uȑ#s/>CرcSJ*Tr$f[lE?$It9?xu馛ұc|֭ ߿$3$͚5ˁt=g}v,Y~ey*9sf: <8I2rN+ P:mf̘6lz4h Iү_$ɉ'g}6:u[lw9 4Ȼロ;wn䨣JC->;v[o^z)IrqǥJо Sj՜5jTnt)m۶_|^{-&MŋSr :4 X +/̌3*u~ҩS\p;k^+f7E]c=6O< g:蠃K/ze'I.OJr疝"no]wur$޽{k׮+S#ۖ[no=#<3gfiРArn\rdӓllY$ɡ p$e]CeΜ9yG3{|iѢEtGzΝ;g[~cԨQYpaի#aծ];k^%{թS'VZ6tfҤII~LM4aժU+jmԨw`iJ ~$IΝӡC(K:ux|駩[n䫯'|R_/LZVٞkѢEcB`SrBG c1ƍK3tЂ'Lv$=\Zn]67u4mtkGJ`ͦ 9rdk\s5/ e϶n[ XyG3sn_C=ܚ)SI&3gN^|4j(]tIIII><Ù7o^vqǴiӦ캷~;%%%i֬Y^{⋩QF:wz-5O˖-.קHN>=ygRFlyg6d֫iӦSN'|{, ,Ȗ[neiiiz̜935JN*K9`2rziذaN8\yʑ}IƍK/eˋ/7<#GLϞ=S~O<7I 6,*U;3'fvȇ~_=~8 yL=zG/H޽Ӻu}СCy4iҤܵrH~ߦgϞIk6guV6d4j(>ljtԩuSN!z[o?M7ݔnݺʷXmuĂ 2ztII}_ɓ+3gN^}Ռ;6zT:}zƍ¯o-=\ƍz+[mUZn޽{L>=sO^|\q9K/=Xs99 ࣏> Trlٲ1ѣGgmvm$` r+7\nmSv$Is'G-wc6mҵk_\a+5\?lRJTTXu-^[s,moSrT\9_~eoP=/;S*UҶm|K][Z.+:k;~0B{o#r$:+R`3` <8UV^_T(OӲe˼;+_ʕ7,H S^lֹ;=;y]weM6In3fLzUy 7t^/šk׮];l}r$v_e}x3a„3f=zH͚5?9?Ͽ׽yuQk$I~~+W$3lذ.^z/^SO=iC!-Z@.\f̙e#<2W^yeN9 6,5jȒ%Kr뭷欳ʿ5Lj#{_~~_[nIӥKOo]&Iάs=W[%Kd7n\fϞzem:u~3gmݖ<}Yԯ_?{WdΜ9{N;emN놿QFc>|xxB(7|;O\veK c3ϔÍ7X5j=أ1XIիW/t\:+G4iRc~\Jfm2{Bޔ#a 0dȐ?.t G?QzB^F;lܒ 80W^y.G{챙?~7ov?̼y2lذvaiϋ.(_Zhmfʔ)g}[g}rM7OΠAҼy֭['x"IRF?>Gyd6m:gN͚5#[^Xsp b-fU뮻;K.{Ҷm۲r}ݗ6h#};fG/sggԨQݻJ9{NyevV[mUH'x"Oglv+'n}SvB` v-:Bխ[7u-t VºXYUԩz:+f͚*U^Re+٪Uv-Zw%/ܿ;gɒ%;vlׯ_n~m͓O>Y7\rUV͍7ޘ /0/Bԩwܱ_,o߾m&O?to_~=4h_y3{G?Jv2{r9{?'O·~6,[neϟ:l]&Mfڴi6mZZl6mڬ8#LϞ=+?oO?{:dVZ''|2Ojժ}4zlV{.천vi9s &@k֬rTV-n2WV--[0ްaå׼y4o|sիWO-oժUۮ fKYJ.ԩGصj*ZZ꽀6cƌ c'N̘1ccFҷot)Io;3f͚{]vޚjժĉzJe]ڵk_j!{wνޛ> קXΞ=;+nʔ)sf;wn> 0 Zʙg%G'|2UTIΝ3p>;60`@zKygX?>/B 70۷φnƍ}$ӷoߴo>guVvywq9ˮ׿N;-7pCcw̘1/'e]>sy+5kV㎔}iӦMVZyL4)lM}ݜp 9ꨣOg˴iӒ$ڵK ҤIo>oyڷojժUV#X% J*:@yWʾ|ӫWb-[dhҿ$_yueN=C;fwρp'NXqXpaO]wu?'NmV/ȡgРAe_~9c2a„vaySFkkM^H9Uf͚PܹsSjղkժ{=:}l?l|K3zaݗZL?IF*>h.\O?_ƍN-V{BQ( 8լYr]we̘1Ko۶ml2[n6l{.?OW:wɒ%zݬYҪUT\\FҠA[ʑ@QSehҤIc{{:(#Fr{w>([mUԩ TXG}}7n7|ss~i>l+`-SR̘1#K2cƌ _}`'+-Zm&_ u9gy睷gΜw1>hUVޫI&7n\8L81L۶mΈ#2pwqI'iܸq9䐜uY\rƌ &f͚˼O͚5s 7C믿}7Irw_n89Ȕ)Srꩧcǎi׮]wpuw檫Jψ#${ow}Lqwݻw^~v3ȼy}(F}=޽{o裏ҽ{,^8OݳhѢ$_|ݻ?.+X,t5. 7ps[ouZl$i׮]6d-2mY^{m;\wu{3bĈ 0 ~{JJt6m/7W3eʔ޹ꪫ{Nȴi2v\r%TR$<@5j}Cŋ3ze{֭[7;w.w=zdĉ9sf?'~xr.-;6h`MH(K,Ʌ^#FcE]ڵkW_ͨQr箻ZeМ2eJN$y3$IM;/|[w=ڵ˩XhQ>hbcƌy'4 IDATdɒt1lIMuaÆ1cF_T^=Ǐ… өSl*--?7x#nav}g} ʕ+'IΝ &䫯JҪU[oOiiivyl>}zj׮6(ofjԨFnܹYpa׹xL81oV5k.]d+={v,Y 70Ǐv!ڵ[j{./R֭;,XӧgpSF4id5#<]w5ժU[e{Λ7/SL6m/SZZ/2Ǐ/{{iiiƏKŮo߾yWsǦo߾za\wu˜;>Vv 4( Zچ ꫯ0s۵knšmػ*W*IHB !! 5^B"EzU:ҋRC/R("$A&JEJͼ' H9~b{fܙd瀹[111cǎ6I&c_#  /R۶mThQ5kLWŊm۶gB_ƍuƍE*W$v߿bŊ)$$D5kTvLƾ}ڵk hѢrqqQjΝYڶm_NNNڲe䤶m۪G8p9޽{ՠA]zU ɓ'uI5_8{l-^X'O|M9;;k3f-dɒsΪXN*oooIҽ{ԥK͚5K 6ϫ_Μ9˫K.ӧϯ^zY=sL/;w֯__NNN ѣGk֬Yf i:x9i*yСׯ)SγgϪQF:x *wiӦѣxEDDhݺu*Xӝ;wTdI͝; G~NJSBԵkW)S晌驨(z||,'`0a9aÆXbԩF9ssΪP?>+^‘+رc>>Y&O͛7kʙ3ZjիaCڻwgLyyխ[W[nU͚5sWZ͛7OvRdd*WRJ[;8p\]XBu^nݺeI)S$[N35jbbbcs+WqjѢ͛'Iٳ֯_ݻw^|YM6ղeT|~V.\PJk׮fm޼yQ6mڤ9sjڵ*RtҚ7o6lw*::Z ow!EGGS={Tݺu5m4pJ6}L8QeʔŋrJdɒٳe{g%{z*UUp+&oooݻW҃>UVIzr~TjUIٲeӠA4`lR3e1cF̙3S\\V^ ,Xñ-_\m۶w/ÇuIeɒE)Rѣhp$3g*T?^zZj;vΝoVK,њ5kTn]:uJNNN:tFAiرs>3)RD+VP޼yu13P(ZҲe4x`/^\gΜш#rJ[Nzu\R۷Wڴi%=غL2Ybbb˗N:>|ڵk'7oVΝՠApmM:MJHHбct1-[L*Zcӕ+W|g.<<\nݒ$͛Wҥ$o^ P׮]s6l`U\Yu5W2dH+Of͚sՉ'l^^^ TR4e;ta^g#W߿!0ի\rJ.:wl7|UTLԻw'kjҤeDuvRΟ?k*&&Xtt2gάQ͚5 T"NZ.]z1c7onNSFY* @;wVƍ5o<,XPta5hhݺ&O͛kĉfoܹOIϒaÆu֚7o:wׯܹs!>}iӦ?8qB5YV-3/_>IҢEtRsϯ{2d<==ɓ'kРAڲecիWرcU~}͘1Cӧ$mܸQ5kT~sSJzA\2gKnܹ[{$CPhС իxkс4a5h 3Fϟʙ3 eΝڸq}z_J. *͟? ,\~;G^qҥȑ#U~}-YSǎu}ڷoh˗Oӧו+WԸqc}ǚ7oիgw}Wahԩ;C]ֲ>Z___[ݻ-Zd/˗^'ҥÇ"##)S&%KLGѠAlٲ'+yZjڴi 2(w:{+]U t]ծ][)S߯.]X+WWW+WN2d~W}+>}xʓ'._,IjժV\͛+C ɓ'1cFXVtvvַ~N:)$$Drҽ{t}С-[/TN汽V\iG͛7Wǎh"KoZxmȭA޽?>} .9rX_EؿfϞM85i&{L8p$h۶T|yըQC+VTʔ)o>M> uH-nݺիھ}߿E[sQFO+VٳOh*RJ*m۶ٳg5qD/^\{y&LP TdI;J:V\͟?\x\\\4h [7o^aÆ`h[j&Md>}ZRUpa9;;+..f D]uD͚5S }v%$$(O<޽{syyUTTV-˹{V֭{n%KLEэ7t]@-_\ǎS\\BBBj 77PDDW_~F{WIn\r=Ӏ)ܨQTF ϟ_ 6Tʔ)ɓZt>]Uf6մiS5nޭX## &}]ըQC3g_[n)88XƍStt|RLٳg+M4Zz=o{jɚ9s|}}UNj\Adɒʚ5+JzUwTtU/gW oKz^zYiҤ͛5c XBׯ_Wxxva[tbYnZOx_6ۯut%y]) <_Dߊza$www(Q&T0SSLy!!! yII}GjԨΞ=q)&&F+WTuAM>]cƌQʕգGݻwO ,P||rϟ_}vEGGf͚ݻݻy@Zz#C" x-Z-<%K?wke…ڷo֦Msڵkk̘1汪U*$$D;w4UC5nX{ajԨ>uqҦM+gE_>^ǏW@@ݦK.)..NŋGK< b6l## ԩS6ۭ=zTnz\vMΝ{f͚:s挖.] ]vZji˖-Z~0ԢE fziҥfѪ]Y;tΝk3Ƹq&M(QBŊMfsިQTr'  Gƶ*TׯR1c"##_h/ ,ٳrz_g6!<nnnjӦ7n(eΜS}R*U)SeʔI?6mI_jWxqmݺUÇWvRJi߾}J&Ϳ{x-## Ю]5kV={Vk׮/It \{ݿ_/6nܨO*TP)yx[NVTT\9Kh… zdɢj͒%K$ܷonݪdɒ)22Rx55ǧLd=ӧOy=tE*66K*RJYjъ~d9sf}x^hh&OXpfj6l#sw+_}]+WNK,ф  JN:RJݻ6׾[Z~$*Q5je˖i„ ԩSz+o޼jѢ/_ѣG+$$\7ot6m(22R_̙ e˖sy7T^=)SF}f͚ܹsgϞV ݆ ‘pd wnF/ۻg?-)RANرc:|<==%IfRV"E(((H .TÆ 7oެ'ON:wVҥKk̘1fHdǎTҦM5kZUUTIM6ՠA"IZ OL IDATjW URE|rEGGȑ#JOV͚5գGM0AԪU+]rEG1ՙ3g-}#GUZm1UZUS>}݊H82‘K`۶mniӦ6ZݻWt4Hk׮UjԡCM40aZn-www͟?_7oԸq̐$(P@#Fm‘_|ҧO!MJ={԰apfΜi٢;00P3gT|Կ]rE-ӧ-3j̙*Wzi<eV" GF8ĢE3fܹsӮ]/_>oZh:$A͛ڱc,۷|`d+jȑŋ'jxxm6]rEnnn_ݸqCp$ H$YV{L,Y2ݹsUV4iLSJ*f]aaajӦMwY񖚻9[z}C{w\\\TR=6mژ+eʑ#! !CGٻ $iС;4h1cɓmcbb4a\Cu>>6ǿ+@e|///M2EC Ѿ}!C~gŊӍ7w^ri:sYO6˧u֭[f=Gڵk-c(QBW^վ}̚ʔ){ܹsf=}ʓ'֮]۷o9sOׯ?C0k2dPܹfݽ{׬ʕK5<==UT)ڵK.\03fTxxV^ͿҥK~ÇZUdIرC.]2y敇lblٲ:}9b6={v?~j۶mzu$߶m̙3+K,ZbeGͿ8;v̬LREі-[tWP![$UXQ։'̚ *H"{ve֜UBο7f=k֬ К5k,c+VL7oԞ={柿ϯ͛f={J6c?777-[Vٳgzt7oǞ%K˗~,Y2.]Z{l"E mذ2vRt%{ߣ[i&IͿֿϿ>j9s[ߊ+,[}}}UX1Y̿G=z[۵kHlGxnv8`H9՟={ۻW¼y I3|p{Əoya[oe7m0 H2_;v?~5jjaTVRoժae6mܹs_K-GaFŊ-a f0`/_>0 L2z׮]x1[cŊ6ׯ}Ԋ)ba(QRիq-1~Gcҥ6waa,XR߿q1V^m,ZȦnѩS'K|aFܹ-.\cFll,Y20 m۶ztta5kV O{̙֭3-50 yz 0`K}رѣGm޽{gYjS6 05jd׭[0 Ȑ!>i$6cz1fK-s̆aF--ZaޖY[ڌ}cذaZl 0ʕ+[ڵ3 0-Xcݺu6c_x8p'O0 \rzΝ 0lXxj*+W~Yj 2 0""",={wܱ7~۷R+QaQpaKo߾ƵklXb7]Zje˖5 0kƥKlXv1|K0 xw-J*a9rԇj={f͛7g϶Ԓ'OnalRVab=8~;v0OnJ0 hڴ^vm0 #cƌ Cٌ~cɖZt 0zY 64 0-iӦ{ȑ#Ƹq, 0 f͚omaZ_~}vO:e1R˒%aQJKM6aW_6l-p0 B zǎ 0 gggK믿6~'O? (`aDFFZݻw7ݻg3ƒ%Ke˖o޼iR+VaQhQK>0nܸa3ƲeˌQLL3f{9 yT'}U+W.zKw1_V\iv^?w;xB7ŹsKoі駟_={ػH:nx5kڻH"Ւ/W[X C=˛|gʑJ͕"E ˊ{nnnc?YO2$iƍ2f(҃հ~mU\٬K&NCTRImfY].00P6cIFZқoi֒%K&I6me3???IҮ],+.f)R}SY$͘1òaԩl3Fppl^^^ҥ5jdIRLLe%5%Kf̙30 {=;f-y䒤[VIL6m Q||jU>}Զm[-IZxet)UT6c*wܖ$飏>RN̺$ם;wzm_%),,LYf]\\$I R=̺$i2d dɢL2xfէO"E IY. @ɓ'Oy7h@K6ko̘10`YO6l1cFyzzڌf͚bŊf-qM0AC 1o֭`ڴiղeKf-qM:ղa۹sengʔInnn6cJJڵSZ̚$/ԩSKԩ4h`ٳ-s;M4I=ս{w5k̬%XWs;!!^zUVf-qnז6mZL2'9:X$}w5BCC={$uͬ'5”9sf-ZԬ%οիYOVzӫTRf-q5J~YOk׮}&MB f-q}'4hYO7o~>}z5o\UT1koIο_~_ԩզMըQì%ο?V?߼Lu{o}ٰ6<{Gp$X9m<͛' %p$HvUmٲŦ *ʕ+ںuT`Gcǎ)[l zZn-ggx## >+*$$oWWW:tHTŊӧOc5iD6mҘ1cԵkdɒ ׬Ys޽ƌc6Z|3)S&>}m7nT H82‘kru}tΜ9f̘Ν;'y|֭ڵkdZ F8pо}{?^ IOԨQ#Nwp$+GI&۷J*c/^Ԃ n:uNimN]|mDm۶ D8p$@V 2h̙Ж-[k}HڴiO>D[nUڵ1cF{mGy5{lY۲e G9ۻRJDԼys/^\J񑇇0~xx #cH?~=^P$O0!/_=̚5Kjذ_ p$+Gkp$Hx #c[mO`ʜ9## L6-^B׮]Ç`.]:ڻp$HOI&  %/lٲnѣFi6^‘pd#<G8 x8p ݻwuС B8#}Z]tw}իkرnɓ'J(a6`Ξ=%KػH8‘X=TV-m۶M...O?ӫ{nѣի:#G¶XTTBCC%I*DYdQPP=[cSŊ?SÆ %IR:uc'NPj՞-Z>虎 mGxbNNN*Rڵk۩#< &h2d]e[NtU7nh~'Oݻ GŋZbݻg;wN۶m$]pA[l1ڰa3߱cǴwg:&F8һwoM|9sflҎq\~]_|͛]viǎv'<<\˗/kG>RŊ`Ŋ֭$Vc۷oW͟bbb4lذg:&F85kVcǎ8fΜܹsL2j֬ƏosNÆ 5i$-ZT... ִit=S~~~rqqQbŴi&aÆiܸqZh .dɒ)eʔ_>d?'N{'ITztm.\X׽{Tpa]xkڵǏYfJ&ܔ7o^}6kĈʞ=\]]={ڵkO<xW˗5k,u1hwD\WWjմyfm۶n0J< IDAT ?^}$}*T &Ԍ3TtimٲE-ZМ9su)}6mԩSԩSZjܹ#G*22R/^Ԕ)STxqYFyt ڵK'(..NM6Ք)S)0}$ۿ\"ػ㯴$DJH%ٚv =#c2%FEvꋲ.RI%iѣs熢Qx=syv9?cbb !!!T\ .0n8hܸ888͚5khڴ)<`٘ryʖ- HAƍiժ ,@WWVZI볳 ‚]vC GEEp8@FFtQTV *g޽&ݻwG__?xtuu177'== ]ƛ7oؽ{7*Ue˖:t>}}8pccc*T -ѣаaClmmi'OpIlmmҢA?_f000Dh|?1cгgO֭[!ZZZٓgJ 3(w?NǎQWWI&޽+c Bq'#AOYYYX#FP\9Gfffҷo_LMM9rYYY,Y]J7ôiӤmgϞ/B عs'_4oޜ3gӥK2e ޽3gΐ,Kw7߿ n߾MZZK.QFR\N:3gg_    #,^;vu"99tZn]ԡ.0i֬L2cݺuYj'NDYYYZ[i1T\Yf>?Rzuc95kWXOG[=JDDܺʕ+HLL А'NȵmԨAAA̚5cA #AO$++={97&6mڔE]u ___̙)^^^DDD`hh$'6oޜC \ٳi_7oޤ}ԩS''o߾8:uDEEFڵڽ}z|     HA7oްiӦ<4hf",,-[~ձ;F`` }2vX\\\(SLټysd dܸq)? 4f͚&SeXXXh"5jTA(~?~Lzz2eee􈍍TR TTTPWW˗(**|233ɓ'TP/PƋ/VǓArr:7##r}~%#A?˗/_~?^n]߾}ٽ{7 ><'&&RdIje~^륥]%%%PlY233IHHdg(_<}=5 BQ9y$;w[>vX233ٹs'>>>ܾ}[2o<bbbXb~~~j|l۶H]6vvvXZZ;f͚E>}dWVVaaa̟?RSSپ};޽cĈާ    |J@8s T^_m֭~4QCCGGG/_Dzaȑ888j*̙CӦM)Yw닗7ndȑL2E& h֬Yԯ_CCCMƚ5kpuu WWW]ڵٰaݻwÃuM-8p vvv_PASdd$/_*_<=b׮]<}i/^9Ka sss5jƍC__///HHH(~wţG(&͛GQPP -- ###8SAAA>ETAlϤI ";;QFa`` ^OOӧm׫W/bc]sQlY|}}D|5j0etuu1c]v_lڴ#GPvmΜ9ßI PQQaϞ==zrI֭KDD<|PڟZbӦMZjSȺu޽ZjY[9r$AAAl۶ iy\\sȈϣM ܿJ*0{lt$AA`Æ jJjҥK_]۳l2VJrr2-bΝ\tSPP'}vttt]f˗ӣGJ*Ő!C(S 7o͍RJIٰa}_~AOO7o0w\vMhhW=AVF <<<䖗(QKAD0qDBBBDOOOZޭ[7F+vvv?~*HT||<۷oŋhiiLff&&1   ?)ׯ_϶Y&GKGGÇ-[ԩS?zU2l0iNرciР-YjVVVXYY,ӓ8Q>>>Q6OOOʖ-+,..K233 zҀͻw8u>ZjtIfÇ(**RD .\@޽D@@aaarXle˖eڵ̞=ϟ[j֬)6))Ga``@DDo&==pʕ+6220J( uԑEEEҴiS?~̛7ox5gϞƆ7nȼGTA~dffbŊO&ШٲeWۛ[nQn]ʗ/O||<;wN\ ~gCSST~g߿/133ȑ#7I&NZZ'NDAA۷o9fGUU$uƹsXW=Ao%66GҥKiYvv6#>>ׯ_ӷo_֭+M}--ZDhh(Kcǎ?^̙3GGG̙ìYh߾qoݺŪUzLbd.eee֭[IJJDm6J,)vС9`Μ9Ó'OX`6mߟ*Uʲe8p^N: <~ ?5k֠( 6oޜ={@~Ws-]tx:wAAA(*LAAL4֭[3~xiٹs;w.<@YYW2c lllx"hhh`hhȝ;wpss~ ___|2&&&y&GL8Q.1Cϗ~ߴi2m.^ѣv$'''͚5cݺu$%%111!==#Gbggʕ+QQQaԩ{055ٳܼyD9s&={ڗ\(""9RȽ{>nŊy}{xxYŋ2UTTXl .ɓ'hjj)GGGC>}u;wsHbb"qqqTZҥKӮ];BCC#11===D˳vZ~|2˖-߽{K.Yjkff̙3JA@LfflAӁP^^^\tAإKĉLpiӦSdggK144nݺ[~5jݻwuǏqW߾}9r׮]hwH||},.33, gϞTV3fpΝ"VS^=~܊^_iӦԫW___={VT ϗ6s+%gggs%6  )  ѩS'oͱ -Pgɓ',,,JFtttk㘘p]nܸ!ɓ(++ӨQ|-|DrȈwޱrJڶmK͚5;w.<(A"ĺu밴J*L<9DAxyy䛞ݻw9s&UVk׮~ƱUUUVX!nxzz{n F^|Y W'OiwϾdܹrؿ?}WU=7 ӧO&MXl/_a R?}9;}ttuu" o~0lmmnMOOgϞ=t ---<==9w\Q* B1!#AB%y_t)r? 8RA2d1113F}=^|ܜiӦannƍe^z_IɑVVVDGGT|2VqUƐ!Cׯwxx8L6Mf:pK$G*U)Q"r?&&j֬)6m͛7E tܙݻ@ll,/aÆ4k֌_~E$ ϶IOO'33cǎaoo&ĉ|XQQfϞ͠A8piӦkkk5k9|0'N~$%%sNaԩ5_3f <ԩS)_<-[dÆ h"LMM>}? ov T77Q2**qQjUlmm *aֱcG1Sgee;?x[1׬_SSS Yd|* ?oỔMDD.]"55իcii>)))hԨ=">>^cjjJɒ% 5\n"88EEELMMiذr]FF'Nh'͛7UfyٲeExӧO9xrcccjժ4hlݺWWW`ƍ8::Rt"\(ʗ/ϩSpwwG[[zC 9u*U*M!3f`̘1ԫW8^|ٶmhhh̙3QTT$!!#F}Ogɒ%̚5 ###TL: &|ً!|wDrkذa̝;WJxp.\ӓ={쌵G'PVs}tuu?S={F:uPSSÇ(**R \z>""ʕ+KGxM6rx}6EYl2>Lvv4|5ƌرcŅݻk2PTUU]6gSSSٽ{7ժUcРA8::RFBQFNOOOXJ/Q}f͸z*RlYLMMY~gRRRb常uV|}}ȠzSꖣ#jjjlܸ-[P^=8-[ё===qҥ9~86m?Ԯ];v`ee%P...21ڔBSSPU&Tfff~'oŲX@͚5QVVld?gw۩R 8::RnB-**թ_>ZZZodjjRCr?Oo޼Ʉ 0a:uɉ޽{)AAAڵk OOOlllDrd̝; *Tl;Ο?͛7K߉k׮~;Z*GѣGPvmD??|}`ccÇAMMڵkS\9=zɓ'\rL7o"#FڴiFnm%%%ΝԩSz*4iDW.SNRRR___,Ϟ={X"...899ahhXT!۹x"EW9|0у]>}x"ښ Ƃ a?wbeeEϞ=qrrYfEҿ֭[ۗϟ~„ 888#}qqo'<<ÇsΝ"U=zT@ّ#G8x ʕ'''QFE¿"jÇ3g3gΤqƸ1pNԩS @__yIjԨ6:::~xժUw|-ZEj۳gOz)l…2g͚%2xxx|tzzz,]TfYСCb˄a$7hҤ Gs+**? S/X9sФIr{L2ׯOrr27,]jժFwL24i҄|w}xBpp0Æ _~8;;cjj*A?J$G @hh(VVVL>ѣGT~}6ܺuB9o޼y)QQQ̟?pԩ3h޼9iӦ#/^K. uǏLJPhݺ5K,aժUtܙEݺu$ tڶmҥKiٲ߳h"6nȽ{PWWO>T_bÆ ƍy%KիL:`iѢӧOk׮2۟9soooΟ?@˖-?>;vM60j( vڵDEE/9cӦMw˗GÆ 4hhkkP8 6ԩSܻw/Jܽ{5kJ˚7oE5B{^z <W>g:t(u m6jԨ[WΞ=+W333ڷoݻwM6ܼyiҤ ᨫM9Pdd$Gfԩ"Ln(((2zZK,aʕ*U *r턄~Osܾ}[.)KuޝQFI}ÇiӦ 4P/k׮Iς Xd WW^Rcbb"nݚSNQ~ ^ŋ<Ǐ)))l޼7Rreqss^z  ŀH۷oׯs̑"K__`Zhƍݻmmm $3ЖJ`` ׮]CCC;bbb"wE/_k.߿zf͚Ҵŋ@NE *tf>}`RSSiҤ }9͛ڵ+GESSnݺXZZJҭ[7Ur6+Wfɬ\ 0h lll:gȐ!̙3]vDPP*UJzbccٸq#fݻZN:q*WLVVzÇ_-[#"$tjlݺ]rJ,)%N2Sti9;s̑+AAA8::Ͷmۤv(++ӠA '5D} )))ҨQ#1c:tܹs.]W^rJr ʕcO\xƍ0zhۇ?ffff:tÇi۶-ZZZlٲqI1]| .k׮/~M!CɱcǨ]6m۶e„ 4%J@CCCn x+;۵kdeeadd)_M5022bĉSR ckԨʕ#99msnܸ7n:tŅ>}|7IyϞ=|]]]lllx-W^sٳ;ݐr={&U-&880ʖ-9R8BCCi߾=vsTZ{'+oijjҶm[VX%K/_wwwe͛7GYYgϞq%llldnJ ASSS:MOOСCܺu w.S޽{<~zg\\\m5oTRZJ:sSNeҤIꊭ7[xƍbeeE  Cs!x!GEYYkk<+Ʋ~={FժU駟 %Έ̤e˖rҥ >}UUUzAڵum9ׯiܸ1ݺuΝ;RV-N<ɕ+Wx)w-Ҕ{. (QLŜ&4RRRDZ$&&“;qҥK̛7ӤIu,XuuuV\)̑#G WNHHHޞVZoQti:۷133dldm###߿Ov8q"'OP#ܹs/_AF|2|rfΜɡC1cưfRRRҥk׮-OLL,Cpp0jբ~L2##3%%#FvZ)LժUJe*@Ι306nԩSٽ{7aaa\~]M6͛?޽{e=+_<6lB oaØ1cLҠ=5k;;;ʔ)СCYt̔o N:2@tt4bCCCԩ4jHf@{\~+WzjBCCY~=W\Ԙ9s&;vmၯ/cǎ:11kkkpppt8p???>Lblٲuҹs篊qʕ۷%%%Ipu_N@@5j(V炐+$$Xopr9%::'O̢EI,?*߂F#seggKN>ͩS'''S֭cѴk׎sI&Oȑ#ٺu+7n`͚5ܸq3gl;l0:wLxann۷owެX%%%n޼;ڔ)SSSSŋ155]v9r䓕{033c2{/_dܸqC&9rܹstSDž ݻ7@N jjjpU&NԩS>>L0A͛1bڵC__3a„O!|qww?gϞ*U-[ɞ={\2ʊǏcjjJDDSLa͚52ӧl2\2cnݚ&Oݻ{.޽.]Krr2[FGG}}}:u*'N3QG>7oW"##3f 'Nw899aiiTWVVfrEmdggK{w}Wٽ}\___/ B[[(Uɑ Z*7QrtWWWzꅙ׿vN*yٱcGٳҥ W\ASSA,ٳghkkj)߿ӧ}Bo||>-Zs_qpprdܸqʦM8q":ubԨQl۶M dȐ!T^ZjqU222عs'Z`>CJݻwgL2E9s&]taX"_qݿ~j*ASSS鱯/())j*ɤIx1QQQRŷ9s0fzETTѢˎ;k0$44'Nx@իW|08==[nq-N8vɞrye#sVP-ڵjJx޿? #?4v&G6h;;;TM4#?~?ؽ{~RJ$%%١Ժu'߿Fqau5bRK^}ڵLU\nnn}e̘1={˞={(ֶ/^ŋDDD~zH~%''˽Wڷocƌ۷,YӧSR%FAJسg2|pΝ׮]akk˼y ?۷/ߝǒm҈ܹs8"AX)yGC>|Xa97,Fd6nڵkVnnnRLqqIFԩ|IU<<<?~<ӦMoܸK~Qn]BBBi LJ9s9''Ofdffe"""000^z\rɑM6Аm۶1diܹ3ժUs;~7LLLf͚899|rjԨ۷o֭L6M~wTVM|t^#F!,ٳg:::ҀoaNB׮]g…ɓ'XYYk\vMz^pN:aggG58}4Æ c2UO8Aݿxӧs ܹ#]gdd0tP˙3gʕ^ܳgR֭[ٸq#ԭ[\p!3fɉzk./_ǥcǎ3r>?~tjӦw9ܿtvI@@ZZZ888$|o:8;;[JEEE(Q=z`Ȑ!rUnvYz(++{j׮qvv- 3g,_ooooٱcښ8 $33[[[?dܸqL6o2e6l؀"ɴmۖe˖IlA2eرc СC$%%I3={???ZlÁ#Gĉ=K.BBB|2jժ tttXp79VA<}7Kзo_رgǫ>Jvs <ooo?wTVM*Ņ qm۶aaa7nܠL2ddd`ccܹsY|93ի 1߼y;={Vz޷o&::+F.]ѣ ./\@n‚_F-[puuߟ+Wbggڵkٷoݺu… 8;;@Ϟ=syѵkWnܸAŊ$/_:)Zj1p@AAQxA䤤䫃$ׯ_SF nJDD͛7gΝ@+WI;F;&Y)k׮\I5?'+n:e-QرǏKwyxx{у%KB -Z$SղTRTT cccfϞʕ+ OV2RJɵQSSKNݒe}(H%>:eddDIHHrpp@<s噐 вeKQSS-s?*S [۳n:,,,{4̙3YJ*U͍h)))rorSg~Jbb";w֭[r+UnΟ?/UE߿bcǎ}w۴iChhlٲTV SSSNJLL ;v,AN(_*\Ç̚5K8Xx1cƌIٳ'R #Fѣt+V&RT\~=:ub@9#ċ+3k,F;w.(((Uشi'_>c4k֌mJ ;wDMMM3WZXx1~~~RFeРA][Mٳg,ZsҨQ#qppT@@2sb s!CȼWiժ!!!xbF!7oaaĉYvmcLMM_̙32JJJqeiKwL0˗/ciiҥK={ 9m'Of޽GUUUDL4&M3r+⤩,s[eee9ILLzꄇK7š5kݻ7ʫW0a$$$~z;&Um GTTo޼l;222ر#nnnxJWA(,AAAyGMTWW ''|8zEQQLTUUqttё֭[r  "9Rs5jԐcQTTJ*qm:t@˖-Y`mڴ!**S+W@QQQ{$\JYfܼySJTdd$L8Q:K~|Q58z(YYY(++SfM7n̈#>Ys֭۷IHHV'חIŋ8Y&̟?_bVVNM6>ʗ//OOO9rkxiRUs 2Рvܽ{ms?[?~<&Lk׮~TuW^4ϛSyJ7!]rCy, /J{.o߾*5Szz:Ҁb|9YYYDFFy&&&$%%}2+ʽ^zzzիNX>NAA,QWW.nsir> sJLcF[bD$JrCbCW9G9D|abuM&\lvs?mu}>;w_Y-RJS;w~[#_Z%dW4uppPHHbccծ];΃f'?[KHHP"___<~I?YK=#FhŒn>}@rrrq7owyP,Yիk$EGGC^_}*g}&ggg󆠿}6..N]tgɤEՑ#Gn;ojѢk[!!!裏lpppPbŬ+VZBm&.{{{'țo(ܸqղeKu/]]vIƎUPA zɓ'cǎJKK />}U7o6o-Zh;1PB\ҶaǷ+B6m޽{?#9xEDD($$DgΜeu>Ho5n85o\|||6AUUlN>>>Թsl^8w<==s֭[#Gm64ΝSZZYq3**ʢ❪[ԩ+))lې!C7ݻԩTk?}tZOOOũJMMx-ƌ㏪R7o͝;ע5oid&=<=}/^l&G?bbb,Z!fWOn2c.]j3ǯ\g}VfÚ5kSTT|||_~U˖-5l0ժU!<(ooosGD)SFƍSdd]yvޭ xٳguiZ<""";ffeIر>ѣGwq֭[L~~iݻ7999)++ˢ5ZfJ{]gϞܹsUzu?~\-R``j׮-g|ٲe港RJĉVݚ4i={hҥ4h?~˿ƅ^ziʔ)j޼NjUe/''NO?,m޼LTonţRt֭'N4;|XrG} /_և~h$0T`AuM]vͱɽzjokVBB}^аaڪyɨQ,Ͳ?ի8EEE=47^xf]v:p cFegyyhV7}۶w===+WɑիWWvmɟǵdٕMԹsgĨq<`-X@͛76%7sdu`5 ð~fffpuuU@@zcaJ*ڹshZjV\kZxM6)99]B ð|?iYYYz{ΪȻHr 4Phhwŋۼ8b M>]֭$M0AaaaرcpBm۶Maaajݺ~w+WbgϞnתU+ 6L#G:q]z.]Z5kTRRUݞ={}۪mjӦƏ^~Ԏ;.\P5h@>ܔ\̨V6m(((HǏWRRbcc5f̘oծ][*T/_?y?gjJW˗ٳzꩧEEEiʔ)jҤ:wsiΜ9-e˖ر̐!Cwގ"E/PDDԬY3]~]s̑Νk1W^޽M'OXd2ej׮@^`,Uի׫SN駟};9s;3gX%Θ1O-˗/״ia+ Z<ήq?rWYܜ訊+}fddZhw5ڽҼys%%%)**b<++KͳTz)͚5K]ջwoy{{[%-[L˖-yu߾}ԭ[7kN.]o+^zRJJMMնm۴t^/::ZM4Q\\~'(44TfRŊƦOZ_llٲo{Ȯ2ymٲ%7o\:uұcǬ*̝;7UQZl9ssVwZbŊP~7E{CO>]=_?S/_n&^֯_%J;Zjw4QX1Ĩ[nfb{_n#322Ç\ޮ];oqwǨQty988I S\\BCCرC7n>}ig}ǥJR\\bbb,E>,թSj<$$D{Ք)SԺukiҤI6 E˗;In܌~m=z|l //\ukx bvUSGGG{jժU&ըQ#u)kAC љ3g4{l5iDSN՛oigZZE;uyZʪSw_^fʹl2Y+3gu5gU6mTF UXQ֭[()SF!!!JNNV&M6mXl3++K_}q^pl6GQjժ(4@0k,iF7ѣ,ggg=zT|LO>ļk}ƍڹsfΜ]vԩS^ʕ+;*44T|7n,%%%i?m?Xzc)==]Ǐך5kqɓ']vrqqQll<==~zǫgϞ}SXXƌT=S|ھ}>S}TLLbcc_sjOZTPPeO?UPP ,XunT:Y|.]˗ȑ#PLLʼn???mܸQ .ڵku5EEEk׮ԩ9Mk׮ٳa(P@gϖN8aԩ7bŊZrj֬iiӦWhҤۧd*T^u=V(+VB k׮6_Ur>P-tyWiiiz5i$y{{ߵ*4fΜiuO9ۤ<ܲ5ѣ喽 >\S߾}5f*TH4h3 0@gVƍմiSmVfR*U3gW_շ~{[qN̍P5jU_k֝zW^?~|}}uE%&&jӦMO{K.rrrRddsNuYaaaV2zh=*[ $WWW֭/V}[$+GGG![n UY zPrrͤ!һ+կ__񊌌]n|իd֬Y4+7i䡽״iӬ*sΩt?~ԠA9pjRJJ}lذAwլYS ۮ[m7?شi>CA``+֭[Wڶmq)>>.|1%&&j$I]v{ァ={jʔ)q՜9s4cƌr޽{+99Y5$=zTjڴiE(ڴi'K.1c .,IZh +VE{6ox;x|}}㣨(mܸQ>}Z].]je0Z+ >\!!!r劮^~Z_~ ___͚5W_}_~Y/\]]Z܁WP[,ZHJLLTbbŲ!Chذaw?X;uׯ[.]7tchժUlCqm۶X1E]tQ.]n9ChsmK5rtjjլ>,\mU]>} Mooo՛(00PrvvV``~@ |uѺu4|pթSGa(00PK,1Or988Xw}ڵkĉZzb&:tHgϞyP5$ݸvZܹSjѢ/~gffjڷo ,-ZXT8zN<==տC˖-3򲲲4`ի222iwyG<Ս$9sFf%ZjiĈ[_HVo߾}6lyԩS7nkeffGo]رc*33SuՠAsNHHfϞ j…?T|yZkФIxbW߾}9o}M>9? իիW/껈a*SN:*V$ە lGĉ5p@s~kvsdРA֭ZjG}OIIњ5k4mڴ;v^vZ)S<6|P*&$$J*zW4l0լYS>pmܸQ~~~n_rUD رcrvvg}vWj„ |||o>uU&Lq=GGG_~*[t+55,$0yduQڳg&OB iܸqz'UZ5۷FZ5j*W{jժZrenXz|G:y8Ijڴwﮈz% 3?!U;vwCRƏom0`Ek׮ۿxqq뷜n\v-'O4222u<ǍK.|rʙ?"E2p@ߟ5k;Zxx1i${;fK{-~VXq"J.m>ŋ;_~?~!uk֬xYt,Yx猲e:u27onxxx|asNp6?5jd$%%a2ׯoTTɈ1yxw35jd>޵kQZ5jժF.]͛omΙ9sѼys#>>3"##ppFǎ-xѾ}{ψ2ڷoo.\ӧq;իW[Kڶmԩ}wbb--A*Usw 5wpp0 ggg#<^1caG5~'… aFÆ >ŚF2~ǏK w[- gg%%K4j߿?W۞0a;{,_ܨZe!2?nH2\bƍtI'W^5 ???gϞ5jg˗ Iӧ?;v;(Rȿ}.]d4lhڴW_?1k,L2ȑ#gWԯ_ߐd-}Uɒ%?p̙PV@T'N-ʔ)cV<|+wwwIRjj3ϨF]>S64[lձcGeff*,,LM4ĉJX{UfTD EGG[… ULLFeV,۰aBCCU|yuAfyzzj߾}fJh}URRa/\`1̙3z駕j_/R;Jd``zahC…6e9:u:w"E8IUTվ}||jnnnVqkW^նmnkࠐZ^dɿݬR}7*T螴-_ZyW|||cL=Zݻj*ٟܹvjK}QM4bŊ)%%E*TRRR̿aXtvpqqܹsi&XB4a߻\]]5m۶wcZRDD7,YbիWOΝĉ5sL%$$I&5jYi0\%IW6mxTn]EDD(44w/66VzҘ1c̱ RJիZli,XPiii>-[f͚j۶Zn1cU+V0kժM*00Pm۶}C瞳XhQ,YҢ#UPsرcոqcm޼Y򊢢yV6l/О={TZ5 4b-Z=v dȐ!ҥKuQ+)):ǰa,:wdk߾.\pSlY-[*UJJ,:svvyꪆ [}3]RxqnU$l.k]N?S V.]Եk׻Rm$G-[;RJ\_pk#F$h'X\9IҀ,NkN+W֚5kԡC%$$h̘1ɑSLQ||_4iE[֪U믿n9g-ZTo]q1BǏ7'X-^̙#~-ZÇ[+ZÇ?P:+99YaTR{ZŋG˖-w(x]V-縺ڵk_z衎;ޓrJKKS޽k|_UtiXI̸=a_~2 C濖-[*66VaaarsswL%ԪU+ (HI7%/Hׯ\z (+Zݜd&Ffk֬O.I^ԩO?TqqqnnРU .(--M-Җ-[,SP!.]Ziiiԩ9~m۶MWk׮6l֬Yc>| VKVnݴ`nZ .T||onͧzJ)))$G>j֬5kʕ+r6mlӍ:VKnVbE'FY)_ݸqc9%x+((H-Z\ ɑȓfΜi|6[Ue˖-'NH5p@ ӧgiݒ[ʕ+6/^vMwJSOY$Ff ְå>ճYݭB T6o,0.]|ڻwooQ92T~ԱcG5i ÇPӦM]f1ꪫWOݻwW=H85j۷:zJ,ɍcw_|oVҍnqqqԩ]*UhС9.Up:{8!!AGV\\ ԩS5n8I7deei6_@]~bԩS9VvppPŊ_I9v3\RSS%I'OTRRRRRlν 6*H@d+ALhtvvV޽+((H3gTHHyM$www]|ܫW\'yt-saС9^BGrdTV-M:a={/TVV Ð{9uM7&qtt?I@ʕ5bEEEVmV/֯_?SW\1o+Q5| J(a1V\9ɓVm\ ЁTtislΝVېݻw|nܬhBc\VÏH ={;ӧk-Zh 𘒒EM6K/K.bҥK-;mٲEE*FSNQFrttTzzy7nٳռy[YYYrvvVVV7oXkNcjRZې}޴it9GNNN$uYJIIHկ_?8pb{KVppGY,訧z1b4i"777s֜9sW7|S< (`۵kqFܽH"""l2ek,K.UZZO~_ wOaaaС, q+""BsQٲeeya/^DD&L{Nf2&''kҤI͹j߾ϟo&>nذA;wUHI7.`EDDO?U``9r$1*Gٹs֮]S:uY9 o1cXrS+W-nΓw}W͛7דO>={hѢZjh"$i֬Yj֬Znhyyy믿Vrr,Ybq@bT^=_eʔі-[*11Ѽ/!!A֭S:u4p@UPA[n{ァ'Zjzk$G@>k.ǫnݺkgΜѾ}~?:uؼyԺu\m'!!AO-///XӠAo^5R…uu]xQ `{Wᡐ,XP:&N\=W-$GY9RJ;+;w_e5^re,Xry9r7jR ek׮)44TTzeʔQǎ-Ɗ/QFQEXV`AӬYpB]rEuնmTdIs^\\ݠ|ڶmOd]tIu֭[oɓ'kZ`N>ʕ+kٲe`(%%EOV5rJH$G@>Qn]_rqqߕ+Wu222neݵk g[]zUx[Nׯ_$޽fU$mYFٳ6[j„ z7k.|s:t蠰0qB4w\M2E{쑏lW[ݺuӮ]ࠚ5kjހԷo_ܹSR9{#%]vrvvɓmވq/[Nݻ~Amx5l&|3߹+!!A 9ΙD{$ZbfΜƍ̙3zT^=mڴIʕدa5|5h@Gј1cM6L2:koVZիZp}Y}j߾$i֬Y?M>a\ԸqcرCO>N:kҤIݻ._`>|تun5k ץKO2d1cLbܿuu5iDgΜСCգGM0Aڰa^~ej޼y {=*UJ/#<"IvzTjJ8q`)R侼;!pO DTTU/=c (ի_UƍջwoըQCo .k׮>%I˖-SժU?Xlٲ={en&FެgϞA׷HmذAΝN:EYЁtUTK@BH?Z-[V7N?^w$izgծ];URE 6Trrgm6޽[:uծ];5jH&M2{Q5kL%JЋ/h+WujӦysy*[ $Iڳg?7]|Yqqqj߾֭[' >\۷o׾}DȿKҥ꫻H|nݺjѢEݭ ,Kʔ)͞=[/>s)RDO>$?ٳgnZW^pUl<Ν;g>9A%JPzz.\ch׮_nsy ɑ7 2Dc}J,)IzHIjժUkתcǎJHHаa,#L8yxx(--MNHRJ>P׮] /XŒnt¸y?^%IJNNdP@͘1C*TК5kԠAM2E֭榙3gTRھ}};~?$GvбcGU^a l%7W^4p@M:U իkݺuJIIZȑ#:~\]]-ƫU6m6l٢L ot8p@x_sZܹsUJ\?os*888%J+]N8!Iz5`YFM6 ꧟~tҥKjذv ѣGu۷QFIٚ6mjNjTT)y>>>Wl٢ ʕ+ٳ瘕Er$\#9FQFȵMUcǎΝ;m.5c ?wԭ[7կ_b{ݺuرch"EFFO֨Q6lta1BoY ڵkJHHPHHXvmKz(IT||լYr$=7suu5+;99O>zմiS}Gj֬*V(I𐿿zeC/^9ʕ+f ҍ.9t!gggV7eZj[H|bҤIV *JHH?^xA2///*,,L_~6mt%''+,,L ^2e`uY .TÆ uI͙3G-[LOjժUjҤ٢{ɒ%ruuUZZ9ojݺԶm[988h…*Qr W=oСCzZh_]5kִBxb]V-ZԩSui+VbYjjj׮ J:u'/h֭UjU=#PDDż]}Ղ !9+==jCꫯls֭[7H]pA:u_^=ٳGsUD -XRjzJR6mo>͟?_?5|5ob>>>Zbϟ 6IÇ׳>kQYTRڲeRRRn:yzzjԨQj۶mZ  ȿ):(22RnnnjٲVZرڶmsU [ .SO='xB۷WRR*T F2嫯ysխ[7}z'%I;vPttv5jH&L^zEmڴ$ٳG=~iܛ@Er$7VUGIС՘ ,hQjժZjY(Q¢vN\]]ս{wu\\\m71IJKKS֭sիWO&Lj_WݺuUxqedd[)))  jȐ! PRt9k̙frtc[oN:)33S:{ #Fڷooٽ{wz /Dr$@ںuƽ$I3$999YٳQFw^\rsBBB~z󱇇NqirqqQjլ+IڵS6mgeffjժrww׾}{EDDh޽$N]>>@>Er$'jĉQ˖-ղeK{ rwʑH .;# {o߾0a@>7(88a͕#I@~B[mȣh H Hȣ H HȣnIr$# 6+g{Gvi0Kڵ#T@~Br$`)));.z-2ܥő8=*r;ޚ䞅+4&[,E~^߮@.vv~ޟ99>}c )ۊc)ժUK&L0UbEݿ0Vre]~M6:x`{XB#FHd ő[v.] &(***#G(&&0vEGGޟ:uJaaau-]|9Mr9o2>H 6u $aȑwcd3gԸq`\R]-kkD"##eoo)!!A 999%9eʔ){&%::Z666d/^u%yjӦMOTT)uY}\]]խ[7Iرc#GYׯouueϞ]KV>}ԤITw19R۷oOy~7͝;W%K԰aÔ[NO"cXm(2f͚f͚$|WG{իWաCYZZjիQenn~MQϞ=5uTM>]_߿yjر$jC)>>^+W_|!wwDyZn?SSt%u]FR5$IgΜȑ#sN=yD:t^VҞ={h"GO0-]T'Nٳgekk+}*Rȫz^ӧO}RaVte9;;KBBBdgggXOOPdd>|XjҢEk.EGGk޼y8p+I מ={ԡC[u떺v:hI V\ IիW G~?~\ժUSBtiEDDhΜ91cfΜi+,,LnJt'Oʕ+FҘ1c4eEFFҥK*Ytٗ^e˪AիKf͚%Ǐ]KԺukM6M˗/W5bŊWG)SlS$9qFjJ R%I?PyyY`ZtۄH@]-[O>d9cƌDk4i=zVŋל9stAM>]ʕ<==o5\w ^Z]tQ>}h…߿"##7o^*TH?~8p5e,XPjԨSҥK4iv!)w/j?3f}wruuڵk5{lDEE飏>A)<>>ruuݻ_=8Vo+#ɚ;w\]]u*00Pںu._ldɒF{=IRѢEƋ)ׯޗ.]ZjJto///YXXLԣG//\\ԡCT}v,XPǏ7I҃tΝDktUYZZ*((H|󍜜d:9k,[}>|۷oCjٲl"IZr7ov)((HcƌQPP .QF^o 2'Oh…rssKAhѢ={666Fkqqq9s|n? T1c$6K,ʒ%fͪٳ.X@>TPP?TN͞=[C$U\Y&MJrG_333*TH7nܐ!{4|pmܸQ 400`տ5mTdkkk}F9sL#/^otۄH YFy1t_U͛7ח_~̙3};Oh<,,LgϞ5tLܹsAPƍ۷O$gggݽ{7њ7n^ʒ%uBBB={TeU|yҥK)Sرvm۶q577`4^jUy{{gΜY{쑯]7ojȐ!:r+nǏ7**믕#GCH"k׮U``uIUXQǎS=>Oyݻx +VO?5)QN>֭[z.\+W@ O˖-ϝ;ڵk)krttT-ujۮ];*UJTR%߿_7n0y,YH"6lQɘ 2D-ZP\R7ۊJsիW/5O$IM4I4onnnr%:*J]tQ.]e>c>>>Ujժe4h]fԬY3}Ϝ9|}}SD ͟?_7V֭UlY]tI;vj֬lllsNH"j߾J(;whݺp6oެjժIJ*:jժѣ,,,(Cۤ+00PM4QժUմiS%$$hÆ  ^+ #hʑ#-[|߾}5qT9k,u޽[fffO7nLO>ݻweffIw333KU?HG1<ÌjРA*^xs:u2LwAu5̵nZ52Z߱cG:uJŋי3g?~߿ߨPErwwO6?wթSʕ+_ŋk _j:taM4b *Ǐo߾sn߾CСCrqq1k޼ydɒ~~ #ɓ'U^=;FQQQzq|1xӊ+Խ{DGP֭+WWWS@:OԸqcխ[v4suuŋ߹sG {+e˖|ׯenGϝkٲ>wß *SL)S&ufffjٲQfIٳu|Zv㕤 &N6^ U-6)_>C3ſ8=W&6l ?~SN=؝;wt1ݿ}7@5g_֮]XSJ TڵM$<<\*Qw .$KK vBBB$IW\1~&22RwO?P]|Y|rktdɢ;v]v*UuӧO:0o+:G0?~ʖ-w]w=iÆ ʗ/n޼ŋk޼y\$i:tt1yxx^<кu p!!!իۧ|ڵkrwwל9s+Wtv*ݸqCӘ1cTlY :T˗7uD;vl٢3g̙3ںu)"OOOGٲe{}\]]m4sIڵk̙3ɓӧkԨQ*Plllt9y{{kĉV\\\]]տ͟?_sya={VVVVCHO>ӧOkʕ+7nz8!s$&G0ӲeFPBDGGA*Tnܸ!'''hҤIjР:{ѣGgϞ=zO./ UZԱcGm޼Y :uW,Y'+<<\ڵK{ܽ{WStt(00PE>}z3f,X` 7 Uhh߯jNk;vǏegg~M9s$͟?_'Nݻ Eׯ_Wvԯ_?{Q]~]NNN/[n/u}F0]pA-2u ٳgkɒ%>|hxMq$&G0RZ5Kݻwݻennn4b =~XW$J#GԹs4vXZpHJNriĉYdќ9sT^=-\PC Iˏ* zA#"""…xc$+...g?!!!ʑ#Uh+X¨[k޼yn:*TH#FP%Iƍ{HI1c/:m>ckkܹsVZjԨ>=y1#$2zhUPA3g42>f͚ #+G͛رc4h7n,Iڽ{gϮÇ'B82k֬ J{|%]tZP!.]ZT%)ݟɈ#wfp|||gϞtd„ jРcУGTT)EDDHUpa5jH 0~GwՑ#G2gάqyݽ{W֭ӆ g͚U4G,X0URJR++=DGGZ[[@*] UJ~gSF:GmBq$D2eʤ˗~jҤ\ll쒼!nLSHH͛N:]v3gbbb,+w}˕7o^-ZT]vUǎM0$$DժUS׮]͛Ytرt/^/BoVѢEUJ}'z_z_ssseϞP uΕ *L2F|}׺֭=z߫pppcW*..Nrpp0u P I+V… ˕+~!kݫҥKʕ+*XդIiҤIX4~D{wy*wt"__ W7o^UP!uъKDzUeZ111z_ӧWLR)ag¢E*{*^j׮m4w}M4I:u2Q~߿^O۷ץKO:UjJ,;wT@@,YEb={ɓM@:X 2>L?6:ZgϞ:yLbt4#G4a„DG>׉'$I666KjFlڴI}Qܹe<9rd.6JJ׮]5k,jJ3gVj8ի*U"##];w\~ƍ*S2g,[[[ծ][{,YbŊ)K,U͚5w^ѣGUjU:uJ 6TL5kV.\XOߡCTn]^eʔQ@@}}Z;w]ğ߿sΥ}mdZ|>}jsvv5sL+WN>>>jڴj׮-yzzaeeaÆiӦݻ5k!CRŊS``|||TV-GuѠAqƷX͛7'SݻwU^=5h@S+W.}Fkʔ)j޼$ig}pݿ_;wVӦM使 }8qtm5kL $=yDN5k;w(,,LSNW_}#Fۻw6lVZ޽{Ԙ1c4tPM>= 2>}wު^jԨ!OOO(P@,X`xH… B F_ٲeK.22R/}0ťG)&&&u?VTTT뢢{VGPV4z$J.?4U\YΝӘ1c{UV:s挆jX%Iرcj*/^\/ָq 5jK.i*R 'Of͚ii_,ZH 64ڿa>P=5kV9::jܸq*U o߾3gў۷o6l0 2D}dggYG3gΔQRI믿4~xmܸQ-ZkĈ4hamxx 0@W͵eM6ѭ_~=z lٲ)SLu#Fн{%UTI7nLrY2̄ IDATe˦ÇNl>9r]Gս{w;vLs5naaÇ%m?LժUrJUZU/IRpp6l䵃̙3%I >}}]9::V-Z{}ʕ+doo/OOO={ְ& @]vUPPʖ-,Y(s*Wlْhύ7tʚ5TF //fiL/ʟ?s˗/hF͚5{5Xvmծ];ɵ3gV۶mSPNohH"eʔItM "Ij֬ {Fd(z433ӯ*sss?>QAQll\h<((H%JPŊݻgϞt Ith]ҥ۷^쬟~IڵKdɢ%K&9gee *Hg%?ȑ̞{^kɒ%W߿>H:qj֬s}ߒwjѢE:}$iРAڵkV\jժ),,LgV*UtA-Z4ѽ;v˗/kÆ Tܹ'Zj:t .0ڵK:u4i8[N]vմi /^#GjT\\ԢE ^ZM4I őd.\X 4xnz%K]~]]z9sF:{8Iwv433Ӯ]ܿe˖:G9NNNÇ%IHrm\\Lr1kFc˗/Wm<'OM6Mwޕ֬Y#I}'777ݺuSlԣGiF:t֯_z.]$ssxP HfffF{… ۚ={:ud(6,QbccbŊD>^ x.^h...N7oԙ3gTD( ggglRSL˵~\Rhܹ.111Q̙պuk4h kbǏkСܹ%I U``lmm ccƌQ\\Zh![[[[~~~ڶmZI/uDdle˖Mu۬Y*,,L-ZhѢEϴi&eΜYu֕w۸89r$u'OEDD)rRxxyq%)gΜzdmm ]vs7ődmذA/_6kժSO~ZjdɒFsVRӦMUR%5o\ P||D{YYYiÆ ޽{ոqcEGGᅲNjX;wn999tjݺfͪ~AW\і-[ ƍSǎUD o^9rΝ;u=m޼ciDBBu߾}Çk֬Y۷dɒںuϟhK.\￯M6%y?CE1|9zeccc:w}YXX(**J|M{=x@wV"ER5G|||Խ{wUP+W\Fk֬tb4VR%-_\ӧOO>gΜ:xF(**J&LЎ; ]˧iӦ)^N>-ݽ{Wꫯ/(k֬ufffZlf͚XݺuK]vՙ3gյxbΝ;ٳ9 szRrʥc?Ї~hkٲ5i$klk1 {5u #88X$Ν;Z`I3^MuqMSG0K.Y^DD#G&0l0 61|7dx6u |go:$iժUÇ ő୴xb=}1k HbSLQXXc5TdISGH7}Q˖-M#dx|jժex\]]M$Zha@XByyyI2eʤݻwĩÇըQ#kܸqjܸ#5utXSKʖ-##@e_.Q>^ś/6q*Dq$;w͍[(%$$j…N>7߮]kxܸqx#\|YQ$kСgGjӃenőHs'NЉ'L… eii c\\vܩGD餫WIoΝ;H-űŋ?x길8mܸ1Sdaah<..N?\bTLHIڲeBCC;offիWcVZ$,--dɒtN #Xmr˗ÇM03gNSG3o$KxsQ йs?+[lj׮ݫuɓ *TvکDH,YB p5ڵKɮ]~|||!5kśtmܹSM6MdLcڻw|}}矛:֭[rwwW͚55m4ڵKҶm4zh.]ZݺuSTT[h͓ubBBtHتU^X)}y)Α2wVZplllԩS'Y7oԦMe˖ŋڵklllL7R||.\lᳵnܸw͎;-WWWVŊS߾}uAO?$>}Z>>>*RgϞ$kmllThQI_@-]4ٟ'B}$JKXZZji@F^^JXXzj%$$]vM׮]ӎ;4uT^ZZ`$i277W,Y`YZZsʛ7 # 6LZlYK.k׮ڷo_3gZnE){I[` *5kM6E*Te˦hR )秪k3qqqZj ɌHgbccqFݹsG...i @FAH ܹsGժUӷ~5iD|BCCuqM:U ԉ'Tn]]p!>[bccdɒdl2EDDY۷7;rܴo>e˖MÇׁtmݸqCׯlmm~zUP!LDDD[n,Y"??=>|}EBCC &]]]S?Hݻwʕ+͓&cǎ)$$$I:Rw\QQ XiFNR̙~zm߾]^^^*XTT) իWhYsN~o>ÑnnnZb^>t]߿o޽{ruu5uWݻ$ő@:y$){*]tYs#[j%gggjҥ1b9srʩFFsO$ũ}hSN%*̜9sJC*WvܩYɽ^W7n0u ť_?/c#{幞С6l(wwDs+RJ~)YGi(44T]Ss666֤Ixb}'c "## f'խY7o*000չ?E'={YfPJs^GJ*=w.%őؼL2%KI҃R}mrhɺtvޭ HVZY;vLt]%I+WV=RKʔ)K~AZǏUF m۶Gߞux}888(221҅% _F(|Gxx (P@$CBBT`_{)RDuݻpBCqY$]<+y͛ծ];EGGAڸqM+7nƍg@;y#f?Ԧ8?:&sss3|_k&WqFݹsG:y,--ջw$𐙙>ϧ8Ç_).նm[EGGS[l0 uc$2#4dee%oooIԩSu…]7sL]x1u-[>}˗FjJOwyGJHH}bcc &͛ivʕ+ձcGKׯO%H[q$#ő@Op5kL!!!/\a}')P|9c m޼YԿ^7aiΝݻ rɓ'СN8!GGG})ʖZ .ԥKY&C(..NqqqG(R)&&F/_NaܹsP&OP}/^(___mVqqq8p`L׮]Sllʖ-5kŋkٲe… UjUm۶ͨH2..NrssӺudcc 6wIKٳx5mTrJ8""g0#GZZZ~ɱ-m`/_kn߾-IRj믿m۶믿GiruuUltm]rE?L1cʔ)ӧ'{… ~ڵkF>,Ys:r<==5kV*THpႢ%I...ZfME… IڶmmۖV^;I&^H\O>^+W$Sʔ={v;VcǎUHH_XɓGE o߾ڷo{/4{ IDATR:u|HFq$`"...c_ƶmteYZZ*X ,ҙHf͚%Ijٲo4McHők… ڵk$&NTt8x͜9S*Yjժe8 Eq$׍o%///uqH#ה-Zd-@q$ jx!#n(BG;? h 5O5cAjY 5%(ZSQZC4ok* %S"!I~t$'ZY|ޟޕL#7#TLL+zrȡL2Y !#jׯX K+VThhx_Zע{.ᕺzrm2XΑHuW.],]Rhźt钥˰A)k֬.)h׮].iH hɖ.)gp.gggK/G8VX‘Vp$*#U! H`UGB8X‘VƖ-[駟Z )0k,UVeDPK bcc-]jB8X‘Vp$*v.xdooo2 tGª}O,]3F'Nt8VX‘Vp$*#U! H`UGB8X;KHѣGkٖ.iիU^=K*ʔ)(KZlڴI+WtX ‘ܿ_111.iÇ-]BuVY葥Kp6* \|dooo2`!ѺyxlmmU@Kj_XK" ҥKU^=K 1bkKʹ̙3.#_.űVp$*#U! H`UGB8X‘Vp$*v.'..NQQQ.0"WWWK5  Tw^.01tPM2ex 8VX‘p6^bŊ\r._~e‘x{=M:e پ}<==-] Xm`.\kǎY?<<\$9sF{6.^]z6lP@@@ p$d0fҖ-[o_Kn:/66VVJN:ݻwHG@r=͟?_K.޽{u1Sxq/ևp$d K.UBԤIhƌfcEaÆz7TLOJHHЄ T`AȑC5RXXqߌ34gm޼Yʝ;<<<Իwo]p!z._ӧK7o޽{jԨ|}}#5jH7n0W!!!+W4`*THrRڵ?ݗ URx&LP\\\'&;Kx}f̘CJﯚ5kjҤIʙ31f޽Z|O h޽0`,YW꧟~Ν rqqQxxvڥׯkĉS]^+W֮]TX1zbccsNIҐ!CtY8p@#Fe=x콄e˖ .f͚U/^,777޽[C /n׻wohҤI*_Ο?/mذA۷o}?w^# رc^:Hʖ-+j޼yOLGݺu$yxx诿uK&N_UWV%INҡCThQIRBTZ5}0`6mׯdQ)a&m>||||={Y\9mذ-888N={uq;wO?d\S˗/MEGG~?R\)66V111&_nݒ$ݽ{C?Z3Ɠ'Oj۶m ݻw_zGg@<))I<%s$F.(Wq$szd.\X5kL7TN4cƌdwTL&M[njݺj׮7nh…rppз~Rs~gjժUR%iҥ&;IN8QZRٲeէOyxx($$D-RǎQɒ%5}tyzzWUTѥK4sLhB]vW@QV-;wN3g6 ,{V &???.]ZF$ 4Hs疿+mҥ7nnݺ"E(::Z/_VN4i$̙3E󅄄SN/jo@ݾ}[}ʕ+5om =! eĈƱڏmݺUFҵkdkk;vhj׮:N:Ç*YN>GiѢE^$髯Rdd٣2e$ аâO:uz…:r䈖.]j2fϞ=4h>+..Nj߾Ve˖޽{۷֮]J*)>>^aaaݻL"[[[_<(OOOm۶M.]Rdd~@zC8*Zl);;~H:t:Os疿$ʕ+ȑ#E.X@ 5vvܴi6mڤu͟]ӧOO3gά3gh׮]ԩΜ9cK 7W &~࠯ڸRJ=zZnjf!<_FT~}K͛+SLW-R<nj3TV- <={m۶6m֪UdtMǛk8qBVҥK`$/^\AAA:uݻKb/X?j֬ٿ#$$8.^D󸸸NQQQʙ3翪yO,Y2z79jժ:GOagg+I&;99Y2yjժYK~fͪxhkkdIH)֭kάoƌrppx>uQm۶U2e4x`9;;/Ν;g6\F:yN>"E`M )t=o^wM?((H۷7uk^Lu;V>$%&&j/U|SU~}/&Gr_tI]v5`I?`cܑ#G4k֬gSP!uM:uRDD~qO>Ql^nH92NRHHݻ nݺ)EDDDݻ*SΟ?X;Tttʕ+uVթSGYfMZۿn޼)IXxWNFC RCCC;;2KY{׫M6z֭[_vvvJHHЪUԼys[ru9r֭[յkWt:{rέ͛7=EM-[j޽ruu$ʕKϗ-*={V}ʕ+άY4l0)SF%KTRRN8>HcƌyHJ:uJӡCT^=9;;k…:yƌ+SL֒%KEi ˵j*رCWN|jԨ"##S2yQAAA:x;`yzzu|%KLxZ8zz쩦MgϞ]ٳgWݾ}[۷۷xo^W\!|պukeΜ$kƍO Ϝ9~O6MYd1&N([[[ ݻu1;wN Vbdc7aMK.ܹ;ȑC%KT\vQڻw=z5k*K,ӧ1r?L˒%O>LJLLT*U7nY֭S9^@Zűڀ SUfM]xQ+W… -[hܹMJ*U|ܹs~(Pҥ<_ZjiK#ooo̙So{==zTԪU$IÇܹs5x`͛W...j֬;f2Wbbf̘rI СC9_ŋ'h̘1ʗ/@N$;wNUVULL};w֚5kR$>OOOuUaaa:oH*R4hA)O}4}t#%ICUնm[7y}= hZvl٢GakʕСN-\P z葪᝛7oO>޽IctIÇ޽-[*22R۷Wlkĉ oDG\\\TBUc```%466x)((HEI{xb-^eX̱cL~֌p$رC͛73ǵjJq㆖/_VZ)00PtѣGjڴ|||4qDc/R7֨Q4eʔŋjժKi\WJHHfϞm66fʕ+S4gLL2yX:|犿<<<bn ={z2Rzuݻ׸Ζ-<<<;O?۷Z@fG #! s:ssǝ;wN2eR|称0+WN^^^0aʖ-0թS$\]]_0իW/پu>ވ=x ٠PlTbE9rG&`ooP_j$,XP%J5kf씙popݾ}[NNN&}<бcR4_"El_r- VsUMtA,YRΝu-'DžVZ9~\|ғ!bŊD>}ٸH%&&{(V 秺ujʔ) ԬY^x'{jذK?~\=Q VZ4l0uJ˗O7o6kz9"""CըQ#;w(<<Gsњ\|YQQQgUb,\%`lٲ$`UPA*TH>|ظ& p$εhB-[͛7#G?5lnΜ9?~UN:uJٳgWddd7n$ۗ\8IٳgףGte͚լݻrppH:Ӓ۷okժ]Zn޽{_F*UTD 2DAAA&ױc֭[)WUT֭[ՠA=66VÇ>.]hMv>}fϞGdɒYy晼iӦɓuwiĉ|nի}}}Un]Kʕ3|jժ3gԹsg}Zhvj?zH]vqrŊUT)uY3gVӦM_⩥ ԩA=70:̙S:|p;Jﮚь1B9sСCճgTJm/^T||Yk޹sGʗ/k]5j)S-RV4jԨT6lI8HG\ڵUF iٲeƎ]Oڽ{M۷K͛ ` TppBCCմiSiFQQQf]v)<<\zz6l(7,4?>^/^\˖-3;ٳ:x͛Bu'vvv?_攰zm۶ך5ktUlY}j޼t颚5k***JsUfjO6MM6UժUխ[7̙S7nԞ={qF#xb5lPSvdoo 6FQ|||4uTyyy[ny̙ycʕ+)SN:ի*UwjѢ +V}ڰa4hGi…3OoZ|g^zgK{|,ڵkտC>Ю]2 ,xQiEӦMu9eΜ٤}ɒ%=|n:͚5Kk]϶mn2dʔ䚝#Xo@-Z5h@!!!cbb ooo͚5K*U$mܸQJJJҩStu/^\Wƍ{D-_\rtt|>ɓG-[ԩS$Iĉnݺ?uT=Z ,Ç%IGQ6m㣒%K|x:zڶmݻwkӦM!___/_^ԪU+UZUUVu=t!j͚5 w}1BѤIթS'پ!I4x`jƍR;RJz->>]ܹ߯sgyyyiڵڿ_}}}ghʕy~?)OOOQD u͸nܸ?RJ)88XS߾}jv$|Rݻ'Ezd0ٳgkر u[O$ni=زemeͻrtKap$2mϔ3gNܹSSNUum9::*::Zھ}*T`ꫯԢE Ν;TP!I-հaCeʔIʛ7/^dɢ͛7kС*WZjiժ\3֊+4l0O...?T䤏?XI{Ϟ=]tIŊӤI'OZnԾ:u$vjr,ure˦~_~O?c$to߬Yd˖-e˚˗Ocǎ}sQܱ0p *L2V 2v̔) *dnjմuV}5jlllTti͟?=/a*\bbbo)k֬_\]]%IW^՝;wTpa{/_8FہtQnݺ &%%i޽ o:u(GFEܹs?~Q ,gϞ՞={5j슋WK.3fL;Vp$22X =ZGVTT޽+wwd~-R;H̙$aggk̘1:raE3fz\\\4o<͜9Srqq1v{󶶶qjܸ\۷o@ʒ%ɘ}D'Nh<\~]+V0vmEԨQC۷oO/w:s挤_Kc*T_=|Pf;]&{ҏMiӦڰa*Tk׮gϞZ`ڶm(5nXfuY{իWǏjժVΝ __d>s:t+WRJ|_O>5kh*[,XUѣGSǎ5sL8}ZfUS}ї_~iEB8H ˗c2e$%[˰WѢE_~777J-xv̟ի>}J(aR̙3[ w1 ѻ;)<`%&&e˖*Y֯_oKf;޻wO^^^7|cnݪVZ)δ;w$:uJr$EFFu4hfϞ-I0`N>'O.^(ooo۳rF8 UjݺsqO J.޽{[ p&_ϟ7j{r.][oi۶m+00d3fGʖ-6nܨ/jΜ9&;5WPAӦM_|aVŋiӦܠA5J&M2ڲfͪKGIz뭷xb͛7O.]҅ ?jٲe&ݵdM6MwyLJȚ5޿;# ,H˖-3vaLYo+WHڶmC*44T*URLL-[CIOبsf|P'NH!;#@dkkk֖5kVId"___j?֭… Kv%;.]h3gÇd-,XVO]vx{7TV-oW_O>O0VZ~\J.-[ٳF=11Q:tPѢEk֬o(%%Ũ/_\~lռysFСCjӦbbblٲO>jҤ;f;r䈱jr<G 3cHmۦH,YTϓ'ԩSڻw?.̧urrr2͛:u<<ݻwWժU5fm%)!!A=zPhh)_3gWn5֬YHhѢ:{\3fZj *VZ*^BCC~M1114h1ޓʖ-ܹsQ'N￯zx@fF82%Kf͚nիWƍV9rHϞ=>vUTl5]ӧO|_ 224hUb :r<<<.gggN:i߾}SRicoow}W={޽{bŊ_$dڵk+,,m zZj^}U)k֬z7uyIwn믧4c ܹsK/EAAAҥΜ9sc߾}['NT"E9r(22R1f…իvء5kC^^^Sva5 TZ5O͚5Ç ʙ3BCC/URg.zhh\\\yըQC/j '88XjR  FUժUUJ`ݼTJ-[`l2lmXaHdf <6nܨӧOۺ իnؿ"##5f͘1COV߾}ՠAmݺU*UR֭uA+V󊋋3TBBƎ+0aõk.ZCڹsƌ2eɓ>|ʖ-]v)G:{VX (66Vfҍ7`EDD>Sݺu%ݹ[oÇk޼yqTR%YFe˖}2/* (QԥK[bB8H)- :tMfOԯ_?iFҝ.YD!!!ڱcʕ+7|S'OV||3TfMСC8qgZ_u맹s皎+Vȑ#Fp_ .Tڵ{iʔ)_~E6mRʕ lٲs:q℮_h͟?_1R.]o߾zI౉Tdd}Ֆ&#LJ*U%nn奢E*WTD 1B>>>JMMɓ5}tIҊ+/_>ݻW{5Ç[|r5i$%wﮨ(Sw#DFFwڵkegg˗/kѢEq:|N8t)8o>4.%%EǏWOZfո˗/k*X>@˗uV_, IDATG 3# @5gIDDnݺ8޽[˖-3hBTBBZnmԯ\MA*SiviȑZzԩcSRR4l0EFF'NhĈ8pQ}z쩊+p²X,ʛ7 d۷7nzвeKyyyٺ +#LbZno*=uwyG]tGK,QFb UVMɚ1cʖ-}ZٳgYfjѢʗ/3gh*YzePuVթSG7nŋk}gݻհaCiӗ_~/*,,L Ҁl݊ Hdf# QrrUI4x`[mo߾֒ԡC]~]-Z_re=zT3fƍ={vũvƘp-[l2ehڰa<==5~x5jȚ55}tܹSرڶm+WWWc\}Zk׮Չ'TlY%%%s}`W@hp$$<<\Zj.]hҥe˖jժꫯ-[6AW^՗_~&MXؗ;w^zFm˖-ڼy$|Q]@zvE̙3ʞ=i͛u95iT߻w֮]+WpjԨҝ*UL۷+k֬*Pv%///={VQ˖-+!!A'OTժU+JNNҥKuq̙S7V```:ʑׯ غ +#d6vvv;v ovکaÆŋa :TnnnիW[3vX-[Lҝx7h@m۶ӧuyEGGjժ:w}~x6͛WUTٳM:U;v$)55Uojժcǎʕ+=z *th[:xU}ܸqZr$)..N͚5S׮]uuܹS%JիUD [Nvvv4iʕ++W]VaaaZdn߾-[(3/VG`[.‘2`*TH~BBB$Iڰa5c Iw~g֭>#׮]W_Iz+Wرcrww$5J:tPjժ' ,RLLcT?\G5 6L۷oסCo߾}aÆ:r|PW^Çx̘1[ CJ  *hz믿y5kiu}zʟ?TPO+G#(T&Ol6HE֬Yuqtt i׮o߮Ǐ˗O%Kԅ 4{lM6Ie}P^=]~]ׯ7jgV͚5 /Hnݺ'jܸq[V>͛7)۰aCyܲeKIw4NNN[;IwV]mK(}j$r$<_U/^uVr$d򋂂R۶m5e5Jҝ[t1*"""oݺ#G@ qttTΝ5uTժUKEӦMS\\1?ԅ TBUL:t*xefz5kVI%K8p@t!mݺUaaaV]vM mݺUIII\}֭*W!ChϞ=:uꔚ7o.NxY rppHwܹsgTxEFF*o޼:}:$b zzzN.\/_6M׮]_t{R&MԵkt;99=1ljjsp$ܹsԩbbbu_*TH%Jg}I&SNrvv$)RDٳgɓ'UT)ӗzyozW5sLM:U]v6K,*]͛gɓ'm6U^j[\tQS͛ڿ#\n]رCE>Smݺ/<ߢ4yd]V:t>f9R&LիW%IWÆ ՠA,Yjmjܸqڻw;Fddd٪U+I&:qℤ;+RG˗/7VYVVq~g[x[x"RSS<#ޘS|}}m݆‘Gvvvʒ%%I... Pxx,XJ*n"OOOӘ hРAjڴriV~}͟?_}Q&???3F͚5{LϫU*00PՓJ*iݻbbbTuMC 1Οy۴iǏjժruuUJJ:v͛]v-ݽz$+:88/P/Y׮]SuVe˖-C_ g\֭m؝?^aaanUpa 4H u+&#0j!!!Z~t 6-[jذa*^^jǍ\rY}rZtӼyիաC}7Fm۶… ͛F}ҤIS֭MsO4aVpa-_\m۶Ν;zԿϟ4ǴivڙД)S4ydVxq-^X-[4ݚK.ѣ)bc֬YJMMUdd~Q;VQ WBB7nCݻ+22R%J01o<]tIQQQFN? 9s *hΜ9W{ޫW/nZeʔ1ͽpB>}Z=z0j...:t^JM>]:qQׯׯJ*^l?Nŋz͚5jժɓFwUUZ5ܫVҁԿ5kVܹS{֊+zݺu5aUTIgΜ1C QٲeUvmk׮Վ;4x`#G}ѣV^m6lѣGL2JNN6#FPѢEU^=7n5l0+W.mذAQQQZnQo޼>KgnjP5n4֭[b 9ҨꫯRǎeަMƪH"qQ8qgϮ-[޵k4n8V`A\Rڵۍz5h [M2Ej۶o>}裏ZbŴtRnZ~Qԩz… 1coG5m4VdI-X@M6Ձztt:wŋ;w/uTh֬YF\rOԠA=zԨSJ.m>ӹsԽ{w#GӼyzʕ5sLթSG?Q۷o_d~W;FCpB^zuM:U5jofUR4+tQϨj۷-[f_yũr:}QTbEժU45k{nF_۶mSϞ=j*^~};Vʕ|%Jnݺ7lؠ7jСF-88X6mRtt֮]kԛ6m#Fdɒ|Q5jϯ o_hĈF-wJLLTNL%kݺ bŊڵkF} VMsܹS .Ԙ1cZ|_7֭[zv`JMM5'Oڴic{޽5kZ"E^Ӯ]zǎէO,X4,'''C4i$M2Ũ(QB-R͵o>nݺXb9ϟo H˗7|S ,Ё秖-[^t/ooo*Uj+W*_|O֢EtIYf ~-]TgΜQ9ԬY3ʕؾyfyzzPBZd>,wwwթS}IEرcʒ%6lB =˄uAUVMsɓ''OnX uPgݻ5`9;;TRz4w\ڼyBCCc}7J{ァcǪE RBB%KX}hYn߾(-ZHM4Q@@fϞh*[1﫯3gZj*TvڥkĈE ԥKթSG 7|c\@,Y|QT?.\htϯ?->J,ii۶̟?Yf=c8v' su;cWdI$K 2|$ҥK-͛-r $˰aL7Β/_>Kjjƍ@Ν;Mc}KӦM-r%114oZonX,ݻ[bbb,ŲqF-W_}eqqq|wby7-,SN5?,7zt%((2qDӸSNYc>}xB I%K>6 0~gKII׮]kə3|MLL۷bX, Is-55ҦMK6m,?X|}}-/_6ݰaC˨Q,˝-'N0={% r9b.]ڲh"b9j9yiaÆYratbX .sN},f۶m˞={Żƿߙ3g2|~ԇΛ̛S}<7X9,66VaÆw8**JǏ!'''uY'O6ߺuKSLQtt;+@lR5k4ST)C&Miĉ֭ʕ+gGDDUV7cPPƌ+55U ,PPPz-Ӹ@=ZǏ OsΙ?3g=44t۫[nڴi;w(W>c̉'~z曒q)66ָT۫D7onX4a9ҸN+K,ZhQ:˔)^ziرI&7/lW|ykx6<GxmذAYfMwefOOV\Թsg,XPGVl[UV$>|XƍzGU۶m|OǕ*UJw\Jt9:r~YKMM۷oޞPv!!!ot'{Yqttﯨ(IɓբE :8|bccӽzrrBCCM:u*( g-[VG1jt% IDATΞ=᧟~җ_~i5ʕ+V=HSnnnnC8@f߉$Qnݒtg:uh̙۷m;ZɪsΥ;>%%E{p!oooUVX~'=\W";;;խ[Wo6oެҥKk̙JLL4{{{k*SL4=vwwΟ?9sZtBBBIII{ycnooouMڵKwsuqd͛5m4[! ,::ZSLw}_7իWOʕ+BCCM_ÇOwEڵkO?۷MT-\tŋŋVs̚5K˗W,Yꫯj۶maŚ5kL^]vɓ5o<,XP%K4׫WOׯ:WrȡN:?0jժiܹV:s6lؠ5j3gZ`X4{lխ[aڵ Ӷm2%+GϰQFVuGGG}ڿ4iܹsk8WLL-[ACCllԩS/7|c6𔊌T޼ymF>|RRRlѭ[7ӧ #A6lиqlR5k|n‘'Oɓ'mѨQ#‘2H#;;;eɒEV~G+K,#GGz>}[n֬YdɒX,X,jٲl"oooI\]]% g~7ݻ ( {{{ݼyS7͛Mˎ;Kr劮_ʕ+kӦM[||F ҥKX,^mۦ{D999… V^yў={,///;fͪ^{Mj֬i[PPmۦ^z)00PruuUϞ=էOcq{ŋk˖-ݻ "WWW9::s:tikÆ ƹƍk֭ʖ-$E֭S~TX1㽉^{M6l0ZxGρ5jXs+..3f`rrrz\-Gxxyy… ϧѣV;cǎĉ5f;wNٳgi̇~hz={v}駺q㆒g'fggKgΜ)<^}ULLΜ9lٲ^3jժx6iDM4̙3&O|Ϲ^x-]T.]ٳ[Ɗ/uիJNNV@@@瘻&O+))Iٲe8///c#Gn!uݻwʕ+u momr$d '''Z# FAAAA#foo@/nnn#K; F}\`` H+G@|TZ5M2E#G4m[rEi{Vr̙Q!3F/ۀ|Zpx|}}h"[a&N+Wں 9O'{x̙Snx`#ŋ`<Β%j֬i;ޓQ裏^ѣG?\0JR*Ull$11-<Vמe˖-u 2Vӈ#l@+[<‘s`ԨQ?~*T*DT^=yzzj޼y$9rD6mҌ3W|4v؇p$23‘s`ΝrtA]vՄ ԱcG)..N 6THHCԩS}x@ffo<9駟e]xQ~m‘X9De˦VZ)..N+VTHHVj@fF8dU|yܹS ӧf͚A @fF8dUT)9rDm۶}*W,gg p$23‘3,,,L .C˧ Za|ƌJNN+$$zhР .O>cE8H%K5o۳f͚"E;Flp$23{[7x#Lp$2n ,\P...n#̄p$GrAeɒE9su+޽{չsg[mV={uO @fF8#:t .*UغSڳgLVj(‘m @fF8{5m4ݸqè]tI=zP>}l@fm<… 뭷҈#t9*ZΝ;ŋۺ=SW^;v@&qFU^m Hdf uMAAA:q._Whhj׮m/]E?ϝ;/BtXv%-]4C?j۶m:'‘Gx`>>>ʛ7UL2rppAGx jѢ}WTT$㊌4}ڶm+wwwӾ/V2ebvءիW+99Y R֭=3g4|8qBAAAjڴ/+O%‘X9Cqtt4Bm+ذܯ8uI֤ItmӘCvښ?gϮ۷+<<\W tEhԨQWHHHЄ TD %$$W.]R֭լY3ݸqê_~E111}ú}٣Ç֭[ҥ.^holl8 IJMMUꫯٳ jϞ= ϟ_۷oW?+<<\GΐHdf [˗/z-[kʕ%I5RϞ=j*կ_4@/ݺuk+W^zwyGaaaZvj׮-N@rɊ2U͚55x`1M8QGѪU4uTIJ7PÇk֭:t9soQF1۴i>Lu5G+VT…Mue#l`۶m:t萭ҥKmcvٺ+gΜٹsgM8*٤IFiΜ9F0R<==Ure߿GVR$????^א!C2yݾ}[qqq1c))I۷7ӦMS:u5i$‘Hd&#Xp&L`62T.]lc̆#]?Xf/>}ZGQ…i___yyyY˗/+T˕++W_~y$={VʕKw{ʕuVIұcǴyfkrrr d&# ?]sS||ř3gwݾ}[JJJ쬿jŋ%Y^OVl٬ݓZh>}{< @ffo_\\zP}'JNN~,_<݀9r<|vvv ?SիLNNּy 4кu`:?^k֬yx@fʑSk׮ʝ;@:FaPM6n׋/3f(&&摎驈M>GΙ3Gݻw=S#FP޽-gggvGGGXnE SVtYjժڴi^z%k`z,ISrrʗ/7oUÇ'|"'''IR"EcWX,yyyot'$xb 6Lt\]]մiSm۶MYdySlǎjٲʕ+gB;cGU@@ck۶mjԨ*V@JNN֦MԲeKjJG/~E܃:v*V/_^4|G Pj^<G 3# Ϲӧ-ZE-[Xm/Y{ j]tQ.]"PfٲedKm aѣGkȑJIIԵkWӸ hy._,Y:;;kȐ!2dk|(Qj֬iU9r<<_U<G 3# rRpp?ӦZJ4uTUTIuԩSUbEݺuK 4Pbb̙$%$$h׮]Ynܸ!ΪC.\q.]ػlIHZ3{NJFJDY{F?JUR#_jW:E$FPFEH&4p$~\9z?[zs}} (K] .Ys]?~\ ֘1c`=Z|ruQ:uٳguQկ__^^^ھ}$ijԨ7o` ֏Ç/\Ë WܹO 8PAAA:uʕ+ki9# l~_DLLO-]4%K^ȑ8::TRڵk:w{[ZZ*%%%xrrss3ʝ;nݺ9# ]VM48٦M5h@֭['KMVVV Qua[JMMƍuMgΑ0gtXBkזɣyO>:|k…5j$iҥ:t$iŊ0` RJ):::S|̙3R\쬦M/^{ڵӆ ~M]pYXX?^* =zH7oޔ$I M ӧO?CbĈ*W\s]tIR@@7nlk׮7onO>:p}vYZZj۶m 4i۶Zlu˗O[lї_~)kkkm۶MΝ_v}]EFF*""Bʕ+]xq?:www8qBڵÇuauEGQѢE |||ԪU+k&Ďp$#l !!A۷WTTN͛AgϞՒ%KTZ5YFmڴɔsnܸ!I+)))S{մiSO7oVjj>5kLǎS޼y3۷* tQ|reaauCڶm/r)44T Ç_ÇWeeeeիWn:={VyUvTt\ѣGuuϟ_Wև~(kkk~PXXUfMyFIIIڴi٣TլYSڱc .+*,,LRѵy6mj?~z?^of߷o$EVRll<<<ԭ[75krvvVF ׯS޽tUh4fuSN<<<2u%JСCFcQܹ3^n Qz4j(խ[%ѧOg^[Fs 8-[V|3kxzzoYYY{޽xɒ% -A6gΜ2tӚ3g+jԨQZv&Mh̙jܸԦM=zTTbEȑp-[F &(QB Ҍ3;hƌF5X[[Cij+V.\JX[[[n:u.\;wTRSƌRJ;w *hʕɓ5`} 0Qddᙏ=Z+W֗_~)>}ZݺuӺu댺M0pL\pA۷o7QÆ %IΝӑ#G G*99Yǎ3tc|w5~xuAz7xCׯ74i}]͘1#1sqO?QWÇS*U2ttN:|I`ĉ5|p^ƍS6m`SK.5Xg^z*_|k*f̘[_կuM޽3~*R>|h4{Pjժ9s˒˗+((H9s攝5tP{RRRɣqiٚ?~2ʕ+F߬EGѣZz .L d(WwgΜyeɓϝ߽{wPzvv=S`vGYƍ Zl<==5bo^ڲeZlissi*W!CWʕ+F7{xxcǎ۶mSxx3:f']:h„ Էotܽ{W&M2ttϯ#FgϞ*Q~'UZUSrrrxbŊڱc&NfnΝϽDrppC9rDÇPOС޽(Û7oE>|xLIsUoQPP  իWiӦOTN]~]g?iuK hB ݻ{g5aCvѢEݻ޽k|[nU`` *dدk׮1c>Su]={~WCgԎ;jժUjݺ̙*U(**J'O֩Slٲ _NC͛7ׂ ԨQ#hҥ͞=zkjŊruufΜ5k֨I&a 幸:+ZZZjӦMS׮]eooRJ)::Z}J,]]];wt瞾5kԎ;B I'NҥK5p@úv髯ի*gggM:UVa]%i&͛7O*\{)_AjݺTvmɓGw6t˛7oΙ3gN)R~3fF)'''˗O?҄B-]Tݺu{킑&LP^DOHHP^tA9::C֭_`$C>(('B:x+/3:G@Æ uVWsΕrʥ'OjΝ4hNj >\ѣGu1/^\jՒ~Gڲe6l}Wjذ uz7i&uI֭Sݺuu5mݺU5bĈ^?e]zU*TP6m'OڵKqqqڼysҖ &(44Tozi4' [zRRR GF7jȰf…^ۧO6668q&N8ȑgrܹg0}g'}$J*ڲe,;;toҤ4iG)%%E9r$-Yh]o)>>^666I}K &[G֭պuk{ 0@ P||yshh4i *;Wcȑܹ֭s+<<\^^^i~zZj@vdaaa>55D#lN:Tpp/.;;;:s>C0aǎuVϟ_:uҎ; a-www;vLӦMʔ)͛7kY5tPIR۶m5rHICacƌ1ܧiӦ:w \rVZ:p  6Ԇ hŊ W*U~Sٲe /_ܣt/B]vU~5fgggFf9sf8oX[[?34[[[8883 ְa2~jժӝ˛7BCCemm-gggShhQGS M:UǏW.]ԲeKiՆ+++8uUHHZh!9::cǎv$i۶mFA种;Vs5_|*U$KKKΝ[]vեKy}*[,,,Ν;aC{V 6Ԏ;}vyzz>K֭V ‘0kt+++5jȨԬYS5kLwZ>>>Iw\rOf6W\ڵk=:t`xmaaaiUTQ*U9|}}9'I5jP5[R:ڴicRDuիW5sLթSGz7u]ܹS۷oԩSqF%&&jƍg}&???IҺu4d͚5K|󍒒~zyyyiӦMjܸ+y:! fϞm2nݺڶmI&iFc .,I3f:v(Iʝ;֭[bŊiΝjܸ맅 #/_ D:y͛H.]Z(`֬YJLLѣqFoUHIO~~~8qV\)I|հaCÇW|4h iF:tV\i>|Կ>}:MA:G@ԲeKUVhs9rPJtyC8]s̑RRRpB-\Pyf.]ZGё#G*V,Y&w魷2#6`CXSz=`;wСCw^%''+))I Iy.]SNu}  sF8Ry{{4c...$.\XZg} }R-$Iw^P>>>7{ ,… Ν;۳I}y,,,dii0G#<&2dzѣG dyjÆ iv9::)SF۶mKǎSɒ% JLLѺ{̙3rwwj*٥YAH3FH3KSx~G~\xQvzAʕ+Ν{gϞv)::ZFũ}ھ}{u_~%M;wcv&OlhKNNUV-URE*UR͚5h.%%EC QpppH3:G>| * )SǶ ;vիJ |ڳgX=^5p@W...qmڴIԸqcݿ_˗/Wz4~4{.\XׯWΝ_nݺy>sU^]G6]ԸqcnZڴieúիWe˖W~mY[[/6oB ‘`&&M$///SĈ#t4O~wUX4]vU"Eƺt题Y_F :uJ> *88XM65ZhxSNiʕ:trʥEM6FyfZJ?,--5x`uIu n:OrppШQԡCYZ(ޜ9p$@ϗvܩ09::m۶jҤ$:w:t'> ^>v ɓG>>>jРseZJF>Gk֬/" կ__;v4HHH0sppPF#++d!OO;fQAAAEÇ|U|y… kܸqϭQlmmէO<ѣz{|9 @j*Zz$IyQ>}t뒓կ_?Cg={]SJdkkK)))믿=+B~~~FݻW|ի+Vx*Pf͚5k*::ڰ߅ TJZJnnnʗ/&O&M(66e<>f ݻw+&&h[+?7uUuU߾}Uxq5nX+VЈ# k6o,{{{5nXwQv4w\uݰfرjР>cŋܹ֬Y~0Zjiņn?}]M6 k„ ڵz衭[*55U;vfϞmoСCr(TBL]8:G9Į]boڵz/_^ŋ+,XAB6lPR쬙3gjѢEijZb5jd7;i$-[0VX1M2(acc *,,LgΜQDDΞ=>h?+++-X@!!!}vFڴic29s$I&yI3V`AݸqCԬY3YXX(44T͛7׉'tmڴIt9r$}RSSDgϪZjSF >}ZjNזJ,'Oݻ{fG… p$`EUM]^Kx]Jzq1))0?h -\P͛7?u]r8\ٲeK-_k1O{۷T~}ٳG*Txg@VEH3‘`ž;СtǗ-[fbŊ:w3իzG6ͼ ﭬ4vX;VΑ0' j\lc`Gs$ HȦ8Vp$dS sE8#`NG&0bYXX~\]]MB sE8B[?~$NNNVHH>0W_޽{v7oϴ0%:GdءC뫞={=bcc*ݺu0ߥK]r%wI 80U{s$HkS@*[ri2cǎѣG.㵱` 0@!!!ڷo֭kZ,,,$'''qFݾ}hI UTre+w$޽[YKז-[teɓG+WVeiRttԥKTKjj"""dccjժI~/^\%JG0W#eԠASt/^\x-ܸqC6lбcO>$M8T\tum۶M9sTUJEEEiҥ yJ='NO_|ۧթSGMܹsRk׮UTT駟*_|=z>s?QҥƎ=]~]EQΝӽgllVZexZR&MͣL2E...*Ta@ ҥKUvmU\Y}*T`G^ZikРAզMʕ+Zf UVp׸qQ82!!AM6۷5l0UVMgΜ)ӧF sB8!K.UӦMUT) 0@ʕӕ+WTHÚ7*,,L 5h@SPP|}}ᡭ[jҥ:|ͫjٲeZ`RSSզM%%%i޼y9sl٢EՓqƩsκzΟ?TEEE޽{JMMռy/_4Ȑ,Y2efΜnݺ|Tzuu#GM6V4i[kիecc>"((Hi}}}ozզMYYY̙3*Pa.55UׯWV;W>b YZZĉӦMS4gΜWR^." [Zd/_.qG͛kɒ%:uZ[[[޽Yw?Pƍ$ 6L͛7עE4i$I5rH͙30azΝ;kϬoђ+Wjʔ)rwwg۴iΝ|aÆS...wWUPPnX7~x5iDӦMӔ)S2|_9rEFFj˖->#jJ+W$9sFVҕ+WThQuf۶mգGk.}ixb]~]ќ:u${{{?kfpˊLuڵk_D9lΝ;?[[[ʕ+)>qjթ$ sXjvaF#S3gu-I0RJ*>H|1j:**JOVvڥ .hĉIq>}J,k׮6kq6V\ x3f{WSEDD$xm\Ssy꺘ݽ{W3gN=|0ٚvMMxuƋjmE8kת]v*QDk#G͛ʟ?ɓdLŋEܹs,Y@ب@?SNI277d+VLYd1~ߠA͜9ST,YTB9;;G:sέ;wtjܸf*777.]Z| 2D&Lȑ#3gN999ȑ#:xJ*o/@˖-rJYYY%;{Zns5bYF}o{ァSJڵԭ[WnO?lnѢE*T\\\n޸Vo+g/z޽{?u%I&LH6onnd;wVΝժUK{Ull$% :JU윮]k׮RS| Oӧ'[+;v쨎;V֬YSSNZt4iI&RJ:{7n]vϽ-[N:i׮]ׯ\\\t mݺUZF)Ijܸk277עE|rؤzL"oooM2E͚5$XB.\(ۈΑx{n}g)$~?B{/ZxqZP,Y#_Ujȗ]G02s o[Ν;U\\:r <<>>zd…rss3uOŋƱ9rhѢTTxq)R577WcƌQս{wmV.AfffS<;w\4UVgϞO2rJݺu+xƍ蘡={V'NP3\$צM w^Ĩlٲʚ52 H)277{O%JPѢE%I۷WӦM5l0+V̸>!!AӧOW˖-#yyyiժUʝ;j̘1z葼5f(W\g޽ *hjѢE̠@:uꔩxa˗/Wҥu5ZJ111ԩS'ʕ+Zr ٣Gԩ$)**Jڳge˦uUVH?Ww… u!e͚U|ڴic|ʕ+Zfu)$$D>TŊխ[7$/!!AWVhhTZ5y{{+gΜiv]g5o\Æ } Pxx9m۶M ,Pbbڷo͛'H7x`uI}1<VZWIiڴ>c}wk׮)!!A44ajJʑ#>ce˖MG}qƩM6:v:t\ջwo YYYwƟU [ٲe3/P߯9sh…Wjtqϟ_d0Իwoȑ#_œN۶mShh,--f͚IJnݪx5lPtu}:~lllF,/^^ʓ'6m*www3Gɓ'բE -\PwVbbԹs$?λ .Ԟ={deeڵkgk # www8g04sd[V֭SIDz|+R\gffݻ{/X52ѣGdXbpdvt+OOO}*Y_ HzܝCʓ'Ə 988aÆqㆺwﮮ]jڴiʟ?|}}ՠA$%$$Wf͒$yjذm>}Tre-]4$]V;wV˖-HKZl,,,~͛7O&M<<<kʔ) 1^aÆrppbcc5c }Wڴi˗1o*d"#!C:occl@~$V^^^7o(}ںu$̙3z1lo111:wfj;{ʗ/bB ׅ $IIMO URENRŊ?=x@gΜ! FvڥG%swwWΜ9%I˗עEs;wV W%Ks͛7`0aÆwZniӦ[n5|>?^#GLrKԩS'-^X-[4Gz4m4}駒}iС7nq[aaaJLLT奩Sk׮ժU^mH@077W||}C2d⢊+J+{{{>|8rʥ'NQdddko߾8Y[[ݻ~R RFFFFʛ7 :wy'}}--,,LIjժe G*RJJ*:pJ,}撤3gO>233ӪUTH$HUSLQ퓅#.\(77$H砿Ǝk G+ :/Rב#G'OOiڴirttիWegg"o .vY˧ 6(00P}5ժUKQQQڿɓkNllܸõ{dg͟?_*T0v|ΝlݡC~yzzV5jЊ+pyխ[WiHOC ъ+|3$'Od/mذA9>>>w_effvڊÇJ*Xo*UtY%&&Jz7PwܹUT)ٳwrȑ ѣG:w܋ys$.__5ɘ w"۷ Xn8-[6͞=[7nZj%ŋkԨQUP!3F-[ԩShk08H4i$ݹsGʕ+BBBkذarrr$͚5KÇգG͛W7oV5n8ʕ^/,XJ]og͚-Zh֬Y6v͗/5je˖zƿ;͛WW\Iqkdmmm˩{`0^ǎKuN6s$ (7]vI֮]+I*XrΝ}dkkA%[giԩ0a/WWWݼyS$FJ2>3w\ 8P}ϯU͛ڻwoV^`+VL%Kƍi&lҸI7ʵkתXbzw]v^WtyժUe`˗/͛S4iR%Jնm[m6&MI&Էo$Wtzھ}3׹j˖-\o Zbh...Zl@s$ L2E.)yKUR%S-‘@&~zS2www xmq6xoLO]t1uxA|d> )RDE1uFZmF! (#p$xo S7ߵk믿 eN>mH%Khɒ%.ZmF! (\ 4W`AeIԨQ% #dJ~W=x@ R͚5eeeew՜9sL]-ŵs?lRhj޼)-ZsΪW4i$%&&D#dn￯s)k֬Xt2dΟ?ٳgT#dCչsThQ8qB{ڵkqI&@fF8@EI&O%J ƍKΝkG>( yzzCtС, komٲE/_փdgg2eʨQF$=|PN$U\Yfff:}߿/kk$ŋqㆲgϮҥK&11QTXX._,IrppPULMʚ5ʖ-+qxqǎrrrRf͞wyGC QΜ9eee\rIkOo'‘@ #_ǎS͚5%IТE4vXҥKʒ%SKLLTӦMu iʔ),YD#GTDDD{ԪUK#FPÆ S߸quŋܹ6m$I{p&L5{$+Vv" d'jСJHH}UREֺrBCCu 8P;wG}l^ziv֯_֭[?m۶ĉ2 ӧO[zRpp$@jҤ/x+$$D;wT&M+ @橞ץKmڴIA*URŊ_J5|IRǎl_o‘@/gI4i3g)ٺu֩O>Zr^lQ7͛5wg#%I*^q<11Q=zВ%Kdii1chʚ5k߿kҤI7nݻ3fxsΩiӦ |~g]~]{˕f# Gjݻkܹv_l޼V*77T͛e*Zh.\KdnZd,,,f5m4=rʥ/BJR5sL=ڄ}Z~SK> .CI *+W\SFx+WN|3}ZlR𐽽LdيׯoӨQ$I~~~#G]v$1"ucƌIbաC5neʕ+Y=&gx39HGӦM$I#Gs=WF 5iD7oN6gaa=zh̘1o5j(YX$U6'$hk.]xQVVVO+88XUT$ٳgW5{gqppҥKoݺUzҩS}pivlÆ 7u0#G #4s Sp$vޭYZZ>WJ1)IzumܸQ͛7O2tR_V.]/ҥK:sTdIk߾}‘Kի0լYS/^/_nNNjtw}  +WHz|Uv_'WIA7֦M4wd@IRݕ3g$sÇjժ/T/^L6PB3fuիWիKs^G(..N&&&>uW6mO?K.H"w|N>nݺkTaҶm[9;; dRK6u if„ zŋ7u QGz2 ?lݴiS-,,ԳgO=Zuu\RԿ]$),,L=Rlٞh5ᎎ277ޗLZrd K)լYS5k4u@6u 5e0uVjՒ$;Vo7>s]Ϟ=ennK.駟~Ҝ9s+ggg5l0g\]]UHݽ{Wfz2h (QB[..._~k?ׯK*Wux3ѣ%IҠAݻVBB3:88I&ZAAA~,g I9ržyƍ5{lIҨQReuEfff:t֯_Ե<Ҵo‘@:]$IӧOWΝruuw}H;֭SQ6m#5nX|M]-cbbW_EJHHЀԢEE899GԵkWmݺ5ٚx}gZj Əux3XusҥT~A֒I&)::Zfŋn:yzzFիWP%$$f͚;vׯs6m… ?83gΧ>cff (!!AW_M2E͛7bbbt)ZJ/_$uIӦM{f=/+00P'NհaCyxxI&ʛ7"""'OJ&L:u[-^o#g1`0(00PӰat)-[L˖-K򌙙ɓ'ѣUv`0_~\ٵrJ͙3GH1hgg'c={vرC RPP6lؠ 6$Yckk/R;vLZGϩ^zZbswjJ͛7޽{޽{ʛ7զM9::JJ,irssS͛Wm۶}~.\ЦM$I x.ww~L+vvv8p` a0uEQ IDAT^̙3%KA.Lp${葾;Ie0+ $Xppnݺ\rK..2‘k,00PԣGΝ@`aYFdkkkj  /n2 ZmF! (#p$xo‘ba#ݾ}eoGG6j(5e2Hd+W(222T9Ξ=w8={v.]:+xq#ႂa(P@rppJ.*U\r277ϰ:u'hÆ )UZU^H_ׯرcڸqq|8+‘@ Zl;vdP%E8HA : tm=zԥdXř I;޾}[QQQ./!&&FoVllKI۷CSEEEۊ6u)&s]ݾ}[ .0u@fTP!ի)Ο>}:+N:۷:sN>7n(&&ƸJʓ'ʖ-*UJ*Z3洒}iΝ:|?k׮ƍ [;w_ՙ3gt]z5Y Bvvv*Rʔ)rU*UyԊO?={Ӻ}qF PJԨQ#5nX ̰N>5khΝ:~.]d e˖M痓ׯFr2ގHWXXvޭp9sF/^ԭ[333S|('''9;;M5kT9LT}9~BBBtٳ~5ٲeZjnݺʙ3 c%&&&7oRJOn!5ǎWٲ%Ӹ"_)ΙeWu/\…uҥtaϞ=ZxV^ȗBSբE ؤa$O:tPϞ=SpႦM~!ןDݻWK,ڵk~rwwW۶mմiSe͚5*}F)ҥv!u<͸q}5o޼W>1cS >L-ZШQTBW#5?&Nzdɒ>|:u$ W4bݻ7Źw}Wfz3^ĵk״b -]TW||Keaa!777uAm۶U޼yӰҧ5pT'Nʕ+:yi޼yxKՐ%KթSG={T˖-33/&!!Ae^ŋ'S'4^OtRQPTݻnhѢE2eN:&{)$$D!!!kذarvvNe֭^u[Zdcg}y㨨(}w>}Ξ=f޹sGK.ҥK?~S}TÑuY;\\\^yUV/2NLLԚ5kvZuIgV\^'.^^zi˖-/3gԭ[7;V-R_#GΝ;8p&M5kּR cرC Pv駟|iܹs'Un޼_ѣGoyX*44Tտe˖ 3z;j/iᧇ6{YTD +͂`)SF;vɓ'圗o>UXQg`dLLLE_~i7nh„ rrr_Yox 0@mڴyŋ&mٲE+V|`?9sFk_ѣGհaC{Zre#-66V/VŊռyt}Y}4s4<믿4d,YR˗/Oӽ GJu.>>>k_ѣGG|uJHHвeTbE;$mjРΟ?g|}b}$ׯ_l޼9~Ѯ];}ccnnn~qDVaÆQZbg>}zr!o{"NW_}uׯ_?ڴiO[cG}guV}qI'FhѢEj*֭[|k+/Ǝ:u*ѨQh֬Ym64i/vwy'ڶmӧO/駟FǎcܹLŊqѲe8蠃I&Qzbx8#c<':WnW^ްaC;f͚UTR%~1۷Oދ_={ByӧO92?Bg+:.]Zu֍<0ZjlٲXgΛ7/z}Y?]tݻGNu KXk֬yżybڴi1{+9Wnݺ_8S lڴ)~]~ˣo߾{Ϗo۶-|͸Kz^=z:u]W`0;;;~_o~8#BhOߞ-[~r{d~~~\s5q 7s{W#vzh4idM6Ųe_ /7|S3/>Cq)_q_>zQ୕ѣGѣGk.Zl˟x饗bG*ի㏏9s>S YO!R.\li<ʼnʇk6]β ] +M۷o5k&co믿ݻf6mgqFԨQXg.^8ᄌ믿.g"ݼLժUZ/&OyFE>}SOΝ;Gvvv̙3'zQ{Æ FÆ u8qb~'3+V~88b *$kkD€Zn~K/M_~xk}5k$\s{D>ob֭ ^SOat 6K&+++x8# N:)LaovJf1rBPB7 ]v-ԯۼys裷gJq"'j.̙3'S͚5+־[l޽{>Ą bq疨ҪU⣏>+POo_s5_٤I?~|\2֭[Ϗ={٠Axbq饗]s| ;◿eL:ŝwޙt/~8qbԩSL:5S .zPƌٰaØ={vȈZjSO=p㢺 ];^zbY+V*,Y]tQߕ<:ꨘ>}zKѴi?xK|ʑ >lyQnawc̙;bѢEq+u֍o9͛WСCc);WvuaVVV\|űhѢ83bŊ)9O>;.֯__?8s"'''%GDT\9-~~ǎ1`7o^2dٳgʕ+[l{odeez;,.첤k~B5a„{WUT)ƍkg-7~G-ԭ׏)SSO=-ZHYYYѫWX`A\qYضm[z>w]7n3gΌ믿>rssSQGϏ_~9%@)GٲeK<#Itҥw}U?x=PW֭^']}߿,,-'''&Lv[TZ5en߾=N;#GDԨQ#&Ncǎ- mU^x'(|iӦׯ_lذd3f$_IoL/Nɓ'G}˗/O8?[nEּy۷oի7,a1dȐu9͛zUTo9}٨UVҵ6l38#o^jy~JқA+777&LPq饗r-r${7˄f͚Ix ]vѿ"]\+Wcu]t݆  ,QJVVV/)ԍfE>^kv}xNKs9X㷿mRx%{޺Fѻwb[v޽{e> gyfE|t2-*ީ}0`@lܸ1麓N:)^|=c_z^z)vmz뭸ҒN:-4iRjgdeeş4hPu͋gyr@iQzk8p`=oM8VZ|xӧ{1c$-[.nR;oJ*|<}ݤkF۷OE5`o\bE3&M~|lْpW_}Ut[nM8/QOf+?3>]z8qb1xq&]3Ă JӟT*{'Ӿ}OɓӘJN9ŋG׮]㩧JRJEkg~5k#F(Ҟ%777|Nfѱ~~'}⦛nJO>1hР]\W]uUm6際o n g~aPle|$]s-DfҔ(n)ZhpzkݳghժU]aÆ%O6-MI 5#Z`As9qǛoYk&7o^3nQFE*Ugij׮]ҢƍLgqFIdz>|Aynnn5*DNNN7.-}Y\%,ٳ;v(\o?IK?F1~kFڲpI'őGp}_3+W'xbJ,ٳg ބ MŲ{-6lyyyqؾ}{|駟'|VիWG~~~mٲeL<9*V,^_t~W9K:4l0>=z.~qh׮]Jwk֬I lnnnȖ5ku=zĤIG'pBt-}ݻG*Ub˖-Z*.Ӝ|6mD=ҘzĉKy-Z5jlСC/]4ZlDP|ʑ+quѦMxbv+g'Op_h .+aOY9("ɓcǎ /JֳgJgwq'ĉ~skգ[nI~"'''F+WNc1uԤGVVV)O|ٳ㫯uVZdh۶m+ҔJγ@Ϟ=cٱ{k֬ k޼yt!_NY5J^^J:?3K\VÆ O>Ilܸ1zguV|iJ?5cƌ>8f͚Dk̙ gy IDAT+)psIyٴiӤeUV1 L[PFԩ>`L27Κ5+ 7Eϟsԩ5jɏ7rrrϏG˖-㪫J[IgϞѽ{-^8w'|rҧ˳͛7op~G ҘN:餤^{-eg5l0e{Wjբ^z _}U@(GWf͸+?>DO&+V^==b.=zH8oI9Q\jUҲ[nS\+(uō7O 2$.\X"FϏɓ'GK.OK=_,Y$iΝ;1Mt!-^ΪVZ*ڵk'[.Id*u(KUTΝ;o߾*,ҥK6lO?tJ)m5jHX\lYm۶gvd,YtޱcǔIrDzebĉZرccرѹs:thzQRg;O>9vYϼꫯFÆ cРA1xhܸqʳRYժU+xw9SvVy)G&_1 r$?k׎;ZlrHm6:w\*e˖%vi)?3RU, *e)?%+++zذaC EEo~|}MOjҤI㫯=أ-őcz#(wWJlƍemڴ#$)խ[7MI(k+V;^xa,Z{ܹ3&O'O.]č7;wNIN8!e]&L(3n1lذ1bD\ve1lذII(`5ҔdիtqƔ#ˋdȢr eBYLy沎P1Y)B Z稣 ԩSKt+]vp ~#ҥK+իW%\~x̝;7%J"*UDvvv_g#2 _B*IQo_ P:~OgϞ1sxbРAv%???СC|)פI=zt|'qmG'HY\rُŠ?ʙ$@Q(G@)^zYG(u???\? Se˖,Oo͛yEEbժU)VNcɒ%1mڴիWve֭q5jT}ZN>N8b۶miLTx^Ҳa 4Hc"(*uT'xbԪU+6lذ#cMs}q'Mv-ͩJ*qiŽ޻Ν;cĈC7Xh޼y+-[LSk֬xgONs䮿,:4iJfРA ˑǏs=7<4*cذauքkz,~zh/ *RTywcʕ%#??? s9'zQ3{D*Ub˖-_?vw̙3wI&q7$[D{taÆ##"~_&)kN;-Yxq< ʑP)G@)K{MX66lXt5ԩd6{줅z%}.9㎋ӧrC 7|3Tts8^H&777B٪UQF|7Km۶TRc=V=bԩy]Λ6mrdVVVs?x'wA)-GIWW^yeZj+WǸk,.,vؑp޻wg}Ҙ( e2پgqF'|C Ix]:_>;wLk-WUW]t`>|x+LZt۸qBGuTub/U֮]fϢ[nي+Rr֮]pVFXlk֬M6$WDW_}t^zUX999~1"|4%JGS&]s%) P\ʑPFOM7ݔD?>oܦM8sӘ*5:vXcƌ;MOS57.{IrKyߒꪫbƍ)٫iӦ gj _~e*#vu&MϏW^y%%""/R=ow իW1-Y$.k=СCť qqW']sUW=ܓD)///~SO=pMVVV9LM#GFZ6lXJx.~8 V`oԨQ[O=Ԥf/Y$ƌS}_L٭p@g}sM9/Ry˖-cmڴI{D}5kVy-RrNqTZ?իWKۗ_~z 6$\Sb"=iHH.(ڵkpw^r-iLs2dH7.ÇGTװaøۓˋ3<34_׿.38#N=bQ^ݻw5_~y,YXŋcIp/;&'H:?#c|pB룏>ׯ/q^x!wq'']G_~eR}g͚5qG˓;qC9 ''']v5qǯ~ʚ5kc/麎;7\yJ9g}v5yyyqǐ!Co-LyyyqǠA , 6j(wWDVVVM㎋%:'?w֭K?ϓl:~XtiΘ?~< .oXb{ ?~˻Ϗ믿>dߎ:ӒiѢEѹsX`Aumڴn)-S4w}G~8^+,ӦMC9$']נA8qbZt뮻+pرc]v1gΜR˲r8 u[h*UbĉQny衇xsʕ+s]2eJt%V^իv;vĀ]zoNy߾}O to9^}be[wI8c=k׮?U֭=XT\9e˖E;;vJ}lٲkV&LUJ # N<ĸ{]1K.1`K;E18N>,ںuӣq);UZ5M[.p… SNqYge˖x`̘1*T~8:uꔒG5kLfʕѩS馛b˖-%>sڵ1tݻw|W?׬Y3%? . |޼yѿظqcoc1o޼kcС }=3|ѷox뭷-"I 2$͚ԡC_R`Q|˖-q!SLIZL-z+t M6%]&L<0eO9lРAq7XϏ'FVSN>o^mO>dp Ѷmx L͚5^:wՋ_|1ZhQ?~|ѷoߘ2eJ~""V^rK4i$.o LVVVqqi]k.pݖ-[+?Fׯ/Y׿ꫯ-ZĘ1cnᆔp?8Ó2eJtСз.\0v9툈O?=6mpG׮]cԨQu}7qe>^V8 /w}wTPpmaѢEqI'AG?Xgnݺ5~8]vwȑ޸ ?㯅O_u֍!C$-5ED3<ҥKg?͛G g-[bڵrxcΜ91k֬XvmժU+LR`Ǭq1gΜݻw̞=yyy1iҤ4iRԩS'uݺu<07o7M> ƢE^7x# RJęgYc2 ~1|~]vqGVQFv;ӦM^{';vw^J0ʊ;3:vj…ѱc8cO>qG^{UVXfM1iҤx' }^ztMd<5k֌;w}z;2%\oVpeHH(*V^xa,^8\eggcҥ1dȐ2+7ǜ9s裏N۹UVaÆŒ%KKӅuYgżyG~QGoFxzNJb„ q 7Ƃ-cǎ-4[D1gώ/{ *K.$W{͋[o5rssv.Pz#iРA;6>>|xԭ[7gTV-~ŋcرQ~c־}1cF̝;7<̨VZӠA?Ċ+sפI6mZѹsߨQ3fL̘1#{+*T?oԾ}xK/-^Gyd̟??.䒨TRӧ{QJڵ[oUVň#J&՜8SwމiӦ-2Ljz B֭_~o/^ƍoo9M?xk*UDNO>9j׮'o߾Y6mҚ9?~|s=ƤIb֬Yvb٠A8wqGNNN Fݣ{xxcɱtb핝:ts9'8㌤G^^.g{g^۶mW^_~9FӦM-[ѭ[袋gϞ)>Uf͸[㢋.+Ə~iobРA1tД|mw1TYӦMKaթS'~~3gNL81^|XdIRJyѷo8Ӣ^z)Ln}Qn:tHv)?mYe"S8Sم 6m8?fٳcҥdɒXrelڴ)֭[+Ws=YfѢEСC~iy;}{0gΜXlY,_<>ذaC|ױsΈUVԪU+whҤI|qGD˖-O>$fΜ~,[,>X~+TjՊ5kFӦMe˖,9Rx_ _~xk֬{wj*:u?ϣAiɖovk) IDAToNJ+_Wlڴ)"oРA4m4ڵkݻw;,-ʋ5kٳߏ?0>c딛ѨQh޼y4k,8h׮]'yyy]L}8^)NY#( HԨPRI9(ʑ@FQ2r$Q# dH (GE9(ʑ@FQ2r$Q# dH (GE9(ʑ@FQ2r$Q# dH (GE9(ʑ@FQ2r$Q# dH (GE9(ʑ@FQ2r$Q# dH (GE9(ʑ@FQ2r$Q# dH (GE9(ʑ@FQ2r$Q# dH (GbY`|pRԯ_cFe #(GSwpYG%jE9(ʑ@FQ2r$Q# dH (GE9(ʑ@FQ2JŲɲ+D:5:?*ur#+???Cg dH (GE9(ʑ@FQ2r$Q# dH (GE9(ʑ@FQ2r$Q# dH (GE9(ʑ@FQ2r$Q# dH (GE9(ʑ@FQ2r$Q# dH (GE9(ʑ@FQ2r$Q# dHwY?!r ݴrGZ R-B RnӖm=eV(lZ,&RPz(`E* r 2. D cL, !$_^sGI|~~?#####################lP[r":#d6q$q$q$q$q$q$q$q$q$q$q$5=kvѪ9 @DDlٲ-V\WckGC^^*5c/ktjIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIjˢE+bE 6lF Uđ$F~0cdUMđVH8H8H8H8H8H8H8H8H8H8H8HJlmrJ4kvz8DYYypB:EllpGv_jIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGIGI8>mq_pRm+V82m######'i;.|'c 6',q$17oQ<Ĭl@B&M f{ jIGIGIGIGIGIGIGIGIGIGIGINV-Z4;g{ HaX cđYңG={jω-cVH8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8HJlPTTׯرcGڵ+ˣ~ѤIh֬YFv|sH c޼y1xw }|ͣ}ѹs׾֭[ԺM6޽{QFqgfx"Nv[ln;⬳DԶ;v+ަMDo߾裏*޲ehҤIuعsgYf}đ@[jU̘1#^xXzuTTT\;vEŢEbڴiѺu4hP\wuѧOq뮋yv[~^@nx.7|3Q?#ƌS8S38@flٲ%=JO2%FQ3&{Z;??ǃ>XkNHN?ӧOKZ|A<裏F۶mcСq7N@Zmcǎ6mĨQ< 6ĽmڴaÆEAAAFO Ԋ]vѣ_bs=}S^^?OK.q 7d88f/Bt5&LiӦEb„ Q^^:V?'͛7ct-[FNsύ&MDnnn4k,vv]v~(((7h=ztW<ѱcc8~#y饗bQXXxv-_ףO>ѢEj[RR-W_}5^{XdI8p/[,zg֣GرcQ+DIIIMđQ9p@yDEEE;cС1lذcjcӦM..,"">OӧO{Zؽ{wp 1wO~ 6<n曏Ν;#ZuJNeee1tИ0aBȮ]O<6lq93fLįݻw>}z 4(ku& đ@ŀ⩧[{.VXÆ V]vY,\0^~ԩS;wn|k_7t@##*))믿~}4hwygZ*8p`;1nܸhܸ_bE/ 30Pđ@_~y,^n:ϟĺԠA;bҥѭ[#nݺˢ(uI Tj߾}qWˣw:t .Çqw}7ػwoq$PѣGK/tSO=M6TGq~#&)SuE ~4iRĿC=999cQ^*3gT@mGbݺuq7FEEEM4)fh1rxG7k֬D@mG*cԨQvr-qwWݻcȑG D88ԩSW_r!Cرc33P;vl 2}x㍘:uj&jKlq(++;~z_bWVV7n<䱼˫5O4|A8pvZuY>"~Xnnn| _d>n*ԩSL<9Cխɓ'oW\qEl2q۳gO曱bŊXfM]6nvݻwGYYY4n86m-ZGv_r\|qgdC{bf͚X~}[.v;v={DF"'''Zjgmڴnݺ\_WSOPcE믿k׮5k֭[cǎwhذa4i$rss΋;F.]K/ӿ?d¾}bɒ%hѢ(((uŇ~5l0qq9DN}qƗQ3gN,_< ?(--SO=5rssOmFNk׮ѯ_hݺuGos΍5k(**:hڴi4m4:t:t=zĥ^z=0ϟ˗/u{%%%QTTѨQˋ͛G˖-[nѽ{ѣGm6đTXx/Ė-[l+WFϞ=y>᯲Tu;w<~߾}c޼y><=C瞫2nܸ(**ti>l~~3ą^xȋh>m1~4iRعsg1#Fܵ{+Wv[Νtjjժ*]{>W=o;n۾}{r-{-DNQQQ̜9p IDAT3~_cϞ=U_ZZ۶m 6Ē%K>]t/< ]te޽1lذJ6,odp?~|,[ڵkN4)~V{wymQ?'ԩQs<۷o_̞=;~3gNl޼zCF߾}㪫+2ZhQgsצ7x#&O/cǎWZZz5k^8sύ+"nƌTqqq<3ovrO+W_|VZŀbȐ!1`j?ٳ_Y?|<ӕnꩧ^zn߼ysL2%Oׯr?>W ⥗^:xv⪫#FDvj:}?qxw>^^ٳg 2$&\zu̘1#~_ի}w۶m;guV\s5qF>} WH|1f͚)=']v'|rN;-5jʼn8{񏾟ּys bƍ}&訮nݺ*Cɓ'G~~~N]VV>l{\đϏ^{[ ,_2GaaaW={Ty1\re}zt!n 69sTk֬k'<6m;xC=&֭[&Lx ǽ]t1Hjs~ős=oV[Cqgyg|0~Z9M~8>[yJu`޼yѢEzW^Df⬳Ί~qcŇbRWm̘1V+h :v(sƌmڴt{iii?D cȑ/})LR+a,]4 ڵ)S7n1rh۶m<F~VYYY̜93zE:5iӦ}G}Tk>V1|ի1wޘ2eJo>n(G]3gFbȐ!F~ѦM7n!wTv#8SRR7o7|3f̘wyg\xѪU뮻D9@Mm߾=NZ>?xuUFbĉU裏FIII&"h߾}[r)++~q7FqqqVfXhQ;Ǝ |>۷o~YYEEEL:5tկ2O?W[<_5 ɒ%ѣGx衇h>ѳgXvmV??.xW3fqqq|[ߊn)k-f͚> ck]6.1cFӧDž^?kŒj82{ѣG?yk׮]m6ڶmgqFԫWڵ+č7"@Vy晇~֣GҥK86~hڴi\y1k֬(//H|V}رi#'''*6mZf!+o߾׏;YsO 4(\p$O?tt-.\xL͍hР10:뮌^0d̙1tУrrr"///7o~Lϙ3'+gG?>}5jyyy[s]6կҥKiڰtիQ^\8֟SN:Wo2eJ\s5G} D^^^4iҤkǰa9Viiim|[ߊI&=g޽{<N ./ucٲe`xUbӦMVZjd>|x >V^-RJܹ3f͚f͊}so~3իW;-]4/_^6m7 N=^{m˿Kl޼,Xׯ;/ÓQ׊c[oU]FO>ѱch׮]4n}bݺujժxcܹV{_q%/^㠺&LwqQE5K.$.ѣGo>986Ϟ={bbŊXlY,\0̙SU?"Xvm̘1#6lxXdItMG8I/AEϞ=Ϗ-[k׮ +oV?oy\Td ,Y*[IYl^='Z%K8h;Ide9YK?}f晥Yz^^/sOsMF>>Bԭ[ =ڶmVZqV3ܿ8s bp8PZ5'899!55UQWW5lmmѵkWQFRJ1l6_ƣGpM̙3"W}wG{b?/B!(9%b```XXX`„ غu+VZ`c׬Yӛ"B!B5k5V1rrrJׯ__BEŋ%>|֭[uVlpwwGƍi#uw:ƌ>>B4j5B߾}tRddd`Ν +˗퍘(8&O?C111)Sz1MiӦ߿?.\O>!66aaaB~%HTё1f̘/// ahh[[[͛7{ƢQF yÇpwwgLŒ30tPdزѼys4o š5kb۶mسgHѰaC899I|lmmt|-0V3gD~~>N8;wBaggCa"?/B!(9+V^^^%o~aB!xm۶-wkΝ;Baصk\zz:,XE^^^6lXZѝ9sFłQ|A/_gXQvm611u0g̛7Bpppɩ<ϟ/rbdf#F{_V-L4 'Nɓ'ȘaÆʕ+SRO>;%K`W2544D`` NիWc͚5B+Inذ}id 6`Сonn0bƌ/..ƸqC 4HȆ bŊ(_KK pvv͛71|>}krss1b$%%SNB*;^&HƏ5w*("Bk͚58{lED!B!) F5γl\pި_><<hN:޾"Z%B!B!D֭stlNNOM4o{8ˌ~rFyGߑ&h,:uJ\jհw^DDDM`(333? .իY,r f̘!N:͛0aץX,?uB߶meKYŋ1p@=x`~}Vyoݺ%R ܹsXrZkhh`ٸ|2ㆄXwܹ3RRRЦMmii+W}eee1V|(rrr0j(g[j+WƦEpetY1 ޲eK={2;Gqi$Q OdGiضmdRbee#G0V H|׮]… GBBF)s ӨQ#\pݾ}AAArB*OVԣG={˵{pddd ;;߾}C5333XXX\oKqmdff:uh߿ҥKS|ŨUѺukt͚5P./^'p}hذ!z+++hjjJnQQRSS,nݺhժccc> |/_Ɲ;wJ^Q `bbnݺe˖r+==IIIx5kA077LwB!D>n߾ͳiܹ 5˖-æM.]T~%==8vl60p@,^B|2pIk^իWǘ1c( 7j eeecǎq|rlܸQ5;x"00Peݻw;v_%rk3CCC`ĉB+A'rm߾}QQv܉bW.^6ŨQ&q8zzzr~ /^`Oqq1|||ëWÇ1h VZaÆ!T6I jРׯ_P?FXXۇׯ_tÇc)W^=7{CILK.WrJ,,, ƍ^^^011Fq;v VVVb޽{_nݺxЯ{!ϟC=χt8vލ+V 331, NNNXjHggg$%%9sF.X,WWWiӦʕ+<={V󳨨شijjj*B"]򓛛5nܹӦMZ6cǎĉʕ+s܏?0k,ܹSF~~0cɒ%>ϟ?۷o%K<#0+Ǐ# Xgέ[b۶m2d$LUPP}<_xjIQ^^^'GRXXcǎرcUF OOOCJQVLk׮ 9F<7n ]]]˻i(իɓ I6mԩS˗cڵr޽={⯿{b/:::C=ϟ?K&]$u6l<<<ۅGFF+92##!!!4nO{b/zzz$Ny @Vv;bXzcv%͛7CWWW(իWk.lٲEs֭[MA8tw.aaa!&NW=CB&Dկ_gta޾} ooo!$$DHxBCCannQF"O>qu#o޽h׮O7p8F-SN lI-[bܸqeE~~>_|57,,,x7++ ~~~prr鵔SRFPb${rQt߹79tZl#F"%F?o&]?~D~%?{?cǎ8qHml6Ν;aÆ[nwB!b ɓEN,WƨQx߿>}`ǎb'yaСɑh ep~zmk:uBPPT?/~666IQ):tO>aǎ /_OJm˗l7Q?~XyڵkRTL󃻻;16mb?LHigfLchhBGGGQU?Zj1pBMqVBWZZݻxbƾ(¦ IDATk 8x $>4ԭ[B,˃~@eZzذaիb1&#G$&,_%K_~/-zzzCjSN1*BQ}I wMԖAFV)0l69v)e8pwa"++ dLf#<<fffxE;.\GGG|qr81&iiiQYBR*}AZ=z%Ky氱ݻEhZY1>244c$Xϟ'bĈĶmФIX~#"ӌY,ZpfͰac>}LJ<Օ'OJǏxLPP:w,־}{ZJaUVykԨc2sq޼y2[I޽E|oߖh]v;uK }+BQ%VTaE*mrJ,Z\ڵaggnݺkǏk׮… x| 'OƝ;wi&[ ̟?l6g[f%3h ZhGGG4oV|G8{,ObfNNpl޼Y߾}Ocر011ׯ_(={`ĉ|޶m=5 wwwժUÓ'Oo>\rBx{{Ç2)_`R1ԫWzzzx-^|$$'' uaI&$vhܸ17y׮]É'xnLa̘1χB2p8ٳ'޽{]]]8;;*Y([˗/3x>e˖l Gn0i$xzzƍ\, ^^^2e#޽Cdd$V\Xt)V\)SU`B3kk׮ڴix?~/^;ooo???իW{LNN.^;vرc|cv܉E G*7a4GF߾}h˘aL8Qj-fG-u$1~x |2N*6l`,Ѿ}{̘1Cueiԩǝ;w"===Kf͚R&kˋe+WСC,..fbT |}}%0=99)))sd(9q=e%K o߾=1b7}Nڵkynmݺ_~EddD>WZu!gȑ#wfre׏B(gNNj*aaaǮ]ZBCC[3f $$jj+;annw.??'9aÆ(..ƈ#&FkvXժU''ɓQ7~6֭['LjDs,6-Ħ-Z Kݻw!޼yw طo16lr hhh`ݺu߿Boaa۷GƍvKKKyaРAɑiii\PDqƎҥXkʚV^>}>>>]w{PYmܧO_+1VZaӦM\cYYY<7^[b!88CLLC߿#ObdÆ Ct1vژ7o=z!Cϝ;W6*xjjjb߾}#m'NΎg=___ƝvիWXnѩS'lܸQ`hB*:wwwDFF2&F-[`\_~ҥK $41ɓڵ+xaa!.\(:dٲe8qϸ]&Q".\`MW.]P߿# !*N:_GܹС֯_/0v]DX<9EBdH2 K~"[dd$8y///1"UZ͚5c#oz:RSScLz Zɓy+++ڊOaU u릐ƔOu$==]5Oyekkk +++ǎkB*"B*66'9^>ט6>Hرcaaa{{{vAAA۷/z)ѺskgbAAFnطocAtuuyfoߞߚ5kЯ_?[u۷w،3^f͚sXjj*υ .p=nٲ%b0n86iiiχHGiӦ\c8r4h zušC0glذdfcرw֭+jjj¨Q5k">>ٳgKoܸh`K!ܹ3qFn­[9QUfllm۶ItR\rgΜxLxx8Zh!ժUCDD͹*O|\m߾m۶۳g233ZFn)٠AL>k,99YX!DUcΜ9"gܹ011e(!T8oe˖[L4\k_*ĉ~:ט16o,s-\=z{Dm+{SB/_`ݰA-p5E$SL/6,U_|8'|:w&M(: \\\瓒 Q%Νc9r"Q~ҺwQ’'d|Rb.w-S=YiذY1z>6kJp8b7eJfXpvv#2dM9FC!ݻ3go暫_>/_.kK~j|Ǐi3vZ055UuXXOߺu$jw$$$NNN/7w xΟ?/^ZH_I& )]]]saa!ZgQF){ի^BǏcҥhѢz]v1$DU:::^o߾}:rssK6oތ/_`Æ lo~P~}kر\ɑO>EFFLMMEzi^(oݺ5Wrdzz&e!qrdiyyyALL ///@TT.YfRYنtrߥ,:u l6kW歪g̘!+>>^Hq?SBcǎ􄛛. 6ttQ)O"??_<%GVoٲeKɑ999RJXD~#dG(CrX,8yKe1%Gjii)EqQz/F ,᭠@S0¾"Ŕy\pA䵔AFF:tP!DQr$!*J*ŋMMMzܽ{weС\ɑׯ_kMKJJzb0h ֨-Z}q ĀлwoL:CX֫Wgʕ+ƣpQ7SLAͥ<yχzYPz%?J- ?ڄQ^=մ%lذ6l@vJnz*ɞiYT֩VϘ$-E]?~P L.^36`gϞ<߯\"kזvXDwxرݨ&x/^S]]:X "HOOGNN@1bxyySNGm¸xbL,{,UJ#Ls/_Rr$tMD(?---EuuuǏ|I8gll,="EUj\^Zֆʁ$ B3 Ђ|U!)ϟSr$!Aɑ(jժÇǰaDN>?~ 00P!}Rs1[ZZ)"KzzzPe®rT|<J@e,455&G2-}Y\#NT׿*GVgyBHyΧ=BT\PPЋjB*UڵkL⤿Ґ&Z(W^xQF2;*sttđ#Gcff&͛e˖ gF֭Caĉػw/I<+6@I4aɲ'ay :U6:a8D}ޕDjB!Cɑɜ9s)) ߏ?ĺ'ePر߿9&//{ADD~z-uttbر S~IOOǔ)SrJl޼Æ *XJ۳뱢mk8!DׯFtt4j5EHL󑛛r7!Dׯ_۹LMMK5iDnU#?SNIQ)NJJ c⮽!TUO rjχȖk!bcy N{ne *,z(9BHB*(Ep82WlCI<֭+V`믿HII3g`ll,tm;;;ؼy3"##& z ...X`VXYe?f˴JQ]EݑGϟzes?{ +V+`aa3NRlZCNN%GB*ѣGݺuT=BCCGEEUHNх%,ފV]2&IA^D|;JQ(''VAʲQCՒe!77W6|q^)JO'{OÇ []x,/]|-Z55_ aaa(,,h]555xyyE^ݻwK3@SSk… Z۷o#1333Bx=BCC@9nB$cjj B*={{ "00]J,OOO-[0& V{*U0V1D4JOOWt"cJfR ߍ0UdQNi\Hbbb8_Ȋ2DRL?w!RqQr$! .T{;rf͚\,/йcʔ)\cٸqF֭^:bbbPF ӧOk]XXXp:u\k29{,=z|BTt3U֩S'>|˗/ !T|߿GddD_OOO$$$XjZj%+]cǎ?}71"۶m&e333kb `' DvsM6E*UD^K_E6B ܘLUM Ͽ%:(9 Ɔk|AA+s`ȑ\ʶ &###t֍kl֭JQRli%4֭[RYKS^Çz$V\EGGs=nڴЋ B*5kbҤI8 sS"Ǐ%^WZTݾ}B[*8ozMV`Xg8p3f̐sTD:ujժ۷o|ϟ?ߣnݺrLto޼ʑDbӧOcС BBPr$!جYV{̞=R;GXXO/-[nٲe߿?ڶm+L4 'ʕ+-իWHMMuضmHeffb͚5%k֬)V¨6WrdK49Q#WVMiҤ x{{_~^ 1k,*UW*"-Z`ǎ***ԩS^ė֭[ިWBc!H&\]]u֒#""cǎ9^SҒ# !޴i;ԩS@Ν~V\p!c`||<"""-d,&&,ZP`iFFJKeOD۷o,Y0 l.nr9zMVGVr >|Hݒ* X[[ɓ| ~928pH!jԨLLLNHB(9 qƘ1c֮][2SSS̛WF3gט5FYš5k0f̘o߾avWKqI͟?'1ؘ'YBGG {PPHMMza p=3޽+R*CJ(8;~Ve˗% l6HKKCfʽo˗/sM05*ڄ&MRt(Խ{٠A3R݀AQN3g۹n̝;NNNRQZaa!PTREGGѣİaD@ժU#qGMMfϞ=OHH@ffuZ}@nDISSS̟?kǏÝ;w$^`ҥ<- |r%"ׯ|1b=ϟcڵIH%Ѽys?k,;;Æ # \K.puuy!Dلq=֭n݊W^!..C H%bm69_|cdw1v*U}r_lѢ|JJJ/ *]_FTThՌb̯I#G(ϰHxBy9r$455?zcD;tRT%caa!BBB0qDL""J$Z*8`4hZAAA۷/C555ٳGm~U,Çڵ+"##fZݻwpvvjMo>}]cŊ<}||NLII=޽{"]|ֆ=XXX߿IAAqPPP???͛7$8kıpBgϞ be˖$FDDHb!*cHzp{44>!Mpp0`HNNƨQ/s\ׯ֭[Fj!?w |2L}}}EFhӦ cgC2IWf6ln߾xٳa‚q>{_Y,Y߾}St<._C RDM&Ϝ9#rU_6m ;;;={#]BB@̛7߿WtJK___=@CN@*]133[rH4/f,+h!DwB*vСC?qZhӧÇk|;wDVpḆX,lٲ...2I͚5qY4i҄k˗/F~>|`nݚnT777)O"L0 Bbb"c^FFaccSSSMM ƍ\п:u>=υ Cѣyq?lܸ\&&&<?C]]Bǎƿqơ}Bpp0ZḣAjqB,oƘaɒ% 6vū]6ϳرc޽;=z$yyy>}:-Z3R !DUҥKXb7-[LhTbb"ݹbxzzٳYZZbҥR9hE&G([+JQQ$%j ڱcGƪSO.WפTVll,h޽aÆa͚5xzzzU K|̔:i )oBJ+{ﴴL ggg|d}иqcԯ_߾}˗/Ke͚5 Zdn0fݻd,;;ܹ3 ǏtQXXȵF^0|pWZDX:u NNNر#tuub$.*\5jDwbD^кukN:x޼yk׮ƍ|?a>}Tc&"۷oɓ'R]S__Gu&յ !-֮]s֭[D`` N* Ǐ# 癛8q"7!"mZZZc츸8`ёcØ1c+@Z ---W__;v%==ϟ{4yr=2۴iзo_Eg57nWWWvK.)N< GGG֗ԇDRSS3vXܺu 6cd}Ce,|UV1*dee$%% <&$$N7oTdMѾ}{ϝ;7b̙rכ7o0vXcV\IG!D IH%ҫW/ܼyHIIXe5jXtUaJAHMMżyS1??gϞub5jfX,DGGְaðzj.Pܹ<׹Ґ5BLL dP333DGGŅ+۷oطoǸ1x`8~U]d ֮]S HHHyMDFFbB:lmm#F ["?999Xf4hڴi#b̚5 999Xx1/_0o<`رptt5 @ϟ?G||<#0 ۷os ByسgXQѣ߿?9Zj1B| CŋcXT }^Zɑ-Bnn\ϩl6<<;ٴi_|o5ώy1nܸxk %KFcРAg߿KԧOXdI?|˖-ݻwz+ s0Zjva9SBn׮]fddcڴiQNرcX`A?SZZZz{*٨Q_ϟSL_|1Oc0r1rȸ뮋뮻n>h۶m/1Um_m۶ FzqJ-zhh"1\pA|wZpNw{ 1}YkE.]bÆ 9;wڵqL>}bŊNn|qb" ^z90aBp z"GׯO?=Ə%JĈ#hС;ْ%Km۶yi ]6vݝsիWG.]bEXՏ;/|vmqGQUɡs$-[f;QZZZt=w7ow}7ϟ˖-kFrb}ZjE˖- 3f<;s!eٲe1gΜXxq\26o*U5kF:uuQRBrqWW__}U̞=;/^+V(SLԨQ#233UV wז]t.]cʔ)tX|y-[6w8h}mn_5jԈGǎ}{\GN>:th :4_\6mVZ4l05kc:# ߫WիWͿ222?`nݺs=lٲqI'E^k׮ 6mNw}1dȐXlYIKK޽{GVVVt]5dȐ⡇q͛kz*8ꨣߚ={v\z1k֬\۷odeejM^{m!~qI'ń |R SNEܓ-X >xꩧv ,(>v;\guVyإ/nݺŤIbŊ-Y$:uT$!̽]zzz<3Ѯ])SD֭cԨQE;_|'|r;;{Wı[5%M5bȑq'裏uO qA裏N9sZj׮R IDAT=Xx;옼f͚ҥKu]q%zM_}UuY; dp q7z=IC ""J*-[-[w)N:XjժUd[իgQ(QڵkGڵ r;/21%JN:E^SO- IBSj_wr~΂qu9e˖-RSS3κE3Gq/xyZ3%%7kpycgRSSo߾ѧOxg'ɓ'ƍwznÆ GѧOw*U’<@TZ5 f͊VZŹ7xcvx)(K,ǣ>RRRbq뭷zmvZ~{p㏏g}o4iRuY7{6mĴiӶ{/;-+m書+⩧#8"[N-Z[o1f͊N:ѣc+#~JgϞ;;y-.͛7/Zh~{Ou;vl\x9vIVbРARNn)?8o.]u]tSOť^cXUVf͚7o^ޥk׮qM7:e˖Kcܸq1|i='N޽{^z=]1H`{ŰavxI&ѽ{ݻwdddae$?^5i$ϝ}-zgB<`0`@ɩKMn*UȺW.]:>8cƍ[oܹs㣏>5kĆ \rQNhԨQjժ@_~ %%%%㎨]v\uU; nݺ5~x3ό+Po}㮻'|2suNZZZ ><;B?(Q"hӦM߿ӧGfo[~黼u"+++?owʕ+Gݺucܹ֞sύFţ>1`x'bȐ!q)X@aƍ1|8p`:yp+S?>hٲeϛYf|M#RRRvicС]B q￿K}}􉯾*nmذ!1bDq_s4h/B^{Ocȑ9۶m[ 2$z꩸ۣ{;wn\+8v1z8 d]K.{,q/BL<9nƸ+ ,$<_5j D\7oתW_~y̚5+͛ ]2ecK.$ Ç#GãѳgOU$_?|?ΎqQGC H--C!GuT92ڵk,VZō7޸q_~eqqƋ/W;37nÆ 5ky{iӦ9YhQzq衇}+Wz~i 4(222+U0v1a„&Ѵiӝ""/_={vs=[nZk׮{'4iuQn }77ވ/u|~Q~tڲeK,\0f͚/r7.֭[m۶ѣGcړ-[6Ə:uiyť^W^ye{Ѿ}hݺudffnw~!>?~L:5&M3gΌ~!5i$Ǝc˭o9xgv:vڴi1mڴSG&cѢE?_cǎk8-/I&cb jՊ믿>Wwy'?KcǎѡC8裣qQZ駟ܹsc̙shƍcرn>+@3D>}rP]hQ\~qW1:t6mDffvCǜ9sbҤI1vrV2eb̘1Ѳe˼<,ȵxgO>1bĈ_re 4( GuTwqѮ]8Cnݺ馜/3fcܸqy5===ƎvX$ 6RRR_w}7uOqnݺkŐ!CbРAK??_+UTTP!ӣ|~XfM[.6nܸ5WT)/mwE%bȑѭ[x7suÇÇGď7UZ5*V)))zXronݺ36{jժū;wUxk1qĘ8q_+_|TZ5ʗ/۶mNJ+vرc<3QJ|ϑ)))裏Ɗ+bɹ:gٲeƃ>wMVP!J(k֬+V隬UV=:RS}YT暨_~f͚\qxW~bŊ?_6mkڵkcÆ M6yTL#<1p\w'Ύ^{-^{}[nk'JѣG=Gz"+++ρٳgٳoüիW *Dvvv,_Fm;bpiEӦMώ_0dA(Yd\1p@߿m69XlY)׾jժŨQC<FJJJvmѦMիW|Wycƍdkݺu{lx;~1f̘7n\}]/t)x9rnIr矏~i5jɓ㨣*5*Ugy&?Eҥ:*MrKw,SL=:- SժUcҤIѺu"[_Ȉ7x#Dzzzqa믿,";v{/.b$|Gٳ#);w{/=b,^|{㥗^(`‘@ҥKL>=&O|ՑbƌꫯF۶mIKKo=^}8 }-[ƴiӢiӦ֞*%%%O.uoxc̙ѢE"[RSSc1eʔ"ݡYfoF˖- }-rW]uU,\0.(SL]vߎ6mٺjժٳsEnٲec+իWd¯ըQ#FSN-}J({ %\R,L \cƭEYYYxxUbܹqʶ1tИ6mZ4lذO  /'N]ZG>8F/ /:Z۶mߏ~PBK*UAŌ3"33'W{o|'q7G m:(7,Y]vYZܑG'NٳgǙgYh;ҢgϞ;_lzڴiӧO^{-?ByO.UT;̙>hԩSG~ Mƍ#++PǏ^{-zؼy._thժUt!u͛7/Ojjj\|qƸqG=gfff\tEqEEŊw8iӦ; va^{5k^kΝ;GΝcѢE/|.٠AguVyTi+YdqƄ bĈ+ĺu=\pA\|Qr;#v:Qa#<2{(gj֬cꫯO?cǎz_~xѳg"cGݥc[w*?i֬Y<Ƙ1cbԨQ1uwi&Mg_|qԮ];Wl2Tcyؓ7j(|~UT)7EtC?5jT̙3'mۖRRR裏ݻGc}-wL29~_wpGzsDdG_+zOz$nXϫUZ|Bh϶`Ǒ-_֨h\d\ziVϫX|Y3* ڹ=\;1aÅP1c^SO훯s׭{'ʗ/['o?woBEP6l3gΌył b…ʕ+cqƟǖ.]:*TUVhܸq4n8:hٲe--[̙3cʔ)1XhQ|'jժزeJ.ѰahҤI~ѵkhРA1V_-[c'|0.#"VZJrqeP4g8SSNͱ 6Ė-[r @ )Y`M.*U*J*Ue+9m_tiii{ٹ.P0DqP#DE8HH Q#DE8HH Q#DE8HH Q#DE8H. K5b;<^^"" @bEf͊ mDE8HH Q#DE8HH Q#DE8HH Q#DE8HH Q#DE8HH Q#DE8HH Q#DE8HH Q#DE8HH Q#DI-V+WW_Wq@!y]^K8μ8 Hj" $p$(‘@G" $p$(‘@G" $p$(‘@GZ . Aʕ+S%ф#vQUz*]VHH Q#DE8HH Q#DI-H-w tE8HH Q#DE8HH Q#DE8HH Q#D% `Tv5]RSwbq'wgE8HH Q#DE8HH Q#DE8HH Q#DE8HH Q#DE8HH Q#DE8HH Q#DE8HH Q#DE8HH Q#DE8HH Q#DE8HH Q#DE8HH Q#DE8HH Q#DE8HH Q#DE8H.ӂGff.j+5kfw9HH Q#DE8HH Q#DE8HH Q#DE8H.>Q~.v>+񏗊 kGR~Ѽ]6n* Eȶ@G" $p$(‘@G" $Jjq:8qZq@}QL.`% [.ٺu[qG6(‘@G" $p$(‘@G" $p$(‘@G" $JjqSfx]Oń ӊ p$@191|.B/ IDAT2f̫‘Ķ@G" $p$(‘@G" $p$(‘@G" $p$(‘@G" $p$(‘@G" $p$(‘@G" $p$(‘@G" $p$(‘@G" $p$(‘@G" $Jjq?C,]4Xvm[.\r/_>ʗ/Ջ}٧$֭KƪUヒuEDDJ"===4hժU+*v;[n~;Lov,\0.\6lիWhܸqn:ڷo\5iƍ?_Gk2===RRR" Zvvv]v+TiiiEX@Yzul۶mJ*˗/ _?|̘1#f͚Ϗ[xNzzzh"Zn]v͛{ݘp$[XvmѣG믿k֬\˗/˗ԩS㡇GΝgϞѾ}(QDAN!2dH 0`ᇢ+"⮻~ lٲEXm֬YѺu?~|EX@i޼y,Zh+FY-[Ę1cGvvv_jUL0!&L G޽K.5kR@~(V'N>;jժ{n⋻ܑO>$zԩSԫW/n~ $ѣ=z/`|'1pW^7VXQE8(r۶mQFEӦM㏏2 ,]4M43<3̙SdkE?m駟^h7Gmڴ)hԨQ?<.kJ)KT$(KBBH(M7\kپ+%;}&Q7]fiS94sfxx}23͜>OHHT # !B!B!ª'Ne˖>|8޽+Xqt JB!Fйsg$$$2_vv6ƍ'''dgg2'!?J$B!B!B+`ooggg} !!*&BвڄB!B!Bϣ]v"%F֬Y8y$222p x{{\HԄ-VXk׮իWXv-ͅ˖-->~(~B!ٸpfΜ)&?B%F6ƌ+WڵkǏ H%GB!B!B!Dv܉ӧOBWSSܹscǎaРAPRRX<$\rBݻٳgB!"Y?رc.{ݯ_?ܿިYYZZ"..PQQaUV=!D<I!B!B!ZloVVVƼysAGGGYYYܹsqP^ѣG֭!B!Df‹/7NBÆ %2'Á7.^---ƾ?K%GB!B!B!DbΝK vvvw|}}Bt:w8ܹgddwa!:B!BRSSgܸq ֭Ʒׯ_1gMᏒ# !B!B!"+V~***ؼy3.\"`ԨQx#NNG!B!D֭CII v+++l޼Gj1t{eرcHIIZ nI!B!B!J … k޼9^ ///Ν;G`%ߣO>x5KB!B}`޽PRRz,ͷ6lz(9B!B!B!r%L6M`?KKK\v ۷g!*)((`ѢE8|0j֬7==...(,,d):B!BHEv܉o߾m9s& YϏqyGCHuFɑB!B!Bׯ߀ ---" Μ9xA_Yf!B!"|TTTK0qD8<R}Qr$!B!B!BRTTaÆ'Nv,EVy666 c7",,!B!z޽˷U&7h1- O>J$B!B!BX֯_k׮1ڵ+?eee={СCPTTd7i$dff!B!xvggg"fll sssqqq,FCHEɑB!B!BӧOtR>M4AdddYސ!Czj>>|9sXB!͛7)**W^,FÍi !S Y@!B!B!g„ veee=zT&KIڴip}v؁ VVV,FFǏݻHIIÇ!77_|AZ:uyh޼9ZjݻC[[["|\~Ǐcdee!''_|"}}}4m[F۶mѱcG())*ݻwArr2Ґ\FUԩƍ-[6m*+ݻwHHH@jj*?~'O ??}N:PUUEÆ abb-Z}h۶-8A~8ܾ})))xi련kFݺua``j =z􀡡C>ŋw^k ''PVVFڵQNe˖hӦ zMMMY^)HLLDbb"=z4 CSSС:v&M:*)55o)Y߶_իWhܸ1RPr$!B!B!BDrYDGG3Yj:wRDpsNܹs)--ŬY.3ׯ+lСƏ/ؒriDDDm r =Wbb"cgggm_x1222*lܹ3ƌ#<%%%x"q9<{L]uׯF =ۛ݀*cǎmrm/_ơC+))5jUS . --Mtttзo_899aРABp… TTTqFx)VZŷ}ڴihٲ%"DDD`t}&p5kb̘1 VT{b޽{PUnܸQoEEEt􄞞h #?82UWWNNNpuuEϞ=(h?޽֬Y3#%(۷IQr$!B!B!BD"h9mKKKL<`X͛7cРA|\~QQQ۷XsEr;wm6[n4q=}E%XFFFɑGG*l]v!00?)"99Xf Zl ///=_'NIir\X~=H9sN7lH,**‘#Gyf\~]"c8<///Id|I{-ZRc={  9fϞ 777(((H(Z֭[7KT߿~`ҤI8q"իǸDN`|pqq09;v Obƍظq#,--pB888TjLA;+V`֭)..ƭ[p-,^C %KкukǔCCC+u?edd ((AAAèQ*I ?YޕR$T_ B!B!B!rӸy&5j 88X.8*f? %%% A1m4$FV$%%hڴ)wCuV]b#<<}A.]p9#b,_&&&ؽ{w#˻wѶm[DEEIt3,XƍcܹK,˗7oLLLg*qRBB1a„J'Fwu 8=zݻw%:vem۶ XvD*,,Dhh(ڶmQFÇ>|{q;g%>/T%TwfB!B!B!R>~x 6l%%% r Yz,,,0n8֒޿sM68{,+sR^ll,:vI&ŋ{MX"o޼-/^,Ĩ_%''_~0a̫[o߾-WWWƒd(gϞk>|}YӱcԨNKpp0N߿2(kǏG޽e\x ߶ 3 !%GB!BVq֭Ǻx{1õmɒ%XtjUW^|0IH!XpϞ=}LR4ell GGG8q<;vL`IR5͟?~~~BWz쉞={M6hѢP^=?rssc۷/v֭[ZZZPVVws$%%pKgee_~عs'F!1n&tsZZZg/B)J$BО}{ٳgJuzh^gϞX`]"y cm۲ !%G"gY Á:`ddCCCtEwx"^|YeǏq2!88`p`nnCbesB!B=DEE1{zz0`VQtrdKWWB?uc{ǎ+HΟ?%K0p8pwwǂ *,ܡCtK.֭['T^^˗/3&ׯ/rь۷gmig  bر]s֮]puuEbb"DDD/%%vvv3aJԩ6m$R dL>+Wի&GݿgƖ-[Ě˗1b#k֬SbƌYn]xxx Xp!cյB1k0%F:;;cbWɓ1~xccbbE52fΜsnݺbݢE c֬YXd n*09zٲeXs2YhPJJJ0aϟ_͕пO[+x IDATF||<+*jjj3gRSS1|psrr0`|Rb18pPGFZZMV:v숓'O"<<\ꔔ 2DJp0k,$$$+555ڵk044?((W>Đ!Cnccc_˚w1118pɁ3$6jժ;vرcYWYY'Oƽ{йsg/_Wz^Qʕ+Xb؉F`` &Sm&R;vĭ[iӦJ%Fgll}h޼9cR=1/''[neR4ToIz!`hh3g L!HӧOѭ[7Y߿yl,Z}Ň!!B!ɓ'@~~C}7nm777hBDUakk~%"!˷J(;nBnݤ>۷ N[1n^&b3*Z=;v@KKKj <ɰʕ+9sbj*())Itl \vM`R̙3Gb~Ç˗… p0bܽ{2`rr2v)ciӦ|2دf͒Lp57Eeccׯ |~=}ԩS?~<4rϞ=qm0;9l۷4(99W]:uXB$ŋݻ7xtttcرcVX!CTx7KеkWJ$B!R)EEEOOO0Vp##mc1acc~="!c۶m-Dg^^[[[bHMMqvAU~W 6Dƍspc н{w\t &9Rމ'pa>5B||}:ܹN:qcʕRBwjjj122uXrw\\A!?lOOO/Zl KKKӧO2W|Ay-[d)cjjʷ--- ,FCشvZxzzlm"<|bS رc'+ҹsgL4444ɓȐjӦMCv:ǯw.,(WT)S`ƌ2֭[XAO^R9'NxB$# 3Ճ7n޼sÇ !W^aƍ\:wӧO \W(f'BQqaN"""~?k׮uHBPηƍFÆ pjD4/_ۦ pSrd~~>޾}b4-#GezUV1EDDKn͚5{E6mX"<sss~x_f ԩ#qL0f">}Zߏ$>K.ebdyJJJ8x 4i"8uց:+U!22Rj`޼yREnݺ|_bpBݺuÚ5k_~VfM6)88g""I266QF 4440gMMMYUmڴ ek֬BEEEy8ˣ/cB!B*} '''4l޸qKrssVZ,F#_6m؞N 5͛72ydߟKѐ,''G9ɓ'c,EĬN:طo)((Dm۶LCQ19sXc L_444~((6ݡwެ,]A Nܓ'O~7Kwjj*N8]EE{쁒XKR:u{nʃ7oϟYDEEaɌ}e""Dɑȱ;⯿|EDhiiaʕ\?ڲ*))ṓi̘1h֬c|I=yB!B!5xW>}B`` ,--ѲeK"==]#*Tcl ӿ@Ԯ][ap8`ƍ|2{A*i֭ۮ+V`X`cCI,#1bϵkԩSx!5k"((HɈLc4 4ץ6,[5k֠oܹsall,ХKL8o{VVXzKJJ°aøДp===##@ ]!cǎex0*B&11ШQ*=={=~?VzlB!(..I&PQQazW|kW^Źs琜>Ǐ(((6m sss888cǎ"\ZZD>}wAZZrrrwBOOѣ``` eggq5x,*wmUTUUŪSff&bccqU͛7Ɂ444sssXXXbj׮S)˗/8<ׯ_ch۶D Y:u*6l ٩X`-Z޽{Ce}I̪)'G :~J <}u<5kɓ'c|ܹ{f/(Rb}V\)7͛>W,jժ{{{?~/_͛7hذHڵ}ʔ)hѢHcƗ/_X[QQά뇃V؞/^H|mЩS'4hЀo[D˗/8z(v̘1CqmѢEسgϚ9s&˾W7ppp` ϟ'''"# s:u{U:9ׯHMMϑRhhh@OO29s<|6lXz-;BEEA۶mQfM E- pm#//G-вeJ+$%%ÇQХK:;۷3 Ȥ/_ϟ#;;҂Zl)Rwz:Tz^zAIINTJ$R￰ڶd,]>˖-?׶dnp%̘1oq1,YڵҥK1x`=z+V{*l›7opvFm߾}e? ___(B bذa8pX &&111򂓓<<<`kk+WՐ ߯5A~R$ *ӱi&ԪUHUOm722 OIIIոqJ$ ݺu )))"%Gfgg3.ǭ*a1c ѣHsAɑݺuxp8BhhhϞ=Ex3ԩS21\GG&L{cċ/p5Ξ?~"WXR(**QF\={&X߿q1ܼy<lNNNpssCVĚbbbJvkhh 88qzz:6l؀Pz DM,--Ell,< .|*++}pttUn"##o߾X~XcUdxIaÆ {|rUAq򂗗HKȑ#Xf ܹӮ;;;,Xݻw8޻w0em7nD x|'8s Ə5krUL:oy߿#22Сagg'ݻ!C0~&_%vr~߿pwwȑ#nH ؼ<#?U=zSN `ػwo?ƙ3grd*8|0cԩSY?o-$Q?~ [[[Nj`BǶ)S`"'V竈`i055ر#ȒxBǎۦ1cƈ#[~zUIɑR˗ڶmЍx' *\&M`鈏gpRRRmڴA*LƳgZYֶi&`\S(--ő#G`nnkkk0&F?**޸q .DӦM䄴4NKK:ϋu \xkgǎhٲ%vt̙3۷ǏӧFPXXg *V$//BCC^bԩSر#z聭[ W\\D,^͛7+?~,7o:7nݸq:tapyƓ yyy3pYW߽Kn0)|"&!B7i$XBD߾}yS<}]v91W 5>| 8򦠠ӦMXݹs$LKK %FB Wy5ѦMXXX`ӦMߴbSݗtVV[!!*_eK. j)))mHCb9c /_"6ccct҅y (ŋSy킮|+7f7&&hW^#q9ԫW!HBUUUǢ\ݿ?LMMe|MKKKΝ;c֬Y[fŘ:uX1sX[[?@rrXc <<fff5k>yqFB/c[`bhnn.z%҇cܸqBGGGxKJJ 333D"m޼=z@RR'O{!ɥu22266!&L+N8!p[B?cȑX`V\sbĈ|Fb?V۷aggiӦa Ē%Kuu ǿu J|z7aÆaڴiXbVXӧՕoURGFaa!\]]ӧ `dd$K@BѧOSB__ BXX\~WL ~999;R JTSSc)"M:t|/_^0x=zķg1"U$EOOZZZ|E-PǷM__JTsvvf}ƍ>gyc|ϗF y8M2;?R3sKT߶kERR[~ 6DTTX-MHܝR\\)S`˖-s8:::PTTħO ܼydoqq1֬Y˗/#22R#""÷]eϞ= 777T^V-XXX@__(**BVVRv!BYW^ѣG/AիW1c M4);s\pdmff& w]c̘1\˺?NZ[[M6;n޼D>wFϞ=1j( !D8::੢ 4j޾}'Oc ի8rH;Zb.\ȳ~h߾=4h---dddy&Obnii)6oތtAIIIxj*,_챲D}%I*%!BSSnnn9rL'DK.Xjzӿ/_Ƽyp-3gða3veee̝;Ǐ5kӹn߾3gb֭j^.((XcccUVOg?W^~vTVVȑ#vUضmvɳhRRvX2$$$=pСڵ+TTTB䕍DfRXXHDFFBCCÇȑ#D1/^|&MT)TڵcL@5jԀ*NJIIANNU0"\~]"666,E"3MMM ]$} kkk甗Bʪʐdl_,O%ePPPfx͚5Ad۷oׯW|!Dr(99WRRS\PrdII qa6CCC̘1...|+bbbaen߾]".."/Ik X>|#G.!C`Ѱ㩸Ӈuɓ'\mW^E^WfϞ͕𩠠???CV >}lw ^0m E6m9snnn\ɗ .Ĉ#PfJcy..?O0}3;v k׮jxi#**J/x/^9:: \?eeeɓɓ'WX%Jjժ%/<ڷo/y!qĤ*rff&9#G sח"lmmqu]s/^ʕ+\j L2\]]#Gĉ\pssCϞ=8B^|2*䥡;5ׯ_Pab=BBB>ԺuklܸK,)Sxx^| !!A}~]Vo̘1[ݓB~:ubu,!((7Ǝ[m.40UTmTm7aee%ƔYZZGJ䛠zR$ߵ'1]K+R@yk߾=jժ/_6<$ q+2#"IL^>| xҴiSΏ?6Yڵk߿וyBd# sYPrdJJ R4)9ԔH4U˖-[2?~# Aɑ[f)'aiUt!??fl*}066YUA((sAXp܂bSrdff&,,,K^=zuUZlllקŋ&!r# swzxѣGyqY>III:t(.\P=99&LCDqa188cǎz/^?Y~!!!"/WF L:߿?)))8qb7͹saܹ|ˋkx|D'MX!gĈ;w.^~]-**Jbɑ߾}+O9|;;;=zTd:u`ŊԩO; ?-[KXԩgϊtQEE>>>Ô)S*; ߇XBHHI5i-N!?YZZb< ? !! >}:[[[(**-!333㹑BXcǎ˗y\po>#RRRBpp0LLL>S]|Ϟ=X1BTTmƵ @φ1b֭ gΜ={_=v`` %FVYpѣ{OB*"0dcii)///C Lc4AɑW\ԩSYF~~|۫JR X!MPLYR=}oo/*QEI SHQ^|^5k֌Hyy.0%G* ^-NjZ!W%]TTVCLLT!HBXll,õgϞӫVVijj"::m۶;uɓ4h2ۇ1dǜ6mH0~xdffrm8q"l"277Ǖ+WеkW?_/ܩS':_ڰ/^{q`ee#G2y̙30dDFFޞ* <}ľwΝ;˶=|qqqUzdJJ OR===L0AFB>|XB_|p4l...DY-^R'W\#GTWÇ UFaȑ.VZZcǎa֬Yb˦bL2g̙3%#6l؀I&"py888Ѝ(UZ wޕM(Dhyb$&&J>۷o8uN: `ĈCuuuj}E: 6uuuY 4AK.OՍo@ET*UX%4#5p7YMTcH 6L`ҹѢE "#z IMM;R0}tÇ\۶mVȟTUUq!$y(nN:smƍE###,9`%MQaN@\?+BPV^55k#ߟk!9"J 666ږ ___Dz+iW<W1ْ WWW/[7n!?lڴ :t@֭ )EHx0xJa``;; <==+56lJ ^G|mnn˗K| &`\>~SRXoL!"[o߾ڵkѶm[oߞƆoǏb4A-^aZV$HyL cp8bݼ'+U}Z;qW*aС#G1J$D|>>>ԩ^zfggGG EPP׶ÇcСM[[ #ĉ"3f +}Ity]b(%64hjjr=NNNxG2:vSGEKT߹sG`<\Jj͛7ׯ\6m---\>x׮]iITGI~T@bذaHNNVa!HԩSC̝;5#BCC7 GHlmm%J5kքu֭骔$mRY2`Æ <7h_^BTQD_W~V @r PPPR$`'LRu1]O*--"JD~?/;H߿nnn"..M6e'0BX(9)ųg"117oٳgsN,\zBFh"Tk._P¸=p8XxďёgK ҧO'''ڵk\ۼ/8Xl233~=UV\߾}˓(+ڵkI&\ӧJ-Y.]x*TVE|kԨ{{{S>92==])**'m!!!2R]N+k",--%2NEK{CYYcYf\^~]q\~kcjܸ֬|Rpw.ɰ!HXڵ1vXY!{fL9pȫTeVuUXj9)IL!sNPe|"6"gΜrqq1x]o)/^P-2gά]v)k֬v @bWNkKxÇDӋFD˒?,,LAAA~&~d|H(]2eʤ7ovȑX?ɣVZa'G)I_uHŋ>9kiooo vr bt<7cAI7OI}zmF-[0*:uV^~u)V}_ }6_]3jkW&NhVinTti :T[nt93gΨG>| ^zjv|d ^[zd{fd-lON:F73dȠ;v@v (QIz?Iod *$$$ޯ ߪ&RLUV1h 9sDuV[N5qdݻ\5e˖1ֺSlcLޡD_~r_4iL}dPtiwޭݻ(R9i})44TI$qd1?;$PV; Ց#GTbEF[P~4qh6o\˗/"9DfkJz{BɲeM<AiŊ:uHްavڥKI&]D m޼YΝԩSxH,߿_}vZJ*#xګ1xyym{Unj۫ŇGf͚:|,YbDDD"Me%<Ȧ1,Y*FvV+^!SCBBb}ld??)ShF "jѢ6nܨZjwӲeˢk1c#ׯ&۴l2NIҥr_XX^j׉/_N)+WɑUTQɈ]tɮ喯_o$tUV29288Xk֬?bRJ&-[#Ϟ=a Zj~4~xU˃sZhQmvӧ}HH@֬Y ^GDDhʔ)"t)iҤzśm#>J*RJzƍ իWoCpBf}z{{o߾OvZ :TgΜ1hkܸq1bDC,Y ^[;92];!u|}}uiyΝ;)2Hh%=Dְa/eXF  VA*\8`Lj={V'Oh...Zոqc&JF#W\O_Gf̙R7onȬ#44TZ҃li9r&G;vnc;vЙ3g/_>{/_j޼yQwvvV bԧ9=#9iI-ZМ9s?qDo>F+"a_>(YFœSZherk״qF5lQ:qD֭xrhW| @z3gnTj$Ey1fl-_{E˞=fϞG*[lܹǪ_ggg5mTǏװaÌO81ΉoU\*{=\~]*U2J,R~gX@,jԨQʓ'X$g)9/^l (J׮]ծ];#4l0Io裏LM(~d#GF_5N:EDDLK\\J*ʜ9sL|h۷oɓ'm ƍfS9rQIOj׮]q\ro߾8i# .dɒYmʕ+X.\`tڽ{:DDD%RJ~q%IMVzґ#GtI 8ɓ'lժUK7o޴QT7qDM4dbŊgϞqH"&6Z|t?M6mbɒ%|kPPP|ڷo~ ,IFߞdhh(z쬎;lӿ=FW_}e0grY,Æ Spp@*(8p@k֬aD3gnܸ-X=\7ڵkm;~x}6  8u^lAdKڶm̙gݺuV絷ӧOzjigFDDXᩩ-`j֬6K:t 5٣L2J26sfǏ{nmUVX UbE]z`{ c; H̙5x`=ޡZd|K.C$,ZH݋qI&UfO?ɓUD +D4n8dWVZz葍e˖_~&xxxhҥruuxʟ?g] IDAT;wyغu={f㓰0}' w(ƌc2A"k֬jժUuss3Y6ԩS&ǵ]vYSb׳gOYuE;ֆEjҤԩS*jPN"E(G? ,w&'G?aDݻw籓vjÈ⯠ իWO7o6ISLѠAl[!9p[V$I޼sfϞmqn߾&M,mYիW7?l0y]ܹ'?r27[$sg&T*U ^?}͌g͒%ri-6,+iҤjҤ%Kq72'OV``͛[|ԩSTyUzumݺ@t%yxvƍg4m(cƌ/^lIg}G^5jV7.Fǔ(QB&Mҍ7AL6M 4ĉ\nݺenСCegϞ XlܷWxxΝkb">뫯wo۷O&L0fРA3uNJʳspNZ^4iM;vkеkW:tdcF%L)STMٳgO5v),,ޡAdʔI:tr7ԭ[7dBDD:v;wDOyaTӧOUvhhܹY @i 2M6Zxm4ib/. `<}ɍ([vU=ZF8!!!]7ۜ믿FMl۰az8?v #F:n?Wf^z=u-gϞZpᛛѡ4hko6*Txq)Sbc$ǎ|`r,YHBCC?;H.[R IJ]؇ r_vM&M2.bI۷o7ZJ*J&UG0~x]~=vԮ];ʕ%n PR&U|ym۶-1bFm]m۶vښ2eJL>}شLڵkol`*UܼySmڴ1 +W.u)cԮ]dB5p@yyyzڲeQ XhѢ('mu_#GGgh̘1"SdIkF%l}ٳMSm[>|hq"""kԨzYlp4 ҿIk֊+tm-YD$FIZxhP?~Mwo&z/nVbdٲe?qEٗ_~cu9+&FO>8`޽jժE{nZ5k֌X&?^8xZl˗/[}ߠTd.^(]tFQԩSէOh0G[MٳgKQFiذa6G^TT(cǎ;w תUԾ}{E+WjP۷oJ*>H<֭[Gb$ 8رcJ,e^_|֭[$yzzj͚56-Rvm 8`[pp7oC˗1˗lwrrٳX`A^xXvޭFڵk ҈#=ڼy svv6a6w\;w.ھ.,Y2UT)̑={v͜9`۫WAxׯl… oBk.ծ]ۨ\@nݴ`)2#Gp4'I^2K))- !/JÝ:uJճHdxxu+VlҴi?8/_ĉo^.\X~_͛7uJku֚2eJ 2e ,ht=zݻ|rJ)M6j1$՗/_~vbx⅚6mj䗄Ç5%=j]\4k,٣G%K,^RÆ (KjՊ +6e˖)s&]tIʕΝ;mWXXz^z)""dۂ h1 Y+VPʔ)M{jժelZ^|nݺ suu/oo(ۄK.ׯ_̉p1Bmڴ16mZZ*Q/tr U\Y'O4S7oVZl{"9p0 0XPΟ?RJ飏>2(M0*Tƌ7&M5k(_|?ZcǎU۶m EDDhʛ7/_nE5c (P@{1?j(5mԬkB+WTrhz)___J\vܹ=H"PiӦi„ Q8y5kfheIBBBT~}]p!~#""4tPlo߾Eh۵ki &(_|Zl/xB&MR\uV}Zjgn͛7^z 2ޯ_?M>#,[ܜ5oB仒0Z… *Rz-[:~,X2ehboi&\|Y~~~J.ڴi h۶m:qN:;vhjٲҥK>}!O4V\R3 M6mJj޽*UJ,i؜?j+Vd͚Uk}Y?XbQYxq-ZT'N4fʓ'͛Po>)SF|I$IիWhѢ/6)V5i$a+V, rᅲv믕/_>\bGҿy̙3G 2k$I~PB,CbԨQ#}f]|ɣӧǸ)Ϟ=ӈ#ThQtRϟbcM4$>|XEU߾}-Rq䵠 9RsIsQ-6~BsUXQ/_6.M4ڽ{>E >0}% J&٣^z-i~M[̝;wBYfx۷Jw=ˍJeG']t1c+FW۶mݻ}?Fev_}5kڬ>>>3fl:|pWP!Rɓ' V ь34c :99iʒ%Ko"F|k׮zjʕZre|wǒ&ݨ|Uybٳg"kxyyiӦj߾*VȪ$00Z*.ZlbŊY?GV.]\+jƌ?5juf͚J.!!!:r6nܨK֭[ӿ >ܪ{U~l2j$7jƍʑ#ԲeKT Ҏ;tRmڴ)F...6l ~H&fϞML '|9s>S&M\0*ڵkƏ]vEٮnݺ*SUVLw]K.ݻwGiԨQիڴiUn?M6iʔ)_:E ,Pݺuc5))SԼyT~}W=\}ܹs5d5k,V+ٳG_}mejժf͚FiرcIڴiÇwjժRNqCBBd}fO~uvvܹsu%G>4Z#2>'|cǪgϞj׮]Wq.\ɓ'-p#GaÆڷo_ҫW/ݻw/߁/_jĉ;w:v쨮]:YիZh̙彤M}z5h@5kT%-[HK>~XnݪuE{8E6m` ڵkMտ 4H*URժUUbE(P@NǼxM{j1*ӝ}9'K,ٳGzN>yDכs229s֭[[mڴ1Z"2.\'|>}jժV*T|;c^xӧO?ƍuN,.]jͨQ͛GժUSJT`(<{LN޽{}vf?ݻw5j5ph5uTM:U RݺuUbE(QhR9t~7޽;F$?^={{r4}YnRm2f̨7o;  $jҩS9s}Q&MhBq={vԩS6m̚E&׾}{S9rH<+Wٳg5c ͜9S/^4jժiѱ^IT _h&ۧMV}zm9Yj߾}4i[ݿdիk„ /̙3kŊ:tMիWGkRR-Էo_Νʑli͚5zj/Xնm[eʔrHpԩcǎK.&W 6?[lZh*Wl(߼|rըQC3k7oҥK <==6mZyzzS/_Գg{Yq˖-5mڴ8MaÆڵkZpYܹsjܹM ˘1gϞfF$;cٲeʞ={lނCI$VZ-ZݻwܳM:ҦM+)88X>͛7c]8}Zfʗ/cbРAvfΜiV{io\X ԃbŋ+W\z hϞ=jڴ_n1ڶmw+u𐳳={gϞwvZUX1Vô*K,ԩBBB:&<<\ҥK/6Gf=ʘ1tbv't ?^ҿL2f(OOO%ID<Ѓb]MsQcu<$&$Gx77$Ū:vݫSNz𡜜*U*e͚U V:u3gN~viP/Ydю]lYڵKOֶmtaݹsGϞ=St={vU^]իWWɒ%իW/ٳG;vЕ+Wt=*cƌ*^֭k9r]ee|i̙3o΁W^[2eR޼y㣲eƸf͚)SyvQdfkXvƍXS^^^6@SLKN[/n$TYf֭[~z5JGU?RR>}ԯ_?V$J۷Wzkܹ"N``kԩZU)'''͟?_)Rɓc||PPٓ82n8կ_?N}8$I( @C 7|j",zZd2d`>3}tH"VdbQTFaQT)_ҥ~Xqݹsb1UXQK.U֬Y-'nZEQ֭uXX' 92Uhkh۶--Z1>ӧzEbɒ%/_ *X?pt$ףz# IDATG!{aӿII4YUe˖Uٲem6fTTxX'W%,ʟ?֭uƹ/gggJQ9`JXZʔ)m2u1&KLחjժp K،3N"|}} EryΜ9H|nnnF}[1F'I1+W6X}p1h̘a aÆڿ/_-[ڵk&I4|||ԲeKn:3d`^>RNYfs:tnjxw5p@u-[srrҤI7o^앻,1w}O?&%$0aʖ-?0F-%UT/ս{w-L'''?^sV޽ٸƍӠAl2JJZb.qxyyiĈݻU€=z[=1xyyijܸ (Ç/ĉKкukM>*XQx>3mT\9E$'BӦMxrҎ;btLΝչsgxbb8p`駟4f$I#Iҵkti]p VJ>rέbŊ)yJ*R^J,-[رc7n֬Y$"G4hڷo/W׮]UR%uQXf͚͛[uq*_  \.\եK#YlTvmKC رc5h uAʕhb$XbZz.]QF)o޼6]m۶͛uyu%'F/_>8p@VrrrR&MtI#͔.]:͛7ORfͬVS~.^Yf=1ܹsk߾}Zn (`14h'NxxxhԨQ~+eΜjcyyy?ֹspB#իuiuEɒ%X Қ5ki&#o͛W6mѣGլY3`[xqXBK !:uJ ,0͛_>>>rrrStLɞ= }:r6lؠ={+Y-[6UREիWW i~I͚5SӦMg-X@ׯSicOOO5kLSl1cF(Q"DLl6%(QBq̙q3yU֭հaC[(Zkذ4h}iZvmVtwwWӦMկ_?)R$v2d0yX-JH5p@_?VZuΝ;q7Yd\5k-[s)RX=sQƙ;wnő'O͞=[&Lzjڵ+%===Uvmu 3g#V1$s!O<1ϖhѢ t߻k8/ݾ}[V\#GiEӫiӦjݺ%R@(ٳ4se˖-AI&C')oDl\+k7,Ѐ_ ֭}V(a;wcZR Y8"3W{/c|>=bt-(P@Ϟ=S5 dն>b|\}|+D֮ݩ&Mz?ʣݴiճ2=:d ց_:.\Gx*U*eɒEsVܹ/_>+WW {n8qB.\Ѕ "MpvvVlٔ'O*THTRY=ZɣGm۶ZlYqㆶo߮cǎ̙3:{l+W.ʕK TʕUtixիW:tvޭǏ9':;;+k֬ʓ' ,(___U\٪Ξ=úx.^ׯ+"񴧧e˦\rD*U|||bzⅎ9}t.]D>yʕ+כUVUrg#/ڿΞ=sի 5j2eJʝ;>UPA JP-[XV2f̨W*,,LRw8,]ժUSjՌӧz啠W$Ih/_*((H=e(̙3?4@=\T$I\r*WǏ, Q5m[nnnuT**Rs qH Q#DQE9HH Qf:Fߟ9۵k*uo>U45jH9k۶mZJO9 ^zѷoLتԪU9+jժ9g`n $+Gg}vn:mկ_?msUT;wƍmZjmOw1mUR%msG9DѣG#luuݺutJΝsΙlj $r$(ʑ@(G $r$(ʑ@(G $r$(U3ض;w94h*s=7N:餴ι.uSN uΝw9>ʑ@FժU+jժ[>~LتlvѨQL6(ʑ@(G $r$(ʑ@(G $r$(ʑ@(G $r$(ʑ@(G $r$(ʑ@(G $r$(ʑ@(G $r$(ʑ@(G $r$(U3io.t ئժ}|鋙[H aƘ;Lm;t*6(ʑ@(G $r$(ʑ@(G $r$(ʑ@(G $JL y=/cL ǘ13ʑ]]m2*|J9ʑj $r$(ʑ@(G $r$(U3`k=3%1H=jԨ[-H-4egsg: 5jd:mDQE9HH Q#DQE9HH Q#DQE9HH Qf:aq1m32${L&)GdH6-gt /gߙMr[m Q#DQE9HH Q#DQE9HH Q#DQE9HH Q#DQE9HH Q#DQE9HH Q#DQE9HH Q#DQE9HH Q#DQE9HH Q#DQE9HH Q#DQE9HH Q#DYlY̞=;-ZVիWNJ+RJQnݨ]vԬY37n-Zv!ӑ B9ȸ˗ĉc„ 1uԘ5kV|%cv-[G{lk.jժUFI.ϟ_6mDj1ۺ~!BpQz Dڭ[.f̘QMF1@5kVZu;c>(֮]yZXhQV^=92NWvvvdeeYִi{5P6>x2@s̉N;-:vj*ӑnƍqy׎:ׯ_96^jVZ4n8ڴi'xbt)6mPA{ãwLDA5jT6l0/^\Ɖφ sGmZVf͘>}zec1x-uZ-[Fݺu^zQvɉUVʕ+cɒ%gώTy7;#*U*uf[nSVZ1jԨRJ9 iaÆ;wn̝;7ƌ*U]_rHTHzkbdDmO_ƹ[o{N?>:t;s?~L0!&LcǎK{۵kƕW^ǏGy$vi*z?YYY1iҤ2JӧOEc*DP~# Rxb̘1qUWիWt, 'w\7CtxgϞO?{zEݣGqGlsիW+6ncǎ{,^z"o^|8蠃⥗^:hӨQ>|x1bDʑׯ^zņ  sG>}1Hw1w^T*˗/ 6w}}|JbȐ!_ѣZje c=6n͛7/ƍWΉ ~;#)t;}ZM9~e]v)'6lYYYoc=6/r?Ψuƅ^X)GM{`˥R߿y_v8p`۷u[ o+"'FEnjժڵk92?r .ӦMn1=ܳAժU6mĕW^ӦMGy$jԨg̐!Cbʔ)JP1L:5N:%\D꫋]<묳"+++r+F]v%|xGϞ=',tnݺٳglܸ1'|r\| ʟr$ /0y<NRq7f.@[.z xzvvJ 80 qo}1"{c=!Y?ʕ~$777zJ92?u-]Uak eK.ѳg<^o3kJ6};ӑ z~D2dH 4hZn}Q\z吪V7xc Qn"nذ!u^9(?0"<kP֔# Os5x3 3 z޵k׸ 2oV\wuסCE[dIE) (vڻK1AfUtH:(>Mg͚EM6-&LӦMٳgNJ+bQniM6qaI'ŷݰaCۀ7n8U?zL0!>XhQ;65kV\2Ə&M3g_k֬T*;SѦMo~w\4lذǶnݺγI&QJR-[˗/xvۭXرccΜ9?F:uqqGݣqƥѣwߍŋAq';w.9?~wyqKVĉc̙1o޼XfMD͚5qѲe8ꨣⷿ}DDOwmz\JhҤIq_~eK{ŬYbŊk׎f͚Ł|WTGp _Y6t-_NNNt-.]ZSN9%y<%Vt͚57|3ڵk ,(t܄ bРAwߍ{1>`4hРAip@u!nɒ%%cժUCŰabe?+WvE~Os{o}Rz֬YѢE͍/n<岈(?{⥗^k8#'LUVO<1.ܹs>mYdɒ= ;czmRT}q7)VFDdgg̙3cرq_v[lv3wӧO;6ߺo6M?pl2xرf<ꨣL{.V_c1zXfMx뭷bذaQJرc\uUѩSR}?y2ny飏>رc#J8ٳcرqwDFO>ѯ_]v$QAOwg}v[bȐ!9#ꊑs=_c9&-[V[n%881@֬Y^xa:΋3<SA)GBun%~ԨQqumؿ&MI&E6mbذaqGh¬Y&8v%_F~W^)7nW^y%^y8#cqGlQLڰaCs9 /lv{ɓ'Ǹq^zElɉcرc]v1thӦMZrmV_[hQp 1lذ9rdxiɳ* xF`*/T^Yfޱ>DV}q衇z}7|'NO>$̙_~e,^8֬Ya׏FŞ{[6mDI&LeRTL>=Ə3f̈7o^^zj֬5k֌ƍG-b}:*ڵk5k^NNN|g1iҤ9sf̙3'|թS'~hذah"Zlx`wqQ~ Ez-\0xc֬Y1k֬駟6QFUVѲew}]vqGo7cƌ7n\L>=⋘7o^Z*~爈UVԪU+5koq!U1&M~a̞=;fϞٱf͚M^nݨW^ԫW/g8CCC=4֭# y^fĈ] JoWPfYxqt)>|jժ]t;ơKԬY3VX .> ƍcǎs啜[c֬YOGcƌɷuѡCo=zts˕N=\T^TǗ oVurH~Ϻu矏ѣGcŊ1wٴvqqŹ]t) 7|AZh5OA|"nF5*pݤIbРAעE\N;-QRx㩧_7k~!~ᇘ1cFꫛ7o<:wg}vy[f͚Æ z*,YR5kĚ5kᅬ?x5jG={3l/5j(\[h,?Oq~'x"zBbݺunݺXlY|駛ovqE]]t);7sg)L5x衇G իWիcҥyW_~viq%DvJ~`xꩧg?xw[|y,_<͛}Q<3sLt- jժ袋>袋SN)TPJ_LU$w9TDZ3B⋹:R_"{o*"6}^i3n݊lٲ|DDjO 80ksM]veʕ+盫k׮7nv9s?W\9գGTVVV2RUW]oΈH~1c{wy'ծ]:3R6l(r)Snɒ%\wuy>c {[vԔ)S `ng[OO_~{#G,4gË8#,Yfb3zT- n(#F(p_4hz駋5ojӦM9T:uj(o?| 6L˼WN5o޼sjժZ|yZ2eСOg;@1R;?ᄋ2}Զm2XSY7p2fvs~:tP׮]yeJ`q*j՚LǧnݶN}E.߶mb渵9sfzE}W;vXW/^ggg?EmW_]}ݱc͞E jРA]vO>sﭴN=Bva喣(F*}Wnse{O>xO?2d\WVRw_jժUez,_}U9FUv3gLviJ*ԩZlYwieggTݺuF=zfΜ,_W.ѱ}EիS7|sN:i= 4H~%> ݷ~իWJ*i=֭[Tnnnwi;N:J*]>qYh߾}yN8rQlEEӧO"瞩+Vd:&l1UYkV?C=տSƍ7X7k,|x7cwϳn̘1?G/W^cEV{oe*Un!>ҥKss11qĸ뮻J*y֍=:_2/n w=ߧZ"+++_ߦ{vQZ.J}]%tK;ѷo|/ʊ;ؗ?cƌq[wM7mqC9$fΜsNw!NguV999qe幒k 80ߕ=KjÆ 1dȐh׮]̟??Mx7zbժUc^K}7"~ynK_>hѢE|ͱt2ϪUbСѢEѣf emڵq7G&M_~[:pQ⋸+yqwǺul_%~kM6+RTZ_bEz뭛we]͚5XW,uŨQ뮻nm+cF˖-cźjpI,]4O;;B([ӦM֭[G˭qxǣUVqwƍe^|믏e˖~.]^{mpo~ 2o޼h׮] <._<СC|Wehٲe ><֯__.ݰaCu]~{W., 65\'tfo%.\w^t֭{ŋ㷿m5k֔龦LGqD|- /[,.8ꨣw-qGF߾}cժU_.+W/Ȣ_;v,TP(GBy'gɳ֭[8~ҥqYּy;vlh޼y7.vaMrssK}5lXT*]vY_ ݻw/sGJ6-͍޽{dž xx[D?^&999ѻw_:>ӧ-_|qzqy5\cƌ. 9;;;vZ?! @z̜93 T!͟?2JjNEVf͸꫋裏O2jĈѾ}3gNFjժ߿s17d$۞UVE^k׮ez͙3gNt)Mw9+O_|Eo>NZ}w7]H-Y$:u}k IDAT`8bذaAV^]vʡEy7M~͙3':r8͆ 믏]իm(ϟ_aT eA9,'''իWu_ {+Wnz\rxG^ziɵ>}ݗgYVVV=Dԭ[7mꫯ￟gY^O>%(~rܹs'H>êU~Ĉn:/dK,G}ty~|xL2%ϲիc=J_ԪU+nOUVy͚5+zT<{FvEqM7_X۬E|Df1Qf\~Eedeꫯzʕ^zѤIh޼y4h j֬Y,~|y\L?״/Hrgb;R mvYeB(/Y+J"-[dIBE:W?̜Y:sfxǜi:s. D<@6mk.EժUannڵk Eaa!6n܈;իWr#O[nxLϫTjժsssTZZZZr˗/ٳ'?.qUj 111rϡ ###ruŸq0{lca˷o`ggӧO<Ԯ]o< %%:uݻlٲE9[srremnnna5aÆ!447n(ڶj* 8P9EV)dzjXtLsHc077ئqTfMDž8t^kժ%ЊĉXj_JbǎB/^,QtttpB 0h[vv6vڅ3fH=ϠAXgȐ!X~}\$%%ҒUMvv6,Y"MSSѣLs!>>]tÇ8pcǎ-q*!5===mV`[5jjժbÆ  !e,jY666:t(\\\PR%%GY~H(BҪbŊ6lㅅؽ{7yRzM6M:7sss]tA-РAe߾}'Ot.^_ضm<==i]t HOO9갱An݊9ԩSGӧOq$$$ ""O>8݃%"##o-33%ѥK4o\SX~~>RRRXDDDÇRő!C &&;wHԩS|8|||$vO@FШQ#!77ǏGppT Rpvv͛7СCpwwG~~Taܸq:t(ׯ/qmmm4iM4WcڵKbŋQBT7ocJ*aҤIF:uRWWGzP^=֭b DGGKǏݻ7n޼)6]^=Cbmmmh-Z#:t_Ĺz*pQ,,,Ę1cjժ5jF-句LMMajj ''',^ضmI ox=K ؔ)S$V5tR޽ww?RЧ%!|?GAll,␒"!È#͸%66Vq6mТE V_/}H,,,F d:ٳgxxx4GYVBJ?~Dǎa:Yyxx]hٹs'uDՇp=m... 9 V555hhhUV[+#((H`[Sy-,,[/^K!B!D1FDD\]]abb___Ξ=8>l0nQ͛7G֭Ŏ߽{?~0"էCCC$600iV¢EЀ^x-[H)!FwԩSBL4 割ߓ%iΕ/bR%F֩S{œ'O0uhٲ%VXTI lcJV޽{Q-IhiinH|ΧO0b{ol8w<==JX" ,ZHHqTL8Qbsto-L #GDrr2%&F{{{DEEŋRULMM$1^Ŋ+cyy?l0g +DTӧ%Vgۙ3g`iiӧTUUAɑ(ǃ ^m۶IwuVZOm$/.իW,[hT"۷o4iLLLw< fΜ)=..-[D=pI|ݭ[Jz;88(x={x\PP u&]]]V[ܪ… HKKV/ÑM!B!Du9::JuݻwXr%ZlM"88߿ !&&qo߾E:~S\AAΟ?a4D޽{OOO맖}6֮]j$}޽{Xx B||ZZZ%O]]HLL2P7 Eppv_DFic֬YHJJ>U~z֎OJLL0A)Xjڶm'O(%BEm aׯF...011O>xж{O+m@WdkҪnݺ8vVZ ,ҥKXYYI=w@@>} l/,,Dll,bccanncȑ\2+w_ Mm׮={ [ҕ <HiT}LSRR۷oɓ'HOOGnn.tuuQzu4n]tA=d^DUdggƍǓ'OC]]066F-Ю];tؑy|>W\A||<[lܸM64B) HL]RRfϞ={X]Yʕ+bMMMKT%c\zErQ2O6 K.e%)DMMM̙3ݻw`Ȑ!s)2220p@V={w Ê+MKKbbb$V,)uuuرmllӧOc c7  A$kiia4iBZҪU+\v SNEhh}>͛7+,i"**J$^Ybǎ011Xb֬Y۷BM6VH;u".. `l/w̛7$wbԩҥKYM/vڈĒ%K0o<4:u*Zl.](,Z cXYYիRc!DZIH1k%mmmrro=x@-{қ]tKG ",, aaah߾=fϞ ggg1VSSþ}аaC!//Ohϟ6lfΜu>}$X_544`dd$P8ox2w^X?HLHH@aaBGBЮ];mٳg VAQb۷1sL;wN'M[[[Hu4&&Çq9@GGܹsѨQ#RR%0]vŋ3g.]*Ga<_̱޼y6m‘#G ^re899a̘1ԩ&mϞ=;ڵkzj)e|Rvu2WBӧqi`BΝ黤 ޼y#tw,D-/瘝:R#$$ޜCvz쉤$aĈ8qgc̘1+A ::Z~QNL: tR )44T!hiia׮]HOOӧܹƍ+1M&񾤾>9Y"6mSSSLcJS4---DDD($1wU3޽FRh,111BؤGGB {n̙3ċu~%3UUSSÚ5k0iҤKZ<s΅ F!.saQp]{s֮]+s+T 4jP233W<}=bWׯ_p"jMH1h۶ ȨDЊW TEP"ajvҮ]v><kkk?0`lmmZ ٳg5k/, Aƍb ־Tfee mѢ7o\qnnP-!R\VVVR%Frt &M"۷o@Ϟ=#ƞ={ЬY3̝;W\уУGXYYaΝR'F?ۼڵ ְWXgϞ]v5k%cƌ)~[K.011ٳRteSveZl)v\gRva׮]&FRV-Il{IX%۩Sp}LLLpe#ロ[J/00Pcƌ$MCCaaa@lڴǺ~ zzzVJYf1&*ˊ+JQZ˖-x 6(<---=zT|>6nXc-YDbիWs;l߾XNrr2͛aTD|>ƌ#ӽjժaڴir >7obϞ=ذa~z޽ׯ_ϟq ̙3uԑj,B8Cɑ(m|!նʼn[#\]]q?[0..;vxu"((iiiؼyvzYYY1cƌJd,NU|$E®? ,Y|UyT ǂ ЦMƖxsΡcǎ={6rrrX/_[nv! .̕`4j;wƖ-[eI ZeYƍŎ}uaWҎodd3gH4m4(@JLI?FFF:aB.*dNN|||r5jHl&l۶qDܹsGc`ĉq!Ƣ6e*ŵn&Lx޽{ [˄ 8]Svm,Yqݻw0Irr2/_θϔ)S" ,Zqkז賀K;^NիWXr%%+WWW%/^TٳGpqq`LQr$!J#TzǎHII쇩$yI <~Ž% mmmmlVaSv{n|"޿'''1c 11111۷ȖR[n4~ow(ŏa``cg'Ė`e"!$44Td[J*o߾7nf͚ѣGwb9sÇ/^z"oӦ y!00&MB^DSj⟲dddb@C1}t :=z@ D>'??c%yƛ7ochh!DV*Ts">>ި^:O˽صt)AT5EB_LLLw^*R/_DHHQlݺuxq]v… 1`}bccqY֏+>/[[[ۗq'N=xx8 @>}>[еkWegG`EhҤ 9~ŽoddꄣGFzĎI'-XUNl2ggϗ}:/_ Ϟ=ڵk'Ӽ<Ǎ70e\~]`xyyD~U%xPSSSTjU{XxeJꔄgx"뇡C2#("K,ȋ]YYYy<;C Ç0~x?^lUo߾!,, {}%̝;ժ#F@EQ` ϟ/v>I߹}{{{?Bcjjjprr„ йsgyyy~:lق %ǣSNxbΏw)t.ggg 0tD)zꅓ'O*dDDD ""ժUÐ!C֭[+x&&&e6ܜ֝l޽;Mxzj=W%y~ՌL8Q%〟5BCCqB ,@Ϟ=Y;nz|EDD(5wpp0x׮]1gf:݋&M ##CiqoJ9o.v<22e@ IDAT 9ѣpACCǏsi׋<;;vDw:-[iӦ}N>mۖ舢M:UbAJg_WGGҥ ,%]!DYr$!*]v%*M]vMdD%G~VYʫbŊXp!?."m֭% /^D޽ *ܢJ+b/.\&8aG_fu*x<VA)] pxWFVвeK\R Y߇U8p 1{l9s`̙njjK.a2+Ucɓ'_fByկ_hqOQoooϘgggw8z(o6kjj{AJJ y)[VρGd۷HBHשS'Nb͚5hӦ 6mxc"===֎eaL ~zU5k;;ٳÈHim65jPXV*1Yիqktuuف ]vezu;w 6myD&k׆Rc8pҎݲeKhB:}Eݝ}(5k[raÆrͭ|[hW]G2cddsα={Z*~+Wă!%:g3co;v*T/QT\YhϳgX_21bmϟ?/qB6o.ts$nݺ033v$::Zq*UФIWKt <[J#(fΜ)E:u#++KIXF¦MNHҥKCLZΝ;KO*Up V|>r*fٸ|\uAdd$֯_/Ԏ9r۷c֭7 !iڴ)ϒ`ԯ_{ƾ}:i1U亭T㸤tիPrUPB,\q;wr )m8h"yп|?fWWW撇ر\ܽ{W9w8>vX#"([+3&mڴa}^iUV ۷;~m|ŎWR3f̐iN.= 4;NtʘYf1BŸ֪U+DFFBWWW>|> yߥ?zzzEX5jǚ?>Wjժz9n7ǏKM_>=zT-11^SZQv[F CECJJJx?vRSS&K*==aaaR+"{{{k׮!==+WfeF{ʼmܹ8zJD>3g̙3T///(m?!ljР֮]+yƌr c[]vN:2ϭm۶I&-4T˗/cժUB-ZDsx:t@aaa֭[EQFɑlTE6lV\YS^^-Z۷x߭Y_~1>}>!$$s-ܷoƵkիBJ?---^1a۷oضmmSSSxzzӓqu-!zzzr?ժU8u/NNNBeQ^= $q|9rSL{^.`ĉB>cǎ-qb<<<[>>>ӧtttd{J9D/;;(!j2?ǏQ,#<<&&&E痪nML-{E߿3Ze"֭[K.C,mmmx{{m3[XXGbGFJpCKKhTWƍ(2$''4ٳg08iSX iq5jDT155c'5i^,ɑL3fsq~~~b;###)9Xr%9oy?l0?~Sի)9*E9sjOM9y`С+VHZT1cƠzfϞ֎[ E?waL+ݻr"վJ}|mFFF~?\`nYb׼%ѴiSG`ۮ]*11˗/֢E 2333Uk֬ϟK<… >TBɑ"ݻTx-BÆ ammP|E®U}SzЭ[7clTj%+'NҠAlop֬YJaG!˗/Fcǎ E^^bS?~hTPUlAKU~;wFݺueK"r <|PT"t_+W'NFDdIhРP0J"(,,ĤIN͛'zzzB7'''VN?|=z5jJ[.,,K˼ynT|Ç{+*U|%l\l@pAA<==T"Trҥt# .xGxyy Ν;VM: !իW1n8ԨQĩS܍lR61^xe455aoo_⹻t"p >wUUk׮ڶzj~,555{IZ.J͚5iQ!(1n8);V1}.,,deQdi8N.ʎ(;6lf͚r |>ׯ,T:9K&&&P'vZjq~LQޓ>}bx%r^ xBx~KYF^Duر+affƸȞKNM(9pB4mT`1vXdgg5gvv6F7 lôiUVBe߼yKKK\rEy?] %ؙJ\-QF 8:: l;uXr%֬Y#ݻwZPPK6l5k=.5<<޽8ӧO/M\e!YYXX%~ 666+T%y%lllS...BDqz)^\_ϝ;' l322hM!`ccvܜ>|jժɓ'ZٚuЁyDW\Ӄ˅/_ j޼B)-,,пmϞ=^)INhQ!(NRRLk1IJJ 㸱1GEW^I+%w_~œ'O8wan[}yr\1Y#ssss@CCCؔ[nx0)GWW9e^ElU;@xy>O/ q!*URzwÇs ! !刞> kkk.7oƍ7~ztY^ &իWaaaPWWg-vil۶ Ef͒xr{bBpA#**JԩSG̟?_ҡ8߿@;m}}}dff2>666 VXp=zT``ٲeE0tPDDD@KKK9995jP;쒘3gnܸm?F޽+{Ǵiӄu֫W[la-n";v}b6l؀4l޼ժU8Gaa!n݊'"77h;Ν;BʯaÆ֭[%ÇXn֭[M *2_DB ^69LMM222PXX{B+ǎ}||.:uJd8͛7g;,B!R[.<<E&M8:I ܋*T%!*ߥرLq)1%.X`*$  _~ex;uuuIݲnA rHԨQC됖<GGG|a_KKK ߿ǩSyfսWӧOАtLMMq!Sʉ'pyL>"odgg#::K,͛7зo_O)\]]1ydڄ=|fœ9sн{wxyy_~*S)_,$b"T X[|>?~P!Qmzv :bB羄­+:t(v*VY )9ÇEz#ի'vq2)]JS³X9,,,8DBb=''Gy={8޸qcR r*88=!rL71??C 96US;xn4HOl~:zqTFɑ: ..NNNHMMEll,ѨQ#ԨQUTAVV޼yD+ڵkpr*D366˗ӧO }LMMQF "++ )))HNNqW4 %%aaa۟jԨccc|o޼ArrP6„*;ckkÇcǎE۲0yd̝;022ׯ_q5'K.Yoڵ~@)WM6hذ!7n MMM$''\YTN,X@! ===DEEaĈؿؗ/_  5jԀ_Ϟ=CAAȹ4h3g|%űc&~@@@ШQ#4hժUCvv6pm[g̘!L!h߾=_|>111)77jckE$E6/taɑIII+W rfnn.)k]!&&Fa%9q"6 *ر:t(޾}㫩cǎ򂛛 TU͚5QbE7_Z ,.4%fAjyHHiQﴵKMKy.(;ɑwaD% (E$Gw^*O IezJ!(^w:::*S֖q֭[IT%G7o;w{󑔔$tO1c`ڵ*qk``YӧO2aڵZ񨩩aϞ=^:V^-o]PPOJ\eCR'GM+;wN`qyz{{cҤI"4hCYŷSJ=z񇔯B6::: f̘!˗/_Dn6lPzq׮]C~DĶ-ߩcъR۳ ڵ v*jI7> J5 W~&>*%HB e:QǏ'.0(Sl|A)nڴ)Zj)%exԩE߹sU.-.]X}FC4-@100ؤq$bj\reMJg_yuHUZ'KJ9QmLU۵k|}}}7\NT  k.\|YzGGGܸq*yquĉx)O.WBC=}ɕV\gϢE2?qqqpvvڈ/44Yʕ+ʸ߯Df͚IOVp kNkȑHNNƌ3kcc .`ݔB6mDY2=aȐ!x1%FB߭_^z%KM6Epp0pv|Bb \A+9kllɓ'֭[xfΜY#aqQ e)1+mb- D6LJ{F灪w}]y}2%//SDT|RX&M8ׯ/v<%%haF#I׮];̚5豑gϞǏ~={n FMM m۶Ѻukkhh(o(&cccX8qN<˗/ݻw"҂%lmm1h 4mڔxq;waaaׯEnݺݺu8IT_CC˗/Ǹqj*DFF U899aƌ]TYfw:۷ʕ+GSS5j "ժ۷oE[j%U<aٲe?>N|Xxʕ9]EK哶P){$}pIɕiYz}qHfjj{#"rSNԩÐJƍ`,Xx9޾}lTT ժUC͚5Y[edd V撤B pww;mSRR7J*?PNWTSSC=u~DVVQvmGGGmnn 6`Æ HKKÛ7oTT jՂܯiϟ#==YYYEQ~}rLKKKXZZ2(...pqqQ%լY3N~x^|߿C[[+WFÆ m 6$$$[aTxJ[]P?*0x!QSScMMM&'fgg+؄jΞ=yy<:v///P!)ڴi###|IxDD|RnΞ=ߋ޽;cZiU^KJ[KJJXemMy}O*_>Jg*u GY( %G^*WQ$a'*HBJ)---4n7Vv( add2UdhkkHzO$%%q Z `{ 9AھZjs2I %GB!B6 Bt]h۾} !_N<)W%ߖ-[bʕx5N>SՊ7nx`` +>UǏ x<嚻4TKOOWvJqR q5q=Y>HzOq Z g _zzj̄(R ?8Fwމ3440BQr$!B!(.jgKm Fڵ+V*mΝ(((PqgΜ WWW?*RcܹR_zuL6 wݻw1m4T^]/GObʕFĝBL0bqvvF嚟)˗/ ?eէO0rEczzzz.o'r'ԲeKƅΝ0%}}}ԯ__Ç{#υ )ڴiX;>>h=6%%h$KMM;fffa$0PvB!dtuuann0QT~ !DZpwwڵk%''ceX|; %iBHy*.兞={B]]===L:~~~b Đ!C`jj]`طoΟ?/v_Ѳkԭ[W_ѢE e(-- bǭ%wS$GjjLm<'٢ kkkƊ}6%`&Snݐ,rQR[%ĉ8*B닭x-˗/xzq !(9B!RbpרSҎ<]LSZ:u*6n(P gΜ9ׯDz1qD1BHy ̙3Gǃ +ƏUVÇ"aÆܹse&Q5-- SLaܧhٲǨY&xrrR#?|O>)ʶ~,XjjFmmmeFJM7ovNlrdaa!vڅ@"\ڵ+߳gJ'G|tT\/_9;<\% \~X/0"~#B!)ݽ{c7oD5@n&&&BU"߿zS:w VC!ѴiӐ!~X`RRRp%9#9VR%3s%paРA%n'.ݜ^\Q4ʐe!V~~>6mĸπdSve'TkD7h x<㡡"[p%# db~[rlbcc1{l?}KL$#5l00dDFFrڵkĤDǑԲV\U#;vLWHMM;nee%sGzOfffҥO>aƍFDRJ8::2xbͧO^" R5k#N^^ƌ`?!!!JDyl|>Ǐ;ޠAֻRI!B)Ν;U)SHLLČ3$#N jv!11Q77777l@f!۷W\wN:Qxظq#/*((Apu#cײe˰n:}Zh!4j׮jժUZu,>'N(تի8xqÇ|RΟ?O)V53fׯ_%K˗bkԨC57{ׯ*J||SLQD;w`ӦM^^^ߗ.]Gq> @8 (Ԑ!CǗ.]Q$XqŅH B!G^^+sx̄oQ{W%00֭;[[[ <=BDDv؁'O<ѣj*B!m'OF\\cO>k׮o͚5>}~QBCCY=߳CDDܹIKIs--- *._ }|}}#vvv8|5wIHzDv={6F-vcǎ!$$&L02񼽽q-}QbE"*1k,1B>;v,8L,xxx(r-){1}tL>]\||ORXXÇb>~W^I.ŋSXB!q}ӧOCxx88P65k (0vXxxxz|GGGL:UxXX,Xm8wg+ <KKKL6Mq~C aL277Ǹq>Ç#99 4Edd$g+O ܽ{W>ӧOGƍaggadϟ={0ӲeK 6J9XIIIbٿ?Zn3fpB=1NB1a"99Y۷1rHݻWnVZ7P__˖-0"huAw^=oZH{riimgDKT"KSRX#69e!YVDTJJYb!%C1 \?]={>3>[}}֮]}M8se['mJ륗^*5jԈ;/^|bРA3%|͛cٲe1o޼3gN; k׎qM7c!V͚5g:u$q?e˖$++VqWb 'wuWgh֬YiӦq5הmX#7h zDP4ʑ@5tȈN8!&O_~ew}qG(' h"^yxc}-uV I'T |4m4ymmݺ5nСC,]4I 7s8c֬Yy1u,,sNI)KG^^^»RrvX;FDY&="},mzkv|+7M:*<̄3?n;r$_^{m 2$8 -su]q&}[z(;غuk3oJuG*߿ПƎy睅u7 :PVl TJ3gΌg}Hx`qѳgϨ[n'k޼y?*TZ㿫U||~ ѰaßSZXٵKsƍpf͚Кiii?[I&;t¤Yg{~:&M+4ہyyyqgGڵKc=c$S6m'O<1֮]pvΜ9Ѳe8p`ׯ̊Y|y\y1y"r!O }gyf <8VXQYgu֍ۗIGnnn erTe˖ӧO,Z(n馤<ΝyyyV6mG.5{Lx>}DC۴iS#fϞ]&?|1w?Y̚5kK.q7G~ͦq98Ke-[,N=ԄE=JWfff<ѶmX|ys7o+2f͚sOegѢE>^yBgkԨ>hdeeITWz:uj~ᅾaȑ+Ę1cM6e_;8nEnnnÇ+FeJ*qAE^b1|5jTr-qWD.]nPQzwo^pa~ѢE/l~a 0 7n.r1Ί3fy1G\rIEkF^^^\pߖxͭ[<mڴ)y1ǔxʨ^z裏& ܹsM6q_|Qjkoݺ5׿_}]v%OկJ-CĿ _:W^q9$iQm۶-ƏtPȝ9 6Yf/~Bg}hժU\Gׯ[o5:"#we6mZw[Hw|7m۶qzGW_E߾}KXLOO~8ZhQjkPρ*rsscǎo|M7ZjU׾xc-rss㢋.*t>8^zRYߏvŰa##"n߇Д#Je֭q]wiiiѾ}x/'q]dM6sFΝ|Β%KϏiOlϏ)Sagyf|7۝k߾}q;|}e˖3^ bСѸqKc޼y^/n)7nW_}ulذs222T-%{lL81a)G[lQFEӦM3ό9s֭[o߾ѤI1bDlٲ%!CO,Zoۘ8qb.Κ5+ڴi:t<6nX5͛\pA>1bĈB{oիXQ9yEދvI's-Z~a'~_/\9S._Pl T*qE?ڵk;7n\ޱoO?t >;mݺ5/_-ŋcΜ9dɒbg}bq!jݺuL0!rss -\2C#8":um۶-ZDc6???/^oV̜93z5{ٳgqUf5kDŽs6m?s=cG֭iӦ?+nݺ5.]/s?/R?g"w)$/&M'xb.rK7N:a-Z l~~~,Y$~5kV o[̝;wu裏 'CĺuO=TѣHo*ؼys?>Ə׏c=6ڷo4kg_㣏>  /3gΌꪸkvqAQu-f̘ݺu+to={mݢcǎqGơM6Ϯk׮?<͛s΍{n#$---ӧ?.*̘:ujtرm''|2Zh6:aӦM`3gN<;F?>b=&H&Hҹ;;P߿o>???ܹs{v-W?C]6֯__*΋aÆnV*,N:)F^ziB?C<̨^zիۡ 1q"oQhAG˖-qŸq~XVVVdggGvvvY&ba2"◿eL>=>b_:wG?Ђd IDATĿ( / /222"''ɴ7n\4o\92I7oJr)oY>(>3fOVZT^=222b͚5EoAիOz-2vA#//Dٸqc|7lٲb#TÆ oDY`G|oF׮]}%*F6k,^xH";;;x≸"--mc…[oK.bdZZZɓ'BH6HLTR%.XhQ\qZl2&L?Z2+^zŜ9sqI[J*1zׯ_֬Lzjjժ\oРA<1mڴnX~>쓴5/mtQ*U뮋yőGYn9j׮<@233;ӧGڵvZn-TԵ$#2UNO>?{[>8MN++K%GqD̟??=2A1cƌӧOSٵj*|͸;J펨ɉk&.\~zR,aof\x%`Qԭ[7۷oCtA#&O-ZHںqWƢEV+yyyp>>|x-UZ8p`,]4hҤI6{8qbܭF1lذOnuiiiѩS5kVpH w .;/vuR~zzzr)1xE^l_{ѽ{Xre<ǜ9s?.322C>:98ꨣ*o-[ӧDze⡇G}4,X۶m+k8So߾{8פIѣGǫVZg}IVVVVUj׮7xc\1mڴ>}z̚5+b_aÆѹsٳgo߾Bxo2eJ,_<ƍ'OwyɆ F^^^75jT\F>O4n8ikU&iiiѥKҥK,[,L?x̛7/6o\֭[7?ڵktܹNDƉԦMs&8Zz.yŔ)S駟E躍5.]\[.9͛7OoWS> >_Qv9k۶mrԪU+n8p`̚5+&M3gΌ+V[SN9%YfpG}tԯ_8fH hw߸bС1uxGW^-[z-[={F^*>( e„[WnFy"2HT-Zq4o~|Ν7oJz*{}$.>/'';V~ qu?1segGn:w7";&TNw=1.tWfNZ5 Ar,]4xXxq,Y$/^_}U[.֭[Vɉ5kFգaÆѼyoYfq衇V앥*{?~,Y$,Y~m[.V^QzQF^ѼyhѢEs1; ?j׮]_#:cc֬Y;|~!^~x7ߏ{//_Wkƍ#'''cw}75k-[vž[҇UVXs̉7|g5jys,І W_W_}5>>UVcƍUVzEFbC9$=hժU.S*^z饘?~|Ɗ+bձf͚FQjըQF4i$5k͛7:*7o^m۶m{K/^,^8>Xre[.֯_ժU[n>Ѵih۶myJ㥗^_~ɾXn]Y&vuΎڵk575lذ<ѻbΑ@_2~_wJnݺqiiVQ222]vѮ]R.ԩyyyWQ(UV:*:Bխ[7uݺu+(tiiiѪUhժUyGԨQ#N88; ;oRr$R# H (G)E9H)ʑ@JQRr$R# H (G)E9H)ʑ@JQRJFydhРhٲemٳK8SZ1bDlQZjŽ[?1bD=V^뮻Jm-Y)G_ԩS <~'1}'qi7|SLZZZL8bdDkVckJ7o^kᆬl TJk׮O<1a12"ꫯN;-I@9tmsN :vӟ (#JgȐ!1eʔ3͚5G}4TT@E T*c grrrbQf$*HXxqzꩱe˖gcҤIѪU$&*HRXfMt5sC .]$)P)G֭[O{/\nnn\}IJTTʑ@7hР1cF™_W1~HKKKR*R*'x" pVZ1}NR*"S* g۶m+p&###NM4Ib2"S*UVʼn'WN8wwGT@eQۖ-[gϞ$;s/.zwuWvm=P(Gu]>l™?9 $cȑ gZhƍtv\Fyvj'{3f̈v-IJcǎQF"?S`} ߚ5kC3P/ݻǦM RJ<ñ>$1Yt5vZ?rdZ[n)hӲ7PSNe˖%>|xt9IT /8^|ń3{/زeK3YYY:\rI[؉)Greʕ+Ό3&n善36lݻ_r$P,k֬IxѫWׯ_t=|{m۶fD`' :7iiiF3cƌےHqʑ@j֬YL2%TǪWSLjժ%ʑ@zqQG1a„hРA¹'Ƙ1cJPM6ruc= .xKeM )Gn)̦M[n&)PY)G•W^ݺuK8駟ƙg[nMR*2R*xy ~[ # #'''LժUK8w 7̙3 l# e/֭[w')P(G駟sN™+VDcIJTʑ@twpfܹqW')PY(GRVVVL6-jժp;S&)P(GVF⡇sg}vIJTtʑ@֥Kꪫά]6vk֬IR*"S*CƱpfѢEq')P)G^*U ${G{MR*R*uc= .x嗓 #JC&ϏSO=5$*HR g>8b˖-IJT$ʑ@=P~ fϞ]]&Ԥ T:9991eʔVZ¹dz>T@E TJp@=:֭[OO>$9 A9zg}v™+WF^^^lڴ)IQ? 4Hb{qu:m۶|qnXFFooXfff;+H`VM4)e]JvQvRJX:uN:IY vVRr$R# /Q IDATH (Gcᆪ,qf"(X@DEFElˢ v%bƣnQC%JQ@DX(6V!Xa=;{<=={\%(GyE9+ʑ@^Qr$W# H (GyE9+ʑ@^Qr$W# H (GyE9+ʑ@^Qr$W# H (GyE9+ʑ@^Qr$W# H (GyE9+ʑ@^ٶv'2lGrg_ᇵqud;PJQ -[G}ov̘1#fϞgώe˖m׾{G4i$:8裏<0 J)9?'ʑ[~L0!zxwbե~ƒ%Kbɒ%ꫯ{uԉ-Z:tFpmرc9w뭷ƩZSN`m΍92ׯ~ JoqmsO> s 1s:th :4fϞ }];6ƎW_}u4i$:tguVzYP.\62H}~i̜9ssׯ/42Hʑ⩧of;w_򗿌;/:묨SNc o{ e]F߾}cws9'k[#~.]]vY4h 믳)c6lǁ={9sd;9@9gfӦM?98xᇣ(ۑ͛g<0.X|y#Eʑ?#ƍMƕW^VvRiӦxǢqѿ(..v$bUVť^CMv-vmVZT^=viXbl޼9""V\˗/+VҴrʸKcذaGFJ rr$@2eJ#ϟ_֬Y3Zh-Z8 6l 65j$cƍ15kV|G1mڴ:ujXT2q衇F~⢋.*=}ʑyj˖-qwmV*3Ggyfj*5k+n_;W\97n7:DDDqqqL>=Ə/BLhѢE̝;D:ꨘ2eJ:+k׮￟B:I9[dItI_gOӧG^ t]v<31lذYfFkWZm۶ P(Ǿ8c\r1nܸ=:u5Np@F8cڵ)%UvJf˖-ѥK2ZWnxy)%˞&Mɓcǎ1iҤOw^ 6ޠ P^1"֯_͹A _(GS7|soW^F*j׮Ǐ.]ċ/x㤓N . t:(</sOFk8∘غuk5/c„ 뮻,TR%FG}t5W??SL@ڔ#ʙ>}w}xaÆ/GZRLW/Bノ׼k1rS6Hrdذa/'k^z꥘*.GժUbƍ) MʑĪU⪫J<_J9rdԯ_?TCfK쳌n2޽{'DOAAA<#qAXw-Z#FnݺAҳ|x7_XD*UC-ZD˖-OT拈Kƛoov[1s[uQѪUhٲe4kg]HݰaC;1~?~|̘1#lْ*U<0N88餓㎋ի~ڷ~ov1iҤO~,Afj]wuѼyRJ)' Pu]?-{G<)'*{{b|rQ\bE 6,y晘6mZo6lw}7}ׯ_T\9;ܹst5jժU ˇ%Kij>C c1z興y督SNѽ{hٲeYybŊmM6-vyD{Ξ=;}0`@|ەoӦM1}>}z|x 2$NZ*(ꫯmիWvEΝ}QFR9Cq~v>7n\r-?իw}bd9ąvENRND6Nj/-Z?>Ǝ[慡uN8!y2=?M7o!:t^{Llq7G ⪫ŋy,[,.8C_L͛_~y4n8 ?fɒ%qYg%TB:th)'ˮM6Yf_:ƍW&?#<M6vԩS,7o>`?>;Sׯn-?Q3fL4k,{lGIdѶm۸+bƍَȚ5kNz(Jk׮K.$z_ei̘1ѯ_ 4z* RL= C9$ƌ(lذ!8?v1lذx7j(|ɼ}_(GI&ŬY#6lr"#<~xf|Ѽy0`@Kׯ}gUsAqqq/;.,X8ҥKUV1nܸlG ۷~!Qn֬Yq9$.VV-F;sիGѫWXfMd?>:zlGɺ>,.;C92jժb*V1i J<.$-[D>}+=k֬OQvRJT^=bڵbŊXpa,\0֯_[.=ܘ>}zQBR*J;vI&ڞ*T;5j5k֌ڵkGjbݺuvXfM̟??f͚U*oDԩSy+/Rٳ4}Ѻuzqݺu㠃uFZz~ᅬ/".\[n&L6mĉf͚۽_yTTT:uիW'^GfRLϏv_|Qj{DժUcݺurXre|w1k֬R+nذ!q|p( ʑ9h͉__G=bȑ۵OzcǎѲeh޼yEl}Y1qxW+q~ŢEbȐ!QJSZh׮vYV8UVѪUhҤIT\ynϏ~;&N&LK8Dze㏏ѣGG֭KOi+**ݻg\g}]vѶm8c]vǻZ7.NZ{řg/R?|ӧO_xq禘(;h߾},[lUVi&??'̙~aL6-F߮,=X̙3'ƌ5jخʛ.(>oG)&(G{/V\hN9 eeݺuѾ}xKjժѹs袋cŒPB4m46mw^l޼9^x'bѱqƌ9rdt!ƌժUx}i7o^j*/^\_:>СCob޽{ǖ-[b1hР9rd^իWGvbѶmیקgϞf+V;v>}D6m V-[-[ƭ_|E%asÆ ѹsxJ%zw͝z1cƌxN(2bƍ{y^z{ 2$}R^|q'SN1Dſ\6y8KT\r\p1sxsΥW}ݯ_}KɓSNkזZ\5uoxnݺs,oWʑ9(i9M6QnݔӐ cرٳg̙3'D Ջ|0̙ݺux/~ +WF۶mcܹmӦM̘1#z8RHׯ?p|%r͚5Ѿ}}em]w^z)ƎM4Iڵk>}zl2kܹs_>|aaa <8{tӧGKTرc|GÆ SHߪUW\qE̝;7N]v͛707|ѥKķVP! {W(Kʑ9f1yDmڴI9 e;dfw_~9[:CW^y%w_J[Fnbqck^gO+1dȐ}|8bݺu)~Z>(~_y 67x#nn\fM\r%)&˾իW̛7/oʒ%Km۶?^z /Ę1cʼ,Zjո?͛g^},n={E%^smʼn'b*A9 Ǽ{M[N9 i{뭷[ohM˖-ߏvڥ*m|nW_}5T=3ZӬYxgϞ)J{FӦM3Z駟f(ٲti2/m J*%^ꫯ+b{3SN[n%Deoѭ[XtiFڴi~at!d4n8&Mi IDATv[Ə߿ <8d?!ƍxSO믿>Ddr$@9sfZjar7Yg[lIώ^{-իbիǏ^z%^e˖իW|)&x73.ro>&O5J)Uf5jSLN:)uQFd}RTTT{͛7'^ӽ{5jTTV-\%;رccǎKbժU)LӦM31-guVFvO2<믣{?T#F]v%dek„ d;?9G/F͚5Yvm{sŋGUV#FDڵSNF(G䘤&M4M0!xw?9Ǧx .HfᩕX`A;sS֦Mk/_~{Yf92valGoیn|SLSJ~Gb~ӧO'^s=č7ޘbwI'K/ SN)JߦMK.bŊk~/b*M9 ̚5+rҲq۷oSN9%-F߿׿No߾qR_ߟxy1tМ TR1"b_ט9sf; f5?+(('x"-ƍ lp [o%?K.I1Qvu]1gΜW^yeGJO˖-o[F?3n馌 k)S$?K1@9 lݺ5͛hV9zZ~xgbŊ)**T>h~Μ9Oj뮻.6lؐhnݺ1rȨ^zfHKڵcԨQnڴ)~ߧ_kҤI\veY;ߩ_~\~fncƌI9QFQqC(;͛ѣ۷o㷳Gq뭷&_fMvmJ/K;,z+#rȚ5kbӦMf#˧ 6$.'UP!;sʩJW:ubذaob;cݺur~VPPO>dnrvYiܸqLJ<駟bbƍfu'pBʉҵ{ă>x駟˗\r%'$۷O1&$F)'! oV̙3'qSNjժߞ|go[.y͛7'Ezh+|mή[.F\rI$r+g-N:wmsvڴiqǖASO%z֭[bRTT J4[FrusƊ+#",XkʩO駟N<'ǭޚ^ rr$@IZYfIHC"G /L9MٳgLtא!C]v\z%>'\1`غu6g}2+GqGY۫bŊqI'%u>(Dg&{C-7~ĨQbՉf{Gʉε^ދ+2|gBY'C>"7c1bĈD}ʕ+loD#GM6w}7fΜh/ΛW6l:tH4[o_rh֬YTTL* GqDs禜Z*:u֭K4_RxgN[#TQwEΝcÆ T=\.)'#)G䐤7BY&$W_}5VZ͹jժE޽Tw}]L2Dg3&\aaaӧDg.,֭[^H9?zerNi9͛7/$8=ܘ3gN5E)ʮ+VkhGnLmݺ59X`A5ӟ#L/9M9 $-G&}67H4wi;n2Vj8s&-/1{WU[N|[org}ҤIDs˗/kצf=1jԨ]v+"DoĖ-[&l}K/%ٳg\z)&"Uv[Gnذ!6nX^bev^]vFI{gyf ݻ߿͹vxwv-s] 3ΈosvҤIe(~erNiu]zQTT٥K~WJߎ믿>|&M'H1Qn8qbG˖-SNC&NrKMƣ>b"HȈUVEݺuSLx1mڴ2;/N'_re|GۜZjkn{;,5kֿ裏8 =eʔظqcٶm&޷<СCrEb…ѠATdZζhРA|ۜ{-[,u떸^Fx˳38# =7,Y$wZj?իWO9w2@^z̟;wniWv}g̘qGuI)I1qձ`9}8c>D,%.re˖޽{|W<)^\rҪUӐM6Enb ⩧F@9 $ 3g%rdR1S8I'hO?hߤѾN;x`9s検&ZjQviDsZ馛_O<WD׮]SL;DsqqǥL\wu1iҤ\sMq)&mΕŭȤ_n]I27vظ{Ϸh"?($y>;rzOS?>Q(G8 ќ#'G3Ϟ=;ܻnI"/27!6lذ$qʍKȟVjDs6mJ9IfϟzD/bQR厤Hbgώ;/zwaÆEŊp717N47eʔĥJWf2oRJ/^ʕ+eZj-owDs_}UI sIʕmܸ1$ɭ_>:wQ+VCf|mylٲDsʑ(:uWN4_RxgE(o#rLG.[,>ӔS2-GY&$)i+"8s=y?7)͑W\qEhݺur?hVU}3f$cM1r$@9c]vI4;qĔԩh2Zfƶnݚhvv(irF֮]rB lْrd =XO?=~ߥ(w% N=Dz8p`.]ĕW^b"3HSXXf/Ȳ׼y(efժUg{)ק d>K7j( k7Ioψ\_x'x"Dwʑ9M6&LWN9?L:5g}u83~*W񚟳'Zj\/-UTI4K&sWN:eT >餓VZ)m+VL4WXJ-}t9mF͚5SLEy; p 把K9M?~|ٶmf?ιeRޫ^zLdQү|;c֬Yosjn+..??͛Ѻ?1dȐR#rPFb}M4;`t +4h7x7[e&rd $ʑW~bĈ%Z{eɓK9Q38?#r}Fx]qqq\p1mڴR @ٳg~ۜ{wbѨQ2H-[ZN;Dg$-?ԩS3IC5j$5q4V[zu[&O^{moذ!8㌘6mZ4hР徤˖-K9 7ވo׭[~zL6-sRLF>PQsNqQ\\o犋Gy嗡C͛͞s9%:#i#;Dg^|m͝;7=2H .L4Wv픓Pږ/_]vVOǎwYdcDs>_G wvio'IQ_"דO>۲eK<Éf:8KtN[=,YRoDsK9Iv%wߔPng}v,^T1cFt%lR*7N4Yv6oݺuK~|A}ٱuRُ zhnƍq&=:̙h6OIZ̙9sf9KZ>SN]gN4LJn[b+7pC˒~ϝ;7VZr""nx뭷Juϑ#G-R{R)G.]D:u>_(lݺ5nDիW^zŜiӦ &{wlԩ#ˏW^y%D>b%V4j( ]ڲeKƌ8~;cȐ!%FQa5jԈ*/O9Q|xicȑ<Ȩ\ri(KwqGz?s{I~ŊѡC҈3ڴihn8O?=.];vkצrB9+bvJ4hѢ۷oʉʯ_~9|DUV[nT= 2$ctߒO'O/N7L[vm1H)t_k&zxO>$:زeK)--[L\ 8p`iwzW_}믿>zx>(>l?SƔ#ʁv) d߿Fe_|1Ҋsvqh߾}!CĒ%KRNOկ~=Ox'⨣JߨQ⦛n*x3ʑ%\͛7O<|beÆ ѵko?y#ٳg?Ozn]tEѥKmW$khѢE'.K4^{1rBݺucԨQ;˙*U?Qzs9'܆ H9 ^z1jԨVڿZj=:k}OFDHr0}ѨTR4J.0F@s m7"R j$(V-ƴѴ5"Ƞ(S(ND hR% S}?42yNU׵\{?]Rsn!7Jcʕ Z*,Yl2[;p@ܬ[oƍӧ߽eң\r1s̄Jk׎G*U>lٲ$,v5kLhg?Y޽;ʼnLcѰa/[n̛7/yyy1hРXjUAcR(G"^zi1"]vE.]-1]3&[ =G G ͎92N:UR^^^92zE^ }FJ}z^qKv~0 E'N:%<%ċ/k#"?z;w3aU|ַ#G;LqG^K. .ǏO߈-ZĔ)S.|;v,o~߈Bʑѣy oٲ%v999)LUr7.|kժU;Dl~?NYK/Ŋ+6lX/_@ϫQF 80كѣ /{wlvM6)ND~1tФzh׮][.Eӧcذa1jԨW\qE|xW^yeRy璺ԩS'͛+WNxgȐ!dɒBP|ߏ.(S[4WX={cǎ%ӯ_رc SIFFF ѼyB;~p1>⏷E\2P(Gbeʔ)SD=;zhqѳgؿUVE-";;;neW^m׏|0ӧ-R ?яHx~ѹsdOx~ʔ)1|8}tJիW'uh2ebܸq iȐ!)yqcԩ xݻwرг3䓄w֭/ĝw}-s[n/BŸy~i\{BBɠ Pʕ+W.fΜW_}uһܹ͋͛sSh<暄O^{m|)L;vl5*֭[O }I}nED7.~iR߂ ⪫J瞋5j(_Ku߾}*GѣGIY&r$@83#;;;ڷo޽{W^qW͛S.u-Z-Z ߦrŌ3[n)Jʔ)SNM7x#ڵk|A %ѽ{3fL;guV̜9_;vlR;/Rt9ݛTɛ4iRt-rss޹K?q Sᄌ馛߲eKdeel5f̘_+2oߞT{'gϞI}NDD|ߌݻ(U=zt'7FKr$@̌7|3z~;7oÆ pܹ3;wM6%_l٘>}z3Sv>}z+W.> Zjƍ+2ǻZW_}5ᝌ2eJwy)L'{ot%e˖Ec)J#GĠAbqر*VӧO *0 ###&Nz|OaugK/ 6LjoݺuѪU1a„*Zk׎7|3j֬Çft!6nܘt߫_|qL2%kUVMAWR7o^R73?c1yԅ)G2ec=\Isȑ9sf\}ѪUx嗓~gj֬ .~rʂ0`@^jժ+oק ٟرcI&'}K٘1cƹ|+oDFޝ7o^\tE1lذصkW W]F~8nFZW^̛7/*UСCcũ UĚ5kzʂ˗/-[FVVV$ׯ_{k6ݛ~ժU^>;J DvvvTX1S oVs9>#///~_ƭuԉ޽{̙3㣏>*Ĥ-'''#+++j׮7xc:K.5kDN 'd }q'3gΌ-[FΝcƌ'Z˗~1f̘|=r=zteJV 7ߌu&{?~|4i$ ,ǏZG+]tv믿sF*\|l2L 'N޽{Sn:~_ yԩxWyq5K/TçN~;vZ9s̘3gN\ve$k۶mR73ѫW")Óz!J;Ɔ ;W^y@g;v,̙y!YfѡCСC4k,4iԭc9qD޽;6n˖-e˖ņ };ruŋ/+W.3S塇5kƈ#I/Z(-Z*Unݺ7h߾}4k,rHNNNoܹsc֭IsCG}@g . VX/۶mKzɓ1cƌ1cF|+_nݺE}q$ucűhѢxW |3'(лw;vl< 8p z+Wի8]СC,[,u{Iz?///.\ .ʕ+5\:u*ለ{{͋_Zjł m۶:ʊ-[?EcʕQJ##\_nݺw -[Ė-[g5l04iՋʕ+Gj"333*T'Oɉ#G޽{cǎPB ]weʔ+~{ߋڵkmݖ 9f͊YfE_ڤIhܸqԯ_?233?/ābϞ=uرcG)###ƌT!(կƊ+k5kƴibڴiQrhܸq4n8֭QzȈ͍>(m۶mC֗T [lIP|~jٲQEKcժUѭ[&;77+F&MiӦNuYqС͍رcGl޼9?qo7.3K|0l?ߴiStMgqFӑ*߿|߈/Myyy={릭Th۶mL4).⎒/YYYqG~bΝ>ñnݺXn]!b*TI&EVVV =?xӑ*b(ǔ)SbqWw^z<ӱjժR[L۶mWUp %)^xa,_D#?SRxcƌu^xaDŊ#;;;4hSO=?RU\9&N3gҤ|裏… dffƼynݺ '?)r,EO9Km۶|>}z\xPTR%F۷o;TF^z̞=;NT8dddĐ!CbݺuѪU⎓}{%!eʔ|;vڸK;i~+VLxgذahѢ*}7ƍ7'!m۶իWǿۿpgܹsB qlٲ"U||IeddDVVVl޼9ϟ:t(HRr{?QF⎔ ÇGٲe;_iٲe;ӟ4*UTqr7ވ}kjժUZ*}٨\rqKu1y 'N>}ĶmR5l0^yXdIlٲ]5k֌ &ĪUEDc„ _?~xbڵѱcS \sMlذ!U ^5kD۶m;_2}x FСC)LU|ڷok׮3fD6m;jժצMx}L:5u˗/XZj|[ߊ%KĎ;bQVU.˜G>}$"}- f<233StTL6mZ\ve k1r0eD\i{vJݻwҥKcaÆؾ}{8pPή]v4m45k;v*իW(gYƲebɒ%xXn]ooTR\~Ѿ}ԩSo>8BN\ݻ7.]+VK͛>FѡCرct).R_(8˗/KƲebEN:ѦM__E>'x1`=U:۷o۷o8x`9r$9qFDDʕZjQZhذa4m44iժU+$8q"v?[n}ENN/'OUF*U"333;hܸfrرعsgl߾=v{ÇGnnn9r$rssjժQŘ5kƹM4M*—'b׮]ܳgO:t(>8rHDʕ3όjժEڵQFѨQhڴi4jԨ(6ʑ@Z)H92/(bʑ@ZQҊr$V# H (GiE9H+ʑ@ZQҊr$V# H (GiE9H+ʑ@ZQҊr$V# H (GiE9H+ʑ@ZQҊr$V# H (GiE9H+ʑ@ZQҊr$V# H (GiE9H+ʑ@ZQҊr$V# H (GiE9H+ʑ@ZQҊr$V#R6kּgY0D{qa^!f(V^ H (GiE9H+ʑ@ZQҊr$V# H (GiE9H+ʑ@Zy52IENDB`geonkick-2.10.2/doc/diagram.svg000066400000000000000000003701231451017372400162720ustar00rootroot00000000000000 image/svg+xml Layers Mixer Oscillator 1 Ampltidue and FrequencyEnvelope Filter Filter Cutoff Envelope Noise Amplitude Envelope Filter Filter Cutoff Envelope FM Oscillator 2 Amplitude and FrequencyEnvelope Filter Filter Cutoff Envelope Layer1 Oscillator 1 Amplitude and FrequencyEnvelope Filter Filter Cutoff Envelope Noise Ampltidue Envelope Filter Filter Cutoff Envelope FM Oscillator 2 Amplitude and FrequencyEnvelope Filter Filter Cutoff Envelope Layer 2 Oscillator 1 Amplitude and FrequencyEnvelope Filter Filter Cutoff Envelope Noise Amplitude Envelope Filter Filter Cutoff Envelope FM Oscillator 2 Amplitude and FrequencyEnvelope Filter Filter Cutoff Envelope Layer 3 Amplitudeand Length Envelope Filter Distortion Compressor Filter CutoffEnvelope Drive Envelope Volume Envelope Percussion ... Percussion 1 Percussion 2 Percussion 16 Limiter Limiter Limiter ... Output channel1 Output channel 2 Output channel 16 ... Kit Geonkick geonkick-2.10.2/doc/man/000077500000000000000000000000001451017372400147125ustar00rootroot00000000000000geonkick-2.10.2/doc/man/geonkick.1000066400000000000000000000011641451017372400165700ustar00rootroot00000000000000.\" Manpage for geonkick. .\" Author: Iurie Nistor .\" License: this manpage is released under CC0 1.0 Universal (CC0 1.0) Public Domain Dedication. .\" Contact iuriehn@gmail.com to correct errors or typos. .TH man 1 "18 August 2020" "1.0" "geonkick man page" .SH NAME geonkick \- a percussion synthesizer. .SH SYNOPSIS geonkick .SH DESCRIPTION geonkick is a synthesizer that can synthesize elements of percussion. .SH OPTIONS geonkick doesn't any take options. .SH EXAMPLES geonkick /path/to/preset.gkick .SH LICENSE geonkick is Free Software released under GPLv3. .SH AUTHOR Iurie Nistor geonkick-2.10.2/presets/000077500000000000000000000000001451017372400150575ustar00rootroot00000000000000geonkick-2.10.2/presets/AVL-Drumkit.tar.xz000066400000000000000000031200101451017372400202630ustar00rootroot000000000000007zXZִF!t/] l1۸.9/&Fڮv6 ɓj] **ZI-`OaL2`5_7@SCX[]S)%-x~ t"p4ofiձ/Tq=L%7ژ#lؘZOy`ySD٥9z`Dq25WS[}gc`rhk+J6>\;.FC08Lp͐ TONkIЅtG[sd||UZ˥T{+ ̲ӾnޣkGhh(+!yeIj]ݤNgJp+ ?VjZYvC.7K-݁'`H,q$<&@&1coLo/B~9EzoT\99COʆuM1slo$N,MDEf+^+B{XGe[LjFz/߅sT,+=߄v{h|ѥ1~d>JT #@C~YVUOGJ[.)Ih<ծGǨQ>H'k%>,͛wem5c'*]M\7gy ZTД;2NU }L{ cmM}߭"6~`KQVqͅPW۟"Gerqh5тíu>Ip[[pf܏eD5&/"Fp*K]%<4Zɸ@f{坩1@gሌoV{>O V3Ӎ6s8J!_rǧળ eAڕEp񪦿 2vGC}Qx9E-JI>nA'3Ћ8!:wChY )`)VA%`?0|}q#.QdW`5MJлt*RD3v./IL}W^CVsd+鉡zbx?|~hQ?nQ :gȑ~dC81=rkvLԽ4XKpVL˿ {9EYN~WawPmi~Uhf=q"f?c\K&TK۝!wDЀs5cvGe0D\BxGyg/X,m|򢀅Fbo/fja㜖udRfzF[s0 ]FO㥅 Mm)Ɉ~9 j@fd"8t`rA AJ@taF;V6: 3lr@F2ISQZAq-`$uf `A͚r/}s(JPjD(#!wo+~ J<@fQ)]J0cs?):>O[k[{0Cj~ l"6A]k$ ej[v( h{Pә},5 9>"c#N >q fWA;p4*J9*ŵ$M,hk3>,$TIn/RuaEwI i(c Dp%.Tt `<9MU@70x&Ek&L~{ 7,\~ Gwxr'bc}fLT'F $D}cTC j :̯^#CnjJL`J-R5ZmGi+HZPSEIf35jJa-xv%Mg'5L Dwf4 ˴ڻUAוVQgJڔ4/g QsIi>VdD8M ]ִ.qD0^g:iB43*/Yhƭ <`(:ە Se@dP׃D Bk\|}lЇ#. ?33o[4xbbǁ??Uco ~00v7)ٙv**?p.'d`UzoqRT}T\tqa-Ly- i% }PCw15Yx/BPƋcn"r~{a~1\Jc7ka:E1Zr\L[]7 b14_ T8Qd#|yF Ӵ!<{ep;]|X5 B,y[mg,Ya} m$Ek8a>zp/_)ɷ@&*fm<:f?ꁺZ]ǣv Hn5ޝW6bc太 2a$32=F)#Ҳ-Tͫde-OcJ1 O3_=jB$X6(sKXǂU6+8DP/Eãu .)_Eb; m*YvMLfn(8th0&3}юX4k/7f␡9aS$f66yQ%4$>dWF0WytMqMtsŢOgT,LC{iórT{t Bp PYÓPoSȹ؛$;-” !=X^NK7+= +)LnJҝ'+1:1dSOo#x7B^ fdv-X* 󠃂3K2qթ1$V,+GL,1ƩQWkb܁|/ 5pʘj"g9Z䷝GU#ޗal>*#` 4iG `%{W1͖ѢFᦋꖰi*.4wN0شf?Z;xEQ 'BvJ$ I[q lⒽ>=PIuft8/p:M]Z29qwT VݐT-HNrۍ4FlBJT'^$8خP<: ķ!zuq}LoG_8(,R1X,ZtT8 @-Znsk UDZ#Wib. .}ai4?lsyG >V ߬cW,*ZxeԄ.ì󦊟F%L^gѾ˸NS#A#֏vgJe-#6]k}U't!DPhWCrB! 3mAD$Fi1  x>026@7]\Tz juԤӀR4OSگb\0O.pO1x&JXw|P$l'# h/xy veZЉv_@窞R&jU 7E [}mR*\:_R)4ݵT΋%zyFz*IM_æ( 5-j1ԍ:Dlb 69~?̳ph1L6S U9Q(؎B ͢(#aF11G1b+?v}V5BW5Tq,co\(F`[Ǘ/!>]4/tfcK=ffvDX AGNgX2f8 `5d<\KKdNg|USHԀi![-)iEzm֏3A 灍M<@}^Yǟ}j`1^i(|/jlO [zNt G%bU ӬwnqTT|9[$f6]{怞)i %./JC8S&n#ҴP+x9-.~Q_1n?ܞ&D'n͐⁽۩a}#Ȯ 6,ˌ\U^GŬoMukNe/;R꣰aA,B xLSc͒laj%L=>Fnq߰xװYZk嵝}V!,#KNKW3aj\+|ΩM dn/(}i WKE'qA+ 観ࢰAuם3K9 I| jӜ\phE"'ܳ*tiϷp% *BR@XڱLCzZ+ex 63;H4o P|)ze`~&; [yH$a+e1K|nXt L=""Ih>74fXHeޠDmG࣢6c[Yp!>4P÷I^ UGTPFvͭ5iwc|UF*Dʼn-$\b'Wdż{K=m8M&d/8MqS3Ud|ϖ<cFx=nsEo҉i?K~O]fCWr# ձFs qO$GC u]q0!j\C3ѫfyl} 'e>f;TG_*&Bb'춣a~ Þ?b1]42-s`)U=kU9h6@)Sշ H [/eG*\ʶU}ujkLNn-nL~N!ld bH9 6i-])}r#_DP]҉MN^oJb/gN4p9L^e7:zY~<7yTts ̉3Or˥{TĠ$p N!㑢qmjy*\p͎QM\5z.9/vx EüRcru!*Ϟz.oڢS.^!)hJΙIm#_ WkB]a6'*}Vn''}pd*NU[Pޫ)@˔3U*_Äb1 4LDh/$z\^Fex]~X;WyORKNP7u^!k)G wP'r TȒ!j.RŇFҩѮöt>i5ZW*.fVol?[9Ifqd~Lq?;HB޴ӭdi^!J kA[k1 l* %a&` 4R }iYvcY7ә'j(G!|xluP >QGnyܜJbK;c͋uj'XM4L0?ވx]Y$ w,y7M(Ak7a|鐍*oS&u"8@&w;ܓWi+;-׏\)^E̸Cie9W3 8HO^u"ڊVk'_ <|1ҥ\ 7Y؍ڸ&0vEN,;Iq)V!l1]%7؝(s"-o ɗȗpuRN:&^Tb&9%1*r+N"8@2[#_@ܕKא&ɖ|[vmZTC'eU_1zX: 'B-B8ó-g@cYfR,Ĥ܃K͔V%Z xŎ^ӹhIF(C(iyDsnezđ6_!}LXW1AO8HGB7THe{]'*{ GY#xmFkdW ܾ{Uf ĭ3i Fy o02"]ڌ%^*SBa9PMdTҘRUޒ;b)%ra ,v5#3̠uG/r/., ~H'o N#eTc 2B~wY&D!t<Xw,2vOEn~}˖9ne"i;_5%Բp0Z olCM5#=2j:h!Ö0'v9z}Zgn"a޽~0s~HVOR'_zl{7m/5Ou'n_,/@zvUTfh]cšVmzo A PQ( q:9vlDıD/v 0mITH5 Ped58o"=lqtYwd-x!7`x u+_zS_e9sNkx=‡j t&'[SM%Vj`ˢ7@g_ukkJ}@o#|nNHk; 8xJK3P@}ZM \^|kʢ!m6n**^ dr`ʗn =ڙ[,C?c0/qe+m7I4\.SLK%>񳛁DڢUATXaӔ!sQXxO_y] k klSF`%0>>+ kdd."nЋlJָ>(Փs[s~RFfBH/ܡ2EAt.Q# sz^H.]txo-NĔ&V$0z7ϵ~Y y؅t0#"{pW1Ě}m/*j ;ٍ6!`Mc:Bh7C<f3&f;{7S:,d;] >lgm*i¼2fwlbgC<^knŪnR)Ҽ1ɢȝ`L6FzwQL=<.$-x[pިSTo5 3li0C h޵+)և4s< EBz9mKq65ˈ4:G5!'\R5Hw.ݽ8aJN{(q98ngIEeiWI1]KUo4YώVlH(DjTLHԀaRc6]9ۊ9e|ۊ$~5RmEv·Bv j[ЍGUs.g|.LB~$Q=[DN8)_n4CW*3i-Eݱ7DxY~U<S0ˎhvlC7Tn{ A arb ⡌Ĕ&xC}08Պ.P2)p|"VD ~V\q?Y!Z[܄(uHTbA"* G}[ʺPG?c,"ͤZ^g5!| m:q;f(i* 8W;OwC#'RijgHJF% \ ]"Nl*D1iyzߵrW޹њ{ih\,:<詏21qR (>lqR<F{PPeop{3[lأZ sA*؝#VI?fPK)ag =W8'%tɇP^8 FOgp$q1] BFH,rl-2ubn Ρ1LPK"eh4t{[X[f]J{ăXݥ"mdvh|ecK:f]U7!8·o>"7'>:) Qm56H_ùl} Ʊ}Zh"wbm+K|Ԁ_U1QleLo71x-[ߌ*}37 HNbHSLIlYO8OMH:T-hx¯dTipޣ(fiJxn]R&m-fd2֦1[%1{g^~Gh`ab]ߎ.ln')iL-b n"ABE˚jZؑD 4 bkW.֮-4=𩱇6'de*ZFej7C/D hJ TF3 #6Obhyzl\]st#1c!Qk@(dCE~%m?RpچcbFBr9(K'KB_YM}z5#{fB?pK8iYuYQ`F2f\4@E?I0I42_=ٸ|]RZ(pfNA OK3O<'q)q" oQ`av7## ZFpPY Sÿ%):0C1u>.>QO:?80gKҦC=B"MFF & lxZ{ 8>,ZRƕֽeݬU~PyRtܴLe=ST$P.i qO3'DyAɾ 8kX9Tac@>51Nz*q?u0iC&'8#k-޾^YsY o[ h [E-0;B3^HCmd^mJR7 5ݝ{Vv߻;_OͤjЗ+:y4[oɄ+Q<ف=8ے4diWQm]TҕWZi_yMB2JʵF-wm9nCPE-ɋ?#C.|v,g 0_}t#?CL8e*f-[PK5u % 3XS&4d" h"ۼYR@-8+ % }|, !qtr@fgJP{<4ih$7H8{75 D; t?n>B@UUax52te2.5^LF`',|7KT^q#G0ZlhQl:OI㆔TFH'gZbPV/:, #M6FcXyT)lRpc^V*q6\vdH H+A؅-oO;I|3@+cd= k~]tϴx Ak2#K(~+^ F>_-4 CK7^]Ly&R9<h2AZ45~2q!Peƣ0FSdM-(UɆϽ6y kPL.k ӂ\AnpFUecH'lݣ2;}2};jj8yA;GHؚ.]VX2ndKXy(j@r*P1;3>@G=jrf2$G'M?\p琢TwT7|B"-sxi ⮶` Y/tPA)Pp)͊ЕpcH |ƠF\M0'HQ]vWX @, X3Ct!4ޮÙ c%@zphcJ#qhPV]fB\Œ[:H Qy8Gjz}w:Z+0EcO"eyhpoJ+ l%3w1uI2y(;2Gpe HKL)f̷x}\'i0G6ڰNh:>e+|Y# 5r"1@[/&7ÿ\f::0<7s oDk2] *D8~] ;vBF3Qe*:%h)qO2$d,v9xđĮC8KVzOexg&;5߇-Џ.q?_`S5.Y$#$TD~ą #]US_6~z<) ¦*k|%X8(7i΅ c?{V=ɂ4y˻ <>pPi8s#>l!@5+Oˤ  ET%h1SX[.D@uS#T3~vyiYP-1Dw}|#1.(Ngs ŗ Վ^$1,, E#lI0i>sD C*%Md8kȇiHӴ73,])f.kL47XdcF{+m|H /!axti7Soڷ3'(Iu1G7FHhg/^^3yonj'S(^WwR'؉Ś ~+lIM\>ik'6#ڕApn![&Ǣ;"FM ĶF"`,g2}/E@E$]Lݢ/J#GwnEEb0I pCS3;epm֑4%a V|?%HmY-6G%+h5/'~'N|Bmm\M!Np#{r8+4(0p;v Q/q2O N,Ymg+"o| $f%wOv D)H9`S)Hv/֝7ғH$,½2$,Ja9 o(v(,͵ Uč` ZBA,B|̾ o!1CƜ|N܉oPXweti͓u+9.WFlGeiyʚ}3Z_u2WCO dx58\hY$d=jKqt2)Rr;&4L,`?]JF=$1omi! e3ѸSo9, _Z[e&TEѺK5 )^5~"zg8c; G Kv(s h)Le 2(iOrsǚ3jBC흕m |Zi[4Wᇒq?8*#}> U$̊ 8,W6?\wƉ`:ϑƔ F1P0~bOkX2oMBHu32qo\jM*&]\2gێ3)<(\7}f@{6@.?w]4Q'LUN|NeJoKXǬ?GwDx a}Yt^AwUl|˒$JsɘR1>kdyk~;I%"`dިRz pyGc%r4ǯeZi@Iyk>9BJygɋtOnf~P.RҜE~} ?WWCXeN۶n²Y' ȓU}v픫h,Nw!AbOA=bDh}en.`a9Cpjಱ'W=l4P<W *[IU?bLt&HRƹOF0f5 P):axcz*2j䗤.ۼ#oiÛ<:Xim𦰏Dz1!'j+*3ySڀr(Aڐ&&ƨ;.7E fslS65G &mKߜ]?\5m'Ť7\{|Nv7{MQN:A)!B(!vߧwJ{欏X|5@FCwN]8?ORŚ/YۇoUWs*|Iyo"dn3˨EĵWuJobmbfDv^9>|N=+I|rCƏuڙu%|Sd-ɭ.U< ntˁ8F3}:R!Ё{82 8g GJ40f%z\zܖ6eo&E œlϾR fQRJ"Ha/ctr n5BXa'>YFaB@(uxOJph=]/f GL;lOmQ!fM '9dH֓'Pf-}gt #392 V^ WW1>k,d]@/ n@9^#ⴇP +&hN2ٳeѺ?K01˜b9C躋\=uH?#Q j)5EY~xK,5onvC U>(6dyQ 8LAS8" D7!ǯNb+AX<.}9c;UarD]mz/z9R6n0к\}m0*2i3o}}rHlVqVES8 G}k/) f佟S#uS|-&F#i0c.C؁qbU ޢԗ[D[d8 `GV3 "V5:JWf a=IL7=g7ɿ^v Nҁ̌N]-[1}z=ihcf]H魯h+B/2w\$:]X5WWTqꘛh+n |m}H\^q3sE^jY2~A\X&gH̵%mi|^53Dzdw,;̽8:tSFV$z?b(D&gA; >Yt;w9Y maPRnBeA4]G<@4yqhf~fV+8HwHf3&\ AҿǏ8(E[`>P >l;5S#Q.T'IC Bp:p)+ALRϲ*ܮGdK{F51c(d[g&Y 1f[EMՀ?qtlƲd?"u*Q |i4,<P֐sAиal[(xI/po\W@Zz5F|r)+ߌ-MvС V})z)l33hY-ul@Ҋ I hmC9;4Cn@ʡuÄ({3 糜$O~0qX'/WqO!BDeH֟ꏙM!)ޭO0uQ (㡳 ?Yz6-Ѹ]]Ո C̴ Ԋ_(N-<٤ϴh9 sޕnb~'C 2Qx!4qPo"%"T4G>jRN|j<󛅢kGQIvtIv#/v( S{t}!̠YAOb,a MV˦Ym_&7۠D grն@G`Bp\6 tHQ >SJ<>%0zH 1PbI%6tSrL;tVβt1֋Tx3j){50N6NaKh~_ );f1ؒjtcq[h{ߋjp;

!cKDrz? *D^XZM!Жo^&3C_9<Ī<͸RP2 { j޳>}F@Gp'<^ OrQfP)+./{!VJ>!L؍X|-ӬLpIҟ@EC 0|mr"bm.DUėoT\&[6r<"U@LڿKD3Sȱ{\Oqp϶0Yqy_*t;E}$p^6͝}C἖ 8Ym oUj%"Es<`ز3_z r9b&R!/Ŗ&S[Bu exykXmFz{H%S5MX@ 8{㐍'3hd/UqM!f|[$㉛6+gf/vMI~M!yOsD Է AR~ so"?Z̓sa4 #ÃִUCp]#JOuiY$Ӑ͏aX`LN^%$!h(UMG(#XMTze3vՂg8hk09w;hګ ebF*<p3*ig͜Ϊ4P B;¶V#j8F^}LNTJ9eR!]r[Lծ۩Cz,p U۔ jS)z   dZCk;ؔՐdZhxv8HZ0AAo'LQO(G``U?5kQCq)ve蛢Ac1ÀH҈Y&{E:+˗"|tܜmTثAj`PM*"l-:Ι)  mcFš`{Z1t z-V T*W'_[ŊB҃{MF NAUwɊPȭ6_ j"j d+go7taEel=8n>78$<_.2망7TVAGQUDaJt301Jw\,rLme:a"S@jl䯷{:6;7 }it}^d1{b/ ߁,T]1$^B,-ZSD_PGa{~CꇨP}V&)dѰԨ3 ztu6:"zQG +cSi4Qh5=kb,Xcm|q ߐvۓH\?}:M5EaEL0MCE)!lڔHM> ze[t{<&5Ǝd8{Y4qk[yɫ:#;+Mxg/ηY٦?TpfXah MEIwVq+S\߅/֨nYPdE1P1u SY)5!%inwHH o]l4n,{w =@s xV ڵMR{N|mBD3=kMtV5BVO`Q*PWckxT"0~42NFLb|y;FUoM~@ :NJ? 8M9S-5R|aTWp[U]"z~]Cȹ}G؝g7@ɷS" ghakTxPdv/J쑗[K^?OCB5u-?/pfcm1pEшx 5} % 4V"荂 2Txwy_Cpr$bcL*I Z%_n64oT ݧ揍\ Uްizߧn|oL$9= a}Ump H .R񱄸Y~v=xS6)^ofׅ̞=~֜L}^Y {ɭE!X`D`"Vq9:}{D}zTzmZ۳|Gu`Q`I[u| aė٫=5{V+rXt,TC'' ]RA#5ATCWQ}oKP,\1E 7c4 DY֌i `^Y ESػe 'ӭZ+Rk("߳ `C,0*'[E7?- pz!hʞh4r$7;jy=:e/"`'j!qs4z35 RDw׆F)f-`- 2 >2F ihE{ថfR$nG>+[Hx”\5g&E^FdP/n )AlWԉ} P>(;(Ա  b@WU t8qo͵*Uo&囓#/y$;9k5QG}|&׬Xusy)bn G8oJ_Zu;RzFۻ290c\a@]C Nя@IN\9+v+wmg`qSeS/>T `[8C L=UOegETsEC-ܳ!GXmݐ"=a?9OaGyNEIąC\G]L%B%Zx QƍB9'0bYALĄu.ϡ^\MK`s=G@)Zp>2Lz.NQ]ZuX͗w}[X$|e%Ɣͽxc*jĮ$kûޱ>KU§J*:ۦ FbJ6河3M:$2c1Cgh5ιg Vz5T[p6ϥI{"9SB7vP{(~ e=5a4Yݴ:g9״]'uN!D]mGbK,I,x"1tCf)W2! @p5$ڕIzg#Zt<A 0Viϕj'ZU,v6 6sŸwJu;^y(` \風ʌ%R3ҪxsfP?GGظѸf@ȳ{ɻN U1d %Q5O:MAT"Rڤ$3di3Tg OvĿ|lcBQf 9#%7HKۜO}Zm n ,3E[-[AcEu}BG/n__"9!RϑA=}|GMK8(48zNl5iEc(`밧?vM W>R֥je^,b{z{X  @Qo綣ہE\^5R?n94!eHcz$L؜T@F?[b&ʒKΕ&fc> uNFq6 rXv53k;n?n0KF$klj/R#@pӟi<},fռPVUMk-}պ4mNt/ 8~Z=ѱVFeEHRTwbu‘sR|w2 ě{OjأqnДkJM;Uf>3cmmq>*s[ L/g*|MU4r(z'S]*fxvZ_/?x}d~{c{Komcd&<KeE1`R#9`2j0 ~@6@J @׃U^L;2WjJ)|4u %dXG>̰lLrs/9jN);MG# cZ%bqBs((H%!Sr}fp3UjRyZ%971wPtl m^mvRr4>{;tJ{Pez?aY`Sumo\1ivyf>~,Mn2\t\1ۡʛ2o/JG'$|DaȸX~Rve[s»H aZoK׃en~g΁̚Ȼch91T`JBW Xn!`@>&cPJT3ӷdq5{T<:"-(tJGro3,ꤴZt~;cU,e~O锦hK#~N}:y335eêV\2eR*,ofu7My^С0(-P~r㑗`BFܑ.k}vzVLQz{_!rCK%_ٻ+.ҵv eNDصfY^uϛwW'E:.[;Rg.: cڋa>RЊ/V ;ZB(yI§dNH11>VnCVðI/D4ģ)ά NGcX zpƆ(͸%Ղut3Ýz0BM85/7*R$"όT[o3j\$!F[eC~Zo ؑ7+Pd7#Q@g5?mبQkc֦U>QS ȎG2 (YQK{98mc9e}RTtWxJӇ2<ɊlNZ'\ hňz+P|WlN1UbV(x]lo֙3B2z$$N&W h sdy]́_?kqliC;IU!Ng߭"uOBEVn_rMs'3>I^ sy$IQbѷ'B\~ň\. {g0~1ٺ.+~]XZMPaŭ.c᠗it76WqŊPG!Nz9_ \M6=0Jl0{; X HD3ÚT|^8}\e mƑwIvx`T\lF4tdڮc @R?o(6PR4kc諘X;kz\&^?,Y.VhG|8p#e`Xn+8s92>lL< :.sc&PRZ9e$,L} S5~ɰNc֘z;>_:?! LD"hP~h0#u%>o8ZBQnޥFm놌`ŀXP6`hm9Cj(} J#0|eu*M\3N}.6鳖ji(ϔx)[V:뉖I!ⰐœuRO3ڜgvφZn'87~(aW$~4_K=&t#뺁]i`FHcht*D\UkctQHIj/Qdv ;)M&u63.j]Sb .a$񜦛Nt8Aui8sy][iDHhJN+ )kbZɀcb699iU#FW|i &p:U;4PQk/-] Ā0W p8aHu~FL-VDM`LS]/ Q<"cg y=ݻ\M'_Fd%2Us"!5cخI qjWܧP+@.VM[Y:=sg[_^Z#c2''ro hTMٕ( g1gW8fWvXF 4z/OBy8"phct1G>a]ߧL_zĬAߦ'i>Mll΄<6IB3D⯃ܞ;iWZMz  KM,c0Bưc2qen~& n°&rjiysKRV-{uڡ}BVN@lC8$^?Ue^,zg>{!Jc1-i`Z` cV-R.YQV.Ljd0 ̴EVѤCCQ-q(Ty­,Of Ha-e}79φboMmƌS4?+T?qU=*8#=O?!H{%䴹Zk#+TZL18d!+ġ,;|hr̦o ]q֚&wFZəĖ5͓vc{JQ!- _)3!<8R='+ɃHSd$C6F=ޢr- 뮄|-Ef~9Ө`ޏXӛ)2 C,+YE7=yVdtfcaKO@̠xF1*=ֿKqs_ PbNo_.P0n-E|I3L(ɾt.HE4o swh;g˄W @[spx†_ iR.fiSp]Lj!$>r00i|~Y|ROrEъ YQfWB>Pqm`No!^ɪ|؈!9BpׁP,3JgZ\n`Y^E%n@ѩ%F8$au$PD*r%7tpmdXfڌ?fM{gn+qi&'c{k/OS+YXZ-͒LO>@,T{u7Z߰zEL@ȝ%1T1='8Iwߟx&];ܵ)e 7jyzWFK^oa~+>cdD}U~,1pL]l/lyRnDLW.ſQaւ?iOfĀl~9WU2IHdJה,#z?vjm{O4cg'xyĪ|% fgyy{|NC)Z{,ϭ_r*{ڥ J#/ČEwL3p.yke=Hp:^m''׷iwnZ.CzECqs)'nj$-OAxOr(bEz^2ՙLgSLG$YSYփP@W+i?V=R%{vKBIDQ^kV&/tK"WW9B4 J λU QxdSv{ }Jdc~8o&ET;M^mU!m¸?wמ&#(\_#.Xrn$mbmb_F}YI{1'k묭؄fQ--`kso6!/:f:h-|0iLyM*޲΂2fJRdHQץZoC 5-1u-up*E1ơ6uc쬩o> k|F2A78~1Ӧ/6~u^*XM[\Qo}%mUcdh|6ޑ5c<|{m/C)Z:FKd ]EL'?Z I+U<7# Ů(664] K>Kk1xyRG69$VZ+US6;C™vr~F#]>"0Mn -yT *27 5:]iM/o&#Uc6 M*d^ނ uر[kYqTz4ۤ4c.- ɓxH8R=?0 (b-:B-w!fY 8Yy(W.#RD1#/}Xa ^ԫSκt"=[PW gGeWQ8ĊꔻM%⼒^;'*|u-ju4s1yw[nq(b*7>3ۈv4L']kK"QbKt^t̫e75/_+r"}]L 2Z0mMn'qRUjx%&L@_U0? ^Ngs\/ g֍dVcd,(l-S6ayVqOUPGƶKş~=pcOJ4m]Jv؞vK`< vjJnBG> ӹ\M,7:%N2VSMMNDA)W)iL [^WNu=Nxcve"tIF[2+|<7YAEȲo)q! VR[ݥ ¬`2XI^{K= 4JΡoߡ/KQjzN3u >V+ Ņz{jmn/A 5x LIp<A4q䣼K|s0/6c`"@O8MrYq.,t'>VCus:'"agǂs}^7*Z^ѴAVp']EN0Ĝ`B)KT%/:RYd &OQ*̆*1bsrAEBh%N38?en5]EQ0<ƹF\s[F'5=޷8NI:,>:VONHOZwhαˤwu|^f{wdw|%\i`T$I?Fj乯aQYM;<PhkZ jh±Ā 6JadPW]F.KM NIt*7Ky%@'ww%NыX¬kYRbo, p?:H0rl,ct ~WJ}C8qrjHZ1p i✒k٪kk&1ьd:Jۜ8UWeW>DN˞9-pɬ/1aWdOH=s|'GlݍANLdw+M'Y"L LVͭĴۮu8PPԼ v~;=0u:g,N볝;#V*Bk? _DehI|JWpm5W5n }ڶ-T͊?jxtniK&hVr4*Pz ϫS,=mn͈66.Y+Zѱe1u9ʨYYEN')}Pu\q/4Ywݣ5Jx;0צM;ps͉3d=,:J=Y MMh4:am!ZU2 %DMf.4ԃbǓm"mZ{Czǩk!,uT=,&/^1^7L'&ޗw#'}zq/X%K;IU׉X %9}A>:{vI&&st@>IarByu]j54dڷgY$\ٟקGʽ*ei¿ӳ W|b>Bvf67d;XK]wE]1IZjذ~8yO!fAE4)݂A*"9G0g]L&!pc߮o-P|,'8^QZ2U1ޏQ+ޓde)rln.l7SÆP͡we7PfH{ SC_E=7^}[R 'ݙU4[ӟކtS$+惐B29aTV$T>0D>4g|ΙA1 8 Щ`5לRK9D"#>b"h/u {e=GkO_%K=%uOQBnZuL6;798#,ȅR|vs" 9z5}jArM7ٽu'$|AbF`'Y,ex d 9i8dYž3b*WyP;&"vUowmS}e}gSF=&ͻ ŸyvTBm[GяDR, @ /tA5rH4"#ڒs0s%G1j?{yN5OLQG{ʗ2RRkSh𿼜3?HB++/KC!V PFi: Jh됩)cm5P3g:VO*MKZ *HEok1y8HRqlbZW跽fʏLG\jvw{DtŢz;C53˄m'-M`6qvD7b@xƖos[!s&]"~&Px04SHҖY, ދ+w7;xܦOae΂aֵ$aO" '1Ӄb:#Yvl>2L&M0Y(>]spBH 3 B'r<*5u:ޔ7b*Hx]TgL6kN9C?D-5N~>Ӂgcw3~ ]YGϥ$U'3 sLh {LF 5VܪjZׁ?,i^i)R:+ͰM&sF@]d0M2a@Vݏf?Oq' /e au~#⫓ؓcX޺* pYԘ} }:GXX ʷ*͑o@7 ldN"]q~8h^ÎB|w*ghA8Eq`jC뵰P_:ihYYH:m<" ~ 4Zhb0e)KJ,v)[e|3[sH"UAG?g+*ヌG{~_ `}eVhDIc|WvݒOU1aQLręxI/2z-@ }&S#|H)B?T5Q|̶T rĚܰEM!d a;7ZD^/)bj| $FmT9~Z (#*[(٭-[uz~yc(j4YOW~&]=~ Uc0I$qM#&yhJKC"Nn [+Vh(L[\O4Hiߝ)W"bv N!OR22d}yk;˹OZ#,)BfĎd,yr3 dUol+J.쏐j.$GCf3b\ݳj4$[F%zfݘHfŻ~bU$\ j"fdﮃg@wxd{f95L@!Q6EdZ[Qez@Mp9 >2p}^91gUTI.I4O)m^AIX/ .D&G ;*K޹smIj6[S]\ҊiJjRxd [^&'vE4o#-lrxђ/y%- 􀬍𼋶SҴ^c6aS {t,n^Wk%KΡ^NC!e]g@B?pijYV*+3Tv$DeQVȝs%8#WV_>,J&06=i1FsO4,]ժHE1taj~CޖEJE[O2?dvI&0q" q29݀$#eRN&"p<*}գ"oru$ i X= . r5G ۬\X>VоEWo{ҫ`3pnx,W, `IJN;>S Krx)pspvg ZldLW4? ;tu7U@a9_o}EI&YcB(fhyOl ?w !]7]zɚʒ҅ RC+]ƇF7)#HP5[E2ܜIn3D|fb"g2y{%Yh w՘~Wtx\d[.ۍqaÐ4:".6'cKKr3Q_p5꺿1!'$,RͨC]K0)Mʯψ/b Y6C9kl'ۈd6Ĭm^>:M8,P%6oԲBHc(T4sXU XR !h{#氻ȅuvF6)"xq]mY[mQs "Dh K+_Ǹʯ[p4-\[Z)??~!XtYܻܺO 3|Tm\hB`':;ԉ4UxOO8W I.'ii$O˫_%'nz)1 -4Ho.vFwb=OPJFZ?-p}cG_jk+Wz6HfƱ aхh? Զb-g17n/\K细Op6,d<D&y!Y3nILž8X̿]Y|~>N9v1 9^ wK.;VL3qҏE~b9B@ j^jTju EC #|t[ე 5Q0SHYXtK/ՊTմ({ ~_!qKх"h) Jhϭ}ퟆH}f&7[juՀBW0V5VĖАv -i=j`ùJ2h`QyJDyDeo J*~Ǭ.v}= /XhJ9_ ywәDYZ})9Q,p0#J@!U5(eL j8pR3󸊂͐KO+?U4Z*jΔ|$FL:>c &6Q. bHE9m+;c\μx2>nzzqw?l@u{B3ڬў59y9_ hYH>M$r(_ buaC3AC҆hzǶc[ܙJ\^'#FԿtc5{2Zh[uUzc psXr0kcь "3hbre=PJglQTk C@ʗe H&.D"fČՓQԛr|oIk~ϤBjƒ+ t=j3/HIZҾ] />31p8BuDꞴpȜlb!gJy'C olːyd52oҋh%TgI7E /D9=`Kw<&7!DjuY7ҟ[.#Q x3}+*GnO7Ƚ@k5 E+@ԒZIRaHWNIxtmmL}_wb?["3yWdz`];p"W,Eix+RBgUP! A/L}yfE8%_Q}k:Nf6% `~CW'cฆFp4;Z)+غ84S+ BQL~I+5u%j "{Z%"8\nͼnKOd tI,DģcٲwRj+$EvKi2s5?Đjb 5_=Ğ<ѓٙGVbq4Fn8Isp^m]R@( bR{TSqg yH1>֔W7Y&M$vM›ŕ p)5c GLV^e$0V4 ҂0U x?OǶdB=B 2Ҳm|b[cIZ8eV r?E*"w0 H@S5id{ek %/  CcA5]>̋8/LQSY [zT_* Z5y49ng=!Q89ݾ%JבKwS׺ܽHr@ɥTVXڭ,Y甩ld~Ѿw `C-C?09 [q>j=[Eq50tbthM&r}0Wqbjo4@ܜOmz"иsrҔLJV [/J$<~oVaOƫ!5R2R}BsMCp\W}bIv4 TBO~|ìbJg z*FoZ' (EPl3\p[rpdd7eŸ A󫁪"F/Q=n6x́?`~ь2y (Yy7iS3J.SD#i0IhwdaN -I8% p7vW$/$5Z\=ѪT쳲4_v1'6UDk :/(8>h T0G=W^2".d'2'*sxkb#xڃxp#G>ؿǍ0J:CF_҇em >Rv#;4npomj&L"KyNؗ _h@dX3}:a:b)gol Gcd7eڧ^Q[ =܉C^U)7YjȎfA E)q 6bLe_Fl7f+;h~Mpl}k1E^W '"D~:]Y:!Zp66^EF~Dps}z!Rl/ut= dWt+̔3mLSemgyyXg<:暌,~/߶Jp#ʘ0wExG^B0#9|@P -#ֈNоu]$;=#+iYţ qkS4e6\wۿGЫ46$hUK>D.36'p[3Nu(VPGB?tLjXZK?9(+ZD&6Un{&=IeXUn"0(:$wXɂ#UzLjFc; !1`ś1 i|D>a@1)^^`u|)5)krH۔l_g74BC *E0{o}-lNIylĭ9fB˘(_}kT9#"Hv-0Ͼc~V2Dd'+BK2js3wb(xqGz+nE4%p6)N>cBSHS;FjXA42HMQx: 4IcOu? Jh_\1k_壁~yUE~SF p| ޜnqv 3B_9cT?yQVw~m-4$l7'!vf\jW48|,9+OA $@ɽF[E_,KD3/6hstŭI{/':Bm {ޑ:hQT6>aKqF{6TblJ[Ze-l|җ愽O\@zWs^_%ʑF r)<#o^CȊӼ.3C #s挆OFΎfgsکӌn,fTaPZ3AXnjF1. `D}[zh|Ogڗ'U0siijPB9/xp''29!G@ȭeEtG tZxZrtz$ ?Q2`%bXrbۙǿ}&xM ٫#5U_4Tu*;w@]:s>J{x&s<bTmܝ QЈ]-sTڶ)t$PUTOT☄Ot pV'̢uW&}l̍ 0B#3d=vuƚ?uN&H[_sUoQ;@-L럥)awWdG;G\/zX_ӝҗ~5 .HCum.:5$9DTog8Fa9HO/4e]쥺+Ý˫0ab [aZ„Φ防;KcNDmG~K@{* g1Ջ %o?8U`#l9ͨħeH xw 3T 4 )$溂LVK?R_8Zۂ{7Ia1>m%MˣZ3 $! $ȕQMk1$(UB~1m$9ſ>;sf`EQHPFI_xUDM>: msM#t(&RλˉI VoAu*فU+oc(AuJt-L r/^j8 ƭ!sJ{FgC8atǦ:Auz7SnZ㱱^L4萙F@1fK;)'誱7L&N >N>*x_=ׯBxF_LI5'9r}]&V‹r烣/Kr=vW'm$ѥ9~Y{v3#cSyB~\sb`T>}IfԋAbh/ =@̆}#lKj!Mͩ g]3"/K#`dX]wq-ո<3%##Lj+Ek횾LZ_̙vgؗX1\^:JSmϥ;Be3sTZ;~q &c(◭\ɐ3>w&҇WK*lxLE?ܓGJ&NnH'K&"/ߗ(;4#`~_jE r+A$$*+s>`&m vwr8b5+!S:o8Cx?_Aւzd:)OD3C3Hl xDFc dh~}9A,붚>E7i6e>#.hC:nиXVH;81{^V5Txb}Q6/c~PiF '=  b^p 9Cqk#-T.>)Ib6PRzeWk{D)#L`#x2MUQRؼdGN?B='! tyL|ېsjnmɀ'X^@޻qWV~+kpq!uݩiv%0_[n0%!s3LaLɔ+ye7(_<آh=Sjo"-2Y} 6<Ǎ騽4@ͽ8v7sSИ6DCӛ (&RpH"e =XԪw7_au(ۧUUV\wS%Nq@mO 1VK Ċ9a~Ι\VUlVO9LuE5>푊%{Ki 'CU'gZj ^+Tz4F%isHȚ ϻU# 0[l4&itp`8Ao]Ń0p!#˜ƗMeRg; Rr@)E_Q Mg(T 5_DDhkxLPLK_A 6\9hVC Z]@:鄭IWydx'g&kΑe0V_*Y^ƚj'X.oP (eiFȓm5_ @ H\0{T嚴rJ:&H~PKH|ĸ7 Ǐ!kjb~clvi,:mTKAvj=$$ܙ=+fS!V~i,.fC|"Pz5M۬+NR/܁:GbCvLF$_9-ԥQAnf)<.%F_j2h#Uc F-Л{) ]ӌ[%ZsɯS|~4NWރh@NIv5v{B%SJk_"Xlv2tP$WLXBeZa^Twr h :LI?[l+u%l %tTևv&dTbRpwӲ͕y/D=SdA{ީ$_ܝm~dS67Mc1`D.#d՗0쀤FAk2'f4ֱGjU7h:Ctm2than.p*lYE5y.^m>\GуcN47s (d/d xNN!Ye3ߕ|rB r6&֚ SEy]q"BK}5U`j6CFQ y^oK;U^+X4o8h:[#`<9:CP%6R.]vt//.)?NX0Qhfבm@ b. \ġe=czs6fg- +N")ʻ+rCi;!:&kS2]o4v,$=S-{Mܹ5|xQ>ݼ u![Q[LUxO*_s@H #u=N_S:F @-)]$ PJ/Nҿ†W2oFϑ$]Y} ԥ0BS~{ <B<*d.iE+CkPutk[5jXz}c`S`₺ \ 敍8&:*̕?R= e&;RPsZ^qӀbB${Y4r$\voEm%7\L%?YR] ~GT[ϲcqY7P|8t? f;̿)Haшs@$~ƞ+,?<+J9ze9d+svXe؎:\l z3}1Bܪ(M=zP\xM]EUFfqScR~SsG۶ۍ6>]kf;g:*Gbun qͺBS)Où aL5蠠5VPW<8FM][劉?'t{cnۊnZ'KT1[qH0 Bn'Q CJ0P10"~ ͑_]Q4C1r"xnK5hJS2rjWe4&Wr`QL&ō?v`̪7ĺ/<$rjW":ónumxk"f]ĖLzt=Wk٪DUKP霿_YiL]jm7W޶k)~Km>TŵdWܯ!J`8ѴGN@54ǏϬa6kr1ɴ ~Qi.{xK5b&B|k71510cn U !vL*9/_%iUC[JLU#Mf nŹVnAd Н|k\LFHtD.?bc;ؑ8Na5B!/ZʯP~ AMqM lz=h#7`c[kБD4fRWR 0Dm.գ:MLIFٗG5!詳'?vq=@ni84~G, X׾LV;<f^*.oPEok} /_>o]xԚE:ͪQH(JB T'Yܗ8RĖ,uxFt_ER+F^95 \v-IPb7 *Kc|'ݻ3-.Z3@+|Pi4 Qb9b_vT8m/EũA8kS,^Z{$o<H:q:):kJfTHʊpp롥YbR8)wv(fo;']e ƖXO}e'$v -.u-A8Pl託|ORhJs\WUSt(û`ej֙A|F-E#S =1(*PnەD f`?GCMYs Vq_fhêp;<<(O QZ}]sɀT|VN&5匇"&T+qCuJ0~ ?=S7.?w! )ifﶕjo):SXn.slh㻰)/ +rZ3ㄨ]<G3'|RGW}.\1j.c H$ 7.èEi 3 VXnvQjƯgiDD! AOf@shـ!T0gHYj%HFD܋@& !3y"ΐHH|̛xq`⻕,y `zryL!3s=IKw'yc:I-&vb!n[4vWl/7WǶ6o>*U$qN yPex)Qy,]|k̔`#]Osi 7 LU|ǨŝsKbzZe1 ތ_+[ҧ{^&gZKiI<>bFR$и$72} JJ#MInKM-04凒K2wE?:ʧsO|9Iwg~Ww|ݨ (kv+mv?!>6)R!<$S9Ds5LjtIhrT8=E[۽ʘsѻBX||TuU@S |&ISsd0hF 8#’YY?zR,+2ّV9Sؐ|4>3u7yR::.HEz /upM%iYS}=D4:F(aA"(1v^S 8q ⾧0tZWaz f&oss? R". $,E.{?Id(R6@ˏNQ ,]$t oAr/rz0hyCFEK6(W!^8$Eiv{ ~8B3$mW0x*zL,DV2ـG:hkTտG/>{3C7at-Sڴ\.&QOv+BB1b/!|mKI5 }6>= 7[=f#^4F=x U/Ɋ iĹ}iu+&k 8?\'0_Ҩ' 4^{.g9Ɂ/F)[M n%>/Y}e˩$#džbo%SʏtZ/|9q 5t(betTZ.m6n,m~AF@P92G.!a䱺7/xdl Q{[: hPLOm/B2ć'e֣m8a(דTp8ґX|)M]vՊ#lXVִکy >ȡKD-d7&|y:~t3׽r$;efro୍Sqӝ|WN,,֓2uówŪa\;3351$40sp. ݂ `mgtb 0#9 ˧W[>j6$Qo3q{?\|SZKD?BD6]Izb| ?![Kvމ|vZҟ^J8H6A@mEBf80mGEOC4peoN.ϳ92}\q CX"8,0Wc@ ث^^tfLwS˔>6l C$'8dOԊ}B[o׎6r1_$}2˪cbB1*s?PKKi}7O,WĻr!#٬qqvQVֶt^爗@ ב'HjVadѰ|G4 6G/ꂰfKC2ަq`~ /^oOqhR4kA7Fp*Lwc}Th',$LFFeRL/XQbARfiK繄 96Mo\^8\f\@nhPB4Լ̌=(^}G4}@ZioMeG_>7),߷(dxHdo(z$ۗշ>AU*=y9hPFɀqx0wyxb mDS5Ջgn؝ }I6>F54OF*>>Q/z$s1N 'qeZU گͯNt4MUR\vOmhǪciXRH#z%sW6 [N^2سl*ݑZoh,m}2Ny4'z,q=JSTw˿y:,5.FsR2 ;slĖ[G|t{-?' ħ9Si{ƋDn7!h+ѳ\w 2D$|+m:&9@ mOW7g,'Y $}kOy]EIC3{z:I4-"=WKUwQ &Abs<2۸5PW7iXhӬ˱$8,)\izC )h)E<J"TwKk`o#51QY;D HJv%=:%# epY&4&sZ ~Zoԃҡq?Rw}ژؽUvT} *Od@S!HI&K.0!eP߈-wqƱCO|);{ؖ;d5Jo%I";ޱB« 1w&A%LTT]6E/-V_pd! [ogoOmЩ!9C'ZM;csx\3ww#:2q+ OkB'Vey%C9%!+ &_NS~qE{3ukJ.F^np8tb|S mXl]eGbwvpYYb^}L֤74aBa\naA}S9¤jYwHP@gQ䏓XWwyT,47Ԃ/R#uӵ1eGBD?}8ZC a}n~G;m=9xǮїrx: -m)ēpXv| ^ B۱L8IRBo|ֳ $barc}FA$>Dq|#@ysmKT~$GY1USwshMR1Yu(])н E_qT2i}g "QA' ՀJg3} zw@hT~B g0c9z ፨8#8ST0Q,x¤ě Tl')N&bT!'sbR~Rd'PO?|dA|Zsk܌g5cPU lCրs<ƥ;%KP,\+&q69&z{z5*> /W}lQĊÈ Bۋ- #sG15g@23âMPƚXN hv";A uQI<5BFHFv>d<5 i|QAAXhT]>>h/#"Aʭ݀"Hl> tb^c`ˎdV$h&_ݧxld̓JkŚApx 2"W!av;~ĞHBnb}id+X'ōYɦaF8OAmbJ8yL3\Կacb&Z:v`UdyC8:ęaުwKDy:79`U rBHvd.$]9(.%cxJXSLIu4.C '\#!(Մ9/BHF ԅ5W8pv1:HILXWBt:=د\odxc񩉙[^jm6Bĥni}ٶk8a8 ВJDk͞w[aKjJ07A;AD1YY!;: 6l+*akKغ䏛R+ٝNc|PAW@J.oǫMe9ڱqB썗@*]*;#p3C)޿##:n <¹ڇį4]:sYάzؿ%4T0CY f2ʫG^ϙ[׋CDwiSp,0K7{ :-wn};ai5<""zZFWT]OZ%uy<ds~guMSUsא1#wb׃:NgȚ%Qs8n!KlXƃ9[`QLM r @Š.+)9_9zfo)L‘7>hяteNʞcҶ|o? $^V%6tZRƙd,=&HbHꈰՏyacBkŝ c`Hif%'!^1w/³Oz5F:WIF( ?!>$R ga1'1~(he;S4B2^@ ͒⫳(CjM}?֮Q:rB VE^DzNp+t8߶x0GyW{J.0rʉK Cw_Xw]>Q%GZ(S%@:u!gҶeh%zŷJç=-u7s>%G``g,۹w3$ͱ|P>Ƣ 'z1 uYk)´cc`f`>()<]}]6$`Z.aoxk *1|3Z?ُRb±+ ]6\%/g3) YɹU.j&UWMM ҽӕ:)cAM/r~Vgo'N(gެ% -Eo҉菲9Pcƕ3 /b"Q %>tyBANuL|$Q͎35!{lq7xm} ָi+R^_Jopcs 3 LȐW}:{fVX)Kdzd,|XT9HʺP^WF~Bx)(1t x2 N.q%1aEqA9_f31`i&@EU;uBOO{SVndV-eWR!} βm#i2or56w bs n Rb#=rgK^{x;TYx DhH8w_8gֺi0 {_зʚvaz7w( mdjz|N1aɒ M{4Uh"br3Z&58!q4ȭ/L|"Jxma/Fl|f*k7a*'Je]!nb܏g9 s:ٿ5WQx-o3 ZTsXo&M5j3&tʩ_9*xƹH^j&+/(dB,`l02e K0EV}ǥj>VQo nsx-w^Z E/TSF;^QUm*-JVPWJ*~ㄔ&1/Z̶&r|F͈BZHfeYDƥ˖231F=RS3 ZXl(<~C@_5u`HZ,wO}g0'z=;VӸ*jKGDprs;d ?FƖIY|(z$^xp>~[U*r9$8 dgPl%Yc@!P~L*N3 Z§S3Pj:K#'B $ OK"],uRޠ{=Uc7oweR}#Јu*+%nRݷSs7 9- ˏ leh}(ٜHfsY +@.@uG-9{.&"OZhg$@ZjC{Rh'Aj-_PSϺVGH#9:SZn@Mt/> 6]%*#\mS~vP&~Zķp=y^!O8  YX;lajZf\oozlIvTۿ8rXɜZ"@׎FRDK (sk' Jwlڄ$%%4X}H՝HWw0d9̒2^1Ԁلd@y_~ݵb }dkkwr{Zӌiqý7i"4 Y3.{F?]@a}}Ҫ\"ɳ^|C.,"=>i+_9oCC s3imc؅bē iU&x\L8z䟁Ze"|Ǘx}xD1d4zFCʑ+UُI!ii  "7C[#vpM[H@R3y.'fie樛,y@ݎW:M < 0Ĩ) Oh2/gfa.-0]M;ӀdӵNˁc=|G&sBH*UemNPW$Q`gě/*+jKwkRli &xvRAE~^ޫ.I~)Is^.קhCTPJi%ќw o\u('MCyZFԔ9p~/BɑAi {kX_/q{B`:o;Qgy @\nk;z=c`K+wY3(f;fimzM&llES!֘/KlbY[-i^ voj?/&ERKOôZ@?Havޘ)+x't$4ևcʟVVI_ly[CM'!.4c~pY!_q_6k{l۞*hP7D2e74=j'hok3򐟢qLQTo񕀸uqgD {sJכd0ikDk+ 3<ॡ1xڙ"W_)9rǝBxu݃(p[H 2FDN 4̡E;j'rqO״rqYO3mwD[w0IإI g\ǒZ j2Ym!ӝˠ0gs)&6*PҜo7dFmz<, 8ѝ! 7_mt q&`{NY ]jT瘎uTo[|ٲHk M4aԠ'q4[1 ,AE 0zMg0I>c50ªvzֹH:pML׏Zz-?U*VCiG *J\,eXɑeEd:#N8Ukk-!QqNy`.`>62Cd6r" >U}- AT47( ⢑Qڐ7\2%ZЪ%$k4B!hEl @G`X5˜lfg:. ;m#5+m!kkXoB,YU"׍'HZV:mA c#0 1w\fU,75jOTPN%~6QfЯ?e[}=g*w̹u!$>ZO~!Rw,Fop؟ "Jpe-q -C^~e-S#h"… d͝{3s4*j]c0<5a_79}3L>p|~)Vdl+)P<#/0,J}JiL쇞?qv8,IK[ː3 žUj9 Fz#1@]Ʀ]CG+TN41jC1P_ycçQKR\rJ=X3]sT$}8̤U MjBgC*m*qBPF[5ǟH|' yW@sTMV^BKMYfig6p9~ŀGugZrDM]b$& nO2y ޘڏ3Qc n>k3K^~nk\&0z/wMGna?q(3}]vSx&OG:vHy{=LZu_p8^Eb?bFOww)Gzo* 2۬zs7T$g7η%w wtS6&q6&}44K|ڰB]!DFJx4AF ]Ub?ՙoPρZjA(hMuC}Kl%p1t]Sʃ[jEbDlr}# 0|I#'& jy @0Yvz˓#rr|LrJenQW=7y{uw]+ȗ[?zzu%A !@~1uQP>ıHʙPY _t% [<YYNKO,Ō<=&.A ,ԛ幤ϧ=yD76]XĀsm3618 `jk7q{#uNjp'C&`aq%WOzv]rV+S.`dEp Inn4k5 Hg !wPrKwE}e w"tׄ8)?kl,[^'Gqm5(j$eOͩjx<,*mc]O8?EKʪbd _} 9&CkY`V b ZM)MjE~K7ṴE0c{ dC(R@ro0U|p0` Ŀ!@5: UX1ZGv o{c8-R(Y<;V}3F[cǀht3巯^缋BFAFkiUJ=#2U;}a!>c2-sa|8&~jA(>[<+:F*nUxQ[wDE1ص|mgpTUE2#-R\`o;-FI:\.sB)CUhc7)(\ȂFp}`_>4x8m;]7 Iw@$^8Ac,XAT>[,JVU#4&LτGv^1V7C$h&o SCzTμa W5L\Wpniiś} qUh)zY,xcj_ͯ[SuEɎ}U$|07EفBjXh6[(-Ij|E ‚yفI[$M5wگ`+S]H͊pG7^\4@Hj&fR]ԡ@vk㽯V?k\ÜDlޓ}gf1nj>vzQ|rUj[^wyR6FeJ2Ʃ(c)7"{a^ڠnjbkX~ \EdK)ljUM7{#dFCމa|_fz(['3kF z$D.EtM흧BfFND&Ǒ#P<D~RabS'qv!vLZz hZ v¹rK4j%?{MSQ< c@XB$CoҚz(Mb@8r1@Os<3 }K[ LsfKn㡸T`0fY q7B `Ob|עO1VŜ>q_=ΗFnǕT[{R0YhMA@9 7$돊⏇~"!&1SJ|QSx(Ìň཰/^9@aOzUj<wK69?> QB,ctC5,f/(s1Ko~g pigc̭=! /`&،jg/\6-MJ%)qy+nhrΣ/Fu,#Sҳ[nn4 <Boe9:_{秡 e z뇤+I}nsq!Ùr^5y/Z::%߾ԁY#5Vౄ_7^^o}#1x okz W *2]z *IIX}"l05+܈'.&׾S傪5Ȇ aU~[FZE!7 &2' ԉ  YѦ5=7],=I pPz5Ϝ-*}%~`?(̢k1wCp5^}f.3 6ɍ#MIgOc@r.v#;:n]vaTc[k5zuY'Qo%Z"\ :ܥ/_(v.l#!n@]W\"b91RzTpn5 cpwj櫃V)s)Pk)ϙMB7dƁvD R}N(>tVh)0 y!-F߅j4c/EPFh5h޻"c nYAnxnUՈ @H(T\vu@X~tXYNr3dAE#TeOyWyxG-X{_Wj䵫lF{OK2.eO~q+%o s~b 6&1Z/}|{7}f@΃)盏|Cf\W] f\G&1W< oۈ}#O] IE;Er(Иi$ڂL{Qy/R%`@e%ats.wi^6yIZLcj{FDdW41qïT1q+'9$1d4؎LĄTSB~wQ|7DD7,\F n`ݻ_(/}AaG(oy$; :oyۄGATDQ΋U9AAWItO)?VcPG6`o}e*Ծ@&+pXc&o::{AKuM" kc-}̨$.H-b-:xxZPoBpvVA&>Ⱥ{5kR]1q}֪;zm3#$^NIkSPG^[]&zmq|)A \++FFw{nEv餻F] M`=!WHS7hO_߉b%D^+ȝ#11IͪbKmҦOBIN?/x2Q܄݆if*\ae|}?yj}cQݞ7l:OyDb:AkW*.X&DFj$]y%`< n¯:K`dci˔m-9 #a )mMIQR7+[Ht?Q &eqQ>qDr"os_Xa/}lTЪ;ٸt,Qs<+Lo7ď]$ # / 6ZXr"KHOP oG&NlӱԊx{a2m8ۅ1ѷyw1F#R Ly 3:$QEʠR_(W Vεbn n*{R`6<9fとԵh^wZX}lm_,UAƸl7UȬ4b@ [ֳӤjbEC`v3On}3$ H;M۸e|HL6aVUmJrUt{{w˘2HO8a=?؈.++??Wy)Cz~×i˘lGJ}yqݸ( !:<$Χt"phG!}b ]*7s ZE͓X9G]dF|~뻚.$'Q,r9KD_L_"]xu{N/KFtKttD\.ߝrc`P)R.>jҹ:,y>kO(Hso͔#Dxp)\I*ژg .Q = Q B6lB-pfy1 [d~oϒTr&5xf?ؚ1*jrTdɆHƛXq+i2t uQSq=jGψ WI'mY`6;>jN`fnΟMO>U1+DhsM^RTD,m;ľn,:&WVܗdYןpLjev6Ń" eD>q [Q3!'Xb)qakPЪ=-uJꬨEPC4Ͽٚ̑du y-oR6"#\Ұ]ZXFu 譕J}U!Z+Ebx!H˾ChM';꛸ /B9? } ÞK 6o|%bfiȀIŠ~J^!KkEXy'a?YwC-eh?TF7Lۍ6Nٛ:s3E{QUCcYAHkk5cmlT˨Iyހ*j 䧤2sv71\Bfm"): N7 ;ƀL5Ze CY{~_`D> 1/}XP9'W֪wؾ, 7ir]! 9c DMN 2^R^QQSjtcR^dEL27DΕH_!y`R`샷(xb|^8y/L뛹25V%K,uLIs4Fal*jyճ 8%r]]|]eRc`zTp$͟dwIwrOEKQMV`풉=W]{YW#]+%)W+/km1@;sq3r):SLl?5PK]1MةT22rs-7~ט۶"A4~12b6&Zg\!ݹ-JN"/Zcl9Bq]Zi+s%fUύ5A8c2ҕI*1\*^Pbv'?I 8 '-Cyf4H9B Yy.jťz "sM2+Xpz"axx{JVCmʶ\[V>'oeGUR DE·bMj`:|3jוFH7Rtju`UU>gXhB{.jsa.ۖSH]s%tp=/ɟ Eι 9o3+ť&W&ڽHb\xEk'Lsce}7.'T] #k2 6(‘ O:m:7l1lbTm w`*6Ȉ3g0.@Z q zwA)Xi`ۤ:BoА]|Xx!ۨyDOS%I{(A-ǟ 6Dߟ<߸9 LP];^BsLpU"afrD8!\'-WF.!ayO&ԸI؏lċh(Խ?r=# g:M!d1z8NvZbEkP$1ΔݞN;.8aKx<`@~v-CKLLkDbONd y&M0.dƽ%͠#x!f o6L3['LL|,j$ BTFYbHڐ ZJ:?3/q#A=We739GP" fr]B2.d`ܗ"+h;ȏA6Zs:+ҥpn\.$ >ۜL*B(ID>Br=MHƚӈJӏ}v VC@I֔wZ:Y uٶ6g,3җ0aޭCS"/F_SJcsͯP g6824ts>͏WOŒ2C8㭏ղӽ1WNk( 9lHw.Y+\+{w~#7_miЖ Sc/YK񟮔P+>;e*םp.H \H_bحe6 +lnH2+)^Tv$M~;HߐUkM:'EC.7xB~Qd铫nWJ*E5^l0=ԩB&GkHyDˍWo4\>ŘilIvΏ{юg).'QDmX3S._ a4DII^x n}b$lvx ˲  DY>ʑF\D&sNYX^r#KRBbU 5 o=H/1gAڝyAtX[%j(_chCvit8W4)oE e)iU3+SX1l{Ͻ!ChnjL.c} y44@]g OK#F -^֭`66\&$Lkz?/6\47չ=2¶\[ &m)|*䧜zGjҕ#FS"J>Uc晚hqRViy ƳJto[eio7.Āɜq1tp[a<Yqе2K@QkNHћ(1J[DŽsrT.tȿU[΂J`]4pOCDG7Jl7 5qdߋr+5FTAV):=H{( 9%f^Jk;ԛf$$0GSbRkBtl&5FGfΙ!RJDu4bBWb4_!\pqo俖9,]+dN ݸ՜Kcqh rRZb Oq#gM!4ŽY̵v+ok@PNtVɨ];FWK-)Mg2^ O8dhM!)YaC<}DdEi4 >Qv@&Q9X4A.?-4maL#0L\>.oOY;m# 8ْJagoKǏ| Fv[f| w$T%d OohaS4ހiX'@4O|11np#_(˅ѕ~*'uMدՐ%S61opMG ё5ni&Y3-?5 MnG6T.pWP4.|/_WnKMCڄLc.-[UEl8] 0l2*TF.ibGسѝ*¬%D쟪´ڕ/ެO+Zh="ԫrh+w-4 ?$^vMۃ-GBVU;>Z`,+z ٞx쓴D1^$i(ٕ:>$7|`_Z֟νyd*L}?uw f=!UkKB!*gNٙ;,|e?5y%& 1]iZ Z DF;EV_yOXOOWۼ*jA}6..+ ZtzkuƘYnm(,"~Ž|&~|O.RVshW2et 'iB"1F8ȒKB4084u*qdWo.ۊ 5tn*,^yz!1ٍ 宲„V )0 :T@ )Qs8mtGQ_ϔZ94t-=axS%sU@}jxW: {Jj5똳8!F6(JM,M. .('~UA$[{ΫOWqi 5CkE[`k"|pHm܍b8'329( [[dY2˅N\% $OB8}tFQ84êh ;2F8޲߁5:]k¦RM~AqD{[xhL7iw"+JU80Sw>_̕{c5{ϣ0'^E yH16">^Tʗ˾y]c(8_krC/#iEԤ~>G #3=}@4Kaݸb ?)0! 0<(/㎱8apˊ0pĀqKueMLBl1{>T*߅wG"8on|}p|Wca{HNHi"33 F‘Hnm_P\ --u@sG);P#5> |؇[u?JyT%Dz2}${,"s ܰl^S%J}wZ uډmY$}ӵd' c9@VrLV@B:pY ]j%;,Vr)ACP@cLE$R'̒%D%w'֛(QүжQ{k(|29\[WD.M__a;44m4i콄qvPEd39."k~D[ŒVj6r"^o'QQ냩fQ/P?Ydt})]ˏY/nY5VZ2htü${fӂJwX^[ STKX,pWˡOD,4Xr푌^:t՝^}6%|OĸRIb`ȱ|>1 D?8q{t*j=n6uT&.gNbR/|G;tZo%x$Wn,>p| ΆW@j`@|_"0r2w =`ODyL'@ЍߐoNXi_j+'uPckoR9fg 3d357Y7D"u˞g{%fOe If)~{䆺 R.NFJ}UZ#.?ʽRɯ`'^~y(p.o [NQCyd)nrKM܆ b$h~]-/wN }.k-'u9Y 8Xylm͵HvATf /~,(ps^E qc}cߙj:[Az<d X6]a}FS -pVyfRCa~ E E(Ӏ7KV#sW&#B`Db3w/!WTihil(xc~+&diJgH>#&iպU&]^ʱD{G2#z3ԅW -=G7$bL J긹F6s}~K׍J%!x)ӝXNvo:f ѥepi7>B 9fJ>ȁP X hklҏë̵IN܄4Ų:Deմ(GE|r^Qِ!/fNN60^rs;-331:=#{861"˶:3-9Qa M"xJoF/GX7r}UL{Մ8OXwXV֪F=7<%;$oQ; %Ѡ)v4^D/}J`r_uKli#"Adc1ӇiW pYj|8 Vag:.iFOX]㪳 {YmsKdf&Y?az/8b;lx/mDt۫IY~]ƞb/^'6 rF |9OPnIMu@fi$.yMԞxh*N% ?$NzFgVj1zHrQy_-C`z ' ?Y*Yw3oWt8m w֙MqkvA;~r7fFkW (q.{kb>ʆs@wq`.;z Kmv*LǸt \ [dǢ<7{"kS$ a,=W!3 9oz (ȻU+i $k2eMm>ҧv#$T%RiueEfKIJՀ#2Kev[Lc~ "Lc3?0CŞjlY6K!r`-B6@* &- F|7e#k4]/k s:CrmVA(I^wp܎ "ԯ >ʱqƒ'߿Fed%Ssz#JMI'q|Dh8 y/9 φDӟ#t:n4ίߥ֗&S \ HW[Sa2^I&Or(+/V$\d>ƥ,u21'B :Oٯ)zt 30#lm/1ebZ. ZqX|344(,wRB31ZEhӀ'-9b~1|8?&l1Zx^@} Xꠙ'RH0^رO8Hu@|tH(GM`A^U$QVtܱoW|t]=!S?r\ )4&’e7vE 6Jvr@SN0χ0xx dVȃgVvbjN>=,)o_Sz!*n"t0R Wm񩉚PHկ$ֵ9lEAelI A|!BPZh\e/h׬o?NŃP6;=^nUK/hݞ6;'L"Pj'?,ʦf:贶bC8?H={g0NcN;**Vhf J-uLXz!Tx*a{h,>uKZ7>pufKct5?1YXqsFgδM+\*Ǎv8aR lmU]߼=!>;rL>;QCcm=[j>n!]6Dw4<P~3Ż/9\Z}I:{ !Ճc;ݚ jcDGǝ2J6Ƙly0k; I4Ņ߾+]q;TAu63韶1yXCs$oRqsS}y]}WisO&ÇӺ>)ڣދ~JLG9'%,#{*^`*l eZR2E.Zb6阾a*R7sTWJ1 XhID.*p7lkgɌr<6뭬=4],hL+![J2a $ROBP|JKnߗwv=z&,sUwi.62Pz >=d'>ScbyLyigvXFcȂ8W ̠ Βe%!3 S! t[)nt:aQTࡂ]ɚ¤NWbTB" Q`LjaS=\fr8/OtAWF3Ў;a8otPBKQKQzhc cNr@lF"CYwG@œF#!8bǏd `sy m''y-&-E.BXm6u\hpƻ2b&fb_XJʯ1|i ߔgwX@-=*X(PKYe\8NwɐnCw`ů#Wnm`Jsud7ls^^ٶ<ŅΙO$a[3KR ޚW5;NcQVk;AV5e0C_tu &\ۈ/6evӣHiֵ}H;A骷 wujar*zJ-Z?`Wo $̶F4-|( bɸ'SMx,LD$huGՐax1A.5@ZKbB/I1O:@%[r{|Nu3 [AD9؍7u)UU(G 5lgg%LHM1E>$OЫ:$A e\I.\< G ;EXTv~{?op񧖼 Mnm9Et@8jx} 73@c&nS.T|rшC+T櫯 6z<$t`Xzk@,=Y4"A]v&dp+6`#/q'f(G*2X0Ԅ}6J_+J&eSiW'h;lHl?6}v"يcYOqZsO^O+ °4c>t?=SX I4\鼗2B}/zj5.m=`ggS{g}2 Bsu~ g$ksK&zlгW&0L>ePoÑ(aoEmb-8w!w+0# Y!_Oi ! vkUuJGUZ 9 :ڜHV)O !Xδ>;Й/&lDa(UQncP B2pdpWYՍFu^1RN "F|Q.E oz=|~N uK[R>|Cx<HKN_m;UQ_>=!åT5q`1Q(:1,;f<߮wuî3l;&6*ZxN|'A!lOM : Ƹ1:֢~f{`\A¼sd%ok@e;:M>3e"q4!;!ˎͮTM&W\c-uT{>ޠ3zϤg;*7=VKgʠڏTFSv\cRs'ҍPDW; kjt̆k2o&b‡G騿eH1*V;0[$xz?ERZm\óWc n K7VR޹_Q8\F,.:ۿ; gчr(*QjfI3JޒW[6Mv5SW,L-~A`KblY v&p0uAiUtb?hBAӭ8-cCc tu_?F?ҋ2{}P+2vVph$:b/,#W'/-o\ǤO9>\C;IBo BqjhC9G SX,7G(U׊z LNH'Is0bA 3dh٬rɥw$2,;&y^ (hx`֏d]!Jͽx>sU1~MKAQ\}݂ )7ro~5uM9 K$Ř|\Sx 2kXA<1eB_O̊yAI˚݇]٦j!|JuszFӴsnHg0}7t"lJh{,2h]m:%@?rT+A FDt(' ":-2HOʐtcSUu|}J%YW}o 3=Yf휉.p̃<,nSq.̆bB7*Wf]jؐüV{3.q'K\G}θX$l ]U1ğвAB1xJ67.\hY-}em`JNNGR#Ur{m쥐˝κ! C z?ϗGJY.p# Q[ɂE@G 2Yͫ=5{:α^=k՛=jXBs~@YeQ:1)䦓n#廻6} c?{6_4U+ ,m/Ϲr<ipHV6(.PA 8Ld?NP; 6OD2CSo snpQcB}QPp8!h.M,u'}3ȪBMPBECǪ Tjy5SKTW^jە^m8*$yCgWcCkMki;PۙtepXdKRwz$K,Dkl׼F! ͎ѐ;+p.^XXt+NùυaסU#9n5zB{a ;46uܕI,4gr r*i t3t=րL<>s{ @3x'C *{lK ѧ6:nϫ~,\Vry yj3gB|vy=}RV ߬FH`W#,Ws%(Qu wwo!31i['C2$ney1O2| >{4Uds2n0s},,k}Ћ(Uzp UK؀>Ʋ djRZ2ޙOC] =;9blNi.E$EwtT' 3J&*Ú2򴧥HxmB\2՝T%H7!ݰpXd(dDˡ6)TxhbȑF,Sۇ6_V_BҼn3ͦˈDt Кj=|2^UXOuDXaFwG_p鿏o¨@|Z Z~ʳ?*:^0UqA a<&l);ٮ'lM>gSki 166?>Q r LMY>"]R/jK)Y 'r 0mͮt;EC-.XȠ8 #ⵥe*|owyL꾺9GGa0 ¯5΁w :P0ٌ莊gXB}J;1‹dRt2T3 Å,/\uzz/N:m\./\8/yE:Bt>ܯ'}spbz?w04R@NR1q=sHFi-@3cqR<6*\L0ג Eu#wc؝khfM0&AQ|,AO:7m=?)a]`($e5_D5aVPs?UG x׊faҔrIdKCb|h3Ҝ9i>%^,hU嵟Ӂk1ʕWlXݽQ`$wRS}"ڭJŠy *[q|?7d8LZQO!AX1MZwM#\jw:Nu๽ 43OGE bl7ޔ?O3n&QbXxaGDQnUMu@P04Nizn6Vq놺xc#V60'uVgX5 JNc 4`9$Y' I ՏO7:3^i]`{W>Yv7AI[ 7EB ]ۦ,#uom'Gm{ ?Xd(II7j,aQszϫ7Ҙ2WoГ$HK4]QVgҘ [E>VxBhvZYc'E5ݴ8 x &,[u5O|\8c1UޚS8W=w›ߒ-`G]oMgz'瀽.XfUP{rLITd %JO PhO tɎ3ߛa|dRBoO1߼(1c4[Sf ^m3{Zzx=j`3PřVQ~ 0 ףpRJ0cU}n(qoB)jTZ,rW$bt3z+ =}8B 7D}%mvac QpgXqLM+e PEB)p=S3G Nq50F)SRӥ*Jtr1^P!1Τp t2{@[OV$v%l> %!msLǒ S tZR&.?($_p?`?NlK& 6t$v9)ґ2CW* Xz`+i91y}[}\<-<ʵt&yyzIaTsg#aLBj!4.^iR#}mVا z(n)s(o`#ċE-kΫ8(LA>Q HX^+V6Y|J-"n8GѻChm'څI U|1- eǧ$_Zy 0cPy`r{~:V ~kF"E}LQ?4D q o2ȒRJ{kq@Ӱ܃dhwTB "~ⶶ"Jz&/%`}lcja҂x-xא5'KL"SִvJhݝ,V(V*,e"B6nvTyyO3Vץyg Eh+&/7B1d$4,rD30ӡEuMm})U ND˹8(n.&c''[zaJV?ܺR:#9M[(_>(6qE72 IPoE=pҢW 孊d#?HEJgOʷc$b^cfCC.vH~( NN)v%FSCgIHͰ-@:Ir?tEʷ vteޱqZ}W rYLjuvs% o`0Vba[OʊEIjyt*3\".oJ#&kBXՆrddK\H0d" \+1Y-arr'9r%a-.גq&K,6g;bpX+3KX/l[0޾,e7#DoiCg.w5#yDeMl ӓQNq3JB{=@/ 4B!77;DrpC9, gVc%چ.lEٶF/䂹Pz,)̓[)"";ngXtc?!˒@EAlo! 6#dA3W]uC2xlS4tOA󻩡FWz"kl t 娜cGcMhb ϼ q~7:?w9$k5ءBt[Re|g"k/Aռ@h*()ajV^QV9kb 2_`'<|LZG:zq  k;[Kqߎ YcunG[x:s>$6o34[;e_mG=έg,q{%߶}S<;yRLu?(Jr ݺRR9 ֧0XL7Rl L[]!+ ֩Q.8"qMC#dY$a7lJ@APEfqzfUd'4s\rI< -Qés?Ee6$Sb5t~DwLm/;=h=*2.LOfuh1gRkzdo\*&glSRg_99wnF11­MxR_ ijQwx'% *q`aZ;"V/ReW]qCP{),YxH74!k{[`37ZiN[,OF@\Y[30{w>l k:٨sx]d]Uq@W1+3e@ir˛TP=`>HPSo#ICDɢ+0,ڴC8buna_`\S/(\ᑡШG$':3ˑRz vgNpySzAOV;-2}LNf 8|2^1v,cs12EV܃p}ِws45 )M+K~o.2tɠcCgѹ9e>7l9Z@()ƣ<کƛ(###h2Z{rTOI_Pn~N7-˄PdȌ-R~na~7I"gl:Lm0RNՏR*+yN˪@/ܜ,S0Id-^@lϽ2R_uGO/FO7BӟĠQl޺/9}zM%̙u/G!AiWFf xsm(d< XhFI3~p/#}hklr@hGdٙZ.1!oG+?>iQ̫6.XӍ883!?\4?9vЅ o!•΁,Uƙ`t[d/L^|c^? (ŢG'pGcDNLPQH6H4uu7nJA}u,ٕfkTWjAU)GJ)̷NXG)3>U(,9lVSL]~}[ޡ]x9_Ae㳂]xb[q(DXE;[Bd+C Jc, 0 j̺38`Wi$/^fʴ$ɗթ gB!%g.$DbNwqG-E;/qQg{o 2T!kQui*k]:OA* x$ c0oeX [WckUE\`ߵ= ".ˠ-lT 1&VK ^+G;3VU\<`'N^ׄᛶAUlI*2)/e2$'q n躈9l]{qp:KϘx>1cziyt?UCG@8u bn)-jU+M۬O24 HMatX^fhJ *@, uBށ@'>d5;,wIJ:K8n' D>.|ʰM_,ՋB|.hd ;[͌dM& YTPi唜ڝ!ɰɑV=mcD8.Xq͹ki[[K͓?Ǭd>`^G`J؊oc2`TZC ;;n )HL|tc A.W"k-{ȏ 5\왏*H@I\͖ r~&5/*x4G/>zz^ԉ_]^YTrbMȆ)DHi"]hb=e$sLG`=0|kYOaz@υHLa:8LaɈ6Z|{ss5b/ꔖVxY@gL#T+YSyw*k"e G%8v( cZdR:R$:% ;;[蚁DžINRRzZ#dIw̝xe>!BG˼iUsB8pV2}-.3MCw}=@C X!]T HIޖK,]`$Zx)2V|@0~$ſ 'MК2Z)yDSS\ sւ<Ӭ>vΗ8Jӛ>3uVpiV֧M ֑lM>2Rv?!u3FVѸH*Nl|[_޲ӟ0Rq`naJfA u:U']MB"*LOnc,:B8r=DYTJ9 g|@1C>xt7àn݈g}4Ȁw5o!iIr!XvcJ{z.VWBIUǡԠ_w=.Z_UCQ hwΉ#xnDGE3֖&g}䆖EkcnYfbMTH1O~F/13fv 4KC\=++QG}GƙvLJe#+FJ'p1w .kDאjr*Vsꐃ9H̯NbD0<϶7a\믦At!G LNk[/J-J%U.9o-F2D'մ7  R– @mp8K ̒.ho34Sɍoi@s{/6ʂl0(54x[p S5q:}d}K1r:&o.g; L>2à,(hJﰌID?MM٬ ZLvԥ̻ Cq~R%ԮK΋wS'~(r8ȹ˫)#hS"ٶ{TqN,,4WCc<6,G3Q87+(idl=)ș@Eڸ$o7bF=ēśK-V|bR=Nj.$sP8 =9 V&Atk"v(?=R{.MVVL7gU,A֕^`#Bi߹i4qJ1%,`٥Gm{e;)IGx̴_ Cn淐IIH#5B+۔_vZoe,ܰDQZQ.T1)(ID_[k:IܔA*} ( '#< h3SеZ&&5`'3_*PZdRTԮgǴs h+c =?d sue+ X/0=mvquZdJ$ю`9X3 xyTLSXh?C̦ˎpodnpxd~ 6Q>*Z3JNVvnҦ7 Og[..% BUuž&u,j֖ώ/@Tp ǹVC~q $Kz;k Q6 8w~McPLjFE-9-jW˔ jz%=g 0JTF dqM<@RW㏖~e7iWuwӤyk7Q&A0-h>F]Dm1V.5Lo=vp4gmf( |YO2~[wK-Oخ㕏zF4Ɏr#I1@HD˶ƕC:Sp$UVXkS)=ġ=ͱ79zɀ$PC42#nO$18/uueG&aCd.H$!ncX74Nuךuo$M. -£p(%0_dX?͌. /y$NS P+c&f@P~Z׻V*Kt Esr.ep: Cu`촑AF-}X}Zݿ^ r 4\źivtV D qbǮtEt3 V߇z2s{m ]pU}NwpmڐooCãcxE|#c(n<]#+ ȝ$=޹>icFJ:X4*xsU BL99.Ǐh7ǙT+'A-5F`ע}/OFo]|]4Gs:v΢k6mxiÛ*0Li`Sg7Q/t%T&,s4aecdЁp-MΗY¿קYkJDT3u"8HSs9]ˆۅ[ZL#hFJ{rAԾ2H7׆ /; Js$p,0=fIZЭe1/OhfZz_ Njx*hBE:ÒpꌧU/ ¹6=s',h {cr,aQbDnBKQZ\Im05,xF|B7n0;HB@Ika`\xD E>- N6`VLuh .(_)VRܓ;&䪦=>Zɘn F&qr\4+ ϸf:"?2\P9 ǂ:~oσJ|Q&` 'O@icnXEAcM#HTkT^\fy$P|eޟ#<af- WUJT#Rh*ظ6m52yot|CxpAಱ7OpwmJQ]ݑ@Hfm[pǀ\{y^c9`c n+yem<@GcUu:90tuqQߕ8L;lz5Y+JCآ2O`飄/UOmų&+q@eT1nhd>f0/*h OM[׫k[]f9xՎ=!S S.tk޸<]~R6E@x$!HcO7x/[Nf*À{߫LH_Zw+x !]Y("z=;$(U|ʎ,eekW},*ranMvFI9i{waˮ~a$:u׫A#0r[#eP6߼>]{SEJaȚYlP_F0~Ņ!>¤ EPFNtQ5squ qѬxp=nNlܲBV:V6y*,Q+/~^덕e㘋]EQ VW-I/qrVT6eGqDJHX_P!.ýkN䤜{ր! qݱwނ" sƯ&z"')=pTa^cy4yP&KzLzѱX˗~P8e$0žU)^ "62'ĕ6K1qwhBؔS+=܋AKuToaKк|4ݝkU* Ktmkh} `@gʴ?>85<+_:ۘ2X,̂9Eura++BgV}\&/2%;? bSe NƝdF3KT״X*MpZS8[rvz7 #к>A㑟ƀaa'+Can3c^w]h!lBM5Yi]bѭs$PsXX )렱,{aSlmX 37|9OAzܥ"7&+ͪx=%}E5V +u,GOT+ܜq]q>éq_W09p zd1]G"2IW۳X"lś]=5Cx͔D\"ֱ dl뷈,v ޣj`ȽT&o,)lYiYzrW-jc!4UFl6xgڅ{x~G Q`cW E >XGormxyQ-o8"Q [kJY;*0Ixu^pZ[Gz)ƃL 3oΓ0㋧ ߾9Һ[U/?ENpubuhch{|JY2tߦ;%Lb˼:n׫h1Qjic@A}7/;eJln|y4qc$ G4 t_9o0EB/E`yy(9O`M}>B$ֶiCobk9ܓ^5ǪCjqX-0@ytbIc R{j@$odW>!]Z-)}NW. 5uA(M)*3 QqG{$xXWh93u"uxѰ$UǻQKe|08uSnT:(a0ze>[0aHd "d9??̓fC9M&֭;f{>V}2ָZ X km/9$j,x67Hc 䠏<3E At0Al]F*Yіw|CgDhoŤU8ǪX ѶrR&adrx[WcH<\&|jXc"4my>(j R'& GNk)iv>pC5t3\MM$oyFMd 87Gx@pj1aa{6,1 "|$96j~z)S5Ri{k<Ľ0'"WN@k),J٫zckiL/)\:Ǚxѩ%=Tef)iXjqHy*!Cw*pe2 &{E-LJ x i9 .qyL&ukr ϸ0aoM}QN^|'HdG`XfQw }gBu۽7atE;>QF^yݥqLd󖓖ƹwbQji`^->%#QcI!{zAx,AC=M\@)C ;lap[ai/9J4,2`*ދm6pSCZuSɮ t=bJ9-a\wZl) ~MKt= nMuLa'5ļp)@r%G!=e9;+,1~`3f_WP1|{`M8Ȓڶ1S,:)⺹ټh])>xAH1̯~N",N&a= ܋hMxܭ6?TxiH]>z%ϰB,8 76^>a D򆻦B2kUx/.;D :oPU[XanB 0Ҁ禹&/HOc:dlIVgmS!%1;ՏO* gȴg .tjRkCѡ\@86LD2#ӝdu`!E `^f;2fm`9D'ķA&ccRM}a'J0!`1c߻Ӈ`ⶂG(HHI50342'&5>8S=Sh="h!C r;c[)I\gamq%rTwn֚ӗ_8,*CA(p峜HɏcE1҃9n_sD 3'9ug8F!8${g?,gjH̹V]~J>鸴^_ub8Qe,*dJz3%)Sk X&/m-s*>鄵7шqqFaV80SOYNq&Aɞ/9l%w$c# CkK;O-ߓ`Mdy|.&m,y(5lT6U)(hKS{9|SǢ>USR, >Qy+J9uq͟-ۙz- 8k}G2%tM]K 1cQĀ %9BGM CWzӆ7ͱr¨E?UԚUdĩQBx}Wn-q]AY4zޏv^ !pPSF-\BON 9u <Gg?&kOol⍆hT%2)'Rb~DђMk}ܑqo#މJ!W}ʗI#["r+D2FČDH6:Rߟlon/B34-D<%\v* c yZ.%ńءXW`d,s)ԃ n.2h` L73jR- ػiԣ="&\+YA;ӸaGԢkd2t?_:s>nsq&0H@-u*@縩@+kv g<%ى8س7/BoDh'0_ oX,/t0 s<6,kЬ҅#2$8)|4sE[M՗qSؘemq4ڒ\oZ9bUGBV+y| 5 nd%|kuá`il5e.1\B-j cR FtKWnT!}6U$bԕ|yÝE KkLH#xo_E+tUXv= |a "k@ɟ UDHSLBbԭAD8_]'_&?;r_~P$xwAR:Q]G([+fRx$2=:_q^ %2jh#3BV0>YrTD3Piyc@j'nBkmjgz IUŐ 3kʒ1§W dzbcS.,t);DQ#)0fZcKUJn6=6* n=Zٌza,-I'2oUeʨ7>PNٴkY])sD v#̶sDno|<nXO`&#3 Ѯ):3½`"XJjsV7onv88{x,m@Fec$ԃ K 3g)JW873&FJS:'L:yp5y݊kB::{,#QI&&[I.O}J#cD9\(9>_0$G7Tkʑh>$Y݀dmdqq2HcX*n3hj( /I` 7'$qrxQ #ؤ:$1'9yI3#Q3dΤsSP'5"$M'8t23M.񔩜@+ځLz}Mv @Ki:dHPL?ք ў M4"o-q9zW(('kʨaƴS"UGIf8)Qua_?scNt$gq_rNr!z'ċyUG cNg8v{ "uȸţ?},e03u(d'mi8/r (ۈ#KnTq >Vl1utEq~=[ Ej.*\? _{1|Vh ^N"G  V38 1:]k%e5pbh~R#+&r5 1Yr0b[J57cX2t3 4<wRqlхZPP2 ITk 5j2S;h1: k})B@SfŒ?XF.k+hZJ2W[Ux?i4B V ̠p\_Xws(5ick^ajq-ןH,g.{.-K6~ihɃR0{Ucᳲ2rZK,k &?AF]4 ޺򟈦mO=76sh nVw (7lS>}q^+xl}:ԒD,@Z(nQc5BQ`k49IN<^-#e-r--L`_8b\3S-!nT;`?$mr#i- P(Fx,B_dRUq\sBA]<xq^"04x`}@I;!ڕ.W3;"Opm,]C,~W,MPiDE(x~): 1m24 hEd[3{ w-E4z'^ArGePތ/x%LH'5ohv=`F;ڕLݧ ^\AsZ3"jJע5Yg ~-dle'?F4w.ԋhWϤO* SJ/cd]FD 0H$@GlYNb:A@%=zҊtM*T͑R&tF/[[`P>7T ך/9ÇN|x6NB0 P_pW ?"]YJkJqxu^JߐU o()6J@\ jD|O%y2#P»w% L=S-@R(j3a2ZORP^T#zAb$5SQho=:p^0BBve }=CDB+\H E &_wz7R^O(]ǴG`Du3 )cj;?V+'(*u/|ͨDۻ-k{[R3v#56/ؠȩ z8 c_e-S/M" jAtš+K't%8}ZB"N }5!ֽ|͙k~!nP9>eXXuﭤIAtaLw鹭qUl.ͺ"'7;㿽W0qW@p:u[H395Nr6=KIv!0pW~XIiP,)Ԧk>qG<Gt-ɧ$l-ӗ)c}XIWԸ ! jҁ{.H ~] CV Vſa7\&?&acM(#82̱\]"/r:Uf "  Dg3%flɱQ1p.+B-*;y< Jj޾"Mk,L(Hw?<Ʊ>D4gǺzѺ겯mq^Q}б P1$9/;lA:Jo w3c P `oKO~h E=Ӝ^TMly)z *}xO O) ҇ ں6lg6"ط{d~ިmN/Ivz~Љ=b :*ɋ{Xa`;]̈f}e_0%SSFM=KHz VKB>{`'%dtda9_5$=3lO ztA3*S*A]/\u)5ĒN⋲,:jܭck4;vm?oM|yіK p*Wx7T&[w&[ @bvGN2-k3 ]!мas9ymT\%h#QX<BɃ:P, 5}KB·t}_Lc 'W& :iSmN0ebrdHڈ7ݵm$,q_3JI*6Dj#;K6[(HyސSqu2L]i{8GY P6"8_M4%mKdKh#o`Hߙcp.(%[nTdk]i'ԣɯM!H(_c]JSɺ2go *^U5T:K8ęE6~pVJֺaɋ:"%6AP;≫*Isd~EMNBbrnB<(>ryWl{P5gǶō_9teYM@CUwU6YQ; tR$zhl̀Q ,rhy޺ \>J",Bsj =:}/?g=ɉEBwX9 vC[mQsvaG4(L;{hkm&]MT~z{װpKbZЧbzSlE~.w$Cƿ@AZXKSG1umV"Cez/D3h5,ku2ܟ' Qb\tHWŽ9]!8 XRZN^- [ߖ֎}1R*V>+GRBx 5jD |{GaZ?yf'Z}TzGAhgKջA]] cxq)$ ROơܾQ|Oi7`.'԰k@(U : ڣ>dSE@c'X4i #g ^6l8*_`:|܈TXR%a[=|٤a`TSJu%\}?wkb0Mc@^%8)J'{?SāJ޴1>/"ع\li0ycikwob5\\=k@}Q(ʄbg߲'V&IWf# 1X!v:^qs0㫇b.O-_s,z-J# le=ieO8Sn5`&Nz0㾹v^qՂx_8n' '0crkcjP+뷐ZQq-P~ "(S!Lijռϝ"X}V=<[V`/p4HL4K LKVqᒰtNvoKAB2ζ`zB]AcQ)*ShM'M80* `}ؗMVwҽ23 ώؽ#F&Q,[m@ à$^rq;GwѰPv3KpU[4:nLPD~%'gq%YH)qLE,x6$#;WAP8Pv [A;'(B}6{l%Y87G0a+"ОBIǽ9 ZsH0ԮQꑶ|I Ai 1jz(r4d#O9D`(+g+ehҥ>EHuDrT>57f#ЄLx|,A JW:5HPE-~Y CxS ~aoz=BO%vA8 O0i!Q"kc_텩d6Eǡƾ!&ZmV]gU,7$mm[|5o@[MTA:V/9A%:}Bh()eK( 2S9Y-Ǯ$4KE'OS?ǐ9҇=1ɒG=҂钆 +Y 깞hfqKmɄR[Sj~Nkz; ô9lڝ)提 Q5f6GO-fn#ٰ~ɊLB`[tqs88IU yem٭.-UIqUI@. fFeM_q7pŚ}x;1ri* yՈ|/؞yr@5ȫB$bnP›s]x|ka`` YېlƻT:-J?A@ưq&"H-:Ӄ}Xؕ:C |{lR{|XVSr\b^'D|{o][a|-U ? 3@ W7aNy$0 &'T{n\(YMԨ|-R `m"Ca i:J2<=oèR>tuX`W~Ϭ+DPɖ](w0[hI@UP!̈́]^oY㡚Þ(ӗV~j.sNnDu)s54>s8jVYC UQbs`Y人oD@qSVb0+1FTLZ0.G?Tɗ ( |dh#ϑw!eV1efk0djDlv}e6R+kr9`͍ HE3jJglx&Lܞ *O0vĮ ie.PVQ>Y')&,6Y ĀGr\Vb !YX`]+.h\v!\x.P :^)IUxdMn#;F^l9^}o+ץ_ ޵wF8VsFެ $*kےC7U\i 9]{Ki sbb.;O(Bf '$VEF Cv)xh#k.LKW)R^w̋LAj c?} 'KT`ןG1|>loIJĂ}8qC)Z 4"cJ&qRVGQlx3ONZE2&NkN0MQ15Ҟ3~&]$YlG7?GV7"h2GPZJkU0cTCS%fޓO4(>ӱ2 L.dUkQFnb~> l;ſl%E:K y6>3\S)jI'osxXFMU)}Գ/Y,Ɣ4H'v .;s'~ZDePwEJF@{ԝysG߹҇ZO{d][RAAv1q 2mr6y6~Kk̦5D1;8ҒUt"PUDAhxY 0u<QPCvim_Nd<&sץ.4cy+WɘR[Pc$O Ytw"mHf(ԩ:fi\D)`cP~{]xPUt-)1X\b7CHSQi|#M^",UYW)uhk2W{S͊xqpG51! 6>%? HN%hEV:ڪBˡe\Z?Ӽn;KQȹtRǹn1L5+HŴ]!MޫT|ӝ "A>\t^z!UhMt6zWOO}4"MlP|_!@Z#ŏT؏FFXZ6)ߖA&3"%֌PPjAp,lރRx]f5لД7ve&);Oex߄m5>mV%һ8PfJH5ڗ.$oJXT@oJN^SQsF+3`3+v`),e恟+;[K.(Ϥz5? D-qܚWq$ԛxCf +k5rS&ptlDz`?8_P.&ؚw$+ۓ(y0nhe:ξ&T:Nq8 UAruK%`])! M3:&a/ 8 U(c1c!K2_-< >s 5{=\;ʛ zs#ܺ'Z*P{TVyD !tgm+sN2\;dWIm56q)@Zv_s4~FkPM%cܞS $-'/gQtQ|_I^I 4;?T&yoߙТբ('{UU-0=16"UsyO坽;/4zIGx-ap`Hɱ? 9rDP3bro{6n6X #@Cr_2G*} G X##Ov8鑷vӢCf^rT,-0E&S6eP۞7U/Y|8PδmVIp>sYP)-$( U?0BxOaVK.6m% U-]>&_=r"<B%u1x$̑!H7:H3:]?ۄ.rBozDC~_; ^@3i X-3]+BΣ"QE8QQq[=n1/90Ŧ}?2e.tfaa'Xi<\yvˎZB ʕ/AdA8S~gWP?fPΔ6C0P2Iʯ|Ma)&~r2ucQj}};B։!2SS,Pa!b &7'O'׍܇O1c^ `8S{6s.ż<:P}vXFJ~)u^zI:׃3Tf+pV-&xu FU/m1XDg |v7]^[\(]$WҾ%Y'/ZFuhX >%߯)j7E Sp19db葍'l vz1&~r"xxPJw6D܄&,Y;Z{0R7ms"lXt/:$Ze[g/{?m);-/W=] z@xoYlдaqB<YƊIxmA<_met-3j!$S|vO]/1&[P /x ЭYVbI04bUJcʑ̿*LY5}<ư^\,Oxmq@<^2@}gTInL_f VJ*uv3Y+(n ,\hֹ*Wi{3~mCvD @ گJd ʵ/:7Pь6o{dxdAgvԝ:FihD` t캴*d=p#APGqNj&`m$rInpjI4Z(I| )S*p"{T:1e(QYu0!ڍ'0s#;v>-ܝ'; ,'*7;}͊dp!Fe޿B;VLWT+Iy9Bgf`fBM3q 4%"+x!Ŝ( Ƙ\D 7r>nm L#Fs%Q~C62Ֆ%LWrA` ߍ8LgPy)|g+.]*|P9 T2ofнк:MVR_UܡvFf'b*X_ϒڌʿxtA4(8 \0n$窂'?pd}$ց}`8Qf1 Fޯc&3ZfHheEl-GwxAg~A Ojܤ\Tcʦ#Ȋ{Y*[go5//Ūcq!<qCB''|zJRyqjGVoyT Dk,;4}\ $ː՛p Ig݇~ЌfJ(vG]i%??)mǴVm\!n6MGa)܈\E#8cm؏I] ^y/ye6_GR0ƗA+e)DlElye½1ocDc9h!N3pR~ i//RzAl۶҉΃7LzRUSv(-LodpsRWL1&8 Z2ha9}N^.=IX\Pc,ȾZ1lԵ#f3\L';ڒtRU! e+.Z38}ԯuG5 *]b%rW9|P4Q>*#zW *[4-Vr*F.Oi!JY+211np#_(˅ѕ~*'uMدՐ%S61opMG ё`α'\YT@j7U w[sAeezhfOѻޓXZ PvyeC9k E*ùrNr@G|DV';TU9qM'F/(jgqIjdjM]&u#|ք~6.LY,<^^r=[N/ğaڏ"0Gj~w5/A9w5aB8p>eğA;!_CΑ,r.0u"&N :-̀0!e0ϧrHt6=Z-} &a$WTNvu/;>T8%;A{883k}2tiNxHFDI9.@OD{%>تo{YLCgkTib,}>x K ][`tx+au’x(xQ4Qڨ$SQG.PUpt[$AdԅLF2bI>Z܁ֲ \-<*aĪ l548bq٠\k9Q޾3U# Rr1j([c OtF u镰dwʏXYRP%|(:\ ďGE03ꍳw<~)}{YԒH8P[wYNjBޣhȕB:46! uMN7 X-Ѥo&ۨ4% _,xsĔ J2_c} T.bP`&5%yµ!OQ%Vֈ#3*DBXn\5~< \QHDk7;u4(RnS'#bI Z|ϟ<#;6xBsʨ='×Ewkw9 9,@uʀ Dtk\@Q-7m_}v (C}_T4dTf4DCQBVƊ0M:sKcWItY˫5c@sڏMP`%A<@9eNضLgk;~oW-3%5~Sz=s3$Ã1ئƕ"x`9;  y31aIWfcӾβgKVL' H] {w?33e!EX"( ⛸9~/;& ,^umi80EBO:Uwwڏ, t8U{hM;1=qҌ.Ϧhv{I0aRM $7rZ÷v:,`pُ#3Oce`ʻGo3}+u4ފki'FWʪ~+}:dW^3e_Nkɖܱj dQݓͼAx!WJ8+/xB1m*Cb0hq6\تI8หH#Q􏲊"ΒsneuLyS% |ug:q.CNJ9=rLL:'H]/)Nw)`+Tֹ)U}KMrTCP^"Ae)=onk˖ e:1#t72d,Xj(mCr/ޜlW*,ÌPr^~Qۘp/>pje"RO,:*h갓&u{Kw²u p\ hL|] %MT\@%"?A4>%0OJ42{?G&A`|SpRB(LLf& Yx(%MINLOXQSYbܞ%=Wj tV }5 y9cSi&k4HbzeR\d]t?) v"雥y*E$1H S[n84d%= GE<yLd`IV#ۿ_D|lդB{ b,pcg\њO1̵yKrfN!FPp7fWdq1 .aW8=.Z zsϡ*NĦx3k dƮ3y!Dݤ]jW1pG,zC oMU@ӱdG6klo-0qr͗nR0.|D$ϹޘcoP)>Lȸ >I8؂|bf 1F k%dM6A'}QhF7N HSN3cwyX +r6Y#[Me^!hijL(W^|oE|JĔD9{SvV m)Rqx[ rl){z#uSmyqBT1Ȝx <@`Pk,`\!)r֥)\q ɎR t~ {-ٝ!+okVs#akگ<~^e=-9qBtEL`rd m2hjM~;6v~Frh1d7ʿA+בx@2D=Б/Bkh p5)Kj'L`B?\9s.N?f)TF Gm6[Ms_ 3 / ܢ.G"H0Ps҈<+WU,3@ܔZO3 NJlt~M>iV>o>P/U)B-*|.P4qUkʼG_vA.C+!:WtZ*_- ";m.-ݎD'X"ZE!v1QaRu6zSL~/]j߷ڋ}1t0FHI2o$9>j敐.^uifVcGTCrDЛ4#Fe؏l "r8;' $nD1B$Ж,mE][PKdP!m/y^*^pŁ;jjѸ';'BCf^v5gi}k_zaY n7ER~ngE$Ɣ㮱jQKSe @BNH(’3aMyg`DE^C2{.L F)UgPyű}`%/! n,W! W‡r;%FTT~O4x&"[Q#kRZ%gUKלuYG<;f#%WivQ_[*5Gi:hh}(.S$f 2M^*{oPbx+U䢓$<݌8{ic!{Tn55SgÇXv}V`e0k܆+=Cs_ v6;u!,ɪ %x&]2B`xNXHOM,.Vo|&K)=+QH'g]ݑJ[\s~Z}k:[rïej{-J#%`Ymo lCÖS0>Q91<0\9eDu>^`ŗㅕQg7 YMCfYmIE| ĔWO@Alg8OWHRgbVybfbʪ!flKҺd]N26>L]Di-HM-{{G.Ø:sK]f "B0ж꿂UEWMN°6WemH~V+#O$vQ6g^A+B|ZxzW[ wU:&(le0*.zVi>Ny oێ (iC8Q߻Q;9 &s Teވ ^(X&4|| Vpi,\_T7_JaC0\ O~ԉ;q̈́pyOr>Dr"m-sZJMɢsQM;,s-psw(ޫ2Ӭh46- cgmNG?8SK we)##ifJ"NDQҮ3k6T8ݚPo!/zhD`th`>1?iZ E|>lh/ٶF k}O+tѦթAlpzy6NGu ~'Uqw* |vO򔕞Ly/ՍiL#ǭiV,L"NՂviiBt~"m:\᭑2<3Jb0w$Ubh)KB4inHjxA}Wj!՚S dn,eW$E| # ЁC{kU_r0@yfDWop~k>\!eH5R9u2j*z]1"fj nZ`y6`?y{sP"D_}۴/ƅbVꖎ2H)uVW ϧ3×FGocΈE UnlimZ&wm ',v?k*36Е:V~T"4} EﶗOBgA{$^ QKJ0+\XBp0uկx#]Oeu 4aEE s2KZlw}h'wDVwzj?&xJ|`먴GRߝQTiB缝ʁC _Si_>B;nja9JOb`a{<3- ٗwR"Mٔ7h sXo7kk8ld%*~+q{pvZgGѸB9r#]X'ɉrSgݱ4 .2܄lpVw! SQa ءZVBZաs/~ )9r S _N{b)v•/Q*篿-} t|w ^}?$$H+ͤF Wrxbe c}"ia0kh ; IO?Jf+kxw(0ބEl❼ñ~Gh"qtFkoxu׶6Fp^ a͙iva9_kp(q7M!E([TqP㶴n4}i$ 6KND( gP`BM7cNtHIh:~``&\PT7@KKM-\y/"8#2,ZɈ8r-YqaNɢ?S=n~a1"Nqo{zec7M))lJd}?.z7K%q]o`\+R82pL F|nP܌)% eQ"}'H10\8:wh,:JPFǠFoȗ+ _@7I}1+QrQ1BXt2"@[|$*,,`dDE-א9{9@by,ƏŃ'Bkc$Ď0B;fe]+rӧ N w!I2Ě5l Xk͓KRb9L^aSS eH޴|l ]Rd6LpT j;Z FkٵFM źSwZƀ2p2(!V~AK'ϡSr-=otT1T-BޭKxS(o9$UBP5l-fq;C밣X%l&N5b&WX: =6ƫW7Rߏ`)A_Qn{qljPX? PiC7V9`I}:pF֞c-2TIky7h40exL\`ϳ)t/fqjD}`.Hwu@ʰЎ&mv YD"ٛQiޣ:TA 9UX{H@eJ\,AdhaW\veTOPIݖՉ?9Pl}I顽>_FBުq8(% ReÏq,"$x3(WWn;T"FcH׹M.MR@mjD_JzDMVEUm+,։a}$3sXOJ;;\ZCUP>u,"F O4뷛+1&rd@kъi"b.j:_)T5UcݣBmd%?zn3@!#f;&ofI'o(ʘ8q}xg7@Aܹ,<*XqÈ0Zy2Yl ,8A`5Rk=2aN7(\5?/5=_fO5RR2 KzBn+4M-e%0 ILKߺӔq8W6,7Ӏh߳&o7_4~OGԮp Ԝnh A%iz@.`@%Ptޢx/cHg|/|!1JޠGIƜm: T,s}dlE׽OhX]H>IP*V0.*H,݄q]Qjvq3(dn9)-Sre8<HjKջ ; e,^O3.1CncH}M@T%9U M1 ;#Tjw;q ɭ[gIM\j)nj@f珳:ӏ&31,Q3Knt}4JHt׃XY=d$?)BQqjYv WiDrՅ zި&pC6EED" a5A|ƧrYO k}zF)GJ= II #74p?? "W&  T rY`?CXۼ2oPb +.U |u']|JOg~̋k +hL+hr`Cx"Kh%>שtADr~ad?|+A\m($ X"u7wpwo *Tւ㡔Rg}0Lgux}>3f r;re|R[2z|HNݩc}s}2qk%9As,~$pPGh>6M'y|_Ӏe/ʳ Q6?] p9.c lgg4\PJjt4qjKH@X#k D*g{`_)~C#@t2H͛P ߧR$B|qS 7S{@X_%nLDj:VO}kS,pJ-TB# DA iڭzqwYӏu7\f1P5)X| `3:|mQ5\AizEe菘_>yD q:jKfs= (ݸQ #Z$6肆hɴEc~IӴONTm،8X3] 1cw' ^ܱ!>Em,#̥ZF0ؠ8hdwF4;6,-z|F~^A7 wU^X~p?^ZV{3+)쬅y YtỸV\Uéx/@J21IxQu?Lni@>2פLMu4-U#'B[OEƸjZ4'g3aM< yh [A]ODvIH4RD}<_RאlHn|N -Rnyg7$n8@Lw8\*LT9r dx7Κ#U&̀(~7j~*/e#jTnr.3l{Sr"w%5!,CaspU xB)hZ~-]+cW&.`l ZG#! ޽C݂e8Ëڷ$HEꑥ i)!غǵ~6 X<ɜJx{6x{`%آmj~.hZB0O Qۺ?B^}hv|.`e\7zͺoV&#kTv;b =F6Ŧre a;E[F^mh7}Z; Jcy\4mc"w!Svg9ߺV_o^ɏ@5lr}U?\6zRgn[ѷg#S<{~PƓ%/. ;Q%TDT^KBٗ&8mnCZv-r("ʷMlWР=;MF ġ9]-꽃%Zum1ߎPu5p qmѡFgkYPn?8c9)Kj551Qʨ3 #ɞX콑J., 8>䀹Qtw~{{qpf3[+ yY.֜?wP|ng4=i$a`X2D kHnL"WW m3]ۢC3Zкk t^ͦ|Z#wY=.윽I揖\9Sq:QdYޕTs'F-7 M 4pu8!K # ѬDlʍ"e t ; y%W(>fNs34N dܱg YY=q_o+ҹ`V2wnw+?` an3j,#8ŧ B޳ $:=pZE[Fദ3.YR9Y<,< cG5?ҞNa4 ǠDvƵ}ф`uY-bj­Gj hΕ>?;64&wO2!G'B[n.f'M+ܑq&KWĘ 7wq-&QoL!mb+IA90Gd>x؛4O>(#RxaH>]>}a PhFvovei+ |} : {|!Oل=7,c/$uO)LTX*4ԆXG鲾Sah?$PT:gG=e wp+5;AlQuKp\U8ʃ%]*Ek+y%6\o1dʥ98HTfÛrV'msk[MWDMЁh=Љ,;5km 3 o@MV1ˑiaE?iʷ։ 4nhI\R 'UE: \,<=Z`<>xG8.њJ)!h*&& Z"/}NLGx"/Da1:`:0NǛoeEwE;AIwv`j.V&$B &pewo.sO *#A\GH$ vNn@ =E0ɗhFS0j')9_L2N-wFG/Ewa!ィ=a@.U rU[AJ*F"t*Q(fGMe`&ƳϋFk I^xƵӟ(3D uyu5=I y|37cͫ}yƒ=xꚷ73˸TAPTe8NSr&%apWeK)3}w,Jw9+NY|Jڃ,s N"9@QOLF+б: +# @H3C2@1&d%Dci:~t僟wl~dVF58T(t[90ڠ%gp.KtC 5IO pKX>՘x6 e(*MW2ւO¶*xem#)Wny|-HTva|u ^:KoW#Uh!~>$djDs5 T6 Mv&X6vJ?ת—o1'E]}[E,_)Ƭ:> Ěw?EKbSk5oxH<UL0o@V$UT4zו] 4mٙvu+_Dǿ"[ 6|O"i,\d2S&^0&A&g;tZ4>Ζ2mULe.|oD{n T]^Jt4c4𻌯Q!`bCj!;%BcfKNkQ7I{&z䦪H EzdE9V$:@#_%t*`JA+t|bMB%2 b`Ԓ2x}`:y2mJSE(ON~1kX_jfjcU. ?Ľ_V2 ޮ b×2MT.:1Œ?Tw>p9 "`ºIG>^Z;d->\&g.Yc衅5)w'Pl?ܿmgҾ.Y8R=T1]U|kl!eiP,NE'")463^<+A-Zm@iY4*Y j* A$SF"=bYrwt,c/)3,rٔQ|,kvC)>%LSX7` y3BZ.{4@r<=Ř0I-.IaYs\ZO\*]}l 4Z5ʦ⒒n~* oQsL9Q`¸Ym)E=.pGUh\6̻վBPU-JmR|D5ͦpE^>ke1Yv1d[{4R4رI}rA &rnk5*9gwVatI:YZIIQDgDǎjmln@= W В\MvǜNơ',wJ n+ĪV#9 NJ!aqKp2d̥5i9&X193:D#켖i'a̩-y`^2Gv G3 O=TP'I^{jHFʷ< Az$ݰ%_w_1(u!+^ Gdίlif?E %_zcdaٺA9߁WT?Aнu8l;qLJ\VOI~7w~Uk ؔj{KI]|gw~\khf,N`>7Tc C2۷iۉa*-\qT&K+? h 2[b_k L+uke8?/x*$dT`2ecSDCz[ z.6ʘzLn{y2| v@kDyl7HvWbZQp#ڷrELQ%R3kZl/ 9v. $NfR! ~n,ɑ?bp)W+fS۟B_!:UzQ u/HD5'e+˱/<">~03qbDN<3zk%)rGGeImeF\=Lz. ֆ,UMf,FrN15JxZz` L"YX4ATCLч;"#NJkaMpWЦZks{jchw=*[I簥X ']U֝elH]W@MhE8\HROb;=auh& JAw ;ۻ*Cm > JM9Ƿo`Vߎf$ . |)epDd,æF70I{(|wiH3Q>[g])2i06@/nua5qyXv_ޗ͸ }^d7 y֭ qgE"bD'9 ֊5wGC㡷.J5$L#^6@=ziL%U'Mx%vzH6C"G3' } +~H_:hȳ0R}$*P`*}7&vFVH#ʎmՠ?KnWz` +SI릪Nyc0kq2]'L.18~b֘+%z}k bfUuvs.?#R.: Uƒo_lUz\.gV1ðʪr!*+t~4NNhaTB#%0V˴Cil@Soa1YatqM1 gXM\, 2ۄ4c\LHK5k}M8LHT!|5nnaT,ohZ|"AD]⋝yhi3ʪeIh!h#HC!?k2[8='oU(9,G3Hl am]8>w1=9.˄aJl)f$ݵL[s%U˥_B5%N z!  !FPI%f^c{T>3sZ u ~|YJkY3!۹e:__c:sQhdWhPn܉n\1dhSNwֳ[\}$&v2C(c+i,j4+蝖L"G1cB[Q?JJg.]UlOaw!=`.ETFv(p|CM09Vule_2?7Wv!KꟆC)ưVB3PmilS#(qW,˻p`?eꪸ<רrm&rPó{3`E7Mu; GbOF5I>,a"W}-95|n|?³=ًF(h$RD>э0!Tyw|9+9B4W`9MKr-uv5HeBJtIȌA_HD BX5rxnxȌ뮍󏌬 F0y(B?MD ۸K$bkFmG]SR6:sjfJrb_tQJ~Ģ X3y7zGVG`*S&=Rqxúy|WbޥRYt"֘ `L6hoPk7D8jcRNV+o)L[=-߲mW-`VDWzگ4Q3)׀l8@gX]hn(Ըf{OK9qWɞxwSmcHy$N-|vԹv^%` QD*Ϳ\BѦ0z$ 72iݠG `C:L/culRٲ}IqI17WP GdHDp%^)OhGȾ*b6o.ꑓHZoGRMPvͺSMɿZ[˴\Z-  8l0gCUnBꮒPTmw,drL 0/d=q-ƣ*D 0,~g .um_Qus- {oLRg ɒB#\݋Or#T.RfIgi7"ۂ/pו_lnAur3ZɝpIui{g;R?m%v&~rr`I;c#h^E4Dhv4Yx=z5yt TFPq$]"KSt{ZoıRJ e;n y$R7]"gV\ (8"{` QV '0~Ȕ Ḥ̂EP[7{vʊqv?zyJk:8rq-d#J7[DڍM4VVzh屳Ve5M |~05os;d';)c06Z`r)N#NEdʈK;>+1L4>)ß"):h`Ie%2f=%u̟}\|`jb׳ONwZъ5Ĝ^g܍WkwAO s3*U0Xz!緹IOi)m 8Wo/W 'ȁ3Uj뇍\$\] tZ/IM$Z2)c[Q_z:2mEl8=6q0(~5ll׼ݛ݊P%~>TD9ޗq ьW҂nmFa;f~^̿C޵<ѬZszn.\z !9va9rT6+8hr_b' 2XǾ&e"2Mo\ꞛӞ!wXRԩ )x=ɀ _krN'";u5HwUY1JѓIpC7 ~9GV.z,ff 4"nrdT' \eo@# #!-n/OD~s#6| @{L\Zanr !oIPn En.BFA wOYE0O ^L)p`,S܆Rua2ɴ+ٿRN F\_?uin *%;yܸ\"*5*l7F΂VP}_ݮKVKi%_Gt3Zрܜ:isN{:SifQ`\Y\ "M D4PeLAU庒$X~"sG|"28UOG;ym>ԏ()@^D}6qL֮F~xTKEi0~s{)6v_ҀtN&6!gGvݢ@^"K$᱙|:& YVxfmO#!ũ,u:BE)_h/훣ݼ6c|@#W$ W\6@BY+n~+, s$#F ë&-1ZAӖ$?쬣WMsSs|&j:_7ۿC癲hveDQȣV#pK. 퓅@j\*I&T3G֠js2˶70Eyϙ2-%;J"(in[S;GW P*ۄJ-PBjWPW\'vYw8[9? }LQ褖>eK!L4}:{:C _BxX&ؑÎ} +-f_X;3 ha)Cل,:jsSSb*/!LkHǁ(j岻at̪6^t*L#%WX)%;R~&Aso>9 H+]m 7G<. "M4׺B h"Ӳ^i 8s,"hn$e%]ArO[yJc0߲D>fjFű)oi×6gB5N&ei:<, nO>sD`qˈ&g)VKE 73H@Ԣ=b'є{ nʉe6:6IfK+E3J*'s zTq*7GΔavzPec![G=(*)oC_g'd\߂hRi#| |>|r 2?͐l4 !pS6 ?֤:vz{DMRh@pk@rV)/)_&Ȗg7aϛ(IPJQ`zt Euac9ڮj00C,L;}|C>SIyy˝NM !(+@^*L0E8Gx- | G+P|"Euy-W~q{U~AP$3E8hݰ?Ij _Kfa.R|UO X^ I6z )$.ѳu'Da)`v)Ƚr:y)187lC3ϜMb ]) ιeR 5 &UcF&E˯x'm9A?-ڻ|Ȍ, ZX|Ŵ/[lU/٨M̢D҆H? [!Kڔ̰7lEFW574 7V|4xuY@ix"v찔c?:_\7k?u2FH}YȬg&{| Ǔ879VNH3tlĬWMC>_K k0SXhqw乊wl3K@ҏRɔ;(%^{~f8r$iO~PRswwLӄTN=W Vm*2yɽ= t^& :lk+맸$= Py,MY2[֪ɿgkp,PL4 ,Rp2Lw?kC'HP"*⯜&pUuk5@ 4Z1Nf"|_uAÀ_ŷyUӗCQ-@(so8>g I=zAOhyJr2ԩwmi0l@d+6mDbi)èh-钄V\2 []t%gόz[R,rpPJ?]n)4y)AM3;_',%Hl! MwDbf 7-~07T(T⣗H oI2]d l-G9\LԘJw9hhz8|(_.ڜd&vb~}UCfMgr|?|pCh8gp{۳4! 6M$_p ŭIH[W\\ 㠸A!90GӠP՚CBMЋ HV8ƥ L ޕPCS5ܠ{b(EkXd{Hu- 2ZY[WÊm%ΑƂ!kAبܘ*ӵTmA;K D",D>=BfG_ԗR dM#$ͤr֋,E!PAQ[S>kqO+lxҰ`3KRZV5m'nTp A}"kWHt]ʲ1dÎ݆d!J!/gG7Q./…ivNuVU@2>PАx5734F;keJF7v$;O癸u-_-o1߷=0Us>t2aC P Lhli:I芭I'Vwv̩؁8&!ȓR Yw|+tu4XbȆHo&>2F'%pr=k+)R'yO՟ޮpG[}6 $ =JGR4~)8€5*^68N 9DW@5 #ѴcZ3vϔ}i'<%;N3O;NV\fyv~nW<%",NJ%M/>=eZ vneCHcM1 $Mͫ:F&RQ"оH|wQ8L4o[4S)(/4љ x_"Dx*3zG-KpXsD5*ڷ$K[4 qT>1a*Sq0A5 bJVi`6nnVI6˕S9FE0i8dK}3iBCΚ8!Xxp .솧_ 3 mXzo}O#H(⍟x^l8o 5@)gt!؏UV\I]J[ԉbi7/O?B͏idz8EM &bJ$EhGf+}C]@l? uaT= (a3lJKy+5޺CU>W/rWɌSE=y!ǃKܘ9Bh}wfDCFqJbt /hO x@K) NY[6G\'Ձc7z3}c&}ӈ.sˎXڧyqLs/OVb9k^^ BT. (:eݡ>= "mݨs>2՛)[߈&,Ipf JDɽy߳ɴkQ췟Xf]lk|dpJfGdj8-oTZ/?(q$FPmZ[#_L_yeB~LR 꾛 W ~ELuo>3:z _y[tya. ᩡXbjUglbY#tRXI*Ǟ!@f]pmyC8[`Eu>͡n-=Sܨ|Ҥ-)_ꁲw铯Ϭhǃ-+ gN)PK0luUc=? -8Bvy/֢8zkIqt&!qG^/%;NJirx>1-!J_(jkxіjZ 3,Ր" pK50ҩt 0b!XvԾX"Rيho  f9'YziVNX^+hKvvx&8KlY%>>yfzjhZZݵm -.=?vʏ1ʹ{t?H:bB˴}D$y@ox]Wv\ekcyZ$;HbIL~' {[f;h_>3 @gn aY"@t./Oz-qQXx) CRs=G:n/? 72A&! L, oeS8HTF@5;>h7ưO7PG/G-BV#D_,pD -?^ ԯ!J>g1 |/ړ?7җOΙ׺a‘{YᅪD `]1} 3Eߓ Ƴ_C7T{>3|]yw-3N+[ pHjCvh؜|骻>ûPdLJk[9D>jE Ѝ1Pbʞ#k^hFr׀C^MT9{53Us]^i̡MWt:eP:ZY9벏bi5cF ڦ {ca'ty#5؀l2@ɦ]ϴ5f\vZlP jFAO9:ȇ]$T!K 5O/!m8b<)pFXXTyJPFAu:Gȹ P]Rnr(Aa(@茻 5Ϊ?C _qޯ, .^ '8K mƮ,< sa4"AczSQWE.KC;sxc5#$%k!Tb>% wo-A ծJSJz)dJ02M ^5MC,>1zqo&zMs]WF?(YΈdz}:ĮfQPf&ITq -,J8V ѡ3ڧ`ERblRqLa~Dֿdr'};ZM3E BXY la%`0:x{ex9iOݟM$6-_NmcIEI<16/߂un&+‚U9~BLN RR(WxFM r@?;Z< `$ɣZ ǥAcZ@N':1po Lʧ#X;6ÊZɲ]GG#d!syJnb{ʖ) SN'R~0_!&+K9I.Q̷B4(N}y~K^!:k5VZWA Ӝu`]ܐ^̦ա囂 =S>7te;aHPr] Wk:eO_&'Blf\bīqϷaک}h:iiW>[˺EXn 'ӅHHTB<>J32ǩY聜b{f x3CkQb΂I1l)h6 r#uO/*Ã@U[#8("~H"F,]Sނcx<AnɲHfVݛh2l-Yy.j '*Y=.cpز/[K4x1B<1|:đ;i^Ifw&a9q]^;ףAL\-Ε+B@A+Gt+_KTt%9 r)@1$+$ݦS)AkPfұ!b0$j}j8p (h=Zِf6c2|5KNd-Ł&h,­bľ.ADXW?8o|xPKs۾[)1x{]>ᮩ eA2yeXq[ 04.LdK"R/{4a?&. կnK ?*Ti8eIsKUxLN }۱tN2`;n(rVik =up@ bMH<.(&3K='~ld#62.úo;b\xǣ)(H7 wykJbpUh*z?_0z5,xB I1TT Tf2FXhS6`JFR~sg`+] <1E#ChC]4׶&ndrZ,9KԵmN%Nqήb}hjW9x ݓ* ٷ˥Ě d2lvx Q7YdqpzΘDbX 3=ӝT뫻܊H'zV (GܲMC瓻JyfmGF10HU^<¨\ܺivfbs&@Hd?Զ&*6+X`NrTV1d &.咐do}R}<9o0oGk&jow!hjB{Uh[YE0> ӓ` Xr:E5MA!G AI[\/_@Od;[ P|kU_=[\PD[[N׌{BssLϙMk^lB~Xs 7[x[\N9{T3^W z8TFߌGGt!Kgwh7vv{ZpGY[o+ETTVrTe,xr;]!ymCO]' B>T};kG7$vYRH׸χeY7/>JDߓ@V*⿽ҹlO: /_KFѳ*PVF/ :jҿuɃZi%zEl[Jp'T-/S6EΣyûS|]oB;1G/ྦ+udjnbf.O}!3A䷫rɺvK4m6Cfa=`~%ߗTRۃcȋqmp"x:bf9RqB%Qvnm&&Kv6{>6>ۺmyC#$#^a6Ga~wP=n/>č)EHqckdt1(A & o*AB7^ö~Wt"HyW]fgFgOz \EQUU_İ\Srtn"> Fd9Gd%_n`!WϚ,o|]1k d%ǾTwƂE@BH WRx mؔpf(H# hOzOU~ '( :" @]=o5E79hل;[VSJ#;jӯ(Oz ,xD/LJ_Zׄ1u}??6|RALʌU+ܨުnO@8s`nYmҬ-H 4u 1+wb1? g#2PX|9"-? ;5Du)V:fF.F`߯Rm`#0@*\IFڹI%SK_B40,dލTLPhyj8HR@eȑ҈l5@S8YJh10{u^ ZJA.*քͧ_W%zB~d) :hKkciX}QH6u˟y-w$ƘD}h\*ECjcyh U;4s8UAfT8 f$]Ǯ:OZ@};tPm$ &g_bEd…!^>zXPfFKTD,<]٦D^ fӆx=?fݶ19$a; jc-gT//GfvvF3/?勗΄h N=cEVr$# ((4<5C3 #,ь92=_^#AtYUStsrl8U]FyLiy=z#3>ܕīpW -&wq&R[2\h#ljf9YQf|Dׁ/ t}sdN YwLeɂ 'k r$?RCA}Yc[Œ#nh󽚰p-Z̐U M"<H6J "#&:?A^4Nm#@h 19K*Θ\);O ?aEb;ޠ+Wk1Uq _(AKn 9gȰE8>h| 72Gm- 69z O!/k%!iu>SRmrݟdl\ `3Qhm~WV ⊅&|m"$%bα cխ("U^؍8fD$ U\|-IOL{u>xٸ0$ p2 _%1,?tvw%i 3N\BZH\Ҕ)I˒xL]wVY$ö]2.>ϗ>))~Ϫ]!}GZW&_ ,8*%c)~/6Wt]] 3D \ uDJb ht'g`T(@h=񦓁_FqJB]@sH r"{nm򗰢#LP7:p;#/Tݙ1X|)A.'v@F@T>vy%|3$kwM49p٭b>_d_JIJSĸntM&J:VZ%^_/K$B>G ~uYъ^Lj|g U0YA{61<ݹL;xt{2}.`=pbRÊϓjO!ɒ EO߁f4^%7S.q Ol!ziNmwq,@ Qom#WAƧwyߥ#xI˃w| aMD@a C ;SIpa)q+c/!K5@H˭YD?:y*jH`Qiԇ9]o\>2cdJ%e-UY֑뎞$nq|!~>49M-a$/*#kVVU'ő4YGW-2.i?; &xT}Ž06dv0}äh9r>Uy4Hğ%\>Ē V2Cb:`39 -X!|*8HMN}^<:B,<ÌQ"GRWXb1wslΎG'M˘œY1)>2ie"g-oWEp#emKw ĩuj-UfFZQBk{:yT(Wफo<+ԲLUC_ RU$_ lP|sXf<휴(7|"4T#sPxԴڐ/n!4 ,?wi`v0ts.fI#nAMYtv@$TJjUm]C,Z W/3=h-axgZ$ E6W:'!ng%eUhQ7_iG0B[~\vf1R )Ĕv<*bՙS.6 tPha\ teȕ[19+'$-A}]?k=rq R Ķx9}zNH~B|HS 'v;@ɣ1iy&9zêˬ>eLmO {'dә Pt+@z"b"8s 0w>fFJN~ ׋/ Ϟɱx1~zoAinܸ/!ǝcCvh> Ge} P%؎VIϰSnڣ!i{שhV[Dh6Eu.QKG >,V{(#&@h ',8V4L1u`rھГ-ʾ QQg Ռ{nKKZ\4|ЮO]D*OӑQ-|;u/Oar[;kVd/ vw8hCぽ!43/XD#~03K[v_7u f +3}4q|Idc@(h{V) = ]nC$Zq7 0ȗ SL:JO?A}Qk[kSsZx,FiWD?ݨJ f̹-Ykr\>,Rzq)dC Ea(D w 3 -_wrT \.:FݓѸVa.$tі/[Ă|A@Z_bSl'Ӿ$m1(3zbHX[ 9l~߫Wd{~٥0$ao]p܄ P;7=wqOI`޾r_A= PqL|94.> }Օ3w`YDۢeo*)*| euc&ћU{r'%܅({K-^F+&ʋ)oLAW(&dDttQ ldsmWewg{Wp!ÜϘU71GG/l,V fdn|S 6̋p8c/ ETr=_(Ƿ踸CvH~W |7b*@18-8n{ ҼEZ%#5xY.ԽPYTj ulǹ7[3S.ux`Zh+QY^k=g31_Vt٧beN0%UA&^G/TfW{ǦR#oXD%h?$بz)(n֌NyN*jzh<hi ŅVDUǀ :(Hcf5ȠyqIw+ FcGt<pM2\+NƽmY(aAusl[V<4 5ƪ+!&Vhgvod6q@+|M$nT'Q7<)U2RgF_bK­)q#@FD ڶ'aAqT$R%:^=:}g:nhbX P!ҶAuiT.ײ/%W͝o2Q =M!xbS @ $ 3^GQ\/f#9*֤%I+\DYj FnՒ7NK`h63N\O|ØH ,zV: Iu=1e,pgk9WGyV(F <' PTZQ{G^ \{%2T RP4P=QoXg WRCu _l6^ Bj.% ?q=JM4w8*熭Q8ZX3a҈6PS&{oyw J^// sz 5n^+ Pc\ϔ?-kw3Sj׫1'$9񘔀_i}Q+i +Z\\=Cxb۸aL>6zd|TX`ЅK2RCYYTSRG`#dEs^h} TOkwŦz=Us2!ȷ{ݱ7K|lRM;#͏( g3 J^B/LI3rYc-J \¦Nu\V7JJa^1}>}Sc{ GlٲLکs~KcLhY`m*&m ʦ[l= F 4R>rm>W+:R S&PLį#'4 t[> -t p/9Vo%09;Jd5%!p(-hq0;\MX-oDչv="0>iI]fKVNmcU AjîllA9;Fi-oi%Ք]P1e-@N5m9߸8 }vH&5AÓͫ,R]{3~oy}jc[l/\S$3|O3^AI{3CiBy"F t.WEt\& V.173kcֱ]- !?uKl]YǴ&'f&+m74yj}#kPj6GTkIYWE3ḑnǡi>URO7R*{;VSd9# NǕ8rfGP~ٓ;xMC!;y2Dup =j p'faP 0d:~Z Yϼ~2zRcdN̼R"!ᨩ7tS ?~a y@9F EC8pͣ>$'YR*̣( 0R xȁz 9؊:ƃ}d0_՜&^>lseU!w=}|b ^>xK?E(3y1otCLk+c/P\˒912pWA|BuMΚ $]9 :D"!X],om[ˆq[|)B@AnobƇnxp)奬?[Vl'eģN! C&g4rQTY=x^ n %Wi/ɕ(=JoPAo !C4lzF׎,Ttv^, (YwNعDH{hbج2`%.lNQk"ZCm~rʪqȍ:D0";6빴Ayy&v؁(1b6pVpGPؚK׃LNHRVЎ|T;sq\3fz_';T߀PT 4ͰA\_6,϶0zꫮ넛Rt@Â`Cźp?~su "macw) .Pmr&K?(Q0W0x"[][yUw|Wx!g%" x9F1ud!R9+Ro^Nj/{8/JV@2 TKج8쟾"A2W[@mn)- L[Uªsm'+;5?J7ڥ>z Pđɠ= qߖf-4KQ+n)WOiԹk?c<D!&):xY 2NBWgc_Q,[UV:.&U |I~UN=#ݓWm+5!xYɕcP`C,!&<ԻYl|Rj¿pz쿶q˳N^531^Z+%/B1&$clerRy!3b}ZQbtI\mޥBˈ?\N&(q=$p#_(˅ѕ~*'uM)Ԟ;RֿQB@ãxg4c=Kub.m [' 25FD{jɂvu(L>=/DP.9mLz)CLE}bFxQ!#l%<'Kѽbu4P6 DrRh\!zO󑥌KڧMI-0|!(w`%eډh^GDţk",0vte}q #\_p&Y)w'~r,p)hjגb._ u3Tfr؋t$6|mDBy;U"RUi[<5kޭ Ō8JUYwv|0d$ֽy!9D[׉ެsMeBeK]C]3tیݰ&I0LߘC Xˇ)H._F%e7XЩc VlB皶j+:~`ƃw%4{xG3_Y]mٝ,ELڬzs8YrIG'H*ßYf{).4C{ppPlF4D=/GIylc^K@U/\ks' Wޖ2_S f ho:PDy^R='pbL>\LELYw~&8]&!uZ4ìH: 4<%ۧoDɷdC:@JcԞ5eQMxzpm6u'0)J _3P.7^ɻfDA>+ixRS=c`P:0qz(G^$% n ?&EYO\–U.,Íɚ*l(0'PAM8#E{Pʵ1@'2XK|kIXMhcsR)N MTj-*SvYUizz @)G]!6#gDw5b:leb#쾼\?i>Q1Dn0$v~$@o'_!q[RoV@p;G5a:SK!9mZE;c2!;1_*n L5{o8aLr&t&2~ RQtX0Х9LJ+c1SU׏gd\؁g;հ:n\z]bITC7uoL6x;"+Oo_:EtQ'1Spi%Z"ำyQxR|Ί7)aEDC㨑g!Z*dPñ%*H^$\U\rp),m><<'fCDK#?`ZYM Lio Gnn=TQ?1${O7i@BȈ* !4~Ew_q,C'ii'pU,|u[A@r.epb+#~q|'EB 5ɍo$@z4jM&/vNpn0;JܞZP /)W\VqEo>M_!V<[']th=!b zj~s%AA/J8ӛH5-/ <@4v].Qo.0 *"Zkٌ.U?=M 7#ǻ/B"ǪA>2gdo"ѹfڿH4B7_.( C9[l$e=) H8GpsF LLRD7INtC=gpS5bF$>&lPg@k KڃgEJӤnXeZ nc&R {.Z M(-X=-FRZyKShѼk%j37ʑO9c,8|n?NE~K|d{fu{GzJ4ʽd] Y@4%W {b'4ެAaK,iASm_ 2oGa)i9$5ӹ ͑nK &u=eQ{&uG\5J!?ms9@O-6&6%zq!|u|&N:8w{OT󸟨PzΪg&K K='6\ᱍmp Qs|< wC~$`ٷ[U sDJX/lTU=)բK:=58es=AXkYΔXGsM/l~$9 RiU0>K~K*0Lx3ۋ-nm 6 @cjՋͼ=Fǝ_i LRyB2^[9 j3wOFJp z[bTAzOZ ]ˣeDH^NOJpQ3:+8ɛp.^埭n˙^YT}c´и%-Hs*YlΌn4CJ..'x-5,mĵˁ+u*uQS=p Ls}D(RDFrnLΞQ&]|%KhH;]L BdzkDWd0Uq"NDҡűeҊ5:0WTP eVXy‘m"@Nµ?z\30 Aηl|-D͞{6cNuf {hV~Tڗ8]jj"Un5feK8~F:-3f'lI>X5nI+L?p޶b0΋rtU Ԧ?ك!!#)Z*_8"B (;9V۫HLuZJF@~۱ hg^C# SSR l?^Pń%cOL(VF[PGEU&g\&qgN̑_y8o,FvќCB`YUǵ_(Xf)d-zDṕc"+Ѵ6*X&ɗHMWHpKom+u溕mM9@S]G>QmgOc6 kh<oWᒐ̅#DB"[DIUdO5֚#}Z*3TK M+tc%҃P8 I%FOTC>68sI 5͐ b&rii[u| UoOj8V"ָ^TNwuD4% Z]G>5oz. ryFz41yavw;7= yaP^'A8Dl i>.U,A8ngE<Ns>DVg@hf-UfΨ$ä@΢U@֣?{f~ѰzHXe6  Hv+a9Bp՝+ԫi@ސ'6!o Kj2 HYye?=|TzIKY?%!nG) uvdzL^HҀcTv 508h#QNHx"7*.$ abp&K5L%%@]0O+#I,Ȅ$㛓𨵏p C(vR| $4Z 4zAB껋K Ũ9椶8u#=K\ڧS#ja-sR[]aB( TW55vy'IjKh%8ٞG:?=kKjd9U lT CrL`z&~"O,1Ȝc{wWo*ZrDd/!hZf!d4^n[Tk{4-Q!n6uSí J *ۘFӃARMg0bv|rV3;=+i5|~24O E)JM#AѠN9~Ӹ JE'nwZv5^u W3J5fUP <l#W)hw^cw 02բWNi{^9 J9̓:}p-)^\Vuq.*Y>sSl?p}ʚyWQLCSheƏ]8<7|ڞ/ \V UɺHH8Gkcr_)0a (V5D^Tm282Mini182n##X[ٷ'[v|udy?6khf$W@~j^¹?qM'+Pc*r>I`ߤ04!O6\qD:V$UI$x*ߴw;!0#ߎPw<Ѫ%3ئ-rӣ5(@3m̭|;LEnƣ5SҖCi7jk_+ZtKS|~6fT7jh@oQS\´#;x>j o҈Y~ y,S,ˠ`KB<*@}WɻFPr>orś[잁} h RSvfmөը9$sQȌq$7 ʙ0|'~Jwci1(Zja{bזYv]]Xt)x^zYdpgtmU,W Cr?H :5u%qM卸W^=O`y(8FHr9߇_Wjl ( ُog:LAl@+i}V?3zח^ṡY*/[-kUb@C{e.O8'nmh[3t(F$e$8wګ#P: jVmxzM4bZ[Ї!JZ(WqFa1%F$ :.щɁ#FCѲa/4$i.q1+cke >.Re%t&q⡢OsHK)S7fطѪF('jZjکewt_|kE.=Ȳfq&Xj_=跎/}VFuVXR38krFa|r [?4뒾b EsӺ 3X həUA7!Wv%g=7Qqwv$\=3*&R yfxh=:ȬtD!jD][ Z»UИ>ʋ}0FHArY'l-gL>9%/hcm? QŘ ΍;HFd"-D3:t[b^kDRe6Ѫ7~J`0ZN^EJne?{aE/Z! 'b<j^%ͦ1cg}ȷk|9+l&_*3ީ;I1WaFKCJK8*0>miX/ov.O; l#QU%nȗoV>=W gwƤC)j# .15#P%e-@Jm1IiSrJ%Ot2|@~(2ѻ6W0@l8Ҿ'Q\a W2nYxi_\5& ?^O44bۿW{9R ]ƯkkI2D0H9enDKgS(Ѽ_k >X o'nrHp{M ɶMU ;!h=2+ 5qBəoDyz, 8Yn^jzYwy:}T,)"&6hl8 ܎S^kCrH'_lkH{-\//r(Iu؆`gRg5*]|Fr9 ?i -X3 Q{KOL-Nvi,] 9a^؎5nai |{(8+'9N+Ӈ$/;pf=s ]/<ۦwRQ ,4t35ݐ?m7 !z6M6ìU2qL0 [pK q';`_f4h&Nqu7` |#o\:P*{㕮V(%񭖬?q;Y-$ѿ8H58xcWczqa{h30ʨj<9^/,1Vvn1vDKl\jVP*ZZ<|X*%o[|pscZAг/ +M'h8MBNVm{.ްҕnapH8[Bw$e@;ԉHX45 /iiﱇ>PQ0\)K絟v-h&ݖ@8n^nYim wmV84Y>@D~dsiL)li 5d!vڽa}HdjZd!mI"E7(ࢁ~rP%5.k42Ì@p9哙CIo~J,vRKu;5MLW"{UxЎ7ѤI,>Xg:vW .H#A>8V: j?>M > szTfSA&\kmGg1;݂|(mJlN8z$q>[BdECVcݱSB6R_%-|f1+3\DuFwDQij;aC _A1F8@ZGHSegfhqZӄAyE' #B1g??Z5GHj} ^K׳jPb>c5Oo9LxM,J-=݃oPcG5:f+{I(ۨkX|'A:~J$Rˮ_$7u,%^ >8 admhfQYUK%u ^fy43־Q/赪+4k6<0y?-`arYsyKyjc״'4\UG+.d/Џ5i+&>(Tqf87MvTfum'MU O=Q0: u⫥xkS,p큯I7b%ҟX Z'͆k~,^E֒3*{y}gl9jhC hE/Izr9kE([5"7@rY ʶݷ>#wNUF~3j)3_ɔMŰC+q!+3oRe L;}Г{g l-a8 &kT)z<$ o2F> f3!Lu8skp1 `I˲^)]Þs58nOWņg$'YR0i U;OC&춪qXvTuٸDШ/"MbDdLlWa} P M$ps itaX 5g#G)tgK ;)l[;;T[wr}YĻ?q}6 q%?^YLr,6P]%qup 9{O+#׾. KvVl1$x0+SZ㦮L$rpȓC\bZPWه+O`ߒXah9Үŵ kAp4ʟ^Z;Yw'xV2^TGOc-¬۹5\kJ7/cБijJȕ^?.ƏTzJ->SQW@UdK5?E_MKpeWB~)EJ/r C&05c&v;ҷlB1/ sS"X'4W_O_^(d T(repɔ SL D ȅ# EۊNf_ &nHA]b.sxfp7>"Y&CM##GU0&6l_CzG^d%Zi`_$"딸503 F` l5$u8uyMb3;d=/X.⨣14ו3!O.HNRՀюHZXւɗRHeߔ/i9xr<*/1*1tma)S::}*XC 9N%_uغf]x(wn8_aV ^nEOȌU`V$)؋MY4ih`WDQC|0tjZ~JضDh_E[߯VvdM>_m_5es 3 G^N33pG_;co0 e:bU.8RzAt_˭Zv2RPc&}/"zs '$ۢ~kLdc^8n .!.)jjED@aaQUFMS%y'*(cE5`񯊼5rF)3t? !4qv ɰ,{ZZ;1 -oWu<-!6RUtϕ~MwQ })P{e 77=^GT:]g 飤b܋v )N &|kORR 3?T؁EyB!OXjɓn L \qkT?T[w"t{-2J צ=Pf:P|,3 ϐ_k i8oܪ«S|҇9/AQ!4DPOL rl&3>ўɞf"Xvؑe,{臖$)Yp^$f,f@l3}))/b"Ӷ=/Bܲ}8c>6U(v=kз7^BlT&@G?V,3%V@X9h=s^ynu%ֆáTaQXJ.?wA*B!s~bÌW7:CׯH|\HK+pʼYw iAX#lpˊa0^)lZ4ni-Ќ}\UfỬ誼pj.~~{p6Tn(U #wS_+lhgNtP!3n!Hj\+]Y#mȠZ2_>tAT%DXWiˮ긨ݹ ukogӀ7r<όOPٔzJLcQ7v&& /Lb!%Y4ǖe~/B$vjH,X3t а ݐN乺TB>/YSn^&9&2<шnHάk6=Z&dsc퉋~HB#~&A|̶,dG(Qb%mMrBwKTՁvK~ |t!Q:L>+UFˈ5H7 ׭Y%Vh 'ʏ[T>KQTAr|Pm29fSDtk6?x/;$iaԄ/ʴ][ئy%9)(19W h tb] &*lNItPQ[ 3)7>b/CAzKգÕG1_oFȲ /kwN MO!FoEībZ736Zpc[}R  2TuFvB }ZM ѝZ%>S}ywA*;wK|Tx(4 5JS Pa.,vnR( Hbi(25337gȆ2)  sn$Y M~)y #`m zό-#/Lsr"O<´5ivQs7?S+jQ̮ԼM[*6+zوƺ3R 7s|oGJu@s":6q ܒxA +[&w/o:;ihVe$>‰b<1Fb( fja [c2ߴ` 'zMkm!{"AmX1٦n1Dbædݐgh趣Tm0udy$R߳}F(8]>ELihg)JuQ7)b)N :%7WO TT!@n ?)@ iN>b1Ckh]B^ l o]Xܴ FK~xz*fX0r2_cu)1SPfXe^67jF"P2/+gk=tNSV i%ة锉:X9֭mﻔ'Ȉǽ:XHgP٢BNvPL 4і#myK pTr86wbUCNv#Q eٶZcO 9ozo0=+ b|VeFXw9YNnoTX^ ̅!yS`Шgㆤr{ 9=PhITw|;uFFU@?3ퟪ-S"CHʈ)j,|NÜ,cWFApHp&SE(lABeCVO\бJd|uLoj7¯]˜7p à(i3Ʒ&72`dtD\"&l~)B1rfy٥&1vIh̢C28{R7uW3HPx-锕bioPI3gU9摮w]1^`ؤ _ව$IsRN$m"ܺcFTcࢰӗw6Z8ET_q 2>K(0ڋi\W5ۻ":NIogVin; 3&`)/PGO10G?DV6+wns:Mǭ dǨ{GV^?f!ҴmY㢣0(Ȋ $ŀB'T-IMڋ#-M6  QGa@nce GdESNR"&v΃vk1!,Yh1b1* <4pxXNzHۼM:t;x>.au5^HuN4g0&uh [lnR"G|N<; |gaYC b4.z jd὜C ׆F3Y*6.gZ"AʔSJPP0+M7%ۭo!?OL,8b %7Q☢hIPIqSղI;uAxVy:U\U|&l"dYv\x+PE#7V'0hAM~=D &=Al0/?dq @^ $"9oC5j3q_*@MUaJkAᦦZr2_֋U>g ŁzpaP9۱mDcDM;O愋A!=ɓ+8+޷yWl`- QwfFD$Dݹk|B?g1沷؇/GX.鉻P,u+?2ޝ$?OuwVuY6$Ȱ9&JLã׮EScslRKdێPШ(}MstQ!ͷĩF>n$"^ ͖ >/ǖj AUg|Y;y*1'<>ћ[ǻ+sD]G^r꽺,}GttE2x "K,KaٰTmQi&r߸XLr]G0 TxUtG 'ȍcxjG-`HY~pJKSj݂IMn/8ƢUYMYvyvXKGA^ gUFY)mi2ٰkn~MʇSג_S<$nT0IB?Z9gv+uXRF RqۨĐTvvl<z8@O+m"M} l :XKC2'1u_Y"0)k$G5 ȧVLf؆O37ޔ6 Dn;53-r^mΊb#惘ѩUbh!2~E |9gȡ.KhHn5WG3;˫Hx/YKw*5T( &-mL,LA b ihʿٸy eŕ~*[ѱ=T, G-YST]izUѐI\ChRK6d"1=dJ-Ѣ(orp榟8oTEwi[[ Apl3ϻtaa:PiE|z*ʒ誊kZL*30K"Ğ-ԌجU*9´VQ)Ltz*2jjge2G2 G#k!qH5"_K)oB*J ags !sƽ#xu\wLV$V}/ռ8C"icoLp8݄Gzc!:Ɛ+nP)3_9hM(ƍH$N][e!0%w :סQ3Uִ^ө[=8 | sA"[1%@=a_dkDUQ+Nl nR6:u>Ms(9S0l{!ggLYj4YVs,qjt^)]xMwߋv0S;X$<7Zay\V)*K_`5rO,bDW*C$(̻|R=rA_o#ƙǶ:JyV@?ݵpU+):B6 8'O:s#6AdT- *.VF~{֊$T;4V%QU?$~?eO¤5k, uh? vv -ʜآ=̳V x]n ڣ+^ Z7瀋Ӧ&m*F UYŠ aFuWT gfEĺe7t8N7JDOl lq8;B?!rKd6Ԕwp|Ǵ͢Ȗ.[iͶZ~[c^c! *?zy0)~- {A[Nur`! $_[W| eSx`~.aQ7vierJ4=cislcP/cGy~_K"Rkp%77k Q [9%ZNw_h2;>w1n#axg#&W`ӆ&E:%)Fн'.HbYK!Ir)!PgsOI AZ!P2O6w6" 9u08HT>Y¢lBˈ?\N&(q=$p#_(˅ѕ~*'uMدՐ%S61opMG ё`α'\YfRXvY3d8Yw\vN7WrT_?C6Nr{iI<3{pvc=s AkW1 oxFswtt5܊Xgڭ}AOɵn6~vܸ~uum2^ZǩM}\-Sqxh0KF;x4g*E|m,Rf@~!a\EF\{_\Iu=48=)يߗ5gC#SAY;->Jfa1EG嬧W^fl,Dddٸ*,JZO.8I8ku?S=NI;mQ^Tx^j PCo[V9#9? A^̞@?jJ*L' '_zJNMT[ZtL+UBfA= S9nzC? ND19s QI ՁΠHuUB_? geKJc2O s^o)-L nwWvfw1Ё>GQ e´k ӵw᛬fY 8s8Ÿ:CzF@Ĥm}LNFMPCwj#J]p7FXJGCq5DlIz qrHJda-Y(~,pD[.WoyL1wq/19 {CIcK W+nN8~׺MxSCyq{Xߘg*s0 3^#!L=#}KQ .wNԎv6%61PCb i)nQx;^"~^R_Ōj_Y#Mo%E{ EZVg)YP?2?F2ֵ4ΥvL~ &{OEmA RUt9%U Xqi#>z⪓.` ɩQl xb$ٻ`4W 6 6Z3#Ͽ׽כs7n qAUh{km=?Y]u/+;䫄5٢yTE DqE,~#YuJr; 6I$\ *q/+tE\`H5'm\S!/!; z ՌPa0r_G P4?~RiI0`ݒF; pkVsr:(DX2 O,u1L k!J] ZN{?!=۹W ; ._#|kč2 I\v,[RMPHƺ! p٬ے} gqN&Ad*؊k& r /89m ETTg]V]BBXOc 6v-绵ޭT/lQR ba-m)vQz2-!tF=,0PmX+9w84^u@o"{p'_2F]Lq.($(+>,.(@J Npjm*7~1?7rM ˔ g:-a^/}-v+^Ԩ5Sل`.R{,K|%2AĄl\ޭ<|fi#A忤+~1qCNO5W6'z}$TG"FRL;,{`YQhyds #*Rֳ 5ﳩ4 W`/91&!`5L{vzoyP[cS`'t*|i~YoAL'HVV O립:w $TS%pcdn Qغ ?` FyFCkNt!V6 %4Xc<F3HQ/婩 4$->ڲDӮ}GC ̸lB/4!QQ?y  9=q6E__QqVPdo`>,sanݖEzq7wiC7 u-52WZNip_0p^m_tq1ؘW1e6KKU5̓N ׏c]%aso `9c[l%u dØ )tƬZCNgY" 6' 2 Ozfq>?q 7''n\2*@X9 [ OБ]SxkZ4=%-5vS\GYt~X!ImFq#~ӔkDH"3Qu^1T/#ᴏ7\5~c]X% [V _{4z4~1ded1ISMmŶV14c[∀+ Vt߹ZȎW҃yҶS9Kwh/@H G;a5= ȳn5:6Y7ySWJ( 5CsU[xtA[ט S.=MG-{GuOuA F%rJ?FK`3e.Ie|A3ɬyy(VZFd;a2,RE9Wwn4`P9ܪ2(Ub2>OoU[KNg2 h^#?_t]&ň&0v-`9ʐ߃#Jn }"eGHp`_zsYuJ쀩>W }t nL?*^\6{B`b7| ;ᡄ΅pc~ V~"AȰo62TotU,2# 0Q]_Zf}r:)ֳQgv;M(3Ԇr ')HbB\hu |Ĩ1; vR7n@VS iC>NJŌj+AE7HrT$`A٤ ,MJ Up@l7do+qŀ2hmTd5\Ӂˆ}V8:ͦAkה,&ϊ*A* H C)!GS?-@|; @+!;Iv^? q1z友Jf]܋KI dFg.(g>ށ'vbU>ks FP*On{YX8|^հ{L#}ોܺSp,,HV hDw;|\ JCT EsgCh;E=>oK?MлЄ@ɐo)1иIeӧyK_ Rzl'+ 5/xI.eKh483-@s^e7}:l.flxx\ϑ13U{# s3*ĘnP+*\Z0m `n x]Ul?;ہW+>>bK\w5HxCyҗ1CJ ޹< vkЧJAʢ{XG3G١Fps5/ߋJOIa[`)1t_B>5o_p_`aËuVݸR۟' `$!!~bݡ.uZ %/iB$Ű4ĺ|*9gMf2ܤvrLca1={CHj V]'LekDX5nj_1+-Uqx2vi0˱bbthËv_;;NڂNYn,BHIk-De`N`U{W8Bb+6Z^[|7(h7d8Ǎ(> wD<*Egv:E#IhlY5>;iTR)#gjuh+mZ31g_brz~KtJhOXFaz+ݮ,@qԊ orlDHVMȩw6θ挃9yK$5.qCRB{ϣr @_)5%bF^I λ0>s}{s[Udex 1ɀ\irBc$-fLվ]53 _M|Hb'E!=#ᱛܳ_$ag ] ]&/`2'Σy즙.beQG>O@὏,J|<,<КVAa15d @"v@ָXLU8#4GWi oSG]/^ve';RP,h>$v jMi94$NCUa@N:*K#`nO32ƪm.>|$V/>P;ÕA`uXc2mZ{=UƁcf88kxLƎVf|2ќֻN( q_ {R',VrK3S2y{tAIYlLO7Q'*.&Ď-Z U$EA!6d WLtDFNNRULeZb;=e(kMٱ%:_XDC{COӠW͖mI㔄eg"Я6.ezHL;?i߈`/{j g q-l4j91m}4:좴 *yNGjHSO^Լfũ[l'xMڞq\m^8<|(Q) |]Dn^d33r(Bga (l#2Թ{%w:e8._w¬D/)$P CZ(YtQv+t?!]V<5H<(exN5%Nz!ux)ǮUVy^$5o 72Z6""nO ߢ{wȦḾ(e5`l^TY%{8<?/i #PZo=EDA.9=yo-ws&N$|r{L,$#3ÉJbй9$;:L^ A{jٔ)KWIE_DrrYt+J)X&Q|dkt ϸ Bx HxeL/Jt WGcc*Be|ƫXΧ gBv*[uJU}J%WѠjA)͝9K"H `2:͘'z]Ldq.:5窼#͡Ɇ}9B %5UM5Sw]Ri=(z>13' 0f|*l%!uT~򁹍8I޹Sr4[l`y36of1/9-bw|Ga?o`y샅YG /Iٽ˘(<+L[+BJ}f%:Z\ LV\"'L p yTQoXtso4.I`˷t؇wO=*-wݦc |󫿌QwwTr%P7N$&S v0 "js=b!q}qEnJJ j[RgbbTZBm"oL?{*rHF$nMj 5H}ꐭ} P =L{Y'jdJՠ [yej*woB,/ X#Eu# jrRdq}W 5Xi`|?Qu @Q0N^ꭵŌؘ: ehDSTA,OXlT%F@ʼ|ta Zdo>!HdԁSy$BIYMw M['"IVP5D$.xAѢgl\ )P R/1AB,y#:}UvG g2kJ=:h4jf5>3X6bM/:%$ ~AVs%xǃG:\A%(DyN-ke'E?mè[,AM,K0_$%H0;mKN} lpos6d&iHSMȇE>-[V0}GFVVt"Q?h5hlSj[@}L#/RI tqu:+!R"5I*f ̟'"$-r:IgJDb&Wp`>8;?G2lK#ß܍G^[sNH|>$ Dq/N&J_(3ܜ{JB+qqh3~ں<#5\vqYJ<ڢ隟-6)ETG,>|b5E[Imolhxs,%_`ΰ*8폀&\\\ W]Eiϓ''|vSnmZ!7i Ox# j<ۙV=I~R{R4=5^ag{?RfG\/}`4b{}*Ę{ 請ӿ|ga?xH g꽵Xňe(RLs8NŴPEO>e uZG[R؎&8Q9/jdN?1ztPq˼2}<TxfH"ԑ7)fg^d+F)@tćK; jMmVx3SuX-*SG>H:#OKݤBǞ Kik⚻Qg'ڡsݿyt _ڕ>p*?T,1b*Eo3rGqd&PdOKM-ao+ΓTfm޴q9OrSxEYq4zzRy\ U2k9EŶ8_燁8gUdz_os.5VnK~4"ʐx6}VG;K-d<Z: ֿA .$7ݨV#4T[R9$+xc;*Sl_Y.]9%tIR;`:۽meYU7Q*0_;<jF_ ;77*e d^:k ` e?ZB0k-ZP]ϙd9L1$Am=4R%sb qiӫQ~lf* xZB4P r@t!@š=2aD.Gn |"%l D:%/Zv˛??q, 7\< [`cT`Ȳy4I8*zLnOhÊ vJ #=r\ح" Ƌ=U oMx4uG6o1Dmo1e+j0ԫv:$n+YY ޭ A2*|Sdb:𝅳(Q+~';O 1w%{l/a"k[ga0UlAMncp r֖(E_; +ȡ :,v*sZ]-Ӟ;8!zam^VGXFGA7B<7n[vf(;*.PY/>_WTի$r&V2a-hWк$jJWY'j u9zw\B#>p_B{v2t+%=S:A&k_c]v8}u>ӴV8Jʸ҈cn*!n8s2Q/3`r7Ȭ!񹛂g`XI#0cf*lVZ#eITH`MSRΐlxIgA51tr9TlqUɸ̾鳽[_CŘD xٴ^U]1]cƎSUnYW&vxàC@ >)j{)w/H|pȸ'u:P߷Zp˄9wFbA~Uέ&dfxbCɊ>twm oa<!24O8(@ِNEiBd] ޟK4cE XȈX> ȱGs QqO;THp /~UX݃b!="ij{B)u ;= y"}ZCv"=5bJHQkd`4 @#u[w6$f/Z1QREų³~KSd2CiON1M@xz'r/-iXe:"v<#oN]EfT53hRpΛ4q̵:ӷ%O[^EO];S^l5)n{3Li|WCN#|'NuI{2T_U}p.Jl@D&ezyE6‚Ԟ*3b3V^4K=}`cf pptjΓ z5ꕔy"}a!f`0JA3q:AqXAVkл .|POSX8XL$O-OwůG*"jW{/Վ>wt!وét/iӌiuqs/͘NS@])|0f ij[Y@׋PjM%OwvGŃhnKa׸T;FZ*~D¥l+߷g\ʏUxCB0`eQ}uqeM+rbr~!oVS~ 9?2ƩiXjH.camH:7qÕNHJyH8X􊺂t8؏.^m^QZG}cr.1gd 0.*sb O? tǁ砎T4?4?#>BC.Z>lɭ8]} FO[pr$ :X!m^cG@]F_yrϜ(d#;\rqejED {hjFiFLǷ- ʋ^Yx[ ::cqlVc_?{|`fǚB>?&ͨhe o dP{;db_MK]_EHRd:@B/ p.tb9kX$%V4Bj O=*Zk,DE@˼Kpjn`i u (FL,U*21-t50T%J@_/3i+9|\n:iw=3^ӟ<8~*![@; t4S1W9 -)P tO}N:D=ј\f*F;p2٤M>@j60dE'4$JUEP y01J|yD+17*$V穥KEi6nB-=h4R'WDï/B/p3ܝھ&ڟ6XmMNG!B/eqS_:"[ClxS߶!?zi:&H^DAꏭS!4 +6Fa&Er3-f NNw甕ԯ5?Xl p[~*gS0VW}͍3=%Л6:?`A  ~GFSJ\q*@9({ rF ft'565']a7ԁV~ey7g7~V>1u ߣ I̹kFhD I+ `NF|%+["U}щ L6=0=tmԣ= Cp9|ˌ+ˣU^Q{և 36_Z_6by vN氜S.<"3/4~[a_Rʡnķ+,@5OdМʳiZ&*ҏ'59UkTITC !Piɺ+d%o6B@\rOȕڜ#^+#_b7)R-o`en11$}?zgR|Ѓ;( I׌o͔%\(QYػ\>0IOU6ӣlhtҽId/ W:w,@RW&j;s L7#O{֐*=,?q*T;\0R3! R 1(u Fm~9I":k+ѵgihEک$ʲ"$YW'upEށ eN;=˰ .gw[5X #\:,SEwj6!EdÊXwwۀx~_삩̖4SG|U.&ȀFr>Uwrm7.e7QtY&H\" ~Æאnd(5OW#Ntx.T&Y\_h]9U ` ;okEi&a]+t` +˲Ȁkœ^ʴ(-I\z 6w`k`ڭi5ẓ 3qKML6"d-m{k2{VYCހuR'9a?Lno4?7ۑ.V>qD\m GiZa[CySͧv'd)HSFoI !x|3 ]q>DUV(;q8v[ lگı{f\t .˷r>#N^DOCU#3lx =_ +3#WS%sY" 0n= = ܎4)H woQ|ήE^mr;Yk{:CZ($q:F8ݴw%:KW!YkQ̣"d4߹ Ⴃ*~Ģ ;xͯWR[R3uY).eoݥigZ5_GgnأG7]^~@`D- \hn`ǮSy-&~%)?] cn@Pfsn?C].ٴW㿒0Fz%lW0vbmKdJ7qAPR/RAj۩G]$r21֟cY?ďWf^Ot9X&qJ?{hǧ6=|td TGVKpG7ՋhoAATeKf黎)k|DXak@ 㘸 Q@sOi5c?k\ "+xVCFea<tW\z.{:x+2MۮytmU-vvu;q@̭`{e.wxbnݍh~ٔ(.8};HuV]1?'|wjctdd(<͆}h (NMo4$ka^/_i}Cƚ܌x.Ҽދa62Ƞ""AHzIe0N%CѰB6Bx.Q`ԓvd3hkŊ,7=>'iz3;<=YAΈ͕|45}iK&~(,%Ad>MlV lk« d!ʸx[az ;ו L :.ʅ]%~ٺ43wk;4"wr3a[_#ufU$Jqz3:j[L:I]%: kGo&k<y19Ck\uæI[?'#ylG/24GA03* O2}v3ĹB1ٔEۗ"rhAҘF$dJrm]tZ :xfƻd/FIg64eKJp-..5:0e.jtj,x1]gZw,q1AkFTTV:G\l9DtZ|*k4n@=*8c[s` mD 0^y-2C0 oOPPEs惁WamZyK)N&qSXH`$—Lhz\1C*`15v lk o_;D %9{PQZKNhDuyZzSA;t52Xwf[njj| #+BH%ً32ÝR ޤ,WZCG{ޱ (GCI{ґMTG4XayfuYKcŏ1g/Vd wM< l.IS7"nkwV=ߩי#/WrýN0'}&Ŀ`0~D@Q!*6pz$*US:{hn2n'}B}3~9AøYSsy@" a7e4_ʗKgɐMXF^/9܊ Z&ߎKqbZU9p q+峐E@6vgNB0 .Y:olz%Лyk9 F2fd|GBMa`=A]q1Dn>[aǞfJT-q'~77!]h`OG@cҺ4A4,=Od!{ FY[tR ɩj838gXAl as$X9Z:: l̓%ɞZ`a2ua~TU gе;ffCe{lSRPkP=}(xϹ0\ytl"2a|7Hs_PGRg DXk(+ 6}`r$oݮlfPpV;6UaP fP2^trD,ah43ltS}xaUJ9KNӳ!l$ yy񺎱wֳ2[ŴVTvm҆)'=Xt b}ZQbtI\mޥBˈ?\N&(q=$p#_(˅ѕ~*'uMدՐ%S61opMG ё`α'U\ S o lYkK'Laؖ(R3dHO(?U[YLH\/'f1+RAl)mc?#8>zR߰]8_ǎY9W%w3"q=E(tff, ؋Ƽ4u! 贫T͖V2c.pͻRVB!d-h8|5ykv5^Gկs ;d&['+`aH"U$AcU$bOg6Ϡq^_`4bFivIxåTUyiHX|g~]Gs?b.T"'T nm[m L[ݝ|rqN5>RRxSdi GaِPt1 J>Քd3 N^yֵ c6M/%SIáW PFBcQhH'+`i$P6Wx8/R=!ŨRTTʔ z1yRYGdI&ZF^@?7 #oΠ슙&C\8u.l#CErJzC ʟpA4q5aw8˙~R( `,P) Y->{ݜaF{_ !\?0:VX[T ƴ@lc/+JmrlS\D-f:J#,~G&OJbc˜ֹ3JCm7z-qTFlB ߤOjq[ʎul~&"IAv?oIyeEm](^ a1A;ҒÄMv uNl`bD*"32-?pͪFo%V\U9&7i(m%gkn8]q3)v8Ν'olg( +׌\0#)P-M,2x6+r.oѯ<)KY&ޞV8m_;A)IuT'{PHfP:Z&&3YK1tKʡ b-H ^ G%Q#Jm屓^_`qcl; ~ |6TYEx>=7%y|R PaUF=WDL[nF _wi`c"]k|-i͜Z1QoH|6RVcEӝW*߻}.+g' `ᱢ1Bȫ$?2f$gM$\q(;XRw S, x1C߶;Ԁ\E3y8ێ}"mt1%DegV9'Ϻ7Q{9)t1B U/[H,Gв.}>"wyXj.zw=I37  /$+ "p1k.!4e =r i{ qz;k8ߏ~q\ ξLcCEBB]āZqkbP/>^&HRzMR|1^T T ~qp%@< E>i@q 2Fbj-@d"_QW*8zyv/r#Y[w`ٸ\N3CDq :35FAިm)D+Ĵ! ;k#pnm+[ըÔ&g28Ov5֓O0Q_"N"$72,A3acJϲ#Y0+=< ) )u 9R {iY*ǫ;5fT#癰z<Iup;ʃfTLlT {NѪa#0I.n3~8NHo*g{/XfbᲶ8ZLY'#RH:/ | xjNN^ҟ% j0XwΓM%Y"4kicKn>bb{RH j]s΍J! ~5O6694'H?`0x#3##p |b^F 83N^5qp$L(.f' FIfܲ~Wx(H]V#deKk-2fF/ĘbDf7=Ym:_>^ijYro&7~rPU{M:iX?mB:]$p,<~OLB%NX]}-4qk $XZfz5|A pAFb0jR]l⟴/~I)J+{|Bhw#l3Ot`. !A7kqգ$ƥhIPU2?4؈HNN.e)8 DVdjR'׺Rˁ o)O:XNԄqظ+8#jҰrÇQ[6qd'-FKn*c> x7^J~tpR.ě^_xogXl]+F++Ӫ/;8ۿ#I/ʹœ@JZ0t5rY 1\$ yD]@cn5N+ct!oiO/VNsؼr /bt/E,Բ)Y}EU1"IEm QVXt3 Dzϰn;thG!b6\9D^/E؟0.yckgbv0QSzM,u _*dx-`Nv6Rvkj\sH2Gq38hT'}LSflwvOhRjڏiOI ͨ4O xP") ɔl^@}\Up9Yߥ}L@-hc8W{S'3W3dA jU%VVDXJu\0!Rum rHv/)o_7%iVnK*a@374Y~ӏgʬ(Q{.8E#?2d#ŏSY"܌BR"Xyr1oO.W-YO$=?v-P\l=r6FzI 淵>ۓ!29=zIӾIWR%x˴SrHJ6x2M[ub*!2>G7%2N1qR?5 s":Iw/*ETM药[qMm) r'֋ĿW̜ʹVJ4XaEE2\epxUZCl~LQx|4xXfrƈ| #ɣdd1痯veRf5 5'e#e)#")H. m Fe1,I/Ħsީ|jP&,^Jp_ȼ{1YW6?o=/zwgb+Wկ<ý+Ȯn`K}F#F*Xh?\ٮ+\2 A *a M%,j~L=@r0WaQ0˵Qy/v <^amXL1AKTᖼ%G1XΗ22}%῏x)Q)LzSe?NkduSނ.?4fwJ/ݴ9 32 $xyF#Ys~Kq6j-Q}J(3M ? $! 9lc~y(U.v/ͥ,`O *߱b8۱s`:\L1A` zpK6 ANoI%YL J´\73*!y.Ru "&{|?[ βrC*O)? c Wб& \Sz` ;36d(ew'#N_AuS֔Q--pR3pJ)a-kT dUC4,-Ӓ'.ޯ|:mw1(x~&BFJ ?jN;? nOI=O  XG.ѯŠ,؃,?B(r#%a Ӻ7^)ώn+f캼9Q dהWUaLtNX6TϴXoJV !V>likRH0׊a,lwUjb\z+%]/oTlW4R9x܁Z7`K0Fr.XomjW\ǵ&*KT,{&gdT w?{OU;J |iwhLq6xn/%Ϙ0$zdeJˉ,?n(]xUFn"=~1I s<`m]gLެmT/Eޣf⥢='LJ˿.0ȫy}cRݱY9(:cYQ̫R@\Ҧ 9|vV[G) 9ʽT"A8:GPEAHm/¹F2q:*ŵ{L!tZ #I o_c]x&Z*YB_H{9eO@Endmb s4+VSk]BkS(Qmܛz5{ີ$bJTOh ?\`[Msb-7@| =`oV|"kWt"! 8I *8?P82/+a`+0`tmy>Oc6++E1&Í<'m2Eg풶ؤfMcT,DPk2 *џCC4Y3 '$m6S@Nmf LMB`𷏷׸&%{ ka?JM]q?/y5 gz9x~48RS_egnKRȪqe]x rtDV,pU\k'~G&sKM[wbȿJ=až_v"`uȿrJ[LFܿIdŤ-Q@=$Pa]DMv%wu KgL/9PQ>`eV SNqb$n) H6ma:FCA{ :zʜ!vqGLJے57㽽U ?3CA:%>[ʳ W*|@2]а8?2:؋4w׫Y<,T\vaR&0}Hzֿ# 9d~3%pᷨ1&_͖W}D {eKo}vvvsQŮu'u<ƞX u0Es $2/^*4+Ix/Tـ.+8˝@+#zOD%T+x3KKIy-ЮRva8;O0h?܄Oj*nS$1~8ɓ䴹WJ /〲lB;׹J$TA3W;`iR*у<hq;LH7Aw>(1 }&nED,MM?rh272J#˴/,u~L  pic=#Yq!W@ɹ>e-!<^X cAC0л.F@ Iw}ttm#ƍL7^&`):-8L@aNb7R2X L PJKKkR_B3V?|8 ].>qV(J;,Yo8riM*P)Dg^m\,u×o_~pۖ~^)6nt =apoC އ2rtNH;m\Aq"M"}Woz8`6;Hp4hr:@Y Tc 9pLqDGvJ ~Tv Dݜ I28WNhu?.Rڼ.X< TeQv"3wGPS(93ڏfBFjR$pqk!lI" BڔL1eǧ |Ewc9pm}c2Q zY`I8 1B[1S0U\)dyA%l@A 65 &^'37.1bq= 1v1b'Q<1;gMjVq1*ʡa`c=-3墲2x陆'zC[vb[׼/Q)"A#oQ RaIh p Ťzik..&3_h@r6Bв:١G͝!D=#xvU JxP˰a]|lA*gš4yϜ)?c^ܳ OoZ:X^}AWG#t:FMk}Ar+:/*ta'm2rO>ON܎G/4̬XQا*#rD@D1{n 渪 aK/.m<pv]5oLaH!5VӨYHp^ @7usk*&^2^+b0f$ El;YE>X%u:yp[U?lwRrHhle7$s\o R! h99%E[F,lY{JAyW2y[pi.闑Wf>9Bv;Сw$xׇ4n~gxܷ< `%')P^5A-Gpk6 6&VkEkVU`vP6Ǧ{7('6\3 6\vZ)n&o'S1FB7,I8zF!b x  -N5j:0^cl-_o5*0!6\#/hRJrgʿx ru&y?g]ml^ng+غlb%jHi"8jevd8 ry<:`bjBfBbvĒq{lAz/lp~la/ X2í{]ޫ:zCnJD ,9ZtWw[~1="zXM. _?y͆Kj9Bq/KU4>U#c#U:r`HsUo^SS:&7!yNof\W 5FYd%ckP21+\Tgencn% v9zf? #n"DH@?~4=7ZşI01~S E.8羙Qy̠-&"俻AaT(y6؊{`>iO=@7Wgl^*ϡp?iMxM(&a H(,LTh  Jݔ>0$Ds"C77[N-oh2. }R?xgVrѲpJƊJ@bq4K uOr  )S9' 2;Opw,_uRS yy[L:j"yp{"S< ` 歐,QB=;Y<H%wvaHޅ5 e-"*B9GzFDvz=T,ttlw9h4*0SagUEHꕑaJ팺J*Sg@t;`#ZcK{pՔa Y;lBܷ$f6SD `UOD y@k@:(*Ɔrc%>6U8e -m-r-0pU&-i2|K;+ =V#oktVf_t؄lplr8/L#9\V2I/e?<.zY?Ng ޒ ^;w94DWS):{LOofI{4/Q\.SEtN}߃ Qے\dX(vzh*x5Tk$}~~*2y4:kyIؤHrz`Jl?~϶mYWo#GYtMN 'yhb\RbAۈ^/*ģ)G7EZv>ia "Wڥ,%$nf*03iq)zqEVҾb2nddMº6ciUCOD-Yu+BJ605=Zœ&MFEDG57;C(ξu1z,Ősn/ypL#Z먎6uY##mv}KbH ;# bjfH{P(ݞV2͙Rhb/eH]^ Bb)(ȺQZNٚ_l9R]l)K#: BADז0_@sR>D"]g֐AړM3JQh6PJ^Q(u0bKaQSS?E,egܐӱq鳓>_а7{;99Ӟg9).opG'h7Ml(?"EG1r˭<6'dܠZbư"/WG$0\H`Í4_&\(QԿ\ L Yt$q1%fRE>Ң'.a>>-H|1G_HH]=Y#]A}Nxa~->*_ \юY>Yb#dFfiZbi{4Cߦ0P^DG]%(V'5EY Z[j ])hw8JnJ^TęMq+~B Gr#[ ?8YBOQOnvx4Sσfi@5fqp 2z bla᫈LZ9~:ain](* b~]I8n;kIWqp_UZYJܹ {*< XDx! . >WKn6_PP6+ʓ8+Ύ2#\4|k#3ʼn|bWwxTN{˾rЇw&k?|nxz^tj1#u9j~)ǵHGqxB^v!Բ. /p;,WEϢސBw5Æw/ԒM0ݡ{1o邅R$Y5ؒup+;fq_VfW }gA q9 d4\uTtۼ /fęcbE@oK-~:A()EP Im"ˣ($.;lA"b, Y{IR/wW-TI [SX|VO+nJZL|%WcdU*QZj@;x+_ bwmRt3u6aʠo#/ rfI+p<, nL'*t?PKz*8͂@x/٦RFz禀9/#6{l: D6ϭy7u};,{gnI+S{T6ѝDAE ,X:P^bxӁZA$K6ܼUtQ\f|=zaYԦ;>ǿ{Й'+%aޠ, bN di) lx,) i?H`8/)qt432gW8c9*F5S.s+_\rgLցixK[(+,3ꊈNU!|ڭv3<>՘]#2fi}#b{yLXqJ*>tq{pò~/-&wP Ʉ0_$t]żU s dT,FZQn$v 7|g3@ >cf*_5} t!-c eX`]Y@uY GDz-щm m VKTI6SfgZA~. Yٽ+~QyŜ߄36bh1Naf蓼ƥ7AIgN@p ʤ]O b ,ˢ6 otAZ|ߕFn:+v-@S0/zŽ#Ф) yzHTа0:YWt;!D#[-hf`/xyۇĨ%JEx֜ZҤ6옅P [L`QHRN PSHYm ^`x|s9!1qz~2 K\Ǧ?P']ߩ-~X]DwFMxu/}Tۦ_\ CA+D.fJ % ]kLĞ7݇܁(%lx6%mcfURB0;W(E/06pJ@arx5| raQpy@$W?ne4zt >#Vu!Q ld?f\j Æ7 4e8ES d 6Β˫eP<U ۧnrd}2 B. 9eI!A"Bz(eEMCq T/l8q{aj/fxjoEΊDg<7j6cDd3!|e\cNqaAx0#"y[a3vM3T鹏ݮNoaҋL\6`Q0(^SoZˢ?:e'pʀ'49LSt18|6jxc\ȸ@ 94oH^$9{GͰxaG =/|%œx35 : 2lX˲UkRgP-$pW&Py.DX9Ԧncbrg /%+x C+a] S6k06ot N%;2Df8iܷc5 XkNIbqHG̥1 g\^]]uLin}Xͣ3A/,-WZ| ֥N!&ȋ,j(rfa<(Օ$zXۑ}a<{Xo&f8 5Wfпw'(0(NΦd:lIt`Nq*4U5`f,'9_a*IRŏWT%Nm/A$r2o*1Z14R-Įao?ir-@_OiF6bP1O Fk ʮW/@Jb_,[>IݭɵɎ,W+om;MBYwxzOn5Lw/aF8n@Wے.fbe%rW%?b0Np^KmK(|R4^QU/sa߬^UP/Bώ ׼FXV1Fj( fmYf^SJ,:EGa~",]y,>W,FjM `^'a9cdʛO}-}` OMU"Rދc䧬\+D,4:H#s ?wF5%C^KЦouǑS/?Mz`E%R`ԭn "ʪ,E#^He7gۭW[!$x+mF05ebPꖯsޏ*"Ee}.ZpfcL0q+NR"凟0/o#]"D C ppnO&5uS+$88eó}-!XL;{ĝ!d6t,3yJZY:)pV @`$frLxdd9Qδ~.u:Ss@Yu }ƌ-~vqd3}˕EO,d*\O/Q R7z7-@SkZ7+OUu%5LyDzhKzMs9kUh|`]xh @ )B3/*(.vx:@Je!. e*q%Za\?=[ N3{A9 \5A0vH[;XOTE28y3zC,dz*ѝ#xBgDEˬ]./GE!Qv[<T I&=Л2&Wwi,\]X]6fz.PQ3z  )QyKx{o1\q,hƐS]AI,1gF5ŃB u:Vc/)KfJ6`2'Yr2oOT ̅6(?#>8` ajԛ>u.b}:z9gp5/qc0Iە䞹Oj.l2xlCVG*֬l07ԨLSS|p@D6Wf?:)m䠆Mdo NcmjvQ7FUlCo8\9cVNC'O?WrVi@-OIo|-:Aܮ=f  KJiw )]1PpzkaJ++` :RYV5,jdtGM`l.kL!t-1}8w%lHȯ7˹AjsWt扊:YW%΂ݜ]s-:a C`7~*O!Nd=ӑoԟ {i&d+NټsA'F'Dh&"c,|j޾m](вM껻d /ׂ\jWNvfQd⻭O1sMpl=#~a<[3,j}r-ܟu-2SaXwT d{{du,};(>w- 9Xitk|9_KeaA bj.xgUShw:I/z/ P)EA1:~95q}5 @.?ieCPfIʩDD%hkd<؍늽JKʀ[qcfe484``mXT$3sk 7_[=t30Ӵ~Fq5EZ]S '}x=RUE ]}V+zx߰ͳ#_9Eq0Bu^;&v Hd@MU5=E>nK &;1FѶebp>lS0\+Ą A`hǸN )n j(!.rpDq2c\KuɏWI@ѽӾ8T8$V@위PzDk伇Y|DQBf.R8%Pʳ-)1So:Z)I?+*EcXZn ~pRBBD9C\JDu!Ğ5",HIpGU_0Kmқxm@R؜1QhrV9٪yS뛂 }o_0̇\$qq` 'Yۍgcج\t1D- eE[}*zbZ]v|mao:I,5)۷F8 EAoE&M9`ɩG|)JYUh+^*e} #4 党7Or{jiUn^$ JзyZ4 貞Ȃ7 b+KfEGib;N 2O^zJ}d^,d&4KOٻJȤkin:+Bפm?0ij]I Mց5 q[+w@o_;Q5RHvfA Mt1Ñ'YbbR|i2,#@+]ߩ5IlmRA#fDi xY([&\a7l5P6 /$)8ՠ(様mq=}#j~YN_s^MI++^'@\gG,K;kRws\1>/ +1.PLBSs|vKc4^cɽgܙHŪ?o^8Hs 3PB/'1L㿋005?)ȯڪ3DFT 5{mk5{;̆[յ9J(p}c;Hؙ+~<8g}[i"aͿG%fN*ʹ'ΐ^oiF~?"7v;swjqʅH,=S55õe Um)4Dtҭa4cC9ʞwrǼA,w"~]@ցSg[_MƒXd[DYfۼJHAn9*}^^&3޼˺Ԗ1jOH˄~VPRS|ca?B>~p8~9I} !J+ V,@ _5W^]*לHT3ČD$mϢH7.G?ӞYܞ,RC%E̔ϊ2\͈j+ePD3RUj0Ênm3e7E+vu;( 3XM[ 4 A259}BlmոZP5j01CbT/~706.>% OraeՁQHs`Uױp.jx6+њyvV%-.ם?HIhҖؘ^r] wG\'7·R`MlJye\XmD:-4<7fq7KU\}L'=zY\<_F[DWG%ip@:wpKšI8QGkGڄ ճO7\6d;`/XUԻP6Yy H'$!iၨL-'hFfn1tVg@9$$'Ċ>gdqxoѫ}ڨ*Cx΢v)=JmǓa 3B+?Rt qa旁׺ IZ_[A{Xl72 : ][mj0"]*咶3=h`AvXhPE+I}(#)Ɉ6Xθ+9(vyԕD?rn&h4QidF3? m~duIR&Pe^24g{`9H vGna418fmz-x,i*u2x$` |Ÿ?  jfFQǫK[u rw"3*YTkꙨwdQDdr6Fv~?'{Wn1] +Dld;FA#6)BR@Af?z` b1iy^?stX6Syj_87Ϩ&>Wo0?ՌY[jҜ9#LZE7it좃4T:O"#y9V/t*a.fi/q'4]x[d(X)[o^5]( L朂[›:՘qt{ܝj@ @i[$Z)ҩ]bG(ۿơ=uqﶟncwT׻@RJʀM S&j/%gb#WHcOٲ!3ʪ'R =d8A3ҹ;ڜ%%݉QE_k8)A [p|?Qͣ$hƏNJϗ=~s .q}Ji+M*Sm? -0;νx೸SC $$iz1QC^ kh'Gn.;>¡aL-&qEKuE;_{2 &;LC0ݿs($m{m9\X-HxRQlN?-_*힪1GOܿb\2A B ыb_s^;YE~̋czX(6AF\)邤ÑGYf*xN˶`^lzblvg1 e/+BӴ][q z%Q. J^ 9e|RHky~X-PXdDsx3lϢʂ>oVB+c !СZ~h2ºC%''/5i9xa} (S RImbe_b!C]s_OFLrp^ⴄunr=VL)y*yw~ɢcV:~'4`L6s4jm!Y=T׺я2ZւX|Þ"M՘𛆹jv}w"diƬG ;cE*ҒLg7֔CC0KaT_' i 8{ǻz /laz4iqjPOu˳6=&k<Ԍui+Kb~ o  m5hV~S2&MLCom4cY"|>9W15O ZշL}/TebV|tG0o$x?% OJIIXp x" n]cbbVνh]qj,Dǚ=E(+e*@+ShN 06,k\[kx^UJ [gxM{seD"S+^ݨ񵓶+# ^}&o¼x/ zh;2vNRbrLakL>2߃=\ $rjWI;|u/r0F,M*Kaj A36Zrf±]2e3+]}`us|'YG! opN(J;TYA(ePg+RhzOuK'O]a P"GAISb%Z?&$|^CQmz/b" bBTI6ULIxVfڵ6=$we@ٔ#kFQ@z.\o: z܅t2~lONeL1= fҫr.a>O rиz(ۣn @yLZ`a$M6Kaq DLd6ԓ1'&| on;5gR! J~}4u5/!6S'Mp6XE2\8}GuMhoDTK-R(Z[2f8}dĎFc)HKC+ÖYS~3gJ e }dUKfFmYFÏ(g?uLSgW:4dZUŰ:d٫$yXxM*blqۿvMn@~뎱xqpghCscR^c-1)C'嚣wOt4.Qi$RF/MU ㌜o'm2L^5`%~z(ixPg:z <,WdMLQ+\wπ[Rri++gT3<R6T7~ܶmNȀ[DW _怦8UA߂M}.+$fޮ*͸ Szu 5k !4>T4slٚF}J&(!`1@45ך-y vn Rk|V2vP+K>{&FVšӧW= ?OftBDzb`7e2VqW~ R~RwT?.q`嫦o66g˩9; LY`7i6f-^L-aNUF[1T:kMÀtRK0!k䉒Q: L{>rsҖ=c67;*](wmAN$QQGid'v9C N]=!%F#Ʌ`o<VGD05'Si4ER,J=>_ m_w tnŞ+UL UIcALΗq${..E P(@ YQ*s$ #|.z /c:cڜ#D2@.܎H'r7N'èD#WP(ޣ/z7JtEC~%@lzA{s328}Oj1+ˇ#@:lOm* T>NCdd 9iZ9IcFHkֲ*1r+k1i%& bY0r6 T[w× ¨g0ЩMi[6Cx a'6JeӚϞw(C$z# }B~{Kc[Gᶥ~<֮QELРcG[U闿4Ф-* H^ `c6Wgm"'R~vqAch?r`xnjfB#Ex3f΋D/b ҹ26% i؟ Bଂ z+%j@o3@%~u9 ۮ:&>LWn0s݇kwUKʺX'B1JA0 IISoo!)9Bوu_笱BAC1p(aZr ¤;+AfJKMU8Ǜm\XG×Aե2,CA1NɥK_Rj26ҵ8LmҢM] Y0#G6C ~@`.1CȲT8dN9KJ?јvdyci*j ($(fi LԺ0S _΁ ҙSG*A@a2aΑ']vf q8(.Kgn~C_cCك#6SkJXe}T7l"AҰZyz7T%rZC8 !f)&r4њ\d*`rO^>|4%ν:u٥2:G9Ua\qs[dx"p@4YgP2bFfZw9} LA6j88eBS/koH+ `׿k@ݢ[eOjȐOLSFY9`HqW }a F Ց̐3RA>JV^"tKLLvŕO2@wH)aTP#toOɄ_DCQ 6ŜRڜCTL(a|kRzV9z6:DFkeMp(j o@a$8al؝1zVpv|n6xwrmAix F”#4Сq+zE%|1Ä=;ϷbThn]#u*GZĖdFf/8 yu(i\!&<l[xdp0ι *!X+kܡX:yNE gOOO&c ZtZ_m&]1OjX撋4iKRVhRj0:}E/ 3ORGt؍Dš`Uq59)I6lu։Oz-/U >?S2TJ6j)il۱K,4c^+ӯ: }p'~j"Mr-l~<2& `5&;zN=ʫPu?ҚW'6aK[`x§1ڀr_nxr_}¢Zq/Qf nϥF[Xg PFƯfOC4u1 &_F k2ͼ Y[HYƌ('%.ӆEl+sN" À3c0ZCVtYm4DՄЅ0qT\l[`0fRn9OW}'}?y9>\:.&*`id >Y1M.kXllt_.A?os1@>u.iX{ vkx9XW+铳&$ُ5>ˊ m-\)xKLm]J Zkʷ݄8ٔ=o;egǕ6.7EXxOG$TtiSQHu[E4Ҭ*3 Rk7b$XՅ^һ~dV;LpV-?zH,㿢ݣŽvW(Ζb~/®)Jn*Gk1Lv&/z xu@bOt Ot;Rr42Xx2M;1}=6-30DϬv]#,0d-92i/*, ħf R'BEc_ 7LٶnK83]9N8+JQ$3{'ѦǵN}پ@g(.rJSu^Ʊ酿!9јϊ#pLJ+9e!ni-N;mi~ۼ*znj@w|jH]^^iBHVE)8%k%WWl 5IRo0qCsRdfO'VdE {?<DThsK!ڦWBXP T]_>MFF4|T&ttdzq{'@.58 S}Pn,/+]ӣG##7N}gN9C4#8; xE4PB-lD\I!CEEhKgLTyʾm7@ ZRݣE??v!7<00QW@ 7m'jay"Oj#ΌZ{ǼMcT9報؊G4#2Jf:PvyG[~ :gHD_DZw<'SjV-}{oM᯿rF1Wjq(6֏;u-b^k;#[q]9&}Ya<*_ :N6Kʊݫ~%rBݤj ٠+>kro򲹁+LRڊ7?8|4\xJA^?iFu@98 +p9CgM" аkWI%\C?UL*BEy X{;ɠkhҘփm2FgAR}Wd>w*zDG0eMp&s|/ʨ*B}c_+Dׅ ߈_10ݻU^fYRmF /Emᐲ9 r8~Kp$HM`\Hꉺ 翤Lt%kMTSYǑPzhAG{ O,g>(DcLbctb1eOX[\._Q6@|mlTѩPҦr_\=nSs2o˿sVbb.Lx *;ៗq)?CIt; OMd{z6ܱ*+n"1z~HFsA-hR 3VZ2,bsV_-+ ɧmB;+]eptuDm[²}b߫ " Q>-N]d#rwvhV҅{,D[T/]ShyU ֯Ӆ X+.Q"3ikÅްG1ߏIO %/+UM#zjS2e|+$LKOTTy[H n+rQFs6&pq(۽!Fe5= Jt=Lس?/ܛ9/ʦ2P{اYls}ʕ 3oK52^y>N?‚n˺fj,WܹUѽ?bBo"ecQR\m [JrU)(br ѣ/ +A5[LI o'nkTij \ @SpbK$9 O:Fϡ/Ґ>gp!Dbk1 76LY-ik'<D\zıNRYH2Jo-6/VH\ HXU迂e < _%2D3b- Շy[+0Wsŋ$ NR+ eL|6"^gK3g9 լfPt͐hҢ}e 6?ޣZTy]zQG71/7QլOj,|Ը+ ;GUvݰ)45 ga^An@x<"#U? ؟Sx8R`^ dz2=s|IVԼbsp?Uq$v̂&GRxE 2c)[# Jߤy.B:uռEFq"RS lW<8\\} .EEFtX:{vBJ@XNV?a ڱi|2C~UV.%,#cSZlf~ubKu1͑4>B@z 9]gAeC~i]J3e): ky$v!%/T pnxC'ieO+!1~;fP}we)6ÿg 7Y~ؑt`v.w->@O K13[b,k//K->ʜCBVį Z[Pؓ8mx2 oHם/4po \)}kP4^JRbxluGS'Au!8y/;_DbDAEy|5UBYBBv4Щ7Ígu 8椦“KwN 5 q\I,FK>oZەiBU f8S72QnC!зBx|:bP'̠Ĵ*~V< WƲ޷}Keix͑Gʏ__h3q'=f=U4$@[łߦS{wh7jklskZ9_dx/ pb6r_6g#&\7"Ճ~>cJpiUba H&Q| ãk k1g7͎˳( qu4!=z.G×(;t+Pk8ఐȸ'O~U#5 ( Eb:X~ ƛl>`sy:%JB8:ڕtr8 YبsM67<[Ԉ9N)?W-ň5조nN԰S] аBKL%Ɲ]ƫOwGa mS:[VZ>?PYyw|t20o|Uh7wU%#B̖ .4e?35@ޞK]Q i/sN&`OtGQ[dO-1C#9 JxK)]`+I lvM`Шx\Wwv(B +qԄɢPdZS0H{kI W\Ƌ[i@#[Ok~nQ')Us i%{<51$cwMMi@ /dqsT*̼t2=̮!VZ} Ϭ#kb9ߍt4WSAaԁm[솺~@J/VPe8n!]Z2^ܳrj삙 Pi,tOrBYQpc=rMza*)+3j"sobvZ) ]_ADŽ5.vm'֊?PH9%п 4}LS^,ոnm.0ki?2.,UJa-O4}~wi 0d! @/8oK;ܕ=q#+L;-W$8W~OH 0ZIb0.u;[PG{ް4͞&"$ ՕlJCw!f|u&_:b#s"O@K?-o/wd,?ԑ B$} +Vޠ `'L.`z4ePșK#QT+ "> ý$2wWFկV:ER KDrXIenR拏- DJV_e3ͽe K|&Z"Yoz ԉxsމtk&GEcyЊ~ԊhCz6 :\{y)$+`{^Y[ G2@ه }F2Ɋۓ+/s#P}5U{VDeGuK wt,8'P6J!Td1S3v~̺%&jӧjf]@yno0>Ez4<\YuY)GblMQ!P=h(sߕ>ٝc<҇^60g~ƻp!iD83z!]>_|StVz|ROfһ߹:Z=Yq 3>KF&^u"DjeZp|;fĴo%>T^#@mW^/3wIT/1/O;#8fj(2ej^Su03ԣS9VG~IWn_:yG6 9Ͻ7|#B )Oִߓjy>U;ҡVpt2cc&`K?P]hFZ,YkCgFdaUU&q>Bmeg!F':D3̂K>ʉיaf#z/,a<ł[^nq. @G VMy[3 ck*>UK=1K-4#йqT ޏd蕁,˶<0%w9rHf#&%J^PE7r\4 ytt6RX-3H׭.MKG򠂦yC#q'rW))|T]^_3ʧ,3+0"F k@ ]UEܒBj1Qm m:I 0_nn 7 H@ 'JA6\s Rx mSrܣ@'w%Q:4_%r8+ c-n<~Xd4`hV y?p/iQV2tEHkk^.: ,{'`?}% œfzl!d8yX?%ǜ:ڋ* eTh[iWnIQBGb, 5c<ƘARhluXCtHr"nuHcv:O DT8N_S=DNIօ7t`qy׷*ra/J<l\Zƪcb]{5ώUj-tL}/36zL,)hZEB*t_ lp蛭74mS V O⇳z{GG}Os84AbfP-r{X2/J}9냃aOHф]1#2zm"3zpO&НdXNda'i5CD2!}bԜr%eSQT,Y|N_BZid9B;A ~*gwgyO=dzy^fK}Ԧ^D<[VG#OQMm npHogwH"B~xh>yEƒk򲿱(8?ےlNE̦^~ }ue4vg0P@}Ϸz@T 5rێk߭S:w |dOa4>>Phf g" NO o5` |'[+0>9nmQ{o{"v$ZB~T.4{ 8UN `cZ/7U4>Š׋.z$W+&LyUL~.rIY䣱g߈w<`'))4X=է-Ћ`Q\Ԋ #=v:~rxM y`0$ w-V5qo:Ϭ2ӹߑZ]0>X*ZOL^\1'Uts9w i9^M5k='^d`W>'k[dV5bS.jSiͼ^Rie-&Pro̟chn4n:K?+A']&;B1\Ip+AuBQ #dL RY=:Q-+6PGn^m1xyqƒN!KM7Q$rݫu Qm폛e9G[w Ȉ:cv&0/{]t{Z"є]8_m9hZJrvޱS3Ax 1rǒxL\V8Gb~B}-#⒊#> ! +  z|)m#R"1?6r@i9hH, x'b@³4B9rH+uW {(Jz<>Ak%&,OQ35DM@@LQ*Yc~6jGAfu&r;G&x:,zoMtN`FSpHBa/H侇S( Gn#=OūKYɟ !ߌ}#+WyBd; G̦GP[7OEa3 SqP: ̉PtDoZ'lAdXz/0$裥gY=4v/=O#vp-*޽"T=g}`9=c lrV4]BLUX&4vO[1w6vto fLH#%@WY?Q=(42ߩk?񒓬wY2XBZ%(OK{`A(b`?'e @d+o)P fw [r%` 2xAI<GNYGPZt^wu#ѤT6*\L!u9o $ۮAg|9w\RhW @T5 ?5d+|@GBksZdlmJL!F4eFde&=TjF2sI)'.CJ?W\\fH}TR/>Voe}>${';)zqזqj%L+4J~ql\sC[#S|EAy<#i~!+B0hzEӼIIaީ}%jC(~KeF&i/ѝ|Q|i8OMX?^2Ie_#/ 9.P5F|| ٻ>,J\%Bӌoc-_90Xb,wbٺ˜UBTbZ *QjwOzS9ޙOq%v{Caۤkl> I׵1뜥IُTY\F=әPEgSq0F)QͰ7Q/3ʋ:A@?o i䟐N>jcT~0axbPݝ(6P{\[ndq+$j{%M.Fxl؈ =+ =23҈0/S^n@qd9Rc<:~k"bnR8 p&NA˄Q6߅NJ183'JBc=?huxT>|yޑ,ғqCqRwjm7iQ:'|d(Yć(^O*'Zwtj@]v(^"[>l~VNz@Cc~m}sU c`> (vӦ~?݈nQW@J]Le)z(#(@q3R9IDsi65`dQ{A4X*K7]D=P!yQs|L|FuP6 ǖ"nAB1Zm *$|de "Z4g%!E/)'a/'@8 ek-j/$-u..dkoze eB}VEnq U2ގ#m F`IdE`P2v JiGwT~I5\xH7Oo$Q{7@k`&rxYގêb~}sY<ťhTQO -L՜K Fli0U=QYP8Y%JNE46~`+|t}N^2! X\C_rh!B1Rf^CŴDC֕}N{ܲI߿;.E̸~͗=.IrͶ̟,Y>_fB p%tAJIQ~Kز}>4W-5YrKcKru9{e/˅TA=+C{~cq_.t>ȃpHFM> 0Ai>l\ 7 L"KCgK\ 9xcGNH_{U#}['XxL&wO'ܓsY8Wťl~ ՗S*!347k\cSܚtdL1:%(_cPTɉ\c^ Zl⨁E־nIn(L(uK3c>t8toMzK( AE_?A`Z,i/(Z:K^TY/ku}9v,x/Ģqtz&'1tm+')c|"tXY__xaݶ_5\n .fkgn&~oK}?{Ol_WY}̩5JZ7 ?CfEmDvWkhNxs*)D]jrdjzEI~PE_a1X,:% ~Q5vyhl21wkwc8a+URhWvYqDⴷp}84Ʀ3B1e)gͱ> sC8s` zWnr<ArzB ;]fٞ%&eoMeW' OIh?J_thm;Q5Wtz,:R*hH&fGk'S*ٍƓKagyZ M뒚2b7J5R&O,X<\Ve!|&7DxOh}^詸@S 90%#*.\,݁_Tn3U7:404|[q=M^Gud[}\6Po'u?l/4,GȺn&sZ},L!$?DV'NzH]2YNu?9KC T`$Og5i0c(O&Em F2+&r5_>ioN s@ L BfUbWC` !vK!JHhƉy% [} D3."|\=xLyr,3H9Laph,",c5#׎'}jwBCCu?q8ܚ\?֫|Lw) "g &jn;0yE]_3'l0h`q ?v I"u=kȪHixCh6d1@S0XNeSH,uc- %aeՂV2=ZUMOUύ'$O?1ZP@mxmGcE1'+@0y[*u{n8 :n^z1f> 19n};Ǵ<&yXДO|BYwnp69BD Y?\AF8Ag5n+ .WafBiZIő!^.Lp詚5S{{;C!Ή5@΢sK\ʭG&|5=[2~F.ԗKE3aVC$L/mm+'<Βi  !TWG?O_oNZCY˘Ig3ARSaΘ:5lUЕ;(rG*h[)\ .mbr+M79 (N\OcN܈ǨQN:5 q| tQk:*S)h8)wVVaCdYR Zokcš(0L~MHDǩe8V2L][aH8mY<5 HR(JVhۻ.! =87fJWda8(*f|/@!fn^E 7Z5eWX.fA$I/,,M* lkgf6EiI59!SoLFc 쀤NmYs+vn#(B yhn།B?}7:Fam\GcP#zj..aߟׅAW Žl{O"r2cr&5s3 XNT *"H4Oխ!w1jn&(e3"?"W%ʏXIwH>?d[GdAU7xsI0"WjM_fv%:nTwnk0&(`3To?;bv3$%'LD*8$c/8`/8*uHР4Fj;mL+ji C{}@Ce70MktBQzaQr^b'@(?E~OhVTOi珘7A ^NyDYU#*5sd/)}!YeWafҹ& cZ?M.v=Ef@혚PAm,lGa}8Gnp`ӟ&,X>#{Us?b'R7WdVRG\4r4 V\$z~kuϖ[mǷ 3/|Na94q/s T``<I+-LNT[ů>w&E$@A&hK'IvL"ѱ14*+q璤tVVM+іL r?WN_m%00Bhj #n) aQ:+ȫs{4O-ݭ S?υn_AAc!`R|E$I9ݷ( TwQAke&1"e0շf,CL\U,vIB270~pդB&Mup{\b 7mS{=a?q%h-D2L Ys<`(Չ߲. ޮaĈ:(ېCD.s:tl|?Pm1Kpn3y7ZJrwub@i?d'|$Y40_{6fF (T4ay{cWYzPa X1?>Fc ZD?}oD\ApPrNi9=ZW_?pQ_@]ueTg89ƤqUv3sʷ|j .!,X*{z=s[Nxif^+mM`w_?҅22]@^@s\.o9gXI|`HUfoM {Ֆh,6 YSi=ߛyAX[`/X1WjSUb.1XCmPʳrb*Pry \zn pΓKkqY|ڳWn.g F݉`G=ԖT9=:uY__MⅤ)}(^+ v{ŀ~2xRgؿm`8JyQ@B2ZVmy8Y:F=lڽ/Z}ݾ? O=M+M*G5-#+HP;}p::%̸ȰX<xpst7FV^5`oOwÔj7A<ͥ82f< d_o̸SbT  QOXRNrlA;U[a׷>zE&XzGhJi(==13MNct@{Jԧ>ǟr~VҀabo c`[ZV/n{CbX@6hBgi'ҿxfneߥk Rl+Oa2V!e)1A# $JU}<#Q -UHKI\w C6^CъƹZOS Qj3$Z@]|tvS|k6bIuFVK:}>*PS^4jD"͹!_ G-)Cqb1[jO906ޠN}s2k OFĥ*RL3Gu(H9” 8`d, |3bJl^'1"+eu GSV`^q H"8s(7 ~u)$HAwɒ 3oVߔ{?\1+:qYZ T4kEB/"Vk(_sFhj mjcp9E#ـ@ l@/*b%\杙 :``l2tD|Y,a/ts7>ll;a?ퟩFH֖E'0jۜn[?IE*Di,~'4't c2]DA4v}dž0.beɮ릓oVCo`fA,^c|#6x-8_3[6XF'&6ʐ)r쀵ػ3Z [0yf!TZ_ls.˳& otxcdfo/%qŤ9M&PFȌpZ׳`Om[kEx v"B ^7uH J#I޻] R;)C``$=N t=WW$°8&r*Y\%wj3 :NJۛx ?(/枯v@ ўBh{fDBhϟ@:*؍_vpoi^u&vc|C 2c.1GO+0&:B+聲P[zwoFmmكBq7 :<[TvZK 7B wߡa { 5&=1|)ȗY¼lL)ѣ;|N!19)@5R#ɿ:O{ hDטK}:dH VDُ+I vװyAÔzw'@+=2Ԉ :&R=R4Ī%߇-)d[Mo͵ +ʽbf .~.]v~K~-rp,%{Ad'KeR& 9|@mS@(#2y]CFWg!DӸzOm3G~O/? cic C=",WM̖mF|vk|B4A0 ͠c(wԟ\&dT55ԕYdp^qMm$dA0 BEQjγZ$zE0Ku=L;h3 o/ky쳷ܠ}tNtv] hj4P7`YVM U6tz wG}<)#u^hyr,TK^%?*zgIU*:A7׺ #'qJس|~3_M81Pmg "fan{Wl 7Osy§zj|q婱-JY"d!0IT!Ӛ\O&e˿#SawՅ"] P\Ir,}8\vFt&X,x$DߐF"cpL}kӚcu6湿BnMM5h&yU*ot6u(+6ncw%cJXuo<oۓ8(FוZ>h;,Kb5W2Z4FXPl8mxVd+P-kG1w;0]ysG(#AGa;`ٽ}o: 8Sd'K$ q%`{c||qrEOaU8ZcH[e9cVHHZ(]ycqB'q7 ~-Wg0)B@tCğ[5ȕP!t^ۻ w5SjN#[_S YqMjn; U13R;!nU0LD.wYgo&2`ۯL(yRj,Ώj/}%R?̹dP4Չ2}YTU f*^sEd1R|bJЦ"dn <@ؙ b؍NK o܄ G *tR}o/ Xfh{u7f9 & ?̆"2誡^pE Ķ!sO6Q ;V.q?Vy,j={>]=0nu6X,kV|Tcn9[ VDULL@9L꣄b̧fl{G^rͪbѴvJ.Oy~4f -aCep-pk냳j? o7gUAkڕ^G'$랑DSƁd}Q@JJ/$/g ӏ~;Hv3 j'Ja<å/z8Ag)Z}G[,X})ޙۂX՜&osWoKJ&6{!u'Ӓ=K8.kͮrSG1H/a:gL[nIA.t Vډ@WH_uo g6 }HdT}tB {=ݸ}%cDcӂΗ¾6m {oHJH&gɯ35 |}w(HHf?B6QBIAD[]cG폯7P386"b@<4h_g }"zx,qH@AMHi#24uR-M XՊ\6">45(M@1MFP]~3!(.Zn W>ADu <$MiUv)"H vCkǴlKج 2;Ҫv|%ܟj| M@Uyea%|II#DDu̘r\a=N- ' *? Jj' Ynsg1'x}?#5Zҹx^`&aIjcٟ/`sFA;pxmd7Svi6 v# ȗG#K߳Ve~HtVn̽芭-.α'\YfRXvY[ڭR+t[ BS=)d ;*lߵ;Ľ._>KfBs~6pIbvrMM:d`}tlp;u*嫽;I!A稌Y;f V8Dͻ:ԸhC֠^R`/v^t#vsѮ`WȹD_-*,fȪq>-"1+xՊ#.سuo뮡 NHKMqB\! kw2"+3]u[5ql Y6n9UP:\g{85ۼ7lhwxA>tu{_IOԼ|fr^3Q>,{`E=ϢT*ޭ)+lPd~ËD[ ={M*CJPH 6%YN0%{=6ͩG==ey2 ;]YVQz$H`etA#T" OC* iyN- |ݴ]-8bs`4d K0%MWm7s#lRڰxHt]S!*?6@k |-v$AWht?%bZQ?s܉`BQšQ.wr'w6,]{V׼zEOB ;NM alQhUIrB$#M)ËI&2"0)Qnck=9pd5b(1Tp#.jDߍ5 %irfw U/L t$aC=e@ۻH1*avlhsLRzgQ_`ߜ.FGwd{\o@6q67,% ä6fY>;|ykbWTLJ#g#ˤtCS+-p0g5+aI~^ ¬}F.O7;@٦ DՒc$ U&jM M1tKZ6m3 W?l!Ez"I$seJR=XfscGBUc dÏp l*bzAYօ8H̽sD CwJ3;{Ȭ m-@+&rgStmmh\@fnL'5tN}"J`GZ^ n@/:( cmI6Gom3zYԦhRṲKFj#_û;фnS^\XZ_@пwl*sTVY4|&ĝ %M8h8nwr]NUsu`GPYm1W!%T01iXNGwMMOV࿐dX,`B/42 .glEC&emRiI[iR],@qڇp+| e )R$jTP@C0V,v{0cohL0R[dvhPd@J,xIl5 q T 6a6}ץ3誽x~mIvDp6m"{2r}${ͭ#&f)B8r;}"kuـ @B`G9~t>1 Cܢ{+ 45+#aVfarHkb>EÀrq-7R;󠦣.0[})XPAW 6LfeéX8IB.{+qL_5I$+bY9xdkG6e ֽA15!J$7@{[CE\ELޓP5MAiHܶ<(=cĤuiN R@ O@Ν[%6OmB׷50˥> cS ~=D]T+[)):ɮsEV(n 9 *.bi5/>I~J' ӈ)im 2]<>z{yr;b7R `8^Em !m$)K;ƖNQ[ė,f)apji5%,f*o^k^m- ]WT"fM kyֵ!&?Jwȵ~M+tW>-+nЧ_pf2"DVlj Hd-fp1'{hCRqVrGpc5OF# ?ߤD 2۲ `W0SDX8)QM@n4^O)g]gĊz J\|LZhW @}sLe}'h?e?4aK)G\ 2ֺ}mOe*}UNY?s8Qq];xk. ʘ/"C$ u:rV/{Lj1FͳEaQ*O$t,SVUcn#qe͑Mr [Z?R |NlrET"#@%#%'2c 9l, *ː ,zrFxy(1)ZgB])41}vsx>.m^/ۊ!ZpLdØHE+6\]ʥ1oU#N?H4K 2>pƌe [m_tdF"hI\K+wH`\RYɈm=3Z Jܛw`ptx2~b"3E/V-^>M?z!]Nf.sea;]7H"{POW?`NI̖dW~&opoM/Π>yR,DݡkQ/K 4X&i+]|-n7j>? Di!0XOdn&͵{ñ=9ĥP@>$2gXRPbK`~xdiV=ʉ a[%J Q;~D ޱG,|R~?x$kB*+pջ*DwBs(N~ZOܟp tȕH}>,U՞V\#L/' HSX;P]bq$ ƕIچOͬT;5d5u1tM\-TDh]}#tRo8P^-M6HFKڇ~IoZiFt +$ fYYVo@ 'F2umvf D3ѓIgYGR+4{tRʅK̢(K?dS > |%.!\KeaY 1E^7n"i8g4$jz/!ptQU=KȞH-2_*; 1z<Á(&NY_Jh1[eOLs9e/0N^=\C}z{̅rK2"":BZC,,j`QLiIEh:.@\V?vzq.},6ȥb!yPTH{*ĸf,WeI6/)( 3K[~( -\Ext`j;\*zX%`(+Zh9Ļ4!Sn4贿SĸC>I\%7w&6ϯ&D%>t ̽^ДEDΏf3"~? kJK@*QM HySTrNض "GqFi+/$jlb\hE(j Cuu$pN\Hu+QfQ12~{Sd(*um#.y`聆hmuS4%OSKН4ug^5 bRuTQ$<崦T6SXKI/R%rB(XmgtcA&;FBE λx[&+NI閇Ж. .8sXZ"d ABe 3諏nj)=us sAYMQ"Li{ӹokqt(z|/$֮4yдa@ t_LSR`/0j*!l,@@~yCw uz@s3DDz1DmwVP5*-~KM_!62Z@>l#d} h[ F :q25 |dSp6"5Nv[L Ehz$B_=vz~ClGXe~CYz8i" jL":fmDosbZ`SbZež`bz"'K `]1}S8g \|qۋpEXJwi`<_7T$Ndi%̲4Dƚ_+*4^xqr܈8kԴAS޼pi.*k2Su43lLY% M$φdgje]o: '<:i BiIbkY3(~z ~כX]p_<=Wu=Ia_jQ utT)q_<8"_ #VђD$\7Cd '#}}%uZQXG9 Dw9uRt4_"*G5Eϊ'#zH{ Ǧ\0"cp>)[Q*/ ˙U;es6gh,rg4pe)e,q<3HSM99+]vi;P bGn_ BsmjFef7#w8|3^_ -|@*Z-Ź;j円")<#=yj ?jB([YM8l05Pq4Wyp hBK+BπO@2G3YРqs^i{(Zt#4h1VllSJ/xk^ް1A^I9/v2ˁ[`$]Ԋ_nzb,ƭy*u ̈́,MI 6L2du#3P-e |sbD;Lo. :֢(\i#ƒĐ|`J(TObG[|(Џ%pq-w0sS( :yJ^gnle:run5T `*3;M T|ie*/<.꾷*.#h,nm^{l-V|xz;N)R^Q+#y)kŎaGmI*κ,75 7*06+ ݰh8ٟd'=c/섏0?rg!;t ɜhiϢ QrJǭbٳfG.wR{ZPC:ۓ.07Kp ƢUlˁV4ꉐAmKfBqxY (F%p|nMiz̚QTMn0|BmJT\4s̷]!of;cIa?M]7nQ#_Λ0Ɋ<^!a6[f״'nzMS$˔0xf 宔:+ 7+|t{X߳ qq 5ë-kP2ΏtHNzt`@k3|o34$"%l#Maszz04㤯W B:qy 9cZjb"FjX sVurt~g| 43?8UU߃]QGU #3壏9JJE2Wn_ ]1ȜFeτPtβUY+#NAźfO- 2]K[͢7a (ewnY8ټ+(qG~T͘kQSqk:G!pe!D&jMJ1&łk>S幺vfwC>T>z;@ږ\2XUbY~@vAR1U0w`f0%+A$MȦ~Nw.nk-ִ[⤭*܇:👍y` Ĝ4y> _f麨Lyy/+|!zU4"5-AxIь}ws芳9(gHToxr~Ufa6=dҰOePK xo!V!y 5ge*%xWZ3E]pU"jr{&?Y&6*e: i]4xVZZ4 ;=l&F$϶OҊ 13y|BjS0?r#Kx.Z{\AEW- ҡo9B%zʚ[e W<`AH1pKhhXR 7dWX0N0`J ]1r&$RjTMb+7Ud0[qjI[BPGC^_cvr (/!fgr% ӭ٢紨xs$C"OHAʺdӬkEFlc2WN0 ac'0[2^DSW[OHPB nqC B^RS7U V&ރWP3_ ^$֣$ԿN{(e$Y?5s.7xdvwjI~!]FHƅ=iI1aG`xkn2ia=(J;l %d? ۲PsyK9.vBߥ9m~ A/$AOD%ҕIwlJpv1(n &ِx4DRԇs8k 9:Xy}(Tj70D{m)֠)J?rl&"u3@Bgϻ{(Y Qiirҁ\(RnEpvMrd0h&P6t(؅s,Y(eԝldlwul\!t2< Q \mc'Ug{QS-- mteC׈B4 nWy ,:|웓ޓpVzI3yOyhXLnjQ[EWtecITEf3+V>4]ewVc'M;VET`|:SM^:X#Jo?-[:yb< na >`:inױHu1 Tz0sp) c3*_ -ޣ@q~.jgcO aJ! ndس*_ –ӣڸW/]QfQllwXy+RAۛlQA]T:XR"=be vk.mᤥs ((U@(ݚw#n5qd̬v+ ?!G]env S;t- ͂dxNmֽAhV?MtF:ՑIfmB/뛒;YJ JC=gS^naNUEVTwG+XwALbrdNO~*>/pu [S>9Vf(bKp_ v؋ZAҷLUӫrO,(k0]\QCh"+#{+FVjl)clӑrlDzL;JkcFNTd;L*jx)g /=l C4z!\iN/ZI.@q=8fU՜r|ѐZvSV'5na=ecql.@zt řv|xIkv`BkY8주gtԸ(tsnd.lfN^fT@-0|cw}|wjZ*JRLqV\ .!ȋG3y#btvu SMF[&<5(\eŸr'}(J{bAs/z0UGm]7O6[0ڡVkp(E=#,V3 I͝?r]k9Yɔ8v j!QTC9g?e˅R_6` 2iz(C@'hc| !E#SF5")q*eU[mC"dꙑO.a,oxOw[hQrC";o-`~=)RZ;V.+RkQḥw\ITSs"tdݽ]^yvϥV w0N+ur@qoF`]k!UI1߈ aXd{G Vo\>Έ^ijr]I_.p+Wn*Y[||L=L toSo 1Y?YQ~# 'HI=Wi눴-]:A?i{DL!  8[/ c~wCA೓,Ő{3W#eSH !8fF [9pkqmuFEBD(xN-~STL_$ Y/b`@8qjc˄yi~Z|%;/oUruW a=,FCr P_25JaXf&0%hHV,wxz忹O6K.[;&Y=a v~Ϣߛm&X !(rKr}kp<,c`Ȑ vWV}ZIx W\}ڭv2dAl1rS&R-N4Ї e+,:,DŽ[]ޖØѣDJWJC*P>y}m&4UwD}lk܈O. dxL0\ aC%&Byz#>!wVOgu[m*;Z ¿^pݴ^u_8MRѫ0x7R=3y<Y_ :#^a@>aueM^/&LFzΙqB GN0w-jWMA*H1Rhs1Lޞ}:*$q#EȮ2lx]|bu)5/7LEpS% (B RHcq|0y½7/L!5?\g;0 peJp0H '`]nOKrj^̏?qh_je玓c뼻 {ֶɩ+c3? V$9|l$h4wCNx57&cuv֥5|].uvgݛD@@i N6}پR)ʺ4hqb<8}\_6~8CoMG:%~kN1FFIϡN'*+Y"x}]og+*UP&Bcv Zj\tJ.0_8VM Jq=7{ĩ/x rW}vfҷB<R(Ն%!ig=#Ot!{G\nu9:^Oņ31<$>V(0 )掬֙GV=։~ R<4%oȔi0 ֱ;MKuB ve dH(Abl_-k&2n 8^+0~ 8 %fCӟūaC$MED cgV(hBg?UZk%?HbJwI}l- 0۫؏sXO\DF&̮` x; %qsCaڱyd3\;c3he#g7LGKn5<[/3UfnxBcԈ N'$Jb.`w6C|  pupA o'Acu Q7%| @]„N%#1?ÄHYd(Z1y[`\u;I 9Rng/ǐw̸~h&1 VWhh~OwfEuI>9*t?!R}b?Z! ~_l (_3OäYhEhyxBԠ!b_ӔɺWO -U3T;C:>j7Ιe{%x[.PUsF ߉ !C^YjO:. f CuFgP2ȭ9 =~Bfl~XqKn6>D #̂a=J(uA6^&?Cgƣ| 𻃓OyZF-NK `(d2$DJS佷{I6t Nì=Cr=f+%8|K%c̫Qv{)0xC_?:%|H'v~O𭝤5)DYDHm!ynM sLVJ_Kh2Zǩ&@ձWu&ѝ6cf<*INz֬euu'Qd(]Go&Kpz}}Ө{P&i@%4m`(Z@[!5dZI)7PbIvs(H#Ԓd'U mb_'=`Ss)܃8tYm7] n(a*^P3ŎA?@;>Tc=H.o+ZhR⇫.#UfrsS 4T\-o9hhS%z)6)8?tyxę<[VKx7-'\}k:tK~,P_ A)PM3Z`w \tE*ud<^DHdۡcDCpSHpK?߽uWe&co.j$0=TC=F ]p\)+’ )sZ@Jg@H2VG :F$@G|]hցN[6w/?wxfϥˆ( \*~j=z'LNtHwc Ș|px/ *5Wzb#taD&N$;2鼱|>"$jcGClC'ׇT̴Sv0$$oq9= F>R\EJQfnm5)}AaUM[I<=w($~xy~ʘZ\ݥ*DZn]U.HmDEc5g1ߪd#`\*;N +O^-RIcjc)T 'y9Ml6n_z~9BMhE{dWmhVg=zP8.h IAW.xo;nV~/(ۓ JǙ@]L%b3;ʢ7E6eu[LJܬ򇎇8m_GqLɓZN TQ)OU~x,)-~ڴm/vUMe?[lV#;Q;oBdq!N>Pωp'0r2{U{p>.0mgkIፔ Z%ΏCTY9Fq Mk7~ʋh@~YC-&9l?]r>'|hoqd_= QyR2qf'v"iPHGAZg/T XQ*n Tjv,0mEĦk]tw5J!C'S 4{|Yg\ L*I};Bʛq'`:3Vnnej!BX" 6{]!_^𧵸3.0N5gZȄbӀP$)O߇vLt,X4T׎] ~C3m6kZ!fC@e_Rg:.^hLLDcĭ&9RemUaОcr](Χ=2W?3t<= ]e2+f60}Z^SwrgHYDM8Ҁ?j3:^mz z%2U!c۞bP$u $&V^nVwxd.qԯqӐLI{P6CnƋaMʒDԩ9lHx$]Ƞuoq;n,4bW Q&k Ĕ9jlk/&I)\ `c{Uz[ky;5`4x aeEjQ o1 )* zm>Tf9t.~ѭ@*\סutEGsR"̣xfޒ70l;0*K$xjSJU?C2"IO_/\~~ z'^eZFbYWIh{^ڗ[QFfpf=N&ѐu͏X:qt$wmN:dPxҢgz6"k iOKֺ̄u/7J -v>k-DȾkMO?\q-_lFb$>oȯ`KF=L]x>\&hS|8_BxͷkM0c~*5CIvJAK^v3rz"~<,rIH~qpk\5/;+L\<0R I]ų$<0Txh6_^$8}? t5ZS/US RAiDŽimtՁv@zթzF&, |2)8=5'UAEBqRu*L)?Pfv-jh7Q̜n1Vlh@?H=9ce"FxgUI@^OA=BR|Xr,#' 5D swDqDAJ7n^T؋BmGcۆT2#`Z=.L긦KbJM&gY x.bLbk6 4a;򬔑HNI41N:`>|>Jt$;6Eƞmc*B9_<Hvv>1nW4,sRNJȮ .N]Ӛ،$Vt55_PHAy(Zq %2ƿƒoZK35)2hj2RpF0h3woz܇+}6[j,&i!S2Ĕά8 DUs]τ_tBnM N%jQIC<KiI`GųS9IMy>4r%*o#B@2qKZ;x9٦I[<|Ý)h=:ըzida׌]Lbho0g $&9[zx>MÄ5a|c ~6]CK9u QFPeX16/FMXT[uuiyc.ڊ}BlocdN+U%ӗ>⾖O_ dwm$BRZ.Gk,Uҍ=PR C{b^ bNmoCu\ WS\M))BFrTKԣGŽ#c=<:>@ DmyxO5像Gn6mu˥1܊T|-ªwHd$6l \`7j?qZdح8><<[gXܧ^="(e̖yPrFЉa3xc҉au`@YBm,0=#eT%'R|FS^?lVFf 6בÚ15˽pX"+g\ TMH715~ Q#~,u]\zެZ:M\!jy3 xq C 8 7Qv.:D i 3 Y4൉8TgT^K#ZdAs&6A;M"~Z^6KTsEv4Lot=xғw1A[+͹ >u6O}DNo\=ACP|DŻ7/6\/qJ?.5^Bun$cx;kbM䚇a$a\0o)@"6N΄rMJJr?k?q*`V^X|j{(P6GՀXOUʴZ#ew*zU7rorRaM63ُ"Ak("NVJL?.KClSu8I)se\***yQ|زdb:|/RtqҰ.uQ@eLrsv,Iz:eKA+H.]mA͏ل`l/b-q݊hg;xS G3E|8 ;>luD̻o[E|ϬId ^  ڶZk?yrB`:]7uDNR$N iӐ`Hc% (6 XQNe䰿 5Q-4=Bgu^:OP=ϴf @)~ø۸nG1Wݶu7'5'iI.V2%[~{Q<^@2l$ b>pW?D`>⩲q]5"5J[VFr[u8ҋ*Kی3q0q{ o@F0$a!WVP%$_Aqت C?g(<fmՠ$PX[-B`Mb p=%1FuLCo[T9X>񀧶E #ʘ6qdeikCͽ4f7@s#8`?qa̶RT1t <7Uvs< *6w7EYãW_ޞB3g Xrmdk(GD d5m+Gs1],+7Q yEWQ{K+t/^(4Ϳ!tTR grxJѓHLv_#χm$,!<0q,oZ(8L_ᆹ>tޅ|μqsRj``I){*D'W#>zkg:ffN[IPrNÆ@[!>{)c9qyT%R27%Y͊hn:y||Z AbmQ1 5tsʝc w{S /D2~T pJZ r<53HA&Qia!y9G!Wq iH_~_?E,>g%F.kPm >2ـN?i,T@Z;BL#.iۍm̳/s8Y7V$GՖo8LU8;xft4ed: )D=-7 y.=<e<,\ ye hC=Rzd-4_IUE*'̿ET 6푎c"j*"`VV_(MʷηzoL 4<14MN>f^&Zejǫ"c_gjցL ,I)=5N]RR&@ ap;uǹss Ct*J+dqiP[PǰE't1 ɼLx۳*C񕧗@pzC)Ca],.B TѾX%^s % n[بM~'(̻X'T+7՗֬E#Zb,eٮ?wRb 9ItI !WXaj7>q 2zF N)f3< 5Vn? B$1?ƥeS~4$IXjv{5=x]&ˍ||ħ 6#Gm`\T!}/x4eGduky0+JBa:p,w,VYq~mH {H9W[t%Ԥ.hv78~"GT+l\9 8NTע$ȵ/S f f"QD ѵEONsK$KLWԇ%t[aKϭ]*nJv?{Qa4>v{y%m!{l5DjL%dKZ2_KM'IȽ 8nЮUe/O )琋iY@o϶1%'IL~F8DY9'K[H~ĸ k G;+,\gN亟p_S9yfggF4_@R,yo8lv|Y.x`baZԎ3-%d?ih[/v/MіR* 4hx]M圴h B+&m~!}jkQOOP"X&$PBjFqtpO8yOݗ<'\ͿygUfXS}:{rvӊHɄLlP])S|J/(2h[Ld\Ҿ}Ts.iH>xJWcAdx"Z:FT+ihC -,BY%3ݛS]*7]B5=w#aϴ! 3R0L.\V@Ics(߸Cul"㫒Ռٲ_2^܆ DlKa,K9[7mrRcpرK(GycJ~Wx.we"2dR}V8"ߝr dv}~bq-?ei:=/ Rc`+ EZziR=u菌Ѕb)O 7˲&{.ecM,fe <~NwCk@R41'03~`0tR(TibN IgޙC5JF^HKъ|g]:2v[9jlyq=tlp BsD 00 ɀ XMQe fPi,!V4X,}X>`>U8H$^!s<{ I<г%6}#RN 5^ȧ2 l-uWh='bE[I"dnXH~79b//ٻ.kV6``ocXT)Z^+vxhcHN^m;>?q".e২ؒ@qk~3 $P&$E]YT0Җ%5ܛ;gd \.o&{~̼gq4if50IuEQD?:L)u<mfP)MItvl񰑭҅RD:NǯGЧiͫƹr`ȯz\ A/rӚD%`ɱb7;fnf& )T\bXfzTE ;򼖘%'|x%F@CqgP!ǦfuL_r[[7HGUuM'q|T7ȯm E 9^j޾Q |IA{V bEc-Pb%"wr?&&ADR稒]{5弡EvLIfQ%ٓVcQdJ T䓏2<^eFvvw~̟]OǦ"PA!V$ăuIRDEÆ`ȶwt㎏iJl3lI=aR"SseDŽj.VrȪ=@VrIo lڤ'm#wڻB2׫RY}_9&=ϷqYZQh,ZhT%6ח -Ρ'NnmG1f/ IM~1ሪ*/p4}־'d3]"i|8+jTk3ٺg|IoqvRcxX|CId~AZaWw@ƒN F*OdesT`Mw%7;FC06aEࢄo.XLW~:̀~%jd0詨͘)7xHsj 8_sRs>M?-1 Al?VÔ;\f6r\gŖd+C%h!bxܳujᑱ|]hV% az5WtKjz6u!ihZBjɡ#r[`X.@rˎ%Y`f7A_H:)6؏懷R$jB'[όGAԦOo a+\q&:lwr^HquPp߬f- |_)"1xA ]O"-V M`W- NwX֍&^M'ˀesLCEa曻(>0G\ 9EPz$ ,=Ɋ!+ugd^7'KBn`g%sv.tF)y3W%5&n*y{Jt&@" 2,"46~-"^w coAcO+ʏN8 Tw]7I3U3F"HdeK\&1q zkb:rgiy5Ξ,1Id CYs+ Ҙ.m.g ?-_Tmp \dSM9{>kx'E$SL=hI띙.gf(Qrk|cmB@aVQtq=oP 넧 X1Z\A 0I"rRO }Num@zJ$u}s@ex>T-ecṰ#.q}.҅c Xd>վl4[0~(V `ZVvTQѼ1`C*Drd3j< SXO"/As‹|(KhcC$WrN4\4Y;d[t؆]Nӆ@a \-u baFw&ө.'‚ REy& bGp1b\߽{vuȓƚrB1yﴝ :1;r߁ԱxKcyŻ hWQ2HK}< #/a(?L ɳ* Xmo nDNavְXM<,Ϣp-,[x89ᦌF".(U"b!/y,4al,g==\(Eˤ%o f&^_?ry!\ic>ObZX`]K!*1Vi9\i@2ʤ Y >b$ }r9BKQk͈6:mm4_%jLmHzgX GWWF8`@rb4QXcݦ.  Zc͸uz|o|v uTw!֥z8"ްϣmU"vmI9ѝN)S66gk=hP)]Tvqmf^[&/N_& $41gݗYY}9VV/Z23=@B%;&/w9# k}۰D1`yUmJla G Gv+6 #.XfK6nZr{*_©2{-!'O3OLiKm83̴X y\lHAgϕNvNflVyj0cLɃYܶ3?m"`<(*bD @_,v6R[kh^j|rk3,-L?[-"<ꑯIcQ8SẄ[n6SWRv;3"):EⰫq^:QKG y Ĥ3Pl8IN`)+zO[ʠ7}#rn$Í *+FeyG;6Qɽy&q~ F~5cɏw_L_q T.cYq:b4.8E<wbt!r!tFs/9Gwx0ՐԘs?`.q8A`diޒXxj @ktx&D5˰2@е{OS# 9 t$A_4susY-x.#R4<\3z+Y &Wi(i N'?"ur)12jad0m9>AxN,:-SY\ oމV#3_;UU?oJh#Hyؖ TYhp3#㡓Z#!{J dXTծMJgjN˭:ear9+EZ f '+og?rH :>,:Ͽ֖+wTT"Q$טԑ.')÷[o \#^LpgSW`E^'}[< M/ŜsOaf*t >q4thC-HݲY-!ȃsON-F|GjEryX3p$a!S&-Ĕ Vyb~t([־bsSUGƄN;⭌#Lg{S`=~#k*g ܣ憍{*8EƸ=v:)w3۝J2-<V E,}/h R`@;Nm)M{`C%V /O_) h)eOj i^!elg^ Vf{DFIQr7tuEH7DD˵_l0Йی5Cɋf<祓rƱ"yU-uOK / ڂ`c" vzO~"نeX%zHiG\~rnnU^:)CFd2fMIM$e ؤ?'yЬZPR .Ԭu5Ji]ZSS$tZsԙ0; T11'2.o=Tg?} 1i+_x?RںJԴ#v8T=ݐے ^~{Fcz_s{7oAF_&vM4[(C/'n,2OZiգBJ MY}o9ٝ;*iZJd,q5}N^rph!Tw ^/PR?:bS :)d5䛘TA7tH_qA0z녮^$GSn qa:K鎰 ".tzuDeR<}B<鷝oild-:5 Y1chN'"G^%{4L}|(J3 i ټ߆~B;$SwGP6[XI{̨=m~mW o^(>$U^hm>&-ǝ*wvkfoQa'P 5]HlȾ8ti~2X|n;ۦ]ء (z1ΥmJ.t] yOWx~Ɋ0k~Ht^kY PDyN%%׎ -5tZN8 &.U<@ "k!?J;_)MiSU-9(h=9I^g,(U SD;ԵH2R|V,t!{ ) ڜԚzE~ .ǀBJ 9RI^vW3{DAe4iWp\ UF!osz "HHS5wG /6 '\,{YՂ0)\3,w1O[و467Mx F뻠/q)Dd;DB6Pԣ<'14RH53Pp" ?h0K=e拲 *Um3,[n̜QeR+vsE.gS&uէX,Ș߂()1KPtgDae8!$epfK$H\f*sm -u} K$c/<S$J"@|zm )[;%ZF[yJ'^oNX΢YA91Y~0a܎G 5N|q#hȥY6Jr7.UDh@3*zO0W?r'zȈF l /Ar}J z0'l Րb4~G"I~|[./QD69jZhBW֩Ena"X(mͽ&0_Kyklܵ*Qh.YpzNz|ٿ^},hoeXuJXe7؃j?+Z#(Y(YWGn8,m+_#;4 JFꗝ#nP!@F4q9Uix{M t EiN:m]D|kvs$)5x2KZk]3Z|q4Xn3X[ڱLi&EOq1LDFt,Ebkc&#,m6,af"]#alcN`2Os,GȎ'`vXkĩG ;֝B+=Vݵ${Dö:ަ: /  \zWQgS"xbl 'j .pvLSSŗSb Atsa`!Mn7T;K~qXf~n ܻۯ ?,AG/c`eeLzn0IXz mߙ$5 ?j7ЏĚozh„s&> r -*$*RRS$Zc~+nN['^b.'!*!dtleS#W<:THPπ.7¸ՃVaΣ:Kf ;}LP9sKɢBߒZoTdrЖَ-r12c^2? =; Vݤ nϳӉi@PX?RHl9eȖIH阋b'ѳYa ;&0V,f+>`N$ BVJnpq TөYD*qK ڎ|/]CY |{S5;1ef/}OsFǍpG3'DaPW€s+&1#ȽkԷG H'UwD͠~I0tcݺUJ]uѠ+ &R-qt7$C &D!~])RbbuѲjNd4 !IyѴlv}c=ߵtR eRc]U)uN\ƍ';*ip/wo!VaȰ3z2hnvSw"Œf_(`cM?a]E% we?(9F'N-Ẅԏh%>A,Iw]f*X(BT[xH`}azHK8獰{mFIÏA*ٴ-H!cIߓy@ֻx{NIGuݩz&~=tl-%ݒ…%On^`tgeAɓP~io^r0uftO:ՃQW{VDcbC)'Kٯ~n%z! AM`OՓQC'ݗ[!2V?6 ~8,JVX֢́\^M@";iڅŠ HkBJT^7yXGA H9˱l;q_a{Ax0|O1'v$EI҄6 $`^I/9]a<_ٷ㲑kJEl0JÚ$$&4-Mn S *U*x"Օڢ_V=4:U67T؟jnb/YBe]fg6߲ B ްs2K-|AH3Xi7X?}ܑrѮ#)[.֧JP(]\O,cFX<'m9u.Wl='`İinPBK5Ƃ52$NJ +nr[sň!I2Υl$r1ĹS 8O'efx! ɝ5wQ_؄8x h̭8Ntzds( ~ ,SQܚ-]a/.ɫjO w?2*R՞;~,KwVjR58v^ݘUsahbπKZթ|@b2SjS+v!aT t%0pchtx;$/ePM1B?Ǭl)ʡ(7eH~B)O:J[x2,NSS9HhGљ j'ha6% ]Wo@w~S<["$o >l " $j' ,uň NTzF$N6Kki]8xu)%7i[zWxQl[lUZCWd_țq8 ,q[X}MB'K?h%g;زUZ$pĐ4wߣ<"!/nKp_e*>aI[mGF=PuPwک;M`mLs*W΀q$xyFvot.!oR ;o-W\sdSNfAdǂ)Xa+t/:n45h]e:R&I{y8"X~PgL9jA]$IN'5,S86@O  Uqm}C>PӍAe- uHFػ:wjy[o|@纠nbJ;1;Oҷ<=S@ fO0:1pZ?A%lĭ^wR^t%WѪEkYdM8/ u6:Rp"CV!)5T|ݭ# \1óRϚ3 3cydQzDÐ?h1 ;m̝Wkzc\Ih%9I. _[ hN<٣U>!xlnC Ig, Y@S7tmd59_\*YrNU PJ_>2Vc @8ezY/ ›i0j0eOAgtÇe~qp9*ѦWP> wE әlk1ڻn}ogd{$%;$Fʓ^l7XzӁGIuAƦxRƱc.M _d: %Lcݏ*NgwJjĚ̲s_Uk䀯cX'~lqaՙ_CP,>͋Z!7v"Eh3D}@lMߘ6QDw@ǏǦcN@R{E$n-.! o^t|B\wЮ{71GZYl $+VN(Q c /jb ѺE0e{wjLĻl&z Mj=fXݿ\|CI3G@S!tCǗ2f N 36q<l>6 6HgӤtyryР+RML_;pm'lAU!&FoY$!ˣ*CN]"-h|!dSut p[6K/lÁqS͐"F4U7 ($IvY0F{`99[Y$,61;`tm{xN5%F 7Vsbk=#k%=([LxIQ[{!l{f%([w Ҋg5y)N릳}O`…amWY;F|w~MLP҄Rg١;V;;Ak1C1힦Bp5Ys|Vԁ \^2nݵ:ny~lr]+/R69# x2w!SqvP _NCcRUn+ߓ%k_i5ҕQQ_ "Aw 0ibn__ҝ5<3ʼn~}bH]|_Io[sn-l8.ri2מ`>$SV<'i!e\f^vv=8rwLTjQǪٴxCEt> o~y֎oau\ʊ,EnW{j^QV4]+-u( A+uJ7R'OJgldT8-`XH'p^J͸WNGol>ۿ~BFXc+$ոfl]H yj5b%͐c-[j쮦.#'>3B:Ƈ&-=TW@mPFyl;!O|ϡ\B$؟/a@AK ɃU(mLԷЉk1w&|01&/(5_v.ZCUTKUPb Fm3 6{Q.Vj_@%2|/nO=s ]eN`1ܝd"p&g^ T}֗X;1 ^U( M3%w'iM pi%t݊*Q*ķfU2j"#-ZقqFȉVpx#grTw<-ю=zpڌ^X ಐfDZLuEv3ì" 8PF?H'+jUb-N$MZVW%嚂+6|<1^"UrP>1aT/-:³ ͍@J ۻ∐ =d64ll@@|sG}P6WaXwme¹-*D$%sFo㵑JvG^\ YXMlef#.yAP9˚Mnȋkb 17ͥޖ^1,jSY=-Jك*/('pH߮huB+x(2m5c`HG%nQRMB"dIckQM,a)c_2ɹ8pŜZ Cg,)N[MqOC`N\[ի[T[erφRjVom6iZVuyrj <܍6:3%=}va_>* ۬WJBKC\&}!,='GԭWvF :blSM۱@֊)7 (Ԧc!LϙD 9~5Dr}Z9֣ECUD=^)Ʀ"JʶZb %L ?7אB{ Oj$ɦ^AnZ R)Ύ+6|#7$%)dOQ|m:T˼Ty]Z4Ǵ&P0\f?+}OY|]㘰h]%yd { @枙Lc 3H4zJ W9yZL(gúu5ڼAX>Q)v(2P+X wE.C?`VgkZ]Bl \z}E~a?ײ߹p YkTIXc)P$5򘗣vI J_|6{4?~H:;VNf _q0J1TLuw[λ7MZۉ+2 ؉glt5[!#}[2ӑ4^4F EIf :'R<ܨr)OxE$ojJY/BQz>ϟ(Ł=&}1녜Sě]M#f]:> {POر+^_pۭN.ˮM]Cпn@}RtG>c'xyVIgk>U[X٧T-;eT/̈́[뚐qj@!zg.ŒBaӉ5_6ۗ`oj\N5 ^/h4[v+fX qslqͼFsٛ4`}1rSnK$mѶ-̺p9:"?;Ox6dԏ2Wiys_\pa,xuU:2PRV'LV2#$v:莆^gQqPT_$O^~"&DnEC>DOwi@)M7'gºJ<{B{j|e f #  Zg4njcz=z$eߢOРl ɏr]a@-RK\gEfciMFJ"kb>Dt%ȱ&ޅjVBZ.W,; H#6OQ;>K ,x8q54߁%fٞ W'oe%֚mB/@$N25Ԯ;i6hA.*7|ԮBNKzl7 cgu濞 INqBɊ0]R<ۋ̪3h|iEpNVaWFuw{?ST  ^Y|O+Z޿H(>s ~uOǘLur! a۟:AxuHP-l~Ff06l;tuD\6\MdkƳ ܣh΋-(VѠ1{bm1t؈@fb)EKN$`jQncsLʷZ&q;-.KnԴm3_^.#oQmCmfT7UgJ~u@~O(gHzӶ͈+EI=$wKL -z'\}VAܺLFi*<}<9GSK@ g[? <مά Yk{2Xź%?1ϴyf⠀m "٩1+=:Ѓ>qm;h`M-6n X=;T')5zTCL"B:\o^ w6 ],0̛8bj_1nHӝT ~T@g2E^Nifiɣo₡%wߍ^ 9b4qoWWRf؋WJ`5+526JC:ɢLp4qa,eIF؝;oJx*9|TG}x:ǭ_kGF|0LpB:0u]L8Rn=JGRyۀ{Q8JQ[oPW&ZDHf7ju^%eFIh {#hdg$o 7EAdzs7$a1ԟ#s 0)h+dohS{@sPs^1os$NmS9iPm~rj"uo;'U X} N6_u =o.-M] ~3QЀrt>M. ؏=OwE%hXyu2oEvqsu_Sf5 n- ͍Hڪ*&jPԾH`9lryjͶyw],;%Bb3Z1ܤ6.VMHrfcNuZas%;CvUO:؁\xav 2 f#iE)Z't'+oDf7Sm{[ny3)5aSܟZfbfLOpmOy%EZ?$<~ H>haҊai(Z" AR z(ŖfQ tl4jS(m&? U_t +kKN/SQvB#!1c(@D57punP:TFXG&_ʶUg}0v9cc,ɵ[WҌXfϸT;Ifֲ`\$@c=郫Ϝ5'cUǖ"[ovdE pZC",RT);=Nx.\W~}?*OLs%e9n+ŪY\Ik cACZT =zΞOSєJ-Žv VGY:ITjue l3K&}fGX;vԥ-f^;u|spmDYE刧±]M9ȲkdHH!9ځ:9ߵtp{f߿ǰwGmh>,7 [DE &#R5&eSC yM8wvڤ$IU"aE t7Ӭ:.&ШEБj(rɜ˰D1,`\>'Ka]9jw+O:@S f[Ob_\n]Ov`?3Q#3K+Xrqƨ\I Cd&?9pW Ekޓ͏+ܤHz+@O&2kiF.IOEPQU1|<*1DŮ;DWMqI8A}s2:H7ƧhXp;׍M{0 "[L3ln(H{'_ucm"ɛ^u> ֔^Agզ$K+75OWBj;,ÌCML,S #@"t 7AUJf(AUDdu3n5ϳT&WО-'RѼ݉ <8? R1?i}5{ǻۛlRZ@w|{BH mG7T/hW {O#o9l45GT0*_0W@}gɃV Dx`Z60v/1wpo*♸h_b5U.T˳aځ<],Qv)ƴeن-'=0'ʩQL8utSZG37 `ct,гf_S_c;9 z<!E6dWy `xjq:A\T/Ōbpu$.aSЅ>gF~XYgԶc%2 ep`)[Vf>_Y[2Eje-;ae~Dp?#Hy[Y£1;pՆ_%LtU]%.\u`pDW FXglD;2V~ԗ,!gmO6:ȅ1dZ;B$'/.Tp0ή+k1I/o bYuϭo`%jުVFAM5|~1Ǟf c*d\Z{3rritgD JNtRʉPa!*239 U}.5`ܧARmpinZdtQ_3;ʣ*Q-Ajz.<h :U>Pa 3a%WN0@wHw9CZ"VUEMd't"*憾}.!6:Px{Pg 5^Q1s<)o| hfnr`= |E FPfIG 3%zh[&gATځHcGvUY}9H?[ yFׇTKrx^ŖcoGHa!<4Ù\6cIm-3i^iVmma2ğWȱ] j@q U&pUgε5;Vx1.]ZtqKPh䟷ɖRTLߛ-: 4%v!K6 EQo`x%vK!GJ"2pj|@GVŒҢ>,k}ZXA]0oy}uB%Y ANUGTJS"Rb?"KW.1wPz!RW˃ 鰏J|H-x ޝϘ bDeҳ;lǙf:qis~6ȷ紪5(Efz(>S;a?{ 9^7=ӱ=RR7d^K+l#X&wbّwɉ^sJ80r \ _$5Uq]i3vkFuZmcW_h(+AzDŽ&#bit$hmjsjq]ft RzuF8z5]\ELD/}e@ڝf@d1 (Z?wZ09l28ʳU,V+d͋+ĉ%  A$G _V ]8-kb*|]Xvz`}0yȰtUBfe3%?e - 9S t+>ψN\JPОJjr\Oj@TItkB'i}Aۊi4ѡ #xq;xMn-"]'E,PrJ3ր 88]8qgXkm$tHT$f?Ae$Hvb~JpXB'4F1P@Xc5/2%;ݶl/-^#a=^X‘;ΙQZI8FET$J8G}L%\ԔH: L=/^/h p=UN퇾e x2@ è@ c684Zϟoڞ0.vgęX?6ۄ/xOV0""k/p_Նje 5EJNEpHX]ަXi-Ev 6¤s_Lߌs 9,|K6K[Wz5ZC- 8::@l Bwvq׊Q#RsWo *7u5F r{jDzD7s&ӸO&S5^Kosc"5=?랓"(QE moAv Yڊ$KG_uFɘ4>VÏhi)L팺>xEkWm^lnq,pe״ov5158U/CoziB}p;@h>m#UWސsBhM>m j# ;ˌYPLvZ%Y21=$3y婗 q@~mgLJ8.Qu%}Eh]"/6\6Z+vda}0\"$Zp :u☤C4g#c>13wƚ2)ɖkﻜCU/Hڱ@:q\9xh` ɒW"m/ NH J΀p{.R .灝!(ؓ n2\+}ym,P}Y=^Xb;鲈0j´a\79jּSۅ?-2;1t7wqR$[jtxq.ǺX|Eq8̫lg@xRX|A˫U!zpKcPUW<[gRIoɝȈjG^;_ F_؆_I#9wjë%к"H2`g!)dL@JF?N+ho(bΰFEI(Oq@ qDQ\jTXr`xICVKcrN{abm=!> 5d BCHf\*wx|f0.noFߏɪ1( *kH4?ige\]+DGGW2Z4 a3/t*&bα-|Twk5%c&_|1=K#7-VwpquP/buj#p.L&VeU`ٗcrYc~іՈ\#1?|6@vBIѧf\JC)@ r E|Hi(s5J)J8pg [lnU4XrMx;Q\rqcDXms;k*0ltH+ y5*8B`3w}zHsڼ5%,۸էP ֏Ck?YY j2wMh}!zcFٗBnj8"y I|!pe4Co_7gZA;-JBI\tYϘ=}%9VV@ eRtW56`$ظC Ⱦ=s8iV\2@ow[$"+. 6egSg_;'0BN1`?&䲬GwkΕΪCyCیL˹Va㶮~Kr*K N?b=U(_dQHj_e²lǻqū |m4ց=sަ 8\*7|rY!w˥ MV%݊DpiaYZEasM*jFSj(ʔVJ\[:;͔lG;!ON)c[C<2p"i |>^vQC%>ٔ<|H1uK/a^g.}S@ {Fc\GՃCX 7y4hY8DxxΤcӪ>l9Z+f˭&Ajo70]vz0e7ݵdzvUZMB:"nDGz'Ҹ |8V}src|.dj3ț`; iNK̤;m1yAwzف':^QŒTkc TKkyЭvfN !cpZ.-ej{5/GsQ'8QB9&*8 Qn9nmj:veQS:Y7FYOD"iH92>C>j Tq*X <!mOx+W&"hMSl1AEEm DC${#l~V;C5S|Q x8g_k.T2^d{aȜ6)RG~5 c@ |ca2\-H!R` DsjՑX呝؅sϟ48] x]IV[9 kNk]| ,Lcz\+v4Xzvv]Rjᠨ^:r~ƧD*bIl>`,UA_>5(e|1,PQLNdZk4HNLg_Ov(I{j[dI 8N[V+ b֢C-ɒQQPŨϜ큒^ O,5.gE"R uqYg`&?Jh$͚k{Bp,0Ȓ|'Z 32%g"jSSue4]h,"U~ȵ-;apr^Ui L[omƢLjQW3xaifj'6BM!*'g`YGn۹9jK(t0Sӿks #޸\b+2W9Gؖ P|Jx"y7)/(go{fqNPW(R V AZU]PS 2Rf[8+2|}*sc82,X #SvH`0^$;=װ͕PR`Ń,%Vamk0N^J4' @2CG {ʣ\e;F_+gMNOcJYz=jXV"'>3tcS*ې'f1GI0g6}7 f;İa:+]QN0թ?Qu RX8&cEΊtlQTJI\\մ  Ccn Wiüܗr-yt4:`ڌPb XY;,2U~I J)  4V⥝Z1V֜ ſ.I ̊ȥ2<|XL<:s,J}%.rCtpDZZNsb;Ֆ̲OFE^Dv xnW&LjřF`]rɋ>sMF [J<۞UJ3N)S-!5C8/Ff%Df8C^'\jѶ6A ,fv]C0z.F{۫NAJ(~١IKkxE@-,jM҉yp4m-ֱcԻCd t:\`kvUeT1e]Mx).j*+&@ҟC;T=a޳I Hn7DNIEQ+$)7dy cCN,rll|@Y}eւP]IbbhEj JٛQd]ĭRdԠ;U0~Vf,-LCx/gFj{[=6h f9E05:#҈+ ˎ6MJୠy |bNSJL WUdAb8~'qg6!eUѨwۜiNT4d" j:‚R D-o<{s۾fhJ~k/5ȭS='cb ;AyQvoOvPCe]1̂ܲ+> 9{3 0]+=j2Тт̡fN+6ўߑShN1KM_Oiyf?Róob¶E`a0ͥǽ>@{cLGA[]aAf6oBbi=%&[Š0|B%3XPfČ$=p+)+nk.{`9q8k1K}X K|`@.1ʢ0 o4HxG#y&:.^zgKA*n%)Z5rE*-KL>Q|?8[[}a|r>fؒ4>%f';7=H0* x9iGAVKt̡&?Ćh%qm Bs|+?Ci/4mvw{4D(K`"mO}2Nm]X]}0)x4m7EsXW޹eO9M#"[ [~L]16P)O<ʔΔ%y{q[P:3PJ'sMGR }#v[@@@z`ZRA.ƒ* ;H*՜qtXReQaMnHeW,yl0^둥XBV%ۑVL솒 MF:zsͥ]҄Gȶ]Zݥh iRXJlJ5(λݨ+83c!Z= ՚EoôkyC`Li3 E!2-&3+@Fuғ> !*!CLOƺA-9018j 񳣣g=sb`^J6o"A;[#kRa[2MMCZA[JƵފ6i^kNOz}Ap JBKUNY~I'c>sWΌ6#).+2Fl!`eH :% ҍImMҋv&ѠK%~J^I>ޚZsi =?|ˌ;D˽˴& F n4mYWJFk FX'TʉKĬ=_KJt6ЎZzN D&1S4NnYh dJ^ZaÂݽ}&M#:ArƤn{“[^.Bb{&ȉ6tt;*JLm' d+% `.=cx𾢇5)Nz rv E`Z3; Jch3I۞{ղ6& v`N>J~0D{;ٷ%X1@ $pv9ڛ(ߢ:LvdS $Z^Q/l%Nzq ?(ʡ^LҭD룬V2.0ڢΖ%Ng|ϑR 1(SazJDE72b*W6V3ڤ}|[RP\GJO0(f$8"VzqM Q&m(:[_+ЅQ/ƥ`t-& ]s",D;5<ڹ5؇!N[QMEWSʎXdښeSZi9_٭!/^j٠wSr&Ku"[,9+I$^99yS)s"0_ВlO~(1!Lhi}%sKOu? SpPdm%ұ#ύ[>wZ T*]O}jvY<ď;/d{+v0- `Yv5‰r7w3f-=]I<*%pIR6Q$F|Tf<Ƀ~0cʺP,BG׶1b/$F.N$^Q1\zUoc"J3E4&H"Es .BM(Zd!La&*^:{d$ &SA򊔶7= RJ鴊W#)?Š"k"@uacqK`dmkGG?%!6-rAibgek(WL1 vY m?:W2g w,7)JAT:"TeR@mQ$("Ii]X+Pa`nn2hCD]92NePv(H,raC,-IjJG s~(X1:MIv`ޓ=eƖ<_PY2ٯ`۲7lzgܩ,$3^l .  1./jޝ\YV̐z#3'4!~C{vl~2"{,>ZTfz >|0ΧViɞsAo x_1gQ|^1 *@Opr`Rd"Ygl2 Hz=ۃ+7~5#iqO@E,EzYlzd1eVvOZ?R1TQhYbVYJz'Cf9iS,@^iY<u&<4U};}6+;+O9ځ,86\Bl^4*s4;%-[=24E|e^: GhruB: -ge#щs'= \b. \=Pk*ȗ (4,&î Y/B9h.&y˘V3C{EW~Y(`x`ISY 9||ԜB3pqJGD$/ Ek v:D4u@Syfb7<s./t>O)<F(b qv+x|?|EV9YjwyWvD8TSqOOmx!SdB+:[piQ(ic3;snSԆ$a2/,Eެ ک1<1ܙ\,ϼIi4hB} ϸ %q Dz,)|OY[O,V C7'6pZ: 1̽#lRnȺYLB>6yM_cs9o`c^/[\HB-=Le]اM ")ͩ3s,fmjd0ѳש.Gu֏ Wn/\gA)1 +:%|U;$3VPEu6^%^ ڰ:8 ޥza%Xdsĺ8$4ΔO[AU #Q.o (yO)2K0R% Y19٩`a}$(?.xJe2r'S5gm6eQU3_H.b4`R'REViԓ:s/ö  -])7ҙ`:Җ+oh՚|K+,fS,*ÒYW9M`lhO`@KҔݘeKqܠoa'DUEn2h= YǶ1oR1^z!)[ȜI*OKŒ\ ](5 ʓV0g=3Uqǭ.Q\={Z335*-L_jfEYٸB(oB?`zss,jϞb(9w%let=0.(1OqT2Ǣfy@H@K҈լf2 Ю\z"%c&֯K%LZIVVG~lpz@ [lXaqk$[5]BpV2 UpXtb6wb t<@)y|T(xqéF^cmbBo?x;&$E"ߦf^헋^*}sIU5&f-.FFQwݲgHh§"n=q:Le KZy EdEk9zW6+B8dn vg~"Á5kw`r>+ 䧋)W5\ `RC̛$\ 﫛N□vs]Yl4cfa'o 9VG^+Ta) RBZZ<0IK,hgV[I.i:锘l.\3|ܪ\؝ o|џ2c$? VңV%-7V_| ÿ҂3dA\BW^'8a\X5#ϒ-Ӿ$qy \:oZ3$G;<^ iNӶr',,+-ǨegYŀ4@hc`NY|b,n*uʼXC6 0!Pg._0l a/nsM]MV^ Q.^] dIQRLݖ#Tq'}-Xr.fo6h)2u?9w(,v}NJ!& i58 3 GH{' )5Z˳ך.s Є0~3&Bg<21?9=rҚ)^A{ 8+u{KϷpTzb,Ove>%>4~렔 rN_ލ\ҺL%e9wʓv&?M ꬩˡlI V(̀n?B F-)uA"Ii!$$®OtA1hA!/;[ '<{zgiSwѤaZ#Kd${:2 aǨZO܎KpBV{g` l ?F$l wFT8gjF? _€8dt#jT"FQ/8 Y4CެdfDutgQ*xUBϖfўX! ltZ秬Nio*&O2A)m 1j+rqjBQ$T쇃fDS[̋k\0\Wt_h)('B,IuCq 3LGz61hkU44e)?M#3~ W܍#fgd6Rw@р̻=*c@z7':^W4jhrZdD#}c!bnxCDN2vT. >q꺎Ձ򜋥:KFIIѯ{Twm"z2ۍ|Yn!5oQR8JVt_+&܈Wefk"4:bG; C m{K1+zOj\<2o$ZZJtE^[.~# g}:S6rzcϻ$\S JFnʮ pOkwr}?TȾ!OsÌ}Жʝm$%fy16r pwFtq 'n sx1P)"'*^%n'_ aQ:B3Tr$0!m ?>/m՚Yzq3h}d (%,$F !e;/'YPD~#w ʊ@+@K5΁Oxv;rL1nt !&DfEWoP%r`kI_TqED#05uY?l3 DIN֏lߘ`ȇ%:=Aσg-v#@_"LĘ)7@|e⡆ jN1p5V+=psBvҧHZC ~=="l2Ekm3p<;7 fڻQb/Lhˋ7 ,Kv- aE5 81h h߃}ml2>{VɀߒEJ/2H{&hS-ɅRs\r7uۭn8c(b0օ$C×y>Skz$'BӺQu/R.t{c\ a:'ߺB7~Z 9`KՎDxdIA4X@mT kqit\)v|,*J߁P87}N_нv/"02YMtLۿɏm R /ҎA̅[aj _ !ڟa+k\3_KQлGB6K&/oAPs^{!G''E}(?ngˈ}3+$H3,Ymhxr i[20SgRB)T*xGmVxMj;\;; U|=U)k۹'7ժfdЧMqpvOHFt? $ z &^Ԧ84ppBJd-tK:ÐF\7iRoRHo֨=j| hwӰv[i&r_UjڛrZzm>h%3Jpg܆7SS,o=s)-N9+L^He1/>np(_HYY mFhŷn9i3 KFlV Nt,(S9 8Þ&?@VP( g,pM/Ɍ֚Xoe\tSޜ<-ޱ'^Y&1Xl<.MPh'hGV/,w{2e|Jib@|Xt*-|(Ph@)*=u-"p.sc,g<Ȏiy5B{͋-_)Ɛ-_ǔ3>;)L74'H/ jj6o1EH^?x &58huZ]>\$8d<"e`Jd1򆫛U)bO8#)UY=KadQEbm=$R^IOz#9Da /\ĨR Mra08hf6Xc?bCX-ס^轹B[W)) JiV9 ;'d);mnpzc-{aI 8tz:#cqٻc.Zi8 k~yTf_X`;Xx]b v9|QUkl0 P~ *(MRx>k]qMkWb~imzQ6WcJ<_qc"6~B$ЂlC l Mhc%8>pHMq]B>8sr# dZxHjXb-)4 `/%3uguٍsMQIʔE._!2Ro':1E:bkBWZ4Q_N] By\1-Sr܄q{33m4%AXj;&FKDOy^R<gV4=B0(yur# qY|PH"'\/>@QQ*Sݛs$[xMWYW^w;UcAz.I3!} wе꺬hv1B9OscXJeGO1eTV *nu0Z]acPi#d0eڞCb&ӢSHjhlB;p1t.&2F~8`CcB4=>AxlCX "KY)tk:k)]`_ T moLTJ,àVbS8]%exoDlfl\ra@+4s,uD =˯ l؟aZŽG>n!:R]B,CbA[(V-n`R)}O)RF+IN|G:|?D{0=_0 Ycgn 36dob΅w6 @ΆuyH}NL :/="Wd 46TAcSc-*+;kLyڷwBPm&AM9paۆ|d9g?A@g0$l]lAd8Db-p> x?@+|&e0&?CvO=-G=Ơ]X6,8)kxtvzrL:h:(+}3] Eb~ ^0X]˲;9D GKߏ `OB3޼Q!-׏^M%Kɖ 7@uttwI Qs 4d̠宊ٛL |F JӁV)tegB'uu_N" FˁT^`l%[ن!˿  *5:sTc;gj_zι8ݼ;9,sC8bpSMQQDpaQ>Uwݽ_BU).+\`]g4 =䵘tG;{pX4t}X'3ۦz/ M?>)wzeFQCp/EK[oϩF~ΨiAp^l8G_ @ZCa}sFU>=,TajHc?"ag>9Ѻ?W+ Cy4J?WAaƚV;FwjN!R{s|!xs|YIUy!;m1;dETCY AML৵+9L x8{a MGLRq:X2Dzr| ۏV2fZ*_|RgVu g=Q~\T}l_-BpF VAqwc{$ڕ _k )fgRk685njg9N=]s3.yo3~'C 3feiLk߹IBqrSE {alx7loPdN-<"~dl ±c\]8ɛ$*!M6wFvI2{KO?B]2URfub8[Dwl`0 4s+K5(T3wIh9hzq/72 _ ֚!3a^ؗzz>JS,] $Kyj6 )Gj0b{3E8Qv&ggԆ7si~zOM}XKt]nn5Ը# T;2O] I&Xe={j!eLyi5hxd`q3Pn!U;V\~J+t5#y/Hd5<`,sEt2K+Y,2wJ3nI*"b4ep8F$GҜ@rIe:7@ʖT0vV_ ^]S`@@ J7Ц4-wO^Pr yv3;G8;K&> F،D5G>I=zʃjNIZȹ鳹eӿ0f[vJ8Hˮ4A)fQ<<6\%QD'rkrނiyrM%"f(a,;u$5C݊ Wo'V;F :wgY}U+9p:4$Y^rV}$81h$_Y^v΋ucdܜ럕8I!4óBn7%P\Q4^`ա|<{2h֐3'f :'"ŒԱFPN;}\IARZWWOei2r'xEL} cf]мWnEIkմ{hc/UAB(@fUS]^.#c`v]PRh`N[f_q-dSV̶E $kiGI&JnsaeOK?4A!B䝃1:7zV|)KքK΢F1V)#;B)nI^GKnfp("wx n<IFDZ-vCLFejrJ>5ۜkv(RLWT56FϐQ1.@M >f<2 tHUP5ALx=j=8{gu+ZfDf@oq $I[Fyr CJ "0܁ؓ(rܯ=.,odj=+-HbNψ(s8p^\ր>R^WL~[aN~ֲ`Mh =!ەμ.iUP~6'H]qASu!['IXbZjg}VKq6az-捣&Ί|)~&0ؕ#+E[葤 `z> fn4X[7(Pgc3e »H9hM- ^6B˜6&q-t[{pAU[oޡL'ߘ@:(,P&Nk>*X9 26x}|D5O2貛M,8Ҁ[i6_f8(n$Qb5M.`=S1OU4!2NAL^ѬW&g  ~-{#轍pB^ 5&.ZPu77*Ŋg<ɨ(a`@lI;na0T D>1E: &cɯŹg|&}EU^$.P;/)*[lEIV .tX1|G@ĚW;G7Dd]]" Yaxq&ƪo-]ETz|$Ot/ |b R&cE]#u5*yRg"X J?!͍f[w2&!"OίZG?Fv`"0=MѪzs5Uc@#Z_?H^`z86*@e0I!ۙ&y_ (&L9S*GN5,G)o,QRv)@ŐYj4̱FV!S?g- ;ʨAf mfvw)8! &^_QUMvBc11&_y%:pQC|-l- ),W+J d:{7w{$PoHw{.. S &!_hm+bz>DC~ʶlh(j#w@iM Aa,3&\3Ev͸ic Y?r駥-Ҿ#⼪)DXi?q'ãkRYf#l$*`JT i@+!7*\^8X\Xړ zMS$n᱊@ ?ӑdCJ<6*wnGI :t=o})']OHӕd-} 35Nݔ59/-k/oH|J1Uj>G@BE2fna6kEQKZ?S0x4i+#hO|1A ' mr+/YU,}v4J',eeչzRգ;'x/CY :79B1cB)6ap4jN Ӡ"7_1Sٝ.ЋY֊,g$ rD);uɔ;k͍o+yM4MJ.jxJPQwZ".8H|v uKW۱G͒3y'*n\F7hLjr mGްCl5_Uv2̏; GB OYFt@ 1%K&'Wn}TzVdvb.̭k-,@D0d+ReI27*txNy 3OT}B2{gU8:Z`B r5Ղm}X2"3v՞30(DX:_I5K_aQWnOkD8eǕ'0&Іp\D q? WJCuwNBYO+*JO:.EDX?eY-iQ~_w/I #&GI&ӽzy'⻷h@m_Ln4Bed'6~+Ƴ,W oGڭG~k۩][: c0ESX%O>i&XaOK+:{ m4WVL)N5 8K#OmB5\W4f'h0}3? :5zGܕ<1~GѦ{,JܼNY߯$}Ӫ#q) ]:mw&N`۫i)^ re# $ilL:7@م= VHB8 KOlaigΈZ^ik:d=kXʒ═Ns=aLf1^Vք3npO8~ 6Н̴Ugv o["\E]/#ņk/@4-E2VpEl)CIG?y~Cϊ!ˣ|?ڡfX顠q)I[L-U?Toq/bG|sn*p &9CH1^{O{$}[='`sʕ0/!Ň1+6yᶗVlZ$󓊝h*Od*{-:AypZXS@' jIncbvA!;Fʼ#>_y8W5k%t]ڔKi1ɓj"9I˅M]DMsT%5VF/zH \P-Ad`s];T*И_^EQLZz:5WyȒ8ħ4UC+ʄ#5lVYd@U%f^bG:u?,W5F*h{L/M5AO$N=Gw3L=#:ҁ]BDAVW/ ͑nWS=[,pz z}@ xJ|ׂgi9}$6w\'45f"@t\ER|;@K:iqs6aVN! wRCq&p4S:qE@17ĽT}/ IRHj#}u\Go6dȤ2D?S1A5tb|!,Wi#Q|+d bZUMA9&}bn'KTeFV%(FݍViLx*D :H#X ,|{;4/!:k;Dӛ~G.EL`t/oXVV-%0|HSv+T~>5Uڋ0ʲ1U/p\S >+%~O9Edb/Bt7`lJ`VVßA9ˣY]ؒ}].OFC ,>; cS 7G¹ԞLe(jK;Z~0l6A{sLcT0SD~'f'|z7ۊ m0PivlT& FO{}a\"CVr4>г~֦Eh(g /`vEÑU 2 t(TxKBƏ|UKVr(Z+[ Bӣ,fWs# Qk:~ A/Az_Kk[  SʑK{0|C0ROS&+R֞&:Q%/?oj?N,kk7O1 G:{w\3mGj$]ׇ1x[vE&#`r1_2_UR8`U=͎4|8r4KP&|+0Kti%9[kh)}Y%BkMI5@Qn3ﱘZ' 7<)"{f^fA HS qM1(24JmcW} |ġ~3"ep'W`Y&zPmZc^Կ?*kϞK[qEKMD]+;NxK7aXhMI0Pop 1GeGa9$g Y=B6Ge dG_zX~q"m CqB8^_"<^s!f<Ù ]4hjc#l {Q_rznkk$z #m,1 k:vEfiQ]Z{pCY*61J߻$EvRp}@% ; ~s}#o\k)adRA т$o'3N7_d#D hlQ87T(*GsqR<IIyQ,"sd-B>XV-LύfiIC_;4#^*αtk'{>PpQg^ '܏"yߋnU+*'BsuF:z{bNZI(ZG#'4J2[v'v.=ʪqa!"c#y['c +i ,֐mT޲jCc =ƨrP6Hst]|HQZ:#* #sWMȦ4m#$XXcK4dF8wxUwT.q f9pK3<Ϫ4eȵq1u;׭\o[95dߧsѣ5dV)8j&:g="-1kX@L܉ ._;es{4׿pt"NӢ4].zg0`r{@Z{KQ6oY2o'u4O'p#bslt4m ~.?Ngnsa_?,C)h@Tg3z8_UuJbqsПA:`TzUmt iҬcj*"z;Fulp|R]k'Ȣa k }%Y%Ce/])~}+Tbpe-gܨNEwR?HԜ%PjSf-qnҤX5__p(k72OtK>0`{]E1PoQʴ~e9:J1NBQtܯB]Y\醶ՖrN#<K:}D`zYē*DgD4'C73&AcU:%ܘ`W_Rk9*}Q҂DiɊEK8SC|]tnXCBsl9:*m|z}恼Q(\~?]En9s|ivs[R7hV{MՒq_QdMCQ"qQs DY6.9 dnUsC!ȍp$p =~.dO!rRcɩ;O$ǥOOTԃRNswY 9<.0&a'lE" +HPAw.D%%Y(vo>A6w'UO%#xx8RLF!=ka4 '1u5ˎMO%;ڮmLJ#f+@]ޱT7c:$YDz1PDZ8GJ  5B~n !c{KʍT1qp4'LyIfi$ί辵9(V ޮ[Zo<+ Rsl\ u_f8MR@ꍨaVLNJR# Yhkuo _ 3$J~&=Z'=">x׌ ſA󪖕)r5O-\Hup'[vh{*ೌp(| G%Xʑs"ӃR`y]eB='2$ww[(ÁgE29Kd!N qo$wUPjY&tq_`/:/Z]$%O+LʹȽ5:nFJs_W,6*y *JHBAw pTN<\tW[-L:<bڳ,J.IŊ,::[R~C8m(x*c']M2Ŧ5Da0z\om!3NI/wuY6%Qf*t",f [Wf4a)ܪ| B z뙛Bjc\g`e]4m]5<:I{ _ǡ ĦYz"$z*B`y&2bpI#bk$ćSk_[xۙ7>դ3zJDmlD/ z7T,>ʵqDҖ ţ2gI,kXcwO`< z[{AݪPJ<93&Q3&ϯ&tY"JHNO2i\jK0fK6'NN3Nu}Pst}r+`^gS3iŏKY"Ub8u{hg)uc϶8i\ U"s4,JZIά!T=\ߕS ${Pyv]즤囐 =B#kgyHEm!$45ZLƶP.--cv+TL^4 ֺ#Q;I%$UWB_W2́1 :EX"mb'ΐLe6<}Ej 8#窶iz9 +`ؾidQ3*VɽgT"B!zFT–u|>5Õ9>/`pINCJ~HbO)0]"s䞣M[F,4vQ&ʤTES"5l.LhS^*CaUpW4-<fD0Ț=kN5[Dm/ĔF:MM+/zev4 dȡ |2{]WHHx7CDo kjj.SխB#rc`nUJ[^OX, _Ixa/˝.7~pGyځMYhTHUauT4Ygp%A1l;O^}q0>fW1Ƴ +S 6bqe&|<] 5!*' x~6،3x-.judrp6|'uV)uMeEzmvN='bHpwl*vU(:SG ލjšݲHK(pqI5L+Ic( ԊY'ՙ8{u6, Ov} |n2tAB62҃a]F(]+CZgdYN~Uuh"K6 :8T=/rcjȒrUAA7/OɿP338M=7#.NWK"ݖ^h[4Z#*ۉ FmM]q9Zx|E쩑gel7xַ'Nz ='0dnG7sP$Z[#8 ޏYDz3ȳˑToⵆX_B3Z|6Km&M4J7)zWkN5WZ`l%5ZQuI P3 94gA:DJ]ߩ? w@!ٞ/RSmd^%[`{ܻ!ˇnb 2%"D ZFkWtAы`غ϶fEC=3S!'"œҡ {%X(x eL^EEmaVŏ&F!uogcꟗ<`k /I5ec'}3 Qgzʼ>-}VCi,H1=o\GF+'Khvb[qf4MK=z}E mCǩ"#,C] Z| TĠb,:Z>~~Kw3!9݉u[{+y_6dl1{'ؤa⣛'wra7dmŽil9cصMccl'f<* fCjeSR6 ZRM`cOק?fO1e9mYn^͟#>u]JLZLWJ+?ɳR #4I|GPq+F0/C 㲞/I2/Pu&P_^4$3j3]!FKralA8fedxs._I 퇢y0[9D { 8V_H;_G^FqO;I=[^$WI-71UJ nﬕū\ [HpТĐ%7O-4WL2p=adeoHIn [0l;VMZ}4l1Ns&+㾪[5^I[N]Iɶ6.q浛՛5]'Bޚa[ɨ o.{fiϐB]ο['9d3N.V#5 Ɛ4$ ;h-&!30֮pKr_ !>v1YeiY<8QAnb#A, 0pq }kǏad %G]hlI1$k Tօ{A$;>$LĎJ `/mI 0WoLom~|Q'a9ĈRs1#C[!8L0Ibc̮/8ůk.*_"I=72Y:B[@d \AЂq!җNn%rc2 #g5i_7-cTp dF{NDhOD" NRO;}V@瘔 '0ɦ~ÃBʯ-EtFm;g:cnKSU_7 NLfSv AT6p#~w Fa"_aso/w^_`uy #Y1&&扟T=L⻣ ՒJ1F[$]}KnB F/8Xo< 5`! 8CJBLF.k@Lfw +?;`uA|I9QZ}-0~o55"&tdN(U˫ vv3=927N*5R۳Z6<4 c.;Bbi75WOGGz_)K}2RϠ^G ށFX +֢@fn64vǜֵ(pߡɂ[[Qu'GCz 6O;n轄{) }r cqnkIv@` M/]N<~[z]_MB(GDA6c4B$q >ZTq%,iQƶ7p% w~~;YFe7:wE"[ yZ.'ܼju)>a^d~g9~%ӄ3_13!Cj=8@"aáɁ5Z:-_Q.XJD Vb[+& _xt3*5tCkc-f"X:oV0DT,a#MLwd53<׏jƃ_GxOȗCG{ofũ+}vK|cLJ Lj&Q fw{$'? XR˂P؁j|y$яOd.U*q2_΅Z'󋎠jyh{,k0taھʎy3CEL#r['_69 /Z3 {|a3*D6ْCC`c Jq Q2 Fc џ"Dl~6sڼв*Co5d"U$byXxǁ\/e]-0n lbҼ82rzarں,{YQaH)[N{GlB+Z{BwqvEZwDkx2+֩pMVVm`u[e:=pJĨv)k :̍XR#:}uaVucvДZ'ēӠ ~l&'AvD,o#V&8Kh6Iցjwn}B'C)̇?f3RVu 6Q%$Qpݖ4FflsI$fMkoבvuӼ1᣶㡉=|c һl>[A%V/KW+8@#9uaNᑔ4m)!%Һ Z c_ʐEUUGGc䤵07v r޵Dِh1rKUC,ܽeViclI\EBw o/yDܳ%>w$7xW[:2xZbc<|h#W7ImJZbܚ_loD#yDUSDxz1=yM'( !XB=,>K.G(5.Uޙ3ZCJ"_5}绂  S#WNz, hڞ<.gQ(exy_q(y~hO ؆8Gcs0~Xm΋H$o$bqևv"Y3Ãǃ[ސ/PBOh I Cﵷ8hM~G_nJ5F IdN:Ald[ݓtR7+^$J\nBbynK~AS!  `֜e3my7qmr ;+Q xE(0E\τhme]Boۄ t Pܦ~J]>oXatXxj{>IYNw2wǤY)"_v%1ȹN.ĬxBRsm$_rSWNF9aV{C79rLFYJG ')N#&Z~vT0s6"I?C:u~Z%/19>,X hATtfwKXp#.AS Sg^c+p ~|ꓖS5yZֹ'R ls/1JF'@yj|іlTQKczgDIKAKP$Q#v$:-ɳ]Zd$GJ?3D >oWTl<n$&eZe=cw{p1ŦEk4wkϡ΅7Lu*'e⷇0&U]*Xnkpẉv1i8 k[ 3(Sװ^)~1k|FS,ӤҗB#,#ġ!22M^恫ǃ\zܔ6itݴ+&$H~C%cHIHtCyEJD݈?`8m_=Ve$YMQju:D'F$7-gH$Xd=_-ۅ;Jk)xBScDgܔ]ICP#<~I C z_ °ݷ>jf!8ԛ;\eG#[h%B47~="ZPYA:޹8HL[y/޲QeHHfO$϶|0#")4 o|Z6hY~bz.=?ZX_%ɆõUa~Ã+#Jӝz%i '#X{0]T= 3aSZA.Iċ| ПZZd>zoE f2jC"uHZ$`[RuwK4׀ l*EuQ!xFW5?mu]6̓A'&dcl™u,$bYI.~KSKj%T7鏃&awcA 9t]yNN9LE_׃FkOPO,n@6'O,`=(mC}7R[yjҳ㮢i,譸sy6~bً",_9HAGH#v3[dxumQci]ůrEKA2!H0ϕdXJQcލr\I| w3 W&+oT@5p5&n96ZO~ܝ]}qMd"x(Bb{n1&$0-8%rPm_k6G]18eIuȤ,&ܷ`AY'Pxg?^Versn+dk}|\sa`閺M}s8k5ydnSI4^ tuf,ޟ@f(TǢ mR"]:|q\gQ.`1,PkpĬZy褅<~F#/jV)wYrF-LP+3k(W(pN}4nT')7q`O {~Ɲ c58Z8=nq=V%rcʜ=fQx®76{^c:$ XTj}ވk~.-6RBP7Z"Fbm B(0KrK~ 2Jԥ{(^J*׼2ˆSlna`;Y!uy5 _Dނa"G@t.#-pfs lF-:mꊳ~sD:oQAF'VJLqAvKeQe)Դ >Av3d+߯vpVEgQ d֙Jzr#HuN䴰pA]SGL!sC_^ OI2gv0Ig'+G&XZ[zzfnXM~\jJre]S{I8vP 8Ik?äb6"q PX&PY0^;ZZ+v#F J?~ۭ3Z1W{:I~0yV3F}$RD>'3[3Ѣw/: .ɀMw~e@PT1j|97\!D>Y$h3sHp" 'kQE_b( dt|DFš3@F]d5\$z&~m\ϒwE<'!P4@ɝIR b0 ũQwe9W`wގQmylC*G| 9 W$aB"}*YRޖ ʥ_+9(k ?0x]Nix>Lz#PG ޙfcTc=錭9'1ee97{cgFQkҵR?4=kIÓU/5HL;o}G%nH\YDLqR9޹b8^huX@zA;!1IeT$d@H{t@mN CO1T~_!DTh{63w|BW[mZ^}\ګ6KfKj zr/8)}MINwyIu[M7 {d=9sTV\7[Ez^?5jܢ8wD7zIi走SiдwĶ%+?29g? /:~C[ጛL_N7>CDY&4*pY7o\w;^+TȚŌяmoa(%o:2(_!Lcr j3N"Gܰ+d?o9f)fXJfZ౏]r-ufΊOfP|N:b書Kw'e `j4'H Z4_څ}ܣ]#OXڒ"e{y*vB_T'uUQ|/_`M|& ib7!B2$7a6.1"M mt7nLu3׼u 6&>3`uK=3~j2!qaߢ BA׈_ٔ= 3]F(WA~[i쮐7R>&B^>P^sS:P,[{I˙ȭJa@qrTMLE/M:z;/7fwOovg+예4D}/Mp֩:5fg0z1~"Gׯfb +:KPNEnǮ9#pC*&!Z__>&DcĐ7z I@bt_#>O5 +WqjtN0k>u*YxJ3%0M]yBX ?z/C̰R)jHl2Uo:SI[6yiٞH6f yޗb!&)olZ&6-%) o/andkY&Q4E]Bٱ=]1Mv|N邜֑iڇK4Ȱ1o`D<QEKR֜ y3k*&vSxA]u({ē~ qTspQSU@}٤/Tt={tzk߀[` [eeҿmj )eĒTᙖةnߔ?S"qR@d)z%XØ"e;rt#VpIE&/!ۦ4<#wu|D isJgR(]if"5=K6;e%Tn !_ *&y} H9cFW_PFu" CN'a:!^7p ઀4Nf:Cg  SM;J;|*kN_Syk.;9A=(&\#LgƐ9jی!H+za̋ƤOkr/|!PQw]59NZ-wLBU}Hǚs()'Qa=O) k3L)F<*H9fNf<Ć9/S}ak >j$Ayw`Tz$)U]~ò+l=Z`Pߟ[\jI,4\z12EͰsĉ\"goE/ȏI}lӗ0/VY6;[0Q\uDhqY>9 t+="7笺X*//9fSNDnmHBʁ]Jؘ^<:.J_ARx*peLB~q2,W,VPq%\^.vKb9Yv@%]j4r2ʖJCbi˥gHx[EWcQ]e1˥Cqi@n)*};I >#O OܗilK%BS;[ X6 r*PFhLlEWCwfg yn3!7ҥh.'h$2?5dlrwoz*6I!w'-CLyYN-9zW8PT5Xo5q GGA 5@ζ̔ټHEi.>H\!}yy2u/:|DED߬ &[w7n{ K8|6ሢ0x/0^zAJ *ӛ*o^/= ǘwr]Xbp)Jp¯%eԕǜqּa%Kˡ8ݣK`==œg}#GL8]GS}j`-R+ Dqq %&k!xH:g"%ghCsGvGp7Z7/M)nE^c UFVY&$S*m =;PbbL-naAT<іw5{IS԰McZ3 z9Z|HF3lZgؔncrU6# >LGNyϠ4@l#CꑚBQEfAUXQ]aWfr.&E ] ޟ=޶8Of7dk'y_9_>Nk:$"Ct\-9JV }n!Q']ej{ %wSgF3գՐ*HsVyX[398\F^Ob9LC.KfķE׍KmI܂5}lyf`$&764GqBz1= V*W\'^` u^'~* &+8Б|2EӦ! [`a(N<^ZW9mgm) PҐ=@r ]T ׃PH#VꊼnKl"1}‘|'Yy /87Rpb(#,GFё1:C9;lj -{FyRpX8H?kY. O򝳃H`뫑8Ym)y 6c4U߹;Y {@ (p&qysd\l$ʹK%c \SҊcpX)ΔI;4gNOHtAs۞˴Μ,qrzFN]8f luPSMw!n/f{h T!U⛒<|U#4iGeI`rp]Nw:^s"oTIL̚#<"d(L$\D[$~Q؈l5z-swCC^VlUb GDO? :ʹ2,xT,a1^Iir%'F!(QLs6wG".ng{aUp9y!'*CLVV}:o{F7ANq:5{yx#S AnD)o80WY+*S:o 30Q7Yym8m%`YOav &m<|O,xO-Z Mc~k`JF[d l!#$`7ӍD N/Osf3 ]OgJLJQMWG8ϣ7e qӃ\g*Ҧuϭ8 1ߛDб- F+ c p x!Wh`Ԅ#Dcůĝv_ yt9{˽G j m PangA?XÐX{(ibЯW%2g}[3g1_eUr1NyEm%e2\"6 kۖ2/+/%$<ѣ;ká.Jz+Syn~?РcX':_n^OcIkXf(.*˧u:eVη!;ILshʲzuIx0CA*\L.Kb@'r(㏹$Ož2yw9(0g [o֗SilkGY ݒ3s߀Xb/ -:*3򵓪,+fuSDG -2u^Bꑰ,= Pʷfqw&*>!*>]r AQ;:jqq٤Ft(̿H2]mT}O'~2/bXds߲ރqD#F*^rĝ9Y`*TFK'fiܡs{\aRa5C“mQ}G4I>81 4اFQ*pzkoGߥnjϰbQٟSQfS QY[BF]|" TCBѩXF?Y`*%_q8Psib,wMWwֻ9y05`XSj=Q>F+6YYIr1lD C o?'uhM4߯YdjXЌ֨ioГ bĺ pm߶C[ޚ*:cu PS\2bEK^3$q6mwp#F ۋEn!wSۉKg ˵<:H,CS*'SDa~H9_$`[%' Hqx <@C'mV,y _WT"$ W5*>0 yxzI–J_g..]m* Ϩd4*02R ybpnoA7xa|h6i?*M>pM:t~s{&ÇINg6&'zם;RPtR)>klh56Y.1.9wUkzN 2&!$I uI_x%p[ 6R,$yNpJVDeVznQV1:,eM!Y A͉EC\u +(M?dfPYE,rFqݫM*=)8Nxaם4oX R,rCv==&D4nH0Y^y/[ӷ2: @ure4s0{~S?rFMnCXth:[.@yubۨn ,N9n:5$fI\ 93!8 K^@``!*,O;G;Uh+Y"-byx$D N8e( 1"^1[K2}Y$ωᴓI80Ko;!_2zՉS%]|2|+ OVX/vXR4U) kp&*X51,3OT ѬUkXq;ށiI 5Thk (2ImR3eS1&L"ςy:φY9M_\#d݅UWQngSA-M^3iqVS#ZPXi _@a[b?st^4t_V.\R˟ ]DV wbbhC ]nτut&.^ѣ;7sC9n#:م~7ރ@gVPo!8m J,2Ws} K|w6,*bOI[8 {!_qW((2F*3"VV_"B< LW+SCӝGjt-Ɂ.ݭk,`J ܑӒZbs{!}X&:iXFͿNgtN0JE"d~Q*7{/\}/z,nz$ZòjЄǬ\E,şAYȆVFW*] _$ʿy"TF5w:lXaduT*A.jgšr!M܈i w3)AlBᾕ+EXO5n!pQ.*R JF``!MU=#tYd8vJPpM#b 4QzD-hQMm uz2[('cs.}l %VQ=|Y2spYzN-tr- '__^R v]E4PiIAޮ"ՋxU#4aоE1. OA}SC[jj5vFViJ3>O9D^ hVBf1DD!N# $=16<6Ldy1W*2X۱hi{AaX Gu :ςJ+t7jKu=B|w'0#I!B"f5h`5vy_%22F8Y"SO.VLD-j= MtloVv:X:M.ظ,4gy̴VϏG[hv9Yz!ў6Φ9'0 %h4" PM1rήG8' <(lk"l-#)gg\p;6 C*D ¤B#iÄz*mby6vmM㜆 n}F߻4"( HY#fA ӏ6s}ʲx@t>#=KY"=Z"~s_]T48&hP1>=gKILD%Q9ʷG L:^VT˼ZDD<3yׂ#~t;'[_k} f2?뾙=O'ci? M_*x5t{WG1/e~@O2r&0x}z+iY dV{Lḭ҇RgDQSL !iK |-#r(j!]):DqR1 cf</7O?TlAIbxAd4鲌pEHa횺gf ʆo̾}05" Lg-5>N.ᛸjd\[6[f'KQ ;L{M\ : }A=wM8_R[\Iw-9Y2FOZBKץ0- [: W{{f@X^23sAsss322[ Dakj>pIGQl0+Hx0}bɈG&HD-r > 4҈vAYP߮z:"XxtAR͙nW< <ЃHr- 7s;.McTND-2n762m+:x3FYTey=qɻǶKpdiFstd Ea-e jhG(JÐbBkA7)7y2].JV;7gH(H~WBʨ stC,g,pS*]aOG4T¥BWգVq1t((; 줒GPT>AM V4^OoQ|Dt7?8s& UzNhJ TsAa A1'B6LuR?w͟R%BGkac]nb 퍃8%=JGrg(KoF^`^^Bch{m>K}%,XuvVZzޏ[Z&GlM!)J™/'L*K/c>~@6<c躷Ѣx4!PG@NvZq7g Ű9\ y8F\JsL ".͡QG|͙?,VGܕ >jBCʛBmVCuL6k\ ^y) GGDǥQ&(FJu B|G`܌3>4s5?m|мfB b2]jHy{TfeuI7!VO^l2EB*S?^!×ܛ)={?^,]dzW8co=m(EUdKu {tPxR֌BCJE дy)œbPf D<7k|y6ǻpr=/ GXBm/ ͨJE!qI~ezޡ+&vZ&x}E2'N]9bGჾ6J}%l(2v2Mu ߅ͽwz9Ÿ3N7'i ݙl| #'? ߩb_6F'0>YSWV0o'%h! &{6K'z#[Yd ]916w'Î~Yhm3t~U5Nj-:=,Ym^ HJ8l4׭^/;Vm8g$G͛QZbQ:3Df3Y HbfK_a!"{%#}kq=@yJ(A7eabs뀊ϩv )e=Cm#D}(MvKB)SkBTz. ХY7&3+Ôm~t@~aV7Z2~[?͞)ҎJ3~{h,*_bqN6,^Kw-Ar'/~#Xߜ3t Ep͒pa-hn\^GUqa(Aca (YNyMJ_ސB(C^JpШ}Yܧ[ϼz`A9? /LK d1.`'7䘀="^xpeo'sAW: F/_h[*ԖstQ<eN@~xS*0Q͖vO T#fG$LZa6<< nm`>t#2"n\(jcpE!|Nƨb>oi1Ug4l|~L{$ 46mnO50=:~EeP=H /ଖrr*} YϝM+6} Z6 zl/ʐs6r `EܙL(LP` !I!tFTt*ݒ`+6,vS.x)rk[v~v6~s$gl& T=/] HN0q~.SWj-ШМPyc0{$+^)!!cL)3qLYmDyҦ3aA鵆 Ұ̐ `5y=Nq尃<_29k C0}| hBj^TKыKtKY ?KbW+Th H+9>DHdO @YxXmB0( ZwE筕$ryO/iС#] 5E-,J؇ V꤂@PI˶ZWlÒdsB PR+ٝ"(:WHHz_/lWY[89dn &J:/|A}̏3Y_2e ā^{ۡ6y8g()*زţTtaT,Cŕ/ðfYQXwʼ'F.[<ŧ:Gmp 2٦c0#}  x<+sO9;7!-I D8&5 ?92,: $CJ^Ys$'{c^}03H4FS27 ㌷ft %m6,a7a҄ QߕZ<" WdڠK-}eVu Jo횈 e0{z {̡ma_$vy87,` EF Ŗ@;ʛM3,0L/Ճ2,5D  hFbEB(!wljmep-41҅ nsᝒShTRyd|x5lr dɼ/9Kup`[%c\ŴiAϏ?f3-YPMFw֞syaU3PBcsGh8 a9U%i=:'Y+6OVҳ<|ז!$bwrn 1K?ۘXmi", |<2w~JiϢ3k)RxRWd!-e@| * b*M{ ˽* lbN#I'kNLt 0^o[=p.]ƵRuBMe>`]HȮIF.*#UV"s7VxJcУ : Ee,ukenP w~Yҽ@*Ci%%>!Re;u[_C`. bw?K}t)R2;a_~,2hpm˗.O8쫥o wK,aHh$2ey0-LWҸ6PXz$بUNfIrF6TOVlS9;ZrhW '>4'st:ًz7"G%6${vN* V9eX@[u0scdO6F *O#l8>z]ںy,~KÆqbm+|Jo3&:Y*}a`!#C+Б9!Yb`Ǻ _;*zohh"@XhP.݅蚰R@VzQL.;, (1+?k7K?w{Krf_49}Ce>o4kv=9$ƞ~eQ8.c$\bHߞq%qՖZq'QD3fҷ^٥%pg:W9 IR2k[;O%vؠqbcHxׅuTZ-4 z CeԁD-Ngf}ozRr?Jb &4Mb c ^l-gm2y4p=IxA8-d/7OQÕԔIFz ½K 1tUT uq\7Ai}mn2M195^\Hb=Z#rxGzL@}wjGrGJޟwNrR%t!a1ҙ7K1<ʀP 0Re"h3d8xoÞ<4 #+T:h9mYV(4S#bm_KƟ7e;ɔL;\T=d1nFg*4Uc y]E >a2c(6:^$u܆W[dʭQJa{XEAc)@/[/ͅ !4=>oZN.Tm 47-eP8+(}wV^9-#|Bp_6KGWbndo#s3G*kCGV_˖U#\XNavv\d9dxRm+`4Qn 5GX#d\:tP0Izg_@6f"Í\ 'Qݘs{9.=M6~>˝=`ޝ7RY6N"镊_&jh,1;ZMߐE)dӝŢfȹ[EgERTUf9.;э݇D67NJ"LQE;8Е8jAo~jXu&\' մz"'zew+h}(g r)j 2pSUDϹ#W#.m2s[ #ใ}ǻo諸N^iYMhQGtM/CdJJT>ٱ&nMLXr^3@BRWnr}Q-Nuȟ@Wd\~~aqRBZ5:$ދb\aS|]>Ǎh$&,Xĭ?F*oLIq5xr8 кhmdUvkPO@P~5_IL~XA:p"'-[ҠOY.ʂcn8>p}or.F'nE)a-"NiLVJ}EeOI(FJ*oK@Ϭiyn;>խ( !$P7EjI$Rn 1QO݁h0+ P@}Et-VQN>x/kWE"hi6w7 M ?]w*oJoJeJ3-u*AcVЃ0o!sZ4"QDn7 G}e# x)=spoYܵ!β*d?+)&u )kXH?MyVSq >ӎͫjQw.|f bD2T N%dESOؤƄLZ9J,{U%γ'P d͐Nj)rHN4Ȓj [f.x7ȾtCflg'i~xT&yfk ^#3YlooSOJ;J.K9O0iĮ&4Ozc4=x3/֜)G C"tUc_ܰ$8720͉U3'MW;*cD@IײeJj8DBUB|.;/f b CWT.iꧨ )YhݣdFŎwa "}89F !¬$Ƨ5#stXAT{4MUVx DZ}f)dp呹W2]D\ىdƼFŶAmD/L-g" ̓T;jJb]aSD-`pS@0jTt,|J&u?kj} \tK"\ ]}x$;_$|0E{MEo ZFM>/GgmAG0ߊfƳ] -W%u1(nsn \W`fgk {Gz0H n!ߦݘk,h\! !<-+l0+< H=` RNj0KP""o{޵;vuT߻72dq߭ 0]:7L_qI .:;eMs Rzy!DwaREi}Zu=y ~N3;g8 Bcwh{S~Y  vZώbna,i׳h%0Uc%15Pi>--¦"l<}ЊSC,LkfYG+`&}3(5v/.W!!П1)ϼ,?O YC6QMvOݍ857: q+}U<}{(D0&U LA.5jz5r(j0STK n'z^n_"ýNv:V VEJg'fF_>nm m Ͽ]<[ ⑞zיwʖ ҃˻ n(0p-=߈3zQIm/IEY pAdߤ#pxnA%IDtLm(,RBy(Ń|[T=Bh<1[EGGjfQ^ܜc)jۍQSacJ;N$E'}% { c7ֈO/JeB[l]ŚjcM nR?}bl+c$%f:;! 7w"u(KČbk).ڰ=="ݲb'zIpj3DƏ*6Ms*AܩDo 'tْH|k;n{~ XKzS OBݍ8y*sGGs7|~+O~:gҙgwp(̇i!%2O4+{M{|U]ͳa`u;Ly갠jx7Vdm;}pY?`b):p y?% UO\ 5Vv)i s1$8%\&,="b ҷs#XB?UO@L_/BZT'o BoJbϚE/:nNEZZsfHE|b{J 2#Q @mjs9P r{nɋeΣcg6<~K) 5C]ig=md:2 ٗؿ%" ܫt&9?at7**`]e l؋BA3b޼a̠WUDԄ?VmG| $T^Wud_$[ r!J&RwHݼ#0]gp$ ;s1d$o$<9E<$+S lw$#iѿ"4FAv84żlVCmWl_>C^pm֕tTAr`!Gʏ y`}nŋi6路sZd9Ҏ*=Nf_^3;C$0QǖJw˒:p)wh?K}j !O]$c~͗/<~k|Pdu1'aI{F~tݐzo9 ,L!1TǙv˸DM~2cL1B,X!u'#O(z1e0h/ט(EISkJAL)v.H ޓ̽K,tWhUw>ڵtAjS`j:P[{R?ә 5Fso;΃Or~DaͧR~\߹(ޤ0&}?pR-ٕ'f-xjem*)"F< 4J'TRT6#g >E(@DJe΢46NHt{F2"|)dEe,v +)7ECrfy/K_`@Y`l gAEGNpbǂOԎNiBO}"rǩ.+–8R`ТA >}bv%Cͺrvd7fc%CgnT;lQdJfOAemKQ )_c$HgEnXpt/Ew]Q@x¼4 ]ZO9G?-+* JrG^aVf8__Aq(L;>~N~ɍOq(Sw ^^+64 JH=i=">Up§c#)uqt(w' w$LR_MfA׬]Lܰ+&PP?+?^9w7̆]q9em4L^9LPn|Zi^bgӥz!XSIQx:K)Լ|dNv`&㸢.DT+ff+o2l]l1J5W I$n%L'V7CWuOQ%^͆rrm@[{'mY(ojDѝI#tE$HR_r5"_%B$TE fsދ`}u&y <0 M*c/FlOv&l/XgY뛩oK}Ku-4*<dzCAc3/eF-FƉ:'A /,_wOJч#N:إE>+Wɋ)Uc.l6O= &%?ܖqV J"z͘W+L^'>&A vhBܝX#eB㙔7zaFƘމ/'E%Ja %zԕM =̕}B9")38BZvˀPY!=KhDE]\+/?@J wV2jKXb!q[xu,)_5oG(JD-f^>f.b&Um} EndL)]l8? bձcŲbp-)*m.M"F*u(x \m`"kWɀo?H(kQy4K {9D=b.A7a/ᘦ;c1΢LnE)[j5 E5^'%;Sx"jxBcN{;CI" v EI]tLPPD 5BiY*YIFb1cEZI.tU0vP>jM]mw}(}~X4ڀ'GFg%/v ~ 2&6:!߆|Cߥ6~(g8)F-xS4`I4b'i6u5.etஜ,PVo۴4d=40)O 7oUNT9(}T*u-{:QFwn){ӪB w"pLҹwzxQ7-Esְ9yhzJ8F aڑ#16kиeX (X7!Ԍ }?˴D8Ս:p2 {UaTTnޥ\>gra- y0``yҭN`s[递Mۊkn%qK NDq ܍ 'b9U49 XĻpfBF^FtZ>s|Ю{`pNoh@I;pvҹְ&#LO9}v۠~IC^:d京wsK4x5Ky%aOKz짿:MNۢڡw7)֗AxCfnTfnq"Xo}-!Rx+&- !^4Z N %V&@9:,U>KNPH3LdӜmDTnR1[aKch pMM-I][JBmV̹s2n,rPje<(кy(1qbvwsx7y ZP2zJw5l_!=T+5;?7Ԭa?spN:t!99wnF}LmiiI;e-BY.Q8{*6cg UA/B@͇i\9 Ƭ%f}#K-4IH!x`cj8{"kp- U127H(Pn)(A%Q\_RЋ7O{m0qy/'[f6-[VT=% #LkTݷS`m}RcI-f*|q6@~_{Sv'辡k)@(h铰g ]TUlJW :1PC/췄i4d<1~7Ƥh< ]FxG>+Z}UNPGkNj'Vxnzg@vo ;tGz_y*(KLn ħ!9%y}3$1o*B/s2[H [ {롱]/hRm͜V(}/Øwfoz]hS)m2 ^ Nh_$wK=afô(F47%b3s]/ʓ1({ovyi]]DB>f(9H7z mc2σ7l~W멽ݢWPx]:ηMCó))mђ )ayH$w;rҁc^DnHwX<,PբrXgXkᒝ>ꁃ&spHmɐQ& Ğra%v+Ju>)T_@‚u('0ۺ> Fl3 $J\Й@>3M"kD _R?ӂ٭{׫%M\X|PDDS:b힢vXu2Ѥ K最qa'i&V]7X gRc\FU̻h(GyVȖbeWkTdV~{!^llr#z+'3̖$ K9.4"^^RZ7 -y9Ox\A{WeLDF8\Cx5E8%TXW7,%.~`_Àtz<1$V$Wi$![oWk@t}ޔrwfxB5w6ë( jӏGqHv/'O ega: Aq,aBsaX?\?9QKL=6՟h(*-cC~~jt3PICpc3y&^PW! jHs g`􉊔(b\k@%debVy|b2ބduh wTJœTǩA%S BF#VH3k({uaFqriWMMV_VMg +L4[<~LҖK2|O1 >O]<;PZ1:LjUph.kZj6Sܻ7FݠH K%N;*#RBA xX%7VH!N}!TA\0K.X zʟꐦ olAÒlkRpV6vtR%z{Ha ܓ\gb >:-Qa౫|1m8  W~rLP!xseu{ȡ{9ħd7BseZuXpOS (E!a/Ug؄?Vht*{C?R5ˇ*l r]{eHj#YTIxMRol.+9VW@u $6a)P %E.#QgoxM[Ud@ĜLLY[ /UGjDb$Qf*|+yAZ. ,+mx цKfq au]h%#>LVY_GƯ;w V2G\]沏$D-dыr~X +ztօn?6Б筦Gfr$z'j-((W|(^!FRWNΐnj4W>*F+B~ 1P}p i\B) :6H7G'dVF]l붻77\Be W;A-3ӟ|µL]D[BQzBM]v-|BS:lc4W'ԃ2*b ^;Ј$\%vlxv$u||yU5$kŃj;KM<q) ~ۂ>XUw"Ry LD|Hߴjy]RA]CHx#AI]yջzhߒF= i_bGK>#ֆ,q!!8r51%]fB5uanXݛe0wB=(DRPSLi4[tܬ(62'G/Ϊ/ x^Ÿu+= ^EpK fC?˰sā&O :"4pP=:$w.M!9`fj- yb v`+L{6_p*y7QVQ}ͻ#r2#yR8(ʩ~m.g/2:y]#,\ޅSeY  7DɝTLL<,n$[Ll>Jj8uÒrd~ 7l >4)\4帽E8>8ђDW =_kM*?Cg,[ UlSqW=W}c`#HL( %kzJ@,䱕<MbX|J3U0iWܹDm $K~(+O )ԱM;T;/c8]kS0[nn 0gۼL9X8t-bHeK!ݣLHrYĒp[o Z/yQUXkqvm)q饮Z NLp5$p:WA5} 2/! 9;Ij!nHښIwc$߅ %> h}IM98†C W<: 90,]pMV5}㈉yUba/0K9tW>T139XFBwG}\,`& Oͱ:6KǹXӕ[i˷}3laģ<  Vf ҟ>x+?Z^/yZezxY\&H7^[JG!ͤjꍵ}d,mqnF'1.@ ;, 8NMɪfHEqt(;wS(N~7|)\V -!M©OuwM.q7RU}EI8!'2qC\ RJ6knSώQf,RIr-.N 3G^f6(37/5&Z8j|'Q+b&AҾh*l1`:þ*Y@ o_O Fb7Q_>'|⨚e+Hżu?!+KRzW$V kK~? T#itD~B=O(0LJ&(mZ7z'/_kl']x0GE-2ONVUЇCac6ճG#y = Ģ h]/ -ȊXܴMN4gG3Ia+AT!91޵igrDu]ۃ)7Yg{&mpKBv電ÄxPΊnqJ6x:Ed g<.x-/H3Ir1$Q>mի6̓ϑt˔v A4G2g$iPb2.|R(w\nUM^+GEB$2#i9z Sqk|TM#qmobVzv\#tlM U*61ogwY}+31y*twy"T5u%tgy#A?v1D^Khc*BO!#CNBGZ<2oN{%h-P|:FT-V_c ^$sEZ]"%,eT֤!GYi %I-,Xߤ3"o浤'qS_ %}]4xYW@Hp1;@a@ڂ|6I3gl)T_iqOVڊgb_noVll]RIqkU(Y.B&:w,bEX;Bdr}Q2M9dq76 &Y,Qы< gn V,^|dL;R8".-xXQ韞{/f, (wL`?5N|pXvŻƗS36msQDŸ_=EֽV`'rjXƙ+JF{7ى*JiZa&CLiYƋ-? ' ޓڥ UʖN"\D] -EHYׅ:P@[_%Jc) <sr -E #곸٠8[2aƵfG Ҝ a@U[taweZwӑLϿӜ׮S#@hP V(+P/RgR=Ҽq's0 ƴ!{=(U!WbVgY}P"#X)1p2)RA? tb;4|ΔǨS`LfwSՙ^Y._q 4>+sV1Wѻehy -0F>nȹӕ, " zuS{`~IMs=k|׫ުX%< +M?Ho[& (ןt2c?wUʕL`sz=kscn.}([%m*NfF ]!gKJ!:on#}xզQ w&>!ȃzB=rLu7xhԾY+1PQ$6FyW,W\͗L⪹PfC_|Nm]6C.P 7s _"m#8] Y[ڛg$ř@4>J3Z>SJ KIj?ʋozjmncEc\"*v׈W;_JDPu;+b7:']j}Rr5 w;`0ǂb_M!_DN*m | Tq?4J#UEA5T uYe4bc1,iq!r"]dH4g1_{yli^΃,Tr ,*elFb?.v<$1zpȝiFg {ٜupŜ?s· l(Qݟ_Ȫs3>a>3?_!꓁,zBCJo̾.YBBiW TGCz 1xN~|64U8h/ٴ o^ DF6k}׎Ay/s=LJ-{A`SOOXx=H92["E\ϐ-ؠ_/Nqپlx.0 N҅|hݭi>;! 2IcflppB 2'Ǔr}$J/Ns2C_Bjsp4Y@i(%f"Da)w.}峉Զt-v%s_3̇J*Z7\slmbC ՉIHN;:nDSE '+碇k;=?U4 <)O$?`I%̑xAnmUTVU c˨n =a^L8puʫE^꟤b`^|֚+N,jSfU7ХHo^i봵ݨ2]C Tt-^".%irǁ[ NXWVxR\ lΛZKŭ(5M {=j0u`\:.ɪ^:眇Z`XnuiF܍" rW e@J )yM:1T7Q;f Aa gpbdo6XE^G,@"&^w?pƘۺFQT"ƅB u'>L {kxtdsn4]Ĭ/z4t+&C J~8J?w(cah37l3{GK}&z5Wٯ"h˺kPj|{rDD 潿<<52xޖlȒ USOC:J=.Yn DŽ0.'Z-(Hux+={_s vxtJ,dGE]|B oG63$ͦ?Ů4A}٧U#GԈd@X[6iS7lXn335ywI,R}Pvg3ҮwKR0&2v#Kg)9} .̹`ЮYrf k=.*T(Y&Kqͦ_c8(p[' ܻL#sي1փ+ pe(&^F$ur t5ʽìg 짇e5.1eKmPcq&4j5x&_o_m?@=3pVTMAš{DΈR&9O汈r@1$Q_hC܉N0٤OB&!@bY3 %maz]pT~A>;X <{&})kJR2ngĦ5ſgHu]P/| *n[r!ܲvI7? nT4*0vT;VIOU2v au$]_B;wt'9*[\N*`"T!#Sx " :5Q*%?uP=X]CШ uv]&_^[ۆ*̙.~̩98B5/#:.*)bͰ"Ƣ&3Ll.|=V&mbE_%%9ms'e98w*˗ңi]ηge2R33 յklg0=Z8PhnR^lgz¹yBZ5r)̽+8dq tݴPU"!f,+VqUA /[¤a]b-t/gT X_\>-ȱTDԗK'Ɲ%T]A\ްu!" 4EIoy.PTLsFiͭɸ<{{|D(PIq\Ov#YoTU>|_;uC'DY(Y|)vG|SG+qK9L6OH0'Lc!Dw% cQ34$xSFwY&:7QO1@ʋqf5"K @~96Lyݜu7}_N R3^H l#~Kg5io1 lkZaRs%z 0JD]d|Cl9Ĝݞ9Kn3Oi-&~̍l]DCLq8u=FZ Gh#rHOIkK$ JF`[imˬ lpG|x bVƼث+Ua<[ ӅTWN>%<󀄪r'צ%WgpUDG#=%7; $y T6TC𲞈{_z[Iq SmwRb|#]/-X.unPk_֏vZĥvճ7*;3 U Pۭ]> P,lGZMu4DBfU(ӃzT՛1,̫R5>ŊgWj}ew/ oFƜJ,3Y-G\0&@kLΕS18!-rC+36~6Z ^͍ w: tfrF)⸬6?@X  ?n7#dn㹼DAIϐbN(.pJPVLjUi=2)e). Y%Afd% gh$ &= du]$K`:M0rԆ zH-n3!PX}V\k G3X-Fܼ@ D7\rF \heSH$`l\`:i0~wyη!W"Z5ٖyRB:ezSQhP^}TvȆh9 ޓ~TT`?gFlgl_ 5]"B5l~Fj R-aEkshk0q* H#ިycM"Bqi]+[HP;4*hYUMH_P!(o";.ylJщ.5]YE'2 dѽ}6n 8թ*oʻPid rc:_@X-'hMl̷3`F눼3FvYEA@l]PK'n+WliuV9yLjF8k%{b;"%x B ~tLVRƍi&祃zst Bo|.NZCrZX cPMo+X#=":Ͽ,} Y +,!@IUhJj ,.q t}=∘ta, (,d+Pk O/yTEZy,: u^wDIwz?i"^E9)7%Y :,3ˆ&s"\$/;Wf]Zp ODCf/T+sX<`ΥS '^$v>ofľǸNcdR _*Мɘ_We2LHOײlБFt{ۺN]kmA{L .AHU2v& WOIQW:y&@dJ y G~8Ba)K+u; m/ J<ȅrqr[ 9U)p_UMe)Ԍ ;s e=O_SOӀRuse mW +h2x{Xf?;[[o7d2]zFۧ5kڒ<ˮdKb,,Ĺ3iՋɕ=Vb>%nӪK=/h0#YHBt yy81G^PI7e_0Q ug0)qI*1oIUG%_.A S?p?F Xr3n]BoWIvśH=|ܼ a&k)b&z`:3Xb~ax0L0T OPF*$hdK{?QK^ٍ 5b(IڌZ8>uX`7s}~yq.3<;ͲS"Y]a[iq䜆XsG?o~;Y މfHMbTLÏ~(@vY2R4(n[dGRy]Wzm"q2T耥|y0^n+E#ffw]صeA.gDmخ 8敐\id؆ ruT/lcvJnm`&b ѣ""GOj ܖUTXpIAM%ͭ8{D |J>l[/ke"\fz࡛l쇔u=2msޏV ps\{ _}0 A["<`KuG+,t#Yn@XYn*}h4"x#U CqAܨ"2 Kt`1Y$5~ҏ3ήaӘ-JgZۍ/Фa9-wD:s"pܗKȶ]+ݵ*顉Dm&!7BԇfUmOoj bދKzEL ЭMIҎ;b;ѻ ޙt,fLO#,?Dُ i>Mtk8P>Qʘx3|=mao,WBRboKe8-X5XcmL+UjYÖOc6"&%.܄7O/XS/s|H .Ey7@om7q"S·  'Q-(UURy{ ֝Jim9H"N8+|;Ž #A6f4ezj-b_⍡#J>,ϖ_70/V q#yyUvpkp0XGHmnetK` #m5#1hIJA;Y&QdF.rNhSG· %̻+t.ٗLU3ZW ۬ ͨg6{& ^Sƪ#{\b$vk8􀢱J!>")C{J` >"#eRd㻬vF]}Nv2I AY7b r5 ؤ@^pnh:5]]aH DZM E QԀB`Q]wX8)"%ʢNC̒C4kix0z;K*H[T,Ftq a6KRf-eL++Sjj_a؅&0S`/Td].28=ܷ%|+ z)_54a*9F4kV/>`_ؙ)A݌oQ.YZs.El wAd9np=/s̲TH50&G"]"łzgDu@״b;ўV0_͓<%>0_' ? Ǖ''g&?FSz€n4.n?Y (wIĩNوl-3De\WHg)ei+l'Ç[7*x+oz)82ʔMWw=aN26|Ե`٦z/P;0,6֍;ZjDg5?rRPd%Q4(#HHk36L:C^Џp0ws|׻.&NV靃@H-Y]bd7n񉎝GLb E3iG*xzc ^7^*=a- [] Aw_({@faI$nP'J$KfVwK'nykj u^b})\o;dhu $eރ%ww" ( U1>D̳'۩߯&4%1x H<\<;Oj^aE U!F8K%sZ}ocv +ĴbZ4p|!)(o@ EBn\ p%rT Q\Ԑ?iܠHK(]6M"/jSs{-y]BU}(PVRAdR@+C?lDG~@p.:r[τ'NN'IVEML L=]pKC>T9n3`TaqѪFU j>b y@M{}5@2jU]BJx!Doĭ c} ߒ T"0$Es4c WL @X<3'igs!n +DW]3HrIQ-ǝM:^Tr7r_;'rqS'>#6ktЏo=\ +[)GZqvg3Zdz,CDu?o#QȮ㸌j7s$ֶO_:D(+U@FhT Yno\uMQ`D'ߛizAqr]Zf;:I72F%/KcQZM {=CKK{TQaz^pKlds/7 Ov<,T1UIX7`Â02Ѩpmןqng>4\SjD}j7X"DM+>'1R*h.dio[L\'ܕvT,86ӒLWK@ PoCB'NSU .ƩdzQ,HC/s#$Pϼ=/"bvH%Sxիe#yh&9ӕPE5bVtGF%C!JXEX]Lk{ۆP \OԒ`lfZ-U'H٥ت Mxsٚo~?6Hfֆk"x]W%[҄? L$ kӛ ~u'&G`lB:pۏx.%(s-إ,2YTN_bAA;g& 0s=z3 -;TCzݺlPVIOլfar J*M` U*'W͓AehW&rH$Ny]zB>p9?=@w}S:eN :ƅj my8[G!(w,*SMߏàKا%!w@b {mc tj.Kaͮ {}_cX8Z`!-σ,npuj_LmJtzZЕYYsO|igqĘo^@fܔNMܦ^_Eқ%.m\ H$8F(WRA8뽻lG [WJohsl2KҙACY#-z ;u\aB|U8n(%RA ;] mj뙡m ]sf/< Q^& c$軄.鳢K|QCs랺E~@M+b7MSVLٽJYۍ)q!(88BФ;[(~:tNs=f~A/wS< Xw.2P qO94>Y{[K4?"JsH"ZPʽ鰥0 ۜHnNv *ݭff 8,tS3GzX\cP_gAC'n)Ƃ[1Ì*&29fɷ#VmjWjmFA]5Ć:7.D\nz8DUh\v R+n;,랣7S^вSsa\@O䒵YF+_g .wwDZh[,kaosȎߵ[T1]CUkgN(- ^/j&Sq^\/eTFRQ6~鍣19DY7z0r\e>2)R$fspZnF K6D L/V7 qD€ӿ*5ST Ąݜn_.EXDKPӠ}40_U k.XwX. vsf"Hkf>.L-W{.((I5Eֺ| 8x!>o%.x[#=-{u&p̲ #!~(CmO`ߣ:TY# B.4BDaHˣКJko,-X; XCHٞ\냈4.QLDZeZvLB> 29Sv1^)$mIox@~↟`_/uQƗ,L?Ƴ8嵱nǿ0זޛZL,/QPݍ'q&($ ?[%̴aFN 揢Hѽ%9!KǞz0JSdJ`xGQ -V?vym~NrTo.]YgwOYBTN! aGI܉:(xt6 /NU'naD 2,vDZcxQF gN"ÿf99@Y{, A  FnptX58MjrGr`7܁2ʩ(xI~9V\AKp?7w "CD}\R\9ΘI<[u 7ljzh!K(%yT%`= M{EoϞL;Mڝc5FE <( B$Zww'hx4: })OAހs[f)J6d<. |{Em?徘a ?=S f&~pe懌0QD؁1=V&r*a!t%\hv9>nm㆒}e>3M>/ZWŒ[0QmKk^7dTK(?1~f:*&fу!gVxA0 \Z[0 6'NDFVp[(fBM{+(n \$Bqb7\M[\ $pf^>4}l[d_>A"1:n:ì}+rZBiG,BItA rCC>",~ sr|iZ1kCHl!WmPh(xN4_ Ao7?u˥}E8D5Zl:ၻ-H. Xz uKCY'oBT7b` .J?4H+j N$l!S{R}RS<\ev&7R}{'ht $͐;|lv2wVp\ܮ{j/LgEG'&sC zzEQmD3uB=Qej db&bў}Rڌ^LzZoS^8 Xg[Z4I{1ja2~EG_g[^.$Cp"1'}A]T/ AQ5@Z^Ms۝Jiӳ5V#T KVn278ܡ ZkU?8cܚ쩩2 %\Cx#lkf8ΞG#⌯:BDiUTIU De#6:3^9ol/=r8i8Q]vy/F[7Ss%}!(\}kC.045拞p3 JS{YDɴP|jm;@׏vs5B|`XLBI&Tk#u,s1In5 _Q:BhWtx8]ZJսc}f \O3!\Epxkj>9^$5%5(gX,Fn כ\Oakv,ﮤk98b$e`YhuCl`O.fۯgA]>V?iW?Rnim"Vܯ7r\wlc0 +VljgyG^,{ckkn8 (糭 TcfVk{ϼ񭍟()X$>^J֙!P Y9\)"X;o{'\օ3/CjT0%-iIx< ԬX*Xskf5kӹK3 * )ZR m/A.!& *砓pnjn{G]l >[\13Ex2*l伽ѽ~Җԭ RA\j,sȗ W}{Kȷ"G.-fJ< FO$5K`/Z͐\ЃMa Ԟ yD="h,x+#ӷj#9 Bpl^.-y(=(9stRdwRV5 gzU۬3>}{^mRrQ67{;9r/MsUP^~~1"RSpk.$ɝVJ?3au[Iˇqh^ r4ڱ3_ /!yF -yE ?Pw4zĕfu*[%|o"ɠy3EђiR{b J{f<~H`o^lqy{\LoN610+l"Q8]*L[Qr WP~|" re}F*%e65- VRXݍ Ә_8_9bu芜Ԯ5/ݚ l1FLx '^o%귨>QtOw[eu736Gv`Z tXŝQ1/fe(F%cNE6S|'xGarЉsilK +| aPmǾ4#3v Mx<[\M涗3ё:&#*nd/Q)Uޠ^'|>Ec+\|&9J-ѿ@ .<&ewaVjMYб *uxoWh\#egId%;*1TbL]蚶8, pfk!nhbCTAWƗ9[3]\PCQGE^Né&yz!Ba6L3Ӊ?\CPeUa 9}x.1,;0^2Ĝt^3ѣE'sS5ܨ ]hHlYwU YPb()EϽ-͖F<6~CBrϱY0VGKJ=ʇ  o`ܜKYRɳ?&Vķ.AKpI! F}l#F"\[-!hPv-y^BĦ bgBK5} N` phA"=6H#V*̧!~c%qTe2|<ɀ< '֭iupgS/qtĵ0J6q,Sp{ܶ` &H}N>)=ᵶYEey#mI=m-3$5BJ&A0=49q≶OǧG !BAQvQCJg@ },-{lv9D^X]AyYʪ!`3j~$_CDU m/fleB .վzO)|LySHRFDYMw:ɳfaP95&>&WryX6G $4bMai!7ITYBμW616xu/@YWFyRl6dPQ-`>PEr,Md4ID=TT^jI{BFb$yߟo k\$X_uAVlqޗn{SNV~3uK{7DoZڤ%VFQ.0eicRx{4u3ȺZ4!7`/\'"ldF'&JEܲ}H;A~r.ăNG3:KPn\kb×f뽙h%U#]ag oO>]F[m"tw^HQ^D/ITR*4YXqy[FW$FVGLYhAF3*U87.'Hal,QT>(ĺ٨,nVd)Z}/#B8ߒ(4|WԂWp&즼{xgLFW+Qc۴vx^aЌ5 :)v/Yҕ% `^toUf\:r +0ÿ*-Zˀ4J S}Szw/#L+[MwS]f xRȞzzW. ~`&}uzl[l˦91|Gs"r@" I3nR]Gp򡊒3sᮖ)@/QȭɔJ%j(8_a7@bc|Dy;ۂF_RR$OAؽԾ##uzFH09F7$B@Ump7x@>^9t˿n=A.i~ jO * fj tpoϴD@v]dYIO)2b(oLc~ JqP(ׄ뮒eA 0:dܠ ؟eMh]_W@@(-"|J7b?j8Φ.EptI1DR{:oq>2!5KWg]o$gD!Ty vlurQb&l2t9iC57:/߹||~ 1YHdOtl2/vyvUG4p_.&)NAL`*Ὤ顕BؑfNN XO_,!THs>1fc:b]AhJ !(J+Q=\sHk}NebkknxiJi-4Std/{fl8ȶɄ <;0([ߘs>[V-sT-Pf>k"ȩ4#L2_|۸NHmSJG9ٹ4q5_b(ҲW< S yфC<d8jd!q'>V?HqvZWg)T.,n%Tbz7)X/>NIEռRI>g,V1 55-?* V)g*ɚ]i̴}U`oi%DD=^\ xLkͽkyyj`\$}C2҈Z=˳ז=yI8sj,9ަȖQWPqE.P=%'(oψ$7ችJUWʻ^z758σXޚvdMЧb]OU,4n̵ne,fQWxár GYωow4U@˲SK$po=T*?;%G"@te *IeF߁#~%Kfݕnn )#j J mੵ+WRwR~dVXy z0uj #B`:[jEet ڨV8,0 RfS00Z jϞn[捦FᯥGa~Lp0ӞO?R,l>4硬k'\P~|BG+!<%шw+qZq/NW}@xlIJR2! yٚx&0}B&d f!5uӊd?v'=DEL>5R E ʁ+7ҕmTIZ.4G\Zh̐FljZo0Y_޿s7J>^ЩCF<6MNCܝh`ח>X=A"A 35=KWn4Z5}|y^/6ȊH"4 C7D`t{?jQgU{^XhY}m dt5!-0KqCOYqق)|9"RQMY >>DN(=}ן ť#T/._FU l5 F<9߫C'JI/:w8I&3N aa|-زqF̃ݏ H${t-;Sbl`g| 5F Å´XZW^<,np=y;_/[g$"\!߳p\Е2U*`-s}· Icp9!kk[{&k=>T/e`f+j6L+Rv Pb`Bˇʶ=1+!W|:n kCG݁|KVڸqX|/b()]4"]#(ct܍<PD_AA33#oxNB oTvӀY< .CxlEt=ȅ0Mؐ9 v w|<kǠ³3}[_Kt yv"C{2wo5a?p1t$:`_J5;i2x\\Kg Ϟ+x1#<ᵼL̪-k}cMUOT0߇%gܫ&Opq_'PHpdz>چ" SY>$d&dz}Mڐ8&nѰ{яisF!dڵ~5]G]3ֽ|Hq׆#j?ׂ|,072IVSR+vl0Fa K|ij>3.V[(._G"R/[.j[ْ-pjԤ+JNO%ťF6 2,SPQӽ5~DZ>:'\к}3z!͎}9lbL=GcjӒz6 m_[1`Β9/VH0{+{uOQ~qi@l? v1^!0:^>Acrz e;-lбKPPuUX@9K.]9΂@< rCH[yCےƆ|!nF9g\D'eD~,*>4;֜ 2HlʾΩpزqW*gp>>a#;-<>8.? cF*Ը5-/ogP}Es)>3J=:EADLr zK=WtxBMxmG@/2;J 3;CHIT60z@/ŌFD]hE&JV;/E]Im҅KBrKOwo%ูV%iYrnXꩯU`lpHUNY+'7L ѡ7>O!["7 kC>DY2:bhއ(" A\"'s)배N BcOwy2 ^yewV=LoEǕ` ]x:r3 ?nXU+BGc|H@+2hڊCTO?z跿ӕ6iऌ(x9~_vl''gF4l;W-l9B OW;At݆ Jsz{jJ-V=;}x?Nwx\j_5Uౚ*fS2֢iN@ϼJ+Qꃛbc7W[46[Ϟ#m'\Wk{J6"&nAZfKX1iE $¼KxVhj*||GlWS4Ե$▟wpgO!8 4ʪ3hqGN&+*\r@\q+JPU=zbF(8u’hj ]|[; ⋦rsȑh$ DS]vRWo Cd^_$r2M^ƅ1*R*bO׳cCT!j)xHzX^^4_u_WFPLs&{J3T/$İ'2%Z\ ]7\qJW&lG.9'5%tꎘ*l*]SQ%#Ȥ`a4Álp+aŚj1|{ 7iDu 0s!= N4d>V5: 3ƂOYsJj@Ǭ4516u :ҩ?}timuY "%ƿvIR "Pzѭ4sMñ/8'$v/PO316'Mbl(xP6!;O|_H~Pƃ@6##>zil ~~'>twsQf0(Ҧhzr(Xd(.#Y3̟[J '!Ih7\y..ҳZ,GNY|•7YgJkVpaZF 5d^ &4[s-a,csfakXi'~Sa!*(a̖y!6=+,WwO9EI Pwu Y +%N2i0_1v)/*ve[n_27X˩%\QqA@-H7JdndH1,Q[_(D)8_0 ;woI"!lɥ2r]Ua^$쑋W ^4XP] UPi(k0BPh*%:b qӺО; 8|sN˧{{n%K&y1ȑ2({CХ·qW*8o>([0u}+%nf>s_HWsRsCЙ@=a@?g6ez}N qA$&A|[lJm7__Ӭ &ZSv`qa{OȜW-3!7ը6w<Žv85in̮I3Jxzu5ʀrk ,ͪFD05u\9a5W*zP{0j"؏K^%RbG W ! HY紽sv/b[ HahoP"³h!%Opo"s8?:[Fp_?p>h? tb,⛵C4T~< R- # Ʈx$V#4Kqbj3&`{3HsX6p)rP˜ʫ^S׏+1Jv+5rKʝ9=JM60!ӝ/VV?D )b#YZIP',c8bGtI W1RC69[B[563E 6Q5Di&O"& rtH% |)EET]3~BߪBNx̲(wwapu*V\U'Ukl׊θX`%Bžn dh§Z 'S.Ku{&;Px]0C|+!1 etL#aݓ"<2[e<huyB'Z2} 6f,lL,ڄ-7b 4GVCN!C$=:*ǹ ^f)gO"Y|\)r›S-uO{7n ߀U$нMw6Ȝ(ɣr=sG}!yN`{ÒʉBңADM 5S#)G J{ѐZiQT"b2&=YZ#s];BT1FD ݯryu*Ɨ6hH6Z R55fH5HX\T0"; Ǧy̪> 0 '"0Z" b^U_XF՘S[W9&Q9[uvRЃ6I%t!>% IA#g5;8Do[DfzVTyns6 *+ dZhX3}-c:m.,z+udP@N[vx.?D;Er8CiM2́:c9 Fbϖu'|2A6g2m#(rkKYq",$]F; Y]uWE.R\/0)_T%sÈe6q"jukMG)ա:ܥ M.󽱘>NVJ>@,x2yѽ}+KmI gF"¢82e*߰&LGRu1,MW(4#SwnfB_;K9f^ETJEA!f~rߊws5Db |˂1Avk,? <>HtEt)l?g]}WhyS&ܢϦDS_xѡw,l%)1Ħ!t=?Z &wWd]J1L?&qWt`(7kd(2z;iR =8]V_>pQ=Q )jNH`"1 Swc+ivΤc޽_clHW'msCLz?O`By(jg`UR@ͫiF~[U5!w2T7kM84B#n[v Pt لم'gZşCD,?޸}Qn5|kWY_6X{!q.OX-*xNv&jԱb?J} !N~jk7[?xj{kF[]#>V z.PPr H3:>°.A,-sճjޑr,5/lA XTZZQhp,D=j:exCٙАYX_H/ԄC!qZ 4(גg0^ Dtn0j̆һK #!;@]ų|t!W}݄{/׾} _NZέX i>(ձd2O/0Ne9{Y r j@[GN3ݏelV 9lHkQZO˜kpiyNTkjI T7 Y̿TY;ͺE A٫< trFtp|MzTsQ~3VX[U'"6bZT˼}{^K^ANe$Yω ePЦ^d[2NZ)C@% .ٱMF+D2` G$:_bƯ}z>F~ l7, 5ePjoc Y(>FϹdqhO3zоɳB=,.\<ʯEQ\@8*VwDqP'~ֶ 37l!׃ ޓqİ*e*wY@4*ґ\hMJLP 4B;"T'!!P/ʵ}D绕ZW`%.;ld&43$gKV_4;|yub!rvȜ.Pg/7Y q:&2]3,d }D$_AmRI ğK_ds;O4ayhP,_/H#$CG"1>BXNSOMA8nU_$7&q3x`}b^aC:K:x[hV(Q-|gP\K.- h۔, 2e`dIZ~jE|=#X Ve6/~''b|Dn>P3\*f3 ^ ?b?HvTwboE)3V|VDi|D3Z?*ee"_}Hu;ތ{{e5HjW.YyHEϴDZ;#F}ͪ7p//UTo[9]՟-.?xd8tʳ"䊋pӲY 4QDbWc| WMk=(0iwyB}RW (3.o%U!* kNǛJL$֛ \;yu/+Nf(uD\<W w!o2Np%x 7YN^ rL]ZYUԩ?Kknpˌy*x&, QLO׷=5 )ϯ¤Ze *Mf ĈƏCQ$u;4CULHX鶱#-,)> &XX/ n, PН:q+Mq8Mωv[Pۆ~t2MO{3 LVqBiPjc? a|zii!U,qnF6UU* :pxj;nOe@hTZTyi[k'+H"PۛSMC.O[4-y]`{7 m2²N +.C2Z;8@M3i\[ʍ,ε.6Xdq'і7j/՟KaN VdeYe`dCDk4Ǫ7 k߬;A_b|mm084߲a~ac^wPR_LjvItBn#*b5!nr?Φܷ aL‘OmWUTUKD]'sueZ K Q¡{+y tK\/CCc*5K(4Y_':Z z?!U uPmFh@` _~ Oℾz^D,ɳ tWZkCKmLYJ*'ɉo᳘*.V1CI#Tg.f lGfQeNKԎ7CzDIEly$_@NkIBS&X8HcGb T:3`K6? [rFX4n㪜,t/nsED%sx!]^XW L/jv' gfJAƮhqScd oS)hjQ ҿ'(.6E&bFqC`qBI+D`>ƀ(.3Kps50 } AR.8y\۾u.6XQGty'E./񩈗P<>A wlI)YxJo kܴ 27r}>R7tkg["=A,ؚ+!w/fQ":}CC)'IF/gQmIw̖#,54Փ,1ٱZh ǕI4(q`I)ՈζpI- !'9WjaTwKh\dªha~5\ 9w*T/Tr?Mߨ_ǝTQMƚs~gD_ 6o^PH*VKnK0I5 (n KgŵbiH]{ZsWPx2!"l{ i2T})DtMb3JbVk;pwևA;5o\֕lg^/ aq$CPba Ia< \\72Z: H6Ks¹t,}`>s\kÿqs!S}9;HQ00D4[x+ǒ%yuaJ5nxqovk5%@ihu_ f87l2^^i-u sDZIG#.^OnX?)ǹ xxZW9wbZe||\MJȽ/xֶB3sOKΤnk7MI8s(q%@]2IL=g7jfԥr;rɺugK~$qs^Dn⨕Ty#'&Yx ם~ģ<{( z8R<\d"L1ᬗ86(?zfT*nJQoTe ũJj}@s޼ϛwz!ɔk\D54֠AIxK0qo, mQI[6ps5ih/'*c!};'8s$NP c jYz W_mT*)V#©w65)$>Q4vebY1B ~9޸/HysUBi2r_8KW z`C͋MVEUA%RT7I|A@-*lkG(O{#mV Qz+o< xbL[)ZYb@]U[;ǶϡxpdY @-4 $w>yl z4OEDy2ocߡ o8f  Õ]+< Q:N_H0&#X!j@ >\6MrR-2K%E0ky *@)">X[][s|jAd)|8wLd{]WZZŖkwK;J[R׹ ,E>^1q/[eJ~@?3kP_` IVNRvrepq1TxJ`Ta?d(gggKA$"aU1A3ńwI&jD:'3w Ev{h<1v-8g:8}lt,ܜAR׽Ur!a!!lŇG ֲ>2~ܠ%GkX v/nqmB$ep̨'Y.9O>ɇ}njOy-WA(GuG<|'6*W!|#_:単]vߥn#30oBS`hNr~2.k,n^;Տ rE^| P69z*XTJSՕBˏ<a%W ZV\%<@dFV^pm,}\ Bx4& nrԌ=E鄗:l@ )7kKqzn 0No{v\A^r}kщ,hݸZJ˂z{6E:wbӥ}frGN;ZϾ`7,I-9dH ݘ҅U;ec[0Ď-9>a'9NhD uM.Fx@ "\X a:C&( @!Xf]Fco So#Lۥ#3=ㄥ_>AuQ& T P1K%**G3)M{(B`bZXrL>9aj`*,0Ofwd ED;:BƲ\;͜bOf$8; IȌ7?wkǡo3زlwcKCh=wO@iB=ʔB9'W<2+ x䶙%1=Sq܌?APP6f_0F76[]S4^+ѫ8ܕk`m -kʊBӈ@Ffţ/bkRnk@"@ؔRaT`~Uu ">7v=¦J@Z|ٳ lKpqZlY~o)fXjbFNC(a+:G;/ D@KPN_UKGOh`;u1/^T5z9Dѓ(MUn, Y OߍmD(!-C^λϚT>?kEH*MǢ,`@ hIj#z 5.D~++mqQMH6<NN iѐGϟ~0Xiƛf*Wj`ϲ3sˆ{{ v0HCdkE+\磵ܞ(yFUZ}^VlU(0Ja{-7Z|eze_7{8IǗF]VYj74я2\w.¼1VnLaDV$gI" D (pNEç{7^ޱKg; 9{VY,e^ $l& 12lQA7z:4brC/mΏᓹ\-잴X@y=Tn2Q1_9Y'B*f@ a;f+NNBM_^tH/PUrYU"G7YKtxWAdzx fC5U 1lv6.: ǝbtxMrM&O^o< 1I%M=%G+[k< /X (#-m1g>r6 kN"d,!H@qh7«rD[]Q7}#m5ƪ-}V|y_L-b`6b.S0 u0=DSEP@e0'1*|DPYG؜8T M=nƃ_QKΈD}yH˜-EذM4E͚ERD װi>MeԖ2cjDkg~\jju*W-RT@ZBI$R O_5Ae t<&N;`VlB!9i< "O'gΊWc80 Vzτ.tI'mxM+hJ}w`Ua|rw_}Brx臗a+| S:!f\[?Räff6} }! ~O\Ճ]$W\N{ 39[!C8ZMFSxXx`*}YIc2_]{w?Zܭ-!ҍsQ*78CnBZ}}~T,eIS-}.?}sg) ϟl]7&gd\ۈڲclblZr=*$y&G%FrBt삱kdsG?(}imՄƏ&Ƨ,Yb壺4av洳If !Ǝ2aPq3 kT\ajLhJ~bdH*dX5MIuK=Ya -uQmF)Vi]>xvBE+TՁxP+X$Mg(}Hbuvv oԬcdg7Idyg2kƠc$tF_' @b,\#: ۇI,Yp֝0?i㔋؇e D"Vz"-&?R߸]sߊy*4Bɵ? *Ms܀S Λ+e𭪑K=! n;U2d_sRn$xT=4~M{^t75ߔ-]Ib#:AFHC*nu{a@c5A۬^]ݜ&K!׋}0Ss b}4siU2 ~\NdR1fxl32 w9M-QQҘ篛( Ӑ-zܿe~c:+9aXTi X6Xo֭o`|b:8 N,NsXO F Raşι'YG!Hha눙e_e$).:mnw."MͶ}]T8lJqY)eu ѩECk{ y =\s,ez4 ÝUc ׿Bv^N';x݆ea8/CY3΁ 0MK9`=]̷ge%{+L=ܳس`VlyѲkϜ<~fK\-?K r);r'k{Qj Zyz2Q?+*XYg->B<1'(^0Lq}+W 9Aw,p#PtG@, &k u(Gi? ~;ԝ&BpP:ш7Yg+=9@ 9}Zr ~<`n>a%qnAG֟8@tR7;2-!iՒV 79Ðڄ:X^(Q}64ej݁_&}-yGGK*=bN/¿w?q}1>%}C ,wx4rdwQB#=#$p 1Rqv-H) h\doR Aw G\VS!ǙxCLPUPP[Eab߭A6΀{>OHVOI?!m@ԏ$Cə0Y(םޅ6߫[x~t4$/^ӈy41Zpw)Cg?JTǸ>bcs2S>J̛ڿ#u1/P{SVm 27&`Θ{&= T4)>6M3EKbjß.m,+߭}Vy;jrIUvtW w_/Uc+5ВqkyӴRsk>G. BW 4]&#;lCWr -sؕܣ@g:XB}i3ܒi@Ǔ&. ǗNɘ'Y~̲Ee>:AZN^ |R'-3',V@N}Q""4rŝk7\}r *I%/ǺW."kY$)"Ufe%x7q? |"2at=w71]'B%Eb}y3AS}'`q!L}QkXr^D(t]b P^׏b- 1}nuGNV"5_5Z4/uokA5?iTY¬2F2p֝Q8{fjcr`-}I4{=k4WI:q653K&I*Var$qP(1r,p^+\yN[#XuؽHmb2PqaF tqž=5qsYB. H*h?5 Rh?,t*smѪ Q꧓ j"ѧU #^_ 9˯&w~vۣjq';Q[&q31Wą2{[̢uA=~c;oML'eՌɧT~դd=>sdWYdWrJal_v2I769\o&!bN>gUAdݤ=I{b~C%&5b_ pC^~FQ qmK4teסg]iIUy- e5MHzNzQN){ѵdbժM6W<VUլDLk=Ǿr,ɧ9hq!.0!v/ s*t@Q.3]܏9m3&gj Bmqj;;0my(.,b5J~)C\wծBf49z*䁆0[v ;6B~:aV%f>f'~.#T6zi`.lt\ ]9VcaⱯP?ˁ!6zV+$ ys_A楙D"#ZZba%!hXSၗ 9Xvp$tmZ*=$zD2O^nNWuV$F|;{V{D䋨hWDd("J ,[Ǭ+ȱ_F591OC=K`bAC7N@sW4EϔC+4u5Fz4|}roLzR&U İ^ߠq Imݸk&UmŖ̫-s1w |kvr@u԰VPA72iF@g|UHUzȐQ4(?_Jc9ae1񠤓 9I-2tzrlPHPEݬؾK-m`)q0~[Zطsbu@Cs#abcM1ⶌ0(\U%ҁ"_ү>靔_ dE+_[ rFw\97nȺA}Hc NzymJQd~*jo$imuI U<4[-6+=C  [u[l 'O eW2ertd |>J@ to t<8:|= 3/pEϝ B7"v4 ̽ԝg4Z |C'9Nن[JRoґ?%aɗ 7q' 4-H)Z.ZS;Us|vnf?9พXdן{0@"^߽OR9 x\4 O&@K( Ţɩ9ޛBX+IS`;I pNO[z/%׊`RO'棜UT)qډ3;tCP&XU|@ț HǞ Ʋ?Ik k]ijځW)݊ s|]G oi*${4,vR Amv <N~wI9/zLi=ƪ5[sZ:H7aIDmJn^lzg(>2ߪ$ 4sm*3cp He Í.>hM筣2s[l_CF[MZr4S ԘVcI.0 z,;i>}iUbk9ht6I؋$jObRRcrPw@V[ѤHv M\jU<Qȁmv޼%8TH5\weZ8M'7U.~# j,չ~?./c9\6Y+@oV׊'T4ZlBGX6"qͳDcZ,1'pH'i(8!x/9R̢JY.wJǏ@h=*E[ km^'ã>[)5K_5"+@|}v._ĿXcA+nܡdWDϨJ##]?"(/aVʙ&52㻊< w; ,Wϸ7J%o.NƉ6,9uDa4SCmBqa7b*|˜a"YY )Ex኉H쒼c< ƒ[SV AA.%6+_%ͫZ}m@3[^w9iQ*1@o\8x%=ץ%x}tȷ/Ej@f;+і¡KI"OV?oYRm]\wJzi[\B^SJopN^![nupovqaA|݁Ta2ںYQ-:KdK4 S]=Gi=$ҫ6(5Uxn񂘍e>懷#r'-јE!=YWD}U"J tK!w>*rX*9M 8CۦCh 9{ș&d|%~1KsvZ"6#ſ$v{M<ƣ9m;$Efqww2|7QJS*6ؙzs&%5R[R(7:ȽOɝkK~$W/áQU72HHN5p[PĸэcnXKsvNf[K~6Cܞ~SI^ &rOgY.jO*]Jט=U69'}rw!8&z(8JKzWi !XgNtcoDL^oy oI t4(?Ml'5f_\2 &l`Қ 6_!NJ͐hI(5w%ݲβ~/py[gt`p_`ك<%ws,CHH1$4 50в*VaŖ|nq+[.ˡYlyls36u O"1C[J+Mi%ykGzsB&(z>A* ~qӌp\EfϺu ;aAF ҵ>O u3H%uk!/ l2C$YE''׮XMi#s2zW'_R &dumźBp-_ z (@ԮoTtr-ݲT_9cB`4$4v#BD Pgi>(BMJnLE$Te8Y :&CGdrdTH]njwsL@!E;&@Q^8Qt<-8Ze.S6X-JqŸڂo]nP紀mJ< OG-x4x'Oˆt \Ȅ^dhQ%BMR+ϋ%-["yWU1 [/~ONLF\6 c!&$ 3K:Ɉ1DmS2B"DP( t{^]dW7z@Ts&@06lG9DDe032 M0:ppu(1kkiwc ϩ5]Fpͥjv2 *ƪz{jVxBX뛯AqUʑid̶4mxƾ/$c=Eyn$ݙսæCW^downYG»T}r-Í>]_$Csb]]yrV5E[+E=t_T!T#G!(bÂ6,g6䞈$p݄qwu@J9LyQ V?X۹G&2ieJ[؞}kR|ϊ›L$G KQJ׌&B|3ˌVŲ7t4}j= :Y2Vs&e?V;[s`~+gȅPb[X?U,cf< -rwQ=<(ܣHƻ`-YХ"|mYoدnf |_@mǍz͛ ȧZo qi<+]kZa9!p/gWF!ۍ^|1G*ѯMt=zP]Y&eN)#$_;-Bi91Dta/2 " ~bJcTV,ynxzV3Fs `I,XSUc10Wӄe>8O>zՕ>G4T̅z.їsK׉rz4jp-aIʋ.7t׿6j➄ȩ7z1Z៟ITL[ꅌ%K΄t]eT8ǭҬYQm "֢/ȫ1\h 0,WٖƖf3\.w[.<2@]@)m{ ]D(̯ǟ a0kKi/vVZ]-(W7 8Wh[hаذYĝR{ӄzz ނ[ޥH>Q3$BE(Ճk+ 8:ìAF'?ƕM!gB`:uF>Iz?nF;]NakBзy]l DǒƬ~N$i%L3g[Yoe3Kʗ=ɋ}g:0EcTE |Q89Fe ^UnHv0.X 1uFݬgcI֊Eg2iF}hnxG| _Lp\yCX"MIWah#5܄&YV0RVTW3MS;˅-DnNOa6^沙8l)HW+\Hu),Vݳ/.PU>D,oE)UC@՘=|I*Ԥ}`%GĶp1A$A-h CϒH+@4xA&9ErdQŖZ o/c;x"k mTDu}c}ДJ8uZC3#>211k k*QjP%ܒ!NO"\.C4W|Wʉuz \$QC줩HvG&,FbEbmǑi{7z/`d&9Șѵlޘ`5t:ey^WP$`M>z7}$OOLΖ67 Ppwi&ռ?!n5h*ٟG]?kFgͱ'srO 0uIe+gkA6gƟV2fX#ӓz% ~!¬!Whdz +%KHs`Ӧޣ \lcv~Q-qoB*k[azuyR5V4bR=C/}gCF1s?`{OZ ,vXw *{Hw.;&'Oȫ) @Ha:E?Қ mkx*Ch}btkrtL6ĭQ|ʼnq=w2""[^㌱-ǔ30\8$y;l %]M-^d[SyxyF;Q_\;>.pFx^>ssv&X81bÿ*pڻȳg=!iͧz7נrRH^)jQ2@LE;L 􂸎JxU '*JgIU,'M23& e4E4XcDM6bҨin.>d=E)Mw4$x* Ecԁ }8̌`cu贬am0T l,L6R VoEl[1 fF/&/aGB1N[.Q^ RƔwӄBk8!*aq{ 2!'TO6 h4!`rru JOݨJ+ZTo$)XcR +0{@@^o{¡W*RU)OlspAl+";Ku=4q  M1 ;ĩ+|f-MuķM +X+QZ RѩA={pm16ĥo^0]/ߏ E,pלjx<5v69c?_r SJitF^@d}/( jOD7ih.R(yƲv.x9KV1wj3L3Neyr!`g~vGe6&y7z=,T;aSP1 ʓjP}EweI.?Fgm`@ŃR01߆H}r#?kccaM22=pH+Qhho*@9"FKg Τ b '^ cyI uL6SbZLHYeZaOL?^vƻ9+3QTnⶅtB+; gA16 Y̒.l.W߲īRQ]!co G vk`c2t*Z^u.({"(ILMʦnѺA7!Z:QtMPOum 8-F,Py:lJ-rueI#Q99\lp-!P۰ThtG[ - 4\MX%G{!x#݌2F2f*_OEQDJ aH9ԉb\-a?SEJC?4dKTrQ2Y'ڔW7 z`cWsg (&ݯ$*N_+lh^5:t#O0 +'vKHS tz%GHORۼRC ƨvx-NKQp0??JWU S0 8>f$r2C b=ܦ Qœ(.~HS\TYX@# \~;U a?^\Pgakafxۭfy"j#NݘN6Kɸb"P(. %܋ػƥ "kgOOuf' K;0Ik-Jsd\DNO4\*1بU/R_SY73QowiM]ߣ;F06{Nm`Ԅ%>0|5 # %<A1i| k9Nԧ5PrE,}c!9ZNfήN@ mQ4%w.A >*n zlvõR]s\M=%ɤ! :l9~I;]$xQ7SJ2$s|dX^GlP~Q`ÔC O=*&dwWs6A) iMID&.wz0NE dگw<ΐᤌ ak:>'`g5Teiîͬ^KJТo |x g7:)pVseW!j7V%,\tʿ<ӫo0#&]Š_l^5ᜠXJ  1x( z߃ 1?F 7vЉ#Hu[kJC=Bٻ-+zg n۰zGA}?NB-QQ9k1BzWQx`Y. ;Ӽii$XkGOq4D4{[]K;4E7l@a1%Kf`- _-ɤso4̖*[&#YLR'^ .Y. g~äۿýqB t'J^UDdw6'56n=b|c$H/1oA&Ǭ4 9 D^@at9r379*0"†"\ǒD X.*r -Y2AU9$T"H ] +H[AKؙe "umo > v=ה 42Yhy`,ɘcIjglhzPɃ;g\?VF8 rMV0X/}yfn(L y`!} VgbX>1+?9 +诹ݾm0D V.jxNStpS)2ęzgrx D^wtyǚ )3C)C2ƏV{wx8dx׎_b UkatbmGB2ףZζ1"RmὋM6ؤF=F `+S)%ټ1&wb5|5oE }j,eɇ9٥3 ,N3.}+\TQ^և8UMtCaȮgR.>H}EzVcgQ֢NDba)Y g9y짺^Z[O Hߠ ܭ"6̺oP&{vI}lpQj˵y?nh؊ D`Gz+EuQR6kbG`yH~9paG LbUl޿~nr3hLzJ149E#dH5uf%KfҗWڳ0O}P^x*J3VY= f $ĽiDLb) l~yD 3OJׯ &7*qx6X/s`<"!d V>ŷtP|hO\ x.;K16I=d{Cp.x z tq'T;'%" ,%bN{۞JاŒ>m!vC}^Z+>g3]GO^mJVs*]EL]ٚ쬆o]:y-sPnP:}%90pxr/^>v&+hRԕ/٩24G(UfWa"39XeU#ݠ g,#γ?P` Ljޫ6}A',kixnxIt=gCis`*o&kNJ8vωӊQD% :6-۶ZgYw&AXfHuWKa-M vc6h>S= (}+9Z)xE(5u1~L$&VTŲ\a=Xd%WX19f)CB2~5rK6{+^ BQ_~FҲ&D҉)XgL *h)e D&H1 S] 8j9IRk;kاbnQkj!``|NΣ\q> [BvyD;y_jRkͯh fO3KArd2ݤ9#dnUa8WT kkNB԰b R%;8=4"3NLة9S=l$7C=HӘb'ixZ=[S"&M^S6)i6Jk%W ^H}:Be K,`A7ӓv%Gmך~\N/UE3An/ěus f&׽ˣH%ė}?\1rG>m j ؙci-MMbQuPLQ 0d)P#]Hs݀3 W6(Ja(@uW"|Xq>eڲj*YU| Q®MțC֫іt %~;*dGe5mҐ;}4Íʮkb?`؊6c̈lo>CT&vv*DqR.>D՗ gMbTйsLV̀gOɫaTdR!GKn8 O3 [Wdj@e߷/kC0ȗ )F" Eq|&Ԑu3EJ+F"6<M=fKL!qm>Yشf+I7< QUz-\V/LY8':4#ѻ="d:k z€P]tP$|ql:OܜÁRƝmeIف3zor|L.,_%5o1u6z>]harA݌o7녖'q)sg&vA5GrpyTK+畚ِ7Ϧ ?L.j(tj1DHb=8_\B@ENyjPl^J-t1eD"/#*٬YK$TJ:=t@_:r@6y܅D1פ ɱ!WYWHo)¦o,* ~!o^%W7(_u֗s "9Տ"AHb);YHy@ Ƀ>el+昪O28ϭIWvݏ!_l7kQX:A}l&_ Ғ6&xd<l\sZ|M^s\[,K]l9Rl oF?!1*bPX~i7WNp $&^PqS}y)5\bq/|_CsbD۫22<7H7[=n6A=.}Ipa<;?,aL9'&K"Zm##,TRz G fs\D|>!H'_OUq/z;'/ԩSOҺ'${?}ygY3~z`Datw^ݨdG/E7q=:r %jY7^,;͌Z]/n½hP]Pp:; <޸Gf&~vmtwھ"8Q-IJķYu-գ AF}DUxZSVIApO%{ic' ƨZjzl4ԆO$NqqcY!Rվvh:]aDr4XeOR \QFZOtu44gju1Yx}FէVVH?,t%yods#X@.*ҌlL11Y8т[FP}%3jU(?6Γ€09';'X65 x |p4blrj[k/Ga^ (_r@It>#S-wp-Qޏ-{ԚmzC_$)DܟUO,%|"̀݇E0hIruA&O=[WC#)ĴJÁg ;t?O/\~BwrSZv Tvz>3/c$دLWh ah~{G<5Y--$m:\)DoӮ!0{"h P$:>o'$קfhQʣ'BoDߣvRcfck2}e SbJSߺ){Z5  O2`0?T%ǵ.ݐB%f6n>~\jV?iNWɂXc L }cʺ/]0+z f EzfwzXrz~KA&YUZWR qT'- īj3sX]f8^Z<f^uJŶoUoEjuԨrH!aPVT5c q]^`DEHEk0E4X.@s8$S.- 0~l}Q[])xPGh{1Kgǂ>C\`ڵ5j-x QO%cG\ϱ*2wA!{)K[o~ ?5qtъՖ%Wņwiq$/LRLLT"W0` ŏUW,x*þ 4-l_sao%$8q~C;SAڢI^dZx|4) ]zz5iw$n#l$l]^J-nj- ;d8 5BOKZG@ e mڣߔ WS◭ OY9N-჋{U i,+vHeW_=̿"Gįw]r 4/b!A5z++N43zp&Qd8ZUmtÁ-)bVa{4mKj>ijĤR}@(DK^sߥtG`2ʢصF|x Mw61tfsQV-ȥP~|)E \H8=x? xzo.7ƈߛ -( `z=ApeNi䏯W5w%>h#Q]љϘ(/=E F zO7y.^lLvq:_cCី̸"3RKiXnsd=qQR fH%׈WҘ|@*C֢nэ(+6"% ެۜIhu?L;Sݧ(ycq2RIf'zp;{с6i} `i߻?oʸ }Xa Z&{rwyVeIZR(~'z[%Kx4hwB6=MMiS-K(`wFY8YZ-Z0 $$_T ="} #+Q1Ω5T;HpTجߠ=ǖVa`}`0ת+0ѱ1&sjj3jojV}Q4ŽVFt A++ N585_Kg;>];(w hN`=< &8p 1>(D1 gٴ|Ӡf\ C5xhܯq[u;߈n6/]u3|k0L[JJ'>ȍ9nVľ8BF)ڊN:wER119ۢxx_EE,UM$ثTŽ"1t aO=〺# ?}['t;1+DrL*h:ϯc]= 3:Dn HWUVomHgך}l3;(]q>ß@YV{({ӻ[ptW00 Zjw%WS@D cR50-v^ug@/#gġTx3~1( [GV?:g N1s謦yn|ptF[d4=Pԝs95A)D܅#bW7-䟪I׈F8_8N|b۰mY[ts]u;cNﱦC0WN1* Hxcd Ag&)^]ߣc^O;αG >7DY_JmМQik`bջ ?)WHN-*lPjr?m&]g[[Xβ ԋ!;4.Y|r:9xkO,HmХ0D]N 5Md]OmEfvd ܉>uL(FCͤҵ <*V-dřjR.nڠ%V7$qֿub į<'A,VvNMuϧ,bbϲ8y%VIf:iM]bovʐ=ژ}gάֹ ŋ0)_6xQiJzG[gV.ISVǁ Co n|MC\*$[/L-sCp÷,BAa,Ϭ1Np$lƿ&C$ 4IPUSy!!vVGsI.& G^ClQ|^NSkSlE2 ŭhi@yRA{V@:èxIȄ 9ctdmuB}s8nPP's ;yg?O{Cz]Q=$8 ב- ԯ5b$f,D2+D Qx42!omddP I7$RtXE,w3{WUCl$FI!jUPzI ;UX}/1TVgB'd> mY\Q\^Qm``f.P]58KDTL\TF'o "O5H \!~2QmЕ."=1raכލ}-Y ,,@`p !{j˓Nm!Jf>NDjL$]f{

mdI zeouS3r!@%D.GrR n0>{O\uZ&!VTJBR]P^X2h< !,/pwɟ>w( zI yF?#-A4pX[ ;s5B= ~A%~9kQ#-U۪__ FvkRCWE&"[)65:P9B ?컚5j z?(c@)úA|ޱHy"n,T/G741,x6:T7y=sD*VD6)^$=O\EBmd9s jy c&Nѱ6`7sC9B~Cң7°'`T3[(bz)"ؽU?j8_ؿ#h.]KE5u6F64=.{t|hpGiu7utZZDW3*A$ժ˕";u7im!~t~uAB VfvW6oA98چhr/Q-F#%+Qzߓy.{Sk܃F$Tڎu<.$޲gyZhKl453]Xw—,5ғQ,<2Mȵ |c/iTmDitO:\@= FUk xN6%mQNmRjx[|l4kiWJ:;!XfyK j@5X?WdnYw8q H;nbo U0}8"-Υe`?t{ kM_OaUɾd! dd0Dw`q41i-VF?<2hGH4s$Cܔu4[HJu:XNYjsUj%|1q.+F XdcS؍bv jޜHV]4Xь 6NX7a>Gsǻ_EמcQ|u*pK?Qȏ@>{7 Qo,QE JIv>5(4]ͫ>>)O1cZXO2Z]cճ5ZmF1aW8hX%PUIMPNW 7~r]\8/4UdrXM"?Rt@WQ~a aU͔SOz ]ʵkT #*j{KNB>F~?{ys >s G*ܧcQ'$I382#wbWyKj":BJCy~ܺ\j P) x4ğJ+_.GD S)Iy LSDr6%kEx,v=LU߅X0B$'Omn[d!%e*zix[2Q/b= ' |Uj5ʖ@$r?-|TlT8K,q[^UwGE1 cӓ'Y.Ewwط5졼"ȱ#`bC?`pDScoE h *œ0-Oj`X,xAocj #=3t%]O[)!is\i!7ziJu f J\!LX[Tpe|4wqL?~7wk aѝx3ʯ!b7#CiM/^`M fv%ˇp`=} c]UL nI&XzL槙‚ 7RYM\'Ne8w pʯ+7>gBVC}<~=&AoWX28q5w1>)e!Ph?F7o?Rᘤt$[ADS8'9A ;37.zUNGMj)B)]'#R==nYajeN$TL*bWmK&jlݰxlĢi4g"Ov* sy.:h:'YEtwʅ}xpa΅ׅ.ӣó'#@xԗW}i*4Juw-N㓲@=PѢNaKSL H1Nn*BS LBt4ui~Fvn?t('NTk}kz\f0mX~X?Ȇ L%cV߁R$sS8*Q%۵0 kAhU_;vefʛ ~EꝏWV1:/d/J.ތ=|!Pڎ,ITB_ \W{XBl1 @OssqyQqX8J+«cUjK>OZj $\&״+3:zkg4pQD-Iua"3\{-|$fMC^W{NT[ >{ڳ!/`=IxE3ODo-ăDPQƜ }JB[?}@OWY ѭl~|w8ia>+bp3"0G6643mp ݾc`+bγn&w:>8z:g#|JZ:v&TDJzckkC1CH">^~XJ &GZlŽsRߩ>[?4R]#BUYdDǎfE̩O*\=)m0-4L|Jșפ+mVŞΪwu"[J=ˣ ! FdXjW4&TE|MKP"ra4 &~}Dy $ IzIOF\pTUdk1Lsb<3uUVg{|QG\UͶv¤HYJ@9x+xKT\,q|rT½(Ebkqb {bn'r#j؛N:DQ<ل^ kz㧃vɮ zA ]a M.|Ǻ.#S1ˊ(&c md^.2ɝ0BWk]瓲;ѳP+N'eSZM ňk_|]=S=o7P4H>vpA,natpMKVDW-/((AeO0YDog~i 0y-N[ xCF'=a"K5y1eZRҽ> )"C/01[`b}=jSUw9n]'ϥobIyM"Fd3ɵbG.J־DVvO""!~RN0ƟGV`U6I2cvRD EpEMΆ a1mk2N{y#N5"O2dkCi}+mMZ;pU޹4#g($by=+ox3Gh#v^;o*+_ ޡShɉ kW(Id ĉNcX} t<#qiPCǤp rr=#jM ]%xqy[ $)A+" RjPe2b*\8L Fߺ10d4b|wWm[MqWDA1 f gV1\g $tHg LRᚱ6nagNŷnv#qPOw}zS ؅J69w6S6s1YYF mmlL^F"̃GGٴ9RKNv7}⺅,C"3x-YyYS?G^qI*r|I~s4TזF#E⧂-?X6+tW ?bz1ކ Upke*O#J^k~ʁ8r҅KQ=>N &ovz(x' H=FH =w};S?^,[]IoFvȅ'?X2`}9Ҕt&Fwc=gBnhk}'EL2 Ca㐍`G"Ȫ޳FV +~cJij"iiN.*[cnjCU@Ji .#\Ҳ%{:z W|y!hh.CHTd(d|]kFN?*WKGzjE)i* @\㓰X#~7աx EjnM&p.Oq`k_}E jC%AL$4uc \a)PX= u0q2gmQ 5$8/5$X.ů4#Nq65>e*fһ9]pB;jdjFl2fltܹ1p"Qs4azAE(0B@. "cP)&UrC\g%9M ߠ7 *n: A}B%s!ZW r}F9$Ib0[RgyFv0Gb<I75x|16"y]K_c+J . x=;' 24&^u.R&Cn9m}&JgkzkӁ_n3 z{"Qr4nBc-sX3vbw^yL^ԣȍ% I3FT2om֖-\KnW έzv5cTB+jA z+V7(sg}80GF/8b5Դ"& BPPDGsvE l`=!a\ ZFbO!e|Ȗ8Ow̷L G%ra"UP5V9N!$M& IeMc ͗ZI&nA Fz~fK0;B4g .a̾rBv{+\O$ң-31v40`j0хe cgo|bqu"Oi5m<-Dx ,CY (?8g.*b}u^2V_U7VaO]@w#Dܴm4xۏ1@w aLw M&NJAѡ.|25Ym#ii4>?A-[Yv~yѧJ.nWUZmq\iYC2W :wvX"C# bu 7M0[jYݤFhius7[tGWUttOq.[+6 ҸhVmA'WqpK`Cg|hI x>cHWg:p*(,}ɺ{DSЖK^0TUfפv%I:t!{} *rkvzMm@5 u. Hpd>vxmH j<밵*"H˸ep59+YŤ X}Zup+iK7mU-t"ۼy-痯;}}6X4RͯJldAAu*ZDQVg>v^jxPWc|W^tgr\(si`&gv^bBP{jr^,j+-õ^E'zHBў<xׅ3&0b ?5upࡢd7ea:nW-f];#@T}YŊ C=WRΦs)< 1軍cZ>QIOD\8,o=35.' {Z5ѡjhӓ65u){t~1uH:H@/(Kq\Y7SL!2lKƗv2B FIVUS*;6O#{r"%uzE{N&p %QZ<&Qy.J4K&@V{X SA4S| GxO[] ^n.U%Lv5ȑBC!IJhi늤|*̟ܜ:GA9S}fAVν0H֫kz+j)#Mg]Ơuy?^̞ ņ] wn9Ϥ2˵ {:!6$r5(XY#LUx^)c`:Nc?"DKQ!=; v5즗½[9yV)=Q-ZLD[w@gw/_o|SFǻT%B.C~;ȍO)`BU .?"PbVvO|я! L Yg($p Ti{dw!6G ߾FQv+}KF[A(0JI .E>dg֞)|;Mpxa(ޮG 9"P784)ʻf2)^Dbpj;b~L\7F_Ry3O5$RvxyR"Oq/G^8@ݷҫ_FQ::!óo炥:XqaZYw辦}JNlR_]_S ^(wO8t n4f΄ff~wpj_K![\.q9E yE ]%DVSпӔY_|zRTrgF;NjCf#Vpg%>OVG[֒FEk@FK6Z:OZݸB{)0a#HD$ ךRMnU:m-c]VW F_Y aȭ E}*!R˺Ƞg&&AVaN3mhTwQ 8>Y.C%vMуJ{:{;LO ;-^-I95-܅$Ϋr~J0?Hz{eXGծH!4)O)J6=U83g\(CRdN'W"pGP[=T30/qVmfՃj`` iB)~xQ՚.$2dG&q( 4AĠl-lF"l!p. ĻngۀĔ3 #Z|fV=[3n/]dC7lϩE;g}\REӍGRR^0UںdS00ۛh9|9[|oA-,?9Q38sPcTW.«YO)Xc &GlI#V?b2Ίkgb8hI aty ){נ} 9Z=뻗rAZ2l* /c>X,2lk4\2DL^"r-nE^(],Z I jyeBO6a,v<JFs6YҮ]|sCJT="{un/$"k)tg#A0+}Z8`Ĕ啸"U.dϪdp c.4mxirI r{#/bkx4*jp[CO=E+9}`ڄT[("΀0ԕ$.\;*T AEV;ϵ69lcW#o]c||`D@?6j5Cl՝H&EJ]d.8Xf,3se"vpնzQL"]*"ù.ܑ~{ט+üdk R$ALb.=Pf7 C $#8$1 ȇ/D~P]2frqEJ)y:Ro\e$ }Yրn UdcǤ.0t?S>ɏ9֏͎*׭ _i/kMʣiSQ8ʬh9tP2C=iWMz턋T\k6 Qd58*I%vw@c4s U\J(8DC^Y֖c )TsY#wVT~4ID~UˏXVJi)?R'秔eӨ{gψ9sm^l pۉ'ƙ.%(@T.KΠy6G1g e'7M?c"Ϯ@5q!j (-oHXK[vN v<Zm&I6Elb2cQ+1jd"5 Yw?RYdK*L @VȜP:jG!c>H8+}W47- y+iD>v:dăkU)sMǗ ǗnLϵ&Mh-[j4b:}D9/Tm\_/.X3~6ѡ::+q -6(aE!G'zԉn El񤧯󎇒)7/زtvI-LJaP?E쇟G \Ԩ2n>9I6؝iS&ʏzZ/ wl~"hNCu9JP.>8Ei ?A 1 ߴ>$rp ؊duame`k&oSb'J*„RzD9/!)ʣWd+ݼCIVׅAc1Ҝqt7SS@q7_4-۲DjC5CB_3.(ƭ92c?iLj9{)'i@{ I!(:_LTWMjd>((2sJXsVE5d?ej9 *̈0]#DUS< |BVdb$k:UQP45Mև\Q}!2ݎ3hRK40w J¢F)QRh~Ӷ,`%]3Pv-*)t2-`6i\{B)-hQ_g 7S%fD:eT?TQ?~!׋ƒYyN4j>^х(V vb?uY{p<,W(Zxѣ *=)wew'ӨI]_+E<'&Yvl\ "K4WEm^Ɍ1 3m řA!)&XMxI  >g\˘Dpr &//=/Suꧽ%۩oٜW""Hyw=Gzo `. i˚ˣ&/UaoOK67Tk?$dIxJ N|}̮pԖ/ Fdu|Qg,)A2=>9*߆ n;[͜-e|#GBWAr}j:KU#RJ9ՍH]-dX1 Yԟc3Ԁa8 6.Wa>y$r/u5L,7l_qmd;fA&wVxy@a&LY硛OrI\8g7Ni256*)'{bZbI RWuފ+͍J>Agz4è jȩOA"j?L$g=<7s=kQuAgv_QŅ;!fnVʷWM6ȄegxGAxZ}v'j<' ~4Lh7{|"&c tXn](4I5%|e\ 9ª= &giX+W߅7mM@woJ$#K0E>$LAnJδ%ӾCCj+@EPe Paie_0Q8AY}DZ0nO2``5mEt&h6[F(USpFj B?'1d\ԫ;񥈍)ʓ@ScVzuof[2 p7ye2p#]gcSm6RNMT~SKgx(p_ndϢ;y]y7x6 Z7ߘCQb!wSMu Sg=WDkc`d<Ϊ)e~5L>++/1y8_ ]ǜb[tMJ .p+R@T Okt2X|CpÁ؉;b@6a9nߜnL=ԋsbG_>X1t}5B)mÄ4FN=_nŒM\3h0)pTH]he9t$@",}1o,F%(w-םj Sf w=dܙ/1@1O^gӣ@CG}5T`IG1ps",jњLBކ#~I[`$!C0_Zw8Tѷ>|}2SG glUFӀ>eu=zJ_uم&dDE8.+;y KIAOOi)Q8?~OZ[ycٸa-ry:aE (i/~tZ.l[ԥf?FZtL&>K8hz[[n zS)-k~*­k&JVvI1}|pCٹ2⾪W3 N8i"LפֿQ{{I~9HGьG*GOD( f#>(N)*!Ѱ?@u'=+f\zh+ґDd_ n,d_? ڋqPy@3tWD5̥\yd @;SF We|3sY}̣s-v=@UB̑/$}lH02g`*(> ű ESFMЕ3$==iD+Ai t5(vj'l@rbRnUB06CGlD%f|Tn4=  "viDh|f^WSL+uo8pTȪDŌQ} Dɜ>#0'ǫ8RV`G\hoPl H|/u$ ˵!*P,=30e(ՏXv0ѼTx`t=7kOMH0[PXph[`| %魱t21Ir0،՗*TDu+0(rtg ԡl/˞ Z. DʯZY{Tiu_ָԓ!Zo<4 /ə">3 CxŹz,)o0AfzurUj-$5Bq | 'sHD_ +$૿p_Uhp_}yONA=qs5YLiCEʙ㒪m~$qؘ+;ٖC:?tĊĈĦxo3W{0زnq8:ѹ!~i3{-%p9Sl=>ph6,͑B6Ft,S1?pSP z>ikvc6)<O䇛:y.+7iƈ}Xk{ ٚ-M`FJ}1D<ϝF#+&0eeUU zJ\QtTuءD_`:#0A%ÇPDA Qv wz=3j^IHGva1X+]ޅ(Ee`v{l,WR w˓H!CRms5Gp 끤layR}w$*) SDF֘"_ZDIޤ?1}JhRS:e..~Pjh¿6-ss9b\8ƅc5KhYL7k/]͡h+M\ŋ!zo.V:- TPp+dV?N!MPro>>7f ;@>icqXxSV/*ZoBev"p%m6w˺5)$2FJ3_Շ=IA;[ r"WV\tK[[;,LZKhTt`@J//+fEu*UF@u ʨHh/F46Xu0_5EAt!;0yؾ]q0%z۷ >wOSߡkq{i!I:j]s䅩-*d,hٷKIhd+Y?r?}"R^Y);2e~o%W{+>4'APS=+ijk;L f1tiίAsӷk g< Ftioj#+Gp{g9`j7+gxH,~$b5b*D+myĊ49S>a}լP+F~d{C,&.왺9MI{fii2y YCY锿xYɩiH}O<aoC*&x| *lM2L+Ϸ;7~&ʀ$~2Y N0L{T8D WCtJR@@ܬʠɠbێ5{A&9חT?Y=ep`Ҏ9Dz6B4SRώx@ϙ4 9$s`.f}hM>\NVg!Qe9CS@A DQ3r00rm0s_(o)CΫy2/"8]Ʈf@0&J`gބ}mɽjRe ts& k=W0~Mi'j;m:#mvjYtLiP W 7ˁ+OD2A/MN$v" OEl_ՍZ!~y<.nʍA0bx  ?2ppooR{Xո% !v@aE0oL0/y! .Jjuwmk K8g/T bEz MgCwyaE)BJrFkOt7bm1a$˙"dݭ'u%M!j!]Xb灣ZP{),} 5PW5ؐb;1]׽$PLYKNyF~`0P`0é8i_Mf*[yY@>3WR=e:biakP&^ͨT-UQ^AQ9swT٢"L$jE>m[-7 h_1 OG,@ڙaȒmP y$aҩ=EȧM&fFp2/BH"b5m% ^|vk/`v.Sϵfa) 8&qjD@Dg9=$ձpAsAi@Sgm ?eN CG>ceP]c~Sel4\휭Vq2GgmK4;E[*ڙ "֢&6fNNU©\傲F_|mg7vkϧ3ٺON7 (B,>5dWM0hb,e B]i kιEJxFb~fC\CKdxG(~D%KY\K= DvB\m"mY|[dN5~B́W$<͚ؓNc.?>^w"x-U4l'j˷|G'^ lqRL *L:.25a;[|7ҾfvJe tL/G9D/ܰB=GԇD@jm\+vVW:wz3e0h Ayn[V⹢9.s:Q̓9=Y8%=%wN +kkmSG-a"ɾ>qIcԣاy.rjňhTq+R{yb*-sMI AMyQȢByLEǛM5GY]cvònȯfO":`(VƤKg5eLivR=]…7@ԡ!qՇt<≐5p8-.C5(|sry|`<ƾPS;~?HoJX6|ycޜ|H"8T[ώMJ,CWTvE:@^^"tHE7%wQ"4MHE239螐 ~k=j /BޯWVPZTmE b}{ $B!3)8`#һI}֯Я:epF{oGH X:}+捍 *Dq1Mܣp5$'l|6FZcي+MW+M9kqܩ^ L ԰{($i}R, ǃ`]s1jr׊7hK~ @u-H.{;?Fc&kr*etkֳ֡$ә#Vf)0^r rNh5CMEvcj~j 6)qls`56 ]Tmv7 E{1 {g=[%"ZJ.wyrCj$GuS }@,*~^M.㐫!/: `h {h%g @-k&*[a\Q<]69|ɂ[)o+ΎD)@7PƒRݘ]i ECDRtyp=y >cÆԝeMRf\8 NϝgcY0os䏺kdNRf ap/Áb:7gPXڡYR,ۨZ6XooM:(u4r GfeV-cOzfʭڵUh%:W}\RK <* 95Y 43#,Z? E PF]1J˩˲[gtCԯX$:v@CE$bMVv ,V8%]7"}ν'SU,nGRȩv@80y1 090a+#e̚v'Yg\A?uMZJ  R >+EHn}D6|jusE_'!ln@ka8-ӵE?0<ֽ5rzyC` ۻEJl͆dn-ІDp1B`vpZrp.!۶VM'iC锄d2΃A/ yhjL9wǾQ۠ߨ1pZnp(¢䩅"Ot[6dP&;;Gv=uI73ސOfBБ DU"ÉjW,N)pjiHųpBԺY'ezSAD< gowz$&2E?-])l=ߩ? mnэkGaMy~Zj;ů.{؛M_ .9OZٻi'MFN{dfšNR[]U)ܱzHN,d"Y0:N98>tSq+°l Cu%/L!-M:eeodfÜ`$0>gu]oZZ¯r"zL[9`96J hhgh+1 UX(o۞pyB.7sl/ꁺ$iN]]onXByu(Xy敕/l-s.v awdqꏔ<84Hr'%>%;HTV9-DvG!@3󣐬Qm=DqNhP=[K h/OQ'GF;D%ss{Q,z˔q8O֮cךn8m ?0[wr,=}&$6Ee0oB^fdS{zVk:qJ{dB)>W\S&k nrx n} 8zi}ޯ{9C sxr= t,zs?47mXw;tW Z?=#C_F8S:VX>C6;{"C` eb'J2OQY׆PQ2'qp2FWΥ^϶gfԳQcSs縆1Z٢_PO='hn6\_ vC`s6i[֢z+!N-ENuеthoӐ1 7S`םs1"Ȏ}F*)$K)6zLR鮀JN$qDwZ+x˺Sm OtMJqD^s+P<ߨ_ 5˾acS'${ NQ ?ܳ'OW4W9 Zv 69l?ՉZ/DKf[VĻ<7\6@{aCRF`[k11rNIX![~")@HJCb=RT_ߪ+|Mi"_t.p _8OzDhsm5!4LS5k9@\ *")CG* "JX.5!:K49ޤ<h$B?Sp5ЎCc87B/5GK4ۄaBE[;i7F:U(B11AGy"۞SC A522eoEFߖ(xn.5AZ/WH| [~ ,/L2rwjER0z/ H iG{EB p[hx( o):/v+s<}w٤L8+NI}inxuI`Eh};w׍F. _tOIKscbx௎D'A8YވJ;pqqy }O=]V'+edd1T}D0Œ9ǩӜi Wr<ԁyxSqbbty8=9ͳ#q ي {wDKPc$6!Z#ſ ތ<ݥgqy˟E8TʊvHuki ЪHƥyDȶdDZ|h:$6#`̩`'V\L=_q |=,6\}`?E_f,H/xϓf@EWYhVw?ߖIY{pQy=ua~Dd峣+&-i;ǯsʞU2*ߏRv%k Pc˨[Fp1~Ipl)R \ɑ'%X @, PvLW2vsջ5J{qJ7r1U νh$9?2(UXI>p\`?׊A2}d-)"$L:󢹠wQ0a9 m Q@,y0Wo~w [4e>mX&х:>]h ul&,D=)<8Fڷ90a.d*|ʩ8 [w#"^ db:lZg/=q&+AxօUJ%9v% (%d:f56\dHG;,gN! -pl#hKad2}%hT|Y).or}Q=}jWKTS 3!*H: { a#7ڶ"D Ϣ8n;cS\ 3K% si:讄XғQ]Ӵ=521RY?f*Q({Bp:ʡr zp: A0DOaA= ppq:&z18cT)G4mFÁj9E@0,Ya Vt 7sj8׿Cdr ntcvK;*Q1HҢPC¤) cGGVekQٺys6OsR+a3tm[Oi1 >B /ڤPzѰCITR$>VV4gAd;yV*shϘt%xְ밊õ0Ĩ߬=5?\̳y`Vn Nt)|mCԯbWXg28"WgS͚X/a>b( ,E#d?n )x.7G+U.Fd9͠E3[jq4E{wÄI!#*L@+VV*A4RV=_A2t񧮑ߐO?'g0yZȣnmm!岁ƙCmwΥZNamaq<~Jbįz"cفdV'+gWl#cbaoo7n :ap`75Ϛh\Gի ܖVc_2pV8ҙcJ q\QVD砊t jd~Gf.]6(EϮBCE%Y"G;1:٦lyrcxPw 3cM WV6cfWӥ\tEN[`iv]r!]1[Eң8R3+ 5(rg .կGk!«<)D]?%,pn8?BΟ(PcjPyz;fak;7m O 8T/9s}>3^? (骢.(Di,'obl3 {k̇V6(_'?uw>I!π{bLo>t&}*d$wpQw`p!#V ޢ6(:g =*v!s!i ]U,df5$MrUE6~ v.jɢ ;}pt\ζabC*XpπoN 9u9q)3m"pn8iM, (g @]3CeR.Ǯ6ۤP#2!0'4!.m Q 򛒆CЧG (C*unqПN!P:ԁI+mϖCzj#.Ʊ9 OlaĞU}@TyQ(pħ~i }xV5GJոO0ӵ|?(Tɴpѓ_wS\~AF9c)B:nûb|{ðfaN]Y c"ͷIdwipj?_*؆g"}DޠGfC'ja5F׸it@3)1-N̡1s}cAcc'ey?$cLJaʼZբŐSB15;}?6UwyQ(?Ȗޘ%ğ6&AN|6vBLoHfwY) 9+Ŭ{x-흈׭4pF-0rg 뫄}9.]q`zaDF$|*{,OspGJ%t$}0KjLDRGR05lߓmva<4A:ٗtn}%MҚE<F ^V`2Eա*} #Lי1`~Tj5k,s Ғ)) !$QzE&IC`pF,'s'Re9Kc8)ۛ+" E]$u`EG궒P߁F?֞WbAD;|G0q2С(i9ۚuE  ҽ [%JD~@F;ZgjYQR5gcx>S0GB :.M("~'~nɴtK`_NMut>P=Y0)[' w4+"5orG'y|7X[[+Nȏ<:^(3i"5b\ƅ|Mѿ`yHxp!E$hz8Y*< 9j-/8byT)ml{BSQQF=%"[MB>Нb O_};';?AJqܧASGD7yStg(x2YsF"xo%!/Q͡ɧh&;Ö3v$qx8{"/[Yo+=]pfqH`C.Tm%m~!,صtR*'h7 ;sD (Y8-% jq7؁ 38q"-5.~q ynq]l=ĕ8 UU6'P8|um-fhtsL$N1TxeI4-D1¢{WEll#Uȟ5$u@yo#(@K"ӗ4/$%:bُFvn}q#=v,&\xMWfsx<{&Zñ`v;:U{k垚$r9%nY䅰(=HΆlw 3%=V8,FD3Mz.P6СQCۨt ,w?Apn]T=䩧:c$%yD4p-1(T`eiۣ7c3 #TDi.L{v=p)=S9+?Kp!*JnFDآ4,p n=dYICjx&\|v$7VH6XkC]cfiyߕ !s׫\ 6i9 fk33S1 űsӷ;>Yv6%2ψ +CiJ,W1Ii>BNsrJT2US'1wx%EuUA7C-̈ҵYzb!f1KJQ0f@%$ e. Qo N%yQ"Kd`}I3Ш"q* ^|A yoBp(+ln >~*¾f,d܁j#v`9 2o!{CLad>e&Hrb)ФY.b~؆PfR5r,-̯Uvx)ZȻv_R'PL[?]2PfH 3\Ej?%yMJ$[$`#Yd'<}Ķ~mu/ Wh(`F# p17 lyQ;8FE`/Yci Y Z !*mjԢ5T1jpsVFwZ BJ|KKQ66LT]G)>ԩ#\t]͸C"뮴5Rz&1pnh?Jb&T`ѨBnw&h3-%)PfNO/c.[丼9oxsNl.-| g[#m;>_񈊸Oo-eR^oF 6d/yCxP4nٓX%c{ï]JnIXQ[\@\XV@zBEwSao3b/S0|Bo⍒N3LNxCFe?WYZ]K☐#acȓz<9t˿,+[MOݐop*/R_)8͚5wN;kHFf=.ͯި()c[܇9M~j!Y8T 95JDRN .0mϛX 3_9M ȃ4Od'x9 Yvs4!Y&kJLWj-zU"hba8) 0ukTF9)PVrz1A|JP6Oq2~GU..-̤itDgIĈ"<)5eOzdʐ#"{}z Sj{\25%lH8[/ ]R!$O9f6d@lCiI)U#I{SNqR}0cg`cyW={G_] ˴cXXkq /|͡Tׅ|7uBʒ5+i]X8v0pނkέMF}(oDSd&AjoGcg? A]8ڦykZ)cVA'$&Ys`ec=6sNÏX2,3s b!osGLU6e4.,(;RqSp!ŭݺGhkJ!/ɞlBP,.!aMoZ|Nnl T*j0:s閍JSau3+2728HqN{ozADga Ҿ+ɽH7 Aj^Ե}"QʫW¤V7/:-56mc6 U!H7| M&VЗCpvrvV'&z"ĝ(N{@o$ÄN/ѫI >D0YG!*>r}Z 뵙)=i+H!m{\0*$k~FEKܵ/عM "tXsl]Ze3Z)*]DŽ'XmAρ>g,d) y>AK'¼ Tp2J!@m_ɯHz;Edူ\*'KzzcU@,aPFo s2sʺp3U2"z.U|qChQN/[=YC֚%ri)?5^<Հ*0g˥Poy I_v9G:/?Dc0WGgf',*\+קKǒ^>cj)yn}ip~r⒃]_#Z< $0\RdR5Hۢa`)ȿqp7zϙ)Kq:o[EmN\%>tqJDyI-HTm J7qSzٱ w);ƻwTlwȿɆB%Hދ5M)Y3 (}˴P`ÖfbT#pF9$ꎽ{YN׸f*ĭ}/qIE # $")lt,5Hܥ}GͧmM__ gJtOݿ$l2-Wu <ƴ>L[o5p'zYY21քTfteC;e+30n42Gy%xu^ 32_=sBmaRvrҼP ,jΕswZ>yK%2g;*{u"_τ;a.k(p; fٹug$5' Z]I}nbTw܃ϣ8 S zگ-J^N RR[ OpQ)+1)ۇ Imy6F6:FNT֨"s cbf#w@1! Kx5gϜG='b?!tB'Bn_pB(1\d%M{v$.P;ƒ"FC^EJd .ɝdU 9cN4ZwcE/%/1\: ,~<ʹPb/:hyDž5[{1*GVD ]*ML/D,__dJ`ۖ>CAM9$`fEf1hrMܙJ d}[TkRe_t}X6><9=׫V.@sZZB_UL7|Z5"c FVVe;ߙ<~yհat[LmteWba\8sTo\3yl653@ 3n,t[|YΈK8|YaԒo(p&\8~]Jɦ:G0qoV#\<kp%aԫnvFK/{L c) .UڊUѡ.|_~52Kt l_3~Ng QݧÏ>\fg%ɰada=D}0"ӲJpk\Ԡ?hW5hZ^!٫} BPʷi==fdsnG*T|uowy]j꺔@Y<u&nEvaW;L(p0lz2P_X!W{d3b.-$aSC3Ln/Q"^aqOKk^w+L,֧J(^; cqquo#l*/Qy}uNj Jk~e *pEaЭ,hUd~$,IjM%`V=d]uCa(S01-q W{oCE)}gxn9؄sri-Rk#=6I ϵ+ߝH- |f +J;`)֟ :v7YM /guMZf|t3Q {w$DEs=ۍ~H$^j+ThNV^,lQZϵ1jUE)elnJQr%xj7}i^Q ]IES1;)W̫qqUz3=. {r ̫k^<}o"1PPf;uwXtb3eaYdB'Θfvwh>M ?y``=0{Z^?t{k!О3;H5kȠzGl}Zr1%3r$UTY4+MYR n)r:qPaE>!!fdǪ ,KOJl(g݄ל-a]WO2򊁾bi80p6V ²N{:_22n&u,ȝR( o 2L/w<՚¿W"BQ:>vU|&]%DV(o!vZ.  7MgU)G ?_N+  -j\?eꂆ@72€C &wvВmƔ.;Dd؛Yи%JBVSP)lg,T`_ԏ 5卪W"s +RO1x*8K[;qa|0 3u uRv2QNs|W4VbQT7tWv[e%< 7 eѮ53b,50g N,۝^WrlרS~ _E=/U|O1$t!%@%!13s{{WIzI q,WPFV:$mwMhKjF/[T SoEmuW|浅]j= ;["r9Ev)J+ oH 6#ٵGtd}28.bb^ B7wU#_a,G+Mei{RRd/gmI)I|?;a865_Ţkka'ػ{RL)e]({(oo_s U(8:|ABO2EHB'UX^,O݌g r"&$s0_Du脢q=..\'}zE_FVV}F]Ղ .5Yo+RZՉ|N!bDvH|5CW5Xsa;j3J~ N! 8Vn0e)ԕQ1 (n;=Z:Co.u<1@'ZBl<]+;,Pi12ҿX`lRp3+Ǔs=v!NC Ò[loU(REؾݱJ'vKN/cQVByT NMMLmsӵV[IP g)ѯZ 3)'ܔN37k) 9vҥPu4II!I=gc3ؤx"#Ќ I?Nc>}|Nh`5PD *%ljy*hOqe]TVrɯX>p$z[-Cs Jkk77" K*:cXz2́]NOl0C fi7N|b?T7/.&t}ӧz{Hy2˅BH︘ פ:To-S?[\ j3tw.uԔ&HX+ǶA,k"۞ZnͫB:>dkPY0:nkYe>?qr6 jz}ʩ bmt)5Ư߸X HX/31M~BmwN~Pq?P!dꀭmͰc^WqjdMG4`G:]q}g(- =qx{ k`WkBUF zn r[ ?yT {S/.*΁pA-$6nz++_(qpFq)]ǬcqT<|5K[i˗H|rZa*SEY֐n}͛1FѥmCQ-pSGgqwz*J"GvKK~ˆ6 ϏqĔ KKI(QŒsX+\c+0SHCᘠƊ.2HBu wK xOFm%9ZT&L1cVV!W}mvg;ՠb %;++Trn{Iۿ_$gl- &{f0OuΆɊZi@tiD3XV)p:|vͫ*ξw0Q44K:ɰ^ =!HnN✿j͎!mov!&AyClW;fz#K"6% YoKޝDC#Pkt'f/Inⷶ$ZZke-].^O_mEv] ۩ƙbǏtH3A@ :ڤMN}5ە>\^Ӌtn{ W@Oc!=!&O"MKt$ Ly%OwGcvu(Al `>C{h˜磭\Jf;]46Ӎ-  sbX$6uu6UM;H׈:S 3TkG7cAGAybj5vr~\9|/_.7-I|?6qdBF?ᶊxIRJ+&U5ψ PY&ԛ%/-S,J6r A#Y/A9ҙv!~=_ )PV!F,eӁIқ6KgnXBFOyg^v[: ͂1d%dyFaesK^Xr4?4/0hoK[R-VhkT3*RG1I LbercTJn&26:wW?G _d?IrXCХNcoWƇ-EαXʌGȕ~eZ%_EBxF˽"CĕX<t`ጕw~e"'**=)HdxKu}|.23B/Cתhtq 2*uUM:iE![i-*2Z=Qiv Cx@c(@fZo\tT :9Yx*{6+LG~oٕ4¾:;<&VPl`_Փ pN4@ =],i)h!Ӳ%qo76 3J9S6'1d؀$-qg3z{B!{Ҥ@UctlJmW&MW `ĚӧmlyLjDcXnA4Fp=Xtu2$A_EBLc2Wڬ RL~ ȵ>:sxT^lZW)t͜J P1*N56ݲ5Y4"9]+UL0H-leBbT{1Y8.*;e{ ɠ8ߣĝIk6 ěc 3p>@xS#1Y\Xa7+tz08^=kmFM[9Wp,ÃIq*ܯ(ݿяXH7rV#BQ;|evvtYZÆIS͔Tyi ~Y趝7k1~>х5c@_ ~/Jx RpmQ a\]VVXk[UHKۥ)SŌv9gp#?8vD;&O[x '!jɰ7qvF% ,$^̦}|(G9Xߗ"TfȆ ~> A034*I쥓QD Sqv!B (B $T; PB 0\bD}ЪODU+X !հ9xcX}Pc;UbgYW:JiXfة\t6(-%sp>bv7ZXpv@ 9Ҷt%Hv~@?rݙ|X>\ܘ./ȣ&\$u\"sDJYd8pQ^nnPWjeX<{O^:]ems2n0ta%AP2FQw֪lrܟo`qKaV\=z^ݑ n ^aBؖGn6.dK mʛt);cU\nW^HdI2(m޶2Nnu ,pUƟC$wc"O4؁ބ\W[B #Ae$Ax78j-RGa,"DEnU +f9TK$$τrЋ+Z(O0!|}7.&lbsG1E+snBÌ*;RȴW7>Cs~@ p]YA)߂yD0O$ Aݨ["]JȶgN̚}0Qp+z0Hc򳼶`gB1Ԝ 2e+wmi/2w$xjñ݇앃{5ե6%u LIKoLǼ[Sc8b?Pï%k3wʝwWeWTsFX3)ΐ{MR^LVk#U5bZ,HfWB]CuG][|2i~96/\R[~^%BXhxCOOb 'PGLA<b΋;![>!flޠIuQ4>R*lpL;A. rf r5= 4yS[WqJ\gh$-6 ad6 JtagT.X`. 2̌$ki[77;Uq-V:C~M ;|n=|:]^'9Gс<lސJ-$alvy0nvM(@73XFx򭚄m-8MB czf 7g(Z5(|G1 aJ7OHdd+4C m,w bAZkCjK듃v"J)`͔/3VOS{.a Kj\“pI@mf,@< w;E"8D]Sҽ_AjY,okw΀\P~_C .AX_ /f2>K+CMAij|i1$3m\?IEjF7HV Mm+ŬnPg&1^Vg3ݚ q.aIu{|pX࠹3-OU& ¸7^Ҝ;hF!ZScE"I ]۸nޅ^;7>& S<6JF˗`}%c>?h),qL fWIɎ=v)deSMˢ.K.B}M.GN1?ų Pv]%.J#5RX7jWqD*ʚ%:KG"ⵆr.ڹ? >\=%Vg.4) 5$qH#ӱ4CSCtb;K_-0(6F%BvjϸIݴZ F7Zo)r49dbX{#Fd#v*֊#< Ο#a;o$;mpOgC(De({LڣU=+ҿ>|MQZo82?; "$5V_SF o;J|3PB{>u"wJhFJVRa sb7U4W Njt-բ Yҁc ܀܇uᜀ y:]GGRk=؄Pkm&*{zKzXVĈGLA7$kjs`RYf :Xi/Ogt;7R<.N\xh]-K4$-[ECk@9 q٥O z۩\KBmHPQz& <٩tgW&&AP0RL3v@Q# FMu|!_mn҂aovGiȹH瓈*AQǨl!^<ջ V~" ܭ9[}(~N^}QcdPQɬּD,c\1"`'m)jԱlTC  c~["2ChM6[9kb*PBt,+H.CLAI.cO\ely+hբ Z@JLo!oQʘS=\վm3)zA*%t܂r …m3=Wg/yhkJ umC3ԉ ~6$GO]J=#b MD Z.*j[*6<\^/s!CP1[cx+ Os/W"H.*n!a!<@kd tڂ"V%M: y11B@J)ڠI t2pfQhZo R:4޳ٿ2{\ q|# G9}v:=' J|1.dѰ3Ow!L`5,WR@D 81d$iJ"͜E)3}l+[2g0ўTٴFtBp{II(N] %࢟7h~d;pHpUugf[{Tn2VouUxqov3{Q̯Eg L=n"pJmKթ_" IE#NgdNq{ZjZ= t>ʴ2vhRS}</=ƞr-59ޯSqmLB[tr2bck5v, %NSۜ,@sKR|'9!p#[dC ol0ٹ ǘT"R3'}{Mazd8b65kٔc`ʾNCT^bwzl{=e~[T8o}U;DR1y)zW^΢nҁ a$R Rր:ڞZ{/ʋZrn%C0_ؓs=\ec 8YK|#yOR~Νmf%s$ZlMcH.FٲgdV6ErblEpH+5}ߓDkq#o~&+u⍫ 06!Ɛz$'D(nƬ49ܧk%V80*Tԡ:O5, l/:vFÍP@WLc t* yw5;hr57 g+ґ]Os}*n@(砥gOW9GӰ*k3W)2}htI.z!x7Y?PdnCvd C(y.)%M@~ƈ,QL3޺ $xȞZp^]wW}D;(l$E m/kn >N6' pmGjp&q\~T3&ma}ȝ8lcp@$>8QٳQ5䓲UL@U*-+bz#bEoӗZMܤ25io6NPtn!ع." &V{mPf!~ؿG8hkKVxY(Oʈg<$RBpwɷR9;:=]qKlʵ kyZGQtN] \ƞ sk ν45>*;do`ń&++גm]ku.(> Lkdfr*鎗l j[EB3$N̥9 X xgɾh:AqwBgX=@jUoўBF4pwkFߜt (=DWJ/[HB7H2|T8MvYv Z6=`HRА,ݹ.Tp' 4Z`ΡKqؕ P2P A8ͬFPV)fZQwRyި`-L[&{yFZྑ4#Sz`38m2?7C&KSf?7gy8NCm1H[͡%Z>a;~mX̴2Ɯ.w9x?w:UC媶ޮZdc?er0Y :JZ}ېPчN4. W{7$g[Y B;\{򈣌ZD!˞~L X}N~ N_(bߡ[+-,^Xq Pɖ ݇ FmkԄ#T+ZGT1+٬Q&Jy\gScNZlsj OiКc(' }e+ml 6";t! o"{4{"WUlʱ$ǥCr໵N #x .Q;jT&l^`~ ~e=H|J'V,]+m$s $Y`0 8E1qU'4ql (_AA'_<[D~>!LJo:m/y1MKzCF;@{j_/<ļ{t}c,);vx8s79M ݂p  ?*[zj(LTs6/8;XGH6 ?_ {R@aGDM8py"OfvC%|h=+e'e>☀  qP8:P(>jKJuDQ ԯ-}y}8b`\yNPCR"݃ K:4Qİ?ضgy42)n%AjOKucmbmS" vjޘ_KOգCכ|YRU_k]sӏc`#ۂ.IQl'`($Dom(! UP%2>iLA iӐ$GF+y$] VU~esk 5\[ bZ/LIָB<އߩ3ѧC#~/엢"%1߱IpPvm.d0+KHwj JQîvA@N)XBpAá FN,gܝPM zS˱l 7TR]B>d@kBA"{pƫXfdh'QBvtgɣOqT> 坿muz0G䵵1T(RUTb< g=. MgMN25OD]ӷePLp_W\_f"A1QGѧr]*L.]&)yfOaĥK** :5t1oS0g! ~KVCK<7N꛾eo1'0kG &ƫW&U2$asnA*c@ҵ]}LֲѶAگy'In, Ӭ܈{uf-V^ǢU <3jK:78Jc-N(P=@;$RI&i$=A,VWܹ@W{PhָPAeɊ"P>.zdia !d6vX'YGkjcJ[279/_ M'PƖT:1nR"x(**6Oߎtj Q"5 QZBT3gv磌NׂnkVW$wTo+ :¯sLJ iw ?YWɒ۞d*1z:c!Qk6bU Kfws(<1*5%dST g`hۮ7v?`1+ôoymI K,I+uB zQ]}D} Cɐ獔~]$[nt-#4o`1~Ϭ,xν.@Ee%Yyɩ՜!bhGo[$aDa-rPe[Esky_y sO:AMR"  HSEƸ xw,3!k{&¦ϱ<oXe014G:|G4ohTj~NZnObL68l؀럽}c-6 ?!&Ff55&~H&iNFs(Y_8|Y75ȋ$$9"e:pW{w&*@7ɵTV~P ꦤ< haʖ6. ENU'&_3J*l,K}UE vsg)xT5GHHzOϨwX'~d&xCkY1 -\x)F:?JV%K Q":!nD=]9e_Ny H$[ |?e.ƴєOhE>w)T+U>w7/HO/dB*ҫ5ԏ ARC#`AuQӼw(m]eS:: 0v&|$J98\fe*u,a9`|;abmĖ1#8LHP&꺢1gӺslC/տcB@qsmO.x#^n=a+.H-{D/БP q)~=uGMM`GҏGA ,h}֢3$^@ׇ1MR>rc~h6 '. g@\ bj>Ot1n4ESW[@~s}X8vQ2a)pē2fFjP Tla[VJkuϤb@qhU/Od<7mleJ+vEABҼ6o lfv\2#?CCU}̖=~؈ C$3eD7V8Ŏb5&VHrٮVntw~ NbВ9bNr9-݉0؍M#4@*&"~+W rG$KzcSl 1K݂0ښqyVޭӬYN᭴臅<qzC%qgaHĘ-u CUBe-?uF 0a}gt05JU3k,fz\H'}f$mxIZ0ܜ=6 P|nb_3iàk9 4 oTPb̎gǃӼF<)MkVd v˼HmifQglg}خ/S9u wKGP«)U0$סȧ"3V ˅Lhuo4vIȱt#XRU{Ҭ w)5ě~BaT7>tkƊ)[{F;kql $XM):2J]#306n)%I,rξ(zeT3H%H$xBGBHsċv:1?[c"'(.y?R$뱦=A՜J2g*LPw`n%P<|p \H' cb{K0j]S*2hcϲ?R )9fлF! "m+p,kYW[E[u7>VߎS̯?`1]rF=2_A n#e_ +4ݖ'Z[$MAu[0xEwu?]f?op1z4nB=mT"g w[!קDGQөOQfC&/?fTkKh&I*%eVͺp&aDU vtu^؛dؘ;Ua8Quh%Y^[si^O(l(-1=fzE|U7zֳݠU2ݸ$`}Kם:ɫ4݈Y)3gc,Y5Bo-ϒCHj)ր *glҤ<19xr2<]ͬxo…( oR'QOkraaen}ܕj&bVi-(~+:PX J7pw늊?ى|I6bfź|_G(SzyH8ZvlOrf^ELz(,~haOj>R,4'R!-g›_*WD'KdmfvA[DWQ4S_mbDs59uT)--fڴD=Li-}V٦ |Q1 0ثɡPʶx-bus "J~\ C!Ei񥐗=B}ӧTScT ZXlnyO6ֿۛPDiӒ\Έ6WH*;ѡ a XroF;zսr@$ESХ ToJ <DH=[M)lH>_t^#E%.A0Ccŀ 1fxr蹗2E!g|Tl$}grm ҜY5.Oռ9)NFԺQ.Ң0:!Q9T KiL'5lD1 W%SJZPP˟3t,֜ތ4ZP_(H|aI`IRܷ[o1M ABv34ro>GGf,Qs|'T(V2+Ve|CpK65s-MY- x_j3zOC*0kExhIxB&Pި980@d#X31H Qu1A 4Y~,YE?ВfW_S>)^}V<% 0;Tiĕh>3Y+Ý"Eq>ڄdܞ~~Zp3::d1Ÿ]nG\qx[O9oF{!x۠r+lʽa8 ̖EӷȪTI=}YT½y ueFz@#!Opc> +Wa1']aѪ5)a'( &ѯ)D5rw)"/Y,!|9N< U$O|mжAG;W-<_BeEq}t,Y/hبx0F_1*{^̭mRC~&n+Mav;s3\cۉaK=I  ڭOw׶42&}~~an&OJ[6 $F]8EK8=D,[n[~ ʜx3,hy)#\Esjs~zQCU,iX¤t?H{}g-,k4 IX)o{(5d!I(JQLeJ cׯ$H(Xl%#G_sPw/#YJ1tqsZa`, FcJ|<.að`ZցHV-+@j,GXe9?~7t넽[\Pj'43T1a$b͛ݍc=8.Z0% Fۑ%zC>6? OR>mU(W(6q7wT=O|5g&O[xRՇq&& AYЦ5P~vq<%~Ȗ|Vn*^x{ sZY(sbMcZ{Vuú НZ}JL,`jCh3>j!aI+t|s505ANL5UUciB-jD I\ğM$ѣ(pT(.T+ryS@ ~ ް|<eC#BcV9Uwi& |ܜ/9 h~[/‰Qw|VMŹ k=!~e.zPj>5@^ 6|118)"_}L`0bo(Ɓj Mz.Q"{8U\!Gfh>U'36<3**?nݙb'XX?:t歶" 8Tkr 8ۜf % 9nYɃ=bkHDn ўA{N]Zl00&NU̧VϨtzXTO DIz3`BJD RA&yR(/P{0L`/A(Q(׈fP5Z~tdJ+>s$!lyyŕ@3VٌLM^wr3D[,Rw( o|+Ycd!ƾ+'*;,VNJMf3FՄEY mIڹR(q>XON|Wt:)#׫w_]ny͊;tRx=F:lsjuHi06YZkQƞ۟n&kn|i97_yJ. iR0WJ,ېe'=`?8Md7IQD]O /nF.u#(F*?>Ce^YhxCPt c+ה3w=3Zt؉t\?1^yv݆ޭ=H~c0;jţ0Uń/\U:+101a#Cmιk۬1t{Vٽd"jtVWV 4|MDj;eV8 suW@ s\`kɼAh_Wj݊E⮭E?#Ɛ/'m ?7հ8-ث~i8ෛz+'|ĹiuvgvOA6v)ꠉ]ޖ|Di-GMv}47@#Oy@!U`U4Lkcl 4@Gexuui<[&jVWUAw' wнN8e ==\?U M-,eg'Q٥hIGRj:A 04?\;. |E>11>4ֈ9_'clOJZIG4(!u50Y{m%yF#Biv9Im47hY b{@*] /[ÞhDI*xz#JHh< P{r!r58tKIcjc CXH5XHrhawCpҲY4 Rb̲W*{²}D0F(& Qugl⇡*9;ZNOGwR Z|h豸4aj0HUDOb).Ec&ձ䗼3O_X[/MEAo>rBFdyŁrߔ4)03F1 z3M/y^JKȝhy67v/4dMNq{p0w|@b}Mn4ЃEC3jFح,d|vqҵIm$}P_ I~>y[XIиyb`$*Zռd+1kX1;}Au JkSvISάѸƽ[%R~%Gب5-LF\D95BKX(h\y;Y#5~I*?F4)ub/Нد+$`3$w)*^BSZ !ՓZڵ=pQfws_k#-PdYil{Oթe )1WR泟!@| 2} H, V脒܊iF{QHbuzš"Jp`R!tomi9h+>+{)__8w +wbY/ߋ'OyDҍ 6ئ,|m H$b 5+i獨.vtw0@$qJHpe#BJ+2=?o A4jXm L(kUhj,ZӕOjN8ݩQKM"g7x3-[uP6LٍDY`NoX)N~inU((iSzd< y6AXP+*׶Hv~1\Sj/!ʮT]ak!`*En~$Tây5_W #l<xA2 9NlaQypaXEߵ&=JTUOYa:z|Ypsl4TT|h[ՂVu;;Ǵepv蜼1 [$DU.\Nܡ#ma(dR XXtqPoMa CPP&$ 6vRn5O gL([2#q`jg^`dM7*1(1O?*g16:7_]yO,ap,Tb\􅠈UXVj!v 6;XpS[>V֩}v?7]HGnEt 'Ж[I{u3oXgU<U+Viɭ*!m n ~a9DWwݗBY_3*ښqtH( wOh od vEm7fcoǿ&&QB*FpB6R`#ȝD+<'8{ HrI`#Lz.U)ޥ:nj QΏv ,KLz(p}5pT~y%"4i3JL@h,($ǭ}rƲ$jֈ|{u|9f'ֺ:TS_{gp6Пcc$#=ag#pN|V7L;4I:y̺Ю7?@ *|`&"( [9Јc(viNmmZ4iJp/FuRfB |%TOI'#?+Ċ֜32bԌhaV$˵E^%'i7tXo! }u" }~_A8e+Y/8?,ǓL(;/ӿ|1BЭ<7wgtJmL&v;$\"xV=Ӑ:d!z(گ>NOFlZӮL i^c+$kH1gLf7YK&GoP \@JK4Mia=>88ŒF疸lz(cok¾ҁ;i3C[H 3X ?pOq.mh)$}Q6WnR殃Oшj `@CJ )/<'//EMK\mkP\@WO{:HNB*Ot#W[߶osw~'5_AHtZ+hsS&):'ITuϛ+҇i,(E/Ԁb /-:$[h*#b$G"DqIo%*6~e.=N#-7EQZ! 렿BeuBu\*I??rdz F:?2SO}I 2tl?=qxMH%M`=>wv6fZ/OCj'ΐڭh9ɀ$ 2wTrey8/처+µȬ{ slՎ|z!H  )g{FHe/(:X3h^'O&8XkEo}&vmD9ZX$ dt6߀4zR{̞`|w>:`jh|'D"ivņ p0:_K8LV**"c奕1 Q c]GN"`mU$ G)D@4͵TJ (a'wc>S8R-K`mɀ ‚ϭXCf9SS6{D9Nʌ^r)4XW!5;5<6(^D3خ#f3a)P~n*nqNNSʓǟnr׎faޟ+o8'?'˘k&<˼c]:Wo?^x{_׹s6A@@P9Rh{:n77Vo(Dƕ)k,᭎`ݜ'rt_d qXȒ27d[Ҁ,ZBld].Os4xQfrojaOَZGBH^,`!z0ֿ%9c`nc_v7DO (\W^I#>8*:yA*n"|p O?EOB^`tK7یxPoL{Bzzjte9u WL(7d}r41{Ui%W2hB~P1XbѤ~k a?nL@!]ī_ګ&'֯|\s)Y$鶲21u @ ЧjK M5離PeFpPURbUx]=nǔq.]@ +9EWK!yl:)x\\u46ASr\E(:۠!/;=1nSĴT/y/L &ndu&0~ۏ&R )Bk]˺ӢD~|JXM 3+r^Q~__SGjů(X:D08l{6|2 bvyNk[{I?_ڮ\N pLx*ɷUm {~DclX{͠0X~goUnb 'g =6 t@CmG a}3rQZ["+Y{EM)4]mvzj#uC\;J0e z`ݽ{A ҙ7D24ރx΃z⊻Kg*ULvno)m 1 j^4D}B+'^Z+(sffTapnkiRֵ1@io\ hW^Ѡ+~H%ރԌDbx|;vkEHLR02rکdVt uU{c5_~g ?,۽T(Nx~:amH`^ûܘh̓^s 1֣a"'^RR ES2r8)Kz31k3_;c>c_م޴vE}Xrγb'`[6MFyuUR7"{R_subB %yVaW-Ki Hq}U>+ <$*#$ 9l::b?͖lS˿y3X@ )P?孨x%&Bi!DS}fr(YbZ[ORDx?"6eدlViuW)i@;kusH[1p8TDl)*~.MƮM8pFTtYqE6C~,h?M?F4u퍝 ?Z2T vfl?I#!j, YT/^P~]^|V#zb݉z5JsΓsxy* ?CK7x{= _`pN1s<`l_-}) 6[6XZwb X䂧Lд= r?t闿+Ɔ-V95(Z띩֙\pPX$ g0qs_Y;//g$ L,jIS*/\gfրtBH߄,zcqR"ܚ_zy&sk޹ũoHBuj(6kz+\u ,>F1_3CrN)s;"%$SϠ%AbX83q:Х[j~/hn ;)QVoKm%v{,e?/5/p͍Lօ}١a71Oӊjf{jK*rt#͗s@SbXh"R8a͢/w,&yv< $Q &y={/Q8PG|锵LàZ"Ӱ[kQ'e@] ]-_@,q9~/<\cL?@ {NQHWDHOǝN <{wK13QܸWP1S{p5s' AE.g\_\y1_d\^11hȁA}?pC.Y bqb%7|TU[͍ 2'}`bF Ρv'S))iiŚ@e*X?^}q[!5(ar""QovR_9x<sퟟ)L e 9hl$/' ՕG{fm'v`~Ys ~|8efMw~_6 [g-В dm*rpwRja)SƟb ,oXȭz~q! =6q׽l[gb6\Fٕ6e = Vaes _>2IƒɆs$W\ [LfP`3O@yTܕDNFˆopF^k1|sB$~NާRO;: g4I_F WfN]bIS!־{ N+C:Gyu+Xo[m!si )ʶX&\'fPxm|u 'a9}v*f :ܜ"HNn܏bn[&¿&aW_B7H) m},IC^'5lx$% E\8JVCUy6)T9^j@Dxg$b>Py7:rvrsO {Ͷ\J"h]!FX1zb?MY ֘[?QhG_oR>16sTr )^$^MQ`=7AL :U-_FõSa /uܰ-lеh'^p=nP3]k=XRkao5:U~ Ąkܗ3\Ԣ9ٲjIۋw;zYAp'[ JڨGVXѰtPccgK r_œ8~.(ؗ,Li U/M`$ ahP,i1w 2,!ƭ8s/&sov׏$Njꠙ!9C 0P&#h<"rQ?=|M6Jg:,%$lB4dzEZؿX:i?aAK5X|܈­P%xQ[I ǜ)׾4qj[_pns!=~],P Û&5G#Wm!ޑJdu 8:Ut0;8D-l=gde`FDH)[|T _җ+##h[h%hSۉ#O2Z 0E(`Tc2pZg\@Y`N F?bU܁q <`*r& e) 56-\s4Tycy̌FQqݖ0ί q@r3H?dS,\ 2~fmihbL t}>LHB-N<CPwʥ^Q%җdq ER=h ύ QbҖ2>m߆Ny]S<J37:'&*̺:ʐ'STQY')xqU(3?6b[+R$dYM"1>BHH&I(X6^PkQ 5Ȼk0$ӼUI¤ԟ$h3%AZFG\D8>2fVDžL|qd|f@W<p{q6Gv +9kŰ|`/dn}Z!}4^[YnY»"ir]G 3%{9@Sh;NLTɼm@X[Pȏ2^W@(HGخb!s}g|6QM2&`WZqN~wP*1g'E/ })knۮX3/'A,$J‘vЂxu#~h£ES?&D fԈv}TjeկmxKZ[dI@ u U\ʞAY; :y_^'1)"s r.@±(m#W6'KL.ԂF*`K\OńۃV˧Slj)fbj!U<:hG3%Qnxiݣ@kB۰gق"ʐ!CnpP/CyOMKqhLz˜%edLWt8"߭)@.zGZ%_>kv@ /*Wb1.qN&ths%HI'E2ph&5dkۭ9ye^Vb$U !|Q+R$%NK]8<]DO}k9z( z.;E9'zp5! #v'Qyl%!Fя=ɨt`I Eۡi].]˻<6kEMpĒB ,ߤM,\K3$`00@^ypQZ.g"SH^5uLjVYO"יbܢ\pg nt(aab}F oqh~ +">W&GvO?k 4 ⠁VYEk d񩄅v)<@R-~~7mBwIO6b0:Mj7jŒ |6ړ4j2~qFq#mW4O|11np#_(˅ѕ~*'uMدՐ%S61cȚ#d#TbV8;$jW =okȱw6ZEZz;^L_8WdoQkgUNiҪsL[ՍtST1_2nS#@ zscB@Q^kE9mgi'}f;z‚bmIIĕ.vG ?7p%hˆaBfZҟi3ecwkQy=Qh-] V]%mnhC |wd~GKGzyE/j=Ai7\iէxG*jU-˚L;K Ɣ%X" r$;F}"{mr"Vva\[B)ghmsgl>n?dKdj q?#"\F%bHvU)H=17KjO*ld[W:psdY^ecw 2uoܖ]] l1+i5 Cψ?ܔi80^m[d kcC8jB2!T6Ad_wñc9sJx_"̤<>jfXw]Q*Oռ> bo sOų)bteKCgɿ6  bN VAx@zdCMdTuvK)CDvoX4riuc,f!GW},JxmSY\yz !E쎝 QϽ}v-Mx(` qe'K0z‘XS!PC65Uc΋$cэ=>XÊ8DsLTQ1#Qؑ[Y3ׇ#PKj6IraDO$zw3QS?er_7eAc@&y+X[+4Sty) dޗ RA9j掋k\sMn+c@DF XR\1P:W Ka!'ppKB}x I 1@wP%?u/! ֦FOE G?g}r8v=EųM $[M:ISVF){ΗnsH~A!!wxKM`>|<  nb[ԩwіYihlo^`-)7cPswۀC'ג`w 1 )Ƕ_њ& d0%Ei]4_yd,wVWD)9ԐRo5pg*@"}nƼ6eH3Ƞr7ý}q'Mbb=#++mK T.`y)~K),7<[.\/ώNdiGz#cˆUvp=X;mW8ǵ7RY}9໒st5@s\%%1Cs:Qn/ʜwN#Тv!~R?X퐤);Z{٣?ͫ(O`mP[*\e\lh 8/i4Fjm#;Uo_jEwrM~UD0ޖs@1^qH\YUs-EvȞ ERzĉ_u(e5|泈:^=H5H"R:?R 5j!Y˴C=ԅѥzqx`NT|'Bbq ҕE}pf q̬ Tyk" bw~-OnfW,؏Q(q܈gE9)ʯB\P'n5X ˽_)_̒DFN[DԱ3՗ť5u,xvR[>3}[to9D%,RV+J~q>rwih8'ssME]!d I=tb$6nmAF1U^MߴiSc\S$zyqu6*$ךBk}][@n (ЉZNXP^f}Ч+J'9U(Mm.ڴqRpSzG2 nƒ ac\)" tn6Rj X') G$4GuH #ÑH֭#58qa͛Yu~>~{O9"YSSWސUUA@Ls?mξ( ͓1Q2{P̒ME?Әd.R?1(Ї,O0,^? Q/.P6={b z$rF4Ol<୦RpnWN5b,7f5R[.%6_Z$DD9EY,#T1Ewquܩc\zM)MJR@ٍA+S\lgG"YĘ8F#tBIU\ cfhOlc*Z~-.kU6՗>I/mG|g5!#򪮦,,Mb"}I.7ZbWy]5} zdzbd2'?)>s?a⍯iX%;@Q; w]Hʴ*k#0>__%%b2#LR)y}kK~㨏%O/݈?t3UFcWs}_3=Vp9oN)oض211<f!-dd\EQ>r=aL]BgQUmW [gr0Zrzu9[V,`H]+4cxSɾ<NhB"NSL/,qQ\y5 ǯ [xdy 5'rzMCYS~m穲7GEG}#EZP+xNڈ.6zx7$Ÿ=@UBL2%xw#Vό7#|6poOXRdʾi}d5_ru<⸫ﳠi(d1[80p'SgB-P]N;Ѫ϶,{svy\mh/Fp&aB?LT' ԁH-ȝF k`sٹ>M$NTFfM!؃ |t=} eb[!R@X$g=k]2Z0ܭ SzZ-tč8l*3gBB!{zޘq;LHBl)ǙueO{iAw d7& Ea*TS qFrHj&>K*m~"nFpٟ*-"uRVr7L pr,O(Sv\Zz16 ľq+hMU `Kj'?3#YN@.ӗmV̪\v(Af_5я/|5%FM ?(&=/`ϖӜs2kj$T 牞>3fO -%:B*l҈o+:S/)|<F|3hYȟ)mx' h6&<wX\/gP|ћ['MbixϏql ̭-U8KqLN;kaLϭͫ9ic.'CQ,0f{̭9sj.%-@J*#zN͘!naR  =Ѝ*<*|АiG;5K.$1 BdM|<0tk<]Kb;>e9ZtPƠ􈞬ZƶAܛDq q4'#n%Xr ^SI<֤8gNǨTvv/#N}6TդH9!-fbaVM*Д yoa~-!(2]ۯ+XΕb.|Eďa %k &´ 4WX~R'=26s=,Q8iQ4w 'K$eC^go1_?ǘ1O襚]攇^'<#7|*Cp fgdo{o oZLt[*d|t}SMiQr,* nn=vP%hcj<0_ʅʉj*ܦ[ 9:5R>krc}(fJY]6ZQe#QT!q6.H{m YSQI,e׃ 7u K,Hcwۇ5-B:M’M F:x2^&`:b;d2X$[$N}op>F8 J*Nz#h.cYyQ!5mQ$ H>CYt׬P=g42z$N#fYƿʥ6OoÈ~Ҡw?]fS ޝ:MBb/H9N{uo'⑟O1/UmP{~Rh^j[]}^þˊEh $DvncQp1%s?'8u߉1Xr"u&sQt+^4m+c!hniDUeR4(@v_ w}Dp Ҟ )ۢSmY /ڒ0,GbR)[ m#kK-G=Zҵi1S~]uaH"W닧AdZMKߺ|4+ũ+҄OfB%lߔ/Rp=rekM**2n©0{Eu@q+G zΒ4Y.T/Ce`#5y ~9k)}Ɣ ګE`/PaiAgy6kd% 2 {Np"ZeLsa"MR#xu؎4~.4\uZ'RDG]ybu{Z^EhS-,&k"ZUd_Xo1"iT8~ YR\#;u,g6gw$GcMQ1.cmpö:t :gbSWtr$n;=ګudTTCE Uo2K\YYq ]r@[ ;Pؒ`imZNm+t'l7R&~@TIB"oҼ矝n6?;>ܷR.\Hcst4#zabQWX:݉/п8t[sHo3Iz0m;޼r2^RM@Xm|31tUqW*p) j/+b{WuLZR1{6`5~1P,Zo54aco}sRvwjU{Jգ6ŹmVf; g7qxOMZhj{2tKzF9C@E7VM'm7ԛhPċW)Ÿ( a(g#z37fZyåMm[~d_4#1nFtǬ":IK-UlWX>/ϑRLH XVt%oJ6WHD ]n'FĶPVSX[;Ƅ֫j.n7t&a#Ki?.7McSK'bLh"rP߻z߄*KgגNpwQǺP5f,QY RC~&\)<_ڱ^q1@=b[}xHr`㯼{9&f|U{A~y| yU3DؠPUڻP@i"5L>z7p# XCv M>W\bj-Јi#8{PKN8ӪAUbmVwdwJM(q^ܟ,Z˻0\q=qˆk*yTfXjdQGC/tG SsvY[Փ$a 03"R82Q"o˪5 ֿe TxrA?h87ebqP=kYttn6p^IAdΨ tٛin;;AJuo&rL_Ll-Q:^wLaA0:f86ʬ"qɐ4G%̇%<9^rr\5(~ R fXTQ Xͷ\I~u6 rueum|XѶt@q+m{6+2–L^S圝,FdW~p,di2> cmtr移S9< ˩Y)F_cQ(Sz8ΦK8R@8ITXaIuEyjW ~32\i0gbd$Ќ.&0猋pֺ <jjfhR犆|VJ۽IG#&߃nPSC*5N!vIĤ)64S ;*qn7"./v\Ir,/4|NAV^ے>AhCGp"XKɉ` 5as)>g%8,̄/H79$ {Ո6{nмG{P/ʀSzW WUzLR0'>+l`!7m|7\qM4Y#^^8;ks%lh D(`ȟ_dQ!b 2,`bSm1>ïՎee=R17F%!|ځce)p %5/a0M'N<+x*n/9q"st+dHr:$%\zӹG^\eވyUsD ,^!@!ZWZT\P(ӣ/z60y_gROjî}wzXP@&$O0%/Gp aD(Ĝ ^w2[NwNę! \ +/F|=PՐBQ65YhbrTEDR: 2:j?A|&bU}RKu* ?0A,I\.֠^xk\=1nܷI|%4eW[+NQL&Knjn՗(L re$~_ݡ+ܷ-}.x+xKh{CP{[UL¾S)@N0Z" ̞ M8QC&%D_B2| (~Ө>5A.ۗH"Um2x_%vQx?}Q\ Q§v*t{_h^l89뽊[%ݙHd r7GY*o˷] [ۺ ]{ܾA3ByQg쐵"¿ Xtv<šcw$[ FL:wrhmGɾǹaUw6c[򻅤 NTؖ&ܻ>0SOe_91'9-mVb gt1.J_Kh?LeuV/%T9tD8b\!ݷKyb?POEWۧ)ψ=Y%uA:ftaZXP+ .Sw|м̱Xk;k+꒠TG&ܸ}y M1nK,>6FԆT* q}s="Z!=a~| :PN@T'!y40i C=vDNrߒ6"BD3$>z[rڻˊL/FhKQSb;2yFTix"LxJ{.XV2Z7v7D! ^sA,ְEA랉E$gְU;<|U*oA"+~ǝn= exzw>k9R1a)30?O,~,S8.j؞ɆE+{UX%-ӱח 2.M/yؐ(0%av@F R !ѽ{by>! \=C=oOWj+dQ),qlrM Q=({ћ9wao'; $_$N"'s>[vǥ.Jl'w;zu~:u՝.m#oTw2Y!o~0`78vN }z_)7Xɹ2tW5EG"D$n[ yhD{n0Cfx c4}FA2;4tyam6!=}/JDrl0YӨOz%GP2OHg"9V<ghKk11>W]떼+sBʹ $y(ugnH]+_U=WBTtFz̯!_tٱrr7[|)@^~hUK 6?kĐFb0>0ȅuxs k⬞aҥmSf渒|dz n[S8Ftm8b^,>}QCxе_`pIj&2~]C2cBXU<Sf1X-`DE\C>CA U5Nul3EGuΜQLbhdDAOn`-H34VmA AH(ji@ڍXki^soSze]C(jIb>/̎j(uK{K P)i˻DZTBQ^p$kn /d_ɇl+(>q~#FQK7hauq(rbv3=wSvkIMDȮ e RCo'3EVMzB1ʊ#½qTZlĊ~ce D[=n [Nu?QښsڕP'TbH19KC$ʘ 3H+<}پ0kԼju Iw%iMƒP1Ž3=z( uYn&Q)K ;I}?V#W4G_0xǝ :|GV;\6(bUu%^-H>. ފqbi&NsQ`V/_MHv aMqz(31 }Ϣn[9!ni_=y{A*O|Jqf,Mk~s5 b<~Ej"!%vc0"A K5&L~ۇQZ$E=-*+&ZqN&X:_B*!j?Pt@! E i~R.vڻAY8AłxgiqWW<=N$BZ̞Qs2u061Lh-`ޯ5u&;;0H.YbhX-Fip@ )|ꮫtOСip.C @_8=#7f,!aJ2`UI8sn0ƙ}3xP"HM*ډ혯vCmly#/U{h RTЙrn['UzI6צeb`~?[8 42$G.hkZc;pT,aN@r`00٠jkq:ؤ GRsG) s9XW8:8kA}~*-ojQ8Ȍ ؆JR !#ǖ_ǜ8g8=&:NGRܦ >Iy##oAAs|2勓r}}ь(&?e_< F.e9-O(y@oaK3tLe Y d?ʽ5l/]5nyX;p|^JSxriDS.b89}:7xe,sonZ;V%Ӻ@-#7z3UXQ9Pr ӭt8·Vʹ9{-cߖh|$%4W`?iɸcNH#x̎6#mc}=TՍ*>W1B⋫;bh(F/W 3i ?=vt#[H;mXnmA_nJcd$R+Km3ȹ~\VQFR*_bَtqhu JU. 4]7ur~8Ud(̟=a0W[@,phZRU3|waزZO7]8<MZ+~&Q٤'H0װ_*UTtJD^.Cx;}"a.j3j1#z@ G£e<)Y]T% 箩>Eɗ=ѱ`{#:|.\\ 'Rе3-PBF&JW'zY qqIUwOTs^O0i΅ ǣ[7=* zD(K<݃,I9hB}ga`#9T- @J׬QG{F +ѽ>-ẕO2:JV.K `_\>]MC,Ǎ]|NR;*]P% Kbni&npJ ,!_Þrfx[j!R[9Tnx]ͺ!0F~s)Kh!P'pGMrKCp_X~b+{c i̓6dpgޚţ_T%vjI%`u^F\쳅T!iZnni';Vy}$ADmGO`!j.{og*`߷2c~4$X`FRՃ7quUc4,81B>2oEfZXQ%nr]痚pHtLB3_/ΪRtדop*8m(_^754(K`_Pjr 'bn:~Fn)󑄨% |DJJ3Ťֆ/0 >%$ᘹMM_{ܗ@Jﭯx17䄰\؈#fb4l)UZ:*+"YucdN +gE@ j[ R8F,>X%HXָrC)ĘH2JT"1g.mښ2' IJ(jɢBBi\_zݟ[<1Ct^'WzdF O7V"ePke4B' )h":9BCɒ1-'竩3-ǎ=c56LQr'Ё&Ljd庂L<6{f~sCV?ln=u]f,]T&OMܑ:@+1u8~4~`Xc`*\Vׇ QkC=etYq:)pA/j](ۂ!%3׳tO,P=uek<ָӼmc*4wJ(Ӵqn g Jo|_s)<ĬoF rT\~>g}4 }9Y( _9t3[1R#nʫD&+֌GٓةPscS_0 ?m&b0.{f Q(yb2^ZJ0 Bk8x䇫a=CrTvnZw'^cst | x#mz _3 }bZ(b ~GSNBeиڦ Pp} 8V"JT&p6=E"I+"AD! vPDHiD q26A]˼#E8Yܽ}=|B23\Y[,:xlXq9Yf|uB(U`j"73~znJMۚW5Ymǁ]PoklQ.h! %gӥ`+ݖ?bE2ɒﱆd/G?6.4j?=G 1J#/"R}tT{޷ӬSlJ Ui40Y 445Ndc "cv?blD3K#9K Hb5FI$L Hs/qؕk }91G}̾?d_"oaaNŮ:Q= i'vw 2re=QSDCA6{c#,CgU7i|B`[U@pC*lq{N)X$Wz^l Sgx"C뢒D?`q4WkA;favs<[݉ktT h;ă 7 T(X7ލj{"U=zvxal] z ,S= 4N1QAb*f$u\;{)3([eZFG>rxFK+9lrP fGS$Cnhj!-uة YҡkwyfS=.$wAdh5(9X(87XqUt\#o}LckF/XâyQ `mAsyNEN*H M;}HcL~}wc i[~'-.F3:`ؚj:a7"zCr̐L_:[īʃr]EDW+aR*GnOّ͑T'ruoScPW}% =@:0Q0JFn $`$lU$u[|\v2"[JLJn:Ig|T:Hq(-=kcA;}4!z]#:gi( NCuq&\jOl-RwR`` 44E8-uTroi,'G|/ٺt R,-ڝ~z-ʏT}]YUWVFYӁU W*7=S Q;iﹶe- $酭HIE 2{& ё~r0hH\Eu.[/F2 Unx+Fv@{p1WLq,5g}Ed*pR5ء C*i$9V\tZ2OcDܫ_1'RAbFV/bpX'$0k8\Q2K0NE3P>HݜK r*jhpʀ0kKI5 U$(:T$PRNb8=r!{^*_He(wYW46yi.f,[ևh : l)âb`GY†Is!+xd@02|G&N_laճ~Je0#Y,>=+oY>J/;؜$T5#& 1uG*38m.`-5ꛥ@ Nˇ֦ol,S?L>6 D6oB"1a5=CT>CTxhD%XP) s%>{r8 #4o2 X(W~#dmqnҀZ-JeiF0 rQEl L!JK z>kT5GO@NbJ [ȧk#06sUo%w_9Br})'@tmo^]>gHɶ3DIEXt>Yhrr>ϽGJz$Yٚ!SYdY >ۣ3B')Réz6e0OV d a؛V>;–JԌ>AߟВFpoXzW<>Ӌ_4i` u]6΢tG7ҳlzϙB'*_%3zɬU|.{# }}.{-^L\L P(XTР_ leD77l ʂ76GYY|ҔVrL3GqmߩZOp6=\RÚvi5Ԁ YuH]A A>m K3taaAǾS1A)a3͹.G攭SRz\i嚷II7^<$=MmroҴbw ^/@z?$IhO|)̰eN Ť߀dr{OĦh*t<B&Zk =e!j9iEYnIDLi *Nu 9x=^jyw^ةr)ElMCx ߞD*AD9=1/TcLZzvE;`&)ed͒3+j!ԡU~f{$Ŏ(pq{r ei?ٿ͗dz Pذ1jga6]DtNݫg{Yf qLJv go& ՟S8ˣF)(@-HWlQa+qjˆEFh(5Xfu]EPd4}99AyW8B#T_(|YEsv˸"3H )F"}[4t~F.;ge~&%Yc-E(ݹz" T[Jߩҙ;5FG#˧yxJ8jҼ&?hvt'AcJ_K9~mhJe0̜"̪ȷW) '?u2 (pMfc=y׸ӏpEs4XiIfѺ+,2V'1O{cV`0vw~l@U|ru=}0\f迵J X4BMa?-W@C]XeJgg>(laX/Cyێ>Mg2=+&wtEHHaVLqEaz1KevBxʓqvds6ȸ["bZN^=y~MC6x117=lh8PƝ*sP(K6n?iGd NzUVyEYhQA{D|pl XNfBE(p3E~3µk3H Γ z譩J;uCW;[qЄEμe$tpU,]x Qj(C Y!|;o\bBWغzfYTfU\h %{(egHG)^~'Uqbsy 8"ߐ;酇T?sK4ڕ'ISC`ue ĮriiXОVo`.tZ7] M#}h  0dBvIUP2qϡKFˏTv!71Dq//o(_Z 8K,ȻPUu+6KX8rt4 ˬ ~ҔfvkܣR(=GzE[[!5ĐH,؈6`I_VPB)Q(:=}W*ñbq=o1iO߈k:96e(!aÆL 5J=:.XλNoZқV4s6X j 菦ǩxؼ 1}'Z@ H1$PFdmک d`574C&vBU&mEQ˨~z=Ɇ QZ8p,.c^({[|> +S!OUǘ&Xc&91{8:-^Kn`&VoY95:=>ْaxsjt:"q_U `'H]%4d2kO?/Z;Cш4u:]KA%|o2_ r"]TB=.2h\0l֦^zD?(7mӅOPNCna/t9`O'6/%bzѓ=v#=bo#iP [C~yqAe K8Go[^wf.*8Wd- {g-#w`*K.JkhbBUAjz1QjB5/&Pm(G l%Ε I/!ԿNa^Ntir >'(E /Z0gaIs 2UYZ[K"iD+{{ \V[^&!OP L̉~3)DzpJ!^pX&Svܫp9oA$Ry ͠j[*7&64"$rx{RN"xٷ/9mG@`Yؖ4.P|Nޞ:+js6Kd]&#-!{mESp2sP?+\g;DkTUmiq6; Bo^rpudWt)y6`g~Αu5"an佰=Jq R8q[m\tv9&E8|$VR;zw V*vGLzzp6'RFB*bD{a>q3**u9^y;wKCJ;rBZ6 No@AGp*n4:>N!uVx%acIPЫOdF3I>fV0 s^fpKZ Jꘜ5vh;ysHܑ3,קk"+~^ KMv=TՆZz^KjA_àr{ MN>+Ш-IWc= D/WQGyFtoeRUo;3~;Y? +B_]U|HFΆU dbνtWa' jx82d1ީ4؅Ddޝ9QZQZcF@#N5;*/3RSHM ږ-TӲ乊u>j/,*ڇG;9f`#Bxڣ_2ԡI>le/J &݋ւ6r6,vWT e=o|^4Y=vù*}.;ߺ}~; Kp=ax뀽9Ga[Ea(1_A$5H? rvC}vM nOA0__,7Hg4G( XJ{P:2,q[zZ2W'-XP v/@͕5\賸o f#:J'KՁzs>JPxoyf )5gbc8^st8ʨ8}|샢$$q<%lnLuɡ|@qS%]j&EP옻X#M`9PToC)ǤK$NX=N\/YByz}1RF, I0ygl\s]Mrhyhedcal4/yS6mH0_3l~<=Z&gF;18[=:/|6]Dl&I9\PqY URP:l|8E}Eg u55Hq4j1UL =Yy;4ܶW!p @l r C5]]1<ݭ.hn )}Ҫ">à *_BűNUyq@8uЍxs{u^箁 ~+yD4^:#I"n)!Oh'q"go]˽NIgt#xocpQy9|_jSȶ\[Xc.dlSÐb8[ wb..ny߬N,2CL*uʶ*%S}YaP^OcB c(h֟˓@}C:tE[޺Zd$)}ќ;,6̉^ǎF#3*LjB.?Դ q!P{ڮ1B~`[A KÓ^m[%Z}Ŝ MjU14P b{To;U6(0?-Y(6鵡œ< /N`i! yH'."2~MYwi0+Q(_4@:ѮTҀAFyG5%K3Ҟ!~qsexٞɹ&Vl}#Cb<>%\Q?W&l 4 ӏjF!f1gѲc)*]O% R]U[h[;}rzP5xnz¼N>˼st=]_;a-kDfؽ=Cޟvm]v֫{FSe)#USُJkd71Rl(85l>XF0L ĪEjG΍}ط;6Vt/)QF 5=&ߥ*0 H&iWX•=N#Re!]EFZƒ*n΂ES}$$®ˍB K@+W=jDk 2c!kR, -j$ϣQ/4yHc^Aƍ,8K;T6`k!,Qt V2G5MXj% ?ۓfw=GӨc695#A 9M]/$sbj+`_?[$_L60q?)11. x!.dP~zm8=awDczUk:_ңns)FKSA7#7s&?Q1 %|s  g1M8Mv}9S7ɀDSr?GIh7zMs0H/^u3~n+O6o.Ϊ69Kլ_ 2rkSoI0?Ẃ`d` *pi_2RQ쒕N(/(rixN>r};X l?0artzxvE'жAЬg}+nSۨ; o%#n1rx~S[%qHVh:.5 ~m)[-uPK?Fq+YC:X0^*[h|)%eηnY}х9Fb.^ZH\jVK#ԭ/8H]it}|Ot*)fV./F gvL˯*_h(m1#MR\h351pNn4pYǷYy' 휣ǀtf+&-d8sʄ JΡQm5UMq'0=zp굳Z)_/9Cf>$Ż] dNgsRQ7@z,7S.d5,=b!T #/w4Hka@%s>L6(snVb-1ȶơi iY!ܽtL{X SQm?B=sH.V| (qgZݰe~>CJan.5@G~3W]{HvKdayh"䆿:k f,V+ dNsŖ8i)8(e8i,QwU3NS1<0'FQ>SC]zo$/KqpSt=2~Yy~cK? rsٜX;>u:q/3zem(,e$Yd'Ի2mESX Dխq,u]V= / @?Ѩj}HAe;&F1KM R$GÏ ؏$]+gH+vQhy1vW{E}VѐԦ㮑cJ)Az{氃`JtsW(kŝ~pxDZae*fP)c*98; 6}Y}aټ8o!T2$Jw8Hb4BK:qw}m)\Ƌ8ΚŔ8<xiAT0 FrrEcAQ~^6x YmVcǶu w_R禟/a"h'^-vRptA+>$q^븠sKb~dJ9qI4 x" GJ|9ꋇOiɎ&݀ѧKosT`ıyg s0!P{HXfqh kx6Zᾝ3}FMj*kn/l|nNf X# ,+Phvj;#ri+,:w2SyN 8.gh%"UQV0JRY`b-#)k4n9ɵMt j̀*C]!턅>zlUQʂHG7CK*^sP[{ T1m~4lo:Ko?&z3FX^ΰ`1k 2mz׿|lUM&@tG>: dT \c'@֬nz>SbvY[ڭR+t[ BS=)d ;*lߵ;Ľ._>KfBqxY (F|0.Ve!9'HhRv'Dm*$OwhKy#b. MmKŎV}]Ax(?pހ\N"o=j.@H/|㸭P@D8"\}8]vzY ș_fE"ѿz&CXcBu3(u%hFDXE_ F1k4ˇz(-!*?멸JopOlAtdNmVCًhq*x.1;M7cbc;KAq (΂%w+SEsADBIi%}ɞXyL{`8(,LGN38q?6 ŏc ٧˴Ïj_ NV8i @ċ"DzXKԞy-𡃥 Jo(C4ʟ;0⻞rzt{$b9l{,ư,cI⳧RXr~G\]@zAJb: D͝:wسX_{§9/xCl`$ly@.p[.`ɷlS^{m,O:B_fbT<3¬r w Zy) fة_Ad3vO#&~5/gjb׬\q#-prb|/ٻ-1O4oqSZ}Hy-K_[}?)Ѡ._5l)73U %ZKg.b&o{|(w7i@@@& 6=)1N\aPAR<Ԩ:&հ4Jq:w-NnM>;qqѱ O[VD ):%ClfU\qd;]A G";ʩvx(hѺwtMQp-!Jp@AI9BOce#@a6VL⢼agZԛ}ѸɵAњN-N >JL9'ĔsylB'/$L1qhTu̮jd>8U]s ΢Yl(Acp7W* x,KFlyG}P}H@5.tUCGr7Yt )2\/f&R!k!/]/EO<v!jpӜ3Fe⻲{ӀƥE>Dm>tZvg%õ Oq1]J+ݴh%f3s>R L -vGO=ɯ93Uwv«&pnƷ:!(·ΆpN9֚o$s N>`Z4J`O}.׭{CE,ro< XJcuҫm-v-D\|K1 5)p oEfv~RdRNJ5Lnft\cR#zK? ?o#7?AzJUc:V :k_D\T:VUi+Zˠ MYcG=8`TUPWsiUbzgjSNlxР|@"3ePnq'l[da_'>]I%+=yhW}'A$vb2,D3~PU5cLck76.9En y8SNxǓlκ*"jKh ̧4mRp|kͦA#=3P1FO$ÙmBo "krVh?O) =!\2њ)B&=d]Hpf#a/;+L@w$$k!ߠZ[TNIg x<#Ҟ|ߖ+׬)و(So ]ubt7ʍD;:?@lQEyJt Ҟ0|c_L{]@r|M7 |O8 +(WG&TP [LD55ٰ@/W93Z%uZv5gzwT9ܼnO{o'>{= W+$0ݨ%@` )pĎETSG]'H'J+/ȹivQHF~ ©Zsȣq#lbksQ e @* Wt+=eJ< `W7qGd`HPyD"V=2-8:K @bg;©\E]-x}SdEh("jBşaZ䆒#C>]ZYp|좟ZZ"DZcޞJp+Vm !O2/$AL&(z;ۼb.|Xe'LB<5GB_1&ѮWT ^t'>3Tǁ3* |Cvw"gרפ8ەTIAr[Im(0ԏ(K/󞀵IVn~t%7bo=fZ,.uG?-W/Z!}._$X6oʓ4tus֗L9rΊd4nq1G X\]MNo)|S7ßN#"v/{&pcv.양"SE^!QeÌ{影وi#}kl~\ 9pWGrWFdlQՌi{I0P0kδIyI5$[mzkqȲs c=F{ggWaeRIΎ*m'Ul<3M2UӴ*bLLC2IZɺ}mx0|@\fsꙔ (]ٓc .>)6,=ዥL|Iyʊ<0~6/"b|)V(ʼqc{$UqIk<ڙl\|UO*&uҦ//Es\E, t rd J폢tg-Aū5J{3Q~&?-UVFTp3{JK @]C a>a\[Sg-QhDF4tB.Qӗ-Ax^< J+D(OW GM-NA|%~$Js%{{Ė7Iq%}u~;K\^־z@JXM{ňk%P]uT.3$ փ!RZ-^ߚi$Q=@@ 'ZZhDedȥŠh|-I!!i}'5ye^QsUU{G Wl<篎$ T?t^Q} *+b bF٧rfF5/zs&[d*z13vLǪtĉDT(vniBF~"\$QF~UMWxs_큡=Wp)m?U" ٴhZS6ک>e کQ*/\}62^$>K An~`TE $16=t[ } rXsΕ:tU?;Ȝ8=g>~̀H+sKy'# D>gR! Qrdd]B|R m *8))b+eY&{W:KYyN,FE7h H[p->OES`#p--H>. BխËW ,z3Jpfr!GqUZUjۃOEPa&#bn^ojSz?|}<zB{V~=6jbY^s4mV̛:*v14,suM;#ӯ >mWt4.LbD Fr\x]8-;sš׊B+YI>!A/yߦ+F4yp{"n Q(i]@5Y"~̉`{>6;śjv2>n.h艑p!N?'$ߎ :>NDA_Ĉ3[^Ҩ F׷~n{f©ص ! =Q'{ ۮFkYYZ]5\9F9oBmfx>|DYuH d|/G).q'ʂ5UE"z?1g%NHEJnjIOsB3/-źbu$y5:alX rJIg(57 ߀kqo&F$7ژJO i^eRcĒXs,mt( "AzdAy*#ȴB> &%Aa Ԥ19w?"GA0ѝN%%VZ^W2fQ"M[C86I'{ xzv>Ў>8xakc!=u7vғψDTTGfSfU䲵D5C|HN)'+nq x9+/T ӛW7p؜+T3%YÂxA_^̻Cۇ@'U979m1T|*}HDKOp5,{ȩ{2| P | aWnŢi4_SbC>{? obb)c[FDQcj Bld*OsJyHhEK.ze~] 9)cSfar/TiId|ل xȵ&vpÉH"//n }.u])dZ3`WȁS} cZЖ@wa+|5$8ƣ>gȤV)˪Baφ@+M"K?Vx-/"p"XѸ>)a[Y(ʰӎ6 ҲCЙJ>Zt M;Ԡ|ϯ l H 'Zߨ8qZ˥+/Xp&满1t¤,# r? _-iU@A'oJ>]kw`c~nNJ(`*!P|OVu]5#\&p~bcƁ.ʏ%1@kg3 DFЍ3o`G@be[g2!p co7;`d l- xEid:[]Az^ɫ711=f|SBx9GdT.v@51Be--/mzP; 9 @ chS^'JSAa@B[ZAP?Hdzg͢|^|2x9Ca sh /nP7 m{:}}+>N|-O!j~LyJk{ε:;CQi=x6@.xD1uUՌ7O+Wl{e@9|#gF(HgD/ P(X/OF'wB7 NZ{mH3CD\.Zi` @| +lzޗH h!"_S>J18AV? $h׀nJ#r [ >WH+Q'(j%ÂLs`Qm-{دY56]+ҡ#1aj,K4-\ g!QYj[wY`8[kך0te[(+*c}46|0JB!̘Npa_Jj^d8KA`Ɯ(]#6H7`g +u-BOwхw6L)4}yH;!?8 J֙|rc \]#&_Ygр-Pˊ`v?a}"F_ޝ( ǾOA>y J.~g%mB|auI!iYh{`,͂o Z 9e/x=\fioKe*bjs 6_ <XL8j;Jw)zڥPO*& U:l8)S%藤\yCֵǡk~mVᡊxLspR*xO,qU(>={H]׌ ?nSw]?\T`@׶1 v)YBqtšc԰vl27ӕBr fHsWQkob5eiKA"e|hrx[2^up\.z*$Q cXa[Z@4~!v蒗)M?{߿yN{ma_p xB|=n,ew!w;st !?嬣ZO J?C ]/:q )VehY sHb@ޑ-vbAW-m/Ļ/j"mܫ&vJ͓AӳLCqFpn`faن<^zH&MjE r{ EN;8;7zO/4'W箟h}rD)0ѰЋ0l3"cy33|G^p@ LI KA-!b)3 M&^u%`^Y;'`< `x 0a9kg٘Q@u` EEuy *4K{J+|eja֝W[_,TV\r4BjJO3*%0R/W1YRw,ѨgЧYV7*8A|r:o䍴YaE?.h0vHFG jƛ&lznYitAƚYVD]W9h\UFeAۊnY"=1^aו&lWTEĂ;Urt C;{ˆ_,8>ԡ劖!٭J˧2z#%7T3o^T & SO7Tz/kVO5dhDWSɁƕJAW/9 kn'ZH=r)/5Kk>֤ç\[2Cy6L-=!/w&ǞWTY; v%ـMi%%ϒcr",Irs Q9?@.R/XLZî, +r@X:=;6{ӕ7^]4iYDz=ѻ5OG#KBfUjpBw'!LL. Gmj9 &I ٴDT~涚aOsگœ&|ӋͲ]rBJJ~bbuH4うSKi4eDm UHy>/nP6ץ$t_ePQY2qjyѕ5G~{qQ7'1ԙ:6{ĹuWaܮA?eQysW0fhzW ts*aucVzL*Q~Fll@F`9P?'2#-hp*-Vu>rAC:ArV,Y5=huS8z;c+6 ՠnD伻T1r,ܢANjY`% UfWO"~"Q!-RA#Ӹxxʝ)3Nr-~P9wMoA:;+?b|$6q1` {2=텦dLՏ_{l7KC69BR{OXW0[W=Mt6sʢP'0ic^Xd}Ce E0O?AL*'mXlъm.|QrMP0E}SҦ.?;4gy'/U 7b;t?gj5_c{oVa"^ĝ?R9<᧸g .Orj/̥Z9l-L]MlRF]ǥ w@]ڙVY@y tuHB9]ʀُضୋQl9)~zf+xu,4Zx:y<,=1f7@="sgM3O^!&O_Eu'5O͝!>Dwlю`AP-H6"3Y4Leex[ȀVb[AB z]ZD􁑋 .^ 3v$LmtoEғK:5%V{+nԼqͬۋ٤'bw D'{[VwY FQL\A`d!Kk#r4Nf/&i|L R @|˜ G~;Hb8?}I';E.5+ բxvOdx-lLxWjNaB*=â@STcA>L𾑈#+W ib=aH솊$O^Q>`Xff39ıKZf(z=OOڔ5j=?E9WdS;WQ?M0#_+'6H?7;KiANc'." l2_B8. [jvȋ\hUɑ{VQ?rf&8Rr%[&*rh1{뾌ƍ޼̄_j5XPM|7Δk 5ڞo?llHS[ r>X}SmE >6j$4Ҭ}ߋ[5*ҿjJrfݩ.kt ~ ilXR_ {.4[z`kQDHgJb^^[iD wRia1A*L[`4HXiL"8HeE2d?CQ0ѬWb%*V} 1-F"3('?YhMRu%G0;"q/H`KÕv !d o㿻-J<7c8tze^E w. `EP^ꕡ%dSjх%Ea įCyvT0S>y0]*UE] 2`M?T7ĚnbfPLJepT ?EjMάp464e=k-V"~HY=d&SWPϱWl *"kKwo"t=hVlVmUC.rs*L" .K^HbT:ڍtxG{0Tɀ5 Yb\yjWBY6dRdžOC@ WjcqYfNrmf3EnELN T!ow\ĩlV5VʍN4d*g@tZP>d9:28uL]Y2 χA/rFLe N<syW<>׎>/lo ]pYH jfF}!oYI/v0! 7bӪ!swke9ލpc)Z[0 Iy H`/2Ư:3<E; SK +uC#՗C9Bє#әAEsf?pZOʘ7jvD=VVan^GOT3a=<\~w) |9zҘbrh͜$*!̀gIzl1*߻_a!sw)2B!…ybvԳ}a: o/Jݣ .x'ˇxU*v أ(TO@tڤm,lѼ^E`3 |tfq1P oCU8|%IG:ZFQ9Oz7xk^bbT<,HzWpq3Yu0 %i$!*}ϱc{4hGظGrC΄a`A dgIx7vH+U_c3?4stQfL n1;}T3,DL;b҅9,˫nsF i8qJlAIf=0W)lF"l.6/shVs݄~3#u  s^5@VIYCaR(m=NInv xzU4۠%3p&LQj4j޴@9&䗌W;7G d#Ϯ6E*) g/%Qhy{N#^D!2;?$BEsh$TkE`kvC79 QYY֘!>i, :gy%`vBBpS v5 V8śo% PF7Qf4('z( ٽ =DgTxqˡӯxz'g3] 5na=v"Mz /NgqZ;0sY@1?Ur {Ě|qB=M!db4>O@.~I?QGmkrex':qt0llF, U1$M|A;:DUvyU7 @axR9;Q/QZ lPFE;Rۈ JIUD96C:&Sȧ(ȯ('˖LAP'7"&d?ELk"btz\/WzH;1@r)1bnWVXtqHHXJaf݅;/#] 8-#:v?_G/H{zKPHv!͞:FHS֬.N-?]Sp2< ->P]nQɩH~>?*mh JPpV$QD`r3;u0LZ2<ȱLIqc6q7jorҬLM$dNO%>ɺf'V`y&'z>_a҂uQOhx;5\re?, db(Fk^v_YN]LnF0tj(?PO܄Uؼ0΀qX>3f?v~ -[=J]W+hvܟyG b)ZgG^Du9j@Xmwɤ9Fل >a'VbxHՎ)} ;.bȀxC]x0"raS b[02^p'!qfHՙ{+DSމ^YgPD;'bu=hh`(U\iRq,R먱/%\-^ڇH !bј~,ww#Oԥ;kh|k8SC;ʧsDG&Em$Ae0w %sE^kceͬA M>m9bX-+tM8U-x)~(9`jaaeo1ܫtm|ܐ  n}|Uxт_ʬFi\?FS\ISR 㜃G424fLO)h x82G#,*0(>uc^PmL_=%hȶ=x@԰s*3wmQ8M<q)UfXN,4˵L15_NZrU<}Uw.GBSk\*!G$yBj'g6Eͺ9W`l!"}9DC Bm9nuW@4EJSPW}vFbih6 O^B##(dNM>L.eP,'E"T:pWs%I)u10M%.,H.{qExZĈn H8<^zk 3I$e\nIK+U+ԩwٷ5) 9*T-ňL#h^nC F6̪)d-N=r?:oѡM` j fK+$19ҿGC-40`<"sմƲC{jMOŸ{jlή ܰǂMBy{8yɣ pcw01A!x yQ!;[?)y$a)Eu'U0%`(#> U7 EJ@/mPP|@IUM!|w zhO |$C}b*sC{lvNt6/ۨ%# xbsoA#$9ޭ> :0 U`d!SЌ(AZposgկ\8do{lRg+atg8l9i͏y|#P,1c>]Ejmѥ=Ce}@BDݞOuYD!h/K|.f:fc:{Br@B{}[!m$Ź绽'c_*l= tUyJ #Lp YOQU Y7EX0aǁ:GJG}r,.PU[ TkE#ډbqK.Les`u;Le= Ry(Ȇ<u;jV\O:^l}$<;1s/t q16#׽g2c b5;{xxBSEtZ碣5ʹ>.ZW[RK!.U㍝`c6pR@Ά\CO=M@-#-S3{eTᘃEOq'>5z &X%tb&EsX%qr.G${Oax"=R. CM<J?mr}U!**S:ȵj&)fLz6 Fz :i-Z)3^_p%>xl -yo.$! e:UA ۸kt? ˲YzyFZmKE+Ej,;_:sc֘0~RvK`ssqmK4~#X(ؾdL+U#=I^]žBua0ʜ^ԶzdE}o㡟qD>YԳ*FkQ͕魚bQb/OӱpY! d܀q+Q%:JxZQ҈UF[o.>'9i]ON"ExݕCzt"Q3-t B-Eq"`jM2=do"k2'}UL|6IWօG=ICgIM6<1.h;TWY鲯i) \olD/֟ot>J9ȫk}!`+@j 3v1}h:s;IxEV_փPu e7 @bl[ot-'B~5tIqR[X<Ĕrʳ% iK ś)‹޼ۥqQ:tQJ4"Sh[ DmPQ˟5_yO(ySGV4k;(?2;hEn];W8(.{v!uׁ@- /fK 4"tS aIF#:+GXq7ܯK(Q7dV=kǒ_Ia%èlmC3r7ۺWwԸs`rzSoɱ'dҰv0skY`SK0JcU /!Xx 4>1(tǰ"F-pDNI:k];L'9XBA0m/cHB7, FnǓy!m) ,.לq/Oo-Ѩ:f{ӥiA6Lbr#ҭ2\gsbݔRianZ6yA`s+oUd*Cw93+VInYnV̽|@􎂔H$UW9I9F}xS Ʋe]މ?Og٤u:9J }J) Ml.K @h!QA2#]cE;Cͫ]W9BES*w ߰wA4iT/̝lNjFDTY|fkL2[K¸@Gqf6AJ$nx6K ĕcLp!!mS1'rP.1(mɕ7%\%:#F,1I:l8{Y<8wl #b\$4U7[&<`5 ,f͠K}b&,ȲG^ +X 5NIV9Bf zCXԯc}9YvzB,tX҃tܫ 6HAU:(@ggD$0UA͑2w{'3xKnxC-2(c?֨!w4J>lmڝCMQmp# "k*nI?#ڔw l9N]ɇ[]%\gi) 6g<3;b&+>L}ˎ~p' %>3Pg,čqhsea S>}iZ#((?ɷvq/>[757M"Ϳl|. Ӽnlim DCITEE?@I,yA1:z9U]|YXteP+XzvTϙv`\l'ĸ/$9~X4oy9a! 9Ϩ_%僋:_d_^|,նW譴ESB+R|"FqS̒12"4T&Oeۣ_vZڻ!jay1&J?0p9Yj} WɗpxoHhH8b`uNuF!&gvʽ`$zp? R#z1bl*mue1X H/DU c`'ۋ/M-(a4[mK:|82I,!zGd;J :ߺE]MOvH=z~Y%a>mE&!h= B@X*=Z墁[ߛK3{8A %IW`B%sPґu3ئg`##G: Q[&Yh)oJ]U()̥'R{3DNzU Ҥ*j$,G޸~ ~@TB" - y-(z}a I;ƔHg3 )5j3?9w伬U N\>DIVTO:8ĩ6[_VX",h[qγZ4d j34Lʔ30)]$< [;8R`qˮEfөY4. ebkҡi<,Zog]1Ȣ1NڪF>P cce4gpi,XN%]adgg4 .0+0WC͡9sFW %83:= &8.̎YZEf(|ɧ 8GlҫbN33MX-k yDHؔG `[]uOGU`LS,qu k|c[қ - JPn)~D?GƐ^{+6;cjՔ ;7moW! CyސPK˂Fa=, z)GOZhyGv}=f]x}/ +sSȗ`;^mfRN(US۞/d74 F,"ZnuV&Vϵ. *c0p1n&G>D;R1$GysP@)C Tɻ]Q% B3!|-$߉k@X3/F)탲0^_T5m2G2? 6X*ɟ7 ?3jE6uY\0Vji#:<|a;@{gϵ橃wn >b]/E{<خpr Ȩ s Niq_i]Ւxe 8ɯ;& NlaSbzX~aglg''Y0,F98@~2e5% V-?^T``7Tg5EK;%4GE/7[&}AYb-GpD'T 84ǙګM-QU󨪚rrӕh"kaogW/qd;2UR@KCf lI3y6;kyaz qA6Y5 ~CL0?gW!%*/\=ucRcahA $2qc"VH5ƿ~ sZ#weaց@,R!^ji_x>Җ]%Vb _jnle#ča!R!lv4 E~TNS{-:.kPtz<*.\x鷱\@~ h*]Cw<_?>Iy87'kdXY9rrojs$L\y8[q +r鿕mp"`Z%?wB^NȱO]_c2?7yS`geɆo<-|MHz yǸ'9;nwcm.U![#<@xU lȑ`42/#YOo[Xt&ƂRZ)[3Fm 8dŔ;FX(A3ӈBZE<'o' rf=!YiƲz0@aˡscLVoKjV:V>o-`6PPXbԊ-,\A6D5¬lXHw,+4&Mr`|L gJ9&kMa`6t3[c34Ew\ӝ zJE[is0 jkm5`_r4S흝Ϭ3*b2)~N<ͳh]!CXUDlZ]+<!kF~j!!T2Oj^Ay(>tʹfb a>fs,uWWSr сH"p!jByeA'RSAPo2?q{L6 =z5(Pu[߾dAk/'_}ܶ&H1cBJQܩϩNR1/Q[2MD\Fuϒ{ThVkAʼ{t=#rq _)L|ٺI8 k+>T{C;ZBOn\h~: mwU/!5+O$zyxf,-1uR Od lԗh#NQSn/{4f2磡;MMr`v ]؅F05UϏ_ L?;jOtS<@C4a088X`O-eEm|3I9}ҠHIE*>9'$5i eă AI2wI#!>W-i )]l =T/{<4?GJIy^v$?bo'U]Қƨ7ԛItSO߼ ۵}$)y=EM-kH B3.!ROWcSn46Ć0hWMA&1J^;ƂĤ <_m FQv>Qz0lB+ل|r(81GOH2irv@//vS2ye!oҭ7onYJ2" qڔfŬ LX`)ֶmz^Q0 OGfĽIM:#t):/sjjWzX3a+Un[rǼ( ^IHO._#'˻un?J=px Bt9wHoWt@Rж5j t'*aiG>oI&},7"Ȑp;fLifPYCfȺ'w`6d_EjwM[^NEٿmU :-I D5 >5YTxpoU*px7MhJ\٥XʻF\3Z'HEZΩ`gMvh.8NݗH^^,K 5 Ŋ,0Boa;F<;*a=n>fLj7aKI° +n0jFCmF;,AG(2ee0j@clUֵdyUq>3@WT@EP:G ./40;'nڇ</`@ypL$RzU*w%jb&C5X⊁@6620Iؼ߼^_\霡Qӄ8鷛@#0Vb 3 pR 2;E\rdVQ "h;JԺ}mBU˅py,qSt߬1by аY("wXAȨUlZHeT3p~GomI xMqŒҟ(]}*ҷyՀ(BۍiG#HhJ0~'U YEюczfe4#0+[3k=ݎjq){`ϗ2Ip(ECTf T^^ "k\68-wٳe]ƽN ki4~0e (rNKRVݴdW UZcRB.6FSM4%C‰Wlc&\ 2A?1MS#7Y6W넂afʟ(rA~k 2ǒ0[OAjEh^畒nțrP BgePkx(s1?Ube$]j9$#.:M]O!D8#>W%$ҮjoV2j  ,<G([ȘQŲEi"EwlHBAq*Qd?/s?b|(ߪsr`"BuW ;= Kyܱ벏%`|sW9'|N*T Vzw{Y۽ny]QB'+AsMVWrHن_3CbeLsLUYN(`-(3m9k1cԀb>/mH Kά@8\Ǜī Agpm[,]F@GEON{Z{iW8U'> 3FM;:&Ѣauqt@A% }UR<,BI3h1 ޹qCY̞ ("On ڦvf=};*d3LOl!tEQ=3g@ 1 @ƇB4S͎'k޸F#D\pi˒E6N qhG?Nj_,DqBnymb xdА0m~ffZ KUE2>HRg%MwU1/hT4tUj`/_F>=l:=.=uL2fÃci"#NMSxN5a 1gI2[r"y.b /T,=VKfSQ`JwNc'҉q-zvbd gC$C9N~:f`| 622is`ԑK]^TVq.1lXrX-Zqbz֋-aE1EOE-v[DH%#+2ٺ${TѢb~Lsgڨrce:QKоVr ,/Uy$/~wДL`Ч2q1/U|zXXF*M>:ҕG9ͦQ2$џvLHإ:m=!ﰒ)ee|) Iz[ %J^bvP13D' f/XZSdRECݮ,;%+2>6z8==+.9Z66+IQiC/DFҝ3ryD(VXW&1/)eT j@dKza4r0xph=geӑڻm6GY($8 SܻCZ/6Si"wY]K/qխ96k'1k%0J%ĂYҥS~Gy %+lɏ2Hzpl u@8~Z]3DfI,FY}DSRB+?L~#B6z@.ft!R8zRJ3>9ciD Gp9}Opg@cuổ[zK-;=Q~sO5&D\ *];?,>K``VvmqgDX'ڇb,='q``-| EbK5~i rSVR>c-A׽&s=:~^ܜbN'`gx xpLzXZJ\(S[&]R$ YO(?^(-{{)uY0eY}V;Mmn)wsC% ދ"TgS FK'# ;.&ػ?|یs5$՟KuO4*xv\F<9QFcflRPV^Z@ęC-EyVsstlZqAB<=6ty[l"8Bg.V9{N2X1 =PUq,`uؠ&?oC}5lőޚN,mLZ" r^FHe/Dq|~1%$~ JXסl((lK4#{=~ bM/C5?x?E~znӠH=Lf8W2Qs[϶mW|)#- I6%vC^ym3\BݾU_' v$mK)lJ򕂹!^Z6nw7GkxdL5hD4ul=fF} o`7-6ݬwlyb!QR9>ozX>s,a E=qz%ȣw`$v24f&[`.y`F6kxj| V hzH(Ue Z5SO-pd¦!"I&N :#whTD-ӽj{_$d||H{u\:b !?+uǓYd0Sr-̕`8.B8=^k5tMa2sH`5AA'LxV_߈#-"`:skEvT=V~`0r ٠mqR7r<Ԕ݃pS ~ C~be' eQ,J5P~'L)Rݽ ` %H@u#1=N`^ Q6H+KXE9lD2T#v#lܕy، Q:r 0P x0}TĤ5/ {. G&JB k]hEjs1n<-t )y7 >[y<|94-9Wm}t[3tcR*KWwYAHk4~4ÖM H;#Q:YȠg*4'e8VWQ4{UP#~:ҳHӠhtAr'B 6$j9nMk' R<[VJE]":bs2-Ř.('~6̟J9әA6?eS`|uHmS\0,7|C[Qf@ tgq"9IRz%#^ndݼɊHrc3轆G.4ʴ1J9R#AvrT{ܞIiЊB LWྌ ge`vtërFep$V\ nyggAEXҴ_MKH*N١>þυjdd+2,k*12\Ъj$:)T(VQ'5^d*`_1b-KQm#|(MtoclG x!dyoDH`pɐLNc U=i$ E/pI[lK2k١?WZS źwPCJ}rJȔ4ֱUhB-mTN:`;Y[/ꑬ[Ra|HmiK5֡iUÅ?*kIVmJ5Yض컴d3ƿE&y$'vu#Lh.2'~vf SG-k"T2b y q^/+hh.xl~I$KomMg۬Uu/<ӊޛP1<3CBkdZC1T# Z]ߞݰ?c WKo;ؗCZ"E_7ZE𝀞b?;Ϯֶj8:B;vތFwg7xv.(T_E]51 T]IoUvj{dfLgqV[x|[H=]|J{*q^5 7DD/C ݬ:sX[KB|~j_# YX6AA3-Պ׃T'x{ U^9D獴qȗϗ6PVҰT6X1Cr]kXf@~JvCBM[O)kP{.KsI_}"}wyN7TNk V!?+Q6F΁mT߬Vj{[TkJR[[wgNa;){6-N|Q\;Q5F, [JU`:~D7]@~^V%>0.kOWyzawn] ;ئ)G/Hf$4x!y Ը_շF>mN]G ZƐ*1CbTt 7Ӆ!Y$ŔLi2eF!i'*h`cOʑW/1Ȟ7dBi4Q2K紑BJN)fPCAAۺv8ҭYri UyHib.Ѵe ]O,tw)XrPl[nN˄69P~Wg :)G!8\Kd_Z/<<4iE$u .΋! n_`A&n1xi/MN~GN@>^~\z YHۖ3O:om@J?G?{Ȉ)E-8ǧr,ڤ1.|dQIXb?;/ 3m FɆr{ lɥ"G;^pꯕؘu\rĭRF%sͯdWe٭ PL5/Nj+FNoGhg[3ڪ;ε<sU0C7f#AǁuRqJ,c$&Mg><aH%v*0 M|Vjz2/HQ;WQ6Ż9gg8;t!f'UcE-LK*;N0?=@ЖsƘZW*{H\m"5j[I('l"H\)nۀaQ LR=Ί9| SyE# zbmy7J:/SPRFgΠC5/꥚P枛|b˂R4B)vJ@I.}sF ɖ YKk s":הI}c-(vX ?/ `!DhP.G>c]D,H^T9>6ڮ ֌c37grX+{ _1u .L' 5aқs\xPtd[s>{&~^f'88iѵUmf=§g~1M%DZl&}# m;) = 4r):zWcycwy֎I vH2ڢJ<=oFTھ$/$~bW3<$.-^j| ?RGhRGab;FӹRO'7ϟ4&2lMVWnbGCZ[lZ^fH!eNVz]'&647& X!23$oiD4Hm[C{9yՑ|j򵭋"hK%.0HxD,g$+KL 7'zb)tA[ p܉u᪺w{GaZ. }G> sӱ02UkNj-aHwM);Y(%o@_J0znm(4oV$b"0 >^Ar~gl"Ima-oi`V .u-*SWAhZ`Oo RW\!鑽dč}8 DmG|*\R(H/;Cߏ=3 fqs bu9zr I)D>X#(=QЌ oTw7N4sS)Hh N): 8%)˫Ax-y1"y{ R Q.c~Wǭ_T7<$^2x[FœlTۋkl:ln;o?6cP gi/=GapDB}c!l}auQSr E⪷m(>JI;y(uaq$.cU9M%%Wz1GҐ|-אaX`#LFNv˕lk~_O9ZdF@d_K+emv<տD,I(|j]VQ)91m^}*U^`GТvM"|0TOcf_#e}tt  #Kb`nd8FO/2xzǸ4PK"][s[Uxe95qJ tU7df5pkm)D}:ʴ"q횝/d Iu yÙ+9 ^ #i@ y=~`4pGs\wM +$@n3d3͹fLw&VSGw0)hZΤumZb4oC2rxPf,vSgcQ%m_y4[0l6P7P@v7]ʠD1o :2E@:T07 Ul+.⧜T4m ,9ژh {g!'b_vYA6ǫB HQ%u|jroî3MD{c{t ph}b) 5PگGW{9pC H^+EDS6]zI3#2Ju2sX1F=]*n!2HUA(۠8^ָ3Doj9N itsZנ^(*6+K䕯d񲪤Hێ&޿nTi* Pei aNdhyhƍ}:dQg+M~`z9"m샛uEJHȸ넨 T}BxSeQgmRM:>?5Ȳ bPQ|?S3P1Ɩrm/-փum@A!6IxX_/fw.Lxb).!I'ok)`|`@4ThⳏL^3Yٮ-Aa.wf)@ovlU6|PEz}Гt0|?*8"tqGHVƮ ᢄs젩^hq' XϥwKiTd =j%n7Rֳ3Ua2+_q7W0O|%{"|وIz_;-GE*s}n@>>Ĕ l(m޶Hm t^!ސq eD1؞&2O&T~wO e΃FsIV pjx%=Ӄ!k/t}:òVl+y™ \,p̟M {iHZm~XfdL.}&`Cٞ'Tŵ(DL:nNj#^~9*hp, ̆-_m?k1,J;ށ v~3}#`|4[}^iR`(53ӡZ;gV?JAR! [\7-қ.T"'`y~кee N7YK'uXA>fM?rc/^0# ˲:ɆXE;/%M^Εogb vk/0fugih4 a1 )B+TS摦S{d):{ak& nɘ<"mk5U ^a>1ߟ{`)| M:azEJMO=P/hsra3kr:la#5]M煴m)kVG;p&sܸXUd"HHhk}ˏftfW]Բ̾ "PBhjU.+ӳ= FzUt']n~%0l Kn!^r*>;WV914\"L7eQ Q i[_aO KeP#$="e$ֈMɯscQB?i,')~;&b$~wjz]tFcr@)bS%^;h"Y1CSzd,:W`q2"_/XD)k(J=l-N0@P&hl4Ĥj"F8lcLG.G6Gɰ.њ̂xWsP,%ٙ66u0?h~)dO~h"P-P͎#un=2O5 VBR2QbPN%ef)I3pNQ:Є% Ng֩ OՑs]kWƭk]pXF=.&=Aøk~̺Zca[[ly9<¢Lg-)J@ETwZCBSgAzh;>xI¼ghv d2[wRBrTwD;aEALj%Ԡ#i|QIj,ŭ0&K~|SId1ٔ6aMva0 j[mL )X*R*DJVyByԆ83;FCjǴ[a5˘!z+juh&n<={Dwg/ZUƲwkD2e?:MF}g$2m?_(:_-cGKwHnVUpcљoD}g4`Aj!~6Ws=y8;@@!g,UNeB̢R{}4gnyGÊ]]P &A3ٻ;T$cY<>ֺ1Dml<-{Oe.FH~}5U(6%^Rud`f1FU)M;S^fzQPG9xx߇ԥ[ii{LG _>½/Ű{T!AM, u6 \QȤᄄ&vt3*0 Xk9si]3mb71If@SX-,X#1u,T_Z`Ҏy{fw!mz[-v]z]x ?߻ֿt|֧Р!5;lEƕ1yX0m(RSxvlHsi ,M3ah'FmN.Qva`g P(KIO5CMB jGa<&p8Or0Wx5g _$ގݑ~ɋR0W.qoH *`S_1Ъ_x*F-lt,%&L,K+T2O04zZ߫S(Ͻzл /#j%,o+!f0XO$\ȷoV}jk|szvCG/ 1tc`.ݲߗy%pyKzSg}p|Js)0+κ6@ N'^YXL .ˉ,oY*SSJh-'i _zY/#;Ql0ճd-:T,o?֎׭ˌ7!w'.ENx[w VkӚdž\(IUlU~֪OjRU-nyDsjv\ M0;sFL~#@!u$Hzg9S}.Q-N}=҂`bJmIPjPrSB}/f`Cz/4 -t2{xQlL8]i{;WD-v_-b1"Mi+oX1Y#>nڸS.T(#gl$ĶX5OwOΐ '!"퉙 !J ū8R % '0emNYkخ]0-%+\k^!"?  B}^WꊼQpdIcҞ.^?0bUfU p*[+瓲N ٶ aݑB<ڋy7]'Ka:'hf ʋ@t^v bb>j]ux_&܎iΫ Gz6*SDp9+w#fHڈQP_@q5c+`ͻQcspal !UT>nVLVJ=jc@K%'pb z=by#{a!T.sblUG}l dZoֹ&Do26=aim@cS, xz*W/'AMytJaz.BVI)Xf}N횙6*epݿ>,)VQQՉtѓ笞F+GLAg_VqH2wyqc/i:DxC;ΆsL?s!9 XHK*N=  -*q}Yn6X#Zw :Mٞo4ai*9ma̪[AH,Lyriћһi)qQsBb(Nmm|}݋TG5U;JmX9t.!/~HWdq\]-:0$->PΜH࢖ݧì-kB9[ؒȀpSGWcT=]ըRpK*#cic? esvD~^/F:xfQp6r\nSHo״N8r 8]Ic+ȫIBhwW>AR:=e(Q3 mC> lZrdgրЖlBK͟Jp2(MAv84(k@|{X:D1%jUhef'x,I`2N5'e ʭٿa >k4tuOavM5Ȳv<" wdxbGs:Ȉ ~ bX 0HwG}j l!9l;8ʉ7-)-gQ:S=j֙ `$#7U{ z UO$i@QƣA>ۙ< `Pۚؗw#~q m.6":D0I $V,ꦚVl):i2̉:*CW#1GBL7Z(|~S6Iqυ<̂vzB %6 jCGF9 _e)2a'B EegK[TеO^!IkG!Rڼ9?.d'tוj(~4 jtCVNlK[i8=xQvhoV KWKA!u bUw0w]u$2vX<O@4{e$OO=8awH:'h6?_Oǡ#A(8#IzdK?:]Hۚ<)nM g,3r5,]1Tvzxۖnq4@ϴADhO=].]Y=esD1F?J/d酜hwueUW$])p @4ߧv`\ʆ-~O\ێZC P4tq#_oh'{W"JPqHŻ}DޝHlj L)Ǩ5$NBAKRILd|,!\m,HhrSYc*mS1j{cq\m wʆJY.=DO43?AAss(Of&诐+@ag5 Oג }a\d:k<09)j\<3mчh7gK" _17U899&@j|M9%W> Y"JO{!p)q 5H&d( NuLpK){q#`MJ繬}cI?ȮM c&:\>gmIz:VF sNblX^J~(X?_,pR< *{@SGccWUj҄:Ejh8 8/\G(,QF]+q° ik##yʉ%vbEFQtӕaFjzb:l1L )D4@NBIdh*p{6Y?4ϭm_Ɔdmpydu3ȏmR:9pHC>gdJΚL۵-, gM UӳPoa3gOI 87qO2Zkڵkr[p`A%ËE~4BI09QoWs*lbc 0gd-dK=kSpT|"Q $Zwdb]H)Ҭ*̡ #G } IF1TNYc{׏턛śmyg]٬#RN銭+<;n5K0Mi Q1lZn\YL7Əo8C=||1?w:˰@e\rxsʱ"S=A~dbrO!ipcq#- (IW4%+M~ I"wۘ-a33Q'[}cGL $qq&btRikC:@]#8bd,Pҏ58gF'Jk&Y/`okfnVhMV?&r]%ŽekMl2!VēKόpF}`Zac]tؿ&=et|eZβ΄'uBxn`.atv\FjByz9Wl4e= uUwm{6L:,6iv\!p8^bR)RSB׬fxV~T;33o[PI4MR.5 HW`~q@{uGM߽TE5zc))"f\򙓤/@ BnӒy⇙\DJרo{W3S߈⊯kB V9f*vRұko̸gl?ZjZ2'bg;71~m B| zxf#?au  ($I;9c%ba g_Z%fg F DsopכWIOswI5^W}%Rخͳhxr(e|]HS!+Ha5[kU%֤_,bbDEٿil7~QfIEmvPVlRLrӓW?8p9GZAacF`Zfu85>FʞpP7|t+fo3O72W!eBKٖˊ[-F,wkbrn]L';8w4ȀcO/R֟;> (cB7\ѢlI#㝖LV{Y:+!U_Apn+:"̹+GUn/- ;/Ll|v-߆}oJ$ɩ9`D&B/ôZj?&CGD3RgUO)N"|ldk#apnSm.G-w8yO|&rw˃\l.u<٧w= B &xKwDVSt8 LJPxQ#hUakTM=QFNs8܇sS?ŷgrۆ grZ3&&7[{a8&KĄ_+܎T\L\%aرmBW|m.E0k۷#5W:wۦ`ʏsl`| ,k&"BZȣZ+jgtӂL6V!g'vevd>bzoJT!y>TGt/^(˱5I:g T `Gmde@ۑĎϡ3ݧP1*¢I+Y"Vfa3!S;fP);ʼuѲ肒K2⎺`,*-jmA콇< Nnoa)S/CAԙT/XyKNmKXƩL9PsLIY_6gI8G|,iA7~EԨ>?_bϧN(jx!? Nq9| l*T`Y26Thz!--Y|"߲RaEf1< ]X2~̤A&t@)G\6O@ԑ npsL5Z,B, ̰dEDaȐUF"xZO뾠w|L!WbnӘlQeUMA\A *ˋv7Ի]n:qd7dʱ ZS'$.qgCj܏Kf#z`p`HGv鳱ن&n3%V\"1\ ]*3b+Aq=塀hzt&nsҬ]5M~o>w<>$%.&G/sQȃk,_gL(CX$G%wU r8$~p=_7}_mytļ X!<Пʱy Yjſx{,eHmŬcKUЉOSNLӏ?`7[SY81d,\tpa8zȜ$tC(.1T δbBwC~ 0Ӆ_.fUc/SZwJyo$Bw>9V5&gm }]up+%lyf+׶?u&I8*/6qc`g7k"oMƷ;$ڼX*CmuWr&y6^Tgq.PrIOXyȷصAVbAOvcB3U/vWZ}|3>HF/Ls}=A/Se@)_ֻ`s#vΫve_2mٽ]G TiIgKfwWh`h}7 uD m oߨe'^ǹJ-Dٳ͜bC]yQ^uO%ə6m YV17ş;FCw3?"I1Bg&%V.'I66UUOfZ k7f^ }`;]gؒ4R^KL219okwqMf;H(g#PoT2W XLwᷳ&|Q<0Hi*͠Ipl:eX4]2OtM"G>R?uV )A-c5EHSźbaSZ. I@TsC#a{'r9xC1g䰪*kD-h?"+*8ǚp4P'ԋG~%&bRZ*(A b]o >EV&d*DU bDܚ r%l SƺNC^g^hYoF15h0sCSҏ$G~{OG:;颃4]$LDe8?#-j*7D6e^LPr@^S5* h%VhJj-kO`8TU̼^̿CTNXOD ^7P,ye[0e ;k!슫ZD%-WL T݊f/WzNupEzWU7kgp1# ij`RIM8[ܱH}j'HQ7>B/seF%C`i+=L3fmހIЗrq;&mi'Ap(bCbiƻx@:BNm'?l"oFY|RT^FOQtY|zYlw9;2"x[xg>>m ʀ|H_ i=ݱ:(>+2[M}Hi"*:D//R0ʒ`!.Hơy,m"XFj051IJ|cǔvZK}̧t|NK=ly@WPpQO)]62*N}N=?LVxhx1GO1v] ~ՀfT}Az59pX&l3%Q:(MbX'IgZnj.,w/J:EP)ޞ^CJlR9qVp,Vs0j'!fKKٝ= "= rJ˲fR/?׸{XGй賿=vrvLF`~sZ&uܟ\*HP6u#BOR<-JYx Dj/ۏjUQ[2\rMCI{Ӹ@c8xUtTpxhk\KIѾWNTp#[EMA` ༤j' 9l5%1 j[i$3Fw׬;ShxfB3_t3V*d.*ǿ*M%kM3#(u<ݑ"snM!\RELag_g%ͭ1화[㍐pCBߊe6с|A Hcv5@.F.lL!!r0NQVcFT'ub1Cl/|rS J5 G\VV0/g)Ja@͢ȑA AMÈei0w00Зe&=^YcyUԮr01ZjJZ%䡷ô8yiLMT-) Q4eesÁVq84=_H!j4@՛BI2;U#.d=Ӊeq)` 5T}NI}r#?-6E|ΎԇGŽB!7 J^!s Jr9fg7N}^DaǦG@~SGu5Oy98y<耇y[r?[|@ȬǐJЧŐi4}iD[]kqXR*F8"֜f*}CapSU'I,}MR3%Z,w[ś2/~'ؐ6}>sDFnWfC1-YKqVP~2CST~VuwXtSt\SF y -[<UtfN^A@r.5?.8Y˸c.=?A1^tNHȥ䋛co~ώ~- a 3ݳlxc`KDh3q<v< ؅evԸbqpKJL4 rb)<_STW6$xŊlore^xo!bKĀD)p "%b?4`[]Ѕf ʝfPC\"tG-6 tP{3/sx}¤U}&pIXtA+.t ish#j&-՘ajI^JBsp4LQQ%WT9S7*99Gy0'hJ$LiGvr`Pɧ4?Qu-)L@"U~=j3X#|*Ķmc4 ޖPRԤύqPXuq*kBY~{Olü!O>0OZ %2ʺ)Z01zeP6ݔDS1vB7-fS8Ol>vU: X\EI }"[!c APf[c ]Ű}6Itox~OI7kV=5 ^O%g* O9A8rjHWEJ0ܕiKP\?a |US eObp^dkAyKy$KwYt ?V+PP]~$.Cm$2{/蹡R#@CzKeUƐzJ–L ͼI=UNEs d\g l!PoAS1l+Vk=bsTVx 4+S~<ð@3W|qo0Dt )!ecih#Ydf18Xu_X) CjSxKg18nM'Ӏ낆NUm`f(Eݨp\|B2,d*LewA-L12ߤ("27a@%vFw5ۂrA P* 8XJ?qUiVoSuQknW ]N<`P{䕣k!Ozb`ru#^Z;Jv~PbZMQ$OY1bNF*Bq.wVZrwz,Rʨm5v(ghГn.~rp=ʶYN(\ ` Wka^-V-t28j2`J/Z5琍 ћ' ώ/ Zo9:h d)$jw7y:L3i*Oir~]uo해{3|W݌$6..sfkv]f-.K;{w38WW\ODk,b3(6?ݙ?w5a0.nRyPau|e]“,17x/i]lX2!چf ^D1|0UUKl\@ v1̇ ,bcmb2 ح5-6/,йYW=51@'ÇI~Z̎cYn43wHX눠?*G#j%W* L{K|!)(Nm(- ltt aDt06:>v5Ml~SNح{;~?qpa"eӀF z,?8U^/S%Z:J2Ȥ9OY =SΟVӷxaΉSb Hx'G3L>Ԗ;ݗJ>.>D|h@xܫfv ;,n_ =E[gY.E&,˥a1zSfyHy,-nn@M4!CGsqXcsjHug/x3/'FrTt4$3WJlVb-1=, ,&t*KiziDry7Fi+Wd+\G.-acf, rxѲGA&V=}f@|' 6K6tZv%soWFW I2j녰]M_Rv(#ϐ l;KgpY9V:uʊjao#DI+l DB(.+$+yʍGQ 0Ja9[9u5pt-C|daӠ@ jj\7ǭ 3qL-q w V])/r!MwnOlVD"37S]n&זH$6]{jR[Imm 'n Fz!h2K9m_YDL("HMGpL, )"iPdčU _m"權Mf>6+D4!aNJwϳhGߘSonô)al dck=OD3ag [!t`SV 7]얯 .0J̾69nRږb򿴶k5?ǎ؛"q!]҈e>-#{I~[D6GBC"&wK@B9(,eqol60˷˞X̾'dbШ:y{4&ђ=Bo"$#慪BCJI륧Q@k~#hL͈RL o&P;]+iq*j;$iLdp{PxAE\n[,MPxmOq[k$TC$#uΨ3U5dK?# Ump' &KJc2~KguP̶#ঋɠF[{`t9Fa2̬Vo0յ(.za(\H[}7CԢh@z<%0uf_bg"^SMi+_qR{¸Gx>Rt%Ia8Hqz {mwW.y΋U볔N꧞|z{xzbm[*T-QVeOl?: d zOS 5q]nB/gCewe#FsM_,3FU9zO>w-uɒ }E]8\zڪv$]~0t4^Ľe\4K|dXT@mA>//2S[~*Kb},Ő Gs\6 ˖3u+QCe>x!'{0֥lX^ɩ>l[SȹvXJ{욑>'݄zljT|֊0 *6Wp紑/o^I"9TxZ/#/&B0_mکX |?k_ctv|xxrS0FUoNA彬Pf1g[^12rgK%Jeт36C)7]C $F|82ϥH<{=[W'Ȼ-O/nxOetGz`K J_B(=dƺByp5KYQin9Wb2FiOCĺSMw]sO6/r';t*F;0(5LVbk|߻FLh7Z8? {wʍ7~#F/촄ԱlG Ϣin=GsP%)]\t!#Q7+<{F%EI "w{F$vLL|{Qg&$o9u89>(fuα&#"D8!*lߵ;Ľ._>KfBqxY (F|0.Ve%S61K'p-F;˨GjE?)D=.gt H0,L\O{m 3E>)"u2b86@jyַ LJ pO2A">!ҁܟ8lveuwuZ;|H!$Axԁ̴C )4H/N{ы(8o\H{4l)BeWni<$"B4X1@3~|w(c.2]tޛpf2fiɷI3[!ͥSz A+ Ζҥ.m(v! NeSo8#;7"2#̻-H=F9[t4z}@cw͖BMa\iijL辰)_*/ƀe*E}ZÖTZCͨ1YDvIz[ڮ,SC閘Č%@yEq.|Hgn\4r)/d^Hv\{9 $8\pg<-_Y6| Ն^J[2<6uc݄3~c̃:1\H$Kw&sࠑʮq% W$׬9T~%WoQ=Ur65ߗAX{;jZzJX9m 'LIz&I`9Bi2tv0D:Qbwb#: L&Ma QHo:>AZsCѸsNXkFd0˘FiDN„q]n3XW}n$hj8TYҷ'X{$EuoRi{P. /->`a>TE TI윟YaKGUe9:<[ۢ/P&[T26PZ\B^Dx7I[V6-.p M-TJNCi|C–^Qp0Х2K,4 E_f}lwyaӆ?삎ׂ!g*n]o{ Yyd}HԚbT4E؉nvڈhgq1q*E#=ޒkS!lj1qv?XML{HR"^طqi.p hv"}qxsJFxr!m yzTެ|^Y8X?ڐ!~)[SyA]$U㷡n][f='\s5.8LqD,ۻ($Bڑ BX;Đkюɰ4d;ȥ<4S@;=^%ᣛ'GxX16CkPtgpц7CEqk֖9?Rv4clOɇv>aTV>N{=&DYbrG.V&KmEeoUT?BSoVHm= P|~]@K7Qr h3Qvj6WuzxU(h֛Mxbu9eeIKHdIP8}e:UГޘxnL~8 ww9S>baZy>&ЁJ;iS׵॰\]] X%5赥h> K˃f:?6nG(ψqå]g/nt0~A̧~76 IБj:F eo .c/tAb{/L. V`[[-i_pbP鰅[$XaExxr\lضf%;1*r[&G4 ^3 k,dcSiޡxr!&X *8Ύ~K Ҡvp8IՠOB.Ew/2m56* h/my`4*?r`Eޒه9'yGKwyY~wLs<( gfe ?h<͢Of U֊ YgMUyĆ˵=_Z12B`z!w8!34zkoJ$fM2A?*LWLvG]fp2%!2݃:yᱲh!\;{JgOPqHTp/H(4&zF2A V/ARkl3W/Rom:cs+9.YumD\o*XtV?21q&k T%qa7 Hz[a/ӚPM̤G!e5K%_]f`g0ȱ\ \ bk4"zrVz"+q/(jy؆5ygAMdż < NGۥA|;34|!#8^୊f('.@!R:E??sQAfID"&`t.sa;QԐ)Ɯ\ KT~St u7voXH8v7N,¤~Z3м/+}ľ89D'0Q?2`;zgƪy* nR^0Tۋ}{ ^Di:Ŀ!nro=ۭ^ ~P>mmt DP{RMGBvN4Zb@o`>+Km2h6'n8\yy]5THuiAJF1wceQsrl1N$CX7ww/'*2r;^=BEM)AY >t=IhO=hlȀbD&.dJ!ǭ"63b[9T퓔 uJ;¥z/cl[P⧲yK9v;YĪoK,_z:Gdڦ;Kta8i3z^bKB OvlH!8vw5$Q#,¡[P i8C$p^W wו`B'3hA ᥫUS9'Fr`>O1(q`羾 CJrvQ%yr潚_ҩPC-A!&UUD' 44/u.8@ a&e5 \5xQxcQPZ~娐M7v ;vNZP)MS(S&}E 1YwX(rU5,͎c!B&P &l$cw$:l r)p%y6Ks[!}捵`p6QXޟtW9pܔJ6-<D,֡,hnk@*ah/#Ƌ'1̮sg=AﻨV"ݑ_yzSғ\ ;.~˒HxW` eg?O-x7xq?`ݏrav\n1v ť PGeȄ(V 提؃PƹdcIqlqPmcpkm_1~$`Jb!v k>6L(/f7Y"@;"IV4:70;-SPKф_ qoٽ{5wO34Qwq v)["(rX?4DCD?m#ۧSW=y7EeDH8oLԁYt#LrP[Rl {B_(s=.6SnJ0Kjn~W%eB+ܚQl54F\H=Ώփ^.`/[0)rus 3CM P8tW}[V|Oz1[CڒR7JtKVS)EmH(_GΓOB6C Gr@MKF0\恕ƑrV_7[s;X7w/)c]vӈب0jL.ve@Ec0P4B$d>ه' 9P5:unEFЏKMI.ϫ'o;]X?`g+bn?x:/gJaIDab=#ĸ?W;uz"O.w-l/P*ߛԃfnFLսd0Y } /X6~Dh.´ׅ`[@|eͭlcq=}yF~bsqPVC GZr \+&dD}wB Z5+(NT2$*z!/aPG}щWe!sy)n[hT7]MnʺT~U[Egނn cC9r;fصlaI%x>nGz|]_σA=$N>ZuZZA5t0xVY]AϜJ@VX!ciGKd7=v< ?Y`0EK[Y-w.i&?-f_NGj#tJ2 POL.,<@98Mr9CnC)Dmg}#L[~mUk7L@MQ-BC:Ů~ۑ~  4Mj0jxL1il&) Q.6Kgwkv. UӥC4WF 0 E蕂ޔ Ӣ~AZA󘾌s<g TG8'3Fٵ;  Y3UמۭzFŔ* k~ -VjcjR}>, Âm| rB@kn@fص>`3opFply|A}*`@C n@"S/ 7{^.+3meP;$q\ȹ %D/d}(P,5tLmB0ACpK%1V.@rU9nq8Qr ~̓13o1 //XYDd MfV@Y[[k<ڼ:7|6pݒC[Ls Vmv#Xݘ 's X|!„J٪4jc?* T$ ;z$dskKa<5=6anۊQ (Yޏ{Z%66[ cu7njcח9^4Ha'Y3'CKJi5>!Lg{+ V+Bw *u{'VNsu 4mY(e^X!ta}w>Lzr e*$!|c6rS&$WXԌ'YPx< ͏/>*+*'w;gs&H36FO]éb.QmkE>ɩDOD3C".iز!@8sbKPiسٰ(=a##mp y ̊}ٟ F;}Tnîm{+wNF-;:\ #ofhDpH.~l5L+0B0(O6NCMŗT<z*rN$@mNK,0m$"K-$ep8$I#]a/B6ۓ;ΰgeE'?.cE8C6`YJvoUߦJ3 ·g$䢷BqB:Pʙwjo'(i,En'@CdP*YkM#ԄGQZםn8_ &[!2y[z C+#^r ][!VqC5DBi9x.gQ4 '8dADOst-h:ǹ_ \#JkZ?3sWKEoܲh5UG;XKJv=򰝈>deOw`-ERYVHvŮ]TVy$ODk$lGƎn9LE>FQ/}u+5_ T{b7,ðE3v+'=0X;BNZn d7ܑ0deZPyS}pRhL& ,Msjlwe>8 Z%7h) .5rI+kpeL4Y&GnC~Z7j<lB' U~v 8-͒³5eMgpB8v o5lrː MMN;KIBX)ܵBZ.2a13>GƃYG Z f>=[<*. ~2a\%}n(nZҶ%iѽ(=|Ď3JNԋK7C;L69Nv6Tc5*͋D%!IĸA"|+aNёBc~8LI؆N#7nir_?s:'xvR-"X\_94j )݄Rlk v;ԧHTXriolDqjpD2g*űEFIVY-3apB8 u1!s=$Wf)t׎9_9$qˇp"Z Av![$IFA׮-=nDձWqXXh:I$ήAU)3w-*cIT-PbEV)#2KDp:_ I0meYZŰI&E)b\<8gq^&pHn @\xՐŔY68e)?be~Ic]a,BG #.t/uѬ!'J!*Q>hGj M"ҁ*gmD3s|z͎656ac2kc> #8;*džV[&hDDi˥ QQgw=9e62B1"+ K/ K6:Nt* Sц% B7BekSR9^GUaz@vG͉5Ʊ %J !d Mp(Yh['KMq‚? 6{Ј6 J5 ߭- b5|V;h#ðrJWhxȉ>ѕ;RZjNDqfPHZ6aUAOmlA{YĂxkD%_ǹ%r+Q[LU`V_)< #q,f0^0Hv}Zx0޻OjzN8N1m9ʼnɑ,\LxFW?%*\Db1?jhؙbH4~zR_hudEh*ŧ#3 n9F#*^ƷsVEHɴ ١xY"MP_YE]uce AwGxC~V )/DDeDLŽHgfO 險gqi^ *-غtS|oۭJYYE%Kn KB0%0Nrؽ,Jť/:2J ,4Bu nCK5TY_"f6i^[؛h v9gبߎ[4F֩e>)G^bcӔ˺]?g[4P$[>EGmeWϪڗCqnP_7 xxK\qnV79YeYs Xgw*1нŕs-whnG9X^ߐ>KOt@Hy9UMփzK5j;'d[kd5c{Bˆ+ Hj׺"d[=l7rij rxyZŗ0cw2CWRZ siY&I:%!Vz=b| 5;͵LrEڏuw3C"Yo9CrU1>t}"̙h;YN֕.׺☺ͽE4Jze¨q[כ w/Q7߲@u9, |wi @ug"!g1]6>1bq@a{\F^phR-=oG٣"$eT@ 4NH{/ 32t2 F tAf)Uc!IU7i(cbHicf ÌY4sd-بrCfVYИB;VExlXYwҚ턫0}B 8QazeiSXC.Zy+]4wն,Sυٴ\X[3Of\E\j 헗y1#۳S]EPٍNG־9(NA`˿l/rpQv /B*Dj+7$T$Liٜz[,*L%=v'ypV;"/$Z孏EH`5ɽ>LҰX ;0.8a>؅oB3;K ^`uC_?e'@t1 {1$*y`2|/yŜ0F?>H~7^m>Y9'oI:b@mY@/>@nWzRjBX6HIi=af Gjh=5G{/ez+A(n9Pt'R}yltKL4We1KD8ص+ce ŚqO#/=0YLɶ$xVz̬N䰪rN"8{v6WQxt{oϠM~`9*3X$i5i)AtJ\^ZXjf"VT4iJqǶ[Õ?S4̪i8;/ ΰKKQ r~\ZC$ɫR?_(d0[Q.$@Z*sP:rY"(ن-s 4q00;ёՉݍJ&cg05$G)@в Z谔wQC8I`moVz7Ӥ֥86`r` Ԅ(QZnjq2jۈY>WK76ZwIwIl֡]RjKV'':.3B-A2X[y~9\Uـ::Tʏ!$T_s[W㰷+KqljR*Scn13gDDhנkC7V :b ʈp`\h BԖŴ5˘HS D)!H ]fAl|̒3oQ{!&4+X\e`=;y-'@% gww7i"R[:?w5N z!m`(i&8 ^rkUk%Wjoډ8`=y3O cƦE|<)Ri e* a HJz\n0D^m(@q.oz 0{ڴs  'RU;箓4TG 5/+g8HTb%.c(S;R7Ֆ7,}pi[]^CGEeKyWmnjB!D$7aε v4yg8 8 aK_+_Zd~~B >MD|i[~. e-T "B]`A覬+vl7ۜBd JO)[]DrM-2^cU,C>K.몕|(}GF6_y۱SD9HSsJW Ŭd[,cCS%&o׹W#a6xLMS(o?@ERoӞ)lv#0PD`U}GCީ4ie;wTY > yYh05o g/[DHŔTDV,O龛 R7?.?K}X>{?NA%Hb,G /˩qlsMKrdmBdžJ±ֆaM/&/1q\lk^#ԷRnS V1NĠFٸ}8)bfx((Ȟ[&-ڿ2MƝOO <.$&AEEa\/H-BYP ׫!N*9n=W>cw d=iVܹY:vjv2S͆o_E{ŢmET=Ѱ@0],ċ_9ÂFOѮ*w~mMC I3g&|`kouSԖU +q ,lxϊAi+iyٽ#رsMxwӓ]=l7 "Q9(G+va{7.  wVUJx<`J5e!GFvMT5DC(R~ E4MmA4I@=!%>N$:7|#RGr;<8~eIPyziNy`k5KiSPC5ҭ%G†ϖ, 6Du$X\ `Ձy2񐞞e(E :HzUP'7 1 %E5zX R%w 1<[Գ1T9dsd. :y?}bFhLY86@1DFSDU<$T{fZh֍{1(R=)iʚhӞ YOBŹz>";CZRNhju*5fkt!#QC̚1ߏa %6z'#!$9% Ո9S4ăa6KSDzx)ˤEzLV"ljPvam{GUfƤwgI-j,/ɮߏsue4'iYFtLNmaƟ%֫';qӾ*:-.MNZ*,-Q)[4p!<8]̹APi*&x7Nzچ_H aw$N`J H1*0!WC zk˜@cX, :$ tSn)Y=AdY"I Ca(` ~U;ㆺמ+H;8C* YMT$h;[%K)7A0< U)9mvyKs0GBrd.T5  e;}mGiɥ V@9}!%+ϗjI [|_D&Ԛ^G~T['J +_`BZ4꼟!;YkP툒wQ6c%r ;M xKpw_/} N-1n!= Aq̛70ui@FINM+S%|:&6OOkO D4 RTN 3|_AV_x7F-O4 ʡC= 0-Ck]S7EoBhCT^T+kr7~Rd~"oQ/Od絛l ~t46D V©@8n//#~4rrew1V `ŽפbK\¶? D?0O;\KX [Pe^I?YвWXO魓o+uO}:TFכ(]Q(DQFj{^? MS-ϔaզ0IҀ6eq|\ĘCPWܱd,kTsq?wQAq&_#|8-.Sxr9 B 7= :nd< t@?ԯ-*_tK:w% x@ސd.=#̋#qYfRҘ.sM}'8a**V(]{?sf匲\^8? _/DLm}H؟oŠך?>h&nc ut7Vj|X})8t8Fhi@~{WKf,s:ڵ~繘 1^gxA"͋VW'",v)_.G\ԇWn-"/HN/>Z!6<1 0x- 킗>j Eo!Ǐ!3t4qJ8o/i8i{BwHO4T8ayC[|6N:<:j߷ /^ИU #\0of oAr V͸8|;g!5Dpc`K\ %Yg GũA A#J\y.Khd9)vhW5{ QFVP8ܔ$LKFU{ 07U}Qe?T[?8*: 8b(m+I(~ڣV\,gN:DSE`E#V5gAY0&)nyŝ¡nx& ^_lkss/ѻ w{0I~;!2@,7}BvT"S ]@Q@Zu([Zܸ(YcWv;J8Ln z*I _ B) 'JdgdA:Fy[x:xr?RE01 RWip-?2shk: gҐu‡}E2Ӄ|;Ǟhm9VqЯtKcA}J, ?2ݲeU~|@͂F;8|Є5T_{ߋ~ǘlQ2e Fe\LjmOгHGǔF(Qڍ4xڨ?e(ڤkמ}# an+u&&r@So0AmɊKuK=hG39If )yqfN.f':pjtQ%kwS&oyzzyCg%:|)aSR(KKA3(!B ~Y{|_*/Ve)cg'r҆5RH~߯T J$гlxղC, ,ԫC&xϯD7!P`uiuq!&z!iD5ao,@iE.c($L{sxf+)z|GQ7m{}9GbՆUsL[Hx0Ŵ'Nd 1hQ !|U.V㷤SeSPr7jd!-:&YYpM*zNC >rx>{B j7JnJ_)|>=刌VXft%N(h`l,멵lj օOnU46Ӟ w$ٚk;)?: M™2?2.h%-f,bO؞VظCO>tDWcZ?cϝG Y%k9Ixސy<u{;%dLa]JPWX+S T]2H}ӳMʶ(ז+UPmoS,3XwYD\?GzXP8p]peb;_{S*r@[O0qoXp iCLKEe7Fji$wF1M_2쁃W;ħkT\Ib$3륩&'I!++{Jէ]fʗA'd hš{=:\ ʅaϓ+]v$KGZ]N ~{$"9r/ V1>ӳWS*e zn\׼&R`&/Q#2xNa޹dw h#)>"iبF7-> 5\jTh2F|{H4h`L/ G zS5N/$y$4b][8ln%A~XGwpEm\O"{lf 7PވaI\.:GԞ φvU$E#h M 9g̋Hb&2uĽ{{Z6RرNqU҃ h WWq3'ʓ rߐX'TfFk2CvB쬒`gj~*.ggO r3!*gv_=<}k.iJ;oD=gM'Mo ЙɁcvS}HH9UѼG.BI P]$a mw$u@:)}#hЇ36~-ec8p ݔWg"Fe*Һ@ Q9z,BV#dٿ4ٚ:jVgkQ<;S5 I~g` s}':;>]PRJ3sIc1 9>RJ%՞d /= z_/p8dn uFpwyOϳ^&V5smJl݃6rCk"+0Bh[CFҭ9 EoT"Dɇ0Q=X,P\ȧ>ѤemwdI:Hs[%ј9W;ED1aBw@ 0m"3~/^~ 8F;^43EWp0ϓ^w}erD{d6'$#(`2񠌧[1S Zh4`X mU{f}ތ 2&6˴5BZXaԩd_Dj,,n,G'}>W9a%ie%ޏ~d6!ϭۇ ڨLʩDzn&G5lߝ0SWuXIRv@v4i VQ.Dc37 I9 tQJ#EzX?XP;LOtHrC$ĥ]1 K$b4{7Իau3_KmAJ݀ ۙ~f+Ļ8c 4z>sd^l`?i yv`Ds˵bKX~YXHD1fBXBmCH6}ⰂUޓ6:_W)~v|䨵|AgxMn] :'6 8Hr~@&mysv25f (oLxlpW vC18QhߗRqKH!J3UO0jD+_`?!Tn -1E#D/O=hkdRm_&gh&)^,^Кy&rN؊&ʒGǎxN+!>²_rs91sLh#'gWx[&'JjӨ{g6cnU7;G'RiW䪅g6uTGv ;yԖڼiSfȝK 0qw\DY`'O9#ˉlBf ֶ? (ڧ%> e6Hn=@e@1sW-"l;'$GnӯI/P{\y=]9'DhkŃr=Y!/uw&97/VzCd՚aҝDA-dAfnz?i|]Qܠ ;=4?EemEڷ»7)P#~ǝۨKXYkMҪs.^FfUVɷ+w/'s=[?9BU RHzǔPAkGˬ.Vؓߕ^$jk0ůcI'ܣr~3E#,煨J*O>j|Kl qh!uWf01 )w9De G#bIZl%=L$%sN3]SDf a{YeE_ԝ/U9{G,c>Tm4m9o][dmR߭sp!yF(&kq7yx7a|X9Tu]ٴlfd,[37yíFs6'D9hE./ t4tv~Kc1<.æ>Z#I G9rz`Mg_-L54^z9Hڷ:3:˺dcmR=0ސ~0`".0w:S[,㿴A9?coW:l\/фߛ>Mbpyx-wMS[G sʈrg+P:e=N-BF ӡHz]Z6 86CLV'6,7&l (Hs#LhE kE62ou)Fs a,/~=|,RBxR=؂W|]'ŕP6|Sr8~S$*b#)i?ѿqP:cA3wbTa [e > cxBYdWEnnXWxEafXQ$JgetC#_؀բ%`2<3^9v}6cN8\N\*ty!׹唙 FOXp& uh⅗9>wC%6}DovosfoWeVsMQnM=|Qށ\]'gG|N RޚA`*Po }y 39<KL!36Pʗ4H.}/4uꡜNEk(|wfϤHamMA21F `^5`LߧW%! ԕջ%%P}7(rC/b,R#6#N\ JLn˼xHJͩ1Y(բsSYH!/ E01xt |;!_ ~o I0f^.w\rwKº?E3NDWDZ% +U/ PA\㛠Zx~vlymT+6;0D˖ݴ'3$ͺÕRbktJ%؄aC7bȦ @ȅݍXπB[Z ?g7z[Rk>?3VR8agNA_¯s}+KF Z_ޭTIE< Ng1+63 q؇,bP fX}kavɈ)OMhR6$}jœ䋟;&La:hJ,~;Qh72rFR]nN >lE8Xw. fǽ&u;'@c+eS59Ԇ9uy_cJuEœ_!TS{|,D%̪͍ 1&FgQSFUWp7in N%ŃY1/g:͌*)l ˗\Ro>!QĤyS9,aXaNR3PoCp䡙Y^Ir cGoT%XeTV/+lX$u?"#Z,~/ 8 P:9%PDy7گ^24CGAAj/d.GJRV@~ҙMJqSUϝ*!T T4V 0f~!U}Bw`E%Sb0Ҳ"Ccw4jLE\U=,L'`>EȅJe u֔.\UJ@'&My?-WY. KN*m&J+'ţAmjqyF&pl]Qtl(DMDNӅiK_1p;滐n8rӰ 1K,eA7ܷx)S[bΒq-|:B5ʌqɴΰ 4.Դ}ReJG998r:<7eU]-l .}Q؃GqQWG*N':8VLչ4Q;)nج:MCut:)~!,|3ݝH[!-D[nc0ps?T7HȜPdU9)co PA&z$=1z C-:s|>W*uS:&;v1L`Hn(ȆYlRg`V(WuꏭBQZCk=o%zؽQ>,G&[$ NI]*#w2o*l:U;YAsX,)Q,k @Cn AvF21M~W.2 EKIGP/}۾.)Fn1h2 [&e`v%*ub˒6m,GAvnZZyw9wFst'ռ\%g~ Rٿ>ſ1P9zH$`UoSGL5-7v[S `rQWAs0-1MrK@֙ä9LРb5xG}?R}1K/0c-!z ܭ8X6X*HUUMNɆ#MH%c/ݬ,ϐ^đ$f)1 れ@Ё/C~)&i \ Ӏt 8g6 m+/ZIQO|^\z?2ߩs/mrd|`XORl"_$ 3kI)f:. 8Cr(lP #9#XGLP2stj0Emu? GA-z dpqz)"9oXIN={ّlu,sWUA10.*u~㚁Կw &@8A2J̳A9E.CmXPkef2HSjƌYH c]\ f%jOmǙ Q?`#Фn?dN7)<.DZ[ˋ8RM@ѓ,^HB)9h$k`@{u!.O6r|tz FS<^FP(*:1S3 į]p}v'5*4oP_1krrZhUlI V[a7\ьR|~>|fVZ7y~ }|1~#zn݄5Ky%j᧐"&iҋ=wF0@9 h!>uiRdi)7eBZ慊 okH2-ZE~j[*J];p4Nt! 0l2u>fcv˿W$O]gK[}(&SOvB+jVq[[]"$;)i6JaM1BSVT̋,7w-3m>,@J4| W(5xy ><a sqj{+E:^5  I}djڅ$.緟SitȴU͑Ø6q/J$t0+7CAN pNt`}%WXMcgq~J=rdRx4_$ xcQm?)MeV%4z&qlSΗ՘-ԽQڰ  {7& E<`mF4Z{Z+#it5, 41I@%i4DR3A9xujw]*NfXJU2]obܧ9Dieª2H`R\۴o݆5ĬZ^{=,D:< ȝH"V<4kJ$RM,k|U:˟A mbU-& ]*u[7haԖK㜌j1 `v@ė6y8dT<}:bKl*ueIG!cY5tVI;< qoUP(uo/!BW52w8dYrhvh,"'6< i:'e# C|=y,qT8~!udcP.7^Q aW Vh# cj9Y"Q>-Xm!p"qyYQOQZЙEW* "`^FYF R|LL<2HH( /S^3zSJ.5vF;gaU$m(? p$@d!dkQrsS OݽΘ p~dSxIfNuj-/͸5k_(a%>f#=! 2=CjQ<Yax 3I/٨R9\jg{ UPapt}ME6su=ԖC 6wW]D=$)ѓ$_Qu nJċ1ppC̵O緩eVt_`/Е+1i+2dە9 zz]Bn^e%]U1M_k.DǞF)>^{_95"SK8&)VƳf ROڄYckc2$inq&M9 4s<>HngY kѿ}ϭtYh.#wi. oȽ 4k͏:鐾TMEsmU2Kaj " *퀔ZM f{d$:i_oQx;pHnt0$a,ܬ̆Aw9+n{LҪӵБjae(),"Ht#udɄ #=QhOYohR@udjx+]̒?Ϧ,{*',L)&ג\pulϝJ=cc\wcv[d20]yvQ|ڞ (ϣtJ@I)˕yq x?-nʁA`.#XR$k7CG{3TܖIN/11%"NܝrzX>{M݉[2f'1ofF.aYpDݜ5J~ (@O1bpwֹj\$!cكw6|n~)S.l=N_lxQ"٭LF~\D2]P f^q|CaTeg,b.t[e–J\1JD˧Hu1ժ&/K.ޱ:-<0AqjzYFOj7 :Pוhv8̜W"cPS1u ֐˰kNd@ͱ[빨9y_7n̍>֐Acg+͔@b\XkT^kU3:5.O #hqrt, zm1tH4I-2ɧӂW.aԍmɕg0N)wp9(FS\ }~ұJ f)}:*˅3d[6Fc*z§UZ-{5wA./:k^>/zڧcwSI.I>S4c3jZMvXv2s-?+lA#]X=2`3!kPG0= UBrz0'ɓdc{ [0ﳀ t[QN;#RaeirEBn7ȰդM'JM|?V-E=c'-N%ֳtsPHs"Le9IZ2 ݎ`!Ÿ]lg9T]w7cFs_ڽ@̖Y~ɦ]di., qһoS\':OkM4;$ll8mK}4ɘDe,[mڭ8>g@A91Zt'K`m7ě6r-{&H3M@@̔0#QA#+ [gw",g-`Sb@JwHH.!D\:W ʹqhPE)oo1) }7E٥[W^ Z-H0!|S3:&e9O~ :A\jRKg gNv|K @G*4iÊ2j}FCKScİ~~,ozg[v|!Թf ERcDvJ!R7rq`) &AʳHuZE;S`U.-,&Xo;F)t9OΩ.!3pLJ2m<""~@R= 77/=Gz=GjʃMw%^rykK %\9ap,;&I:ToǦH̱1'f4P}H?kNHmE93,p* FU s)J1QQlnԛ"{- 2M@k%\}WJ}3ETKtsNxgLd^RhM_5v1C\ا[cGck2q&Q%(S6h9"_~w$hfw.y(Tr:#Epw;\{j  zjO5 ֐5T}v8q 24Q8UcR z訒L/ČK B935g1_|x Ŋ(?pC6F/v Y$FikbkK;RY7?^9#,#:eKW$XR2>v"vKT"|}e8=+EٜK$2:W9 DžҴ|I]^ySyxKXx3XektY"y487Qڶ]8 t.zЛ.}Yg]Ô~Bu B}A/+rux@*Py9eY[1M@o!ҭ?U:g[p:\vv#]ϧ2c9Ӷ7xσ07;Oo7a4qcLJF'C+Ai=Fg+?n)b Xmjumvn6e/.cvSc eK $e_r ,ķE-e 0!LLn>hmOe/ $ޞ=ӄd%)~/7%K? 9Tcă<'mz\ݖ+~7Wk)R>ԝOZ.E|ڱA99k'rzaSdqF~/Nӧ" /#(ڷqJB(y7J{)8xpd⩁*oV|^"#(|]tCxQ~ۂNX9c@DžrMC!zg'E}fgY֌r7f G#a@+憞;]M谥<ݑ N$EZ/}A-}+)/a2u~*`j--Vr=$o![0Yx[3n2R )U{ D|xNҁ&8 ԅBC^~cM}q0ո Y4;}'Q \+Lݰ̻̠Vz / Z+f2Ig36~ׄ1`KdB5Aph}S!k%zZ=Jx OY:)=z3Y/ӊ0ziE&1+O#jE0lQ|o . gMMj۟O*ݿ*óDEJưrw?&~I#<{󧜌k3SLV4gd 6N(.M,.|sI"c$ߝl:H >U7s T0dtsK$$h;ui";k qO$)9Vu3v{l^"^XR %4 Mk>AFNx9CCƿm^pq>Y+Id>Z*)ȞGr:n|yeF@n&_i<)Q15j&c>m(Ok$%u6Im+kEF4)^tǭ"!rEu8W^pAel 0eNZ j3cD")SJ(@VbXFFt2a$]syE EpO,H߃2YYS &9b5jYU yΨDm,k#Ʃrե*[u}ds3mh\K*l=6%:%Tx@|qI8b8b.:\:=&m&öqPēAR>1̿xbou2>ZZgV,:L 097\K܍b@w$j4d%0N7M  #SVˤ'.ygF5GG+WoMNEWS*8U>b7 \ 7Qb;䭨ڒLp' UucNpOb{B6.}gOCJ}.LDRy3 IvSDj9~s74÷TSؔ\T |[zZF'5@Q VO/ՓDufhrv41{jZ-*M?" &O6ќkj]3wnϓ#hT;#%PWMqTz ʼAHYFY`:ˁHg{ӱ˭C17 յQ;.Y)|9$ +Ko\GܟTBh fvoP ?y؁<f*F$E$|{+۴8T~/ӀdgemvY1CE>Sб7N:-o{1ù'i0$2|_ ®\ƏU!w6Պ|ߴUFX냝"aby yŠ_C=}o6UU}mbnbi̚ $+:1yޞxJΪJ2vCg.Olܧ|?+oE$O;\nX4##E#- QxT]!s2П?n:XXvvxXLInTCk31Sk`Vt.1,G|| c#q]02+}yWvL#&y HsWW$o[H'@&2Gf 6CYp&x/KM, բ "ZW# FĶZy^G]ɲvQg>”x|ޝJ"gK'ݓkɳ6|g^g&sr0mɊG-пDXVxut&Qp(#I'驌8SS$3?Oc^ 28XNk(~~GmL״i\ʍ_5@$$޽G]ޭS#2̊?0m0hBQ7kLr`2vBp]`uyf:$owdNE5sʹ~8scy:[G-BL)Ź5_jD,^V-N*^u}$1U[*1Xsn#RI:yAO4^M gg;؎#fS3FOqTt9_C7L,s!Bg ϔtD6 |^2* 1ՠ7="6 [sPE?̒nzVwhT4TX7,ʊK/au ~q(2h?rݢ+C?IwӇB}|66z`z ِcZ;@xUw<.RiBA8e|%t>/VɁ$_RLx}HAJ6/:[G7|聯WE ¯D8̳̃ Iz[HXT~1cJqD[KN`6S#8n% >lB`N>e9,LSV %8Z9c= w gP~+jTKi6}`N,MÁmaaJɀbRyR{M=+ @/ZYxv8崮3*v7 V' (EʥΧl4 p]eI!!0S鉖kHyo54>8vgm55 \yo5E44$}_-tl.ڴjn 9O4d|aţ^N3QB)/#b:n7Xbw IU:b#ri'HCn$c)Pcb;=pyDFVه/?GJ}&z&8+B4ZNzM9UMOz02t_<\PLj !~7?QA2#[ɶ}__mXKO8X3YKR>Aٷ/ DԘGVYJo#ƺ Ֆ(<IXfḇ *HgԡzlPi,bpHZEW%Mtb5cJbQc/e)t9C]6s7/2] }3-ZNqP~Φt?JoBFK3$K9\O@3vl7׏a]'w,rᙤ,)W1]6DoC?@o ݸqzҞQvERSE-[*pđf zh4EnuM A{К}eE 4.%kkjK{BcOrrXԭ=^\b7 &f _âko*+W ?$o'^ݝ--(`m>2jVqjߣgq <4"EIEl}U0ث,UE%'{S A= 1@CV“npt?>S%+5M9=:>Eɝ`9ܳےI-;/MNhs~wj=,f.gCg&ߗ {({0R{ \meX0|7.%*DȮUYNu#n3R\a1.Q!l1!J5[94n:v0ݔA`-91X@=@0m){ۨP80PW XQ'OzzytCih"wB.h4&6 oYѐD¥J͘Z-I G {ѣӀVUX$4M7Ik0…#7_&ݡ8#&_6ɋ},Т@)KfPwc_S]CddXqY$R>8XDNVVY}7aL]&)t? ByARI4 L:Nx?0ٓ+Y &;=n P uD"} rW:U#=2ϛ@tY)\g 8_ GO;A\4 /q@>?s.1$ey!^͹p_"}+ <~8W"y5+k`#aGu+܇Cɨ (@q?8l,e?帾!ؐnF7-$&0%bwNkFTai7B';pj||ͻTwM=S*Me5̰w^7mY3 $8'lyf,%}k>:ظuš/@aE=) dkqFYA)謬tPs3a%SG lF}H{Q/0!L`̀z(D\~7Gr Wa=sa%Չ=/U}< ,)O%f$ZRxm)V },-yyRI}CavbzVpm }ikDQ5T O\-R(\Z^˩ɝbXUjƲ>&]MJˏ#d%?+O@E A7#1@z!Zt>rSId-\'zRPHbL[P!🺵P0p;.7b~6vA /}jQ f8QaXVPE m g6$!a06rKiLO2I]޽j VгMeN,8L;Yt Iqy-B##lKyu 1|:R+{d&WQqpY7#tq`Uq@"Lf4~#}G]Մ2̕1A u .+0T nZL|ʴ+4ӯc[!i*`6^Yu=01~z0AE"ӯlo+OhS}HǼA[#Ɨ1tk4${/`MhջxcP!?3])r@/Xp*B)%}#v#Yu1LY&[!@)u8T٨`T>~_u28Fdn 'l@s621m.}') ud ߛ>Q[DxRXMLDmIh7'u]"K9S Nc٥VMI}iq)b*k[ t7!dR"S^f+,iRkRSDŞh{\ ;);Rg"r-n.. Xi5ghq =#dPqS+rNUZӼ~z U-qk 뉲P'fY$J(8zn̙ss {AFRʏ8)Ov| 0"`\{5 ~9Q|յ~:k\?!`J:GJF"sX9|5%X0VTz+#^PpmH=_O6A{@7ttX q{p1x0TlJ*Y0ŊYI(BF4¸+/3G1qq]9op^o\ BD[$g\ -w^ @yC;q~ھmCQ/gQ>қQ~*Ⱦ&d=dH_0SyŪ0 <14ϜdMfF M|9쐶ԅpPpL5\H [aè8k!pa_欼YWNUeUVȹAL}DS/R>xG^ݻula/bo^ІЀW^cG㎧;I{1*N/ɑue`s!{ZkL&TQuub5ymH)!B@%/ZH'=kjW? QghSoBW~͐c?w3+yeq ,(@Ubrǔ LLMcgם ^, JHܱC !G ڃ=5ޢ4@*'`* a} wB/rG!lDAM\n ̈́gXkz:"^qjt.)oC6|G%N,4HuCGw"XM#>@Im6~ݒZ}hdM"j# 1^bA.HV%*ӮDn$i¬9a PBJzmR[(ѭa%=S #hVY?Cd֘Ϯլwŧ,LO]׬gŅ.@d<8TDv衹Y^pw<4Ybwnb1ƎiPM]L%ȓ6$yQráupIO"2f7tH,m- Fuz4C~#Ģ]wqƏ1+<VgJdifoeE^gl? ЇnRdV'Dί Sk ڼa]`UQ{\`˯a-T+tS|Iؼ*p{{FQN[/a9BIWL|юi.x3(Aاdő~-G X"e2mh_BF!@ՇUI?h3jHX$0!+%xC^:= 7ji}Y,=ZM?ٻ|nZ}쏵z@wdm]Я[TJ#aT- q;s <~+RlfS)3m<{0}LP)=A9Yn2b6&ު0 n\vwǐQe \*řs4ŀl5P{_ |]I.Vm~)tX^U@=( ) "E8AK"zOҨzlk FR5B M)JSbAh$з԰zױJ3!G&uo%&wS02>)k.{AǓ%V3Je7rVCל+{:U\)܃  @ ]|wb|z ~|ζrp4=>ž80Pyxzk8Rb}43nGD /~cu" 46<wa8äo6w@֮=&܍A"w7v1Fj$9Y= Ok w+¬؅Xs9ɀRՌi9yMv7&;5vva|<(3dÞlTUڼ7¸0&U"2TOx1c?(^['kv*= L,!#WE KOٖeRlbWv$AvPq@*,ݤ:IpfjoM \'Bz=_^mTO~pKiB<=|_9NDi>kxw>8C'fN=I+GNRVDFTu,0PT"_p;27 *Z&r 9$p+SH(j9m Vv?s+I.g)0yldI.?!]?+/+6Sf׽~>7Οh,k%c5ZvC r z;Jm|)pxK[ 3-r>$Yոp@’ 8?՛eAdYȷmߊ3=[x!XxIVMC& {JDgJ ƄA|` c_t x];]>F7XY6¾FicK< ϵI/ tZw(8>+DFΆ$nۘ~~ulb_s ؏|[0QJ:9͞X0rXZd/H7xk!hA|֑u5Eck baܓ1SB>UX[ ӿ"؈K'59u{YFyJP.dZ.Xp[EL8Rdb0;;Ukff)YBqx˸ nqg#c~^{$f]gael2sR'vu1N+]'&T'ݝCZN~%,62еnöK݂HW5T`?byx&׶m:r j eg@NzEa{?/8,ۊ S~ۻK~ti43-sz$q%X#KƧfi|ZgGo , LOZ9KNiLT/o3vOIgj?rw>-HG?4&>1=ϠӼF6S`Qf!mtqx6\NHml;gSbR;^dKL^KB _77)l `#B`S.&jY#k%d7+>z!9SmQA'CM-)Ӈ"ˌ$J5E0ݻ>CJ݁$XmpwN9É.FWz)uWC!8+O ܬ JG @ ^#td$w?o02Mj>b9^VΧ㻕tB]Տt͒:$EDgE+EQٍpgWUV/p<3DO]aS0s>BSH$!i)迾k[xŢLfstJ»9 @W4yjr}] F5m*!0, ~2LLѕ9Y :>4aHLqE4@VP7 Cps/% nB5afs iV/e @Z 7cz H(J{g܄UNL@lhF}w,ʆnj 0j>ӫu#( ٜ;n^A%id;O _udDR^ZƂ,J*vZސw̓&qPEH͛V 1=řyTT/|C@r{xV\ah*͇̏Y|kyc+s&TṕiMjmuvl~6j\cٌ%V<5yܫ$GY٠)˗/oCx3dklk"P!RFS} $_ U+'/|YMdsnr(\9ch'~22)U**EDC]vSʝI +hiCb78yBr;N0zCf?2P1tZPXGdA/*g)qIZW* fՅ1e銊vWh¦ehkӋtj4*/{0svbԡISUf1Nܴ*qKƶFTo8RT5W |Ro+ 0ۉ缭h,I + j`NAI7\|޶p j4- -UiIO`-eLZn'[DH٘aMlL`lm^N( w1:=ys(Ⱥf:;-#A[N^f nauJRy yQ2hQ0=)@z&KTP͡zJ?*n\E^CĄYIɅ sO+v9JPxe}e+.ؽb\Z]cʆZ,JZ~7qj? 2,NN3x@A8+"ܪQڮ36 _6zhA?uhQЬe dfkDǃSޏ2S\ȻMo"_&Su ͱwD ѿ`.' _ͫ3SY'Fm3(?/nZz GiȂ[0T|hdn|@NhN DYQv(,y͏z1#g=a+Ǯt_c~L{JE!#EE@9]$C/>Pl=92[_5̕F(&BDv>;PM_;0zy 80|Z=t뀯8l-|lf)?ȼRU;~ּedFo7i`v53+wKl?8pK1jh@f쵑OSJ:0ݚOZnehEa{wwMUu8IX+΍W¯t4=5\f}k$G7-{nkjbY¥ ~Z;aU>hGe4;XL7l|>&~{2(38E)qz*rP.4ZP pu7"_oĔd3m8e#]y 5*7!XTR'RdMq: D\Ql}O ]^eŊCgu!˱Ԑ- tD8Y>V/.sSUI>؃qz!Q $3 fP{[и'/]6EY{s]љI8WdwX='=E$ȱϽG1q2(` R E썵M7ͳ?H} RL I.ƶ;?3T\۔F?¼LF|S_:Mx/.Knʊzgi (W8uI'Ml8e/\e 1 5@̴dئO6)*rܭ[ G0WyITLNXg7{R_#F]z{ys./s]sNbY?fVÔّaEE n\!BTbӒ4fΩT^PMrBQ$ZX!@#/:7@P*-{C( EmPfHt4R&l HpBT@?ζ.G8 2lG%>BlS a)l4YDԿ&(hݲBa(/A Z"". ׸]֚!$%b8韓9'{x=0Ӳ`C\ I)'",-P3(XӞ xcŁ7]֫hA^KXκ\~`>AfmqHjrd[}RwڼI??<2#-z 'K"2s,Sc^u W1yIS;x{τ]Wu7i6S @FUP"Ek]&jC~ł1b0{lM瘧̀z[y~rKkZ].mM+|;߻uYq'_9.%|?@8s\Y8yVܐ gVF|@4u}Y?% k~ڊ9!&O;t߹\]GUaFz(1/p_ǃm9NGO Z1@70k6ٿ`r7‹N5s;]]걖4l7ځ7>K6bQ^pյ vC"Hg-/ H6K.܏T3ɽh4IgeE\Z?nzL0\W^*<1_?,msGj;aNBMBVYs Xg:.p8 /rZыKbZ |OQ=DvX1b V7Er?$b _".^b;5ҦXSr{ 2fN _#$jy^s^~e{ I.> z MMjbj LsKri s1Aac[EnP[ {dùX,PRl&YAYz J^fl ^VmflY`r ֈ^em% =MRǝ۽G TtւHr}:ur]}' Ts mgH bloCb٧=%2ÜX]31:d,ğ/Fl0IGwmP&wсVhlp,hO{g #2qVk8O!}V{1J4.QV,TV3&k~Vk|i9~nv.x:\v=4t~5llp~x'k&:][9?=\Nd8zfxoB),HdL븆R= AHqϘh8vl2̶r^1]o~NeпրrZdϩL̖A oV"se? aOO;.b=B$@fb.O.y)0z9z/{|tW~n]?DYdanLo1[yסy?(~ĩ :qBovp;s9Oc_J n:m_Wnj9DBB'r=jo3NP|pNӬg72E`-*c15'GWJպa7}zV5([Eb2'd\ BsK>2۰HJBYQXeFswp}H i?O#<79?4R5Z޸3AxEx96^zS-ST9c[.ft[l88@e \H7<:9!)+dX-WU'ͯO9dR0]^T}nQM!@i{7[ۣrOƖ&]M QH_ɠd8 OJob}Wn`ܬ'h@ZrkNy c.yp jD'#y2]iש(q&} zv4y9 ʕa1 P)ZDxRT\ m Ħ%Ũ!бղ`Ae#oZYї3Y5pg"KByu0BIˉNCC`#'lC~6Mm?Nuٴn{E tÑ 3;(65x)bQ#ݫ@YȤ=ސ5_.3]4 (G"1-r!G5%N CS5Ao{Uy!X=b~cbd֨;Pp=@3"wK@Av!:`'iO~!5ZYA Ud#1F٭~ %ldp]B`V5 'z4!Fo+dhԹV6YPe&( EH!`,O񔍚 cDmqG HX< /6rG=Y;)lӕ4:'T_D])t8^JkJdḿ5ʃEw(d[x\ 'LCY:wYN- h]Nia/#u)=iHT}&w26ml!B \t|72i7#xF9('IN␠gi#.sf1^äi{2D}gIUH[ 6=a7^J?jdxU'esoHXb)2ca%O7[,sdVI5_}S' O0 >Z߳u.(iKض>AAkntB}a?kl9㷸045w :]A|ArDC.9 |»xL_^hTs~sGLR?(+yr9(iC&A]T7{Μb {5 hyv֨y$kyO_9(!P:Nzxp4 V#Vfxl0?_=AKHxG@_h43dvZ:tyu7Ay}{`$̴ȓ$ Z{ZM+煯1%{1C%3tWJ$E8٤AҌºmn8nʈ=pH.yt`՛ţkȇܨ,td9⚅ #B|4#ȢE<)H<[%^OYSRؠyqg$-N9UTj<{tWӫ,l־V{h9""F |t=Y) n?dsXnR[F3*H2 Y;w(>h9sU}6}~(")ʄ=.ɮ_42l>݆QVgï1$%z";^Ҁ8ד# Sgug=2B>g ykIlN&^4`:j)AEa{7cϑI.| A(>FZ1qń'J_ [*nyCe}=K0vkes"*7:V!O>ߗȺ۾6>0v@T]dZGKQ[ypꞘY1em 2]FX7wmWBė2z'ւ8jbdI4V3b~#kDZm RÙTię(SإԛjgWb$|ydY@!j&Kݾ\Jܬdc$aj'it~Щ8AXq^1h䣈X2+]1T= ^qlqd6 y$V$h;I-f\bV! 0bl*5vV׬A5Բ3%ŕ}{2AJTˈdpqEBQn[, (t4,\v8OK1L>"|^8+ Mc>A!sX$h8GnVq[Ǯx(6pAT}-Ϡ\ .B'# Nf?pB({kIwBҞ]7;sx&ɪl?uRN0* &ڝ>q2nznd4UmPR3Y׵$yu2طלzfSfMr~DO8/IFGu .λW6H?ܤ3nbl)o|ǴNBmIIxbߗ?}&\uSg30 X{8 ws-N|QDs3J.=%X2lYVp|UX=7;3 U2fc\ғƖ4U-1٤ Tl&_ } Y<Os_ ^1*E;3IG!۹KKz4xZFOŹ~| ҵJ]='J ?: ῿{vQ[zE\0mlh|ɀ#vě^Ń*6Qxqn[ 6S`oXmT63(UƦn8kjHl݉ȵOMPͦܲS$ I 3T 1A40:l'lޣz vk+(MaL=t[0=e' ߘ-*&\q}qp*tcw?V݆1ꯜc_ Ѳ~S /4%/+¯]OBt},6FPi[VNo \#ܸ>1Ͽ[Яs&Iͽyo,圮.Su5i^J @?bR`3*/?2k %Y@d*?m@?@a- $A{QJѽ~'cZ9)BUi'ՅaKa O617;~XG~nd*{|9TŌ0 =*4k֔k0V jǡ(X 1Cr/k@2i(jeo'0.k=$GN%8Z. Aj ]c/E\ ޘ*5m ~jColR 01,|R[BVQq5`4dgrD \%6y t볶e4<~{ [-{T eZrn>^r_tfieQղ.,X ڔA'b6:G6"16N ґ =ۨ^h :(\,u+ЙbӻXu%&T7Wii[A:JSIDHhHTŨl@,ԌEx37z׹K:N%NUyCN(4{y~WE""an'$[Og6'p ǘGYb95JUg]LfvH,"( Hz 80w MqZH!ʫ٘.Qo hweWd" 6[n^zy:켂Hjj݁Zx uq@ Y9UG >&t1:*܍{Z@7@2$0t^V:m?N8#Iu D> }B 82<-YX1Cnx\18#$&j6r .2o2G##S>'+.%mj> c"Kf;CAѩ]@RVEw`?/ ݒVN˞ڻ^:4P ͌^(H6r=<X5A8h9dⲇOSoK3ȷ/?sC?G\r֙Z[5XGnѱH_~`pR +MIQb@ >CpwA8 e4"5_q;QQ/ @8n:۰ÿ+rJ 3T=Ch]R_ RZM:ّjUڽBk |!"M2{řIK0FKӨe0U;HsάgsR1"OlNĠ' b{N#3VLCʅՆ cE.>୼xp #JkGFB XtlŁ|޷J&4eaЀrS+;?~,}Cz{2^yd?D 'JpT)pɏ/^(]˭<~Zi`mf5gaֲ>LDؓ$ƪ67=|TeM9ap*\jm) JE D1ypkǔ_Xq/åpi{zH`3 W l1OQԕ3_L  7ƒpqF^G] 3Ǔ;u\}'h{UB\vUH<*=aBcoj7(܂裨cNpbHZ%m$Ly5v-so(y^s7V~}dݱ)|@VfMy3k#y  o:eDj>>{RF˒N9 $y\_6a#EmoaW0ʯM(I?5Pzݳ,bE{Jq#:&D{^ցh={8.fL54n*гQ(dRoW鉯4Y!FaٯBr$*Pׁ{݀3`?.> M$R8B.їwBDY_W` xՊ6LD~:^?̍+L? z<Ϫwq)|(Rɗ-y *J9o=ߜܘ9#, :$c%ܑh]HюFz'Ur("жゎČyf"9.V:{=˛ט07h ȾPu ^Dw+{" @^}YvxAyc qс cW8jH5{CjSg ) 郹NZ̳/%}?sk;zm]C8 Gbt^o% zHd}0v8uuT` D'L+"G91P7צ ~ׂL["5!*$JZF|_CR2x܆>LpkDf' T"U l$m {u#یSfMaď QЬ'0R!>tSb ,z$sD7p]Wr#i3]E Z@{6LfSrF5BBJ0G?R'ҿօQw{UeP7۾|0-aG{L,0*Le]X *_!|k M!`簛"5IW0A)~HawRWi Az|7Dfѓ39b ~`%@2r$!`7g{0\Q^Zz{{/rfsD˸M:-$vq/3-欌QtL3Y c\/Uz6=wWDH8K^Hi|'A34Uu7nu$O=pDR V69] qD~}gʅk?y!re8gxp!`Jz2XFjpgWH!q"KЕekd+ d[yH iĨIDFQZ 0mIguy6+zU$ho)\D+qV%!cC9v#\F4Iy`{uY{Lƻ` CSnuŸJiC 7$H݇Iiy'C/VЉ9x%›Ae^(%Jƪ|}w)ǖ9HN|Oђ=(w BYt!F祀Gݻ/Yv$X{ fm<;ϿM(6ۍ¤' 䵼&Kxʭ.EM 4 aԝ+{ =7 xv|VsH2akpךFԛk榝k@ Sv}ڒ0F@j&wZzow x&1 I9=o҅Ln3.FO`Z:~é{Tzvӕ/6fOA<=R~bG [6SМکa‡%o\ W'A!^߉zP35[7~}Ax7 \bHch #w:wjr1]l_0#w [4iJգ|M kjhxwL g2-…c#e"8}WbCM3>S,we@ck<k&'h57tdNmjdoW}HpLj7btf-rK4!nfR (.*U5[Ӌcp-ӊ12/{5G&E;[HrDžI B~Gt pPlEp:و*NNy$TGqf33B4iYr=։mO=kEw" (,ChaڞIzMjosxqƖ#Qh@n\aㆭzj73&Sqv  6;NQ9`sZ |@7ђ5$90X_d`vӧZ'W7~~W#{=Q >iq9jg rϼE^ibX`.KVYyd* Wi/~p4bٻ[͸!  tw̕nr-K y&YpQ\N/u)GnDW\a*2܀v?)yyN|⤂ ($w-WXl(0E_S^L&t93=6*-Eo&rW\΂(v.:WHvn.UN_%x+vIEs)_Cu)ڠC忄;Js o0X8ǹ|Ѧf+ $M{+ ;:|t9U #_M"&U)Ǝ^W9[~z~+.GЦb;:F=+$C+_':b:2> /TB@{VKbաz2u;0|A(FH/]l~ :< ӠT%XAw~s¶),T $[ *"0r1 0h8pf?)lgY6J*Xu^ewn(2'9q/QuPg72eH4 G(^WRY+6L~ޖW[¬1vmg>n s$ôXd?iNY2! ab44:o+Yb9CnF~/HR,7h;)[Eǜ!Cx)_+&>[vOo!qAJUk~O'7x6GJJשS#{sz+O;uRlpkV M!Nșyum /ZKp* < pVߚ>.TT:&Q P2qk>vu/MBX@dLsA4{Z*wn KUi7 +q7W&mÝ;6Gڍ_P(G)5iB"kG͕@H}o;]lYs$N6X>פ'߄Pp^#p%kZd58Ou$ABqʕ~ ˹#A= vؠe??UDҕP#9a;_$ΤdT-N8 ;?%%R:|_Dؕ]e.n1H~\鼤z1ѵOhUtSM:[vxFNo(_nЅK)&Ȉ:1.: 286͊ 5:LS yXghlu^Y *mNv$3j+-!jȢѻ(YY Xzo _c}patBSg:[Qd1bĄ²xLçw(;} !.!C ^?Nz%: `iހm3P=s>ƉPvm̬vs sH^oI Ǖ"~`%/\YI&8˛NgrRl+1ֶ:;,NKY~dws&FB`r<S=K#R0gn łkx4lܐxQwlI$ਣՀ vC#ƨ~eRP~~[]mFOqDQTz@4FjլҪR$l@^ӵ>,rQ|V.'n&<ۚȁX૿j ETPk@#Iڝkz6H kF 䍳TƤh.j<#tt*k }m=UPsZV>$ZްzEFv'EbٮW+],}o*h^\A^rsD9{Lk3h~D;|X*af"LbhЇp<+ߓwyofEiP2 -S,s:q\ǜ`oy0xi_#ӭp-_06BA-ѽ]@_*Kԯdv f9,%Z6h2ᔀ V_E$iPqt̃HOV 0w0mv 'R[s`q} CL9GiZ ^?K"9>jޅyK-cZ;.Dlz9(ɜ_Aђ j0zB4yO Vkc&nœb,bER,l:N5#gD\RLI?2wpG]Iz ~@r6R.DRdsDgAxyH_ǸDƹkΑ22vlUn4Z5 nQd;j^ {FP5L\@S/W`ޏq,|K K.oU}vXP$XmP.9}HYH_dB<\o}^T; "^ЙB Bt+cԖqE7s5cv+7 3קċ1gscv=GdT:^c!e@.n!IwlOWgyĨi5/✹E$BS|mP`o4Baf5f)0k"lzPM,́Hr7:0\-v 2 SÖ?uIn4kV&ϝ WmT{ ЙdwZm3h[4wi6:ٍ_6=x)fBez޶@# S PMDhW LAh#,tw_" yH7CZU !T\O\U|2eСãLc{Ld}w$ cUC۷ayr$KcoE$#pt[c] :*?gU͍t?p\2_)\)^3Q&&K:Q!]2V}nZ4*u; g^@K_89) ]Րzm &w |[=$|pnFF; { ?~ڛal7>e,O*6Odžm3@I;{MZ巯$?++rH@W"9sM-9;tjtib=Tƴ&ViE%#QE]/ k~ TS2Tw, T:R ߑI~: ꧖JzY_%!J8 -P-[e,cr $Sh`ILV\`mzkW~ %^r/dz4{].ȔlUL)8WO< @!N&ԘF`3.o!u $&Q">Sm8[7GZLPKp$'..Te'ek eJ~) n\QH,ue5R\CampmruoMv K[Kdҷ Fƃj8X#ȥ-KaoO1d2;Sgɭ07R4qWҮD3^X濇x(sx5h[R@5{qRM9v :ۜ=2[GrlP9syx:+&®(:02ONEQK3 HVPmK!:M okQܖR4=8?2N9+aҩ̐F&/eWkBO@ejcL#@/Os\ `@yʰwE1aCS7.tۈ{ɀQcfU RoHlH|L^ .hN'߄m1گgqO[m@a\Š!QJ}|]_R}.O )NbJ-whlq9a;56hQ!~ۏ3@u˖)>Tb\tPչs `Y9g*aT W&R+*MZ7dn@ӡN'qxXyD 6sS37c6 Ϩ"Qه>b P;,g飪ŚNpPOi4u\>f a- k`hQ_۶t~ 0KG8uH</Q!^CTOGU3BP872c+.ƤF!@፜ʲV1˶#+0ֹWu}RrQ•8ûޗ.4ݫ@jq?=apmSk)5 ! .{~#&;t\;˶~deG:'H_*t?#:R^Ift;%pX'[g{ʽ}hDJbKu%|M% @SL6{Zu߾HD7~F'߅$|.7O_xøο`5U\$ "K=)RT-vA3.c pn^G'wCTd`TCg`i-[5*@+} գ><`0eɏ#DG=Xa]Z k65Pya+;WKP"f'ʸ4ԅo |`>ꤧcaEepA1p6gKIqH &%NG:PV&%?#JeYHvSZe* 3=HK>u"]*)F@_\2ה@cPZML/m'CӶL*HT^V%,Q)ܵ*٫J/5 /;Cc׼bTn /_>mxvZ`!q$qTBJz6q %|[=Vdav˛cܝcn"M**N\Nrwp?ԷCNz|knʬhڔqfzmYVٙ^)l4f{nA=]ik99ph<+@<ĐO2Eњ9HGXFz-+[톶/H' &)\ji&}7aŜL '+I2k0~/PFbYwڿVu 95X#zRo}hݯ]' K<2zz o[ocg8uӛ #w\RpGp#C WS?"=?|ycW_BǘDOVO1Pڸx`"Jb` !Ewu{Ux4EdԹ9T'Df@?/O=lRQm(9©3gHT@ mm5C~^[{kYd 9ϢP?r;28dQ Ѳ<};L m[*B?%"*ܾ)p?},ؗ"W@RQ ǧ4 Xfq蘝M]9c] ddc Pz֎`m`7M^ ;}&7/07u\vnz-uo+,9(F[ 9*ugyR,Ihjm;lj7I3^;`Ohq.]Vٿzn2D6;Px1XiCRP\!B.\]5@%_Y&,Rgn6lkLjRNoM=ַEʴ;?eKw쎎aEhse!`~ (dws10 CH补w~YbR_"Y_Rfd_ZKX!6Yj[p=v޻Enϖ UޫZ?NM͓Sz4Gpz_6$_~}z ݭnDӍS4!/poieǪ2(N$FN@x&l'D pYWYڼ?~";NVٖfB"sd:G%%o갡R:MvL@T{<Ǎ &y)Ɲ:s8#Oe:pAYsNYR=(fV߿[!V ]K4>RqÌ@{޾fVՈpUM_(fc yVjc<}xxv[bwh^_,sX_6d^T_Fx%k3 u㙧/i.!MIy*Q5ގkqnLŖd"Ӿ`hWա;S FH!:HؠVHLb-~mਥ>_/BU./m+l;,꼑q2V+K}]vj, -l20@{rWBA&nyLD2F7w"ՊT|Bf,|Sˣ4V37kܮwun{dMҜo˴c ƴLnz4so,G3˪-q-'f{_#Hp`I"|"`mzz.i>A!WXbdu-_w60I\?:x5ͼtߦwx}c_[Ck`Ng(ꐮaӝYcnHJjV$&A2\5zO>9 Wڝ`~Bht8#ݸ_2CrՍ8b3|:%*HͲ]tk6Ue!?DbrB{(cjGF 3'zcbl\x7W!Ho,-L0 swf\nށ iNo ?ZIMJZF^=3} ?~UYԎچڪȗ=fQ|#˟ 4I| ~%봗"G>h5wQ[p= qGÙs (gL&X;'S-Ǔ _W tY1\555. SN!m=Z8~F\X9sP..q>-)edϿR2KBAL QUJϺt 4_LOdgPwN, k #I__]Td+9u6{0MSJq/7Lބxں$_T-I ̼yU1d!o#SJi$g4wASJ_kv׬ TNnKMFL7ӭ+kY_٤ 7I=k'_aZxy诫b-Up6Tb#6$'VC"=< 8++L:o].좈f0:޶2Ϋ/򍡀{<5B&jyZR5j`UqY- [A/2꣹5B<ؔ sobx^ V)R޼]U vF}W5uYybɟ羽g6\z4zN_ER9FrDP6CM~ѳ1Nw bݦwN^s]hH0rxsB}V٠,z 7y1 :n+h 淉/G: 0}W,Zx7,DiTtYoR$ Ӿ|Ӭ(Y|9mMt˯  jb2օ+-rJoCps16x +NO3473C@Xy|N ;ׂ C `?C%[v|wp,2_w4Xܤ4[ WL''1+B@uk<!'K?pt嫷igdžmͻV8uM|#V|.O6$2HdS[{#Y] < AOT:gnxԌ2ӭ5GW!j;Hh qM+D́jfwe;j%84( 2eeUYq=!*vezwy&da<WM0 2'ҹlɞ/3iTC*r]f0{p`DիfhO}#Ms>Y;V6r:s)[G"Vc=$aasRV<~Asp bmzLW˦O{HUAf0Wu:Kj :Dµh/9ޒmVuϒ=~Rhj!Z-.LjkcCߣ'>MhIBW'4Olisnvn(5%vDԛn/3_::ΐK#>4?nnp18V^۷fe:E >!ex+@j|ڰۅlPcaYs5?_-b{O(ȗtR'؅U Rbѕ5TYH;ZfcY>Pi?;k}B:)lyrs5[6(Xugvv-<]7p/=g.5/.>SU]2b\%\Gh2 o llNLe6%9-ה6(+??D += lʏ]Z2ʼn31^Ug䫢v_CV]gԚkX #aLVjf;`kW`72LaY5JX<힏 \[T:(8H^XD| y#?$T  H^]"/ck֠}oΡ dj/5vȟt6T]E;n֕Jco uWѯXsfsv@]'oa\Fmʇ#tav~$W/!u'|&d2R5BZ jXQPtaw^@R}ic _1׋6MҮsUyrYD^z!i+3 /PG\:5_àKHQ+kb^u3 -6Gj}5nEd<ɭSUARWa|[᜔la:>,Wl.Bip(?J>TT(,^Bڝx nA C;f%[9h%CH^бWT,> .deBh"etA1ubo^ Rl9xp~(l/jr|T Vݷ!Ri|7K7Yj=@I9^ [\1T{E1&WLB^qE}aآ>tGn^HKo'fwtBI4$<5=uDPID[ g7ju{ 'ě $ӚtOyYUNCj"I;mFw24l5JGE-'3Qifjq{̬~Ue {;G q-^)SypawD؄ii.1d j5@wE(v 4˞} . X]ڥF&` w,TTú=ԃS|v)яdF@2hs!5`Y9N gI ԭʂ-cmfvS!UT۫;t63OyZ$!w upHկt~r,c%X%k(/z+װB!8FESlTvKpMҶh- F" .%[dRRD^ ҦF&P6w> iM@y\%to4Ş%0B>k؝F7~4';,4[F0ʇLX{ӱ@I0d|_~ֲ^o&4Kzee&(W 4zc~n3N+\*mRZ:U%+*pHxSQS 6>gk.б9QcM2E=`erCR2bp[?ulǪiV=ZTd*\bDbh%$+bcY$8|Dfvy[QփY& )8RS5 I)74֗Źԝ(ǿ4#=fMJR%χЂ3Hv5V"C.p!{5LPO)(X~%pCZb]OC"\3Ep.s{TH20nÍ ڨieЂuaRIQ:`5.|Ug4;_e㸃7`^zXx#d-%$OXmC ʏϾi<:E^v/CW^3ʵ1`uk8QC)ҥH +)@>FcN#@ZE"N+ս4XS}+,fn#\ IJ%8%Lrkq>z-+FԺʚ^SdCNXꈟAYlh#TwǍ6rݏf&@\$Gw߻@iR~FI۔^ {V;K`PkNWڵSīrRI4f)'6NL!c~FjNHn,h K'>*.M: I~i癵 -D`G*f74kC6i\DKm.9S- \':06t8ʂ{ڍ Y:u>JJțL-0|ͪvfm^/{VK!Mf嘌k}M@>yn5\O4 ?Ց5?lR)b͞[S,̣3[ McNP Jy%'"jAOh"*R xEcjh48IiaNg4Hiym'灲8 =/nkY=d*嗭$Г7l6X+`DjztCYRȢnmo34ZO@h3 bpNm&-cYAdNJlCk"=|(;Ν ׂ|mvei!dE/S Gh4t؏OzH391~31A4t-pE᭶ς?.:VN uooټ 4/פ%;|Ūy:5QJIm,#X@t7q6ɯ(DcAZlo@qeAxd/Pa\luZwK T`BdS2[{c|K ӝV'zon$?x1Lཋ*yLrf$~d Re=z!(-yC~ijrM.2^ f5Ē#=UńKsFp?NT\m8 ؄p긬_qpıR8?*k:̕@ t;7߅&r|q%ݻY<-1n`jtnRL S5/>m",G9sIšh |CQUH%{iaɲ*Ɇ"1xU^-襵*; Zh)pĥ|jPK9^bTJ6Ǻ;O |*[B³$]4ai>@T)#gf yWRri#8E5QE\Euz}`q&vľp>$pE@!=i<;PތK I%2?\1Wי:75KqwfR/AyUjGpT2TgRpB#e!%%þ ̴HlP8lL'lƤov"פ,$^!5~jY!o`V Ǵ$5) ad(z`ю |bg6_5J걁bC N8YcCחLISzQ'ZLﮋO|*i8D$DQM^HՒ bFY:4 ҡ)Ӊ w3~|}߁lHLzlΞoWQHK"p}ҨEUB;:!*hnP$6^G.#|d$hbKAP8/^ƛ9aԵ-<Ѷ}]L_ѕx5ټ`.ɯC2ٺEU݈xYZaKbu75B[ܢ6H>}EIߐ 0eY겁fZ>w|pнߐ\WnoG5"҇o2CjVFhrqxXD%ݍ"~]dRF}͇КJp3<LB~1X|Oo ;ҋaaN\9ޔZW=71h{ȲR)gfC+jKvҧvnD7cxi&DEf,7P3q8#՟giMp[|oFƔ R/$b쓕t k_Bq^* nx#68;f71f<3mӍO쬷崥ʓ±G*rB߻T|LD&v;|T}'`ќ\#T.^PN0ދ ]jU|O蓉:;D?ݬD!Tlʡؽ$?Y>2]rJksJn{8o,lir> k~ *zZIAoXbh Úh_ŤB8ٝ1U,::2 euofRBZT2?w֏9ni_E+.hDuT9ƈ;-2!0yC*qϘ2VN)V d6 Gy ߓ{6^wSc}-,@G2"M4]^Zaβ#DnE݂?!~7Y.~"(Ora877M%of^%;IlQ-D)w/wh}mN.69l+KWj_pSC9pYık4@JԤCӋ$S̠YExt .9N֛qec_}o a JZF*}{8C+}Pq"iW1ve_B-X: YفP;MO 7H|)8rCK "E,jO=Y)!WvT?}ŸE9/ Dǧ 0,mSۼ$-@v-S`3].Wq>m"~ؔ]Tn]>\oI.MΖ!3-_i e9c X2mWo|xQ`pjTxƔK}RB*]< XN 6k5ͷO5̿i<}Ig{9s.>Yp1u.-,9EAFK)K#O__+9a_~ZܾH=ruבSA.| kN]&MZAƋHV-ש VdS4CCr2FVc(@6n74Mo~;EOfYr{`ͼrqVMdp'+[=3Eʾ j܄Duy(p Uh:&XeԂA M+*ʙqZ,}-nAYf`Rw $'7VFnXYREٟ~jx&ש]<;6 - (%0. ɸ[pA*,(@fԽ猪dwwCGb>ۇJ-6rgƟ$Ukפrcu$&-D0 LxIaf?nZ_; ~YS_<@5wat#ِX.ef[ɱ<)jԵ{s6$}͂2<[G3'AA)~7OyD:&(Ml.bvj CTPG6ā_KAY( =Ύÿ6UV#4^zf'iGjgNCim˱'5%*hrrݴ0ҸCXmϏޖ`V]7׋zS)Ȱ_c ?z%@.bH< XBbmmJ^WN$ O7w NgUSY13?yxHl_@ (djkO?"h ~bes][ CzKMԟ9lۡ|q]c;+2RKrcf5[bɴgn`לH_a9I#| 〚m'fN<*S8B&҆hlMGNOjˆG+0I(UEBXS0Km[rOt!<4zQvC_=]b]+@:gB!|Ro.Q_b˒űF ;X^l dGRQYYC&#~آHlZ Q}${`Eqi:'ΘW}]*hˍs3jhˉMJhS߼y5uFܻ>>{Ax$t#˯Ry= X;L/JP9v>qI&"9ֽV m]7al3]R rL}oٕO|:!ymTOO`O3d)BKٴv!S3X ]M']*QW]dL5/7DiiL~Us6g*rL+h=]EEoFYl_[V1=l'yY'.u(@Otato!f*KLP&ԠbH;Q\=UƱ{ڭ[FغUa&KYn Ꙡp zi>L8\`G L7v 2+ 騻e|ҺR vvQ4SQT"7$!s*:rjtSdww4^T\Cl,ۡ mb5cx1NwpGpX#vtm=%Cdʼnn뻙zs- ,ލ'e>xgڸM2S_zctA\x"lji"kIwiP^"ͭ7YESddVKLapzg nn>oS:P@0&;lG"&Uj "l/8aUw)TI$o-;OcAecISG=ph@ XǾGz ~/4-?U0_J lDS -?TL)RYhVMlL[j ֌U $ǯ$5X I{Iz$ @JƱ`VޫGgkORI{8 VnyݟXK1[$hwƒUa#\Uպ5sCߒj;d)ϑ7feA9+-#۶شBF9Խ՗xiHHK{v?? Fsl{|:eJb3]Huz|JJψ81 0b%Xh}2ovsf'I9 !]8G؇fB+ͷ{ S!Pv 'DoI[Q"x9u+OE "ff.x!42WqO1,O$>LQm0 0xa, ǐ~!}wY"`E^Eo56&:$Xh..)ԂX"l'K ?m8O빉X:?9hMbSGđ¦f}ru݌l@W8AM\FB7;AvlGL X]>ǍTj*7uW}>倈W{WP 2zbCB_C ٸL-)0DZ´2ޞAq#LZV3N2>Jp 3d-n1>/ }5 G4ZMK y`^t䨣R_!*3WCT. Fld \9[g oBYs^N~r ]v(n jyq9KɆ1 u>ϔcYjՂC/B5sƲP&?+1L:՘7ck썏_rL]9q4p x!S6isI/q/R9)F&aBi?jf-yQUluwqyR=ĴT~>hқbV5H7?KMo9F]_}vHJy}."؍ ޥܣ+U=}nzMηL* Jr/ݞ2[tZS,EP҃ő=]L8eLnK\׿աNj@=X$C.YzC2/PHy1`A*ǤۙŭYW}9 Zc{t2iBJ,8 QfFn])4f$2ޝc0=xך}|6]=}GrW";? |#oJT?|Q)UP@(r~!8=7=tF}|SWSJ^D;`Cjp:cNU#! `׏Xúժ[B]r^r%ǖ\#Ȇ@~'u4f#q|@Xg.l(( +Ҩ@*%r$V>.3H=99y#.P%B74<rWfH{, Iz ʀ ˒n٪NE%K6̀GZVOc[ڬWyҍ88FOuHІ¯AkGl((\c-ǝ><Ӷ"봭d[M*,i4L}`F$fcRMMQo?#үکɡF$ ]o,1AW.: .^, +iwx'./lSMqw!K-p-U9߃m:E3NBoaɇ:Ŝ {LF+ 8W#)*i䝵jŊ۵SFYbk;-̍NܵqԱ4֨{]rmks<4{Mw ?2L|f_ DL]tJ\rW i$S I3(ɨHrej:!T`9jL5#H@kC `CMZ >98Zk04Z/ 6oP51~4??ok'N{VL:]'wϼ5{m9S_W;=/ڮJGF"NFD˧,LWN;2CoFI9=iPyBװ%d>^ n8f*&>$Oژ=Yh4Ꜵ|1ᵠ:郂8^(D» ,ME"⭿abZp[ϚtYUm^}%~-,Cz,ekx}chgjq4ܠvͺY شSѬ#t,1)􀎊EެޤQ)bv:Xa6AFw3IȄWjhb)~8] *qysζjImý|ժElP3N49%CUIR9؂p~TAk8ErD|&߻) RKw0_)*Y|$9n&5/W(Q+oכni7Z) /,xop=R'MvwW,6+,mGJT*PƗ5RT "z#Za53ueA0 6ǵx%cǛ Sw)Uk LQ1[)5CksR6R'in.y:tv-ptl$sm5p^K ޻8sNQ%J^E46mCbF٦T3)C"Fo?p' Lip0]#XR"FٶVDqeaǜJLyiӿN)r4J;UTStY'V>vgy%b"4 F\ia t^|czG8T4C_,ٌb>hvr'aJ|1BgK_W#NhHGq;[4\N[ԮkᇱsfelIݎX[V0Q+P>5"ja?+R0ez|$)<qÍlp.~¬†QG3wuEsh˂1k .'(N@δ9݈] g`!ɮbZɪ`t \%+GhuAG9ҭ톀!)^!g6jͅ,v`,x1Tf:4{%.[h 1Iwx.nNތrg O/kۅABl3 8Luyw^VTwmmRn@3YeӸ:y7js8ʳ!3e͈s| W,⥥~ąL\ d-Tnv.F v_f3_.ѸA_dԃVr1G:(3jq VxDlW/n1{GVO l_4:ȶ<<+--B&ʝ+]$ס"4_{YgV4sdd8%Br pɼ l5 OF-"ӤF{$ʙN;A$CxBT6#k N.M( z7CrUT$1=tv Lˬ'ƣ(I 9.lTj˅ ڛnBϸͤ H FckB d#G[ JwUsL @>Uo21tDz+J?8j]@ QRsX,߇ 727.\ːqTb ~\s 5察D/DuR`q NRqmF$V!O`DMrՇEbwG+=K3I/{q/!~žvP? fZ Z;yB5g@%c"HzJFo7`\HY*|/2 CoG-y9x[喙D̺4g'=#!u:;1sb+Gb1(ILɵ#C1gOfdc&$8'`<`gf"lJkJ\~r40. 熊L7 ~*dfZqk;=]t&LhQ3SC&s?0xͶ;Кm:V5ڽ˯.ji{hVk޹cQ@Z2.&-kIA6綝dm[/uwv,߼̼PyVE@B6uyPYz./aenW;ƯY6LK;%Ůij5{/lxJæO.^,biqȕ(j({2~Zhͯ#nzOK- BLF Dඃ] Y-|,*[ϥP?uz۰]#'):΢ vxٟӭG8z6ԫ@~vz+ZL!G B=f#ۗ ,vi7%C ྡ|6ϛٝil@+[BpNBM_3$ytJ߇CÎ8y.Q5ï {0֭#p;ӆ},Gm U{|$sNayFi!ڶ!(#'>_t($Z$gNˁ+:r`X<;NG<%>~ -{QQ=w)츅Ĭ1 G,SQi!V8I(+ -Ypp~\%(7s#gϥ.N,%@Kyunp_!q^L!YשNi@# bA؝_b3էW櫾mDC^VcāYv<@]|o$;Oani3_$ʏpU?n;$&shڵJrݫ O멱󱇱U.Mr\:fmBYݠ2ݏ;yZbM*i>:f{ v>969.gz`/T>%bNH~)ct9ᡦ|L d= gWwC_j]V"PR)?3wh'j\KYZӋ |88y:i& ::.z^mfO5^X nV0H=Q)uaA|Ɇ!bGRyT [h_yp}ǟ0#iE9@0={IylΡK$K].sX) Pc}ܤ9m~9[c<"M Ğ9a+aa%%e[/"*^mH^41B P6KD{{+ 2jyxلg:ԥ)r Y)]M<6ݚ[Ybt2ːTkq¢o{*5$331 Diն<XPdX S,X H61BrwJϘT1 A}JK}4IN"֌dxm="?X5 ᄍP,ƫ5#^/>,;N0[B|&(e#`-n:)+_!CތaҎ, rz-/uDNɊPN3.gO$EنK0*o8pfpC>.* 'Bv1W̲lqjTJ&t6 ;Ӡc@8|ހP6qb&9Ҵ~G(QZ u#|6;b |,g䋰_/_jN: ZlZc88)(px){2n+ʔY@ S¸3'j诿H8 T3G .rƩ p;9_w 8؎pxTY3*;ߑ„(~* OWgv:=Mzz~h U70l +%@a=͡+j!kVJBG(ofZT%GzHnЮ u_xhtxvǵ낚pڳCTaܶq/ӰMϫ.|$. {+ݖs'9WAfO.DQ-Tݞ1 =LM&U%H ᭖/{B;헗Cya-w\&lFUb22ѡ4&wsbJ$Dd~lʐPrn3{.) 3 Gz52Mmu)zLn8ĹngJ 49`YL`㐍`=Cyq"`O\piyc&DHfh^$ #Hy5h}sqB0} Qv靶WJ/va8ma-Қ^T]M! d0]B`_s4,Qn p>Wtjw%@:V{dX?YtNcvk{"_sKjѰ[7O(Y̓a7ŹvgLP=& sQ5 nxʸSkF3+q[S [Hv#"zS4Α+X _\C@ɿ?v~ - /6fH¶nZl͌w9%b#{C5N pz%_e\"t?wrDS#yd0xW Mip*QMS1K(Ϥf}ex`W\jn^#z&&_I>T"$ *;}-Hhr+Lu?*Xo^}eYK&RoɪMN`>KMHwAdn癒T{k%1- ac[Is'lY0D)zq΅2}ypy庉kE%<ގwMc>ɷ.O"_чCiVg|hĿ L (F|H<ٗE}%uE :a .D%iVl#MdZR;+GҳoBL5%;MPbsiEwsKP|r(;Vo+6}2seg9vEs9o5+<3R# lfNB6-Yx5ߩ?VUϓ AFI٭Y x^腏g ra ñÆ3,6"\!N4 \@_{Q§$8Վ#\ex6#t_L4xٍ#oDpʧNV=WNO+jˎUmp o#4ĪfgI[k4Qj5\u2^-/ zc/jdy!'饴&0̊`3?j*}i[%vW kgbfVzr ƣpCuXMDr`YlP0u}VM\420`_GzJ TP<fh`|ьp ^UBDU,cw$ݥ#Hݴi H6捎$Z6L@515P#'mZ Ƽ *&= X<׾6d%K>_+/[eNf炌*r: %3eM55sҫP6r" 2zJsKF@?<榩q/^ꞿ9j0fXs?_b!(Zk]VG1BP[$e>T\n)uAJa~5U+R')K*ǿT̡NNi?x35' u%W=ߋ]P<2H& ͯ2(ћf D)S~ݦʴ F3 K*6Z w8d50K4>X/+U§:5.Yi{+7Bƌ}nmzu˞ܿv c#U  Ap6;v́ SL& x}#;Τ2Ԕ~1!q1 (vn*MڬC / u!0z56;dWF!X"NA FRaP @p ;:nhN#1՛Q:E}Z{An$T>$}EB i݉`Ť(*rDtFZ.2'J. 89Ek?p%Uу X_/GBһ53F& ߊ|!_vZپQ6MGlylv`CsZlẓ<{93T. W 2΅Q#o6s?SgkR%uE?TxhDžTHqCzԻڌy_SU?}Y>70H7'N;CȾ.(R'C|~N>J gNK~/CQcU>#r  =F H ,gU/ '/X"hmm')xa?dcHn b։-.R^TM* _|ãc\+Ա Td֤|Ck*/6ѢkN䕤@SS=W\~ooǩU@ :H Z }<ՅC`bt-RmBhTR809W /+J)n%w\UQ@] SI/g |g#`R̜Q/#1~:4h cB|d?&ċG\6BĀsƗ$sH)hko (+#ilbDv3:OG@kS7;le2T(Q.=f r3[?!st}%7g*LE/U/zטb±} (X,,m2B8VS^9|rbL^ivJ]{EV)m KvvƃҺx\AM.*xQyp_#u8:%SYmLJo @wAgRi :T>͆-LkriFݛ3*n[hD^+lENt|udT+BhZDcxך c )oX"o~eR¬Ls/x`jaS 'Dmvz܄e,BO㲔ǐ`ڐ^xJ(vŘ}jCzt(c]fy.'Jw++?Y&5H| zo,{%W^q%aJؔL;4+޲SoJ%-|]/:Ec1f Ud?(Cd)ʃ23 &İ 6ZìW aA#UrGNhqgGE9K 9"V_jʜng>͡ u)BopcɊ33 5Έ?֥8?;9C.(xfQ5@ z4==L2pޕNvxz|(fHpY׺A&$~cr)XsGJ;P:E6 /v 6.MmD^#i呷ƞWӭ+U&q>h wWV%)=zVa~0ڵa@RLt7DVy^ϙ*7enr({QRJ\ Kj;cZq5.{FF 0l¹Ҳ[骭[k_6N::|oʀֹwF`y_T%2)|8,:|q.&:kl'rp#w@g4Qmt 8qFf?m_\~~+",ɠcV32taxzͿzFAMa)@:Y$w侷y@swNRT&ȼwa2cnaeQLBY#AU& (=qn^H |F7.i_LUN' NᰘR?ˀ+L"Jz'~JW{ $r2GWj?Hmijݲ''|(xݡ~|ޱk}#[4 .O^ 8>*]do V}УfvB6#u^NŒL \Gm{nNpzb^`+p 4fʉsAs|-觔F4+p>uA#tOy|À_yKl.'R+ ݖ~Kw@*)p4m'?Ji*Od$!16t:2h 0`*׻J *@܎EW9$ÜP &|/sL v|` p!BqUȋF9NuAd4(@0N VZUWN`6L:b<,U>]jqݕn,(WC}ě冎0ӊ"ޖ0ވܪ#V2.0.g?8,6a %AU*S&-qUib>H3W쇭%K :!@.9:e z[!](n_'~o6#iy ThGʆ1b0ن,on-JD: |B)+tl+Du mlzQnѺ3r+Ub*iVlIQe|F^G#askBBˢ7P4yQNgp1ÿC~OgUS@b".`q |w5TބUF[kBS/dsQG5?ñ3e2Z %} u_-oJ)΢IVlp Ɖ9U$Y[dfEvAb*S.+>?reϠaO/:K :yl"H6#) d`S+id6Oji+S,$]h2ߡy014IO<)$MԲ1CٴXl;>):NUR9y1qӎFWQCvlM*Ϡ͵#nKͼN6#iPUIjr_gL/7:H1eۂPD_ԫW%nyqHt;{)1lyiN "bGH-ޥBˈ?\N&(q=$p#_(˅ѕ~*'uMدՐ%S61opMG ёR7t&@ND2}D {M \8\)iTw9v^ +RyI5Y~O*$/Yz.y-0җC/*q:z<:"{6@g?+AbC|W1&Ctn07j3N?!8'+"_Iu3F1ZDmz*&0yk#rs? Eԥrat4U-@_{9r u)ʗ_{'} |@+>of8`S~8d^voҚd* :DOWw|?5ֲcy@ɘʌ0t |æmaf#Ǎ:&i O\SX7$nXau .\(9[\s ^ }zB[‡@?{ |yTT)v?KԾAvb/a'10-Nw Iǘ<>AROa>c #wY 5Wz&t36څN3bսA{e@ מG.A-Գ =0q9Hy 0%keK,6x( ş*YߛkuCa@Rfz̶ൗ K<[E2 ѹ˒Jh'+ep>-h;UσG gvRK9U!'瑣EjU#Iqw94Q9N)N' XVLz@ȾMJ/;C%F`P;&r]Z(&9imlpw!RX%#o /N58G jNYC(@VQu+&qH$rMCTB9Q |[*Eeb$&Ne;uss!ҍ<73<g 8_"JaeS0Pj麽2u\dZlwC36lm<Ӭr-2 >.}؇wYG(ܟ+\f( o͇g@6|y>Qd7ix2dҫcXni>p'n׮e;o sMD[@.NDb۷R$/rsg}]i /nw3I/%93#>uX#;Ds▃;ҁns(FwvKw-E0𨕡@ldӚ;E/FҼnz<7xuiOOU룮}Vjr{fc,9P5m#`j}rέ0d mF*T<,ta`'==v:۱\6j;Ch~GT?trpzWkDժ}ծ7z9OS| O 'LDKt-}:\ (=ߑPVOcRv:ZZ~Y(FD)ly2r].^g\9Z2z^^LZf/,-&YPnEF˒=~~H`:&,:`<B)-oGC\JTqzZJBj7h0 L&R_#?w|"̈sT)nln E ̂ +_K}śSS+]B߉W&q h `N0E% g0X#&l΂Bt& }_H Ѧ(|(.k=& itE ѣ;5ϛ[LW Cf;(+)%Vl'dIl]c:'ipٰzLDgƩVvT@  R/JM FY}_{1z8H y"쪘QkAAܹHRlD&f=.qOh璱u8C[쪞'Byt~DR,Ga>GnxpI_VrלHC>]uc85]-Ec&B't| IZ.DwZd_وeshv,ZoM:(:͉de1bG[Z)u̳$Is6Hj]ܪUHZб\Hr$_7,v ]nFץSǀR NH!%̄훚@ syaIBoIƤE h|Wa EtcZdl˲2='0i:6@;* `@uZyC4%K =Z}A 4v,5Shl| ԌaPE|W\zRD7,^.RC4e0v!h# g(Jrs7<#]U {Z'0(XW;OΑz*g }$@p z-Z;#7]ɍcç+sFE?G iM+Oe?o3Yweμ~|"*"EkGT >ɫ-_ټ8RzLPQ}]= c蹍u@[׃2$:+B.zՑP$;\fraxoT*yTgNS߱6Da!Țwo>\+^K,z[U#!2 J>, "1˜!B*Y#R/nz}hN_Pj]Rzڏn5nBd/<% B5񓻶Lˎvyq/ dFT7GI*|/CDrk#wy/% v?9C$uo&y eTn8aĀh{#Y;A=9z]8{c2H[^dZ ~ -f;[?2%T EƟHl/4+0fVY]z ;xLiܟUک;G8"}iԶ"FTbT[QRQHkԐ?UmLp+1O6ُkuс} *kq4V6~~=Xw#k; EC\~kSsT3Eɘ`=&xɖK?;􅎚#le*sPN{Y牜3޲YLO" k@#Ľ*Xw,7C|U6Tf29DN% vo{i˔U~Iyo;Z=KnO={gH~cq6=;h'@PmKA8cEp*-"Qe鉌!DP[C1CӪy~#J< k R=OuA 0Ss@E{3Zd–W?x]ApTԆF֮3x:qҦΙa(.2ʿz^ȴ`Iy1a YhRѷu<5EM1mrl/%\:gƺiSTuC/]"9‰w=2};歉 ZyF *ruBeM:4ufL|d  'v|#˂>;0fg6 ީ%vdf 2I%~ШHR .MɮFp4u)BIPrW3o-CIѕ]Kou*>+$7aaKM?^揀IqtG20Ϟb>uRɜ\cFg'hM@7Fe]슀ӳI+!ب4Ole@epȱ삉a!;Z5xjunמ7|=f`e~n~qj6&35Oզ+t'zQNYdq1]tItm:U ,wbpnL!;\F uK#Bhrԛ{Yw |;M̝ϸ;~_5) 8ȟA8)ļ",ץKqh|IJ4h/U q-B-㕡=eY:iKyʰ6V=t֏}e\0qux<`V 术ѢQlD#6D'~^kfypZ2_hLَ7om?zjxV}NGO>@:x89+F*yr" +l=XWqt"}BtnL@S(Ey#ne0jmݰ$Lip^diraXOK9|tXPmyHJ,؟*0Brc>)aShJw`ɑ!lAN!{A^*ECaYS1ZUIgKlV`pu]@)|9=[ `h)g C](m<`xNYMHӍQVԇu߳+>9mҾO߁mɥC_`꺮 1f4r1 FDB~[& *[,m/I;1urq.Q`M*2̤iVV;m[Bu>ZL4'2R8a~@VEkk==ߖZ3ZMʱ}$/*=f=Ct;APeL1۔`Tw+qpJp~ݵƒJK_ b(K۵&F. |c?1hw2 '.tW@fЮBD"|$,X T[ųݎ#*mU"n#;z_7KVH"/}nяj=UŜχKom^ 6_y!.:yys HԗĐ"O]kN@"뒞v"z(8!x,ڂjy '^wM3+Bs)׭rS[aXrPI4LvQMCy<4Yn)4Yxp[KpP|1 pU?\X!3c %t}ib8Yכ^5(h1N[bʨFuѰ&(A$x{?dşͣdSshJAOE*o3:hox>p0sA҃;9IM>VF&{:Gsқ%6l.z^VjFfG@&liZVG~\]!xkdT]qL 7 AIMGEP$pKyjcInSlM=W/Y{pm9FL~\y,@ Hʅx@c5ӎ!cϚc^EvJE Y5:6 <7wC#-j6sovG' Gh%8H?;ɛz]eB#rՄ*.A(M]CĽwV~ Ӏ,A0P,ѡNbg `{+ַ^OrAB™-=CNN}Nb**uKً9M߆dI/{5]C8\ԣ 9}?;zJ6Ԕwp )! ٻV'HSdS 翎=΅o FƤ0GdB20vBӃGr0A0}J٘ Zu&iia=9Xwu-)&"Icӆ鄟ř½. W0:* Ek".^K pXȠIEov$p^M<ǃnx!-WV22?!VyJS4խmԄ9T[sF6$zbeElu hee@ |1A$#خ\S$zͣm[ZX>$1)p/y-BodWeÚv$kFrnGFfMT71v) cA6s|>X 3J@}G`9(w3 a~a(V?FIN.NTj#I@;IY=vpœ% F7#FUj|,[n̊TKo7uU#S MCOB&x,creΐRlG9 C+=P!⪭XyF2KYNDAL%̋Rw-80O?7}){n2OHE9wu?SUy)[>tE𨩘ܼ!w5Vg6'dX'@ؾ[bI &* zn_!? 'E-'DˆT6 ;2a6teibӁ XGߨGtP7qj=yIBiV6r1].A%)(BWu *)sB‡FV2n*PWIUxpQ/6P`ZFCśuKuVAS+ja=}Dz]K,B WJmqwHCN-}E*;= L׍-wTRe&C|אZKƒ@ܟ;V/HOjl5}RSfiv_OJ4/$;?G•S_F}RS+NjFmSXY %b:+Ca:I/nm1 D- h$.,7R1n,Vպy )2 -J1$ismrP (<~ѷFFw6iG=9EƽTT=>!*~܄RՅ*fpMYǭŷL3G4?Nܫ;J99ëCCsË41abؗu~sp!9.knWNfYto}<=hdkEg~N֊sٹB&JZܴX 06 yLN{fR!4JNĎ r]C<9ZǓ SImSh9dz2%Pҹ &l ڦ܎BhȼW~qz RaF͋}2ui*UE2:YFBշft0FeYE枤P;yNms.bz+ư5 7Y/Kz0,*#Ol1OP/Nz=<8#.eቈOslܗqo[5>\zpx 2>wN둣޼vw,!ڋ)3ɤhl45~ѳ>c1:3 x}̃壳.9ٹ3W @Np@ryثۖr(tUÅ0wvš5l`󕪽I!#/(MF\#*N_f=(XvY#Ǥ<oո6ȍ>>ԕK`1]`9iT 3D O WxSF˧7;:Bs? QtݩJZ3o^RmKeKkR?y@nJl4@y!Ғ'ẗ́.SӔ<'k.m9{mqt(*=ևE+nsLDh8' "!_+ wɀ4(b'!8nmKWwghjr9w~G3ʨw(2]Bj)Ouv?l9ddhF2Dl ?/ ʸ=&,taE(f߀YG9;޶M++5G< l"8Pf#ԘLP_q27-;bsnpz=f!e{.VRWKgnbivKٝtXZ:`:ոyO+' dHToo#])#X=A)%]N0h]h#f9p-U#LXQWŸa<n2<x͝^G-(A:tR+~vʽa|a&VMeSzgć ϙ1iɍKYs9mky:$ єxP,ɀ@HT]'3O%lȁjh젷T*C }9Hj 6$d+d e u #*%/|A)iMʬLJMՐ&3Lm /J^ӡRC){ whcN /3_E`ъo( 䅿)ʅhh/>dj#-:ih\VkD'#jO$9g"1D]qH{FBیE3՚ŭɋLh$ 3ma+\zEV⾬{?u- -fſD?nnc1^..ld1? NXhW66`aHzxsH6]R;9|Fw W h)ZB>KZQi\JњN+V-b=@O.o+~;ͯ%pJlRtJἵu`ΎmGjy@i^%݊Nn,Z_w!Z¸gż/P<0?׬HJl(n Z~1GOV򨾐Uz Wӡ@pydB9( ǧ˯mK;en ʼn>Ox/)~Ѵ?RHU75(`h[*y xcTl5γ+pL9;z۔q\oC93tEv@g܂>%3_ dh;}`"xG *IkL50OCI$M LHG)즔ˑJVFL۾ T`h.Wa/)\UHs)<k 8Q9'2(QГbSGlL|?22D[L>/NԿT  p-Z]2x o9L^3B%!XUhu?Yc8WmQIE_.uT4`Ӽ5.Y[. 1dtQ6iI r4.lq(/R^e~] H4Z/_rlsPm?8PܗT;qдF~*l"64i witj*+H*f $\K6 ZOĕ)巚QGVƵƒʴ"'єԶjl!\$>?%dzC72ғuUu!lZbqWK23|%)MPez[P`3J_S!P{]4P]ng6'2!JtXGfb2fLjN.m RO``u ]?T5%l!qbCv:1GΡq3H[F`a Wv?>#N 2;4'S<i `V=C+󥦚n;Y0NYJ UUwc̋DJo7r2DItR%NE@'WYg5 c>rC ۥq݄YTK-*M U+hf/`W@Xyץm4aMUݪb.XW+~ H2`w)i,}Pzӓ$ zmUG}Bl8Z?%O(塾EB2?Si xɧ7(aQJa~KgؤFJ#fj`!"Y&u4 أV.Z:j.ǃD H_7RK pQdO ~dov]K]Ļ.tӾsz=*hIXJ=` *Z=ymu$]+e9-J5ǚ>]EL3Tby t](ee8΋Ws7;p2x\VwFL72 jM"ǃ;il0ɐ@ UL <\Zi\drHt% d4JEYƽw<&L^T3|L\i(9UBH,Qh 7 e\˩Ut9`M~ FOedeE`c"'}Xcr`7 2gr鷄\eJpra5䘌I$B:NAYQF"(NMR h5E?$67z̲sˀ8<&e9_q(>"M.Lߴ µ16gEn!?RBtJ]agkK>M&6O:[A]l˩>' kJ@'L- 3#ۈf5|vI.)tyR3N?`*>ɵZ|quBvztr32 d]M+lEҹy$rJ*yLm,heOq`VEJG\$ P0RQX% (d4i{K.{^Ca'1!nΊoF%HB5 3i=E~DE">1) =KkﯩĦdÐq$tܝ 9L@ոyq Cj\lBV*8@6Y YK 3}S ?_O`hcG\jZQ%ɢ)D۟J8]f]*ǎdhlN06\71~7lR7\cuF% %b:}T3xB”<$`#P-~cN_-9gkL9ժ[)h{)rg4"VCƀ/W\?ǂ3Tgt~@ߠ`3Lxo::My~@߻TڵE#W0]H3EƝvz.9%;HOE>{{6\vyFs '$Ċ |\Z>Y3pf=@TΠMVmR!"==<=Z u_@GА~1H%_ǛC,i 㰸* aOY|>1'DdY/;86z3PYk79AXYֱd.@yM fZFT\N/;'if{ f-5Dc1- 4,/zcu'X5Ҋ\k o'Z3bk!0Q6P~X?X㟲g"ҷu-$U.-p$" p1OodcGvWn?l@l D8X)q}~D]*!hEd[3cfwH`Tb1$h3"Ba87LM TI؄=y/}ƴ%/|aV_;Yow&BŌWn} 9riQ0u~5<#-Wʯ yU,C4bKa ׫x]Qp,}:qUe|kߓ_p* _`j,Շ y~xe@o[wA2h5ͼ}>w/}zCC!.܃p,#|ߚ dV-#ٿ*Dg#{`㛷NNb3|0RAyeNr^!OpJ)Z/N`_|L g#Hą \gڮ4qh(X꿰F1 (Cw,hϚ|w&c_u< a`ZWRHOc_K^/CA^DMK!'?B>Y ?EM魍wj7na A2\q dv&j~*b3)w4iO63s 4q{N$ .Veqv\=W=otD"D*!?yE?B QdžS> 󆫽ų't+;UlM`ZE|_p_y 6\-)7*VPe0ZQ&S$Ci.}]U'Z_ ?KoCj:ؑјp41[(}uP~]x"CQԨBRTx. :6Oʝ*1c[z lmf&O2h4H@Kq+~G6ͱeM$VLXA#AsjGw&EUlk?{w;=_h3DwٳrtŕVup ?ST _T;h(۠t7#3Rj`_b2!WIG=^ohhsAb= ȝ1%;Ji-_% f3̷Evۮ-2ikp>*# Xmŭ q,|"ƾ+'F;&H'KW7ryX @"sDĘ cRtr}yPKa)0XcDij\!!F63NxrbŸ8oJW)hݠo1 XJ) .?cl;xV ai?L4Gp*M6VȋZX~22@h;ga*zW7=T!cw6=Jbmqfu±,+;K{ce ]w8X2ɫ1~^"ηPpnQs“'$a3|C~C˪76*A`/_\Y ;(7i;ܘ˩v"_jK߯)!R >\"P{%;/Τ#ֆڨ)RP?F7nfƄ}2-N/k l,Zp11ʛn6N'KRdljȺ?vǼf&mW?U&-iՎ3]3V4_uwʭ݆`fj/]/$yQ?gEi:|˓.28ӑ=ܫ_dz-CG yPUq+3> }dS??X4?`+ҠEŨ4_lnj.(P (18hyi)I 6M0μh ~–qjH^"GI璈9ּ_.qu ,'Z.?H|g{E8GOKIj͛O&Ȏ w y~ES]Dk>G76" 4Qu(+;+~ڨ5Dk+IXGh atD{B5=g31^t782/2ɀUi7xBd=3wH~)KPo.)x(dB=A͇;_<tAK3V73qu loQ:9Spjqes6 EN7l  (`Up3OA< +ҕAm[a8c:|vѽ.KpEE~%$a8+:<ϔ?ݴsU+3hƂ^ӑuj3f7 ^;{ܹ*rs(Z-%ie\\!,O$g9;({\8@ygZp)\9_&F &,u(\䭱09+g}knڗhkY 4b89v=-1MMG2f 85M}$ \ʐԌ֩DH#mŒm'|>=O1Oӻנfa`A %}Z4RCSۿ^ՐL@#jϪ[w§%}=砻$ʀN~nut Tx!Y:`V\4IRKݨ$yVE{t␈T~?֝#|0a]j*HÕ{[EͲ]fffEDůMHNQ}?˚VY6?e2$c#iBG 9X"4AFR R+ʦz mQs{ ZTPWbb3")7ixʙI8;m3 0=cM 3͙UE9cΤKN6=L`ΕlA7i&It4Q潂|nExѡE Mv<,5JۼeB $gm;㽩cRpIH9M {00nU "JϪ%\>Ow/%HuӴ Ob^O!"訃Dt{4p;O1FjԌl DGYA&8cRAr͵>]BЛ۠@f>|f1EʄZ lԌ|)ʘgBOŤ Jkg7_.w\0E_4 ;LA׹"߰G;Q+ݭWl3'rXTZ:&R1Ɠ*;-j# IՠÆz R" &9Hq W-s-F($ոN ʛKء@g`!U?$0v*! QuBw;g{ 3_S<(b2դqFpkCMY3 Xm䕑(QJQxo(2Bwoi/*u{n >%7b(3 Cgg}COǫk`?fmQgcS`npjX=`2 JLh-v,NEy$˒J+uv=f;"bZJ׎)9ό*8-OT<~ZUg᡽Ckyv&Ad'f=&p._6 Ql{aQ,E9`m Uk/ˠ53Lz6śҍ<2Tn`l@w n-(ԆtB0F"ԅuBz:#uǗhn0]kU}X N> wG?HjR1VJM|*15AKсoXjU6NI@E趋< IX+9B{w:./8`8z*].J3y^q>6&-Ųd!fSPBվpVӁdq\bqJ)oQ&e?J_bZcY{ viitG? ʎN̨ @b>&$?nQYyg9ΎW0g܈Idk'ݚ0R.O7XU0ѷ^K,x99X%8x-  nh}֬[3'Umk3YC'%x;e%S{P#IVUt5{EFz& fիglYP{ȏm>8lrFn_s4fOs 6ˣ%.A?]`ܤ(),9%‘s;JD#O韚#6(aD(f׹H]D7eKB&i]EgAeN:T ^" "cPz*d3`Y+pݡBOLUBʅh庰M~ a[c f5"+I @eR,*oUws2Dגl&Ƅ,5;>܀*q\=yxF$}U.Vm.ƟN`\@jsq v |b a݅ 38 {fRx"3?K+ EQoC{mHUFyQ%K}7ŵ۽ORU] 5{ž `-R ^%L>3$r_)GS.I?hÉ5vL&j>X9[ROxv3:绵XT*bT9HG:g Y49g-0eFD5=G:VT3J"yL0.N"/DPI;7kKdo}zגbi($M$trJnCWeҢi?,"4+)fU_dkazg9PDI_W<< as|Kh9)_5ǁ]D1tTiY+u/5(k!}ժ:^  ԌFew :} ׯ?4Y.3#{|hfҟ~"Iez+Khƫ@%Mq-P=_51AL͞>C$ms 'oZ(Y~O͘QrrQBDW N?mNF9KBP+'X\hvT@x@F_9io>D-[92RVjzV>H=tbd @J%|n5`&N ]O6%#Ԧ'닋 >K8 #&-lI"dGcvKlvRqATdlKk Iws>odٜzCӋS1 [x}t` vG|A-5Ri&KFm}Z]^33gFkZ7`' Flez~N~}% H2,|$9N2dT4ptJ3roP|miJp)nw@K[~{6*!Ur|rKwXU\qø y9%XNwt6u>(Y@G,.܇š9J5_|GV.J 1#W liڗ.N-ڥ8WÙay/\L8*+7[Ja6 }N`s;^1{9/_D󉒵"8z6Nz8leZVV Wi.X~4- g2)Cö8zc Q Tr?l}Gaq;a4z:-2T֣_sh8|N%,8g%%1tz]e2K:ǖ u dxn~_x߃5$kbi=N+b^#^l";wXd m)i L*2' jYu2LcU3y'.[2?LM=SӇAnWY4q1}W8=| ]Ƅ5G&`ŜPv{H9?ux*f9AuJ/"˓[m@읊3_y@ǠY/K_|?f}ۊyJwgwa-wiV5A>VVDS&΄t6Sp;\`j[~FjtrԨz jzvU{/f]ܩ{`ҘZ.X2#08kEJ1:<Z `0?sJfw3^̑T~{px7Xȃ'ڀbvE(r/?[Jc*" o:f5@G)vz*tvفU ӫdBp! "@ULV_zZMTFcJJt'ؕ*ҋׇ+9ygԕ i4zzrq8vSQxF.yD}fsq5y>YQ%Nd6R cMd w^J"YM>8CQPVc@㎲;e1EeYϙY4 Pj) ^wV oY۔OoJ-bho{|yr3G(9F7rn (y]|G[W&{! ǨǨɼ;Hr-rOS?Hy Q>vi\֬JԞ'.&)D[tz^C<2/f0e~(h0E)vrxe*7Tk(-5\YQ& xX!BS^V;['i]?/@Kejt =iHH5*ES5;V*pbA4i}*{@"T~Xrq'0w؁"PJ!u2/"ܭLHYx`DbsG !' byG1_V7km^c8h,yE xȚ8x.)DFn,換KDLHa(KP"qHG š(N7eo]s2\3̚DwlNĻ—]l\'%7Ξ֟"\ N@Z̵$ wP5}WlMrv^#;k. 3׆M+2^1`V: @{#O}>3˂w& F KM(RiRAҤG0oy"CDue5Ŷi`m JW~g= ؄[Y|;"2 7scsO_1IBƑF穂 [6r)K͂Kh8gP6@Z  Ytm$ ߁N ULpM{w A"=pYPBI36'OkMҸ#䋹N>WV(bsSi>,.:n#!TbCHS-ioF_}Φ'iqWtubpJ!]Ea-(?GS"`% 5U kSDY%%+Fh܎#3t! C@Mp .jd;f"Tn*,I^>0 Kq22:=ˉ:#8>&'x)1~î 8^,tY>s[Vq;Qf=@}SWh"XhDzɹu''$`(z۠)xp΋T׬XA$_{m3h2m?܋GMNkɂɡ_ ޮԭDoJꢜ=į5ygY XlfMû}"KwvPe-uz ilF[j lgJE%t*UrrTȎ#Nz&NJ/E}exS~W΁L ZfJW#"dK38Vy|þĻ|KKFk¤_R Fm_o2~KN9-Ӷ U#&?!H.KϳН)q)ή tn֫{wjs9;8NYo¤9P\Z7[/!tqlby#CҼ*(B|Yv4ڏd^AY ?=%_~a mTnpV7'C";#9P@"%tW66xmPvI=2͝ )vKEG Jm)+u2pWQ4~^X9=}̎hbozRytMbjl՗R#4kuWLjRM`:ǵB&f>rYTբxe-7%v0XnXqM&M)_E+ IF OMuȮ>m>gD3|ZĵڞR۽P[<%+OeWą2HN 2LJFw(uthbL!Ȋ T\D:_Ƨp&+싖pɾ4bL_65lTsE,UFZjn&SL76E BnѹS@cBM^5\$7&a+ϙH4"K2O4qֺL}4W 2춪{Q8Xf+^lߥv{` qDCwqsGeI[VuxYMr2]?O6wmMGV4 *ۜ8yTaMPfrpocfуh|xBoc"4Q! VbڐX8%)¢T-pd~iTkG7{stA)lI@)܂/{+ !&OYU!égHؖeCks$VR>dnev-=Rv뎲^BK $m>_C@M%kX ͝)QTbSOؓ餜!"JCAyǼ i)ބ(65mE>G7AK-BP_ǻ1,̵'[}쾷cS\Dß*1A\RSPN%,=cyiC!Ӻ+ rTZ#exUpSpZrUbQDs*AҦL#}QL~P=fdYʅS"E\"(]Ÿ1?E%4? !}MwkѤvO8MC>%oZe7!ؾ7 GN663dU R*,x.g"vsRܱ,ct$Y '6;.]ǶQ S֮L+m0 C[wM>/\ Ѯ,Q~ZR൬ؑ?6wket]v@PQZ# \L]:u=P+c?۰HCyc.F4Sg աGζky'6ɚmQ)t`?P~ ka۶JDV5אx%Em+S@x+ʩ|22z?ٸ7Rb7Zp=~-_pŀe[.3s~p=LvE)g`%ᙔ .^rJ̧Hl9T'wyT@ca?-ΰ'OM &-ΐ#-jngY 5J%8Ovcnd~w&UVvPG .Pt_m{73=mw.ys2G0.l: kh^i#ny?ds9ΝWԯ - WzS3t\~ #W?j+9<<>ħŲn-ub䠟PABV0~NYc9SBl L QpO+1_~u) څh?Q^Ɗ?LEy(6T"xV1a ]U)L"(hkbS;5s2o\`Ur܄hV)@hL N-c?e7iV⡋bYhwo K[߆W2pLUG뎠U2]fBz;mʪ"UDJq@ nP~ܔRv xV{`]T@?q[I]ۗź50w2J1.X64)&V/ [% ~:&{!- ; zh20϶( 3{ՒW|\pҠ EXб,q#BI 齩_ENnCcJd8&Ym GhƩ}$}͍HKiwAͳЙO4WD+~0xH//S)Q'oK͸ׁ$b*,~DEDZ#8q-© NĠA?Q/קk=/B\Ip>};CsDzcZݿ'+uz.R!WPK ;R2Mh>~kE{2>\/m8?tvXLZMMnj&ے Ebv[N ߢ$W Ք:BT8ļ ijgrиĂYF\QſV5/2u? $2^afP%InJ~^g!I0Ϩ쌏Uk\ [!2x=-4>ϢíT-J#$<`N.u9@Dko6S)Љn[r).Ұ>Q&f#\Lt_Q,@a_Ue &bTn2MvU-eQ|DItvGoնmdxxP ){b ~L2ƕy[uDžZ۰*sT98W$}n,syU/5Nu}[dgXmQ 6SǾ~ŮٚҝVH17\.+OgI:唼W=41@4D9y>pZ3}*K?PdHf'G0'ӑPY"Rfϛg DewX|,u:w9֊^4ij )񨳭p,S0 G-}dit$]95HK~t$yk8b릧梋8+^⫺Ȇw- micac3F3V>/@ym #D4ɘȵhsh`8 reɲDap$@> ۡR|0NaFR3AOno,BN2(_ؤm G0HO9#\cL39jS}=V9}k4~;M? ,&/)PFD1kYypGL$?F2PmA44Tb/v̑HG@Kq.WhiAaN48{6lȿA7,Yo1Xm 9kLyCj-@ke `F{^U]DCBoO\W@z`.nEZfQ.~< .Mm(ɘa6٣4}*۷>ؑB_>xG5&YHڸc# 72DRpa㔡(h$9h? ?MIBJkGIp0v>LNJ9+c^Qht6h2HJNzhF;UbR,ZaC+Z " K翋tWx|DFMO^0M J\Q`(4rK*uq=B%B~%Yc^oN#?%3uЬ P-3W.BP{hc5?0r N>OJ";J4.Zh^=D&79D7}&[DOm:9~ъ؎BǾ~kCUT2?hqO؜ +oYz \W6ta*2}gK"c{Χ}./H@fn,-Y=@S*ʭp9\󡧑ԕ|U@|J">Z/鴿g޹.ƩEk/ 6/Rű<ڎWb'I[=ecMvcsTFMF@;m3oRP^\^=D" 婬N4FEdV,, 6x( @iN|]^(lZk?;?"h i̮r^۲b+X4g& ku0*\ݚl[cpeLs^yV9c| (6U!#E\J1Mg+-͹I-ӑ7[dOC(I-OZCCYl8*XѺcXc1ѹg{E]XʡWW5W9hPpj NGWx+RN4MW%CD})fi~mG4.Q4[V2lWɃn1<+'fdkτyۡ 4r@rQ)q 9#!`v2ᦆk3Ɨd3xxNMlVsjpT V o阊~0,gE!װqjԑ$r\2Bwx6 9g5 cwkOm9b *Z9!^Zl1y"#`BTAgxq9^:wR><5jt,[.CIOY,V;Z/vcN*KpK ݄HzW5#0޺־e x (ءldu<0~ spE !ь֛Z+,%i\BK$k*en)ڎ^O.ʇbQ)fPo'RP.%j-1y' 4GQIA?ȑȊ*Js#0yXU19kaA.^-r19w]lo qfȑk9mh%{sz}=0hD`q°njKʚ)K|Hyla,!MtdImL( #;%qL.3Z!cԨ8FA_"S38gVe'Æ5Z{_s^Alajkɳ'&Yxij7tZRF>6N5|rjH1[$D*!㹋Wdk0p(y= H*lCnQo.7q(o:%0œtnckeꍫfѧwnkƏ'm ~;-%I݁&o.f3#ڀ.4vCa=1:"+lH)XOVY".fYuZ P[Wdh@/'T@5L7צI8,o_o@^b%=Bmخ>60s9T NeD IYU\> E>>)لPt:cm@qiTaoN es{! E+ޕU@1+ae)톶a}񱔡,V=DW +xrn.0}%+X1뢇oo6)7t&isJ/2lƶi4K\-<w `#pM*"}vџ`rGj!x7Grz^ N8Okϖ:Jrہo͋bvuDx&Xҙ%/jľt\]KQF%)ui3U733:`k5I 2ɦ˰ ϑ6 ϻBOǨ=3~)UKJtV:>#=˩kb޹YZo:GdbOY1]C}z ~"H# DյG : LJd0`nXYwxcXBD'" xB! nTyG:xs\xw)A_;ݞxRIE㫤gҕĪW[mf'=WUrI5X;lzD.o^_zƫsM]5 çA' )OS;3p| d0gy)eW\' T$ IFNer,8eʆzPӞiQY[Q!43%s )̶_mEIsLu h!9.:v8 ^~E =RTLt"[=颌Dv e8z &_f>n3rbHVe-P\mwJ1+b=+j+2V`ѮOHCZ# 8JKO^}WoI/u]E 3 q}`6ϲ@]CJ0!y e'_R?!Bpni y>R:<ylh_?`sE[uJܨ{XeA'CdE.H,3]OB@/qx*vuvE%bT\4-*dz (g61S`PlM頶yՒt܌hfJB`G y#k7MVdWW^ѶHkVh)2.=x+p1cK0ZCgf Vg(ScD7)K,X,$"nXHuDG}CJht:A90 iɑ._h/3Nh|OBEʹ5cBmFp*-˂ŃҞwׇ!SLīu_&0 ˂L#+z k:_jY4z-1eʱHoʅ|Z8ZY Ohޅ~&2λ1 $$BݘBhQ% /m'aQ^ 7Ó{Y\TyaOUH@t]J\fM< ~)P 3T 2mru17K=4.]W l!vOusqhڂ8a7Dǧ &\.nY+:m" mE4|wi/d$k:+v: Xb ָgj 1L@}-NcP[)8;L>k O['qɬd5M~\JZʥtx-d{G~@X OkF)O6Ku #[B{Դ0'@i!D8{}10[[ƃĀOJ@/9r8RKtמ@t?/B4ɶ=JO$Z "-:u@'h ETęh sW7@V—֑0gjpB! {gKq3"<7i",tJL# ݧYYQw.Qm1X7P)0\QckGjReUVn`{+Oy"_eΰu#6;$[pz< B;Bat@rVew, eoqWf^䧘uӧↄf]Tw"4[(SH 46QO5؄@0`$нE)sÝiRhߤdӭ:ɣuNj-j #eE[݆C_m,V-O0(*?mZ(3w, ;!۩ !L+KN7"AMo23yVU !޶^zQkU݄dk]tҞ]T.?%8lH8s~(E o#$ٺBz0I"_\s|e+ioDZ4.bs3jdz9>ᑰdXψ`%IO-"% vuZ!AAl̪mڃ!Ñd:%\vVbkUapcHBULE@kLگ쮐 nXql8?曌_S|ԝ}Cl#'uZc<Щ^:4NG/WU(֣0-UviB4I" 97Cf䤺?vגOGQ:j#npDE0R>W[kҬ#g2GX1fTje)$Rwzdm2DO=rb?*b~H._T\T4Q`J&=Lܫ)pT¶W٥&K7 Hp zG?`ӎ%rs<<l>=! qco."[al[,GWgm7ygw1 d :\ ]e̝[19<7۵!Vri%*Yh"֏kz8hu3o'}t Gi۾-/lzhoEkOHj-IޗM/w&+DӀ2k%\A/@VؕO_˻3|؆f9jX9 W"{e1>MT4 hl'&XyiF2"% Z`1ZF*2ddnHl*΀R=z1!ԜaBan)y6}',$i-[8ۀ bwfs#f\s= !.cF/3d.`zhn l/xj5ZHr{L"=?R :w0YčaJqVDA8yqo^B4rV_(?LWii6+DdM_C˦>8`uvFŦ_yV|*F٬* V fҫ*iBIdBwDjePW5 TD֏JnK @BRZaM^z?G/%6w9dMK.!ؕW(.;DbCjl,0WYgj0v,̸cJ*9186@T}YѮ~sN$967,GH/ f҄߶{ к! !,$9+2c5^0Ƕ`ǚHM][iqJ^wZ_ƳdK5N'*>7Gk4z"Y٥xc[ܼd 53a&Q% "5~)vͨ~q]EE '/%:BUroe~Ġ5^:m[oO]3 8zK>n+vRBK= fO))YESJΙ{>!9\K)^Fo! pz*}A CUؒŊ\)aݶM[mmPOϪnjE>^P[9=^S1@Y.ÚJtoQLDݮ:{VEǏ"V <;7u{cǓԉxN棗稐Z-QVzU] LK3vsJ텾7I0ؑS[&wnV q| :+7+C3I-q'_[\ F,5۟_K%R`KjŧpP=?͌>f8M*ۈɦ(6&w༭ -{+57&@tM|4V>bӚIP|ޚd|k1(vkD2+AU 궑]>RX_ ]hqtw,Q~Y6|b]}]7!1l 2?|r2rx7d{g k\&B(Pp+)13>?hjq,6^Y[MW]o'-bRcUUY9 /5Gfhw0 O%kv-lNWS{㺱4ThSh֎+'3x4~³ m5 (QOk0lӕW TN7pP~dw9+K #Ò>n!3Vnń/kʯ.)$q];x;aS%Q?qX !]jk-1mEmt:]vpY$b6օtҴ]Λdݩ*8nW[`r_#̮0l)w$e\&tF\c卜R^Fy³r؛@%8<RQwc֙FϹTt}>-Ma΅knNӂoqD`?wqYASMTd2<ŗYeJCGh8bx>[We;? ].o?EXeyAɊ q8T}+y&w23o8"=qBPz"m~\jl1|Kck 54~-TeY+#HW'vgc`%4+pN)FoW3 F靜[J\7@SW\^ƽs M*OXg5`VЌ\jweL'N2G"!tv#cr&b6U}N-x؁x?f LDZsrl~4Lc{U깼R)bߎ57=t'Ml\09g]!<F-lr4{$vԀmx7a"SѶ[ŵ*E"C $/=.iO$~ n9? f\YS4]1n\øK)rfýwYl@YP~UBHjt9P8/QG .oSpL*a;M%_ օQ>baAz=$]k^|ږfk2 /⿓*2ףJXq)`"Q^:j-0Cj )K{2heG]Rmf}>PۍРӆZq HNy[,mC_.%1 KWVM+BlEjq?ZiL+s+˵$7b[ߙ(SՏ71+fP1V0;@̝fAMuRc C7dw 8 }nz0.k y:mjQtvT˻5%)? `De WXufnw{SXÐ9[gӐR7n_+B=s53DgݰxgQ&jDc[k sdZmrə8VLwvʢ沲O zIosmoRCQֺ֒X%ufRiYmYE_y a0Z)[FՁ4Rʳl8]hXCkd;u` 0G]5VOi}欺<:d 3"wWÕDL μJn--T:əO6V-[Z¶)|zd|>( B_sSu\M@O,Q ˃ҙqڽ$ANp KY>_XeGܶe;&T9 w WA ՔUA6ХHF0$v!Yq阥#ZoW0}N?q3`/ {SuwNk+.y?rA.mH6tA2@!x&'Jw ]oBY*n3=,S;ȴU7Bl+)Jt^G~]G*v|z"ƾ˾%}brw<+ ;s5&l$ T\}`N}"OgHh#bz\'fX4@9 ͡x?Oʺr[`k3vf|(fUqTZh0 49z| ޷LO!$&>G9bxn2RHlH$1IZ|pu5h)jV p͒B)PFቪ{%"~vyV4PUhB"PVRy,MeSpr`*'ӫ_JHCL/A%DNG< vr̲4o!"ez9sf8/rA[Q۸wlG 8ٻ"v]bx=ӿQ_.I7B2˷ɿf $ ;]Bkίq>Ͼ3g>7Ëߦ(i 4/Ӿ :w7kA$89S=D0NrA"ZD m-ٴܤqM[42N;o$2q .tt(9l[C74A .l`ڳG8k#r*C:jRۧbBL*Nbڅ%yq\s' iq%'P==^Tσ wţ]i 2Y3 ;|ؘ@s"ekŗBzf XOam.ݵ % ./.gQЦ0K!J oVMjHM\>pi}9l+mHחƀoRvyj'Ki$b c5}s 3*):԰s8Ƹ]n1)6psUsp,"yK,Kb['R^ʽ*/<1PV7ȋk0q7,0sT1ٰ SxWä,9(~ddE)܊CK(*s~ݰ&V>EA|Bl- RV\ej(w#T4E9 #|ZM\gi$+!88$r";j6om|`o|XF$IKc!˨kb? oANLӻ'V)/ D$I96w%5=t\ Xކ?_ne2f$5@ d>!kB?&j^;"u%E l.x$eB0 Kh|J՜H@gH0&4ELn{~L.5ΦGd)׺I'+?=I4B˲?Y\pC-j5gԝ5?GLky -2kH<<jMr^zP^( ~͜f޻Ϻڮ^>7;sTdhpqW;脈-JOG),(ry">&zɕ5 )6:WGR;V{ vb⟨5("¿Ȏ9zMə1S'pIi-/{ɫ"Wʳ"h@;̄iDIsS6EiC=5!Xu8Ӷ2)-:n&\F;_œ؄\ӻF|1٠y ż1lG$q1& |&j(MP 䠵s]TeF׾}ɈGUV9B3 &,Y/rR(J_bk@Zlf'ַ9a7EUk`"=A g9^ _gjRh1*cզ>fl=h"0pޅS R]XAٿ@$oDaE""uTODcSF8o@Tխ|{T P;uCda1 I]BXTs'R g@EY-$9p1Zkq2Q0&qΒ1ypBghg h"z̽i3-X)76I]նwP/)lEHP#l0 /zގn35џ28kk8fyM^ V4R"=zKx{QD{5,' }f t%0"p_F,I.i / ڙ#9ʔ75пXr% ~E#-7ˁ;X01V(?aiӬ1y2wv : ?9$vJ$ kY;~5#*W V[|'6 {k<'ـ3d#Gggw|*~OdtHyb7̀+SRPt/ʿ/gL0;3qל7?¹/F: 'Px`<1Iˢ;vAwJ+T| ^LlήP_֪'n^QTN덠+a5:>B]BxU^sD>%/ћ[/ӗOQ8Hya˓㫢3%gk(Oq*3 iKz4of*UOV$ gܠ=cR"]Hn7(f # 5gi\brrXmUEZ8 S)(n*.8d =e ն;2M?j/3VCproՅr{7R jޡ2 !kv5VȅRaZ FEibW(!p==fm{SuD,}G7n {3%;!ǸtUZ Rks/Z' _PQ__Umo]dQ`+{y"8lm%lܞw&D Ok$j.2bj4_ 8-+g |A \> 0V5ީ[+6ԙWS}Y=s7V)2*+<[v:8mϻb:*q=}]0aM-2nwVm7BL?g^U]8g1eНS:/vZ!Y*=>[!`@(ګ*üKVaI=Q{'Sg^;W7xp ᮝt'w E \Xvzbz騅i!z$<&_'5 wUl<V77980|"0 pKM414Ҩ %6ULaa>7\ Zv6ÕÊ38X"}"@9ݴQLVնc؟62-V: *C:}P8^M4smBRB{<[Nh}I>jiA"Ϛy\NKZBxuգ.x6=jwк .!c`E#e(s8*G"nMTM gӢ9. @b:a^D#ŗ+{$*e \-`KQQP* W6*rZhكY;Ï]]Z nh~\R]^D),)0ue}3QMlU>vm?_XvxfDT'o"/&P/GuO@`7Z/vb edz\_0/Ќu9dA:ۮ=w[g:cHNghR,5YU9C:n>E_p>ۀ%:ae6L4jS6]v %m;+{1ATdnf]w)*@Rʔńgu]p':v9)Maac1i::LHG1g.x?esm* Be!BwUva4eZ,#jAVFnyXUoD@g%4 Chh T/w6)A6>]߲iG_є|' j)Uu~?T==l;:W죆,n$ !L"K,wP>Axڅa|Zo>s# Ը{<2~'7\`7hE p;0 PusJɐQ}TuR*$w_ޫ=H@wN*Y%\3 'cH_lcy<tG*d,v FH7R3mh+nsCNp700*t߁Kz(2LCcU٦hdW!ŋ 3m8FZKsnx*WR06qIg0‚A.(5᤮3hTaR\AQ/B 7ʍݟ$ @e?'%Z|LJA7>}IOya>Q_73uT';y .,'k$267u%86)d H\9zfR< 0 n*SCK<$r[sw:7 ' RZQO"N`")iGZQ.rjwqN'}F:Z23G<ב0V&>3ű'tU5 0o%斘[QV^RKkE=vPo>U1mi C<2ɡ s5o:7Kap^i -Ø=]>սЇ%4Uז;hiu Xʖ5tG9h6Lpdeyk' Aig*B8<#)Ok zd:)EN])Y[y^Ѡ֘)cLQ(/-:[ó4YoݑIwQ7Ykq*J= a7涱|;8 [*ձ"64]$+rcI(&BoywD^P=C{y.}2 =`›jy% 2U7;pL{AHPoqk ˙#)bؙ-֩r:1cRVUT밑{he9$6tC޽}.ΰV!89ס῰AӋ'qֲaIy֞]?|j ӆa'9ܸ`p,P7$۰mvjʐE(̗s^h#E_^ X7fG 7Z)KLHƙ9GT.Ł3;yp^qFLo.~n:.n>hu)b=^$?Iш͹6X& MR׹n V᝖h{Q'hgl\K0-*SiE W rBq؉9-b4<ѫ!4U#ݷ-cGg"`2=Ӯ5~ H{j`#gj.P%k!St͞)Dsg<^i12ŜOyo7ʹwAT IDg ?"#|/U;+/rz:Xvf|<vIm$P|OQnp|+ۙzkEA8sޞQ7kʵ|%Ғ 1c= ,"hM` JENtM|%É LЄ1EbKkGKDT$22 H|ti $8 r<Ϗ]UsRm82Oh-۰Pt<-6~.z (5^ޭC3ߒxݕ2X1Ơr_e>I"pRj4-C؀=kgMh|nSsSTl t!mT17ŪA`ɪ4`Wo_8TZjGT5˕E}oJX+ +W+:0PǨ2Cvp<%+Y)Xc_(ElG*~)hBh6oԗ1Rcy=k\5 c&E q[=эx8d:nv~XB>4[YCQÊnfhKHCK۾0>u_i|@b\NDn;]U|#z}iDd,X;(e6bwG_k Ź@T#R ]rdk]!'* yY73vmN j7/B bJȴ|Oчrzy&zzS xó96PM. v'g!c ^R:z%l Fd[[S mwr@F(reN7ȴwZD]ul/4pit*KX xդ`XzdA=9ԣE^3":r ~P'-*So^$}[MGI\LoVή~ALHр0F N0 5hJCC$Q Z l8@^?2b& T߂_0 wp+6RT|"՗ܑ->Q%(H|f>v$;ȥ.ZjUo*]!آeCkye xw \QSO=)J +9՟t}fj E!_ "Z\8ӴJ-H: ^wDC_!A%d\qşDAwz$ Jh٥wGzgW]7 j V+"EmŃP>b%Y r3 xiÈ?zx^ Urc7*Af\ڍnuJ /:0%Hs XT|G if0 cb'"hVJ*_IBYU #ziX9F>hmeXB'ń녮IB.ɑc7+gjt6KkS=_[N@S~sp9xZ[)[ܢ*?дlx772M)sROL`Cv*#pl`8є抗m]d U~m ʚD1,aۅx)8X׹~C\zIFnB:< gq P+^mkid+=dǟ18K$? 0<`jEN$E? Eh$*Y*nO"L=_/ncWOYj1,CWǪZeymkx%2|SkMgLd?ĩ!?ǩD[,U{S-aw>dMT%"!JՆrP]6aSLǣ Ab,dI 7ʷfR1>k dwB5\k^u78:8&p$B<"b -l =-[Efpe~5fc|ktWmb}Q~"&%$xXsJ&F? Ųד[4_L˻?{;׎s7>jUlWdANy6.U -6l|TfQYRM3 ;SaH!D M xQR;BE^o{7#@P)VQWvlkQD'Ai ZfW>fJY4rsfLޞZCڞpã2 8js˶b|C%~2Dzg%YwM[i[cSbψÁ? nAb닽a9,KSm8}>(3r%S跬X]^>+Q~=i~Rs]$!'a]~#1pD?k^µn7 gb ÿpC5 jvܭ;:j }>70m UND(6TA>V^LQ;$ey0ieiQB"AGAWKy%\c_baU7ߜ{CihU)+eҨx WmHPrGL҂gx]EKϵPiGm?.% D6Pah<Xa|xjN춽vuuIlq!oݸn9K\?AO'!ϛհS,BCC_J6BFX#bp!잊Z+Xx h >orrmz@>x1Z'!`!Zj &6{yUDtFʁ7BGdJ:#tq!LOA@sZHʣW牆G1k -^#~i7R^7N{]E7vfB1r%;"XtcYֹ[) xs;Z`Ծyw nEPg5  (l1:wCUbP{!k*: l"d̹6 188=[a+LBj?EONA#iU}ǔ4Ә< sdHb%(p⧴5dz4,yc78y3 sz5(]9ߐ )MmȥT邆FAfa` u>؛ qt89":Xܚ.{(t "؂< -9/P4.O, beӡ|-=A\H鈇6yw"{|NB< c~|ON{4BtdLF5rKDb Z؎@q߀;s-EmŒt'JQ5wmLi?d\4[s`O<qR %j)~㩄1t%݉AkI]cxYyT喓ql33m)3.HL)[l#b4ҥN5r=Y?F8'34Eiո_7(` ,B3$a~X0D>E%CL^,K,9< ju)Z\go F* ^6h G&(sAXl848SeK 8{M߈zBE)nHUxnRS~dw mvH&i %e KC+ nA7޷Vr|6ǹmq5>@UGc6Iŗfi5N@թxeeF;=;F%ޙR-*ÚVQ" +$5d&"OXylR @!wXDrftP6H=ȹO?ST8^)@*eY|C7$E^<~;a۔|QT2_`d\#<*]`b^ǻ4Рr+~wˌn5wxXG[֢?RP \:1Ԗ֊MW{3(ߎQ0zCGÄ&IVd /)Y̓gؾa\j4ۨ (sv$8m4M4?Y!8^6z P77ԸkpKǫ13ǁU2CЯ6n>,{̄BP[A-D Coo|lﻶ(E ۹=sV?av v ݐ Ȓϸ+>W/oYv یSB8 }􍩗/<KF脋9HcO~)}= N ݡ *g,9 J{:I4d؃$8޷Sr >BR&a/p81 w9ޱxjhD&6D:VX/KAqOS`^&$BȖ XkNj  "*ciDԨ10&蚵1Qnf6 V$o5V,R5 εb Ϋh3o ̡MQZ lq.5Q3ZeEBλ=ɝ8׏:z!hyӉ!=* &S_D+Ԝ>;XFez~4H- EV:&'#t'm}GCL6ܑjE5jة#$i7Fd6b7z@p '~TcxU֠7NtױxG"b^#rfqC[^ͳU;vC GZKU!N6? RcY VT#R@20/3;2qH} (mceZGRp4R5'Epq'??G|_hYnhB@: c+>-7Ƙ KP| e^?!١y:vlE=R|.iG$j,VH\꼥ֵ'Rϟ]K9 ltbyGj) £+M >w`iͮLB\ TW0>8P(茆J4wɶ ɠ}V\ss'_/U Ę|R*+esb몋nIjQӔDjkqRuVq=/c2q":{N:9,ML*_~eL1"P%efn7?>_$XP=Q0n ,g1O3ߴ1ntSeL^?\x*ˢJVihl(zMC n Y5d5T%Y 2<%)T"FALgkdz.ص!}ſ/b|mQl.uIdW;%!:u!aDy!w}SQ=.n]PVr}d 3iS 5{gLfnqOn$ ʒ idxgc)F5!8 D FYxSbP]9xGg'C(5R7Sj GB!+/?xT |8&/-Bu ȯ5{oDNlWLly@sWĠ=8liLD0;# 6uӔ_Mt+'<}wg3c伍s8';$E+^ !j%\=N>1#7_57mU<ï}$]5:m#Ʃ hFCS+~Y,qTYϳ3$~1&鼤7^H<99Z%&s =`bG+I٦lɷ[ ?ˡoȋъE,`dgܹ;&|s]6~Ch՚j"wf*NcD<3`~?E }ό$9uFk.TAݤ"}+ax9,ʱ  ˫5:Ut% S#ϰK/ժowaDzjH[Ru]C|ZC!V`2Rtcǐ6X?koIL8:2 LbQoN "wc2bwK@^O>#QSѶcu'"1t*&['%}(VZ:3}*w=zhkÃ9!6. IݶP-+8\10nUp-txtz|92Ӱ(B$MlF県S`zh``$~\%kC$UjC]Ԟ\$UD`AO?s)W:"= ?ѤIk69$-Z-V3Dc" \mscPF~ԛ_ȼu< X򡐛ԄB(\vgNX]i0v>2\g r=N+ cֺ-moE_'ԾiǐenfURn4zdqzv1@PwYy%%gPHGլYcKmj%"eݹ^?,x@"WJ҅܌rkӤ޽ }SU7-?(\QURj)V{jt!^rv}94@CBE'nHtJq[·co=4b$ x&:eyTi2VmBmt0,`1 x=HLl!tAv?A5ao?"Fe[ۤ.C|iLYpDk靇uZ|;O K(vK5dFK7bc4ge26RYٝaHZCO.Srܠ %s=DC(hz ]CyC/:m;Q-r s49%*Om8GO@](_݅dhKYE|Y.NfNٍTWj~WCKlAFQϤYm%TUOq eР ssA UgƉce<$Zh4!SD \:7ߒ8N{42 Ĺӕ ^1by+-E zgSңGDJ!iJȽENUN|ш 7¥/oP%)ưE 6ٟ讦_qHKgUFqڹ_.RϫzFTM]Dc2$2}C@B6N w ^@Oc=lbx~8Ci=;-4۽WIhJSiehU%NsW>yHRfHjԦrˁtk3>pJi}q z JK[%'t"8p]k~cT3Ǻgnkf?&/Nk|yhEn}i a;AkZ...I&a9Uyc!l]=fM `䎡H'#8pAiNWɿT^\Hpo D,{9ϊ ==xzQCTAIYaSKڭ~)x|]be H#Jidl"ąyJ jKRd[ T(78FrLƙ$w*v%3h?O_5-lUHD$F)Pk oJ$2eS9~cfa lr+!o[(+ Q;S2g[!(Ϫ[bkǃǻ+ff& #یxR}ZoT8Kw 9$eN#&pc#Djg -+ f#{?*\ewꢃJl$z o( !⎬BX<9rf|aaE|]DxL[^CdAc#G#Y:.^/f( IRt/sN#8Ҍ}5] _q]րC],K$.0xLаApI9^6Wo I LEd:U`Rc&Τ>F's|,5ciEʔ#SY:jFƜҎ\r|/qAն"[J[| o 5+,Jég O sEhC*HN'/}Z L:ߢ:_|zO@WM9].Y1LAgr@RXvY[ڭR+t[ BS=)d ;*lߵ;Ľ._>KfBqxY (F|0.VW%e#*jh"qA⫝*ݳ#7¹Aޓ\;,˒7kb؎oQ-F â@2IaZ.Kl@02hA8*p}: ;Y=h8XeG!%Γxw\[ UI'mi =D놯,6E"b- YA;汲a%ѣڥ/ILʼ<6F)*kXN ADu"`lD~_ɩVO{;HVO.-cJF*SռLlLt^B|ڥ8i)/qU{bJy O]وn֟DC$=r4f~Imu W-:t9o3ٟi3*M.m/zH({'`nZ^vYV-(b>{UL,/ 5NiN r93:u%+ |!;f:zh(h+>$2xs:LSH r:JVfl;qo)xTC 7k"^v5;:`$2tǎBhbD13EPkq?嶝.\ҽԎbWТZ4o'밇7ȧrq;1v`亮}J\ԻzVJK#}#)c]=h^ɽxy),M6gon@Vؼ)nDIOcpP^&k\ jF{e:Ol|\4s?,J+*X^<$S 蕔=&#Dqrh)Ҳ"*B-P#b||+HDcdϦIJ- ӳ1쒄|aF;U IsdHutR҂2n*O%k3SJ_R\󥿺UEmo=GUh캡(Tby˾Mt1VRJkBT7iؼ2=-gM1x>{nwSffტNmD5rnA3 }voRaDЧo@7o$v [I9B(xJ =K*wAkgV)ӳ(g%%.MiuMz.f&1 A}C=7K.,t.a5,P8 #[zdPT}b )wG{QWɴ{]IXv=8ypdvK4ݤ[E#עXh78*#L3vG㼠n,񾄘g/3_`z$a @=l(lX8.YZW#pGnMyK=Y=ҠƬyl2 77VGh7WBزxHgB.ӎI7sچ|3]~"t$b]Tg 2jT092rӰljw !Y?/ C Lՠ xwrϦ=B'(6]dD!K y|=O<*#̊z$i>2X 9 DlLaA C6Ql{|.P i^ZB1;!tf+Pr<2sok1D<*YWJn&~?5EԲP0qT[uOhwqF8#kV_qyfꮉU_c`"ⶺ s'Ee%=Jk;ċJ ަ-McڅA[HMUo@$ CO("M!#{#zLN ыYP%O*J`/GN42(,cZ_ Vq6 ?A~@_.Xatټld\6L%ÞْG" y韴 W'Ôehue"PU.Oӓpɫ-}-Ĥ`'JlKF96?Stо(o˺e'6R0Tڈj:0a# 1fCX u;5E2BH|l "W<M: 6ZƘckQi;[cV)d96V/ؕځLX+/]r%X8}1.E 8Ru]۪Qp[! vWBqDՖx5s_r`Dp,ng& ̒˨u&%O/Nqql>FmA.ȗ1;}XoAX(fPHXy\s5RٰҊ a0L*+UM>¦uQa-A"*Z~^CW'C!!tFCc9 Pu@XIn :=]%vIt(#ZA[4X2xdAZz}ӜMzQI;#6 b1;?m|wszN:nƩM1 1%x C\Klt݌uqYmcNKCZ$8/bT;)#,y{cރX 9xLM*õsmFGEtVsqE YF\l)FY޾1OwjNp[펭?^#̚k("*="Ǭ%pƙ{A>BLw*T:[T?iG.O*Jz`|) MSC|kSV,!GFi|3-1& 7,(,_t*脖5ԔJ6k[e.[ivqP@s &\wbAGߞW@XZS] FU]0]NǏr|Us8,[3 |HeJ plmdRA.0\%F;gBv΃+LVlB:В ez$ >T2s'H/(ԡA(9X糇lg>p1qõHӋi1Om~i{Um33hxAuN'.`j$AwowSS`hqg  C|WP<_OuyqLC,%I[ceQ^C&N@2.=C iS@4MQ/pf”ÝU8!`N_o|8WdZlHb3 p-^vq#T7F~ܗ*\x62s o _H ΐqO ϴd|ef;,B,OqNJ]t޲ c /40p" |٪b MEiO=~}h:98age7zOg@KM!.|!5 m;R15QPrKʜ;Ep6 -M!@DWH"yA5[-m;z?)Zi(*~ KǶ i2kB;3I PBxTݢƿ>1=V`T99e5 :#=FJ=Wƻ1:J%83QLE `QÏ{i_<{:] [Ӵgw~޾T tW>(#gvXL4 Y'Sk1/I_NB-TcM sԿ ,V(Yt4o-qNzY՗6{|h!qlN8K\xU.,5KZB W&h6UUB5 {*hHC&ZZrenS݁tyg]tA5 +Bvoժj׊$+'=.:fʖ[aqȱ2CVO VN' L:q=œ+</L-vλ7ڼb3EԲSԊe%>BC9(4 'DHH)(! %1Kry>X%?Ziv>sr>ZgnIt? BVW\MDE`*v 6>{eJn[Қv챬[*suJýałE&ܔߌmxS[3>a [:M"JH;REݷE-Vq ׷nV:傾@=D N>3V(da-rKR) gAg;:i5C$ m՞YCIУETWm MX:2Ă hݒJNW9 M֟DĄĶѝ -zij@l%B>dy,u۷\dAO9#ڶ頇ߎ&'jU`X`p:ODhag(ހ*=jR%z#Z dwZ6't/H( *.R#[h=F8m &p^3hL~Z] ʠBr8IG13q:c"1ЁGTI *^GIMG}]dDkH93)flFr67vx%n]fxH+!~@ R1W#)mwJV_aLs7- $MLN#STF)e %dXk?5| 5~zSul}gy^[~i5u ?~+(I5^.R V*f$Ɋ+;>ŒȽ## #K%v[;s Jw2+1װC5j ZL,sęʆ%iC&ނX.ye)*| k_n /*p"Fv@{u xw*H=dc)JGy{1wVgAwgGx04b{?bazVNp=VD̯h ||uk ۑ#j૷)"`: 휧kq5cm\F"tW&@Q*ބ(pN'\j6 YhRxp`OR,ãJnJO.be\3vjнz(-v'Y*)H%';D|4(ڲTX}PVE(;3 bW˃vAg} t\A'C ]N4ۺ/ZOgs.訰gtx~ & Yz퉭c`O7_lO w^I)Uo#6A$VTN'sjn.]) ʃ؉lviO*/H1C% 3Ɂ77/vj=ɢř~ӆ/J.fLdcm1dHދA`Pk{Hi> JWPEzաyo]?Im>ZG'G_o?.Uu $'% cO]Nty3#Vo2 KlJ"y ` nTm Rv|u'!k p$\ -rn]l߬|;y@Ypїe(6 Œ#;囚y9m#[=y9i4X`-1j̤KYeHb\kCrwL/m[{Wׂ@jiQ]TACl]ŲfJX;6Zآxam =Q֒f$-yJHĽTB'ZTmh+3][{ QƳζ} %L99,Ϫ,oQ"V:Gč݉-'Xr4*^)q/pB] le%4\^ LL 3F#Dzo+(97;&T`Ͱr IPnrsFZ F ,f4,  8Y8ugn-tEhF34ų&?lK̈ 4"Y4m&<ف%䷅C<Ӛ/*߫ty@ߖǘW!ҫb7(_t͍w=!O(]m-S`@g/H bi}9Ҕzqd+#.K6T+ /y1)?wYf>(~)@\'HͦJVF!Qvy8:ѐ ge2ޏ_]UNj:w:ܿIC6}H@ E*]O65cA];śԊ%[bL\/11(_Co0DRNf|Qzl1'-E+ˢ`Y \r=maFfrpN[& gK@q}8=r$ʐ_;J+ QAs1.C+H*| u;Ů/Z_ A)Z^xςy*w1pN:9ewcBDIw1/a/XF ̳>UVXkxvP8i^@Ү5Z?{DU}x΍ɥ0G8y'G}ςsǙ9)J%81G5Q#JT>FǼʉg{r =&h$T _JJX Dyqs1U\=l3|z a0;sbJ@M1&3e 0ФM|k$O(NxGȒEWAZ3%l#L}+tقTdPsJ R5gQ-j#KPGYIVTcWΖA)t͋Ju'p 31Ǧqr3~ ފX:?sgQS¨C+./3{~:g6C![O-oځ{J )ns.PM;S}pkUh͞$OVr(Xq \@^Bq q#-rӼmb.g?i:so'Wx72xҫƠ'B>Sj֗q5x{&ӹл1 k1 e/M=֨VI^kf#۪bֈS5:5W2L|Y݀0x _/AOIJg{y^U}d.6ӖĭBԼ;J'4h-X]<]'tYf-Owa~l;ш.߸_eTıLGuW5JPc˰D4N_|mł]|zyn2~~ɼK!@v.ƼߊDN2ETP|ݶ16w躴g$Dܲ*8^p?p;M,]W!4M 綛 ģ $s*np)3|Me(mke4W(p Uo{Aga,?/i\ZYמǖgf ;|s'> Q 1ҝူjsГú D3s[:א%[ށ{>lo7O&܅ؿQLcF%}ϖ9qѡ».#"={4 ެ3y{6 t^IsG^`uģaaHoL[ξ_C?mMu(JL)ì1\wWx嵔T;I$3&!*]vh ?%Jf4'Z+T6ӫCY3*IsCzA==( %\-o˯mPr,؊YނNyѰX_[#U3%,IKt_?Zp=?ձAfrb N 5uC1fs"ȑ;ޝR1#zgiԄ$M W XUV@)C!ց gbͨ? >*ɾ1zP.ր~?id2TL 9WS! #wT7ݜ?-@+"([-!o^hk3^ w|֣Aۑf/b.z_q̝U#6vRsl-[J3>9o> #c3N!v qPߘ1i= vXVe4sg?bNQ#E!`m>/%kҰ } x?[/RYU3|&~^ 2(àabkzEpdؘRfj;-y7+B*=21 |Iצ,VqU!8,J-U_|9d֔B` !XU)Ǧ G"yDV*cD~ 8|D^za{)< .l`8{1faQDEw$`aG/'xW8/c:T DNQ_!DYNߚ [I?gwxDʹHnP K$%ಧ2AA@x0Nw//~F:R8W=}Q7=Щ0MsM!FC va'-]ZWɷlC>DP@]6|6;ڵv$E#CɦԒcݛWVtWOKa˓x4vBvowX%v.PuHUBkŁYOgϽG&ނ]9ȁ4,h~jD2=,ˇ涞G$ AV(yX^gS60ej!E o6@5":$ \*i)0I'k 9:k0) < =%%q=W83|t;v JD{c@riL{ʞBD=:&IWt:xZ |9amH d[{Fkt`w@>)Vhjg q jQQ]5 n$OI⌡ pu/=ԻGt[x^ԓo؊ր-jss}':S! 2^saIAW;G[6Z;QS*aMCXFi1-[H gzu?E)Y7b*RDz#aF%0ʕW0~"q l(k |w{lbWgE?h@5e.Xᓼw{z,hs7t44P(iȖ2"?YEyBx޿4c,˦Eˌty%t4HjCw' ´ HEmIyŘQq}/qFִgNA>2JXH|SF-Y~M?YeFv;|ۄ|iIB}mTa 80U>LWFgVY~jF}zU< F fsKdK@NXռy |*%rVBv+HYZ*"8+`"8imK_[tJ[bE&p.R-'9?/nbH'9{I^.u r,nGSq~TU­Z\gqjIEɻɋMUM(ls9 0dK'mYn8Ybb/If_פme}ˤ{ emV<7V _ -$H9{mlLBwM_ڨ zCvc'l6ft Ǫ\27^8!f k39eĤ.h R/G b88!QMJC ^ulW|1$'AZ'm-WtOzV}md^͏skn 'gLNIp3C08{' kͧccVdAYGӡQiG )8J yCJҢeIM%a^zn7E.K kQ&qh';>h$L⪆* M XwsfkhG^.f'xk}R!%g,ItYˊZfz*ױFQ>jUUSZwc"a<0euAZ-ә^!4e+=qHXSBxo Z]- )݅%6,P-.SU^^P:3>$hɣVmim Ppɜlim1^ޞ j`+\#g:pE]%2UxdR WNMePlk"lߥRQ?E,f&$!v9or|FA92WP.#<ڎN&fpJ4UkʅCO\ I̢؆*²_\wKWNE3 <(*Ntˣ*8 ,ԛ렣T1ߨm+1=  ݼ>\፽lUM6(X:Є?Kښ?CSZ{*÷lC_*^^2t´>0{75#vJ\f(S"7Cr+Y 3#r"]gCfE+L!/8J#-k .di 0fJk{!\Qv%?}ҝr3(ݞx'i[oBaY(5[!K/~3Vp+_{2@ҟl)@7ęXڱӹgl!gB1=<\f.\tŢm2]O0kI5|KmTC{>6{t97p ד׭*͚\Q޻K'6iNEU\y֪!H$"נ>A@jlm7پqBUԁjG#IJ}_`-;6w,} {@nt ? ." ~Q[? ~@SnfTirowcϝ`THЗ)]M㛸t,}Hޓر_-{Zo:"HޑGn+&98JRi~=W'fJ? b~ԴRD_!?~A?_.ryHd*釅aēP *sZĔ5wl4a}sw8;R5z.tZ@+FϾ|M2qn嵾UoO2>V_#ѐf'-iL]rx*ނꞵ;`$Niֲ;y~555\_@"@[3\@mτ L z̾LM9\Ͼ 9,jKXkT6xJz*-@S*RvڃI/7%!y"?#q͍-ilU~g;Zj)V}eҡ-']_cbeõ". DYo Ekn0cu7Pe4fu@o<ÀfJlדB@ǚ\ Xr^wVK ek8[e#n"\q)J bCocOgI t,-<99yU7"#?[UOy EKf)h`'%CD٬|j'F#Z"E[ |u5TF6%KBcuw :E;d7{)~Ay5"-dBo.eLM8 LRi36ə}T-QIو`tuʙdzU@!2C]9C"e@e|9sU bmcXaX22HV{b t|AQF F.2GpuYg{΀#OZl?8t"a3$W,"vC6zٽ4%o Zval2|srp J㨲-UiOC{@|vMv=*/&%c إو࿒`8sTsi/УG*4u)o[GGKz~\.EBjVM6zq(@22P8A\("LA'kn»ڥ#ewxgŸ ^ nN~H&ƯX>8']"]Sn7\.+~,Nkzňpe Y W+aoH#/0adJ?&3y>9rM<f2ȣg̏S'EQkeाdAGYE|yأu; nW@s3TY#44S\'<^6ZU]O5`()5?bb{PttM?u}UẺ M`$>N;nN˴T !0ds>ta}b;ov(f7!)E+NЍ(>@sk|⢱h#x( $WcE={Ws*筡D,z^ہ^vpѥ&eqdm 5sj&ۢ Tr6;}F |z4Jx9c^B \qOUIahXM$.k63ngY@`?QN4ڇgs ħ:ƒ |U@tߤtgBV?q$FHE(xCnHME 4yD=\Gi[%Si&Tbr(tKm'9ޡU=k} 8h\.mM`fIz %^gܩ-=A<:K !a=p|!Hs:ELiW#8{[u`TKhr=uDB5.;>8ĘZ3nS[B7mz#.>;0v=zlA}JN;@ke˓: C1$xܚ`/1z)/;t.HdCCM}mqV]~OP<۴U|#89Z*ĺq?f>LnC7xVnZ620s>'{BBm]͈0exa0ëS'QD/9j8BtFm{kg;"Nj\ul\8g @0I aCf+:l-:|!ڈHb_]\xp0=K7d|wڃvfӿ[?jZUZ¨<ʮ6~wF8LZ;˟ ?5i/gm)ŎnD6~Ɩ֡02ZXǣ;nsѶSDw\g9ߺ!RB[>/3WlB:{ [@7r wz [- Q6UNuzubNA^o`(gqaP)v8pI dE<Zm="pa=Dq`¼)`kmdCVQ)|MCݿ޶S> ۗvc] 駼- 2qFNt>:>"Ƈ(gͷ҇&ȦVbn*K+V),xB SG*\-ySG+Y> {6C6P@LMK`oNn֚Eqw4~woJXUϧު9<]#-: ؙTL>%U,uJQoB&nϣSHHh&ŦwtUiWO X%. [UNnjnB P(us&0#_RC:ni[$^R]7ʋa#yF-= 6&pI35kMRXZr arC=4besZb0_E|t CEr]Osr٬`֡?i#QXJ,jZxf|p~-SaL_|n9չuB<acE۹']-x ,_-HF3{CuQUO1D}0.t^4B'%6)!~M7G J{^^ښ~,?9(`n{+H]{Y"ۡO4 x1ph.8(('TL{;NrgJv)HVJUYX=!^_Ŕ~I4S7C1 =yU?yAWP$VL/ܰ>U u!mH̘  ~ \<60Qh+8" `9=Wj[JS7#q-SףQzxJ朳zDJG* 1=k%Lnf. X.Pќs?ܙK^:H*'¾Wey!"@}U|YHn$%X ?=z-82@N9udr+N qqe8ZH&''.ktۙ0;UPiO]qqu)RC HAa714 „rKgL_HHraxLוع!4Om.EZcԴmrXK#\&'[GO1@(-WiRаOKi'։~r;ich8>C4Z=ӚTAbp}JtKHxn,{dgڴKjkk FI֜{Z1WTI\BxW67˰U!M'wl#s&0ԖRLnS@ nez1ڴ鳉PC؉(.'5N4Ĉ8p'X%g"qUb.#$nRv7R/):m9qjCY*$ >/8 _ i.9g\b[>t2MVאajNI+]m<@/Cw"nl5p/:,Qr\܃w¿+p(Uv8 ^&Ƿ$jqHA-do$@ gӭIZM5{ByfZ#Žߨ*7/WP>!^K8lL o3NnbܦRK/Ţ:/+YX.r! 55Kk=AN G s GvF,sfHК\ 7*qGW -1>qkhc!a9ਭ `5]n, G9D5qdfplzUEm']$!S0vYqyQy|:#9C~Z&r?Сdot^4<)oH aɕ{3]D`yP4pPZPZyLyV>YDswcqR&o|d z7H^!spK<"ek+5c1(_ F&͞1rhuX)_24V?eg30;;*ڭ\zh]kocwB*hi-͢He^Q D[7 x#w^2*iiENgv6bm}XC +dzޑ4;Nc}9w$'|`B~|hMeSј' OԤ;'oo#ғV`Pq aqIDazJ#OJO:b/ZX|1i`P ԥZn2qT 7άkb8c'e+zjd[87RQ /ăݱNB:J۶3;v{!m (:A_5Xc|kW3]Qq+]MQ]QXJ<$-~P%1:`x4byR:Y"w3Q.OzR*?(vOóҟ3o =Yǐ:ga<(/ER@%?*7&r^9HHhA_W1| ~( z#YޖTz,&h֙6BR,OV;GT[>m5]Wbik$QOl^_|p䞐|(FR_e蹦YK?cpx~a!l~(Zb0kZK9^9e'\|Юxq# t2/ݡ+76֦]dexٳ}Ԫ0-<֒ ~>%Ӧ.^k,+J,K32}Pݛ$|ޗ#!ӱܡԬCKEnGws9N7⪦8D߽wMwΦT CHz+;)Qt,_`ljJswY 5{VR]_tGp7\ JGrx̩79%ZUp p mGEs .aؕ/ߣ_>BonѤS>T &.A|\2LDb*Vʶ}!`LHҰ%9;8bi|+ YF&'8TW \7qF BCj߂+.xB< tM͉;exV"C6fT/¬cezk\f6#RNJ)+N;zWl"*2,hzY`}fsdmPN5/yYȕyP.4Wtݨ t8gQqB6D۩ŠbLoW+/ 0knTG Jckjh!DFY -J߽Twj$Ue˩=Ti4V|܊6fw*Z+BJщB- 矋kZAVG6z"GE-q4b $!lgK/:w/0(F X5CYbF҃ܦ@ @kd7/c9_b5hw񑉱 ԯ\ER*6tP=4Hл6^\zAlUWCiQLEoD0|@k.AӅ4EbO0ły0)_򼽞V$Mαʺ݅9 !N9sN8ujPӧet@2*!PO"Kjb TL!y{Ħ8(jܑP;l?XZc=RBFA]K94HEVsj a%&PA4֧>KxCIQ5@I(k-mOT•#O˧,J2|-c 3Pi6k'4,HBdl(#|yn*S{ Q#Fh^ۣ7)_ h xy96+9-]̤Kv~򸨸z,syf|xa~%jtZt_R:WJ:}}AOvAYUw Wi2bFtixwY_8 ?`PfײBဴ?3R _'܃CoIYOZxe#̵gt<>>)25VL ;'-^ֱ[dM QL`bF»KHFysVuݳrC;oiX7O'^6:h󹊉:8L28!ؽg#z('=m) L/b$:[ -v~5%J!-= 3va)hm7M{FGjgS:#.Mrey^eϸԟ#X; 1B`HpmG(*9d3)4yM%^=W8rڕ "nqf,[ iҷW@KO:\ይ&b2Z 7M暄ʗ JMK QЄ$U\dE[d6}0 ]CU?6W>,2䵁!IԈ,`кK?y= e 4]&i1Qduog+d4T6o~ݶ!9US3/ݕ\A쳑rsIO*Mm U@zQuAѭ`DHW ΔPyjأE Y9b'ij l?s[||(,/qfG?KMp:#\?WBVYB挆``]g2'g=} T@>ōLk_qU'[bJ4:‚ j1A>䉩+oնk&gG؄HSCñtK()1XH3m>`g@`mO4Ћˑ~d3i96Cf3ˬCDBh!'le:k*(kO5>B\^pdU$쒖l]S9wt+Mhrz|"l ^YYk[炟F|u^1fGDޛSw%{~A"M$84 ß=QS[~w~ x3}`zX m;En8ʞysnEm7T UɔeLEɮ)TDځ_Hbն.ף*''d =%YW8n PB (wCJ`'r'V{oWh<ݏ "{/@cO L"iT3*( N+uBRfAtdܪ"IL^!{P(5_LkἽ캏g3qXq%L,ʑ"c'pӣӋ9KhYoWiV#6`pnJ9m- $kP2Hg-jhLX9.٥>@8RZ3NAς} kym'f h'x[4 >s(3[10_#~<7(0b̤lmf/{3CoH*PJ~4Tr\iW9?򿜇{+`SÐN탍iJj+9dNZi/`tYP!x/T G%A{4| *sřH%Ga KzCzi' <tu7i=NNPᙹ2`Bk2*rC/˗q  elʎ*~_ }b08t_^Ypɖ%p imicO@?e%6yn䁛*lU az v%U5 T ȵpp,ƊF&ғhpձ- zƦrBMjuc{Po~_NAb<1GC vpo^ 1;KA(;|\ j)Qn"ѣ ڢILؔg~)U:> Z3 ̆ROw_^8;hS*:&L~>y8Q1z|YshiQpz.S`U 6S.![b.k|J@ZXzw IRaoVc,nbW("$1U|dOP<{AVջ^P8x;9reOj-eBReE?BpCO:1$;LelBJvV ^_˂vW浻T=3q+ѺqC2>d㐓n.J:͏f=뙆m%&ey=% ?rjxB~}.LE:lhaHF5T+w$Y{{{Qx[I|G,; Ԭ"Mte&|[-v $[M5L~;u>3\xp|tWyHJ_5L;܏j2GDޗo HkҀ<Ae.i_/4L3зy>zn :z:Qs ӎG&@y$IIFt/3(Gv#8(j:!tdϷe+[ѾUӟU2Č^Mm΅.8^(Nv(wJT)kn|WQȳHq *˟۫5T Yo& dݠ;wKNba LM8 43j]R{}ze'ުF~eg(G- ye{/jX-!J,7H bSgi]Oבݛ̔2:lSg6!ґ  XZ+]]-:nZGcq:ȁO'O+p{LYԼfDIiB}3A.7Ts+˞I ht;kc|CtRMN ZZŚp#^4LVqK{Lyӕ,lqTk-d] (hӔ@3+$ ,k?A9 YNx^jZH%u7R;@hǻ9spsm$gt1 7<罹[LT?|\==wZǹbBMb3 &+ʗ{bq1Zw=@ѦԾUBLM, 'L3% ^D+,B7Jm*DžjD΁B􈏀v:,SbJ<"L4(mE6[+9Y&8)/mǗ:W2u ~P? `i:= 8*D-|m[A !At7d~wq'Bl6sX*`t"2 &Ү*gxSc:AaWNC񆘙Y3+a¥03 SA}<|oO,P /+9=9r/H1wܒu~9:SQ{e9z +Ajٟ<` Hlxx횥T%\;Tt]!.OȑRw\?UJt N*5J#iNYZ([PTLPhr&]f8ĤpvW/4+)'tξxGntz6=(b *҅}v"HnF%f3~S"D PHA:T_WR]GOӷ׈^HXP> *5L8jHyD$ȑyiYVZ}'5ic>rxc}Z\UF (w+Tʥx\7U=?,R~ Bu-eS+>l/IC\v>SZ LaOB@;]4:{q#yLJn9q `sB}+\,;o֜6PN@lX3E!'=:&hz>!}a;WC4U~J ǸJ J蕤`@w/)i̓*UDL9Unn8 xwbP>1%>|u5U e7 SoNSf]O*gDpw?x{b5 jr, Uh8Ǥ0s6W|i:UCDaCRD\0G(^>12Zoer02407\I'6$s6f*+iqY23Ȧmq4Gŝiv#:Xz$ƾu*ѣn;OCrU3j =O`PwDص'\,1I%2&M)C@ 0"*WsTI<0\{zC̵աSsSy<ͭq.(x8Q.2_@"s'G8z][S)r3gL!v%QXuugS \'!(s(Vsw9?kA[3L{'Of.ν᷃;v ܰ˷i2-\O5pnxǹDX?SDMH޷Q "*!Im2Ҹ%c):mk+{TaQGߡpT3ZEtW8*>tWE|RR3f3A#T &tVwX7J~ֹ4'~eE0N)q9[xp~3{Iikc %Il?Ҵ <`Axx~D˄ »qFODt Vp-w9bDDa,%!x'v<htB1SrQWSR&s^"x-G&PozpxYlpL`j,=>CQ!wiM2x N|TBHwUDbyd+_0* t]bi!cinq{`?L"!>-mSt4U-lW d8}4/a(&0*kŚ&M 7P+9{=d>!/!*Ҟ(L癆%=ZȮ;h?B=!K&NmQƍx3{UHs(m6[^c1)䀅q`K7-zm*߈.FPɼ>O tĖu>֮1O3ۂGr0Ŕ$`gҹ}(#Y+ \. mk4Aoz_C Blo6 S;,XtdQ%]ے; f51A/ak zcRc{"Awrr8JPZa& 3:Sh\ WsJϯLgvjոvh0 +k2(lΒ#OVrQt[ Uv7mO`u~aa (Vz2$ <|묬"&N!؝lnf@8zi2 ZNDN,!+ UiU_ ,A -8m|7C( 7CS8IƷe: v\#ؤ P>q7܁M*h]]jWN{ռ%nXRzEx,0rD<ĪE$RQ,uqi0ՓNw i25 N*==~FZ?Q+jv7kim 6cCW2߆'N1قDҊsr D:y2ޫ \أ5x Cp}ٳC}X}vWu`# Jй~uiv9}IeLЩv t#sŪvm:w,S`|Fpk$=jCz;v\xf^ I&xͤ +zW6n :@CXׯ5,c~W=_+Ot8hg-Ϫ0:폎۩^9 wL(1=o Ԗe}2@V?Mmh>Y|aPʹ)nݑyIZ -LJZ 73oY)kaSAP֥@pJKtI*6k,GzSP_PūTf sJ\ Mwb\qL:^v ߲T3a dS{l.8`Q;gm>?5Y ~ =u\V4ӕڿD ݈!㝈֚ld P%fY1Gg!lYT\{G>x HlmIJYtG0`\Zp|UU.k{[fF¯"]\#ygNUOc틮=H :G;Ϣ 5ǵvfq2KLO+T&1}ӻoA!'YL/bNuZwllZ &gDFe,1hC2,P+CszjM; jEYh1W@ݠwlmR@=vYw? Rrq#ezKa跥EG䪡4iDTbUb_m˜$ZŢ4j'VsIv]H!h OއyDaHv i~[ x|F% 0O@ʩbc/OZcg*Ƞ^<^@qh BPHprB%BX-G]8GX;4Qo]3Z3͗bz* [^V8Dx՞*(wZ>u=ֶnܜ|Yۊ(tc< $ea\iN1\Μ 5!A1yJmY ̷e4кuDa qKSD%?n} )\t?"];P^dvx}"wQ;\/uGg9P?v'L`G+jj$⺻Fl֋nnTӓy߶gQR𑰼;4\)Tbݧ_N6 tDc[]inc)y!>syw[znG+-SM|oΓ&()rOS[/<W^C-Y:Z;e6CҰjdM2T@ lŐV{m8~4\'wLNY|U`R_4Uqѽ,5/CCj6"hoT:\l:("[x 8;DMɤr霐7SI;QpӬ,b"k(%Q?W_:`5r#z@ &pL&,BڵOKrrkNnTsI%u,?\vy`m}˽BKOР|k!ʟTqȞA#֩%j^G}{'s-{)sS-P;N,NM]ۆ*ca}qj|D&xe$5wXm⁾`3FX^5ifqk2zPkQTy()Rxo)ia:lou[Cn:^jCo$JR IDu[Nb`ޫ'R<> *MŦ5߿^Jy3zvR@PaXs7(!zwWH:8ȷ[FE(? ""‹ !*ʸ zxW;$E?C^7)` [!l Q8Rs!BS:XҊ|Ĝ>*PTVqfq a5 /^󈧗fT!EhLxRC"ѡ ߆j-M4:l''w+ ÁrX7 je(SnӪ!WIBl10zs<DV=zynq} ^=^qmK}5sÉ9lOpIG%4*gi0+⮟YC̀*r K @/;˙o—5xձP0H;|i]|{ ,G*z(`_<}|Ӎ.eaw™[T(~A2u֝G+B8AyߚW`aP7۹O9!G_W=:veW6b(5ʺ?tA|{m m¹skèXUͮUSe̊/Df]^T}kE`n@"&H>X!( |Z݀*Et틴4Jhw|7lDB^Ơ?\FʇBXVӡ3T_BSOZSd nN}&w׀'mN먁a* q'{U]$0sٮlVAZ"uF]þY9GDaڥ5[{.:}窥U2UaЍ3(gFڑ]f4x}. 't6wfN͙,^Ft)5l+, 4ZRLt-7z ~pbah֩`Qr5x,߾s6P%C?qX@e\)i=< f|5aT~N:e[Y/հp[tkJJ xP/zrS,UbϐCQkB Yh6|¸8p5~{ }x\eFo0J]13׆V/kbvTtҥ.z^NŽU+6]CM|y4~#5d5~DӲ$b@4ॹo]-4U>8>V e@7ԕi~Q3} t0N=7`E6:rkEKE;{Vh`@Ǎ4718\x1'k #Ng"Ab'+vm Ad:2tA''kpcr22՟E!~'V6y|S37?֖R9ZF?cn1dI gfQ]zrV^v}mJ/^|EsZN85J?ɽK,/V֎8qꯧ_Rϸ%_s*ٮIi:J7:KR_y{팦|(}^DwK7Nw bkj4= yQŌR$+}ȹma8RC2/91R2bXL;LAQ? `td|+s])e-~ .<<%V'c5HJ֖=WP$^p], <tU{+e#mM> ?Zvqt[rR9%h#G8ة%RQR"I_ FzΔ# vc5hem(ߐ "na#Ԉ (5`*g@뺋n.Y*"R L&!! qN\#orswj'G !Ӣ`* 3CZ%t 3eT/$+0-< LimK3}\)wSw<l*2lx'Csȅ!PF%lieZ 蚵gs^4*MUv.0 Hb1_3 /ϮbfWAy]D!MGd I"[rhNQ%$bAʎܨhgZXu^Xwd1:6jz8DU0:gq.fzFK?9;ĢIK:]ub/ow{صt2C{v2ZtLa=e` 2kJCBv1<ʱfl쾔x0MxcƏJ W%=KvJC=SQ*cÿ: [ˮEJe0r!/wŸ"܂{Nl cDts$ex@*#5 ,О.:B>%v T Fٓ|CZ6m`˸* NΎ)(#UfZ&ș޺)P3Uqikc4 Lײ'oob^)tLI6`5Ӽ{l3ߡ竚MZ|bsI$Kk>LPɢ9'dJI+LHߘ__VD#>b YS+Å`{QCk67]^5$t <?aD+,gD#h!h1amu/*:ǧ5cc 7{iTLi:RR9{ɝ~6a݅d\v-򲂣>r]Au7 kP{\pZV$b4o]͙y&ZZh"`JLߦpA=Wчg{{ce18qEQ)\@KMTs>˶gȵ䖛 {z=ZheC1f[2 ^p:$war=@ƭ~ߚyO r澰;T6)r}MX1Cj豊ډ <50p©5og=ze J4ڊ& C0@BzKuS؈/ݞ1|+cF+ki Ȓ. S:>SՓZĸbA& V>܌|#_-u4I PҦcB@R9v sVʛpIS=%_tPLciu:8Lo- cz9,ܥB'UI11)TA[rw;:_6`X;Fi&9*oNS' ևH2 {4ѿ6PzL1^EX7ʷVGJ%b3aم!j_ʫ663QOqPZ'}м_yo/gA>nT S5]>|skqbϩK cWL2Zh>ŏZa9/]QL?n{_; γ( ϧ6B6^\+T Uo^)qw: DzfwOpD=~n^/U5LٷA&&[Yt! в`2(Tlz1\;4u ʃ9LfT_̤X@p*iIdg'nWK坱 V[XG9şWGH֫{p뒔xdBɕ&0,0r*qVaBFlgytF;L.ʔq@WR9ëBUY,j zTMxG~RfK44{R(m)n Nҁ Xv/fhƷ5>P3Izˀū?9k@j# StxA8 ?Eig|5)(=Ỗ{͡,ͅ㥞6ךllV@kS \'l-U $*S{u lU {^TɏvjR}-?U;W? {Pp:@±,SրT.w7qތF#jJj{ԶE3 @`XS8Խ+TN{3QBE-QyOtGHyaĜ8[B%ffܖD^\W(Υ-MvTT>4K7RmN 9 =>^|d,絘q*Py I!n=ǁ$ l?_l2Hpq+MOq!mbtR\dXs핑t@?k:2mHYD.ѮA:0LW-surשJY[yR!R>6&f ڿYoII?&&v,B3ISS7ƒT;^@(/@FsVjaEP +saE4B < x}loDoKU6Og[\ͬZ.C"N^8l$~u(֛qc(xlr'zoՏtʸT  qc(JFe :p7W5Eh. \6gq8@W2e:1 g~_w> Vo%_Ll[ \2]^\o~is{ Dqj1Y}8+,B ))~DTI5"_QHo €ߡ)fg\1rC(ۦ?hzulX)5 މ~eB̾ h G<NH D h3S{,UDe=qq)V\])GD J]?XztT'{sL~i \(*k"SBY?gT$?'UPuXf`rNgK68ZDf+y @)5&?(ߵvǫL=qpu\E+F[ qpNƳy^{(^B|RnaU?@C+$M5x,Z ydJR-aG=NՁ":O*E~Knʌ-:kҚjzy6 ~u4g}iCC}8ۤ:(yS*(IӺWtkT#1Oxkb5݊;1(M&r/J}&?o;׋VKnŞQ@Su˼W=rvMFH)7M+Ye㙸,B94jo:;04|YD.5`w(xU&c߸GR]Dפq N^o ͍mgMH~5 f{تf33d3l1?~oI*4<$"@X0rOn!y<l]CVVIXg6 3y>֋|PWr~E=CJd`zd..ަ㈉b䊓gx+nb."R4x9ەQ;'Xa뫁]_)9_rkX4jFַEu2-։qzN(vۅD/CzCc;"ʴuA=]oQ_n.N].1ɠY34T /Le d)T84o?ϐPwo|ip"!z=vq={Qp^Иr~4P}lΓmzVBTXv-ԬHM_-X#~?JsCſ,&ԕY伦0e$|$&$4L*;t0U8O_+BUb=ϋ5F(^V/#~y3|ݲ~BUkJXk w:+oT_Fp]uT[D-rAdk&E8lsw/.He)69¿G͌4}gh =`_. .CMttݛlu4g9# p09Joy=27KbڼL2~/x"݃Ѓ7O0\u:?nr`"{@<$a -$CR[רrf7ݢuU >4H-kl據I9"#w V+8%bQ$)>@NTbG} S׋=uAd]]pte3p:JAHZ1>6 =2zmȝ/`͟]8(AP l|!n(i\ln_X})',p5+8 `hIpuO*0?dTXj Ut t}[E]1@4Mn0G^:eL~\ieh,*:ѥR3?wXJTz.pvhߛgekLӶFpؔ]H/f!ZR*xut?G FPwh'f73z1 [FQ$[c Hˉ(y mQtO*5!WW~(.9 8ɦ`S6&L~ᙶ$ ش+&Ք@@%دQi)_{}yCh `- ^3B󳊄{}5]^.TG!W0yvVUS3 ,/^#|Z &}^$e{,ߡG)=?5`tjzfvB&wk_47Y9~ IuCЋ¯W؄)pe8~伜 "4I 2scaoD)t@sVAUզԨ"Z !σ"9񹳯FC5c˸yq.+  Uhp5፯#F+T:.qPPkAM|璕)WDPO$Ed?eW4zNʪrG27>y)>JȔ!yH+>cwxi7(E"|] &錶 ~'4(pф.2u*C…Eπ,v ŵ_ jܘv6(~{:ld(‰lcFh?x&G1H\:~^h׵/l Mx6:,ϻ!J=$Z"7-MPsFTˇ\CX09w|2}G}^"N/Jjo 0DF3e1#%p+/$g>1!7rgq7@v֜ݱRX"sQ $ ;6SeT:$Mzq`ȾH1o.9nt# TY5;pJSB.P##fhwZs(~[ۣ5Wh/oIna|勽qbGJ"k~2;!20lC{_W9%ҸD˱+<4jb.ngʚe7Jiw gE4T<{HMCIJN˥Zqh6%$Z~g4-d0 6 Yɱ mt~$b(ꃟiok+({M@$R{H<|`vb/Ȱح; qǫ"~B쟽m+AT9n]9p *L^{@j?ԧ.z0 Cͬn.-s ظ{2SD~^Snd! eZ(&~"1#ߤ+qX"p(̲m靵T ![=e<&S -XE .n2 Rb}jC6?X]w_d@@ E ϡ; 9Mij ̀$U˺ C6ExV.1ú_ U@)[+5-1);{sҔcM;WUڠ|:lj̍FK^ԭ?!F9pO̬\\49YpYzܚN)=\kS9O)\~7y c"ύ/8=f,D"yQd%IÝa4yh6nAN|NK)3BNP(!ˡٴ8_&ӖMNZX{y~-J%Z!ldhP) [pPG- `~EpZ:b7).:hbI=Uk޻ ](١ͼ :ό)vk~3z|ޑICYZpm5^e#jJ͎!TFnSRȡ=]^E^%xjY Q|-n n|5eQS&@;H1s!9K3{36+ wvs0Pb4 _&> ;mBn`ο N~sX]Q:}{"|sP'}jC^W)S"E\; Z‡:60'q7C *0#:՜ =CX<Ү#F4 'J앚&,zm0BKMB[DAXwQ @un)Ha:݂ɀ|\&97yD4Y5/y#4" l<,;c]V~յ;\7O"nP|BzGp5}qedɦSʻpCDCINe'(qswk[ܨegPq41WA5/^̈́t?eswPj&ބrpk٭hFMkB*F"O g9Jړ<> 7Pl:ɧh {h,8Q{ZMEǰD Yu /Ms_H{mvmdFcS3iD7t 7|IHh|.y rvXޫ`!#AJys#Q{-T+ykw2{kDTР6C#_ !xctmjoc?7B +9CntX{zO/`3.A7ӘVڪ*҉loV`8Ad@bX7KzI S(^_Z8ꏥkej͂+"3f !Ѿhv54%[՛.O5Ko=-eu0YGsAtX Qc/f. qjˤllRwΰ w͗Xv_|=I0pˠ_@Gq]zːTHӔfwŔq$Ťe7/-A¾xzɟsCdp$ ]R`(X+tiE̶.'Ĉ'p$̕= }p/ɬ]J8+͙5_~U̳|,NTeiRѣ$Mz_Nxؒi#Ùtr0(.w5K7AՑy4I*ts)`0!waGW1s.Q{F`LG{ \[7f/ij-Ҭ:fP-do ~S-ZxBo+Hw"Ix'4*v^J,iř}^LGI+!UM i8P51[< PyBh|J U15 HTء}Ip*g0[nQ8mZx[~~6]@R=z(% BZfVB}QM eX۔si% nq,QNJߢ!MjY~?/3)vh>"ZB3NJAlc/ 0\:@Ȭ CYϴTT\*{?XGS#.mՖstVU!L Z& n|5ӵg-iQ/(GZ~wE%0u4x9jAiF r}]1.f LYTzyƘ@4M, fMڕ"{ įZNjB78\tVWib5pZ/k[O!!3}#v垍9GgXv\dC]:ˊTl<D~(G2u+ 3;NJ+>I;)?T8Է?XD|agb*<̛ZWCEa-X1&θ5\WH$ۏ?C Uh}˹kw'MYז&;f~]mZTܱ*Cd@hL^Fd`Z `-gtpr]>ԡy Kc}|&qa§u0$[aS>V=K.'٩<|Gǝx6*:RZ,@_J]C 7){ BwOOkB/եsD'-Y9|[ŝ"1w;n} ̰z9i:uW] aq0O$~\DRw'o]t?VBz:92G{J!#;!S•%CrCD0~|Բ`)CgRl Bй$o!JC!cTK s.sxEIˏӿŅtsB"sDZ)J,1`TOo s6=ڧ~KTl- #.=zkDYl\A~µ&w&.iyqC-gwUdL0gO?GjPǏlz`Nl>4s_-[A?g1Q&¡ԹE]pdD1d%֞O5(w)` 3Zf u yܚ;D"(VEaW,JCKJ%{$ xWp;'^{zpy(BLO ^ JbmR$ϧ3d'3Aeh@LT7?tp.t m%'zfzk"Lu\R ^*CL‰@T!B-XA sF^K!(6Uɾ M NȀIU(<6_AdK#zw |&N 3^ j$ =M0p펇dM@D6]Op/X_t iGbB7י|ܒ՝IҦJ:" ebpM'h+p9|7>өh)`Gv^5^լ+<[Qn ËA6v;24I>-ݎQHEꯙme$p sb9ni2ˊÔNHcsWU}hLf 2yt1/$VϢRTN-.memw"`%cs)#2UL;\0P4TxqY"{X#[ i>7UIELxJシLje' 0/]GF,Us9eAH~jAwSk*nOHC`ƪI6g_vc]P~H '2 .n-6U LqIoHwkv z$?N.:n`L Kv-~N?Yi[ܮiB6DNQi8t9l\͑ފ "j'y>cGT7n"mѝl % gvDuy';/ !>c8/Fê)@ 8P*ܻK0>WJj2 .'Ǽ@8K_i%֣xdݠh4qrućQk65/< țTa^1cPYȈhTD[ zU6cg{?cḩL!eO1/_+IBKgI"AfU6oyG3I>J'gnBy#T?dFSnovVS:v_-.˕"u yz(7dW 6Dv"Z0Z%ȟ|xRTaV|/k.`a7nrzM(p`"_|F\OC&V09)@/cB s|b&[5oDQ;=;-+vf- - YAn8c~h_UmT5@=+U0)>oiPrb>vU߆,p$RWmBN+ Z⢪@F.DDՎ57 Sq]m^'a^U} jxU 0>4M|:8 1_Ү67G*]4ڮ/50g,ϕe6a'U^ Gou`"[&V'CVǵPV2 =9d=#sEvnymncn&C& tT^rg\"ļ[rNh6Lά'pp 6Zҧ M;%v*78g.ЋsWBD(UGHCD!NAKū:CyS<4?V"-0zqmԖf {kGEN6̇s*MAْn8Y阑0Iԇz7\'HH#VKA0i9} >tu6^?.u(M2wo}mR 6HkvU<7|FG~^~FRSɟ6)9:N[ {1 Kh4J@.NSQVxգl&[CGkKzz_JM >dw/!^YבJ`ΗgaZ~UbrTFr7k"s8w@?Dm1 +(<T7t<-&VJ70Y֊-)s,WmB1k+Ψ[pA'zTåIUOWA|P;6;f[˅ֲv"҈=pR[[LcBpIEjpjǪ{ kV{xJB'u_&_ҚCjBik/3F-Qi?jr+Y#;,`!ԕؓ5;afVnݍ5eVCJ}R`kz5/|v؋ H-JS-zv xИq;B`By>eFa1v O`-tjBJx/ Hdk/1tpF_ [%Qe Z"?֛,8Zyd!?[%BW:\eL3XdWBH0D\q84٢79`ǫ{ٛw mEhE+ܱD68#ws!kн_a?jkYtC$}o !_̔0| X).XGgaѽ.Fbnk-ʰЋM6b}[.}?!tW~/ H_I"/F''٦Bjt5 ,·Mטzzӽ[ӞKǬU gIy)W6ө? Ș^֯n%6ǖUNǙAf(G2mQn_V=U֋rhNs<{T]*\w*m#Cjo?,N]MV.n$zSM=Rx;bۗ%$&vZX.ѳ(þIPKt<8]m<߃C,cVVX7ȜQ9{٧ #xN6Dl;(1z½Nx !Yxlj콖JE/t31r^Ϯ/.KLU9mdo2@g@DZ(l5CFIiҼo1# l]v5iFcFkx \f(0 IbK Ko=Hum4i`"P&) hj=!5yX1}pϋj}4g[ DZ7D~@7!/0n& uQ3[@0o_3l/HK웯jԖT?`:j=QQ XHj20q/zB?k!잡@DU]0Z3[Jv?^jΪ 8~z"mg輪2J%HI3S獍Qe f~2[̿zG_Uq* #OuP.{K@z`fIZT*= 8iɗOzػ Yo5":K>c#KDsB}%; 7fc@DD RYVljg٨YF^>>^)6{zx{'x)U5}SE"ÚuMm^NΪ O>*v͵CO/«H#?=l)SϪ:pZˠCk# ,Ti'HLӃy+*dTe dz*{R)٘sa)+ngr0j- IqѾ܇(NoW Щ"txALۢST;Ȣ3x~ϓ(RCĩy`BE`oh)CZnybb~\ `և~<R=2OGWy^7BY--<7c!d+YKY8k+RBB%W#l}!d+ uδ Zpߪn&Wn=4_ Q0j8x@<֗C!Y$28;N"Mń6FYhiP ;!Lo#w?Ӻ!3Qu DJz|v}|JvBuP*b;ޤv+{D,Мx V/qmdN9P_gUPOfp]d"# utDABCiSԸërXRNС.;Cօ8ɬg گPEFiyyDUmUa Є{P֞dh2$AˇѾn-q좛 qBHR 6: TsbUSG.CWB)+ + N `<NI[Zo` Qcr m4Lե3͚@cu>u M !zaA#L*v,,`Xvݔ ݲd=˲۷q]븄H%m[50Y[.yf;y`J/)wȨRɘ;AмO ,~[ǀns`B#k)i F4BC4pW39o ֞ ~?U,C+tHF0lJ8}6Xꪅ[>^D܇2)5wb S)y$E?7tھg8%ci Q# ຶYcw g*p%*$EhqŽRs48v=ͺ ꒩9,)tu,_i3YKB-Y,jUxvQNWgǙFvJӵ},7dJ̶ɛnyb:8)JG %f$7:oҤ \ 'I@; |25b Uv(׽!R3B*y Ļx NwSm1.洑*nヌ^kѹQ8U݆CW[;io'+ J8Q w;Дѩ%;5=J9v՟HE)F38gs">]' k7r;Kw +tїы+30w,A7>G(1>G#v=[!~^?e> A5)Y[֫N4 ~]ӕ"IH*6 3]ܼ,o oOtݱS"ڀaO]LYS]L?Ial&,$%WQؼ|fțy!;E֎E8-Y cJ?@Zlτ|6ݿS-\ud =) H< l$YNkxmwGf g_ic~A": HPu~|_'wbG9v GKqTBǻP%?Y' ao(9aN&|m:fքQ\`jIJuGo4) e$\0AEv2-g*JQ{kddsC~OOag'*D)0]&9%8-iЕL~w?dVwFJ=go6UU._c6ӻ:M/=~,Snf6Ӿ9&1_ XhvihWG/|YOx܂&d5s?VS}-,6k;^\STCe: Ă Z }UH^ 8k B^ @#KѩN1_ ^9-abZ)އ"v4D-fxrb.>yE,'^ݩtEzןDjӠ(˪`G(?U }jr~KW/ MM!W3*列X h/.QT 8V0oR GkTVع,e0 ,g.a.d{o\|ܞ쁑̲Ĩ\>qQ'ͻꒅ<d0mݡFC T>ec0jWJa^(Wʶ,4^.JFwf1Em'1/ D(o8mB5G5ۛMu$#4B%v$Ry*n^f̬(`z raC_ܳЃ.R=7#Sq MJ̑V/ 30jmõ><…u)|TbJZ 6* -Vj hpŅyxH$t7栮 Y'LKyi(i@Xi)jzy ;) 7JCxobWˊb{i^a bcTK|mOd`b9%>;+t@ܸkjw1jdvǍvj^Aj&I9.v`;$d lKcz=Կ+#'R%$F:r$1sgl)@T`<ƅUoL;yT@4,Lk$Q`؛oF wjtdLC3%& @c=2 # P_|Gu@6+|NrfO0v# vx~YVWQٚ(wGQ~'y>}/K, !D^ag`OaQTc LLٓ.Aޣ,[_,N |v)qH'g}>;z=E8A :x]vP v2M$b!4N[y#eypǹd29ppx날-r\̻5'97/]8/pwqNXQETxߎc*hh^ti<(d2T3Qb*h@0arK*6w..qAPL- Х_RƪZ۟U]uĖ',,@7Tj3=֚"yI&F(r0tYfy=1Dh y8gO,vU ^XwKnj xhT4!b8-"ߒ)? o!T3zY"(%@w"R 9S,]ӟBG)~Su_E;8Di~8i>De92똁v'@4EרMTxpeeHE`5^+ "'vD,Pɣy8{+8ўy=\B(yC!hKn/n22}Q +lTQyt= QVI"4ը&$q[RnH30)?Ԫuڻ?az@@ڥ&6 J]NR8:[,QY^e`sI0?ةS@4\]g63t7i_mfi>;kA^"F903C]oA?QУ+@~ct8 Q~ǣǷ֠>|#FZ\k.+<mՋFt'zI(:+kF XaOKԂ,_Y=~+T_)1M{1 H?K]dr٣pG. A1!(69ۋn<37F?vT\.g?ֻITt JGQO 6f' ۙ`t'쨀9>b(c$?fWOr箫V%c @T71^޵EݾT#qW ds !1?[Ջ]Gŧ|o.2h4ѠY 4zqmgNMUV{2Q)̚o M]dg:?REz|l)h 6s^lMЉhWSYHD_,_V7ՂV|F-/?{ fBu%n˄X -y29= ~ctO!q* L@`4 =o݋ڧ}B s_B?ؾ]"2$%:VdzHCVTEu '4n9bzO\8BD% `5aΓgC:[ ,)bOm̶_ tNLvJ8?'&CoEŏGL BPpQCKI$dɷ(-^D_4m{.DjYF*3ejZ~~ksm |Pu0]%A)Z˹3OLe^z\Y9ӛ}[nkEB F)VsDE5V)6C+螩CJ[/%t<8l̂s۲x\h!%+x6ӇXvka77ٞK1>elgB\v C `6wh(Ϸ*C:µӺ]J4 y5QwS| ެEe7VzMRGx鼏mw-grkU[4WZ OMWַ↳Ig7CD(Aiي#ۨ3P$[Pta> h|`ߟk뛮/ 7f, Ura!_@ޢPyGJ UVSs9ݑn(ωќ#iluфr-CR Xuy6d3||)/ӹq*{y`@/N{#rz(G4: ONη?@R}+7ߔ h )XɅ=6]XL M~ϻOC٬ð^M *J3_p>R6$Zoc ۰h7PK!Qn!`N'K|f0CA471Nݓ0ag6@{$&_}+vLRS@;ԣئ׭,-> NL9 HL"Qiu&Nm4nh_dZk$8K2´纆UtKI7MR43{3$a=׃< QLcR0j :2o;0YZgeonkick-2.10.2/presets/CMakeLists.txt000066400000000000000000000013621451017372400176210ustar00rootroot00000000000000set(GKICK_PRESETS GeonkickBells.tar.xz AVL-Drumkit.tar.xz DigitalSamba.tar.xz DSoS.tar.xz TimeKit.tar.xz UnfaTutorial.tar.xz) foreach(compressedFolder ${GKICK_PRESETS}) message(STATUS "uncompress file ${compressedFolder}") get_filename_component(presetsFolder ${compressedFolder} NAME) string(REPLACE ".tar.xz" "" presetsFolder ${presetsFolder}) add_custom_command(OUTPUT ${presetsFolder} COMMAND tar xvf ${GKICK_PRESETS_DIR}/${compressedFolder}) set(GKICK_PRESETS_OUTPUT ${GKICK_PRESETS_OUTPUT} ${CMAKE_BINARY_DIR}/presets/${presetsFolder}) endforeach() add_custom_target(gkick_presets ALL DEPENDS ${GKICK_PRESETS_OUTPUT}) install(DIRECTORY ${GKICK_PRESETS_OUTPUT} DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/geonkick/presets) geonkick-2.10.2/presets/DSoS.tar.xz000066400000000000000000000032641451017372400170440ustar00rootroot000000000000007zXZִF!t/q]"TJG\B2 +'HKwXm]Tmz,lIW;SL9(m'S"GmlqdjX?J 8k}~| ٤? i>ͻT)%J_(.@yƝKt_{Xw'knREQcL挅Hhލ;O};U2~h")C lNQ~RVl[,%ȃ(⍎l@fL ECs6/sllAT| 0M „qXʛ=0Җg0 vHy+0!GgbD̩Xhi m#xN P/va,?Jn>^Ѻ.-hAX\r{Aƃw1 AaaNJdsKq0Uu=?Ɠ:ɧ B6Xi{u1Rᠾ ZD"NL@v`ݡP)뇔olte2^ZrIS R(a0Sn4{恉0 QGUpF+' н"䘮?E;}06;v!SI9ҺɌi56̽4gIY0^eiJhJRuR5j˥̏F_ִNA ~3`HM`fD}N'5u@+naj1ݏ9JB[Kn DZ]53!#+¥8<']/=8<snD*D_z.pjЇgJP^IckA@0뒇&,Ho Kw?PA}5jb!r,ºj{C15}Ѿ-_qf4N:%cF}k%Vbw\“|c {: =ԨYת~X.7-ug(1A}JGTc}D!-zAؑGn%3VѢkI1ŴnsG6oϒdUdOrw=TR Aam{QZerh;ePi-NݕX:h"$sGovf7;vQ``'`¥mb$ T@pLz@ufKKd+?PL~K쓧~Nk[VauI% 1k7"#D_TQSQ!ެ|H"KRr3Rt胦X*e3X ^eyb DNV?.c5m;-7{]g Lj4&dq$&첮{Nv٠ZK;Ea4k0%^K oAon,q)n큮6$0Il1pgO@>4̣mƿ|޻lQ?l=C)Zu3y,Zk4;xCS& g( L=W'O7zXǧmO;K\n;u}/ڱ!9-RE #dԺiUuId7×Bd] ҳ W:0}͆Y.fM`{lb\wkt>$?Q4EL؝H(l\eu] WL/?OHEIMvҺt 2qr棎 4wC]_haJcʄU{JqYb sѲDVkLBՋ]XWJ;9r#*^7lǟW7SjÄ ~3?(:X+ ʠ&TMω! KI5#кv԰l'v֪dVa4Y0SDDq]@|F%#cz$"ͽ$li;[ '7].|>DXn32}ha[bނ$sL28a5sQ*R_i pi[Vk 5e w>ŏ,ԉ)4CK78툾Z튞->бAC)uZ ;+tġQנw<ن7AerwڀN*B * .eVKj3 }-|S/w_mL#*|t $Q/͓wl(8I{&mY yhN2 e9`Vڍݏ+ɑΤB?{>:{yH y]n-+:ZAtY>TL&}p='S0 oH&)"v xNG!pg ޣRXI)2$-e"(jz$-Ǎ>= vm{qz1I2!?у7OA8#q J?O1O d%қmCX+/#ԳС12EU53pYѸ2uCI][UYq7jA:I@J$dqfRr<&'ȼ7loUM)*Y{xӷ亳㔸0w=qSV4:O]36|Y293 'í9N6񎉅y r*~>n9`>D2oY6_!q(˻ƌ\(LLBNz x>P{VIV;gD"&H4c#dc V@->I|D((;l?HҾ+*|<5 Ir"lL &E*D,7M] zVTƐ (6.J>b ޺+,y`JҺ)Z.C [H:񄻌dMKN#ғ?lO5*و5XT*o 9Ȇ<@*Y/g!jhp c"6BrQv75 vGPyO Av϶+qCͰ,].n|diI=kMcxkEULц![%%9E<2 ven[ՕӅ.B:E5m_AZ=,'cLؓJbIY}N"7iA@\EzE^jRYs(xRVo.$&R2n4dW{K!nB&H3ܭ=^^ZC[ks;mjD'~iޏ˧ܶ ae_70얇mSC%;32,O R V sXDrߍiM>nJ:{k~ Wo}X^kԱ/(qKhR_QlSxsbW:uhٌ'Zɍv#I,%rz^+jjB_QnC-RdQ c@80># i[xqA٧HZBVzAua~rb<ESC+A3K%Vd~u"2q*A$Ɲ4=o<uQw˲ ξw6%K:ru 9(\ihoaQ-KVU61ttVĊ3m_QPG.K  G'f~>}N<(wZbH@ف9u=%"xzэ@ ٚ6Lik]?eksy8Rʳ 3hbUf xܐ2.xtx?g36юhANvw~߶FMfp@[ #LifaәB/U44ao0ȳI\r&|(+s0Ɨ,Ze1w:lJC^浡Ta,]g= w FctEKC3tKM6Z&(:&ʏC4T˘9K% '(v.9}H!J9fdy2roEĽ7f/4d9/VsUFX'EhGgaΎ&lXeL4#!@s;e6a6;ߟфSů&e3!xٽnp:2p+2úD1.$C-WAfzËI Cn<8&`$@su0DzۤD2m./[eWUKFFU̍Cag q X?.${+J1VM)燐쏻781'QӀ`itZv>4n^L'Ǟ-T(J^&BMBQb .6JȥSF f !A)ڢ殹s%~#ӷ,Ra_#mg*ݜs;"Ʉemxts` ^T,#rNhc€(v8C[r철ģe ?I瞍]@2NHK Iˢ|ܗJm{}ü 3,|HǻǬ"#9o[ 7ݐ6xkEnhMj*7kmlk.F\XWK#z tl)J@0Y籖z %U/sn7~D6e' HFk>LFC0r k<1:;PF-*i9xD1B*AZDU!vQX+Ƥt.^]AF~,?|VGwqboqԤfH{*>^13Y,rNě@~2TuM4mIz(d k!Na&ӪG9olte6Ǡ5-hîgQjѧ;[~7H#PLZo0 ;7Iz~x6V3 g7,u!C!bIJ*r6)|u!l,W5G _K]YQRfSy9fp׳nl^8K224e#%}s6xqG=\EKy~o'uiR Fn ~;e;Wu}x:{*9PWovY@"_ uW.g'%kF6=< y/*$##ĺ!$uAUyؚn@%}Ecx{{8' 0jW^֬uV 5=?3*0rn:x;ۆ׃uw_B?EԪG@.=hw=7[6@ׯht FXЌP2c79ב"\Ǒp1{ քq  .rd͜5ym $JܻNFә cW[u2 kM[wdx/$P}cԛU ::K1!G<;>[^56&"L^-TKx C?Q{^?QC#M tu*ah >\̿%H D(%K#? U3ͷ ]L&)c7B +`t$*1 j~|x;䲾 eT֓fX gYkQ2W_JhsJ4vH71RS#݆ㅀ8q. WO{S)bs|߽ ^s!jzxhTk5$3>"X7`s6\eJ4eMsͅH\џn}PJk4K'bz,-&z3^PĶb{E4t)ojGG}ڦ5L#K-Qpue̬6_ys'qT0gV`54++Ԑ6*/W.Z~4#X"DhIBZ^숗>?ʦ\<?D!|s﹃=ڳ^);`$Z`B+( 䰵giM7^7)P9ߧQX{3Wy *ͱ@!kl / tRɰo7U6$\VZho be?+qOW&xVC5KE` YA)|mTCw#'8|(I`OW(bĥk2+j=,E%ҧ45 Q2eb)I-v +|UL/,SHx ~Ӵ+vgoאzN( uwstr%'t !$>lnwCSh<ɫs7w:H -IWҕQO |XHWL[.==8[ggb;bDZ\Z5PJLbq ~D+gᤇx4Ȭ\Ն͞WOJrLc'$ꕉpqfXH8K/sSBЅeԢ y+c?5&:v 8"| g[֝tnYkrKCAy@0:跠f5?0Y}:d/VKw^dә'ezw-= -gYZgeonkick-2.10.2/presets/GeonkickBells.tar.xz000066400000000000000000000045301451017372400207450ustar00rootroot000000000000007zXZִF!t/K ]#I ٩/dmW˔ _5QɈD#(>a(61+PFfp j0@8Ĉ4f<ޭ|{&DAߐ_*!^Kz>g!Ӗ;.Ǡ^ʠxdh,1Ib7Sk QFȌI%XŔM 7A[]{{Ri!7ea%bOK*´C+ \ ,ڶ6afP$Ih*dhљג!,D+VU[-|*_3.m=Ыn¾>F|"nv--}KhB$1oJҡ} _e!؞a10Ddf@=yh윿Rb bRmF-_H7 im#3o/[J/c]ux.>U+l"ݼ\r/Q>zݳEhOС)*hcYJ/֏睎^YSJ/=rSOG87F $uN:װ-?9bL9(Q}}X穠 լSH(k/_VWL э'bJ!ftk 2鄼0Knl[:Q1KU+-~Xk#%PFJMy_Lg6ZJ;xު{ƺjD\`{m [R5hڲ"qQH|3fd x;B$ewК\`N'O48p ߝџcj q#aEk@u }}~>ײ >.+6+$J]f)/xD˛IRHUŒ7!)7 T@X!sNju*ߴgvUa :J涑Q62,v(p>+rki1T8p6hMy)w^uV[J)N~kB|`v*B(͘L؎uex Gk+i03 3dé23.t(s- kGY씮/ 767h&NkJ+XlcXšNBhh-^ltm]c_JE{s#tln&( nI%rcXqhCW6j ?LGIi&b=&LdDvoPi1M?$.Dgw]=3dCfo9S.?|j]JӠ1p>a;l5Oe3<=#C߆axnC^FV }SPrU>{x%d],@$ZP~*i5BD .i-;ww3յH`f[ &)=!Ts߭bYtd.o+!̿ >jT%!d-9o% @(0<-sMq(\X1}me&OKٹMa4*<'מH.ޥ6*8{F$)RBɨ,3Mٓeěҁr%׍^ xPSHy\*Q$?_̥nʱ gYZgeonkick-2.10.2/presets/TimeKit.tar.xz000066400000000000000000000060541451017372400176020ustar00rootroot000000000000007zXZִF!t/ ]*IhˬZǑ)8YAJ_ C]U( s:4oQ3Hɴ8$&GcqZMVR{(OnV\Ixc,(p0;q7{)ٗ fsM<?58̝ -YQ1 } x|yfbg(3! $TFkcJ* +Ē&e ܧN"BTcBYO]8:-`zY(G 퓄#^S]~{ڜy;f>)y wa6յ1hVgc&HGRϢL!H6sYn<ۈm_O4v9o)?&=NQd*|Hp<%9M4SEG -,}AC G7QKoFC ^V\HEeU5e(A@loP0_eɟUl^|_/j`\X MT6j\x8~ 4˰$+跻}6k\jJqlpxV&y.dڬa/_1C\>jSMuhh@!v.^ɁQy⋀{\H3 TmU7z(̇iȋ[;mLfoaA{]m$Τ /ҬА(wFA#B+ b8x~%zuyu|wč}@iX f X,!zt+|Y>!k45@W>ʍk{_g(M Tu_ qTtPMd)`!JAYL%9VC,5lt56@hAu4P̔Ěd?RݦGh\g[v~*ws,"nBbޠRpT]Sl(PF De}QeՈv=`?.-M/;gS'q :ԝNdEԎo#kKpJ+Jf4nWX{ +B8w;6Ek0[cQ~ڮ:vSUkU!Q CKD\ A%A@vpjbtcx&睳wDN-0iaTнz%]ݡ>_`&.2ccpj{b% }/_<+GHi#fVC_EN=FWwg͆2aCd3{6v0`lgν[x,cu.G.B"{cw<] л}IͲD櫌6!hP6 AmΨ|gh1 QÑTu$[ߏGDz qZϰׄ` 5)&@3%Kҙn3Yڰf? }&bZ8o+Xj fC7>@֗w?L$7mGigYߵu2LF{eQ/NT8}n-7`iݩEsNPY6v 1l1f'q@o:d[b;E"sJK(;W,A }B cp]WjF<q" U[BֿqvQTS޵ (VjEL=/պBIDr~'irF= >̬G!tܟE64yplEQPe{@:cF`okN!`BgoNԒs6H\XD8aFHdHAr4Mj3zb{5_Xѻ²Ǖ}ײ\#!嬰\"ɫgl nHڧ?vOhC!x5˫mK&,dTZjZ( E`t7ݡ ^XbiDmh:U8n&c!-\IY^u@r:M4PI XPK#~@;TphH*\5a<*hurBy4,,Q|"R<kq̙ {;&OKfDkLD4zq::2_o".t[ٖ#g&D: phpL.+aƬY$U,,,sLOalGTdT[%~ 3/.j'5r%^w`:5_sux 9q&dm(V W},D"' 'a@RuP*:ԛIeylCaJe@jXڪ*yakU{r?#:˗=%8&=>aґ!X\Ud^a+1&Yu#J7 xx2ra~ $8FjwJTWDaXtUH˅s0<=eȐ] j'4GݑvWn5 93x6Y&ޝ5ks3#"_$/ .!8ElTDJnٔ`D[T(ڋAq3 H[C ŽA=?7Ggu-?k!f+.C|rˍ}֗3nF*ဥs7ǪGDϏ(~R5<0;"b> M@S߲Y(l]gYZgeonkick-2.10.2/presets/UnfaTutorial.tar.xz000066400000000000000000000111101451017372400206360ustar00rootroot000000000000007zXZִF!t/W]*#9>y a͖&GPZ*y}/UXx#\n7Nn L>鶏F5WV5 aSJzJ(׬1)C˗Qj8w펃&!'6CJ~Bf4.=zk RHnSx,@ͺE ? xai[BG_ezЀf_L]~ @mwt6+ xGR {HMTS-zҦ ǎhQZmM"LkώK3`О|6ݜPS&Z]9bj; !:ӠU\U!l"{Oj^[2]Gn=_wWלTZNKOSJ7Ub`Kc4dꖙ=W+WܦȣK'(g6Z?]`:v*$T`ys9)#GN^`jy~ZaBTF >U"nxڳZE8']x0 :xI#}G/^)@Uh{;%hߡC_я`lI|7.] b]A+=HP:mjd6xy8VEV- MzI3صjځ?bcg XX:zkBIYJVt5V e98yIz6"y+Sb-vΆ 1oQ2sueŰw־@_P8߃eV%RBꠒL,s+en2vEr@t])⟁8E4.&UQ'M^O#*Ih2Q>{kuT;6*tS`vu28"Sje.k ޠΉȻ\F>󀒃M1Is5n'nao$Pm5\"-BM? \$ *! 曑\+N-u@zgY'c£+ _獀Ajb5wŠ(fPP, /P1z1mI @Vr7*u 畻c$:3h.^HN a`'$Ol4?z<̍9 Xa<5xA0c۬- )J]G: (d%Z_;)#ޅ+7o9`׾lwpj$bp_dèE_Jخ sۥoVr8g?σ5M+$uiR4d.Y U\g M~F̎a Bȃ.3 zY9ضtC$\; <ú\’)#d^1.Ϙ<G~݊kwlxgѫWsl'.0P5 cxInwm*z$"mQڸ=lÞVF:NKÚ Q~q O 13By ڣ`_cv4 B|@;M VuQQm&d8z&n@H):8KJD@<d;$sIf)Ht2Hq)Rp.d:f*ot/$ m徦ݮ)s0eB[BU'c԰Ұ{1mD-fGKv5q>\AUIt[+c:~0xۀ E4J0|,9[x{l=pVU 2yao'|VܹyYE,TOU#d+ZrTp1nWT.*jxB'`:} WED0)8`QVSb<ymr_rsnsڵ07ҥI'_ń:GcꔳBF]\لc;{U@nbգLvryW^1`zoX@q}Ǜbк +,,+셿|Y Q@LT 30gw=FSoNS]^\#)xdz/72z 2㲢$5;Ԇ!baNۆÍ~_5CڦiD%#h,ӛɹ%z4..Plpƀ(:mBRP3ZyB"K+ :i25*/DbcP2}*AQ 1} lOA=|O:o߹\Wf޻Q23d4ƻvLX" qbwOxa5(Fvo6E"]fծos9huUIGA>emV?zX2Lr*v 2v. jk$W)t3|vT%Si1SW_a+R%xڍp "I0qD.6XH0̙PnThmR|m26Ű]2[8XX{- ]TKiVsYF}5E9BGYamlD{N<qnE>iHU_}J'AIFJ )~s y it#Y@qbn^=6<%_W/ȥ7X51no@i9-! tx^Ko98Ks]s FQLp>${TE"3#ɱĶ=*XVpFؼpivL9dceQ_gK(,O!5NmBݦo)n 7xoTp\cɳ׶4|7@DqXY"Tpw׫h, bUF>'@uBZA O&Cb1n8eu =BזaEw4Rpigg nA wTnC&1H 0 uV 8S 7 jp\Nľ?u{ s#?= RYzajO_ʴy v?ylJ&BGu'Z2"#Y$G|Xmz;Hؖ;q#g{euLy&3|>yhp_25kSLUf '@ Ð眪:ŏ{r̬"VM5? r^;= 1AL+śh\%FMM,S~~'I暝MdɎv5dwX͊u5c[&Χ[(tPsH)ÎLI5."GGA⭲RkiPN4!0!`y$vP),?sjc9aDK%b {VOim`.8fOg !G% CZo>&zZȸ^KmL JwEF  LJL.~o;шl &Oq>Z h~^MmRa}Rl+-)a̛WW/hnNAD΍+9Qn{B։W3ׯ,8__3K!;db'3h 'SDx>cSD0&)-nw=/ݗ$?F,feEOm^ä@߄.IGhD&}+jFzmgG_C S_R¦x(smјy*!=Wua_kƊ>`<7Qч+#%\4 ~>T {1 !֜,fCw[t -;Oa)CXʼjUWs[QVC`KCNHVlb>-7M=WcYnʫhݹ`!I p$ 0lܱgYZgeonkick-2.10.2/src/000077500000000000000000000000001451017372400141615ustar00rootroot00000000000000geonkick-2.10.2/src/Base64EncoderDecoder.cpp000066400000000000000000000061271451017372400205050ustar00rootroot00000000000000/** * File name: Base64EncoderDecoder.cpp * Project: Geonkick (A percussion synthesizer) F * * Copyright (C) 2023 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "Base64EncoderDecoder.h" #include #include #include #include std::string Base64EncoderDecoder::encode(const std::vector &data) { auto b64 = BIO_new(BIO_f_base64()); if (!b64) { GEONKICK_LOG_ERROR("can't create bio64"); return std::string(); } BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); auto bio = BIO_new(BIO_s_mem()); if (!bio) { GEONKICK_LOG_ERROR("can't create bio"); BIO_free_all(b64); return {}; } BIO_push(b64, bio); BIO_write(b64, data.data(), static_cast(data.size() * sizeof(float))); BIO_flush(b64); BUF_MEM *bufferPtr; if (BIO_get_mem_ptr(b64, &bufferPtr) != 1) { GEONKICK_LOG_ERROR("can't read BIO ptr"); BIO_free_all(b64); return {}; } std::string encoded(bufferPtr->data, bufferPtr->length); BIO_free_all(b64); GEONKICK_LOG_DEBUG("ecoded: " << encoded); return encoded; } std::vector Base64EncoderDecoder::decode(const std::string &input) { auto bio = BIO_new_mem_buf(input.c_str(), -1); if (!bio) { GEONKICK_LOG_ERROR("can't create bio"); return {}; } auto b64 = BIO_new(BIO_f_base64()); if (!b64) { GEONKICK_LOG_ERROR("can't create bio64"); BIO_free_all(bio); return {}; } BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); BIO_push(b64, bio); std::string outputBuffer(input.length(), 0x0); auto decodedLength = BIO_read(b64, outputBuffer.data(), input.length()); if (decodedLength < 1 ) { GEONKICK_LOG_ERROR("can't decode base64"); } else { GEONKICK_LOG_DEBUG("decoded lengh: " << decodedLength); std::vector decoded(static_cast(decodedLength / sizeof(float))); std::memcpy(decoded.data(), outputBuffer.data(), decodedLength); BIO_free_all(b64); return decoded; } BIO_free_all(b64); return {}; } geonkick-2.10.2/src/Base64EncoderDecoder.h000066400000000000000000000022461451017372400201500ustar00rootroot00000000000000/** * File name: Base64EncoderDecoder.h * Project: Geonkick (A percussion synthesizer) F * * Copyright (C) 2023 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_BASE64_ENCODER_DECODER #define GEONKICK_BASE64_ENCODER_DECODER #include "globals.h" class Base64EncoderDecoder { public: static std::string encode(const std::vector &data); static std::vector decode(const std::string &input); }; #endif // GEONKICK_BASE64_ENCODER_DECODER geonkick-2.10.2/src/BufferView.cpp000066400000000000000000000056051451017372400167370ustar00rootroot00000000000000/** * File name: BufferView.cpp * Project: Geonkick (A percussion synthesizer) F * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "BufferView.h" #include #include BufferView::BufferView(GeonkickWidget* parent, const std::vector &data) : GeonkickWidget(parent) , bufferData{data} , updateGraph{true} , graphImage{nullptr} { setBackgroundColor(50, 50, 50); setBorderColor(40, 40, 40); setBorderWidth(1); } void BufferView::setData(const std::vector &data) { bufferData = data; updateGraph = true; update(); } std::vector BufferView::getData() const { return bufferData; } void BufferView::paintWidget(RkPaintEvent *event) { RK_UNUSED(event); if (updateGraph) drawGraph(); if (graphImage && !graphImage->isNull()) { RkPainter painter(this); painter.drawImage(*graphImage.get(), 0, 0); } } void BufferView::drawGraph() { graphImage = std::make_unique(size()); RkPainter painter(graphImage.get()); painter.fillRect(RkRect({0, 0}, size()), background()); if (bufferData.empty()) return; RkPen pen = painter.pen(); pen.setColor({59, 130, 4, 255}); painter.setPen(pen); std::vector graphPoints; int x = 0; int kY = height() / 2; float yScale = 1.0f; float max = fabs(*std::max_element(bufferData.begin(), bufferData.end(), [](float a, float b){ return fabs(a) < fabs(b); })); if (max > 1e-5) yScale = 1.0f / max; int graphWidth = width(); for (const auto &val: bufferData) { int y = kY * (1.0f - yScale * val); graphPoints.emplace_back(RkPoint(x / graphWidth, y)); x++; } painter.drawPolyline(graphPoints); updateGraph = false; } void BufferView::mouseButtonPressEvent(RkMouseEvent *event) { if (event->button() == RkMouseEvent::ButtonType::Left) action graphPressed(); } geonkick-2.10.2/src/BufferView.h000066400000000000000000000031651451017372400164030ustar00rootroot00000000000000/** * File name: BufferView.h * Project: Geonkick (A percussion synthesizer) F * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_BUFFER_VIEW #define GEONKICK_BUFFER_VIEW #include "geonkick_widget.h" #include class BufferView : public GeonkickWidget { public: BufferView(GeonkickWidget* parent, const std::vector &data = std::vector()); virtual ~BufferView() = default; void setData(const std::vector &data); std::vector getData() const; RK_DECL_ACT(graphPressed, graphPressed(), RK_ARG_TYPE(), RK_ARG_VAL()); protected: void mouseButtonPressEvent(RkMouseEvent *event) override; void paintWidget(RkPaintEvent *event) override; void drawGraph(); private: std::vector bufferData; bool updateGraph; std::unique_ptr graphImage; }; #endif // GEONKICK_BUFFER_VIEW geonkick-2.10.2/src/EnvelopePointContextWidget.cpp000066400000000000000000000051661451017372400221750ustar00rootroot00000000000000/** * File name: EnvelopePointContextWidget.cpp * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2023 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "EnvelopePointContextWidget.h" #include "envelope.h" #include #include EnvelopePointContextWidget::EnvelopePointContextWidget(Envelope* envelope, GeonkickWidget *parent, Rk::WindowFlags flag) : GeonkickWidget(parent, flag) , pointEnvelope{envelope} , lineEdit{new RkLineEdit(this)} { setFixedSize(110, 30); setBackgroundColor({68, 68, 70}); setBorderColor(40, 40, 40); setBorderWidth(1); lineEdit->setSize(100, 20); lineEdit->setPosition(5, 5); lineEdit->show(); RK_ACT_BIND(lineEdit, editingFinished, RK_ACT_ARGS(), this, onUpdateValue()); RK_ACT_BIND(lineEdit, escapePressed, RK_ACT_ARGS(), this, close()); pointEnvelope->setEditCurrentPoint(); setValue(pointEnvelope->getSelectedPointValue()); } void EnvelopePointContextWidget::setFocus() { lineEdit->setFocus(); } void EnvelopePointContextWidget::setValue(rk_real val) { double roundedValue = std::round(val * 10000.0) / 10000.0; lineEdit->setText(Geonkick::doubleToStr(roundedValue, 4)); lineEdit->moveCursorToEnd(); } void EnvelopePointContextWidget::closeEvent(RkCloseEvent *event) { RkWidget::closeEvent(event); } void EnvelopePointContextWidget::onUpdateValue() { double value = 0.0; try { value = std::stod(lineEdit->text()); } catch (...) { } pointEnvelope->updateSelectedPointValue(value); close(); } geonkick-2.10.2/src/EnvelopePointContextWidget.h000066400000000000000000000033501451017372400216330ustar00rootroot00000000000000/** * File name: EnvelopePointContextWidget.h * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2023 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_ENVELOPE_POINT_CONTEXT_WIDGET_H #define GEONKICK_ENVELOPE_POINT_CONTEXT_WIDGET_H #include "geonkick_widget.h" class Envelope; class RkLineEdit; class EnvelopePointContextWidget: public GeonkickWidget { public: EnvelopePointContextWidget(Envelope *envelope, GeonkickWidget *parent, Rk::WindowFlags flag = Rk::WindowFlags::Popup); void setValue(rk_real val); RK_DECL_ACT(valueUpdated, valueUpdated(rk_real val), RK_ARG_TYPE(rk_real), RK_ARG_VAL(val)); void setFocus(); protected: void closeEvent(RkCloseEvent *event) override; void onUpdateValue(); private: Envelope *pointEnvelope; RkLineEdit *lineEdit; }; #endif // GEONKICK_ENVELOPE_POINT_CONTEXT_WIDGET_H geonkick-2.10.2/src/ExportAbstract.cpp000066400000000000000000000034331451017372400176350ustar00rootroot00000000000000/** * File name: ExportAbstract.cpp * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "ExportAbstract.h" ExportAbstract::ExportAbstract([[maybe_unused]] RkObject *parent, const std::filesystem::path &path, ExportFormat expFormat) : RkObject(nullptr) , exportFormat{expFormat} , exportPath{path} , exportProgress{0} { } std::filesystem::path ExportAbstract::getExportPath() const { return exportPath; } bool ExportAbstract::isError() const { return exportError.empty(); } std::string ExportAbstract::errorString() const { return exportError; } ExportAbstract::ExportFormat ExportAbstract::format() const { return exportFormat; } int ExportAbstract::progress() const { return exportProgress; } void ExportAbstract::setProgress(const int val) { exportProgress = val; } void ExportAbstract::setError(const std::string &error) { exportError = error; } geonkick-2.10.2/src/ExportAbstract.h000066400000000000000000000051621451017372400173030ustar00rootroot00000000000000/** * File name: ExportAbstract.h * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONGKICK_EXPORT_ABSTRACT_H #define GEONGKICK_EXPORT_ABSTRACT_H #include "globals.h" #include class ExportAbstract: public RkObject { public: enum class ExportFormat : int { Flac = static_cast(GeonkickTypes::Formats::Flac), Wav = static_cast(GeonkickTypes::Formats::Wav), Ogg = static_cast(GeonkickTypes::Formats::Ogg), Sfz = static_cast(GeonkickTypes::Formats::Sfz) }; RK_DECL_ACT(exportFinished, exportFinished(), RK_ARG_TYPE(), RK_ARG_VAL()); RK_DECL_ACT(onError, onError(std::string error), RK_ARG_TYPE(std::string), RK_ARG_VAL(error)); RK_DECL_ACT(currentProgress, currentProgress(int progress), RK_ARG_TYPE(int progress), RK_ARG_VAL(progress)); ExportAbstract(RkObject *parent, const std::filesystem::path &path = std::filesystem::path(), ExportFormat expFormat = ExportFormat::Flac); virtual ~ExportAbstract() = default; virtual bool doExport() = 0; ExportFormat format() const; void setFormat(ExportFormat exFormat); std::string errorString() const; bool isError() const; int progress() const; protected: void setError(const std::string &error); void setProgress(const int val); std::filesystem::path getExportPath() const; private: ExportFormat exportFormat; std::filesystem::path exportPath; std::string exportError; int exportProgress; }; #endif // GEONGKICK_EXPORT_ABSTRACT_H geonkick-2.10.2/src/ExportSoundData.cpp000066400000000000000000000124461451017372400177600ustar00rootroot00000000000000/** * File name: ExportSoundData.cpp * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "ExportSoundData.h" #include ExportSoundData::ExportSoundData(RkObject *parent, const std::filesystem::path &file, const std::vector &data, ExportFormat exportFormat) : ExportAbstract(parent, file, exportFormat) , soundData{data} , exportSubformat{getDefaultSubformat()} , sampleRate{Geonkick::defaultSampleRate} , nChannels{2} { } bool ExportSoundData::doExport() { SF_INFO sndinfo; memset(&sndinfo, 0, sizeof(sndinfo)); sndinfo.samplerate = getSampleRate(); sndinfo.channels = numberOfChannels(); sndinfo.format = sfExportFormat(); auto tempBuffer = soundData; sndinfo.frames = tempBuffer.size(); std::vector dataBuffer; if (sndinfo.channels == 2) { dataBuffer.resize(2 * tempBuffer.size()); size_t k = 0; while (k < tempBuffer.size()) { dataBuffer[2 * k] = dataBuffer[2 * k + 1] = tempBuffer[k]; k++; } } else { dataBuffer = std::move(tempBuffer); } if (dataBuffer.empty() || !sf_format_check(&sndinfo)) { setError("error on exporting data"); return false; } auto filePath = getExportPath(); if (filePath.empty()) { setError("wrong file name is empty"); return false; } SNDFILE *sndFile = sf_open(filePath.string().c_str(), SFM_WRITE, &sndinfo); if (!sndFile) { setError("can't open file " + filePath.string()); return false; } size_t n; n = sf_write_float(sndFile, dataBuffer.data(), dataBuffer.size()); if (n != dataBuffer.size()) { setError("error on exporting"); sf_close(sndFile); return false; } sf_close(sndFile); return true; } ExportSoundData::Subformat ExportSoundData::subformat() const { return exportSubformat; } void ExportSoundData::setSubformat(ExportSoundData::Subformat subFormat) { if (!validateSubformat(subFormat)) { GEONKICK_LOG_ERROR("wrong subformat " << static_cast(subFormat) << " for format " << static_cast(format())); } else { exportSubformat = subFormat; } } int ExportSoundData::getSampleRate() const { return sampleRate; } void ExportSoundData::setSmapleRate(int srate) { sampleRate = srate; } int ExportSoundData::numberOfChannels() const { return nChannels; } void ExportSoundData::setNumberOfChannels(int channels) { nChannels = channels; } bool ExportSoundData::validateSubformat(Subformat subFormat) { switch (format()) { case ExportFormat::Flac: return subFormat == Subformat::Flac16 || subFormat == Subformat::Flac24; case ExportFormat::Wav: return subFormat == Subformat::Wav16 || subFormat == Subformat::Wav24 || subFormat == Subformat::Wav32; case ExportFormat::Ogg: return subFormat == Subformat::Ogg; default: return false; } } ExportSoundData::Subformat ExportSoundData::getDefaultSubformat() const { switch (format()) { case ExportFormat::Wav: return Subformat::Wav16; case ExportFormat::Ogg: return Subformat::Ogg; case ExportFormat::Flac: default: return Subformat::Flac16; } } int ExportSoundData::sfExportFormat() const { switch (subformat()) { case Subformat::Flac16: return SF_FORMAT_FLAC | SF_FORMAT_PCM_16; case Subformat::Flac24: return SF_FORMAT_FLAC | SF_FORMAT_PCM_24; case Subformat::Wav16: return SF_FORMAT_WAV | SF_FORMAT_PCM_16; case Subformat::Wav24: return SF_FORMAT_WAV | SF_FORMAT_PCM_24; case Subformat::Wav32: return SF_FORMAT_WAV | SF_FORMAT_PCM_32; case Subformat::Ogg: return SF_FORMAT_OGG | SF_FORMAT_VORBIS; default: return SF_FORMAT_WAV | SF_FORMAT_PCM_24; } } geonkick-2.10.2/src/ExportSoundData.h000066400000000000000000000040161451017372400174170ustar00rootroot00000000000000/** * File name: ExportSoundData.h * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONGKICK_SOUND_DATA_H #define GEONGKICK_SOUND_DATA_H #include "ExportAbstract.h" class ExportSoundData : public ExportAbstract { public: enum class Subformat : int { Flac16 = 0, Flac24 = 1, Wav16 = 2, Wav24 = 3, Wav32 = 4, Ogg = 5 }; ExportSoundData(RkObject *parent, const std::filesystem::path &file, const std::vector &data, ExportFormat exportFormat = ExportFormat::Flac); bool doExport() override; Subformat subformat() const; void setSubformat(Subformat format); int getSampleRate() const; void setSmapleRate(int srate); int numberOfChannels() const; void setNumberOfChannels(int channels); protected: Subformat getDefaultSubformat() const; bool validateSubformat(Subformat subFormat); int sfExportFormat() const; private: std::vector soundData; Subformat exportSubformat; int sampleRate; int nChannels; }; #endif // GEONGKICK_SOUND_DATA_H geonkick-2.10.2/src/ExportToSfz.cpp000066400000000000000000000115661451017372400171450ustar00rootroot00000000000000/** * File name: ExportToSfz.cpp * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "ExportToSfz.h" #include "ExportSoundData.h" #include "kit_model.h" #include "percussion_model.h" ExportToSfz::ExportToSfz(KitModel *model, const std::filesystem::path &file) : ExportAbstract(model, file, ExportFormat::Sfz) , kitModel{model} { } bool ExportToSfz::doExport() { std::ostringstream sfzStream; sfzStream << "// Exported from " << Geonkick::appName << " version " << Geonkick::applicationVersionStr << std::endl; sfzStream << "// Name: " << kitModel->name() << std::endl; sfzStream << "// Author: " << kitModel->author() << std::endl; sfzStream << "// License: " << kitModel->license() << std::endl; sfzStream << "// Instruments:" << std::endl; for (const auto &per : kitModel->percussionModels()) sfzStream << "// * " << per->name() << std::endl; sfzStream << std::endl; sfzStream << "" << std::endl; sfzStream << "default_path=" << (dataPath() / std::filesystem::path()).string() << std::endl; sfzStream << "" << std::endl; sfzStream << "ampeg_attack=0.001" << std::endl; sfzStream << "ampeg_release=3" << std::endl; sfzStream << "ampeg_dynamic=1" << std::endl; sfzStream << "volume=0" << std::endl << std::endl; sfzStream << "" << std::endl; sfzStream << "seq_length=2" << std::endl; sfzStream << "seq_position=1" << std::endl << std::endl; try { if (!std::filesystem::exists(dataPath())) { if (!std::filesystem::create_directories(dataPath())) { GEONKICK_LOG_ERROR("can't create path " << dataPath()); return false; } } } catch(const std::exception& e) { GEONKICK_LOG_ERROR("error on setup user data paths: " << e.what()); return false; } for (const auto &instrument : kitModel->percussionModels()) { sfzStream << "" << std::endl; std::filesystem::path wavFile = dataPath() / std::filesystem::path(cleanName(instrument->name()) + ".wav"); sfzStream << "sample=" << wavFile.filename().string() << std::endl; sfzStream << "lokey=" << static_cast(instrument->key()) << std::endl; sfzStream << "hikey=" << static_cast(instrument->key()) << std::endl; sfzStream << "pitch_keycenter=" << static_cast(instrument->key()) << std::endl; sfzStream << "lovel=0" << std::endl; sfzStream << "hivel=127" << std::endl; sfzStream << "volume=15" << std::endl; sfzStream << std::endl; ExportSoundData exportToWav(this, wavFile, instrument->data(), ExportSoundData::ExportFormat::Wav); exportToWav.setSubformat(ExportSoundData::Subformat::Wav32); exportToWav.doExport(); if (exportToWav.isError()) setError(exportToWav.errorString()); } auto sfzFilePath = getExportPath(); if (!sfzFilePath.has_extension() && sfzFilePath.extension() != ".sfz" && sfzFilePath.extension() != ".SFZ") sfzFilePath += ".sfz"; std::ofstream sfzFile (sfzFilePath); if (!sfzFile.is_open()) { GEONKICK_LOG_INFO("can't open sfz file " << getExportPath()); return false; } else { sfzFile << sfzStream.str(); } sfzFile.close(); return true; } std::string ExportToSfz::cleanName(const std::string &name) { std::string str = name; std::replace(str.begin(), str.end(), ' ', '_'); return str; } std::filesystem::path ExportToSfz::dataPath() const { return getExportPath().parent_path() / getExportPath().stem(); } geonkick-2.10.2/src/ExportToSfz.h000066400000000000000000000025541451017372400166070ustar00rootroot00000000000000/** * File name: ExportToSfz.h * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONGKICK_EXPORT_TO_SFZ_H #define GEONGKICK_EXPORT_TO_SFZ_H #include "ExportAbstract.h" class KitModel; class ExportToSfz : public ExportAbstract { public: ExportToSfz(KitModel *model, const std::filesystem::path &file); bool doExport() override; protected: std::filesystem::path dataPath() const; static std::string cleanName(const std::string &name); private: std::string sfzFileName; KitModel *kitModel; }; #endif // GEONGKICK_EXPORT_TO_SFZ_H geonkick-2.10.2/src/GeonkickConfig.cpp000066400000000000000000000077751451017372400175650ustar00rootroot00000000000000/** * File name: GeonkickConfig.cpp * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "GeonkickConfig.h" #include #include GeonkickConfig::GeonkickConfig() : scaleFactor{1} { const char *configHome = std::getenv("XDG_CONFIG_HOME"); if (configHome == nullptr || *configHome == '\0') { const char *homeDir = std::getenv("HOME"); if (homeDir == nullptr || *homeDir == '\0') { configFile = std::filesystem::path(".") / std::filesystem::path(".config") / std::filesystem::path(GEONKICK_APP_NAME) / "config.json"; } else { configFile = homeDir / std::filesystem::path(".config") / std::filesystem::path(GEONKICK_APP_NAME) / "config.json"; } } else { configFile = configHome / std::filesystem::path(GEONKICK_APP_NAME) / "config.json"; } open(); } void GeonkickConfig::setScaleFactor(double factor) { scaleFactor = factor; } double GeonkickConfig::getScaleFactor() const { return scaleFactor; } bool GeonkickConfig::open() { try { if (!std::filesystem::exists(configFile.parent_path())) return true; } catch(...) { } std::ifstream sfile; sfile.open(std::filesystem::absolute(configFile)); if (!sfile.is_open()) { GEONKICK_LOG_ERROR("can't open file: " << configFile); return false; } std::string fileData((std::istreambuf_iterator(sfile)), (std::istreambuf_iterator())); loadConfig(fileData); return true; } void GeonkickConfig::loadConfig(const std::string &data) { rapidjson::Document document; document.Parse(data.c_str()); for (const auto &m: document.GetObject()) { if (m.name == "scaleFactor" && m.value.IsDouble()) scaleFactor = m.value.GetDouble(); } } bool GeonkickConfig::save() { try { auto configPath = configFile.parent_path(); if (!std::filesystem::exists(configPath)) { if (!std::filesystem::create_directories(configPath)) { GEONKICK_LOG_ERROR("can't create path " << configPath); return false; } } } catch(const std::exception& e) { GEONKICK_LOG_ERROR("error on creating config path: " << e.what()); } std::ofstream file; file.open(std::filesystem::absolute(configFile)); if (!file.is_open()) { GEONKICK_LOG_ERROR("can't open file for saving: " << configFile); return false; } file << toJson(); file.close(); return true; } std::string GeonkickConfig::toJson() const { std::ostringstream jsonStream; jsonStream << "{" << std::endl; jsonStream << "\"scaleFactor\": " << std::fixed << std::setprecision(2) << scaleFactor << std::endl; jsonStream << "}" << std::endl; return jsonStream.str(); } geonkick-2.10.2/src/GeonkickConfig.h000066400000000000000000000025021451017372400172110ustar00rootroot00000000000000/** * File name: GeonkickConfig.h * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONGKICK_CONFIG_H #define GEONGKICK_CONFIG_H #include "globals.h" class GeonkickConfig { public: GeonkickConfig(); void setScaleFactor(double factor); double getScaleFactor() const; bool open(); bool save(); protected: void loadConfig(const std::string &data); std::string toJson() const; private: double scaleFactor; std::filesystem::path configFile; }; #endif // GEONGKICK_CONFIG_H geonkick-2.10.2/src/MainWindow.cpp000066400000000000000000000430471451017372400167510ustar00rootroot00000000000000 /** * File name: MainWindow.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "MainWindow.h" #include "oscillator.h" #include "envelope_widget.h" #include "oscillator_group_box.h" #include "general_group_box.h" #include "control_area.h" #include "top_bar.h" #include "limiter.h" #include "export_widget.h" #include "geonkick_api.h" #include "percussion_state.h" #include "ViewState.h" #include "UiSettings.h" #include "GeonkickConfig.h" #include constexpr int MAIN_WINDOW_WIDTH = 940; constexpr int MAIN_WINDOW_HEIGHT = 705; MainWindow::MainWindow(RkMain *app, GeonkickApi *api, const std::string &preset) : GeonkickWidget(app) , geonkickApi{api} , topBar{nullptr} , envelopeWidget{nullptr} , presetName{preset} , limiterWidget{nullptr} , kitModel{new KitModel(this, geonkickApi)} { GeonkickConfig config; setScaleFactor(config.getScaleFactor()); createViewState(); setFixedSize(MAIN_WINDOW_WIDTH, MAIN_WINDOW_HEIGHT); setTitle(GEONKICK_NAME); geonkickApi->registerCallbacks(true); RK_ACT_BIND(geonkickApi, stateChanged, RK_ACT_ARGS(), this, updateGui()); createShortcuts(); show(); } MainWindow::MainWindow(RkMain *app, GeonkickApi *api, const RkNativeWindowInfo &info) : GeonkickWidget(app, info) , geonkickApi{api} , topBar{nullptr} , envelopeWidget{nullptr} , presetName{std::string()} , limiterWidget{nullptr} , kitModel{new KitModel(this, geonkickApi)} { GeonkickConfig config; setScaleFactor(config.getScaleFactor()); createViewState(); setFixedSize(MAIN_WINDOW_WIDTH, MAIN_WINDOW_HEIGHT); setTitle(GEONKICK_NAME); geonkickApi->registerCallbacks(true); RK_ACT_BIND(geonkickApi, stateChanged, RK_ACT_ARGS(), this, updateGui()); createShortcuts(); show(); } MainWindow::~MainWindow() { if (geonkickApi) { geonkickApi->registerCallbacks(false); geonkickApi->setEventQueue(nullptr); if (geonkickApi->isStandalone()) delete geonkickApi; } } void MainWindow::createViewState() { auto viewState = new ViewState(this); viewState->setName("ViewState"); UiSettings *uiSettings = geonkickApi->getUiSettings(); viewState->setMainView(uiSettings->getMainView()); viewState->setSamplesBrowserPath(uiSettings->samplesBrowserPath()); RK_ACT_BIND(viewState, mainViewChanged, RK_ACT_ARGS(ViewState::View view), geonkickApi, getUiSettings()->setMainView(view)); RK_ACT_BIND(viewState, samplesBrowserPathChanged, RK_ACT_ARGS(const std::string &path), geonkickApi, getUiSettings()->setSamplesBrowserPath(path)); setViewState(viewState); } void MainWindow::createShortcuts() { addShortcut(Rk::Key::Key_K, Rk::KeyModifiers::Control_Left); addShortcut(Rk::Key::Key_K, Rk::KeyModifiers::Control_Right); addShortcut(Rk::Key::Key_k, Rk::KeyModifiers::Control_Left); addShortcut(Rk::Key::Key_k, Rk::KeyModifiers::Control_Right); addShortcut(Rk::Key::Key_O, Rk::KeyModifiers::Control_Left); addShortcut(Rk::Key::Key_O, Rk::KeyModifiers::Control_Right); addShortcut(Rk::Key::Key_o, Rk::KeyModifiers::Control_Left); addShortcut(Rk::Key::Key_o, Rk::KeyModifiers::Control_Right); addShortcut(Rk::Key::Key_S, Rk::KeyModifiers::Control_Left); addShortcut(Rk::Key::Key_S, Rk::KeyModifiers::Control_Right); addShortcut(Rk::Key::Key_s, Rk::KeyModifiers::Control_Left); addShortcut(Rk::Key::Key_s, Rk::KeyModifiers::Control_Right); addShortcut(Rk::Key::Key_H, Rk::KeyModifiers::Control_Left); addShortcut(Rk::Key::Key_H, Rk::KeyModifiers::Control_Right); addShortcut(Rk::Key::Key_h, Rk::KeyModifiers::Control_Left); addShortcut(Rk::Key::Key_h, Rk::KeyModifiers::Control_Right); addShortcut(Rk::Key::Key_E, Rk::KeyModifiers::Control_Left); addShortcut(Rk::Key::Key_E, Rk::KeyModifiers::Control_Right); addShortcut(Rk::Key::Key_e, Rk::KeyModifiers::Control_Left); addShortcut(Rk::Key::Key_e, Rk::KeyModifiers::Control_Right); addShortcut(Rk::Key::Key_C, Rk::KeyModifiers::Control_Left); addShortcut(Rk::Key::Key_C, Rk::KeyModifiers::Control_Right); addShortcut(Rk::Key::Key_c, Rk::KeyModifiers::Control_Left); addShortcut(Rk::Key::Key_c, Rk::KeyModifiers::Control_Right); addShortcut(Rk::Key::Key_V, Rk::KeyModifiers::Control_Left); addShortcut(Rk::Key::Key_V, Rk::KeyModifiers::Control_Right); addShortcut(Rk::Key::Key_v, Rk::KeyModifiers::Control_Left); addShortcut(Rk::Key::Key_v, Rk::KeyModifiers::Control_Right); addShortcut(Rk::Key::Key_R, Rk::KeyModifiers::Control_Left); addShortcut(Rk::Key::Key_R, Rk::KeyModifiers::Control_Right); addShortcut(Rk::Key::Key_r, Rk::KeyModifiers::Control_Left); addShortcut(Rk::Key::Key_r, Rk::KeyModifiers::Control_Right); addShortcut(Rk::Key::Key_f, Rk::KeyModifiers::Control_Left); addShortcut(Rk::Key::Key_F, Rk::KeyModifiers::Control_Left); addShortcut(Rk::Key::Key_Control_Left, Rk::KeyModifiers::Control_Left); } bool MainWindow::init(void) { oscillators = geonkickApi->oscillators(); if (geonkickApi->isStandalone() && !geonkickApi->isJackEnabled()) { GEONKICK_LOG_INFO("Jack is not installed or not running. " << "There is a need for jack server running " << "in order to have audio output."); } topBar = new TopBar(this, kitModel); topBar->setX(10); topBar->show(); RK_ACT_BIND(this, updateGui, RK_ACT_ARGS(), topBar, updateGui()); RK_ACT_BIND(topBar, openFile, RK_ACT_ARGS(), this, openFileDialog(FileDialog::Type::Open)); RK_ACT_BIND(topBar, saveFile, RK_ACT_ARGS(), this, openFileDialog(FileDialog::Type::Save)); RK_ACT_BIND(topBar, resetToDefault, RK_ACT_ARGS(), this, resetToDefault()); RK_ACT_BIND(topBar, openExport, RK_ACT_ARGS(), this, openExportDialog()); RK_ACT_BIND(topBar, layerSelected, RK_ACT_ARGS(GeonkickApi::Layer layer, bool b), geonkickApi, enbaleLayer(layer, b)); // Create envelope widget. envelopeWidget = new EnvelopeWidget(this, geonkickApi, oscillators); envelopeWidget->setX(10); envelopeWidget->setY(topBar->y() + topBar->height()); envelopeWidget->setFixedSize(850, 305); envelopeWidget->show(); RK_ACT_BIND(this, updateGui, RK_ACT_ARGS(), envelopeWidget, updateGui()); RK_ACT_BIND(envelopeWidget, requestUpdateGui, RK_ACT_ARGS(), this, updateGui()); limiterWidget = new Limiter(geonkickApi, this); limiterWidget->setPosition(envelopeWidget->x() + envelopeWidget->width() + 8, envelopeWidget->y()); RK_ACT_BIND(this, updateGui, RK_ACT_ARGS(), limiterWidget, onUpdateLimiter()); limiterWidget->show(); controlAreaWidget = new ControlArea(this, kitModel, oscillators); controlAreaWidget->setPosition(10, envelopeWidget->y() + envelopeWidget->height()); controlAreaWidget->show(); RK_ACT_BIND(this, updateGui, RK_ACT_ARGS(), controlAreaWidget, updateGui()); kitModel = controlAreaWidget->getKitModel(); RK_ACT_BIND(kitModel, limiterUpdated, RK_ACT_ARGS(KitModel::PercussionIndex index), this, updateLimiter(index)); RK_ACT_BIND(limiterWidget, limiterUpdated, RK_ACT_ARGS(int val), kitModel, updatePercussion(kitModel->selectedPercussion())); RK_ACT_BIND(this, updateGui, RK_ACT_ARGS(), controlAreaWidget, updateGui()); if (geonkickApi->isStandalone() && !presetName.empty()) openPreset(presetName); topBar->setPresetName(geonkickApi->getPercussionName(geonkickApi->currentPercussion())); updateGui(); return true; } void MainWindow::openExportDialog() { auto w = new ExportWidget(this, geonkickApi); w->setPosition(30, 40); } void MainWindow::savePreset(const std::string &fileName) { auto state = geonkickApi->getPercussionState(); if (state->save(fileName)) { std::filesystem::path filePath(fileName); topBar->setPresetName(state->getName()); geonkickApi->setCurrentWorkingPath("SavePreset", filePath.has_parent_path() ? filePath.parent_path() : filePath); } } void MainWindow::openPreset(const std::string &fileName) { if (fileName.size() < 7) { RK_LOG_ERROR("Open Preset: " << "Can't open preset. File name " << "empty or wrong format. Format example: 'mykick.gkick'"); return; } std::filesystem::path filePath(fileName); if (filePath.extension().empty() || !std::filesystem::is_regular_file(filePath) || (filePath.extension() != ".gkick" && filePath.extension() != ".GKICK")) { RK_LOG_ERROR("Open Preset: " << "Can't open preset. Wrong file format."); return; } std::ifstream file; file.open(std::filesystem::absolute(filePath)); if (!file.is_open()) { RK_LOG_ERROR("Open Preset" + std::string(" - ") + std::string(GEONKICK_NAME) << ". Can't open preset."); return; } std::string fileData((std::istreambuf_iterator(file)), (std::istreambuf_iterator())); auto state = geonkickApi->getDefaultPercussionState(); state->loadData(fileData); state->setId(geonkickApi->currentPercussion()); geonkickApi->setPercussionState(state); action geonkickApi->percussionUpdated(state->getId()); file.close(); geonkickApi->setCurrentWorkingPath("OpenPreset", filePath.has_parent_path() ? filePath.parent_path() : filePath); updateGui(); } void MainWindow::openFileDialog(FileDialog::Type type) { auto fileDialog = new FileDialog(this, type, type == FileDialog::Type::Open ? "Open Preset" : "Save Preset"); fileDialog->setPosition(30, 40); fileDialog->setFilters({".gkick", ".GKICK"}); fileDialog->setHomeDirectory(geonkickApi->getSettings("GEONKICK_CONFIG/HOME_PATH")); if (type == FileDialog::Type::Open) { fileDialog->setCurrentDirectoy(geonkickApi->currentWorkingPath("OpenPreset")); RK_ACT_BIND(fileDialog, selectedFile, RK_ACT_ARGS(const std::string &file), this, openPreset(file)); } else { fileDialog->setCurrentDirectoy(geonkickApi->currentWorkingPath("SavePreset")); RK_ACT_BIND(fileDialog, selectedFile, RK_ACT_ARGS(const std::string &file), this, savePreset(file)); } } void MainWindow::shortcutEvent(RkKeyEvent *event) { if (event->type() == RkEvent::Type::KeyPressed) { if (event->modifiers() & static_cast(Rk::KeyModifiers::Control) && (event->key() == Rk::Key::Key_k || event->key() == Rk::Key::Key_K)) { geonkickApi->playKick(); } else if (event->modifiers() & static_cast(Rk::KeyModifiers::Control) && (event->key() == Rk::Key::Key_r || event->key() == Rk::Key::Key_R)) { resetToDefault(); } else if (event->modifiers() & static_cast(Rk::KeyModifiers::Control) && (event->key() == Rk::Key::Key_o || event->key() == Rk::Key::Key_O)) { openFileDialog(FileDialog::Type::Open); } else if (event->modifiers() & static_cast(Rk::KeyModifiers::Control) && (event->key() == Rk::Key::Key_s || event->key() == Rk::Key::Key_S)) { openFileDialog(FileDialog::Type::Save); } else if (event->modifiers() & static_cast(Rk::KeyModifiers::Control) && (event->key() == Rk::Key::Key_e || event->key() == Rk::Key::Key_E)) { openExportDialog(); } else if ((event->modifiers() & static_cast(Rk::KeyModifiers::Control)) && (event->key() == Rk::Key::Key_c || event->key() == Rk::Key::Key_C)) { geonkickApi->copyToClipboard(); } else if ((event->modifiers() & static_cast(Rk::KeyModifiers::Control)) && (event->key() == Rk::Key::Key_v || event->key() == Rk::Key::Key_V)) { geonkickApi->pasteFromClipboard(); geonkickApi->notifyPercussionUpdated(geonkickApi->currentPercussion()); updateGui(); } else if ((event->modifiers() & static_cast(Rk::KeyModifiers::Control)) && (event->key() == Rk::Key::Key_F || event->key() == Rk::Key::Key_f)) { setScaleFactor((scaleFactor() + 0.5 > 2.1) ? 1 : scaleFactor() + 0.5); setFixedSize(MAIN_WINDOW_WIDTH, MAIN_WINDOW_HEIGHT); updateGui(); GeonkickConfig config; config.setScaleFactor(scaleFactor()); config.save(); action onScaleFactor(scaleFactor()); } if (event->modifiers() & static_cast(Rk::KeyModifiers::Control_Left)) envelopeWidget->setPointEditingMode(true); if (event->modifiers() & static_cast(Rk::KeyModifiers::Control) && (event->key() == Rk::Key::Key_h || event->key() == Rk::Key::Key_H)) envelopeWidget->hideEnvelope(true); } else { if (!(event->modifiers() & static_cast(Rk::KeyModifiers::Control_Left))) envelopeWidget->setPointEditingMode(false); if (!(event->modifiers() & static_cast(Rk::KeyModifiers::Control)) || (event->key() == Rk::Key::Key_h || event->key() == Rk::Key::Key_H)) envelopeWidget->hideEnvelope(false); } } void MainWindow::resetToDefault() { auto currId = geonkickApi->currentPercussion(); auto state = geonkickApi->getDefaultPercussionState(); state->setId(currId); state->setName(geonkickApi->getPercussionName(currId)); state->setPlayingKey(geonkickApi->getPercussionPlayingKey(currId)); state->setChannel(geonkickApi->getPercussionChannel(currId)); geonkickApi->setPercussionState(state); geonkickApi->notifyPercussionUpdated(geonkickApi->currentPercussion()); updateGui(); } void MainWindow::dropEvent(RkDropEvent *event) { std::string fileExtention; try { std::filesystem::path path(event->getFilePath()); fileExtention = path.extension(); } catch (const std::exception& e) { GEONKICK_LOG_ERROR("can't create path " << e.what()); return; } std::string file = event->getFilePath(); if (fileExtention == ".gkit" || fileExtention == ".GKIT") { kitModel->open(file); } else if (fileExtention == ".gkick" || fileExtention == ".GKICK") { openPreset(file); } else if (fileExtention == ".wav" || fileExtention == ".WAV" || fileExtention == ".flac" || fileExtention == ".FLAC" || fileExtention == ".ogg" || fileExtention == ".OGG") { setSample(file); } } void MainWindow::setSample(const std::string &file) { auto osc = envelopeWidget->getCurrentOscillator(); if (osc) { osc->setFunction(Oscillator::FunctionType::Sample); geonkickApi->setOscillatorSample(file, osc->index()); geonkickApi->notifyPercussionUpdated(geonkickApi->currentPercussion()); updateGui(); } } void MainWindow::updateLimiter(KitModel::PercussionIndex index) { if (kitModel->isPercussionSelected(index)) limiterWidget->onUpdateLimiter(); } geonkick-2.10.2/src/MainWindow.h000066400000000000000000000050671451017372400164160ustar00rootroot00000000000000/** * File name: mainwindow.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONGKICK_MAINWINDOW_H #define GEONGKICK_MAINWINDOW_H #include "geonkick_widget.h" #include "file_dialog.h" #include "kit_model.h" class Oscillator; class GeonkickApi; class TopBar; class EnvelopeWidget; class ControlArea; class Limiter; class MainWindow : public GeonkickWidget { public: explicit MainWindow(RkMain* app, GeonkickApi *api, const std::string &preset = std::string()); explicit MainWindow(RkMain* app, GeonkickApi *api, const RkNativeWindowInfo &info); ~MainWindow(); bool init(void); RK_DECL_ACT(onScaleFactor, onScaleFactor(double factor), RK_ARG_TYPE(double), RK_ARG_VAL(factor)); protected: void shortcutEvent(RkKeyEvent *event) override; void dropEvent(RkDropEvent *event) override; void openFileDialog(FileDialog::Type type); void openPreset(const std::string &fileName); void savePreset(const std::string &fileName); void setPreset(const std::string &fileName); void openExportDialog(); void openPreset(); void resetToDefault(); void updateLimiter(KitModel::PercussionIndex index); RK_DECL_ACT(updateGui, updateGui(), RK_ARG_TYPE(), RK_ARG_VAL()); void setSample(const std::string &file); private: void createViewState(); void createShortcuts(); GeonkickApi *geonkickApi; std::vector> oscillators; TopBar *topBar; EnvelopeWidget* envelopeWidget; ControlArea* controlAreaWidget; std::string presetName; std::string currentWorkingPath; Limiter *limiterWidget; KitModel *kitModel; }; #endif // GEONKICK_MAINWINDOW_H geonkick-2.10.2/src/MidiKeyWidget.cpp000066400000000000000000000205551451017372400173730ustar00rootroot00000000000000/** * File name: MidiKeyWidget.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "MidiKeyWidget.h" #include MidiKeyWidget::MidiKeyWidget(GeonkickWidget *parent, PercussionModel *model, Rk::WindowFlags flag) : GeonkickWidget(parent, flag) , percussionModel{model} , cellSize{32, 32} , widgetPadding{8} , midiRows{8} , midiColumns{12} { setFixedSize(midiColumns * cellSize.width() + 2 * widgetPadding, midiRows * cellSize.height() + 2 * widgetPadding); setBackgroundColor({68, 68, 70, 240}); RkImage img(size()); RkPainter painter(&img); auto font = painter.font(); font.setSize(10); painter.setFont(font); auto pen = painter.pen(); pen.setColor({10, 10, 10, 230}); pen.setWidth(1); painter.setPen(pen); RkRect borderRect = rect(); borderRect.setSize({borderRect.width() - 1, borderRect.height() - 1}); painter.drawRect(borderRect); pen.setColor({40, 40, 40, 230}); pen.setWidth(1); painter.setPen(pen); GeonkickTypes::MidiKey key = 21; for (int row = 0; row < midiRows && key <= 109; row++) { for (int col = 0; col < midiColumns && key <= 109; col++) drawCell(painter, key++, row, col); } setBackgroundImage(img); selectedCell = getCell(percussionModel->key()); RK_ACT_BIND(percussionModel, keyUpdated, RK_ACT_ARGS(PercussionModel::KeyIndex key), this, onUpdateKey(key)); } void MidiKeyWidget::drawCell(RkPainter &painter, GeonkickTypes::MidiKey key, int row, int col) { RkRect cellRect({widgetPadding + col * cellSize.width(), widgetPadding + row * cellSize.height()}, cellSize); auto penBk = painter.pen(); painter.fillRect(cellRect, {60, 60, 60}); painter.setPen(penBk); painter.drawRect(cellRect); auto pen = penBk; RkFont font; if (col == 0 || row == 0) { font = painter.font(); font.setWeight(RkFont::Weight::Bold); pen.setColor({240, 240, 240}); } else { pen.setColor({200, 200, 200}); font = painter.font(); font.setWeight(RkFont::Weight::Normal); } painter.setPen(pen); painter.setFont(font); painter.drawText(cellRect, midiKeyToNote(key)); painter.setPen(penBk); } void MidiKeyWidget::paintWidget([[maybe_unused]] RkPaintEvent *event) { RkPainter painter(this); if (hoverCell.isValid()) { // Draw cell background. RkColor bkColor(80, 80, 80); RkRect innerRect({hoverCell.rect().left() + 1, hoverCell.rect().top() + 1}, RkSize(hoverCell.rect().width() - 1, hoverCell.rect().height() - 1)); painter.fillRect(innerRect, bkColor); // Draw cell text. RkFont font = painter.font(); font.setSize(10); if (hoverCell.column() == 0 || hoverCell.row() == 0) font.setWeight(RkFont::Weight::Bold); else font.setWeight(RkFont::Weight::Normal); auto pen = painter.pen(); pen.setColor({230, 230, 230}); painter.setPen(pen); painter.setFont(font); painter.drawText(hoverCell.rect(), midiKeyToNote(hoverCell.key())); } if (selectedCell.isValid()) { // Draw cell background. RkColor bkColor; bkColor = RkColor(100, 100, 100); RkRect innerRect({selectedCell.rect().left() + 1, selectedCell.rect().top() + 1}, RkSize(selectedCell.rect().width() - 1, selectedCell.rect().height() - 1)); painter.fillRect(innerRect, bkColor); // Draw cell text. RkFont font = painter.font(); font.setSize(10); if (selectedCell.column() == 0 || selectedCell.row() == 0) font.setWeight(RkFont::Weight::Bold); else font.setWeight(RkFont::Weight::Normal); auto pen = painter.pen(); pen.setColor({230, 230, 230}); painter.setPen(pen); painter.setFont(font); painter.drawText(selectedCell.rect(), midiKeyToNote(selectedCell.key())); } } KeyCell MidiKeyWidget::getCell(int x, int y) const { KeyCell cell; int row = (y - widgetPadding) / cellSize.height(); if (row > midiRows - 1) return KeyCell(); int col = (x - widgetPadding) / cellSize.width(); if (col > midiColumns - 1) return KeyCell(); GeonkickTypes::MidiKey key = row * midiColumns + col + 21; if (key < 21 || key > 109) return KeyCell(); cell.setColumn(col); cell.setRow(row); cell.setKey(key); cell.setRect(RkRect({widgetPadding + col * cellSize.width(), widgetPadding + row * cellSize.height()}, RkSize(cellSize.width(), cellSize.height()))); return cell; } KeyCell MidiKeyWidget::getCell(GeonkickTypes::MidiKey key) const { int row; int col; if (key < 21 || key > 108) { // Any cell. row = 7; col = 4; } else { row = (key - 21) / midiColumns; col = (key - 21) % 12; } KeyCell cell; cell.setColumn(col); cell.setRow(row); cell.setKey(key); cell.setRect(RkRect({widgetPadding + col * cellSize.width(), widgetPadding + row * cellSize.height()}, RkSize(cellSize.width(), cellSize.height()))); return cell; } void MidiKeyWidget::mouseButtonPressEvent(RkMouseEvent *event) { if (event->button() == RkMouseEvent::ButtonType::Left) { if (auto cell = getCell(event->x(), event->y()); cell.isValid() && cell != selectedCell) { selectedCell = cell; percussionModel->setKey(selectedCell.key()); } } } void MidiKeyWidget::mouseButtonReleaseEvent(RkMouseEvent *event) { } void MidiKeyWidget::mouseMoveEvent(RkMouseEvent *event) { if (auto cell = getCell(event->x(), event->y()); cell != hoverCell) { hoverCell = cell; update(); } } RkString MidiKeyWidget::midiKeyToNote(GeonkickTypes::MidiKey key) { if (key < 21 || key > 108) return RkString("Any"); constexpr std::array notes = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}; return RkString(notes[(key - 21 + 9) % 12]) + std::to_string((key - 20 + 9) / 12); } void MidiKeyWidget::onUpdateKey(PercussionModel::KeyIndex key) { selectedCell = getCell(key); update(); } void MidiKeyWidget::closeEvent(RkCloseEvent *event) { action isAboutToClose(); RkWidget::closeEvent(event); } void MidiKeyWidget::hoverEvent(RkHoverEvent *event) { if (!event->isHover()) { hoverCell = KeyCell(); update(); } } geonkick-2.10.2/src/MidiKeyWidget.h000066400000000000000000000077261451017372400170450ustar00rootroot00000000000000/** * File name: MidiKeyWidget.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_MIDIKEY_WIDGET_H #define GEONKICK_MIDIKEY_WIDGET_H #include "geonkick_widget.h" #include "percussion_model.h" #include class KeyCell { public: constexpr KeyCell() : midiKey{0}, cellColumn{-1}, cellRow{-1} {} friend constexpr bool operator==(const KeyCell &c1, const KeyCell &c2) { return c1.rect() == c2.rect() && c1.key() == c2.key() && c1.column() == c2.column() && c1.row() == c2.row(); } friend constexpr bool operator!=(const KeyCell &c1, const KeyCell &c2) { return c1.rect() != c2.rect() || c1.key() != c2.key() || c1.column() != c2.column() || c1.row() != c2.row(); } constexpr void setColumn(int col) { cellColumn = col; } constexpr int column() const { return cellColumn; } constexpr void setRow(int row) { cellRow = row; } constexpr int row() const { return cellRow; } constexpr void setRect(const RkRect &r) { cellRect = r; } constexpr const RkRect& rect() const {return cellRect; } constexpr void setKey(GeonkickTypes::MidiKey k) { midiKey = k; } constexpr GeonkickTypes::MidiKey key() const { return midiKey; } constexpr bool isValid() const { return (cellColumn >= 0 && cellRow >= 0 && cellColumn <= 12 && cellRow <= 7) && ((midiKey >= 21 && midiKey <= 109) || (midiKey == GeonkickTypes::geonkickAnyKey)); } private: RkRect cellRect; GeonkickTypes::MidiKey midiKey; int cellColumn; int cellRow; }; class MidiKeyWidget: public GeonkickWidget { public: MidiKeyWidget(GeonkickWidget *parent, PercussionModel *model, Rk::WindowFlags flag = Rk::WindowFlags::Popup); RK_DECL_ACT(keySelected, keySelected(GeonkickTypes::MidiKey key), RK_ARG_TYPE(GeonkickTypes::MidiKey), RK_ARG_VAL(key)); RK_DECL_ACT(isAboutToClose, isAboutToClose(), RK_ARG_TYPE(), RK_ARG_VAL()); static RkString midiKeyToNote(GeonkickTypes::MidiKey key); protected: void drawCell(RkPainter &painter, GeonkickTypes::MidiKey key, int row, int col); KeyCell getCell(int x, int y) const; KeyCell getCell(GeonkickTypes::MidiKey key) const; void paintWidget(RkPaintEvent *event) override; void mouseButtonPressEvent(RkMouseEvent *event) override; void mouseButtonReleaseEvent(RkMouseEvent *event) override; void mouseMoveEvent(RkMouseEvent *event) override; void onUpdateKey(PercussionModel::KeyIndex key); void closeEvent(RkCloseEvent *event) override; void hoverEvent(RkHoverEvent *event) override; private: PercussionModel *percussionModel; RkSize cellSize; int widgetPadding; int midiRows; int midiColumns; KeyCell selectedCell; KeyCell hoverCell; }; #endif // GEONKICK_MIDIKEY_WIDGET_H geonkick-2.10.2/src/PathListModel.cpp000066400000000000000000000054511451017372400174030ustar00rootroot00000000000000/** * File name: PathListModel.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "PathListModel.h" PathListModel::PathListModel(RkObject *parent) : RkModel(parent) , pathList{"", "", ""} { stringFont.setWeight(RkFont::Weight::Bold); stringFont.setSize(12); } void PathListModel::setHomeDirectory(const std::string &path) { pathList[0] = path; pathList[1] = std::filesystem::path(path) / "Desktop"; pathList[2] = std::filesystem::path(path) / "Downloads"; action modelChanged(); } RkVariant PathListModel::itemData(size_t index, int dataType) const { if (index > itemsNumber() - 1) return RkVariant(); if (static_cast(dataType) == RkModelItem::DataType::String) { if (index == 0) return "Home"; else if (pathList[index].has_filename()) return pathList[index].filename(); } else if (static_cast(dataType) == RkModelItem::DataType::Size) { return RkSize(0, 18); } else if (static_cast(dataType) == RkModelItem::DataType::Font) { return stringFont; } else if (static_cast(dataType) == RkModelItem::DataType::Color) { return RkColor(150, 150, 150); } else if (static_cast(dataType) == PathListDataType::Path) { return pathList[index]; } return RkVariant(); } size_t PathListModel::itemsNumber() const { return pathList.size(); } int PathListModel::itemSpan() const { return 20; } void PathListModel::addPath(const std::filesystem::path &path) { auto it = std::find(pathList.begin(), pathList.end(), path); if (it != pathList.end()) { if (pathList.size() > 8) pathList.erase(pathList.begin() + 3); pathList.push_back(path); action modelChanged(); } } geonkick-2.10.2/src/PathListModel.h000066400000000000000000000031671451017372400170520ustar00rootroot00000000000000/** * File name: PathListModel.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_PATH_LIST_MODEL_H #define GEONKICK_PATH_LIST_MODEL_H #include "globals.h" #include class PathListModel: public RkModel { public: enum class PathListDataType : int { Path = static_cast(RkModelItem::DataType::UserType) }; explicit PathListModel(RkObject *parent); void setHomeDirectory(const std::string &path); RkVariant itemData(size_t index, int dataType = static_cast(RkModelItem::DataType::String)) const override; size_t itemsNumber() const override; int itemSpan() const override; void addPath(const std::filesystem::path &path); private: std::vector pathList; RkFont stringFont; }; #endif // GEONKICK_PATH_LIST_MODEL_H geonkick-2.10.2/src/SampleBrowser.cpp000066400000000000000000000236751451017372400174670ustar00rootroot00000000000000/** * File name: SampleBrowser.cpp * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "SampleBrowser.h" #include "file_dialog.h" #include "ViewState.h" #include "BufferView.h" #include "geonkick_button.h" #include "geonkick_slider.h" #include "RkContainer.h" RK_DECLARE_IMAGE_RC(play_preview_sample); RK_DECLARE_IMAGE_RC(play_preview_sample_hover); RK_DECLARE_IMAGE_RC(play_preview_sample_pressed); RK_DECLARE_IMAGE_RC(load_preview_sample); RK_DECLARE_IMAGE_RC(load_preview_sample_hover); RK_DECLARE_IMAGE_RC(load_preview_sample_pressed); RK_DECLARE_IMAGE_RC(osc1_preview_sample); RK_DECLARE_IMAGE_RC(osc1_preview_sample_hover); RK_DECLARE_IMAGE_RC(osc1_preview_sample_pressed); RK_DECLARE_IMAGE_RC(osc2_preview_sample); RK_DECLARE_IMAGE_RC(osc2_preview_sample_hover); RK_DECLARE_IMAGE_RC(osc2_preview_sample_pressed); SampleBrowser::SampleBrowser(GeonkickWidget *parent, GeonkickApi* api) : GeonkickWidget(parent) , geonkickApi{api} , fileBrowser{new FileDialog(this, FileDialog::Type::Browse, std::string())} , samplePreviewWidget{new BufferView(this)} , playButton{nullptr} , loadButton{nullptr} , osc1Button{nullptr} , osc2Button{nullptr} , previewLimiter{nullptr} { setFixedSize(parent->size()); fileBrowser->setFilters({".wav", ".WAV", ".flac", ".FLAC", ".ogg", ".OGG"}); fileBrowser->setHomeDirectory(geonkickApi->getSettings("GEONKICK_CONFIG/HOME_PATH")); fileBrowser->setCurrentDirectoy(viewState()->samplesBrowserPath()); setPreviewSample(viewState()->samplesBrowserPreviewFile()); RK_ACT_BIND(fileBrowser, directoryChanged, RK_ACT_ARGS(const std::string &path), viewState(), setSamplesBrowserPath(path)); RK_ACT_BIND(fileBrowser, currentFileChanged, RK_ACT_ARGS(const std::string &file), this, setPreviewSample(file)); RK_ACT_BIND(fileBrowser, currentFileChanged, RK_ACT_ARGS(const std::string &file), viewState(), setSamplesBrowserPreviewFile(file)); RK_ACT_BINDL(fileBrowser, selectedFile, RK_ACT_ARGS(const std::string &file), [&](const std::string &file) { viewState()->setSamplesBrowserPreviewFile(file); loadSample(); }); samplePreviewWidget->setSize(250, 260); samplePreviewWidget->show(); RK_ACT_BIND(samplePreviewWidget, graphPressed, RK_ACT_ARGS(), geonkickApi, playSamplePreview()); previewLimiter = new GeonkickSlider(this, GeonkickSlider::Orientation::Vertical); previewLimiter->onSetValue(Geonkick::toDecibel(geonkickApi->samplePreviewLimiter()) + 80); RK_ACT_BIND(previewLimiter, valueUpdated, RK_ACT_ARGS(int val), geonkickApi, setSamplePreviewLimiter(Geonkick::fromDecibel(val - 80))); previewLimiter->setSize(16, samplePreviewWidget->height()); auto mainLayout = new RkContainer(this); mainLayout->setSize(parent->size()); mainLayout->addWidget(fileBrowser); auto previewWidgetContainer = new RkContainer(this, Rk::Orientation::Horizontal); previewWidgetContainer->setSize({samplePreviewWidget->width() + 10 + previewLimiter->width(), samplePreviewWidget->height()}); previewWidgetContainer->addWidget(samplePreviewWidget); previewWidgetContainer->addSpace(8); previewWidgetContainer->addWidget(previewLimiter); auto previewContainer = new RkContainer(this, Rk::Orientation::Vertical); previewContainer->setSize({previewWidgetContainer->width() + 10, mainLayout->height()}); previewContainer->addSpace(35); previewContainer->addContainer(previewWidgetContainer); auto previewMenuContainer = new RkContainer(this); previewMenuContainer->setSize({previewContainer->width(), 25}); createPreviewMenu(previewMenuContainer); previewContainer->addSpace(5); previewContainer->addContainer(previewMenuContainer); mainLayout->addSpace(5); mainLayout->addContainer(previewContainer); setOscillator(viewState()->samplesBrowserOscillator()); show(); } void SampleBrowser::createPreviewMenu(RkContainer* container) { playButton = new GeonkickButton(this); playButton->setType(RkButton::ButtonType::ButtonPush); playButton->setSize(33, 18); playButton->setImage(RkImage(playButton->size(), RK_IMAGE_RC(play_preview_sample)), RkButton::State::Unpressed); playButton->setImage(RkImage(playButton->size(), RK_IMAGE_RC(play_preview_sample_hover)), RkButton::State::UnpressedHover); playButton->setImage(RkImage(playButton->size(), RK_IMAGE_RC(play_preview_sample_pressed)), RkButton::State::Pressed); RK_ACT_BIND(playButton, pressed, RK_ACT_ARGS(), geonkickApi, playSamplePreview()); container->addSpace(5); container->addWidget(playButton); container->addSpace(3); loadButton = new GeonkickButton(this); loadButton->setType(RkButton::ButtonType::ButtonPush); loadButton->setSize(33, 18); loadButton->setImage(RkImage(loadButton->size(), RK_IMAGE_RC(load_preview_sample)), RkButton::State::Unpressed); loadButton->setImage(RkImage(loadButton->size(), RK_IMAGE_RC(load_preview_sample_hover)), RkButton::State::UnpressedHover); loadButton->setImage(RkImage(loadButton->size(), RK_IMAGE_RC(load_preview_sample_pressed)), RkButton::State::Pressed); RK_ACT_BIND(loadButton, pressed, RK_ACT_ARGS(), this, loadSample()); container->addWidget(loadButton); container->addSpace(15); osc1Button = new GeonkickButton(this); osc1Button->setSize(33, 18); osc1Button->setImage(RkImage(osc1Button->size(), RK_IMAGE_RC(osc1_preview_sample)), RkButton::State::Unpressed); osc1Button->setImage(RkImage(osc1Button->size(), RK_IMAGE_RC(osc1_preview_sample_hover)), RkButton::State::UnpressedHover); osc1Button->setImage(RkImage(osc1Button->size(), RK_IMAGE_RC(osc1_preview_sample_pressed)), RkButton::State::Pressed); RK_ACT_BIND(osc1Button, pressed, RK_ACT_ARGS(), this, setOscillator(GeonkickApi::OscillatorType::Oscillator1)); container->addWidget(osc1Button); container->addSpace(3); osc2Button = new GeonkickButton(this); osc2Button->setSize(33, 18); osc2Button->setImage(RkImage(osc2Button->size(), RK_IMAGE_RC(osc2_preview_sample)), RkButton::State::Unpressed); osc2Button->setImage(RkImage(osc2Button->size(), RK_IMAGE_RC(osc2_preview_sample_hover)), RkButton::State::UnpressedHover); osc2Button->setImage(RkImage(osc2Button->size(), RK_IMAGE_RC(osc2_preview_sample_pressed)), RkButton::State::Pressed); RK_ACT_BIND(osc2Button, pressed, RK_ACT_ARGS(), this, setOscillator(GeonkickApi::OscillatorType::Oscillator2)); container->addWidget(osc2Button); } void SampleBrowser::setOscillator(GeonkickApi::OscillatorType osc) { osc1Button->setPressed(osc == GeonkickApi::OscillatorType::Oscillator1); osc2Button->setPressed(osc == GeonkickApi::OscillatorType::Oscillator2); } void SampleBrowser::loadSample() { if (osc1Button->isPressed()) { geonkickApi->setOscillatorSample(samplePreviewWidget->getData(), static_cast(GeonkickApi::OscillatorType::Oscillator1)); geonkickApi->setOscillatorFunction(static_cast(GeonkickApi::OscillatorType::Oscillator1), GeonkickApi::FunctionType::Sample); } else if (osc2Button->isPressed()) { geonkickApi->setOscillatorSample(samplePreviewWidget->getData(), static_cast(GeonkickApi::OscillatorType::Oscillator2)); geonkickApi->setOscillatorFunction(static_cast(GeonkickApi::OscillatorType::Oscillator2), GeonkickApi::FunctionType::Sample); } } void SampleBrowser::setPreviewSample(const std::string &file) { try { if (std::filesystem::exists(file) && !std::filesystem::is_directory(file)) { std::vector data = geonkickApi->setPreviewSample(file); if (!data.empty()) { samplePreviewWidget->setData(data); geonkickApi->playSamplePreview(); } } } catch (...) { } } geonkick-2.10.2/src/SampleBrowser.h000066400000000000000000000033531451017372400171230ustar00rootroot00000000000000/** * File name: file_dialog.h * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_SAMPLE_BROWSER_H #define GEONKICK_SAMPLE_BROWSER_H #include "geonkick_widget.h" #include "geonkick_api.h" class FileDialog; class BufferView; class GeonkickButton; class RkContainer; class GeonkickSlider; class SampleBrowser: public GeonkickWidget { public: SampleBrowser(GeonkickWidget *parent, GeonkickApi* api); protected: void setPreviewSample(const std::string &file); void setOscillator(GeonkickApi::OscillatorType osc); void loadSample(); private: void createPreviewMenu(RkContainer* container); GeonkickApi *geonkickApi; FileDialog *fileBrowser; BufferView *samplePreviewWidget; GeonkickButton *playButton; GeonkickButton *loadButton; GeonkickButton *osc1Button; GeonkickButton *osc2Button; GeonkickSlider *previewLimiter; }; #endif // GEONKICK_SAMPLE_BROWSER_H geonkick-2.10.2/src/UiSettings.cpp000066400000000000000000000120771451017372400167720ustar00rootroot00000000000000/** * File name: UiSettings.h * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "UiSettings.h" UiSettings::UiSettings() : mainView{UiSettings::View::Controls} , samplesBrowser{"", "", UiSettings::Oscillator::Oscillator1} { } void UiSettings::fromJsonObject(const rapidjson::Value &obj) { for (const auto &m: obj.GetObject()) { if (m.name == "MainView" && m.value.IsInt()) mainView = static_cast(m.value.GetInt()); if (m.name == "SamplesBrowser" && m.value.IsObject()) parserSamplesBrowser(m.value); } } void UiSettings::parserSamplesBrowser(const rapidjson::Value &obj) { for (const auto &m: obj.GetObject()) { if (m.name == "currentDirectory" && m.value.IsString()) samplesBrowser.currentDirectory = m.value.GetString(); if (m.name == "previewFile" && m.value.IsString()) samplesBrowser.previewFile = m.value.GetString(); if (m.name == "limiterValue" && m.value.IsDouble()) samplesBrowser.previewLimiter = m.value.GetDouble(); if (m.name == "oscillator" && m.value.IsInt()) samplesBrowser.oscillator = static_cast(m.value.GetInt()); if (m.name == "settings" && m.value.IsArray()) { for (const auto &el: m.value.GetArray()) { if (el.IsArray() && el.GetArray().Size() == 2) uiSettings.insert({el.GetArray()[0].GetString(), el.GetArray()[1].GetString()}); } } } } std::string UiSettings::toJson() const { std::ostringstream jsonStream; jsonStream << std::fixed << std::setprecision(7); jsonStream << "{" << std::endl; jsonStream << "\"MainView\": " << static_cast(mainView) << ", " << std::endl; jsonStream << "\"SamplesBrowser\": { " << std::endl; jsonStream << " \"currentDirectory\": \"" << samplesBrowser.currentDirectory << "\"," << std::endl; jsonStream << " \"previewFile\": \"" << samplesBrowser.previewFile << "\"," << std::endl; jsonStream << " \"previewLimiter\": " << samplesBrowser.previewLimiter << "," << std::endl; jsonStream << " \"oscillator\": " << static_cast(samplesBrowser.oscillator) << std::endl; jsonStream << "}," << std::endl; jsonStream << "\"settings\": [" << std::endl; bool first = true; for (const auto &e: uiSettings) { if (!first) jsonStream << ", "; jsonStream << "[\"" << e.first << "\", \"" << e.second << "\"]"; first = false; } jsonStream << "]" << std::endl; jsonStream << "}" << std::endl; return jsonStream.str(); } UiSettings::View UiSettings::getMainView() const { return mainView; } void UiSettings::setMainView(View view) { mainView = view; } void UiSettings::setSamplesBrowserPath(const std::string &path) { samplesBrowser.currentDirectory = path; } std::string UiSettings::samplesBrowserPath() const { return samplesBrowser.currentDirectory; } void UiSettings::setSamplesBrowserPreviewFile(const std::string &file) { samplesBrowser.previewFile = file; } std::string UiSettings::samplesBrowserPreviewFile() const { return samplesBrowser.previewFile; } void UiSettings::setSamplesPreviewLimiter(double &value) { samplesBrowser.previewLimiter = value; } double UiSettings::samplesPreviewLimiter() const { return samplesBrowser.previewLimiter; } void UiSettings::setSamplesBrowserOscillator(UiSettings::Oscillator osc) { samplesBrowser.oscillator = osc; } UiSettings::Oscillator UiSettings::samplesBrowserOscillator() const { return samplesBrowser.oscillator; } void UiSettings::setSettings(const std::string &key, const std::string &value) { uiSettings[key] = value; } std::string UiSettings::getSettings(const std::string &key) const { if (auto it = uiSettings.find(key); it != uiSettings.end()) return it->second; return std::string(); } geonkick-2.10.2/src/UiSettings.h000066400000000000000000000046221451017372400164340ustar00rootroot00000000000000/** * File name: UiSettings.h * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONGKICK_SETTINGS_H #define GEONGKICK_SETTINGS_H #include "globals.h" #include "ViewState.h" #include #include #include class UiSettings { public: using View = ViewState::View; using Oscillator = GeonkickApi::OscillatorType; UiSettings(); void fromJsonObject(const rapidjson::Value &obj); std::string toJson() const; View getMainView() const; void setMainView(View view); void setSamplesBrowserPath(const std::string &path); std::string samplesBrowserPath() const; void setSamplesBrowserPreviewFile(const std::string &file); std::string samplesBrowserPreviewFile() const; void setSamplesPreviewLimiter(double &value); double samplesPreviewLimiter() const; void setSamplesBrowserOscillator(UiSettings::Oscillator osc); UiSettings::Oscillator samplesBrowserOscillator() const; void setSettings(const std::string &key, const std::string &value); std::string getSettings(const std::string &key) const; protected: void parserSamplesBrowser(const rapidjson::Value &obj); private: struct SamplesBrowser { std::string currentDirectory; std::string previewFile; Oscillator oscillator; double previewLimiter; }; View mainView; SamplesBrowser samplesBrowser; std::unordered_map uiSettings; }; #endif // GEONGKICK_UI_SETTINGS_H geonkick-2.10.2/src/ViewState.cpp000066400000000000000000000076271451017372400166140ustar00rootroot00000000000000/** * File name: ViewState.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "ViewState.h" #include ViewState::ViewState(RkObject *parent) : RkObject(parent) , mainView{ViewState::View::Controls} , samplesBrowser{"", "", ViewState::Oscillator::Oscillator1} , envelopeType{Envelope::Type::Amplitude} , envelopeCategory{Envelope::Category::General} { } ViewState::View ViewState::getMainView() const { return mainView; } void ViewState::setMainView(ViewState::View view) { if (mainView != view) { mainView = view; auto act = std::make_unique(this); act->setCallback([this](void){ mainViewChanged(mainView); }); eventQueue()->postAction(std::move(act)); } } void ViewState::setSamplesBrowserPath(const std::string &path) { if (path != samplesBrowser.currentDirectory) { samplesBrowser.currentDirectory = path; action samplesBrowserPathChanged(samplesBrowser.currentDirectory); } } void ViewState::setSamplesBrowserPreviewFile(const std::string &file) { samplesBrowser.previewFile = file; } std::string ViewState::samplesBrowserPath() const { return samplesBrowser.currentDirectory; } std::string ViewState::samplesBrowserPreviewFile() const { return samplesBrowser.previewFile; } void ViewState::setSamplesBrowserOscillator(ViewState::Oscillator osc) { if (osc != samplesBrowser.oscillator) { samplesBrowser.oscillator = osc; action samplesBrowserOscChanged(samplesBrowser.oscillator); } } ViewState::Oscillator ViewState::samplesBrowserOscillator() const { return samplesBrowser.oscillator; } void ViewState::setEnvelope(Envelope::Category category, Envelope::Type envelope) { if (envelopeCategory != category || envelopeType != envelope) { envelopeCategory = category; envelopeType = envelope; auto act = std::make_unique(this); act->setCallback([this](void){envelopeChanged(envelopeCategory, envelopeType); }); eventQueue()->postAction(std::move(act)); } } void ViewState::setEnvelopeType(Envelope::Type envelope) { if (envelopeType != envelope) { envelopeType = envelope; auto act = std::make_unique(this); act->setCallback([this](void){envelopeChanged(envelopeCategory, envelopeType); }); eventQueue()->postAction(std::move(act)); } } Envelope::Type ViewState::getEnvelopeType() const { return envelopeType; } void ViewState::setEnvelopeCategory(Envelope::Category category) { if (envelopeCategory != category) { envelopeCategory = category; auto act = std::make_unique(this); act->setCallback([this](void){ envelopeChanged(envelopeCategory, envelopeType); }); eventQueue()->postAction(std::move(act)); } } Envelope::Category ViewState::getEnvelopeCategory() const { return envelopeCategory; } geonkick-2.10.2/src/ViewState.h000066400000000000000000000071711451017372400162530ustar00rootroot00000000000000/** * File name: ViewState.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONGKICK_VIEW_STATE_H #define GEONGKICK_VIEW_STATE_H #include "globals.h" #include "geonkick_api.h" #include "envelope.h" #include class ViewState: public RkObject { public: enum class View : int { Controls = 0, Kit = 1, Presets = 2, Samples = 3 }; using Oscillator = GeonkickApi::OscillatorType; ViewState(RkObject *parent); RK_DECL_ACT(mainViewChanged, mainViewChanged(ViewState::View view), RK_ARG_TYPE(ViewState::View), RK_ARG_VAL(view)); RK_DECL_ACT(samplesBrowserPathChanged, samplesBrowserPathChanged(const std::string &path), RK_ARG_TYPE(const std::string&), RK_ARG_VAL(path)); RK_DECL_ACT(samplesBrowserOscChanged, samplesBrowserOscChanged(ViewState::Oscillator osc), RK_ARG_TYPE(ViewState::Oscillator), RK_ARG_VAL(osc)); RK_DECL_ACT(envelopeCategoryChanged, envelopeCategoryChanged(Envelope::Category category), RK_ARG_TYPE(Envelope::Category), RK_ARG_VAL(category)); RK_DECL_ACT(envelopeTypeChanged, envelopeTypeChanged(Envelope::Type envelope), RK_ARG_TYPE(Envelope::Type), RK_ARG_VAL(envelope)); RK_DECL_ACT(envelopeChanged, envelopeChanged(Envelope::Category category, Envelope::Type envelope), RK_ARG_TYPE(Envelope::Category, Envelope::Type), RK_ARG_VAL(category, envelope)); ViewState::View getMainView() const; void setMainView(ViewState::View view); void setSamplesBrowserPath(const std::string &path); void setSamplesBrowserPreviewFile(const std::string &file); std::string samplesBrowserPath() const; std::string samplesBrowserPreviewFile() const; void setSamplesBrowserOscillator(Oscillator osc); Oscillator samplesBrowserOscillator() const; void setEnvelope(Envelope::Category category, Envelope::Type envelope); void setEnvelopeCategory(Envelope::Category category); Envelope::Category getEnvelopeCategory() const; void setEnvelopeType(Envelope::Type envelope); Envelope::Type getEnvelopeType() const; private: struct SamplesBrowser { std::string currentDirectory; std::string previewFile; Oscillator oscillator; }; ViewState::View mainView; SamplesBrowser samplesBrowser; Envelope::Type envelopeType; Envelope::Category envelopeCategory; }; #endif // GEONGKICK_VIEW_STATE_H geonkick-2.10.2/src/compressor_group_box.cpp000066400000000000000000000165521451017372400211560ustar00rootroot00000000000000/** * File name: compressor_group_box.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "compressor_group_box.h" #include "geonkick_slider.h" #include "geonkick_button.h" #include "geonkick_api.h" #include #include RK_DECLARE_IMAGE_RC(compressor_enable); RK_DECLARE_IMAGE_RC(compressor_enable_hover); RK_DECLARE_IMAGE_RC(compressor_enable_active); RK_DECLARE_IMAGE_RC(compressor_attack_label); RK_DECLARE_IMAGE_RC(compressor_threshold_label); RK_DECLARE_IMAGE_RC(compressor_ratio_label); RK_DECLARE_IMAGE_RC(compressor_makeup_label); CompressorGroupBox::CompressorGroupBox(GeonkickApi *api, GeonkickWidget *parent) : GeonkickGroupBox(parent) , geonkickApi{api} , attackSlider{nullptr} , thresholdSlider{nullptr} , ratioSlider{nullptr} , makeupSlider{nullptr} , compressorCheckbox{nullptr} { setFixedSize(224, 63); compressorCheckbox = new GeonkickButton(this); RK_ACT_BIND(compressorCheckbox, toggled, RK_ACT_ARGS(bool b), geonkickApi, enableCompressor(b)); compressorCheckbox->setCheckable(true); compressorCheckbox->setFixedSize(78, 21); compressorCheckbox->setImage(RkImage(compressorCheckbox->size(), RK_IMAGE_RC(compressor_enable)), RkButton::State::Unpressed); compressorCheckbox->setImage(RkImage(compressorCheckbox->size(), RK_IMAGE_RC(compressor_enable_active)), RkButton::State::Pressed); compressorCheckbox->setImage(RkImage(compressorCheckbox->size(), RK_IMAGE_RC(compressor_enable_hover)), RkButton::State::UnpressedHover); compressorCheckbox->setImage(RkImage(compressorCheckbox->size(), RK_IMAGE_RC(compressor_enable_hover)), RkButton::State::PressedHover); compressorCheckbox->setPosition((width() - compressorCheckbox->width()) / 2, 0); int sliderW = 56; int sliderH = 14; auto vContainer = new RkContainer(this, Rk::Orientation::Vertical); vContainer->setSize(size()); vContainer->addSpace(32); auto hContainer = new RkContainer(this); hContainer->setSize({vContainer->width(), sliderH}); auto attackLabel = new RkLabel(this); attackLabel->setBackgroundColor(background()); attackLabel->setFixedSize(53, 14); attackLabel->setImage(RkImage(attackLabel->size(), RK_IMAGE_RC(compressor_attack_label))); attackLabel->show(); hContainer->addWidget(attackLabel); attackSlider = new GeonkickSlider(this); attackSlider->setFixedSize(sliderW, 12); attackSlider->onSetValue(50); RK_ACT_BIND(attackSlider, valueUpdated, RK_ACT_ARGS(int val), this, setAttack(val)); hContainer->addSpace(3); hContainer->addWidget(attackSlider); auto ratioLabel = new RkLabel(this); ratioLabel->setBackgroundColor(background()); ratioLabel->setFixedSize(53, 14); ratioLabel->setImage(RkImage(ratioLabel->size(), RK_IMAGE_RC(compressor_ratio_label))); ratioLabel->show(); hContainer->addWidget(ratioLabel); ratioSlider = new GeonkickSlider(this); ratioSlider->setFixedSize(sliderW, 12); RK_ACT_BIND(ratioSlider, valueUpdated, RK_ACT_ARGS(int value), this, setRatio(value)); hContainer->addWidget(ratioSlider); vContainer->addContainer(hContainer); vContainer->addSpace(3); hContainer = new RkContainer(this); hContainer->setSize({vContainer->width(), sliderH}); auto thresholdLabel = new RkLabel(this); thresholdLabel->setBackgroundColor(background()); thresholdLabel->setFixedSize(53, 14); thresholdLabel->setImage(RkImage(thresholdLabel->size(), RK_IMAGE_RC(compressor_threshold_label))); thresholdLabel->show(); hContainer->addWidget(thresholdLabel); thresholdSlider = new GeonkickSlider(this); thresholdSlider->setFixedSize(sliderW, 12); RK_ACT_BIND(thresholdSlider, valueUpdated, RK_ACT_ARGS(int value), this, setThreshold(value)); hContainer->addSpace(3); hContainer->addWidget(thresholdSlider); auto makeupLabel = new RkLabel(this); makeupLabel->setFixedSize(53, 14); makeupLabel->setBackgroundColor(background()); makeupLabel->setImage(RkImage(makeupLabel->size(), RK_IMAGE_RC(compressor_makeup_label))); makeupLabel->show(); hContainer->addWidget(makeupLabel); makeupSlider = new GeonkickSlider(this); makeupSlider->setFixedSize(sliderW, 12); RK_ACT_BIND(makeupSlider, valueUpdated, RK_ACT_ARGS(int value), this, setMakeup(value)); hContainer->addWidget(makeupSlider); vContainer->addContainer(hContainer); vContainer->update(); show(); updateGui(); } void CompressorGroupBox::setAttack(int val) { if (val == 0) geonkickApi->setCompressorAttack(0); else geonkickApi->setCompressorAttack(pow(10, (static_cast(val) / 100) * log10(2000)) / 1000); } void CompressorGroupBox::setThreshold(int val) { double db = -60.0 * static_cast(100 - val) / 100; geonkickApi->setCompressorThreshold(pow(10, db / 20)); } void CompressorGroupBox::setRatio(int val) { double v = log2(19) * static_cast(val) / 100; geonkickApi->setCompressorRatio(pow(2, v)); } void CompressorGroupBox::setMakeup(int val) { double db = 36.0 * static_cast(val) / 100; geonkickApi->setCompressorMakeup(pow(10, db / 20)); } void CompressorGroupBox::updateGui() { compressorCheckbox->setPressed(geonkickApi->isCompressorEnabled()); // Attack attackSlider->onSetValue(100 * (log10(1000 * geonkickApi->getCompressorAttack()) / log10(2000))); // Threshold auto threshold = geonkickApi->getCompressorThreshold(); double db = 0; if (threshold < std::numeric_limitsgetCompressorThreshold())>::min()) db = -60; else db = 20.0 * log10(threshold); thresholdSlider->onSetValue(100 - 100 * (db / -60)); // Ratio double ratio = geonkickApi->getCompressorRatio(); if (ratio < 1.0) ratio = 0; else ratio = log2(ratio); ratioSlider->onSetValue(100 * ratio / log2(19)); // Makeup double makeup = geonkickApi->getCompressorMakeup(); if (makeup < 1.0) makeup = 1.0; db = 20 * log10(makeup); makeupSlider->onSetValue(100 * (db / 36)); } geonkick-2.10.2/src/compressor_group_box.h000066400000000000000000000031611451017372400206130ustar00rootroot00000000000000/** * File name: compressor_group_box.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_COMPRESSOR_GROUPBOX_H #define GEONKICK_COMPRESSOR_GROUPBOX_H #include "geonkick_groupbox.h" class GeonkickSlider; class GeonkickCheckbox; class GeonkickApi; class GeonkickButton; class CompressorGroupBox: public GeonkickGroupBox { public: CompressorGroupBox(GeonkickApi *api, GeonkickWidget* parent); void updateGui(); void setAttack(int val); void setThreshold(int val); void setRatio(int val); void setMakeup(int val); private: GeonkickApi *geonkickApi; GeonkickSlider *attackSlider; GeonkickSlider *thresholdSlider; GeonkickSlider *ratioSlider; GeonkickSlider *makeupSlider; GeonkickButton *compressorCheckbox; }; #endif // GEONKICK_COMPRESSOR_WIDGET_H geonkick-2.10.2/src/control_area.cpp000066400000000000000000000067411451017372400173450ustar00rootroot00000000000000/** * File name: control_area.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "control_area.h" #include "controls_widget.h" #include "kit_model.h" #include "kit_widget.h" #include "preset_browser_model.h" #include "preset_browser_view.h" #include "SampleBrowser.h" ControlArea::ControlArea(GeonkickWidget *parent, KitModel* model, const std::vector> &oscillators) : GeonkickWidget(parent) , kitModel{model} , oscillators{oscillators} , presetsModel{new PresetBrowserModel(this, kitModel->api())} , currentWidget{nullptr} { setFixedSize(920, 370); RK_ACT_BIND(viewState(), mainViewChanged, RK_ACT_ARGS(ViewState::View view), this, showWidget(view)); showWidget(viewState()->getMainView()); } void ControlArea::showWidget(ViewState::View view) { switch (view) { case ViewState::View::Controls: showControls(); break; #ifndef GEONKICK_SINGLE case ViewState::View::Kit: showKit(); break; #endif // GEONKICK_SINGLE case ViewState::View::Presets: showPresets(); break; case ViewState::View::Samples: if (currentWidget) currentWidget->close(); currentWidget = new SampleBrowser(this, kitModel->api()); break; default: showControls(); } } void ControlArea::showControls() { if (!dynamic_cast(currentWidget)) { if (currentWidget) currentWidget->close(); auto controlsWidget = new ControlsWidget(this, kitModel->api(), oscillators); RK_ACT_BIND(this, updateGui, RK_ACT_ARGS(), controlsWidget, updateGui()); controlsWidget->setSize({width(), height()}); currentWidget = controlsWidget; currentWidget->show(); } } #ifndef GEONKICK_SINGLE void ControlArea::showKit() { if (!dynamic_cast(currentWidget)) { if (currentWidget) currentWidget->close(); currentWidget = new KitWidget(this, kitModel); currentWidget->show(); } } #endif // GEONKICK_SINGLE void ControlArea::showPresets() { if (!dynamic_cast(currentWidget)) { if (currentWidget) currentWidget->close(); currentWidget = new PresetBrowserView(this, presetsModel); currentWidget->show(); } } KitModel* ControlArea::getKitModel() const { return kitModel; } geonkick-2.10.2/src/control_area.h000066400000000000000000000036531451017372400170110ustar00rootroot00000000000000/** * File name: control_area.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GKICK_CONTROL_AREA_H #define GKICK_CONTROL_AREA_H #include "envelope.h" #include "geonkick_widget.h" #include "ViewState.h" class Oscillator; class ControlsWidget; class KitModel; #ifndef GEONKICK_SINGLE class KitWidget; #endif // GEONKICK_SINGLE class EnvelopeWidget; class PresetBrowserView; class PresetBrowserModel; class ControlArea: public GeonkickWidget { public: ControlArea(GeonkickWidget *parent, KitModel* model, const std::vector> &oscillators); ~ControlArea() = default; RK_DECL_ACT(updateGui, updateGui(), RK_ARG_TYPE(), RK_ARG_VAL()); void showControls(); #ifndef GEONKICK_SINGLE void showKit(); #endif // GEONKICK_SINGLE void showPresets(); KitModel* getKitModel() const; private: void showWidget(ViewState::View view); private: KitModel *kitModel; const std::vector> &oscillators; PresetBrowserModel *presetsModel; RkWidget* currentWidget; }; #endif // GKICK_CONTROL_AREA_H geonkick-2.10.2/src/controls_widget.cpp000066400000000000000000000052001451017372400200700ustar00rootroot00000000000000/** * File name: controls_widget.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "controls_widget.h" #include "oscillator.h" #include "oscillator_group_box.h" #include "general_group_box.h" #include "effects_group_box.h" #include "geonkick_api.h" ControlsWidget::ControlsWidget(GeonkickWidget *parent, GeonkickApi* api, const std::vector> &oscillators) : GeonkickWidget(parent) , geonkickApi{api} { auto oscillator = oscillators[static_cast(Oscillator::Type::Oscillator1)].get(); auto widget = new OscillatorGroupBox(this, oscillator); widget->setPosition(0, 0); RK_ACT_BIND(this, updateGui, RK_ACT_ARGS(), widget, updateGui()); widget->show(); oscillator = oscillators[static_cast(Oscillator::Type::Oscillator2)].get(); widget = new OscillatorGroupBox(this, oscillator); widget->setPosition(8 + 224, 0); RK_ACT_BIND(this, updateGui, RK_ACT_ARGS(), widget, updateGui()); widget->show(); oscillator = oscillators[static_cast(Oscillator::Type::Noise)].get(); widget = new OscillatorGroupBox(this, oscillator); widget->setPosition(2 * (8 + 224), 0); RK_ACT_BIND(this, updateGui, RK_ACT_ARGS(), widget, updateGui()); widget->show(); auto generalWidget = new GeneralGroupBox(this, geonkickApi); generalWidget->setPosition(3 * (8 + 224), 0); RK_ACT_BIND(this, updateGui, RK_ACT_ARGS(), generalWidget, updateGui()); generalWidget->show(); effectsWidget = new EffectsGroupBox(geonkickApi, this); effectsWidget->setFixedSize(500, 82); effectsWidget->setPosition(2 * (8 + 224), 283); RK_ACT_BIND(this, updateGui, RK_ACT_ARGS(), effectsWidget, updateGui()); effectsWidget->show(); } geonkick-2.10.2/src/controls_widget.h000066400000000000000000000027131451017372400175430ustar00rootroot00000000000000/** * File name: controls_widget.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef CONTROLS_WIDGET_H #define CONTROLS_WIDGET_H #include "geonkick_widget.h" class GeonkickApi; class Oscillator; class EnvelopeWidget; class EffectsGroupBox; class ControlsWidget: public GeonkickWidget { public: ControlsWidget(GeonkickWidget *parent, GeonkickApi* api, const std::vector> &oscillators); ~ControlsWidget() = default; RK_DECL_ACT(updateGui, updateGui(), RK_ARG_TYPE(), RK_ARG_VAL()); private: GeonkickApi* geonkickApi; EffectsGroupBox *effectsWidget; }; #endif // CONTROLS_WIDGET_H geonkick-2.10.2/src/distortion_group_box.cpp000066400000000000000000000225411451017372400211530ustar00rootroot00000000000000/** * File name: distortion_group_box.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "distortion_group_box.h" #include "geonkick_slider.h" #include "geonkick_button.h" #include "geonkick_api.h" #include "envelope_widget.h" #include "ViewState.h" #include RK_DECLARE_IMAGE_RC(distortion_enable); RK_DECLARE_IMAGE_RC(distortion_enable_hover); RK_DECLARE_IMAGE_RC(distortion_enable_active); RK_DECLARE_IMAGE_RC(distortion_in_limiter); RK_DECLARE_IMAGE_RC(distortion_volume_env); RK_DECLARE_IMAGE_RC(distortion_volume_env_hover); RK_DECLARE_IMAGE_RC(distortion_volume_env_active); RK_DECLARE_IMAGE_RC(distortion_drive_env); RK_DECLARE_IMAGE_RC(distortion_drive_env_hover); RK_DECLARE_IMAGE_RC(distortion_drive_env_active); RK_DECLARE_IMAGE_RC(env_button); RK_DECLARE_IMAGE_RC(env_button_hover); RK_DECLARE_IMAGE_RC(env_button_on); DistortionGroupBox::DistortionGroupBox(GeonkickApi *api, GeonkickWidget *parent) : GeonkickGroupBox(parent) , geonkickApi{api} , volumeSlider{nullptr} , driveSlider{nullptr} , distortionCheckbox{nullptr} , volumeEnvButton{nullptr} , driveEnvButton{nullptr} { setFixedSize(134, 75); distortionCheckbox = new GeonkickButton(this); distortionCheckbox->setSize(69, 21); distortionCheckbox->setCheckable(true); RK_ACT_BIND(distortionCheckbox, toggled, RK_ACT_ARGS(bool b), geonkickApi, enableDistortion(b)); distortionCheckbox->setImage(RkImage(distortionCheckbox->size(), RK_IMAGE_RC(distortion_enable)), RkButton::State::Unpressed); distortionCheckbox->setImage(RkImage(distortionCheckbox->size(), RK_IMAGE_RC(distortion_enable_active)), RkButton::State::Pressed); distortionCheckbox->setImage(RkImage(distortionCheckbox->size(), RK_IMAGE_RC(distortion_enable_hover)), RkButton::State::UnpressedHover); distortionCheckbox->setImage(RkImage(distortionCheckbox->size(), RK_IMAGE_RC(distortion_enable_hover)), RkButton::State::PressedHover); distortionCheckbox->setPosition((width() - distortionCheckbox->width()) / 2, 0); int sliderW = 60; int sliderH = 14; int yoffset = 8; int labelD = 5; // In limiter inLimiterSlider = new GeonkickSlider(this); inLimiterSlider->setFixedSize(sliderW, sliderH); inLimiterSlider->setPosition(width() / 2 + labelD / 2, yoffset + (height() - sliderH) / 3); inLimiterSlider->onSetValue(50); RK_ACT_BIND(inLimiterSlider, valueUpdated, RK_ACT_ARGS(int val), this, setInLimiter(val)); auto inLimiterLabel = new RkLabel(this); inLimiterLabel->setBackgroundColor(background()); inLimiterLabel->show(); inLimiterLabel->setFixedSize(53, 14); inLimiterLabel->setImage(RkImage(inLimiterLabel->size(), RK_IMAGE_RC(distortion_in_limiter))); inLimiterLabel->setPosition(inLimiterSlider->x() - inLimiterLabel->width() - labelD, inLimiterSlider->y()); // Volume volumeSlider = new GeonkickSlider(this); volumeSlider->setFixedSize(sliderW, sliderH); volumeSlider->setPosition(width() / 2 + labelD / 2, yoffset + (height() - sliderH) / 3 + sliderH + 2); volumeSlider->onSetValue(50); RK_ACT_BIND(volumeSlider, valueUpdated, RK_ACT_ARGS(int val), this, setVolume(val)); volumeEnvButton = new GeonkickButton(this); volumeEnvButton->setPressed(viewState()->getEnvelopeType() == Envelope::Type::DistortionDrive && viewState()->getEnvelopeCategory() == Envelope::Category::General); volumeEnvButton->show(); volumeEnvButton->setFixedSize(53, 14); volumeEnvButton->setImage(RkImage(volumeEnvButton->size(), RK_IMAGE_RC(distortion_volume_env)), RkButton::State::Unpressed); volumeEnvButton->setImage(RkImage(volumeEnvButton->size(), RK_IMAGE_RC(distortion_volume_env_active)), RkButton::State::Pressed); volumeEnvButton->setImage(RkImage(volumeEnvButton->size(), RK_IMAGE_RC(distortion_volume_env_hover)), RkButton::State::UnpressedHover); volumeEnvButton->setImage(RkImage(volumeEnvButton->size(), RK_IMAGE_RC(distortion_volume_env_hover)), RkButton::State::PressedHover); volumeEnvButton->setPosition(volumeSlider->x() - labelD - volumeEnvButton->width(), volumeSlider->y()); RK_ACT_BIND(viewState(), envelopeChanged, RK_ACT_ARGS(Envelope::Category category, Envelope::Type envelope), volumeEnvButton, setPressed(envelope == Envelope::Type::DistortionVolume && Envelope::Category::General == category)); RK_ACT_BIND(volumeEnvButton, pressed, RK_ACT_ARGS(), viewState(), setEnvelope(Envelope::Category::General, Envelope::Type::DistortionVolume)); // Drive driveSlider = new GeonkickSlider(this); driveSlider->setFixedSize(sliderW, sliderH); driveSlider->setPosition(width() / 2 + labelD / 2, yoffset + (height() - sliderH) / 3 + 2 * sliderH + 4); RK_ACT_BIND(driveSlider, valueUpdated, RK_ACT_ARGS(int val), this, setDrive(val)); driveEnvButton = new GeonkickButton(this); driveEnvButton->setPressed(viewState()->getEnvelopeType() == Envelope::Type::DistortionDrive && viewState()->getEnvelopeCategory()== Envelope::Category::General); driveEnvButton->setFixedSize(53, 14); driveEnvButton->setImage(RkImage(driveEnvButton->size(), RK_IMAGE_RC(distortion_drive_env)), RkButton::State::Unpressed); driveEnvButton->setImage(RkImage(driveEnvButton->size(), RK_IMAGE_RC(distortion_drive_env_active)), RkButton::State::Pressed); driveEnvButton->setImage(RkImage(driveEnvButton->size(), RK_IMAGE_RC(distortion_drive_env_hover)), RkButton::State::UnpressedHover); driveEnvButton->setImage(RkImage(driveEnvButton->size(), RK_IMAGE_RC(distortion_drive_env_hover)), RkButton::State::PressedHover); driveEnvButton->setPosition(driveSlider->x() - driveEnvButton->width() - labelD, driveSlider->y()); driveEnvButton->show(); RK_ACT_BIND(viewState(), envelopeChanged, RK_ACT_ARGS(Envelope::Category category, Envelope::Type envelope), driveEnvButton, setPressed(envelope == Envelope::Type::DistortionDrive && Envelope::Category::General == category)); RK_ACT_BIND(driveEnvButton, pressed, RK_ACT_ARGS(), viewState(), setEnvelope(Envelope::Category::General, Envelope::Type::DistortionDrive)); show(); updateGui(); } void DistortionGroupBox::setInLimiter(int val) { double logVal = -60 * (1.0 - (static_cast(val) / 100)); double limit = pow(10, logVal / 20); geonkickApi->setDistortionInLimiter(limit); } void DistortionGroupBox::setVolume(int val) { double logVal = -60 * (1.0 - (static_cast(val) / 100)); double volume = pow(10, logVal / 20); geonkickApi->setDistortionVolume(volume); } void DistortionGroupBox::setDrive(int val) { double db = 36.0 * static_cast(val) / 100.0; geonkickApi->setDistortionDrive(pow(10, db / 20)); } void DistortionGroupBox::updateGui() { distortionCheckbox->setPressed(geonkickApi->isDistortionEnabled()); // In Limiter double limit = geonkickApi->getDistortionInLimiter(); double logVal; if (limit > 0) logVal = 20 * log10(limit); else logVal = 60; inLimiterSlider->onSetValue(100 * (60 - fabs(logVal)) / 60); // Volume double volume = geonkickApi->getDistortionVolume(); if (volume > 0) logVal = 20 * log10(volume); else logVal = 60; volumeSlider->onSetValue(100 * (60 - fabs(logVal)) / 60); // Drive auto distortion = geonkickApi->getDistortionDrive(); if (distortion < std::numeric_limits::min()) distortion = 0; else distortion = 20 * log10(distortion); driveSlider->onSetValue(100 * distortion / 36); } geonkick-2.10.2/src/distortion_group_box.h000066400000000000000000000033171451017372400206200ustar00rootroot00000000000000/** * File name: distortion_group_box.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_DISTORTION_GROUPBOX_H #define GEONKICK_DISTORTION_GROUPBOX_H #include "geonkick_groupbox.h" #include "envelope.h" class GeonkickSlider; class GeonkickCheckbox; class GeonkickApi; class GeonkickButton; class EnvelopeWidget; class DistortionGroupBox: public GeonkickGroupBox { public: DistortionGroupBox(GeonkickApi *api, GeonkickWidget *parent); ~DistortionGroupBox() = default; void updateGui(); void setVolume(int val); void setDrive(int val); void setInLimiter(int val); private: GeonkickApi *geonkickApi; GeonkickSlider *volumeSlider; GeonkickSlider *inLimiterSlider; GeonkickSlider *driveSlider; GeonkickButton *distortionCheckbox; GeonkickButton *volumeEnvButton; GeonkickButton *driveEnvButton; }; #endif // GEONKICK_DISTORTION_WIDGET_H geonkick-2.10.2/src/dsp/000077500000000000000000000000001451017372400147475ustar00rootroot00000000000000geonkick-2.10.2/src/dsp/CMakeLists.txt000066400000000000000000000034541451017372400175150ustar00rootroot00000000000000set(GKICK_API_HEADERS ${GKICK_API_DIR}/src/audio_output.h ${GKICK_API_DIR}/src/compressor.h ${GKICK_API_DIR}/src/distortion.h ${GKICK_API_DIR}/src/envelope.h ${GKICK_API_DIR}/src/filter.h ${GKICK_API_DIR}/src/geonkick.h ${GKICK_API_DIR}/src/geonkick_internal.h ${GKICK_API_DIR}/src/gkick_audio.h ${GKICK_API_DIR}/src/mixer.h ${GKICK_API_DIR}/src/gkick_buffer.h ${GKICK_API_DIR}/src/gkick_log.h ${GKICK_API_DIR}/src/oscillator.h ${GKICK_API_DIR}/src/synthesizer.h ${GKICK_API_DIR}/src/worker.h) if (GKICK_STANDALONE) list(APPEND GKICK_API_HEADERS ${GKICK_API_DIR}/src/gkick_jack.h) endif(GKICK_STANDALONE) set(GKICK_API_SOURCES ${GKICK_API_DIR}/src/audio_output.c ${GKICK_API_DIR}/src/compressor.c ${GKICK_API_DIR}/src/distortion.c ${GKICK_API_DIR}/src/envelope.c ${GKICK_API_DIR}/src/filter.c ${GKICK_API_DIR}/src/geonkick.c ${GKICK_API_DIR}/src/gkick_audio.c ${GKICK_API_DIR}/src/mixer.c ${GKICK_API_DIR}/src/gkick_buffer.c ${GKICK_API_DIR}/src/gkick_log.c ${GKICK_API_DIR}/src/oscillator.c ${GKICK_API_DIR}/src/synthesizer.c ${GKICK_API_DIR}/src/worker.c) if (GKICK_STANDALONE) list(APPEND GKICK_API_SOURCES ${GKICK_API_DIR}/src/gkick_jack.c) endif(GKICK_STANDALONE) include_directories(${GKICK_API_DIR}/src) if (GKICK_STANDALONE) add_library(api_standalone STATIC ${GKICK_API_HEADERS} ${GKICK_API_SOURCES}) target_compile_options(api_standalone PUBLIC ${GKICK_API_FLAGS}) endif (GKICK_STANDALONE) if (GKICK_PLUGIN) add_library(api_plugin_single STATIC ${GKICK_API_HEADERS} ${GKICK_API_SOURCES}) target_compile_options(api_plugin_single PUBLIC ${GKICK_API_PLUGIN_SINGLE_FLAGS}) add_library(api_plugin STATIC ${GKICK_API_HEADERS} ${GKICK_API_SOURCES}) target_compile_options(api_plugin PUBLIC ${GKICK_API_PLUGIN_FLAGS}) endif (GKICK_PLUGIN) geonkick-2.10.2/src/dsp/src/000077500000000000000000000000001451017372400155365ustar00rootroot00000000000000geonkick-2.10.2/src/dsp/src/audio_output.c000066400000000000000000000221311451017372400204220ustar00rootroot00000000000000/** * File name: audio_output.c * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "audio_output.h" enum geonkick_error gkick_audio_output_create(struct gkick_audio_output **audio_output, int sample_rate) { if (audio_output == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } *audio_output = (struct gkick_audio_output*)calloc(1, sizeof(struct gkick_audio_output)); if (*audio_output == NULL) { gkick_log_error("can't allocate memory"); return GEONKICK_ERROR; } (*audio_output)->decay = -1; (*audio_output)->play = false; (*audio_output)->enabled = true; (*audio_output)->muted = false; (*audio_output)->solo = false; (*audio_output)->channel = 0; (*audio_output)->sample_rate = sample_rate; gkick_buffer_new((struct gkick_buffer**)&(*audio_output)->updated_buffer, (*audio_output)->sample_rate * GEONKICK_MAX_LENGTH); if ((*audio_output)->updated_buffer == NULL) { gkick_log_error("can't create updated buffer"); gkick_audio_output_free(audio_output); return GEONKICK_ERROR; } gkick_buffer_set_size((struct gkick_buffer*)(*audio_output)->updated_buffer, 0); gkick_buffer_new((struct gkick_buffer**)&(*audio_output)->playing_buffer, (*audio_output)->sample_rate * GEONKICK_MAX_LENGTH); if ((*audio_output)->playing_buffer == NULL) { gkick_log_error("can't create playing buffer"); gkick_audio_output_free(audio_output); return GEONKICK_ERROR; } gkick_buffer_set_size((struct gkick_buffer*)(*audio_output)->playing_buffer, 0); if (pthread_mutex_init(&(*audio_output)->lock, NULL) != 0) { gkick_log_error("error on init mutex"); gkick_audio_output_free(audio_output); return GEONKICK_ERROR; } return GEONKICK_OK; } void gkick_audio_output_free(struct gkick_audio_output **audio_output) { if (audio_output != NULL && *audio_output != NULL) { struct gkick_buffer *p = (struct gkick_buffer*)((*audio_output)->playing_buffer); gkick_buffer_free(&p); p = (struct gkick_buffer*)((*audio_output)->updated_buffer); gkick_buffer_free(&p); pthread_mutex_destroy(&(*audio_output)->lock); free(*audio_output); *audio_output = NULL; } } enum geonkick_error gkick_audio_output_key_pressed(struct gkick_audio_output *audio_output, struct gkick_note_info *key) { if (key->state == GKICK_KEY_STATE_PRESSED) { audio_output->key = *key; audio_output->is_play = true; gkick_audio_output_swap_buffers(audio_output); } else { audio_output->decay = GEKICK_KEY_RELESE_DECAY_TIME; audio_output->key.state = key->state; } return GEONKICK_OK; } enum geonkick_error gkick_audio_output_play(struct gkick_audio_output *audio_output) { audio_output->play = true; return GEONKICK_OK; } gkick_real gkick_audio_output_tune_factor(int note_number) { return exp2f((gkick_real)(note_number - 69) / 12.0f); } enum geonkick_error gkick_audio_output_get_frame(struct gkick_audio_output *audio_output, gkick_real *val) { int release_time = GEKICK_KEY_RELESE_DECAY_TIME; gkick_real decay_val; if (audio_output->play) { struct gkick_note_info key; key.channel = 1; key.note_number = 69; key.velocity = 127; key.state = GKICK_KEY_STATE_PRESSED; gkick_audio_output_key_pressed(audio_output, &key); audio_output->play = false; } *val = 0; if (audio_output->is_play) { if (gkick_buffer_is_end((struct gkick_buffer*)audio_output->playing_buffer)) { audio_output->is_play = false; } else { struct gkick_buffer *buff = (struct gkick_buffer*)audio_output->playing_buffer; gkick_real factor = gkick_audio_output_tune_factor(audio_output->key.note_number); if (audio_output->tune) *val = gkick_buffer_stretch_get_next(buff, factor); else *val = gkick_buffer_get_next(buff); if (gkick_buffer_size(buff) - gkick_buffer_index(buff) == GEKICK_KEY_RELESE_DECAY_TIME) { audio_output->decay = GEKICK_KEY_RELESE_DECAY_TIME; audio_output->key.state = GKICK_KEY_STATE_RELEASED; } if (audio_output->key.state == GKICK_KEY_STATE_RELEASED) decay_val = - 1.0f * ((gkick_real)(release_time - audio_output->decay) / release_time) + 1.0; else decay_val = 1.0f; *val *= decay_val * ((gkick_real)audio_output->key.velocity / 127); if (audio_output->key.state == GKICK_KEY_STATE_RELEASED) { audio_output->decay--; if (audio_output->decay < 0) audio_output->is_play = false; } } } *val *= (gkick_real)audio_output->limiter / 1000000; return GEONKICK_OK; } void gkick_audio_output_lock(struct gkick_audio_output *audio_output) { if (audio_output != NULL) pthread_mutex_lock(&audio_output->lock); } void gkick_audio_output_unlock(struct gkick_audio_output *audio_output) { if (audio_output != NULL) pthread_mutex_unlock(&audio_output->lock); } struct gkick_buffer* gkick_audio_output_get_buffer(struct gkick_audio_output *audio_output) { return (struct gkick_buffer*)audio_output->playing_buffer; } void gkick_audio_output_swap_buffers(struct gkick_audio_output *audio_output) { gkick_buffer_reset((struct gkick_buffer*)audio_output->playing_buffer); /** * Try lock. If successful, swap buffers. If not, continue with * the current one to play until the next press of the key. */ if (pthread_mutex_trylock(&audio_output->lock) == 0) { /* Test if the updated buffer is full. Otherwise it means that it was not updated. */ if (gkick_buffer_size((struct gkick_buffer*)audio_output->updated_buffer) > 0 && gkick_buffer_is_end((struct gkick_buffer*)audio_output->updated_buffer)) { char *buff = audio_output->updated_buffer; audio_output->updated_buffer = audio_output->playing_buffer; audio_output->playing_buffer = buff; } gkick_buffer_reset((struct gkick_buffer*)audio_output->playing_buffer); gkick_audio_output_unlock(audio_output); } } enum geonkick_error gkick_audio_output_set_playing_key(struct gkick_audio_output *audio_output, signed char key) { if (key < 21 || key > 108) key = GEONKICK_ANY_KEY; audio_output->playing_key = key; return GEONKICK_OK; } enum geonkick_error gkick_audio_output_get_playing_key(struct gkick_audio_output *audio_output, signed char *key) { *key = audio_output->playing_key; return GEONKICK_OK; } void gkick_audio_output_tune_output(struct gkick_audio_output *audio_output, bool tune) { audio_output->tune = tune; } bool gkick_audio_output_is_tune_output(struct gkick_audio_output *audio_output) { return audio_output->tune; } enum geonkick_error gkick_audio_output_set_channel(struct gkick_audio_output *audio_output, size_t channel) { audio_output->channel = channel; return GEONKICK_OK; } enum geonkick_error gkick_audio_output_get_channel(struct gkick_audio_output *audio_output, size_t *channel) { *channel = audio_output->channel; return GEONKICK_OK; } geonkick-2.10.2/src/dsp/src/audio_output.h000066400000000000000000000112511451017372400204300ustar00rootroot00000000000000/** * File name: audio_output.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GKICK_AUDO_OUTPUT_H #define GKICK_AUDO_OUTPUT_H #include "geonkick_internal.h" #include /** * Audio output (which maybe is not the best name) * is an interface to an array that holds the synthesised percussion. * Is has nothing to do with any real audio interfaces. * It provides access to the samples from the array in a "playable mode", * i.e. in a state machine fashion. */ /* Decay time measured in number of audio frames. */ #define GEKICK_KEY_RELESE_DECAY_TIME 1000 struct gkick_note_info { enum gkick_key_state state; signed char channel; signed char note_number; signed char velocity; }; struct gkick_audio_output { int sample_rate; /* Specifies if this audio output is active. */ _Atomic bool enabled; // On this buffer the synthesizer is copying. char* _Atomic updated_buffer; char* _Atomic playing_buffer; _Atomic bool buffer_updated; /* Note info is changed only by the audio thread. */ struct gkick_note_info key; /* The key number that triggres playing. */ _Atomic char playing_key; /** * Specifies if the audio output is in the * playing state (the percussion is playing) */ _Atomic bool is_play; /** * Triggers the audio thread to start to play * the percussion with the maximum key velocity. */ _Atomic bool play; /** * Specifies if to tune the output in accordance with * the note (central note is A4). */ _Atomic bool tune; /** * decay - note release time measured in number of audio frames. * Relaxation curve for audio is liniear: * - 1.0 * (GEKICK_NOTE_RELEASE_TIME - decay) / GEKICK_NOTE_RELEASE_TIME + 1.0, * decay from GEKICK_NOTE_RELEASE_TIME to 0; */ _Atomic int decay; /* Output channel. */ atomic_size_t channel; /* The output is muted or not. */ _Atomic bool muted; /* The output is muted or not. */ _Atomic bool solo; /* Output audio limiter value. */ atomic_int limiter; pthread_mutex_t lock; }; enum geonkick_error gkick_audio_output_create(struct gkick_audio_output **audio_output, int sample_rate); void gkick_audio_output_free(struct gkick_audio_output **audio_output); struct gkick_buffer* gkick_audio_output_get_buffer(struct gkick_audio_output *audio_output); enum geonkick_error gkick_audio_output_key_pressed(struct gkick_audio_output *audio_output, struct gkick_note_info *key); enum geonkick_error gkick_audio_output_play(struct gkick_audio_output *audio_output); gkick_real gkick_audio_output_tune_factor(int note_number); enum geonkick_error gkick_audio_output_get_frame(struct gkick_audio_output *audio_output, gkick_real *val); void gkick_audio_output_lock(struct gkick_audio_output *audio_output); void gkick_audio_output_unlock(struct gkick_audio_output *audio_output); void gkick_audio_output_swap_buffers(struct gkick_audio_output *audio_output); enum geonkick_error gkick_audio_output_set_playing_key(struct gkick_audio_output *audio_output, signed char key); enum geonkick_error gkick_audio_output_get_playing_key(struct gkick_audio_output *audio_output, signed char *key); void gkick_audio_output_tune_output(struct gkick_audio_output *audio_output, bool tune); bool gkick_audio_output_is_tune_output(struct gkick_audio_output *audio_output); enum geonkick_error gkick_audio_output_set_channel(struct gkick_audio_output *audio_output, size_t channel); enum geonkick_error gkick_audio_output_get_channel(struct gkick_audio_output *audio_output, size_t *channel); #endif // GKICK_AUDO_OUTPUT_H geonkick-2.10.2/src/dsp/src/compressor.c000066400000000000000000000176461451017372400201140ustar00rootroot00000000000000/** * File name: compressor.c * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "compressor.h" enum geonkick_error gkick_compressor_new(struct gkick_compressor **compressor, int sample_rate) { if (compressor == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } *compressor = (struct gkick_compressor*)calloc(1, sizeof(struct gkick_compressor)); if (*compressor == NULL) { gkick_log_error("can't allocate memory"); return GEONKICK_ERROR; } (*compressor)->sample_rate = sample_rate; (*compressor)->attack = 0.01f * sample_rate; (*compressor)->release = 0.01f * sample_rate; (*compressor)->threshold = 0.0f; (*compressor)->ratio = 1.0f; (*compressor)->knee = 0.0f; (*compressor)->makeup = 1.0f; if (pthread_mutex_init(&(*compressor)->lock, NULL) != 0) { gkick_log_error("error on init mutex"); gkick_compressor_free(compressor); return GEONKICK_ERROR; } return GEONKICK_OK; } void gkick_compressor_free(struct gkick_compressor **compressor) { if (compressor != NULL && *compressor != NULL) { pthread_mutex_destroy(&(*compressor)->lock); free(*compressor); *compressor = NULL; } } void gkick_compressor_lock(struct gkick_compressor *compressor) { pthread_mutex_lock(&compressor->lock); } void gkick_compressor_unlock(struct gkick_compressor *compressor) { pthread_mutex_unlock(&compressor->lock); } enum geonkick_error gkick_compressor_enable(struct gkick_compressor *compressor, int enable) { gkick_compressor_lock(compressor); compressor->enabled = enable; gkick_compressor_unlock(compressor); return GEONKICK_OK; } enum geonkick_error gkick_compressor_is_enabled(struct gkick_compressor *compressor, int *enabled) { gkick_compressor_lock(compressor); *enabled = compressor->enabled; gkick_compressor_unlock(compressor); return GEONKICK_OK; } enum geonkick_error gkick_compressor_val(struct gkick_compressor *compressor, gkick_real in_val, gkick_real *out_val) { gkick_compressor_lock(compressor); gkick_real threshold = compressor->threshold; if (fabs(compressor->threshold) < DBL_EPSILON || compressor->ratio < 1.0) { gkick_compressor_unlock(compressor); *out_val = compressor->makeup * in_val; return GEONKICK_OK; } gkick_real sign = in_val >= 0.0f ? 1.0f : -1.0f; in_val = fabs(in_val); if (in_val > threshold) { gkick_real ratio = compressor->ratio; if (compressor->frames <= compressor->attack && compressor->attack > 0) { /* Linear increase of the ratio. */ ratio = 1.0 + ((compressor->ratio - 1.0) / compressor->attack) * compressor->frames; compressor->frames++; } gkick_real diff = (in_val - threshold) / ratio; *out_val = threshold + diff; } else { compressor->frames = 0; *out_val = in_val; } *out_val *= compressor->makeup; gkick_compressor_unlock(compressor); *out_val = sign * (*out_val); return GEONKICK_OK; } enum geonkick_error gkick_compressor_set_attack(struct gkick_compressor *compressor, gkick_real attack) { gkick_compressor_lock(compressor); compressor->attack = compressor->sample_rate * attack; gkick_compressor_unlock(compressor); return GEONKICK_OK; } enum geonkick_error gkick_compressor_get_attack(struct gkick_compressor *compressor, gkick_real *attack) { gkick_compressor_lock(compressor); *attack = (gkick_real)compressor->attack / compressor->sample_rate; gkick_compressor_unlock(compressor); return GEONKICK_OK; } enum geonkick_error gkick_compressor_set_release(struct gkick_compressor *compressor, gkick_real release) { gkick_compressor_lock(compressor); compressor->release = compressor->sample_rate * release; gkick_compressor_unlock(compressor); return GEONKICK_OK; } enum geonkick_error gkick_compressor_get_release(struct gkick_compressor *compressor, gkick_real *release) { gkick_compressor_lock(compressor); *release = (double)compressor->release / compressor->sample_rate; gkick_compressor_unlock(compressor); return GEONKICK_OK; } enum geonkick_error gkick_compressor_set_threshold(struct gkick_compressor *compressor, gkick_real threshold) { gkick_compressor_lock(compressor); compressor->threshold = threshold; gkick_compressor_unlock(compressor); return GEONKICK_OK; } enum geonkick_error gkick_compressor_get_threshold(struct gkick_compressor *compressor, gkick_real *threshold) { gkick_compressor_lock(compressor); *threshold = compressor->threshold; gkick_compressor_unlock(compressor); return GEONKICK_OK; } enum geonkick_error gkick_compressor_set_ratio(struct gkick_compressor *compressor, gkick_real ratio) { gkick_compressor_lock(compressor); if (ratio < 1.0f) compressor->ratio = 1.0f; else compressor->ratio = ratio; gkick_compressor_unlock(compressor); return GEONKICK_OK; } enum geonkick_error gkick_compressor_get_ratio(struct gkick_compressor *compressor, gkick_real *ratio) { gkick_compressor_lock(compressor); *ratio = compressor->ratio; gkick_compressor_unlock(compressor); return GEONKICK_OK; } enum geonkick_error gkick_compressor_set_knee(struct gkick_compressor *compressor, gkick_real knee) { gkick_compressor_lock(compressor); compressor->knee = knee; gkick_compressor_unlock(compressor); return GEONKICK_OK; } enum geonkick_error gkick_compressor_get_knee(struct gkick_compressor *compressor, gkick_real *knee) { gkick_compressor_lock(compressor); *knee = compressor->knee; gkick_compressor_unlock(compressor); return GEONKICK_OK; } enum geonkick_error gkick_compressor_set_makeup(struct gkick_compressor *compressor, gkick_real makeup) { gkick_compressor_lock(compressor); compressor->makeup = makeup; gkick_compressor_unlock(compressor); return GEONKICK_OK; } enum geonkick_error gkick_compressor_get_makeup(struct gkick_compressor *compressor, gkick_real *makeup) { gkick_compressor_lock(compressor); *makeup = compressor->makeup; gkick_compressor_unlock(compressor); return GEONKICK_OK; } geonkick-2.10.2/src/dsp/src/compressor.h000066400000000000000000000076471451017372400201210ustar00rootroot00000000000000/** * File name: compressor.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_COMPRESSOR_H #define GEONKICK_COMPRESSOR_H #include "geonkick_internal.h" #include struct gkick_compressor { int sample_rate; int enabled; /* Attack and release time in number of audio frames. */ uint64_t attack; uint64_t release; /* Threshold in -dB. */ gkick_real threshold; /* Ratio from 1.0 to 60. */ gkick_real ratio; /* Knee in dB. */ gkick_real knee; /* Makeup in dB. */ gkick_real makeup; /* Number of framess elapsed since the start of compression. */ uint64_t frames; /* The frame at which the compressor is deactivated. */ uint64_t deactivation; pthread_mutex_t lock; }; enum geonkick_error gkick_compressor_new(struct gkick_compressor **compressor, int sample_rate); void gkick_compressor_free(struct gkick_compressor **compressor); void gkick_compressor_lock(struct gkick_compressor *compressor); void gkick_compressor_unlock(struct gkick_compressor *compressor); enum geonkick_error gkick_compressor_enable(struct gkick_compressor *compressor, int enable); enum geonkick_error gkick_compressor_is_enabled(struct gkick_compressor *compressor, int *enabled); enum geonkick_error gkick_compressor_val(struct gkick_compressor *compressor, gkick_real in_val, gkick_real *out_val); enum geonkick_error gkick_compressor_set_attack(struct gkick_compressor *compressor, gkick_real attack); enum geonkick_error gkick_compressor_get_attack(struct gkick_compressor *compressor, gkick_real *attack); enum geonkick_error gkick_compressor_set_release(struct gkick_compressor *compressor, gkick_real release); enum geonkick_error gkick_compressor_get_release(struct gkick_compressor *compressor, gkick_real *release); enum geonkick_error gkick_compressor_set_threshold(struct gkick_compressor *compressor, gkick_real threshold); enum geonkick_error gkick_compressor_get_threshold(struct gkick_compressor *compressor, gkick_real *threshold); enum geonkick_error gkick_compressor_set_ratio(struct gkick_compressor *compressor, gkick_real ratio); enum geonkick_error gkick_compressor_get_ratio(struct gkick_compressor *compressor, gkick_real *ratio); enum geonkick_error gkick_compressor_set_knee(struct gkick_compressor *compressor, gkick_real knee); enum geonkick_error gkick_compressor_get_knee(struct gkick_compressor *compressor, gkick_real *knee); enum geonkick_error gkick_compressor_set_makeup(struct gkick_compressor *compressor, gkick_real makeup); enum geonkick_error gkick_compressor_get_makeup(struct gkick_compressor *compressor, gkick_real *makeup); #endif // GEONKICK_COMPRESSOR_H geonkick-2.10.2/src/dsp/src/distortion.c000066400000000000000000000141311451017372400201000ustar00rootroot00000000000000/** * File name: distortion.c * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "distortion.h" #include "envelope.h" enum geonkick_error gkick_distortion_new(struct gkick_distortion **distortion, int sample_rate) { if (distortion == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } *distortion = (struct gkick_distortion*)calloc(1, sizeof(struct gkick_distortion)); if (*distortion == NULL) { gkick_log_error("can't allocate memory"); return GEONKICK_ERROR; } (*distortion)->drive_env = NULL; (*distortion)->volume_env = NULL; (*distortion)->drive = 1.0f; (*distortion)->sample_rate = sample_rate; struct gkick_envelope *env = gkick_envelope_create(); if (env == NULL) { gkick_log_error("can't create distortion drive envelope"); gkick_distortion_free(distortion); return GEONKICK_ERROR; } else { /* Add two default points. */ gkick_envelope_add_point(env, 0.0f, 1.0f); gkick_envelope_add_point(env, 1.0f, 1.0f); (*distortion)->drive_env = env; } env = gkick_envelope_create(); if (env == NULL) { gkick_log_error("can't create distortion volume envelope"); gkick_distortion_free(distortion); return GEONKICK_ERROR; } else { /* Add two default points. */ gkick_envelope_add_point(env, 0.0f, 1.0f); gkick_envelope_add_point(env, 1.0f, 1.0f); (*distortion)->volume_env = env; } if (pthread_mutex_init(&(*distortion)->lock, NULL) != 0) { gkick_log_error("error on init mutex"); gkick_distortion_free(distortion); return GEONKICK_ERROR; } return GEONKICK_OK; } void gkick_distortion_free(struct gkick_distortion **distortion) { if (distortion != NULL && *distortion != NULL) { if ((*distortion)->drive_env != NULL) gkick_envelope_destroy((*distortion)->drive_env); if ((*distortion)->volume_env != NULL) gkick_envelope_destroy((*distortion)->volume_env); pthread_mutex_destroy(&(*distortion)->lock); free(*distortion); *distortion = NULL; } } void gkick_distortion_lock(struct gkick_distortion *distortion) { pthread_mutex_lock(&distortion->lock); } void gkick_distortion_unlock(struct gkick_distortion *distortion) { pthread_mutex_unlock(&distortion->lock); } enum geonkick_error gkick_distortion_enable(struct gkick_distortion *distortion, int enable) { gkick_distortion_lock(distortion); distortion->enabled = enable; gkick_distortion_unlock(distortion); return GEONKICK_OK; } enum geonkick_error gkick_distortion_is_enabled(struct gkick_distortion *distortion, int *enabled) { gkick_distortion_lock(distortion); *enabled = distortion->enabled; gkick_distortion_unlock(distortion); return GEONKICK_OK; } enum geonkick_error gkick_distortion_val(struct gkick_distortion *distortion, gkick_real in_val, gkick_real *out_val, gkick_real env_x) { gkick_distortion_lock(distortion); gkick_real x = distortion->in_limiter * in_val; x *= 1.0f + (distortion->drive - 1.0f) * gkick_envelope_get_value(distortion->drive_env, env_x); if (x > 1.0f) x = 1.0f; else if (x < -1.0f) x = -1.0f; *out_val= (x < 0.0f ? -1.0f : 1.0f) * (1.0f - exp(-4.0f * log(10.0f) * fabs(x))); *out_val *= distortion->volume * gkick_envelope_get_value(distortion->volume_env, env_x); gkick_distortion_unlock(distortion); return GEONKICK_OK; } enum geonkick_error gkick_distortion_set_volume(struct gkick_distortion *distortion, gkick_real volume) { gkick_distortion_lock(distortion); distortion->volume = volume; gkick_distortion_unlock(distortion); return GEONKICK_OK; } enum geonkick_error gkick_distortion_get_volume(struct gkick_distortion *distortion, gkick_real *volume) { gkick_distortion_lock(distortion); *volume = distortion->volume; gkick_distortion_unlock(distortion); return GEONKICK_OK; } enum geonkick_error gkick_distortion_set_in_limiter(struct gkick_distortion *distortion, gkick_real limit) { gkick_distortion_lock(distortion); distortion->in_limiter = limit; gkick_distortion_unlock(distortion); return GEONKICK_OK; } enum geonkick_error gkick_distortion_get_in_limiter(struct gkick_distortion *distortion, gkick_real *limit) { gkick_distortion_lock(distortion); *limit = distortion->in_limiter; gkick_distortion_unlock(distortion); return GEONKICK_OK; } enum geonkick_error gkick_distortion_set_drive(struct gkick_distortion *distortion, gkick_real drive) { gkick_distortion_lock(distortion); distortion->drive = drive; gkick_distortion_unlock(distortion); return GEONKICK_OK; } enum geonkick_error gkick_distortion_get_drive(struct gkick_distortion *distortion, gkick_real *drive) { gkick_distortion_lock(distortion); *drive = distortion->drive; gkick_distortion_unlock(distortion); return GEONKICK_OK; } geonkick-2.10.2/src/dsp/src/distortion.h000066400000000000000000000053651451017372400201160ustar00rootroot00000000000000/** * File name: distortion.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_DISTORTION_H #define GEONKICK_DISTORTION_H #include "geonkick_internal.h" #include struct gkick_distortion { int enabled; int sample_rate; /* Input limiter for distortion. */ gkick_real in_limiter; gkick_real volume; gkick_real drive; struct gkick_envelope *drive_env; struct gkick_envelope *volume_env; pthread_mutex_t lock; }; enum geonkick_error gkick_distortion_new(struct gkick_distortion **distortion, int sample_rate); void gkick_distortion_free(struct gkick_distortion **distortion); void gkick_distortion_lock(struct gkick_distortion *distortion); void gkick_distortion_unlock(struct gkick_distortion *distortion); enum geonkick_error gkick_distortion_enable(struct gkick_distortion *distortion, int enable); enum geonkick_error gkick_distortion_is_enabled(struct gkick_distortion *distortion, int *enabled); enum geonkick_error gkick_distortion_val(struct gkick_distortion *distortion, gkick_real in_val, gkick_real *out_val, gkick_real env_x); enum geonkick_error gkick_distortion_set_volume(struct gkick_distortion *distortion, gkick_real volume); enum geonkick_error gkick_distortion_get_volume(struct gkick_distortion *distortion, gkick_real *volume); enum geonkick_error gkick_distortion_set_in_limiter(struct gkick_distortion *distortion, gkick_real limit); enum geonkick_error gkick_distortion_get_in_limiter(struct gkick_distortion *distortion, gkick_real *limit); enum geonkick_error gkick_distortion_set_drive(struct gkick_distortion *distortion, gkick_real drive); enum geonkick_error gkick_distortion_get_drive(struct gkick_distortion *distortion, gkick_real *drive); #endif // GEONKICK_DISTORTION_H geonkick-2.10.2/src/dsp/src/envelope.c000066400000000000000000000154361451017372400175300ustar00rootroot00000000000000/** * File name: envelope.c * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "envelope.h" struct gkick_envelope* gkick_envelope_create(void) { struct gkick_envelope *envelope; envelope = (struct gkick_envelope*)calloc(1, sizeof(struct gkick_envelope)); return envelope; } gkick_real gkick_envelope_get_value(const struct gkick_envelope* envelope, gkick_real xm) { gkick_real x1; gkick_real y1 = 0.0f; gkick_real x2; gkick_real y2 = 0.0f; gkick_real ym; struct gkick_envelope_point *p; if (envelope == NULL || (envelope->first == NULL || envelope->last == NULL)) { return 0.0f; } if (xm < envelope->first->x || xm > envelope->last->x) { return 0.0f; } else if (fabsl(xm - envelope->first->x) < DBL_EPSILON) { return envelope->first->y; } else if (fabsl(envelope->last->x - xm) < DBL_EPSILON) { return envelope->last->y; } x2 = x1 = xm; p = envelope->first; while (p) { if (fabs(xm - p->x) < DBL_EPSILON) { return p->y; } else if (xm < p->x) { x2 = p->x; y2 = p->y; break; } p = p->next; } p = envelope->last; while (p) { if (fabs(xm - p->x) < DBL_EPSILON) { return p->y; } else if (p->x < xm) { x1 = p->x; y1 = p->y; break; } p = p->prev; } if (fabsl(x2 - x1) < DBL_EPSILON) ym = y1; else ym = (y1 * (x2 - xm) + y2 * (xm - x1)) / (x2 - x1); return ym; } struct gkick_envelope_point* gkick_envelope_add_point(struct gkick_envelope *envelope, float x, float y) { struct gkick_envelope_point *point; if (envelope == NULL) return NULL; point = (struct gkick_envelope_point*)malloc(sizeof(struct gkick_envelope_point)); if (point == NULL) return NULL; point->x = x; point->y = y; point->next = point->prev = NULL; if (envelope->first == NULL || envelope->last == NULL) envelope->first = envelope->last = point; else gkick_envelope_add_sorted(envelope, point); envelope->npoints++; return point; } void gkick_envelope_add_sorted(struct gkick_envelope *envelope, struct gkick_envelope_point *point) { struct gkick_envelope_point *p; if (point->x >= envelope->last->x) { /* Add as a last element. */ envelope->last->next = point; point->prev = envelope->last; envelope->last = point; } else if (point->x <= envelope->first->x) { /* Add as a first element. */ envelope->first->prev = point; point->next = envelope->first; envelope->first = point; } else { p = envelope->first; while (p) { if (p->next != NULL && point->x < p->next->x) { p->next->prev = point; point->next = p->next; point->prev = p; p->next = point; break; } p = p->next; } } } void gkick_envelope_destroy(struct gkick_envelope *envelope) { struct gkick_envelope_point *point; if (envelope == NULL) return; if (envelope->npoints != 0) { while (envelope->first != NULL) { point = envelope->first; envelope->first = point->next; free(point); } } free(envelope); } void gkick_envelope_get_points(struct gkick_envelope *env, gkick_real **buff, size_t *npoints) { struct gkick_envelope_point *p; gkick_real *points; size_t i; if (buff == NULL) return; *buff = NULL; if (env->npoints < 1) return; points = (gkick_real *)calloc(1, sizeof(gkick_real) * (2 * env->npoints)); p = env->first; i = 0; while (p) { points[i] = p->x; points[i + 1] = p->y; p = p->next; i += 2; } *buff = points; *npoints = env->npoints; } void gkick_envelope_set_points(struct gkick_envelope *env, const gkick_real *buff, size_t npoints) { if (env == NULL || buff == NULL) return; gkick_envelope_clear(env); for (size_t i = 0; i < npoints; i++) gkick_envelope_add_point(env, buff[2 * i], buff[2 * i + 1]); } void gkick_envelope_clear(struct gkick_envelope* env) { struct gkick_envelope_point *p = env->first; struct gkick_envelope_point *curr; while (p != NULL) { curr = p; p = p->next; free(curr); env->npoints--; } env->first = env->last = NULL; } void gkick_envelope_remove_point(struct gkick_envelope *env, size_t index) { if (env == NULL || index >= env->npoints) return; struct gkick_envelope_point *p = env->first; size_t i = 0; while (p) { if (i == index) { if (p == env->first) { env->first = p->next; } else if (p == env->last) { env->last = p->prev; } else { p->prev->next = p->next; p->next->prev = p->prev; } free(p); env->npoints--; break; } p = p->next; i++; } } void gkick_envelope_update_point(struct gkick_envelope *env, size_t index, gkick_real x, gkick_real y) { if (env == NULL || index >= env->npoints) return; struct gkick_envelope_point *p = env->first; size_t i = 0; while (p) { if (i == index) { p->x = x; p->y = y; break; } p = p->next; i++; } } geonkick-2.10.2/src/dsp/src/envelope.h000066400000000000000000000045001451017372400175230ustar00rootroot00000000000000/** * File name: envelope.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GKICK_ENVELOPE_H #define GKICK_ENVELOPE_H #include "geonkick_internal.h" struct gkick_envelope_point { struct gkick_envelope_point *prev; struct gkick_envelope_point *next; gkick_real x; gkick_real y; }; struct gkick_envelope { size_t npoints; struct gkick_envelope_point *first; struct gkick_envelope_point *last; }; struct gkick_envelope* gkick_envelope_create(void); gkick_real gkick_envelope_get_value(const struct gkick_envelope* envelope, gkick_real xm); struct gkick_envelope_point* gkick_envelope_add_point(struct gkick_envelope *envelope, float x, float y); void gkick_envelope_add_sorted(struct gkick_envelope *envelope, struct gkick_envelope_point *point); void gkick_envelope_destroy(struct gkick_envelope *envelope); void gkick_envelope_get_points(struct gkick_envelope *env, gkick_real **buff, size_t *npoints); void gkick_envelope_set_points(struct gkick_envelope *env, const gkick_real *buff, size_t npoints); void gkick_envelope_clear(struct gkick_envelope* env); void gkick_envelope_remove_point(struct gkick_envelope *env, size_t index); void gkick_envelope_update_point(struct gkick_envelope *env, size_t index, gkick_real x, gkick_real y); #endif // GKICK_ENVELOPE_H geonkick-2.10.2/src/dsp/src/filter.c000066400000000000000000000175701451017372400172010ustar00rootroot00000000000000/** * File name: filter.c * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "filter.h" enum geonkick_error gkick_filter_new(struct gkick_filter **filter, int sample_rate) { if (filter == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } *filter = (struct gkick_filter *)calloc(1, sizeof(struct gkick_filter)); if (*filter == NULL) { gkick_log_error("can't allocate memory"); return GEONKICK_ERROR_MEM_ALLOC; } (*filter)->type = GEONKICK_FILTER_LOW_PASS; (*filter)->queue_empty = true; (*filter)->sample_rate = sample_rate; (*filter)->cutoff_env = gkick_envelope_create(); if ((*filter)->cutoff_env == NULL) { gkick_log_error("can't create filter cutoff envelope"); gkick_filter_free(filter); return GEONKICK_ERROR; } else { gkick_envelope_add_point((*filter)->cutoff_env, 0.0f, 1.0f); gkick_envelope_add_point((*filter)->cutoff_env, 1.0f, 1.0f); } if (pthread_mutex_init(&(*filter)->lock, NULL) != 0) { gkick_log_error("error on init mutex"); gkick_filter_free(filter); return GEONKICK_ERROR; } (*filter)->cutoff_freq = GEONKICK_DEFAULT_FILTER_CUTOFF_FREQ; (*filter)->factor = GEONKICK_DEFAULT_FILTER_FACTOR; gkick_filter_update_coefficents(*filter); return GEONKICK_OK; } enum geonkick_error gkick_filter_init(struct gkick_filter *filter) { if (filter == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_filter_lock(filter); filter->queue_empty = true; memset(filter->queue_l, 0, sizeof(filter->queue_l)); memset(filter->queue_b, 0, sizeof(filter->queue_b)); memset(filter->queue_h, 0, sizeof(filter->queue_h)); gkick_filter_update_coefficents(filter); gkick_filter_unlock(filter); return GEONKICK_OK; } void gkick_filter_free(struct gkick_filter **filter) { if (filter != NULL && *filter != NULL) { gkick_envelope_destroy((*filter)->cutoff_env); pthread_mutex_destroy(&(*filter)->lock); free(*filter); *filter = NULL; } } void gkick_filter_lock(struct gkick_filter *filter) { pthread_mutex_lock(&filter->lock); } void gkick_filter_unlock(struct gkick_filter *filter) { pthread_mutex_unlock(&filter->lock); } enum geonkick_error gkick_filter_update_coefficents(struct gkick_filter *filter) { if (filter == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_real F = 2.0f * sin(M_PI * filter->cutoff_freq / filter->sample_rate); gkick_real Q = filter->factor; filter->coefficients[0] = F; filter->coefficients[1] = Q; return GEONKICK_OK; } enum geonkick_error gkick_filter_set_type(struct gkick_filter *filter, enum gkick_filter_type type) { if (filter == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_filter_lock(filter); filter->type = type; gkick_filter_update_coefficents(filter); gkick_filter_unlock(filter); return GEONKICK_OK; } enum geonkick_error gkick_filter_get_type(struct gkick_filter *filter, enum gkick_filter_type *type) { if (filter == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_filter_lock(filter); *type = filter->type; gkick_filter_unlock(filter); return GEONKICK_OK; } enum geonkick_error gkick_filter_set_cutoff_freq(struct gkick_filter *filter, gkick_real cutoff) { if (filter == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_filter_lock(filter); filter->cutoff_freq = cutoff; gkick_filter_update_coefficents(filter); gkick_filter_unlock(filter); return GEONKICK_OK; } enum geonkick_error gkick_filter_set_factor(struct gkick_filter *filter, gkick_real factor) { if (filter == NULL || factor < 0.5f) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_filter_lock(filter); filter->factor = 10.0f / factor; gkick_filter_update_coefficents(filter); gkick_filter_unlock(filter); return GEONKICK_OK; } enum geonkick_error gkick_filter_get_factor(struct gkick_filter *filter, gkick_real *factor) { if (filter == NULL || factor == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_filter_lock(filter); *factor = 10.0f / filter->factor; gkick_filter_unlock(filter); return GEONKICK_OK; } enum geonkick_error gkick_filter_get_cutoff_freq(struct gkick_filter *filter, gkick_real *cutoff) { if (filter == NULL || cutoff == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_filter_lock(filter); *cutoff = filter->cutoff_freq; gkick_filter_unlock(filter); return GEONKICK_OK; } /** * gkick_filter_val function * * Implements low, high, and band pass digital state variable filter. */ enum geonkick_error gkick_filter_val(struct gkick_filter *filter, gkick_real in_val, gkick_real *out_val, gkick_real env_x) { if (isnan(in_val) || in_val > 1.0f || in_val < -1.0f) { *out_val = 0.0f; return GEONKICK_ERROR; } if (filter == NULL || out_val == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_filter_lock(filter); gkick_real *l = filter->queue_l; gkick_real *b = filter->queue_b; gkick_real *h = filter->queue_h; gkick_real F = gkick_envelope_get_value(filter->cutoff_env, env_x) * filter->coefficients[0]; gkick_real Q = filter->coefficients[1]; size_t n = 1; if (filter->queue_empty) { l[n - 1] = l[n] = 0; b[n - 1] = b[n] = 0; h[n - 1] = h[n] = 0; filter->queue_empty = false; } else { h[n - 1] = h[n]; b[n - 1] = b[n]; l[n - 1] = l[n]; } h[n] = in_val - l[n - 1] - Q * b[n - 1]; b[n] = F * h[n] + b[n - 1]; l[n] = F * b[n] + l[n - 1]; gkick_real val = 0.0f; if (filter->type == GEONKICK_FILTER_HIGH_PASS) val = h[n]; else if (filter->type == GEONKICK_FILTER_BAND_PASS) val = b[n]; else val = l[n]; *out_val = val; gkick_filter_unlock(filter); return GEONKICK_OK; } geonkick-2.10.2/src/dsp/src/filter.h000066400000000000000000000056621451017372400172050ustar00rootroot00000000000000/** * File name: filter.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_FILTER_H #define GEONKICK_FILTER_H #include "geonkick_internal.h" #include "envelope.h" #define GEONKICK_DEFAULT_FILTER_CUTOFF_FREQ (350.0f) #define GEONKICK_DEFAULT_FILTER_FACTOR (1.0f) struct gkick_filter { enum gkick_filter_type type; int sample_rate; /* Filter cuttoff frequency. */ gkick_real cutoff_freq; /* Filter damping factor. */ gkick_real factor; /* A queue of a three elements */ gkick_real queue_l[2]; gkick_real queue_b[2]; gkick_real queue_h[2]; bool queue_empty; /* Filter coefficients. */ gkick_real coefficients[2]; /* Filter cutoff envelope. */ struct gkick_envelope *cutoff_env; pthread_mutex_t lock; }; enum geonkick_error gkick_filter_new(struct gkick_filter **filter, int sample_rate); enum geonkick_error gkick_filter_init(struct gkick_filter *filter); void gkick_filter_free(struct gkick_filter **filter); void gkick_filter_lock(struct gkick_filter *filter); void gkick_filter_unlock(struct gkick_filter *filter); enum geonkick_error gkick_filter_update_coefficents(struct gkick_filter *filter); enum geonkick_error gkick_filter_set_type(struct gkick_filter *filter, enum gkick_filter_type type); enum geonkick_error gkick_filter_get_type(struct gkick_filter *filter, enum gkick_filter_type *type); enum geonkick_error gkick_filter_set_cutoff_freq(struct gkick_filter *filter, gkick_real cutoff); enum geonkick_error gkick_filter_set_factor(struct gkick_filter *filter, gkick_real factor); enum geonkick_error gkick_filter_get_cutoff_freq(struct gkick_filter *filter, gkick_real *cutoff); enum geonkick_error gkick_filter_get_factor(struct gkick_filter *filter, gkick_real *factor); enum geonkick_error gkick_filter_val(struct gkick_filter *filter, gkick_real in_val, gkick_real *out_val, gkick_real env_x); #endif // GEONKICK_FILTER_H geonkick-2.10.2/src/dsp/src/geonkick.c000066400000000000000000001662121451017372400175040ustar00rootroot00000000000000/** * File name: geonkick.c * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "geonkick_internal.h" #include "synthesizer.h" #include "gkick_audio.h" #include "audio_output.h" #include "envelope.h" #include "mixer.h" enum geonkick_error geonkick_create(struct geonkick **kick, int sample_rate) { if (kick == NULL) return GEONKICK_ERROR; *kick = (struct geonkick*)calloc(1, sizeof(struct geonkick)); if (*kick == NULL) return GEONKICK_ERROR_MEM_ALLOC; strcpy((*kick)->name, "Geonkick"); (*kick)->sample_rate = sample_rate; (*kick)->synthesis_on = false; (*kick)->per_index = 0; if (pthread_mutex_init(&(*kick)->lock, NULL) != 0) { gkick_log_error("error on init mutex"); geonkick_free(kick); return GEONKICK_ERROR; } if (gkick_audio_create(&(*kick)->audio, sample_rate) != GEONKICK_OK) { gkick_log_warning("can't create audio"); geonkick_free(kick); return GEONKICK_ERROR; } (*kick)->sample_rate = (*kick)->audio->sample_rate; for (size_t i = 0; i < GEONKICK_MAX_PERCUSSIONS; i++) { if (gkick_synth_new(&(*kick)->synths[i], (*kick)->sample_rate) != GEONKICK_OK) { gkick_log_error("can't create synthesizer %u", i); geonkick_free(kick); return GEONKICK_ERROR; } (*kick)->synths[i]->id = i; } for (size_t i = 0; i < GEONKICK_MAX_PERCUSSIONS; i++) { gkick_synth_set_output((*kick)->synths[i], (*kick)->audio->audio_outputs[i]); geonkick_set_percussion_channel(*kick, i, i % GEONKICK_MAX_CHANNELS); } if (!geonkick_worker_created()) { if (geonkick_worker_create() != GEONKICK_OK) { gkick_log_error("can't init worker"); geonkick_free(kick); return GEONKICK_ERROR; } if (geonkick_worker_start() != GEONKICK_OK) { gkick_log_error("can't start worker"); geonkick_free(kick); return GEONKICK_ERROR; } } geonkick_worker_add_instance(*kick); if (gkick_start_audio((*kick)->audio) != GEONKICK_OK) { gkick_log_error("can't start audio module"); geonkick_free(kick); } return GEONKICK_OK; } void geonkick_free(struct geonkick **kick) { if (kick != NULL && *kick != NULL) { geonkick_worker_remove_instance(*kick); if (geonkick_worker_reference_count() == 0) geonkick_worker_destroy(); gkick_log_debug("ref count: %d", geonkick_worker_reference_count()); for (size_t i = 0; i < GEONKICK_MAX_PERCUSSIONS; i++) gkick_synth_free(&((*kick)->synths[i])); gkick_audio_free(&((*kick)->audio)); pthread_mutex_destroy(&(*kick)->lock); free(*kick); } } void geonkick_lock(struct geonkick *kick) { pthread_mutex_lock(&kick->lock); } void geonkick_unlock(struct geonkick *kick) { pthread_mutex_unlock(&kick->lock); } enum geonkick_error geonkick_enable_oscillator(struct geonkick* kick, size_t index) { if (kick == NULL) return GEONKICK_ERROR; enum geonkick_error res; res = gkick_synth_enable_oscillator(kick->synths[kick->per_index], index, 1); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_disable_oscillator(struct geonkick* kick, size_t index) { if (kick == NULL) return GEONKICK_ERROR; enum geonkick_error res; res = gkick_synth_enable_oscillator(kick->synths[kick->per_index], index, 0); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_is_oscillator_enabled(struct geonkick* kick, size_t index, int *enabled) { if (kick == NULL || enabled == NULL) return GEONKICK_ERROR; return gkick_synth_osc_is_enabled(kick->synths[kick->per_index], index, enabled); } enum geonkick_error geonkick_get_oscillators_number(struct geonkick *kick, size_t *number) { if (kick == NULL || number == NULL) return GEONKICK_ERROR; return gkick_synth_get_oscillators_number(kick->synths[kick->per_index], number); } enum geonkick_error geonkick_osc_envelope_add_point(struct geonkick *kick, size_t osc_index, size_t env_index, gkick_real x, gkick_real y) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_osc_env_add_point(kick->synths[kick->per_index], osc_index, env_index, x, y); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_set_sample_rate(struct geonkick *kick, gkick_real rate) { return GEONKICK_OK; } enum geonkick_error geonkick_osc_envelope_get_points(struct geonkick *kick, size_t osc_index, size_t env_index, gkick_real **buf, size_t *npoints) { if (kick == NULL || buf == NULL || npoints == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_synth_osc_envelope_points(kick->synths[kick->per_index], osc_index, env_index, buf, npoints); } enum geonkick_error geonkick_osc_envelope_set_points(struct geonkick *kick, size_t osc_index, size_t env_index, const gkick_real *buff, size_t npoints) { if (kick == NULL || buff == NULL || npoints == 0) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_synth_osc_envelope_set_points(kick->synths[kick->per_index], osc_index, env_index, buff, npoints); } enum geonkick_error geonkick_osc_envelope_remove_point(struct geonkick *kick, size_t osc_index, size_t env_index, size_t index) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_osc_env_remove_point(kick->synths[kick->per_index], osc_index, env_index, index); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_osc_envelope_update_point(struct geonkick *kick, size_t osc_index, size_t env_index, size_t index, gkick_real x, gkick_real y) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_osc_env_update_point(kick->synths[kick->per_index], osc_index, env_index, index, x, y); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_osc_set_fm(struct geonkick *kick, size_t index, bool is_fm) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_osc_set_fm(kick->synths[kick->per_index], index, is_fm); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_osc_is_fm(struct geonkick *kick, size_t index, bool *is_fm) { if (kick == NULL || is_fm == NULL) return GEONKICK_ERROR; return gkick_synth_osc_is_fm(kick->synths[kick->per_index], index, is_fm); } enum geonkick_error geonkick_set_osc_function(struct geonkick *kick, size_t osc_index, enum geonkick_osc_func_type type) { if (kick == NULL) return GEONKICK_ERROR; enum geonkick_error res; res = gkick_synth_set_osc_function(kick->synths[kick->per_index], osc_index, type); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_get_osc_function(struct geonkick *kick, size_t osc_index, enum geonkick_osc_func_type *type) { if (kick == NULL || type == NULL) return GEONKICK_ERROR; return gkick_synth_get_osc_function(kick->synths[kick->per_index], osc_index, type); } enum geonkick_error geonkick_set_osc_phase(struct geonkick *kick, size_t osc_index, gkick_real phase) { if (kick == NULL) return GEONKICK_ERROR; enum geonkick_error res; res = gkick_synth_set_osc_phase(kick->synths[kick->per_index], osc_index, phase); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_get_osc_phase(struct geonkick *kick, size_t osc_index, gkick_real *phase) { if (kick == NULL || phase == NULL) return GEONKICK_ERROR; return gkick_synth_get_osc_phase(kick->synths[kick->per_index], osc_index, phase); } enum geonkick_error geonkick_set_osc_seed(struct geonkick *kick, size_t osc_index, unsigned int seed) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_set_osc_seed(kick->synths[kick->per_index], osc_index, seed); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_get_osc_seed(struct geonkick *kick, size_t osc_index, unsigned int *seed) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_synth_get_osc_seed(kick->synths[kick->per_index], osc_index, seed); } enum geonkick_error geonkick_set_length(struct geonkick *kick, gkick_real len) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_set_length(kick->synths[kick->per_index], len); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_get_length(struct geonkick *kick, gkick_real *len) { if (kick == NULL || len == NULL) return GEONKICK_ERROR; return gkick_synth_get_length(kick->synths[kick->per_index], len); } enum geonkick_error geonkick_get_max_length(struct geonkick *kick, gkick_real *len) { GEONKICK_UNUSED(kick); if (kick == NULL || len == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } *len = GEONKICK_MAX_LENGTH; return GEONKICK_OK; } enum geonkick_error geonkick_kick_set_amplitude(struct geonkick *kick, gkick_real amplitude) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_kick_set_amplitude(kick->synths[kick->per_index], amplitude); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_kick_get_amplitude(struct geonkick *kick, gkick_real *amplitude) { if (kick == NULL || amplitude == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_synth_kick_get_amplitude(kick->synths[kick->per_index], amplitude); } enum geonkick_error geonkick_kick_set_filter_frequency(struct geonkick *kick, gkick_real frequency) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_kick_set_filter_frequency(kick->synths[kick->per_index], frequency); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_kick_filter_enable(struct geonkick *kick, int enable) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = geonkick_synth_kick_filter_enable(kick->synths[kick->per_index], enable); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_kick_filter_is_enabled(struct geonkick *kick, int *enabled) { if (kick == NULL || enabled == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return geonkick_synth_kick_filter_is_enabled(kick->synths[kick->per_index], enabled); } enum geonkick_error geonkick_kick_get_filter_frequency(struct geonkick *kick, gkick_real *frequency) { if (kick == NULL || frequency == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_synth_kick_get_filter_frequency(kick->synths[kick->per_index], frequency); } enum geonkick_error geonkick_kick_set_filter_factor(struct geonkick *kick, gkick_real factor) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_kick_set_filter_factor(kick->synths[kick->per_index], factor); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_kick_get_filter_factor(struct geonkick *kick, gkick_real *factor) { if (kick == NULL || factor == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_synth_kick_get_filter_factor(kick->synths[kick->per_index], factor); } enum geonkick_error geonkick_set_kick_filter_type(struct geonkick *kick, enum gkick_filter_type type) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_set_kick_filter_type(kick->synths[kick->per_index], type); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_get_kick_filter_type(struct geonkick *kick, enum gkick_filter_type *type) { if (kick == NULL || type == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_synth_get_kick_filter_type(kick->synths[kick->per_index], type); } enum geonkick_error geonkick_kick_envelope_get_points(struct geonkick *kick, enum geonkick_envelope_type env_type, gkick_real **buf, size_t *npoints) { if (kick == NULL || buf == NULL || npoints == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_synth_kick_envelope_get_points(kick->synths[kick->per_index], env_type, buf, npoints); } enum geonkick_error geonkick_kick_envelope_set_points(struct geonkick *kick, enum geonkick_envelope_type env_type, const gkick_real *buff, size_t npoints) { if (kick == NULL || buff == NULL || npoints == 0) return GEONKICK_ERROR; enum geonkick_error res; res = gkick_synth_kick_envelope_set_points(kick->synths[kick->per_index], env_type, buff, npoints); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_kick_add_env_point(struct geonkick *kick, enum geonkick_envelope_type env_type, gkick_real x, gkick_real y) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_kick_add_env_point(kick->synths[kick->per_index], env_type, x, y); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_kick_remove_env_point(struct geonkick *kick, enum geonkick_envelope_type env_type, size_t index) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_kick_remove_env_point(kick->synths[kick->per_index], env_type, index); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_kick_update_env_point(struct geonkick *kick, enum geonkick_envelope_type env_type, size_t index, gkick_real x, gkick_real y) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_kick_update_env_point(kick->synths[kick->per_index], env_type, index, x, y); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_set_osc_frequency(struct geonkick *kick, size_t osc_index, gkick_real v) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_set_osc_frequency(kick->synths[kick->per_index], osc_index, v); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_set_osc_pitch_shift(struct geonkick *kick, size_t osc_index, gkick_real semitones) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_set_osc_pitch_shift(kick->synths[kick->per_index], osc_index, semitones); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_set_osc_amplitude(struct geonkick *kick, size_t osc_index, gkick_real v) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_set_osc_amplitude(kick->synths[kick->per_index], osc_index, v); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_get_osc_amplitude(struct geonkick *kick, size_t osc_index, gkick_real *v) { if (kick == NULL || v == NULL) return GEONKICK_ERROR; return gkick_synth_get_osc_amplitude(kick->synths[kick->per_index], osc_index, v); } enum geonkick_error geonkick_get_osc_frequency(struct geonkick *kick, size_t osc_index, gkick_real *v) { if (kick == NULL || v == NULL) return GEONKICK_ERROR; return gkick_synth_get_osc_frequency(kick->synths[kick->per_index], osc_index, v); } enum geonkick_error geonkick_get_osc_pitch_shift(struct geonkick *kick, size_t osc_index, gkick_real *semitones) { if (kick == NULL || semitones == NULL) return GEONKICK_ERROR; return gkick_synth_get_osc_pitch_shift(kick->synths[kick->per_index], osc_index, semitones); } enum geonkick_error geonkick_play(struct geonkick *kick, size_t id) { if (kick == NULL || id >= GEONKICK_MAX_PERCUSSIONS) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_audio_play(kick->audio, id); } enum geonkick_error geonkick_key_pressed(struct geonkick *kick, bool pressed, int note, int velocity) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_audio_key_pressed(kick->audio, pressed, note, velocity); } enum geonkick_error geonkick_get_kick_buffer_size(struct geonkick *kick, size_t *size) { enum geonkick_error res; if (kick == NULL || size == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } res = gkick_synth_get_buffer_size(kick->synths[kick->per_index], size); return res; } enum geonkick_error geonkick_get_kick_buffer(struct geonkick *kick, gkick_real *buffer, size_t size) { enum geonkick_error res; if (kick == NULL || buffer == NULL || size < 1) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } res = gkick_synth_get_buffer(kick->synths[kick->per_index], buffer, size); return res; } enum geonkick_error geonkick_set_kick_buffer_callback(struct geonkick *kick, void (*callback)(void*, gkick_real* buff, size_t size, size_t id), void *arg) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } geonkick_lock(kick); for (size_t i = 0; i < GEONKICK_MAX_PERCUSSIONS; i++) { kick->synths[i]->buffer_callback = callback; kick->synths[i]->callback_args = arg; } geonkick_unlock(kick); return GEONKICK_OK; } enum geonkick_error geonkick_set_kick_limiter_callback(struct geonkick *kick, void (*callback)(void*, size_t index, gkick_real val), void *arg) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_audio_set_limiter_callback(kick->audio, callback, arg); } enum geonkick_error geonkick_set_limiter_value(struct geonkick *kick, gkick_real limit) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_audio_set_limiter_val(kick->audio, kick->per_index, limit); } enum geonkick_error geonkick_get_limiter_value(struct geonkick *kick, gkick_real *limit) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_audio_get_limiter_val(kick->audio, kick->per_index, limit); } enum geonkick_error geonkick_set_osc_filter_type(struct geonkick *kick, size_t osc_index, enum gkick_filter_type type) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_set_osc_filter_type(kick->synths[kick->per_index], osc_index, type); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_get_osc_filter_type(struct geonkick *kick, size_t osc_index, enum gkick_filter_type *type) { if (kick == NULL || type == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_synth_get_osc_filter_type(kick->synths[kick->per_index], osc_index, type); } enum geonkick_error geonkick_set_osc_filter_cutoff_freq(struct geonkick *kick, size_t osc_index, gkick_real cutoff) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_set_osc_filter_cutoff(kick->synths[kick->per_index], osc_index, cutoff); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_get_osc_filter_cutoff_freq(struct geonkick *kick, size_t osc_index, gkick_real *cutoff) { if (kick == NULL || cutoff == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_synth_get_osc_filter_cutoff(kick->synths[kick->per_index], osc_index, cutoff); } enum geonkick_error geonkick_set_osc_filter_factor(struct geonkick *kick, size_t osc_index, gkick_real factor) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_set_osc_filter_factor(kick->synths[kick->per_index], osc_index, factor); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_get_osc_filter_factor(struct geonkick *kick, size_t osc_index, gkick_real *factor) { if (kick == NULL || factor == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_synth_get_osc_filter_factor(kick->synths[kick->per_index], osc_index, factor); } enum geonkick_error geonkick_enbale_osc_filter(struct geonkick *kick, size_t osc_index, int enable) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_osc_enable_filter(kick->synths[kick->per_index], osc_index, enable); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_osc_filter_is_enabled(struct geonkick *kick, size_t osc_index, int *enable) { if (kick == NULL || enable == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_synth_osc_is_enabled_filter(kick->synths[kick->per_index], osc_index, enable); } enum geonkick_error geonkick_get_sample_rate(struct geonkick *kick, int *sample_rate) { GEONKICK_UNUSED(kick); if (sample_rate == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } *sample_rate = kick->sample_rate; return GEONKICK_OK; } enum geonkick_error geonkick_enable_synthesis(struct geonkick *kick, bool enable) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } kick->synthesis_on = enable; if (kick->synthesis_on) { for (size_t i = 0; i < GEONKICK_MAX_PERCUSSIONS; i++) { if (kick->synths[i]->is_active) kick->synths[i]->buffer_update = true; } geonkick_wakeup(kick); } return GEONKICK_OK; } enum geonkick_error geonkick_get_audio_frame(struct geonkick *kick, int channel, gkick_real *val) { return gkick_audio_get_frame(kick->audio, channel, val); } enum geonkick_error geonkick_audio_process(struct geonkick *kick, float **out, size_t channel, size_t size) { return gkick_audio_process(kick->audio, out, channel, size); } enum geonkick_error geonkick_compressor_enable(struct geonkick *kick, int enable) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_compressor_enable(kick->synths[kick->per_index], enable); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_compressor_is_enabled(struct geonkick *kick, int *enabled) { if (kick == NULL || enabled == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_synth_compressor_is_enabled(kick->synths[kick->per_index], enabled); } enum geonkick_error geonkick_compressor_set_attack(struct geonkick *kick, gkick_real attack) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_compressor_set_attack(kick->synths[kick->per_index], attack); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_compressor_get_attack(struct geonkick *kick, gkick_real *attack) { if (kick == NULL || attack == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_synth_compressor_get_attack(kick->synths[kick->per_index], attack); } enum geonkick_error geonkick_compressor_set_release(struct geonkick *kick, gkick_real release) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_compressor_set_release(kick->synths[kick->per_index], release); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_compressor_get_release(struct geonkick *kick, gkick_real *release) { if (kick == NULL || release == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_synth_compressor_get_release(kick->synths[kick->per_index], release); } enum geonkick_error geonkick_compressor_set_threshold(struct geonkick *kick, gkick_real threshold) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_compressor_set_threshold(kick->synths[kick->per_index], threshold); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_compressor_get_threshold(struct geonkick *kick, gkick_real *threshold) { if (kick == NULL || threshold == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_synth_compressor_get_threshold(kick->synths[kick->per_index], threshold); } enum geonkick_error geonkick_compressor_set_ratio(struct geonkick *kick, gkick_real ratio) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_compressor_set_ratio(kick->synths[kick->per_index], ratio); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_compressor_get_ratio(struct geonkick *kick, gkick_real *ratio) { if (kick == NULL || ratio == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_synth_compressor_get_ratio(kick->synths[kick->per_index], ratio); } enum geonkick_error geonkick_compressor_set_knee(struct geonkick *kick, gkick_real knee) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_compressor_set_knee(kick->synths[kick->per_index], knee); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_compressor_get_knee(struct geonkick *kick, gkick_real *knee) { if (kick == NULL || knee == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_synth_compressor_get_knee(kick->synths[kick->per_index], knee); } enum geonkick_error geonkick_compressor_set_makeup(struct geonkick *kick, gkick_real makeup) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_compressor_set_makeup(kick->synths[kick->per_index], makeup); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_compressor_get_makeup(struct geonkick *kick, gkick_real *makeup) { if (kick == NULL || makeup == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_synth_compressor_get_makeup(kick->synths[kick->per_index], makeup); } enum geonkick_error geonkick_distortion_enable(struct geonkick *kick, int enable) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_distortion_enable(kick->synths[kick->per_index], enable); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_distortion_is_enabled(struct geonkick *kick, int *enabled) { if (kick == NULL || enabled == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_synth_distortion_is_enabled(kick->synths[kick->per_index], enabled); } enum geonkick_error geonkick_distortion_set_in_limiter(struct geonkick *kick, gkick_real limit) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_distortion_set_in_limiter(kick->synths[kick->per_index], limit); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_distortion_get_in_limiter(struct geonkick *kick, gkick_real *limit) { if (kick == NULL || limit == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_synth_distortion_get_in_limiter(kick->synths[kick->per_index], limit); } enum geonkick_error geonkick_distortion_set_volume(struct geonkick *kick, gkick_real volume) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_distortion_set_volume(kick->synths[kick->per_index], volume); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_distortion_get_volume(struct geonkick *kick, gkick_real *volume) { if (kick == NULL || volume == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_synth_distortion_get_volume(kick->synths[kick->per_index], volume); } enum geonkick_error geonkick_distortion_set_drive(struct geonkick *kick, gkick_real drive) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_distortion_set_drive(kick->synths[kick->per_index], drive); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_distortion_get_drive(struct geonkick *kick, gkick_real *drive) { if (kick == NULL || drive == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_synth_distortion_get_drive(kick->synths[kick->per_index], drive); } int geonkick_is_module_enabed(struct geonkick *kick, enum GEONKICK_MODULE module) { if (kick == NULL) { gkick_log_error("wrong arguments"); return 0; } switch (module) { case GEONKICK_MODULE_JACK: if (kick->audio != NULL && kick->audio->jack != NULL) return 1; else return 0; default: return 0; } } enum geonkick_error geonkick_enable_group(struct geonkick *kick, size_t index, bool enable) { if (kick == NULL || index >= GKICK_OSC_GROUPS_NUMBER) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_synth_enable_group(kick->synths[kick->per_index], index, enable); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_group_enabled(struct geonkick *kick, size_t index, bool *enabled) { if (kick == NULL || enabled == NULL || index >= GKICK_OSC_GROUPS_NUMBER) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_synth_group_enabled(kick->synths[kick->per_index], index, enabled); } enum geonkick_error geonkick_group_set_amplitude(struct geonkick *kick, size_t index, gkick_real amplitude) { if (kick == NULL || index >= GKICK_OSC_GROUPS_NUMBER) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = geonkick_synth_group_set_amplitude(kick->synths[kick->per_index], index, amplitude); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_group_get_amplitude(struct geonkick *kick, size_t index, gkick_real *amplitude) { if (kick == NULL || index >= GKICK_OSC_GROUPS_NUMBER || amplitude == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return geonkick_synth_group_get_amplitude(kick->synths[kick->per_index], index, amplitude); } enum geonkick_error geonkick_tune_audio_output(struct geonkick *kick, size_t index, bool tune) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_mixer_tune_output(kick->audio->mixer, index, tune); } enum geonkick_error geonkick_is_audio_output_tuned(struct geonkick *kick, size_t index, bool *tune) { if (kick == NULL || tune == NULL || index >= GEONKICK_MAX_PERCUSSIONS) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_mixer_is_output_tuned(kick->audio->mixer, index, tune); } enum geonkick_error geonkick_set_osc_sample(struct geonkick *kick, size_t osc_index, const gkick_real *data, size_t size) { if (kick == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } enum geonkick_error res; res = geonkick_synth_set_osc_sample(kick->synths[kick->per_index], osc_index, data, size); if (res == GEONKICK_OK && kick->synths[kick->per_index]->buffer_update) geonkick_wakeup(kick); return res; } enum geonkick_error geonkick_get_osc_sample(struct geonkick *kick, size_t osc_index, gkick_real **data, size_t *size) { if (kick == NULL || data == NULL || size == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return geonkick_synth_get_osc_sample(kick->synths[kick->per_index], osc_index, data, size); } enum geonkick_error geonkick_set_current_percussion(struct geonkick *kick, size_t index) { if (kick == NULL || index >= GEONKICK_MAX_PERCUSSIONS) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } kick->per_index = index; kick->audio->mixer->limiter_callback_index = index; return GEONKICK_OK; } enum geonkick_error geonkick_get_current_percussion(struct geonkick *kick, size_t *index) { if (kick == NULL || index == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } *index = kick->per_index; return GEONKICK_OK; } void geonkick_process(struct geonkick *kick) { for (size_t i = 0; i < GEONKICK_MAX_PERCUSSIONS; i++) { struct gkick_synth *synth = kick->synths[i]; if (synth != NULL && synth->is_active && synth->buffer_update) gkick_synth_process(synth); } } /** * Finds the first unused/free percussion index. */ enum geonkick_error geonkick_unused_percussion(struct geonkick *kick, int *index) { if (kick == NULL || index == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } *index = -1; for (size_t i = 0; i < GEONKICK_MAX_PERCUSSIONS; i++) { if (!kick->synths[i]->is_active) { *index = i; return GEONKICK_OK; } } return GEONKICK_ERROR; } enum geonkick_error geonkick_enable_percussion(struct geonkick *kick, size_t index, bool enable) { if (kick == NULL || index >= GEONKICK_MAX_PERCUSSIONS) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } kick->synths[index]->is_active = enable; kick->audio->audio_outputs[index]->enabled = enable; return GEONKICK_OK; } enum geonkick_error geonkick_is_percussion_enabled(struct geonkick *kick, size_t index, bool *enable) { if (kick == NULL || enable == NULL || index >= GEONKICK_MAX_PERCUSSIONS) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } *enable = kick->synths[index]->is_active; return GEONKICK_OK; } size_t geonkick_percussion_number() { return GEONKICK_MAX_PERCUSSIONS; } enum geonkick_error geonkick_set_playing_key(struct geonkick *kick, size_t id, signed char key) { if (kick == NULL || id >= GEONKICK_MAX_PERCUSSIONS) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_audio_output_set_playing_key(kick->synths[id]->output, key); } enum geonkick_error geonkick_get_playing_key(struct geonkick *kick, size_t id, signed char *key) { if (kick == NULL || key == NULL || id >= GEONKICK_MAX_PERCUSSIONS) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_audio_output_get_playing_key(kick->synths[id]->output, key); } enum geonkick_error geonkick_set_percussion_name(struct geonkick *kick, size_t id, const char *name, size_t size) { if (kick == NULL || id >= GEONKICK_MAX_PERCUSSIONS || name == NULL || size < 1) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } struct gkick_synth *synth = kick->synths[id]; gkick_synth_lock(synth); memset(synth->name, '\0', sizeof(synth->name)); if (size < strlen(synth->name)) strcpy(synth->name, name); else strncpy(synth->name, name, sizeof(synth->name) - 1); gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error geonkick_get_percussion_name(struct geonkick *kick, size_t id, char *name, size_t size) { if (kick == NULL || id >= GEONKICK_MAX_PERCUSSIONS || name == NULL || size < 1) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } struct gkick_synth *synth = kick->synths[id]; gkick_synth_lock(synth); memset(name, '\0', size); if (size > strlen(synth->name)) strcpy(name, synth->name); else strncpy(name, synth->name, size - 1); gkick_synth_unlock(synth); return GEONKICK_OK; } size_t geonkick_channels_number() { return GEONKICK_MAX_CHANNELS; } enum geonkick_error geonkick_set_percussion_channel(struct geonkick *kick, size_t id, size_t channel) { if (kick == NULL || id >= GEONKICK_MAX_PERCUSSIONS || channel >= GEONKICK_MAX_CHANNELS) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_audio_output_set_channel(kick->synths[id]->output, channel); } enum geonkick_error geonkick_get_percussion_channel(struct geonkick *kick, size_t id, size_t *channel) { if (kick == NULL || channel == NULL || id >= GEONKICK_MAX_PERCUSSIONS) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_audio_output_get_channel(kick->synths[id]->output, channel); } enum geonkick_error geonkick_percussion_set_limiter(struct geonkick *kick, size_t id, gkick_real val) { if (kick == NULL || id >= GEONKICK_MAX_PERCUSSIONS) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_mixer_limiter_set(kick->audio->mixer, id, val); } enum geonkick_error geonkick_percussion_get_limiter(struct geonkick *kick, size_t id, gkick_real *val) { if (kick == NULL || val == NULL || id >= GEONKICK_MAX_PERCUSSIONS) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_mixer_limiter_get(kick->audio->mixer, id, val); } enum geonkick_error geonkick_percussion_mute(struct geonkick *kick, size_t id, bool b) { if (kick == NULL || id >= GEONKICK_MAX_PERCUSSIONS) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_mixer_mute(kick->audio->mixer, id, b); } enum geonkick_error geonkick_percussion_is_muted(struct geonkick *kick, size_t id, bool *b) { if (kick == NULL || b == NULL || id >= GEONKICK_MAX_PERCUSSIONS) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_mixer_is_muted(kick->audio->mixer, id, b); } enum geonkick_error geonkick_percussion_solo(struct geonkick *kick, size_t id, bool b) { if (kick == NULL || id >= GEONKICK_MAX_PERCUSSIONS) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_mixer_solo(kick->audio->mixer, id, b); } enum geonkick_error geonkick_percussion_is_solo(struct geonkick *kick, size_t id, bool *b) { if (kick == NULL || b == NULL || id >= GEONKICK_MAX_PERCUSSIONS) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_mixer_is_solo(kick->audio->mixer, id, b); } enum geonkick_error geonkick_set_preview_sample(struct geonkick *kick, const gkick_real *data, size_t size) { if (kick == NULL || data == NULL || size < 1) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } struct gkick_audio_output *output = kick->audio->mixer->audio_outputs[GEONKICK_MAX_PERCUSSIONS]; gkick_audio_output_lock(output); gkick_buffer_set_data((struct gkick_buffer*)output->updated_buffer, data, size); gkick_audio_output_unlock(output); return GEONKICK_OK; } void geonkick_play_sample_preview(struct geonkick *kick) { gkick_audio_play(kick->audio, GEONKICK_MAX_PERCUSSIONS); } enum geonkick_error geonkick_set_sample_preview_limiter(struct geonkick *kick, gkick_real val) { return gkick_audio_set_limiter_val(kick->audio, GEONKICK_MAX_PERCUSSIONS, val); } enum geonkick_error geonkick_get_sample_preview_limiter(struct geonkick *kick, gkick_real *val) { return gkick_audio_get_limiter_val(kick->audio, GEONKICK_MAX_PERCUSSIONS, val); } void geonkick_wakeup(struct geonkick *kick) { if (kick->synthesis_on) geonkick_worker_wakeup(); } bool geonkick_is_plugin() { return !GKICK_IS_STANDALONE; } geonkick-2.10.2/src/dsp/src/geonkick.h000066400000000000000000000511211451017372400175010ustar00rootroot00000000000000/** * File name: geonkick.h * Project: GeonKick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of GeonKick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_H #define GEONKICK_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include #include #include #include #ifdef __STDC_NO_ATOMICS__ #error atomic operations are not supported #endif #include "gkick_log.h" #ifdef GEONKICK_DOUBLE_PRECISION typedef double gkick_real; #else typedef float gkick_real; #endif #define GEONKICK_UNUSED(expr) (void)expr #define GEONKICK_VERSION 0x021002 #define GEONKICK_NAME "Geonkick" #define GEONKICK_APP_NAME "geonkick" #define GEOKICK_VERSION_STRING "2.10.2" #ifdef GEONKICK_AUDIO_JACK #define GKICK_IS_STANDALONE (1) #else #define GKICK_IS_STANDALONE (0) #endif #define GEONKICK_DEFAULT_SAMPLE_RATE 48000 enum geonkick_error { GEONKICK_OK = 0, GEONKICK_ERROR = 1, GEONKICK_ERROR_MEM_ALLOC = 2, GEONKICK_ERROR_WRONG_ARGUMENTS = 3 }; enum gkick_key_state { GKICK_KEY_STATE_DEFAULT = 0, GKICK_KEY_STATE_PRESSED = 1, GKICK_KEY_STATE_RELEASED = 2 }; enum geonkick_envelope_type { GEONKICK_AMPLITUDE_ENVELOPE = 0, GEONKICK_FREQUENCY_ENVELOPE = 1, GEONKICK_FILTER_CUTOFF_ENVELOPE = 2, GEONKICK_DISTORTION_DRIVE_ENVELOPE = 3, GEONKICK_DISTORTION_VOLUME_ENVELOPE = 4, GEONKICK_PITCH_SHIFT_ENVELOPE = 5 }; enum gkick_filter_type { GEONKICK_FILTER_LOW_PASS = 0, GEONKICK_FILTER_HIGH_PASS = 1, GEONKICK_FILTER_BAND_PASS = 2 }; enum geonkick_osc_func_type { GEONKICK_OSC_FUNC_SINE = 0, GEONKICK_OSC_FUNC_SQUARE = 1, GEONKICK_OSC_FUNC_TRIANGLE = 2, GEONKICK_OSC_FUNC_SAWTOOTH = 3, GEONKICK_OSC_FUNC_NOISE_WHITE = 4, GEONKICK_OSC_FUNC_NOISE_PINK = 5, GEONKICK_OSC_FUNC_NOISE_BROWNIAN = 6, GEONKICK_OSC_FUNC_SAMPLE = 7 }; enum GEONKICK_MODULE { GEONKICK_MODULE_UNKNOWN = 0, GEONKICK_MODULE_JACK = 1 }; #define GKICK_OSC_GROUPS_NUMBER 3 #define GKICK_OSC_GROUP_SIZE 3 enum geonkick_channel_type { GEONKICK_CHANNEL_AUDIO_INPUT = 0, GEONKICK_CHANNEL_AUDIO_OUTPUT = 1, GEONKICK_CHANNEL_MIDI_INPUT = 2, GEONKICK_CHANNEL_MIDI_OUTPUT = 3 }; #ifdef GEONKICK_SINGLE #define GEONKICK_MAX_PERCUSSIONS 1 #else #define GEONKICK_MAX_PERCUSSIONS 16 #endif /** * Maximum audio number of output stereo channels. */ #if defined(GEONKICK_SINGLE) #define GEONKICK_MAX_CHANNELS 1 #else #define GEONKICK_MAX_CHANNELS 16 #endif // if defined(GEONKICK_SINGLE) #define GEONKICK_ANY_KEY -1 struct geonkick; enum geonkick_error geonkick_create(struct geonkick **kick, int sample_rate); void geonkick_free(struct geonkick **kick); enum geonkick_error geonkick_add_oscillator(struct geonkick* kick, size_t *index); enum geonkick_error geonkick_remove_oscillator(struct geonkick* kick, size_t index); enum geonkick_error geonkick_enable_oscillator(struct geonkick* kick, size_t index); enum geonkick_error geonkick_disable_oscillator(struct geonkick* kick, size_t index); enum geonkick_error geonkick_is_oscillator_enabled(struct geonkick* kick, size_t index, int *enabled); enum geonkick_error geonkick_get_oscillators_number(struct geonkick* kick, size_t *number); enum geonkick_error geonkick_osc_envelope_get_points(struct geonkick *kick, size_t osc_index, size_t env_index, gkick_real **buf, size_t *npoints); enum geonkick_error geonkick_osc_envelope_set_points(struct geonkick *kick, size_t osc_index, size_t env_index, const gkick_real *buff, size_t npoints); enum geonkick_error geonkick_osc_envelope_add_point(struct geonkick *kick, size_t osc_index, size_t env_index, gkick_real x, gkick_real y); enum geonkick_error geonkick_osc_envelope_remove_point(struct geonkick *kick, size_t osc_index, size_t env_index, size_t index); enum geonkick_error geonkick_osc_envelope_update_point(struct geonkick *kick, size_t osc_index, size_t env_index, size_t index, gkick_real x, gkick_real y); enum geonkick_error geonkick_osc_set_fm(struct geonkick *kick, size_t index, bool is_fm); enum geonkick_error geonkick_osc_is_fm(struct geonkick *kick, size_t index, bool *is_fm); enum geonkick_error geonkick_set_osc_function(struct geonkick *kick, size_t osc_index, enum geonkick_osc_func_type type); enum geonkick_error geonkick_get_osc_function(struct geonkick *kick, size_t osc_index, enum geonkick_osc_func_type *type); enum geonkick_error geonkick_set_osc_phase(struct geonkick *kick, size_t osc_index, gkick_real phase); enum geonkick_error geonkick_get_osc_phase(struct geonkick *kick, size_t osc_index, gkick_real *phase); enum geonkick_error geonkick_set_osc_seed(struct geonkick *kick, size_t osc_index, unsigned int seed); enum geonkick_error geonkick_get_osc_seed(struct geonkick *kick, size_t osc_index, unsigned int *seed); enum geonkick_error geonkick_set_length(struct geonkick *kick, gkick_real len); enum geonkick_error geonkick_get_length(struct geonkick *kick, gkick_real *len); enum geonkick_error geonkick_get_max_length(struct geonkick *kick, gkick_real *len); enum geonkick_error geonkick_kick_set_amplitude(struct geonkick *kick, gkick_real amplitude); enum geonkick_error geonkick_kick_get_amplitude(struct geonkick *kick, gkick_real *amplitude); enum geonkick_error geonkick_kick_filter_enable(struct geonkick *kick, int enable); enum geonkick_error geonkick_kick_filter_is_enabled(struct geonkick *kick, int *enabled); enum geonkick_error geonkick_kick_set_filter_frequency(struct geonkick *kick, gkick_real frequency); enum geonkick_error geonkick_kick_get_filter_frequency(struct geonkick *kick, gkick_real *frequency); enum geonkick_error geonkick_kick_set_filter_factor(struct geonkick *kick, gkick_real factor); enum geonkick_error geonkick_kick_get_filter_factor(struct geonkick *kick, gkick_real *factor); enum geonkick_error geonkick_set_kick_filter_type(struct geonkick *kick, enum gkick_filter_type type); enum geonkick_error geonkick_get_kick_filter_type(struct geonkick *kick, enum gkick_filter_type *type); enum geonkick_error geonkick_kick_envelope_get_points(struct geonkick *kick, enum geonkick_envelope_type env_type, gkick_real **buf, size_t *npoints); enum geonkick_error geonkick_kick_envelope_set_points(struct geonkick *kick, enum geonkick_envelope_type env_type, const gkick_real *buff, size_t npoints); enum geonkick_error geonkick_kick_add_env_point(struct geonkick *kick, enum geonkick_envelope_type env_type, gkick_real x, gkick_real y); enum geonkick_error geonkick_kick_remove_env_point(struct geonkick *kick, enum geonkick_envelope_type env_type, size_t index); enum geonkick_error geonkick_kick_update_env_point(struct geonkick *kick, enum geonkick_envelope_type env_type, size_t index, gkick_real x, gkick_real y); enum geonkick_error geonkick_set_osc_amplitude(struct geonkick *kick, size_t osc_index, gkick_real v); enum geonkick_error geonkick_set_osc_frequency(struct geonkick *kick, size_t osc_index, gkick_real v); enum geonkick_error geonkick_set_osc_pitch_shift(struct geonkick *kick, size_t osc_index, gkick_real semitones); enum geonkick_error geonkick_get_osc_amplitude(struct geonkick *kick, size_t osc_index, gkick_real *v); enum geonkick_error geonkick_get_osc_frequency(struct geonkick *kick, size_t osc_index, gkick_real *v); enum geonkick_error geonkick_get_osc_pitch_shift(struct geonkick *kick, size_t osc_index, gkick_real *semitones); enum geonkick_error geonkick_play(struct geonkick *kick, size_t id); /** * This function must be called * only from the audio thread. */ enum geonkick_error geonkick_key_pressed(struct geonkick *kick, bool pressed, int note, int velocity); enum geonkick_error geonkick_get_kick_buffer_size(struct geonkick *kick, size_t *size); enum geonkick_error geonkick_get_kick_buffer(struct geonkick *kick, gkick_real *buffer, size_t size); enum geonkick_error geonkick_set_kick_buffer_callback(struct geonkick *kick, void (*callback)(void*, gkick_real *buff, size_t size, size_t id), void *arg); enum geonkick_error geonkick_set_kick_limiter_callback(struct geonkick *kick, void (*callback)(void*, size_t index, gkick_real val), void *arg); enum geonkick_error geonkick_set_limiter_value(struct geonkick *kick, gkick_real limit); enum geonkick_error geonkick_get_limiter_value(struct geonkick *kick, gkick_real *limit); enum geonkick_error geonkick_set_osc_filter_type(struct geonkick *kick, size_t osc_index, enum gkick_filter_type type); enum geonkick_error geonkick_get_osc_filter_type(struct geonkick *kick, size_t osc_index, enum gkick_filter_type *type); enum geonkick_error geonkick_set_osc_filter_cutoff_freq(struct geonkick *kick, size_t osc_index, gkick_real cutoff); enum geonkick_error geonkick_get_osc_filter_cutoff_freq(struct geonkick *kick, size_t osc_index, gkick_real *cutoff); enum geonkick_error geonkick_set_osc_filter_factor(struct geonkick *kick, size_t osc_index, gkick_real factor); enum geonkick_error geonkick_get_osc_filter_factor(struct geonkick *kick, size_t osc_index, gkick_real *factor); enum geonkick_error geonkick_enbale_osc_filter(struct geonkick *kick, size_t osc_index, int enable); enum geonkick_error geonkick_osc_filter_is_enabled(struct geonkick *kick, size_t osc_index, int *enable); enum geonkick_error geonkick_get_sample_rate(struct geonkick *kick, int *sample_rate); enum geonkick_error geonkick_enable_synthesis(struct geonkick *kick, bool enable); enum geonkick_error geonkick_get_audio_frame(struct geonkick *kick, int channel, gkick_real *val); enum geonkick_error geonkick_audio_process(struct geonkick *kick, float **out, size_t channel, size_t size); enum geonkick_error geonkick_compressor_enable(struct geonkick *kick, int enable); enum geonkick_error geonkick_compressor_is_enabled(struct geonkick *kick, int *enabled); enum geonkick_error geonkick_compressor_set_attack(struct geonkick *kick, gkick_real attack); enum geonkick_error geonkick_compressor_get_attack(struct geonkick *kick, gkick_real *attack); enum geonkick_error geonkick_compressor_set_release(struct geonkick *kick, gkick_real release); enum geonkick_error geonkick_compressor_get_release(struct geonkick *kick, gkick_real *release); enum geonkick_error geonkick_compressor_set_threshold(struct geonkick *kick, gkick_real threshold); enum geonkick_error geonkick_compressor_get_threshold(struct geonkick *kick, gkick_real *threshold); enum geonkick_error geonkick_compressor_set_ratio(struct geonkick *kick, gkick_real ratio); enum geonkick_error geonkick_compressor_get_ratio(struct geonkick *kick, gkick_real *ratio); enum geonkick_error geonkick_compressor_set_knee(struct geonkick *kick, gkick_real knee); enum geonkick_error geonkick_compressor_get_knee(struct geonkick *kick, gkick_real *knee); enum geonkick_error geonkick_compressor_set_makeup(struct geonkick *kick, gkick_real makeup); enum geonkick_error geonkick_compressor_get_makeup(struct geonkick *kick, gkick_real *makeup); enum geonkick_error geonkick_distortion_enable(struct geonkick *kick, int enable); enum geonkick_error geonkick_distortion_is_enabled(struct geonkick *kick, int *enabled); enum geonkick_error geonkick_distortion_set_in_limiter(struct geonkick *kick, gkick_real limit); enum geonkick_error geonkick_distortion_get_in_limiter(struct geonkick *kick, gkick_real *limit); enum geonkick_error geonkick_distortion_set_volume(struct geonkick *kick, gkick_real volume); enum geonkick_error geonkick_distortion_get_volume(struct geonkick *kick, gkick_real *volume); enum geonkick_error geonkick_distortion_set_drive(struct geonkick *kick, gkick_real drive); enum geonkick_error geonkick_distortion_get_drive(struct geonkick *kick, gkick_real *drive); int geonkick_is_module_enabed(struct geonkick *kick, enum GEONKICK_MODULE module); enum geonkick_error geonkick_enable_group(struct geonkick *kick, size_t index, bool enable); enum geonkick_error geonkick_group_enabled(struct geonkick *kick, size_t index, bool *enabled); enum geonkick_error geonkick_group_set_amplitude(struct geonkick *kick, size_t index, gkick_real amplitude); enum geonkick_error geonkick_group_get_amplitude(struct geonkick *kick, size_t index, gkick_real *amplitude); enum geonkick_error geonkick_tune_audio_output(struct geonkick *kick, size_t index, bool tune); enum geonkick_error geonkick_is_audio_output_tuned(struct geonkick *kick, size_t index, bool *tune); enum geonkick_error geonkick_set_osc_sample(struct geonkick *kick, size_t osc_index, const gkick_real *data, size_t size); enum geonkick_error geonkick_get_osc_sample(struct geonkick *kick, size_t osc_index, gkick_real **data, size_t *size); enum geonkick_error geonkick_set_current_percussion(struct geonkick *kick, size_t index); enum geonkick_error geonkick_get_current_percussion(struct geonkick *kick, size_t *index); enum geonkick_error geonkick_unused_percussion(struct geonkick *kick, int *index); enum geonkick_error geonkick_enable_percussion(struct geonkick *kick, size_t index, bool enable); enum geonkick_error geonkick_is_percussion_enabled(struct geonkick *kick, size_t index, bool *enable); size_t geonkick_percussion_number(); enum geonkick_error geonkick_set_playing_key(struct geonkick *kick, size_t id, signed char key); enum geonkick_error geonkick_get_playing_key(struct geonkick *kick, size_t id, signed char *key); enum geonkick_error geonkick_set_percussion_name(struct geonkick *kick, size_t id, const char *name, size_t size); enum geonkick_error geonkick_get_percussion_name(struct geonkick *kick, size_t id, char *name, size_t size); size_t geonkick_channels_number(); enum geonkick_error geonkick_set_percussion_channel(struct geonkick *kick, size_t id, size_t channel); enum geonkick_error geonkick_get_percussion_channel(struct geonkick *kick, size_t id, size_t *channel); enum geonkick_error geonkick_percussion_set_limiter(struct geonkick *kick, size_t id, gkick_real val); enum geonkick_error geonkick_percussion_get_limiter(struct geonkick *kick, size_t id, gkick_real *val); enum geonkick_error geonkick_percussion_mute(struct geonkick *kick, size_t id, bool b); enum geonkick_error geonkick_percussion_is_muted(struct geonkick *kick, size_t id, bool *b); enum geonkick_error geonkick_percussion_solo(struct geonkick *kick, size_t id, bool b); enum geonkick_error geonkick_percussion_is_solo(struct geonkick *kick, size_t id, bool *b); enum geonkick_error geonkick_set_preview_sample(struct geonkick *kick, const gkick_real *data, size_t size); void geonkick_play_sample_preview(struct geonkick *kick); enum geonkick_error geonkick_set_sample_preview_limiter(struct geonkick *kick, gkick_real val); enum geonkick_error geonkick_get_sample_preview_limiter(struct geonkick *kick, gkick_real *val); bool geonkick_is_plugin(); #ifdef __cplusplus } #endif #endif geonkick-2.10.2/src/dsp/src/geonkick_internal.h000066400000000000000000000037451451017372400214060ustar00rootroot00000000000000/** * File name: geonkick_internal.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_INTERNAL_H #define GEONKICK_INTERNAL_H #include "geonkick.h" #include "synthesizer.h" #include "gkick_audio.h" #include "gkick_buffer.h" #include #include #define GEONKICK_MAX_INSTANCES 500 #include "worker.h" /* Kick maximum length in seconds. */ #define GEONKICK_MAX_LENGTH 4 struct geonkick { size_t id; char name[30]; int sample_rate; /* The list of synths of available synths. */ struct gkick_synth *synths[GEONKICK_MAX_PERCUSSIONS]; struct gkick_audio *audio; /* Current controllable percussion index. */ _Atomic size_t per_index; /** * Specifies if the synthesis is tuned off. * If it is false any updates of the synthesizers parameters * will not trigger the synthesis. */ atomic_bool synthesis_on; pthread_mutex_t lock; struct gkick_worker *worker; }; void geonkick_lock(struct geonkick *kick); void geonkick_unlock(struct geonkick *kick); void geonkick_process(struct geonkick *kick); void geonkick_wakeup(struct geonkick *kick); #endif // GEONKICK_INTERNAL_H geonkick-2.10.2/src/dsp/src/gkick_audio.c000066400000000000000000000143221451017372400201550ustar00rootroot00000000000000/** * File name: gkick_audio.c * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "gkick_audio.h" #include "audio_output.h" #include "mixer.h" #ifdef GEONKICK_AUDIO_JACK #include "gkick_jack.h" #endif enum geonkick_error gkick_audio_create(struct gkick_audio** audio, int sample_rate) { if (audio == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } *audio = (struct gkick_audio*)calloc(1, sizeof(struct gkick_audio)); if (*audio == NULL) { gkick_log_error("can't allocate memory"); return GEONKICK_ERROR_MEM_ALLOC; } (*audio)->sample_rate = sample_rate; #ifdef GEONKICK_AUDIO_JACK if (gkick_create_jack(&(*audio)->jack) != GEONKICK_OK) { gkick_log_warning("can't create jack module. " "Jack server is either not running or not installed"); } else { (*audio)->sample_rate = gkick_jack_sample_rate((*audio)->jack); } #endif // GEONKICK_AUDIO_JACK gkick_log_debug("sample rate: %d", (*audio)->sample_rate); for (size_t i = 0; i < GEONKICK_MAX_PERCUSSIONS + 1; i++) { if (gkick_audio_output_create(&(*audio)->audio_outputs[i], sample_rate) != GEONKICK_OK) { gkick_log_error("can't create audio output"); gkick_audio_free(audio); return GEONKICK_ERROR; } (*audio)->audio_outputs[i]->enabled = true; } (*audio)->audio_outputs[GEONKICK_MAX_PERCUSSIONS]->limiter = 1000000; if (gkick_mixer_create(&(*audio)->mixer) != GEONKICK_OK) { gkick_log_error("can't create mixer"); gkick_audio_free(audio); return GEONKICK_ERROR; } (*audio)->mixer->audio_outputs = (*audio)->audio_outputs; return GEONKICK_OK; } enum geonkick_error gkick_start_audio(struct gkick_audio *audio) { #ifdef GEONKICK_AUDIO_JACK if (audio->jack != NULL && geonkick_jack_start(audio->jack, audio->mixer) != GEONKICK_OK) { gkick_log_warning("can't start jack module"); return GEONKICK_ERROR; } #endif // GEONKICK_AUDIO_JACK return GEONKICK_OK; } void gkick_audio_free(struct gkick_audio** audio) { if (audio != NULL && *audio != NULL) { #ifdef GEONKICK_AUDIO_JACK gkick_jack_free(&(*audio)->jack); #endif // GEONKICK_AUDIO_JACK gkick_mixer_free(&(*audio)->mixer); for (size_t i = 0; i < GEONKICK_MAX_PERCUSSIONS; i++) gkick_audio_output_free(&(*audio)->audio_outputs[i]); free(*audio); *audio = NULL; } } enum geonkick_error gkick_audio_set_limiter_val(struct gkick_audio *audio, size_t index, gkick_real limit) { if (limit < 0.0f) limit = 0.0f; else if (limit > 10.0f) limit = 10.0f; return gkick_mixer_limiter_set(audio->mixer, index, limit); } enum geonkick_error gkick_audio_get_limiter_val(struct gkick_audio *audio, size_t index, gkick_real *limit) { return gkick_mixer_limiter_get(audio->mixer, index, limit); } enum geonkick_error gkick_audio_play(struct gkick_audio *audio, size_t id) { if (audio == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } if (id < GEONKICK_MAX_PERCUSSIONS && audio->audio_outputs[id]->enabled) { gkick_audio_output_play(audio->audio_outputs[id]); } else if (id == GEONKICK_MAX_PERCUSSIONS) { // Play sample preview. gkick_audio_output_play(audio->audio_outputs[id]); } return GEONKICK_OK; } enum geonkick_error gkick_audio_key_pressed(struct gkick_audio *audio, bool pressed, int note, int velocity) { if (audio == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } struct gkick_note_info key; key.channel = 1; key.note_number = note; key.velocity = velocity; key.state = pressed ? GKICK_KEY_STATE_PRESSED : GKICK_KEY_STATE_RELEASED; return gkick_mixer_key_pressed(audio->mixer, &key); } enum geonkick_error gkick_audio_get_frame(struct gkick_audio *audio, int channel , gkick_real *val) { if (audio == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_mixer_get_frame(audio->mixer, channel, val); } enum geonkick_error gkick_audio_process(struct gkick_audio *audio, float** out, size_t channel, size_t size) { return gkick_mixer_process(audio->mixer, out, channel, size); } enum geonkick_error gkick_audio_set_limiter_callback(struct gkick_audio *audio, void (*callback)(void*, size_t index, gkick_real val), void *arg) { return gkick_mixer_set_limiter_callback(audio->mixer, callback, arg); } geonkick-2.10.2/src/dsp/src/gkick_audio.h000066400000000000000000000051201451017372400201560ustar00rootroot00000000000000/** * File name: gkick_audio.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GKICK_AUDIO_H #define GKICK_AUDIO_H #include "geonkick_internal.h" struct audio_output; struct gkick_jack; struct gkick_mixer; struct gkick_audio { int sample_rate; /* Audio outputs. The last audio output is used as a sample preview. */ struct gkick_audio_output *audio_outputs[GEONKICK_MAX_PERCUSSIONS + 1]; struct gkick_mixer *mixer; struct gkick_jack *jack; }; enum geonkick_error gkick_audio_create(struct gkick_audio** audio, int sample_rate); enum geonkick_error gkick_start_audio(struct gkick_audio *audio); void gkick_audio_free(struct gkick_audio** audio); enum geonkick_error gkick_audio_set_limiter_val(struct gkick_audio *audio, size_t index, gkick_real limit); enum geonkick_error gkick_audio_get_limiter_val(struct gkick_audio *audio, size_t index, gkick_real *limit); enum geonkick_error gkick_audio_play(struct gkick_audio *audio, size_t id); enum geonkick_error gkick_audio_key_pressed(struct gkick_audio *audio, bool pressed, int note, int velocity); enum geonkick_error gkick_audio_get_frame(struct gkick_audio *audio, int channel, gkick_real *val); enum geonkick_error gkick_audio_process(struct gkick_audio *audio, float** out, size_t channel, size_t size); enum geonkick_error gkick_audio_set_limiter_callback(struct gkick_audio *audio, void (*callback)(void*, size_t index, gkick_real val), void *arg); #endif // GKICK_AUDIO_H geonkick-2.10.2/src/dsp/src/gkick_buffer.c000066400000000000000000000122171451017372400203260ustar00rootroot00000000000000/** * File name: gkick_buffer.c * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "gkick_buffer.h" void gkick_buffer_new(struct gkick_buffer **buffer, int size) { if (buffer == NULL || size < 1) { gkick_log_error("wrong arguments"); return; } *buffer = (struct gkick_buffer*)malloc(sizeof(struct gkick_buffer)); if (*buffer == NULL) { gkick_log_error("can't allocate memory"); return; } (*buffer)->max_size = size; (*buffer)->size = (*buffer)->max_size; (*buffer)->currentIndex = 0; (*buffer)->floatIndex = 0.0f; (*buffer)->buff = (gkick_real*)malloc(sizeof(gkick_real) * (*buffer)->max_size); if ((*buffer)->buff == NULL) { gkick_log_error("can't allocate memory"); gkick_buffer_free(buffer); return; } } void gkick_buffer_free(struct gkick_buffer **buffer) { if (buffer == NULL || *buffer == NULL) return; if ((*buffer)->buff != NULL) free((*buffer)->buff); free(*buffer); *buffer = NULL; } void gkick_buffer_reset(struct gkick_buffer *buffer) { buffer->currentIndex = 0; buffer->floatIndex = 0.0f; } void gkick_buffer_set_data(struct gkick_buffer *buffer, const gkick_real *data, size_t size) { if (buffer == NULL) return; if (size > buffer->max_size) size = buffer->max_size; if (data != NULL && size > 0) memcpy(buffer->buff, data, sizeof(gkick_real) * size); else memset(buffer->buff, 0, sizeof(gkick_real) * buffer->max_size); buffer->size = size; buffer->currentIndex = buffer->size; buffer->floatIndex = buffer->size; } void gkick_buffer_set_at(struct gkick_buffer *buffer, size_t index, gkick_real val) { if (buffer != NULL && buffer->size > 0 && index < buffer->size) buffer->buff[index] = val; } gkick_real gkick_buffer_get_at(struct gkick_buffer *buffer, size_t index) { if (buffer != NULL && buffer->size > 0 && index < buffer->size) return buffer->buff[index]; return 0.0; } gkick_real gkick_buffer_get_next(struct gkick_buffer *buffer) { gkick_real val = 0.0f; if (buffer->size > 0 && buffer->currentIndex < buffer->size) { val = buffer->buff[buffer->currentIndex++]; buffer->floatIndex = buffer->currentIndex; } return val; } gkick_real gkick_buffer_stretch_get_next(struct gkick_buffer *buffer, gkick_real factor) { if (buffer->currentIndex < buffer->size - 2) { /* Do linear interpolation. */ gkick_real d = buffer->floatIndex - buffer->currentIndex; gkick_real val = buffer->buff[buffer->currentIndex] * (1.0f - d) + buffer->buff[buffer->currentIndex + 1] * d; buffer->floatIndex += factor; buffer->currentIndex = buffer->floatIndex; return val; } else if (buffer->currentIndex < buffer->size - 1) { return buffer->buff[buffer->currentIndex]; } return 0.0f; } void gkick_buffer_set_size(struct gkick_buffer *buffer, size_t size) { if (size < 1) buffer->size = 0; else if (size > buffer->max_size) buffer->size = buffer->max_size; else buffer->size = size; buffer->currentIndex = 0; buffer->floatIndex = 0.0f; } size_t gkick_buffer_index(struct gkick_buffer *buffer) { return buffer->currentIndex; } size_t gkick_buffer_size(struct gkick_buffer *buffer) { return buffer->size; } void gkick_buffer_push_back(struct gkick_buffer *buffer, gkick_real val) { if (buffer->size < 1 || buffer->currentIndex > buffer->size - 1) return; buffer->buff[buffer->currentIndex++] = val; buffer->floatIndex = buffer->currentIndex; } bool gkick_buffer_is_end(struct gkick_buffer *buffer) { return (buffer->size < 1) || (buffer->currentIndex > buffer->size - 1); } geonkick-2.10.2/src/dsp/src/gkick_buffer.h000066400000000000000000000050131451017372400203270ustar00rootroot00000000000000/** * File name: gkick_buffer.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GKICK_BUFFER_H #define GKICK_BUFFER_H #include "geonkick_internal.h" struct gkick_buffer { gkick_real *buff; /** * Real allocated fixed size, alwayse bigger than current size. * Never changes during the life-time of the buffer. */ size_t max_size; /** * Current position in the buffer. */ size_t currentIndex; gkick_real floatIndex; /** * Current size of the buffer. Can change during * the life-time of the buffer. */ size_t size; }; void gkick_buffer_new(struct gkick_buffer **buffer, int size); void gkick_buffer_free(struct gkick_buffer **buffer); void gkick_buffer_reset(struct gkick_buffer *buffer); void gkick_buffer_set_data(struct gkick_buffer *buffer, const gkick_real *data, size_t size); void gkick_buffer_set_at(struct gkick_buffer *buffer, size_t index, gkick_real val); gkick_real gkick_buffer_get_at(struct gkick_buffer *buffer, size_t index); gkick_real gkick_buffer_get_next(struct gkick_buffer *buffer); gkick_real gkick_buffer_stretch_get_next(struct gkick_buffer *buffer, gkick_real factor); void gkick_buffer_set_size(struct gkick_buffer *buffer, size_t size); size_t gkick_buffer_size(struct gkick_buffer *buffer); size_t gkick_buffer_index(struct gkick_buffer *buffer); void gkick_buffer_push_back(struct gkick_buffer *buffer, gkick_real val); bool gkick_buffer_is_end(struct gkick_buffer *buffer); #endif // GKICK_BUFFER_H geonkick-2.10.2/src/dsp/src/gkick_jack.c000066400000000000000000000261251451017372400177700ustar00rootroot00000000000000/** * File name: gkick_kack.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "gkick_jack.h" #include "oscillator.h" int gkick_jack_process_callback(jack_nframes_t nframes, void *arg) { if (nframes < 1) return 0; struct gkick_jack *jack = (struct gkick_jack*)arg; jack_default_audio_sample_t *buffer[2 * GEONKICK_MAX_CHANNELS]; gkick_jack_get_output_buffers(jack, buffer, nframes); void* port_buf = jack_port_get_buffer(jack->midi_in_port, nframes); jack_midi_event_t event; jack_nframes_t events_count = jack_midi_get_event_count(port_buf); jack_nframes_t event_index = 0; if (events_count > 0) jack_midi_event_get(&event, port_buf, event_index); for (size_t i = 0; i < 2 * GEONKICK_MAX_CHANNELS; i++) memset(buffer[i], 0, nframes * sizeof(float)); size_t currentFrame = 0; size_t offset = 0; while (event_index < events_count) { size_t eventFrame = event.time; size_t size = eventFrame - currentFrame; if (size > 0) { gkick_mixer_process(jack->mixer, buffer, offset, size); offset += size; } struct gkick_note_info note; memset(¬e, 0, sizeof(struct gkick_note_info)); gkick_jack_get_note_info(&event, ¬e); if (note.state == GKICK_KEY_STATE_PRESSED || note.state == GKICK_KEY_STATE_RELEASED) { gkick_mixer_key_pressed(jack->mixer, ¬e); } currentFrame = eventFrame; if (++event_index < events_count) jack_midi_event_get(&event, port_buf, event_index); } if (currentFrame < nframes) gkick_mixer_process(jack->mixer, buffer, offset, nframes - currentFrame); return 0; } jack_nframes_t gkick_jack_sample_rate(struct gkick_jack *jack) { jack_nframes_t sample_rate; if (jack == NULL) { gkick_log_error("wrong arguments"); return 0; } gkick_jack_lock(jack); sample_rate = jack->sample_rate; gkick_jack_unlock(jack); return sample_rate; } enum geonkick_error gkick_jack_get_output_buffers(struct gkick_jack *jack, jack_default_audio_sample_t **channel_buf, jack_nframes_t nframes) { for (size_t i = 0; i < GEONKICK_MAX_CHANNELS; i++) { channel_buf[2 * i] = (jack_default_audio_sample_t*)jack_port_get_buffer(jack->output_port[2 * i], nframes); channel_buf[2 * i + 1] = (jack_default_audio_sample_t*)jack_port_get_buffer(jack->output_port[2 * i + 1], nframes); } return GEONKICK_OK; } void gkick_jack_get_note_info(jack_midi_event_t *event, struct gkick_note_info *note) { if (event == NULL || note == NULL) return; note->state = GKICK_KEY_STATE_DEFAULT; if (((*(event->buffer) & 0xf0)) == 0x90) { note->state = GKICK_KEY_STATE_PRESSED; note->channel = *(event->buffer) & 0x0f; note->note_number = event->buffer[1]; note->velocity = event->buffer[2]; } else if(((*(event->buffer) & 0xf0)) == 0x80) { note->state = GKICK_KEY_STATE_RELEASED; note->channel = *(event->buffer) & 0x0f; note->note_number = event->buffer[1]; note->velocity = event->buffer[2]; } } jack_port_t* gkick_jack_get_midi_in_port(struct gkick_jack *jack) { jack_port_t *port; if (jack == NULL) { gkick_log_error("wrong arguments"); return NULL; } gkick_jack_lock(jack); port = jack->midi_in_port; gkick_jack_unlock(jack); return port; } int gkick_jack_srate_callback(jack_nframes_t nframes, void *arg) { return 0; } enum geonkick_error gkick_jack_enable_midi_in(struct gkick_jack *jack, const char *name) { const char *midi_name; enum geonkick_error error; if (jack == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } if (name == NULL) midi_name = "midi_in"; else midi_name = name; error = GEONKICK_OK; gkick_jack_lock(jack); if (jack->midi_in_port == NULL) { jack->midi_in_port = jack_port_register(jack->client, midi_name, JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); if (jack->midi_in_port == NULL) { gkick_log_error("can't register port: %s", midi_name); error = GEONKICK_ERROR; } } else { gkick_log_warning("midi in is already set"); } gkick_jack_unlock(jack); return error; } enum geonkick_error gkick_jack_create_output_ports(struct gkick_jack *jack) { enum geonkick_error error; if (jack == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } error = GEONKICK_OK; gkick_jack_lock(jack); for (size_t i = 0; i < GEONKICK_MAX_CHANNELS; i++) { char name[30]; sprintf(name, "outL_%u", (unsigned int)(i + 1)); jack->output_port[2 * i] = jack_port_register(jack->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); if (jack->output_port[2 * i] == NULL) { gkick_log_error("can't register output ports"); error = GEONKICK_ERROR; break; } sprintf(name, "outR_%u", (unsigned int)(i + 1)); jack->output_port[2 * i + 1] = jack_port_register(jack->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); if (jack->output_port[2 * i + 1] == NULL) { gkick_log_error("can't register output ports"); error = GEONKICK_ERROR; break; } } gkick_jack_unlock(jack); return error; } enum geonkick_error gkick_create_jack(struct gkick_jack **jack) { if (jack == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } *jack = (struct gkick_jack*)calloc(1, sizeof(struct gkick_jack)); if (*jack == NULL) return GEONKICK_ERROR; if (pthread_mutex_init(&(*jack)->lock, NULL) != 0) { gkick_log_error("error on init mutex"); gkick_jack_free(jack); return GEONKICK_ERROR; } (*jack)->client = jack_client_open(GEONKICK_NAME, JackNoStartServer, NULL); if ((*jack)->client == NULL) { gkick_log_error("can't create jack client"); gkick_jack_free(jack); return GEONKICK_ERROR; } (*jack)->sample_rate = jack_get_sample_rate((*jack)->client); jack_set_process_callback((*jack)->client, gkick_jack_process_callback, (void*)(*jack)); if (gkick_jack_create_output_ports(*jack) != GEONKICK_OK) { gkick_log_error("can't create output ports"); gkick_jack_free(jack); return GEONKICK_ERROR; } gkick_jack_enable_midi_in(*jack, "midi_in"); return GEONKICK_OK; } enum geonkick_error geonkick_jack_start(struct gkick_jack *jack, struct gkick_mixer *mixer) { jack->mixer = mixer; if (jack_activate(jack->client) != 0) { gkick_log_error("cannot activate client"); return GEONKICK_ERROR; } // Connect to system playback ports. for (size_t i = 0; i < GEONKICK_MAX_CHANNELS; i++) { jack_connect(jack->client, jack_port_name(jack->output_port[2 * i]), "system:playback_1"); jack_connect(jack->client, jack_port_name(jack->output_port[2 * i + 1]), "system:playback_2"); } return GEONKICK_OK; } int gkick_jack_is_midi_in_enabled(struct gkick_jack *jack) { int enabled; if (jack == NULL) { gkick_log_error("wrong argument"); return 0; } gkick_jack_lock(jack); enabled = (jack->midi_in_port != NULL) ? 1 : 0; gkick_jack_unlock(jack); return enabled; } void gkick_jack_free(struct gkick_jack **jack) { if (jack != NULL && *jack != NULL) { if ((*jack)->client != NULL) { jack_deactivate((*jack)->client); for (size_t i = 0; i < 2 * GEONKICK_MAX_CHANNELS; i++) { if ((*jack)->output_port[i] != NULL) { jack_port_unregister((*jack)->client, (*jack)->output_port[i]); } } jack_client_close((*jack)->client); } free(*jack); *jack = NULL; } } void gkick_jack_lock(struct gkick_jack *jack) { if (jack != NULL) pthread_mutex_lock(&jack->lock); } void gkick_jack_unlock(struct gkick_jack *jack) { if (jack != NULL) pthread_mutex_unlock(&jack->lock); } geonkick-2.10.2/src/dsp/src/gkick_jack.h000066400000000000000000000046521451017372400177760ustar00rootroot00000000000000/** * File name: gkick_jack.c * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GKICK_JACK_H #define GKICK_JACK_H #include "geonkick_internal.h" #include "mixer.h" #include #include struct gkick_jack { jack_port_t *output_port[2 * GEONKICK_MAX_CHANNELS]; jack_port_t *midi_in_port; jack_client_t *client; jack_nframes_t sample_rate; struct gkick_mixer *mixer; pthread_mutex_t lock; }; int gkick_jack_process_callback(jack_nframes_t nframes, void *arg); jack_nframes_t gkick_jack_sample_rate(struct gkick_jack *jack); enum geonkick_error gkick_jack_get_output_buffers(struct gkick_jack *jack, jack_default_audio_sample_t **channel_buf, jack_nframes_t nframes); void gkick_jack_get_note_info(jack_midi_event_t *event, struct gkick_note_info *note); jack_port_t* gkick_jack_get_midi_in_port(struct gkick_jack *jack); int gkick_jack_srate_callback(jack_nframes_t nframes, void *arg); enum geonkick_error gkick_jack_enable_midi_in(struct gkick_jack *jack, const char *name); enum geonkick_error gkick_jack_create_output_ports(struct gkick_jack *jack); enum geonkick_error gkick_create_jack(struct gkick_jack **jack); enum geonkick_error geonkick_jack_start(struct gkick_jack *jack, struct gkick_mixer *mixer); int gkick_jack_is_midi_in_enabled(struct gkick_jack *jack); void gkick_jack_free(struct gkick_jack **jack); void gkick_jack_lock(struct gkick_jack *jack); void gkick_jack_unlock(struct gkick_jack *jack); #endif // GKICK_JACK_H geonkick-2.10.2/src/dsp/src/gkick_log.c000066400000000000000000000026761451017372400176460ustar00rootroot00000000000000/** * File name: gkick_log.c * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "gkick_log.h" void gkick_log_msg(const char *message, ...) { va_list args; va_start (args, message); vprintf (message, args); va_end (args); printf("\n"); } void gkick_msg_info(const char *msg, ...) { va_list args; printf("Info: "); va_start (args, msg); vprintf (msg, args); va_end (args); printf("\n"); } void gkick_msg_error(const char *msg, ...) { va_list args; printf("Error: "); va_start (args, msg); vprintf (msg, args); va_end (args); printf("\n"); } geonkick-2.10.2/src/dsp/src/gkick_log.h000066400000000000000000000063601451017372400176450ustar00rootroot00000000000000/** * File name: gkick_log.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GKICK_LOG_H #define GKICK_LOG_H #include #include void gkick_log_msg(const char *message, ...); #ifdef GKICK_LIB_LOG_LEVEL_TRACE #define GKICK_LIB_LOG_LEVEL_DEBUG #ifdef GKICK_LIB_LOG_FUNCTION #define gkick_log_trace(message, ...) \ gkick_log_msg("[TRACE][%s] "message, __func__, ##__VA_ARGS__) #else #define gkick_log_trace(message, ...) gkick_log_msg("[TRACE] " message, ##__VA_ARGS__) #endif #else #define gkick_log_trace(message, ...) #endif /* GKICK_LIB_LOG_LEVEL_TRACE */ #ifdef GKICK_LIB_LOG_LEVEL_DEBUG #define GKICK_LIB_LOG_LEVEL_INFO #ifdef GKICK_LIB_LOG_FUNCTION #define gkick_log_debug(message, ...) \ gkick_log_msg("[DEBUG][%s] "message, __func__, ##__VA_ARGS__) #else #define gkick_log_debug(message, ...) gkick_log_msg("[DEBUG] " message, ##__VA_ARGS__) #endif #else #define gkick_log_debug(message, ...) #endif /* GKICK_LIB_LOG_LEVEL_DEBUG */ #ifdef GKICK_LIB_LOG_LEVEL_INFO #define GKICK_LIB_LOG_LEVEL_WARNING #ifdef GKICK_LIB_LOG_FUNCTION #define gkick_log_info(message, ...) \ gkick_log_msg("[INFO][%s] "message, __func__, ##__VA_ARGS__) #else #define gkick_log_info(message, ...) gkick_log_msg("[INFO] " message, ##__VA_ARGS__) #endif #else #define gkick_log_info(message, ...) #endif /* GKICK_LIB_LOG_LEVEL_INFO */ #ifdef GKICK_LIB_LOG_LEVEL_WARNING #define GKICK_LIB_LOG_LEVEL_ERROR #ifdef GKICK_LIB_LOG_FUNCTION #define gkick_log_warning(message, ...) \ gkick_log_msg("[WARNING][%s] "message, __func__, ##__VA_ARGS__) #else #define gkick_log_warning(message, ...) gkick_log_msg("[WARNING] " message, ##__VA_ARGS__) #endif #else #define gkick_log_warning(message, ...) #endif /* GKICK_LIB_LOG_LEVEL_WARNING */ #ifdef GKICK_LIB_LOG_LEVEL_ERROR #define GKICK_LIB_LOG_LEVEL_CRITICAL #ifdef GKICK_LIB_LOG_FUNCTION #define gkick_log_error(message, ...) \ gkick_log_msg("[ERROR][%s] "message, __func__, ##__VA_ARGS__) #else #define gkick_log_error(message, ...) gkick_log_msg("[ERROR] " message, ##__VA_ARGS__) #endif #else #define gkick_log_error(message, ...) #endif /* GKICK_LIB_LOG_LEVEL_ERROR */ #ifdef GKICK_LIB_LOG_LEVEL_CRITICAL #ifdef GKICK_LIB_LOG_FUNCTION #define gkick_log_critical(message, ...) \ gkick_log_msg("[CRITICAL] " message, __func__, ##__VA_ARGS__) #else #define gkick_log_critical(message, ...) \ gkick_log_msg("[CRITICAL] " message, ##__VA_ARGS__) #endif #else #define gkick_log_critical(message, ...) #endif /* GKICK_LIB_LOG_LEVEL_CRITICAL */ #endif geonkick-2.10.2/src/dsp/src/mixer.c000066400000000000000000000156631451017372400170410ustar00rootroot00000000000000/** * File name: mixer.c * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "mixer.h" enum geonkick_error gkick_mixer_create(struct gkick_mixer **mixer) { *mixer = (struct gkick_mixer*)calloc(1, sizeof(struct gkick_mixer)); (*mixer)->solo = false; if (*mixer == NULL) { gkick_log_error("can't allocate memory"); return GEONKICK_ERROR_MEM_ALLOC; } return GEONKICK_OK; } enum geonkick_error gkick_mixer_key_pressed(struct gkick_mixer *mixer, struct gkick_note_info *note) { if (note->note_number < 0 || note->note_number > 127) return GEONKICK_ERROR; for (size_t i = 0; i < GEONKICK_MAX_PERCUSSIONS; i++) { struct gkick_audio_output *output = mixer->audio_outputs[i]; if (output->enabled && (output->playing_key == GEONKICK_ANY_KEY || output->playing_key == note->note_number || output->tune)) { gkick_audio_output_key_pressed(output, note); } } return GEONKICK_OK; } enum geonkick_error gkick_mixer_tune_output(struct gkick_mixer *mixer, size_t index, bool tune) { if (index < GEONKICK_MAX_PERCUSSIONS) gkick_audio_output_tune_output(mixer->audio_outputs[index], tune); return GEONKICK_OK; } enum geonkick_error gkick_mixer_is_output_tuned(struct gkick_mixer *mixer, size_t index, bool *tune) { if (index < GEONKICK_MAX_PERCUSSIONS) *tune = gkick_audio_output_is_tune_output(mixer->audio_outputs[index]); return GEONKICK_OK; } enum geonkick_error gkick_mixer_get_frame(struct gkick_mixer *mixer, int channel, gkick_real *val) { *val = 0.0f; for (size_t i = 0; i < GEONKICK_MAX_PERCUSSIONS; i++) { struct gkick_audio_output *out = mixer->audio_outputs[i]; if (out->enabled && !out->muted && mixer->solo == out->solo && out->channel == channel) { gkick_real v = 0.0f; gkick_audio_output_get_frame(out, &v); if (i == mixer->limiter_callback_index) gkick_mixer_set_leveler(mixer, i, v); *val += v; } } return GEONKICK_OK; } enum geonkick_error gkick_mixer_process(struct gkick_mixer *mixer, float** out, size_t offset, size_t size) { if (size < 1) return GEONKICK_OK; for (size_t channel = 0; channel < GEONKICK_MAX_CHANNELS; channel++) { size_t left_index = 2 * channel; size_t right_index = left_index + 1; for (size_t per = 0; per < GEONKICK_MAX_PERCUSSIONS; per++) { struct gkick_audio_output *output = mixer->audio_outputs[per]; if (output->enabled && !output->muted && mixer->solo == output->solo && output->channel == channel) { for (size_t i = 0; i < size; i++) { gkick_real v = 0.0f; gkick_audio_output_get_frame(output, &v); out[left_index][i + offset] += v; out[right_index][i + offset] += v; if (i == 0) gkick_mixer_set_leveler(mixer, per, v); } } } } struct gkick_audio_output *output = mixer->audio_outputs[GEONKICK_MAX_PERCUSSIONS]; for (size_t i = 0; i < size; i++) { gkick_real v = 0.0f; gkick_audio_output_get_frame(output, &v); out[0][i + offset] += v; out[1][i + offset] += v; } return GEONKICK_OK; } void gkick_mixer_set_leveler(struct gkick_mixer *mixer, size_t index, gkick_real val) { if (mixer->limiter_callback != NULL && mixer->limiter_callback_arg != NULL) mixer->limiter_callback(mixer->limiter_callback_arg, index, val); } void gkick_mixer_free(struct gkick_mixer **mixer) { if (mixer != NULL && *mixer != NULL) { free(*mixer); *mixer = NULL; } } enum geonkick_error gkick_mixer_limiter_set(struct gkick_mixer *mixer, size_t index, gkick_real val) { if (index <= GEONKICK_MAX_PERCUSSIONS) mixer->audio_outputs[index]->limiter = 1000000 * val; return GEONKICK_OK; } enum geonkick_error gkick_mixer_limiter_get(struct gkick_mixer *mixer, size_t index, gkick_real *val) { *val = 0.0f; if (index <= GEONKICK_MAX_PERCUSSIONS) *val = (gkick_real)mixer->audio_outputs[index]->limiter / 1000000; return GEONKICK_OK; } enum geonkick_error gkick_mixer_mute(struct gkick_mixer *mixer, size_t id, bool b) { mixer->audio_outputs[id]->muted = b; return GEONKICK_OK; } enum geonkick_error gkick_mixer_is_muted(struct gkick_mixer *mixer, size_t id, bool *b) { *b = mixer->audio_outputs[id]->muted; return GEONKICK_OK; } enum geonkick_error gkick_mixer_solo(struct gkick_mixer *mixer, size_t id, bool b) { mixer->audio_outputs[id]->solo = b; bool is_solo = false; for (size_t i = 0; i < GEONKICK_MAX_PERCUSSIONS; i++) { if (mixer->audio_outputs[i]->enabled && mixer->audio_outputs[i]->solo) is_solo = true; } mixer->solo = is_solo; return GEONKICK_OK; } enum geonkick_error gkick_mixer_is_solo(struct gkick_mixer *mixer, size_t id, bool *b) { *b = mixer->audio_outputs[id]->solo; return GEONKICK_OK; } enum geonkick_error gkick_mixer_set_limiter_callback(struct gkick_mixer *mixer, void (*callback)(void*, size_t index, gkick_real val), void *arg) { mixer->limiter_callback = callback; mixer->limiter_callback_arg = arg; return GEONKICK_OK; } geonkick-2.10.2/src/dsp/src/mixer.h000066400000000000000000000057201451017372400170370ustar00rootroot00000000000000/** * File name: mixer.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GKICK_MIXER_H #define GKICK_MIXER_H #include "audio_output.h" struct gkick_mixer { struct gkick_audio_output **audio_outputs; size_t connection_matrix[127]; _Atomic int solo; _Atomic int limiter; void (*limiter_callback) (void*, size_t index, gkick_real val); void *limiter_callback_arg; _Atomic size_t limiter_callback_index; }; enum geonkick_error gkick_mixer_create(struct gkick_mixer **mixer); enum geonkick_error gkick_mixer_key_pressed(struct gkick_mixer *mixer, struct gkick_note_info *note); enum geonkick_error gkick_mixer_tune_output(struct gkick_mixer *mixer, size_t index, bool tune); enum geonkick_error gkick_mixer_is_output_tuned(struct gkick_mixer *mixer, size_t index, bool *tune); enum geonkick_error gkick_mixer_get_frame(struct gkick_mixer *mixer, int channel, gkick_real *val); enum geonkick_error gkick_mixer_process(struct gkick_mixer *mixer, float** out, size_t offset, size_t size); void gkick_mixer_set_leveler(struct gkick_mixer *mixer, size_t index, gkick_real val); void gkick_mixer_free(struct gkick_mixer **mixer); enum geonkick_error gkick_mixer_limiter_set(struct gkick_mixer *mixer, size_t index, gkick_real val); enum geonkick_error gkick_mixer_limiter_get(struct gkick_mixer *mixer, size_t index, gkick_real *val); enum geonkick_error gkick_mixer_mute(struct gkick_mixer *mixer, size_t id, bool b); enum geonkick_error gkick_mixer_is_muted(struct gkick_mixer *mixer, size_t id, bool *b); enum geonkick_error gkick_mixer_solo(struct gkick_mixer *mixer, size_t id, bool b); enum geonkick_error gkick_mixer_is_solo(struct gkick_mixer *mixer, size_t id, bool *b); enum geonkick_error gkick_mixer_set_limiter_callback(struct gkick_mixer *mixer, void (*callback)(void*, size_t index, gkick_real val), void *arg); #endif // GKICK_MIXER_H geonkick-2.10.2/src/dsp/src/oscillator.c000066400000000000000000000225161451017372400200630ustar00rootroot00000000000000/** * File name: oscillator.c * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "oscillator.h" #include #define GKICK_2PI 6.283185307 struct gkick_oscillator *gkick_osc_create(int sample_rate) { struct gkick_oscillator *osc; osc = (struct gkick_oscillator*)calloc(1, sizeof(struct gkick_oscillator)); if (osc == NULL) return NULL; osc->state = GEONKICK_OSC_STATE_ENABLED; osc->func = GEONKICK_OSC_FUNC_SINE; osc->initial_phase = 0.0f; osc->phase = osc->initial_phase; osc->sample_rate = sample_rate; osc->amplitude = GKICK_OSC_DEFAULT_AMPLITUDE; osc->frequency = GKICK_OSC_DEFAULT_FREQUENCY; osc->pitch_shift = 0.0f; osc->env_number = 3; osc->brownian = 0; osc->is_fm = false; osc->fm_input = 0.0f; osc->seed = 100; osc->seedp = osc->seed; if (gkick_osc_create_envelopes(osc) != GEONKICK_OK) { gkick_osc_free(&osc); return NULL; } if (gkick_filter_new(&osc->filter, osc->sample_rate) != GEONKICK_OK) { gkick_log_error("can't create filter"); gkick_osc_free(&osc); return NULL; } osc->filter_enabled = 0; return osc; } void gkick_osc_free(struct gkick_oscillator **osc) { if (osc == NULL || *osc == NULL) return; if ((*osc)->envelopes != NULL) { size_t i; for (i = 0; i < (*osc)->env_number; i++) gkick_envelope_destroy((*osc)->envelopes[i]); free((*osc)->envelopes); gkick_filter_free(&(*osc)->filter); gkick_buffer_free(&(*osc)->sample); } free(*osc); *osc = NULL; } void gkick_osc_set_state(struct gkick_oscillator *osc, enum geonkick_osc_state state) { osc->state = state; } enum geonkick_error gkick_osc_create_envelopes(struct gkick_oscillator *osc) { if (osc->env_number < 1) return GEONKICK_ERROR; osc->envelopes = (struct gkick_envelope**)calloc(1, sizeof(struct gkick_envelope*) *osc->env_number); if (osc->envelopes == NULL) return GEONKICK_ERROR_MEM_ALLOC; for (size_t i = 0; i < osc->env_number; i++) { struct gkick_envelope *env = gkick_envelope_create(); if (env == NULL) { return GEONKICK_ERROR; } else { /* Add two default points. */ if (i == GKICK_OSC_PITCH_SHIFT_ENVELOPE) { gkick_envelope_add_point(env, 0.0f, 0.5f); gkick_envelope_add_point(env, 1.0f, 0.5f); } else { gkick_envelope_add_point(env, 0.0f, 1.0f); gkick_envelope_add_point(env, 1.0f, 1.0f); } osc->envelopes[i] = env; } } return GEONKICK_OK; } struct gkick_envelope* gkick_osc_get_envelope(struct gkick_oscillator *osc, size_t env_index) { if (osc == NULL) return NULL; switch (env_index) { case GEONKICK_FILTER_CUTOFF_ENVELOPE: return osc->filter->cutoff_env; case GEONKICK_AMPLITUDE_ENVELOPE: return osc->envelopes[GKICK_OSC_AMPLITUDE_ENVELOPE]; case GEONKICK_FREQUENCY_ENVELOPE: return osc->envelopes[GKICK_OSC_FREQUENCY_ENVELOPE]; case GEONKICK_PITCH_SHIFT_ENVELOPE: return osc->envelopes[GKICK_OSC_PITCH_SHIFT_ENVELOPE]; default: return NULL; } } void gkick_osc_increment_phase(struct gkick_oscillator *osc, gkick_real t, gkick_real kick_len) { gkick_real f; f = osc->frequency * gkick_envelope_get_value(osc->envelopes[GKICK_OSC_FREQUENCY_ENVELOPE], t / kick_len); f += f * osc->fm_input; osc->phase += (GKICK_2PI * f) / (osc->sample_rate); if (osc->phase > GKICK_2PI) osc->phase -= GKICK_2PI; } gkick_real gkick_osc_value(struct gkick_oscillator *osc, gkick_real t, gkick_real kick_len) { gkick_real amp; gkick_real v; gkick_real env_x; // Calculate the x coordinate between 0 and 1.0 for the envelope. env_x = t / kick_len; amp = osc->amplitude * gkick_envelope_get_value(osc->envelopes[GKICK_OSC_AMPLITUDE_ENVELOPE], env_x); v = 0.0f; switch (osc->func) { case GEONKICK_OSC_FUNC_SINE: v = amp * gkick_osc_func_sine(osc->phase); break; case GEONKICK_OSC_FUNC_SQUARE: v = amp * gkick_osc_func_square(osc->phase); break; case GEONKICK_OSC_FUNC_TRIANGLE: v = amp * gkick_osc_func_triangle(osc->phase); break; case GEONKICK_OSC_FUNC_SAWTOOTH: v = amp * gkick_osc_func_sawtooth(osc->phase); break; case GEONKICK_OSC_FUNC_NOISE_WHITE: v = amp * gkick_osc_func_noise_white(&osc->seedp); break; case GEONKICK_OSC_FUNC_NOISE_PINK: v = amp * gkick_osc_func_noise_pink(); break; case GEONKICK_OSC_FUNC_NOISE_BROWNIAN: v = amp * gkick_osc_func_noise_brownian(&(osc)->brownian, &osc->seedp); break; case GEONKICK_OSC_FUNC_SAMPLE: if (osc->sample != NULL && (t > (0.25f * osc->initial_phase / M_PI) * kick_len)) { struct gkick_envelope *env = osc->envelopes[GKICK_OSC_PITCH_SHIFT_ENVELOPE]; float env_val = gkick_envelope_get_value(env, env_x); float pitch_shift = 2.0f * (env_val - 0.5f) * osc->pitch_shift; v = amp * gkick_osc_func_sample(osc->sample, pitch_shift); } break; default: v = amp * gkick_osc_func_sine(osc->phase); }; if (osc->filter_enabled) gkick_filter_val(osc->filter, v, &v, env_x); return v; } gkick_real gkick_osc_func_sine(gkick_real phase) { return sin(phase); } gkick_real gkick_osc_func_square(gkick_real phase) { if (phase < M_PI) return -1.0f; else return 1.0f; } gkick_real gkick_osc_func_triangle(gkick_real phase) { if (phase < M_PI) return -1.0f + M_2_PI * phase; else return 3.0f - M_2_PI * phase; } gkick_real gkick_osc_func_sawtooth(gkick_real phase) { if (phase < M_PI) return M_1_PI * phase; else return M_1_PI * phase - 2.0f; } gkick_real gkick_osc_func_noise_white(unsigned int *seed) { return 2.0f * ((gkick_real)(rand_r(seed) % RAND_MAX)) / (gkick_real)RAND_MAX - 1.0f; } gkick_real gkick_osc_func_noise_pink(void) { return 0.0f; } gkick_real gkick_osc_func_noise_brownian(gkick_real *previous, unsigned int *seed) { gkick_real sign = 1.0f; gkick_real walk; if (rand_r(seed) % 2) sign = -1.0f; walk = sign * 0.1f * (((gkick_real)(rand_r(seed) % RAND_MAX)) / (gkick_real)RAND_MAX); if (*previous + walk > 1.0f || *previous + walk < -1.0f) *previous -= walk; else *previous += walk; return *previous; } gkick_real gkick_osc_func_sample(struct gkick_buffer *sample, float semitones) { float factor = exp2f(semitones / 12.0f); return gkick_buffer_stretch_get_next(sample, factor); } void gkick_osc_get_envelope_points(struct gkick_oscillator *osc, size_t env_index, gkick_real **buff, size_t *npoints) { if (buff != NULL) *buff = NULL; struct gkick_envelope *env = gkick_osc_get_envelope(osc, env_index); if (env != NULL) gkick_envelope_get_points(env, buff, npoints); } void gkick_osc_set_envelope_points(struct gkick_oscillator *osc, size_t env_index, const gkick_real *buff, size_t npoints) { if (buff == NULL) return; struct gkick_envelope *env = gkick_osc_get_envelope(osc, env_index); if (env != NULL) gkick_envelope_set_points(env, buff, npoints); } int gkick_osc_enabled(struct gkick_oscillator *osc) { return osc != NULL && (osc->state == GEONKICK_OSC_STATE_ENABLED); } geonkick-2.10.2/src/dsp/src/oscillator.h000066400000000000000000000075371451017372400200760ustar00rootroot00000000000000/** * File name: oscillator.c * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GKICK_OSCILLATOR_H #define GKICK_OSCILLATOR_H #include "geonkick_internal.h" #include "envelope.h" #include "filter.h" #define GKICK_OSC_DEFAULT_AMPLITUDE 1.0f #define GKICK_OSC_DEFAULT_FREQUENCY 150.0f #define GKICK_OSC_DEFAULT_PITCH 69.0f enum geonkick_osc_state { GEONKICK_OSC_STATE_DISABLED = 0, GEONKICK_OSC_STATE_ENABLED = 1 }; enum gkick_osc_envelope_type { GKICK_OSC_AMPLITUDE_ENVELOPE = 0, GKICK_OSC_FREQUENCY_ENVELOPE = 1, GKICK_OSC_PITCH_SHIFT_ENVELOPE = 2 }; struct gkick_oscillator { int sample_rate; enum geonkick_osc_state state; enum geonkick_osc_func_type func; /* Used for Brownian noise */ gkick_real brownian; /* User as a seed for pseudo random generator. */ unsigned int seedp; unsigned int seed; gkick_real initial_phase; gkick_real phase; gkick_real frequency; gkick_real amplitude; /* Pitch shift semitones. */ gkick_real pitch_shift; struct gkick_buffer *sample; /* FM input value for this OSC. */ gkick_real fm_input; /* Specifies if this OSC is a FM source to other oscillator. */ bool is_fm; size_t env_number; struct gkick_envelope **envelopes; struct gkick_filter *filter; int filter_enabled; pthread_mutex_t lock; }; struct gkick_oscillator *gkick_osc_create(int sample_rate); void gkick_osc_free(struct gkick_oscillator **osc); void gkick_osc_set_state(struct gkick_oscillator *osc, enum geonkick_osc_state state); int gkick_osc_enabled(struct gkick_oscillator *osc); enum geonkick_error gkick_osc_create_envelopes(struct gkick_oscillator *osc); struct gkick_envelope* gkick_osc_get_envelope(struct gkick_oscillator *osc, size_t env_index); void gkick_osc_increment_phase(struct gkick_oscillator *osc, gkick_real t, gkick_real kick_len); gkick_real gkick_osc_value(struct gkick_oscillator *osc, gkick_real t, gkick_real kick_len); gkick_real gkick_osc_func_sine(gkick_real phase); gkick_real gkick_osc_func_square(gkick_real phase); gkick_real gkick_osc_func_triangle(gkick_real phase); gkick_real gkick_osc_func_sawtooth(gkick_real phase); gkick_real gkick_osc_func_noise_white(unsigned int *seed); gkick_real gkick_osc_func_noise_pink(void); gkick_real gkick_osc_func_noise_brownian(gkick_real *previous, unsigned int *seed); gkick_real gkick_osc_func_sample(struct gkick_buffer *sample, float pitch_shift); void gkick_osc_get_envelope_points(struct gkick_oscillator *osc, size_t env_index, gkick_real **buff, size_t *npoints); void gkick_osc_set_envelope_points(struct gkick_oscillator *osc, size_t env_index, const gkick_real *buff, size_t npoints); #endif // GKICK_OSCILLATOR_H geonkick-2.10.2/src/dsp/src/presets_folder.h000066400000000000000000000051671451017372400207400ustar00rootroot00000000000000/** * File name: preset_folder.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "globals.h" clss Preset { public: class enum PresetType : int { PercussionPreset, KitPreset }; explicit Preset(const std::filesystem::path path); virtual ~Preset() = default; PresetType type() const; std::string name() const; std::string license() const; std::string author() const; std::string authorUrl() const; std::filesystem::path path() const; void load(); private: PresetType presetType; std::string presetName; std::filesystem::path filePath; std::string presetLicense; std::string presetAuthor; std::string presetAuthorUrl; }; class PresetFolder { public: explicit PresetsFolder(std::filesystem::path &dir); virtual ~PresetsFolder() = default; bool load(std::filesystem::path &dir); std::string name() const; const std::vector& presets() const; void addPreset(const Preset &preset); private: std::vector presetList; }; void GeonkickApi::loadPresetsFromDir(const std::filesystem::path &dir) { if (presetFolder.) try { for (const auto &file : std::filesystem::directory_iterator(dir)) { if (!file.path().empty() && std::filesystem::s::is_regular_file(file.path()) && (file.path().extention() == ".gkick" || file.path().extention() == ".gkit" || file.path().extention() == ".GKICK" || file.path().extention() == ".GKIT")) { loadPresetsFromDir(entry.path()); } } } catch(...) { GEONKICK_LOG_ERROR("error on reading path: " << path); } } geonkick-2.10.2/src/dsp/src/synthesizer.c000066400000000000000000001714401451017372400203000ustar00rootroot00000000000000/** * File name: synthesizer.c * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of GeonKick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "synthesizer.h" #include "oscillator.h" enum geonkick_error gkick_synth_new(struct gkick_synth **synth, int sample_rate) { if (synth == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } *synth = (struct gkick_synth*)calloc(1, sizeof(struct gkick_synth)); if (*synth == NULL) { gkick_log_error("can't allocate memory"); return GEONKICK_ERROR_MEM_ALLOC; } memset(*synth, 0, sizeof(struct gkick_synth)); (*synth)->sample_rate = sample_rate; (*synth)->length = 0.3f; (*synth)->oscillators_number = GKICK_OSC_GROUPS_NUMBER * GKICK_OSC_GROUP_SIZE; (*synth)->buffer_update = 0; (*synth)->amplitude = 1.0f; (*synth)->buffer_size = (size_t)((*synth)->length * (*synth)->sample_rate); (*synth)->buffer_update = false; (*synth)->is_active = false; memset((*synth)->name, '\0', sizeof((*synth)->name)); for (size_t i = 0; i < GKICK_OSC_GROUPS_NUMBER; i++) (*synth)->osc_groups_amplitude[i] = 1.0f; if (gkick_filter_new(&(*synth)->filter, (*synth)->sample_rate) != GEONKICK_OK) { gkick_log_error("can't create filter"); gkick_synth_free(synth); return GEONKICK_ERROR; } (*synth)->filter_enabled = 0; if (gkick_compressor_new(&(*synth)->compressor, (*synth)->sample_rate) != GEONKICK_OK) { gkick_log_error("can't create compressor"); gkick_synth_free(synth); return GEONKICK_ERROR; } if (gkick_distortion_new(&(*synth)->distortion, (*synth)->sample_rate) != GEONKICK_OK) { gkick_log_error("can't create distortion"); gkick_synth_free(synth); return GEONKICK_ERROR; } if (gkick_filter_new(&(*synth)->filter, (*synth)->sample_rate) != GEONKICK_OK) { gkick_log_error("can't create filter"); gkick_synth_free(synth); return GEONKICK_ERROR; } (*synth)->envelope = gkick_envelope_create(); if ((*synth)->envelope == NULL) { gkick_log_error("can't create envelope"); gkick_synth_free(synth); return GEONKICK_ERROR; } else { /* Add two default points. */ gkick_envelope_add_point((*synth)->envelope, 0.0f, 1.0f); gkick_envelope_add_point((*synth)->envelope, 1.0f, 1.0f); } /* Create synthesizer kick buffer. */ struct gkick_buffer *buff; gkick_buffer_new(&buff, (*synth)->sample_rate * GEONKICK_MAX_LENGTH); if (buff == NULL) { gkick_log_error("can't create synthesizer kick buffer"); gkick_synth_free(synth); } gkick_buffer_set_size(buff, (*synth)->buffer_size); (*synth)->buffer = (char*)buff; if (gkick_synth_create_oscillators(*synth) != GEONKICK_OK) { gkick_log_error("can't create oscillators"); gkick_synth_free(synth); return GEONKICK_ERROR; } return GEONKICK_OK; } void gkick_synth_free(struct gkick_synth **synth) { if (synth != NULL && *synth != NULL) { if ((*synth)->oscillators != NULL) { for (size_t i = 0; i < (*synth)->oscillators_number; i++) gkick_osc_free(&((*synth)->oscillators[i])); free((*synth)->oscillators); (*synth)->oscillators = NULL; if ((*synth)->buffer != NULL) { free((*synth)->buffer); (*synth)->buffer = NULL; } if ((*synth)->filter) gkick_filter_free(&(*synth)->filter); if ((*synth)->compressor) gkick_compressor_free(&(*synth)->compressor); if ((*synth)->distortion) gkick_distortion_free(&(*synth)->distortion); if ((*synth)->envelope) { gkick_envelope_destroy((*synth)->envelope); (*synth)->envelope = NULL; } } pthread_mutex_destroy(&(*synth)->lock); free(*synth); *synth = NULL; } } void gkick_synth_lock(struct gkick_synth *synth) { pthread_mutex_lock(&synth->lock); } void gkick_synth_unlock(struct gkick_synth *synth) { pthread_mutex_unlock(&synth->lock); } enum geonkick_error gkick_synth_create_oscillators(struct gkick_synth *synth) { if (synth == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } size_t size; size_t i; struct gkick_oscillator *osc; size = sizeof(struct gkick_oscillator*) * synth->oscillators_number; synth->oscillators = (struct gkick_oscillator**)malloc(size); if (synth->oscillators == NULL) return GEONKICK_ERROR_MEM_ALLOC; memset(synth->oscillators, 0, size); for (i = 0; i < synth->oscillators_number; i++) { osc = gkick_osc_create(synth->sample_rate); if (osc == NULL) return GEONKICK_ERROR; synth->oscillators[i] = osc; } return GEONKICK_OK; } struct gkick_oscillator* gkick_synth_get_oscillator(struct gkick_synth *synth, size_t index) { if (synth == NULL) { gkick_log_error("wrong arguments"); return NULL; } if (index < synth->oscillators_number) return synth->oscillators[index]; return NULL; } enum geonkick_error gkick_synth_get_oscillators_number(struct gkick_synth *synth, size_t *number) { if (synth == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); *number = synth->oscillators_number; gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_enable_oscillator(struct gkick_synth *synth, size_t index, int enable) { if (synth == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); struct gkick_oscillator* osc = gkick_synth_get_oscillator(synth, index); if (osc == NULL) { gkick_log_error("can't get oscillator"); gkick_synth_unlock(synth); return GEONKICK_ERROR; } if (enable) gkick_osc_set_state(osc, GEONKICK_OSC_STATE_ENABLED); else gkick_osc_set_state(osc, GEONKICK_OSC_STATE_DISABLED); if (synth->osc_groups[index / GKICK_OSC_GROUP_SIZE]) synth->buffer_update = true; gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_osc_is_enabled(struct gkick_synth *synth, size_t index, int *enabled) { struct gkick_oscillator* osc; if (synth == NULL || enabled == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); osc = gkick_synth_get_oscillator(synth, index); if (osc == NULL) { gkick_log_error("can't get oscillator"); gkick_synth_unlock(synth); return GEONKICK_ERROR; } *enabled = gkick_osc_enabled(osc); gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_osc_set_fm(struct gkick_synth *synth, size_t index, bool is_fm) { gkick_synth_lock(synth); struct gkick_oscillator* osc = gkick_synth_get_oscillator(synth, index); if (osc == NULL) { gkick_log_error("can't get oscillator"); gkick_synth_unlock(synth); return GEONKICK_ERROR; } osc->is_fm = is_fm; if (osc->state == GEONKICK_OSC_STATE_ENABLED) synth->buffer_update = true; gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_osc_is_fm(struct gkick_synth *synth, size_t index, bool *is_fm) { gkick_synth_lock(synth); struct gkick_oscillator* osc = gkick_synth_get_oscillator(synth, index); if (osc == NULL) { gkick_log_error("can't get oscillator"); gkick_synth_unlock(synth); return GEONKICK_ERROR; } *is_fm = osc->is_fm; gkick_synth_unlock(synth); return GEONKICK_OK; } struct gkick_envelope* gkick_synth_osc_get_env(struct gkick_synth *synth, size_t osc_index, size_t env_index) { if (synth == NULL) { gkick_log_error("wrong arguments"); return NULL; } struct gkick_oscillator *osc; osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscillator %d", osc_index); return NULL; } return gkick_osc_get_envelope(osc, env_index); } enum geonkick_error gkick_synth_osc_envelope_points(struct gkick_synth *synth, int osc_index, int env_index, gkick_real **buf, size_t *npoints) { if (synth == NULL || buf == NULL || npoints == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } *npoints = 0; *buf = NULL; gkick_synth_lock(synth); struct gkick_oscillator *osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscillator %d", osc_index); gkick_synth_unlock(synth); return GEONKICK_ERROR; } gkick_osc_get_envelope_points(osc, env_index, buf, npoints); gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_osc_envelope_set_points(struct gkick_synth *synth, int osc_index, int env_index, const gkick_real *buf, size_t npoints) { if (synth == NULL || buf == NULL || npoints == 0) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); struct gkick_oscillator *osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscillator %d", osc_index); gkick_synth_unlock(synth); return GEONKICK_ERROR; } gkick_osc_set_envelope_points(osc, env_index, buf, npoints); if (synth->osc_groups[osc_index / GKICK_OSC_GROUP_SIZE] && osc->state == GEONKICK_OSC_STATE_ENABLED) { synth->buffer_update = true; } gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_osc_env_add_point(struct gkick_synth *synth, int osc_index, int env_index, gkick_real x, gkick_real y) { if (synth == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); struct gkick_oscillator *osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscillator %d", osc_index); gkick_synth_unlock(synth); return GEONKICK_ERROR; } struct gkick_envelope *env = gkick_osc_get_envelope(osc, env_index); if (env == NULL) { gkick_synth_unlock(synth); gkick_log_error("can't get envelope"); return GEONKICK_ERROR; } if (gkick_envelope_add_point(env, x, y) == NULL) { gkick_log_error("can't add envelope point"); gkick_synth_unlock(synth); return GEONKICK_ERROR; } if (synth->osc_groups[osc_index / GKICK_OSC_GROUP_SIZE] && osc->state == GEONKICK_OSC_STATE_ENABLED) { synth->buffer_update = true; } gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_osc_env_remove_point(struct gkick_synth *synth, size_t osc_index, size_t env_index, size_t index) { if (synth == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); struct gkick_oscillator *osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscillator %d", osc_index); gkick_synth_unlock(synth); return GEONKICK_ERROR; } struct gkick_envelope *env = gkick_osc_get_envelope(osc, env_index); if (env == NULL) { gkick_synth_unlock(synth); gkick_log_error("can't get envelope"); return GEONKICK_ERROR; } gkick_envelope_remove_point(env, index); if (synth->osc_groups[osc_index / GKICK_OSC_GROUP_SIZE] && osc->state == GEONKICK_OSC_STATE_ENABLED) { synth->buffer_update = true; } gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_osc_env_update_point(struct gkick_synth *synth, int osc_index, int env_index, int index, gkick_real x, gkick_real y) { if (synth == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); struct gkick_oscillator *osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscillator %d", osc_index); gkick_synth_unlock(synth); return GEONKICK_ERROR; } struct gkick_envelope *env = gkick_osc_get_envelope(osc, env_index); if (env == NULL) { gkick_synth_unlock(synth); gkick_log_error("can't get envelope"); return GEONKICK_ERROR; } gkick_envelope_update_point(env, index, x, y); if (synth->osc_groups[osc_index / GKICK_OSC_GROUP_SIZE] && osc->state == GEONKICK_OSC_STATE_ENABLED) { synth->buffer_update = true; } gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_set_osc_function(struct gkick_synth *synth, size_t osc_index, enum geonkick_osc_func_type type) { struct gkick_oscillator *osc; if (synth == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscilaltor"); gkick_synth_unlock(synth); return GEONKICK_ERROR; } else { osc->func = type; } if (synth->osc_groups[osc_index / GKICK_OSC_GROUP_SIZE] && osc->state == GEONKICK_OSC_STATE_ENABLED) { synth->buffer_update = true; } gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_get_osc_function(struct gkick_synth *synth, size_t osc_index, enum geonkick_osc_func_type *type) { struct gkick_oscillator *osc; if (synth == NULL || type == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscilaltor"); gkick_synth_unlock(synth); return GEONKICK_ERROR; } else { *type = osc->func; } gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_set_osc_phase(struct gkick_synth *synth, size_t osc_index, gkick_real phase) { struct gkick_oscillator *osc; if (synth == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscilaltor"); gkick_synth_unlock(synth); return GEONKICK_ERROR; } else { osc->initial_phase = phase; } if (synth->osc_groups[osc_index / GKICK_OSC_GROUP_SIZE] && osc->state == GEONKICK_OSC_STATE_ENABLED) synth->buffer_update = true; gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_get_osc_phase(struct gkick_synth *synth, size_t osc_index, gkick_real *phase) { struct gkick_oscillator *osc; if (synth == NULL || phase == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscilaltor"); gkick_synth_unlock(synth); return GEONKICK_ERROR; } else { *phase = osc->initial_phase; } gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_set_osc_seed(struct gkick_synth *synth, size_t osc_index, unsigned int seed) { struct gkick_oscillator *osc; if (synth == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscilaltor"); gkick_synth_unlock(synth); return GEONKICK_ERROR; } else { osc->seed = seed; } if (synth->osc_groups[osc_index / GKICK_OSC_GROUP_SIZE] && osc->state == GEONKICK_OSC_STATE_ENABLED) synth->buffer_update = true; gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_get_osc_seed(struct gkick_synth *synth, size_t osc_index, unsigned *seed) { struct gkick_oscillator *osc; if (synth == NULL || seed == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscilaltor"); gkick_synth_unlock(synth); return GEONKICK_ERROR; } else { *seed = osc->seed; } gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_get_length(struct gkick_synth *synth, gkick_real *len) { if (synth == NULL || len == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); *len = synth->length; gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_set_length(struct gkick_synth *synth, gkick_real len) { if (synth == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); synth->length = len; synth->buffer_size = synth->length * synth->sample_rate; synth->buffer_update = true; gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_kick_set_amplitude(struct gkick_synth *synth, gkick_real amplitude) { if (synth == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); synth->amplitude = amplitude; synth->buffer_update = true; gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_kick_get_amplitude(struct gkick_synth *synth, gkick_real *amplitude) { if (synth == NULL || amplitude == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); *amplitude = synth->amplitude; gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error geonkick_synth_kick_filter_enable(struct gkick_synth *synth, int enable) { if (synth == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); synth->filter_enabled = enable; synth->buffer_update = true; gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error geonkick_synth_kick_filter_is_enabled(struct gkick_synth *synth, int *enabled) { if (synth == NULL || enabled == NULL) { return GEONKICK_ERROR; } gkick_synth_lock(synth); *enabled = synth->filter_enabled; gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_kick_set_filter_frequency(struct gkick_synth *synth, gkick_real frequency) { enum geonkick_error res; if (synth == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); res = gkick_filter_set_cutoff_freq(synth->filter, frequency); if (synth->filter_enabled) synth->buffer_update = true; gkick_synth_unlock(synth); return res; } enum geonkick_error gkick_synth_kick_get_filter_frequency(struct gkick_synth *synth, gkick_real *frequency) { if (synth == NULL || frequency == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_filter_get_cutoff_freq(synth->filter, frequency); } enum geonkick_error gkick_synth_kick_set_filter_factor(struct gkick_synth *synth, gkick_real factor) { enum geonkick_error res; if (synth == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); res = gkick_filter_set_factor(synth->filter, factor); if (synth->filter_enabled) synth->buffer_update = true; gkick_synth_unlock(synth); return res; } enum geonkick_error gkick_synth_kick_get_filter_factor(struct gkick_synth *synth, gkick_real *factor) { if (synth == NULL || factor == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_filter_get_factor(synth->filter, factor); } enum geonkick_error gkick_synth_set_kick_filter_type(struct gkick_synth *synth, enum gkick_filter_type type) { enum geonkick_error res; if (synth == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); res = gkick_filter_set_type(synth->filter, type); if (synth->filter_enabled) synth->buffer_update = true; gkick_synth_unlock(synth); return res; } enum geonkick_error gkick_synth_get_kick_filter_type(struct gkick_synth *synth, enum gkick_filter_type *type) { if (synth == NULL || type == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } return gkick_filter_get_type(synth->filter, type); } enum geonkick_error gkick_synth_kick_envelope_get_points(struct gkick_synth *synth, enum geonkick_envelope_type env_type, gkick_real **buf, size_t *npoints) { if (synth == NULL || buf == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } *npoints = 0; *buf = NULL; gkick_synth_lock(synth); if (env_type == GEONKICK_AMPLITUDE_ENVELOPE) gkick_envelope_get_points(synth->envelope, buf, npoints); else if (env_type == GEONKICK_FILTER_CUTOFF_ENVELOPE) gkick_envelope_get_points(synth->filter->cutoff_env, buf, npoints); else if (env_type == GEONKICK_DISTORTION_DRIVE_ENVELOPE) gkick_envelope_get_points(synth->distortion->drive_env, buf, npoints); else if (env_type == GEONKICK_DISTORTION_VOLUME_ENVELOPE) gkick_envelope_get_points(synth->distortion->volume_env, buf, npoints); gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_kick_envelope_set_points(struct gkick_synth *synth, enum geonkick_envelope_type env_type, const gkick_real *buf, size_t npoints) { if (synth == NULL || buf == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); if (env_type == GEONKICK_AMPLITUDE_ENVELOPE) gkick_envelope_set_points(synth->envelope, buf, npoints); else if (env_type == GEONKICK_FILTER_CUTOFF_ENVELOPE) gkick_envelope_set_points(synth->filter->cutoff_env, buf, npoints); else if (env_type == GEONKICK_DISTORTION_DRIVE_ENVELOPE) gkick_envelope_set_points(synth->distortion->drive_env, buf, npoints); else if (env_type == GEONKICK_DISTORTION_VOLUME_ENVELOPE) gkick_envelope_set_points(synth->distortion->volume_env, buf, npoints); if (env_type == GEONKICK_AMPLITUDE_ENVELOPE || (env_type == GEONKICK_FILTER_CUTOFF_ENVELOPE && synth->filter_enabled) || ((env_type == GEONKICK_DISTORTION_DRIVE_ENVELOPE || env_type == GEONKICK_DISTORTION_VOLUME_ENVELOPE) && synth->distortion->enabled)) { synth->buffer_update = true; } gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_kick_add_env_point(struct gkick_synth *synth, enum geonkick_envelope_type env_type, gkick_real x, gkick_real y) { if (synth == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); if (env_type == GEONKICK_AMPLITUDE_ENVELOPE) gkick_envelope_add_point(synth->envelope, x, y); else if (env_type == GEONKICK_FILTER_CUTOFF_ENVELOPE) gkick_envelope_add_point(synth->filter->cutoff_env, x, y); else if (env_type == GEONKICK_DISTORTION_DRIVE_ENVELOPE) gkick_envelope_add_point(synth->distortion->drive_env, x, y); else if (env_type == GEONKICK_DISTORTION_VOLUME_ENVELOPE) gkick_envelope_add_point(synth->distortion->volume_env, x, y); if (env_type == GEONKICK_AMPLITUDE_ENVELOPE || (env_type == GEONKICK_FILTER_CUTOFF_ENVELOPE && synth->filter_enabled) || ((env_type == GEONKICK_DISTORTION_DRIVE_ENVELOPE || env_type == GEONKICK_DISTORTION_VOLUME_ENVELOPE) && synth->distortion->enabled)) synth->buffer_update = true; gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_kick_remove_env_point(struct gkick_synth *synth, enum geonkick_envelope_type env_type, size_t index) { if (synth == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); if (env_type == GEONKICK_AMPLITUDE_ENVELOPE) gkick_envelope_remove_point(synth->envelope, index); else if (env_type == GEONKICK_FILTER_CUTOFF_ENVELOPE) gkick_envelope_remove_point(synth->filter->cutoff_env, index); else if (env_type == GEONKICK_DISTORTION_DRIVE_ENVELOPE) gkick_envelope_remove_point(synth->distortion->drive_env, index); else if (env_type == GEONKICK_DISTORTION_VOLUME_ENVELOPE) gkick_envelope_remove_point(synth->distortion->volume_env, index); if (env_type == GEONKICK_AMPLITUDE_ENVELOPE || (env_type == GEONKICK_FILTER_CUTOFF_ENVELOPE && synth->filter_enabled) || ((env_type == GEONKICK_DISTORTION_DRIVE_ENVELOPE || env_type == GEONKICK_DISTORTION_VOLUME_ENVELOPE) && synth->distortion->enabled)) { synth->buffer_update = true; } gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_kick_update_env_point(struct gkick_synth *synth, enum geonkick_envelope_type env_type, size_t index, gkick_real x, gkick_real y) { if (synth == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); if (env_type == GEONKICK_AMPLITUDE_ENVELOPE) gkick_envelope_update_point(synth->envelope, index, x, y); else if (env_type == GEONKICK_FILTER_CUTOFF_ENVELOPE) gkick_envelope_update_point(synth->filter->cutoff_env, index, x, y); else if (env_type == GEONKICK_DISTORTION_DRIVE_ENVELOPE) gkick_envelope_update_point(synth->distortion->drive_env, index, x, y); else if (env_type == GEONKICK_DISTORTION_VOLUME_ENVELOPE) gkick_envelope_update_point(synth->distortion->volume_env, index, x, y); if (env_type == GEONKICK_AMPLITUDE_ENVELOPE || (env_type == GEONKICK_FILTER_CUTOFF_ENVELOPE && synth->filter_enabled) || ((env_type == GEONKICK_DISTORTION_DRIVE_ENVELOPE || env_type == GEONKICK_DISTORTION_VOLUME_ENVELOPE) && synth->distortion->enabled)) { synth->buffer_update = true; } gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_set_osc_frequency(struct gkick_synth *synth, size_t osc_index, gkick_real v) { struct gkick_oscillator* osc; if (synth == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscillator"); gkick_synth_unlock(synth); return GEONKICK_ERROR; } osc->frequency = v; if (synth->osc_groups[osc_index / GKICK_OSC_GROUP_SIZE] && osc->state == GEONKICK_OSC_STATE_ENABLED) { synth->buffer_update = true; } gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_get_osc_frequency(struct gkick_synth *synth, size_t osc_index, gkick_real *v) { if (synth == NULL || v == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); struct gkick_oscillator* osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscillator"); gkick_synth_unlock(synth); return GEONKICK_ERROR; } *v = osc->frequency; gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_set_osc_pitch_shift(struct gkick_synth *synth, size_t osc_index, gkick_real semitones) { struct gkick_oscillator* osc; if (synth == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscillator"); gkick_synth_unlock(synth); return GEONKICK_ERROR; } osc->pitch_shift = semitones; if (synth->osc_groups[osc_index / GKICK_OSC_GROUP_SIZE] && osc->state == GEONKICK_OSC_STATE_ENABLED) { synth->buffer_update = true; } gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_get_osc_pitch_shift(struct gkick_synth *synth, size_t osc_index, gkick_real *semitones) { if (synth == NULL || semitones == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); struct gkick_oscillator* osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscillator"); gkick_synth_unlock(synth); return GEONKICK_ERROR; } *semitones = osc->pitch_shift; gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_set_osc_amplitude(struct gkick_synth *synth, size_t osc_index, gkick_real v) { if (synth == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); struct gkick_oscillator* osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscillator"); gkick_synth_unlock(synth); return GEONKICK_ERROR; } osc->amplitude = v; if (synth->osc_groups[osc_index / GKICK_OSC_GROUP_SIZE] && osc->state == GEONKICK_OSC_STATE_ENABLED) { synth->buffer_update = true; } gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_get_osc_amplitude(struct gkick_synth *synth, size_t osc_index, gkick_real *v) { struct gkick_oscillator* osc; if (synth == NULL || v == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscillator"); gkick_synth_unlock(synth); return GEONKICK_ERROR; } *v = osc->amplitude; gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_get_buffer_size(struct gkick_synth *synth, size_t *size) { if (synth == NULL || size == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); *size = synth->buffer_size; gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_get_buffer(struct gkick_synth *synth, gkick_real *buffer, size_t size) { if (synth == NULL || buffer == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); if (size >= synth->buffer_size) memcpy(buffer, synth->buffer, synth->buffer_size * sizeof(gkick_real)); else memcpy(buffer, synth->buffer, size * sizeof(gkick_real)); gkick_synth_unlock(synth); return GEONKICK_ERROR; } void gkick_synth_set_output(struct gkick_synth *synth, struct gkick_audio_output *output) { if (synth == NULL || output == NULL) { gkick_log_error("wrong argument"); return; } synth->output = output; } enum geonkick_error gkick_synth_process(struct gkick_synth *synth) { if (synth == NULL) return GEONKICK_ERROR; gkick_synth_lock(synth); synth->buffer_update = false; gkick_buffer_set_size((struct gkick_buffer*)synth->buffer, synth->buffer_size); gkick_real dt = synth->length / synth->buffer_size; gkick_synth_reset_oscillators(synth); gkick_filter_init(synth->filter); gkick_synth_unlock(synth); /* Synthesize the percussion into the synthesizer buffer. */ size_t i = 0; size_t tries = 0; while (1) { /** * Try lock in order not too block the access * of the synthesizer parameters for too long time. */ if (pthread_mutex_trylock(&synth->lock) != 0) { usleep(50); /** * Check how many tries for locking to avoind infinite loop. * It should be maximum around 30ms. */ if (++tries > 600) break; else continue; } if (gkick_buffer_is_end((struct gkick_buffer*)synth->buffer)) { gkick_synth_unlock(synth); break; } else { gkick_real val = gkick_synth_get_value(synth, (gkick_real)(i * dt)); if (isnan(val)) val = 0.0f; else if (val > 1.0f) val = 1.0f; else if (val < -1.0f) val = -1.0f; gkick_buffer_push_back((struct gkick_buffer*)synth->buffer, val); i++; } gkick_synth_unlock(synth); } gkick_synth_lock(synth); if (synth->buffer_callback != NULL && synth->callback_args != NULL) { synth->buffer_callback(synth->callback_args, ((struct gkick_buffer*)synth->buffer)->buff, synth->buffer_size, synth->id); } /** * Don't update the output audio buffer if * the synthesizer parameters * were updated during the synthesis. */ if (!synth->buffer_update) { gkick_audio_output_lock(synth->output); char* buff = synth->output->updated_buffer; synth->output->updated_buffer = synth->buffer; synth->buffer = buff; gkick_audio_output_unlock(synth->output); } gkick_synth_unlock(synth); return GEONKICK_OK; } gkick_real gkick_synth_get_value(struct gkick_synth *synth, gkick_real t) { gkick_real val = 0.0f; gkick_real env_x = 0.0f; gkick_real fm_val = 0.0f; size_t n = synth->oscillators_number; for (size_t i = 0; i < n; i++) { if (synth->osc_groups[i / GKICK_OSC_GROUP_SIZE] && gkick_osc_enabled(synth->oscillators[i])) { if (synth->oscillators[i]->is_fm && i % 3 == 0 && i + 1 < n) { fm_val = gkick_osc_value(synth->oscillators[i], t, synth->length); synth->oscillators[i + 1]->fm_input = fm_val; } else { gkick_real group_ampl = synth->osc_groups_amplitude[i / GKICK_OSC_GROUP_SIZE]; val += group_ampl * gkick_osc_value(synth->oscillators[i], t, synth->length); } gkick_osc_increment_phase(synth->oscillators[i], t, synth->length); } } env_x = t / synth->length; val *= synth->amplitude * gkick_envelope_get_value(synth->envelope, env_x); if (synth->filter_enabled) gkick_filter_val(synth->filter, val, &val, env_x); int enabled = 0; gkick_distortion_is_enabled(synth->distortion, &enabled); if (enabled) gkick_distortion_val(synth->distortion, val, &val, env_x); gkick_compressor_is_enabled(synth->compressor, &enabled); if (enabled) gkick_compressor_val(synth->compressor, val, &val); return val; } void gkick_synth_reset_oscillators(struct gkick_synth *synth) { if (synth == NULL) return; for (size_t i = 0; i < synth->oscillators_number; i++) { struct gkick_oscillator *osc = synth->oscillators[i]; osc->phase = osc->initial_phase; osc->fm_input = 0.0f; osc->seedp = osc->seed; gkick_filter_init(osc->filter); if (osc->sample != NULL) gkick_buffer_reset(osc->sample); } } int gkick_synth_is_update_buffer(struct gkick_synth *synth) { return synth->buffer_update; } enum geonkick_error gkick_synth_set_osc_filter_type(struct gkick_synth *synth, size_t osc_index, enum gkick_filter_type type) { struct gkick_oscillator *osc; enum geonkick_error res; if (synth == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscillator"); gkick_synth_unlock(synth); return GEONKICK_ERROR; } res = gkick_filter_set_type(osc->filter, type); if (osc->filter_enabled && synth->osc_groups[osc_index / GKICK_OSC_GROUP_SIZE] && osc->state == GEONKICK_OSC_STATE_ENABLED) { synth->buffer_update = true; } gkick_synth_unlock(synth); return res; } enum geonkick_error gkick_synth_get_osc_filter_type(struct gkick_synth *synth, size_t osc_index, enum gkick_filter_type *type) { if (synth == NULL || type == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); struct gkick_oscillator *osc; osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscillator"); gkick_synth_unlock(synth); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_filter_get_type(osc->filter, type); gkick_synth_unlock(synth); return res; } enum geonkick_error gkick_synth_set_osc_filter_cutoff(struct gkick_synth *synth, size_t osc_index, gkick_real cutoff) { if (synth == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); struct gkick_oscillator *osc; osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscillator"); gkick_synth_unlock(synth); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_filter_set_cutoff_freq(osc->filter, cutoff); if (osc->filter_enabled && synth->osc_groups[osc_index / GKICK_OSC_GROUP_SIZE] && osc->state == GEONKICK_OSC_STATE_ENABLED) { synth->buffer_update = true; } gkick_synth_unlock(synth); return res; } enum geonkick_error gkick_synth_get_osc_filter_cutoff(struct gkick_synth *synth, size_t osc_index, gkick_real *cutoff) { if (synth == NULL || cutoff == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); struct gkick_oscillator *osc; osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscillator"); gkick_synth_unlock(synth); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_filter_get_cutoff_freq(osc->filter, cutoff); gkick_synth_unlock(synth); return res; } enum geonkick_error gkick_synth_set_osc_filter_factor(struct gkick_synth *synth, size_t osc_index, gkick_real factor) { if (synth == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); struct gkick_oscillator *osc; osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscillator"); gkick_synth_unlock(synth); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_filter_set_factor(osc->filter, factor); if (osc->filter_enabled && synth->osc_groups[osc_index / GKICK_OSC_GROUP_SIZE] && osc->state == GEONKICK_OSC_STATE_ENABLED) { synth->buffer_update = true; } gkick_synth_unlock(synth); return res; } enum geonkick_error gkick_synth_get_osc_filter_factor(struct gkick_synth *synth, size_t osc_index, gkick_real *factor) { if (synth == NULL || factor == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); struct gkick_oscillator *osc; osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscillator"); gkick_synth_unlock(synth); return GEONKICK_ERROR; } enum geonkick_error res; res = gkick_filter_get_factor(osc->filter, factor); gkick_synth_unlock(synth); return res; } enum geonkick_error gkick_synth_osc_enable_filter(struct gkick_synth *synth, size_t osc_index, int enable) { if (synth == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); struct gkick_oscillator *osc; osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscillator"); gkick_synth_unlock(synth); return GEONKICK_ERROR; } osc->filter_enabled = enable; if (synth->osc_groups[osc_index / GKICK_OSC_GROUP_SIZE] && osc->state == GEONKICK_OSC_STATE_ENABLED) { synth->buffer_update = true; } gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_osc_is_enabled_filter(struct gkick_synth *synth, size_t osc_index, int *enabled) { if (synth == NULL || enabled == NULL) { gkick_log_error("wrong arguments"); return GEONKICK_ERROR; } gkick_synth_lock(synth); struct gkick_oscillator *osc; osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscillator"); gkick_synth_unlock(synth); return GEONKICK_ERROR; } *enabled = osc->filter_enabled; gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_compressor_enable(struct gkick_synth *synth, int enable) { synth->buffer_update = true; return gkick_compressor_enable(synth->compressor, enable); } enum geonkick_error gkick_synth_compressor_is_enabled(struct gkick_synth *synth, int *enabled) { return gkick_compressor_is_enabled(synth->compressor , enabled); } enum geonkick_error gkick_synth_compressor_set_attack(struct gkick_synth *synth, gkick_real attack) { enum geonkick_error res; res = gkick_compressor_set_attack(synth->compressor, attack); int enabled = 0; gkick_compressor_is_enabled(synth->compressor, &enabled); if (res == GEONKICK_OK && enabled) synth->buffer_update = true; return res; } enum geonkick_error gkick_synth_compressor_get_attack(struct gkick_synth *synth, gkick_real *attack) { return gkick_compressor_get_attack(synth->compressor, attack); } enum geonkick_error gkick_synth_compressor_set_release(struct gkick_synth *synth, gkick_real release) { enum geonkick_error res; res = gkick_compressor_set_release(synth->compressor, release); int enabled = 0; gkick_compressor_is_enabled(synth->compressor, &enabled); if (res == GEONKICK_OK && enabled) synth->buffer_update = true; return res; } enum geonkick_error gkick_synth_compressor_get_release(struct gkick_synth *synth, gkick_real *release) { return gkick_compressor_get_release(synth->compressor, release); } enum geonkick_error gkick_synth_compressor_set_threshold(struct gkick_synth *synth, gkick_real threshold) { enum geonkick_error res; res = gkick_compressor_set_threshold(synth->compressor, threshold); int enabled = 0; gkick_compressor_is_enabled(synth->compressor, &enabled); if (res == GEONKICK_OK && enabled) synth->buffer_update = true; return res; } enum geonkick_error gkick_synth_compressor_get_threshold(struct gkick_synth *synth, gkick_real *threshold) { return gkick_compressor_get_threshold(synth->compressor, threshold); } enum geonkick_error gkick_synth_compressor_set_ratio(struct gkick_synth *synth, gkick_real ratio) { enum geonkick_error res; res = gkick_compressor_set_ratio(synth->compressor, ratio); int enabled = 0; gkick_compressor_is_enabled(synth->compressor, &enabled); if (res == GEONKICK_OK && enabled) synth->buffer_update = true; return res; } enum geonkick_error gkick_synth_compressor_get_ratio(struct gkick_synth *synth, gkick_real *ratio) { return gkick_compressor_get_ratio(synth->compressor, ratio); } enum geonkick_error gkick_synth_compressor_set_knee(struct gkick_synth *synth, gkick_real knee) { enum geonkick_error res; res = gkick_compressor_set_knee(synth->compressor, knee); int enabled = false; gkick_compressor_is_enabled(synth->compressor, &enabled); if (res == GEONKICK_OK && enabled) synth->buffer_update = true; return res; } enum geonkick_error gkick_synth_compressor_get_knee(struct gkick_synth *synth, gkick_real *knee) { return gkick_compressor_get_threshold(synth->compressor, knee); } enum geonkick_error gkick_synth_compressor_set_makeup(struct gkick_synth *synth, gkick_real makeup) { enum geonkick_error res; res = gkick_compressor_set_makeup(synth->compressor, makeup); int enabled; gkick_compressor_is_enabled(synth->compressor, &enabled); if (res == GEONKICK_OK && enabled) synth->buffer_update = true; return res; } enum geonkick_error gkick_synth_compressor_get_makeup(struct gkick_synth *synth, gkick_real *makeup) { return gkick_compressor_get_makeup(synth->compressor, makeup); } enum geonkick_error gkick_synth_distortion_enable(struct gkick_synth *synth, int enable) { synth->buffer_update = true; return gkick_distortion_enable(synth->distortion, enable); } enum geonkick_error gkick_synth_distortion_is_enabled(struct gkick_synth *synth, int *enabled) { return gkick_distortion_is_enabled(synth->distortion, enabled); } enum geonkick_error gkick_synth_distortion_set_in_limiter(struct gkick_synth *synth, gkick_real limit) { gkick_distortion_set_in_limiter(synth->distortion, limit); int enabled = 0; gkick_distortion_is_enabled(synth->distortion, &enabled); if (enabled) synth->buffer_update = true; return GEONKICK_OK; } enum geonkick_error gkick_synth_distortion_get_in_limiter(struct gkick_synth *synth, gkick_real *limit) { return gkick_distortion_get_in_limiter(synth->distortion, limit); } enum geonkick_error gkick_synth_distortion_set_volume(struct gkick_synth *synth, gkick_real volume) { enum geonkick_error res; int enabled; res = gkick_distortion_set_volume(synth->distortion, volume); gkick_distortion_is_enabled(synth->distortion, &enabled); if (res == GEONKICK_OK && enabled) synth->buffer_update = true; return res; } enum geonkick_error gkick_synth_distortion_get_volume(struct gkick_synth *synth, gkick_real *volume) { return gkick_distortion_get_volume(synth->distortion, volume); } enum geonkick_error gkick_synth_distortion_set_drive(struct gkick_synth *synth, gkick_real drive) { enum geonkick_error res; int enabled; res = gkick_distortion_set_drive(synth->distortion, drive); gkick_distortion_is_enabled(synth->distortion, &enabled); if (res == GEONKICK_OK && enabled) synth->buffer_update = true; return res; } enum geonkick_error gkick_synth_distortion_get_drive(struct gkick_synth *synth, gkick_real *drive) { return gkick_distortion_get_drive(synth->distortion, drive); } enum geonkick_error gkick_synth_enable_group(struct gkick_synth *synth, size_t index, bool enable) { gkick_synth_lock(synth); synth->osc_groups[index] = enable; synth->buffer_update = true; gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error gkick_synth_group_enabled(struct gkick_synth *synth, size_t index, bool *enabled) { gkick_synth_lock(synth); *enabled = synth->osc_groups[index]; gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error geonkick_synth_group_set_amplitude(struct gkick_synth *synth, size_t index, gkick_real amplitude) { gkick_synth_lock(synth); synth->osc_groups_amplitude[index] = amplitude; synth->buffer_update = true; gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error geonkick_synth_group_get_amplitude(struct gkick_synth *synth, size_t index, gkick_real *amplitude) { gkick_synth_lock(synth); *amplitude = synth->osc_groups_amplitude[index]; gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error geonkick_synth_set_osc_sample(struct gkick_synth *synth, size_t osc_index, const gkick_real *data, size_t size) { gkick_synth_lock(synth); struct gkick_oscillator *osc; osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscillator"); gkick_synth_unlock(synth); return GEONKICK_ERROR; } if (osc->sample == NULL) gkick_buffer_new(&osc->sample, osc->sample_rate * GEONKICK_MAX_LENGTH); gkick_buffer_set_data(osc->sample, data, size); gkick_buffer_reset(osc->sample); if (synth->osc_groups[osc_index / GKICK_OSC_GROUP_SIZE] && osc->state == GEONKICK_OSC_STATE_ENABLED) synth->buffer_update = true; gkick_synth_unlock(synth); return GEONKICK_OK; } enum geonkick_error geonkick_synth_get_osc_sample(struct gkick_synth *synth, size_t osc_index, gkick_real **data, size_t *size) { *data = NULL; *size = 0; gkick_synth_lock(synth); struct gkick_oscillator *osc; osc = gkick_synth_get_oscillator(synth, osc_index); if (osc == NULL) { gkick_log_error("can't get oscillator"); gkick_synth_unlock(synth); return GEONKICK_ERROR; } if (osc->sample == NULL) { gkick_synth_unlock(synth); return GEONKICK_OK; } *size = gkick_buffer_size(osc->sample); if (*size > 0) { *data = (gkick_real*)malloc(sizeof(gkick_real) * (*size)); if (*data == NULL) { gkick_log_error("can't allocate memory"); *size = 0; gkick_synth_unlock(synth); return GEONKICK_ERROR; } memcpy(*data, osc->sample->buff, sizeof(gkick_real) * (*size)); } gkick_synth_unlock(synth); return GEONKICK_OK; } geonkick-2.10.2/src/dsp/src/synthesizer.h000066400000000000000000000364361451017372400203120ustar00rootroot00000000000000/** * File name: synthesizer.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GKICK_SYNTHESIZER_H #define GKICK_SYNTHESIZER_H #include "geonkick_internal.h" #include "compressor.h" #include "distortion.h" #include "audio_output.h" #include #include struct gkick_synth { int sample_rate; atomic_size_t id; char name[30]; /* Speciafies if the synthesizer is active. */ atomic_bool is_active; gkick_real current_time; struct gkick_oscillator **oscillators; size_t oscillators_number; /* Groups of oscillators. */ bool osc_groups[GKICK_OSC_GROUPS_NUMBER]; /* Total amplitude for every group of oscillatos. */ gkick_real osc_groups_amplitude[GKICK_OSC_GROUPS_NUMBER]; /* Kick general amplitude */ gkick_real amplitude; /* Time length of the kick in seconds. */ gkick_real length; /* Kick general filter */ struct gkick_filter *filter; int filter_enabled; /* Kick compressor */ struct gkick_compressor *compressor; /* Kick distortion */ struct gkick_distortion *distortion; /* General synthesizer amplitude envelope. */ struct gkick_envelope *envelope; /* To update or not the buffer. */ atomic_bool buffer_update; /** * Kick samples buffer where the synthesizer is doing the synthesis. * It is swapped with one of the oudio output buffers atomically. */ char* _Atomic buffer; /* Kick buffer size. */ _Atomic size_t buffer_size; /** * Audio output that is shared with audio thread */ struct gkick_audio_output *output; /** * Pointer to a function to be * called when the synth has finished the synthesis. */ void (*buffer_callback) (void*, gkick_real *buff, size_t size, size_t id); void *callback_args; pthread_mutex_t lock; }; enum geonkick_error gkick_synth_new(struct gkick_synth **synth, int sample_rate); void gkick_synth_free(struct gkick_synth **synth); void gkick_synth_lock(struct gkick_synth *synth); void gkick_synth_unlock(struct gkick_synth *synth); enum geonkick_error gkick_synth_create_oscillators(struct gkick_synth *synth); enum geonkick_error gkick_synth_get_oscillators_number(struct gkick_synth *synth, size_t *number); struct gkick_oscillator* gkick_synth_get_oscillator(struct gkick_synth *synth, size_t index); enum geonkick_error gkick_synth_enable_oscillator(struct gkick_synth *synth, size_t index, int enable); enum geonkick_error gkick_synth_osc_is_enabled(struct gkick_synth *synth, size_t index, int *enabled); struct gkick_envelope* gkick_synth_osc_get_env(struct gkick_synth *synth, size_t osc_index, size_t env_index); enum geonkick_error gkick_synth_osc_set_fm(struct gkick_synth *synth, size_t index, bool is_fm); enum geonkick_error gkick_synth_osc_is_fm(struct gkick_synth *synth, size_t index, bool *is_fm); enum geonkick_error gkick_synth_osc_envelope_points(struct gkick_synth *synth, int osc_index, int env_index, gkick_real **buf, size_t *npoints); enum geonkick_error gkick_synth_osc_envelope_set_points(struct gkick_synth *synth, int osc_index, int env_index, const gkick_real *buf, size_t npoints); enum geonkick_error gkick_synth_osc_env_add_point(struct gkick_synth *synth, int osc_index, int env_index, gkick_real x, gkick_real y); enum geonkick_error gkick_synth_osc_env_remove_point(struct gkick_synth *synth, size_t osc_index, size_t env_index, size_t index); enum geonkick_error gkick_synth_osc_env_update_point(struct gkick_synth *synth, int osc_index, int env_index, int index, gkick_real x, gkick_real y); enum geonkick_error gkick_synth_set_osc_function(struct gkick_synth *synth, size_t osc_index, enum geonkick_osc_func_type type); enum geonkick_error gkick_synth_get_osc_function(struct gkick_synth *synth, size_t osc_index, enum geonkick_osc_func_type *type); enum geonkick_error gkick_synth_set_osc_phase(struct gkick_synth *synth, size_t osc_index, gkick_real phase); enum geonkick_error gkick_synth_get_osc_phase(struct gkick_synth *synth, size_t osc_index, gkick_real *phase); enum geonkick_error gkick_synth_set_osc_seed(struct gkick_synth *synth, size_t osc_index, unsigned int seed); enum geonkick_error gkick_synth_get_osc_seed(struct gkick_synth *synth, size_t osc_index, unsigned *seed); enum geonkick_error gkick_synth_get_length(struct gkick_synth *synth, gkick_real *len); enum geonkick_error gkick_synth_set_length(struct gkick_synth *synth, gkick_real len); enum geonkick_error gkick_synth_kick_set_amplitude(struct gkick_synth *synth, gkick_real amplitude); enum geonkick_error gkick_synth_kick_get_amplitude(struct gkick_synth *synth, gkick_real *amplitude); enum geonkick_error geonkick_synth_kick_filter_enable(struct gkick_synth *synth, int enable); enum geonkick_error geonkick_synth_kick_filter_is_enabled(struct gkick_synth *synth, int *enabled); enum geonkick_error gkick_synth_kick_set_filter_frequency(struct gkick_synth *synth, gkick_real frequency); enum geonkick_error gkick_synth_kick_get_filter_frequency(struct gkick_synth *synth, gkick_real *frequency); enum geonkick_error gkick_synth_kick_set_filter_factor(struct gkick_synth *synth, gkick_real factor); enum geonkick_error gkick_synth_kick_get_filter_factor(struct gkick_synth *synth, gkick_real *factor); enum geonkick_error gkick_synth_set_kick_filter_type(struct gkick_synth *synth, enum gkick_filter_type type); enum geonkick_error gkick_synth_get_kick_filter_type(struct gkick_synth *synth, enum gkick_filter_type *type); enum geonkick_error gkick_synth_kick_envelope_get_points(struct gkick_synth *synth, enum geonkick_envelope_type env_type, gkick_real **buf, size_t *npoints); enum geonkick_error gkick_synth_kick_envelope_set_points(struct gkick_synth *synth, enum geonkick_envelope_type env_type, const gkick_real *buf, size_t npoints); enum geonkick_error gkick_synth_kick_add_env_point(struct gkick_synth *synth, enum geonkick_envelope_type env_type, gkick_real x, gkick_real y); enum geonkick_error gkick_synth_kick_remove_env_point(struct gkick_synth *synth, enum geonkick_envelope_type env_type, size_t index); enum geonkick_error gkick_synth_kick_update_env_point(struct gkick_synth *synth, enum geonkick_envelope_type env_type, size_t index, gkick_real x, gkick_real y); enum geonkick_error gkick_synth_set_osc_frequency(struct gkick_synth *synth, size_t osc_index, gkick_real v); enum geonkick_error gkick_synth_get_osc_frequency(struct gkick_synth *synth, size_t osc_index, gkick_real *v); enum geonkick_error gkick_synth_set_osc_pitch_shift(struct gkick_synth *synth, size_t osc_index, gkick_real semitones); enum geonkick_error gkick_synth_get_osc_pitch_shift(struct gkick_synth *synth, size_t osc_index, gkick_real *semitones); enum geonkick_error gkick_synth_set_osc_amplitude(struct gkick_synth *synth, size_t osc_index, gkick_real v); enum geonkick_error gkick_synth_get_osc_amplitude(struct gkick_synth *synth, size_t osc_index, gkick_real *v); enum geonkick_error gkick_synth_get_buffer_size(struct gkick_synth *synth, size_t *size); enum geonkick_error gkick_synth_get_buffer(struct gkick_synth *synth, gkick_real *buffer, size_t size); void gkick_synth_set_output(struct gkick_synth *synth, struct gkick_audio_output *output); enum geonkick_error gkick_synth_process(struct gkick_synth *synth); gkick_real gkick_synth_get_value(struct gkick_synth *synth, gkick_real t); void gkick_synth_reset_oscillators(struct gkick_synth *synth); int gkick_synth_is_update_buffer(struct gkick_synth *synth); enum geonkick_error gkick_synth_set_osc_filter_type(struct gkick_synth *synth, size_t osc_index, enum gkick_filter_type type); enum geonkick_error gkick_synth_get_osc_filter_type(struct gkick_synth *synth, size_t osc_index, enum gkick_filter_type *type); enum geonkick_error gkick_synth_set_osc_filter_cutoff(struct gkick_synth *synth, size_t osc_index, gkick_real cutoff); enum geonkick_error gkick_synth_get_osc_filter_cutoff(struct gkick_synth *synth, size_t osc_index, gkick_real *cutoff); enum geonkick_error gkick_synth_set_osc_filter_factor(struct gkick_synth *synth, size_t osc_index, gkick_real factor); enum geonkick_error gkick_synth_get_osc_filter_factor(struct gkick_synth *synth, size_t osc_index, gkick_real *factor); enum geonkick_error gkick_synth_osc_enable_filter(struct gkick_synth *synth, size_t osc_index, int enable); enum geonkick_error gkick_synth_osc_is_enabled_filter(struct gkick_synth *synth, size_t osc_index, int *enabled); enum geonkick_error gkick_synth_compressor_enable(struct gkick_synth *synth, int enable); enum geonkick_error gkick_synth_compressor_is_enabled(struct gkick_synth *synth, int *enabled); enum geonkick_error gkick_synth_compressor_set_attack(struct gkick_synth *synth, gkick_real attack); enum geonkick_error gkick_synth_compressor_get_attack(struct gkick_synth *synth, gkick_real *attack); enum geonkick_error gkick_synth_compressor_set_release(struct gkick_synth *synth, gkick_real release); enum geonkick_error gkick_synth_compressor_get_release(struct gkick_synth *synth, gkick_real *release); enum geonkick_error gkick_synth_compressor_set_threshold(struct gkick_synth *synth, gkick_real threshold); enum geonkick_error gkick_synth_compressor_get_threshold(struct gkick_synth *synth, gkick_real *threshold); enum geonkick_error gkick_synth_compressor_set_ratio(struct gkick_synth *synth, gkick_real ratio); enum geonkick_error gkick_synth_compressor_get_ratio(struct gkick_synth *synth, gkick_real *ratio); enum geonkick_error gkick_synth_compressor_set_knee(struct gkick_synth *synth, gkick_real knee); enum geonkick_error gkick_synth_compressor_get_knee(struct gkick_synth *synth, gkick_real *knee); enum geonkick_error gkick_synth_compressor_set_makeup(struct gkick_synth *synth, gkick_real makeup); enum geonkick_error gkick_synth_compressor_get_makeup(struct gkick_synth *synth, gkick_real *makeup); enum geonkick_error gkick_synth_distortion_enable(struct gkick_synth *synth, int enable); enum geonkick_error gkick_synth_distortion_is_enabled(struct gkick_synth *synth, int *enabled); enum geonkick_error gkick_synth_distortion_get_in_limiter(struct gkick_synth *synth, gkick_real *limit); enum geonkick_error gkick_synth_distortion_set_in_limiter(struct gkick_synth *synth, gkick_real limit); enum geonkick_error gkick_synth_distortion_set_volume(struct gkick_synth *synth, gkick_real volume); enum geonkick_error gkick_synth_distortion_get_volume(struct gkick_synth *synth, gkick_real *volume); enum geonkick_error gkick_synth_distortion_set_drive(struct gkick_synth *synth, gkick_real drive); enum geonkick_error gkick_synth_distortion_get_drive(struct gkick_synth *synth, gkick_real *drive); enum geonkick_error gkick_synth_enable_group(struct gkick_synth *synth, size_t index, bool enable); enum geonkick_error gkick_synth_group_enabled(struct gkick_synth *synth, size_t index, bool *enabled); enum geonkick_error geonkick_synth_group_set_amplitude(struct gkick_synth *synth, size_t index, gkick_real amplitude); enum geonkick_error geonkick_synth_group_get_amplitude(struct gkick_synth *synth, size_t index, gkick_real *amplitude); enum geonkick_error geonkick_synth_set_osc_sample(struct gkick_synth *synth, size_t osc_index, const gkick_real *data, size_t size); enum geonkick_error geonkick_synth_get_osc_sample(struct gkick_synth *synth, size_t osc_index, gkick_real **data, size_t *size); #endif // SYNTHESIZER_H geonkick-2.10.2/src/dsp/src/worker.c000066400000000000000000000120351451017372400172140ustar00rootroot00000000000000/** * File name: worker.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "worker.h" struct gkick_worker *geonkick_worker = NULL; bool geonkick_worker_created() { return geonkick_worker != NULL; } size_t geonkick_worker_reference_count() { if (geonkick_worker != NULL) return geonkick_worker->ref_count; return 0; } enum geonkick_error geonkick_worker_create() { if (geonkick_worker != NULL) return GEONKICK_OK; geonkick_worker = (struct gkick_worker*)calloc(1, sizeof(struct gkick_worker)); if (geonkick_worker == NULL) return GEONKICK_ERROR_MEM_ALLOC; geonkick_worker->running = false; if (pthread_cond_init(&geonkick_worker->condition_var, NULL) != 0) { gkick_log_error("can't init worker condition variable"); return GEONKICK_ERROR; } geonkick_worker->cond_var_initilized = true; return GEONKICK_OK; } enum geonkick_error geonkick_worker_start() { if (!geonkick_worker) return GEONKICK_ERROR; if (geonkick_worker->running) return GEONKICK_OK; geonkick_worker->running = true; if (pthread_create(&geonkick_worker->thread, NULL, &geonkick_worker_thread, NULL) != 0) { gkick_log_error("can't create worker thread"); geonkick_worker->running = false; return GEONKICK_ERROR; } return GEONKICK_OK; } void geonkick_worker_destroy() { if (geonkick_worker->running) geonkick_worker->running = false; pthread_mutex_lock(&geonkick_worker->lock); pthread_cond_signal(&geonkick_worker->condition_var); pthread_mutex_unlock(&geonkick_worker->lock); gkick_log_debug("join thread: %d", geonkick_worker->running); pthread_join(geonkick_worker->thread, NULL); pthread_mutex_lock(&geonkick_worker->lock); if (geonkick_worker->cond_var_initilized) pthread_cond_destroy(&geonkick_worker->condition_var); geonkick_worker->cond_var_initilized = false; pthread_mutex_unlock(&geonkick_worker->lock); free(geonkick_worker); geonkick_worker = NULL; } void geonkick_worker_add_instance(struct geonkick *instance) { if (geonkick_worker->ref_count < GEONKICK_MAX_INSTANCES) { pthread_mutex_lock(&geonkick_worker->lock); instance->id = geonkick_worker->ref_count; geonkick_worker->instances[geonkick_worker->ref_count++] = instance; pthread_mutex_unlock(&geonkick_worker->lock); } } void geonkick_worker_remove_instance(struct geonkick *instance) { if (geonkick_worker->ref_count == 0) return; pthread_mutex_lock(&geonkick_worker->lock); if (geonkick_worker->ref_count) { geonkick_worker->instances[instance->id] = geonkick_worker->instances[geonkick_worker->ref_count - 1]; geonkick_worker->instances[instance->id]->id = instance->id; } geonkick_worker->instances[--geonkick_worker->ref_count] = NULL; pthread_mutex_unlock(&geonkick_worker->lock); } void *geonkick_worker_thread(void *arg) { while (geonkick_worker->running) { /** * Ignore too many updates. * The last updates will be processed. */ usleep(40000); gkick_log_debug("process..."); pthread_mutex_lock(&geonkick_worker->lock); for (size_t i = 0; geonkick_worker->instances[i] != NULL && i < GEONKICK_MAX_INSTANCES; i++) geonkick_process(geonkick_worker->instances[i]); if (!geonkick_worker->running) { pthread_mutex_unlock(&geonkick_worker->lock); break; } gkick_log_debug("wait..."); pthread_cond_wait(&geonkick_worker->condition_var, &geonkick_worker->lock); pthread_mutex_unlock(&geonkick_worker->lock); gkick_log_debug("next"); } gkick_log_debug("exit"); return NULL; } void geonkick_worker_wakeup() { pthread_mutex_lock(&geonkick_worker->lock); pthread_cond_signal(&geonkick_worker->condition_var); pthread_mutex_unlock(&geonkick_worker->lock); } geonkick-2.10.2/src/dsp/src/worker.h000066400000000000000000000035211451017372400172210ustar00rootroot00000000000000/** * File name: worker.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_WORKER_H #define GEONKICK_WORKER_H #include "geonkick_internal.h" struct geonkick; struct gkick_worker { /* The worker thread. */ pthread_t thread; /* Condition variable used for the worker thread. */ pthread_cond_t condition_var; bool cond_var_initilized; /* Specifies if the worker is running. */ atomic_bool running; /* Geonkick instances. */ struct geonkick *instances[GEONKICK_MAX_INSTANCES]; /* Reference count for Geonkick instances. */ atomic_size_t ref_count; pthread_mutex_t lock; }; bool geonkick_worker_created(); size_t geonkick_worker_reference_count(); enum geonkick_error geonkick_worker_create(); enum geonkick_error geonkick_worker_start(); void geonkick_worker_destroy(); void geonkick_worker_add_instance(struct geonkick *instance); void geonkick_worker_remove_instance(struct geonkick *instance); void *geonkick_worker_thread(void *arg); void geonkick_worker_wakeup(); #endif // GEONKICK_WORKER_H geonkick-2.10.2/src/effects_group_box.cpp000066400000000000000000000053201451017372400203700ustar00rootroot00000000000000/** * File name: effects_group_box.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "effects_group_box.h" #include "compressor_group_box.h" #include "distortion_group_box.h" #include "layers_group_box.h" #include "geonkick_api.h" #include EffectsGroupBox::EffectsGroupBox(GeonkickApi *api, GeonkickWidget *parent) : GeonkickGroupBox(parent, Orientation::Horizontal) , geonkickApi(api) , distortionGroupBox{nullptr} { auto compressor = new CompressorGroupBox(geonkickApi, this); RK_ACT_BIND(this, updateGui, RK_ACT_ARGS(), compressor, updateGui()); distortionGroupBox = new DistortionGroupBox(geonkickApi, this); distortionGroupBox->setPosition(compressor->width(), 0); RK_ACT_BIND(this, updateGui, RK_ACT_ARGS(), distortionGroupBox, updateGui()); auto layers = new LayersGroupBox(geonkickApi, this); layers->setBackgroundColor({100, 100, 100}); layers->setPosition(distortionGroupBox->x() + distortionGroupBox->width() + 4, 4); RK_ACT_BIND(this, updateGui, RK_ACT_ARGS(), layers, updateGui()); RkString geonkickInfo{"Standalone"}; if (geonkickApi->getInstanceType() == GeonkickApi::InstanceType::Lv2) geonkickInfo = "LV2"; else if (geonkickApi->getInstanceType() == GeonkickApi::InstanceType::Vst3) geonkickInfo = "VST3"; geonkickInfo += std::string(" ") + Geonkick::applicationVersionStr; auto version = new RkLabel(this, geonkickInfo); version->setSize(120, 14); version->setBackgroundColor(background()); version->setTextColor({150, 150, 150}); auto f = font(); f.setSize(10); f.setWeight(RkFont::Weight::Bold); version->setFont(f); version->setPosition(compressor->x() + (compressor->width() - version->width()) / 2, compressor->y() + compressor->height() + 5); version->show(); } geonkick-2.10.2/src/effects_group_box.h000066400000000000000000000026061451017372400200410ustar00rootroot00000000000000/** * File name: effects_group_box.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_EFFECTS_GROUP_BOX_H #define GEONKICK_EFFECTS_GROUP_BOX_H #include "geonkick_groupbox.h" class GeonkickApi; class EnvelopeWidget; class DistortionGroupBox; class EffectsGroupBox: public GeonkickGroupBox { public: EffectsGroupBox(GeonkickApi *api, GeonkickWidget *parent); ~EffectsGroupBox() = default; RK_DECL_ACT(updateGui, updateGui(), RK_ARG_TYPE(), RK_ARG_VAL()); private: GeonkickApi *geonkickApi; DistortionGroupBox *distortionGroupBox; }; #endif // GKICK_GENERAL_GROUP_BOX_H geonkick-2.10.2/src/envelope.cpp000066400000000000000000000567241451017372400165200ustar00rootroot00000000000000/** * File name: envelope.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "envelope.h" #include "globals.h" #include #include Envelope::Envelope(const RkRect &area) : drawingArea{area} , pointRadius{10} , dotRadius{3} , selectedPointIndex{0} , supportedTypes({Type::Amplitude, Type::Frequency, Type::FilterCutOff, Type::DistortionDrive, Type::DistortionVolume, Type::PitchShift}) , overPointIndex{0} , isOverPoint{false} , pointSelected{false} , envelopeCategory{Category::Oscillator1} , envelopeType{Type::Amplitude} , editedPointIndex{0} , isEditingPoint{false} { } int Envelope::W(void) const { return drawingArea.width(); } int Envelope::H(void) const { return drawingArea.height(); } RkPoint Envelope::getOrigin(void) const { return drawingArea.bottomLeft(); } void Envelope::draw(RkPainter &painter, DrawLayer layer) { if (layer == DrawLayer::Axies) { drawAxies(painter); drawScale(painter); } else if (layer == DrawLayer::Envelope) { drawPoints(painter); drawLines(painter); } } void Envelope::drawAxies(RkPainter & painter) { auto pen = painter.pen(); pen.setColor(RkColor(125, 125, 125)); pen.setWidth(1); painter.setPen(pen); RkPoint point = getOrigin(); painter.drawLine(point.x(), point.y(), point.x() + W() + 10, point.y()); painter.drawLine(point.x(), point.y(), point.x(), point.y() - H() - 10); } void Envelope::drawScale(RkPainter &painter) { drawTimeScale(painter); drawValueScale(painter); } void Envelope::drawTimeScale(RkPainter &painter) { RkFont font = painter.font(); font.setSize(10); painter.setFont(font); auto val = envelopeLength() / 10; int dx = W() / 10; RkPoint point = getOrigin(); int x = point.x() + dx; for (auto i = 1; i <= 10; i++) { RkPen pen(RkColor(80, 80, 80)); pen.setStyle(RkPen::PenStyle::DotLine); painter.setPen(pen); painter.drawLine(x, point.y() - font.size() - 4, x, point.y() - H()); RkRect rect(x - 12, point.y() - 12, 25, font.size()); painter.setPen(RkColor(110, 110, 110)); painter.drawText(rect, std::to_string(std::llround(i * val))); x += dx; } font.setSize(12); painter.setFont(font); painter.setPen(RkPen(RkColor(180, 180, 180, 200))); painter.drawText(point.x() + W() / 2 - 35, point.y() + font.size() + 10, "Length, " + std::to_string(std::llround(envelopeLength())) + " ms"); } void Envelope::drawValueScale(RkPainter &painter) { std::string text; switch (type()) { case Type::Amplitude: text = "Amplitude"; break; case Type::DistortionDrive: text = "Distortion Drive"; break; case Type::DistortionVolume: text = "Distortion Volume"; break; case Type::Frequency: case Type::FilterCutOff: text = "Frequency, Hz"; break; case Type::PitchShift: text = "Semitones"; break; default: text = "Unknown"; } painter.translate(RkPoint(getOrigin().x() - 30, getOrigin().y() - H() / 2 + 35)); painter.rotate(-M_PI / 2); painter.drawText(-5, -5, text); painter.rotate(M_PI / 2); painter.translate(RkPoint(-(getOrigin().x() - 30), -(getOrigin().y() - H() / 2 + 35))); RkFont font = painter.font(); font.setSize(10); painter.setFont(font); int rectH = font.size() + 2; painter.setPen(RkPen(RkColor(110, 110, 110))); if (type() == Type::Amplitude || type() == Type::DistortionDrive || type() == Type::DistortionVolume) { double step = envelopeAmplitude() / 10; double amplitude = envelopeAmplitude(); for (int i = 1; i <= 10; i++) { int x = getOrigin().x(); int y = 0; if (amplitude > std::numeric_limits::min()) y = getOrigin().y() - H() * (i * step / amplitude); RkPen pen(RkColor(80, 80, 80)); pen.setStyle(RkPen::PenStyle::DotLine); painter.setPen(pen); painter.drawLine(x + 1, y, x + W(), y); RkRect rect(x - 28, y - rectH / 2, 22, rectH); painter.setPen(RkPen(RkColor(110, 110, 110))); std::ostringstream ss; if ( type() == Type::DistortionDrive || type() == Type::DistortionVolume) ss << std::fixed << std::setprecision(2) << i * step * pow(10, 36.0/20); else ss << std::fixed << std::setprecision(2) << i * step; painter.drawText(rect, ss.str(), Rk::Alignment::AlignRight); } } else if (type() == Type::PitchShift && envelopeAmplitude() > std::numeric_limits::min()) { double step = 2 * std::abs(envelopeAmplitude() / 10); double amplitude = 2 * std::abs(envelopeAmplitude()); for (int i = 1; i <= 10; i++) { int x = getOrigin().x(); int y = 0; y = getOrigin().y() - H() * (i * step / amplitude); RkPen pen(RkColor(80, 80, 80)); pen.setStyle(RkPen::PenStyle::DotLine); painter.setPen(pen); painter.drawLine(x + 1, y, x + W(), y); RkRect rect(x - 28, y - rectH / 2, 22, rectH); painter.setPen(RkPen(RkColor(110, 110, 110))); std::ostringstream ss; ss << std::fixed << std::setprecision(1) << i * step - amplitude / 2; painter.drawText(rect, ss.str(), Rk::Alignment::AlignRight); } } else if (type() == Type::Frequency || type() == Type::FilterCutOff) { std::vector values {20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 16000}; for (auto value : values) { int x = getOrigin().x(); int y = getOrigin().y() - H() * (log10(value) - log10(20)) / (log10(envelopeAmplitude()) - log10(20)); if (y < 0) break; RkPen pen(RkColor(80, 80, 80)); pen.setStyle(RkPen::PenStyle::DotLine); painter.setPen(pen); if (value != 20) painter.drawLine(x, y, x + W(), y); RkRect rect; if (value == 20) rect = RkRect(x - 28, y - rectH / 2, 22, rectH); else rect = RkRect(x - 28, y - rectH / 2, 22, rectH); painter.setPen(RkPen(RkColor(110, 110, 110))); std::string text; if (value >= 1000) text = std::to_string(value / 1000) + "k"; else text = std::to_string(value); painter.drawText(rect, text, Rk::Alignment::AlignRight); } } } void Envelope::drawPoints(RkPainter &painter) { RkPen pen; pen.setWidth(2); pen.setColor(RkColor(200, 200, 200, 150)); RkPoint origin = getOrigin(); for (decltype(envelopePoints.size()) i = 0; i < envelopePoints.size(); i++) { if (pointSelected && i == selectedPointIndex) { RkPen penSeleted; penSeleted.setWidth(2); penSeleted.setColor(RkColor(255, 255, 255, 255)); painter.setPen(penSeleted); } else if (!pointSelected && isOverPoint && i == overPointIndex) { RkPen penSeleted; penSeleted.setWidth(2); penSeleted.setColor(RkColor(230, 230, 230, 200)); painter.setPen(penSeleted); } else { painter.setPen(pen); } RkPoint scaledPoint = scaleUp(envelopePoints[i]); scaledPoint = RkPoint(scaledPoint.x() + origin.x(), origin.y() - scaledPoint.y()); drawPoint(painter, scaledPoint); scaledPoint = RkPoint(scaledPoint.x(), scaledPoint.y() - 1.4 * getPointRadius()); drawPointValue(painter, scaledPoint, envelopePoints[i].y()); } } void Envelope::drawPoint(RkPainter &painter, const RkPoint &point) { painter.drawCircle(point, getPointRadius()); painter.drawCircle(point, getDotRadius()); } void Envelope::drawPointValue(RkPainter &painter, const RkPoint &point, double value) { if (type() == Envelope::Type::Amplitude || type() == Type::DistortionDrive || type() == Type::DistortionVolume) { value *= envelopeAmplitude(); if (type() == Type::DistortionDrive || type() == Type::DistortionVolume) value *= pow(10, 36.0 / 20); painter.drawText(point.x(), point.y(), Geonkick::doubleToStr(value, 2)); } else if (type() == Type::PitchShift) { value *= envelopeAmplitude(); value = 2 * value - envelopeAmplitude(); painter.drawText(point.x(), point.y(), Geonkick::doubleToStr(value, 1)); } else if (type() == Envelope::Type::Frequency || type() == Type::FilterCutOff) { value *= envelopeAmplitude(); if (value < 20) painter.drawText(point.x(), point.y(), "20Hz " + frequencyToNote(20)); if (value >= 20 && value < 1000) { painter.drawText(point.x(), point.y(), Geonkick::doubleToStr(value, 0) + "Hz " + frequencyToNote(value)); } else if (value >= 1000 && value <= 20000) { painter.drawText(point.x(), point.y(), Geonkick::doubleToStr(value / 1000, 0) + std::string("kHz ") + frequencyToNote(value)); } } } void Envelope::drawLines(RkPainter &painter) { if (envelopePoints.size() < 2) return; std::vector points; auto origin = getOrigin(); for (const auto& point : envelopePoints) { auto scaledPoint = scaleUp(point); points.push_back(RkPoint(origin.x() + scaledPoint.x(), origin.y() - scaledPoint.y())); } auto pen = painter.pen(); pen.setWidth(2); pen.setColor(RkColor(200, 200, 200, 150)); painter.setPen(pen); painter.drawPolyline(points); } void Envelope::overPoint(const RkPoint &point) { overPointIndex = 0; isOverPoint = false; for (decltype(envelopePoints.size()) i = 0; i < envelopePoints.size(); i++) { if (hasPoint(envelopePoints[i], point)) { overPointIndex = i; isOverPoint = true; break; } } } bool Envelope::hasOverPoint() const { return isOverPoint; } bool Envelope::hasSelected(void) const { return pointSelected; } void Envelope::selectPoint(const RkPoint &point) { std::vector::size_type index = 0; for (const auto& p : envelopePoints) { if (hasPoint(p, point)) { selectedPointIndex = overPointIndex = index; pointSelected = isOverPoint = true; break; } index++; } } void Envelope::updateSelectedPointValue(double val) { if (hasEditingPoint() && editedPointIndex < envelopePoints.size()) { pointUpdatedEvent(editedPointIndex, envelopePoints[editedPointIndex].x(), convertFromHumanValue(val)); updatePoints(); } } void Envelope::setEditCurrentPoint(bool edit) { if (hasOverPoint() && overPointIndex < envelopePoints.size()) { editedPointIndex = overPointIndex; isEditingPoint = edit; return; } editedPointIndex = 0; isEditingPoint = false; } double Envelope::getSelectedPointValue() const { return convertToHumanValue(getSelectedPoint().y()); } RkRealPoint Envelope::getSelectedPoint() const { if (hasSelected()) { if (overPointIndex < envelopePoints.size()) return envelopePoints[selectedPointIndex]; } else if (hasOverPoint()) { if (overPointIndex < envelopePoints.size()) return envelopePoints[overPointIndex]; } return {}; } void Envelope::unselectPoint(void) { pointSelected = false; } double Envelope::getLeftPointLimit(void) const { double x; if (!pointSelected || envelopePoints.empty() || selectedPointIndex < 1) x = 0; else x = envelopePoints[selectedPointIndex - 1].x(); return x; } double Envelope::getRightPointLimit(void) const { double x; if (!pointSelected || envelopePoints.empty()) x = 0; else if (selectedPointIndex >= envelopePoints.size() - 1) x = 1; else x = envelopePoints[selectedPointIndex + 1].x(); return x; } void Envelope::moveSelectedPoint(int x, int y) { if (!pointSelected || envelopePoints.empty()) return; auto scaledPoint = scaleDown(RkPoint(x, y)); auto &selectedPoint = envelopePoints[selectedPointIndex]; if (scaledPoint.x() < getLeftPointLimit()) selectedPoint.setX(getLeftPointLimit()); else if (scaledPoint.x() > getRightPointLimit()) selectedPoint.setX(getRightPointLimit()); else selectedPoint.setX(scaledPoint.x()); if (y < 0) selectedPoint.setY(0); else if (y > H()) selectedPoint.setY(1); else selectedPoint.setY(scaledPoint.y()); pointUpdatedEvent(selectedPointIndex, selectedPoint.x(), selectedPoint.y()); } void Envelope::setPoints(const std::vector &points) { removePoints(); for (const auto &point : points) envelopePoints.push_back(point); } void Envelope::addPoint(const RkPoint &point) { auto scaledPoint = scaleDown(point); if (scaledPoint.y() < 0) scaledPoint.setY(0); else if (scaledPoint.y() > 1) scaledPoint.setY(1); if (scaledPoint.x() > 1) { scaledPoint.setX(1); envelopePoints.push_back(scaledPoint); } else if (scaledPoint.x() < 0) { scaledPoint.setX(0); envelopePoints.insert(envelopePoints.begin(), scaledPoint); } else if (envelopePoints.empty()) { envelopePoints.push_back(scaledPoint); } else if (scaledPoint.x() <= envelopePoints[0].x()) { envelopePoints.insert(envelopePoints.begin(), scaledPoint); } else if (scaledPoint.x() >= envelopePoints.back().x()) { envelopePoints.push_back(scaledPoint); } else { for (auto it = envelopePoints.begin(); it != envelopePoints.end(); ++it) { if (scaledPoint.x() <= it->x()) { envelopePoints.insert(it, scaledPoint); break; } } } pointAddedEvent(scaledPoint.x(), scaledPoint.y()); } void Envelope::removePoint(const RkPoint &point) { for (decltype(envelopePoints.size()) i = 0; i < envelopePoints.size(); i++) { if (hasPoint(envelopePoints[i], point)) { if (i != 0 && i != envelopePoints.size() - 1) { envelopePoints.erase(envelopePoints.begin() + i); pointRemovedEvent(i); } break; } } } void Envelope::setCategory(Envelope::Category cat) { envelopeCategory = cat; } void Envelope::setType(Type type) { if (isSupportedType(type)) { envelopeType = type; updatePoints(); } } Envelope::Category Envelope::category(void) const { return envelopeCategory; } Envelope::Type Envelope::type(void) const { return envelopeType; } bool Envelope::isSupportedType(Type type) const { if (supportedTypes.find(type) != supportedTypes.end()) return true; return false; } void Envelope::addSupportedType(Type type) { if (!isSupportedType(type)) supportedTypes.insert(type); } void Envelope::removeSupportedType(Type type) { if (isSupportedType(type)) supportedTypes.erase(type); } void Envelope::removePoints() { envelopePoints.clear(); } const RkRect& Envelope::getDrawingArea() { return drawingArea; } void Envelope::setDrawingArea(const RkRect &rect) { drawingArea = rect; } RkRealPoint Envelope::scaleDown(const RkPoint &point) { RkRealPoint scaledPoint; if (type() == Type::Amplitude || type() == Type::DistortionDrive || type() == Type::DistortionVolume || type() == Type::PitchShift) { scaledPoint = RkRealPoint(static_cast(point.x()) / W(), static_cast(point.y()) / H()); } else { scaledPoint.setX(static_cast(point.x()) / W()); double logVal = (static_cast(point.y()) / H()) * (log10(envelopeAmplitude()) - log10(20)); double val = pow(10, logVal + log10(20)); scaledPoint.setY(val / envelopeAmplitude()); } return scaledPoint; } RkPoint Envelope::scaleUp(const RkRealPoint &point) { int x, y; if (type() == Type::Amplitude || type() == Type::DistortionDrive || type() == Type::DistortionVolume || type() == Type::PitchShift) { x = point.x() * W(); y = point.y() * H(); } else { x = static_cast(point.x() * W()); double logRange = log10(envelopeAmplitude()) - log10(20); double k = 0; if (point.y() > 0) { double logValue = log10(envelopeAmplitude() * point.y()) - log10(20); if (logValue > 0) k = logValue / logRange; } y = k * H(); } return RkPoint(x, y); } bool Envelope::hasPoint(const RkRealPoint &point, const RkPoint &p) { RkPoint scaledPoint = scaleUp(point); int r = getPointRadius(); if (pow(p.x() - scaledPoint.x(), 2) + pow(p.y() - scaledPoint.y(), 2) < pow(r, 2)) return true; return false; } int Envelope::getPointRadius() const { return pointRadius; } void Envelope::setPointRadius(int radius) { pointRadius = radius; } int Envelope::getDotRadius() const { return dotRadius; } void Envelope::setDotRadius(int radius) { dotRadius = radius; } /** * This conversion should comply with this table: * https://homes.luddy.indiana.edu/donbyrd/Teach/MusicalPitchesTable.htm */ std::string Envelope::frequencyToNote(rk_real f) const { if (f < 27.500 || f > 13289.752) return ""; constexpr std::array noteNames = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}; int midiNote = std::round(12.0 * (log2(f / 27.5)) + 21.0); int octave = midiNote / 12 - 1; int noteIndex = static_cast(midiNote) % 12; return noteNames[noteIndex] + std::to_string(octave); } double Envelope::convertToHumanValue(double val) const { if (type() == Envelope::Type::Amplitude || type() == Type::DistortionDrive || type() == Type::DistortionVolume) { val *= envelopeAmplitude(); if (type() == Type::DistortionDrive || type() == Type::DistortionVolume) return val * pow(10, 36.0 / 20); else return val; } else if (type() == Type::PitchShift) { val *= envelopeAmplitude(); return 2 * val - envelopeAmplitude(); } else if (type() == Envelope::Type::Frequency || type() == Type::FilterCutOff) { val *= envelopeAmplitude(); } return val; } double Envelope::convertFromHumanValue(double val) const { if ( envelopeAmplitude() == 0.0 ) return val; if (type() == Envelope::Type::Amplitude || type() == Type::DistortionDrive || type() == Type::DistortionVolume) { val /= envelopeAmplitude(); if (type() == Type::DistortionDrive || type() == Type::DistortionVolume) val /= pow(10, 36.0 / 20); } else if (type() == Type::PitchShift) { val = (val / envelopeAmplitude() + 1.0) / 2.0; } else if (type() == Envelope::Type::Frequency || type() == Type::FilterCutOff) { val /= envelopeAmplitude(); } return std::clamp(val, 0.0, 1.0); } bool Envelope::hasEditingPoint() const { return isEditingPoint; } std::string Envelope::getCurrentPointInfo() const { if (!hasSelected() && !hasOverPoint() ) return std::string(); auto point = getSelectedPoint(); double roundedValue = std::round(convertToHumanValue(point.y()) * 10000.0) / 10000.0; std::string info = Geonkick::doubleToStr(roundedValue, 4); switch (type()) { case Type::Frequency: case Type::FilterCutOff: info += "Hz " + frequencyToNote(point.y()); break; default: break; } info += ", " + Geonkick::doubleToStr(point.x() * envelopeLength(), 0) + "ms"; return info; } geonkick-2.10.2/src/envelope.h000066400000000000000000000123621451017372400161530ustar00rootroot00000000000000/** * File name: envelope.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_ENVELOPE_H #define GEONKICK_ENVELOPE_H #include "geonkick_api.h" #include #include #include #include class Envelope : public RkObject { public: enum class DrawLayer { Axies, Envelope }; using Type = GeonkickApi::EnvelopeType; enum class Category:int { Oscillator1 = static_cast(GeonkickApi::OscillatorType::Oscillator1), Oscillator2 = static_cast(GeonkickApi::OscillatorType::Oscillator2), Noise = static_cast(GeonkickApi::OscillatorType::Noise), General }; explicit Envelope(const RkRect &area = RkRect()); virtual ~Envelope() = default; int W(void) const; int H(void) const; virtual double envelopeLength(void) const { return 0;} virtual double envelopeAmplitude(void) const { return 0;} RkPoint getOrigin(void) const; void draw(RkPainter &painter, DrawLayer layer); void overPoint(const RkPoint &point); bool hasSelected() const; bool hasOverPoint() const; void selectPoint(const RkPoint &point); double getSelectedPointValue() const; RkRealPoint getSelectedPoint() const; void updateSelectedPointValue(double val); void setEditCurrentPoint(bool edit = true); void unselectPoint(void); void moveSelectedPoint(int x, int y); void addPoint(const RkPoint &point); void removePoint(const RkPoint &point); Category category() const; Type type() const; bool isSupportedType(Type type) const; const RkRect& getDrawingArea(); virtual void setEnvelopeLengh(double len) { RK_UNUSED(len); } void setCategory(Category cat); void setType(Type type); void addSupportedType(Type type); void removeSupportedType(Type type); void setPoints(const std::vector &points); void removePoints(); void setDrawingArea(const RkRect &rect); virtual void updatePoints() {}; RK_DECL_ACT(envelopeLengthUpdated, envelopeLengthUpdated(double val), RK_ARG_TYPE(double), RK_ARG_VAL(val)); RK_DECL_ACT(amplitudeUpdated, amplitudeUpdated(double val), RK_ARG_TYPE(double), RK_ARG_VAL(val)); RK_DECL_ACT(envelopeUpdated, envelopeUpdated(), RK_ARG_TYPE(), RK_ARG_VAL()); std::string getCurrentPointInfo() const; protected: virtual void pointAddedEvent(double x, double y) = 0; virtual void pointUpdatedEvent(unsigned int index, double x, double y) = 0; virtual void pointRemovedEvent(unsigned int index) = 0; void drawAxies(RkPainter &painter); void drawScale(RkPainter &painter); void drawTimeScale(RkPainter &painter); void drawValueScale(RkPainter &painter); void drawPoints(RkPainter &painter); void drawPoint(RkPainter &painter, const RkPoint &point); void drawPointValue(RkPainter &painter, const RkPoint &point, double value); void drawLines(RkPainter &painter); RkRealPoint scaleDown(const RkPoint &point); RkPoint scaleUp(const RkRealPoint &point); bool hasPoint(const RkRealPoint &point, const RkPoint &p); int getPointRadius() const; void setPointRadius(int radius); int getDotRadius() const; void setDotRadius(int radius); double getLeftPointLimit(void) const; double getRightPointLimit(void) const; std::string frequencyToNote(rk_real f) const; double convertToHumanValue(double val) const; double convertFromHumanValue(double val) const; bool hasEditingPoint() const; private: RkRect drawingArea; std::vector envelopePoints; int pointRadius; int dotRadius; std::vector::size_type selectedPointIndex; std::unordered_set supportedTypes; std::vector::size_type overPointIndex; bool isOverPoint; bool pointSelected; Category envelopeCategory; Type envelopeType; size_t editedPointIndex; bool isEditingPoint; }; #endif // GEONKICK_ENVELOPE_H geonkick-2.10.2/src/envelope_draw_area.cpp000066400000000000000000000203631451017372400205130ustar00rootroot00000000000000/** * File name: envelope_draw_area.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "envelope_draw_area.h" #include "envelope.h" #include "kick_graph.h" #include "EnvelopePointContextWidget.h" #include #include EnvelopeWidgetDrawingArea::EnvelopeWidgetDrawingArea(GeonkickWidget *parent, GeonkickApi *api) : GeonkickWidget(parent) , geonkickApi{api} , currentEnvelope{nullptr} , hideEnvelope{false} , kickGraphImage{nullptr} , kickGraphics{nullptr} , pointEditingMode{false} { setFixedSize(850, 300); int padding = 50; drawingArea = RkRect(1.1 * padding, padding / 2, width() - 1.5 * padding, height() - 1.2 * padding); setBackgroundColor(40, 40, 40); kickGraphics = new KickGraph(this, geonkickApi, drawingArea.size()); RK_ACT_BIND(kickGraphics, graphUpdated, RK_ACT_ARGS(std::shared_ptr graphImage), this, updateKickGraph(graphImage)); } void EnvelopeWidgetDrawingArea::setEnvelope(Envelope* envelope) { if (envelope) { currentEnvelope = envelope; envelopeUpdated(); } } void EnvelopeWidgetDrawingArea::paintWidget([[maybe_unused]] RkPaintEvent *event) { if (width() != envelopeImage.width() || height() != envelopeImage.height()) { RkImage im(size()); envelopeImage = im; } RkPainter painter(&envelopeImage); painter.fillRect(rect(), background()); if (kickGraphImage && !kickGraphImage->isNull()) painter.drawImage(*kickGraphImage.get(), drawingArea.topLeft().x(), drawingArea.topLeft().y()); else kickGraphics->updateGraphBuffer(); if (currentEnvelope) currentEnvelope->draw(painter, Envelope::DrawLayer::Axies); if (currentEnvelope && !isHideEnvelope()) currentEnvelope->draw(painter, Envelope::DrawLayer::Envelope); painter.drawText(150, height() - 12, getEnvStateText()); auto pen = painter.pen(); pen.setWidth(1); pen.setColor({20, 20, 20, 255}); painter.setPen(pen); painter.drawRect({0, 0, width() - 1, height() - 1}); RkPainter paint(this); paint.drawImage(envelopeImage, 0, 0); } std::string EnvelopeWidgetDrawingArea::getEnvStateText() const { std::string str = "L" + std::to_string(static_cast(geonkickApi->layer()) + 1) + " / "; if (currentEnvelope->category() == Envelope::Category::Oscillator1) str += "OSC1"; else if (currentEnvelope->category() == Envelope::Category::Oscillator2) str += "OSC2"; else if (currentEnvelope->category() == Envelope::Category::Noise) str += "NOISE"; else str += "GEN"; str += " / "; if (currentEnvelope->type() == Envelope::Type::Amplitude) str += "AENV"; else if (currentEnvelope->type() == Envelope::Type::Frequency) str += "FENV"; else if (currentEnvelope->type() == Envelope::Type::DistortionDrive) str += "DIST / DRIVE"; else if (currentEnvelope->type() == Envelope::Type::DistortionVolume) str += "DIST / VOL"; else str += "CFENV"; return str; } void EnvelopeWidgetDrawingArea::mouseButtonPressEvent(RkMouseEvent *event) { if (event->button() != RkMouseEvent::ButtonType::Right && event->button() != RkMouseEvent::ButtonType::Left) return; RkPoint point(event->x() - drawingArea.left(), drawingArea.bottom() - event->y()); if (event->button() == RkMouseEvent::ButtonType::Right) { if (currentEnvelope) { currentEnvelope->removePoint(point); envelopeUpdated(); } } else { mousePoint.setX(event->x()); mousePoint.setY(event->y()); if (currentEnvelope) { currentEnvelope->selectPoint(point); if (currentEnvelope->hasSelected()) envelopeUpdated(); } } setFocus(true); } void EnvelopeWidgetDrawingArea::mouseButtonReleaseEvent(RkMouseEvent *event) { if (!currentEnvelope) return; auto toUpdate = false; if (currentEnvelope->hasSelected()) { currentEnvelope->unselectPoint(); toUpdate = true; } if (currentEnvelope->hasOverPoint()) toUpdate = true; if (toUpdate) envelopeUpdated(); } void EnvelopeWidgetDrawingArea::mouseDoubleClickEvent(RkMouseEvent *event) { if (event->button() == RkMouseEvent::ButtonType::Left) { if (!currentEnvelope) return; RkPoint point(event->x() - drawingArea.left(), drawingArea.bottom() - event->y()); if (pointEditingMode && currentEnvelope->hasOverPoint()) { auto act = std::make_unique(); int x = event->x(); int y = event->y(); auto topWidget = dynamic_cast(RkWidget::getTopWidget()); act->setCallback([&, x, y, topWidget](void){ auto widget = new EnvelopePointContextWidget(currentEnvelope, topWidget); widget->setPosition({x, y + 40}); widget->show(); }); eventQueue()->postAction(std::move(act)); } else { currentEnvelope->addPoint(point); currentEnvelope->selectPoint(point); envelopeUpdated(); } } } void EnvelopeWidgetDrawingArea::mouseMoveEvent(RkMouseEvent *event) { if (!currentEnvelope) return; RkPoint point(event->x() - drawingArea.left(), drawingArea.bottom() - event->y()); if (currentEnvelope->hasSelected()) { currentEnvelope->moveSelectedPoint(point.x(), point.y()); mousePoint.setX(event->x()); mousePoint.setY(event->y()); envelopeUpdated(); return; } auto overPoint = currentEnvelope->hasOverPoint(); currentEnvelope->overPoint(point); if (overPoint != currentEnvelope->hasOverPoint()) envelopeUpdated(); } void EnvelopeWidgetDrawingArea::envelopeUpdated() { action isOverPoint(currentEnvelope->getCurrentPointInfo()); update(); } Envelope* EnvelopeWidgetDrawingArea::getEnvelope() const { return currentEnvelope; } const RkRect EnvelopeWidgetDrawingArea::getDrawingArea() { return drawingArea; } void EnvelopeWidgetDrawingArea::updateKickGraph(const std::shared_ptr &graphImage) { kickGraphImage = graphImage; update(); } bool EnvelopeWidgetDrawingArea::isHideEnvelope() const { return hideEnvelope; } void EnvelopeWidgetDrawingArea::setHideEnvelope(bool b) { if (hideEnvelope != b) { hideEnvelope = b; update(); } } void EnvelopeWidgetDrawingArea::setPointEditingMode(bool b) { pointEditingMode = b; } geonkick-2.10.2/src/envelope_draw_area.h000066400000000000000000000045341451017372400201620ustar00rootroot00000000000000/** * File name: envelope_draw_area.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef ENVELOPE_DRAW_AREA_H #define ENVELOPE_DRAW_AREA_H #include "globals.h" #include "geonkick_widget.h" #include "RkRect.h" #include "RkRealPoint.h" #include "RkImage.h" class Envelope; class KickGraph; class RkMouseEvent; class KickGraph; class GeonkickApi; class EnvelopeWidgetDrawingArea : public GeonkickWidget { public: EnvelopeWidgetDrawingArea(GeonkickWidget *parent, GeonkickApi *api); Envelope* getEnvelope() const; const RkRect getDrawingArea(); void updateKickGraph(const std::shared_ptr &graphImage); bool isHideEnvelope() const; void setEnvelope(Envelope* envelope); void envelopeUpdated(); void setHideEnvelope(bool b); void setPointEditingMode(bool b = true); RK_DECL_ACT(isOverPoint, isOverPoint(const std::string info), RK_ARG_TYPE(const std::string), RK_ARG_VAL(info)); protected: void paintWidget(RkPaintEvent *event) override; void mouseMoveEvent(RkMouseEvent *event) override; void mouseButtonPressEvent(RkMouseEvent *event) override; void mouseButtonReleaseEvent(RkMouseEvent *event) override; void mouseDoubleClickEvent(RkMouseEvent *event) override; std::string getEnvStateText() const; private: GeonkickApi* geonkickApi; Envelope* currentEnvelope; RkRect drawingArea; RkRealPoint mousePoint; bool hideEnvelope; std::shared_ptr kickGraphImage; RkImage envelopeImage; KickGraph *kickGraphics; bool pointEditingMode; }; #endif // ENVELOPE_DRAW_AREA_H geonkick-2.10.2/src/envelope_widget.cpp000066400000000000000000000216731451017372400200560ustar00rootroot00000000000000/** * File name: envelope_widget.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "envelope_widget.h" #include "general_envelope.h" #include "oscillator_envelope.h" #include "envelope_draw_area.h" #include "geonkick_button.h" #include "ViewState.h" #include #include RK_DECLARE_IMAGE_RC(separator); RK_DECLARE_IMAGE_RC(layer1_env); RK_DECLARE_IMAGE_RC(layer1_env_active); RK_DECLARE_IMAGE_RC(layer2_env); RK_DECLARE_IMAGE_RC(layer2_env_active); RK_DECLARE_IMAGE_RC(layer3_env); RK_DECLARE_IMAGE_RC(layer3_env_active); EnvelopeWidget::EnvelopeWidget(GeonkickWidget *parent, GeonkickApi *api, const std::vector> &oscillators) : GeonkickWidget(parent) , drawArea{nullptr} , layer1Button{nullptr} , layer2Button{nullptr} , layer3Button{nullptr} , geonkickApi{api} { // Create drawing area. drawArea = new EnvelopeWidgetDrawingArea(this, geonkickApi); drawArea->show(); auto rect = drawArea->getDrawingArea(); // Oscillator1 envelope auto oscillator = oscillators[static_cast(Oscillator::Type::Oscillator1)].get(); auto envelope = std::dynamic_pointer_cast(std::make_shared(oscillator, rect)); envelopes.insert({static_cast(Envelope::Category::Oscillator1), envelope}); envelope->setCategory(Envelope::Category::Oscillator1); // Oscillator2 envelope oscillator = oscillators[static_cast(Oscillator::Type::Oscillator2)].get(); envelope = std::dynamic_pointer_cast(std::make_shared(oscillator, rect)); envelopes.insert({static_cast(Envelope::Category::Oscillator2), envelope}); envelope->setCategory(Envelope::Category::Oscillator2); // Noise envelope oscillator = oscillators[static_cast(Oscillator::Type::Noise)].get(); envelope = std::dynamic_pointer_cast(std::make_shared(oscillator, rect)); envelopes.insert({static_cast(Envelope::Category::Noise), envelope}); envelope->setCategory(Envelope::Category::Noise); // General envelope envelope = std::dynamic_pointer_cast(std::make_shared(geonkickApi, rect)); envelopes.insert({static_cast(Envelope::Category::General), envelope}); envelope->setCategory(Envelope::Category::General); createButtomMenu(); showEnvelope(Envelope::Category::General, Envelope::Type::Amplitude); RK_ACT_BIND(viewState(), envelopeChanged, RK_ACT_ARGS(Envelope::Category category, Envelope::Type envelope), this, showEnvelope(category, envelope)); createPointInfoLabel(); } void EnvelopeWidget::createButtomMenu() { auto buttomAreaWidget = new GeonkickWidget(this); buttomAreaWidget->setBackgroundColor(40, 40, 40); buttomAreaWidget->setFixedSize(90, 20); buttomAreaWidget->setPosition(55 + drawArea->x(), drawArea->y() + drawArea->height() - buttomAreaWidget->height() - 6); buttomAreaWidget->show(); createLayersButtons(buttomAreaWidget); menuContainer = new RkContainer(buttomAreaWidget); menuContainer->addWidget(layer1Button); menuContainer->addSpace(5); menuContainer->addWidget(layer2Button); menuContainer->addSpace(5); menuContainer->addWidget(layer3Button); } void EnvelopeWidget::createPointInfoLabel() { auto pointInfoLabel = new RkLabel(this); pointInfoLabel->setBackgroundColor(drawArea->background()); pointInfoLabel->setTextColor({200, 200, 200}); pointInfoLabel->setFixedSize(150, 20); pointInfoLabel->setPosition(drawArea->width() - 160, drawArea->y() + drawArea->height() - pointInfoLabel->height() - 6); pointInfoLabel->show(); RK_ACT_BIND(drawArea, isOverPoint, RK_ACT_ARGS(const std::string &info), pointInfoLabel, setText(info)); } void EnvelopeWidget::updateKickGraph(std::shared_ptr graphImage) { if (graphImage && !graphImage->isNull()) drawArea->updateKickGraph(graphImage); } Envelope* EnvelopeWidget::getEnvelope(Envelope::Category category) { if (auto res = envelopes.find(static_cast(category)); res != envelopes.end()) return res->second.get(); return nullptr; } void EnvelopeWidget::showEnvelope(Envelope::Category category, Envelope::Type type) { if (auto envelope = getEnvelope(category); envelope) { envelope->setType(type); drawArea->setEnvelope(envelope); } } void EnvelopeWidget::hideEnvelope(bool b) { drawArea->setHideEnvelope(b); } void EnvelopeWidget::createLayersButtons(GeonkickWidget *buttomAreaWidget) { layer1Button = new GeonkickButton(buttomAreaWidget); layer1Button->setBackgroundColor(buttomAreaWidget->background()); layer1Button->setSize(24, 18); layer1Button->setImage(RkImage(layer1Button->size(), RK_IMAGE_RC(layer1_env)), RkButton::State::Unpressed); layer1Button->setImage(RkImage(layer1Button->size(), RK_IMAGE_RC(layer1_env_active)), RkButton::State::Pressed); layer1Button->setImage(RkImage(layer1Button->size(), RK_IMAGE_RC(layer1_env_active)), RkButton::State::UnpressedHover); layer1Button->setCheckable(true); layer1Button->setPressed(true); RK_ACT_BIND(layer1Button, toggled, RK_ACT_ARGS(bool b), this, setLayer(GeonkickApi::Layer::Layer1)); layer2Button = new GeonkickButton(buttomAreaWidget); layer2Button->setBackgroundColor(buttomAreaWidget->background()); layer2Button->setSize(24, 18); layer2Button->setImage(RkImage(layer2Button->size(), RK_IMAGE_RC(layer2_env)), RkButton::State::Unpressed); layer2Button->setImage(RkImage(layer2Button->size(), RK_IMAGE_RC(layer2_env_active)), RkButton::State::Pressed); layer2Button->setImage(RkImage(layer2Button->size(), RK_IMAGE_RC(layer2_env_active)), RkButton::State::UnpressedHover); layer2Button->setCheckable(true); RK_ACT_BIND(layer2Button, toggled, RK_ACT_ARGS(bool b), this, setLayer(GeonkickApi::Layer::Layer2)); layer3Button = new GeonkickButton(buttomAreaWidget); layer3Button->setBackgroundColor(buttomAreaWidget->background()); layer3Button->setSize(24, 18); layer3Button->setImage(RkImage(layer3Button->size(), RK_IMAGE_RC(layer3_env)), RkButton::State::Unpressed); layer3Button->setImage(RkImage(layer3Button->size(), RK_IMAGE_RC(layer3_env_active)), RkButton::State::Pressed); layer3Button->setImage(RkImage(layer3Button->size(), RK_IMAGE_RC(layer3_env_active)), RkButton::State::UnpressedHover); layer3Button->setCheckable(true); RK_ACT_BIND(layer3Button, toggled, RK_ACT_ARGS(bool b), this, setLayer(GeonkickApi::Layer::Layer3)); } void EnvelopeWidget::setLayer(GeonkickApi::Layer layer) { layer1Button->setPressed(GeonkickApi::Layer::Layer1 == layer); layer2Button->setPressed(GeonkickApi::Layer::Layer2 == layer); layer3Button->setPressed(GeonkickApi::Layer::Layer3 == layer); geonkickApi->setLayer(layer); action requestUpdateGui(); } void EnvelopeWidget::updateGui() { for (const auto &envelope: envelopes) envelope.second->updatePoints(); drawArea->update(); } Oscillator* EnvelopeWidget::getCurrentOscillator() const { if (auto envelope = drawArea->getEnvelope(); envelope) return static_cast(envelope)->getOscillator(); return nullptr; } void EnvelopeWidget::setPointEditingMode(bool b) { drawArea->setPointEditingMode(b); } geonkick-2.10.2/src/envelope_widget.h000066400000000000000000000047541451017372400175240ustar00rootroot00000000000000/** * File name: envelope_widget.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_ENVELOPE_WIDGET_H #define GEONKICK_ENVELOPE_WIDGET_H #include "oscillator.h" #include "geonkick_widget.h" #include "geonkick_api.h" #include "envelope.h" class RkContainer; class EnvelopeWidgetDrawingArea; class GeonkickButton; class EnvelopeWidget : public GeonkickWidget { public: explicit EnvelopeWidget(GeonkickWidget *parent, GeonkickApi *api, const std::vector> &oscillators); void hideEnvelope(bool b); void showEnvelope(Envelope::Category category = Envelope::Category::General, Envelope::Type type = Envelope::Type::Amplitude); void updateGui(); RK_DECL_ACT(requestUpdateGui, requestUpdateGui(), RK_ARG_TYPE(), RK_ARG_VAL()); RK_DECL_ACT(envelopeTypeSelected, envelopeTypeSelected(Envelope::Type type), RK_ARG_TYPE(Envelope::Type), RK_ARG_VAL(type)); Oscillator* getCurrentOscillator() const; void setPointEditingMode(bool b = true); protected: Envelope* getEnvelope(Envelope::Category category); void updateKickGraph(std::shared_ptr graphImage); void createLayersButtons(GeonkickWidget *buttomAreaWidget); void setLayer(GeonkickApi::Layer layer); void createButtomMenu(); void createPointInfoLabel(); private: std::unordered_map> envelopes; EnvelopeWidgetDrawingArea *drawArea; GeonkickButton *layer1Button; GeonkickButton *layer2Button; GeonkickButton *layer3Button; GeonkickApi *geonkickApi; RkContainer *menuContainer; }; #endif geonkick-2.10.2/src/export_widget.cpp000066400000000000000000000413341451017372400175560ustar00rootroot00000000000000/** * File name: export_widget.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "export_widget.h" #include "geonkick_api.h" #include "geonkick_button.h" #include "file_dialog.h" #include #include #include #include RK_DECLARE_IMAGE_RC(export_bk); RK_DECLARE_IMAGE_RC(export_format_unpressed); RK_DECLARE_IMAGE_RC(export_format_pressed); RK_DECLARE_IMAGE_RC(export_browse); RK_DECLARE_IMAGE_RC(export_button); RK_DECLARE_IMAGE_RC(export_cancel); RK_DECLARE_IMAGE_RC(flac16_radio_button); RK_DECLARE_IMAGE_RC(flac16_radio_button_active); RK_DECLARE_IMAGE_RC(flac24_radio_button); RK_DECLARE_IMAGE_RC(flac24_radio_button_active); RK_DECLARE_IMAGE_RC(wav16_radio_button); RK_DECLARE_IMAGE_RC(wav16_radio_button_active); RK_DECLARE_IMAGE_RC(wav24_radio_button); RK_DECLARE_IMAGE_RC(wav24_radio_button_active); RK_DECLARE_IMAGE_RC(wav32_radio_button); RK_DECLARE_IMAGE_RC(wav32_radio_button_active); RK_DECLARE_IMAGE_RC(ogg_radio_button); RK_DECLARE_IMAGE_RC(ogg_radio_button_active); RK_DECLARE_IMAGE_RC(mono_radio_button); RK_DECLARE_IMAGE_RC(mono_radio_button_active); RK_DECLARE_IMAGE_RC(stereo_radio_button); RK_DECLARE_IMAGE_RC(stereo_radio_button_active); ExportWidget::ExportWidget(GeonkickWidget *parent, GeonkickApi *api) : GeonkickWidget(parent, Rk::WindowFlags::Dialog) , geonkickApi{api} , locationEdit{nullptr} , fileNameEdit{nullptr} , browseLocation{nullptr} , flac16Button{nullptr} , flac24Button{nullptr} , wav16Button{nullptr} , wav24Button{nullptr} , wav32Button{nullptr} , oggButton{nullptr} , monoButton{nullptr} , stereoButton{nullptr} , progressBar{nullptr} , exportButton{nullptr} , cancelButton{nullptr} , errorLabel{nullptr} , selectedFormat{ExportFormat::Wav16} , channelsType{ChannelsType::Mono} { setPosition(30, 40); setBorderWidth(1); setBorderColor(40, 40, 40); std::string format = geonkickApi->getSettings("ExportDialog/Format"); if (!format.empty()) selectedFormat = static_cast(std::stoi(format)); std::string channel = geonkickApi->getSettings("ExportDialog/Channel"); if (!channel.empty()) channelsType = static_cast(std::stoi(channel)); setFixedSize(521, 184); setTitle("Export - " + std::string(GEONKICK_NAME)); setBackgroundImage(RkImage(521, 184, RK_IMAGE_RC(export_bk))); errorLabel = new RkLabel(this); errorLabel->setFixedSize(150, 15); errorLabel->setBackgroundColor(background()); errorLabel->setPosition(width() - 180, 105); errorLabel->setTextColor({200, 0, 0}); errorLabel->hide(); locationEdit = new RkLineEdit(this); locationEdit->setFont(font()); locationEdit->setTitle("locationEdit"); locationEdit->show(); locationEdit->setText(geonkickApi->currentWorkingPath("ExportDialog/Location")); locationEdit->setSize(120, 25); locationEdit->setPosition(82, 54); RK_ACT_BIND(locationEdit, textEdited, RK_ACT_ARGS(const std::string& text), this, resetProgressBar()); browseLocation = new GeonkickButton(this); browseLocation->setCheckable(true); browseLocation->setUnpressedImage(RkImage(90, 30, RK_IMAGE_RC(export_browse))); browseLocation->setPosition(205, 52); RK_ACT_BIND(browseLocation, toggled, RK_ACT_ARGS(bool b), this, browse()); fileNameEdit = new RkLineEdit(this); fileNameEdit->setFont(font()); fileNameEdit->show(); fileNameEdit->setFixedSize(100, 25); fileNameEdit->setPosition(380, 54); fileNameEdit->setText(geonkickApi->getSettings("ExportDialog/FileName")); RK_ACT_BIND(fileNameEdit, textEdited, RK_ACT_ARGS(const std::string& text), this, resetProgressBar()); RK_ACT_BIND(fileNameEdit, textEdited, RK_ACT_ARGS(const std::string& text), this, showError()); createFormatButtons(); createChannelsButtons(); progressBar = new RkProgressBar(this); progressBar->setFixedSize(width() - 50, 4); progressBar->setPosition(25, 122); progressBar->setRange(0, 100); progressBar->setBorderWidth(1); progressBar->setBorderColor(40, 40, 40); progressBar->setBackgroundColor(background()); exportButton = new GeonkickButton(this); exportButton->setCheckable(true); exportButton->setPosition((width() - 2 * 90 - 45) / 2, height() - 50); exportButton->setUnpressedImage(RkImage(90, 30, RK_IMAGE_RC(export_button))); RK_ACT_BIND(exportButton, toggled, RK_ACT_ARGS(bool b), this, exportKick()); cancelButton = new GeonkickButton(this); cancelButton->setCheckable(true); cancelButton->setPosition((width() - 2 * 90 - 45) / 2 + 90 + 45, height() - 50); cancelButton->setUnpressedImage(RkImage(90, 30, RK_IMAGE_RC(export_cancel))); RK_ACT_BIND(cancelButton, toggled, RK_ACT_ARGS(bool b), this, close()); show(); } ExportWidget::~ExportWidget() { } void ExportWidget::createFormatButtons() { int y = 19; flac16Button = new GeonkickButton(this); flac16Button->setPressed(ExportFormat::Flac16 == selectedFormat); flac16Button->setFixedSize(63, 16); flac16Button->setUnpressedImage(RkImage(flac16Button->size(), RK_IMAGE_RC(flac16_radio_button))); flac16Button->setPressedImage(RkImage(flac16Button->size(), RK_IMAGE_RC(flac16_radio_button_active))); flac16Button->setPosition(34, y); RK_ACT_BIND(flac16Button, toggled, RK_ACT_ARGS(bool b), this, setFormat(ExportFormat::Flac16)); flac24Button = new GeonkickButton(this); flac24Button->setPressed(ExportFormat::Flac24 == selectedFormat); flac24Button->setFixedSize(65, 16); flac24Button->setUnpressedImage(RkImage(flac24Button->size(), RK_IMAGE_RC(flac24_radio_button))); flac24Button->setPressedImage(RkImage(flac24Button->size(), RK_IMAGE_RC(flac24_radio_button_active))); flac24Button->setPosition(34 + 79, y); RK_ACT_BIND(flac24Button, toggled, RK_ACT_ARGS(bool b), this, setFormat(ExportFormat::Flac24)); wav16Button = new GeonkickButton(this); wav16Button->setPressed(ExportFormat::Wav16 == selectedFormat); wav16Button->setFixedSize(62, 16); wav16Button->setUnpressedImage(RkImage(wav16Button->size(), RK_IMAGE_RC(wav16_radio_button))); wav16Button->setPressedImage(RkImage(wav16Button->size(), RK_IMAGE_RC(wav16_radio_button_active))); wav16Button->setPosition(34 + 2 * 79, y); RK_ACT_BIND(wav16Button, toggled, RK_ACT_ARGS(bool b), this, setFormat(ExportFormat::Wav16)); wav24Button = new GeonkickButton(this); wav24Button->setPressed(ExportFormat::Wav24 == selectedFormat); wav24Button->setFixedSize(62, 16); wav24Button->setUnpressedImage(RkImage(wav24Button->size(), RK_IMAGE_RC(wav24_radio_button))); wav24Button->setPressedImage(RkImage(wav24Button->size(), RK_IMAGE_RC(wav24_radio_button_active))); wav24Button->setPosition(34 + 3 * 79, y); RK_ACT_BIND(wav24Button, toggled, RK_ACT_ARGS(bool b), this, setFormat(ExportFormat::Wav24)); wav32Button = new GeonkickButton(this); wav32Button->setPressed(ExportFormat::Wav32 == selectedFormat); wav32Button->setFixedSize(62, 16); wav32Button->setUnpressedImage(RkImage(wav32Button->size(), RK_IMAGE_RC(wav32_radio_button))); wav32Button->setPressedImage(RkImage(wav32Button->size(), RK_IMAGE_RC(wav32_radio_button_active))); wav32Button->setPosition(34 + 4 * 79, y); RK_ACT_BIND(wav32Button, toggled, RK_ACT_ARGS(bool b), this, setFormat(ExportFormat::Wav32)); oggButton = new GeonkickButton(this); oggButton->setPressed(ExportFormat::Ogg == selectedFormat); oggButton->setFixedSize(51, 16); oggButton->setUnpressedImage(RkImage(oggButton->size(), RK_IMAGE_RC(ogg_radio_button))); oggButton->setPressedImage(RkImage(oggButton->size(), RK_IMAGE_RC(ogg_radio_button_active))); oggButton->setPosition(34 + 5 * 79, y); RK_ACT_BIND(oggButton, toggled, RK_ACT_ARGS(bool b), this, setFormat(ExportFormat::Ogg)); } void ExportWidget::setFormat(ExportFormat format) { if (selectedFormat == format) return; flac16Button->setPressed(ExportFormat::Flac16 == format); flac24Button->setPressed(ExportFormat::Flac24 == format); wav16Button->setPressed(ExportFormat::Wav16 == format); wav24Button->setPressed(ExportFormat::Wav24 == format); wav32Button->setPressed(ExportFormat::Wav32 == format); oggButton->setPressed(ExportFormat::Ogg == format); selectedFormat = format; resetProgressBar(); showError(); } void ExportWidget::createChannelsButtons() { int y = 95; int x = 192; monoButton = new GeonkickButton(this); monoButton->setPressed(ChannelsType::Mono == channelsType); monoButton->setFixedSize(51, 16); monoButton->setPosition(x , y); monoButton->setUnpressedImage(RkImage(monoButton->size(), RK_IMAGE_RC(mono_radio_button))); monoButton->setPressedImage(RkImage(monoButton->size(), RK_IMAGE_RC(mono_radio_button_active))); RK_ACT_BIND(monoButton, toggled, RK_ACT_ARGS(bool b), this, setChannels(ChannelsType::Mono)); stereoButton = new GeonkickButton(this); stereoButton->setPressed(ChannelsType::Stereo == channelsType); stereoButton->setFixedSize(56, 16); stereoButton->setPosition(x + 66, y); stereoButton->setUnpressedImage(RkImage(stereoButton->size(), RK_IMAGE_RC(stereo_radio_button))); stereoButton->setPressedImage(RkImage(stereoButton->size(), RK_IMAGE_RC(stereo_radio_button_active))); RK_ACT_BIND(stereoButton, toggled, RK_ACT_ARGS(bool b), this, setChannels(ChannelsType::Stereo)); } void ExportWidget::setChannels(ChannelsType channels) { if (channels == channelsType) return; if (channels == ChannelsType::Stereo) { stereoButton->setPressed(true); monoButton->setPressed(false); } else { stereoButton->setPressed(false); monoButton->setPressed(true); } channelsType = channels; resetProgressBar(); showError(); } void ExportWidget::browse() { locationEdit->setFocus(false); fileNameEdit->setFocus(false); auto fileDialog = new FileDialog(this, FileDialog::Type::Open, "Select Path - " + std::string(GEONKICK_NAME)); fileDialog->setPosition(20, 20); fileDialog->setHomeDirectory(geonkickApi->getSettings("GEONKICK_CONFIG/HOME_PATH")); fileDialog->setCurrentDirectoy(geonkickApi->currentWorkingPath("ExportDialog/Location")); RK_ACT_BIND(fileDialog, selectedFile, RK_ACT_ARGS(const std::string &file), this, setLocation(fileDialog->currentDirectory())); } void ExportWidget::setLocation(const std::string &location) { locationEdit->setText(location); geonkickApi->setCurrentWorkingPath("ExportDialog/Location", std::filesystem::path(location)); } bool ExportWidget::validateInput() { if (locationEdit->text().empty()) { showError("File location is empty"); return false; } if (fileNameEdit->text().empty()) { showError("File name is empty"); return false; } return true; } void ExportWidget::exportKick() { resetProgressBar(); if (!validateInput()) return; SF_INFO sndinfo; memset(&sndinfo, 0, sizeof(sndinfo)); sndinfo.samplerate = geonkickApi->getSampleRate(); if (sndinfo.samplerate == 0) { showError("Error on exporting kick"); return; } sndinfo.channels = channelsType == ChannelsType::Mono ? 1 : 2; sndinfo.format = exportFormat(); auto tempBuffer = geonkickApi->getKickBuffer(); sndinfo.frames = tempBuffer.size(); std::vector kickBuffer; if (sndinfo.channels == 2) { kickBuffer.resize(2 * tempBuffer.size()); size_t k = 0; while (k < tempBuffer.size()) { kickBuffer[2 * k] = kickBuffer[2 * k + 1] = tempBuffer[k]; k++; } } else { kickBuffer = std::move(tempBuffer); } if (kickBuffer.empty() || !sf_format_check(&sndinfo)) { showError("Error: error on exporting kick"); return; } auto filePath = getFilePath(); if (filePath.empty()) { showError("Wrong file name format"); return; } SNDFILE *sndFile = sf_open(filePath.c_str(), SFM_WRITE, &sndinfo); if (!sndFile) { showError("Error on exporting kick1"); return; } size_t n; #ifdef GEONKICK_DOUBLE_PRECISION n = sf_write_double(sndFile, kickBuffer.data(), kickBuffer.size()); #else n = sf_write_float(sndFile, kickBuffer.data(), kickBuffer.size()); #endif if (n != kickBuffer.size()) showError("Error on exporting"); else progressBar->setValue(100); sf_close(sndFile); close(); if (!fileNameEdit->text().empty()) geonkickApi->setSettings("ExportDialog/FileName", fileNameEdit->text()); geonkickApi->setSettings("ExportDialog/Format", std::to_string(static_cast(selectedFormat))); geonkickApi->setSettings("ExportDialog/Channel", std::to_string(static_cast(channelsType))); } void ExportWidget::resetProgressBar() { progressBar->reset(); } int ExportWidget::exportFormat() { switch (selectedFormat) { case ExportFormat::Flac16: return SF_FORMAT_FLAC | SF_FORMAT_PCM_16; case ExportFormat::Flac24: return SF_FORMAT_FLAC | SF_FORMAT_PCM_24; case ExportFormat::Wav16: return SF_FORMAT_WAV | SF_FORMAT_PCM_16; case ExportFormat::Wav24: return SF_FORMAT_WAV | SF_FORMAT_PCM_24; case ExportFormat::Wav32: return SF_FORMAT_WAV | SF_FORMAT_PCM_32; case ExportFormat::Ogg: return SF_FORMAT_OGG | SF_FORMAT_VORBIS; default: return SF_FORMAT_WAV | SF_FORMAT_PCM_24; } } std::string ExportWidget::getFilePath() { auto path = std::filesystem::path(fileNameEdit->text()); std::string ext = path.extension(); if (ext == ".wav" || ext == ".WAV" || ext == ".flac" || ext == ".FLAC" || ext == ".ogg" || ext == ".OGG") { path.replace_extension("." + fileSuffix()); } else { path = std::filesystem::path(path.string() + "." + fileSuffix()); } return std::filesystem::path(locationEdit->text()) / path; } std::string ExportWidget::fileSuffix() { switch (selectedFormat) { case ExportFormat::Flac16: case ExportFormat::Flac24: return std::string("flac"); case ExportFormat::Wav16: case ExportFormat::Wav24: case ExportFormat::Wav32: return std::string("wav"); case ExportFormat::Ogg: return std::string("ogg"); default: return std::string(); } } void ExportWidget::showError(const std::string &error) { if (error.empty()) { errorLabel->hide(); progressBar->setProgressColor({52, 116, 209}); } else { errorLabel->setText(error); errorLabel->show(); progressBar->setProgressColor({200, 0, 0}); progressBar->setValue(100); } } geonkick-2.10.2/src/export_widget.h000066400000000000000000000053071451017372400172230ustar00rootroot00000000000000/** * File name: export_widget.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_EXPORT_WIDGET_H #define GEONKICK_EXPORT_WIDGET_H #include "geonkick_widget.h" class GeonkickApi; class RkLineEdit; class RkLabel; class RkProgressBar; class GeonkickButton; class RkProgressBar; class ExportWidget: public GeonkickWidget { enum class ChannelsType: int { Mono, Stereo }; enum class ExportFormat: int { Flac16 = 0, Flac24 = 1, Wav16 = 2, Wav24 = 3, Wav32 = 4, Ogg = 5 }; public: ExportWidget(GeonkickWidget *parent, GeonkickApi *api); ~ExportWidget(); protected: void createFormatButtons(); void setFormat(ExportFormat format); void createChannelsButtons(); void setChannels(ChannelsType channels); int exportFormat(); std::string getFilePath(); std::string fileSuffix(); bool validateInput(); void setLocation(const std::string &location); void browse(); void exportKick(); void resetProgressBar(); void showError(const std::string &error = std::string()); private: GeonkickApi *geonkickApi; RkLineEdit *locationEdit; RkLineEdit *fileNameEdit; GeonkickButton *browseLocation; GeonkickButton *flac16Button; GeonkickButton *flac24Button; GeonkickButton *wav16Button; GeonkickButton *wav24Button; GeonkickButton *wav32Button; GeonkickButton *oggButton; GeonkickButton *monoButton; GeonkickButton *stereoButton; RkProgressBar *progressBar; GeonkickButton *exportButton; GeonkickButton *cancelButton; RkLabel *errorLabel; ExportFormat selectedFormat; ChannelsType channelsType; }; #endif // GEONKICK_EXPORT_WIDGET_H geonkick-2.10.2/src/file_dialog.cpp000066400000000000000000000460231451017372400171300ustar00rootroot00000000000000/** * File name: file_dialog.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "file_dialog.h" #include "geonkick_button.h" #include "geonkick_slider.h" #include "PathListModel.h" #include #include #include #include #include #include RK_DECLARE_IMAGE_RC(open_active); RK_DECLARE_IMAGE_RC(save_active); RK_DECLARE_IMAGE_RC(cancel); RK_DECLARE_IMAGE_RC(scrollbar_button_up); RK_DECLARE_IMAGE_RC(scrollbar_button_down); FilesView::FilesView(GeonkickWidget *parent) : GeonkickWidget(parent) , selectedFileIndex{-1} , hightlightLine{-1} , offsetIndex{-1} , currentPath{std::filesystem::current_path()} , lineHeight{15} , lineSacing{lineHeight / 2} , visibleLines{0} , topScrollBarButton{nullptr} , bottomScrollBarButton{nullptr} , scrollBarWidth{12} , scrollBar{nullptr} , isScrollBarVisible{false} { setFixedSize(parent->width() - 110, parent->height() - 100); visibleLines = height() / (lineHeight + lineSacing); setPosition(104, 50); setBackgroundColor(50, 50, 50); setBorderColor(40, 40, 40); setBorderWidth(1); createScrollBar(); showScrollBar(false); show(); } std::string FilesView::getCurrentPath() const { return currentPath.string(); } void FilesView::setCurrentPath(const std::string &path) { currentPath = path; selectedFileIndex = -1; loadCurrentDirectory(); update(); } void FilesView::createScrollBar() { topScrollBarButton = new GeonkickButton(this); topScrollBarButton->setUnpressedImage(RkImage(12, 17, RK_IMAGE_RC(scrollbar_button_up))); topScrollBarButton->setSize(scrollBarWidth, ((float)3 / 2) * scrollBarWidth); topScrollBarButton->setPosition(width() - scrollBarWidth, 0); topScrollBarButton->setCheckable(true); RK_ACT_BIND(topScrollBarButton, toggled, RK_ACT_ARGS(bool b), this, onLineUp()); bottomScrollBarButton = new GeonkickButton(this); bottomScrollBarButton->setUnpressedImage(RkImage(12, 17, RK_IMAGE_RC(scrollbar_button_down))); bottomScrollBarButton->setSize(scrollBarWidth, ((float)3 / 2) * scrollBarWidth); bottomScrollBarButton->setPosition(width() - scrollBarWidth, height() - bottomScrollBarButton->height()); bottomScrollBarButton->setCheckable(true); RK_ACT_BIND(bottomScrollBarButton, toggled, RK_ACT_ARGS(bool b), this, onLineDown()); scrollBar = new GeonkickSlider(this, GeonkickSlider::Orientation::Vertical); scrollBar->setSize(scrollBarWidth, bottomScrollBarButton->y() - topScrollBarButton->y() - topScrollBarButton->height()); scrollBar->setPosition(topScrollBarButton->x(), topScrollBarButton->y() + topScrollBarButton->height()); RK_ACT_BIND(scrollBar, valueUpdated, RK_ACT_ARGS(int val), this, scrollBarChanged(val)); } void FilesView::showScrollBar(bool b) { isScrollBarVisible = b; if (isScrollBarVisible) { topScrollBarButton->show(); bottomScrollBarButton->show(); scrollBar->show(); scrollBar->onSetValue(0); updateScrollBar(); } else { topScrollBarButton->hide(); bottomScrollBarButton->hide(); scrollBar->hide(); } } void FilesView::updateScrollBar() { if (isScrollBarVisible) { if (filesList.empty() || offsetIndex < 0) scrollBar->onSetValue(100); else scrollBar->onSetValue(100 - (double)(offsetIndex) / (filesList.size() - visibleLines) * 100); } } void FilesView::scrollBarChanged(int val) { val = 100 - val; if (filesList.size() > visibleLines) { offsetIndex = ((double)val / 100) * (filesList.size() - visibleLines); update(); } } void FilesView::loadCurrentDirectory() { if (selectedFileIndex > -1) currentPath = filesList[selectedFileIndex]; if (!std::filesystem::is_directory(currentPath)) return; decltype(filesList) files; decltype(filesList) dirs; try { for (const auto &entry : std::filesystem::directory_iterator(currentPath)) { if (entry.path().empty()) continue; if (std::filesystem::is_directory(entry.path())) { dirs.emplace_back(entry.path()); } else if (std::find(fileFilters.begin(), fileFilters.end(), entry.path().extension()) != fileFilters.end()) { files.emplace_back(entry.path()); } } filesList = files; } catch(...) { GEONKICK_LOG_ERROR("error on reading directory"); files.clear(); } if (!files.empty()) { std::sort(files.begin(), files.end(), [] (decltype(files)::value_type &a, decltype(files)::value_type &b) -> bool { return a < b; }); } if (!dirs.empty()) { std::sort(dirs.begin(), dirs.end(), [] (decltype(dirs)::value_type &a, decltype(dirs)::value_type &b) -> bool { return a < b; }); } filesList = std::move(dirs); filesList.insert(filesList.end(), files.begin(), files.end()); if (currentPath.parent_path() != currentPath.root_path() && !currentPath.parent_path().empty()) filesList.insert(filesList.begin(), currentPath.parent_path()); else if (!currentPath.root_path().empty()) filesList.insert(filesList.begin(), currentPath.root_path()); offsetIndex = 0; selectedFileIndex = 0; showScrollBar(filesList.size() > visibleLines); action currentPathChanged(currentPath.string()); } void FilesView::paintWidget(RkPaintEvent *event) { RkImage img(width(), height()); RkPainter painter(&img); painter.fillRect(rect(), background()); RkPen normalPen = painter.pen(); auto hightlightPen = normalPen; hightlightPen.setColor({200, 200, 200}); normalPen.setColor({150, 150, 150}); painter.setPen(normalPen); RkPen selectedPen = normalPen; selectedPen.setColor({255, 255, 255}); auto font = painter.font(); font.setSize(lineHeight); painter.setFont(font); int lineYPos = 0; auto index = offsetIndex; int line = 0; while(index >= 0 && (static_cast(index) < filesList.size()) && (static_cast(index - offsetIndex) < visibleLines)) { auto fileName = filesList[index].filename().string(); auto font = painter.font(); if (std::filesystem::is_directory(filesList[index])) font.setWeight(RkFont::Weight::Bold); else font.setWeight(RkFont::Weight::Normal); painter.setFont(font); if (selectedFileIndex == index) painter.setPen(selectedPen); else if (hightlightLine == line) painter.setPen(hightlightPen); else painter.setPen(normalPen); if (index == 0) fileName = "[ " + fileName + ".. ]"; painter.drawText(RkRect(10, lineYPos, width() - 5, lineHeight), fileName, Rk::Alignment::AlignLeft); lineYPos += lineHeight + lineSacing; index++; line++; } RkPainter paint(this); paint.drawImage(img, 0, 0); } void FilesView::mouseButtonPressEvent(RkMouseEvent *event) { if (event->button() == RkMouseEvent::ButtonType::WheelUp) { onLineUp(); return; } else if (event->button() == RkMouseEvent::ButtonType::WheelDown) { onLineDown(); return; } auto line = getLine(event->x(), event->y()); if (line > -1) { selectedFileIndex = offsetIndex + line; std::string file = getSelectedFile(); if (!std::filesystem::is_directory(file)) action currentFileChanged(file); update(); } } void FilesView::mouseDoubleClickEvent(RkMouseEvent *event) { if (event->button() == RkMouseEvent::ButtonType::WheelUp) { onLineUp(); return; } else if(event->button() == RkMouseEvent::ButtonType::WheelDown) { onLineDown(); return; } auto line = getLine(event->x(), event->y()); if (line > -1) { selectedFileIndex = offsetIndex + line; openSelectedFile(); } } void FilesView::mouseMoveEvent(RkMouseEvent *event) { if (event->x() > width() - scrollBarWidth) { hightlightLine = -1; update(); return; } auto line = hightlightLine; hightlightLine = getLine(event->x(), event->y()); if (hightlightLine != line) update(); } void FilesView::keyPressEvent(RkKeyEvent *event) { if (!filesList.empty() && (event->key() == Rk::Key::Key_Down || event->key() == Rk::Key::Key_Up)) { event->key() == Rk::Key::Key_Down ? selectedFileIndex++ : selectedFileIndex--; if (selectedFileIndex > -1 && static_cast(selectedFileIndex) > filesList.size() - 1) selectedFileIndex = filesList.size() - 1; else if (selectedFileIndex < 0) selectedFileIndex = 0; if (selectedFileIndex < offsetIndex || static_cast(selectedFileIndex) > offsetIndex + visibleLines - 1) { offsetIndex = selectedFileIndex; } update(); updateScrollBar(); return; } if (event->key() == Rk::Key::Key_Return) openSelectedFile(); } std::string FilesView::getSelectedFile() const { if (!filesList.empty() && selectedFileIndex > -1 && static_cast(selectedFileIndex) < filesList.size() && !filesList[selectedFileIndex].empty()) { return filesList[selectedFileIndex]; } return ""; } void FilesView::openSelectedFile() { std::string file = getSelectedFile(); if (!file.empty()) { if (!std::filesystem::is_directory(file)) action openFile(filesList[selectedFileIndex].string()); else loadCurrentDirectory(); update(); } } int FilesView::getLine(int x, int y) const { if (x > 0 && x < width() - scrollBarWidth && y > 0 && y < height()) { decltype(filesList.size()) line = y / (lineHeight + lineSacing); if (line <= filesList.size() - static_cast(offsetIndex + 1)) return line; } return -1; } std::string FilesView::selectedFile() const { if (selectedFileIndex > -1) return filesList[selectedFileIndex].string(); return std::string(); } void FilesView::onLineUp() { offsetIndex--; if (offsetIndex < 0) offsetIndex = 0; updateScrollBar(); update(); } void FilesView::onLineDown() { if (offsetIndex + visibleLines < filesList.size()) offsetIndex++; updateScrollBar(); update(); } void FilesView::setFilters(const std::vector &filters) { fileFilters = filters; } FileDialog::FileDialog(GeonkickWidget *parent, FileDialog::Type type, const std::string& title) : GeonkickWidget(parent, type == FileDialog::Type::Browse ? Rk::WindowFlags::Widget : Rk::WindowFlags::Dialog) , dialogType{type} , filesView{nullptr} , pathLabel{nullptr} , status{AcceptStatus::Cancel} , shortcutDirectoriesModel{new PathListModel(this)} , shortcutDirectoriesView{new RkList(this, shortcutDirectoriesModel)} { setTitle(title); setFixedSize(600, 400); setBorderWidth(1); setBorderColor(40, 40, 40); filesView = new FilesView(this); RK_ACT_BIND(filesView, openFile, RK_ACT_ARGS(const std::string &), this, onAccept()); RK_ACT_BIND(filesView, currentFileChanged, RK_ACT_ARGS(const std::string &file), this, currentFileChanged(file)); RK_ACT_BIND(filesView, currentPathChanged, RK_ACT_ARGS(const std::string &pathName), this, onPathChanged(pathName)); RK_ACT_BIND(filesView, currentPathChanged, RK_ACT_ARGS(const std::string &pathName), this, directoryChanged(pathName)); RK_ACT_BIND(shortcutDirectoriesModel, itemSelected, RK_ACT_ARGS(RkModelItem item), filesView, setCurrentPath(std::get(item.data(static_cast(PathListModel::PathListDataType::Path))))); shortcutDirectoriesView->setBorderColor(40, 40, 40); shortcutDirectoriesView->setBorderWidth(1); shortcutDirectoriesView->setBackgroundColor({50, 50, 50}); shortcutDirectoriesView->setPosition(0, filesView->y()); shortcutDirectoriesView->setSize(100 ,filesView->height()); shortcutDirectoriesView->show(); pathLabel = new RkLabel(this, "Path: " + filesView->getCurrentPath()); pathLabel->setBackgroundColor(background()); pathLabel->setFixedSize(filesView->width(), 20); pathLabel->setPosition(filesView->x(), 15); pathLabel->setFont(font()); pathLabel->setTextColor(textColor()); pathLabel->show(); auto buttomContainer = new RkContainer(this); buttomContainer->setSize({width(), 30}); buttomContainer->setPosition({5, height() - buttomContainer->height() - 5}); if (dialogType != Type::Browse) { auto acceptButton = new GeonkickButton(this); acceptButton->setFixedSize(90, 30); if (dialogType == Type::Save) acceptButton->setUnpressedImage(RkImage(90, 30, RK_IMAGE_RC(save_active))); else acceptButton->setUnpressedImage(RkImage(90, 30, RK_IMAGE_RC(open_active))); RK_ACT_BIND(acceptButton, toggled, RK_ACT_ARGS(bool pressed), this, onAccept()); acceptButton->show(); buttomContainer->addSpace(10, Rk::Alignment::AlignRight); buttomContainer->addWidget(acceptButton, Rk::Alignment::AlignRight); auto cancelButton = new GeonkickButton(this); cancelButton->setFixedSize(90, 30); cancelButton->setUnpressedImage(RkImage(90, 30, RK_IMAGE_RC(cancel))); RK_ACT_BIND(cancelButton, toggled, RK_ACT_ARGS(bool pressed), this, onCancel()); cancelButton->show(); buttomContainer->addSpace(5); buttomContainer->addWidget(cancelButton, Rk::Alignment::AlignRight); } if (dialogType == Type::Save) { fileNameEdit = new RkLineEdit(this); fileNameEdit->setFont(font()); fileNameEdit->setSize(width() - 200, 20); fileNameEdit->show(); RK_ACT_BIND(fileNameEdit, enterPressed, RK_ACT_ARGS(), this, onAccept()); buttomContainer->addWidget(fileNameEdit); } show(); } void FileDialog::onPathChanged(const std::string &pathName) { if (!pathName.empty()) pathLabel->setText("Path: " + pathName); } void FileDialog::onAccept() { status = AcceptStatus::Accept; switch (dialogType) { case Type::Open: if (!filesView->selectedFile().empty()) { pathSelected = filesView->selectedFile(); action selectedFile(pathSelected); close(); } break; case Type::Save: if (!fileNameEdit->text().empty()) { pathSelected = filesView->getCurrentPath() / std::filesystem::path(fileNameEdit->text()); action selectedFile(pathSelected); close(); } else if (!filesView->selectedFile().empty() && !std::filesystem::is_directory(filesView->selectedFile())) { pathSelected = filesView->selectedFile(); action selectedFile(pathSelected); close(); } break; case Type::Browse: if (!filesView->selectedFile().empty()) { pathSelected = filesView->selectedFile(); action selectedFile(pathSelected); } break; default: return; } } void FileDialog::onCancel() { status = AcceptStatus::Cancel; close(); } void FileDialog::closeEvent(RkCloseEvent *event) { status = AcceptStatus::Cancel; RkWidget::closeEvent(event); } std::string FileDialog::currentDirectory() const { return filesView->getCurrentPath(); } void FileDialog::setCurrentDirectoy(const std::string &path) { filesView->setCurrentPath(path); } std::string FileDialog::filePath() const { return pathSelected; } FileDialog::AcceptStatus FileDialog::acceptStatus() const { return status; } void FileDialog::setFilters(const std::vector &filters) { filesView->setFilters(filters); } void FileDialog::setHomeDirectory(const std::string &path) { shortcutDirectoriesModel->setHomeDirectory(path); } geonkick-2.10.2/src/file_dialog.h000066400000000000000000000120421451017372400165670ustar00rootroot00000000000000/** * File name: file_dialog.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_FILE_DIALOG_H #define GEONKICK_FILE_DIALOG_H #include "geonkick_widget.h" class RkLineEdit; class RkPaintEvent; class RkMouseEvent; class RkCloseEvent; class GeonkickButton; class RkLabel; class GeonkickSlider; class PathListModel; class RkList; class FilesView: public GeonkickWidget { public: explicit FilesView(GeonkickWidget *parent); std::string selectedFile() const; std::string getCurrentPath() const; void setCurrentPath(const std::string &path); RK_DECL_ACT(openFile, openFile(const std::string &fileName), RK_ARG_TYPE(const std::string &), RK_ARG_VAL(fileName)); RK_DECL_ACT(currentPathChanged, currentPathChanged(const std::string &pathName), RK_ARG_TYPE(const std::string &), RK_ARG_VAL(pathName)); RK_DECL_ACT(fileSelected, fileSelected(const std::string &file), RK_ARG_TYPE(const std::string&), RK_ARG_VAL(file)); RK_DECL_ACT(currentFileChanged, currentFileChanged(const std::string &file), RK_ARG_TYPE(const std::string&), RK_ARG_VAL(file)); void setFilters(const std::vector &filters); protected: void createScrollBar(); void showScrollBar(bool b); void paintWidget(RkPaintEvent *event) override; void mouseButtonPressEvent(RkMouseEvent *event) override; void mouseDoubleClickEvent(RkMouseEvent *event) override; void mouseMoveEvent(RkMouseEvent *event) override; void keyPressEvent(RkKeyEvent *event) override; void loadCurrentDirectory(); int getLine(int x, int y) const; void onLineUp(); void onLineDown(); void openSelectedFile(); void scrollBarChanged(int val); void updateScrollBar(); std::string getSelectedFile() const; private: std::vector filesList; int selectedFileIndex; int hightlightLine; int offsetIndex; std::filesystem::path currentPath; int lineHeight; int lineSacing; decltype(filesList.size()) visibleLines; GeonkickButton *topScrollBarButton; GeonkickButton *bottomScrollBarButton; int scrollBarWidth; GeonkickSlider *scrollBar; bool isScrollBarVisible; std::vector fileFilters; }; class FileDialog: public GeonkickWidget { public: enum class Type: int { Save, Open, Browse }; enum class AcceptStatus: int { Cancel, Accept }; explicit FileDialog(GeonkickWidget *parent, FileDialog::Type type = FileDialog::Type::Open, const std::string& title = std::string()); RK_DECL_ACT(selectedFile, selectedFile(const std::string &file), RK_ARG_TYPE(const std::string&), RK_ARG_VAL(file)); RK_DECL_ACT(currentFileChanged, currentFileChanged(const std::string &file), RK_ARG_TYPE(const std::string&), RK_ARG_VAL(file)); RK_DECL_ACT(directoryChanged, directoryChanged(const std::string &path), RK_ARG_TYPE(const std::string&), RK_ARG_VAL(path)); std::string currentDirectory() const; void setCurrentDirectoy(const std::string &path); std::string filePath() const; AcceptStatus acceptStatus() const; void setFilters(const std::vector &filters); void setHomeDirectory(const std::string &path); protected: void onAccept(); void onCancel(); void onPathChanged(const std::string &pathName); void closeEvent(RkCloseEvent *event) final; private: RkLineEdit *fileNameEdit; Type dialogType; FilesView *filesView; RkLabel *pathLabel; std::string pathSelected; AcceptStatus status; PathListModel* shortcutDirectoriesModel; RkList *shortcutDirectoriesView; }; #endif // GEONKICK_FILE_DIALOG_H geonkick-2.10.2/src/filter.cpp000066400000000000000000000250021451017372400161510ustar00rootroot00000000000000/** * File name: filter.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "filter.h" #include "geonkick_button.h" #include "knob.h" #include "ViewState.h" #include RK_DECLARE_IMAGE_RC(knob); RK_DECLARE_IMAGE_RC(knob_bk_image); RK_DECLARE_IMAGE_RC(hboxbk_filter); RK_DECLARE_IMAGE_RC(filter_enabled); RK_DECLARE_IMAGE_RC(filter_enabled_hover); RK_DECLARE_IMAGE_RC(filter_disabled); RK_DECLARE_IMAGE_RC(knob_bk_50x50); RK_DECLARE_IMAGE_RC(knob_50x50); RK_DECLARE_IMAGE_RC(filter_type_lp); RK_DECLARE_IMAGE_RC(filter_type_hp); RK_DECLARE_IMAGE_RC(filter_type_bp); RK_DECLARE_IMAGE_RC(filter_type_lp_checked); RK_DECLARE_IMAGE_RC(filter_type_hp_checked); RK_DECLARE_IMAGE_RC(filter_type_bp_checked); RK_DECLARE_IMAGE_RC(filter_type_lp_hover); RK_DECLARE_IMAGE_RC(filter_type_hp_hover); RK_DECLARE_IMAGE_RC(filter_type_bp_hover); RK_DECLARE_IMAGE_RC(filter_cutoff_button_on); RK_DECLARE_IMAGE_RC(fl_cutoff_button_on); RK_DECLARE_IMAGE_RC(fl_cutoff_button_hover); RK_DECLARE_IMAGE_RC(fl_cutoff_button_off); Filter::Filter(GeonkickWidget *parent, Envelope::Category category) : GeonkickWidget(parent) , filterCheckbox{nullptr} , cutOffKnob{nullptr} , resonanceKnob{nullptr} , lpFilterButton{nullptr} , hpFilterButton{nullptr} , bpFilterButton{nullptr} , envelopeCategory{category} { setBackgroundImage(RkImage(224, 125, RK_IMAGE_RC(hboxbk_filter))); setFixedSize(224, 125); filterCheckbox = new GeonkickButton(this); filterCheckbox->setCheckable(true); filterCheckbox->setBackgroundColor(68, 68, 70); filterCheckbox->setSize(32, 21); filterCheckbox->setPosition(width() / 2 - filterCheckbox->width() / 2, 10); filterCheckbox->setImage(RkImage(filterCheckbox->size(), RK_IMAGE_RC(filter_enabled)), RkButton::State::Pressed); filterCheckbox->setImage(RkImage(filterCheckbox->size(), RK_IMAGE_RC(filter_disabled)), RkButton::State::Unpressed); filterCheckbox->setImage(RkImage(filterCheckbox->size(), RK_IMAGE_RC(filter_enabled_hover)), RkButton::State::PressedHover); filterCheckbox->setImage(RkImage(filterCheckbox->size(), RK_IMAGE_RC(filter_enabled_hover)), RkButton::State::UnpressedHover); RK_ACT_BIND(filterCheckbox, toggled, RK_ACT_ARGS(bool b), this, enabled(b)); cutOffKnob = new Knob(this); cutOffKnob->setRangeType(Knob::RangeType::Logarithmic); cutOffKnob->setPosition((224 / 2 - 80) / 2, (125 - 80) / 2); cutOffKnob->setFixedSize(80, 80); cutOffKnob->setKnobBackgroundImage(RkImage(80, 80, RK_IMAGE_RC(knob_bk_image))); cutOffKnob->setKnobImage(RkImage(70, 70, RK_IMAGE_RC(knob))); cutOffKnob->setRange(20, 20000); RK_ACT_BIND(cutOffKnob, valueUpdated, RK_ACT_ARGS(double val), this, cutOffChanged(val)); auto cutoffEnvelopeButton = new GeonkickButton(this); cutoffEnvelopeButton->setPressed(viewState()->getEnvelopeType() == Envelope::Type::FilterCutOff && viewState()->getEnvelopeCategory() == envelopeCategory); cutoffEnvelopeButton->setFixedSize(63, 21); cutoffEnvelopeButton->setPosition(cutOffKnob->x() + cutOffKnob->width() / 2 - cutoffEnvelopeButton->width() / 2, cutOffKnob->y() + cutOffKnob->height() - 3); cutoffEnvelopeButton->setImage(RkImage(cutoffEnvelopeButton->size(), RK_IMAGE_RC(fl_cutoff_button_off)), RkButton::State::Unpressed); cutoffEnvelopeButton->setImage(RkImage(cutoffEnvelopeButton->size(), RK_IMAGE_RC(fl_cutoff_button_on)), RkButton::State::Pressed); cutoffEnvelopeButton->setImage(RkImage(cutoffEnvelopeButton->size(), RK_IMAGE_RC(fl_cutoff_button_hover)), RkButton::State::PressedHover); cutoffEnvelopeButton->setImage(RkImage(cutoffEnvelopeButton->size(), RK_IMAGE_RC(fl_cutoff_button_hover)), RkButton::State::UnpressedHover); RK_ACT_BIND(cutoffEnvelopeButton, pressed, RK_ACT_ARGS(), viewState(), setEnvelope(envelopeCategory, Envelope::Type::FilterCutOff)); RK_ACT_BIND(viewState(), envelopeChanged, RK_ACT_ARGS(Envelope::Category category, Envelope::Type envelope), cutoffEnvelopeButton, setPressed(envelope == Envelope::Type::FilterCutOff && category == envelopeCategory)); resonanceKnob = new Knob(this); resonanceKnob->setRangeType(Knob::RangeType::Logarithmic); int w = 60; int h = 60; resonanceKnob->setPosition(224 / 2 + (224 / 2 - w) / 2, (125 - h) / 4 - 2); resonanceKnob->setFixedSize(w, h); resonanceKnob->setKnobBackgroundImage(RkImage(w, h, RK_IMAGE_RC(knob_bk_50x50))); resonanceKnob->setKnobImage(RkImage(50, 50, RK_IMAGE_RC(knob_50x50))); resonanceKnob->setRange(0.01, 10); RK_ACT_BIND(resonanceKnob, valueUpdated, RK_ACT_ARGS(double val), this, resonanceChanged(val)); int x = resonanceKnob->x() + resonanceKnob->width() / 2 - (3 * 25 + 8) / 2 ; int y = height() - 30; lpFilterButton = new GeonkickButton(this); lpFilterButton->setBackgroundColor(background()); lpFilterButton->setFixedSize(25, 18); lpFilterButton->setPosition(x, y); lpFilterButton->setImage(RkImage(lpFilterButton->size(), RK_IMAGE_RC(filter_type_lp)), RkButton::State::Unpressed); lpFilterButton->setImage(RkImage(lpFilterButton->size(), RK_IMAGE_RC(filter_type_lp_checked)), RkButton::State::Pressed); lpFilterButton->setImage(RkImage(lpFilterButton->size(), RK_IMAGE_RC(filter_type_lp_hover)), RkButton::State::UnpressedHover); RK_ACT_BIND(lpFilterButton, toggled, RK_ACT_ARGS(bool b), this, setFilterType(GeonkickApi::FilterType::LowPass, b)); bpFilterButton = new GeonkickButton(this); bpFilterButton->setBackgroundColor(background()); bpFilterButton->setFixedSize(25, 18); bpFilterButton->setPosition(lpFilterButton->x() + lpFilterButton->width() + 4, lpFilterButton->y()); bpFilterButton->setImage(RkImage(bpFilterButton->size(), RK_IMAGE_RC(filter_type_bp)), RkButton::State::Unpressed); bpFilterButton->setImage(RkImage(bpFilterButton->size(), RK_IMAGE_RC(filter_type_bp_checked)), RkButton::State::Pressed); bpFilterButton->setImage(RkImage(bpFilterButton->size(), RK_IMAGE_RC(filter_type_bp_hover)), RkButton::State::UnpressedHover); RK_ACT_BIND(bpFilterButton, toggled, RK_ACT_ARGS(bool b), this, setFilterType(GeonkickApi::FilterType::BandPass, b)); hpFilterButton = new GeonkickButton(this); hpFilterButton->setBackgroundColor(background()); hpFilterButton->setFixedSize(25, 18); hpFilterButton->setPosition(bpFilterButton->x() + bpFilterButton->width() + 4, bpFilterButton->y()); hpFilterButton->setImage(RkImage(hpFilterButton->size(), RK_IMAGE_RC(filter_type_hp)), RkButton::State::Unpressed); hpFilterButton->setImage(RkImage(hpFilterButton->size(), RK_IMAGE_RC(filter_type_hp_checked)), RkButton::State::Pressed); hpFilterButton->setImage(RkImage(hpFilterButton->size(), RK_IMAGE_RC(filter_type_hp_hover)), RkButton::State::UnpressedHover); RK_ACT_BIND(hpFilterButton, toggled, RK_ACT_ARGS(bool b), this, setFilterType(GeonkickApi::FilterType::HighPass, b)); show(); } void Filter::enable(bool b) { filterCheckbox->setPressed(b); } bool Filter::isEnabled() const { return filterCheckbox->isPressed(); } void Filter::setCutOff(double val) { cutOffKnob->setCurrentValue(val); } double Filter::cutOff() const { return cutOffKnob->getValue(); } void Filter::setResonance(double val) { resonanceKnob->setCurrentValue(val); } double Filter::resonance() const { return resonanceKnob->getValue(); } void Filter::setType(GeonkickApi::FilterType type) { lpFilterButton->setPressed(type == GeonkickApi::FilterType::LowPass); hpFilterButton->setPressed(type == GeonkickApi::FilterType::HighPass); bpFilterButton->setPressed(type == GeonkickApi::FilterType::BandPass); } void Filter::setFilterType(GeonkickApi::FilterType type, bool b) { if (b) { setType(type); typeChanged(type); } } GeonkickApi::FilterType Filter::type() const { if (lpFilterButton->isPressed()) return GeonkickApi::FilterType::LowPass; else if (hpFilterButton->isPressed()) return GeonkickApi::FilterType::HighPass; else return GeonkickApi::FilterType::BandPass; } void Filter::setCutOffRange(double from, double to) { cutOffKnob->setRange(from, to); } void Filter::setResonanceRange(double from, double to) { resonanceKnob->setRange(from, to); } void Filter::mouseButtonPressEvent(RkMouseEvent *event) { if (event->button() == RkMouseEvent::ButtonType::Right || event->button() == RkMouseEvent::ButtonType::Left) { bool b = !isEnabled(); enable(b); enabled(b); } } geonkick-2.10.2/src/filter.h000066400000000000000000000046421451017372400156250ustar00rootroot00000000000000/** * File name: filter.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_FILTER_H #define GEONKICK_FILTER_H #include "geonkick_widget.h" #include "geonkick_api.h" #include "envelope.h" class GeonkickButton; class Knob; class Filter: public GeonkickWidget { public: explicit Filter(GeonkickWidget *parent, Envelope::Category category); void enable(bool b); bool isEnabled() const; void setCutOff(double val); double cutOff() const; void setResonance(double val); double resonance() const; void setType(GeonkickApi::FilterType type); void setCutOffRange(double from, double to); void setResonanceRange(double from, double to); GeonkickApi::FilterType type() const; RK_DECL_ACT(enabled, enabled(bool b), RK_ARG_TYPE(bool), RK_ARG_VAL(b)); RK_DECL_ACT(cutOffChanged, cutOffChanged(double val), RK_ARG_TYPE(double), RK_ARG_VAL(val)); RK_DECL_ACT(resonanceChanged, resonanceChanged(double val), RK_ARG_TYPE(double), RK_ARG_VAL(val)); RK_DECL_ACT(typeChanged, typeChanged(GeonkickApi::FilterType type), RK_ARG_TYPE(GeonkickApi::FilterType), RK_ARG_VAL(type)); protected: void setFilterType(GeonkickApi::FilterType type, bool b); void mouseButtonPressEvent(RkMouseEvent *event) final; private: GeonkickButton *filterCheckbox; Knob *cutOffKnob; Knob *resonanceKnob; GeonkickButton *lpFilterButton; GeonkickButton *hpFilterButton; GeonkickButton *bpFilterButton; Envelope::Category envelopeCategory; }; #endif // GEONKICK_FILTER_H geonkick-2.10.2/src/general_envelope.cpp000066400000000000000000000052151451017372400202020ustar00rootroot00000000000000/** * File name: general_envelope.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "general_envelope.h" #include "oscillator.h" GeneralEnvelope::GeneralEnvelope(GeonkickApi *api, const RkRect &area) : Envelope(area) , geonkickApi{api} { removeSupportedType(Envelope::Type::Frequency); removeSupportedType(Envelope::Type::PitchShift); RK_ACT_BIND(geonkickApi, kickLengthUpdated, RK_ACT_ARGS(double val), this, envelopeUpdated()); RK_ACT_BIND(geonkickApi, kickAmplitudeUpdated, RK_ACT_ARGS(double val), this, envelopeUpdated()); setType(Envelope::Type::Amplitude); setPoints(geonkickApi->getKickEnvelopePoints(type())); } void GeneralEnvelope::pointAddedEvent(double x, double y) { geonkickApi->addKickEnvelopePoint(type(), x, y); } void GeneralEnvelope::pointUpdatedEvent(unsigned int index, double x, double y) { geonkickApi->updateKickEnvelopePoint(type(), index, x, y); } void GeneralEnvelope::pointRemovedEvent(unsigned int index) { geonkickApi->removeKickEnvelopePoint(type(), index); } double GeneralEnvelope::envelopeLength(void) const { return geonkickApi->kickLength(); } void GeneralEnvelope::setEnvelopeLengh(double len) { geonkickApi->setKickLength(len); } double GeneralEnvelope::envelopeAmplitude(void) const { if (type() == Envelope::Type::Amplitude) return geonkickApi->kickAmplitude(); else if (type() == Envelope::Type::FilterCutOff) return geonkickApi->kickFilterFrequency(); else if (type() == Envelope::Type::DistortionDrive) return geonkickApi->getDistortionDrive() / (pow(10, 36.0 / 20)); else if (type() == Envelope::Type::DistortionVolume) return geonkickApi->getDistortionVolume(); return 0; } void GeneralEnvelope::updatePoints() { setPoints(geonkickApi->getKickEnvelopePoints(type())); } geonkick-2.10.2/src/general_envelope.h000066400000000000000000000027461451017372400176550ustar00rootroot00000000000000/** * File name: general_envelope.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GKICK_GENERAL_ENVELOPE_H #define GKICK_GENERAL_ENVELOPE_H #include "envelope.h" class Oscillator; class GeonkickApi; class GeneralEnvelope: public Envelope { public: GeneralEnvelope(GeonkickApi *api, const RkRect &area); double envelopeLength(void) const final; void setEnvelopeLengh(double len) final; void updatePoints() final; protected: void pointAddedEvent(double x, double y) final; void pointUpdatedEvent(unsigned int index, double x, double y) final; void pointRemovedEvent(unsigned int index) final; double envelopeAmplitude(void) const final; private: GeonkickApi *geonkickApi; }; #endif geonkick-2.10.2/src/general_group_box.cpp000066400000000000000000000151461451017372400203750ustar00rootroot00000000000000/** * File name: general_group_box.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "geonkick_api.h" #include "general_group_box.h" #include "knob.h" #include "geonkick_button.h" #include "filter.h" #include "ViewState.h" #include RK_DECLARE_IMAGE_RC(hboxbk_ampl_env); RK_DECLARE_IMAGE_RC(hboxbk_filter); RK_DECLARE_IMAGE_RC(knob_bk_image); RK_DECLARE_IMAGE_RC(knob); RK_DECLARE_IMAGE_RC(general_groupbox_label); RK_DECLARE_IMAGE_RC(osc_ampl_button_off); RK_DECLARE_IMAGE_RC(osc_ampl_button_on); RK_DECLARE_IMAGE_RC(osc_ampl_button_hover); GeneralGroupBox::GeneralGroupBox(GeonkickWidget *parent, GeonkickApi *api) : GeonkickGroupBox(parent) , geonkickApi{api} , filterBox{nullptr} , kickAmplitudeKnob{nullptr} , kickLengthKnob{nullptr} { setFixedSize(224, 380); auto label = new RkLabel(this); label->setBackgroundColor(background()); label->setPosition(10, 0); label->setSize(45, 21); label->setImage(RkImage(label->size(), RK_IMAGE_RC(general_groupbox_label))); label->show(); createAplitudeEnvelopeHBox(); createFilterHBox(); updateGui(); } void GeneralGroupBox::createAplitudeEnvelopeHBox() { auto amplitudeEnvelopeBox = new GeonkickWidget(this); amplitudeEnvelopeBox->setPosition(0, 25); amplitudeEnvelopeBox->setFixedSize(224, 125); amplitudeEnvelopeBox->setBackgroundImage(RkImage(224, 125, RK_IMAGE_RC(hboxbk_ampl_env))); amplitudeEnvelopeBox->show(); kickAmplitudeKnob = new Knob(amplitudeEnvelopeBox); kickAmplitudeKnob->setFixedSize(80, 80); kickAmplitudeKnob->setPosition((224 / 2 - 80) / 2, (125 - 80) / 2); kickAmplitudeKnob->setKnobBackgroundImage(RkImage(80, 80, RK_IMAGE_RC(knob_bk_image))); kickAmplitudeKnob->setKnobImage(RkImage(70, 70, RK_IMAGE_RC(knob))); kickAmplitudeKnob->setRange(0, 1.0); kickAmplitudeKnob->show(); RK_ACT_BIND(kickAmplitudeKnob, valueUpdated, RK_ACT_ARGS(double val), geonkickApi, setKickAmplitude(val)); auto amplEnvelopeButton = new GeonkickButton(amplitudeEnvelopeBox); amplEnvelopeButton->setPressed(viewState()->getEnvelopeType() == Envelope::Type::Amplitude && Envelope::Category::General == viewState()->getEnvelopeCategory()); amplEnvelopeButton->setFixedSize(63, 21); amplEnvelopeButton->setPosition(kickAmplitudeKnob->x() + kickAmplitudeKnob->width() / 2 - amplEnvelopeButton->width() / 2, kickAmplitudeKnob->y() + kickAmplitudeKnob->height() - 3); amplEnvelopeButton->setImage(RkImage(amplEnvelopeButton->size(), RK_IMAGE_RC(osc_ampl_button_off)), RkButton::State::Unpressed); amplEnvelopeButton->setImage(RkImage(amplEnvelopeButton->size(), RK_IMAGE_RC(osc_ampl_button_on)), RkButton::State::Pressed); amplEnvelopeButton->setImage(RkImage(amplEnvelopeButton->size(), RK_IMAGE_RC(osc_ampl_button_hover)), RkButton::State::PressedHover); amplEnvelopeButton->setImage(RkImage(amplEnvelopeButton->size(), RK_IMAGE_RC(osc_ampl_button_hover)), RkButton::State::UnpressedHover); amplEnvelopeButton->show(); RK_ACT_BIND(amplEnvelopeButton, pressed, RK_ACT_ARGS(), viewState(), setEnvelope(Envelope::Category::General, Envelope::Type::Amplitude)); RK_ACT_BIND(amplitudeEnvelopeBox->viewState(), envelopeChanged, RK_ACT_ARGS(Envelope::Category category, Envelope::Type envelope), amplEnvelopeButton, setPressed(envelope == Envelope::Type::Amplitude && category == Envelope::Category::General)); kickLengthKnob = new Knob(amplitudeEnvelopeBox); kickLengthKnob->setFixedSize(80, 80); kickLengthKnob->setPosition(224 / 2 + (224 / 2 - 80) / 2, (125 - 80) / 2); kickLengthKnob->setKnobBackgroundImage(RkImage(80, 80, RK_IMAGE_RC(knob_bk_image))); kickLengthKnob->setKnobImage(RkImage(70, 70, RK_IMAGE_RC(knob))); kickLengthKnob->setRange(50, geonkickApi->kickMaxLength()); kickLengthKnob->show(); RK_ACT_BIND(kickLengthKnob, valueUpdated, RK_ACT_ARGS(double val), geonkickApi, setKickLength(val)); } void GeneralGroupBox::createFilterHBox() { filterBox = new Filter(this, Envelope::Category::General); filterBox->setCutOffRange(20, 20000); filterBox->setResonanceRange(1, 1000); filterBox->setPosition(0, 150); RK_ACT_BIND(filterBox, enabled, RK_ACT_ARGS(bool b), geonkickApi, enableKickFilter(b)); RK_ACT_BIND(filterBox, cutOffChanged, RK_ACT_ARGS(double val), geonkickApi, setKickFilterFrequency(val)); RK_ACT_BIND(filterBox, resonanceChanged, RK_ACT_ARGS(double val), geonkickApi, setKickFilterQFactor(val)); RK_ACT_BIND(filterBox, typeChanged, RK_ACT_ARGS(GeonkickApi::FilterType type), geonkickApi, setKickFilterType(type)); } void GeneralGroupBox::updateGui() { kickAmplitudeKnob->setCurrentValue(geonkickApi->kickAmplitude()); kickLengthKnob->setCurrentValue(geonkickApi->kickLength()); filterBox->enable(geonkickApi->isKickFilterEnabled()); filterBox->setCutOff(geonkickApi->kickFilterFrequency()); filterBox->setResonance(geonkickApi->kickFilterQFactor()); filterBox->setType(geonkickApi->kickFilterType()); } geonkick-2.10.2/src/general_group_box.h000066400000000000000000000027571451017372400200460ustar00rootroot00000000000000/** * File name: general_group_box.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GKICK_GENERAL_GROUP_BOX_H #define GKICK_GENERAL_GROUP_BOX_H #include "oscillator.h" #include "geonkick_groupbox.h" class Oscillator; class GeonkickApi; class GeonkickCheckbox; class Knob; class GeonkickButton; class Filter; class GeneralGroupBox: public GeonkickGroupBox { public: GeneralGroupBox(GeonkickWidget *parent, GeonkickApi *api); void updateGui(); protected: void createAplitudeEnvelopeHBox(); void createFilterHBox(); private: GeonkickApi* geonkickApi; Filter *filterBox; Knob *kickAmplitudeKnob; Knob *kickLengthKnob; }; #endif //GKICK_GENERAL_GROUP_BOX_H geonkick-2.10.2/src/geonkick_api.cpp000066400000000000000000001766231451017372400173270ustar00rootroot00000000000000/** * File name: geonkick_api.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "geonkick_api.h" #include "oscillator.h" #include "globals.h" #include "percussion_state.h" #include "kit_state.h" #include "preset.h" #include "preset_folder.h" #include "UiSettings.h" #include #include GeonkickApi::GeonkickApi(int sample_rate, InstanceType instance, geonkick *dsp) : geonkickApi{dsp} , instanceType{instance} , limiterLevelers{} , jackEnabled{false} , standaloneInstance{false} , eventQueue{nullptr} , currentLayer{Layer::Layer1} , kitName{"Unknown"} , kitAuthor{"Author"} , clipboardPercussion{nullptr} , uiSettings{std::make_unique()} , sampleRate{sample_rate} { setupPaths(); uiSettings->setSamplesBrowserPath(getSettings("GEONKICK_CONFIG/HOME_PATH")); } GeonkickApi::~GeonkickApi() { if (geonkickApi) geonkick_free(&geonkickApi); } void GeonkickApi::setInstanceType(InstanceType type) { instanceType = type; } GeonkickApi::InstanceType GeonkickApi::getInstanceType() const { return instanceType; } unsigned int GeonkickApi::getVersion() { return GEONKICK_VERSION; } void GeonkickApi::setEventQueue(RkEventQueue *queue) { std::lock_guard lock(apiMutex); eventQueue = queue; } bool GeonkickApi::initDSP() { if (!geonkickApi) { if (geonkick_create(&geonkickApi, sampleRate) != GEONKICK_OK) { GEONKICK_LOG_ERROR("can't create geonkick API"); return false; } } return true; } bool GeonkickApi::init() { if (!initDSP()) return false; loadPresets(); jackEnabled = geonkick_is_module_enabed(geonkickApi, GEONKICK_MODULE_JACK); geonkick_enable_synthesis(geonkickApi, false); auto n = getPercussionsNumber(); auto nChannels = numberOfChannels(); kickBuffers = std::vector>(n); for (decltype(n) i = 0; i < n; i++) { auto state = getDefaultPercussionState(); state->setId(i); state->setChannel(i % nChannels); setPercussionState(state); } setKitState(std::move(getDefaultKitState())); enablePercussion(0, true); addOrderedPercussionId(0); // Set the first the percussion by default to be controllable. geonkick_set_current_percussion(geonkickApi, 0); geonkick_enable_synthesis(geonkickApi, true); return true; } size_t GeonkickApi::numberOfChannels() { return geonkick_channels_number(); } std::unique_ptr GeonkickApi::getDefaultKitState() { return std::make_unique(); } std::shared_ptr GeonkickApi::getDefaultPercussionState() { std::shared_ptr state = std::make_shared(); state->setName("Default"); state->setId(0); state->setPlayingKey(-1); state->setChannel(0); state->setLimiterValue(1.0); state->tuneOutput(false); state->setKickLength(300); state->setKickAmplitude(0.8); state->enableKickFilter(false); state->setKickFilterFrequency(200); state->setKickFilterQFactor(10); state->setKickFilterType(GeonkickApi::FilterType::LowPass); std::vector envelope; envelope.push_back({0, 1}); envelope.push_back({1, 1}); state->setKickEnvelopePoints(GeonkickApi::EnvelopeType::Amplitude, envelope); state->setKickEnvelopePoints(GeonkickApi::EnvelopeType::FilterCutOff, envelope); state->setKickEnvelopePoints(GeonkickApi::EnvelopeType::DistortionDrive, envelope); state->setKickEnvelopePoints(GeonkickApi::EnvelopeType::DistortionVolume, envelope); state->enableCompressor(false); state->setCompressorAttack(0.01); state->setCompressorRelease(0.01); state->setCompressorThreshold(0); state->setCompressorRatio(1); state->setCompressorKnee(0); state->setCompressorMakeup(1); state->enableDistortion(false); state->setDistortionVolume(0.1); state->setDistortionInLimiter(1.0); state->setDistortionDrive(1.0); std::vector oscillators = { GeonkickApi::OscillatorType::Oscillator1, GeonkickApi::OscillatorType::Oscillator2, GeonkickApi::OscillatorType::Noise }; std::vector layers = { GeonkickApi::Layer::Layer1, GeonkickApi::Layer::Layer2, GeonkickApi::Layer::Layer3 }; for (auto layer: layers) { state->setLayerEnabled(layer, layer == Layer::Layer1); state->setLayerAmplitude(layer, 1.0); for (auto const &osc: oscillators) { int index = static_cast(osc) + GKICK_OSC_GROUP_SIZE * static_cast(layer); state->setOscillatorEnabled(index, osc == GeonkickApi::OscillatorType::Oscillator1); if (osc == GeonkickApi::OscillatorType::Noise) { state->setOscillatorFunction(index, GeonkickApi::FunctionType::NoiseWhite); state->setOscillatorSeed(index, 100); } else { state->setOscillatorFunction(index, GeonkickApi::FunctionType::Sine); state->setOscillatorPhase(index, 0); } state->setOscillatorAmplitue(index, 0.26); state->setOscillatorFrequency(index, 800); state->setOscillatorPitchShift(index, 0); state->setOscillatorFilterEnabled(index, false); state->setOscillatorFilterType(index, GeonkickApi::FilterType::LowPass); state->setOscillatorFilterCutOffFreq(index, 800); state->setOscillatorFilterFactor(index, 10); state->setOscillatorEnvelopePoints(index, envelope, GeonkickApi::EnvelopeType::Amplitude); if (osc != GeonkickApi::OscillatorType::Noise) { state->setOscillatorEnvelopePoints(index, envelope, GeonkickApi::EnvelopeType::Frequency); state->setOscillatorEnvelopePoints(index, envelope, GeonkickApi::EnvelopeType::PitchShift); std::vector env = envelope; env[0].setY(0.5); env[1].setY(0.5); state->setOscillatorEnvelopePoints(index, env, GeonkickApi::EnvelopeType::PitchShift); } state->setOscillatorEnvelopePoints(index, envelope, GeonkickApi::EnvelopeType::FilterCutOff); state->setOscillatorPitchShift(index, 12); } } return state; } void GeonkickApi::setPercussionState(const std::shared_ptr &state) { if (!state) return; geonkick_enable_synthesis(geonkickApi, false); geonkick_enable_percussion(geonkickApi, state->getId(), state->isEnabled()); auto currentId = currentPercussion(); geonkick_set_current_percussion(geonkickApi, state->getId()); setPercussionName(state->getId(), state->getName()); setPercussionPlayingKey(state->getId(), state->getPlayingKey()); setPercussionChannel(state->getId(), state->getChannel()); mutePercussion(state->getId(), state->isMuted()); soloPercussion(state->getId(), state->isSolo()); for (auto i = 0; i < 3; i++) { enbaleLayer(static_cast(i), state->isLayerEnabled(static_cast(i))); setLayerAmplitude(static_cast(i), state->getLayerAmplitude(static_cast(i))); } setLimiterValue(state->getLimiterValue()); tuneAudioOutput(state->getId(), state->isOutputTuned()); setKickLength(state->getKickLength()); setKickAmplitude(state->getKickAmplitude()); enableKickFilter(state->isKickFilterEnabled()); setKickFilterFrequency(state->getKickFilterFrequency()); setKickFilterQFactor(state->getKickFilterQFactor()); setKickFilterType(state->getKickFilterType()); setKickEnvelopePoints(GeonkickApi::EnvelopeType::Amplitude, state->getKickEnvelopePoints(GeonkickApi::EnvelopeType::Amplitude)); setKickEnvelopePoints(GeonkickApi::EnvelopeType::FilterCutOff, state->getKickEnvelopePoints(GeonkickApi::EnvelopeType::FilterCutOff)); setKickEnvelopePoints(GeonkickApi::EnvelopeType::DistortionDrive, state->getKickEnvelopePoints(GeonkickApi::EnvelopeType::DistortionDrive)); setKickEnvelopePoints(GeonkickApi::EnvelopeType::DistortionVolume, state->getKickEnvelopePoints(GeonkickApi::EnvelopeType::DistortionVolume)); for (auto i = 0; i < 3; i++) { setOscillatorState(static_cast(i), OscillatorType::Oscillator1, state); setOscillatorState(static_cast(i), OscillatorType::Oscillator2, state); setOscillatorState(static_cast(i), OscillatorType::Noise, state); } enableCompressor(state->isCompressorEnabled()); setCompressorAttack(state->getCompressorAttack()); setCompressorRelease(state->getCompressorRelease()); setCompressorThreshold(state->getCompressorThreshold()); setCompressorRatio(state->getCompressorRatio()); setCompressorKnee(state->getCompressorKnee()); setCompressorMakeup(state->getCompressorMakeup()); enableDistortion(state->isDistortionEnabled()); setDistortionInLimiter(state->getDistortionInLimiter()); setDistortionVolume(state->getDistortionVolume()); setDistortionDrive(state->getDistortionDrive()); geonkick_set_current_percussion(geonkickApi, currentId); geonkick_enable_synthesis(geonkickApi, true); } void GeonkickApi::setPercussionState(const std::string &data) { auto state = getDefaultPercussionState(); state->loadData(data); setPercussionState(state); } std::shared_ptr GeonkickApi::getPercussionState(size_t id) const { if (id == currentPercussion()) { return getPercussionState(); } else { auto tmpId = currentPercussion(); auto res = geonkick_set_current_percussion(geonkickApi, id); if (res != GEONKICK_OK) { geonkick_set_current_percussion(geonkickApi, tmpId); return getPercussionState(); } auto state = getPercussionState(); geonkick_set_current_percussion(geonkickApi, tmpId); return state; } } std::shared_ptr GeonkickApi::getPercussionState() const { auto state = std::make_shared(); state->setId(currentPercussion()); state->setName(getPercussionName(state->getId())); state->setLimiterValue(limiterValue()); state->tuneOutput(isAudioOutputTuned(state->getId())); state->setPlayingKey(getPercussionPlayingKey(state->getId())); state->setChannel(getPercussionChannel(state->getId())); state->setMute(isPercussionMuted(state->getId())); state->setSolo(isPercussionSolo(state->getId())); for (int i = 0; i < 3; i++) { state->setLayerEnabled(static_cast(i), isLayerEnabled(static_cast(i))); state->setLayerAmplitude(static_cast(i), getLayerAmplitude(static_cast(i))); } state->setKickLength(kickLength()); state->setKickAmplitude(kickAmplitude()); state->enableKickFilter(isKickFilterEnabled()); state->setKickFilterFrequency(kickFilterFrequency()); state->setKickFilterQFactor(kickFilterQFactor()); state->setKickFilterType(kickFilterType()); state->setKickEnvelopePoints(GeonkickApi::EnvelopeType::Amplitude, getKickEnvelopePoints(GeonkickApi::EnvelopeType::Amplitude)); state->setKickEnvelopePoints(GeonkickApi::EnvelopeType::FilterCutOff, getKickEnvelopePoints(GeonkickApi::EnvelopeType::FilterCutOff)); state->setKickEnvelopePoints(GeonkickApi::EnvelopeType::DistortionDrive, getKickEnvelopePoints(GeonkickApi::EnvelopeType::DistortionDrive)); state->setKickEnvelopePoints(GeonkickApi::EnvelopeType::DistortionVolume, getKickEnvelopePoints(GeonkickApi::EnvelopeType::DistortionVolume)); for (int i = 0; i < 3; i++) { getOscillatorState(static_cast(i), OscillatorType::Oscillator1, state); getOscillatorState(static_cast(i), OscillatorType::Oscillator2, state); getOscillatorState(static_cast(i), OscillatorType::Noise, state); } state->enableCompressor(isCompressorEnabled()); state->setCompressorAttack(getCompressorAttack()); state->setCompressorRelease(getCompressorRelease()); state->setCompressorThreshold(getCompressorThreshold()); state->setCompressorRatio(getCompressorRatio()); state->setCompressorKnee(getCompressorKnee()); state->setCompressorMakeup(getCompressorMakeup()); state->enableDistortion(isDistortionEnabled()); state->setDistortionInLimiter(getDistortionInLimiter()); state->setDistortionVolume(getDistortionVolume()); state->setDistortionDrive(getDistortionDrive()); return state; } void GeonkickApi::getOscillatorState(GeonkickApi::Layer layer, OscillatorType osc, const std::shared_ptr &state) const { auto temp = currentLayer; currentLayer = layer; auto index = static_cast(osc); state->setCurrentLayer(layer); state->setOscillatorEnabled(index, isOscillatorEnabled(index)); state->setOscillatorFunction(index, oscillatorFunction(index)); state->setOscillatorSample(index, getOscillatorSample(index)); if (osc != OscillatorType::Noise) state->setOscillatorPhase(index, oscillatorPhase(index)); if (osc == OscillatorType::Noise) state->setOscillatorSeed(index, oscillatorSeed(index)); state->setOscillatorAmplitue(index, oscillatorAmplitude(index)); state->setOscillatorFrequency(index, oscillatorFrequency(index)); state->setOscillatorPitchShift(index, oscillatorPitchShift(index)); state->setOscillatorFilterEnabled(index, isOscillatorFilterEnabled(index)); state->setOscillatorFilterType(index, getOscillatorFilterType(index)); state->setOscillatorFilterCutOffFreq(index, getOscillatorFilterCutOffFreq(index)); state->setOscillatorFilterFactor(index, getOscillatorFilterFactor(index)); auto points = oscillatorEvelopePoints(index, GeonkickApi::EnvelopeType::Amplitude); state->setOscillatorEnvelopePoints(index, points, GeonkickApi::EnvelopeType::Amplitude); if (osc != OscillatorType::Noise) { points = oscillatorEvelopePoints(index, GeonkickApi::EnvelopeType::Frequency); state->setOscillatorEnvelopePoints(index, points, GeonkickApi::EnvelopeType::Frequency); points = oscillatorEvelopePoints(index, GeonkickApi::EnvelopeType::PitchShift); state->setOscillatorEnvelopePoints(index, points, GeonkickApi::EnvelopeType::PitchShift); } points = oscillatorEvelopePoints(index, GeonkickApi::EnvelopeType::FilterCutOff); state->setOscillatorEnvelopePoints(index, points, GeonkickApi::EnvelopeType::FilterCutOff); state->setOscillatorAsFm(index, isOscillatorAsFm(index)); currentLayer = temp; } void GeonkickApi::setOscillatorState(GeonkickApi::Layer layer, OscillatorType oscillator, const std::shared_ptr &state) { auto temp = currentLayer; currentLayer = layer; auto osc = static_cast(oscillator); state->setCurrentLayer(layer); enableOscillator(osc, state->isOscillatorEnabled(osc)); setOscillatorFunction(osc, state->oscillatorFunction(osc)); setOscillatorSample(state->getOscillatorSample(osc), osc); if (oscillator != OscillatorType::Noise) setOscillatorPhase(osc, state->oscillatorPhase(osc)); if (oscillator == OscillatorType::Noise) setOscillatorSeed(osc, state->oscillatorSeed(osc)); setOscillatorAmplitude(osc, state->oscillatorAmplitue(osc)); if (oscillator != OscillatorType::Noise) { setOscillatorFrequency(osc, state->oscillatorFrequency(osc)); setOscillatorPitchShift(osc, state->oscillatorPitchShift(osc)); } enableOscillatorFilter(osc, state->isOscillatorFilterEnabled(osc)); setOscillatorFilterType(osc, state->oscillatorFilterType(osc)); setOscillatorFilterCutOffFreq(osc, state->oscillatorFilterCutOffFreq(osc)); setOscillatorFilterFactor(osc, state->oscillatorFilterFactor(osc)); setOscillatorEvelopePoints(osc, EnvelopeType::Amplitude, state->oscillatorEnvelopePoints(osc, EnvelopeType::Amplitude)); if (oscillator != OscillatorType::Noise) { setOscillatorEvelopePoints(osc, EnvelopeType::Frequency, state->oscillatorEnvelopePoints(osc, EnvelopeType::Frequency)); setOscillatorEvelopePoints(osc, EnvelopeType::PitchShift, state->oscillatorEnvelopePoints(osc, EnvelopeType::PitchShift)); } setOscillatorEvelopePoints(osc, EnvelopeType::FilterCutOff, state->oscillatorEnvelopePoints(osc, EnvelopeType::FilterCutOff)); setOscillatorAsFm(osc, state->isOscillatorAsFm(osc)); currentLayer = temp; } std::unique_ptr GeonkickApi::getKitState() const { auto kit = std::make_unique(); kit->setName(getKitName()); kit->setAuthor(getKitAuthor()); kit->setUrl(getKitUrl()); size_t i = 0; for (const auto &id : ordredPercussionIds()) { auto state = getPercussionState(id); state->setId(i); kit->addPercussion(state); GEONKICK_LOG_DEBUG("PER: " << state->getName() << ": id = " << state->getId()); i++; } return kit; } bool GeonkickApi::setKitState(const std::string &data) { auto state = std::make_unique(); state->fromJson(data); return setKitState(state); } bool GeonkickApi::setKitState(const std::unique_ptr &state) { auto n = getPercussionsNumber(); for (decltype(n) i = 0; i < n; i++) enablePercussion(i, false); setKitName(state->getName()); setKitAuthor(state->getAuthor()); setKitUrl(state->getUrl()); clearOrderedPercussionIds(); for (const auto &per: state->percussions()) { GEONKICK_LOG_DEBUG("PER: " << per->getName() << ": id = " << per->getId()); setPercussionState(per); addOrderedPercussionId(per->getId()); } if (!percussionIdList.empty()) setCurrentPercussion(percussionIdList.front()); else setCurrentPercussion(0); return true; } std::vector> GeonkickApi::oscillators(void) { std::vector> oscillators; size_t n = 0; geonkick_get_oscillators_number(geonkickApi, &n); for (decltype(n) i = 0; i < n; i++) oscillators.push_back(std::make_unique(this, static_cast(i % GKICK_OSC_GROUP_SIZE))); return oscillators; } std::vector GeonkickApi::oscillatorEvelopePoints(int oscillatorIndex, EnvelopeType envelope) const { gkick_real *buf = NULL; std::vector points; size_t npoints = 0; geonkick_osc_envelope_get_points(geonkickApi, getOscIndex(oscillatorIndex), static_cast(envelope), &buf, &npoints); for (decltype(npoints) i = 0; i < 2 * npoints; i += 2) points.push_back(RkRealPoint(buf[i], buf[i+1])); free(buf); return points; } void GeonkickApi::setOscillatorEvelopePoints(int index, EnvelopeType envelope, const std::vector &points) { if (points.empty()) return; std::vector data(2 * points.size() * sizeof(gkick_real), 0); gkick_real *buff = reinterpret_cast(data.data()); for (decltype(points.size()) i = 0; i < points.size(); i++) { buff[2 * i] = points[i].x(); buff[2 * i + 1] = points[i].y(); } geonkick_osc_envelope_set_points(geonkickApi, getOscIndex(index), static_cast(envelope), buff, points.size()); } void GeonkickApi::addOscillatorEnvelopePoint(int oscillatorIndex, EnvelopeType envelope, const RkRealPoint &point) { geonkick_osc_envelope_add_point(geonkickApi, getOscIndex(oscillatorIndex), static_cast(envelope), point.x(), point.y()); } void GeonkickApi::removeOscillatorEvelopePoint(int oscillatorIndex, EnvelopeType envelope, int pointIndex) { geonkick_osc_envelope_remove_point(geonkickApi, getOscIndex(oscillatorIndex), static_cast(envelope), pointIndex); } void GeonkickApi::updateOscillatorEvelopePoint(int oscillatorIndex, EnvelopeType envelope, int pointIndex, const RkRealPoint &point) { geonkick_osc_envelope_update_point(geonkickApi, getOscIndex(oscillatorIndex), static_cast(envelope), pointIndex, point.x(), point.y()); } void GeonkickApi::setOscillatorFunction(int oscillatorIndex, FunctionType function) { geonkick_set_osc_function(geonkickApi, getOscIndex(oscillatorIndex), static_cast(function)); } void GeonkickApi::setOscillatorPhase(int oscillatorIndex, gkick_real phase) { geonkick_set_osc_phase(geonkickApi, getOscIndex(oscillatorIndex), phase); } gkick_real GeonkickApi::oscillatorPhase(int oscillatorIndex) const { gkick_real phase = 0; geonkick_get_osc_phase(geonkickApi, getOscIndex(oscillatorIndex), &phase); return phase; } void GeonkickApi::setOscillatorSeed(int oscillatorIndex, int seed) { geonkick_set_osc_seed(geonkickApi, getOscIndex(oscillatorIndex), seed); } int GeonkickApi::oscillatorSeed(int oscillatorIndex) const { unsigned int seed = 0; geonkick_get_osc_seed(geonkickApi, getOscIndex(oscillatorIndex), &seed); return seed; } GeonkickApi::FunctionType GeonkickApi::oscillatorFunction(int oscillatorIndex) const { enum geonkick_osc_func_type function; geonkick_get_osc_function(geonkickApi, getOscIndex(oscillatorIndex), &function); return static_cast(function); } void GeonkickApi::setKickLength(double length) { geonkick_set_length(geonkickApi, length / 1000); } double GeonkickApi::kickLength(void) const { gkick_real length = 0; geonkick_get_length(geonkickApi, &length); return 1000 * length; } void GeonkickApi::setKickAmplitude(double amplitude) { geonkick_kick_set_amplitude(geonkickApi, amplitude); } double GeonkickApi::kickAmplitude() const { gkick_real amplitude = 0; geonkick_kick_get_amplitude(geonkickApi, &litude); return static_cast(amplitude); } std::vector GeonkickApi::getKickEnvelopePoints(EnvelopeType envelope) const { gkick_real *buf; std::vector points; size_t npoints = 0; geonkick_kick_envelope_get_points(geonkickApi, static_cast(envelope), &buf, &npoints); for (decltype(npoints) i = 0; i < 2 * npoints; i += 2) points.push_back(RkRealPoint(buf[i], buf[i+1])); if (buf) free(buf); return points; } void GeonkickApi::setKickEnvelopePoints(EnvelopeType envelope, const std::vector &points) { std::vector data(2 * points.size() * sizeof(gkick_real), 0); auto *buff = data.data(); for (decltype(points.size()) i = 0; i < points.size(); i++) { buff[2 * i] = points[i].x(); buff[2 * i + 1] = points[i].y(); } geonkick_kick_envelope_set_points(geonkickApi, static_cast(envelope), buff, points.size()); } void GeonkickApi::enableKickFilter(bool b) { geonkick_kick_filter_enable(geonkickApi, b); } bool GeonkickApi::isKickFilterEnabled() const { int enabled = 0; geonkick_kick_filter_is_enabled(geonkickApi, &enabled); return enabled; } void GeonkickApi::setKickFilterType(FilterType type) { geonkick_set_kick_filter_type(geonkickApi, static_cast(type)); } GeonkickApi::FilterType GeonkickApi::kickFilterType() const { enum gkick_filter_type type; geonkick_get_kick_filter_type(geonkickApi, &type); return static_cast(type); } void GeonkickApi::setKickFilterFrequency(double frequency) { geonkick_kick_set_filter_frequency(geonkickApi, frequency); } double GeonkickApi::kickFilterFrequency(void) const { gkick_real frequency; geonkick_kick_get_filter_frequency(geonkickApi, &frequency); return static_cast(frequency); } void GeonkickApi::setKickFilterQFactor(double factor) { geonkick_kick_set_filter_factor(geonkickApi, factor); } double GeonkickApi::kickFilterQFactor() const { gkick_real factor = 0; geonkick_kick_get_filter_factor(geonkickApi, &factor); return static_cast(factor); } void GeonkickApi::addKickEnvelopePoint(EnvelopeType envelope, double x, double y) { geonkick_kick_add_env_point(geonkickApi, static_cast(envelope), x, y); } void GeonkickApi::updateKickEnvelopePoint(EnvelopeType envelope, int index, double x, double y) { geonkick_kick_update_env_point(geonkickApi, static_cast(envelope), index, x, y); } void GeonkickApi::removeKickEnvelopePoint(EnvelopeType envelope, int pointIndex) { geonkick_kick_remove_env_point(geonkickApi, static_cast(envelope), pointIndex); } bool GeonkickApi::setOscillatorAmplitude(int oscillatorIndex, double amplitude) { auto res = geonkick_set_osc_amplitude(geonkickApi, getOscIndex(oscillatorIndex), amplitude); if (res != GEONKICK_OK) return false; return true; } void GeonkickApi::setOscillatorAsFm(int oscillatorIndex, bool b) { geonkick_osc_set_fm(geonkickApi, getOscIndex(oscillatorIndex), b); } bool GeonkickApi::isOscillatorAsFm(int oscillatorIndex) const { bool fm = false; geonkick_osc_is_fm(geonkickApi, getOscIndex(oscillatorIndex), &fm); return fm; } void GeonkickApi::enableOscillator(int oscillatorIndex, bool enable) { if (enable) geonkick_enable_oscillator(geonkickApi, getOscIndex(oscillatorIndex)); else geonkick_disable_oscillator(geonkickApi, getOscIndex(oscillatorIndex)); } double GeonkickApi::oscillatorAmplitude(int oscillatorIndex) const { gkick_real value = 0; if (geonkick_get_osc_amplitude(geonkickApi, getOscIndex(oscillatorIndex), &value) != GEONKICK_OK) return 0; return value; } bool GeonkickApi::setOscillatorFrequency(int oscillatorIndex, double frequency) { return geonkick_set_osc_frequency(geonkickApi, getOscIndex(oscillatorIndex), frequency) != GEONKICK_OK; } double GeonkickApi::oscillatorFrequency(int oscillatorIndex) const { gkick_real value = 0; if (geonkick_get_osc_frequency(geonkickApi, getOscIndex(oscillatorIndex), &value) != GEONKICK_OK) { return 0; } return value; } bool GeonkickApi::setOscillatorPitchShift(int oscillatorIndex, double semitones) { return geonkick_set_osc_pitch_shift(geonkickApi, getOscIndex(oscillatorIndex), semitones) == GEONKICK_OK; } double GeonkickApi::oscillatorPitchShift(int oscillatorIndex) const { gkick_real semitones = 0; if (geonkick_get_osc_pitch_shift(geonkickApi, getOscIndex(oscillatorIndex), &semitones) != GEONKICK_OK) { return 0; } return semitones; } bool GeonkickApi::isOscillatorEnabled(int oscillatorIndex) const { int enabled = 0; geonkick_is_oscillator_enabled(geonkickApi, getOscIndex(oscillatorIndex), &enabled); return enabled; } double GeonkickApi::kickMaxLength(void) const { gkick_real len = 0; geonkick_get_max_length(geonkickApi, &len); return len * 1000; } void GeonkickApi::enableOscillatorFilter(int oscillatorIndex, bool enable) { geonkick_enbale_osc_filter(geonkickApi, getOscIndex(oscillatorIndex), enable); } bool GeonkickApi::isOscillatorFilterEnabled(int oscillatorIndex) const { int enabled = false; geonkick_osc_filter_is_enabled(geonkickApi, getOscIndex(oscillatorIndex), &enabled); return enabled; } void GeonkickApi::setOscillatorFilterType(int oscillatorIndex, FilterType filter) { geonkick_set_osc_filter_type(geonkickApi, getOscIndex(oscillatorIndex), static_cast(filter)); } GeonkickApi::FilterType GeonkickApi::getOscillatorFilterType(int oscillatorIndex) const { enum gkick_filter_type type; geonkick_get_osc_filter_type(geonkickApi, getOscIndex(oscillatorIndex), &type); return static_cast(type); } void GeonkickApi::setOscillatorFilterCutOffFreq(int oscillatorIndex, double frequency) { geonkick_set_osc_filter_cutoff_freq(geonkickApi, getOscIndex(oscillatorIndex), frequency); } double GeonkickApi::getOscillatorFilterCutOffFreq(int oscillatorIndex) const { gkick_real frequency = 0; geonkick_get_osc_filter_cutoff_freq(geonkickApi, getOscIndex(oscillatorIndex), &frequency); return frequency; } void GeonkickApi::setOscillatorFilterFactor(int oscillatorIndex, double factor) { geonkick_set_osc_filter_factor(geonkickApi, getOscIndex(oscillatorIndex), factor); } double GeonkickApi::getOscillatorFilterFactor(int oscillatorIndex) const { gkick_real factor = 0; geonkick_get_osc_filter_factor(geonkickApi, getOscIndex(oscillatorIndex), &factor); return factor; } double GeonkickApi::limiterValue() const { gkick_real val = 0; geonkick_get_limiter_value(geonkickApi, &val); return val; } void GeonkickApi::setLimiterValue(double value) { geonkick_set_limiter_value(geonkickApi, value); } void GeonkickApi::kickUpdatedCallback(void *arg, gkick_real *buff, size_t size, size_t id) { std::vector buffer(size, 0); std::memcpy(buffer.data(), buff, size * sizeof(gkick_real)); GeonkickApi *obj = static_cast(arg); if (obj) obj->updateKickBuffer(std::move(buffer), id); } void GeonkickApi::limiterCallback(void *arg, size_t index, gkick_real val) { GeonkickApi *obj = static_cast(arg); if (obj) obj->setLimiterLevelerValue(index, val); } void GeonkickApi::setLimiterLevelerValue(size_t index, double val) { if (index < limiterLevelers.size()) limiterLevelers[index] = val; } double GeonkickApi::getLimiterLevelerValue(size_t index) const { if (index == static_cast(-1)) index = currentPercussion(); if (index < limiterLevelers.size()) return limiterLevelers[index]; return 0; } void GeonkickApi::updateKickBuffer(const std::vector &&buffer, size_t id) { std::lock_guard lock(apiMutex); if (id < getPercussionsNumber()) kickBuffers[id] = buffer; if (eventQueue && id == currentPercussion()) { auto act = std::make_unique(); act->setCallback([&](void){ kickUpdated(); }); eventQueue->postAction(std::move(act)); } } std::vector GeonkickApi::getInstrumentBuffer(int id) const { { std::lock_guard lock(apiMutex); if (static_cast(id) < kickBuffers.size()) return kickBuffers[id]; } return std::vector(); } std::vector GeonkickApi::getKickBuffer() const { std::lock_guard lock(apiMutex); return kickBuffers[currentPercussion()]; } int GeonkickApi::getSampleRate() const { int sample_rate; if (geonkick_get_sample_rate(geonkickApi, &sample_rate) != GEONKICK_OK) sample_rate = 0; return sample_rate; } // This function is called only from the audio thread. void GeonkickApi::setKeyPressed(bool b, int note, int velocity) { geonkick_key_pressed(geonkickApi, b, note, velocity); } void GeonkickApi::playKick(int id) { if (id < 0) id = currentPercussion(); geonkick_play(geonkickApi, id); } void GeonkickApi::playSamplePreview() { geonkick_play_sample_preview(geonkickApi); } // This function is called only from the audio thread. gkick_real GeonkickApi::getAudioFrame(int channel) const { gkick_real val; geonkick_get_audio_frame(geonkickApi, channel, &val); return val; } void GeonkickApi::process(float** out, size_t offset, size_t size) { geonkick_audio_process(geonkickApi, out, offset, size); } void GeonkickApi::enableCompressor(bool enable) { geonkick_compressor_enable(geonkickApi, enable); } bool GeonkickApi::isCompressorEnabled() const { int enabled = false; geonkick_compressor_is_enabled(geonkickApi, &enabled); return enabled; } void GeonkickApi::setCompressorAttack(double attack) { geonkick_compressor_set_attack(geonkickApi, attack); } void GeonkickApi::setCompressorRelease(double release) { geonkick_compressor_set_release(geonkickApi, release); } void GeonkickApi::setCompressorThreshold(double threshold) { geonkick_compressor_set_threshold(geonkickApi, threshold); } void GeonkickApi::setCompressorRatio(double ratio) { geonkick_compressor_set_ratio(geonkickApi, ratio); } void GeonkickApi::setCompressorKnee(double knee) { geonkick_compressor_set_knee(geonkickApi, knee); } void GeonkickApi::setCompressorMakeup(double makeup) { geonkick_compressor_set_makeup(geonkickApi, makeup); } double GeonkickApi::getCompressorAttack() const { gkick_real val = 0; geonkick_compressor_get_attack(geonkickApi, &val); return val; } double GeonkickApi::getCompressorRelease() const { gkick_real val = 0; geonkick_compressor_get_release(geonkickApi, &val); return val; } double GeonkickApi::getCompressorThreshold() const { gkick_real val = 0; geonkick_compressor_get_threshold(geonkickApi, &val); return val; } double GeonkickApi::getCompressorRatio() const { gkick_real val = 0; geonkick_compressor_get_ratio(geonkickApi, &val); return val; } double GeonkickApi::getCompressorKnee() const { gkick_real val = 0; geonkick_compressor_get_knee(geonkickApi, &val); return val; } double GeonkickApi::getCompressorMakeup() const { gkick_real val = 1.0; geonkick_compressor_get_makeup(geonkickApi, &val); return val; } void GeonkickApi::enableDistortion(bool enable) { geonkick_distortion_enable(geonkickApi, enable); } bool GeonkickApi::isDistortionEnabled() const { int enabled = false; geonkick_distortion_is_enabled(geonkickApi, &enabled); return enabled; } void GeonkickApi::setDistortionInLimiter(double limit) { geonkick_distortion_set_in_limiter(geonkickApi, limit); } double GeonkickApi::getDistortionInLimiter() const { gkick_real limit = 0.0f; geonkick_distortion_get_in_limiter(geonkickApi, &limit); return limit; } void GeonkickApi::setDistortionVolume(double volume) { geonkick_distortion_set_volume(geonkickApi, volume); } double GeonkickApi::getDistortionVolume(void) const { gkick_real volume = 0; geonkick_distortion_get_volume(geonkickApi, &volume); return volume; } void GeonkickApi::setDistortionDrive(double drive) { geonkick_distortion_set_drive(geonkickApi, drive); } double GeonkickApi::getDistortionDrive(void) const { gkick_real drive = 0; geonkick_distortion_get_drive(geonkickApi, &drive); return drive; } void GeonkickApi::registerCallbacks(bool b) { if (b) { geonkick_set_kick_buffer_callback(geonkickApi, &GeonkickApi::kickUpdatedCallback, this); geonkick_set_kick_limiter_callback(geonkickApi, &GeonkickApi::limiterCallback, this); } else { geonkick_set_kick_buffer_callback(geonkickApi, NULL, NULL); geonkick_set_kick_limiter_callback(geonkickApi, NULL, NULL); } } bool GeonkickApi::isJackEnabled() const { return jackEnabled; } void GeonkickApi::setStandalone(bool b) { standaloneInstance = b; } bool GeonkickApi::isStandalone() const { return standaloneInstance; } void GeonkickApi::triggerSynthesis() { geonkick_enable_synthesis(geonkickApi, true); } void GeonkickApi::setLayer(Layer layer) { currentLayer = layer; } GeonkickApi::Layer GeonkickApi::layer() const { return currentLayer; } void GeonkickApi::setLayerAmplitude(Layer layer, double amplitude) { geonkick_group_set_amplitude(geonkickApi, static_cast(layer), amplitude); } double GeonkickApi::getLayerAmplitude(Layer layer) const { gkick_real amplitude = 0; geonkick_group_get_amplitude(geonkickApi, static_cast(layer), &litude); return amplitude; } void GeonkickApi::enbaleLayer(Layer layer, bool enable) { geonkick_enable_group(geonkickApi, static_cast(layer), enable); } bool GeonkickApi::isLayerEnabled(Layer layer) const { bool enabled = false; geonkick_group_enabled(geonkickApi, static_cast(layer), &enabled); return enabled; } int GeonkickApi::getOscIndex(int index) const { return index + GKICK_OSC_GROUP_SIZE * static_cast(currentLayer); } std::filesystem::path GeonkickApi::currentWorkingPath(const std::string &key) const { auto it = workingPaths.find(key); if (it != workingPaths.end()) return it->second; return getSettings("GEONKICK_CONFIG/HOME_PATH"); } void GeonkickApi::setCurrentWorkingPath(const std::string &key, const std::filesystem::path &path) { workingPaths[key] = path; } bool GeonkickApi::setPercussionLimiter(size_t id, double val) { return geonkick_percussion_set_limiter(geonkickApi, id, val) == GEONKICK_OK; } double GeonkickApi::percussionLimiter(size_t id) const { gkick_real val = 0.0f; geonkick_percussion_get_limiter(geonkickApi, id, &val); return val; } bool GeonkickApi::mutePercussion(size_t id, bool b) { return geonkick_percussion_mute(geonkickApi, id, b) == GEONKICK_OK; } bool GeonkickApi::isPercussionMuted(size_t id) const { bool muted = false; geonkick_percussion_is_muted(geonkickApi, id, &muted); return muted; } bool GeonkickApi::soloPercussion(size_t id, bool b) { return geonkick_percussion_solo(geonkickApi, id, b) == GEONKICK_OK; } bool GeonkickApi::isPercussionSolo(size_t id) const { bool solo = false; geonkick_percussion_is_solo(geonkickApi, id, &solo); return solo; } size_t GeonkickApi::getPercussionsNumber() const { return geonkick_percussion_number(); } int GeonkickApi::getUnusedPercussion() const { int index; geonkick_unused_percussion(geonkickApi, &index); return index; } bool GeonkickApi::enablePercussion(int index, bool enable) { auto res = geonkick_enable_percussion(geonkickApi, index, enable); return res == GEONKICK_OK; } bool GeonkickApi::isPercussionEnabled(int index) const { bool enabled = false; geonkick_is_percussion_enabled(geonkickApi, index, &enabled); return enabled; } size_t GeonkickApi::enabledPercussions() const { auto n = getPercussionsNumber(); size_t enabled = 0; for (decltype(n) i = 0; i < n; i++) { if (isPercussionEnabled(i)) enabled++; } return enabled; } bool GeonkickApi::setPercussionPlayingKey(int index, int key) { auto res = geonkick_set_playing_key(geonkickApi, index, key); GEONKICK_LOG_DEBUG("index " << index << " key:" << key); return res == GEONKICK_OK; } int GeonkickApi::getPercussionPlayingKey(int index) const { signed char key = -1; geonkick_get_playing_key(geonkickApi, index, &key); return key; } int GeonkickApi::percussionsReferenceKey() const { // MIDI key A4 return 69; } bool GeonkickApi::setPercussionChannel(int index, size_t channel) { auto res = geonkick_set_percussion_channel(geonkickApi, index, channel); return res == GEONKICK_OK; } int GeonkickApi::getPercussionChannel(int index) const { size_t channel; auto res = geonkick_get_percussion_channel(geonkickApi, index, &channel); if (res != GEONKICK_OK) return -1; return channel; } bool GeonkickApi::setPercussionName(int index, const std::string &name) { auto res = geonkick_set_percussion_name(geonkickApi, index, name.c_str(), name.size()); return res == GEONKICK_OK; } std::string GeonkickApi::getPercussionName(int index) const { auto n = getPercussionsNumber(); if (index > -1 && index < static_cast(n)) { char name[30]; geonkick_get_percussion_name(geonkickApi, index, name, sizeof(name)); return name; } return ""; } void GeonkickApi::setSettings(const std::string &key, const std::string &value) { uiSettings->setSettings(key, value); } std::string GeonkickApi::getSettings(const std::string &key) const { return uiSettings->getSettings(key); } void GeonkickApi::tuneAudioOutput(int id, bool tune) { geonkick_tune_audio_output(geonkickApi, id, tune); } bool GeonkickApi::isAudioOutputTuned(int id) const { bool tune = false; geonkick_is_audio_output_tuned(geonkickApi, id, &tune); return tune; } bool GeonkickApi::setCurrentPercussion(int index) { auto res = geonkick_set_current_percussion(geonkickApi, index); return res == GEONKICK_OK; } size_t GeonkickApi::currentPercussion() const { size_t index = 0; geonkick_get_current_percussion(geonkickApi, &index); return index; } void GeonkickApi::setOscillatorSample(const std::string &file, int oscillatorIndex) { int sRate = Geonkick::defaultSampleRate; geonkick_get_sample_rate(geonkickApi, &sRate); std::vector sampleData = loadSample(file, kickMaxLength() / 1000, sRate, 1); setOscillatorSample(sampleData, oscillatorIndex); } void GeonkickApi::setOscillatorSample(const std::vector &sample, int oscillatorIndex) { geonkick_set_osc_sample(geonkickApi, getOscIndex(oscillatorIndex), sample.data(), sample.size()); } std::vector GeonkickApi::getOscillatorSample(int oscillatorIndex) const { gkick_real *data = nullptr; size_t size = 0; geonkick_get_osc_sample(geonkickApi, getOscIndex(oscillatorIndex), &data, &size); if (data) return std::vector(data, data + size); return {}; } std::vector GeonkickApi::loadSample(const std::string &file, double length, int sampleRate, int channels) { GEONKICK_UNUSED(channels); SF_INFO sndinfo; memset(&sndinfo, 0, sizeof(sndinfo)); SNDFILE *sndFile = sf_open(file.c_str(), SFM_READ, &sndinfo); if (!sndFile) { GEONKICK_LOG_ERROR("can't open sample file"); return std::vector(); } auto formatType = sndinfo.format & SF_FORMAT_TYPEMASK; if (formatType != SF_FORMAT_FLAC && formatType != SF_FORMAT_WAV && formatType != SF_FORMAT_FLAC && formatType != SF_FORMAT_WAVEX && formatType != SF_FORMAT_OGG) { GEONKICK_LOG_ERROR(std::hex << "unsupported audio format"); sf_close(sndFile); return std::vector(); } std::vector data(sndinfo.samplerate * length * sndinfo.channels, 0.0f); auto n = sf_read_float(sndFile, data.data(), data.size()); sf_close(sndFile); if (static_cast(n) < 1) { GEONKICK_LOG_ERROR("error on reading samples"); return std::vector(); } else if (static_cast(n) < data.size()) { GEONKICK_LOG_DEBUG("read less then data, resize to " << n); data.resize(n); } if (sndinfo.channels > 1) { GEONKICK_LOG_DEBUG("multichannel file, get only the first channel"); for (decltype(data.size()) i = 0; i < data.size(); i += sndinfo.channels) data[i / sndinfo.channels] = data[i]; data.resize(data.size() / sndinfo.channels); } float max = std::fabs(*std::max_element(data.begin(), data.end(), [](float a, float b){ return fabs(a) < fabs(b); })); if (max > std::numeric_limits::min()) { float k = 1.0f / max; for (auto &v: data) v *= k; } if (sampleRate != sndinfo.samplerate) { GEONKICK_LOG_DEBUG("different sample rate " << sndinfo.samplerate << ", resample to " << sampleRate); float f = static_cast(sndinfo.samplerate) / sampleRate; float x = 0.0f; std::vector out_data; for (decltype(data.size()) i = 0; i < data.size() - 1;) { float d = x - i; float val = data[i] * (1.0f - d) + data[i + 1] * d; x += f; i = x; out_data.push_back(val); } return out_data; } return data; } void GeonkickApi::setKitName(const std::string &name) { kitName = name; } std::string GeonkickApi::getKitName() const { return kitName; } void GeonkickApi::setKitAuthor(const std::string &author) { kitAuthor = author; } std::string GeonkickApi::getKitAuthor() const { return kitAuthor; } void GeonkickApi::setKitUrl(const std::string &url) { kitUrl = url; } std::string GeonkickApi::getKitUrl() const { return kitUrl; } void GeonkickApi::copyToClipboard() { clipboardPercussion = getPercussionState(); } void GeonkickApi::pasteFromClipboard() { if (clipboardPercussion) { auto state = std::make_shared(*clipboardPercussion); auto currId = currentPercussion(); state->setId(currId); state->setName(getPercussionName(currId)); state->setPlayingKey(getPercussionPlayingKey(currId)); state->setChannel(getPercussionChannel(currId)); state->setMute(isPercussionMuted(currId)); state->setSolo(isPercussionSolo(currId)); setPercussionState(state); } } void GeonkickApi::notifyUpdateGraph() { if (eventQueue) { auto act = std::make_unique(); act->setCallback([&](void){ action kickUpdated(); }); eventQueue->postAction(std::move(act)); } } void GeonkickApi::notifyUpdateParameters() { if (eventQueue) { auto act = std::make_unique(); act->setCallback([&](void){ action stateChanged(); }); eventQueue->postAction(std::move(act)); } } void GeonkickApi::notifyUpdateGui() { if (eventQueue) { auto act = std::make_unique(); act->setCallback([&](void){ action kickUpdated(); action stateChanged(); }); eventQueue->postAction(std::move(act)); } } void GeonkickApi::notifyPercussionUpdated(int id) { if (eventQueue) { auto act = std::make_unique(); act->setCallback([this, id](void){ GEONKICK_LOG_DEBUG("update percussion :" << id); action percussionUpdated(id); }); eventQueue->postAction(std::move(act)); } } void GeonkickApi::notifyKitUpdated() { if (eventQueue) { auto act = std::make_unique(); act->setCallback([&](void){ action kitUpdated(); }); eventQueue->postAction(std::move(act)); } } std::vector GeonkickApi::ordredPercussionIds() const { return percussionIdList; } void GeonkickApi::removeOrderedPercussionId(int id) { for (auto it = percussionIdList.begin(); it != percussionIdList.end(); ++it) { if (*it == id) { percussionIdList.erase(it); break; } } } void GeonkickApi::addOrderedPercussionId(int id) { removeOrderedPercussionId(id); percussionIdList.push_back(id); } void GeonkickApi::clearOrderedPercussionIds() { percussionIdList.clear(); } bool GeonkickApi::moveOrdrepedPercussionId(int index, int n) { if (index < 0) return false; auto size = percussionIdList.size(); for (decltype(percussionIdList.size()) i = 0; i < size; i++) { if (percussionIdList[i] == index) { auto newId = static_cast(i) + n; if (newId > -1 && static_cast(newId) < size) { std::swap(percussionIdList[i], percussionIdList[newId]); return true; } } } return false; } void GeonkickApi::loadPresets() { std::unordered_set prestsPaths; auto presetsPathSufix = std::filesystem::path(GEONKICK_APP_NAME) / "presets"; std::filesystem::path presetsPath = getSettings("GEONKICK_CONFIG/USER_PRESETS_PATH"); prestsPaths.insert(presetsPath); #ifdef GEONKICK_DATA_DIR prestsPaths.insert(std::filesystem::path(GEONKICK_DATA_DIR) / presetsPathSufix); #endif // GEONKICK_DATA_DIR const char *dataDirs = std::getenv("XDG_DATA_DIRS"); if (dataDirs == nullptr || *dataDirs == '\0') { prestsPaths.insert("/usr/share" / presetsPathSufix); prestsPaths.insert("/usr/local/share" / presetsPathSufix); } else { std::stringstream ss(dataDirs); std::string path; while (std::getline(ss, path, ':')) prestsPaths.insert(std::filesystem::path(path) / presetsPathSufix); } for (const auto &path: prestsPaths) { try { if (std::filesystem::exists(path)) loadPresetsFolders(path); } catch(const std::exception& e) { GEONKICK_LOG_ERROR("error on reading path: " << path << ": " << e.what()); } } } void GeonkickApi::loadPresetsFolders(const std::filesystem::path &path) { try { for (const auto &entry : std::filesystem::directory_iterator(path)) { if (!entry.path().empty() && std::filesystem::is_directory(entry.path())) { auto presetFolder = std::make_unique(entry.path()); GEONKICK_LOG_DEBUG("preset folder " << presetFolder->path()); if (!presetFolder->loadPresets()) { GEONKICK_LOG_ERROR("can't load preset from folder " << presetFolder->path()); } else if (presetFolder->numberOfPresets() > 0) { presetsFoldersList.push_back(std::move(presetFolder)); } } } } catch(...) { GEONKICK_LOG_ERROR("error on reading path: " << path); } } void GeonkickApi::setupPaths() { std::filesystem::path dataPath; const char *dataHome = std::getenv("XDG_DATA_HOME"); if (dataHome == nullptr || *dataHome == '\0') { const char *homeDir = std::getenv("HOME"); if (homeDir == nullptr || *homeDir == '\0') { GEONKICK_LOG_ERROR("can't get home directory"); return; } dataPath = homeDir / std::filesystem::path(".local/share") / std::filesystem::path(GEONKICK_APP_NAME); setSettings("GEONKICK_CONFIG/HOME_PATH", homeDir); } else { dataPath = dataHome / std::filesystem::path(GEONKICK_APP_NAME); setSettings("GEONKICK_CONFIG/HOME_PATH", dataHome); } try { if (!std::filesystem::exists(dataPath)) { if (!std::filesystem::create_directories(dataPath)) { GEONKICK_LOG_ERROR("can't create path " << dataPath); return; } } setSettings("GEONKICK_CONFIG/USER_DATA_PATH", dataPath); auto presetsPath = dataPath / std::filesystem::path("presets"); if (!std::filesystem::exists(presetsPath)) { if (!std::filesystem::create_directories(presetsPath)) { GEONKICK_LOG_ERROR("can't create path " << presetsPath); return; } } setSettings("GEONKICK_CONFIG/USER_PRESETS_PATH", presetsPath); } catch(const std::exception& e) { GEONKICK_LOG_ERROR("error on setup user data paths: " << e.what()); } } PresetFolder* GeonkickApi::getPresetFolder(size_t index) const { if (index < presetsFoldersList.size()) return presetsFoldersList[index].get(); return nullptr; } size_t GeonkickApi::numberOfPresetFolders() const { return presetsFoldersList.size(); } UiSettings* GeonkickApi::getUiSettings() const { return uiSettings.get(); } void GeonkickApi::setState(const std::string &data) { rapidjson::Document document; document.Parse(data.c_str()); if (!document.IsObject()) return; for (const auto &m: document.GetObject()) { if (m.name == "UiSettings" && m.value.IsObject()) uiSettings->fromJsonObject(m.value); if (m.name == "KitState" && m.value.IsObject()) { auto kitState = std::make_unique(); kitState->fromJsonObject(m.value); setKitState(std::move(kitState)); } } } std::string GeonkickApi::getState() const { std::ostringstream jsonStream; jsonStream << "{\"UiSettings\": " << std::endl; jsonStream << uiSettings->toJson() << ", " << std::endl; jsonStream << "\"KitState\": " << std::endl; jsonStream << getKitState()->toJson() << std::endl; jsonStream << "}" << std::endl; return jsonStream.str(); } std::vector GeonkickApi::setPreviewSample(const std::string &file) { int sRate = Geonkick::defaultSampleRate; geonkick_get_sample_rate(geonkickApi, &sRate); std::vector sampleData = loadSample(file, kickMaxLength() / 1000, sRate, 1); if (!sampleData.empty()) { geonkick_set_preview_sample(geonkickApi, sampleData.data(), sampleData.size()); return sampleData; } return std::vector(); } void GeonkickApi::setSamplePreviewLimiter(double val) { geonkick_set_sample_preview_limiter(geonkickApi, val); } double GeonkickApi::samplePreviewLimiter() const { gkick_real val = 0; geonkick_get_sample_preview_limiter(geonkickApi, &val); return val; } geonkick-2.10.2/src/geonkick_api.h000066400000000000000000000360601451017372400167620ustar00rootroot00000000000000/** * File name: geonkick_api.h * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_API_H #define GEONKICK_API_H #include "globals.h" #include class Oscillator; class PercussionState; class KitState; class RkEventQueue; class PresetFolder; class UiSettings; class GeonkickApi : public RkObject { public: enum class InstanceType: int { Standalone = 0, Lv2 = 1, Vst3 = 2 }; enum class Layer: int { Layer1 = 0, Layer2 = 1, Layer3 = 2 }; enum class OscillatorType: int { Oscillator1 = 0, Oscillator2 = 1, Noise = 2 }; enum class FunctionType: int { Sine = GEONKICK_OSC_FUNC_SINE, Square = GEONKICK_OSC_FUNC_SQUARE, Triangle = GEONKICK_OSC_FUNC_TRIANGLE, Sawtooth = GEONKICK_OSC_FUNC_SAWTOOTH, NoiseWhite = GEONKICK_OSC_FUNC_NOISE_WHITE, NoisePink = GEONKICK_OSC_FUNC_NOISE_PINK, NoiseBrownian = GEONKICK_OSC_FUNC_NOISE_BROWNIAN, Sample = GEONKICK_OSC_FUNC_SAMPLE, }; enum class EnvelopeType: int { Amplitude = GEONKICK_AMPLITUDE_ENVELOPE, Frequency = GEONKICK_FREQUENCY_ENVELOPE, FilterCutOff = GEONKICK_FILTER_CUTOFF_ENVELOPE, DistortionDrive = GEONKICK_DISTORTION_DRIVE_ENVELOPE, DistortionVolume = GEONKICK_DISTORTION_VOLUME_ENVELOPE, PitchShift = GEONKICK_PITCH_SHIFT_ENVELOPE }; enum class FilterType: int { LowPass = GEONKICK_FILTER_LOW_PASS, HighPass = GEONKICK_FILTER_HIGH_PASS, BandPass = GEONKICK_FILTER_BAND_PASS }; GeonkickApi(int sample_rate = Geonkick::defaultSampleRate, InstanceType instance = InstanceType::Standalone, geonkick *dsp = nullptr); ~GeonkickApi(); void setInstanceType(InstanceType type); InstanceType getInstanceType() const; static unsigned int getVersion(); size_t static numberOfChannels(); void setEventQueue(RkEventQueue *queue); bool initDSP(); bool init(); void registerCallbacks(bool b); std::vector> oscillators(void); bool isOscillatorEnabled(int oscillatorIndex) const; std::vector oscillatorEvelopePoints(int oscillatorIndex, EnvelopeType envelope) const; void addOscillatorEnvelopePoint(int oscillatorIndex, EnvelopeType envelope, const RkRealPoint &point); void removeOscillatorEvelopePoint(int oscillatorIndex, EnvelopeType envelope, int pointIndex); void updateOscillatorEvelopePoint(int oscillatorIndex, EnvelopeType envelope, int pointIndex, const RkRealPoint &point); FunctionType oscillatorFunction(int oscillatorIndex) const; gkick_real oscillatorPhase(int oscillatorIndex) const; int oscillatorSeed(int oscillatorIndex) const; void setOscillatorSample(const std::string &file, int oscillatorIndex); void setOscillatorSample(const std::vector &sample, int oscillatorIndex); std::vector getOscillatorSample(int oscillatorIndex) const; double kickMaxLength(void) const; double kickLength(void) const; double kickAmplitude() const; bool isKickFilterEnabled() const; double kickFilterFrequency(void) const; double kickFilterQFactor() const; FilterType kickFilterType() const; std::vector getKickEnvelopePoints(EnvelopeType envelope) const; bool setOscillatorFrequency(int oscillatorIndex, double frequency); bool setOscillatorPitchShift(int oscillatorIndex, double semitones); void setOscillatorAsFm(int oscillatorIndex, bool b); bool isOscillatorAsFm(int oscillatorIndex) const; double oscillatorAmplitude(int oscillatorIndex) const; double oscillatorFrequency(int oscillatorIndex) const; double oscillatorPitchShift(int oscillatorIndex) const; void addKickEnvelopePoint(EnvelopeType envelope, double x, double y); void removeKickEnvelopePoint(EnvelopeType envelope, int pointIndex); void updateKickEnvelopePoint(EnvelopeType envelope, int index, double x, double y); void setOscillatorEvelopePoints(int index, EnvelopeType envelope, const std::vector &points); void setOscillatorFunction(int oscillatorIndex, FunctionType function); void setOscillatorPhase(int oscillatorIndex, gkick_real phase); void setOscillatorSeed(int oscillatorIndex, int seed); void enableOscillator(int oscillatorIndex, bool enable); void enableOscillatorFilter(int oscillatorIndex, bool enable); bool isOscillatorFilterEnabled(int oscillatorIndex) const; void setOscillatorFilterType(int oscillatorIndex, FilterType filter); FilterType getOscillatorFilterType(int oscillatorIndex) const; void setOscillatorFilterCutOffFreq(int oscillatorIndex, double frequency); double getOscillatorFilterCutOffFreq(int oscillatorIndex) const; void setOscillatorFilterFactor(int oscillatorIndex, double factor); double getOscillatorFilterFactor(int oscillatorIndex) const; bool setOscillatorAmplitude(int oscillatorIndex, double amplitude); double limiterValue() const; int getSampleRate() const; static std::unique_ptr getDefaultKitState(); static std::shared_ptr getDefaultPercussionState(); // This function is called only from the audio thread. gkick_real getAudioFrame(int channel) const; // This function is called only from the audio thread. void setKeyPressed(bool b, int note, int velocity); // This function is called only from the audio thread. void process(float** out, size_t channel, size_t size); std::shared_ptr getPercussionState(size_t id) const; std::shared_ptr getPercussionState() const; bool isCompressorEnabled() const; double getCompressorAttack() const; double getCompressorRelease() const; double getCompressorThreshold() const; double getCompressorRatio() const; double getCompressorKnee() const; double getCompressorMakeup() const; bool isDistortionEnabled() const; double getDistortionInLimiter() const; double getDistortionVolume() const; double getDistortionDrive() const; bool isJackEnabled() const; void setStandalone(bool b); bool isStandalone() const; void setKickAmplitude(double amplitude); void setKickLength(double length); void setLimiterValue(double value); void setKickFilterFrequency(double frequency); void setKickFilterQFactor(double factor); void enableKickFilter(bool b); void setKickFilterType(FilterType type); void setPercussionState(const std::string &data); void setPercussionState(const std::shared_ptr &state); std::unique_ptr getKitState() const; bool setKitState(const std::string &data); bool setKitState(const std::unique_ptr &state); void setKitName(const std::string &name); std::string getKitName() const; void setKitAuthor(const std::string &author); std::string getKitAuthor() const; void setKitUrl(const std::string &url); std::string getKitUrl() const; void setKickEnvelopePoints(EnvelopeType envelope, const std::vector &points); void playKick(int id = -1); void enableCompressor(bool enable); void setCompressorAttack(double attack); void setCompressorRelease(double release); void setCompressorThreshold(double threshold); void setCompressorRatio(double ratio); void setCompressorKnee(double knee); void setCompressorMakeup(double makeup); void enableDistortion(bool enable); void setDistortionVolume(double volume); void setDistortionInLimiter(double limit); void setDistortionDrive(double drive); std::vector getKickBuffer() const; std::vector getInstrumentBuffer(int id) const; void triggerSynthesis(); void setLayer(Layer layer); Layer layer() const; void setLayerAmplitude(Layer layer, double amplitude); double getLayerAmplitude(Layer layer) const; void enbaleLayer(Layer layer, bool enable = true); bool isLayerEnabled(Layer layer) const; int getOscIndex(int index) const; double getLimiterLevelerValue(size_t index = -1) const; std::filesystem::path currentWorkingPath(const std::string &key) const; void setCurrentWorkingPath(const std::string &key, const std::filesystem::path &path); void tuneAudioOutput(int id, bool tune); bool isAudioOutputTuned(int id) const; size_t getPercussionsNumber() const; bool setCurrentPercussion(int index); size_t currentPercussion() const; int getUnusedPercussion() const; bool enablePercussion(int index, bool enable = true); bool isPercussionEnabled(int index) const; size_t enabledPercussions() const; bool setPercussionPlayingKey(int index, int key); int getPercussionPlayingKey(int index) const; int percussionsReferenceKey() const; bool setPercussionChannel(int index, size_t channel); int getPercussionChannel(int index) const; bool setPercussionName(int index, const std::string &name); bool setPercussionLimiter(size_t id, double val); double percussionLimiter(size_t id) const; bool mutePercussion(size_t id, bool b); bool isPercussionMuted(size_t id) const; bool soloPercussion(size_t id, bool b); bool isPercussionSolo(size_t id) const; std::string getPercussionName(int index) const; void copyToClipboard(); void pasteFromClipboard(); RK_DECL_ACT(kickLengthUpdated, kickLengthUpdated(double val), RK_ARG_TYPE(double), RK_ARG_VAL(val)); RK_DECL_ACT(kickAmplitudeUpdated, kickAmplitudeUpdated(double val), RK_ARG_TYPE(double), RK_ARG_VAL(val)); RK_DECL_ACT(kickUpdated, kickUpdated(), RK_ARG_TYPE(), RK_ARG_VAL()); RK_DECL_ACT(newKickBuffer, newKickBuffer(std::vector buffer), RK_ARG_TYPE(std::vector), RK_ARG_VAL(std::move(buffer))); RK_DECL_ACT(currentPlayingFrameVal, currentPlayingFrameVal(double val), RK_ARG_TYPE(double), RK_ARG_VAL(val)); RK_DECL_ACT(stateChanged, stateChanged(), RK_ARG_TYPE(), RK_ARG_VAL()); RK_DECL_ACT(kitUpdated, kitUpdated(), RK_ARG_TYPE(), RK_ARG_VAL()); RK_DECL_ACT(percussionUpdated, percussionUpdated(int id), RK_ARG_TYPE(int), RK_ARG_VAL(id)); void setSettings(const std::string &key, const std::string &value); std::string getSettings(const std::string &key) const; void notifyUpdateGraph(); void notifyUpdateParameters(); void notifyUpdateGui(); void notifyPercussionUpdated(int id); void notifyKitUpdated(); std::vector ordredPercussionIds() const; void removeOrderedPercussionId(int id); void addOrderedPercussionId(int id); void clearOrderedPercussionIds(); bool moveOrdrepedPercussionId(int index, int n); PresetFolder* getPresetFolder(size_t index) const; size_t numberOfPresetFolders() const; UiSettings* getUiSettings() const; void setState(const std::string &data); std::string getState() const; std::vector setPreviewSample(const std::string &file); void playSamplePreview(); void setSamplePreviewLimiter(double val); double samplePreviewLimiter() const; bool exportInstrumentBuffer(int id, int sampleRate, size_t channels, const std::string &filePath); protected: void setupPaths(); void loadPresets(); void loadPresetsFolders(const std::filesystem::path &path); static void kickUpdatedCallback(void *arg, gkick_real *buff, size_t size, size_t id); static void limiterCallback(void *arg, size_t index, gkick_real val); void updateKickBuffer(const std::vector &&buffer, size_t id); void setOscillatorState(Layer layer, OscillatorType oscillator, const std::shared_ptr &state); void getOscillatorState(Layer layer, OscillatorType osc, const std::shared_ptr &state) const; void setLimiterLevelerValue(size_t index, double val); static std::vector loadSample(const std::string &file, double length = 4.0, int sampleRate = 48000, int channels = 1); private: mutable struct geonkick *geonkickApi; InstanceType instanceType; std::array, GEONKICK_MAX_PERCUSSIONS> limiterLevelers; bool jackEnabled; bool standaloneInstance; mutable std::mutex apiMutex; RkEventQueue *eventQueue; std::vector> kickBuffers; mutable Layer currentLayer; std::string kitName; std::string kitAuthor; std::string kitUrl; std::shared_ptr clipboardPercussion; /** * Current working paths for entire application. * Since on plugins the GUI is closed, there is a need to * store this path in the API instance. */ std::unordered_map workingPaths; std::unordered_map apiSettings; std::vector percussionIdList; std::vector> presetsFoldersList; std::unique_ptr uiSettings; int sampleRate; }; #endif // GEONKICK_API_H geonkick-2.10.2/src/geonkick_button.cpp000066400000000000000000000023131451017372400200510ustar00rootroot00000000000000/** * File name: geonkick_button.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "geonkick_button.h" GeonkickButton::GeonkickButton(GeonkickWidget *parent) : RkButton(parent) { show(); } void GeonkickButton::setPressedImage(const RkImage &img) { setImage(img, RkButton::State::Pressed); } void GeonkickButton::setUnpressedImage(const RkImage &img) { setImage(img, RkButton::State::Unpressed); } geonkick-2.10.2/src/geonkick_button.h000066400000000000000000000027271451017372400175270ustar00rootroot00000000000000/** * File name: geonkick_button.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_BUTTON_H #define GEONKICK_BUTTON_H #include #include #include class GeonkickButton: public RkButton { public: explicit GeonkickButton(GeonkickWidget *parent); virtual ~GeonkickButton() = default; void setPressedImage(const RkImage &img); void setUnpressedImage(const RkImage &img); virtual void mouseButtonPressEvent(RkMouseEvent *event) override { RK_UNUSED(event); setFocus(true); RkButton::mouseButtonPressEvent(event); } }; #endif // GEONKICK_BUTTON_H geonkick-2.10.2/src/geonkick_checkbox.cpp000066400000000000000000000052551451017372400203340ustar00rootroot00000000000000/** * File name: geonkick_button.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "geonkick_checkbox.h" #include "geonkick_button.h" #include "geonkick_label.h" GeonkickCheckbox::GeonkickCheckbox(GeonkickWidget *parent) : GeonkickWidget(parent), checkboxButton{new GeonkickButton(this)}, checkboxLabel{new GeonkickButton(this)}, { checkboxButton->setCheckable(true); checkboxLabel->setCheckable(true); mainLayout->addWidget(checkboxButton, 0, Qt::AlignLeft); mainLayout->addWidget(checkboxLabel, 0, Qt::AlignLeft); RK_ACT_BIND(checkboxButton, SIGNAL(toggled(bool)), this, SIGNAL(stateUpdated(bool))); RK_ACT_BIND(checkboxButton, SIGNAL(toggled(bool)), this, SLOT(setChecked(bool))); RK_ACT_BIND(checkboxLabel, SIGNAL(toggled(bool)), this, SLOT(setChecked(bool))); RK_ACT_BIND(checkboxLabel, SIGNAL(toggled(bool)), this, SIGNAL(stateUpdated(bool))); } void GeonkickCheckbox::setCheckboxLabelImage(const QPixmap &pixmap) { checkboxLabel->setUnpressedImage(pixmap); } void GeonkickCheckbox::setCheckboxLabelImage(const std::string &file) { setCheckboxLabelImage(QPixmap(file)); } void GeonkickCheckbox::setCheckedImage(const QPixmap &pixmap) { checkboxButton->setPressedImage(pixmap); } void GeonkickCheckbox::setUncheckedImage(const QPixmap &pixmap) { checkboxButton->setUnpressedImage(pixmap); } void GeonkickCheckbox::setCheckedImage(const std::string &file) { setCheckedImage(QPixmap(file)); } void GeonkickCheckbox::setUncheckedImage(const std::string &file) { setUncheckedImage(QPixmap(file)); } void GeonkickCheckbox::setPadding(int left, int top, int right, int bottom) { mainLayout->setContentsMargins(left, top, right, bottom); } void GeonkickCheckbox::setChecked(bool checked) { checkboxButton->setPressed(checked); checkboxLabel->setPressed(checked); } geonkick-2.10.2/src/geonkick_checkbox.h000066400000000000000000000033301451017372400177710ustar00rootroot00000000000000/** * File name: geonkick_checkbox.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_CHECKBOX_H #define GEONKICK_CHECKBOX_H #include "geonkick_widget.h" class GeonkickButton; class GeonkickCheckbox: public GeonkickWidget { public: explicit GeonkickCheckbox(GeonkickWidget *parent); virtual ~GeonkickCheckbox() = default; void setCheckboxLabelImage(const QPixmap &pixmap); void setCheckboxLabelImage(const std::string &file); void setCheckedImage(const QPixmap &pixmap); void setUncheckedImage(const QPixmap &pixmap); void setCheckedImage(const std::string &file); void setUncheckedImage(const std::string &file); void setPadding(int left, int top, int right, int bottom); void setChecked(bool checked); void stateUpdated(bool state); private: GeonkickButton *checkboxButton; GeonkickButton *checkboxLabel; }; #endif // GEONKICK_CHECKBOX_H geonkick-2.10.2/src/geonkick_groupbox.cpp000066400000000000000000000026011451017372400204030ustar00rootroot00000000000000/** * File name: geonkick_groupbox.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "geonkick_groupbox.h" GeonkickGroupBox::GeonkickGroupBox(GeonkickWidget *parent, Orientation orientation) : GeonkickWidget(parent) , groupBoxOrientation{orientation} , groupBoxLabel{nullptr} { } GeonkickWidget* GeonkickGroupBox::getGroupBoxLabel() { return groupBoxLabel; } void GeonkickGroupBox::setGroupBoxLabel(GeonkickWidget *label) { if (label) groupBoxLabel = label; } GeonkickWidget* GeonkickGroupBox::addWidget(GeonkickWidget *widget) { return widget; } geonkick-2.10.2/src/geonkick_groupbox.h000066400000000000000000000031631451017372400200540ustar00rootroot00000000000000/** * File name: geonkick_groupbox.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEOBKICK_GROUPBOX_H #define GEOBKICK_GROUPBOX_H #include "geonkick_widget.h" class GeonkickGroupBox: public GeonkickWidget { public: enum class Orientation:char { Vertical, Horizontal }; GeonkickGroupBox(GeonkickWidget *parent = nullptr, Orientation orientation = Orientation::Vertical); virtual ~GeonkickGroupBox() = default; GeonkickWidget* getGroupBoxLabel(); void setGroupBoxLabel(GeonkickWidget *label); GeonkickWidget* addWidget(GeonkickWidget *widget); void setWidgetAlignment(GeonkickWidget *widget); void setPadding(int left, int top, int right, int bottom); private: Orientation groupBoxOrientation; GeonkickWidget *groupBoxLabel; }; #endif // GEOBKICK_GROUPBOX_H geonkick-2.10.2/src/geonkick_slider.cpp000066400000000000000000000120441451017372400200220ustar00rootroot00000000000000/** * File name: geonkick_slider.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "geonkick_slider.h" #include #include GeonkickSlider::GeonkickSlider(GeonkickWidget *parent, Orientation orientation) : GeonkickWidget(parent) , sliderOrientation{orientation} , isSelected{false} , sliderValue{0} , sliderPixels{0} { setBackgroundColor(100, 100, 100); show(); } void GeonkickSlider::paintWidget(RkPaintEvent *event) { RK_UNUSED(event); RkImage img(width(), height()); RkPainter painter(&img); painter.fillRect(rect(), background()); painter.setPen(RkPen(RkColor(40, 40, 40))); painter.drawRect({0, 0, width() - 1, height() - 1}); if (sliderOrientation == Orientation::Horizontal) painter.fillRect(RkRect(1, 1, sliderPixels, height() - 1 - 1), RkColor(200, 200, 200)); else painter.fillRect(RkRect(1, height() - 1 - sliderPixels, width() - 2, sliderPixels), RkColor(200, 200, 200)); RkPainter paint(this); paint.drawImage(img, 0, 0); } void GeonkickSlider::mouseButtonPressEvent(RkMouseEvent *event) { setFocus(true); if (event->button() == RkMouseEvent::ButtonType::WheelUp || event->button() == RkMouseEvent::ButtonType::WheelDown) { sliderValue += event->button() == RkMouseEvent::ButtonType::WheelUp ? 2 : -2; onSetValue(sliderValue); if (sliderValue < 0) sliderValue = 0; else if (sliderValue > 100) sliderValue = 100; sliderPixels = pixelsFromValue(); action valueUpdated(sliderValue); update(); return; } if (event->x() >= 0 && event->x() < width() && event->y() >= 0 && event->y() < height()) { int value = calculateValue(event->x(), event->y()); isSelected = true; if (value != sliderValue) { sliderValue = value; valueUpdated(sliderValue); update(); } } } void GeonkickSlider::mouseMoveEvent(RkMouseEvent *event) { if (isSelected) { int value = calculateValue(event->x(), event->y()); sliderValue = value; valueUpdated(sliderValue); update(); } } void GeonkickSlider::mouseButtonReleaseEvent(RkMouseEvent *event) { RK_UNUSED(event); isSelected = false; } int GeonkickSlider::calculateValue(int x, int y) { int value = 0; if (sliderOrientation == Orientation::Horizontal) { if (x < 1) value = 0; else if (x > width() - 1 - 1) value = width() - 2 * 1; else value = x; sliderPixels = value; value = 100 * ((double)sliderPixels / (width() - 2)); } else { if (y < 1) value = height() - 2; else if (y > height() - 2) value = 0; else value = height() - y; sliderPixels = value; value = 100 * ((double)sliderPixels / (height() - 2)); } return value; } int GeonkickSlider::getValue() const { return sliderValue; } int GeonkickSlider::pixelsFromValue() const { int pixels; if (sliderOrientation == Orientation::Horizontal) pixels = (static_cast(sliderValue) / 100) * (width() - 2); else pixels = (static_cast(sliderValue) / 100) * (height() - 2); return pixels; } void GeonkickSlider::onSetValue(int value) { if (value > 100) value = 100; else if (value < 0) value = 0; if (value != sliderValue) { sliderValue = value; sliderPixels = pixelsFromValue(); update(); } } GeonkickSlider::Orientation GeonkickSlider::getOrientation() const { return sliderOrientation; } geonkick-2.10.2/src/geonkick_slider.h000066400000000000000000000037451451017372400174770ustar00rootroot00000000000000/** * File name: geonkick_slider.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_SLIDER_H #define GEONKICK_SLIDER_H #include "geonkick_widget.h" class RkMouseEvent; class GeonkickSlider: public GeonkickWidget { public: enum class Orientation:char { Horizontal, Vertical }; GeonkickSlider(GeonkickWidget *parent, Orientation orientation = Orientation::Horizontal); ~GeonkickSlider() = default; int getValue() const; void onSetValue(int value); RK_DECL_ACT(valueUpdated, valueUpdated(int value), RK_ARG_TYPE(int), RK_ARG_VAL(value)); Orientation getOrientation() const; protected: int calculateValue(int x, int y); virtual void paintWidget(RkPaintEvent *event) override; void mouseButtonPressEvent(RkMouseEvent *event) override; void mouseMoveEvent(RkMouseEvent *event) override; void mouseButtonReleaseEvent(RkMouseEvent *event) override; int pixelsFromValue() const; private: Orientation sliderOrientation; bool isSelected; int sliderValue; int sliderPixels; }; #endif // GEONKICK_SLIDER_H geonkick-2.10.2/src/geonkick_widget.cpp000066400000000000000000000052241451017372400200250ustar00rootroot00000000000000/** * File name: geonkick_widget.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "geonkick_widget.h" #include GeonkickWidget::GeonkickWidget(RkMain *app, const RkNativeWindowInfo &info, Rk::WindowFlags flags) : RkWidget(app, info, flags) , viewingState{nullptr} { setBackgroundColor(68, 68, 70); setTextColor({210, 226, 226, 140}); auto f = font(); f.setSize(12); setFont(f); } GeonkickWidget::GeonkickWidget(RkMain *app, Rk::WindowFlags flags) : RkWidget(app, flags) , viewingState{nullptr} { setBackgroundColor({68, 68, 70}); setTextColor({210, 226, 226, 140}); auto f = font(); f.setSize(12); setFont(f); } GeonkickWidget::GeonkickWidget(GeonkickWidget *parent, Rk::WindowFlags flags) : RkWidget(parent, flags) , viewingState{parent->viewState()} { setBackgroundColor(68, 68, 70); setTextColor({210, 226, 226, 140}); auto f = font(); f.setSize(12); setFont(f); } void GeonkickWidget::setViewState(ViewState *state) { viewingState = state; } ViewState* GeonkickWidget::viewState() const { return viewingState; } void GeonkickWidget::paintEvent(RkPaintEvent *event) { if (!backgroundImage.isNull()) { RkPainter painter(this); painter.fillRect(rect(), background()); painter.drawImage(backgroundImage, 0, 0); } paintWidget(event); } void GeonkickWidget::paintWidget([[maybe_unused]] RkPaintEvent *event) { } void GeonkickWidget::setBackgroundImage(const RkImage &img) { if (!img.isNull()) { backgroundImage = img; update(); } } void GeonkickWidget::mouseButtonPressEvent(RkMouseEvent *event) { setFocus(true); } geonkick-2.10.2/src/geonkick_widget.h000066400000000000000000000040501451017372400174660ustar00rootroot00000000000000/** * File name: geonkick_widget.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_WIDGET_H #define GEONKICK_WIDGET_H #include "globals.h" #include #include class RkPaintEvent; class RkNativeWindowInfo; class ViewState; class GeonkickWidget: public RkWidget { public: explicit GeonkickWidget(RkMain *app, Rk::WindowFlags flags = Rk::WindowFlags::Widget); explicit GeonkickWidget(RkMain *app, const RkNativeWindowInfo &info, Rk::WindowFlags flags = Rk::WindowFlags::Widget); explicit GeonkickWidget(GeonkickWidget *parent, Rk::WindowFlags flags = Rk::WindowFlags::Widget); virtual ~GeonkickWidget() = default; void setBackgroundImage(const RkImage &img); void setBackgroundImage(const std::string &file); void setViewState(ViewState *state); ViewState* viewState() const; protected: void paintEvent(RkPaintEvent *event) final; virtual void paintWidget(RkPaintEvent *event); void mouseButtonPressEvent(RkMouseEvent *event) override; private: RkImage backgroundImage; ViewState* viewingState; }; #endif // GEONKICK_WIDGET_H geonkick-2.10.2/src/globals.h000066400000000000000000000077231451017372400157660ustar00rootroot00000000000000/** * File name: globals.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_GLOBALS_H #define GEONKICK_GLOBALS_H #include "geonkick.h" #include "RkLog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef RK_LOG_DEBUG_LEVEL #define GEONKICK_LOG_INFO(msg) std::cout << "[" << GEONKICK_NAME << "] " \ << " [" << std::this_thread::get_id() << "] " \ << __PRETTY_FUNCTION__ << "[INFO] " \ << msg << std::endl; #define GEONKICK_LOG_ERROR(msg) std::cout << "[" << GEONKICK_NAME << "] " \ << " [" << std::this_thread::get_id() << "] " \ << __PRETTY_FUNCTION__ << "[ERROR] " \ << msg << std::endl; #define GEONKICK_LOG_DEBUG(msg) std::cout << "[" << GEONKICK_NAME << "] " \ << " [" << std::this_thread::get_id() << "] " \ << __PRETTY_FUNCTION__ << "[DEBUG] " \ << msg << std::endl; #else #define GEONKICK_LOG_INFO(msg) std::cout << "[" << GEONKICK_NAME << "] " \ << "[INFO] " << msg << std::endl; #define GEONKICK_LOG_ERROR(msg) std::cout << "[" << GEONKICK_NAME << "] " \ << "[ERROR] " << msg << std::endl; #define GEONKICK_LOG_DEBUG(msg) #endif // // GKICK_LIB_LOG_LEVEL_DEBUG namespace Geonkick { constexpr int defaultSampleRate = GEONKICK_DEFAULT_SAMPLE_RATE; constexpr char appName[] = GEONKICK_NAME; constexpr char applicationName[] = GEONKICK_APP_NAME; constexpr char applicationVersionStr[] = GEOKICK_VERSION_STRING; constexpr double toDecibel(double val) { if (val < std::numeric_limits::min()) return -80; return 20 * log10(val); } constexpr double fromDecibel(double decibel) { if (decibel < -80) return 0; return pow(10, decibel / 20); } constexpr double truncateDouble(double val, unsigned int precision) { double n = std::pow(10, precision); return std::trunc(val * n) / n; } inline std::string doubleToStr(double val, unsigned int precision) { std::stringstream ss; ss << std::fixed << std::setprecision(precision) << truncateDouble(val, precision); return ss.str(); } } // namespace Geonkick namespace GeonkickTypes { using MidiKey = unsigned char; constexpr MidiKey geonkickAnyKey = GEONKICK_ANY_KEY; enum class Formats : int { Gkick = 0, Gkit = 1, Flac = 2, Wav = 4, Ogg = 7, Sfz = 8 }; } // namespace GeonkickTypes #endif // GEONKICK_GLOBALS_H geonkick-2.10.2/src/kick_graph.cpp000066400000000000000000000117701451017372400167750ustar00rootroot00000000000000/** * File name: kick_graph.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "kick_graph.h" #include "geonkick_api.h" #include "globals.h" #include KickGraph::KickGraph(RkObject *parent, GeonkickApi *api, const RkSize &size) : RkObject(parent) , geonkickApi{api} , graphThread{nullptr} , graphSize{size} , isRunning{true} , updateGraph{true} { RK_ACT_BIND(geonkickApi, kickUpdated, RK_ACT_ARGS(), this, updateGraphBuffer()); } KickGraph::~KickGraph() { if (graphThread) { isRunning = false; threadConditionVar.notify_one(); graphThread->join(); } } void KickGraph::start() { graphThread = std::make_unique(&KickGraph::drawKickGraph, this); } void KickGraph::updateGraphBuffer() { if (!graphThread) start(); std::unique_lock lock(graphMutex); kickBuffer = geonkickApi->getKickBuffer(); updateGraph = true; if (kickBuffer.empty()) geonkickApi->triggerSynthesis(); threadConditionVar.notify_one(); } void KickGraph::drawKickGraph() { while (isRunning) { // Ignore too many updates. The last update will be processed. std::this_thread::sleep_for(std::chrono::milliseconds(60)); std::unique_lock lock(graphMutex); if (!updateGraph) threadConditionVar.wait(lock); if (!isRunning) break; if (kickBuffer.empty()) { updateGraph = false; continue; } auto graphImage = std::make_shared(graphSize.width(), graphSize.height()); RkPainter painter(graphImage.get()); RkPen pen(RkColor(59, 130, 4, 255)); painter.setPen(pen); std::vector graphPoints(kickBuffer.size()); gkick_real k = static_cast(graphSize.width()) / kickBuffer.size(); /** * In this loop there is an implementation of an * antialiasing algorithm that reduces in most of * the cases antialiasing, and at the same time * reduces and normalizes the size of the buffer. */ int j = 0; RkPoint prev; for (decltype(kickBuffer.size()) i = 0; i < kickBuffer.size(); i++) { int x = k * i; int y = graphSize.height() * 0.5 * (1 - kickBuffer[i]); RkPoint p(k * i, graphSize.height() * 0.5 * (1 - kickBuffer[i])); if (p == prev) continue; else prev = p; graphPoints[j++] = p; int i0 = i; int ymin, ymax; ymin = ymax = y; while (++i < kickBuffer.size()) { if (x != static_cast(k * i)) break; y = graphSize.height() * 0.5 * (1 - kickBuffer[i]); if (ymin > y) ymin = y; if (ymax < y) ymax = y; } if (i - i0 > 4) { graphPoints[j++] = {x, ymin}; graphPoints[j++] = {x, ymax}; graphPoints[j++] = {x, y}; } } graphPoints.resize(j); painter.drawPolyline(graphPoints); if (eventQueue()) { auto act = std::make_unique(this); act->setCallback([this, graphImage](void){ graphUpdated(graphImage); }); graphImage.reset(); eventQueue()->postAction(std::move(act)); } updateGraph = false; } } geonkick-2.10.2/src/kick_graph.h000066400000000000000000000033531451017372400164400ustar00rootroot00000000000000/** * File name: kick_graph.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_GRAPTH_H #define GEONKICK_GRAPTH_H #include "geonkick_api.h" #include #include class RkEventQueue; class KickGraph : public RkObject { public: KickGraph(RkObject* parent, GeonkickApi *api, const RkSize &size); ~KickGraph(); void start(); RK_DECL_ACT(graphUpdated, graphUpdated(std::shared_ptr graphImage), RK_ARG_TYPE(std::shared_ptr), RK_ARG_VAL(graphImage)); void updateGraphBuffer(); protected: void drawKickGraph(); private: GeonkickApi *geonkickApi; std::unique_ptr graphThread; std::mutex graphMutex; std::condition_variable threadConditionVar; std::vector kickBuffer; RkSize graphSize; std::atomic isRunning; std::atomic updateGraph; }; #endif // GEONKICK_GRAPH geonkick-2.10.2/src/kit_model.cpp000066400000000000000000000272311451017372400166410ustar00rootroot00000000000000/** * File name: kit.cpp * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "kit_model.h" #include "geonkick_api.h" #include "percussion_state.h" #include "percussion_model.h" #include "kit_state.h" #include "ExportToSfz.h" #include #include KitModel::KitModel(RkObject *parent, GeonkickApi *api) : RkObject(parent) , geonkickApi{api} , midiKeys {"A4", "A#4", "B4", "C5", "C#5", "D5", "D#5", "E5", "F5", "F#5", "G5", "G#5", "A5", "A#5", "B5", "C6", "Any"} { loadModelData(); RK_ACT_BIND(geonkickApi, kitUpdated, RK_ACT_ARGS(), this, loadModelData()); RK_ACT_BIND(geonkickApi, percussionUpdated, RK_ACT_ARGS(size_t id), this, updatePercussion(getIndex(id))); } void KitModel::updatePercussion(PercussionIndex index) { if (isValidIndex(index)) action percussionsList[index]->modelUpdated(); } bool KitModel::isValidIndex(PercussionIndex index) { return index > -1 && static_cast(index) < percussionsList.size(); } void KitModel::selectPercussion(PercussionIndex index) { if (isValidIndex(index) && geonkickApi->setCurrentPercussion(percussionId(index))) { geonkickApi->notifyUpdateGui(); action percussionSelected(); } } bool KitModel::isPercussionSelected(PercussionIndex index) const { return static_cast(percussionId(index)) == geonkickApi->currentPercussion(); } KitModel::PercussionIndex KitModel::selectedPercussion() const { return getIndex(geonkickApi->currentPercussion()); } PercussionModel* KitModel::currentPercussion() const { return percussionsList[getIndex(geonkickApi->currentPercussion())]; } size_t KitModel::numberOfChannels() const { return geonkickApi->numberOfChannels(); } int KitModel::percussionChannel(PercussionIndex index) const { return geonkickApi->getPercussionChannel(percussionId(index)); } bool KitModel::setPercussionChannel(PercussionIndex index, int channel) { return geonkickApi->setPercussionChannel(percussionId(index), channel); } bool KitModel::setPercussionName(PercussionIndex index, const std::string &name) { if (geonkickApi->setPercussionName(percussionId(index), name)) { geonkickApi->notifyUpdateGui(); return true; } return false; } std::string KitModel::percussionName(PercussionIndex index) const { return geonkickApi->getPercussionName(percussionId(index)); } size_t KitModel::keysNumber() const { return midiKeys.size(); } std::string KitModel::keyName(KeyIndex index) const { if (index >= 0 && static_cast(index) < midiKeys.size()) return midiKeys[index]; return std::string(); } bool KitModel::setPercussionKey(PercussionIndex index, KeyIndex keyIndex) { if (!isValidIndex(index)) return false; if (geonkickApi->setPercussionPlayingKey(percussionId(index), keyIndex)) { action percussionUpdated(percussionsList[index]); return true; } return false; } KitModel::KeyIndex KitModel::percussionKey(PercussionIndex index) const { return geonkickApi->getPercussionPlayingKey(percussionId(index)); } void KitModel::playPercussion(PercussionIndex index) { geonkickApi->playKick(percussionId(index)); } bool KitModel::setPercussionLimiter(PercussionIndex index, int value) { double logVal = (75.0 / 100) * value - 55; auto realVal = pow(10, logVal / 20); if (geonkickApi->setPercussionLimiter(percussionId(index), realVal)) { action limiterUpdated(index); return true; } return false; } int KitModel::percussionLimiter(PercussionIndex index) const { auto realVal = geonkickApi->percussionLimiter(percussionId(index)); double logVal = 20 * log10(realVal); int val = (logVal + 55.0) * 100.0 / 75; return val; } int KitModel::percussionLeveler(PercussionIndex index) const { auto realVal = geonkickApi->getLimiterLevelerValue(percussionId(index)); double logVal = 20 * log10(realVal); int val = (logVal + 55.0) * 100.0 / 75; if (val < 0) val = 0; return val; } bool KitModel::mutePercussion(PercussionIndex index, bool b) { return geonkickApi->mutePercussion(percussionId(index), b); } bool KitModel::isPercussionMuted(PercussionIndex index) const { return geonkickApi->isPercussionMuted(percussionId(index)); } bool KitModel::soloPercussion(PercussionIndex index, bool b) { return geonkickApi->soloPercussion(percussionId(index), b); } bool KitModel::isPercussionSolo(PercussionIndex index) const { return geonkickApi->isPercussionSolo(percussionId(index)); } void KitModel::loadModelData() { for (auto &per: percussionsList) delete per; percussionsList.clear(); for (const auto &id : geonkickApi->ordredPercussionIds()) { auto model = new PercussionModel(this, id); percussionsList.push_back(model); } action modelUpdated(); } bool KitModel::open(const std::string &file) { auto kit = std::make_unique(); if (kit->open(file)) { GEONKICK_LOG_ERROR("can't open kit"); return false; } auto filePath = std::filesystem::path(file); auto path = filePath.has_parent_path() ? filePath.parent_path() : filePath; if (!geonkickApi->setKitState(kit)) { GEONKICK_LOG_ERROR("can't set kit state"); return false; } else { geonkickApi->setCurrentWorkingPath("OpenKit", path); loadModelData(); geonkickApi->notifyUpdateGui(); action modelUpdated(); } return true; } bool KitModel::save(const std::string &file) { auto state = geonkickApi->getKitState(); if (!state || !state->save(file)) { GEONKICK_LOG_ERROR("can't save kit state"); return false; } auto filePath = std::filesystem::path(file); auto path = filePath.has_parent_path() ? filePath.parent_path() : filePath; geonkickApi->setCurrentWorkingPath("SaveKit", path); return true; } void KitModel::addNewPercussion() { int newId = geonkickApi->getUnusedPercussion(); if (newId < 0) return; auto state = geonkickApi->getDefaultPercussionState(); state->setId(newId); state->enable(true); geonkickApi->setPercussionState(state); geonkickApi->addOrderedPercussionId(newId); auto model = new PercussionModel(this, newId); percussionsList.push_back(model); action percussionAdded(model); } void KitModel::copyPercussion(PercussionIndex index) { if (!isValidIndex(index)) return; auto newId = geonkickApi->getUnusedPercussion(); if (newId < 0) return; auto state = geonkickApi->getPercussionState(percussionId(index)); if (state) { state->setId(newId); state->enable(true); geonkickApi->setPercussionState(state); geonkickApi->addOrderedPercussionId(newId); auto model = new PercussionModel(this, newId); percussionsList.push_back(model); action percussionAdded(model); } } void KitModel::removePercussion(PercussionIndex index) { if (!isValidIndex(index) || percussionsList.size() < 2) return; for (auto it = percussionsList.begin(); it != percussionsList.end(); ++it) { if ((*it)->index() == index && geonkickApi->enablePercussion(percussionId(index), false)) { action percussionRemoved(index); bool notify = (*it)->isSelected(); delete *it; percussionsList.erase(it); geonkickApi->removeOrderedPercussionId(percussionId(index)); if (notify) { geonkickApi->setCurrentPercussion(percussionId(0)); action selectPercussion(0); } break; } } for (const auto & per: percussionsList) action per->modelUpdated(); } void KitModel::moveSelectedPercussion(bool down) { auto currentIndex = getIndex(geonkickApi->currentPercussion()); auto nextIndex = currentIndex + (down ? 1 : -1); if (isValidIndex(currentIndex) && isValidIndex(nextIndex)) { bool res = geonkickApi->moveOrdrepedPercussionId(geonkickApi->currentPercussion(), down ? 1 : -1); if (res) { percussionsList[currentIndex]->setId(percussionId(currentIndex)); percussionsList[nextIndex]->setId(percussionId(nextIndex)); selectPercussion(nextIndex); } } } size_t KitModel::percussionNumber() const { return geonkickApi->ordredPercussionIds().size(); } size_t KitModel::maxPercussionNumber() const { return geonkickApi->getPercussionsNumber(); } int KitModel::percussionId(int index) const { const auto &ids = geonkickApi->ordredPercussionIds(); if (index < -1 || index > static_cast(ids.size() - 1)) return -1; return ids[index]; } KitModel::PercussionIndex KitModel::getIndex(int id) const { const auto &ids = geonkickApi->ordredPercussionIds(); auto it = std::find(ids.begin(), ids.end(), id); if (it != ids.end()) return it - ids.begin(); return -1; } std::filesystem::path KitModel::workingPath(const std::string &key) const { return geonkickApi->currentWorkingPath(key); } std::filesystem::path KitModel::getHomePath() const { return geonkickApi->getSettings("GEONKICK_CONFIG/HOME_PATH"); } const std::vector& KitModel::percussionModels() const { return percussionsList; } GeonkickApi* KitModel::api() const { return geonkickApi; } bool KitModel::doExport(const std::string &file, KitModel::ExportFormat format) { switch (format) { case ExportFormat::Sfz: { ExportToSfz toSfz(this, file); return toSfz.doExport(); } default: return false; } } RkString KitModel::name() const { return RkString("Unknown"); } RkString KitModel::author() const { return RkString("Unknown"); } RkString KitModel::license() const { return RkString("Unknown"); } std::vector KitModel::instrumentData(PercussionIndex index) const { return geonkickApi->getInstrumentBuffer(percussionId(index)); } geonkick-2.10.2/src/kit_model.h000066400000000000000000000113561451017372400163070ustar00rootroot00000000000000/** * File name: kit_model.h * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef KIT_MODEL_H #define KIT_MODEL_H #include "globals.h" #include "ExportAbstract.h" class GeonkickApi; class GeonkickState; class PercussionModel; class KitModel : public RkObject { public: using ExportFormat = ExportAbstract::ExportFormat; using PercussionIndex = int; using KeyIndex = int; explicit KitModel(RkObject* parent, GeonkickApi *api); bool isValidIndex(PercussionIndex index); bool open(const std::string &file); bool save(const std::string &file); void selectPercussion(PercussionIndex index); bool isPercussionSelected(PercussionIndex index) const; PercussionIndex selectedPercussion() const; PercussionModel* currentPercussion() const; size_t numberOfChannels() const; int percussionChannel(PercussionIndex index) const; bool setPercussionChannel(PercussionIndex index, int channel); bool setPercussionKey(PercussionIndex index, KeyIndex key); KeyIndex percussionKey(PercussionIndex index) const; bool setPercussionName(PercussionIndex index, const std::string &name); std::string percussionName(PercussionIndex index) const; void addNewPercussion(); void copyPercussion(PercussionIndex index); void removePercussion(PercussionIndex index); void moveSelectedPercussion(bool down = true); int percussionKeyIndex(PercussionIndex index) const; size_t keysNumber() const; std::string keyName(KeyIndex index) const; size_t percussionNumber() const; size_t maxPercussionNumber() const; void playPercussion(PercussionIndex index); std::filesystem::path workingPath(const std::string &key) const; std::filesystem::path getHomePath() const; const std::vector& percussionModels() const; PercussionIndex getIndex(int id) const; bool setPercussionLimiter(PercussionIndex index, int value); int percussionLimiter(PercussionIndex index) const; int percussionLeveler(PercussionIndex index) const; bool mutePercussion(PercussionIndex index, bool b); bool isPercussionMuted(PercussionIndex index) const; bool soloPercussion(PercussionIndex index, bool b); bool isPercussionSolo(PercussionIndex index) const; void updatePercussion(PercussionIndex index); GeonkickApi* api() const; bool doExport(const std::string &file, ExportFormat format); RK_DECL_ACT(modelUpdated, modelUpdated(), RK_ARG_TYPE(), RK_ARG_VAL()); RK_DECL_ACT(percussionAdded, percussionAdded(PercussionModel* model), RK_ARG_TYPE(PercussionModel*), RK_ARG_VAL(model)); RK_DECL_ACT(percussionRemoved, percussionRemoved(PercussionIndex index), RK_ARG_TYPE(PercussionIndex), RK_ARG_VAL(index)); RK_DECL_ACT(percussionSelected, percussionSelected(), RK_ARG_TYPE(), RK_ARG_VAL()); RK_DECL_ACT(limiterUpdated, limiterUpdated(PercussionIndex index), RK_ARG_TYPE(PercussionIndex), RK_ARG_VAL(index)); RK_DECL_ACT(percussionUpdated, percussionUpdated(PercussionModel* model), RK_ARG_TYPE(PercussionModel*), RK_ARG_VAL(model)); RkString name() const; RkString author() const; RkString license() const; std::vector instrumentData(PercussionIndex index) const; protected: int percussionId(int index) const; void loadModelData(); private: GeonkickApi *geonkickApi; std::vector percussionsList; std::vector midiKeys; }; #endif // KIT_MODEL_H geonkick-2.10.2/src/kit_state.cpp000066400000000000000000000133431451017372400166600ustar00rootroot00000000000000/** * File name: kit_state.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "percussion_state.h" #include "kit_state.h" KitState::KitState() : kitAppVersion{GEONKICK_VERSION} , kitName{"Default"} , kitAuthor{"Unknown"} { } bool KitState::open(const std::string &fileName) { if (fileName.size() < 6) { RK_LOG_ERROR("can't open preset. File name empty or wrong format."); return false; } std::filesystem::path filePath(fileName); if (filePath.extension().empty() || (filePath.extension() != ".gkit" && filePath.extension() != ".GKIT")) { RK_LOG_ERROR("can't open kit. Wrong file format."); return false; } std::ifstream sfile; sfile.open(std::filesystem::absolute(filePath)); if (!sfile.is_open()) { RK_LOG_ERROR("can't open kit."); return false; } std::string fileData((std::istreambuf_iterator(sfile)), (std::istreambuf_iterator())); sfile.close(); fromJson(fileData); return false; } bool KitState::save(const std::string &fileName) { if (fileName.size() < 6) { RK_LOG_ERROR("can't save kit. Wrong file name"); return false; } std::filesystem::path filePath(fileName); if (filePath.extension().empty() || (filePath.extension() != ".gkit" && filePath.extension() != ".GKIT")) { filePath.replace_extension(".gkit"); } std::ofstream file; file.open(std::filesystem::absolute(filePath)); if (!file.is_open()) { RK_LOG_ERROR("can't open file for saving: " << filePath); return false; } file << toJson(); file.close(); auto path = filePath.has_parent_path() ? filePath.parent_path() : filePath; return true; } void KitState::setName(const std::string &name) { kitName = name; } std::string KitState::getName() const { return kitName; } void KitState::setAuthor(const std::string &author) { kitAuthor = author; } std::string KitState::getAuthor() const { return kitAuthor; } void KitState::setUrl(const std::string &url) { kitUrl = url; } std::string KitState::getUrl() const { return kitUrl; } std::vector>& KitState::percussions() { return percussionsList; } void KitState::fromJson(const std::string &jsonData) { rapidjson::Document document; document.Parse(jsonData.c_str()); if (!document.IsObject()) return; fromJsonObject(document); } void KitState::fromJsonObject(const rapidjson::Value &obj) { for (const auto &m: obj.GetObject()) { if (m.name == "KitAppVersion" && m.value.IsInt()) kitAppVersion = m.value.GetInt(); if (m.name == "name" && m.value.IsString()) setName(m.value.GetString()); if (m.name == "author" && m.value.IsString()) setAuthor(m.value.GetString()); if (m.name == "url" && m.value.IsString()) setUrl(m.value.GetString()); if (m.name == "percussions" && m.value.IsArray()) parsePercussions(m.value); } } void KitState::parsePercussions(const rapidjson::Value &percussionsArray) { size_t i = 0; for (const auto &per: percussionsArray.GetArray()) { auto state = std::make_shared(); state->setId(i++); state->loadObject(per); addPercussion(state); } } std::string KitState::toJson() const { std::ostringstream jsonStream; jsonStream << "{" << std::endl; jsonStream << "\"KitAppVersion\": " << GEONKICK_VERSION << "," << std::endl; jsonStream << "\"name\": \"" << getName() << "\"," << std::endl; jsonStream << "\"author\": \"" << getAuthor() << "\"," << std::endl; jsonStream << "\"url\": \"" << getUrl() << "\"," << std::endl; jsonStream << "\"percussions\": [" << std::endl; size_t i = 0; for (const auto &per: percussionsList) { if (i < percussionsList.size() - 1) jsonStream << per->toJson() << "," << std::endl; else jsonStream << per->toJson(); i++; } jsonStream << "]" << std::endl; jsonStream << "}" << std::endl; return jsonStream.str(); } void KitState::addPercussion(const std::shared_ptr &percussion) { percussionsList.push_back(percussion); } std::shared_ptr KitState::getPercussion(size_t id) { if (id < percussionsList.size()) return percussionsList[id]; return nullptr; } geonkick-2.10.2/src/kit_state.h000066400000000000000000000040051451017372400163200ustar00rootroot00000000000000/** * File name: kit_state.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "globals.h" #include #include #include class PercussionState; class KitState { public: KitState(); bool open(const std::string &fileName); bool save(const std::string &fileName); void fromJson(const std::string &jsonData); void fromJsonObject(const rapidjson::Value &obj); void setName(const std::string &name); std::string getName() const; void setAuthor(const std::string &author); std::string getAuthor() const; void setUrl(const std::string &url); std::string getUrl() const; std::string toJson() const; void addPercussion(const std::shared_ptr &percussion); std::shared_ptr getPercussion(size_t id); std::vector>& percussions(); protected: void parsePercussions(const rapidjson::Value &percussionsArray); private: std::vector> percussionsList; int kitAppVersion; std::string kitName; std::string kitAuthor; std::string kitUrl; }; geonkick-2.10.2/src/kit_widget.cpp000066400000000000000000000245641451017372400170320ustar00rootroot00000000000000/** * File name: kit_widget.cpp * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "kit_widget.h" #include "kit_model.h" #include "geonkick_slider.h" #include "percussion_view.h" #include "percussion_model.h" #include #include #include #include #include #include #include RK_DECLARE_IMAGE_RC(add_per_button); RK_DECLARE_IMAGE_RC(save_kit_button); RK_DECLARE_IMAGE_RC(open_kit_button); RK_DECLARE_IMAGE_RC(export_kit_button); KitWidget::KitWidget(GeonkickWidget *parent, KitModel *model) : GeonkickWidget(parent) , kitModel{model} , addButton{nullptr} , openKitButton{nullptr} , saveKitButton{nullptr} , percussionsContainer{new RkContainer(this, Rk::Orientation::Vertical)} , levelersTimer{new RkTimer(this, 30)} { RK_ACT_BIND(levelersTimer, timeout, RK_ACT_ARGS(), this, onUpdateLevelers()); percussionsContainer->setHiddenTakesPlace(); setSize(parent->size()); RK_ACT_BIND(kitModel, modelUpdated, RK_ACT_ARGS(), this, updateView()); RK_ACT_BIND(kitModel, percussionAdded, RK_ACT_ARGS(PercussionModel *model), this, addPercussion(model)); RK_ACT_BIND(kitModel, percussionRemoved, RK_ACT_ARGS(PercussionIndex index), this, removePercussion(index)); addShortcut(Rk::Key::Key_Up); addShortcut(Rk::Key::Key_Down); addShortcut(Rk::Key::Key_Up, Rk::KeyModifiers::Control_Left); addShortcut(Rk::Key::Key_Up, Rk::KeyModifiers::Control_Right); addShortcut(Rk::Key::Key_Down, Rk::KeyModifiers::Control_Left); addShortcut(Rk::Key::Key_Down, Rk::KeyModifiers::Control_Right); auto kitContainer = new RkContainer(this, Rk::Orientation::Vertical); kitContainer->setHiddenTakesPlace(); kitContainer->setSize(size()); auto topContainer = new RkContainer(this); topContainer->setSpacing(5); percussionsContainer->setHiddenTakesPlace(); topContainer->setSize({width(), 30}); addButton = new RkButton(this); addButton->setBackgroundColor(background()); addButton->setCheckable(true); addButton->setSize(16, 16); addButton->setImage(RkImage(16, 16, RK_IMAGE_RC(add_per_button))); RK_ACT_BIND(addButton, toggled, RK_ACT_ARGS(bool b), kitModel, addNewPercussion()); addButton->show(); topContainer->addWidget(addButton); openKitButton = new RkButton(this); openKitButton->setBackgroundColor(background()); openKitButton->setCheckable(true); openKitButton->setSize(16, 16); openKitButton->setImage(RkImage(16, 16, RK_IMAGE_RC(open_kit_button))); RK_ACT_BIND(openKitButton, toggled, RK_ACT_ARGS(bool b), this, showFileDialog(FileDialog::Type::Open)); openKitButton->show(); topContainer->addWidget(openKitButton); saveKitButton = new RkButton(this); saveKitButton->setBackgroundColor(background()); saveKitButton->setCheckable(true); saveKitButton->setSize(16, 16); saveKitButton->setImage(RkImage(16, 16, RK_IMAGE_RC(save_kit_button))); RK_ACT_BIND(saveKitButton, toggled, RK_ACT_ARGS(bool b), this, showFileDialog(FileDialog::Type::Save)); saveKitButton->show(); topContainer->addWidget(saveKitButton); auto exportKitButton = new RkButton(this); exportKitButton->setBackgroundColor(background()); exportKitButton->setCheckable(true); exportKitButton->setSize(16, 16); exportKitButton->setImage(RkImage(16, 16, RK_IMAGE_RC(export_kit_button))); RK_ACT_BIND(exportKitButton, toggled, RK_ACT_ARGS(bool b), this, exportKitDialog()); exportKitButton->show(); topContainer->addWidget(exportKitButton); percussionsContainer->setHeight(kitContainer->height() - topContainer->height()); auto kitChannelsView = new KitChannelsView(this, kitModel); kitChannelsView->show(); topContainer->addSpace(100 - 4 * 16 - 3 * 5 - 10); topContainer->addWidget(kitChannelsView); kitContainer->addContainer(topContainer); kitContainer->addContainer(percussionsContainer); updateView(); levelersTimer->start(); } void KitWidget::updateView() { percussionsContainer->clear(); for (auto &percussionView: percussionViewList) delete percussionView; percussionViewList.clear(); auto &models = kitModel->percussionModels(); for (const auto &m: models) addPercussion(m); } void KitWidget::addPercussion(PercussionModel *model) { auto percussionView = new KitPercussionView(this, model); percussionsContainer->addWidget(percussionView, Rk::Alignment::AlignTop); percussionViewList.push_back(percussionView); percussionView->show(); } void KitWidget::updatePercussion(PercussionIndex index, PercussionModel *model) { auto percussionView = dynamic_cast(percussionsContainer->at(index)); if (percussionView) percussionView->setModel(model); } void KitWidget::removePercussion(PercussionIndex index) { for (auto it = percussionViewList.begin(); it != percussionViewList.end(); ++it) { if ((*it)->getModel()->index() == index) { percussionsContainer->removeAt(index); delete *it; percussionViewList.erase(it); percussionsContainer->update(); break; } } } void KitWidget::copyPercussion(int index) { kitModel->copyPercussion(index); } void KitWidget::showFileDialog(FileDialog::Type type) { auto fileDialog = new FileDialog(static_cast(getTopWidget()), type, type == FileDialog::Type::Open ? "Open Kit" : "Save Kit"); fileDialog->setPosition(30, 40); fileDialog->setFilters({".gkit", ".GKIT"}); fileDialog->setHomeDirectory(kitModel->getHomePath()); if (type == FileDialog::Type::Open) { fileDialog->setCurrentDirectoy(kitModel->workingPath("OpenKit")); RK_ACT_BIND(fileDialog, selectedFile, RK_ACT_ARGS(const std::string &file), this, openKit(file)); } else { fileDialog->setCurrentDirectoy(kitModel->workingPath("SaveKit")); RK_ACT_BIND(fileDialog, selectedFile, RK_ACT_ARGS(const std::string &file), this, saveKit(file)); } } void KitWidget::openKit(const std::string &file) { if (std::filesystem::is_regular_file(file) && kitModel->open(file)) setFocus(); } void KitWidget::saveKit(const std::string &file) { kitModel->save(file); } void KitWidget::keyPressEvent(RkKeyEvent *event) { if (event->key() != Rk::Key::Key_Up && event->key() != Rk::Key::Key_Down) return; auto index = kitModel->selectedPercussion(); if ((event->modifiers() & static_cast(Rk::KeyModifiers::Control))) { kitModel->moveSelectedPercussion(event->key() == Rk::Key::Key_Down); } else if (event->key() == Rk::Key::Key_Up) { kitModel->selectPercussion(--index); } else if (event->key() == Rk::Key::Key_Down) { kitModel->selectPercussion(++index); } } KitChannelsView::KitChannelsView(KitWidget *parent, KitModel *model) : GeonkickWidget(parent) , kitModel{model} , channelWidth{30} { setSize(kitModel->numberOfChannels() * channelWidth, channelWidth); } void KitChannelsView::paintWidget(RkPaintEvent *event) { RkImage img(size()); RkPainter paint(&img); paint.fillRect(rect(), background()); auto pen = paint.pen(); pen.setColor({200, 200, 200}); auto font = paint.font(); font.setSize(12); paint.setFont(font); auto nChannels = kitModel->numberOfChannels(); for (decltype(nChannels) i = 0; i < nChannels; i++) { auto rect = RkRect(i * channelWidth, 0, channelWidth, channelWidth); if (i % 2) paint.fillRect(rect, {60, 60, 60}); else paint.fillRect(rect, {50, 50, 50}); RkRect txtRect(rect.left(), (rect.height() - paint.font().size()) / 2, rect.width(), paint.font().size()); paint.setPen(pen); paint.drawText(txtRect, std::to_string(i + 1)); } RkPainter painter(this); painter.drawImage(img, 0, 0); } KitModel* KitWidget::getModel() const { return kitModel; } void KitWidget::onUpdateLevelers() { for (const auto &per: percussionViewList) per->updateLeveler(); } void KitWidget::exportKitDialog() { auto fileDialog = new FileDialog(static_cast(getTopWidget()), FileDialog::Type::Save, "Export kit to sfz"); fileDialog->setPosition(30, 40); fileDialog->setFilters({".sfz", ".sfz"}); fileDialog->setHomeDirectory(kitModel->getHomePath()); fileDialog->setCurrentDirectoy(kitModel->workingPath("Export/Kit/Sfz")); RK_ACT_BIND(fileDialog, selectedFile, RK_ACT_ARGS(const std::string &file), kitModel, doExport(file, KitModel::ExportFormat::Sfz)); } geonkick-2.10.2/src/kit_widget.h000066400000000000000000000045361451017372400164740ustar00rootroot00000000000000/** * File name: kit_widget.h * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef KIT_WIDGET_H #define KIT_WIDGET_H #include "geonkick_widget.h" #include "file_dialog.h" #include "RkPainter.h" class RkLineEdit; class RkButton; class KitModel; class RkContainer; class PercussionModel; class KitPercussionView; class RkTimer; class KitWidget: public GeonkickWidget { public: using PercussionIndex = int; KitWidget(GeonkickWidget *parent, KitModel *model); KitModel* getModel() const; protected: void updateView(); void addPercussion(PercussionModel *model); void updatePercussion(PercussionIndex index, PercussionModel *model); void removePercussion(PercussionIndex index); void copyPercussion(int index); void showFileDialog(FileDialog::Type type); void openKit(const std::string &file); void saveKit(const std::string &file); void keyPressEvent(RkKeyEvent *event) override; void onUpdateLevelers(); void exportKitDialog(); private: KitModel *kitModel; RkButton *addButton; RkButton *openKitButton; RkButton *saveKitButton; RkContainer *percussionsContainer; std::vector percussionViewList; RkTimer *levelersTimer; }; class KitChannelsView: public GeonkickWidget { public: explicit KitChannelsView(KitWidget *parent, KitModel *model); protected: void paintWidget(RkPaintEvent *event) override; private: KitModel *kitModel; int channelWidth; }; #endif // KIT_WIDGET_H geonkick-2.10.2/src/knob.cpp000066400000000000000000000127771451017372400156340ustar00rootroot00000000000000/** * File name: knob.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "knob.h" #include #include #define GEONKICK_KNOB_MAX_DEGREE 270 #define GEONKICK_KNOB_MIN_DEGREE 0 #define GEONKICK_KNOB_RANGE_DEGREE (GEONKICK_KNOB_MAX_DEGREE - GEONKICK_KNOB_MIN_DEGREE) Knob::Knob(GeonkickWidget *parent) : GeonkickWidget(parent) , knobValueDegree{GEONKICK_KNOB_MIN_DEGREE} , rangeFrom{0} , rangeTo{0} , rangeType{RangeType::Linear} , isSelected{false} { show(); } void Knob::setKnobImage(const RkImage &img) { knobImage = img; } void Knob::setKnobBackgroundImage(const RkImage &img) { knobBackground = img; } void Knob::paintWidget(RkPaintEvent *event) { RK_UNUSED(event); RkImage img(80, 80); RkPainter painter(&img); painter.fillRect(rect(), background()); if (!knobImage.isNull()) { painter.drawImage(knobBackground, 0, 0); painter.translate(RkPoint(width() / 2, height() / 2)); painter.rotate((2 * M_PI / 360) * knobValueDegree); int x = (width() - knobImage.width()) / 2 - width() / 2; int y = (height() - knobImage.height()) / 2 - height() / 2; painter.drawImage(knobImage, x, y); } RkPainter paint(this); paint.drawImage(img, 0, 0); } void Knob::rotateKnob(int degree) { knobValueDegree += degree; if (knobValueDegree < GEONKICK_KNOB_MIN_DEGREE) knobValueDegree = GEONKICK_KNOB_MIN_DEGREE; else if (knobValueDegree > GEONKICK_KNOB_MAX_DEGREE) knobValueDegree = GEONKICK_KNOB_MAX_DEGREE; double k = (knobValueDegree - GEONKICK_KNOB_MIN_DEGREE) / GEONKICK_KNOB_RANGE_DEGREE; if (getRangeType() == RangeType::Logarithmic) { double logVal = log10(rangeFrom) + k * (log10(rangeTo) - log10(rangeFrom)); valueUpdated(pow(10, logVal)); } else { valueUpdated(rangeFrom + k * (rangeTo - rangeFrom)); } } void Knob::mouseButtonPressEvent(RkMouseEvent *event) { setFocus(true); if (event->button() == RkMouseEvent::ButtonType::WheelUp || event->button() == RkMouseEvent::ButtonType::WheelDown) { rotateKnob(event->button() == RkMouseEvent::ButtonType::WheelUp ? 2 : -2); update(); return; } if (!knobImage.isNull()) { int xCenter = width() / 2; int yCenter = height() / 2; int r = knobImage.width() / 2; if ((event->x() - xCenter) * (event->x() - xCenter) + (event->y() - yCenter) * (event->y() - yCenter) <= r * r) { isSelected = true; lastPositionPoint.setX(event->x()); lastPositionPoint.setY(event->y()); } } } void Knob::mouseButtonReleaseEvent(RkMouseEvent *event) { RK_UNUSED(event); isSelected = false; } void Knob::mouseMoveEvent(RkMouseEvent *event) { if (isSelected) { int dy = event->y() - lastPositionPoint.y(); rotateKnob(-dy); lastPositionPoint.setX(event->x()); lastPositionPoint.setY(event->y()); update(); } } double Knob::getValue(void) const { double k = (knobValueDegree - GEONKICK_KNOB_MIN_DEGREE) / GEONKICK_KNOB_RANGE_DEGREE; double val; if (getRangeType() == RangeType::Logarithmic) { double logVal = log10(rangeFrom) + k * (log10(rangeTo) - log10(rangeFrom)); val = pow(10, logVal); } else { val = rangeFrom + k * (rangeTo - rangeFrom); } return val; } void Knob::setRange(double from, double to) { rangeFrom = from; rangeTo = to; } void Knob::setRangeType(RangeType type) { rangeType = type; } Knob::RangeType Knob::getRangeType() const { return rangeType; } void Knob::setCurrentValue(double val) { if (val > rangeTo) val = rangeTo; else if (val < rangeFrom) val = rangeFrom; double k = 0; if (fabs(rangeTo - rangeFrom) < std::numeric_limits::epsilon()) { knobValueDegree = GEONKICK_KNOB_MIN_DEGREE; } else { if (getRangeType() == RangeType::Logarithmic) k = (log10(val) - log10(rangeFrom)) / (log10(rangeTo) - log10(rangeFrom)); else k = (val - rangeFrom) / (rangeTo - rangeFrom); } knobValueDegree = GEONKICK_KNOB_MIN_DEGREE + k * GEONKICK_KNOB_RANGE_DEGREE; update(); } geonkick-2.10.2/src/knob.h000066400000000000000000000040301451017372400152600ustar00rootroot00000000000000/** * File name: knob.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GKICK_KNOB_H #define GKICK_KNOB_H #include "geonkick_widget.h" #include #include class RkMouseEvent; class Knob : public GeonkickWidget { public: enum RangeType: int { Linear, Logarithmic }; explicit Knob(GeonkickWidget *parent); virtual ~Knob() = default; double getValue(void) const; void setRange(double from, double to); void setRangeType(RangeType type); RangeType getRangeType() const; void setCurrentValue(double val); void setKnobImage(const RkImage &img); void setKnobBackgroundImage(const RkImage &img); void rotateKnob(int degree); RK_DECL_ACT(valueUpdated, valueUpdated(double v), RK_ARG_TYPE(double), RK_ARG_VAL(v)); protected: void paintWidget(RkPaintEvent *event) final; void mouseButtonPressEvent(RkMouseEvent *event) final; void mouseButtonReleaseEvent(RkMouseEvent *event) final; void mouseMoveEvent(RkMouseEvent *event) final; private: RkImage knobImage; RkImage knobBackground; RkPoint lastPositionPoint; double knobValueDegree; double rangeFrom; double rangeTo; RangeType rangeType; bool isSelected; }; #endif geonkick-2.10.2/src/layers_group_box.cpp000066400000000000000000000051501451017372400202510ustar00rootroot00000000000000/** * File name: layers_group_box.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "layers_group_box.h" #include "geonkick_slider.h" #include "geonkick_button.h" #include "geonkick_api.h" #include RK_DECLARE_IMAGE_RC(layers_mixer); LayersGroupBox::LayersGroupBox(GeonkickApi *api, GeonkickWidget *parent) : GeonkickGroupBox(parent) , geonkickApi{api} , layerSliders{nullptr, nullptr, nullptr} { setFixedSize(110, 65); setBackgroundImage(RkImage(size(), RK_IMAGE_RC(layers_mixer))); int y = 23; for (auto i = 0; i < 3; i++) { layerSliders[i] = new GeonkickSlider(this); layerSliders[i]->setFixedSize(width() - 38, 10); layerSliders[i]->setPosition(18, y); y += layerSliders[i]->height() + 6; layerSliders[i]->show(); RK_ACT_BIND(layerSliders[i], valueUpdated, RK_ACT_ARGS(int val), this, setLayerAmplitude(i, val)); } show(); updateGui(); } void LayersGroupBox::setLayerAmplitude(int layer, int val) { double logVal = -60 * (1.0 - (static_cast(val) / 100)); double amplitude = pow(10, logVal / 20); geonkickApi->setLayerAmplitude(static_cast(layer), amplitude); } void LayersGroupBox::updateGui() { for (auto i = 0; i < 3; i++) { double amplitude = geonkickApi->getLayerAmplitude(static_cast(i)); double logVal; if (amplitude > 0) logVal = 20 * log10(amplitude); else logVal = 60; layerSliders[i]->onSetValue(100 * (60 - fabs(logVal)) / 60); } } geonkick-2.10.2/src/layers_group_box.h000066400000000000000000000025671451017372400177270ustar00rootroot00000000000000/** * File name: layers_group_box.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_LAYERS_GROUPBOX_H #define GEONKICK_LAYERS_GROUPBOX_H #include "geonkick_groupbox.h" class GeonkickSlider; class GeonkickCheckbox; class GeonkickApi; class GeonkickButton; class LayersGroupBox: public GeonkickGroupBox { public: LayersGroupBox(GeonkickApi *api, GeonkickWidget *parent); void updateGui(); protected: void setLayerAmplitude(int layer, int val); private: GeonkickApi *geonkickApi; GeonkickSlider *layerSliders[3]; }; #endif // GEONKICK_LAYERS_WIDGET_H geonkick-2.10.2/src/limiter.cpp000066400000000000000000000103301451017372400163270ustar00rootroot00000000000000/** * File name: limiter.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "limiter.h" #include "geonkick_slider.h" #include "geonkick_api.h" #include RK_DECLARE_IMAGE_RC(meter_scale); Limiter::Limiter(GeonkickApi *api, GeonkickWidget *parent) : GeonkickWidget(parent) , geonkickApi{api} , faderSlider{new GeonkickSlider(this, GeonkickSlider::Orientation::Vertical)} , meterValue{0} , meterTimer{new RkTimer(this, 30)} , levelerValueTimer{new RkTimer(this, 40)} , scaleImage{36, 300, RK_IMAGE_RC(meter_scale)} { setFixedSize(65, scaleImage.height()); faderSlider->setPosition(0, 0); faderSlider->setFixedSize(20, height()); RK_ACT_BIND(faderSlider, valueUpdated, RK_ACT_ARGS(int val), this, onSetLimiterValue(val)); RK_ACT_BIND(levelerValueTimer, timeout, RK_ACT_ARGS(), this, onUpdateMeter()); RK_ACT_BIND(meterTimer, timeout, RK_ACT_ARGS(), this, onUpdateMeterTimeout()); onUpdateLimiter(); meterTimer->start(); levelerValueTimer->start(); } void Limiter::paintWidget(RkPaintEvent *event) { RK_UNUSED(event); RkImage img(width(), height()); RkPainter painter(&img); painter.fillRect(rect(), background()); int x = faderSlider->width() + 10; int meterWidth = 7; int meterHeight = scaleImage.height() - 4; int meterPadding = 2; int meterInnerW = meterWidth - 2 * meterPadding; int meterInnerH = meterHeight - 2 * meterPadding; int meterPixels = meterInnerH * (static_cast(meterValue) / 100); painter.drawImage(scaleImage, 25, 0); painter.fillRect(RkRect(x + 2, meterPadding + height() - meterPixels, meterInnerW, meterPixels), RkColor(125, 200, 125)); RkPainter paint(this); paint.drawImage(img, 0, 0); } void Limiter::onUpdateMeterTimeout() { int val = getMeterValue() - 1; if (val > -1) onSetMeterValue(val); } int Limiter::getFaderValue(void) const { return faderSlider->getValue(); } int Limiter::getMeterValue() const { return meterValue; } void Limiter::onSetFaderValue(int val) { faderSlider->onSetValue(val); } void Limiter::onUpdateMeter() { int value = toMeterValue(std::fabs(geonkickApi->getLimiterLevelerValue())); if (meterValue < value) onSetMeterValue(value); } void Limiter::onSetMeterValue(int val) { meterValue = val; update(); } void Limiter::onUpdateLimiter() { double val = geonkickApi->limiterValue(); if (val < 1e-3) onSetFaderValue(0); else onSetFaderValue(toMeterValue(val)); } int Limiter::toMeterValue(double val) const { if (val < 1e-3) return 0; double logVal = 20 * log10(val); double k = 70.0 / (1 - 0.07); double b = 20.0 - k; double c = (b / k); double x = logVal / k - c ; int value = 100 * x; return value; } void Limiter::onSetLimiterValue(int val) { double k = 70.0 / (1 - 0.07); double b = 20.0 - k; double x = static_cast(val) / 100; double logVal = k * x + b; double value; if (logVal < -60) value = 0; else value = pow(10, logVal / 20); geonkickApi->setLimiterValue(value); action limiterUpdated(val); } geonkick-2.10.2/src/limiter.h000066400000000000000000000036311451017372400160020ustar00rootroot00000000000000/** * File name: limiter.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_LIMITER_H #define GEONKICK_LIMITER_H #include "geonkick_widget.h" #include class GeonkickSlider; class GeonkickLevel; class GeonkickApi; class RkTimer; class Limiter: public GeonkickWidget { public: Limiter(GeonkickApi *api, GeonkickWidget *parent); int getFaderValue(void) const; int getMeterValue() const; RK_DECL_ACT(limiterUpdated, limiterUpdated(int val), RK_ARG_TYPE(int), RK_ARG_VAL(val)); void onUpdateLimiter(); void onSetLimiterValue(int val); protected: int toMeterValue(double val) const; void onUpdateMeter(); void onUpdateMeterTimeout(); void onSetFaderValue(int val); void onSetMeterValue(int val); private: void paintWidget(RkPaintEvent *event) final; GeonkickApi *geonkickApi; GeonkickSlider *faderSlider; int meterValue; RkTimer *meterTimer; RkTimer *levelerValueTimer; RkImage scaleImage; }; #endif // GEONKICK_LIMITER_H geonkick-2.10.2/src/oscillator.cpp000066400000000000000000000134571451017372400170520ustar00rootroot00000000000000/** * File name: oscillator.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "oscillator.h" Oscillator::Oscillator(GeonkickApi *api, Oscillator::Type type) : geonkickApi{api} , oscillatorType{type} , filterType{FilterType::LowPass} { RK_ACT_BIND(geonkickApi, kickLengthUpdated, RK_ACT_ARGS(double val), this, kickLengthUpdated(val)); } void Oscillator::setAsFm(bool b) { geonkickApi->setOscillatorAsFm(index(), b); } bool Oscillator::isFm() const { return geonkickApi->isOscillatorAsFm(index()); } void Oscillator::setFunction(FunctionType func) { geonkickApi->setOscillatorFunction(index(), static_cast(func)); } void Oscillator::setPhase(gkick_real phase) { geonkickApi->setOscillatorPhase(index(), phase); } gkick_real Oscillator::getPhase() const { return geonkickApi->oscillatorPhase(index()); } void Oscillator::setSeed(int seed) { geonkickApi->setOscillatorSeed(index(), seed); } int Oscillator::getSeed() const { return geonkickApi->oscillatorSeed(index()); } Oscillator::FunctionType Oscillator::function() const { return static_cast(geonkickApi->oscillatorFunction(index())); } std::vector Oscillator::envelopePoints(EnvelopeType envelope) const { return geonkickApi->oscillatorEvelopePoints(index(), static_cast(envelope)); } void Oscillator::addEnvelopePoint(EnvelopeType envelope, double x, double y) { geonkickApi->addOscillatorEnvelopePoint(index(), static_cast(envelope), RkRealPoint(x, y)); } void Oscillator::removeEnvelopePoint(EnvelopeType envelope, int point_index) { geonkickApi->removeOscillatorEvelopePoint(index(), static_cast(envelope), point_index); } void Oscillator::updateEnvelopePoint(EnvelopeType envelope, int point_index, double x, double y) { geonkickApi->updateOscillatorEvelopePoint(index(), static_cast(envelope), point_index, RkRealPoint(x, y)); } void Oscillator::setType(Oscillator::Type type) { oscillatorType = type; } Oscillator::Type Oscillator::type() const { return oscillatorType; } void Oscillator::setAmplitude(double amp) { if (geonkickApi->setOscillatorAmplitude(index(), amp)) amplitudeUpdated(amp); } double Oscillator::amplitude(void) const { return geonkickApi->oscillatorAmplitude(index()); } void Oscillator::setFrequency(double freq) { if (geonkickApi->setOscillatorFrequency(index(), freq)) frequencyUpdated(freq); } void Oscillator::setPitchShift(double semitones) { if (geonkickApi->setOscillatorPitchShift(index(), semitones)) pitchShiftUpdated(semitones); } double Oscillator::frequency(void) const { return geonkickApi->oscillatorFrequency(index()); } double Oscillator::pitchShift(void) const { return geonkickApi->oscillatorPitchShift(index()); } int Oscillator::index() const { return static_cast(oscillatorType); } Oscillator::FilterType Oscillator::filter() const { return static_cast(geonkickApi->getOscillatorFilterType(index())); } void Oscillator::setFilterType(FilterType filter) { geonkickApi->setOscillatorFilterType(index(), static_cast(filter)); } void Oscillator::enableFilter(bool b) { geonkickApi->enableOscillatorFilter(index(), b); } bool Oscillator::isFilterEnabled() const { return geonkickApi->isOscillatorFilterEnabled(index()); } void Oscillator::setFilterFrequency(double f) { geonkickApi->setOscillatorFilterCutOffFreq(index(), f); } double Oscillator::filterFrequency(void) const { return geonkickApi->getOscillatorFilterCutOffFreq(index()); } void Oscillator::setFilterQFactor(double factor) { return geonkickApi->setOscillatorFilterFactor(index(), factor); } double Oscillator::filterQFactor() const { return geonkickApi->getOscillatorFilterFactor(index()); } void Oscillator::enable(bool b) { geonkickApi->enableOscillator(index(), b); } bool Oscillator::isEnabled() const { return geonkickApi->isOscillatorEnabled(index()); } double Oscillator::envelopeLength() const { return geonkickApi->kickLength(); } void Oscillator::setSample(const std::string &file) { geonkickApi->setOscillatorSample(file, index()); auto path = std::filesystem::path(file); geonkickApi->setCurrentWorkingPath("Samples", path.has_parent_path() ? path.parent_path() : path); } std::string Oscillator::samplesPath() const { return geonkickApi->currentWorkingPath("Samples"); } geonkick-2.10.2/src/oscillator.h000066400000000000000000000066021451017372400165110ustar00rootroot00000000000000/** * File name: gkick_oscillator.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_OSCILLATOR_H #define GEONKICK_OSCILLATOR_H #include "geonkick_api.h" class Oscillator: public RkObject { public: using Type = GeonkickApi::OscillatorType; using FunctionType = GeonkickApi::FunctionType; using EnvelopeType = GeonkickApi::EnvelopeType; using FilterType = GeonkickApi::FilterType; explicit Oscillator(GeonkickApi *api, Oscillator::Type type); Oscillator::FunctionType function() const; std::vector envelopePoints(EnvelopeType envelope) const; double amplitude(void) const; double frequency(void) const; double pitchShift(void) const; void setType(Type type); Oscillator::Type type(void) const; bool isFilterEnabled() const; Oscillator::FilterType filter() const; double filterFrequency(void) const; double filterQFactor() const; bool isEnabled() const; double envelopeLength() const; void enable(bool b); void setAsFm(bool b); bool isFm() const; void setFunction(FunctionType func); void setPhase(gkick_real phase); gkick_real getPhase() const; void setSeed(int seed); int getSeed() const; void setAmplitude(double amp); void setFrequency(double freq); void setPitchShift(double semitones); void enableFilter(bool b); void setFilterType(FilterType filter); void setFilterFrequency(double f); void setFilterQFactor(double factor); void addEnvelopePoint(EnvelopeType envelope, double x, double y); void removeEnvelopePoint(EnvelopeType envelope, int point_index); void updateEnvelopePoint(EnvelopeType envelope, int point_index, double x, double y); void setSample(const std::string &file); int index() const; std::string samplesPath() const; RK_DECL_ACT(amplitudeUpdated, amplitudeUpdated(double v), RK_ARG_TYPE(double), RK_ARG_VAL(v)); RK_DECL_ACT(frequencyUpdated, frequencyUpdated(double v), RK_ARG_TYPE(double), RK_ARG_VAL(v)); RK_DECL_ACT(pitchShiftUpdated, pitchShiftUpdated(double semitones), RK_ARG_TYPE(double), RK_ARG_VAL(semitones)); RK_DECL_ACT(kickLengthUpdated, kickLengthUpdated(double len), RK_ARG_TYPE(double), RK_ARG_VAL(len)); protected: int envelopeIndex(EnvelopeType type) const; private: GeonkickApi *geonkickApi; Type oscillatorType; FilterType filterType; }; #endif // GEONKICK_OSCILLATOR_H geonkick-2.10.2/src/oscillator_envelope.cpp000066400000000000000000000052641451017372400207440ustar00rootroot00000000000000/** * File name: oscillator_envelope.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "oscillator_envelope.h" #include "oscillator.h" OscillatorEnvelope::OscillatorEnvelope(Oscillator* osc, const RkRect &area) : Envelope(area) , oscillator{osc} { removeSupportedType(Envelope::Type::DistortionDrive); removeSupportedType(Envelope::Type::DistortionVolume); if (oscillator->type() == Oscillator::Type::Noise) removeSupportedType(Envelope::Type::Frequency); setType(Envelope::Type::Amplitude); setPoints(oscillator->envelopePoints(static_cast(type()))); } void OscillatorEnvelope::updatePoints() { setPoints(oscillator->envelopePoints(static_cast(type()))); } void OscillatorEnvelope::pointAddedEvent(double x, double y) { oscillator->addEnvelopePoint(static_cast(type()), x, y); } void OscillatorEnvelope::pointUpdatedEvent(unsigned int index, double x, double y) { oscillator->updateEnvelopePoint(static_cast(type()), index, x, y); } void OscillatorEnvelope::pointRemovedEvent(unsigned int index) { oscillator->removeEnvelopePoint(static_cast(type()), index); } double OscillatorEnvelope::envelopeLength(void) const { return oscillator->envelopeLength(); } double OscillatorEnvelope::envelopeAmplitude() const { switch (type()) { case Type::Amplitude: return oscillator->amplitude(); case Type::Frequency: return oscillator->frequency(); case Type::PitchShift: return oscillator->pitchShift(); case Type::FilterCutOff: return oscillator->filterFrequency(); default: return 0; } } Oscillator* OscillatorEnvelope::getOscillator() const { return oscillator; } geonkick-2.10.2/src/oscillator_envelope.h000066400000000000000000000027211451017372400204040ustar00rootroot00000000000000/** * File name: oscillator_envelope.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GKICK_OSCILLLATOR_ENVELOPE_H #define GKICK_OSCILLLATOR_ENVELOPE_H #include "envelope.h" class Oscillator; class OscillatorEnvelope: public Envelope { public: OscillatorEnvelope(Oscillator* osc, const RkRect &area); double envelopeLength(void) const; Oscillator* getOscillator() const; protected: void updatePoints() final; void pointAddedEvent(double x, double y) final; void pointUpdatedEvent(unsigned int index, double x, double y) final; void pointRemovedEvent(unsigned int index) final; double envelopeAmplitude() const final; private: Oscillator *oscillator; }; #endif geonkick-2.10.2/src/oscillator_group_box.cpp000066400000000000000000000753121451017372400211340ustar00rootroot00000000000000/** * File name: oscillator_group_box.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "oscillator_group_box.h" #include "oscillator.h" #include "geonkick_widget.h" #include "geonkick_button.h" #include "knob.h" #include "geonkick_slider.h" #include "filter.h" #include "file_dialog.h" #include "ViewState.h" #include RK_DECLARE_IMAGE_RC(controls_osc1_on); RK_DECLARE_IMAGE_RC(controls_osc1_hover); RK_DECLARE_IMAGE_RC(controls_osc1_off); RK_DECLARE_IMAGE_RC(controls_osc2_on); RK_DECLARE_IMAGE_RC(controls_osc2_hover); RK_DECLARE_IMAGE_RC(controls_osc2_off); RK_DECLARE_IMAGE_RC(controls_noise_on); RK_DECLARE_IMAGE_RC(controls_noise_hover); RK_DECLARE_IMAGE_RC(controls_noise_off); RK_DECLARE_IMAGE_RC(fm_radio); RK_DECLARE_IMAGE_RC(fm_radio_hover); RK_DECLARE_IMAGE_RC(fm_radio_active); RK_DECLARE_IMAGE_RC(wf_bk_hbox); RK_DECLARE_IMAGE_RC(wave_button_sine); RK_DECLARE_IMAGE_RC(wave_button_sine_active); RK_DECLARE_IMAGE_RC(wave_button_square); RK_DECLARE_IMAGE_RC(wave_button_square_active); RK_DECLARE_IMAGE_RC(wave_button_triangle); RK_DECLARE_IMAGE_RC(wave_button_triangle_active); RK_DECLARE_IMAGE_RC(wave_button_sawtooth); RK_DECLARE_IMAGE_RC(phase_label); RK_DECLARE_IMAGE_RC(wave_button_sample); RK_DECLARE_IMAGE_RC(wave_button_sample_active); RK_DECLARE_IMAGE_RC(wave_button_sawtooth_active); RK_DECLARE_IMAGE_RC(button_browse_sample); RK_DECLARE_IMAGE_RC(hboxbk_noise_env); RK_DECLARE_IMAGE_RC(hboxbk_osc_env); RK_DECLARE_IMAGE_RC(knob); RK_DECLARE_IMAGE_RC(noise_type_white); RK_DECLARE_IMAGE_RC(noise_type_white_active); RK_DECLARE_IMAGE_RC(noise_type_brownian); RK_DECLARE_IMAGE_RC(noise_type_brownian_active); RK_DECLARE_IMAGE_RC(knob_bk_image); RK_DECLARE_IMAGE_RC(osc_ampl_button_off); RK_DECLARE_IMAGE_RC(osc_ampl_button_on); RK_DECLARE_IMAGE_RC(osc_ampl_button_hover); RK_DECLARE_IMAGE_RC(osc_freq_button_off); RK_DECLARE_IMAGE_RC(osc_freq_button_on); RK_DECLARE_IMAGE_RC(osc_freq_button_hover); RK_DECLARE_IMAGE_RC(osc_pitch_button_off); RK_DECLARE_IMAGE_RC(osc_pitch_button_on); RK_DECLARE_IMAGE_RC(osc_pitch_button_hover); OscillatorGroupBox::OscillatorGroupBox(GeonkickWidget *parent, Oscillator *osc) : GeonkickGroupBox{parent} , oscillator{osc} , oscillatorCheckbox{nullptr} , fmCheckbox{nullptr} , filterBox{nullptr} , sineButton{nullptr} , squareButton{nullptr} , triangleButton{nullptr} , sawtoothButton{nullptr} , sampleButton{nullptr} , sampleBrowseButton{nullptr} , phaseSlider{nullptr} , noiseWhiteButton{nullptr} , noiseBrownianButton{nullptr} , amplitudeKnob{nullptr} , frequencyKnob{nullptr} , pitchShiftKnob{nullptr} , filterTypeIsChecked{false} , amplitudeEnvelopeBox{nullptr} , oscFreqEnvelopeButton{nullptr} , pitchEnvelopeButton{nullptr} { setFixedSize(224, 380); oscillatorCheckbox = new GeonkickButton(this); oscillatorCheckbox->setCheckable(true); oscillatorCheckbox->setX(3); RK_ACT_BIND(oscillatorCheckbox, toggled, RK_ACT_ARGS(bool b), oscillator, enable(b)); if (oscillator->type() == Oscillator::Type::Oscillator1) { oscillatorCheckbox->setFixedSize(69, 21); oscillatorCheckbox->setImage(RkImage(oscillatorCheckbox->size(), RK_IMAGE_RC(controls_osc1_on)), RkButton::State::Pressed); oscillatorCheckbox->setImage(RkImage(oscillatorCheckbox->size(), RK_IMAGE_RC(controls_osc1_off)), RkButton::State::Unpressed); oscillatorCheckbox->setImage(RkImage(oscillatorCheckbox->size(), RK_IMAGE_RC(controls_osc1_hover)), RkButton::State::UnpressedHover); oscillatorCheckbox->setImage(RkImage(oscillatorCheckbox->size(), RK_IMAGE_RC(controls_osc1_hover)), RkButton::State::PressedHover); fmCheckbox = new GeonkickButton(this); fmCheckbox->setCheckable(true); fmCheckbox->setPosition(oscillatorCheckbox->x() + oscillatorCheckbox->width() + 5, 0); fmCheckbox->setFixedSize(76, 21); fmCheckbox->setImage(RkImage(fmCheckbox->size(), RK_IMAGE_RC(fm_radio_active)), RkButton::State::Pressed); fmCheckbox->setImage(RkImage(fmCheckbox->size(), RK_IMAGE_RC(fm_radio)), RkButton::State::Unpressed); fmCheckbox->setImage(RkImage(fmCheckbox->size(), RK_IMAGE_RC(fm_radio_hover)), RkButton::State::UnpressedHover); fmCheckbox->setImage(RkImage(fmCheckbox->size(), RK_IMAGE_RC(fm_radio_hover)), RkButton::State::PressedHover); RK_ACT_BIND(fmCheckbox, toggled, RK_ACT_ARGS(bool b), oscillator, setAsFm(b)); } else if (oscillator->type() == Oscillator::Type::Oscillator2) { oscillatorCheckbox->setFixedSize(69, 21); oscillatorCheckbox->setImage(RkImage(oscillatorCheckbox->size(), RK_IMAGE_RC(controls_osc2_on)), RkButton::State::Pressed); oscillatorCheckbox->setImage(RkImage(oscillatorCheckbox->size(), RK_IMAGE_RC(controls_osc2_off)), RkButton::State::Unpressed); oscillatorCheckbox->setImage(RkImage(oscillatorCheckbox->size(), RK_IMAGE_RC(controls_osc2_hover)), RkButton::State::UnpressedHover); oscillatorCheckbox->setImage(RkImage(oscillatorCheckbox->size(), RK_IMAGE_RC(controls_osc2_hover)), RkButton::State::PressedHover); } else { oscillatorCheckbox->setFixedSize(69, 21); oscillatorCheckbox->setImage(RkImage(oscillatorCheckbox->size(), RK_IMAGE_RC(controls_noise_on)), RkButton::State::Pressed); oscillatorCheckbox->setImage(RkImage(oscillatorCheckbox->size(), RK_IMAGE_RC(controls_noise_off)), RkButton::State::Unpressed); oscillatorCheckbox->setImage(RkImage(oscillatorCheckbox->size(), RK_IMAGE_RC(controls_noise_hover)), RkButton::State::UnpressedHover); oscillatorCheckbox->setImage(RkImage(oscillatorCheckbox->size(), RK_IMAGE_RC(controls_noise_hover)), RkButton::State::PressedHover); } if (oscillator->type() != Oscillator::Type::Noise) createWaveFunctionGroupBox(); createEvelopeGroupBox(); createFilterGroupBox(); updateGui(); } void OscillatorGroupBox::createWaveFunctionGroupBox() { auto waveFunctionHBox = new GeonkickWidget(this); waveFunctionHBox->setBackgroundColor(67, 68, 68); waveFunctionHBox->setFixedSize(224, 85); waveFunctionHBox->setPosition(0, 25); waveFunctionHBox->setBackgroundImage(RkImage(waveFunctionHBox->size(), RK_IMAGE_RC(wf_bk_hbox))); waveFunctionHBox->show(); sineButton = new GeonkickButton(waveFunctionHBox); sineButton->setBackgroundColor(waveFunctionHBox->background()); sineButton->setFixedSize(67, 14); sineButton->setPosition(20, 25); sineButton->setUnpressedImage(RkImage(sineButton->size(), RK_IMAGE_RC(wave_button_sine))); sineButton->setPressedImage(RkImage(sineButton->size(), RK_IMAGE_RC(wave_button_sine_active))); RK_ACT_BIND(sineButton, toggled, RK_ACT_ARGS(bool b), this, setSineWave(b)); sineButton->show(); squareButton = new GeonkickButton(waveFunctionHBox); squareButton->setBackgroundColor(waveFunctionHBox->background()); squareButton->setFixedSize(67, 14); squareButton->setPosition(sineButton->x(), sineButton->y() + sineButton->height() + 5); squareButton->setUnpressedImage(RkImage(squareButton->size(), RK_IMAGE_RC(wave_button_square))); squareButton->setPressedImage(RkImage(squareButton->size(), RK_IMAGE_RC(wave_button_square_active))); RK_ACT_BIND(squareButton, toggled, RK_ACT_ARGS(bool b), this, setSquareWave(b)); squareButton->show(); triangleButton = new GeonkickButton(waveFunctionHBox); triangleButton->setBackgroundColor(waveFunctionHBox->background()); triangleButton->setFixedSize(67, 14); triangleButton->setPosition(sineButton->x() + 60, sineButton->y()); triangleButton->setUnpressedImage(RkImage(triangleButton->size(), RK_IMAGE_RC(wave_button_triangle))); triangleButton->setPressedImage(RkImage(triangleButton->size(), RK_IMAGE_RC(wave_button_triangle_active))); RK_ACT_BIND(triangleButton, toggled, RK_ACT_ARGS(bool b), this, setTriangleWave(b)); triangleButton->show(); sawtoothButton = new GeonkickButton(waveFunctionHBox); sawtoothButton->setBackgroundColor(waveFunctionHBox->background()); sawtoothButton->setSize(67, 14); sawtoothButton->setPosition(sineButton->x() + 60, sineButton->y() + sineButton->height() + 5); sawtoothButton->setUnpressedImage(RkImage(sawtoothButton->size(), RK_IMAGE_RC(wave_button_sawtooth))); sawtoothButton->setPressedImage(RkImage(sawtoothButton->size(), RK_IMAGE_RC(wave_button_sawtooth_active))); RK_ACT_BIND(sawtoothButton, toggled, RK_ACT_ARGS(bool b), this, setSawtoothWave(b)); sawtoothButton->show(); sampleButton = new GeonkickButton(waveFunctionHBox); sampleButton->setBackgroundColor(waveFunctionHBox->background()); sampleButton->setFixedSize(67, 14); sampleButton->setPosition(triangleButton->x() + 73, triangleButton->y()); sampleButton->setUnpressedImage(RkImage(sampleButton->size(), RK_IMAGE_RC(wave_button_sample))); sampleButton->setPressedImage(RkImage(sampleButton->size(), RK_IMAGE_RC(wave_button_sample_active))); RK_ACT_BIND(sampleButton, toggled, RK_ACT_ARGS(bool b), this, setSampleFunction(b)); sampleBrowseButton = new GeonkickButton(waveFunctionHBox); sampleBrowseButton->setCheckable(true); sampleBrowseButton->setBackgroundColor(waveFunctionHBox->background()); sampleBrowseButton->setFixedSize(67, 14); sampleBrowseButton->setPosition(triangleButton->x() + 73, sampleButton->y() + sampleButton->height() + 5); sampleBrowseButton->setUnpressedImage(RkImage(sampleBrowseButton->size(), RK_IMAGE_RC(button_browse_sample))); RK_ACT_BIND(sampleBrowseButton, pressed, RK_ACT_ARGS(), this, browseSample()); auto phaseLabel = new RkLabel(waveFunctionHBox); phaseLabel->setFixedSize(30, 8); phaseLabel->setPosition(sineButton->x(), sawtoothButton->y() + sawtoothButton->height() + 8); phaseLabel->setBackgroundColor(waveFunctionHBox->background()); phaseLabel->setImage(RkImage(phaseLabel->size(), RK_IMAGE_RC(phase_label))); phaseLabel->show(); phaseSlider = new GeonkickSlider(waveFunctionHBox); phaseSlider->setFixedSize(150, 8); phaseSlider->onSetValue(50); phaseSlider->setPosition(phaseLabel->x() + phaseLabel->width() + 5, phaseLabel->y() + 1); phaseSlider->show(); RK_ACT_BIND(phaseSlider, valueUpdated, RK_ACT_ARGS(int value), this, setOscillatorPhase(value)); } void OscillatorGroupBox::createEvelopeGroupBox() { amplitudeEnvelopeBox = new GeonkickWidget(this); amplitudeEnvelopeBox->setFixedSize(224, 125); if (oscillator->type() == Oscillator::Type::Noise) { amplitudeEnvelopeBox->setBackgroundImage(RkImage(224, 125, RK_IMAGE_RC(hboxbk_noise_env))); amplitudeEnvelopeBox->setPosition(0, 25); } else { amplitudeEnvelopeBox->setBackgroundImage(RkImage(224, 125, RK_IMAGE_RC(hboxbk_osc_env))); amplitudeEnvelopeBox->setPosition(0, 110); } amplitudeEnvelopeBox->show(); amplitudeKnob = new Knob(amplitudeEnvelopeBox); amplitudeKnob->setPosition((224 / 2 - 80) / 2, (125 - 80) / 2 - 1); amplitudeKnob->setFixedSize(80, 80); amplitudeKnob->setKnobBackgroundImage(RkImage(80, 80, RK_IMAGE_RC(knob_bk_image))); amplitudeKnob->setKnobImage(RkImage(70, 70, RK_IMAGE_RC(knob))); amplitudeKnob->setRange(0, 1.0); amplitudeKnob->show(); RK_ACT_BIND(amplitudeKnob, valueUpdated, RK_ACT_ARGS(double val), oscillator, setAmplitude(val)); auto oscAmplEnvelopeButton = new GeonkickButton(amplitudeEnvelopeBox); oscAmplEnvelopeButton->setPressed(viewState()->getEnvelopeType() == Envelope::Type::Amplitude && static_cast(viewState()->getEnvelopeCategory()) == oscillator->type()); oscAmplEnvelopeButton->setFixedSize(63, 21); oscAmplEnvelopeButton->setPosition(amplitudeKnob->x() + amplitudeKnob->width() / 2 - oscAmplEnvelopeButton->width() / 2, amplitudeKnob->y() + amplitudeKnob->height() - 3); oscAmplEnvelopeButton->setImage(RkImage(oscAmplEnvelopeButton->size(), RK_IMAGE_RC(osc_ampl_button_off)), RkButton::State::Unpressed); oscAmplEnvelopeButton->setImage(RkImage(oscAmplEnvelopeButton->size(), RK_IMAGE_RC(osc_ampl_button_on)), RkButton::State::Pressed); oscAmplEnvelopeButton->setImage(RkImage(oscAmplEnvelopeButton->size(), RK_IMAGE_RC(osc_ampl_button_hover)), RkButton::State::PressedHover); oscAmplEnvelopeButton->setImage(RkImage(oscAmplEnvelopeButton->size(), RK_IMAGE_RC(osc_ampl_button_hover)), RkButton::State::UnpressedHover); oscAmplEnvelopeButton->show(); RK_ACT_BIND(oscAmplEnvelopeButton, pressed, RK_ACT_ARGS(), viewState(), setEnvelope(static_cast(oscillator->type()), Envelope::Type::Amplitude)); RK_ACT_BIND(viewState(), envelopeChanged, RK_ACT_ARGS(Envelope::Category category, Envelope::Type envelope), oscAmplEnvelopeButton, setPressed(envelope == Envelope::Type::Amplitude && static_cast(category) == oscillator->type())); if (oscillator->type() == Oscillator::Type::Noise) { noiseWhiteButton = new GeonkickButton(amplitudeEnvelopeBox); noiseWhiteButton->setPosition(224 / 2 + (224 / 2 - 90) / 2 - 10, 10); noiseWhiteButton->setFixedSize(90, 30); noiseWhiteButton->setUnpressedImage(RkImage(90, 30, RK_IMAGE_RC(noise_type_white))); noiseWhiteButton->setPressedImage(RkImage(90, 30, RK_IMAGE_RC(noise_type_white_active))); RK_ACT_BIND(noiseWhiteButton, toggled, RK_ACT_ARGS(bool b), this, setNoiseWhite(b)); noiseWhiteButton->show(); noiseBrownianButton = new GeonkickButton(amplitudeEnvelopeBox); noiseBrownianButton->setPosition(224 / 2 + (224 / 2 - 90) / 2 - 10, noiseWhiteButton->y() + noiseWhiteButton->height()); noiseBrownianButton->setFixedSize(90, 30); RK_ACT_BIND(noiseBrownianButton, toggled, RK_ACT_ARGS(bool b), this, setNoiseBrownian(b)); noiseBrownianButton->setUnpressedImage(RkImage(90, 30, RK_IMAGE_RC(noise_type_brownian))); noiseBrownianButton->setPressedImage(RkImage(90, 30, RK_IMAGE_RC(noise_type_brownian_active))); noiseBrownianButton->show(); auto seedLabel = new RkLabel(amplitudeEnvelopeBox, "Seed"); seedLabel->setFixedSize(30, 10); seedLabel->setTextColor({210, 226, 226, 160}); seedLabel->setPosition(noiseBrownianButton->x() + (noiseBrownianButton->width() - seedLabel->width()) / 2, noiseBrownianButton->y() + noiseBrownianButton->height() + 7); seedLabel->setBackgroundColor(amplitudeEnvelopeBox->background()); seedLabel->show(); seedSlider = new GeonkickSlider(amplitudeEnvelopeBox); seedSlider->setFixedSize(115, 8); seedSlider->onSetValue(40); seedSlider->setPosition(noiseBrownianButton->x() - 13, noiseBrownianButton->y() + 55); seedSlider->show(); RK_ACT_BIND(seedSlider, valueUpdated, RK_ACT_ARGS(int value), this, setOscillatorSeed(value)); } else { pitchShiftKnob = new Knob(amplitudeEnvelopeBox); pitchShiftKnob->setSize(80, 80); pitchShiftKnob->setPosition(224 / 2 + (224 / 2 - 80) / 2, (125 - 80) / 2 - 1); pitchShiftKnob->setKnobBackgroundImage(RkImage(80, 80, RK_IMAGE_RC(knob_bk_image))); pitchShiftKnob->setKnobImage(RkImage(70, 70, RK_IMAGE_RC(knob))); pitchShiftKnob->setRange(0, 48); RK_ACT_BIND(pitchShiftKnob, valueUpdated, RK_ACT_ARGS(double val), oscillator, setPitchShift(val)); frequencyKnob = new Knob(amplitudeEnvelopeBox); frequencyKnob->setRangeType(Knob::RangeType::Logarithmic); frequencyKnob->setSize(80, 80); frequencyKnob->setPosition(224 / 2 + (224 / 2 - 80) / 2, (125 - 80) / 2 - 1); frequencyKnob->setKnobBackgroundImage(RkImage(80, 80, RK_IMAGE_RC(knob_bk_image))); frequencyKnob->setKnobImage(RkImage(70, 70, RK_IMAGE_RC(knob))); frequencyKnob->setRange(200, 16000); RK_ACT_BIND(frequencyKnob, valueUpdated, RK_ACT_ARGS(double val), oscillator, setFrequency(val)); oscFreqEnvelopeButton = new GeonkickButton(amplitudeEnvelopeBox); oscFreqEnvelopeButton->setPressed(viewState()->getEnvelopeType() == Envelope::Type::Frequency && static_cast(viewState()->getEnvelopeCategory()) == oscillator->type()); oscFreqEnvelopeButton->setFixedSize(63, 21); oscFreqEnvelopeButton->setPosition(frequencyKnob->x() + frequencyKnob->width() / 2 - oscFreqEnvelopeButton->width() / 2, frequencyKnob->y() + frequencyKnob->height() - 3); oscFreqEnvelopeButton->setImage(RkImage(oscFreqEnvelopeButton->size(), RK_IMAGE_RC(osc_freq_button_off)), RkButton::State::Unpressed); oscFreqEnvelopeButton->setImage(RkImage(oscFreqEnvelopeButton->size(), RK_IMAGE_RC(osc_freq_button_on)), RkButton::State::Pressed); oscFreqEnvelopeButton->setImage(RkImage(oscFreqEnvelopeButton->size(), RK_IMAGE_RC(osc_freq_button_hover)), RkButton::State::PressedHover); oscFreqEnvelopeButton->setImage(RkImage(oscFreqEnvelopeButton->size(), RK_IMAGE_RC(osc_freq_button_hover)), RkButton::State::UnpressedHover); RK_ACT_BIND(oscFreqEnvelopeButton, pressed, RK_ACT_ARGS(), viewState(), setEnvelope(static_cast(oscillator->type()), Envelope::Type::Frequency)); RK_ACT_BIND(viewState(), envelopeChanged, RK_ACT_ARGS(Envelope::Category category, Envelope::Type envelope), oscFreqEnvelopeButton, setPressed(envelope == Envelope::Type::Frequency && static_cast(category) == oscillator->type())); pitchEnvelopeButton = new GeonkickButton(amplitudeEnvelopeBox); pitchEnvelopeButton->setPressed(viewState()->getEnvelopeType() == Envelope::Type::PitchShift && static_cast(viewState()->getEnvelopeCategory()) == oscillator->type()); pitchEnvelopeButton->setFixedSize(63, 21); pitchEnvelopeButton->setPosition(frequencyKnob->x() + frequencyKnob->width() / 2 - pitchEnvelopeButton->width() / 2, frequencyKnob->y() + frequencyKnob->height() - 3); pitchEnvelopeButton->setImage(RkImage(pitchEnvelopeButton->size(), RK_IMAGE_RC(osc_pitch_button_off)), RkButton::State::Unpressed); pitchEnvelopeButton->setImage(RkImage(pitchEnvelopeButton->size(), RK_IMAGE_RC(osc_pitch_button_on)), RkButton::State::Pressed); pitchEnvelopeButton->setImage(RkImage(pitchEnvelopeButton->size(), RK_IMAGE_RC(osc_pitch_button_hover)), RkButton::State::PressedHover); pitchEnvelopeButton->setImage(RkImage(pitchEnvelopeButton->size(), RK_IMAGE_RC(osc_pitch_button_hover)), RkButton::State::UnpressedHover); RK_ACT_BIND(pitchEnvelopeButton, pressed, RK_ACT_ARGS(), viewState(), setEnvelope(static_cast(oscillator->type()), Envelope::Type::PitchShift)); RK_ACT_BIND(viewState(), envelopeChanged, RK_ACT_ARGS(Envelope::Category category, Envelope::Type envelope), pitchEnvelopeButton, setPressed(envelope == Envelope::Type::PitchShift && static_cast(category) == oscillator->type())); if (oscillator->function() == Oscillator::FunctionType::Sample) { pitchEnvelopeButton->show(); pitchShiftKnob->show(); } else { oscFreqEnvelopeButton->show(); frequencyKnob->show(); } } } void OscillatorGroupBox::createFilterGroupBox() { filterBox = new Filter(this, static_cast(oscillator->type())); if (oscillator->type() == Oscillator::Type::Noise) filterBox->setPosition(0, 150); else filterBox->setPosition(0, 235); filterBox->setCutOffRange(20, 20000); filterBox->setResonanceRange(1, 1000); RK_ACT_BIND(filterBox, enabled, RK_ACT_ARGS(bool b), oscillator, enableFilter(b)); RK_ACT_BIND(filterBox, cutOffChanged, RK_ACT_ARGS(double val), oscillator, setFilterFrequency(val)); RK_ACT_BIND(filterBox, resonanceChanged, RK_ACT_ARGS(double val), oscillator, setFilterQFactor(val)); RK_ACT_BIND(filterBox, typeChanged, RK_ACT_ARGS(GeonkickApi::FilterType type), oscillator, setFilterType(type)); } void OscillatorGroupBox::setSineWave(bool pressed) { if (pressed) { squareButton->setPressed(false); triangleButton->setPressed(false); sawtoothButton->setPressed(false); sampleButton->setPressed(false); oscillator->setFunction(Oscillator::FunctionType::Sine); updateAmpltudeEnvelopeBox(); } } void OscillatorGroupBox::setSquareWave(bool pressed) { if (pressed) { sineButton->setPressed(false); triangleButton->setPressed(false); sawtoothButton->setPressed(false); sampleButton->setPressed(false); oscillator->setFunction(Oscillator::FunctionType::Square); updateAmpltudeEnvelopeBox(); } } void OscillatorGroupBox::setTriangleWave(bool pressed) { if (pressed) { sineButton->setPressed(false); squareButton->setPressed(false); sawtoothButton->setPressed(false); sampleButton->setPressed(false); oscillator->setFunction(Oscillator::FunctionType::Triangle); updateAmpltudeEnvelopeBox(); } } void OscillatorGroupBox::setSawtoothWave(bool pressed) { if (pressed) { sineButton->setPressed(false); squareButton->setPressed(false); triangleButton->setPressed(false); sampleButton->setPressed(false); oscillator->setFunction(Oscillator::FunctionType::Sawtooth); updateAmpltudeEnvelopeBox(); } } void OscillatorGroupBox::setSampleFunction(bool pressed) { if (pressed) { sineButton->setPressed(false); squareButton->setPressed(false); triangleButton->setPressed(false); sawtoothButton->setPressed(false); oscillator->setFunction(Oscillator::FunctionType::Sample); updateAmpltudeEnvelopeBox(); } } void OscillatorGroupBox::setOscillatorPhase(int value) { oscillator->setPhase((static_cast(value) / 100) * (2 * M_PI)); } void OscillatorGroupBox::setOscillatorSeed(int value) { oscillator->setSeed(10 * value); } void OscillatorGroupBox::setNoiseWhite(bool pressed) { if (pressed) { noiseBrownianButton->setPressed(false); oscillator->setFunction(Oscillator::FunctionType::NoiseWhite); } } void OscillatorGroupBox::setNoiseBrownian(bool pressed) { if (pressed) { noiseWhiteButton->setPressed(false); oscillator->setFunction(Oscillator::FunctionType::NoiseBrownian); } } void OscillatorGroupBox::groupBoxLabelUpdated(bool state) { if (filterTypeIsChecked && state == true) filterBox->enable(true); oscillator->enable(state); } void OscillatorGroupBox::updateGui() { oscillatorCheckbox->setPressed(oscillator->isEnabled()); if (oscillator->type() == Oscillator::Type::Noise) { noiseWhiteButton->setPressed(false); noiseBrownianButton->setPressed(false); if (oscillator->function() == Oscillator::FunctionType::NoiseWhite) noiseWhiteButton->setPressed(true); else noiseBrownianButton->setPressed(true); seedSlider->onSetValue(oscillator->getSeed() / 10); } else { sineButton->setPressed(oscillator->function() == Oscillator::FunctionType::Sine); squareButton->setPressed(oscillator->function() == Oscillator::FunctionType::Square); triangleButton->setPressed(oscillator->function() == Oscillator::FunctionType::Triangle); sawtoothButton->setPressed(oscillator->function() == Oscillator::FunctionType::Sawtooth); sampleButton->setPressed(oscillator->function() == Oscillator::FunctionType::Sample); phaseSlider->onSetValue(oscillator->getPhase()); updateAmpltudeEnvelopeBox(); } amplitudeKnob->setCurrentValue(oscillator->amplitude()); if (oscillator->type() != Oscillator::Type::Noise) { frequencyKnob->setCurrentValue(oscillator->frequency()); pitchShiftKnob->setCurrentValue(oscillator->pitchShift()); } if (oscillator->type() == Oscillator::Type::Oscillator1) fmCheckbox->setPressed(oscillator->isFm()); filterBox->enable(oscillator->isFilterEnabled()); filterBox->setResonance(oscillator->filterQFactor()); filterBox->setCutOff(oscillator->filterFrequency()); filterBox->setType(oscillator->filter()); } void OscillatorGroupBox::browseSample() { viewState()->setSamplesBrowserOscillator(static_cast(oscillator->type())); viewState()->setMainView(ViewState::View::Samples); } void OscillatorGroupBox::updateAmpltudeEnvelopeBox() { pitchShiftKnob->show(oscillator->function() == Oscillator::FunctionType::Sample); pitchEnvelopeButton->show(oscillator->function() == Oscillator::FunctionType::Sample); frequencyKnob->show(oscillator->function() != Oscillator::FunctionType::Sample); oscFreqEnvelopeButton->show(oscillator->function() != Oscillator::FunctionType::Sample); amplitudeEnvelopeBox->update(); } geonkick-2.10.2/src/oscillator_group_box.h000066400000000000000000000056321451017372400205770ustar00rootroot00000000000000/** * File name: oscillator_group_box.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GKICK_OSCILLATOR_GROUP_BOX_H #define GKICK_OSCILLATOR_GROUP_BOX_H #include "oscillator.h" #include "geonkick_groupbox.h" class GKickOscillator; class GeonkickWidget; class GeonkickButton; class GeonkickCheckbox; class Knob; class GeonkickSlider; class Filter; class OscillatorGroupBox: public GeonkickGroupBox { public: OscillatorGroupBox(GeonkickWidget *parent, Oscillator *osc); void updateGui(); RK_DECL_ACT(oscillatorFunctionChanged, oscillatorFunctionChanged(int index), RK_ARG_TYPE(int), RK_ARG_VAL(index)); protected: void createWaveFunctionGroupBox(); void createEvelopeGroupBox(); void createFilterGroupBox(); void setSineWave(bool pressed); void setSquareWave(bool pressed); void setTriangleWave(bool pressed); void setSawtoothWave(bool pressed); void setOscillatorPhase(int value); void setOscillatorSeed(int value); void setNoiseWhite(bool pressed); void setNoiseBrownian(bool pressed); void setSampleFunction(bool pressed); void groupBoxLabelUpdated(bool state); void browseSample(); void updateAmpltudeEnvelopeBox(); private: Oscillator *oscillator; GeonkickButton *oscillatorCheckbox; GeonkickButton *fmCheckbox; Filter *filterBox; GeonkickButton *sineButton; GeonkickButton *squareButton; GeonkickButton *triangleButton; GeonkickButton *sawtoothButton; GeonkickButton *sampleButton; GeonkickButton *sampleBrowseButton; GeonkickSlider *phaseSlider; GeonkickSlider *seedSlider; GeonkickButton *noiseWhiteButton; GeonkickButton *noiseBrownianButton; Knob *amplitudeKnob; Knob *frequencyKnob; Knob *pitchShiftKnob; bool filterTypeIsChecked; GeonkickWidget *amplitudeEnvelopeBox; GeonkickButton *oscFreqEnvelopeButton; GeonkickButton *pitchEnvelopeButton; }; #endif // GKICK_OSCILLATOR_GROUP_BOX_H geonkick-2.10.2/src/percussion_model.cpp000066400000000000000000000121261451017372400202410ustar00rootroot00000000000000/** * File name: percussion_model.cpp * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "percussion_model.h" #include "kit_model.h" #include #include PercussionModel::PercussionModel(KitModel* parent, int id) : RkObject(parent) , kitModel{parent} , percussionId{id} { RK_ACT_BIND(kitModel, percussionSelected, RK_ACT_ARGS(), this, onPercussionSelected()); } void PercussionModel::setId(int id) { percussionId = id; action modelUpdated(); } PercussionModel::PercussionIndex PercussionModel::index() const { return kitModel->getIndex(percussionId); } void PercussionModel::select() { kitModel->selectPercussion(index()); } bool PercussionModel::isSelected() const { return kitModel->isPercussionSelected(index()); } void PercussionModel::increasePercussionChannel() { auto channel = kitModel->percussionChannel(index()); if (channel < 0) return; if (++channel > static_cast(kitModel->numberOfChannels() - 1)) channel = 0; if (kitModel->setPercussionChannel(index(), channel)) action channelUpdated(channel); } void PercussionModel::decreasePercussionChannel() { auto channel = kitModel->percussionChannel(index()); if (channel < 0) return; if (channel - 1 < 0) channel = kitModel->numberOfChannels() - 1; else channel--; if (kitModel->setPercussionChannel(percussionId, channel)) action channelUpdated(channel); } void PercussionModel::setChannel(int index) { if (kitModel->setPercussionChannel(percussionId, index)) action channelUpdated(index); } size_t PercussionModel::keysNumber() const { return kitModel->keysNumber(); } void PercussionModel::setKey(PercussionModel::KeyIndex keyIndex) { if (kitModel->setPercussionKey(index(), keyIndex)) action keyUpdated(keyIndex); } PercussionModel::KeyIndex PercussionModel::key() const { return kitModel->percussionKey(index()); } void PercussionModel::setName(const std::string &name) { if (kitModel->setPercussionName(index(), name)) action nameUpdated(name); } std::string PercussionModel::name() const { return kitModel->percussionName(index()); } int PercussionModel::channel() const { return kitModel->percussionChannel(index()); } size_t PercussionModel::numberOfChannels() const { return kitModel->numberOfChannels(); } bool PercussionModel::canCopy() const { auto n = kitModel->percussionNumber(); return n > 0 && n < kitModel->maxPercussionNumber(); } bool PercussionModel::canRemove() const { return kitModel->percussionNumber() > 1; } void PercussionModel::play() { kitModel->playPercussion(index()); } void PercussionModel::setLimiter(int value) { if (kitModel->setPercussionLimiter(index(), value)) action limiterUpdated(value); } int PercussionModel::limiter() const { return kitModel->percussionLimiter(index()); } int PercussionModel::leveler() const { return kitModel->percussionLeveler(index()); } void PercussionModel::mute(bool b) { if (kitModel->mutePercussion(index(), b)) action muteUpdated(b); } bool PercussionModel::isMuted() const { return kitModel->isPercussionMuted(index()); } void PercussionModel::solo(bool b) { if (kitModel->soloPercussion(index(), b)) action soloUpdated(b); } bool PercussionModel::isSolo() const { return kitModel->isPercussionSolo(index()); } void PercussionModel::onPercussionSelected() { action selected(); } void PercussionModel::remove() { auto act = std::make_unique(); auto model = kitModel; PercussionIndex i = index(); act->setCallback([i, model](void){ model->removePercussion(i); }); eventQueue()->postAction(std::move(act)); } void PercussionModel::copy() { kitModel->copyPercussion(index()); } KitModel* PercussionModel::model() const { return kitModel; } std::vector PercussionModel::data() const { return kitModel->instrumentData(index()); } geonkick-2.10.2/src/percussion_model.h000066400000000000000000000071421451017372400177100ustar00rootroot00000000000000/** * File name: percussion_model.h * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef PERCUSSION_MODEL_H #define PERCUSSION_MODEL_H #include "globals.h" class GeonkickApi; class GeonkickState; class KitModel; class PercussionModel : public RkObject { public: using PercussionIndex = int; using KeyIndex = GeonkickTypes::MidiKey; explicit PercussionModel(KitModel* parent, int id = -1); virtual ~PercussionModel() = default; void setId(int id); PercussionIndex index() const; void select(); bool isSelected() const; void increasePercussionChannel(); void decreasePercussionChannel(); size_t keysNumber() const; void setKey(KeyIndex keyIndex); KeyIndex key() const; void setName(const std::string &name); std::string name() const; void setChannel(int index); int channel() const; size_t numberOfChannels() const; bool canCopy() const; bool canRemove() const; void play(); void setLimiter(int value); int limiter() const; int leveler() const; void mute(bool b); bool isMuted() const; void solo(bool b); bool isSolo() const; void remove(); void copy(); KitModel* model() const; std::vector data() const; RK_DECL_ACT(modelUpdated, modelUpdated(), RK_ARG_TYPE(), RK_ARG_VAL()); RK_DECL_ACT(selected, selected(), RK_ARG_TYPE(), RK_ARG_VAL()); RK_DECL_ACT(nameUpdated, nameUpdated(std::string name), RK_ARG_TYPE(std::string), RK_ARG_VAL(name)); RK_DECL_ACT(keyUpdated, keyUpdated(KeyIndex key), RK_ARG_TYPE(KeyIndex), RK_ARG_VAL(key)); RK_DECL_ACT(channelUpdated, channelUpdated(int val), RK_ARG_TYPE(int), RK_ARG_VAL(val)); RK_DECL_ACT(limiterUpdated, limiterUpdated(int val), RK_ARG_TYPE(int), RK_ARG_VAL(val)); RK_DECL_ACT(levelerUpdated, levelerUpdated(int val), RK_ARG_TYPE(int), RK_ARG_VAL(val)); RK_DECL_ACT(muteUpdated, muteUpdated(bool b), RK_ARG_TYPE(bool), RK_ARG_VAL(b)); RK_DECL_ACT(soloUpdated, soloUpdated(bool b), RK_ARG_TYPE(bool), RK_ARG_VAL(b)); protected: void onPercussionSelected(); private: KitModel* kitModel; int percussionId; }; #endif // PERCUSSION_MODEL_H geonkick-2.10.2/src/percussion_state.cpp000066400000000000000000001221061451017372400202610ustar00rootroot00000000000000/** * File name: percussion_state.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iuri Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "percussion_state.h" #include "Base64EncoderDecoder.h" #include PercussionState::PercussionState() : appVersion{GEONKICK_VERSION} , kickId{0} , kickName{"Default"} , playingKey{-1} , outputChannel{0} , kickEnabled{true} , percussionMuted{false} , percussionSolo{false} , limiterValue{0} , kickLength{50} , kickAmplitude{0.8} , kickFilterEnabled{0} , kickFilterFrequency{200} , kickFilterQFactor{1.0} , kickFilterType{GeonkickApi::FilterType::LowPass} , compressor{false, 0, 0, 0, 0, 0, 0} , distortion{false, 1.0, 1.0, 1.0} , layers{false, false, false} , layersAmplitude{1.0, 1.0, 1.0} , currentLayer{GeonkickApi::Layer::Layer1} , tunedOutput{false} { initOscillators(); } bool PercussionState::loadFile(const std::string &file) { if (file.size() < 7) { RK_LOG_ERROR("can't open preset."); return false; } std::filesystem::path filePath(file); if (filePath.extension().empty() || (filePath.extension() != ".gkick" && filePath.extension() != ".GKICK")) { RK_LOG_ERROR("can't open preset. Wrong file format."); return false; } std::ifstream sfile; sfile.open(std::filesystem::absolute(filePath)); if (!sfile.is_open()) { RK_LOG_ERROR("can't open preset file " << file); return false; } std::string fileData((std::istreambuf_iterator(sfile)), (std::istreambuf_iterator())); if (!loadData(fileData)) { GEONKICK_LOG_ERROR("error on parsing preset file " << file); return false; } return true; } bool PercussionState::loadData(const std::string &data) { rapidjson::Document document; if (document.Parse(data.c_str()).HasParseError()) { GEONKICK_LOG_ERROR("error on parsing JSON data"); return false; } loadObject(document); return true; } void PercussionState::loadObject(const rapidjson::Value &obj) { if (!obj.IsObject()) return; bool kickParsed = false; for (const auto &m: obj.GetObject()) { if (m.name == "kick" && m.value.IsObject()) { parseKickObject(m.value); kickParsed = true; break; } } if (!kickParsed) return; for (const auto &m: obj.GetObject()) { for (decltype(layers.size()) i = 0; i < layers.size(); i++) { setCurrentLayer(static_cast(i)); if (m.name == ("osc" + std::to_string(0 + i * GKICK_OSC_GROUP_SIZE)).c_str()) { parseOscillatorObject(0, m.value); break; } else if (m.name == ("osc" + std::to_string(1 + i * GKICK_OSC_GROUP_SIZE)).c_str()) { parseOscillatorObject(1, m.value); break; } else if (m.name == ("osc" + std::to_string(2 + i * GKICK_OSC_GROUP_SIZE)).c_str()) { parseOscillatorObject(2, m.value); break; } } } } size_t PercussionState::getId() const { return kickId; } void PercussionState::setId(size_t id) { kickId = id; } std::string PercussionState::getName() const { return kickName; } void PercussionState::setName(const std::string &name) { kickName = name; } signed char PercussionState::getPlayingKey() const { return playingKey; } void PercussionState::setPlayingKey(signed char key) { playingKey = key; } void PercussionState::setChannel(size_t channel) { outputChannel = channel; } void PercussionState::setMute(bool b) { percussionMuted = b; } bool PercussionState::isMuted() const { return percussionMuted; } void PercussionState::setSolo(bool b) { percussionSolo = b; } bool PercussionState::isSolo() const { return percussionSolo; } size_t PercussionState::getChannel() const { return outputChannel; } bool PercussionState::isEnabled() const { return kickEnabled; } void PercussionState::enable(bool b) { kickEnabled = b; } void PercussionState::initOscillators() { for (decltype(layers.size()) i = 0; i < layers.size(); i++) { oscillators.insert({static_cast(GeonkickApi::OscillatorType::Oscillator1) + GKICK_OSC_GROUP_SIZE * i, std::make_shared()}); oscillators.insert({static_cast(GeonkickApi::OscillatorType::Oscillator2) + GKICK_OSC_GROUP_SIZE * i, std::make_shared()}); oscillators.insert({static_cast(GeonkickApi::OscillatorType::Noise) + GKICK_OSC_GROUP_SIZE * i, std::make_shared()}); } } void PercussionState::parseKickObject(const rapidjson::Value &kick) { if (kick.IsNull() || !kick.IsObject()) return; for (const auto &m: kick.GetObject()) { if (m.name == "PercussionAppVersion" && m.value.IsInt()) appVersion = m.value.GetInt(); if (m.name == "name" && m.value.IsString()) setName(m.value.GetString()); if (m.name == "id" && m.value.IsInt()) setId(m.value.GetInt()); if (m.name == "channel" && m.value.IsInt()) setChannel(m.value.GetInt()); if (m.name == "mute" && m.value.IsBool()) setMute(m.value.GetBool()); if (m.name == "solo" && m.value.IsBool()) setSolo(m.value.GetBool()); if (m.name == "playing_key" && m.value.IsInt()) setPlayingKey(m.value.GetInt()); if (m.name == "limiter" && m.value.IsDouble()) setLimiterValue(m.value.GetDouble()); if (m.name == "tuned_output" && m.value.IsBool()) tuneOutput(m.value.GetBool()); if (m.name == "layers" && m.value.IsArray()) { layers = {false, false, false}; for (const auto &el: m.value.GetArray()) if (el.IsInt()) setLayerEnabled(static_cast(el.GetInt()), true); } if (m.name == "layers_amplitude" && m.value.IsArray()) { layersAmplitude = {1.0, 1.0, 1.0}; int i = 0; for (const auto &el: m.value.GetArray()) { if (el.IsDouble()) setLayerAmplitude(static_cast(i), el.GetDouble()); i++; } } if (m.name == "ampl_env" && m.value.IsObject()) { for (const auto &el: m.value.GetObject()) { if (el.name == "length" && el.value.IsDouble()) setKickLength(el.value.GetDouble()); if (el.name == "amplitude" && el.value.IsDouble()) setKickAmplitude(el.value.GetDouble()); if (el.name == "points" && el.value.IsArray()) { setKickEnvelopePoints(GeonkickApi::EnvelopeType::Amplitude, parseEnvelopeArray(el.value)); } } } if (m.name == "filter" && m.value.IsObject()) { for (const auto &el: m.value.GetObject()) { if (el.name == "enabled" && el.value.IsBool()) enableKickFilter(el.value.GetBool()); if (el.name == "cutoff" && el.value.IsDouble()) setKickFilterFrequency(el.value.GetDouble()); if (el.name == "factor" && el.value.IsDouble()) setKickFilterQFactor(el.value.GetDouble()); if (el.name == "type" && el.value.IsInt()) setKickFilterType(static_cast(el.value.GetInt())); if (el.name == "cutoff_env" && el.value.IsArray()) { setKickEnvelopePoints(GeonkickApi::EnvelopeType::FilterCutOff, parseEnvelopeArray(el.value)); } } } if (m.name == "compressor" && m.value.IsObject()) { for (const auto &el: m.value.GetObject()) { if (el.name == "enabled" && el.value.IsBool()) enableCompressor(el.value.GetBool()); if (el.name == "attack" && el.value.IsDouble()) setCompressorAttack(el.value.GetDouble()); if (el.name == "release" && el.value.IsDouble()) setCompressorRelease(el.value.GetDouble()); if (el.name == "threshold" && el.value.IsDouble()) setCompressorThreshold(el.value.GetDouble()); if (el.name == "ratio" && el.value.IsDouble()) setCompressorRatio(el.value.GetDouble()); if (el.name == "knee" && el.value.IsDouble()) setCompressorKnee(el.value.GetDouble()); if (el.name == "makeup" && el.value.IsDouble()) setCompressorMakeup(el.value.GetDouble()); } } if (m.name == "distortion" && m.value.IsObject()) { for (const auto &el: m.value.GetObject()) { if (el.name == "enabled" && el.value.IsBool()) enableDistortion(el.value.GetBool()); if (el.name == "in_limiter" && el.value.IsDouble()) setDistortionInLimiter(el.value.GetDouble()); if (el.name == "volume" && el.value.IsDouble()) setDistortionVolume(el.value.GetDouble()); if (el.name == "drive" && el.value.IsDouble()) setDistortionDrive(el.value.GetDouble()); if (el.name == "drive_env" && el.value.IsArray()) setKickEnvelopePoints(GeonkickApi::EnvelopeType::DistortionDrive, parseEnvelopeArray(el.value)); if (el.name == "volume_env" && el.value.IsArray()) setKickEnvelopePoints(GeonkickApi::EnvelopeType::DistortionVolume, parseEnvelopeArray(el.value)); } } } } void PercussionState::parseOscillatorObject(int index, const rapidjson::Value &osc) { if (osc.IsNull() || !osc.IsObject()) return; for (const auto &m: osc.GetObject()) { if (m.name == "enabled" && m.value.IsBool()) setOscillatorEnabled(index, m.value.GetBool()); if (m.name == "is_fm" && m.value.IsBool()) setOscillatorAsFm(index, m.value.GetBool()); if (m.name == "sample" && m.value.IsString()) setOscillatorSample(index, fromBase64F(std::string(m.value.GetString()))); if (m.name == "function" && m.value.IsInt()) setOscillatorFunction(index, static_cast(m.value.GetInt())); if (m.name == "phase" && m.value.IsDouble()) setOscillatorPhase(index, m.value.GetDouble()); if (m.name == "seed" && m.value.IsInt()) setOscillatorSeed(index, m.value.GetInt()); if (m.name == "ampl_env" && m.value.IsObject()) { for (const auto &el: m.value.GetObject()) { if (el.name == "amplitude" && el.value.IsDouble()) setOscillatorAmplitue(index, el.value.GetDouble()); if (el.name == "points" && el.value.IsArray()) setOscillatorEnvelopePoints(index, parseEnvelopeArray(el.value), GeonkickApi::EnvelopeType::Amplitude); } } if (static_cast(index) != GeonkickApi::OscillatorType::Noise) { if (m.name == "freq_env" && m.value.IsObject()) { for (const auto &el: m.value.GetObject()) { if (el.name == "amplitude" && el.value.IsDouble()) setOscillatorFrequency(index, el.value.GetDouble()); if (el.name == "points" && el.value.IsArray()) setOscillatorEnvelopePoints(index, parseEnvelopeArray(el.value), GeonkickApi::EnvelopeType::Frequency); } } if (m.name == "pitchshift_env" && m.value.IsObject()) { for (const auto &el: m.value.GetObject()) { if (el.name == "amplitude" && el.value.IsDouble()) setOscillatorPitchShift(index, el.value.GetDouble()); if (el.name == "points" && el.value.IsArray()) setOscillatorEnvelopePoints(index, parseEnvelopeArray(el.value), GeonkickApi::EnvelopeType::PitchShift); } } } if (m.name == "filter" && m.value.IsObject()) { for (const auto &el: m.value.GetObject()) { if (el.name == "enabled" && el.value.IsBool()) setOscillatorFilterEnabled(index, el.value.GetBool()); if (el.name == "cutoff" && el.value.IsDouble()) setOscillatorFilterCutOffFreq(index, el.value.GetDouble()); if (el.name == "factor" && el.value.IsDouble()) setOscillatorFilterFactor(index, el.value.GetDouble()); if (el.name == "type" && el.value.IsInt()) setOscillatorFilterType(index, static_cast(el.value.GetInt())); if (el.name == "cutoff_env" && el.value.IsArray()) { setOscillatorEnvelopePoints(index, parseEnvelopeArray(el.value), GeonkickApi::EnvelopeType::FilterCutOff); } } } } } std::vector PercussionState::parseEnvelopeArray(const rapidjson::Value &envelopeArray) { std::vector points; for (const auto &el: envelopeArray.GetArray()) if (el.IsArray() && el.GetArray().Size() == 2) points.emplace_back(RkRealPoint(el.GetArray()[0].GetDouble(), el.GetArray()[1].GetDouble())); return points; } void PercussionState::setLimiterValue(double val) { limiterValue = val; } void PercussionState::setKickLength(double val) { kickLength = val; } void PercussionState::setKickAmplitude(double val) { kickAmplitude = val; } void PercussionState::enableKickFilter(bool b) { kickFilterEnabled = b; } void PercussionState::setKickFilterFrequency(double val) { kickFilterFrequency = val; } void PercussionState::setKickFilterQFactor(double val) { kickFilterQFactor = val; } void PercussionState::setKickFilterType(GeonkickApi::FilterType type) { kickFilterType = type; } void PercussionState::setKickEnvelopePoints(GeonkickApi::EnvelopeType envelope, const std::vector &points) { if (envelope == GeonkickApi::EnvelopeType::Amplitude) kickEnvelopePoints = points; else if (envelope == GeonkickApi::EnvelopeType::FilterCutOff) kickFilterCutOffEnvelope = points; else if (envelope == GeonkickApi::EnvelopeType::DistortionDrive) kickDistortionDriveEnvelope = points; else if (envelope == GeonkickApi::EnvelopeType::DistortionVolume) kickDistortionVolumeEnvelope = points; } double PercussionState::getLimiterValue() const { return limiterValue; } double PercussionState::getKickLength() const { return kickLength; } double PercussionState::getKickAmplitude() const { return kickAmplitude; } bool PercussionState::isKickFilterEnabled() const { return kickFilterEnabled; } double PercussionState::getKickFilterFrequency() const { return kickFilterFrequency; } double PercussionState::getKickFilterQFactor() const { return kickFilterQFactor; } GeonkickApi::FilterType PercussionState::getKickFilterType() const { return kickFilterType; } std::vector PercussionState::getKickEnvelopePoints(GeonkickApi::EnvelopeType envelope) const { if (envelope == GeonkickApi::EnvelopeType::Amplitude) return kickEnvelopePoints; else if (envelope == GeonkickApi::EnvelopeType::FilterCutOff) return kickFilterCutOffEnvelope; else if (envelope == GeonkickApi::EnvelopeType::DistortionDrive) return kickDistortionDriveEnvelope; else if (envelope == GeonkickApi::EnvelopeType::DistortionVolume) return kickDistortionVolumeEnvelope; else return {}; } std::shared_ptr PercussionState::getOscillator(int index) const { index += GKICK_OSC_GROUP_SIZE * static_cast(currentLayer); auto it = oscillators.find(index); if (it != oscillators.end()) return it->second; return nullptr; } void PercussionState::setOscillatorEnabled(int index, bool b) { auto oscillator = getOscillator(index); if (oscillator) oscillator->isEnabled = b; } void PercussionState::setOscillatorFunction(int index, GeonkickApi::FunctionType type) { auto oscillator = getOscillator(index); if (oscillator) oscillator->function = type; } void PercussionState::setOscillatorPhase(int index, double phase) { auto oscillator = getOscillator(index); if (oscillator) oscillator->phase = phase; } void PercussionState::setOscillatorSeed(int index, int seed) { auto oscillator = getOscillator(index); if (oscillator) oscillator->seed = seed; } void PercussionState::setOscillatorAmplitue(int index, double val) { auto oscillator = getOscillator(index); if (oscillator) oscillator->amplitude = val; } void PercussionState::setOscillatorFrequency(int index, double val) { auto oscillator = getOscillator(index); if (oscillator) oscillator->frequency = val; } void PercussionState::setOscillatorPitchShift(int index, double val) { if (auto oscillator = getOscillator(index); oscillator) oscillator->pitchShift = val; } void PercussionState::setOscillatorFilterEnabled(int index, bool b) { auto oscillator = getOscillator(index); if (oscillator) oscillator->isFilterEnabled = b; } void PercussionState::setOscillatorFilterType(int index, GeonkickApi::FilterType type) { auto oscillator = getOscillator(index); if (oscillator) oscillator->filterType = type; } void PercussionState::setOscillatorFilterCutOffFreq(int index, double val) { auto oscillator = getOscillator(index); if (oscillator) oscillator->filterFrequency = val; } void PercussionState::setOscillatorFilterFactor(int index, double val) { auto oscillator = getOscillator(index); if (oscillator) oscillator->filterFactor = val; } void PercussionState::setOscillatorEnvelopePoints(int index, const std::vector &points, GeonkickApi::EnvelopeType envelope) { auto oscillator = getOscillator(index); if (oscillator) { switch (envelope) { case GeonkickApi::EnvelopeType::Amplitude: oscillator->amplitudeEnvelope = points; break; case GeonkickApi::EnvelopeType::Frequency: oscillator->frequencyEnvelope = points; break; case GeonkickApi::EnvelopeType::PitchShift: oscillator->pitchShiftEnvelope = points; break; case GeonkickApi::EnvelopeType::FilterCutOff: oscillator->filterCutOffEnvelope = points; break; default: return; } } } bool PercussionState::isOscillatorAsFm(int index) const { auto oscillator = getOscillator(index); if (oscillator) return oscillator->isFm; return false; } void PercussionState::setOscillatorAsFm(int index, bool b) { auto oscillator = getOscillator(index); if (oscillator) oscillator->isFm = b; } bool PercussionState::isOscillatorEnabled(int index) const { auto oscillator = getOscillator(index); if (oscillator) return oscillator->isEnabled; return false; } GeonkickApi::FunctionType PercussionState::oscillatorFunction(int index) const { auto oscillator = getOscillator(index); if (oscillator) return oscillator->function; return GeonkickApi::FunctionType::Sine; } double PercussionState::oscillatorPhase(int index) const { auto oscillator = getOscillator(index); if (oscillator) return oscillator->phase; return 0; } int PercussionState::oscillatorSeed(int index) const { auto oscillator = getOscillator(index); if (oscillator) return oscillator->seed; return 0; } double PercussionState::oscillatorAmplitue(int index) const { if (auto oscillator = getOscillator(index); oscillator) return oscillator->amplitude; return 0; } double PercussionState::oscillatorFrequency(int index) const { if (auto oscillator = getOscillator(index); oscillator) return oscillator->frequency; return 0; } double PercussionState::oscillatorPitchShift(int index) const { if (auto oscillator = getOscillator(index); oscillator) return oscillator->pitchShift; return 0; } bool PercussionState::isOscillatorFilterEnabled(int index) const { if (auto oscillator = getOscillator(index); oscillator) return oscillator->isFilterEnabled; return false; } GeonkickApi::FilterType PercussionState::oscillatorFilterType(int index) const { auto oscillator = getOscillator(index); if (oscillator) return oscillator->filterType; return GeonkickApi::FilterType::LowPass; } double PercussionState::oscillatorFilterCutOffFreq(int index) const { auto oscillator = getOscillator(index); if (oscillator) return oscillator->filterFrequency; return 0; } double PercussionState::oscillatorFilterFactor(int index) const { auto oscillator = getOscillator(index); if (oscillator) return oscillator->filterFactor; return 0; } std::vector PercussionState::oscillatorEnvelopePoints(int index, GeonkickApi::EnvelopeType type) const { if (auto oscillator = getOscillator(index); oscillator) { switch (type) { case GeonkickApi::EnvelopeType::Amplitude: return oscillator->amplitudeEnvelope; case GeonkickApi::EnvelopeType::Frequency: return oscillator->frequencyEnvelope; case GeonkickApi::EnvelopeType::PitchShift: return oscillator->pitchShiftEnvelope; case GeonkickApi::EnvelopeType::FilterCutOff: return oscillator->filterCutOffEnvelope; default: return {}; } } return {}; } void PercussionState::enableCompressor(bool enable) { compressor.enabled = enable; } bool PercussionState::isCompressorEnabled() const { return compressor.enabled; } void PercussionState::setCompressorAttack(double attack) { compressor.attack = attack; } void PercussionState::setCompressorRelease(double release) { compressor.release = release; } void PercussionState::setCompressorThreshold(double threshold) { compressor.threshold = threshold; } void PercussionState::setCompressorRatio(double ratio) { compressor.ratio = ratio; } void PercussionState::setCompressorKnee(double knee) { compressor.knee = knee; } void PercussionState::setCompressorMakeup(double makeup) { compressor.makeup = makeup; } double PercussionState::getCompressorAttack() const { return compressor.attack; } double PercussionState::getCompressorRelease() const { return compressor.release; } double PercussionState::getCompressorThreshold() const { return compressor.threshold; } double PercussionState::getCompressorRatio() const { return compressor.ratio; } double PercussionState::getCompressorKnee() const { return compressor.knee; } double PercussionState::getCompressorMakeup() const { return compressor.makeup; } void PercussionState::enableDistortion(bool enable) { distortion.enabled = enable; } bool PercussionState::isDistortionEnabled() const { return distortion.enabled; } void PercussionState::setDistortionInLimiter(double limit) { distortion.in_limiter = limit; } void PercussionState::setDistortionVolume(double volume) { distortion.volume = volume; } void PercussionState::setDistortionDrive(double drive) { distortion.drive = drive; } double PercussionState::getDistortionInLimiter() const { return distortion.in_limiter; } double PercussionState::getDistortionVolume() const { return distortion.volume; } double PercussionState::getDistortionDrive() const { return distortion.drive; } std::string PercussionState::toJson() const { std::ostringstream jsonStream; jsonStream << std::fixed << std::setprecision(7); jsonStream << "{" << std::endl; oscJson(jsonStream); kickJson(jsonStream); jsonStream << "}" << std::endl; return jsonStream.str(); } void PercussionState::envelopeToJson(std::ostringstream &jsonStream, const std::string &envName, double amplitude, const std::vector &envelope) { jsonStream << "\"" << envName << "\": {" << std::endl; jsonStream << "\"amplitude\": " << amplitude << ", " << std::endl; jsonStream << "\"points\": [" << std::endl; bool first = true; for (const auto &point: envelope) { if (!first) jsonStream << ", "; jsonStream << "[ " << point.x() << " , " << point.y() << "]"; first = false; } jsonStream << "]" << std::endl; jsonStream << "}" << std::endl; } void PercussionState::oscJson(std::ostringstream &jsonStream) const { for (const auto& val: oscillators) { jsonStream << "\"osc" << val.first << "\": {" << std::endl; jsonStream << "\"enabled\": " << (val.second->isEnabled ? "true" : "false") << ", " << std::endl; jsonStream << "\"is_fm\": " << (val.second->isFm ? "true" : "false") << ", " << std::endl; if (val.second->function == GeonkickApi::FunctionType::Sample && !val.second->sample.empty()) jsonStream << "\"sample\": \"" << toBase64F(val.second->sample) << "\"," << std::endl; jsonStream << "\"function\": " << static_cast(val.second->function) << "," << std::endl; jsonStream << "\"phase\": " << val.second->phase << ", " << std::endl; jsonStream << "\"seed\": " << val.second->seed << ", " << std::endl; envelopeToJson(jsonStream, "ampl_env", val.second->amplitude, val.second->amplitudeEnvelope); jsonStream << "," << std::endl; envelopeToJson(jsonStream, "freq_env", val.second->frequency, val.second->frequencyEnvelope); jsonStream << "," << std::endl; envelopeToJson(jsonStream, "pitchshift_env", val.second->pitchShift, val.second->pitchShiftEnvelope); jsonStream << "," << std::endl; jsonStream << "\"filter\": {" << std::endl; jsonStream << "\"enabled\": " << (val.second->isFilterEnabled ? "true" : "false"); jsonStream << ", " << std::endl; jsonStream << "\"type\": " << static_cast(val.second->filterType) << ", " << std::endl; jsonStream << "\"cutoff\": " << val.second->filterFrequency << ", " << std::endl; jsonStream << "\"cutoff_env\": ["; bool first = true; for (const auto &point: val.second->filterCutOffEnvelope) { if (first) first = false; else jsonStream << ", "; jsonStream << "[ " << point.x() << " , " << point.y() << "]"; } jsonStream << "], " << std::endl; jsonStream << "\"factor\": " << val.second->filterFactor << std::endl; jsonStream << "}" << std::endl; // filter; jsonStream << "}" << std::endl; // osc; jsonStream << "," << std::endl; } } void PercussionState::kickJson(std::ostringstream &jsonStream) const { jsonStream << "\"kick\": {" << std::endl; jsonStream << "\"PercussionAppVersion\": " << GEONKICK_VERSION << "," << std::endl; jsonStream << "\"id\": " << getId() << "," << std::endl; jsonStream << "\"channel\": " << getChannel() << "," << std::endl; jsonStream << "\"mute\": " << (isMuted() ? "true" : "false") << "," << std::endl; jsonStream << "\"solo\": " << (isSolo() ? "true" : "false") << "," << std::endl; jsonStream << "\"name\": \"" << getName() << "\"," << std::endl; jsonStream << "\"playing_key\": " << static_cast(getPlayingKey()) << "," << std::endl; jsonStream << "\"layers\": ["; bool first = true; for (decltype(layers.size()) i = 0; i < layers.size(); i++) { if (layers[i]) { if (first) first = false; else jsonStream << ", "; jsonStream << i; } } jsonStream << "]," << std::endl; jsonStream << "\"layers_amplitude\": ["; first = true; for (decltype(layersAmplitude.size()) i = 0; i < layersAmplitude.size(); i++) { if (first) first = false; else jsonStream << ", "; jsonStream << layersAmplitude[i]; } jsonStream << "]," << std::endl; jsonStream << "\"limiter\": " << getLimiterValue() << ", " << std::endl; jsonStream << "\"tuned_output\": " << (isOutputTuned() ? "true" : "false") << ", " << std::endl; jsonStream << "\"ampl_env\": {" << std::endl; jsonStream << "\"amplitude\": " << getKickAmplitude() << ", " << std::endl; jsonStream << "\"length\": " << getKickLength() << ", " << std::endl; auto points = getKickEnvelopePoints(GeonkickApi::EnvelopeType::Amplitude); jsonStream << "\"points\": ["; first = true; for (const auto &point: points) { if (first) first = false; else jsonStream << ", "; jsonStream << "[ " << point.x() << " , " << point.y() << "]"; } jsonStream << "]}, " << std::endl; jsonStream << "\"filter\": {" << std::endl; jsonStream << "\"enabled\": " << (isKickFilterEnabled() ? "true" : "false"); jsonStream << ", " << std::endl; jsonStream << "\"type\": " << static_cast(getKickFilterType()) << ", " << std::endl; jsonStream << "\"cutoff\": " << getKickFilterFrequency() << ", " << std::endl; jsonStream << "\"factor\": " << getKickFilterQFactor() << ", " << std::endl; points = getKickEnvelopePoints(GeonkickApi::EnvelopeType::FilterCutOff); jsonStream << "\"cutoff_env\": ["; first = true; for (const auto &point: points) { if (first) first = false; else jsonStream << ", "; jsonStream << "[ " << point.x() << " , " << point.y() << "]"; } jsonStream << "]" << std::endl; // points jsonStream << "}, " << std::endl; // filter; jsonStream << "\"compressor\": {" << std::endl; jsonStream << "\"enabled\": " << (isCompressorEnabled() ? "true" : "false") << ", " << std::endl; jsonStream << "\"attack\": " << getCompressorAttack() << ", " << std::endl; jsonStream << "\"release\": " << getCompressorRelease() << ", " << std::endl; jsonStream << "\"threshold\": " << getCompressorThreshold() << ", " << std::endl; jsonStream << "\"ratio\": " << getCompressorRatio() << ", " << std::endl; jsonStream << "\"knee\": " << getCompressorKnee() << ", " << std::endl; jsonStream << "\"makeup\": " << getCompressorMakeup() << std::endl; jsonStream << "}, " << std::endl; jsonStream << "\"distortion\": {" << std::endl; jsonStream << "\"enabled\": " << (isDistortionEnabled() ? "true" : "false") << ", " << std::endl; jsonStream << "\"in_limiter\": " << getDistortionInLimiter() << ", " << std::endl; jsonStream << "\"volume\": " << getDistortionVolume() << ", " << std::endl; jsonStream << "\"drive\": " << getDistortionDrive() << ", " << std::endl; jsonStream << "\"drive_env\": [" << std::endl; points = getKickEnvelopePoints(GeonkickApi::EnvelopeType::DistortionDrive); first = true; for (const auto &point: points) { if (first) first = false; else jsonStream << ", "; jsonStream << "[ " << point.x() << " , " << point.y() << "]"; } jsonStream << "], " << std::endl; jsonStream << "\"volume_env\": [" << std::endl; points = getKickEnvelopePoints(GeonkickApi::EnvelopeType::DistortionVolume); first = true; for (const auto &point: points) { if (first) first = false; else jsonStream << ", "; jsonStream << "[ " << point.x() << " , " << point.y() << "]"; } jsonStream << "]" << std::endl; jsonStream << "}" << std::endl; // distortion jsonStream << "}" << std::endl; // kick } void PercussionState::setLayerEnabled(GeonkickApi::Layer layer, bool b) { auto index = static_cast(layer); if (index >= 0 && index < layers.size()) layers[index] = b; } bool PercussionState::isLayerEnabled(GeonkickApi::Layer layer) const { auto index = static_cast(layer); if (index >= 0 && index < layers.size()) return layers[index]; else return false; } void PercussionState::setCurrentLayer(GeonkickApi::Layer layer) { currentLayer = layer; } void PercussionState::setLayerAmplitude(GeonkickApi::Layer layer, double amplitude) { auto index = static_cast(layer); if (index >= 0 && index < layersAmplitude.size()) layersAmplitude[index] = amplitude; } double PercussionState::getLayerAmplitude(GeonkickApi::Layer layer) const { auto index = static_cast(layer); if (index >= 0 && index < layersAmplitude.size()) return layersAmplitude[index]; return 0; } void PercussionState::tuneOutput(bool tune) { tunedOutput = tune; } bool PercussionState::isOutputTuned() const { return tunedOutput; } std::vector PercussionState::fromBase64F(const std::string &str) { return Base64EncoderDecoder::decode(str); } std::string PercussionState::toBase64F(const std::vector &data) { return Base64EncoderDecoder::encode(data); } bool PercussionState::save(const std::string &fileName) { if (fileName.size() < 7) { GEONKICK_LOG_ERROR("file name is wrong"); return false; } std::filesystem::path filePath(fileName); if (filePath.extension().empty() || (filePath.extension() != ".gkick" && filePath.extension() != ".GKICK")) filePath.replace_extension(".gkick"); std::ofstream file; file.open(std::filesystem::absolute(filePath)); if (!file.is_open()) { GEONKICK_LOG_ERROR("can't open file for saving: " << filePath); return false; } file << toJson(); file.close(); return true; } void PercussionState::setOscillatorSample(int oscillatorIndex, const std::vector &sample) { auto oscillator = getOscillator(oscillatorIndex); if (oscillator) oscillator->sample = sample; } std::vector PercussionState::getOscillatorSample(int oscillatorIndex) const { auto oscillator = getOscillator(oscillatorIndex); return oscillator->sample; } geonkick-2.10.2/src/percussion_state.h000066400000000000000000000230421451017372400177250ustar00rootroot00000000000000/** * File name: percussion_state.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_STATE_H #define GEONKICK_STATE_H #include "geonkick_api.h" #include #include #include #include class PercussionState { public: PercussionState(); bool loadFile(const std::string &file); bool loadData(const std::string &data); void loadObject(const rapidjson::Value &obj); size_t getId() const; void setId(size_t id); void setChannel(size_t channel); size_t getChannel() const; void setMute(bool b); bool isMuted() const; void setSolo(bool b); bool isSolo() const; std::string getName() const; void setName(const std::string &name); signed char getPlayingKey() const; void setPlayingKey(signed char key); bool isEnabled() const; void enable(bool b); void setLimiterValue(double val); void setKickLength(double val); void setKickAmplitude(double val); void enableKickFilter(bool b); void setKickFilterFrequency(double val); void setKickFilterQFactor(double val); void setKickFilterType(GeonkickApi::FilterType type); void setKickEnvelopePoints(GeonkickApi::EnvelopeType envelope, const std::vector &points); double getLimiterValue() const; double getKickLength() const; double getKickAmplitude() const; bool isKickFilterEnabled() const; double getKickFilterFrequency() const; double getKickFilterQFactor() const; GeonkickApi::FilterType getKickFilterType() const; std::vector getKickEnvelopePoints(GeonkickApi::EnvelopeType envelope) const; void setOscillatorEnabled(int index, bool b); void setOscillatorFunction(int index, GeonkickApi::FunctionType type); void setOscillatorPhase(int index, double phase); void setOscillatorSeed(int index, int seed); void setOscillatorAmplitue(int index, double val); void setOscillatorFrequency(int index, double val); void setOscillatorPitchShift(int index, double val); void setOscillatorFilterEnabled(int index, bool b); void setOscillatorFilterType(int index, GeonkickApi::FilterType type); void setOscillatorFilterCutOffFreq(int index, double val); void setOscillatorFilterFactor(int index, double val); void setOscillatorEnvelopePoints(int index, const std::vector &points, GeonkickApi::EnvelopeType envelope); bool isOscillatorAsFm(int index) const; void setOscillatorAsFm(int index, bool b); bool isOscillatorEnabled(int index) const; GeonkickApi::FunctionType oscillatorFunction(int index) const; double oscillatorAmplitue(int index) const; double oscillatorPhase(int index) const; int oscillatorSeed(int index) const; double oscillatorFrequency(int index) const; double oscillatorPitchShift(int index) const; bool isOscillatorFilterEnabled(int index) const; GeonkickApi::FilterType oscillatorFilterType(int index) const; double oscillatorFilterCutOffFreq(int index) const; double oscillatorFilterFactor(int index) const; std::vector oscillatorEnvelopePoints(int index, GeonkickApi::EnvelopeType type) const; void setOscillatorSample(int oscillatorIndex, const std::vector &sample); std::vector getOscillatorSample(int oscillatorIndex) const; void enableCompressor(bool enable); bool isCompressorEnabled() const; void setCompressorAttack(double attack); void setCompressorRelease(double release); void setCompressorThreshold(double threshold); void setCompressorRatio(double ratio); void setCompressorKnee(double knee); void setCompressorMakeup(double makeup); double getCompressorAttack() const; double getCompressorRelease() const; double getCompressorThreshold() const; double getCompressorRatio() const; double getCompressorKnee() const; double getCompressorMakeup() const; void enableDistortion(bool enable); bool isDistortionEnabled() const; void setDistortionVolume(double volume); void setDistortionInLimiter(double limit); void setDistortionDrive(double drive); double getDistortionInLimiter() const; double getDistortionVolume() const; double getDistortionDrive() const; std::string toJson() const; void setLayerEnabled(GeonkickApi::Layer layer, bool b); bool isLayerEnabled(GeonkickApi::Layer layer) const; void setCurrentLayer(GeonkickApi::Layer layer); void setLayerAmplitude(GeonkickApi::Layer layer, double amplitude); double getLayerAmplitude(GeonkickApi::Layer layer) const; void tuneOutput(bool tune); bool isOutputTuned() const; static std::vector fromBase64F(const std::string &str); static std::string toBase64F(const std::vector &data); bool save(const std::string &fileName); protected: void parseKickObject(const rapidjson::Value &kick); void parseOscillatorObject(int index, const rapidjson::Value &osc); static std::vector parseEnvelopeArray(const rapidjson::Value &envelopeArray); static void envelopeToJson(std::ostringstream &jsonStream, const std::string &envName, double amplitude, const std::vector &envelope); void oscJson(std::ostringstream &jsonStream) const; void kickJson(std::ostringstream &jsonStream) const; private: void initOscillators(); struct OscillatorInfo { OscillatorInfo() : type{GeonkickApi::OscillatorType::Oscillator1} , isEnabled{false} , isFm{false} , function{GeonkickApi::FunctionType::Sine} , phase{0} , seed{0} , amplitude{0.8} , frequency{200} , pitchShift{0} , isFilterEnabled{false} , filterType{GeonkickApi::FilterType::LowPass} , filterFrequency{200} , filterFactor{1.0} {} GeonkickApi::OscillatorType type; std::vector sample; bool isEnabled; bool isFm; GeonkickApi::FunctionType function; double phase; int seed; double amplitude; double frequency; double pitchShift; bool isFilterEnabled; GeonkickApi::FilterType filterType; double filterFrequency; double filterFactor; std::vector amplitudeEnvelope; std::vector frequencyEnvelope; std::vector filterCutOffEnvelope; std::vector pitchShiftEnvelope; }; std::shared_ptr getOscillator(int index) const; struct Compressor { bool enabled; double attack; double release; double threshold; double ratio; double knee; double makeup; }; struct Distortion { bool enabled; double in_limiter; double volume; double drive; }; int appVersion; size_t kickId; std::string kickName; signed char playingKey; size_t outputChannel; bool kickEnabled; bool percussionMuted; bool percussionSolo; double limiterValue; double kickLength; double kickAmplitude; bool kickFilterEnabled; double kickFilterFrequency; double kickFilterQFactor; GeonkickApi::FilterType kickFilterType; std::vector kickFilterCutOffEnvelope; std::vector kickEnvelopePoints; std::vector kickDistortionDriveEnvelope; std::vector kickDistortionVolumeEnvelope; std::unordered_map> oscillators; Compressor compressor; Distortion distortion; std::vector layers; std::vector layersAmplitude; GeonkickApi::Layer currentLayer; bool tunedOutput; }; #endif // GEONKICK_STATE_H geonkick-2.10.2/src/percussion_view.cpp000066400000000000000000000407721451017372400201230ustar00rootroot00000000000000/** * File name: percussion_view.cpp * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "kit_widget.h" #include "percussion_view.h" #include "percussion_model.h" #include "geonkick_slider.h" #include "MidiKeyWidget.h" #include "geonkick_button.h" #include #include #include #include #include #include RK_DECLARE_IMAGE_RC(mute); RK_DECLARE_IMAGE_RC(mute_hover); RK_DECLARE_IMAGE_RC(mute_on); RK_DECLARE_IMAGE_RC(solo); RK_DECLARE_IMAGE_RC(solo_hover); RK_DECLARE_IMAGE_RC(solo_on); RK_DECLARE_IMAGE_RC(per_play); RK_DECLARE_IMAGE_RC(per_play_hover); RK_DECLARE_IMAGE_RC(per_play_on); RK_DECLARE_IMAGE_RC(remove_per_button); RK_DECLARE_IMAGE_RC(remove_per_button_hover); RK_DECLARE_IMAGE_RC(remove_per_button_on); RK_DECLARE_IMAGE_RC(copy_per_button); RK_DECLARE_IMAGE_RC(copy_per_button_hover); RK_DECLARE_IMAGE_RC(copy_per_button_on); RK_DECLARE_IMAGE_RC(kit_midi_on); RK_DECLARE_IMAGE_RC(kit_midi_off); RK_DECLARE_IMAGE_RC(kit_midi_hover); PercussionLimiter::PercussionLimiter(GeonkickWidget *parent) : GeonkickSlider(parent) , levelerValue{0} { } void PercussionLimiter::setLeveler(int value) { levelerValue = value; if (value > 100) levelerValue = 100; else if (value < 0) levelerValue = 0; else levelerValue = value; update(); } int PercussionLimiter::getLeveler() const { return levelerValue; } void PercussionLimiter::paintWidget(RkPaintEvent *event) { GeonkickSlider::paintWidget(event); RkPainter painter(this); double value = (static_cast(levelerValue) / 100) * (width() - 2); RkColor color(40, 200, 40); if (levelerValue > 0) { if (getOrientation() == GeonkickSlider::Orientation::Horizontal) painter.fillRect(RkRect(1, 2, value, height() - 4), color); else painter.fillRect(RkRect(height() - 2 - value, 2, width() - 4, value), color); } } KitPercussionView::KitPercussionView(KitWidget *parent, PercussionModel *model) : GeonkickWidget(parent) , parentView{parent} , percussionModel{model} , nameWidth{100} , channelWidth{30} , editPercussion{nullptr} , keyButton{nullptr} , copyButton{nullptr} , removeButton{nullptr} , playButton{nullptr} , muteButton{nullptr} , soloButton{nullptr} , percussionLimiter{nullptr} { setSize(parent->width(), 20); createView(); setModel(model); } KitPercussionView::PercussionIndex KitPercussionView::index() const { if (percussionModel) return percussionModel->index(); return -1; } void KitPercussionView::createView() { auto percussionContainer = new RkContainer(this); percussionContainer->setSize(size()); percussionContainer->setHiddenTakesPlace(); percussionContainer->addSpace(nameWidth + percussionModel->numberOfChannels() * channelWidth + 10); // Midi key button keyButton = new GeonkickButton(this); keyButton->setTextColor({250, 250, 250}); keyButton->setType(RkButton::ButtonType::ButtonUncheckable); keyButton->setSize(30, 20); keyButton->setImage(RkImage(keyButton->size(), RK_IMAGE_RC(kit_midi_off)), RkButton::State::Unpressed); keyButton->setImage(RkImage(keyButton->size(), RK_IMAGE_RC(kit_midi_on)), RkButton::State::Pressed); keyButton->setImage(RkImage(keyButton->size(), RK_IMAGE_RC(kit_midi_hover)), RkButton::State::UnpressedHover); RK_ACT_BIND(keyButton, toggled, RK_ACT_ARGS(bool pressed), this, showMidiPopup()); percussionContainer->addWidget(keyButton); percussionContainer->addSpace(10); // Remove button removeButton = new RkButton(this); removeButton->setType(RkButton::ButtonType::ButtonPush); removeButton->setSize(16, 16); removeButton->setImage(RkImage(removeButton->size(), RK_IMAGE_RC(remove_per_button)), RkButton::State::Unpressed); removeButton->setImage(RkImage(removeButton->size(), RK_IMAGE_RC(remove_per_button_hover)), RkButton::State::UnpressedHover); removeButton->setImage(RkImage(removeButton->size(), RK_IMAGE_RC(remove_per_button_on)), RkButton::State::Pressed); removeButton->setImage(RkImage(removeButton->size(), RK_IMAGE_RC(remove_per_button_hover)), RkButton::State::PressedHover); removeButton->show(); percussionContainer->addWidget(removeButton); percussionContainer->addSpace(3); // Copy button copyButton = new RkButton(this); copyButton->setType(RkButton::ButtonType::ButtonPush); copyButton->setSize(16, 16); copyButton->setImage(RkImage(copyButton->size(), RK_IMAGE_RC(copy_per_button)), RkButton::State::Unpressed); copyButton->setImage(RkImage(copyButton->size(), RK_IMAGE_RC(copy_per_button_hover)), RkButton::State::UnpressedHover); copyButton->setImage(RkImage(copyButton->size(), RK_IMAGE_RC(copy_per_button_on)), RkButton::State::Pressed); copyButton->setImage(RkImage(copyButton->size(), RK_IMAGE_RC(copy_per_button_hover)), RkButton::State::PressedHover); copyButton->show(); percussionContainer->addWidget(copyButton); percussionContainer->addSpace(10); // Limiter percussionLimiter = new PercussionLimiter(this); percussionLimiter->setSize(100, 10); auto limiterBox = new RkContainer(this, Rk::Orientation::Vertical); limiterBox->setHiddenTakesPlace(); limiterBox->setSize({percussionLimiter->width(), percussionContainer->height()}); limiterBox->addSpace((height() - percussionLimiter->height()) / 2); limiterBox->addWidget(percussionLimiter); percussionContainer->addSpace(5); percussionContainer->addContainer(limiterBox); percussionContainer->addSpace(10); // Mute button muteButton = new RkButton(this); muteButton->setType(RkButton::ButtonType::ButtonCheckable); muteButton->setSize(16, 16); muteButton->setImage(RkImage(muteButton->size(), RK_IMAGE_RC(mute)), RkButton::State::Unpressed); muteButton->setImage(RkImage(muteButton->size(), RK_IMAGE_RC(mute_hover)), RkButton::State::UnpressedHover); muteButton->setImage(RkImage(muteButton->size(), RK_IMAGE_RC(mute_on)), RkButton::State::Pressed); muteButton->setImage(RkImage(muteButton->size(), RK_IMAGE_RC(mute_hover)), RkButton::State::PressedHover); muteButton->show(); percussionContainer->addWidget(muteButton); percussionContainer->addSpace(3); // Solo button soloButton = new RkButton(this); soloButton->setType(RkButton::ButtonType::ButtonCheckable); soloButton->setSize(16, 16); soloButton->setImage(RkImage(soloButton->size(), RK_IMAGE_RC(solo)), RkButton::State::Unpressed); soloButton->setImage(RkImage(soloButton->size(), RK_IMAGE_RC(solo_hover)), RkButton::State::UnpressedHover); soloButton->setImage(RkImage(soloButton->size(), RK_IMAGE_RC(solo_on)), RkButton::State::Pressed); soloButton->setImage(RkImage(soloButton->size(), RK_IMAGE_RC(solo_hover)), RkButton::State::PressedHover); soloButton->show(); percussionContainer->addWidget(soloButton); percussionContainer->addSpace(3); // Play button playButton = new RkButton(this); playButton->setType(RkButton::ButtonType::ButtonPush); playButton->setSize(16, 16); playButton->setImage(RkImage(playButton->size(), RK_IMAGE_RC(per_play)), RkButton::State::Unpressed); playButton->setImage(RkImage(playButton->size(), RK_IMAGE_RC(per_play_hover)), RkButton::State::UnpressedHover); playButton->setImage(RkImage(playButton->size(), RK_IMAGE_RC(per_play_on)), RkButton::State::Pressed); playButton->show(); percussionContainer->addWidget(playButton); } void KitPercussionView::updateView() { percussionLimiter->onSetValue(percussionModel->limiter()); muteButton->setPressed(percussionModel->isMuted()); soloButton->setPressed(percussionModel->isSolo()); keyButton->setText(MidiKeyWidget::midiKeyToNote(percussionModel->key())); keyButton->setBackgroundColor((index() % 2) ? RkColor(100, 100, 100) : RkColor(50, 50, 50)); update(); } void KitPercussionView::setModel(PercussionModel *model) { if (!model) return; percussionModel = model; RK_ACT_BIND(removeButton, released, RK_ACT_ARGS(), this, remove()); RK_ACT_BIND(copyButton, released, RK_ACT_ARGS(), percussionModel, copy()); RK_ACT_BIND(playButton, pressed, RK_ACT_ARGS(), percussionModel, play()); RK_ACT_BIND(muteButton, toggled, RK_ACT_ARGS(bool toggled), percussionModel, mute(toggled)); RK_ACT_BIND(soloButton, toggled, RK_ACT_ARGS(bool toggled), percussionModel, solo(toggled)); RK_ACT_BIND(percussionLimiter, valueUpdated, RK_ACT_ARGS(int val), percussionModel, setLimiter(val)); RK_ACT_BIND(percussionModel, nameUpdated, RK_ACT_ARGS(std::string name), this, update()); RK_ACT_BIND(percussionModel, keyUpdated, RK_ACT_ARGS(KeyIndex index), this, updateView()); RK_ACT_BIND(percussionModel, channelUpdated, RK_ACT_ARGS(int val), this, update()); RK_ACT_BIND(percussionModel, limiterUpdated, RK_ACT_ARGS(int val), percussionLimiter, onSetValue(val)); RK_ACT_BIND(percussionModel, muteUpdated, RK_ACT_ARGS(bool b), muteButton, setPressed(b)); RK_ACT_BIND(percussionModel, soloUpdated, RK_ACT_ARGS(bool b), soloButton, setPressed(b)); RK_ACT_BIND(percussionModel, selected, RK_ACT_ARGS(), this, update()); RK_ACT_BIND(percussionModel, modelUpdated, RK_ACT_ARGS(), this, updateView()); updateView(); } PercussionModel* KitPercussionView::getModel() { return percussionModel; } void KitPercussionView::remove() { if (getModel()) getModel()->remove(); } void KitPercussionView::paintWidget(RkPaintEvent *event) { RkImage img(size()); RkPainter paint(&img); paint.fillRect(rect(), background()); auto pen = paint.pen(); pen.setColor({200, 200, 200}); auto font = paint.font(); font.setSize(12); paint.setFont(font); RkColor backgroundColor = {160, 160, 160, 80}; if (index() % 2) backgroundColor = {200, 200, 200, 80}; paint.fillRect(RkRect(0, 0, nameWidth, height()), backgroundColor); paint.setPen(pen); paint.drawText(RkRect(7, (height() - font.size()) / 2, nameWidth, font.size()), percussionModel->name(), Rk::Alignment::AlignLeft); auto n = percussionModel->numberOfChannels(); int x = nameWidth; while (n--) { if (n % 2) paint.fillRect(RkRect(x, 0, channelWidth, height()), {backgroundColor.red() + 20, backgroundColor.green() + 20, backgroundColor.blue() + 20, 80}); else paint.fillRect(RkRect(x, 0, channelWidth, height()), {backgroundColor.red() - 20, backgroundColor.green() - 20, backgroundColor.blue() - 20, 80}); x += channelWidth; } if (percussionModel->isSelected()) paint.fillRect(RkRect(0, 0, 4, height()), {255, 255, 255, 90}); pen = paint.pen(); pen.setColor({50, 160, 50}); pen.setWidth(8); paint.setPen(pen); paint.drawCircle({nameWidth + percussionModel->channel() * channelWidth + channelWidth / 2 , height() / 2}, 4); RkPainter painter(this); painter.drawImage(img, 0, 0); } void KitPercussionView::mouseButtonPressEvent(RkMouseEvent *event) { if (event->button() != RkMouseEvent::ButtonType::Left && event->button() != RkMouseEvent::ButtonType::WheelUp && event->button() != RkMouseEvent::ButtonType::WheelDown) return; updatePercussionName(); setFocus(true); if (event->button() == RkMouseEvent::ButtonType::Left) { int leftLimit = nameWidth; int rightLimit = nameWidth + channelWidth * percussionModel->numberOfChannels(); if (event->x() <= leftLimit) percussionModel->select(); else if (event->x() > leftLimit && event->x() < rightLimit) { GEONKICK_LOG_INFO("set channel: " << (event->x() - nameWidth) / channelWidth); percussionModel->setChannel((event->x() - nameWidth) / channelWidth); } } } void KitPercussionView::mouseDoubleClickEvent(RkMouseEvent *event) { if (event->button() == RkMouseEvent::ButtonType::WheelUp || event->button() == RkMouseEvent::ButtonType::WheelDown) { mouseButtonPressEvent(event); return; } if (event->button() == RkMouseEvent::ButtonType::Left && event->x() < nameWidth) { if (editPercussion == nullptr) { editPercussion = new RkLineEdit(this); editPercussion->setSize({nameWidth, height()}); RK_ACT_BIND(editPercussion, editingFinished, RK_ACT_ARGS(), this, updatePercussionName()); } editPercussion->setText(percussionModel->name()); editPercussion->moveCursorToFront(); editPercussion->show(); editPercussion->setFocus(); } } void KitPercussionView::updatePercussionName() { if (editPercussion) { auto name = editPercussion->text(); if (!name.empty()) { percussionModel->setName(name); editPercussion->close(); editPercussion = nullptr; } } } void KitPercussionView::updateLeveler() { if (percussionModel->leveler() > percussionLimiter->getLeveler()) percussionLimiter->setLeveler(percussionModel->leveler()); else if (percussionLimiter->getLeveler() > 0) percussionLimiter->setLeveler(percussionLimiter->getLeveler() - 2); } void KitPercussionView::showMidiPopup() { auto midiPopup = new MidiKeyWidget(dynamic_cast(getTopWidget()), percussionModel); midiPopup->setPosition(keyButton->x() - midiPopup->width() - 5, getTopWidget()->height() - 2 * midiPopup->height() + height() * (index() - 3)); RK_ACT_BIND(midiPopup, isAboutToClose, RK_ACT_ARGS(), keyButton, setPressed(false)); midiPopup->show(); } geonkick-2.10.2/src/percussion_view.h000066400000000000000000000051641451017372400175640ustar00rootroot00000000000000/** * File name: percussion_view.h * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef PERCUSSION_VIEW_H #define PERCUSSION_VIEW_H #include "geonkick_widget.h" #include "geonkick_slider.h" #include class RkLineEdit; class RkButton; class PercussionModel; class RkProgressBar; class KitWidget; class GeonkickButton; class PercussionLimiter : public GeonkickSlider { public: PercussionLimiter(GeonkickWidget *parent); void setLeveler(int value); int getLeveler() const; protected: virtual void paintWidget(RkPaintEvent *event) override; private: int levelerValue; }; class KitPercussionView: public GeonkickWidget { public: using PercussionIndex = int; using KeyIndex = int; explicit KitPercussionView(KitWidget *parent, PercussionModel *model = nullptr); void setModel(PercussionModel *model); PercussionModel* getModel(); PercussionIndex index() const; void updateLeveler(); protected: void createView(); void updateView(); void setNameWidth(int width); void setKeyWidth(int width); void paintWidget(RkPaintEvent *event) override; void mouseButtonPressEvent(RkMouseEvent *event) override; void mouseDoubleClickEvent(RkMouseEvent *event) override; void updatePercussionName(); void remove(); void showMidiPopup(); private: KitWidget *parentView; PercussionModel *percussionModel; int nameWidth; int channelWidth; RkLineEdit *editPercussion; GeonkickButton *keyButton; RkButton *copyButton; RkButton *removeButton; RkButton *playButton; RkButton *muteButton; RkButton *soloButton; PercussionLimiter *percussionLimiter; }; #endif // KIT_WIDGET_H geonkick-2.10.2/src/plugin/000077500000000000000000000000001451017372400154575ustar00rootroot00000000000000geonkick-2.10.2/src/plugin/CMakeLists.txt000066400000000000000000000002151451017372400202150ustar00rootroot00000000000000if (GKICK_PLUGIN_LV2) add_subdirectory(lv2) endif(GKICK_PLUGIN_LV2) if (GKICK_PLUGIN_VST) add_subdirectory(vst) endif(GKICK_PLUGIN_VST) geonkick-2.10.2/src/plugin/lv2/000077500000000000000000000000001451017372400161625ustar00rootroot00000000000000geonkick-2.10.2/src/plugin/lv2/CMakeLists.txt000066400000000000000000000027311451017372400207250ustar00rootroot00000000000000set(GKICK_LV2_SOURCES ${GKICK_LV2_DIR}/geonkick_lv2.cpp) add_library(geonkick_lv2 SHARED ${GKICK_COMMON_HEADERS} ${GKICK_COMMON_RESOURCES} ${GKICK_LV2_SOURCES}) set_target_properties(geonkick_lv2 PROPERTIES PREFIX "") target_link_libraries(geonkick_lv2 geonkick_common api_plugin redkite) target_link_libraries(geonkick_lv2 "-lstdc++fs") target_link_libraries(geonkick_lv2 "-lX11 -lsndfile -lssl -lcrypto -lrt -lm -ldl -lpthread -lcairo") add_dependencies(geonkick_lv2 api_plugin) add_dependencies(geonkick_lv2 redkite) if (GKICK_PLUGIN) add_library(geonkick_single_lv2 SHARED ${GKICK_COMMON_HEADERS} ${GKICK_COMMON_RESOURCES} ${GKICK_LV2_SOURCES}) set_target_properties(geonkick_single_lv2 PROPERTIES PREFIX "") target_link_libraries(geonkick_single_lv2 geonkick_common_single api_plugin_single redkite) target_link_libraries(geonkick_single_lv2 "-lstdc++fs") target_link_libraries(geonkick_single_lv2 "-lX11 -lsndfile -lcrypto -lrt -lm -ldl -lpthread -lcairo") add_dependencies(geonkick_single_lv2 api_plugin_single) target_compile_options(geonkick_single_lv2 PUBLIC ${GKICK_API_PLUGIN_SINGLE_FLAGS}) install(TARGETS geonkick_single_lv2 DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/lv2/geonkick.lv2) endif(GKICK_PLUGIN) install(TARGETS geonkick_lv2 DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/lv2/geonkick.lv2) install(DIRECTORY ${GKICK_LV2_DIR}/geonkick.lv2 DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/lv2) geonkick-2.10.2/src/plugin/lv2/geonkick.lv2/000077500000000000000000000000001451017372400204565ustar00rootroot00000000000000geonkick-2.10.2/src/plugin/lv2/geonkick.lv2/geonkick.ttl000066400000000000000000000343571451017372400230110ustar00rootroot00000000000000@prefix foaf: . @prefix ls: . @prefix ls: . @prefix lv2: . @prefix midi: . @prefix atom: . @prefix doap: . @prefix rdfs: . @prefix state: . @prefix ui: . @prefix pg: . @prefix urid: . a foaf:Person ; foaf:name "Iurie Nistor" ; foaf:mbox ; foaf:homepage . a doap:Project ; doap:maintainer ; doap:name "Geonkick" . a lv2:Plugin, lv2:InstrumentPlugin; doap:name "Geonkick"; lv2:project ; lv2:extensionData state:interface ; lv2:optionalFeature lv2:hardRTCapable ; lv2:requiredFeature urid:map ; lv2:minorVersion 20 ; lv2:microVersion 0 ; doap:license ; ui:ui ; lv2:port [ a lv2:InputPort , atom:AtomPort ; atom:bufferType atom:Sequence ; atom:supports midi:MidiEvent ; lv2:designation lv2:control ; lv2:index 0 ; lv2:symbol "midi_in" ; lv2:name "MIDI In" ; ] , [ a lv2:OutputPort, atom:AtomPort ; atom:bufferType atom:Sequence ; lv2:index 1 ; lv2:symbol "notify" ; lv2:name "Notify"; ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 2 ; lv2:symbol "out__1_left" ; lv2:name "Output Left" ; lv2:designation pg:left ; pg:group ls:Out1 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 3 ; lv2:symbol "out__1_right" ; lv2:name "Output Right" ; lv2:designation pg:right ; pg:group ls:Out1 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 4 ; lv2:symbol "out__2_left" ; lv2:name "Output 2 Left" ; lv2:designation pg:left ; pg:group ls:Out2 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 5 ; lv2:symbol "out__2_right" ; lv2:name "Output 2 Right" ; lv2:designation pg:right ; pg:group ls:Out2 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 6 ; lv2:symbol "out__3_left" ; lv2:name "Output 3 Left" ; lv2:designation pg:left ; pg:group ls:Out3 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 7 ; lv2:symbol "out__3_right" ; lv2:name "Output 3 Right" ; lv2:designation pg:right ; pg:group ls:Out3 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 8 ; lv2:symbol "out__4_left" ; lv2:name "Output 4 Left" ; lv2:designation pg:left ; pg:group ls:Out4 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 9 ; lv2:symbol "out__4_right" ; lv2:name "Output 4 Right" ; lv2:designation pg:right ; pg:group ls:Out4 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 10 ; lv2:symbol "out__5_left" ; lv2:name "Output 5 Left" ; lv2:designation pg:left ; pg:group ls:Out5 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 11 ; lv2:symbol "out__5_right" ; lv2:name "Output 5 Right" ; lv2:designation pg:right ; pg:group ls:Out5 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 12 ; lv2:symbol "out__6_left" ; lv2:name "Output 6 Left" ; lv2:designation pg:left ; pg:group ls:Out6 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 13 ; lv2:symbol "out__6_right" ; lv2:name "Output 6 Right" ; lv2:designation pg:right ; pg:group ls:Out6 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 14 ; lv2:symbol "out__7_left" ; lv2:name "Output 7 Left" ; lv2:designation pg:left ; pg:group ls:Out7 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 15 ; lv2:symbol "out__7_right" ; lv2:name "Output 7 Right" ; lv2:designation pg:right ; pg:group ls:Out7 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 16 ; lv2:symbol "out__8_left" ; lv2:name "Output 8 Left" ; lv2:designation pg:left ; pg:group ls:Out8 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 17 ; lv2:symbol "out__8_right" ; lv2:name "Output 8 Right" ; lv2:designation pg:right ; pg:group ls:Out8 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 18 ; lv2:symbol "out__9_left" ; lv2:name "Output 9 Left" ; lv2:designation pg:left ; pg:group ls:Out9 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 19 ; lv2:symbol "out__9_right" ; lv2:name "Output 9 Right" ; lv2:designation pg:right ; pg:group ls:Out9 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 20 ; lv2:symbol "out__10_left" ; lv2:name "Output 10 Left" ; lv2:designation pg:left ; pg:group ls:Out10 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 21 ; lv2:symbol "out__10_right" ; lv2:name "Output 10 Right" ; lv2:designation pg:right ; pg:group ls:Out10 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 22 ; lv2:symbol "out__11_left" ; lv2:name "Output 11 Left" ; lv2:designation pg:left ; pg:group ls:Out11 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 23 ; lv2:symbol "out__11_right" ; lv2:name "Output 11 Right" ; lv2:designation pg:right ; pg:group ls:Out11 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 24 ; lv2:symbol "out__12_left" ; lv2:name "Output 12 Left" ; lv2:designation pg:left ; pg:group ls:Out12 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 25 ; lv2:symbol "out__12_right" ; lv2:name "Output 12 Right" ; lv2:designation pg:right ; pg:group ls:Out12 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 26 ; lv2:symbol "out__13_left" ; lv2:name "Output 13 Left" ; lv2:designation pg:left ; pg:group ls:Out13 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 27 ; lv2:symbol "out__13_right" ; lv2:name "Output 13 Right" ; lv2:designation pg:right ; pg:group ls:Out13 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 28 ; lv2:symbol "out__14_left" ; lv2:name "Output 14 Left" ; lv2:designation pg:left ; pg:group ls:Out14 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 29 ; lv2:symbol "out__14_right" ; lv2:name "Output 14 Right" ; lv2:designation pg:right ; pg:group ls:Out14 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 30 ; lv2:symbol "out__15_left" ; lv2:name "Output 15 Left" ; lv2:designation pg:left ; pg:group ls:Out15 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 31 ; lv2:symbol "out__15_right" ; lv2:name "Output 15 Right" ; lv2:designation pg:right ; pg:group ls:Out15 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 32 ; lv2:symbol "out__16_left" ; lv2:name "Output 16 Left" ; lv2:designation pg:left ; pg:group ls:Out16 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 33 ; lv2:symbol "out__16_right" ; lv2:name "Output 16 Right" ; lv2:designation pg:right ; pg:group ls:Out16 ] . a foaf:Person ; foaf:name "Iurie Nistor" ; foaf:mbox ; foaf:homepage . a doap:Project ; doap:maintainer ; doap:name "Geonkick Single" . a doap:Project ; doap:maintainer ; doap:name "Geonkick Single" . a lv2:Plugin, lv2:InstrumentPlugin; doap:name "Geonkick Single"; lv2:project ; lv2:extensionData state:interface ; lv2:optionalFeature lv2:hardRTCapable ; lv2:requiredFeature urid:map ; lv2:minorVersion 20 ; lv2:microVersion 0 ; doap:license ; ui:ui ; lv2:port [ a lv2:InputPort , atom:AtomPort ; atom:bufferType atom:Sequence ; atom:supports midi:MidiEvent ; lv2:designation lv2:control ; lv2:index 0 ; lv2:symbol "midi_in" ; lv2:name "MIDI In" ; ] , [ a lv2:OutputPort, atom:AtomPort ; atom:bufferType atom:Sequence ; lv2:index 1 ; lv2:symbol "notify" ; lv2:name "Notify"; ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 2 ; lv2:symbol "out__1_left" ; lv2:name "Output Left" ; lv2:designation pg:left ; pg:group ls:Out1 ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 3 ; lv2:symbol "out__1_right" ; lv2:name "Output Right" ; lv2:designation pg:right ; pg:group ls:Out1 ]. ls:Out1 a pg:OutputGroup, pg:DiscreteGroup; rdfs:label "Out 1" ; lv2:symbol "out1" ; pg:element [ lv2:index 0 ; lv2:designation pg:left ] , [ lv2:index 1 ; lv2:designation pg:right ]. ls:Out2 a pg:OutputGroup, pg:DiscreteGroup; rdfs:label "Out 2" ; lv2:symbol "out2" ; pg:element [ lv2:index 0 ; lv2:designation pg:left ] , [ lv2:index 1 ; lv2:designation pg:right ]. ls:Out3 a pg:OutputGroup, pg:DiscreteGroup; rdfs:label "Out 3" ; lv2:symbol "out3" ; pg:element [ lv2:index 0 ; lv2:designation pg:left ] , [ lv2:index 1 ; lv2:designation pg:right ]. ls:Out4 a pg:OutputGroup, pg:DiscreteGroup; rdfs:label "Out 4" ; lv2:symbol "out4" ; pg:element [ lv2:index 0 ; lv2:designation pg:left ] , [ lv2:index 1 ; lv2:designation pg:right ]. ls:Out5 a pg:OutputGroup, pg:DiscreteGroup; rdfs:label "Out 5" ; lv2:symbol "out5" ; pg:element [ lv2:index 0 ; lv2:designation pg:left ] , [ lv2:index 1 ; lv2:designation pg:right ]. ls:Out6 a pg:OutputGroup, pg:DiscreteGroup; rdfs:label "Out 6" ; lv2:symbol "out6" ; pg:element [ lv2:index 0 ; lv2:designation pg:left ] , [ lv2:index 1 ; lv2:designation pg:right ]. ls:Out7 a pg:OutputGroup, pg:DiscreteGroup; rdfs:label "Out 7" ; lv2:symbol "out7" ; pg:element [ lv2:index 0 ; lv2:designation pg:left ] , [ lv2:index 1 ; lv2:designation pg:right ]. ls:Out8 a pg:OutputGroup, pg:DiscreteGroup; rdfs:label "Out 8" ; lv2:symbol "out8" ; pg:element [ lv2:index 0 ; lv2:designation pg:left ] , [ lv2:index 1 ; lv2:designation pg:right ]. ls:Out9 a pg:OutputGroup, pg:DiscreteGroup; rdfs:label "Out 9" ; lv2:symbol "out9" ; pg:element [ lv2:index 0 ; lv2:designation pg:left ] , [ lv2:index 1 ; lv2:designation pg:right ]. ls:Out10 a pg:OutputGroup, pg:DiscreteGroup; rdfs:label "Out 10" ; lv2:symbol "out10" ; pg:element [ lv2:index 0 ; lv2:designation pg:left ] , [ lv2:index 1 ; lv2:designation pg:right ]. ls:Out11 a pg:OutputGroup, pg:DiscreteGroup; rdfs:label "Out 11" ; lv2:symbol "out11" ; pg:element [ lv2:index 0 ; lv2:designation pg:left ] , [ lv2:index 1 ; lv2:designation pg:right ]. ls:Out12 a pg:OutputGroup, pg:DiscreteGroup; rdfs:label "Out 12" ; lv2:symbol "out12" ; pg:element [ lv2:index 0 ; lv2:designation pg:left ] , [ lv2:index 1 ; lv2:designation pg:right ]. ls:Out13 a pg:OutputGroup, pg:DiscreteGroup; rdfs:label "Out 13" ; lv2:symbol "out13" ; pg:element [ lv2:index 0 ; lv2:designation pg:left ] , [ lv2:index 1 ; lv2:designation pg:right ]. ls:Out14 a pg:OutputGroup, pg:DiscreteGroup; rdfs:label "Out 14" ; lv2:symbol "out14" ; pg:element [ lv2:index 0 ; lv2:designation pg:left ] , [ lv2:index 1 ; lv2:designation pg:right ]. ls:Out15 a pg:OutputGroup, pg:DiscreteGroup; rdfs:label "Out 15" ; lv2:symbol "out15" ; pg:element [ lv2:index 0 ; lv2:designation pg:left ] , [ lv2:index 1 ; lv2:designation pg:right ]. ls:Out16 a pg:OutputGroup, pg:DiscreteGroup; rdfs:label "Out 16" ; lv2:symbol "out16" ; pg:element [ lv2:index 0 ; lv2:designation pg:left ] , [ lv2:index 1 ; lv2:designation pg:right ]. a ui:X11UI ; lv2:requiredFeature ; lv2:requiredFeature ui:idleInterface, ui:parent, ui:resize; lv2:extensionData ui:idleInterface; ui:binary . a ui:X11UI ; lv2:requiredFeature ; lv2:requiredFeature ui:idleInterface, ui:parent, ui:resize; lv2:extensionData ui:idleInterface; ui:binary . geonkick-2.10.2/src/plugin/lv2/geonkick.lv2/manifest.ttl000066400000000000000000000006141451017372400230120ustar00rootroot00000000000000@prefix lv2: . @prefix rdfs: . @prefix ui: . a lv2:Plugin; lv2:binary ; rdfs:seeAlso . a lv2:Plugin; lv2:binary ; rdfs:seeAlso . geonkick-2.10.2/src/plugin/lv2/geonkick_lv2.cpp000066400000000000000000000433621451017372400212530ustar00rootroot00000000000000/** * File name: geonkick_lv2.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include "MainWindow.h" #include "geonkick_api.h" #include "kit_state.h" #include "GeonkickConfig.h" #include #include #include #include #include #ifdef GEONKICK_SINGLE #define GEONKICK_URI "http://geontime.com/geonkick/single" #define GEONKICK_URI_UI "http://geontime.com/geonkick/single#ui" #define GEONKICK_URI_STATE "http://geontime.com/geonkick/single#state" #else #define GEONKICK_URI "http://geontime.com/geonkick" #define GEONKICK_URI_UI "http://geontime.com/geonkick#ui" #define GEONKICK_URI_STATE "http://geontime.com/geonkick#state" #endif // GEONKICK_SINGLE #define GEONKICK_URI_STATE_CHANGED "http://lv2plug.in/ns/ext/state#StateChanged" class GeonkickLv2Plugin : public RkObject { public: GeonkickLv2Plugin(int sampleRate) : geonkickApi{std::make_unique(sampleRate, GeonkickApi::InstanceType::Lv2)} , midiIn{nullptr} , notifyHostChannel{nullptr} , atomInfo{0} , kickIsUpdated{false} { RK_ACT_BIND(geonkickApi.get(), kickUpdated, RK_ACT_ARGS(), this, kickUpdated()); RK_ACT_BIND(geonkickApi.get(), stateChanged, RK_ACT_ARGS(), this, kickUpdated()); } bool init() { if (!geonkickApi->init()) { GEONKICK_LOG_ERROR("can't init DSP"); return false; } outputChannels = std::vector(2 * geonkickApi->numberOfChannels(), nullptr); return true; } void setAudioChannel(float *data, size_t channel) { if (channel < outputChannels.size()) outputChannels[channel] = data; } void setMidiIn(LV2_Atom_Sequence *data) { midiIn = data; } void setNotifyHostChannel(LV2_Atom_Sequence *data) { notifyHostChannel = data; } void setStateId(LV2_URID id) { atomInfo.stateId = id; } LV2_URID getStateId() const { return atomInfo.stateId; } void setAtomChunkId(LV2_URID id) { atomInfo.atomStringId = id; } LV2_URID getAtomChunkId() const { return atomInfo.atomStringId; } void setAtomSequence(LV2_URID sequence) { atomInfo.atomSequence = sequence; } LV2_URID getAtomSequence(void) const { return atomInfo.atomSequence; } void setAtomStateChanged(LV2_URID changed) { atomInfo.atomStateChanged = changed; } LV2_URID getAtomStateChanged(void) const { return atomInfo.atomStateChanged; } void setAtomObject(LV2_URID object) { atomInfo.atomObject = object; } LV2_URID getAtomObject(void) const { return atomInfo.atomObject; } void setStateData(const std::string &data, int flags = 0) { RK_UNUSED(flags); if (data.find("UiSettings") == std::string::npos) { GEONKICK_LOG_INFO("old plugin state version"); geonkickApi->setKitState(data); geonkickApi->notifyUpdateGui(); geonkickApi->notifyKitUpdated(); } else { geonkickApi->setState(data); geonkickApi->notifyUpdateGui(); geonkickApi->notifyKitUpdated(); } } std::string getStateData() { return geonkickApi->getState(); } GeonkickApi* getApi() const { return geonkickApi.get(); } bool isNote(const uint8_t* buffer) const { return (((*(buffer) & 0xf0)) == 0x90) || (((*(buffer) & 0xf0)) == 0x80); } void processSamples(size_t nsamples) { if (!midiIn || nsamples < 1) return; for (size_t i = 0; i < outputChannels.size(); i++) memset(outputChannels[i], 0, nsamples * sizeof(float)); size_t currentFrame = 0; size_t offset = 0; auto it = lv2_atom_sequence_begin(&midiIn->body); while (!lv2_atom_sequence_is_end(&midiIn->body, midiIn->atom.size, it)) { size_t eventFrame = it->time.frames; size_t size = eventFrame - currentFrame; if (size > 0 && size <= nsamples) { geonkickApi->process(outputChannels.data(), offset, size); offset += size; } const uint8_t* const msg = (const uint8_t*)(it + 1); switch (lv2_midi_message_type(msg)) { case LV2_MIDI_MSG_NOTE_ON: geonkickApi->setKeyPressed(true, msg[1], msg[2]); break; case LV2_MIDI_MSG_NOTE_OFF: geonkickApi->setKeyPressed(false, msg[1], msg[2]); break; default: break; } currentFrame = eventFrame; it = lv2_atom_sequence_next(it); } if (currentFrame < nsamples) geonkickApi->process(outputChannels.data(), offset, nsamples - currentFrame); if (isKickUpdated()) { notifyHost(); setKickUpdated(false); } } void notifyHost() const { if (!notifyHostChannel) return; auto sequence = static_cast(notifyHostChannel); size_t neededAtomSize = sizeof(LV2_Atom_Event) + sizeof(LV2_Atom_Object_Body); if (sequence) { sequence->atom.type = getAtomSequence(); sequence->atom.size = sizeof(LV2_Atom_Sequence_Body); sequence->body.unit = 0; sequence->body.pad = 0; auto event = reinterpret_cast(sequence + 1); event->time.frames = 0; auto atomObject = reinterpret_cast(&event->body); atomObject->atom.type = getAtomObject(); atomObject->atom.size = sizeof(LV2_Atom_Object_Body); atomObject->body.id = 0; atomObject->body.otype = getAtomStateChanged(); sequence->atom.size += neededAtomSize; } } protected: void setKickUpdated(bool b) { kickIsUpdated = b; } void kickUpdated() { setKickUpdated(true); } bool isKickUpdated() const { return kickIsUpdated; } private: std::unique_ptr geonkickApi; LV2_Atom_Sequence *midiIn; LV2_Atom_Sequence *notifyHostChannel; std::vector outputChannels; struct AtomInfo { LV2_URID stateId; LV2_URID atomStringId; LV2_URID atomSequence; LV2_URID atomStateChanged; LV2_URID atomObject; }; AtomInfo atomInfo; std::atomic kickIsUpdated; }; /** * Creates and shows an instance of Geonkick GUI that takes * the geonkick API instance as a pointer. */ static LV2UI_Handle gkick_instantiate_ui(const LV2UI_Descriptor* descriptor, const char* plugin_uri, const char* bundle_path, LV2UI_Write_Function write_function, LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features) { GeonkickLv2Plugin *geonkickLv2PLugin = nullptr; if (!features) return nullptr; void *parent = nullptr; LV2UI_Resize *resize = nullptr; const LV2_Feature *feature; while ((feature = *features)) { if (std::string(feature->URI) == std::string(LV2_UI__parent)) parent = static_cast(feature->data); if (std::string(feature->URI) == std::string(LV2_UI__resize)) resize = static_cast(feature->data); if (std::string(feature->URI) == std::string(LV2_INSTANCE_ACCESS_URI)) { geonkickLv2PLugin = static_cast(feature->data); if (!geonkickLv2PLugin) return nullptr; } features++; } // Get the info about X Window parent window. const uintptr_t parentWinId = (uintptr_t)parent; Display* xDisplay = XOpenDisplay(nullptr); int screenNumber = DefaultScreen(xDisplay); auto info = rk_from_native_x11(xDisplay, screenNumber, parentWinId); auto guiApp = new RkMain(); geonkickLv2PLugin->getApi()->setEventQueue(guiApp->eventQueue()); auto mainWidget = new MainWindow(guiApp, geonkickLv2PLugin->getApi(), info); RK_ACT_BINDL(mainWidget, onScaleFactor, RK_ACT_ARGS(double factor), [=](double factor) { resize->ui_resize(resize->handle, mainWidget->width() * factor, mainWidget->height() * factor); }); if (!mainWidget->init()) { GEONKICK_LOG_ERROR("can't init main window"); delete guiApp; return nullptr; } auto winId = mainWidget->nativeWindowInfo()->window; *widget = (LV2UI_Widget)static_cast(winId); resize->ui_resize(resize->handle, mainWidget->width() * mainWidget->scaleFactor(), mainWidget->height() * mainWidget->scaleFactor()); return static_cast(guiApp); } static void gkick_cleanup_ui(LV2UI_Handle handle) { delete static_cast(handle); } static void gkick_port_event_ui(LV2UI_Handle ui, uint32_t port_index, uint32_t buffer_size, uint32_t format, const void *buffer ) { } static int gkick_idle(LV2UI_Handle ui) { static_cast(ui)->exec(false); return 0; } static const void* gkick_extension_data(const char* uri) { static const LV2UI_Idle_Interface idleInterface = {gkick_idle}; if (std::string(uri) == std::string(LV2_UI__idleInterface)) return &idleInterface; return nullptr; } static const LV2UI_Descriptor gkick_descriptor_ui = { GEONKICK_URI_UI, gkick_instantiate_ui, gkick_cleanup_ui, gkick_port_event_ui, gkick_extension_data }; const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index) { switch (index) { case 0: return &gkick_descriptor_ui; default: return nullptr; } } static LV2_Handle gkick_instantiate(const LV2_Descriptor* descriptor, double rate, const char* bundle_path, const LV2_Feature* const* features) { auto geonkickLv2PLugin = new GeonkickLv2Plugin(rate); if (!geonkickLv2PLugin->init()) { GEONKICK_LOG_ERROR("can't create DSP instance"); delete geonkickLv2PLugin; return nullptr; } const LV2_Feature *feature; while ((feature = *features)) { if (std::string(feature->URI) == std::string(LV2_URID__map)) { auto uridMap = static_cast(feature->data); if (uridMap && uridMap->map && uridMap->handle) { geonkickLv2PLugin->setStateId(uridMap->map(uridMap->handle, GEONKICK_URI_STATE)); geonkickLv2PLugin->setAtomChunkId(uridMap->map(uridMap->handle, LV2_ATOM__Chunk)); geonkickLv2PLugin->setAtomSequence(uridMap->map(uridMap->handle, LV2_ATOM__Sequence)); geonkickLv2PLugin->setAtomStateChanged(uridMap->map(uridMap->handle, GEONKICK_URI_STATE_CHANGED)); geonkickLv2PLugin->setAtomObject(uridMap->map(uridMap->handle, LV2_ATOM__Object)); } break; } features++; } return static_cast(geonkickLv2PLugin); } static void gkick_connect_port(LV2_Handle instance, uint32_t port, void* data) { auto geonkickLv2PLugin = static_cast(instance); auto portNumber = static_cast(port); if (portNumber == 0) geonkickLv2PLugin->setMidiIn(static_cast(data)); else if (portNumber == 1) geonkickLv2PLugin->setNotifyHostChannel(static_cast(data)); else if (portNumber > 1) geonkickLv2PLugin->setAudioChannel(static_cast(data), portNumber - 2); } static void gkick_activate(LV2_Handle instance) { } static void gkick_run(LV2_Handle instance, uint32_t n_samples) { auto geonkickLv2PLugin = static_cast(instance); geonkickLv2PLugin->processSamples(n_samples); } static void gkick_deactivate(LV2_Handle instance) { } static void gkick_cleaup(LV2_Handle instance) { auto geonkickLv2PLugin = static_cast(instance); delete geonkickLv2PLugin; } static LV2_State_Status gkick_state_save(LV2_Handle instance, LV2_State_Store_Function store, LV2_State_Handle handle, uint32_t flags, const LV2_Feature* const* features) { auto geonkickLv2PLugin = static_cast(instance); if (geonkickLv2PLugin){ std::string stateData = geonkickLv2PLugin->getStateData(); store(handle, geonkickLv2PLugin->getStateId(), stateData.data(), stateData.size(), geonkickLv2PLugin->getAtomChunkId(), LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); } return LV2_STATE_SUCCESS; } static LV2_State_Status gkick_state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve, LV2_State_Handle handle, uint32_t flags, const LV2_Feature* const* features) { auto geonkickLv2PLugin = static_cast(instance); if (geonkickLv2PLugin) { size_t size = 0; LV2_URID type = 0; const char *data = (const char*)retrieve(handle, geonkickLv2PLugin->getStateId(), &size, &type, &flags); if (data && size > 0) geonkickLv2PLugin->setStateData(std::string(data, size), flags); } return LV2_STATE_SUCCESS; } static const void* gkick_extention_data(const char* uri) { static const LV2_State_Interface state = {gkick_state_save, gkick_state_restore}; if (std::string(uri) == std::string(LV2_STATE__interface)) return &state; return nullptr; } static const LV2_Descriptor gkick_descriptor = { GEONKICK_URI, gkick_instantiate, gkick_connect_port, gkick_activate, gkick_run, gkick_deactivate, gkick_cleaup, gkick_extention_data, }; const LV2_Descriptor* lv2_descriptor(uint32_t index) { switch (index) { case 0: return &gkick_descriptor; default: return nullptr; } } geonkick-2.10.2/src/plugin/vst/000077500000000000000000000000001451017372400162735ustar00rootroot00000000000000geonkick-2.10.2/src/plugin/vst/CMakeLists.txt000066400000000000000000000040231451017372400210320ustar00rootroot00000000000000set(GKICK_VST_DIR ${CMAKE_CURRENT_SOURCE_DIR}) list(APPEND CMAKE_MODULE_PATH "${GKICK_VST_SDK_PATH}/cmake/modules") include_directories(${GKICK_VST_SDK_PATH}) include_directories(${GKICK_REDKITE_SDK_PATH}/include) link_directories(${GKICK_REDKITE_SDK_PATH}/lib) get_filename_component(VST3SDK_FOLDER_NAME ${GKICK_VST_SDK_PATH} NAME) if(NAME_FROM_PATH STREQUAL "" AND (VST3SDK_FOLDER_NAME STREQUAL "." OR VST3SDK_FOLDER_NAME STREQUAL "..")) message(FATAL_ERROR "The folder name '${VST3SDK_FOLDER_NAME}' of VST3 SDK is invalid or empty.") else() link_directories(${CMAKE_BINARY_DIR}/${VST3SDK_FOLDER_NAME}/build/lib) endif() add_subdirectory(${GKICK_VST_SDK_PATH} EXTERN) set(GKICK_VST_HEADERS ${GKICK_VST_SDK_PATH}/public.sdk/source/vst/vstcomponentbase.h ${GKICK_VST_SDK_PATH}/public.sdk/source/vst/vstsinglecomponenteffect.h ${GKICK_VST_DIR}/VstIds.h ${GKICK_VST_DIR}/GKickVstProcessor.h ${GKICK_VST_DIR}/GKickVstEditor.h) set(GKICK_VST_SOURCES ${GKICK_VST_SDK_PATH}/public.sdk/source/vst/vstcomponentbase.cpp ${GKICK_VST_SDK_PATH}/public.sdk/source/vst/vstsinglecomponenteffect.cpp ${GKICK_VST_DIR}/VstFactory.cpp ${GKICK_VST_DIR}/GKickVstProcessor.cpp ${GKICK_VST_DIR}/GKickVstEditor.cpp) set(target geonkick_vst) add_library(${target} SHARED ${GKICK_VST_HEADERS} ${GKICK_VST_SOURCES}) set_target_properties(${target} PROPERTIES PREFIX "") set_target_properties(${target} PROPERTIES OUTPUT_NAME "Geonkick") set_target_properties(${target} PROPERTIES SUFFIX ".so") target_link_libraries(${target} PRIVATE geonkick_common api_plugin redkite) target_link_libraries(${target} PRIVATE "-lstdc++fs") target_link_libraries(${target} PRIVATE base sdk sdk_common pluginterfaces) target_link_libraries(${target} PRIVATE "-lX11 -lsndfile -lcrypto -lrt -lm -ldl -lpthread -lcairo") target_link_libraries(${target} PRIVATE "-Wl,--no-undefined") add_dependencies(${target} geonkick_common) install(TARGETS geonkick_vst DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/vst3/Geonkick.vst3/Contents/${GKICK_ARCHITECTURE}-linux) geonkick-2.10.2/src/plugin/vst/GKickVstEditor.cpp000066400000000000000000000067061451017372400216440ustar00rootroot00000000000000/** * File name: GKickVstEditor.cpp * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "geonkick_api.h" #include "MainWindow.h" #include "GKickVstEditor.h" #include #include GKickVstTimer::GKickVstTimer(RkMain *app) : guiApp{app} , countT{1} { } void PLUGIN_API GKickVstTimer::onTimer() { if (guiApp) guiApp->exec(false); } GKickVstEditor::GKickVstEditor(Vst::EditController *controller, GeonkickApi *api) : Vst::EditorView(controller) , guiApp{nullptr} , mainWindow{nullptr} , geonkickApi{api} , loopTimer{nullptr} { } tresult PLUGIN_API GKickVstEditor::isPlatformTypeSupported(Steinberg::FIDString type) { RK_UNUSED(type); return kResultTrue; } tresult PLUGIN_API GKickVstEditor::attached(void* parent, FIDString type) { guiApp = std::make_unique(); loopTimer = std::make_unique(guiApp.get()); geonkickApi->setEventQueue(guiApp->eventQueue()); Display* xDisplay = XOpenDisplay(nullptr); if (!xDisplay) { GEONKICK_LOG_ERROR("can't open display"); return kResultFalse; } int screenNumber = DefaultScreen(xDisplay); auto info = rk_from_native_x11(xDisplay, screenNumber, reinterpret_cast(parent)); mainWindow = new MainWindow(guiApp.get(), geonkickApi, info); mainWindow->show(); if (!mainWindow->init()) { GEONKICK_LOG_ERROR("can't init main window"); return kResultFalse; } IRunLoop* loop = nullptr; if (plugFrame->queryInterface(IRunLoop::iid, (void**)&loop) == Steinberg::kResultOk) { loop->registerTimer(loopTimer.get(), 30); } else { GEONKICK_LOG_INFO("can't get loop"); return kResultFalse; } return Vst::EditorView::attached(parent, type); } tresult PLUGIN_API GKickVstEditor::removed() { IRunLoop* loop = nullptr; if (plugFrame->queryInterface(IRunLoop::iid, (void**)&loop) == Steinberg::kResultOk) { loop->unregisterTimer(loopTimer.get()); } else { GEONKICK_LOG_INFO("can't get loop"); return kResultFalse; } if (guiApp) guiApp = nullptr; return kResultOk; } tresult PLUGIN_API GKickVstEditor::getSize(ViewRect* newSize) { if (newSize == nullptr || mainWindow == nullptr) return kResultFalse; newSize->left = mainWindow->x(); newSize->right = mainWindow->x() + mainWindow->width(); newSize->top = mainWindow->y(); newSize->bottom = mainWindow->y() + mainWindow->height(); return kResultOk; } geonkick-2.10.2/src/plugin/vst/GKickVstEditor.h000066400000000000000000000042731451017372400213060ustar00rootroot00000000000000/** * File name: GKickVstEditor.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GKICK_VST_EDITOR_H #define GKICK_VST_EDITOR_H #include "public.sdk/source/vst/vsteditcontroller.h" #include "pluginterfaces/gui/iplugview.h" #include using namespace Steinberg; using namespace Linux; class MainWindow; class GeonkickApi; class RkMain; class GKickVstTimer: public ITimerHandler { public: explicit GKickVstTimer(RkMain *app); virtual ~GKickVstTimer() = default; tresult PLUGIN_API queryInterface (const TUID _iid, void** obj) override { return 0; } uint32 PLUGIN_API addRef() override { return countT++; } uint32 PLUGIN_API release() override { return --countT; } protected: void PLUGIN_API onTimer() override; private: RkMain *guiApp; uint32 countT; }; class GKickVstEditor : public Vst::EditorView { public: GKickVstEditor(Vst::EditController *controller, GeonkickApi *api); tresult PLUGIN_API isPlatformTypeSupported(Steinberg::FIDString type) override; tresult PLUGIN_API attached(void* parent, FIDString type) override; tresult PLUGIN_API removed() override; tresult PLUGIN_API getSize(ViewRect* newSize) override; private: std::unique_ptr guiApp; MainWindow *mainWindow; GeonkickApi *geonkickApi; std::unique_ptr loopTimer; }; #endif // GKICK_VST_EDITOR_H geonkick-2.10.2/src/plugin/vst/GKickVstProcessor.cpp000066400000000000000000000215401451017372400223660ustar00rootroot00000000000000/** * File name: GKickVstProcessor.h * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "GKickVstProcessor.h" #include "GKickVstEditor.h" #include "VstIds.h" #include "base/source/fstreamer.h" #include "pluginterfaces/base/ibstream.h" #include "pluginterfaces/vst/ivstparameterchanges.h" #include "pluginterfaces/vst/ivstevents.h" #include "geonkick_api.h" #include "kit_state.h" bool ModuleEntry (void*) { return true; } bool ModuleExit (void) { return true; } GKickVstProcessor::GKickVstProcessor() : geonkickApi{nullptr} { } FUnknown* GKickVstProcessor::createInstance(void*) { return static_cast(new GKickVstProcessor()); } tresult PLUGIN_API GKickVstProcessor::initialize(FUnknown* context) { auto res = Vst::SingleComponentEffect::initialize(context); if (res != kResultTrue) return kResultFalse; geonkickApi = std::make_unique(Geonkick::defaultSampleRate, GeonkickApi::InstanceType::Vst3); if (!geonkickApi->init()) { geonkickApi = nullptr; GEONKICK_LOG_ERROR("can't init Geonkick API"); return kResultFalse; } auto nChannels = GeonkickApi::numberOfChannels(); for (decltype(nChannels) i = 0; i < nChannels; i++) { std::wstring_convert,char16_t> convert; std::u16string str16 = convert.from_bytes(std::string("Out" + std::to_string(i))); addAudioOutput(str16.c_str(), Vst::SpeakerArr::kStereo); } addEventInput(STR16("MIDI in"), 1); channelsBuffers = std::vector(2 * nChannels, nullptr); return kResultTrue; } tresult PLUGIN_API GKickVstProcessor::setBusArrangements(Vst::SpeakerArrangement* inputs, int32 numIns, Vst::SpeakerArrangement* outputs, int32 numOuts) { auto n = GeonkickApi::numberOfChannels(); if (numIns == 0 && numOuts == static_cast(n)) return Vst::SingleComponentEffect::setBusArrangements(inputs, numIns, outputs, numOuts); return kResultFalse; } tresult PLUGIN_API GKickVstProcessor::setupProcessing(Vst::ProcessSetup& setup) { auto data = geonkickApi->getKitState()->toJson(); geonkickApi = std::make_unique(setup.sampleRate, GeonkickApi::InstanceType::Vst3); if (!geonkickApi->init()) { geonkickApi = nullptr; GEONKICK_LOG_ERROR("can't init Geonkick API"); return kResultFalse; } geonkickApi->setKitState(data); geonkickApi->notifyUpdateGui(); geonkickApi->notifyKitUpdated(); return Vst::SingleComponentEffect::setupProcessing(setup); } tresult PLUGIN_API GKickVstProcessor::setActive(TBool state) { return Vst::SingleComponentEffect::setActive(state); } tresult PLUGIN_API GKickVstProcessor::process(Vst::ProcessData& data) { if (data.numSamples < 1) return kResultOk; size_t nChannels = std::min(geonkickApi->numberOfChannels(), static_cast(data.numOutputs)); for (decltype(nChannels) ch = 0; ch < nChannels; ch++) { channelsBuffers.data()[2 * ch] = data.outputs[ch].channelBuffers32[0]; channelsBuffers.data()[2 * ch + 1] = data.outputs[ch].channelBuffers32[1]; memset(channelsBuffers.data()[2 * ch], 0, data.numSamples * sizeof(float)); memset(channelsBuffers.data()[2 * ch + 1], 0, data.numSamples * sizeof(float)); } auto events = data.inputEvents; auto nEvents = events->getEventCount(); auto eventIndex = 0; Vst::Event event; auto res = events->getEvent(eventIndex, event); size_t offset = 0; size_t currentFrame = 0; while (res == kResultOk && eventIndex < nEvents) { size_t eventFrame = event.sampleOffset; size_t size = eventFrame - currentFrame; if (size > 0) { geonkickApi->process(channelsBuffers.data(), offset, size); offset += size; } switch (event.type) { case Vst::Event::kNoteOnEvent: geonkickApi->setKeyPressed(true, event.noteOn.pitch, 127 * event.noteOn.velocity); break; case Vst::Event::kNoteOffEvent: geonkickApi->setKeyPressed(false, event.noteOff.pitch, 127 * event.noteOff.velocity); break; default: break; } currentFrame = eventFrame; eventIndex++; if (eventIndex < nEvents) res = events->getEvent(eventIndex, event); } if (static_cast(currentFrame) < data.numSamples) geonkickApi->process(channelsBuffers.data(), offset, data.numSamples - currentFrame); return kResultOk; } tresult PLUGIN_API GKickVstProcessor::setState(IBStream* state) { if (state == nullptr || geonkickApi == nullptr) { GEONKICK_LOG_ERROR("wrong arguments or DSP is not ready"); return kResultFalse; } if (state->seek(0, IBStream::kIBSeekEnd, 0) == kResultFalse) { GEONKICK_LOG_ERROR("can't seek in stream"); return kResultFalse; } int64 endStream = 0; if (state->tell(&endStream) == kResultFalse) { GEONKICK_LOG_ERROR("can't get current position in stream"); return kResultFalse; } else if (endStream < 1) { GEONKICK_LOG_ERROR("stream is empty"); return kResultFalse; } if (state->seek(0, IBStream::kIBSeekSet, 0) == kResultFalse) { GEONKICK_LOG_ERROR("can't seek in stream"); return kResultFalse; } std::string data(endStream, '\0'); int32 nBytes = 0; if (state->read(data.data(), data.size(), &nBytes) == kResultFalse) { GEONKICK_LOG_ERROR("error on reading the state"); return kResultFalse; } if (static_cast(data.size()) != nBytes) { GEONKICK_LOG_ERROR("error on reading the state"); return kResultFalse; } geonkickApi->setKitState(data); geonkickApi->notifyUpdateGui(); geonkickApi->notifyKitUpdated(); return kResultOk; } tresult PLUGIN_API GKickVstProcessor::getState(IBStream* state) { if (state == nullptr || geonkickApi == nullptr) { GEONKICK_LOG_ERROR("wrong arguments or DSP is not ready"); return kResultFalse; } int32 nBytes = 0; auto data = geonkickApi->getKitState()->toJson(); if (state->write(data.data(), data.size(), &nBytes) == kResultFalse) { GEONKICK_LOG_ERROR("error on saving the state"); return kResultFalse; } if (static_cast(data.size()) != nBytes) { GEONKICK_LOG_ERROR("error on saving the state"); return kResultFalse; } return kResultOk; } IPlugView* PLUGIN_API GKickVstProcessor::createView(FIDString name) { if (geonkickApi && name && std::string(name) == std::string("editor")) return static_cast(new GKickVstEditor(this, geonkickApi.get())); return nullptr; } tresult PLUGIN_API GKickVstProcessor::setComponentState(IBStream* state) { return kResultOk; } geonkick-2.10.2/src/plugin/vst/GKickVstProcessor.h000066400000000000000000000047111451017372400220340ustar00rootroot00000000000000/** * File name: GKickVstProcessor.h * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_PLUGIN_VST_PROCESSOR_H #define GEONKICK_PLUGIN_VST_PROCESSOR_H #include "globals.h" #include "public.sdk/source/vst/vstsinglecomponenteffect.h" #ifdef __cplusplus extern "C" { #endif __attribute__((visibility("default"))) bool ModuleEntry (void*); __attribute__((visibility("default"))) bool ModuleExit (void); #ifdef __cplusplus } #endif class GeonkickApi; using namespace Steinberg; class GKickVstProcessor : public Vst::SingleComponentEffect { public: GKickVstProcessor(); ~GKickVstProcessor() = default; tresult PLUGIN_API initialize(FUnknown* context) SMTG_OVERRIDE; static FUnknown* createInstance(void*); tresult PLUGIN_API setBusArrangements(Vst::SpeakerArrangement* inputs, int32 numIns, Vst::SpeakerArrangement* outputs, int32 numOuts) SMTG_OVERRIDE; tresult PLUGIN_API setupProcessing(Vst::ProcessSetup& setup) SMTG_OVERRIDE; tresult PLUGIN_API setActive(TBool state) SMTG_OVERRIDE; tresult PLUGIN_API process(Vst::ProcessData& data) SMTG_OVERRIDE; tresult PLUGIN_API setState(IBStream* state) SMTG_OVERRIDE; tresult PLUGIN_API getState(IBStream* state) SMTG_OVERRIDE; IPlugView* PLUGIN_API createView(FIDString name) SMTG_OVERRIDE; tresult PLUGIN_API setComponentState(IBStream* state) SMTG_OVERRIDE; protected: std::unique_ptr geonkickApi; std::vector channelsBuffers; }; #endif // GEONKICK_PLUGIN_VST_PROCESSOR_H geonkick-2.10.2/src/plugin/vst/VstFactory.cpp000066400000000000000000000037021451017372400211050ustar00rootroot00000000000000/** * File name: VstFactory.h * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "GKickVstProcessor.h" #include "VstIds.h" #include "public.sdk/source/main/pluginfactory.h" #include "pluginterfaces/base/fplatform.h" #define gkickPluginName "Geonkick" #define gkickOriginalFilename "Geonkick.vst3" #if SMTG_PLATFORM_64 #define gkickFileDescription gkickPluginName" Geonkick (64Bit)" #else #define gkickFileDescription gkickPluginName" Geonkick" #endif #define gkickCompanyName "Geonkick" #define gkickCompanyWeb "http://geonkick.org" #define gkickCompanyEmail "" #define gkickLegalCopyright "Copyright (C) 2020 Iurie Nistor. Licensed under the GNU General Public License, Version 3" BEGIN_FACTORY_DEF(gkickCompanyName, gkickCompanyWeb, gkickCompanyEmail) DEF_CLASS2(INLINE_UID_FROM_FUID(GKickVstProcessorUID), PClassInfo::kManyInstances, kVstAudioEffectClass, gkickPluginName, 0, Vst::PlugType::kInstrumentSynth, GEOKICK_VERSION_STRING, kVstVersionString, GKickVstProcessor::createInstance) END_FACTORY bool InitModule() { return true; } bool DeinitModule() { return true; } geonkick-2.10.2/src/plugin/vst/VstIds.h000066400000000000000000000021701451017372400176600ustar00rootroot00000000000000/** * File name: VstIds.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_VST_IDS_H #define GEONKICK_VST_IDS_H #include "globals.h" #include "pluginterfaces/base/funknown.h" #include using namespace Steinberg; static const FUID GKickVstProcessorUID(0xE2CC9532, 0x90E863F0, 0x6405E139, 0x24E34B24); #endif // GEONKICK_VST_IDS_H geonkick-2.10.2/src/preset.cpp000066400000000000000000000027161451017372400161750ustar00rootroot00000000000000/** * File name: preset.cpp * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "preset.h" Preset::Preset(const std::filesystem::path& path) : presetName{path.stem()} , presetPath{path} { } Preset::PresetType Preset::type() const { if (path().extension() == ".gkit") return PresetType::PercussionKit; return PresetType::Percussion; } std::string Preset::name() const { return presetName; } void Preset::setName(const std::string &name) { presetName = name; } std::filesystem::path Preset::path() const { return presetPath; } void Preset::setPath(const std::filesystem::path &path) { presetPath = path; } geonkick-2.10.2/src/preset.h000066400000000000000000000026351451017372400156420ustar00rootroot00000000000000/** * File name: preset.h * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef PRESET_H #define PRESET_H #include "globals.h" class Preset { public: enum class PresetType: int { Percussion, PercussionKit, }; explicit Preset(const std::filesystem::path& path); PresetType type() const; std::string name() const; void setName(const std::string &name); std::filesystem::path path() const; void setPath(const std::filesystem::path& path); private: std::string presetName; std::filesystem::path presetPath; }; #endif // PRESET_H geonkick-2.10.2/src/preset_browser_model.cpp000066400000000000000000000147741451017372400211270ustar00rootroot00000000000000/** * File name: preset_browser_model.cpp * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "preset_browser_model.h" #include "preset_folder.h" #include "preset.h" #include "geonkick_api.h" #include "percussion_state.h" #include "kit_state.h" PresetBrowserModel::PresetBrowserModel(RkObject *parent, GeonkickApi *api) : RkObject(parent) , geonkickApi{api} , folderPageIndex{0} , presetPageIndex{0} , numberOfPresetColumns{3} , rowsPerColumn{12} , selectedFolder{geonkickApi->getPresetFolder(0)} , selectedPreset{nullptr} { } std::string PresetBrowserModel::presetName(int row, int column) const { if (column == 0) { auto presetFolder = getPresetFolder(row); if (presetFolder) return presetFolder->name(); } else if (column) { auto preset = getPreset(row, column); if (preset) return preset->name(); } return std::string(); } bool PresetBrowserModel::isKit(int row, int column) const { auto preset = getPreset(row, column); return preset && preset->type() == Preset::PresetType::PercussionKit; } PresetFolder* PresetBrowserModel::getPresetFolder(int row) const { return geonkickApi->getPresetFolder(folderPage() * rowsPerColumn + row); } Preset* PresetBrowserModel::getPreset(int row, int column) const { if (column > 0 && selectedFolder) { return selectedFolder->preset(presetPage() * numberOfPresetColumns * rowsPerColumn + (column - 1) * rowsPerColumn + row); } else { return nullptr; } } size_t PresetBrowserModel::folderPages() const { return geonkickApi->numberOfPresetFolders() / rowsPerColumn + ((geonkickApi->numberOfPresetFolders() % rowsPerColumn) ? 1 : 0); } size_t PresetBrowserModel::folderPage() const { return folderPageIndex; } void PresetBrowserModel::folderNextPage() { if (folderPageIndex < folderPages() - 1) { folderPageIndex++; action folderPageChanged(); } } void PresetBrowserModel::folderPreviousPage() { if (folderPageIndex > 0) { folderPageIndex--; action folderPageChanged(); } } void PresetBrowserModel::setFolderPage(size_t index) { folderPageIndex = index; } size_t PresetBrowserModel::presetPages() const { if (selectedFolder) { auto presetsPerPage = numberOfPresetColumns * rowsPerColumn; return selectedFolder->numberOfPresets() / presetsPerPage + ((selectedFolder->numberOfPresets() % presetsPerPage) ? 1 : 0); } return 0; } size_t PresetBrowserModel::presetPage() const { return presetPageIndex; } void PresetBrowserModel::nextPresetPage() { if (presetPageIndex < presetPages() - 1) { presetPageIndex++; action presetPageChanged(); } } void PresetBrowserModel::previousPresetPage() { if (presetPageIndex > 0) { presetPageIndex--; action presetPageChanged(); } } void PresetBrowserModel::setPresetPage(size_t index) { presetPageIndex = index; } size_t PresetBrowserModel::columns() const { return numberOfPresetColumns + 1; } size_t PresetBrowserModel::rows() const { return rowsPerColumn; } void PresetBrowserModel::select(size_t row, size_t column) { if (row < rows() && column < columns()) { if (column == 0 && getPresetFolder(row)) { selectedFolder = getPresetFolder(row); selectedPreset = nullptr; presetPageIndex = 0; if (selectedFolder) action folderSelected(selectedFolder); } if (column > 0 && selectedFolder) { selectedPreset = getPreset(row, column); if (selectedPreset && setPreset(selectedPreset)) action presetSelected(selectedPreset); } } } bool PresetBrowserModel::isSelected(size_t row, size_t column) const { if (column == 0) return getPresetFolder(row) == selectedFolder; else if (column > 0) return getPreset(row, column) == selectedPreset; else return false; } bool PresetBrowserModel::setPreset(Preset* preset) { if (preset->type() == Preset::PresetType::Percussion) { GEONKICK_LOG_DEBUG("path:" << preset->path()); auto state = geonkickApi->getDefaultPercussionState(); if (!state->loadFile(preset->path())) { GEONKICK_LOG_ERROR("can't open preset"); return false; } else { state->setId(geonkickApi->currentPercussion()); geonkickApi->setPercussionState(state); geonkickApi->notifyUpdateGui(); geonkickApi->notifyPercussionUpdated(state->getId()); return true; } } else if (preset->type() == Preset::PresetType::PercussionKit) { auto kit = std::make_unique(); if (kit->open(preset->path())) { GEONKICK_LOG_ERROR("can't open kit"); return false; } else if (geonkickApi->setKitState(kit)) { geonkickApi->notifyKitUpdated(); geonkickApi->notifyUpdateGui(); return true; } } return false; } geonkick-2.10.2/src/preset_browser_model.h000066400000000000000000000055221451017372400205630ustar00rootroot00000000000000/** * File name: preset_browser_model.h * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef PRESET_BROWSER_MODEL_H #define PRESET_BROWSER_MODEL_H #include "globals.h" #include class GeonkickApi; class PresetFolder; class Preset; class PresetBrowserModel : public RkObject { public: explicit PresetBrowserModel(RkObject *parent, GeonkickApi *api); std::string presetName(int row, int column) const; size_t folderPages() const; size_t folderPage() const; void folderNextPage(); void folderPreviousPage(); void setFolderPage(size_t index); size_t presetPages() const; size_t presetPage() const; void nextPresetPage(); void previousPresetPage(); void setPresetPage(size_t index); size_t columns() const; size_t rows() const; void select(size_t row, size_t column); bool isSelected(size_t row, size_t column) const; bool isKit(int row, int column) const; RK_DECL_ACT(folderSelected, folderSelected(PresetFolder* folder), RK_ARG_TYPE(PresetFolder*), RK_ARG_VAL(folder)); RK_DECL_ACT(presetSelected, presetSelected(Preset* preset), RK_ARG_TYPE(Preset*), RK_ARG_VAL(preset)); RK_DECL_ACT(folderPageChanged, folderPageChanged(), RK_ARG_TYPE(), RK_ARG_VAL()); RK_DECL_ACT(presetPageChanged, presetPageChanged(), RK_ARG_TYPE(), RK_ARG_VAL()); protected: PresetFolder* getPresetFolder(int row) const; Preset* getPreset(int row, int column) const; bool setPreset(Preset* preset); private: GeonkickApi* geonkickApi; size_t folderPageIndex; size_t presetPageIndex; size_t numberOfPresetColumns; size_t rowsPerColumn; PresetFolder* selectedFolder; Preset* selectedPreset; }; #endif // PRESET_BROWSER_MODEL_H geonkick-2.10.2/src/preset_browser_view.cpp000066400000000000000000000231671451017372400207750ustar00rootroot00000000000000/** * File name: preset_browser_view.h * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "preset_browser_view.h" #include "preset_browser_model.h" #include #include #include #include #include RK_DECLARE_IMAGE_RC(next_page); RK_DECLARE_IMAGE_RC(next_page_hover); RK_DECLARE_IMAGE_RC(next_page_on); RK_DECLARE_IMAGE_RC(prev_page); RK_DECLARE_IMAGE_RC(prev_page_hover); RK_DECLARE_IMAGE_RC(prev_page_on); PresetBrowserView::PresetBrowserView(GeonkickWidget *parent, PresetBrowserModel* model) : GeonkickWidget(parent) , browserModel{model} , topPadding{15} , leftPadding{5} , columnWidth{150} , rowHeight{20} , overRow{-1} , overColumn{-1} , nextPresetPageButton{nullptr} , prevPresetPageButton{nullptr} , nextFolderPageButton{nullptr} , prevFolderPageButton{nullptr} , bottomContainer{nullptr} { setSize(620, 290); RK_ACT_BIND(browserModel, folderSelected, RK_ACT_ARGS(PresetFolder*), this, update()); RK_ACT_BIND(browserModel, presetSelected, RK_ACT_ARGS(Preset*), this, update()); RK_ACT_BIND(browserModel, presetPageChanged, RK_ACT_ARGS(), this, updatePageButtons()); RK_ACT_BIND(browserModel, folderPageChanged, RK_ACT_ARGS(), this, updatePageButtons()); RK_ACT_BIND(browserModel, folderSelected, RK_ACT_ARGS(PresetFolder*), this, updatePageButtons()); RK_ACT_BIND(browserModel, presetSelected, RK_ACT_ARGS(Preset*), this, updatePageButtons()); bottomContainer = new RkContainer(this); bottomContainer->setSize({width(), 24}); bottomContainer->setPosition({0, height() - 24 - 5}); bottomContainer->setHiddenTakesPlace(); bottomContainer->addSpace((leftPadding + columnWidth) - 2 * 24 - 10); nextFolderPageButton = new RkButton(this); nextFolderPageButton->setType(RkButton::ButtonType::ButtonPush); nextFolderPageButton->setSize(24, 24); nextFolderPageButton->setImage(RkImage(nextFolderPageButton->size(), RK_IMAGE_RC(next_page)), RkButton::State::Unpressed); nextFolderPageButton->setImage(RkImage(nextFolderPageButton->size(), RK_IMAGE_RC(next_page_hover)), RkButton::State::UnpressedHover); nextFolderPageButton->setImage(RkImage(nextFolderPageButton->size(), RK_IMAGE_RC(next_page_on)), RkButton::State::Pressed); RK_ACT_BIND(nextFolderPageButton, pressed, RK_ACT_ARGS(), browserModel, folderNextPage()); prevFolderPageButton = new RkButton(this); prevFolderPageButton->setType(RkButton::ButtonType::ButtonPush); prevFolderPageButton->setSize(24, 24); prevFolderPageButton->setImage(RkImage(prevFolderPageButton->size(), RK_IMAGE_RC(prev_page)), RkButton::State::Unpressed); prevFolderPageButton->setImage(RkImage(prevFolderPageButton->size(), RK_IMAGE_RC(prev_page_hover)), RkButton::State::UnpressedHover); prevFolderPageButton->setImage(RkImage(prevFolderPageButton->size(), RK_IMAGE_RC(prev_page_on)), RkButton::State::Pressed); RK_ACT_BIND(prevFolderPageButton, pressed, RK_ACT_ARGS(), browserModel, folderPreviousPage()); bottomContainer->addWidget(prevFolderPageButton); bottomContainer->addSpace(5); bottomContainer->addWidget(nextFolderPageButton); bottomContainer->addSpace(10, Rk::Alignment::AlignRight); nextPresetPageButton = new RkButton(this); nextPresetPageButton->setType(RkButton::ButtonType::ButtonPush); nextPresetPageButton->setSize(24, 24); nextPresetPageButton->setImage(RkImage(nextPresetPageButton->size(), RK_IMAGE_RC(next_page)), RkButton::State::Unpressed); nextPresetPageButton->setImage(RkImage(nextPresetPageButton->size(), RK_IMAGE_RC(next_page_hover)), RkButton::State::UnpressedHover); nextPresetPageButton->setImage(RkImage(nextPresetPageButton->size(), RK_IMAGE_RC(next_page_on)), RkButton::State::Pressed); RK_ACT_BIND(nextPresetPageButton, pressed, RK_ACT_ARGS(), browserModel, nextPresetPage()); bottomContainer->addWidget(nextPresetPageButton, Rk::Alignment::AlignRight); bottomContainer->addSpace(5, Rk::Alignment::AlignRight); prevPresetPageButton = new RkButton(this); prevPresetPageButton->setType(RkButton::ButtonType::ButtonPush); prevPresetPageButton->setSize(24, 24); prevPresetPageButton->setImage(RkImage(prevPresetPageButton->size(), RK_IMAGE_RC(prev_page)), RkButton::State::Unpressed); prevPresetPageButton->setImage(RkImage(prevPresetPageButton->size(), RK_IMAGE_RC(prev_page_hover)), RkButton::State::UnpressedHover); prevPresetPageButton->setImage(RkImage(prevPresetPageButton->size(), RK_IMAGE_RC(prev_page_on)), RkButton::State::Pressed); RK_ACT_BIND(prevPresetPageButton, pressed, RK_ACT_ARGS(), browserModel, previousPresetPage()); bottomContainer->addWidget(prevPresetPageButton, Rk::Alignment::AlignRight); updatePageButtons(); setBackgroundColor({60, 60, 60}); } void PresetBrowserView::paintWidget(RkPaintEvent *event) { RkImage img(size()); RkPainter painter(&img); painter.fillRect(rect(), background()); painter.fillRect({0, 0, leftPadding + columnWidth, height()}, {40, 40, 40, 70}); painter.setPen(RkColor(50, 50, 50)); painter.drawLine({leftPadding + columnWidth, 0}, {leftPadding + columnWidth, height()}); auto font = painter.font(); font.setSize(12); painter.setFont(font); if (browserModel->columns()) { int xColumn = leftPadding; int yRow = topPadding; for (size_t col = 0; col < browserModel->columns(); col++) { for (size_t row = 0; row < browserModel->rows(); row++) { auto presetName = browserModel->presetName(row, col); if (browserModel->isKit(row, col)) presetName = "KIT: " + presetName; auto font = painter.font(); RkRect textRect(xColumn + 15, yRow, columnWidth, rowHeight); painter.setPen(RkColor(60, 60, 60)); if (browserModel->isSelected(row, col)) { font.setWeight(RkFont::Weight::Bold); painter.setFont(font); painter.setPen(RkColor(230, 230, 230)); } else { font.setWeight(RkFont::Weight::Normal); painter.setFont(font); painter.setPen(RkColor(180, 180, 180)); } if (static_cast(overRow) == row && static_cast(overColumn) == col) { painter.setPen(RkColor(255, 255, 255)); } painter.drawText(textRect, presetName, Rk::Alignment::AlignLeft); yRow += rowHeight; } xColumn += columnWidth; yRow = topPadding; } } RkPainter paint(this); paint.drawImage(img, 0, 0); } void PresetBrowserView::mouseButtonPressEvent(RkMouseEvent *event) { browserModel->select((event->y() - topPadding) / rowHeight, (event->x() - leftPadding) / columnWidth); } void PresetBrowserView::mouseMoveEvent(RkMouseEvent *event) { auto overColumnTmp = (event->x() - leftPadding) / columnWidth; bool toUpdate = false; if (overColumnTmp != overColumn) { overColumn = overColumnTmp; toUpdate = true; } auto overRowTmp = (event->y() - topPadding) / rowHeight; if (overRowTmp != overRow) { overRow = overRowTmp; toUpdate = true; } if (toUpdate) update(); } void PresetBrowserView::updatePageButtons() { prevFolderPageButton->show(browserModel->folderPages() > 1); nextFolderPageButton->show(browserModel->folderPages() > 1); prevPresetPageButton->show(browserModel->presetPages() > 1); nextPresetPageButton->show(browserModel->presetPages() > 1); bottomContainer->update(); update(); } geonkick-2.10.2/src/preset_browser_view.h000066400000000000000000000034461451017372400204400ustar00rootroot00000000000000/** * File name: preset_browser_view.h * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef PRESET_BROWSER_H #define PRESET_BROWSER_H #include "globals.h" #include "geonkick_widget.h" class PresetBrowserModel; class RkButton; class RkContainer; class PresetBrowserView: public GeonkickWidget { public: PresetBrowserView(GeonkickWidget *parent, PresetBrowserModel* model); protected: void mouseButtonPressEvent(RkMouseEvent *event) override; void mouseMoveEvent(RkMouseEvent *event) override; void paintWidget(RkPaintEvent *event) override; void updatePageButtons(); private: PresetBrowserModel *browserModel; int topPadding; int leftPadding; int columnWidth; int rowHeight; int overRow; int overColumn; RkButton* nextPresetPageButton; RkButton* prevPresetPageButton; RkButton* nextFolderPageButton; RkButton* prevFolderPageButton; RkContainer *bottomContainer; }; #endif // PRESET_BROWSER_H geonkick-2.10.2/src/preset_folder.cpp000066400000000000000000000044771451017372400175360ustar00rootroot00000000000000/** * File name: preset_folder.cpp * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "preset_folder.h" #include "preset.h" PresetFolder::PresetFolder(const std::filesystem::path &path) : folderPath{path} { } std::string PresetFolder::name() const { return folderPath.stem(); } std::filesystem::path PresetFolder::path() const { return folderPath; } void PresetFolder::setPath(const std::filesystem::path &path) { folderPath = path; } bool PresetFolder::loadPresets() { try { for (const auto &entry : std::filesystem::directory_iterator(folderPath)) { if (!entry.path().empty() && std::filesystem::is_regular_file(entry.path()) && (entry.path().extension() == ".gkick" #ifndef GEONKICK_SINGLE || entry.path().extension() == ".gkit" #endif // GEONKICK_SINGLE )) { GEONKICK_LOG_DEBUG("preset: " << entry.path()); presetList.push_back(std::make_unique(entry.path())); } } } catch(...) { GEONKICK_LOG_ERROR("error on reading path: " << folderPath); return false; } return true; } Preset* PresetFolder::preset(size_t index) const { if (index < presetList.size()) return presetList[index].get(); return nullptr; } size_t PresetFolder::numberOfPresets() const { return presetList.size(); } geonkick-2.10.2/src/preset_folder.h000066400000000000000000000026411451017372400171720ustar00rootroot00000000000000/** * File name: preset_folder.h * Project: Geonkick (A percussion synthesizer) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef PRESET_FOLDER_H #define PRESET_FOLDER_H #include "globals.h" class Preset; class PresetFolder { public: explicit PresetFolder(const std::filesystem::path &path); std::string name() const; std::filesystem::path path() const; void setPath(const std::filesystem::path &path); bool loadPresets(); Preset* preset(size_t index) const; size_t numberOfPresets() const; private: std::filesystem::path folderPath; std::vector> presetList; }; #endif // PRESET_FOLDER_H geonkick-2.10.2/src/redkite/000077500000000000000000000000001451017372400156105ustar00rootroot00000000000000geonkick-2.10.2/src/redkite/CMakeLists.txt000066400000000000000000000173701451017372400203600ustar00rootroot00000000000000cmake_minimum_required (VERSION 3.8) project(redkite VERSION 1.3.1) option(ENABLE_EXAMPLES "Build examples" OFF) if (CMAKE_SYSTEM_NAME MATCHES Windows) message(STATUS "Build Redkite for Windows") add_compile_options(-DRK_OS_WIN) add_compile_options(-DRK_FOR_SHARED) elseif(CMAKE_SYSTEM_NAME MATCHES Darwin) message(FATAL_ERROR "Redkite is not implemented yet for Mac OS X") elseif (CMAKE_SYSTEM_NAME MATCHES Linux) message(STATUS " Build Redkite for GNU/Linux") elseif (CMAKE_SYSTEM_NAME MATCHES FreeBSD) message(STATUS " Build Redkite for FreeBSD") else() message(FATAL_ERROR "Redkite is not implemented yet for ${CMAKE_SYSTEM_NAME}") endif() set(RK_INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/include) set(RK_SRC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/src) set(RK_EXAMPLES_PATH ${CMAKE_CURRENT_SOURCE_DIR}/examples) set(RK_TOOLS_PATH ${CMAKE_CURRENT_SOURCE_DIR}/tools) set(RK_DOC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/doc) if (NOT CMAKE_BUILD_TYPE) message(STATUS "no build type selected, default to Release") set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type Release" FORCE) endif() set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_FLAGS "-Wall ${CMAKE_CXX_FLAGS} -fvisibility=hidden") set(CMAKE_C_FLAGS "-Wall ${CMAKE_C_FLAGS} -fvisibility=hidden") if (NOT RK_EXPORT_INTERFACE) message(STATUS "don't export Redkite interface") else () message(STATUS "export Redkite interface") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DRK_EXPORT_INTERFACE") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DRK_EXPORT_INTERFACE") endif () if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) set (CMAKE_INSTALL_PREFIX "/usr/local" CACHE PATH "default install path" FORCE) endif() if (NOT CMAKE_INSTALL_LIBDIR) message(STATUS "setting the default lib dir path to /usr/lib") set(CMAKE_INSTALL_LIBDIR /usr/lib CACHE STRING "lib dir to /usr/lib" FORCE) endif() if(CMAKE_BUILD_TYPE STREQUAL "Debug") message(STATUS "enable logging") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DRK_LOG_DEBUG_LEVEL") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DRK_LOG_DEBUG_LEVEL") endif() if (NOT RK_GRAPHICS_BACKEND) message(STATUS "use default Cairo graphics backend") set(RK_GRAPHICS_BACKEND "Cairo") # find_package(Cairo REQUIRED) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DRK_GRAPHICS_CAIRO_BACKEND") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DRK_GRAPHICS_CAIRO_BACKEND") elseif (RK_GRAPHICS_BACKEND MATCHES Cairo) # find_package(Cairo REQUIRED) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DRK_GRAPHICS_CAIRO_BACKEND") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DRK_GRAPHICS_CAIRO_BACKEND") endif() set(RK_HEADERS ${RK_INCLUDE_PATH}/Rk.h ${RK_INCLUDE_PATH}/RkVariant.h ${RK_INCLUDE_PATH}/RkObserver.h ${RK_INCLUDE_PATH}/RkObject.h ${RK_INCLUDE_PATH}/RkAction.h ${RK_INCLUDE_PATH}/RkLog.h ${RK_INCLUDE_PATH}/RkPoint.h ${RK_INCLUDE_PATH}/RkRealPoint.h ${RK_INCLUDE_PATH}/RkSize.h ${RK_INCLUDE_PATH}/RkRect.h ${RK_INCLUDE_PATH}/RkColor.h ${RK_INCLUDE_PATH}/RkPen.h ${RK_INCLUDE_PATH}/RkFont.h ${RK_INCLUDE_PATH}/RkPlatform.h ${RK_INCLUDE_PATH}/RkEvent.h ${RK_INCLUDE_PATH}/RkEventQueue.h ${RK_INCLUDE_PATH}/RkTimer.h ${RK_INCLUDE_PATH}/RkWidget.h ${RK_INCLUDE_PATH}/RkLabel.h ${RK_INCLUDE_PATH}/RkButton.h ${RK_INCLUDE_PATH}/RkLineEdit.h ${RK_INCLUDE_PATH}/RkList.h ${RK_INCLUDE_PATH}/RkProgressBar.h ${RK_INCLUDE_PATH}/RkCanvas.h ${RK_INCLUDE_PATH}/RkImage.h ${RK_INCLUDE_PATH}/RkPainter.h ${RK_INCLUDE_PATH}/RkMain.h ${RK_INCLUDE_PATH}/RkModel.h ${RK_INCLUDE_PATH}/RkContainerItem.h ${RK_INCLUDE_PATH}/RkContainerWidgetItem.h ${RK_INCLUDE_PATH}/RkContainer.h ${RK_INCLUDE_PATH}/RkTransition.h) set(RK_HEADERS_IMPL ${RK_INCLUDE_PATH}/impl/RkMainImpl.h ${RK_INCLUDE_PATH}/impl/RkObjectImpl.h ${RK_INCLUDE_PATH}/impl/RkShortcut.h ${RK_INCLUDE_PATH}/impl/RkEventQueueImpl.h ${RK_INCLUDE_PATH}/impl/RkWidgetImpl.h ${RK_INCLUDE_PATH}/impl/RkLabelImpl.h ${RK_INCLUDE_PATH}/impl/RkListImpl.h ${RK_INCLUDE_PATH}/impl/RkModelImpl.h ${RK_INCLUDE_PATH}/impl/RkButtonImpl.h ${RK_INCLUDE_PATH}/impl/RkLineEditImpl.h ${RK_INCLUDE_PATH}/impl/RkProgressBarImpl.h ${RK_INCLUDE_PATH}/impl/RkCanvasInfo.h ${RK_INCLUDE_PATH}/impl/RkImageImpl.h ${RK_INCLUDE_PATH}/impl/RkPainterImpl.h) if (RK_GRAPHICS_BACKEND MATCHES Cairo) set(RK_GRAPHICS_BACKEND_HEADRES ${RK_INCLUDE_PATH}/impl/RkCairoGraphicsBackend.h ${RK_INCLUDE_PATH}/impl/RkCairoImageBackendCanvas.h) set(RK_GRAPHICS_BACKEND_SOURCES ${RK_SRC_PATH}/RkCairoGraphicsBackend.cpp ${RK_SRC_PATH}/RkCairoImageBackendCanvas.cpp) endif() if(CMAKE_SYSTEM_NAME MATCHES Windows) set(RK_HEADERS_PLATFORM ${RK_INCLUDE_PATH}/impl/platforms/win/RkWindowWin.h ${RK_INCLUDE_PATH}/impl/platforms/win/RkEventQueueWin.h) elseif (CMAKE_SYSTEM_NAME MATCHES Darwin) set(RK_HEADERS_PLATFORM ${RK_INCLUDE_PATH}/impl/platforms/mac/RkWindowMac.h ${RK_INCLUDE_PATH}/impl/platforms/mac/RkEventQueueMac.h) else() # defaut GNU/Linux set(RK_HEADERS_PLATFORM ${RK_INCLUDE_PATH}/impl/platforms/xwin/RkWindowX.h ${RK_INCLUDE_PATH}/impl/platforms/xwin/RkEventQueueX.h) endif() set(RK_SOURCES ${RK_SRC_PATH}/RkObject.cpp ${RK_SRC_PATH}/RkObjectImpl.cpp ${RK_SRC_PATH}/RkWidget.cpp ${RK_SRC_PATH}/RkWidgetImpl.cpp ${RK_SRC_PATH}/RkMain.cpp ${RK_SRC_PATH}/RkEventQueue.cpp ${RK_SRC_PATH}/RkTimer.cpp ${RK_SRC_PATH}/RkEventQueueImpl.cpp ${RK_SRC_PATH}/RkMainImpl.cpp ${RK_SRC_PATH}/RkModel.cpp ${RK_SRC_PATH}/RkModelImpl.cpp ${RK_SRC_PATH}/RkLabel.cpp ${RK_SRC_PATH}/RkLabelImpl.cpp ${RK_SRC_PATH}/RkButton.cpp ${RK_SRC_PATH}/RkButtonImpl.cpp ${RK_SRC_PATH}/RkLineEdit.cpp ${RK_SRC_PATH}/RkLineEditImpl.cpp ${RK_SRC_PATH}/RkList.cpp ${RK_SRC_PATH}/RkListImpl.cpp ${RK_SRC_PATH}/RkProgressBar.cpp ${RK_SRC_PATH}/RkProgressBarImpl.cpp ${RK_SRC_PATH}/RkPainter.cpp ${RK_SRC_PATH}/RkImage.cpp ${RK_SRC_PATH}/RkImageImpl.cpp ${RK_SRC_PATH}/RkPainterImpl.cpp ${RK_SRC_PATH}/RkContainer.cpp) if (CMAKE_SYSTEM_NAME MATCHES Windows) set(RK_SOURCES_PLATFORM ${RK_SRC_PATH}/platforms/win/RkLogWin.cpp ${RK_SRC_PATH}/platforms/win/RkPlatformWin.cpp ${RK_SRC_PATH}/platforms/win/RkWindowWin.cpp ${RK_SRC_PATH}/platforms/win/RkEventQueueWin.cpp) elseif (CMAKE_SYSTEM_NAME MATCHES Darwin) set(RK_SOURCES_PLATFORM ${RK_SRC_PATH}/platforms/mac/RkPlatformMac.cpp ${RK_SRC_PATH}/platforms/mac/RkWindowMac.cpp ${RK_SRC_PATH}/platforms/mac/RkEventQueueMac.cpp) else() # defaut GNU/Linux set(RK_SOURCES_PLATFORM ${RK_SRC_PATH}/platforms/xwin/RkLogX.cpp ${RK_SRC_PATH}/platforms/xwin/RkPlatformX.cpp ${RK_SRC_PATH}/platforms/xwin/RkWindowX.cpp ${RK_SRC_PATH}/platforms/xwin/RkEventQueueX.cpp) endif() add_definitions(-fPIC) include_directories(${RK_INCLUDE_PATH}) include_directories(${RK_INCLUDE_PATH}/impl) if (CMAKE_SYSTEM_NAME MATCHES Windows) include_directories(${RK_INCLUDE_PATH}/impl/platforms/win) elseif (CMAKE_SYSTEM_NAME MATCHES Darwin) include_directories(${RK_INCLUDE_PATH}/impl/platforms/mac) # not implemented yet else() # defualt GNU/Linux include_directories(${RK_INCLUDE_PATH}/impl/platforms/xwin) endif() # Redkite is not supposed to be provided as shared library, # it must be only as static library for development. add_library(redkite STATIC ${RK_HEADERS} ${RK_HEADERS_IMPL} ${RK_HEADERS_PLATFORM} ${RK_GRAPHICS_BACKEND_HEADRES} ${RK_GRAPHICS_BACKEND_SOURCES} ${RK_SOURCES} ${RK_SOURCES_PLATFORM}) #if (CMAKE_SYSTEM_NAME MATCHES Windows) # install(TARGETS redkite DESTINATION ${CMAKE_INSTALL_PREFIX}) # install(FILES ${RK_HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include) #elseif(CMAKE_SYSTEM_NAME MATCHES Darwin) # Not implemented #else() # install(TARGETS redkite DESTINATION ${CMAKE_INSTALL_LIBDIR}) # install(FILES ${RK_HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/redkite) #endif() add_subdirectory(tools) if (ENABLE_EXAMPLES) add_subdirectory(examples) endif (ENABLE_EXAMPLES) geonkick-2.10.2/src/redkite/LICENSE000066400000000000000000001045151451017372400166230ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. 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. Copyright (C) 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 . 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: Copyright (C) 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 . 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 . geonkick-2.10.2/src/redkite/README000066400000000000000000000002431451017372400164670ustar00rootroot00000000000000Redkite is no longer developed as a standalone library; its code has become a part of the Geonkick codebase and is now released under the same license as Geonkick.geonkick-2.10.2/src/redkite/README_old.md000066400000000000000000000066171451017372400177370ustar00rootroot00000000000000# Redkite #### Description Redkite is a small free software GUI toolkit. It is developed in C++ and inspired from other well known GUI toolkits. Version: 1.3.1 License: GPLv3 #### The problem An application that is developed in a particular GUI toolkit is loading in its address space GUI plugins that are developed with different versions of the same or different GUI toolkit. Using well known GUI toolkits for this purpose can cause a lot of issues. For example, often these conflicts arise in music software when a Digital Audio Workstation as a host is loading GUI audio plugins. The Redkite GUI toolkit is developed to address this issue, and provide a way to develop easily self-sufficient GUI plugins that can be embedded the host GUI. ### Goals * Generally to have the same basic properties as common GUI toolkits. * Many instances of the same or different versions of the toolkit must be able to run without problems in the same process address space. * Can be easily linked statically. Shared libraries that are developed using the toolkit and loaded dynamically must not cause conflicts. * Not to add dependencies that breaks the above points. Also, maximum to relay on dependencies for runtime that are provided by default by the system. Avoid requiring to install additional dependencies for runtime that may cause version conflicts. * Interface to be simple ### Main features * Object tree ownership * Widgets * Popup and dialog widget * Graphics (with Cairo backend) * Painter (paints RkImage and widgets) * Events * Actions (a binding mechanism between objects) * GNU/Linux (XLib/X11) * rkpng2c (a tool that converts PNG images to C array encoded in ARGB32) * RkTimer (a timer class) * Widget container used for vertical and horizontal layout * Shortcuts * Support to scale the UI (useful for HiDPI cases) ### Interface * RkAction * RkButton * RkCanvas * RkColor * RkContainer * RkContainerItem * RkContainerWidgetItem * RkEvent * RkEventQueue * RkFont * Rk * RkImage * RkLabel * RkLineEdit * RkList * RkLog * RkMain * RkModel * RkObject * RkPainter * RkPen * RkPlatform * RkPoint * RkProgressBar * RkRealPoint * RkRect * RkSize * RkTimer * RkTransition * RkVariant * RkWidget * RkString ### Technical limitations * It is not cross-plafrom, supports only GNU/Linux (X11/Xlib) * RkLineEdit is limited, doesn't implement all standard features * Doesn't have many standard widgets: combo box, popup menus, file dialog, tree view, grid view, multi-line edit etc. * Supports only Cairo graphics backend * Doesn't support copy/paste * Doesn't support unicode ### For what Redkite is recommended * Standalone applications with relative simple UI * GUI apps/plugins with relative simple UI that can be embedded into the host GUI ### Build & Install Redkite In order to build Redkite there is a need to install the following development packages: * Cairo On Debian, Ubuntu, Ubuntu Studio install: apt-get install build-essential apt-get install cmake apt-get install libcairo2-dev Clone the code repository, compile and install. Note: it is recommended to use release versions (see repository tags) git clone https://gitlab.com/iurie-sw/redkite.git mkdir redkite/build cd redkite/build cmake ../ make make install ### What applications were developed with Redkite? * [Geonkick](https://gitlab.com/iurie-sw/geonkick) - a percussion synthesizer. geonkick-2.10.2/src/redkite/doc/000077500000000000000000000000001451017372400163555ustar00rootroot00000000000000geonkick-2.10.2/src/redkite/doc/man/000077500000000000000000000000001451017372400171305ustar00rootroot00000000000000geonkick-2.10.2/src/redkite/doc/man/rkpng2c.1000066400000000000000000000011151451017372400205560ustar00rootroot00000000000000.\" Manpage for rkpng2c. .\" Contact iuriehn@gmail.com to correct errors or typos. .TH man 1 "05 June 2020" "1.0" "rkpng2c man page" .SH NAME rkpng2c \- converts a PNG image to C array encoded in ARGB32 format. .SH SYNOPSIS rkpng2c .SH DESCRIPTION rkpng2c is a part of Redkite GUI toolkit. It converts a PNG image to C array encoded in ARGB32 format. .SH OPTIONS rkpng2c doesn't any take options. .SH EXAMPLES rkpng2c image.png image.c image .SH LICENSE rkpng2c is Free Software released under GPLv3. .SH AUTHOR Iurie Nistor geonkick-2.10.2/src/redkite/include/000077500000000000000000000000001451017372400172335ustar00rootroot00000000000000geonkick-2.10.2/src/redkite/include/Rk.h000066400000000000000000000450421451017372400177650ustar00rootroot00000000000000/** * File name: Rk.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_GLOBAL_H #define RK_GLOBAL_H #define RK_VERSION 0x010300 #define RK_MAJOR 0x01 #define RK_MINOR 0x03 #define RK_PATCH 0x01 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define RK_UNUSED(expr) (void)expr #ifdef RK_OS_WIN #define RK_NO_EXPORT #ifdef RK_EXPORT_INTERFACE // Export Redkite interface #define RK_EXPORT __declspec(dllexport) #else #define RK_EXPORT #endif #elif RK_OS_MAC #error not implemented for Mac #else // RK_OS_GNU #define RK_NO_EXPORT __attribute__((visibility("hidden"))) #ifdef RK_EXPORT_INTERFACE // Export Redkite interface #define RK_EXPORT __attribute__((visibility("default"))) #else #define RK_EXPORT #endif #endif #define RK_DECLARE_IMPL(Class) \ class Class##Impl; \ std::unique_ptr o_ptr #define RK_DELCATE_IMPL_PTR(Class) \ class Class##Impl; \ Class##Impl *impl_ptr #define RK_DECALRE_INTERFACE_PTR(Class) Class *inf_ptr #define RK_CLASS_INFO(name, value) virtual std::string rk__property_ ##name () const { return std::string( #value ); } #define RK_SET_CLASS_INFO(name, value) virtual std::string rk__property_ ##name () const override { return std::string( #value ); } #define RK_DISABLE_COPY(Class) \ Class(const Class &other) = delete; \ Class& operator=(const Class &other) = delete #define RK_DISABLE_MOVE(Class) \ Class(Class &&other) = delete; \ Class& operator=(Class &&other) = delete #if defined(RK_OS_WIN) && !defined(RK_FOR_SHARED) int rkMain(int, char **); #define main rkMain #endif // RK_OS_WIN && !RK_FOR_SHARED #if defined(RK_SINGLE_PRECISION) using rk_real = float; #else using rk_real = double; #endif // RK_SINGLE_PRECISION namespace Rk { enum class Alignment : int { AlignLeft = 1, AlignRight = 2, AlignCenter = 3, AlignTop = 4, AlignBottom = 5 }; enum class Orientation : int { Horizontal = 0, Vertical = 1 }; enum class WindowFlags: int { Widget = 0x00000000, Dialog = 0x00000001, Popup = 0x00000002 }; enum class Modality : int { NonModal = 0, // Disable input for parent and all its children // except the current modal one. ModalParent = 1, // Disable input for all widgets hierarhy // of the top widget, including top widget, // except the current modal one. ModalTopWidget = 2 }; enum class WidgetAttribute : int { NoAttributes = 0x00000000, KeyInputEnabled = 0x00000001, MouseInputEnabled = 0x00000002, CloseInputEnabled = 0x00000004 }; enum class Key : int { Key_None = 0x00000000, /** * Group: key modifiers, as bit flags. */ Key_Shift_Left = 0x00010000, Key_Shift_Right = 0x00020000, Key_Control_Left = 0x00040000, Key_Control_Right = 0x00080000, Key_Caps_Lock = 0x00100000, Key_Shift_Lock = 0x00200000, Key_Meta_Left = 0x00400000, Key_Meta_Right = 0x00800000, Key_Alt_Left = 0x01000000, Key_Alt_Right = 0x02000000, Key_Super_Left = 0x04000000, Key_Super_Right = 0x08000000, Key_Hyper_Left = 0x10000000, Key_Hyper_Right = 0x20000000, /** * Group: Cursor control keys. * Range: 0x00500000 - 0x005f0000 */ Key_Home = 0x00500000, Key_Left = 0x00510000, Key_Up = 0x00520000, Key_Right = 0x00530000, Key_Down = 0x00540000, Key_Page_Up = 0x00550000, Key_Page_Down = 0x00560000, Key_End = 0x00570000, Key_Begin = 0x00580000, /** * Group: Other edit keys. * Range: 0x00590000 - 0x00620000 */ Key_BackSpace = 0x00590000, Key_Tab = 0x005a0000, Key_Linefeed = 0x005b0000, Key_Clear = 0x005c0000, Key_Return = 0x005d0000, Key_Pause = 0x005e0000, Key_Scroll_Lock = 0x005f0000, Key_Sys_Req = 0x00600000, Key_Escape = 0x00610000, Key_Delete = 0x00620000, /** * Group: LATIN1 * Rnage: 0x00000020 - 0x000000ff */ Key_Space = 0x00000020, Key_Exclam = 0x00000021, Key_Quotedbl = 0x00000022, Key_Numbersign = 0x00000023, Key_Dollar = 0x00000024, Key_Percent = 0x00000025, Key_Ampersand = 0x00000026, Key_Apostrophe = 0x00000027, Key_Quoteright = 0x00000027, Key_Parenleft = 0x00000028, Key_Parenright = 0x00000029, Key_Asterisk = 0x0000002a, Key_Plus = 0x0000002b, Key_Comma = 0x0000002c, Key_Minus = 0x0000002d, Key_Period = 0x0000002e, Key_Slash = 0x0000002f, Key_0 = 0x00000030, Key_1 = 0x00000031, Key_2 = 0x00000032, Key_3 = 0x00000033, Key_4 = 0x00000034, Key_5 = 0x00000035, Key_6 = 0x00000036, Key_7 = 0x00000037, Key_8 = 0x00000038, Key_9 = 0x00000039, Key_Colon = 0x0000003a, Key_Semicolon = 0x0000003b, Key_Less = 0x0000003c, Key_Equal = 0x0000003d, Key_Greater = 0x0000003e, Key_Question = 0x0000003f, Key_At = 0x00000040, Key_A = 0x00000041, Key_B = 0x00000042, Key_C = 0x00000043, Key_D = 0x00000044, Key_E = 0x00000045, Key_F = 0x00000046, Key_G = 0x00000047, Key_H = 0x00000048, Key_I = 0x00000049, Key_J = 0x0000004a, Key_K = 0x0000004b, Key_L = 0x0000004c, Key_M = 0x0000004d, Key_N = 0x0000004e, Key_O = 0x0000004f, Key_P = 0x00000050, Key_Q = 0x00000051, Key_R = 0x00000052, Key_S = 0x00000053, Key_T = 0x00000054, Key_U = 0x00000055, Key_V = 0x00000056, Key_W = 0x00000057, Key_X = 0x00000058, Key_Y = 0x00000059, Key_Z = 0x0000005a, Key_Bracketleft = 0x0000005b, Key_Backslash = 0x0000005c, Key_Bracketright = 0x0000005d, Key_Asciicircum = 0x0000005e, Key_Underscore = 0x0000005f, Key_Grave = 0x00000060, Key_Quoteleft = 0x00000060, Key_a = 0x00000061, Key_b = 0x00000062, Key_c = 0x00000063, Key_d = 0x00000064, Key_e = 0x00000065, Key_f = 0x00000066, Key_g = 0x00000067, Key_h = 0x00000068, Key_i = 0x00000069, Key_j = 0x0000006a, Key_k = 0x0000006b, Key_l = 0x0000006c, Key_m = 0x0000006d, Key_n = 0x0000006e, Key_o = 0x0000006f, Key_p = 0x00000070, Key_q = 0x00000071, Key_r = 0x00000072, Key_s = 0x00000073, Key_t = 0x00000074, Key_u = 0x00000075, Key_v = 0x00000076, Key_w = 0x00000077, Key_x = 0x00000078, Key_y = 0x00000079, Key_z = 0x0000007a, Key_Braceleft = 0x0000007b, Key_Bar = 0x0000007c, Key_Braceright = 0x0000007d, Key_Asciitilde = 0x0000007e, Key_Nobreakspace = 0x000000a0, Key_Exclamdown = 0x000000a1, Key_Cent = 0x000000a2, Key_Sterling = 0x000000a3, Key_Currency = 0x000000a4, Key_Yen = 0x000000a5, Key_Brokenbar = 0x000000a6, Key_Section = 0x000000a7, Key_Diaeresis = 0x000000a8, Key_Copyright = 0x000000a9, Key_Ordfeminine = 0x000000aa, Key_Guillemotleft = 0x000000ab, Key_Notsign = 0x000000ac, Key_Hyphen = 0x000000ad, Key_Registered = 0x000000ae, Key_Macron = 0x000000af, Key_Degree = 0x000000b0, Key_Plusminus = 0x000000b1, Key_Twosuperior = 0x000000b2, Key_Threesuperior = 0x000000b3, Key_Acute = 0x000000b4, Key_Mu = 0x000000b5, Key_Paragraph = 0x000000b6, Key_Periodcentered = 0x000000b7, Key_Cedilla = 0x000000b8, Key_Onesuperior = 0x000000b9, Key_Masculine = 0x000000ba, Key_Guillemotright = 0x000000bb, Key_Onequarter = 0x000000bc, Key_Onehalf = 0x000000bd, Key_Threequarters = 0x000000be, Key_Questiondown = 0x000000bf, Key_Agrave = 0x000000c0, Key_Aacute = 0x000000c1, Key_Acircumflex = 0x000000c2, Key_Atilde = 0x000000c3, Key_Adiaeresis = 0x000000c4, Key_Aring = 0x000000c5, Key_Ae = 0x000000c6, Key_Ccedilla = 0x000000c7, Key_Egrave = 0x000000c8, Key_Eacute = 0x000000c9, Key_Ecircumflex = 0x000000ca, Key_Ediaeresis = 0x000000cb, Key_Igrave = 0x000000cc, Key_Iacute = 0x000000cd, Key_Icircumflex = 0x000000ce, Key_Idiaeresis = 0x000000cf, Key_Eth = 0x000000d0, Key_Ntilde = 0x000000d1, Key_Ograve = 0x000000d2, Key_Oacute = 0x000000d3, Key_Ocircumflex = 0x000000d4, Key_Otilde = 0x000000d5, Key_Odiaeresis = 0x000000d6, Key_Multiply = 0x000000d7, Key_Oslash = 0x000000d8, Key_Ooblique = 0x000000d8, Key_Ugrave = 0x000000d9, Key_Uacute = 0x000000da, Key_Ucircumflex = 0x000000db, Key_Udiaeresis = 0x000000dc, Key_Yacute = 0x000000dd, Key_Thorn = 0x000000de, Key_Ssharp = 0x000000df, Key_agrave = 0x000000e0, Key_aacute = 0x000000e1, Key_acircumflex = 0x000000e2, Key_atilde = 0x000000e3, Key_adiaeresis = 0x000000e4, Key_aring = 0x000000e5, Key_ae = 0x000000e6, Key_ccedilla = 0x000000e7, Key_egrave = 0x000000e8, Key_eacute = 0x000000e9, Key_ecircumflex = 0x000000ea, Key_ediaeresis = 0x000000eb, Key_igrave = 0x000000ec, Key_iacute = 0x000000ed, Key_icircumflex = 0x000000ee, Key_idiaeresis = 0x000000ef, Key_eth = 0x000000f0, Key_ntilde = 0x000000f1, Key_ograve = 0x000000f2, Key_oacute = 0x000000f3, Key_ocircumflex = 0x000000f4, Key_otilde = 0x000000f5, Key_odiaeresis = 0x000000f6, Key_division = 0x000000f7, Key_oslash = 0x000000f8, Key_ooblique = 0x000000f8, Key_ugrave = 0x000000f9, Key_uacute = 0x000000fa, Key_ucircumflex = 0x000000fb, Key_udiaeresis = 0x000000fc, Key_yacute = 0x000000fd, Key_thorn = 0x000000fe, Key_Ydiaeresis = 0x000000ff }; enum class KeyModifiers: int { NoModifier = 0x0000, Shift_Left = 0x0001, Shift_Right = 0x0002, Shift = Shift_Left | Shift_Right, Control_Left = 0x0004, Control_Right = 0x0008, Control = Control_Left | Control_Right, Caps_Lock = 0x0010, Shift_Lock = 0x0020, Meta_Left = 0x0040, Meta_Right = 0x0080, Meta = Meta_Left | Meta_Right, Alt_Left = 0x0100, Alt_Right = 0x0200, Alt = Alt_Left | Alt_Right, Super_Left = 0x0400, Super_Right = 0x0800, Super = Super_Left | Super_Right, Hyper_Left = 0x1000, Hyper_Right = 0x2000, Hyper = Hyper_Left | Hyper_Right }; enum class PointerShape: int { NoShape = 0, Arrow = 1, UpArrow = 2, Cross = 3, Wait = 4, WhatsThis = 5, IBeam = 6, SizeVertical = 7, SizeHorizontal = 8, SizeTLDiagonal = 9, SizeTRDiagonal = 10, SizeAll = 11, SplitVertical = 12, SplitHorizontal = 13, PointingHand = 14, OpenHand = 15, ClosedHand = 16, Forbidden = 17, Busy = 18 }; enum class ObjectType : int { Object = 0, Widget = 1 }; } #define RK_ACT_ARGS(arg, ...) arg, ##__VA_ARGS__ #define RK_ARG_TYPE(type, ...) type, ##__VA_ARGS__ #define RK_ARG_VAL(val, ...) val, ##__VA_ARGS__ #define RK_DECL_ACT(name, prot, type, val) \ class rk__observer_##name : public RkObserver { \ public: \ rk__observer_ ##name (RkObject *obj, const std::function &cb) \ : RkObserver(obj), \ observerCallback{cb} {} \ rk__observer_##name () = default; \ std::function observerCallback; \ }; \ \ void prot \ { \ for (const auto& ob: rk__observers()) { \ auto observer = dynamic_cast(ob.get()); \ if (observer) \ observer->observerCallback(val); \ } \ } \ void rk__add_action_cb_##name (RkObject *obj, const std::function &cb) \ { \ rk__add_observer(std::make_unique(obj, cb)); \ } #define RK_ACT_BIND(obj1, act, act_args, obj2, callback) \ obj1->rk__add_action_cb_##act (obj2, [=](act_args){ obj2->callback; }); \ obj2->rk__add_bound_object(obj1) // Bind lamda functions to object actions. #define RK_ACT_BINDL(obj1, act, act_args, lamda) \ obj1->rk__add_action_cb_##act (nullptr, lamda) #define action #define RK_DECLARE_IMAGE_RC(name) extern const unsigned char rk__ ## name ## _png[] #define RK_IMAGE_RC(name) rk__ ## name ## _png using RkString = std::string; #endif // RK_GLOBAL_H geonkick-2.10.2/src/redkite/include/RkAction.h000066400000000000000000000027551451017372400211270ustar00rootroot00000000000000/** * File name: RkAction.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_ACTION_H #define RK_ACTION_H #include "Rk.h" #include "RkObject.h" class RK_EXPORT RkAction { public: explicit RkAction(RkObject *obj = nullptr) : actionObject{obj} {} virtual ~RkAction() = default; void setCallback(const std::function &cb) { actionCallback = cb; } void call() { if (actionCallback) actionCallback(); } RkObject *object() { return actionObject; } private: RkObject *actionObject; std::function actionCallback; }; #endif // RK_ACTION_H geonkick-2.10.2/src/redkite/include/RkButton.h000066400000000000000000000051331451017372400211560ustar00rootroot00000000000000/** * File name: RkButton.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_BUTTON_H #define RK_BUTTON_H #include "RkWidget.h" #include "RkImage.h" class RK_EXPORT RkButton: public RkWidget { public: enum class ButtonType: int { ButtonUncheckable = 0, ButtonCheckable = 1, ButtonPush = 3 }; enum class State: int { Unpressed = 0, UnpressedHover = 1, Pressed = 2, PressedHover = 3 }; explicit RkButton(RkWidget *parent); virtual ~RkButton() = default; void setText(const std::string &text); std::string text() const; void setImage(const RkImage &img, State state = State::Unpressed); void setPressed(bool pressed); bool isPressed() const; ButtonType type() const; void setCheckable(bool b); bool isCheckable() const; void setType(ButtonType type); RK_DECL_ACT(toggled, toggled(bool pressed), RK_ARG_TYPE(bool), RK_ARG_VAL(pressed)); RK_DECL_ACT(pressed, pressed(), RK_ARG_TYPE(), RK_ARG_VAL()); RK_DECL_ACT(released, released(), RK_ARG_TYPE(), RK_ARG_VAL()); protected: virtual void mouseButtonPressEvent(RkMouseEvent *event) override; virtual void mouseButtonReleaseEvent(RkMouseEvent *event) override; virtual void hoverEvent(RkHoverEvent *event) override; virtual void paintEvent(RkPaintEvent *event) override; private: RK_DELCATE_IMPL_PTR(RkButton); RK_DISABLE_COPY(RkButton); RK_DISABLE_MOVE(RkButton); }; #endif // RK_BUTTON_H geonkick-2.10.2/src/redkite/include/RkCanvas.h000066400000000000000000000021671451017372400211220ustar00rootroot00000000000000/** * File name: RkCanvas.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_CANVAS_H #define RK_CANVAS_H #include "Rk.h" struct RK_EXPORT RkCanvasInfo; class RK_EXPORT RkCanvas { public: RkCanvas() = default; virtual ~RkCanvas() = default; virtual const RkCanvasInfo* getCanvasInfo() const = 0; }; #endif // RK_CANVAS_H geonkick-2.10.2/src/redkite/include/RkColor.h000066400000000000000000000061001451017372400207540ustar00rootroot00000000000000/** * File name: RkColor.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_COLOR_H #define RK_COLOR_H #include "Rk.h" class RK_EXPORT RkColor { public: constexpr RkColor() : redValue{255} , greenValue{255} , blueValue{255} , alphaValue{255} { } constexpr RkColor(int red, int green, int blue, int alpha = 255) : redValue(red) , greenValue(green) , blueValue(blue) , alphaValue(alpha) { } friend constexpr bool operator==(const RkColor &c1, const RkColor &c2) { return c1.redValue == c2.redValue && c1.greenValue == c2.greenValue && c1.blueValue == c2.blueValue && c1.alphaValue == c2.alphaValue; } friend constexpr bool operator!=(const RkColor &c1, const RkColor &c2) { return c1.redValue != c2.redValue || c1.greenValue != c2.greenValue || c1.blueValue != c2.blueValue || c1.alphaValue != c2.alphaValue; } constexpr int red() const { return redValue; } constexpr int green() const { return greenValue; } constexpr int blue() const { return blueValue; } constexpr int alpha() const { return alphaValue; } constexpr void setRed(int red) { redValue = red; } constexpr void setGreen(int green) { greenValue = green; } constexpr void setBlue(int blue) { blueValue = blue; } constexpr void setAlpha(int alpha) { alphaValue = alpha; } unsigned long int argb() const { return (((unsigned long int)alphaValue) << 24) | (((unsigned long int)redValue) << 16) | (((unsigned long int)greenValue) << 8) | (((unsigned long int)blueValue)); } private: unsigned short redValue; unsigned short greenValue; unsigned short blueValue; unsigned short alphaValue; }; #endif // RK_COLOR_H geonkick-2.10.2/src/redkite/include/RkContainer.h000066400000000000000000000045751451017372400216360ustar00rootroot00000000000000/** * File name: RkWidgetContainer.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_CONTAINER_H #define RK_CONTAINER_H #include "RkWidget.h" #include "RkContainerItem.h" class RK_EXPORT RkContainer: public RkContainerItem { public: RkContainer(RkWidget *parent = nullptr, Rk::Orientation orientation = Rk::Orientation::Horizontal); virtual ~RkContainer() = default; void addContainer(RkContainer *contaier, Rk::Alignment align = Rk::Alignment::AlignLeft); void addWidget(RkWidget *widget, Rk::Alignment align = Rk::Alignment::AlignLeft); void removeWidget(RkWidget *widget); void addSpace(int space, Rk::Alignment align = Rk::Alignment::AlignLeft); void removeAt(size_t index); RkContainerItem* at(size_t index) const; void update(); void clear(); Rk::Orientation orientation() const; Rk::Alignment alignment(RkWidget *widget) const; Rk::Alignment alignment(size_t index) const; void setSize(const RkSize &size) override; void setWidth(int width) override; void setHeight(int height) override; void setPosition(const RkPoint &position) override; void setX(int val) override; void setY(int val) override; void setSpacing(size_t space); size_t spacing() const; void setHiddenTakesPlace(bool b = true); bool hiddenTakesPlace() const; protected: int initPosition(Rk::Alignment alignment); private: RK_DISABLE_COPY(RkContainer); RK_DISABLE_MOVE(RkContainer); std::vector containerItems; Rk::Orientation containerOrientation; size_t itemSpacing; bool isHiddenTakesPlace; }; #endif // RK_CONTAINER_H geonkick-2.10.2/src/redkite/include/RkContainerItem.h000066400000000000000000000055311451017372400224460ustar00rootroot00000000000000/** * File name: RkContainerItem.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_CONTAINER_ITEM_H #define RK_CONTAINER_ITEM_H #include class RK_EXPORT RkContainerItem: public RkObject { public: enum class ItemType : int { ItemContainer = 0, ItemWidget = 1, ItemSpace = 2, }; RkContainerItem(RkObject *parent = nullptr, ItemType type = ItemType::ItemContainer, Rk::Alignment align = Rk::Alignment::AlignLeft) : RkObject(parent) , itemType{type} , itemAlignment{align} , itemHidden{false} {} virtual ~RkContainerItem() = default; ItemType type() const { return itemType; } virtual void setPosition(const RkPoint &point) { itemPosition = point; } virtual RkPoint position() const { return itemPosition; } virtual void setX(int val) { itemPosition.setX(val); } virtual int x() const { return itemPosition.x(); } virtual void setY(int val) { itemPosition.setY(val); } virtual int y() const { return itemPosition.y(); } virtual void setSize(const RkSize &size) { itemSize = size; } virtual RkSize size() const { return itemSize; } virtual void setWidth(int val) { itemSize.setWidth(val); } virtual int width() const { return itemSize.width(); } virtual void setHeight(int val) { itemSize.setHeight(val); } virtual int height() const { return itemSize.height(); } void setAlignment(Rk::Alignment align) { itemAlignment = align; } Rk::Alignment alignment() const { return itemAlignment; } virtual void hide(bool b) { itemHidden = b; } virtual bool isHidden() const { return itemHidden; } private: RK_DISABLE_COPY(RkContainerItem); RK_DISABLE_MOVE(RkContainerItem); RkSize itemSize; RkPoint itemPosition; ItemType itemType; Rk::Alignment itemAlignment; bool itemHidden; }; #endif // RK_CONTAINER_ITEM_H geonkick-2.10.2/src/redkite/include/RkContainerWidgetItem.h000066400000000000000000000051001451017372400236020ustar00rootroot00000000000000/** * File name: RkContainerWidgetItem.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_CONTAINER_WIDGET_ITEM_H #define RK_CONTAINER_WIDGET_ITEM_H #include class RkWidget; class RK_EXPORT RkContainerWidgetItem: public RkContainerItem { public: RkContainerWidgetItem(RkWidget *parent, Rk::Alignment align = Rk::Alignment::AlignLeft) : RkContainerItem(parent, ItemType::ItemContainer, align) , itemWidget{parent} {} virtual ~RkContainerWidgetItem() = default; void setPosition(const RkPoint &point) override { itemWidget->setPosition(point); } RkPoint position() const override { return itemWidget->position(); } void setX(int val) override { itemWidget->setX(val); } int x() const override { return itemWidget->x(); } void setY(int val) override { itemWidget->setY(val); } int y() const override { return itemWidget->y(); } void setSize(const RkSize &size) override { itemWidget->setSize(size); } RkSize size() const override { return itemWidget->size(); } void setWidth(int val) override { itemWidget->setWidth(val); } int width() const override { return itemWidget->width(); } void setHeight(int val) override { itemWidget->setHeight(val); } int height() const override { return itemWidget->height(); } RkWidget* widget() const { return itemWidget; } void hide(bool b) override { return b ? itemWidget->hide() : itemWidget->show(); } bool isHidden() const override { return !itemWidget->isShown(); } private: RK_DISABLE_COPY(RkContainerWidgetItem); RK_DISABLE_MOVE(RkContainerWidgetItem); RkSize itemSize; RkWidget *itemWidget; }; #endif // RK_CONTAINER_WIDGET_ITEM_H geonkick-2.10.2/src/redkite/include/RkEvent.h000066400000000000000000000136521451017372400207710ustar00rootroot00000000000000/** * File name: RkEvent.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_EVENT_H #define RK_EVENT_H #include "Rk.h" #include "RkPoint.h" class RkCloseEvent; class RkKeyEvent; class RkMouseEvent; class RkWheelEvent; class RkMoveEvent; class RkResizeEvent; class RkPaintEvent; class RkShowEvent; class RkHideEvent; class RkHoverEvent; class RkWidget; class RK_EXPORT RkEvent { public: enum class Type: int { NoEvent = 0, Close = 1, KeyPressed = 2, KeyReleased = 3, MouseMove = 4, MouseButtonPress = 5, MouseButtonRelease = 6, MouseDoubleClick = 7, Wheel = 8, Move = 9, Resize = 10, Paint = 11, Show = 12, Hide = 13, DeleteChild = 14, FocusedIn = 15, FocusedOut = 16, Hover = 17, Drop = 18, ScaleFactor = 19 }; explicit RkEvent(Type type = Type::NoEvent) : eventType{type} , eventTime{std::chrono::system_clock::now()} {} virtual ~RkEvent() = default; void setType(Type type) { eventType = type; } Type type() const { return eventType; } std::chrono::system_clock::time_point time() const { return eventTime; } void setTime(const std::chrono::system_clock::time_point &time) { eventTime = time; } private: Type eventType; std::chrono::system_clock::time_point eventTime; }; class RkCloseEvent: public RkEvent { public: RkCloseEvent() : RkEvent(Type::Close) { } }; class RK_EXPORT RkKeyEvent: public RkEvent { public: RkKeyEvent(Type type = Type::KeyPressed) : RkEvent(type) , keyValue{Rk::Key::Key_None} , keyModifiers{0} , typeShortcut{false} { } void setKey(Rk::Key key) { keyValue = key; } Rk::Key key() const { return keyValue; } void setModifiers(int mod) { keyModifiers = mod; } int modifiers() const { return keyModifiers; } void setShortcut(bool b = true) { typeShortcut = b; } bool isShortcut() const { return typeShortcut; } private: Rk::Key keyValue; int keyModifiers; bool typeShortcut; }; class RK_EXPORT RkMouseEvent: public RkEvent { public: enum class ButtonType : int { Unknown = 0, Left = 1, Middle = 2, Right = 3, WheelUp = 4, WheelDown = 5 }; RkMouseEvent(Type type = Type::MouseButtonPress) : RkEvent(type) , buttonType{ButtonType::Unknown} {} int x() const { return mouseCoordinates.x(); } void setX(int x) { mouseCoordinates.setX(x); } int y() const { return mouseCoordinates.y(); } void setY(int y) { mouseCoordinates.setY(y); } ButtonType button() const { return buttonType; } void setButton(ButtonType type) { buttonType = type; } private: RkPoint mouseCoordinates; ButtonType buttonType; }; class RkDropEvent: public RkEvent { public: RkDropEvent() : RkEvent(Type::Drop) {} int x() const { return position.x(); } void setX(int x) { position.setX(x); } int y() const { return position.y(); } void setY(int y) { position.setY(y); } void setFilePath(const std::string &path) { filePath = path; } std::string getFilePath() const { return filePath; } private: RkPoint position; std::string filePath; }; class RkWheelEvent: public RkEvent { }; class RkMoveEvent: public RkEvent { }; class RkResizeEvent: public RkEvent { public: RkResizeEvent() : RkEvent(Type::Resize) { } }; class RkPaintEvent: public RkEvent { public: RkPaintEvent() : RkEvent(Type::Paint) { } }; class RkShowEvent: public RkEvent { }; class RkHideEvent: public RkEvent { }; class RkDeleteChild: public RkEvent { public: RkDeleteChild(RkWidget *parent, RkWidget* child) : RkEvent(Type::DeleteChild) , parentWidget{parent} , childWidget{child} {} RkWidget* child() const { return childWidget; } RkWidget* parent() const { return parentWidget; } private: RkWidget *parentWidget; RkWidget *childWidget; }; class RkFocusEvent: public RkEvent { public: RkFocusEvent(Type type = Type::FocusedIn) : RkEvent(type) {}; }; class RkHoverEvent: public RkEvent { public: RkHoverEvent() : RkEvent(Type::Hover), hOver{false} {} void setHover(bool b) { hOver = b; } bool isHover() const { return hOver; } private: bool hOver; }; class RkScaleFactorEvent: public RkEvent { public: RkScaleFactorEvent() : RkEvent(Type::ScaleFactor), scaleFactor{1.0} {} void setFactor(double f) { scaleFactor = f; } double factor() const { return scaleFactor; } private: double scaleFactor; }; #endif // RK_EVENT_H geonkick-2.10.2/src/redkite/include/RkEventQueue.h000066400000000000000000000045301451017372400217710ustar00rootroot00000000000000/** * File name: RkEventQueue.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_EVENT_QUEUE_H #define RK_EVENT_QUEUE_H #include "Rk.h" #include "RkObject.h" #include "RkAction.h" class RkEvent; class RkTimer; class RK_EXPORT RkEventQueue { public: RkEventQueue(); virtual ~RkEventQueue(); void addObject(RkObject *obj); void addShortcut(RkObject *obj, Rk::Key key, Rk::KeyModifiers modifier = Rk::KeyModifiers::NoModifier); void removeShortcut(RkObject *obj, Rk::Key key, Rk::KeyModifiers modifier = Rk::KeyModifiers::NoModifier); void removeObject(RkObject *obj); void postEvent(RkObject *obj, std::unique_ptr event); void postAction(std::unique_ptr act); void subscribeTimer(RkTimer *timer); void unsubscribeTimer(RkTimer *timer); void processEvents(); void processActions(); void processTimers(); void processQueue(); void clearObjectEvents(const RkObject *obj); void clearObjectActions(const RkObject *obj); void clearEvents(); void clearActions(); void clearQueue(); RkObject* findObjectByName(const std::string &name) const; void setScaleFactor(double factor); protected: RK_DECLARE_IMPL(RkEventQueue); RkEventQueue(std::unique_ptr impl); private: RK_DISABLE_COPY(RkEventQueue); RK_DISABLE_MOVE(RkEventQueue); }; #endif // RK_EVENT_QUEUE_H geonkick-2.10.2/src/redkite/include/RkFont.h000066400000000000000000000062161451017372400206140ustar00rootroot00000000000000/** * File name: RkFont.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_FONT_H #define RK_FONT_H #include "Rk.h" #include "RkColor.h" class RK_EXPORT RkFont { public: enum class Style : int { Normal = 0, Italic = 1, Oblique = 3 }; enum class Weight: int { Normal = 0, Bold = 1 }; RkFont() : fontFamily{"Arial"} // TODO: get default font family , fontSize{10} , fontWeight{Weight::Normal} , fontStyle{Style::Normal} {} explicit RkFont(const std::string &family, int size = 10, Weight weight = Weight::Normal, Style style = Style::Normal) : fontFamily{family} , fontSize{size} , fontWeight{weight} , fontStyle{style} {} RkFont(const RkFont & font) = default; RkFont & operator=(const RkFont & font) = default; RkFont & operator=(RkFont && other) = default; friend bool operator==(const RkFont &font1, const RkFont &font2) { return font1.fontFamily == font2.fontFamily && font1.fontStyle == font2.fontStyle && font1.fontWeight == font2.fontWeight && font1.fontSize == font2.fontSize; } friend bool operator!=(const RkFont &font1, const RkFont &font2) { return font1.fontFamily != font2.fontFamily || font1.fontStyle != font2.fontStyle || font1.fontWeight != font2.fontWeight || font1.fontSize != font2.fontSize; } Style style() const { return fontStyle; } void setStyle(Style style) { fontStyle = style; } std::string family() const { return fontFamily; } void setFamily(const std::string &family) { fontFamily = family; } int size() const { return fontSize; }; void setSize(int size) { fontSize = size; } Weight weight() const { return fontWeight; } void setWeight(Weight weight) { fontWeight = weight; } private: std::string fontFamily; int fontSize; Weight fontWeight; Style fontStyle; }; #endif // RK_FONT_H geonkick-2.10.2/src/redkite/include/RkImage.h000066400000000000000000000052761451017372400207350ustar00rootroot00000000000000 /** * File name: RkImage.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_IMAGE_H #define RK_IMAGE_H #include "RkCanvas.h" #include "RkSize.h" #include "RkColor.h" class RK_EXPORT RkImage : public RkCanvas { public: enum class Format : int { ARGB32 = 0, RGB32 = 1 }; RkImage(); explicit RkImage(int width, int height, const unsigned char *data = nullptr, Format format = Format::ARGB32); explicit RkImage(const RkSize &size, const unsigned char *data = nullptr, Format format = Format::ARGB32); virtual ~RkImage(); RkImage(const RkImage &image); RkImage& operator=(const RkImage &other); friend bool operator==(const RkImage &im1, const RkImage &im2) { return im1.width() == im2.width() && im1.height() == im2.height() && im1.format() == im2.format() && im1.dataCopy() == im2.dataCopy(); } friend bool operator!=(const RkImage &im1, const RkImage &im2) { return im1.width() != im2.width() || im1.height() != im2.height() || im1.format() != im2.format() || im1.dataCopy() != im2.dataCopy(); } void fill(const RkColor &color); const RkCanvasInfo* getCanvasInfo() const; unsigned char* data() const; std::vector dataCopy() const; Format format() const; int width() const; int height() const; RkSize size() const; bool isNull() const; protected: RK_DECLARE_IMPL(RkImage); explicit RkImage(std::unique_ptr impl); private: RK_DISABLE_MOVE(RkImage); }; #endif // RK_IMAGE_H geonkick-2.10.2/src/redkite/include/RkLabel.h000066400000000000000000000026641451017372400207300ustar00rootroot00000000000000/** * File name: RkLabel.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_LABEL_H #define RK_LABEL_H #include "RkWidget.h" #include "RkImage.h" class RK_EXPORT RkLabel : public RkWidget { public: RkLabel(RkWidget *parent, const std::string &text = std::string()); virtual ~RkLabel() = default; void setText(const std::string &text); std::string text() const; void setImage(const std::string &file); void setImage(const RkImage &image); protected: virtual void paintEvent(RkPaintEvent *event) override; private: RK_DISABLE_COPY(RkLabel); RK_DISABLE_MOVE(RkLabel); RK_DELCATE_IMPL_PTR(RkLabel); }; #endif // RK_LABEL_H geonkick-2.10.2/src/redkite/include/RkLineEdit.h000066400000000000000000000043531451017372400214030ustar00rootroot00000000000000/** * File name: RkLineEdit.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_LINE_EDIT_H #define RK_LINE_EDIT_H #include "RkWidget.h" class RK_EXPORT RkLineEdit : public RkWidget { public: RkLineEdit(RkWidget *parent, const std::string &text = std::string()); virtual ~RkLineEdit() = default; void setText(const std::string &text); std::string text() const; void showCursor(); void hideCursor(); void moveCursorToFront(); void moveCursorToEnd(); RK_DECL_ACT(textEdited, textEdited(const std::string &text), RK_ARG_TYPE(const std::string&), RK_ARG_VAL(text)); RK_DECL_ACT(escapePressed, escapePressed(), RK_ARG_TYPE(), RK_ARG_VAL()); RK_DECL_ACT(enterPressed, enterPressed(), RK_ARG_TYPE(), RK_ARG_VAL()); RK_DECL_ACT(editingFinished, editingFinished(), RK_ARG_TYPE(), RK_ARG_VAL()); protected: virtual void paintEvent(RkPaintEvent *event) override; virtual void keyPressEvent(RkKeyEvent *event) override; virtual void mouseButtonPressEvent(RkMouseEvent *event) override; virtual void focusEvent(RkFocusEvent *event) override; virtual void resizeEvent(RkResizeEvent* event) override; private: RK_DISABLE_COPY(RkLineEdit); RK_DISABLE_MOVE(RkLineEdit); RK_DELCATE_IMPL_PTR(RkLineEdit); }; #endif // RK_LABEL_H geonkick-2.10.2/src/redkite/include/RkList.h000066400000000000000000000034551451017372400206230ustar00rootroot00000000000000/** * File name: RkList.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_LIST_H #define RK_LIST_H #include "RkWidget.h" #include "RkImage.h" class RkModel; class RK_EXPORT RkList : public RkWidget { public: RkList(RkWidget *parent, RkModel *model = nullptr); virtual ~RkList() = default; RkModel* getModel() const; protected: virtual void paintEvent(RkPaintEvent *event) override; virtual void keyPressEvent(RkKeyEvent *event) override; virtual void keyReleaseEvent(RkKeyEvent *event) override; virtual void mouseMoveEvent(RkMouseEvent *event) override; virtual void mouseButtonPressEvent(RkMouseEvent *event) override; virtual void mouseButtonReleaseEvent(RkMouseEvent *event) override; virtual void mouseDoubleClickEvent(RkMouseEvent *event) override; virtual void wheelEvent(RkWheelEvent *event) override; protected: private: RK_DISABLE_COPY(RkList); RK_DISABLE_MOVE(RkList); RK_DELCATE_IMPL_PTR(RkList); }; #endif // RK_LIST_H geonkick-2.10.2/src/redkite/include/RkLog.h000066400000000000000000000047351451017372400204330ustar00rootroot00000000000000/** * File name: RkLog.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_LOGGING_H #define RK_LOGGING_H #include "Rk.h" enum class RkLogLevel : int { Debug, Info, Warning, Error }; void RK_EXPORT rkLogMessage(RkLogLevel level, const std::string &func_name, const std::string &msg); #if defined(__GNUC__) || defined(__MINGW32__) #define RK_LOG_FUNC_NAME __PRETTY_FUNCTION__ #elif defined(_MSC_VER) #define RK_LOG_FUNC_NAME __FUNCTION__ #else #define RK_LOG_FUNC_NAME __PRETTY_FUNCTION__ #endif #ifdef RK_LOG_DEBUG_LEVEL #define RK_LOG_DEBUG(msg) do { \ std::ostringstream rk_stream; \ rk_stream << msg; \ rkLogMessage(RkLogLevel::Debug, RK_LOG_FUNC_NAME, rk_stream.str()); } while(0) #define RK_LOG_INFO_LEVEL #else #define RK_LOG_DEBUG(msg) #endif // RK_LOG_DEBUG_LEVEL #ifdef RK_LOG_INFO_LEVEL #define RK_LOG_INFO(msg) do { \ std::ostringstream rk_stream; \ rk_stream << msg; \ rkLogMessage(RkLogLevel::Info, RK_LOG_FUNC_NAME, rk_stream.str()); } while(0) #define RK_LOG_WARN_LEVEL #else #define RK_LOG_INFO(msg) #endif // RK_LOG_INFO_LEVEL #ifdef RK_LOG_WARN_LEVEL #define RK_LOG_WARN(msg) do { \ std::ostringstream rk_stream; \ rk_stream << msg; \ rkLogMessage(RkLogLevel::Warning, RK_LOG_FUNC_NAME, rk_stream.str()); } while(0) #define RK_LOG_ERROR_LEVEL #else #define RK_LOG_WARN(msg) #endif // RK_LOG_WARN_LEVEL #ifdef RK_LOG_ERROR_LEVEL #define RK_LOG_ERROR(msg) do { \ std::ostringstream rk_stream; \ rk_stream << msg; \ rkLogMessage(RkLogLevel::Error, RK_LOG_FUNC_NAME, rk_stream.str()); } while(0) #else #define RK_LOG_ERROR(msg) #endif // RK_LOG_ERROR_LEVEL #endif // RK_LOGGING_H geonkick-2.10.2/src/redkite/include/RkMain.h000066400000000000000000000025541451017372400205730ustar00rootroot00000000000000/** * File name: RkMain.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_MAIN_H #define RK_MAIN_H #include "Rk.h" class RkWidget; class RkEventQueue; class RK_EXPORT RkMain { public: RkMain(); RkMain(int argc, char **argv); ~RkMain(); bool setTopLevelWidget(RkWidget* widget); RkWidget* topLevelWidget(void) const; int exec(bool block = true); RkEventQueue* eventQueue() const; private: RK_DECLARE_IMPL(RkMain); RK_DISABLE_COPY(RkMain); RK_DISABLE_MOVE(RkMain); }; #endif // RK_MAIN_H geonkick-2.10.2/src/redkite/include/RkModel.h000066400000000000000000000046371451017372400207530ustar00rootroot00000000000000/** * File name: RkModel.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_MODEL_H #define RK_MODEL_H #include "Rk.h" #include "RkObject.h" #include "RkVariant.h" class RK_EXPORT RkModel; class RK_EXPORT RkModelItem { public: enum class DataType : int { String = 0, Color = 1, Size = 2, Font = 4, UserType = 5, }; RkModelItem(RkModel *model, size_t index) : itemModel{model} , itemIndex{index} { } RkVariant data(int dataType = static_cast(RkModelItem::DataType::String)) const; size_t getIndex() const { return itemIndex; } RkModel *model() const { return itemModel; } private: RkModel *itemModel; size_t itemIndex; }; class RK_EXPORT RkModel: public RkObject { public: explicit RkModel(RkObject *parent); virtual ~RkModel() = default; RK_DECL_ACT(modelChanged, modelChanged(), RK_ARG_TYPE(), RK_ARG_VAL()); RK_DECL_ACT(itemSelected, itemSelected(RkModelItem item), RK_ARG_TYPE(RkModelItem), RK_ARG_VAL(item)); virtual RkVariant itemData(size_t index, int dataType = static_cast(RkModelItem::DataType::String)) const = 0; virtual size_t itemsNumber() const = 0; virtual int itemSpan() const = 0; void selectItem(size_t index); bool isItemSelected(size_t index) const; bool isValidIndex(size_t index) const; protected: RK_DELCATE_IMPL_PTR(RkModel); private: RK_DISABLE_COPY(RkModel); RK_DISABLE_MOVE(RkModel); }; #endif // RK_MODEL_H geonkick-2.10.2/src/redkite/include/RkObject.h000066400000000000000000000044531451017372400211150ustar00rootroot00000000000000/** * File name: RkObject.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_OBJECT_H #define RK_OBJECT_H #include "Rk.h" #include "RkObserver.h" class RkEvent; class RkEventQueue; class RK_EXPORT RkObject { public: explicit RkObject(RkObject *parent = nullptr); virtual ~RkObject(); Rk::ObjectType type() const; RkObject* parent() const; const std::unordered_set& children() const; void setEventQueue(RkEventQueue* queue); RkEventQueue* eventQueue() const; virtual void event(RkEvent *event); void addShortcut(Rk::Key key, Rk::KeyModifiers modifier = Rk::KeyModifiers::NoModifier); void removeShortcut(Rk::Key key, Rk::KeyModifiers modifier = Rk::KeyModifiers::NoModifier); RkObject* findObject(const std::string &name) const; void setName(const std::string &name); std::string name() const; void rk__add_bound_object(RkObject* obj); protected: RK_DECLARE_IMPL(RkObject); explicit RkObject(RkObject *parent, std::unique_ptr impl); void rk__add_observer(std::unique_ptr observer); const std::vector>& rk__observers() const; private: RK_DISABLE_COPY(RkObject); RK_DISABLE_MOVE(RkObject); void addChild(RkObject *child); void removeChild(RkObject *child); void removeObservers(RkObject *ob); void removeBoundObject(RkObject *obj); friend class RkEventQueue; }; #endif // RK_OBJECT_H geonkick-2.10.2/src/redkite/include/RkObserver.h000066400000000000000000000024411451017372400214710ustar00rootroot00000000000000/** * File name: RkObserver.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_OBSERVER_H #define RK_OBSERVER_H #include "Rk.h" #include "RkLog.h" class RkObject; class RK_EXPORT RkObserver { public: RkObserver(RkObject *obj = nullptr) : observerObject{obj} { } virtual ~RkObserver() = default; RkObject *object() { return observerObject; } void setObject(RkObject *obj) { obj = observerObject; } private: RkObject *observerObject; }; #endif // RK_OBSERVER_H geonkick-2.10.2/src/redkite/include/RkPainter.h000066400000000000000000000044631451017372400213120ustar00rootroot00000000000000/** * File name: RkPainter.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_PAINTER_H #define RK_PAINTER_H #include "Rk.h" #include "RkImage.h" #include "RkPoint.h" #include "RkPen.h" #include "RkRect.h" #include "RkFont.h" class RkCanvas; class RK_EXPORT RkPainter { public: RkPainter(RkCanvas *canvas); ~RkPainter(); void drawText(int x, int y, const std::string &text); void drawText(const RkPoint &p, const std::string &text); void drawText(const RkRect &rect, const std::string &text, Rk::Alignment alignment = Rk::Alignment::AlignCenter); void drawImage(const RkImage &image, int x, int y); void drawCircle(int x, int y, int radius); void drawCircle(const RkPoint &p, int radius); void drawLine(int x1, int y1, int x2, int y2); void drawLine(const RkPoint &p1, const RkPoint &p2); void drawRect(const RkRect &rect); void drawPolyline(const std::vector &points); void fillRect(const RkRect &rect, const RkColor &color); void applyAlpha(int alpha); const RkPen& pen() const; void setPen(const RkPen &pen); const RkFont& font() const; void setFont(const RkFont &font); void translate(const RkPoint &offset); void rotate(rk_real angle); int getTextWidth(const std::string &text) const; private: RK_DISABLE_COPY(RkPainter); RK_DISABLE_MOVE(RkPainter); RK_DECLARE_IMPL(RkPainter); }; #endif // RK_PAINTER_H geonkick-2.10.2/src/redkite/include/RkPen.h000066400000000000000000000053361451017372400204320ustar00rootroot00000000000000/** * File name: RkPen.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_PEN_H #define RK_PEN_H #include "Rk.h" #include "RkColor.h" class RK_EXPORT RkPen { public: enum class PenStyle { NoLine = 0, SolidLine = 1, DashLine = 2, DotLine = 3 }; explicit constexpr RkPen(PenStyle style = PenStyle::SolidLine) : penStyleVal{style} , penWidthVal{1} , penColorVal{RkColor()} { } constexpr RkPen(const RkColor &color) : penStyleVal{PenStyle::SolidLine} , penWidthVal{1} , penColorVal{color} { } friend constexpr bool operator==(const RkPen &pen1, const RkPen &pen2) { return pen1.penStyleVal == pen2.penStyleVal && pen1.penColorVal == pen2.penColorVal && pen1.penWidthVal == pen2.penWidthVal; } friend constexpr bool operator!=(const RkPen &pen1, const RkPen &pen2) { return pen1.penStyleVal != pen2.penStyleVal || pen1.penColorVal != pen2.penColorVal || pen1.penWidthVal != pen2.penWidthVal; } constexpr PenStyle style() const { return penStyleVal; } constexpr void setStyle(PenStyle style) { penStyleVal = style; } constexpr const RkColor& color() const { return penColorVal; } constexpr void setColor(const RkColor &color) { penColorVal = color; } constexpr int width() const { return penWidthVal; } constexpr void setWidth(int width) { penWidthVal = width; } private: PenStyle penStyleVal; int penWidthVal; RkColor penColorVal; }; #endif // RK_PEN_H geonkick-2.10.2/src/redkite/include/RkPlatform.h000066400000000000000000000031331451017372400214650ustar00rootroot00000000000000/** * File name: RkPlatform.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_PLATFORM_H #define RK_PLATFORM_H #include "Rk.h" #include "RkLog.h" #include struct RK_EXPORT RkWindowId { Window id; }; struct _DndClass; class RK_EXPORT RkNativeWindowInfo { public: RkNativeWindowInfo() : display{nullptr} , screenNumber{0} , window{0} , scaleFactor{1}{} ~RkNativeWindowInfo() = default; Display* display; int screenNumber; Window window; double scaleFactor; }; RkNativeWindowInfo RK_EXPORT rk_from_native_x11(Display* display, int screenNumber, Window window); RkWindowId RK_EXPORT rk_id_from_x11(Window window); #endif // RK_PLATFORM_H geonkick-2.10.2/src/redkite/include/RkPoint.h000066400000000000000000000034521451017372400207760ustar00rootroot00000000000000/** * File name: RkPoint.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_POINT_H #define RK_POINT_H #include "Rk.h" class RK_EXPORT RkPoint { public: constexpr RkPoint() : xValue{0}, yValue{0} { } constexpr RkPoint(int x, int y) : xValue{x}, yValue{y} { } friend constexpr bool operator==(const RkPoint &p1, const RkPoint &p2) { return p1.xValue == p2.xValue && p1.yValue == p2.yValue; } friend constexpr bool operator!=(const RkPoint &p1, const RkPoint &p2) { return p1.xValue != p2.xValue || p1.yValue != p2.yValue; } constexpr int x() const { return xValue; } constexpr void setX(int x) { xValue = x; } constexpr int y() const { return yValue; } constexpr void setY(int y) { yValue = y; } private: int xValue; int yValue; }; #endif // RK_POINT_H geonkick-2.10.2/src/redkite/include/RkProgressBar.h000066400000000000000000000031601451017372400221320ustar00rootroot00000000000000/** * File name: RkProgressBar.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_PROGRESS_BAR_H #define RK_PROGRESS_BAR_H #include "RkWidget.h" class RK_EXPORT RkProgressBar : public RkWidget { public: explicit RkProgressBar(RkWidget *parent); int beginValue() const; void setBeginValue(int value); int endValue() const; void setEndValue(int value); void setRange(int begin, int end); int value() const; Rk::Orientation orientation() const; void setOrientation(Rk::Orientation orientation); void setValue(int value); void reset(); void setProgressColor(const RkColor &color); protected: virtual void paintEvent(RkPaintEvent* event) override; private: RK_DISABLE_COPY(RkProgressBar); RK_DISABLE_MOVE(RkProgressBar); RK_DELCATE_IMPL_PTR(RkProgressBar); }; #endif // RK_PROGRESS_BAR_H geonkick-2.10.2/src/redkite/include/RkRealPoint.h000066400000000000000000000031761451017372400216050ustar00rootroot00000000000000/** * File name: RkRealPoint.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_REAL_POINT_H #define RK_REAL_POINT_H #include "RkPoint.h" class RK_EXPORT RkRealPoint { public: constexpr RkRealPoint() : xValue{0}, yValue{0} { } constexpr RkRealPoint(const RkPoint &p) : xValue(p.x()), yValue(p.y()) { } constexpr RkRealPoint(rk_real x, rk_real y) : xValue{x}, yValue{y} { } constexpr rk_real x() const { return xValue; } constexpr void setX(rk_real x) { xValue = x; } constexpr rk_real y() const { return yValue; } constexpr void setY(rk_real y) { yValue = y; } private: rk_real xValue; rk_real yValue; }; #endif // RK_REAL_POINT_H geonkick-2.10.2/src/redkite/include/RkRect.h000066400000000000000000000112051451017372400205750ustar00rootroot00000000000000/** * File name: RkRect.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_RECT_H #define RK_RECT_H #include "RkSize.h" #include "RkPoint.h" class RK_EXPORT RkRect { public: constexpr RkRect() { } constexpr RkRect(int x, int y, int width, int height) : rectTopLeft(x, y) , rectBottomRight(x + width, y + height) { } constexpr RkRect(const RkPoint &topLeft, const RkSize &size) : rectTopLeft{topLeft} , rectBottomRight(topLeft.x() + size.width(), topLeft.y() + size.height()) { } constexpr RkRect(const RkPoint &topLeft, const RkPoint &bottomRight) : rectTopLeft{topLeft} , rectBottomRight{bottomRight} { } friend constexpr bool operator==(const RkRect &r1, const RkRect &r2) { return r1.rectTopLeft == r2.rectTopLeft && r1.rectBottomRight == r2.rectBottomRight; } friend constexpr bool operator!=(const RkRect &r1, const RkRect &r2) { return r1.rectTopLeft != r2.rectTopLeft || r1.rectBottomRight != r2.rectBottomRight; } constexpr int width() const { if (rectBottomRight.x() >= rectTopLeft.x()) return rectBottomRight.x() - rectTopLeft.x(); else return rectTopLeft.x() - rectBottomRight.x(); } constexpr void setWidth(int width) { rectBottomRight.setX(rectTopLeft.x() + width); } constexpr int height() const { if (rectBottomRight.y() >= rectTopLeft.y()) return rectBottomRight.y() - rectTopLeft.y(); else return rectTopLeft.y() - rectBottomRight.y(); } constexpr void setHeight(int height) { rectBottomRight.setY(rectTopLeft.y() + height); } constexpr RkSize size() const { return RkSize(width(), height()); } constexpr void setSize(const RkSize &size) { rectBottomRight.setX(rectTopLeft.x() + size.width()); rectBottomRight.setY(rectTopLeft.y() + size.height()); } constexpr void setTopLeft(const RkPoint &p) { rectTopLeft = p; } constexpr void setTopRight(const RkPoint &p) { rectTopLeft.setY(p.y()); rectBottomRight.setX(p.x()); } constexpr void setBottomLeft(const RkPoint &p) { rectTopLeft.setX(p.x()); rectBottomRight.setY(p.y()); } constexpr void setBottomRight(const RkPoint &p) { rectBottomRight = p; } constexpr RkPoint topLeft() const { return rectTopLeft; } constexpr RkPoint topRight() const { return RkPoint(rectBottomRight.x(), rectTopLeft.y()) ; } constexpr RkPoint bottomLeft() const { return RkPoint(rectTopLeft.x(), rectBottomRight.y()) ; } constexpr RkPoint bottomRight(void) const { return rectBottomRight; } constexpr int left() const { return rectTopLeft.x(); } constexpr int top() const { return rectTopLeft.y(); } constexpr int bottom() const { return rectBottomRight.y(); } constexpr int right() const { return rectBottomRight.x(); } constexpr int area() const { return width() * height(); } private: RkPoint rectTopLeft; RkPoint rectBottomRight; }; #endif // RK_RECT_H geonkick-2.10.2/src/redkite/include/RkSize.h000066400000000000000000000036471451017372400206250ustar00rootroot00000000000000/** * File name: RkSize.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_SIZE_H #define RK_SIZE_H #include "Rk.h" class RK_EXPORT RkSize { public: constexpr RkSize() : widthValue{0}, heightValue{0} { } constexpr RkSize(int width, int height) : widthValue{width}, heightValue{height} { } friend constexpr bool operator==(const RkSize &s1, const RkSize &s2) { return s1.widthValue == s2.widthValue && s1.heightValue == s2.heightValue; } friend constexpr bool operator!=(const RkSize &s1, const RkSize &s2) { return s1.widthValue != s2.widthValue || s1.heightValue != s2.heightValue; } constexpr int width() const { return widthValue; } constexpr void setWidth(int width) { widthValue = width; } constexpr int height() const { return heightValue; } constexpr void setHeight(int height) { heightValue = height; } private: int widthValue; int heightValue; }; #endif // RK_SIZE_H geonkick-2.10.2/src/redkite/include/RkTimer.h000066400000000000000000000031211451017372400207560ustar00rootroot00000000000000/** * File name: RkTimer.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_TIMER_H #define RK_TIMER_H #include "Rk.h" #include "RkObject.h" class RkEventQueue; class RK_EXPORT RkTimer: public RkObject { public: explicit RkTimer(RkObject *parent, int interval = 0); virtual ~RkTimer(); RK_DECL_ACT(timeout, timeout(), RK_ARG_TYPE(), RK_ARG_VAL()); void start(); void stop(); bool started() const; long int interval() const; void setInterval(int val); bool isTimeout() const; void callTimeout(); protected: long int getCurrentTime() const; private: RK_DISABLE_COPY(RkTimer); RK_DISABLE_MOVE(RkTimer); long int timerInterval; bool timerStarted; long int lastTime; }; #endif // RK_TIMER_H geonkick-2.10.2/src/redkite/include/RkTransition.h000066400000000000000000000077061451017372400220450ustar00rootroot00000000000000/** * File name: RkTransition.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_TRANSITION_H #define RK_TRANSITION_H #include "RkObject.h" #include "RkTimer.h" #include class RK_EXPORT RkTransition : public RkObject { public: enum class TransitionDirection: int { TransitionForward = 0, TransitionBackward = 1, TransitionRepeat = 2, TransitionCycle = 3 }; enum class TransitionType: int { TransitionLinear = 0, TransitionAccelerated = 1, }; explicit RkTransition(RkObject *parent = nullptr) : RkObject{parent} , transitionDirection{TransitionDirection::TransitionForward} , transitionType{TransitionType::TransitionLinear} , transitionTimer{new RkTimer(this, 30)} , startValue{0} , endValue{0} , currentValue{0} , speedValue{1} { RK_ACT_BIND(transitionTimer, timeout, RK_ARG_TYPE(), this, onTimeout()); } virtual ~RkTransition() = default; void setRange(int start, int end) { startValue = start; endValue = end; } void setCallback(const std::function &cb) { actionCallback = cb; } void start() { currentValue = startValue; transitionTimer->start(); } void stop() { transitionTimer->stop(); } void setDirection(TransitionDirection direction) { transitionDirection = direction; } void setTransitionType(TransitionType type) { transitionType = type; } void setSpeed(int speed) { if (speed < 1) speedValue = 1; else if (speed > 10) speedValue = 10; else speedValue = speed; } private: void onTimeout() { if (actionCallback) { int tempVal = currentValue; auto curVal = calculateCurrentValue(); if (curVal != tempVal) actionCallback(curVal); } } int calculateCurrentValue() { if (transitionType == TransitionType::TransitionLinear) { currentValue += std::fabs(endValue - startValue) * ((double)speedValue / 50); if (currentValue >= endValue) { currentValue = endValue; transitionTimer->stop(); } } return currentValue; } std::function actionCallback; TransitionDirection transitionDirection; TransitionType transitionType; RkTimer *transitionTimer; int startValue; int endValue; double currentValue; int speedValue; }; #endif // RK_TRANSITION_H geonkick-2.10.2/src/redkite/include/RkVariant.h000066400000000000000000000021311451017372400213020ustar00rootroot00000000000000/** * File name: RkVariant.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_VARIANT_H #define RK_VARIANT_H #include "Rk.h" #include "RkColor.h" #include "RkSize.h" #include "RkFont.h" #include #include using RkVariant = std::variant; #endif // RK_VARIANT_H geonkick-2.10.2/src/redkite/include/RkWidget.h000066400000000000000000000142161451017372400211300ustar00rootroot00000000000000/** * File name: RkWidget.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_WIDGET_H #define RK_WIDGET_H #include "Rk.h" #include "RkObject.h" #include "RkEventQueue.h" #include "RkCanvas.h" #include "RkRect.h" #include "RkColor.h" #include "RkFont.h" class RkEvent; class RkCloseEvent; class RkKeyEvent; class RkMouseEvent; class RkWheelEvent; class RkDropEvent; class RkMoveEvent; class RkResizeEvent; class RkPaintEvent; class RkShowEvent; class RkHideEvent; class RkFocusEvent; class RkHoverEvent; class RkMain; struct RkWindowId; class RkNativeWindowInfo; class RK_EXPORT RkWidget: public RkObject, public RkCanvas { public: RK_CLASS_INFO(style_element, "RkWidget") RK_CLASS_INFO(style_class, "") RK_CLASS_INFO(style_id, "") explicit RkWidget(RkMain *mainApp, Rk::WindowFlags flags = Rk::WindowFlags::Widget); explicit RkWidget(RkMain *mainApp, const RkNativeWindowInfo &parent, Rk::WindowFlags flags = Rk::WindowFlags::Widget); explicit RkWidget(RkWidget *parent, Rk::WindowFlags flags = Rk::WindowFlags::Widget); virtual ~RkWidget(); Rk::WindowFlags windowFlags() const; void show(bool b = true); bool isShown() const; void hide(); void setTitle(const std::string &title); const std::string& title() const; const RkNativeWindowInfo* nativeWindowInfo() const; bool isClose() const; RkWidget* parentWidget() const; RkWindowId id() const; void setSize(int w, int h); void setSize(const RkSize &size); RkSize size() const; void setWidth(int w); int width() const; int minimumWidth() const; int maximumWidth() const; void setMinimumWidth(int width); void setMaximumWidth(int width); void setHeight(int h); int height() const; int minimumHeight() const; int maximumHeight() const; void setMinimumHeight(int height); void setMaximumHeight(int height); void setFixedWidth(int width); void setFixedHeight(int height); void setFixedSize(const RkSize &size); void setFixedSize(int width, int height); int x() const; void setX(int x); int y() const; void setY(int y); void setPosition(int x, int y); void setPosition(const RkPoint &p); RkPoint position() const; void setBorderWidth(int width); int borderWidth() const; void setBackgroundColor(int red, int green, int blue); void setBackgroundColor(const RkColor &color); const RkColor& background() const; void setBorderColor(int red, int green, int blue); const RkColor& borderColor() const; void setTextColor(const RkColor &color); const RkColor& textColor() const; const RkColor& color() const; void setColor(const RkColor &color); const RkFont& font() const; void setFont(const RkFont &font); const RkCanvasInfo *getCanvasInfo() const override; RkRect rect() const; void update(); void close(); Rk::Modality modality() const; bool isModal() const; void setWidgetAttribute(Rk::WidgetAttribute attribute); void clearWidgetAttribute(Rk::WidgetAttribute attribute); Rk::WidgetAttribute widgetAttributes() const; void enableInput(); void disableInput(); bool isInputEnabled() const; RkWidget* getTopWidget(); bool isTopWindow() const; void enableGrabKey(bool b); bool grabKeyEnabled() const; void propagateGrabKey(bool b); bool propagateGrabKeyEnabled() const; void setFocus(bool b = true); bool hasFocus() const; void setPointerShape(Rk::PointerShape shape); Rk::PointerShape pointerShape() const; void setScaleFactor(double factor); double scaleFactor() const; bool pointerIsOverWindow() const; bool isChild(RkWidget *widget); protected: RK_DELCATE_IMPL_PTR(RkWidget); RkWidget(RkWidget *parent, std::unique_ptr impl); void event(RkEvent *event) override; virtual void closeEvent(RkCloseEvent *event); virtual void keyPressEvent(RkKeyEvent *event); virtual void keyReleaseEvent(RkKeyEvent *event); virtual void shortcutEvent(RkKeyEvent *event); virtual void mouseMoveEvent(RkMouseEvent *event); virtual void mouseButtonPressEvent(RkMouseEvent *event); virtual void mouseButtonReleaseEvent(RkMouseEvent *event); virtual void mouseDoubleClickEvent(RkMouseEvent *event); virtual void wheelEvent(RkWheelEvent *event); virtual void dropEvent(RkDropEvent *event); virtual void moveEvent(RkMoveEvent *event); virtual void resizeEvent(RkResizeEvent *event); virtual void paintEvent(RkPaintEvent *event); virtual void showEvent(RkShowEvent *event); virtual void hideEvent(RkHideEvent *event); virtual void focusEvent(RkFocusEvent *event); virtual void hoverEvent(RkHoverEvent *event); private: RK_DISABLE_COPY(RkWidget); RK_DISABLE_MOVE(RkWidget); friend class RkEventQueue; }; #endif // RK_WIDGET_H geonkick-2.10.2/src/redkite/include/impl/000077500000000000000000000000001451017372400201745ustar00rootroot00000000000000geonkick-2.10.2/src/redkite/include/impl/RkButtonImpl.h000066400000000000000000000035761451017372400227520ustar00rootroot00000000000000/** * File name: RkButtonImpl.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_LABEL_IMPL_H #define RK_LABEL_IMPL_H #include "RkWidgetImpl.h" #include "RkButton.h" #include "RkImageImpl.h" #include "RkPainter.h" class RkButton::RkButtonImpl : public RkWidget::RkWidgetImpl { public: RkButtonImpl(RkButton *interface, RkWidget *parent = nullptr); virtual ~RkButtonImpl() = default; void setText(const RkString &text); RkString text() const; void setImage(const RkImage &img, RkButton::State type); bool isPressed() const; void setPressed(bool pressed); ButtonType type() const; void setType(ButtonType type); void drawButton(RkPainter &painter); void setEmphasize(bool b); protected: void applyEffect(RkImage &img); void updateButtonState(); private: RK_DECALRE_INTERFACE_PTR(RkButton); ButtonType buttonType; bool is_pressed; std::array buttonImages; RkButton::State buttonState; bool isEmphasizeEnabled; RkString buttonText; }; #endif // RK_LABEL_H geonkick-2.10.2/src/redkite/include/impl/RkCairoGraphicsBackend.h000066400000000000000000000040701451017372400246310ustar00rootroot00000000000000/** * File name: RkCairoGraphicsBackend.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_CAIRO_GRAPHICS_BACKEND_H #define RK_CAIRO_GRAPHICS_BACKEND_H #include "Rk.h" #include "RkImage.h" #include "RkPoint.h" #include "RkPen.h" #include "RkRect.h" #include "RkFont.h" #include class RkCanvas; class RkCairoGraphicsBackend { public: RkCairoGraphicsBackend(RkCanvas* canvas); ~RkCairoGraphicsBackend(); void drawText(const std::string &text, int x, int y); void drawImage(const std::string &file, int x, int y); void drawImage(const RkImage &image, int x, int y); void drawEllipse(const RkPoint& p, int width, int height); void drawLine(const RkPoint &p1, const RkPoint &p2); void drawRect(const RkRect &rect); void drawPolyLine(const std::vector &points); void fillRect(const RkRect &rect, const RkColor &color); void applyAlpha(int alpha); void setPen(const RkPen &pen); void setFont(const RkFont &font); int getTextWidth(const std::string &text) const; void translate(const RkPoint &offset); void rotate(rk_real angle); protected: cairo_t* context() const; private: cairo_t* cairoContext; }; #endif // RK_CAIRO_GRAPHICS_BACKEND_H geonkick-2.10.2/src/redkite/include/impl/RkCairoImageBackendCanvas.h000066400000000000000000000034151451017372400252510ustar00rootroot00000000000000/** * File name: RkImageBackendCanvas.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_CAIRO_IMAGE_BACKEND_CANVAS_H #define RK_CAIRO_IMAGE_BACKEND_CANVAS_H #include "RkImage.h" #include class RkCairoImageBackendCanvas { public: RkCairoImageBackendCanvas(const RkSize &size, RkImage::Format format, const unsigned char *data); ~RkCairoImageBackendCanvas(); const RkSize& size() const; bool isNull() const; unsigned char* data(); std::vector dataCopy() const; RkCanvasInfo *getCanvasInfo() const; void fill(const RkColor &color); protected: cairo_format_t toCairoFormat(RkImage::Format format) const; int pixelLength(RkImage::Format format) const; private: std::unique_ptr canvasInfo; std::vector imageData; RkSize imageSize; }; #endif // RK_CAIRO_IMAGE_BACKEND_CANVAS_H geonkick-2.10.2/src/redkite/include/impl/RkCanvasInfo.h000066400000000000000000000022121451017372400226660ustar00rootroot00000000000000/** * File name: RkCanvasInfo.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_CANVAS_INFO_H #define RK_CANVAS_INFO_H #include "Rk.h" #ifdef RK_GRAPHICS_CAIRO_BACKEND #include #include struct RkCanvasInfo { cairo_surface_t* cairo_surface; }; #else #error No graphics backend defined #endif #endif // RK_CANVAS_INFO_H geonkick-2.10.2/src/redkite/include/impl/RkEventQueueImpl.h000066400000000000000000000070141451017372400235540ustar00rootroot00000000000000/** * File name: RkEventQueueImpl.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_EVENT_QUEUE_IMPL_H #define RK_EVENT_QUEUE_IMPL_H #include "RkEventQueue.h" #include "RkPlatform.h" #include "RkEvent.h" #include "RkShortcut.h" #ifdef RK_OS_WIN class RkEventQueueWin; #elif RK_OS_MAC class RkEventQueueMac; #else // X11 class RkEventQueueX; #endif class RkEventQueue::RkEventQueueImpl { public: explicit RkEventQueueImpl(RkEventQueue* interface); virtual ~RkEventQueueImpl(); bool objectExists(RkObject *t) const; void addObject(RkObject *obj); void addShortcut(RkObject *obj, Rk::Key key, Rk::KeyModifiers modifier = Rk::KeyModifiers::NoModifier); void removeShortcut(RkObject *obj, Rk::Key key, Rk::KeyModifiers modifier = Rk::KeyModifiers::NoModifier); void removeObject(RkObject *obj); RkWidget* findWidget(const RkWindowId &id) const; void removeObjEvents(RkObject *obj); void postEvent(RkObject *obj, std::unique_ptr event); void processEvent(RkObject *obj, RkEvent *event); void processEvents(); void postAction(std::unique_ptr act); void processActions(); void subscribeTimer(RkTimer *timer); void unsubscribeTimer(RkTimer *timer); void processTimers(); void clearEvents(const RkObject *obj); void clearActions(const RkObject *obj); RkObject* findObjectByName(const std::string &name) const; void setScaleFactor(double factor); protected: void processShortcuts(RkKeyEvent *event); void processPopups(RkWidget *widget, RkEvent* event); void removeObjectShortcuts(RkObject *obj); bool isTopWidget(RkObject *obj) const; private: RK_DECALRE_INTERFACE_PTR(RkEventQueue); RK_DISABLE_COPY(RkEventQueueImpl); RK_DISABLE_MOVE(RkEventQueueImpl); std::unordered_set objectsList; std::unordered_map windowIdsMap; std::unordered_map> shortcutsList; std::vector>> eventsQueue; std::vector> actionsQueue; std::unordered_set timersList; std::unordered_map popupList; std::mutex actionsQueueMutex; #ifdef RK_OS_WIN std::unique_ptr platformEventQueue; #elif RK_OS_MAC std::unique_ptr platformEventQueue; #else // X11 std::unique_ptr platformEventQueue; #endif }; #endif // RK_EVENT_QUEUE_IMPL_H geonkick-2.10.2/src/redkite/include/impl/RkImageImpl.h000066400000000000000000000037321451017372400225130ustar00rootroot00000000000000/** * File name: RkImageImpl.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkImage.h" #ifdef RK_GRAPHICS_CAIRO_BACKEND class RkCairoImageBackendCanvas; #else #error No graphics backend defined. #endif class RkImage::RkImageImpl { public: RkImageImpl(RkImage *interface, int width, int height, const unsigned char *data, RkImage::Format format = RkImage::Format::ARGB32); virtual ~RkImageImpl(); const RkCanvasInfo *getCanvasInfo() const; unsigned char* data() const; std::vector dataCopy() const; Format format() const; int width() const; int height() const; RkSize size() const; bool isNull() const; void createImage(const RkSize &size, RkImage::Format format, const unsigned char *data); void fill(const RkColor &color); private: RK_DECALRE_INTERFACE_PTR(RkImage); RkImage::Format imageFormat; #ifdef RK_GRAPHICS_CAIRO_BACKEND std::unique_ptr imageBackendCanvas; #else #error No graphics backend defined #endif }; geonkick-2.10.2/src/redkite/include/impl/RkLabelImpl.h000066400000000000000000000055561451017372400225160ustar00rootroot00000000000000/** * File name: RkLabelImpl.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_LABEL_IMPL_H #define RK_LABEL_IMPL_H /** * File name: RkLabelImpl.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_LABEL_IMPL_H #define RK_LABEL_IMPL_H #include "RkWidgetImpl.h" #include "RkLabel.h" #include "RkImage.h" class RkLabel::RkLabelImpl : public RkWidget::RkWidgetImpl { public: RkLabelImpl(RkLabel *interface, const std::string &text, RkWidget *parent = nullptr); virtual ~RkLabelImpl(); void setText(const std::string &text); std::string text() const; void setImage(const RkImage &image); void drawLabel(); private: RK_DECALRE_INTERFACE_PTR(RkLabel); std::string labelText; RkImage labelImage; }; #endif // RK_LABEL_H #include "RkWidgetImpl.h" #include "RkLabel.h" #include "RkImage.h" class RkLabel::RkLabelImpl : public RkWidget::RkWidgetImpl { public: RkLabelImpl(RkLabel *interface, const std::string &text, RkWidget *parent = nullptr); virtual ~RkLabelImpl(); void setText(const std::string &text); std::string text() const; void setImage(const RkImage &image); void drawLabel(); private: RK_DECALRE_INTERFACE_PTR(RkLabel); std::string labelText; RkImage labelImage; }; #endif // RK_LABEL_H geonkick-2.10.2/src/redkite/include/impl/RkLineEditImpl.h000066400000000000000000000050461451017372400231660ustar00rootroot00000000000000/** * File name: RkLineEditImpl.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_LINE_EDIT_IMPL_H #define RK_LINE_EDIT_IMPL_H #include "RkLineEdit.h" #include "RkWidgetImpl.h" class RkTimer; class RkLineEdit::RkLineEditImpl : public RkWidget::RkWidgetImpl, public RkObject { public: RkLineEditImpl(RkLineEdit *interface, RkWidget *parent = nullptr, const std::string &text = std::string()); virtual ~RkLineEditImpl(); void init(); void setText(const std::string &text); void addText(const std::string& text); void removeText(int n, bool after); std::string text() const; void moveCursorRight(int n = 1); void moveCursorLeft(int n = 1); void moveCursorToFront(); void moveCursorToBack(); void moveSelectionToFront(); void moveSelectionToBack(); void enableSelectionMode(bool b); bool selectionMode() const; std::string textTo(int index) const; bool isCursorHidden() const; void showCursor(bool b = true); int getCursorIndex() const; int selectionStart() const; int selectionEnd() const; std::string getText(int pos, size_t n) const; void selectAll(); void deleteSelection(); void paintEvent(RkPaintEvent *event); void updateSize(); bool hasEditFocus() const; protected: void onCursorTimeout(); private: RK_DECALRE_INTERFACE_PTR(RkLineEdit); std::string editedText; int cursorIndex; int selectionIndex; bool isSelectionMode; RkTimer *cursorTimer; bool isShowCursor; decltype(std::chrono::system_clock::now()) lastCahnges; RkRect contentsRect; int beginX; int endX; }; #endif // RK_LABEL_H geonkick-2.10.2/src/redkite/include/impl/RkListImpl.h000066400000000000000000000026501451017372400224020ustar00rootroot00000000000000/** * File name: RkListImpl.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_LIST_IMPL_H #define RK_LIST_IMPL_H #include "RkList.h" #include "RkWidgetImpl.h" #include "RkPainter.h" class RkModel; class RkList::RkListImpl : public RkWidget::RkWidgetImpl { public: RkListImpl(RkList *interface, RkWidget *parent, RkModel *model); virtual ~RkListImpl() = default; void drawList(RkPainter &painter); RkModel* getModel() const; size_t getIndex(int y) const; private: RK_DECALRE_INTERFACE_PTR(RkList); RkModel* listModel; int topMargin; int leftMargin; }; #endif // RK_LIST_IMPL_H geonkick-2.10.2/src/redkite/include/impl/RkMainImpl.h000066400000000000000000000033371451017372400223560ustar00rootroot00000000000000/** * File name: RkMainXWin.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_MAIN_IMPL_H #define RK_MAIN_IMPL_H #include "RkMain.h" class RkWidget; #ifdef RK_OS_WIN class RkEventQueueWin; #elif RK_OS_MAC #else class RkEventQueueX; #endif // RK_WIN_OS class RkMain::RkMainImpl { public: RkMainImpl(RkMain *interface); RkMainImpl(RkMain *interface, int argc, char **argv); virtual ~RkMainImpl(); RkMainImpl(const RkMainImpl &other) = delete; RkMainImpl& operator=(const RkMainImpl &other) = delete; RkMainImpl(RkMainImpl &&other) = delete; RkMainImpl& operator=(RkMainImpl &&other) = delete; bool setTopLevelWidget(RkWidget* widget); RkWidget* topLevelWidget(void); RkEventQueue* getEventQueue() const; int exec(bool block = true); private: RK_DECALRE_INTERFACE_PTR(RkMain); RkWidget* topWidget; std::unique_ptr eventQueue; }; #endif // RK_MAIN_XWIN_H geonkick-2.10.2/src/redkite/include/impl/RkModelImpl.h000066400000000000000000000024641451017372400225320ustar00rootroot00000000000000/** * File name: RkModelImpl.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_MODEL_IMPL_H #define RK_MODEL_IMPL_H #include "RkModel.h" #include "RkObjectImpl.h" class RkModel::RkModelImpl : public RkObject::RkObjectImpl { public: RkModelImpl(RkModel* interface, RkObject* parent); virtual ~RkModelImpl() = default; bool isItemSelected(size_t index) const; void selectItem(size_t index); private: RK_DECALRE_INTERFACE_PTR(RkModel); size_t selectedIndex; }; #endif // RK_MODEL_IMPL_H geonkick-2.10.2/src/redkite/include/impl/RkObjectImpl.h000066400000000000000000000043661451017372400227030ustar00rootroot00000000000000/** * File name: RkObjectImpl.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_OBJECT_IMPL_H #define RK_OBJECT_IMPL_H #include "RkObject.h" class RkEventQueue; class RkObject::RkObjectImpl { public: explicit RkObjectImpl(RkObject* interface, RkObject* parent, Rk::ObjectType type = Rk::ObjectType::Object); virtual ~RkObjectImpl(); void removeChildrens(); RkObject* parent() const; const std::unordered_set& getChildren() const; void setEventQueue(RkEventQueue *queue); RkEventQueue* getEventQueue() const; void addObserver(std::unique_ptr ob); void removeObservers(RkObject *obj); const std::vector>& observers() const; void addBoundObject(RkObject *obj); void removeBoundObject(RkObject *obj); void addChild(RkObject* child); void removeChild(RkObject* child); Rk::ObjectType getObjectType() const; void setName(const std::string &name); std::string name() const; private: RK_DECALRE_INTERFACE_PTR(RkObject); Rk::ObjectType objectType; RkObject *parentObject; RkEventQueue *eventQueue; std::unordered_set objectChildren; std::vector> observersList; std::vector boundObjects; std::string objectName; }; #endif // RK_OBJECT_IMPL_H geonkick-2.10.2/src/redkite/include/impl/RkPainterImpl.h000066400000000000000000000043551451017372400230750ustar00rootroot00000000000000/** * File name: RkPainterImpl.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_PAINTER_IMPL_H #define RK_PAINTER_IMPL_H #include "RkPainter.h" #ifdef RK_GRAPHICS_CAIRO_BACKEND class RkCairoGraphicsBackend; #else #error No graphics backend defined #endif class RkPainter::RkPainterImpl { public: RkPainterImpl(RkPainter* interface, RkCanvas* canvas); ~RkPainterImpl(); void drawText(const std::string &text, int x, int y); void drawImage(const std::string &file, int x, int y); void drawImage(const RkImage &image, int x, int y); void drawEllipse(const RkPoint& p, int width, int height); void drawLine(const RkPoint &p1, const RkPoint &p2); void drawRect(const RkRect &rect); void drawPolyline(const std::vector &points); void fillRect(const RkRect &rect, const RkColor &color); void applyAlpha(int alpha); const RkPen& pen() const; void setPen(const RkPen &pen); const RkFont& font() const; void setFont(const RkFont &font); void translate(const RkPoint &offset); void rotate(rk_real angle); int getTextWidth(const std::string &text) const; private: RK_DECALRE_INTERFACE_PTR(RkPainter); #ifdef RK_GRAPHICS_CAIRO_BACKEND std::unique_ptr backendGraphics; #else #error No graphics backend defined #endif RkPen painterPen; RkFont painterFont; }; #endif // RK_PAINTER_IMPL_H geonkick-2.10.2/src/redkite/include/impl/RkProgressBarImpl.h000066400000000000000000000034121451017372400237150ustar00rootroot00000000000000/** * File name: RkProgressBarImpl.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_PROGRESS_BAR_IMPL_H #define RK_PROGRESS_BAR_IMPL_H #include "RkWidgetImpl.h" #include "RkProgressBar.h" class RkProgressBar::RkProgressBarImpl : public RkWidget::RkWidgetImpl { public: RkProgressBarImpl(RkProgressBar *interface, RkWidget *parent = nullptr); virtual ~RkProgressBarImpl(); int beginValue() const; void setBeginValue(int val); int endValue() const; void setEndValue(int val); Rk::Orientation orientation() const; void setOrientation(Rk::Orientation orientation); int value() const; void setValue(int val); void setProgressColor(const RkColor &color); void drawProgressBar(); private: RK_DECALRE_INTERFACE_PTR(RkProgressBar); int beginVal; int endVal; int currentVal; RkColor progressColor; Rk::Orientation progressOrientation; }; #endif // RK_PROGRESS_BAR_H geonkick-2.10.2/src/redkite/include/impl/RkShortcut.h000066400000000000000000000037061451017372400224630ustar00rootroot00000000000000/** * File name: RkShortcut.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_SHORTCUT_H #define RK_SHORTCUT_H #include "Rk.h" class RkObject; class RkShortcut { public: explicit RkShortcut(Rk::Key key, Rk::KeyModifiers modifier = Rk::KeyModifiers::NoModifier) : shortcutKey{key} , shortcutModifier{modifier} {} virtual ~RkShortcut() = default; void addObject(RkObject *obj) { shortcutObjects.insert(obj); } void removeObject(RkObject *obj) { auto res = shortcutObjects.find(obj); if (res != shortcutObjects.end()) shortcutObjects.erase(obj); } bool hasObject(RkObject *obj) const { return shortcutObjects.find(obj) != shortcutObjects.end(); } Rk::Key key() const { return shortcutKey; } Rk::KeyModifiers modifiers() const { return shortcutModifier; } std::unordered_set objects() { return shortcutObjects; }; private: Rk::Key shortcutKey; Rk::KeyModifiers shortcutModifier; std::unordered_set shortcutObjects; }; #endif // RK_SHORTCUT_H geonkick-2.10.2/src/redkite/include/impl/RkWidgetImpl.h000066400000000000000000000113721451017372400227130ustar00rootroot00000000000000/** * File name: RkWidgetImpl.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_WIDGET_IMPL_H #define RK_WIDGET_IMPL_H #include "RkWidget.h" #include "RkObjectImpl.h" #ifdef RK_OS_WIN class RkWindowWin; #elif RK_OS_MAC #else class RkWindowX; #endif // RK_WIN_OS class RkWidget::RkWidgetImpl : public RkObject::RkObjectImpl { public: explicit RkWidgetImpl(RkWidget* interface, RkWidget* parent = nullptr, Rk::WindowFlags flags = Rk::WindowFlags::Widget, bool isTopWindow = false); explicit RkWidgetImpl(RkWidget* interface, const RkNativeWindowInfo &parent, Rk::WindowFlags flags = Rk::WindowFlags::Widget, bool isTopWindow = false); RkWidgetImpl(const RkWidget &other) = delete; RkWidgetImpl& operator=(const RkWidgetImpl &other) = delete; RkWidgetImpl(RkWidgetImpl &&other) = delete; RkWidgetImpl& operator=(RkWidgetImpl &&other) = delete; virtual ~RkWidgetImpl(); Rk::WindowFlags windowFlags() const; void show(bool b); bool isShown() const; void setTitle(const std::string &title); const std::string& title() const; const RkNativeWindowInfo* nativeWindowInfo() const; bool isClose() const; RkWindowId id() const; void event(RkEvent *event); void setSize(const RkSize &size); RkSize size() const; int minimumWidth() const; int maximumWidth() const; int minimumHeight() const; int maximumHeight() const; void setMinimumWidth(int width); void setMaximumWidth(int widht); void setMinimumHeight(int heigth); void setMaximumHeight(int heigth); void setPosition(const RkPoint &position); RkPoint position() const; void setBorderWidth(int width); int borderWidth() const; void setBorderColor(const RkColor &color); const RkColor &borderColor() const; void setBackgroundColor(const RkColor &color); const RkColor& background() const; RkRect rect() const; const RkCanvasInfo* getCanvasInfo() const; void update(); static Rk::WidgetAttribute defaultWidgetAttributes(); Rk::Modality modality() const; void setWidgetAttribute(Rk::WidgetAttribute attribute); void clearWidgetAttribute(Rk::WidgetAttribute attribute); Rk::WidgetAttribute getWidgetAttributes() const; void setFocus(bool b); bool hasFocus() const; void setTextColor(const RkColor &color); const RkColor& textColor() const; const RkColor& color() const; void setColor(const RkColor &color); const RkFont& font() const; void setFont(const RkFont &font); void setPointerShape(Rk::PointerShape shape); Rk::PointerShape pointerShape() const; void enableGrabKey(bool b); bool grabKeyEnabled() const; void propagateGrabKey(bool b); bool propagateGrabKeyEnabled() const; bool pointerIsOverWindow() const; void setScaleFactor(double factor); double scaleFactor() const; private: RK_DECALRE_INTERFACE_PTR(RkWidget); #ifdef RK_OS_WIN std::unique_ptr platformWindow; #elif RK_OS_MAC #else std::unique_ptr platformWindow; #endif // RK_WIN_OS std::string widgetTitle; // std::list widgetChildren; bool widgetClosed; RkSize widgetMinimumSize; RkSize widgetMaximumSize; RkSize widgetSize; RkColor widgetBackground; Rk::WidgetAttribute widgetAttributes; Rk::Modality widgetModality; RkColor widgetTextColor; RkColor widgetDrawingColor; RkFont widgetFont; Rk::PointerShape widgetPointerShape; bool isWidgetSown; bool isGrabKeyEnabled; bool isPropagateGrabKey; }; #endif // RK_WIDGET_IMPL_H geonkick-2.10.2/src/redkite/include/impl/platforms/000077500000000000000000000000001451017372400222035ustar00rootroot00000000000000geonkick-2.10.2/src/redkite/include/impl/platforms/xwin/000077500000000000000000000000001451017372400231705ustar00rootroot00000000000000geonkick-2.10.2/src/redkite/include/impl/platforms/xwin/RkEventQueueX.h000066400000000000000000000037731451017372400260660ustar00rootroot00000000000000/** * File name: RkQueueX.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_EVENT_QUEUE_X_H #define RK_EVENT_QUEUE_X_H #include "RkEvent.h" #include "RkPlatform.h" #include #include class RkEvent; class RkObject; class RkEventQueueX { public: RkEventQueueX(); ~RkEventQueueX(); bool pending() const; void setDisplay(Display *display); Display* display() const; std::vector>> getEvents(); void setScaleFactor(double factor); protected: std::unique_ptr getButtonPressEvent(XEvent *e); std::unique_ptr getMouseMove(XEvent *e); std::unique_ptr getKeyEvent(XEvent *e); std::unique_ptr getFocusEvent(XEvent *e); void updateModifiers(Rk::Key key, RkEvent::Type type); Rk::Key fromKeysym(int keycode) const; static std::string decodeUri(const std::string &dropFilePath); private: RK_DISABLE_COPY(RkEventQueueX); RK_DISABLE_MOVE(RkEventQueueX); Display* xDisplay; std::chrono::system_clock::time_point lastTimePressed; mutable int keyModifiers; double scaleFactor; }; #endif // RK_EVENT_QUEUE_X_H geonkick-2.10.2/src/redkite/include/impl/platforms/xwin/RkWindowX.h000066400000000000000000000064151451017372400252430ustar00rootroot00000000000000/** * File name: RkWindowX.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef RK_WINDOW_X_H #define RK_WINDOW_X_H #include "Rk.h" #include "RkPlatform.h" #include "RkSize.h" #include "RkPoint.h" #include "RkColor.h" #include struct RkCanvasInfo; class RkWindowX { public: explicit RkWindowX(const RkNativeWindowInfo *parent, Rk::WindowFlags flags = Rk::WindowFlags::Widget, bool isTop = false); explicit RkWindowX(const RkNativeWindowInfo &parent, Rk::WindowFlags flags = Rk::WindowFlags::Widget, bool isTop = false); ~RkWindowX(); Rk::WindowFlags flags() const; bool init(); void show(bool b); const RkNativeWindowInfo* nativeWindowInfo() const; void setTitle(const std::string &title); Display* display() const; RkSize size() const; void setSize(const RkSize &size); RkPoint position() const; void setPosition(const RkPoint &position); RkWindowId id() const; void setBorderWidth(int width); int borderWidth() const; void setBorderColor(const RkColor &color); const RkColor& borderColor() const; void setBackgroundColor(const RkColor &color); const RkColor& background() const; void resizeCanvas(); const RkCanvasInfo* getCanvasInfo() const; void update(); void setFocus(bool b); bool hasFocus() const; void setPointerShape(Rk::PointerShape shape); bool pointerIsOverWindow() const; void setScaleFactor(double factor); double getScaleFactor() const; protected: bool openDisplay(); bool isWindowCreated() const; bool hasParent() const; void createCanvasInfo(); void freeCanvasInfo(); private: RK_DISABLE_COPY(RkWindowX); RK_DISABLE_MOVE(RkWindowX); RkNativeWindowInfo parentWindowInfo; Rk::WindowFlags windowFlags; Display *xDisplay; int screenNumber; Window xWindow; Atom deleteWindowAtom; mutable RkPoint windowPosition; mutable RkSize windowSize; int winBorderWidth; RkColor winBorderColor; RkColor winBackgroundColor; std::unique_ptr canvasInfo; std::unique_ptr windowInfo; XVisualInfo visualInfo; double scaleFactor; bool isTopWindow; }; #endif // RK_WIDGET_XWIN_H geonkick-2.10.2/src/redkite/src/000077500000000000000000000000001451017372400163775ustar00rootroot00000000000000geonkick-2.10.2/src/redkite/src/RkButton.cpp000066400000000000000000000063501451017372400206570ustar00rootroot00000000000000/** * File name: RkButton.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkButton.h" #include "RkButtonImpl.h" #include "RkPainter.h" #include "RkEvent.h" RkButton::RkButton(RkWidget *parent) : RkWidget(parent, std::make_unique(this, parent)) , impl_ptr{static_cast(o_ptr.get())} { if (parent) setBackgroundColor(parent->background()); } void RkButton::setText(const std::string &text) { impl_ptr->setText(text); update(); } std::string RkButton::text() const { return impl_ptr->text(); } void RkButton::setImage(const RkImage &img, RkButton::State state) { impl_ptr->setImage(img, state); update(); } bool RkButton::isPressed() const { return impl_ptr->isPressed(); } void RkButton::setPressed(bool pressed) { if (isPressed() != pressed) { impl_ptr->setPressed(pressed); update(); } } void RkButton::setCheckable(bool b) { if (isCheckable() != b) { setType(b ? ButtonType::ButtonCheckable : ButtonType::ButtonUncheckable); update(); } } bool RkButton::isCheckable() const { return type() == ButtonType::ButtonCheckable; } RkButton::ButtonType RkButton::type() const { return impl_ptr->type(); } void RkButton::setType(RkButton::ButtonType type) { if (impl_ptr->type() != type) { impl_ptr->setType(type); update(); } } void RkButton::mouseButtonPressEvent(RkMouseEvent *event) { RK_UNUSED(event); if (type() == ButtonType::ButtonCheckable) { setPressed(!isPressed()); action toggled(isPressed()); } else if ((type() == ButtonType::ButtonUncheckable || type() == ButtonType::ButtonPush) && !isPressed()) { setPressed(true); action toggled(true); } action pressed(); } void RkButton::mouseButtonReleaseEvent(RkMouseEvent *event) { if (type() == ButtonType::ButtonPush) { setPressed(false); action toggled(false); } action released(); } void RkButton::hoverEvent(RkHoverEvent *event) { impl_ptr->setEmphasize(event->isHover()); update(); } void RkButton::paintEvent(RkPaintEvent *event) { RK_UNUSED(event); RkPainter painter(this); painter.fillRect(rect(), background()); impl_ptr->drawButton(painter); } geonkick-2.10.2/src/redkite/src/RkButtonImpl.cpp000066400000000000000000000101601451017372400214730ustar00rootroot00000000000000/** * File name: RkButtonImpl.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkButtonImpl.h" RkButton::RkButtonImpl::RkButtonImpl(RkButton *interface, RkWidget *parent) : RkWidgetImpl(static_cast(interface), parent) , inf_ptr{interface} , buttonType{ButtonType::ButtonUncheckable} , is_pressed{false} , buttonState{RkButton::State::Unpressed} , isEmphasizeEnabled{false} { } void RkButton::RkButtonImpl::setText(const RkString &text) { buttonText = text; } RkString RkButton::RkButtonImpl::text() const { return buttonText; } void RkButton::RkButtonImpl::setImage(const RkImage &img, RkButton::State state) { buttonImages[static_cast(state)] = img; if (state == RkButton::State::Unpressed) inf_ptr->setSize(img.size()); } bool RkButton::RkButtonImpl::isPressed() const { return is_pressed; } void RkButton::RkButtonImpl::setPressed(bool pressed) { is_pressed = pressed; isEmphasizeEnabled = false; updateButtonState(); } void RkButton::RkButtonImpl::setType(RkButton::ButtonType type) { buttonType = type; updateButtonState(); } RkButton::ButtonType RkButton::RkButtonImpl::type(void) const { return buttonType; } void RkButton::RkButtonImpl::drawButton(RkPainter &painter) { if (isPressed()) { if (!buttonImages[static_cast(buttonState)].isNull()) painter.drawImage(buttonImages[static_cast(buttonState)], 0, 0); else if (!buttonImages[static_cast(RkButton::State::Pressed)].isNull()) painter.drawImage(buttonImages[static_cast(RkButton::State::Pressed)], 0, 0); else if (!buttonImages[static_cast(RkButton::State::Unpressed)].isNull()) painter.drawImage(buttonImages[static_cast(RkButton::State::Unpressed)], 0, 0); } else if (!buttonImages[static_cast(RkButton::State::Unpressed)].isNull()) { if (!buttonImages[static_cast(buttonState)].isNull()) painter.drawImage(buttonImages[static_cast(buttonState)], 0, 0); else painter.drawImage(buttonImages[static_cast(RkButton::State::Unpressed)], 0, 0); } else if (!buttonImages[static_cast(RkButton::State::Unpressed)].isNull()) { painter.drawImage(buttonImages[static_cast(RkButton::State::Unpressed)], 0, 0); } if (!buttonText.empty()) { auto pen = painter.pen(); pen.setColor(textColor()); painter.setPen(pen); painter.drawText(inf_ptr->rect(), buttonText); } } void RkButton::RkButtonImpl::setEmphasize(bool b) { isEmphasizeEnabled = b; updateButtonState(); } void RkButton::RkButtonImpl::updateButtonState() { if (isPressed()) { if (isEmphasizeEnabled) buttonState = RkButton::State::PressedHover; else buttonState = RkButton::State::Pressed; } else { if (isEmphasizeEnabled) buttonState = RkButton::State::UnpressedHover; else buttonState = RkButton::State::Unpressed; } } geonkick-2.10.2/src/redkite/src/RkCairoGraphicsBackend.cpp000066400000000000000000000156771451017372400234060ustar00rootroot00000000000000/** * File name: RkCairoGraphicsBackend.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkCairoGraphicsBackend.h" #include "RkCanvas.h" #include "RkCanvasInfo.h" #include "RkLog.h" #include RkCairoGraphicsBackend::RkCairoGraphicsBackend(RkCanvas *canvas) : cairoContext{cairo_create(canvas->getCanvasInfo()->cairo_surface)} { cairo_set_font_size(context(), 10); cairo_set_line_width (context(), 1); } cairo_t* RkCairoGraphicsBackend::context() const { return cairoContext; } RkCairoGraphicsBackend::~RkCairoGraphicsBackend() { cairo_destroy(context()); } void RkCairoGraphicsBackend::drawText(const std::string &text, int x, int y) { cairo_move_to(context(), x, y); cairo_show_text(context(), text.c_str()); } void RkCairoGraphicsBackend::drawImage(const std::string &file, int x, int y) { auto image = cairo_image_surface_create_from_png(file.c_str()); cairo_set_source_surface(context(), image, x, y); cairo_paint(context()); cairo_surface_destroy(image); } void RkCairoGraphicsBackend::drawImage(const RkImage &image, int x, int y) { cairo_set_source_surface(context(), image.getCanvasInfo()->cairo_surface, x, y); cairo_paint(context()); } void RkCairoGraphicsBackend::drawEllipse(const RkPoint& p, int width, int height) { if (width == height) { cairo_move_to(context(), p.x() + width / 2, p.y()); cairo_arc(context(), p.x(), p.y(), width / 2, 0, 2 * M_PI); cairo_stroke(context()); } else { // TODO: implemented ellipse. RK_LOG_ERROR("ellipse not implemented yet"); } } void RkCairoGraphicsBackend::drawLine(const RkPoint &p1, const RkPoint &p2) { cairo_move_to(context(), p1.x() + 0.5, p1.y() + 0.5); cairo_line_to(context(), p2.x() + 0.5, p2.y() + 0.5); cairo_stroke(context()); } void RkCairoGraphicsBackend::drawRect(const RkRect &rect) { cairo_rectangle(context(), rect.left() + 0.5, rect.top() + 0.5, rect.width(), rect.height()); cairo_stroke(context()); } void RkCairoGraphicsBackend::setPen(const RkPen &pen) { cairo_set_line_width(context(), pen.width()); cairo_set_source_rgba(context(), static_cast(pen.color().red()) / 255, static_cast(pen.color().green()) / 255, static_cast(pen.color().blue()) / 255, static_cast(pen.color().alpha()) / 255); double dashLine[] = {12, 8}; double dotLine[] = {1, 2}; switch (pen.style()) { case RkPen::PenStyle::DashLine: cairo_set_dash(context(), dashLine, 2, 0); break; case RkPen::PenStyle::DotLine: cairo_set_dash(context(), dotLine, 2, 0); break; case RkPen::PenStyle::NoLine: case RkPen::PenStyle::SolidLine: default: cairo_set_dash(context(), nullptr, 0, 0); break; } } void RkCairoGraphicsBackend::setFont(const RkFont &font) { cairo_set_font_size(context(), font.size()); cairo_font_slant_t slant; switch (font.style()) { case RkFont::Style::Normal: slant = CAIRO_FONT_SLANT_NORMAL; break; case RkFont::Style::Italic: slant = CAIRO_FONT_SLANT_ITALIC; break; case RkFont::Style::Oblique: slant = CAIRO_FONT_SLANT_OBLIQUE; break; default: slant = CAIRO_FONT_SLANT_NORMAL; } cairo_font_weight_t weight; switch (font.weight()) { case RkFont::Weight::Normal: weight = CAIRO_FONT_WEIGHT_NORMAL; break; case RkFont::Weight::Bold: weight = CAIRO_FONT_WEIGHT_BOLD; break; default: weight = CAIRO_FONT_WEIGHT_NORMAL; } auto face = cairo_toy_font_face_create(font.family().c_str(), slant, weight); cairo_set_font_face(context(), face); cairo_font_face_destroy(face); cairo_set_font_size(context(), font.size()); } void RkCairoGraphicsBackend::drawPolyLine(const std::vector &points) { bool first = true; RkPoint currPoint; for (const auto &point: points) { if (first) { cairo_move_to(context(), point.x() + 0.5, point.y() + 0.5); currPoint = point; first = false; } else if (currPoint != point) { cairo_rel_line_to(context(), point.x() - currPoint.x(), point.y() - currPoint.y()); currPoint = point; } } cairo_stroke(context()); } void RkCairoGraphicsBackend::fillRect(const RkRect &rect, const RkColor &color) { cairo_rectangle(context(), rect.left(), rect.top(), rect.width(), rect.height()); cairo_set_source_rgba(context(), static_cast(color.red()) / 255, static_cast(color.green()) / 255, static_cast(color.blue()) / 255, static_cast(color.alpha()) / 255); cairo_fill(context()); } void RkCairoGraphicsBackend::applyAlpha(int alpha) { cairo_paint_with_alpha(context(), (float) alpha / 255); } void RkCairoGraphicsBackend::translate(const RkPoint &offset) { cairo_translate(context(), offset.x(), offset.y()); } void RkCairoGraphicsBackend::rotate(rk_real angle) { cairo_rotate(context(), angle); } int RkCairoGraphicsBackend::getTextWidth(const std::string &text) const { if (text.empty()) return 0; cairo_text_extents_t extents; cairo_text_extents (context(), text.data(), &extents); return extents.x_advance; } geonkick-2.10.2/src/redkite/src/RkCairoImageBackendCanvas.cpp000066400000000000000000000076031451017372400240120ustar00rootroot00000000000000/** * File name: RkImageBackendCanvas.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkCairoImageBackendCanvas.h" #include "RkCanvasInfo.h" #include "RkLog.h" RkCairoImageBackendCanvas::RkCairoImageBackendCanvas(const RkSize &size, RkImage::Format format, const unsigned char *data) : canvasInfo{nullptr} , imageSize{size} { #ifdef RK_OS_WIN #elif RK_OS_MAC #else // X11 auto cairoFormat = toCairoFormat(format); if (cairoFormat != CAIRO_FORMAT_INVALID && imageSize.width() > 0 && imageSize.height() > 0) { canvasInfo = std::make_unique(); auto stride = cairo_format_stride_for_width(cairoFormat, imageSize.width()); if (data == nullptr) imageData = std::vector(imageSize.width() * imageSize.height() * pixelLength(format), 0); else imageData.assign(data, data + imageSize.width() * imageSize.height() * pixelLength(format)); canvasInfo->cairo_surface = cairo_image_surface_create_for_data(imageData.data(), CAIRO_FORMAT_ARGB32, imageSize.width(), imageSize.height(), stride); } #endif } RkCairoImageBackendCanvas::~RkCairoImageBackendCanvas() { if (canvasInfo) cairo_surface_destroy(canvasInfo->cairo_surface); } cairo_format_t RkCairoImageBackendCanvas::toCairoFormat(RkImage::Format format) const { switch (format) { case RkImage::Format::ARGB32: return CAIRO_FORMAT_ARGB32; case RkImage::Format::RGB32: return CAIRO_FORMAT_RGB24; default: return CAIRO_FORMAT_INVALID; } } int RkCairoImageBackendCanvas::pixelLength(RkImage::Format format) const { switch (format) { case RkImage::Format::ARGB32: case RkImage::Format::RGB32: return 4; default: return 1; } } const RkSize& RkCairoImageBackendCanvas::size() const { return imageSize; } bool RkCairoImageBackendCanvas::isNull() const { return canvasInfo == nullptr; } unsigned char* RkCairoImageBackendCanvas::data() { return imageData.data(); } std::vector RkCairoImageBackendCanvas::dataCopy() const { return imageData; } RkCanvasInfo* RkCairoImageBackendCanvas::getCanvasInfo() const { return canvasInfo.get(); } void RkCairoImageBackendCanvas::fill(const RkColor &color) { unsigned char *data = imageData.data(); for (decltype(imageData.size()) i = 0; i < imageData.size() - 4; i += 4) { *(data + i) = color.red(); *(data + i + 1) = color.green(); *(data + i + 2) = color.blue(); *(data + i + 3) = color.alpha(); } } geonkick-2.10.2/src/redkite/src/RkContainer.cpp000066400000000000000000000117671451017372400213360ustar00rootroot00000000000000/** * File name: RkWidgetContainer.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkContainer.h" #include "RkContainerWidgetItem.h" RkContainer::RkContainer(RkWidget *parent, Rk::Orientation orientation) : RkContainerItem(parent, ItemType::ItemContainer) , containerOrientation{orientation} , itemSpacing{0} , isHiddenTakesPlace{false} { setSize(parent->size()); } void RkContainer::addContainer(RkContainer *container, Rk::Alignment align) { container->setAlignment(align); containerItems.push_back(container); update(); } void RkContainer::addWidget(RkWidget *widget, Rk::Alignment align) { auto item = new RkContainerWidgetItem(widget, align); containerItems.push_back(item); update(); } void RkContainer::removeWidget(RkWidget *widget) { } void RkContainer::addSpace(int space, Rk::Alignment align) { auto item = new RkContainerItem(this, RkContainerItem::ItemType::ItemSpace, align); item->setSize({space, space}); containerItems.push_back(item); update(); } void RkContainer::removeAt(size_t index) { if (index < containerItems.size()) { if (containerItems[index]->parent() == this) delete containerItems[index]; containerItems.erase(containerItems.begin() + index); } } RkContainerItem* RkContainer::at(size_t index) const { if (index < containerItems.size()) return containerItems[index]; return nullptr; } void RkContainer::update() { int posLeft = initPosition(Rk::Alignment::AlignLeft); int posRight = initPosition(Rk::Alignment::AlignRight); for (const auto &item: containerItems) { if (!hiddenTakesPlace() && item->isHidden()) continue; auto align = item->alignment(); if (orientation() == Rk::Orientation::Horizontal) { (align == Rk::Alignment::AlignLeft || align == Rk::Alignment::AlignTop) ? item->setX(posLeft) : item->setX(posRight - item->width()); item->setY(y() + (height() - item->height()) / 2); } else { (align == Rk::Alignment::AlignLeft || align == Rk::Alignment::AlignTop) ? item->setY(posLeft) : item->setY(posRight - item->height()); item->setX(x() + (width() - item->width()) / 2); } auto w = (orientation() == Rk::Orientation::Horizontal) ? item->width() : item->height(); if (align == Rk::Alignment::AlignLeft || align == Rk::Alignment::AlignTop) posLeft += w + itemSpacing; else posRight -= w + itemSpacing; } } int RkContainer::initPosition(Rk::Alignment alignment) { int pos = 0; if (alignment == Rk::Alignment::AlignLeft) { if (orientation() == Rk::Orientation::Horizontal) pos = x(); else pos = y(); } else { if (orientation() == Rk::Orientation::Horizontal) pos = x() + width(); else pos = y() + height(); } return pos; } void RkContainer::clear() { for (auto &item: containerItems) { if (item->parent() == this) delete item; } containerItems.clear(); } Rk::Orientation RkContainer::orientation() const { return containerOrientation; } void RkContainer::setSize(const RkSize &size) { RkContainerItem::setSize(size); update(); } void RkContainer::setWidth(int width) { RkContainerItem::setWidth(width); update(); } void RkContainer::setHeight(int height) { RkContainerItem::setHeight(height); update(); } void RkContainer::setPosition(const RkPoint &position) { RkContainerItem::setPosition(position); update(); } void RkContainer::setX(int val) { RkContainerItem::setX(val); update(); } void RkContainer::setY(int val) { RkContainerItem::setY(val); update(); } void RkContainer::setSpacing(size_t space) { itemSpacing = space; update(); } size_t RkContainer::spacing() const { return itemSpacing; } void RkContainer::setHiddenTakesPlace(bool b) { isHiddenTakesPlace = b; update(); } bool RkContainer::hiddenTakesPlace() const { return isHiddenTakesPlace; } geonkick-2.10.2/src/redkite/src/RkEventQueue.cpp000066400000000000000000000066051451017372400214750ustar00rootroot00000000000000/** * File name: RkEventQueue.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkEventQueue.h" #include "RkEventQueueImpl.h" RkEventQueue::RkEventQueue() : o_ptr{std::make_unique(this)} { } RkEventQueue::RkEventQueue(std::unique_ptr impl) : o_ptr{std::move(impl)} { } RkEventQueue::~RkEventQueue() { } void RkEventQueue::addObject(RkObject *obj) { o_ptr->addObject(obj); } void RkEventQueue::addShortcut(RkObject *obj, Rk::Key key, Rk::KeyModifiers modifier) { o_ptr->addShortcut(obj, key, modifier); } void RkEventQueue::removeShortcut(RkObject *obj, Rk::Key key, Rk::KeyModifiers modifier) { o_ptr->removeShortcut(obj, key, modifier); } void RkEventQueue::removeObject(RkObject *obj) { o_ptr->removeObject(obj); } void RkEventQueue::postEvent(RkObject *obj, std::unique_ptr event) { o_ptr->postEvent(obj, std::move(event)); } void RkEventQueue::postAction(std::unique_ptr act) { o_ptr->postAction(std::move(act)); } void RkEventQueue::subscribeTimer(RkTimer *timer) { if (timer) o_ptr->subscribeTimer(timer); } void RkEventQueue::unsubscribeTimer(RkTimer *timer) { if (timer) o_ptr->unsubscribeTimer(timer); } void RkEventQueue::processEvents() { o_ptr->processEvents(); } void RkEventQueue::processActions() { o_ptr->processActions(); } void RkEventQueue::processTimers() { o_ptr->processTimers(); } void RkEventQueue::processQueue() { #ifdef RK_LOG_DEBUG_LEVEL static int n = 0; if (++n > 10000) { RK_LOG_DEBUG("called"); n = 0; } #endif // RK_LOG_DEBUG_LEVEL // The order is important. processTimers(); processActions(); processEvents(); } void RkEventQueue::clearObjectEvents(const RkObject *obj) { if (obj) o_ptr->clearEvents(obj); } void RkEventQueue::clearObjectActions(const RkObject *act) { if (act) o_ptr->clearActions(act); } void RkEventQueue::clearEvents() { o_ptr->clearEvents(nullptr); } void RkEventQueue::clearActions() { o_ptr->clearActions(nullptr); } void RkEventQueue::clearQueue() { clearEvents(); clearActions(); } RkObject* RkEventQueue::findObjectByName(const std::string &name) const { return o_ptr->findObjectByName(name); } void RkEventQueue::setScaleFactor(double factor) { o_ptr->setScaleFactor(factor); } geonkick-2.10.2/src/redkite/src/RkEventQueueImpl.cpp000066400000000000000000000342251451017372400223160ustar00rootroot00000000000000/** * File name: RkEventQueueImpl.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkWidget.h" #include "RkWidgetImpl.h" #include "RkEventQueueImpl.h" #include "RkTimer.h" #include "RkAction.h" #ifdef RK_OS_WIN #include "RkEventQueueWin.h" #elif RK_OS_MAC #include "RkEventQueueMac.h" #else // X11 #include "RkEventQueueX.h" #endif RkEventQueue::RkEventQueueImpl::RkEventQueueImpl(RkEventQueue* interface) : inf_ptr{interface} #ifdef RK_OS_WIN , platformEventQueue{std::make_unique()} #elif RK_OS_MAC , platformEventQueue{std::make_unique()} #else // X11 , platformEventQueue{std::make_unique()} #endif { RK_LOG_DEBUG("called"); } RkEventQueue::RkEventQueueImpl::~RkEventQueueImpl() { RK_LOG_DEBUG("called"); } bool RkEventQueue::RkEventQueueImpl::objectExists(RkObject *obj) const { return objectsList.find(obj) != objectsList.end(); } void RkEventQueue::RkEventQueueImpl::addObject(RkObject *obj) { RK_LOG_DEBUG("add object: " << obj); if (!obj || objectExists(obj)) return; if (obj->type() == Rk::ObjectType::Widget) { RK_LOG_DEBUG("obj " << obj << " is widget"); auto widgetImpl = dynamic_cast(obj->o_ptr.get()); if (!widgetImpl) { RK_LOG_ERROR("can't cast o_ptr to RkWidgetImpl"); return; } #if !defined(RK_OS_WIN) && !defined(RK_OS_MAC) // Set the display from the top window. if (!widgetImpl->parent() && !platformEventQueue->display()) { RK_LOG_DEBUG("widget " << obj << " is top window"); platformEventQueue->setDisplay(widgetImpl->nativeWindowInfo()->display); } #else #error platform not implemented #endif RK_LOG_DEBUG("add widget window id"); auto id = widgetImpl->nativeWindowInfo()->window; windowIdsMap.insert({id, obj}); if (static_cast(widgetImpl->windowFlags()) & static_cast(Rk::WindowFlags::Popup)) { popupList.insert({id, obj}); RK_LOG_DEBUG("poup added: " << obj); } } objectsList.insert(obj); if (!obj->eventQueue()) obj->setEventQueue(inf_ptr); } void RkEventQueue::RkEventQueueImpl::addShortcut(RkObject *obj, Rk::Key key, Rk::KeyModifiers modifier) { auto res = shortcutsList.find(static_cast(key)); if (res != shortcutsList.end()) { RK_LOG_DEBUG("obj: " << obj << " key: " << static_cast(key) << ", modifier: " << static_cast(modifier) << " hashKey: " << static_cast(key)); res->second->addObject(obj); } else { RK_LOG_DEBUG("new: obj : " << obj << " key: " << static_cast(key) << ", modifier: " << static_cast(modifier) << " hashKey: " << static_cast(key)); auto shortcut = std::make_unique(key, modifier); shortcut->addObject(obj); shortcutsList.insert({static_cast(key), std::move(shortcut)}); } } void RkEventQueue::RkEventQueueImpl::removeShortcut(RkObject *obj, Rk::Key key, Rk::KeyModifiers modifier) { auto res = shortcutsList.find(static_cast(key)); if (res != shortcutsList.end()) { res->second->removeObject(obj); if (res->second->objects().empty()) shortcutsList.erase(static_cast(key)); } } void RkEventQueue::RkEventQueueImpl::removeObject(RkObject *obj) { if (objectsList.find(obj) != objectsList.end()) { objectsList.erase(obj); removeObjectShortcuts(obj); if (obj->type() == Rk::ObjectType::Widget) { auto widgetImpl = dynamic_cast(obj->o_ptr.get()); if (!widgetImpl) { RK_LOG_ERROR("can't cast o_ptr to RkWidgetImpl"); return; } auto id = widgetImpl->nativeWindowInfo()->window; if (windowIdsMap.find(id) != windowIdsMap.end()) { RK_LOG_DEBUG("widget id removed from queue"); windowIdsMap.erase(id); if (popupList.find(id) != popupList.end()) popupList.erase(id); } } } } void RkEventQueue::RkEventQueueImpl::removeObjectShortcuts(RkObject *obj) { for (auto &shortcut : shortcutsList) { if (shortcut.second->hasObject(obj)) shortcut.second->removeObject(obj); } } RkWidget* RkEventQueue::RkEventQueueImpl::findWidget(const RkWindowId &id) const { auto it = windowIdsMap.find(id.id); if (it != windowIdsMap.end()) { if (it->second->type() == Rk::ObjectType::Widget) { auto widget = dynamic_cast(it->second); if (!widget) { RK_LOG_ERROR("can't cast RkObject[" << it->second << "] to RkWidget"); return nullptr; } return widget; } } return nullptr; } void RkEventQueue::RkEventQueueImpl::postEvent(RkObject *obj, std::unique_ptr event) { eventsQueue.push_back({obj, std::move(event)}); } void RkEventQueue::RkEventQueueImpl::processEvent(RkObject *obj, RkEvent *event) { // Do not process events for objects that were removed from the event queue. if (objectExists(obj)) obj->event(event); } void RkEventQueue::RkEventQueueImpl::processEvents() { auto events = platformEventQueue->getEvents(); if (!events.empty()) { for (auto &event: events) { auto widget = findWidget(event.first); if (widget) { auto pair = std::make_pair>(widget, std::move(event.second)); eventsQueue.push_back(std::move(pair)); } } } events.clear(); /** * Moving events in a separeted queue for processing * because during the processing the execution of some events * may add new events into the queue and this for * in some cases can lead to a infinite looping. */ decltype(eventsQueue) queue = std::move(eventsQueue); for (const auto &e: queue) { if (e.second->type() == RkEvent::Type::KeyPressed || e.second->type() == RkEvent::Type::KeyReleased) { processShortcuts(dynamic_cast(e.second.get())); } if (!popupList.empty() && dynamic_cast(e.first)) processPopups(dynamic_cast(e.first), e.second.get()); processEvent(e.first, e.second.get()); } } void RkEventQueue::RkEventQueueImpl::processPopups(RkWidget *widget, RkEvent* event) { if (event->type() == RkEvent::Type::MouseButtonPress) { for (auto it = popupList.begin(); it != popupList.end();) { auto w = static_cast((*it).second); if (widget != w && !w->isChild(widget)) { w->close(); it = popupList.erase(it); } else { ++it; } } } } bool RkEventQueue::RkEventQueueImpl::isTopWidget(RkObject *obj) const { if (objectExists(obj)) { auto widget = dynamic_cast(obj); if (widget && widget->getTopWidget() == widget) return true; } return false; } void RkEventQueue::RkEventQueueImpl::processShortcuts(RkKeyEvent *event) { if (!event) { RK_LOG_ERROR("wrong arguments"); return; } if (static_cast(event)->isShortcut()) return; auto hashKey = static_cast(event->key()); RK_LOG_DEBUG("key: " << static_cast(event->key()) << ", modifier: " << static_cast(event->modifiers()) << " hashKey: " << hashKey); auto res = shortcutsList.find(hashKey); if (res != shortcutsList.end()) { for (RkObject* obj : res->second->objects()) { auto shurtcutEvent = std::make_unique(event->type()); shurtcutEvent->setKey(event->key()); shurtcutEvent->setModifiers(event->modifiers()); shurtcutEvent->setShortcut(); auto pair = std::make_pair(obj, std::move(shurtcutEvent)); eventsQueue.push_back(std::move(pair)); } } else { RK_LOG_DEBUG("can't find shortcut"); } } void RkEventQueue::RkEventQueueImpl::postAction(std::unique_ptr act) { std::lock_guard lock(actionsQueueMutex); actionsQueue.push_back(std::move(act)); } void RkEventQueue::RkEventQueueImpl::processActions() { decltype(actionsQueue) q; { std::lock_guard lock(actionsQueueMutex); /** * Moving actions in a separeted queue for processing * because during the processing the execution of some actions * may add new actions into the queue and this for * in some cases can lead to a infinite looping. */ q = std::move(actionsQueue); } for (const auto &act: q) { // Do not process actions for objects that were removed from the event queue. if (!act->object() || objectExists(act->object())) act->call(); } } void RkEventQueue::RkEventQueueImpl::subscribeTimer(RkTimer *timer) { timersList.insert(timer); } void RkEventQueue::RkEventQueueImpl::unsubscribeTimer(RkTimer *timer) { if (timersList.find(timer) != timersList.end()) timersList.erase(timer); } void RkEventQueue::RkEventQueueImpl::processTimers() { for (const auto &timer: timersList) { if (timer->started() && timer->isTimeout()) timer->callTimeout(); } } void RkEventQueue::RkEventQueueImpl::clearEvents(const RkObject *obj) { if (!obj) return; RK_LOG_DEBUG("clear object " << obj << " events"); eventsQueue.erase(std::remove_if(eventsQueue.begin(), eventsQueue.end(), [obj](std::pair> &ev) { if (ev.first == obj) { RK_LOG_DEBUG("clear: [obj: " << obj << "] ev: " << ev.second.get()); return true; } return false; }) , eventsQueue.end()); } void RkEventQueue::RkEventQueueImpl::clearActions(const RkObject *obj) { if (!obj) return; std::lock_guard lock(actionsQueueMutex); actionsQueue.erase(std::remove_if(actionsQueue.begin(), actionsQueue.end(), [obj](const std::unique_ptr &act) { if (act->object() && act->object() == obj) { RK_LOG_DEBUG("clear: [obj: " << obj << "] act: " << act.get()); return true; } return false; }) , actionsQueue.end()); } RkObject* RkEventQueue::RkEventQueueImpl::findObjectByName(const std::string &name) const { // TODO: use less complexity O(1) with hashtable. for (auto it = objectsList.cbegin(); it != objectsList.cend(); ++it) { if ((*it)->name() == name) return *it; } return nullptr; } void RkEventQueue::RkEventQueueImpl::setScaleFactor(double factor) { platformEventQueue->setScaleFactor(factor); } geonkick-2.10.2/src/redkite/src/RkImage.cpp000066400000000000000000000050321451017372400204220ustar00rootroot00000000000000/** * File name: RkImage.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkImage.h" #include "RkImageImpl.h" RkImage::RkImage() : o_ptr{std::make_unique(this, 0, 0, nullptr)} { } RkImage::RkImage(int width, int height, const unsigned char *data, Format format) : o_ptr{std::make_unique(this, width, height, data, format)} { } RkImage::RkImage(const RkSize &size, const unsigned char *data, Format format) : o_ptr{std::make_unique(this, size.width(), size.height(), data, format)} { } RkImage::RkImage(std::unique_ptr impl) : o_ptr{std::move(impl)} { } RkImage::RkImage(const RkImage &image) : o_ptr{std::make_unique(this, 0, 0, nullptr)} { o_ptr->createImage({image.width(), image.height()}, image.format(), image.data()); } RkImage::~RkImage() { } RkImage& RkImage::operator=(const RkImage &other) { o_ptr->createImage({other.width(), other.height()}, other.format(), other.data()); return *this; } void RkImage::fill(const RkColor &color) { o_ptr->fill(color); } const RkCanvasInfo* RkImage::getCanvasInfo() const { return o_ptr->getCanvasInfo(); } unsigned char* RkImage::data() const { return o_ptr->data(); } std::vector RkImage::dataCopy() const { return o_ptr->dataCopy(); } RkImage::Format RkImage::format() const { return o_ptr->format(); } int RkImage::width() const { return o_ptr->width(); } int RkImage::height() const { return o_ptr->height(); } RkSize RkImage::size() const { return o_ptr->size(); } bool RkImage::isNull() const { return o_ptr->isNull(); } geonkick-2.10.2/src/redkite/src/RkImageImpl.cpp000066400000000000000000000064101451017372400212450ustar00rootroot00000000000000/** * File name: RkImageImpl.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkImageImpl.h" #ifdef RK_GRAPHICS_CAIRO_BACKEND #include "RkCairoImageBackendCanvas.h" #else #error No graphics backend defined. #endif RkImage::RkImageImpl::RkImageImpl(RkImage *interface, int width, int height, const unsigned char *data, RkImage::Format format) : inf_ptr{interface} , imageFormat{format} #ifdef RK_GRAPHICS_CAIRO_BACKEND , imageBackendCanvas{std::make_unique(RkSize(width, height), imageFormat, data)} #else #error No graphics backend defined #endif { RK_UNUSED(inf_ptr); } RkImage::RkImageImpl::~RkImageImpl() { } const RkCanvasInfo* RkImage::RkImageImpl::getCanvasInfo() const { if (imageBackendCanvas) return imageBackendCanvas->getCanvasInfo(); return nullptr; } unsigned char* RkImage::RkImageImpl::data() const { return imageBackendCanvas->data(); } std::vector RkImage::RkImageImpl::dataCopy() const { if (imageBackendCanvas) return imageBackendCanvas->dataCopy(); return std::vector(); } RkImage::Format RkImage::RkImageImpl::format() const { return imageFormat; } int RkImage::RkImageImpl::width() const { if (imageBackendCanvas) return imageBackendCanvas->size().width(); return 0; } int RkImage::RkImageImpl::height() const { if (imageBackendCanvas) return imageBackendCanvas->size().height(); return 0; } RkSize RkImage::RkImageImpl::size() const { if (imageBackendCanvas) return imageBackendCanvas->size(); return RkSize(); } bool RkImage::RkImageImpl::isNull() const { return !imageBackendCanvas || imageBackendCanvas->isNull(); } void RkImage::RkImageImpl::createImage(const RkSize &size, RkImage::Format format, const unsigned char *data) { imageFormat = format; #ifdef RK_GRAPHICS_CAIRO_BACKEND if (imageBackendCanvas) imageBackendCanvas = std::make_unique(size, format, data); #else #error No graphics backend defined #endif } void RkImage::RkImageImpl::fill(const RkColor &color) { if (imageBackendCanvas) imageBackendCanvas->fill(color); } geonkick-2.10.2/src/redkite/src/RkLabel.cpp000066400000000000000000000027461451017372400204300ustar00rootroot00000000000000/** * File name: RkLabel.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkLabel.h" #include "RkLabelImpl.h" #include "RkLog.h" RkLabel::RkLabel(RkWidget *parent, const std::string &text) : RkWidget(parent, std::make_unique(this, text, parent)) , impl_ptr{static_cast(o_ptr.get())} { } void RkLabel::setText(const std::string &text) { impl_ptr->setText(text); update(); } std::string RkLabel::text() const { return impl_ptr->text(); } void RkLabel::setImage(const RkImage &image) { impl_ptr->setImage(image); } void RkLabel::paintEvent(RkPaintEvent *event) { RK_UNUSED(event); impl_ptr->drawLabel(); } geonkick-2.10.2/src/redkite/src/RkLabelImpl.cpp000066400000000000000000000040551451017372400212450ustar00rootroot00000000000000/** * File name: RkLabelImpl.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkLabelImpl.h" #include "RkPainter.h" #include "RkLog.h" RkLabel::RkLabelImpl::RkLabelImpl(RkLabel *interface, const std::string &text, RkWidget *parent) : RkWidgetImpl(static_cast(interface), parent) , inf_ptr{interface} , labelText{text} { } RkLabel::RkLabelImpl::~RkLabelImpl() { } void RkLabel::RkLabelImpl::setText(const std::string &text) { labelText = text; } std::string RkLabel::RkLabelImpl::text() const { return labelText; } void RkLabel::RkLabelImpl::setImage(const RkImage &image) { labelImage = image; } void RkLabel::RkLabelImpl::drawLabel() { RkImage img(size()); RkPainter painter(&img); painter.fillRect(rect(), background()); if (!labelImage.isNull()) painter.drawImage(labelImage, 0, 0); if (!labelText.empty()) { auto pen = painter.pen(); pen.setColor(textColor()); painter.setPen(pen); painter.setFont(font()); painter.drawText(inf_ptr->rect(), labelText); } RkPainter paint(inf_ptr); paint.drawImage(img, 0, 0); } geonkick-2.10.2/src/redkite/src/RkLineEdit.cpp000066400000000000000000000146121451017372400211010ustar00rootroot00000000000000/** * File name: RkLineEdit.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkLineEdit.h" #include "RkLineEditImpl.h" #include "RkLog.h" #include "RkEvent.h" #include "RkPainter.h" RkLineEdit::RkLineEdit(RkWidget *parent, const std::string &text) : RkWidget(parent, std::make_unique(this, parent, text)) , impl_ptr{static_cast(o_ptr.get())} { setPointerShape(Rk::PointerShape::IBeam); propagateGrabKey(false); impl_ptr->init(); } void RkLineEdit::setText(const std::string &text) { impl_ptr->setText(text); update(); } std::string RkLineEdit::text() const { return impl_ptr->text(); } void RkLineEdit::paintEvent(RkPaintEvent *event) { impl_ptr->paintEvent(event); } /** * [OK] Left Arrow * [ok] Shift + Left Arrow * [OK] Right Arrow * [OK] Shift + Right Arrow * [OK] Home * [OK] End * [OK] Backspace * Ctrl + Backspace // TODO: v1.0 * [OK] Delete * Ctrl + Delete // TODO: v1.0 * [OK] Ctrl + A * Ctrl + C * Ctrl + Insert * Ctrl + K * Ctrl + V * Shift + Insert * Ctrl + X * Shift + Delete * Ctrl+Z // TODO: v1.0 * Ctrl+Y // TODO: v1.0 */ void RkLineEdit::keyPressEvent(RkKeyEvent *event) { if (!hasFocus()) return; showCursor(); switch (event->key()) { case Rk::Key::Key_Left: if (event->modifiers() & static_cast(Rk::KeyModifiers::Shift)) { impl_ptr->enableSelectionMode(true); impl_ptr->showCursor(false); } else { impl_ptr->showCursor(true); impl_ptr->enableSelectionMode(false); } impl_ptr->moveCursorLeft(); update(); return; case Rk::Key::Key_Right: if (event->modifiers() & static_cast(Rk::KeyModifiers::Shift)) { impl_ptr->enableSelectionMode(true); impl_ptr->showCursor(false); } else { impl_ptr->enableSelectionMode(false); impl_ptr->showCursor(true); } impl_ptr->moveCursorRight(); update(); return; case Rk::Key::Key_Home: case Rk::Key::Key_End: if (event->modifiers() & static_cast(Rk::KeyModifiers::Shift)) { impl_ptr->enableSelectionMode(true); impl_ptr->showCursor(false); Rk::Key::Key_Home == event->key() ? impl_ptr->moveSelectionToFront() : impl_ptr->moveSelectionToBack(); } else { impl_ptr->showCursor(true); impl_ptr->enableSelectionMode(false); Rk::Key::Key_Home == event->key() ? impl_ptr->moveCursorToFront() : impl_ptr->moveCursorToBack(); } update(); return; case Rk::Key::Key_BackSpace: impl_ptr->removeText(1, false); textEdited(impl_ptr->text()); return; case Rk::Key::Key_Delete: if (impl_ptr->selectionMode()) impl_ptr->deleteSelection(); else impl_ptr->removeText(1, true); textEdited(impl_ptr->text()); update(); return; case Rk::Key::Key_Return: action enterPressed(); action editingFinished(); return; case Rk::Key::Key_Escape: hideCursor(); action escapePressed(); return; case Rk::Key::Key_a: case Rk::Key::Key_A: if (event->modifiers() & static_cast(Rk::KeyModifiers::Control)) { impl_ptr->selectAll(); update(); return; } break; // Ignore these keys. case Rk::Key::Key_Control_Left: case Rk::Key::Key_Control_Right: case Rk::Key::Key_Shift_Left: case Rk::Key::Key_Shift_Right: return; default: break; } // Allow only Latin1. int key = static_cast(event->key()); if (0x00000020 <= key && key <= 0x000000ff) { impl_ptr->enableSelectionMode(false); std::string str = std::string(1, static_cast(static_cast(event->key()))); impl_ptr->addText(str); textEdited(impl_ptr->text()); update(); } } void RkLineEdit::mouseButtonPressEvent(RkMouseEvent* event) { setFocus(true); } void RkLineEdit::focusEvent(RkFocusEvent* event) { RK_LOG_DEBUG("focus event"); if (event->type() == RkEvent::Type::FocusedIn) { RK_LOG_DEBUG("RkEvent::Type::FocusedIn"); showCursor(); } else if (event->type() == RkEvent::Type::FocusedOut) { RK_LOG_DEBUG("RkEvent::Type::FocusedOut"); if (impl_ptr->hasEditFocus()) action editingFinished(); hideCursor(); } } void RkLineEdit::resizeEvent(RkResizeEvent* event) { RK_UNUSED(event); impl_ptr->updateSize(); } void RkLineEdit::showCursor() { impl_ptr->showCursor(true); update(); } void RkLineEdit::hideCursor() { impl_ptr->showCursor(false); update(); } void RkLineEdit::moveCursorToFront() { impl_ptr->moveCursorToFront(); update(); } void RkLineEdit::moveCursorToEnd() { impl_ptr->moveCursorToBack(); update(); } geonkick-2.10.2/src/redkite/src/RkLineEditImpl.cpp000066400000000000000000000253521451017372400217260ustar00rootroot00000000000000/** * File name: RkLineEditImpl.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkLineEditImpl.h" #include "RkPainter.h" #include "RkLog.h" #include "RkTimer.h" #include "RkEvent.h" RkLineEdit::RkLineEditImpl::RkLineEditImpl(RkLineEdit *interface, RkWidget *parent, const std::string &text) : RkWidgetImpl(static_cast(interface), parent) , inf_ptr{interface} , editedText{text} , cursorIndex{0} , selectionIndex{0} , isSelectionMode{false} , cursorTimer{nullptr} , isShowCursor{hasFocus()} , lastCahnges{std::chrono::system_clock::now()} , contentsRect{0, 0, 0, 0} , beginX{0} , endX{0} { } RkLineEdit::RkLineEditImpl::~RkLineEditImpl() { cursorTimer->stop(); } void RkLineEdit::RkLineEditImpl::init() { cursorTimer = new RkTimer(inf_ptr, 800); hasFocus() ? showCursor(true) : showCursor(false); RK_ACT_BIND(cursorTimer, timeout, RK_ACT_ARGS(), this, onCursorTimeout()); } void RkLineEdit::RkLineEditImpl::setText(const std::string &text) { selectionIndex = 0; enableSelectionMode(false); editedText = text; if (editedText.size() < 1) cursorIndex = 0; else if (cursorIndex > static_cast(editedText.size()) - 1) cursorIndex = editedText.size() - 1; lastCahnges = std::chrono::system_clock::now(); } void RkLineEdit::RkLineEditImpl::moveCursorLeft(int n) { if (editedText.empty()) { cursorIndex = 0; } else if (selectionMode()) { selectionIndex--; if (selectionIndex < 0) selectionIndex = 0; if (selectionIndex == cursorIndex) { enableSelectionMode(false); showCursor(true); } } else { cursorIndex -= n; if (cursorIndex < 0) cursorIndex = 0; } lastCahnges = std::chrono::system_clock::now(); } void RkLineEdit::RkLineEditImpl::moveCursorRight(int n) { if (editedText.empty()) { cursorIndex = 0; } else if (selectionMode()) { selectionIndex++; if (selectionIndex > static_cast(editedText.size())) selectionIndex = editedText.size(); if (selectionIndex == cursorIndex) { enableSelectionMode(false); showCursor(true); } } else { cursorIndex += n; if (cursorIndex > static_cast(editedText.size() - 1)) cursorIndex = editedText.size(); } lastCahnges = std::chrono::system_clock::now(); } void RkLineEdit::RkLineEditImpl::moveCursorToFront() { cursorIndex = 0; } void RkLineEdit::RkLineEditImpl::moveCursorToBack() { cursorIndex = editedText.size(); } void RkLineEdit::RkLineEditImpl::moveSelectionToFront() { if (selectionMode()) selectionIndex = 0; } void RkLineEdit::RkLineEditImpl::moveSelectionToBack() { if (selectionMode()) selectionIndex = editedText.size(); } void RkLineEdit::RkLineEditImpl::addText(const std::string& text) { if (isSelectionMode) { } else { if (cursorIndex == static_cast(editedText.size())) editedText += text; else editedText.insert(cursorIndex, text); cursorIndex += text.size(); } lastCahnges = std::chrono::system_clock::now(); } void RkLineEdit::RkLineEditImpl::removeText(int n, bool after) { if (editedText.size() < 1) return; if (after) { if (static_cast(cursorIndex + n) > editedText.size()) editedText.erase(cursorIndex, editedText.size() - 1); else editedText.erase(cursorIndex, n); } else if (cursorIndex > 0) { if (cursorIndex - n < 0) { editedText.erase(editedText.begin(), editedText.begin() + cursorIndex); cursorIndex = 0; } else { editedText.erase(cursorIndex - n, n); cursorIndex -= n; } } if (isSelectionMode) selectionIndex = cursorIndex; lastCahnges = std::chrono::system_clock::now(); } std::string RkLineEdit::RkLineEditImpl::text() const { return editedText; } std::string RkLineEdit::RkLineEditImpl::textTo(int index) const { if (editedText.empty()) return std::string(); else return editedText.substr(0, index); } void RkLineEdit::RkLineEditImpl::enableSelectionMode(bool b) { if (isSelectionMode != b) { isSelectionMode = b; selectionIndex = cursorIndex; } } bool RkLineEdit::RkLineEditImpl::selectionMode() const { return isSelectionMode; } bool RkLineEdit::RkLineEditImpl::isCursorHidden() const { return !isShowCursor; } void RkLineEdit::RkLineEditImpl::onCursorTimeout() { if (std::chrono::duration_cast(std::chrono::system_clock::now() - lastCahnges).count() > 1000) { isShowCursor = !isShowCursor; inf_ptr->update(); } } void RkLineEdit::RkLineEditImpl::showCursor(bool b) { isShowCursor = b; isShowCursor ? cursorTimer->start() : cursorTimer->stop(); } void RkLineEdit::RkLineEditImpl::selectAll() { if (!editedText.empty()) { enableSelectionMode(true); showCursor(false); selectionIndex = editedText.size(); cursorIndex = 0; } } int RkLineEdit::RkLineEditImpl::getCursorIndex() const { return cursorIndex; } int RkLineEdit::RkLineEditImpl::selectionStart() const { return selectionIndex > cursorIndex ? cursorIndex : selectionIndex; } int RkLineEdit::RkLineEditImpl::selectionEnd() const { return selectionIndex < cursorIndex ? cursorIndex : selectionIndex; } std::string RkLineEdit::RkLineEditImpl::getText(int pos, size_t n) const { if (editedText.empty()) return std::string(); if (pos > static_cast(editedText.size())) return std::string(); else if (pos < 0) pos = 0; else if (n > editedText.npos) n = editedText.npos; return editedText.substr(pos, n); } void RkLineEdit::RkLineEditImpl::deleteSelection() { if (selectionMode() && selectionStart() != selectionEnd()) { editedText.erase(selectionStart(), selectionEnd() - selectionStart()); selectionIndex = cursorIndex = selectionStart(); enableSelectionMode(false); showCursor(true); } } void RkLineEdit::RkLineEditImpl::paintEvent(RkPaintEvent *event) { RK_UNUSED(event); if (contentsRect.height() * contentsRect.width() == 0) updateSize(); RkImage img(size()); { RkPainter painter(&img); painter.fillRect(rect(), background()); painter.setFont(font()); int cursorX = 0; if (selectionMode()) cursorX = painter.getTextWidth(textTo(selectionIndex)); else cursorX = painter.getTextWidth(textTo(cursorIndex)); if (cursorX > endX) { endX += cursorX - endX; beginX = endX - contentsRect.width(); cursorX = contentsRect.width(); } else if (cursorX < beginX) { beginX -= beginX - cursorX; endX = beginX + contentsRect.width(); cursorX = 1; } else { cursorX = cursorX - beginX; endX = beginX + contentsRect.width(); } // Draw selection background. if (selectionMode()) { auto text = getText(0, selectionStart()); int xpos = painter.getTextWidth(text); int nSelectedChars = selectionEnd() - selectionStart(); text = getText(selectionStart(), nSelectedChars); int w = painter.getTextWidth(text); painter.fillRect(RkRect(contentsRect.left() + xpos - beginX, contentsRect.top(), w, contentsRect.height()) , {52, 116, 209}); } // Draw edited text. auto pen = painter.pen(); pen.setColor(textColor()); painter.setPen(pen); painter.drawText(contentsRect.left() - beginX, contentsRect.top() + (contentsRect.height() - font().size()) / 2 + font().size(), text()); // Draw cursor. if (!isCursorHidden()) { pen = painter.pen(); pen.setColor(color()); painter.setPen(pen); painter.drawLine(contentsRect.left() + cursorX + 1, contentsRect.top(), contentsRect.left() + cursorX + 1, contentsRect.top() + contentsRect.height()); } } RkPainter paint(inf_ptr); paint.drawImage(img, 0, 0); } void RkLineEdit::RkLineEditImpl::updateSize() { contentsRect = RkRect(2, 2, size().width() - 6, size().height() - 4); beginX = 0; endX = contentsRect.width(); } bool RkLineEdit::RkLineEditImpl::hasEditFocus() const { return cursorTimer->started(); } geonkick-2.10.2/src/redkite/src/RkList.cpp000066400000000000000000000041331451017372400203140ustar00rootroot00000000000000/** * File name: RkList.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkList.h" #include "RkModel.h" #include "RkListImpl.h" #include "RkLog.h" #include "RkPainter.h" #include "RkEvent.h" RkList::RkList(RkWidget *parent, RkModel *model) : RkWidget(parent, std::make_unique(this, parent, model)) , impl_ptr{static_cast(o_ptr.get())} { RK_ACT_BIND(getModel(), modelChanged, RK_ACT_ARGS(), this, update()); } RkModel* RkList::getModel() const { return impl_ptr->getModel(); } void RkList::paintEvent(RkPaintEvent *event) { RkImage img(size()); RkPainter painter(&img); painter.fillRect(rect(), background()); impl_ptr->drawList(painter); RkPainter paint(this); paint.drawImage(img, 0, 0); } void RkList::keyPressEvent(RkKeyEvent *event) { } void RkList::keyReleaseEvent(RkKeyEvent *event) { } void RkList::mouseMoveEvent(RkMouseEvent *event) { } void RkList::mouseButtonPressEvent(RkMouseEvent *event) { if (event->button() == RkMouseEvent::ButtonType::Left) getModel()->selectItem(impl_ptr->getIndex(event->y())); } void RkList::mouseButtonReleaseEvent(RkMouseEvent *event) { } void RkList::mouseDoubleClickEvent(RkMouseEvent *event) { } void RkList::wheelEvent(RkWheelEvent *event) { } geonkick-2.10.2/src/redkite/src/RkListImpl.cpp000066400000000000000000000055541451017372400211460ustar00rootroot00000000000000/** * File name: RkListImpl.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkListImpl.h" #include "RkLog.h" #include "RkModel.h" RkList::RkListImpl::RkListImpl(RkList *interface, RkWidget *parent, RkModel *model) : RkWidgetImpl(static_cast(interface), parent) , inf_ptr{interface} , listModel{model} , topMargin{0} , leftMargin{10} { } void RkList::RkListImpl::drawList(RkPainter &painter) { int y = topMargin; size_t i = 0; RkPen pen = painter.pen(); while (i < listModel->itemsNumber()) { RkVariant itemData = listModel->itemData(i, static_cast(RkModelItem::DataType::Color)); if (std::holds_alternative(itemData)) { pen.setColor(std::get(itemData)); painter.setPen(pen); } itemData = listModel->itemData(i, static_cast(RkModelItem::DataType::Font)); if (std::holds_alternative(itemData)) painter.setFont(std::get(itemData)); if (listModel->isItemSelected(i)) { pen.setColor({255, 255, 255}); painter.setPen(pen); } itemData = listModel->itemData(i, static_cast(RkModelItem::DataType::String)); if (std::holds_alternative(itemData)) { RkFont font = painter.font(); painter.drawText(RkRect(leftMargin, y, inf_ptr->width() - leftMargin, listModel->itemSpan()), std::get(itemData), Rk::Alignment::AlignLeft); } y += listModel->itemSpan(); i++; } } RkModel* RkList::RkListImpl::getModel() const { return listModel; } size_t RkList::RkListImpl::getIndex(int y) const { if (listModel->itemSpan() > 0) return (y - topMargin) / listModel->itemSpan(); return -1; } geonkick-2.10.2/src/redkite/src/RkMain.cpp000066400000000000000000000027201451017372400202650ustar00rootroot00000000000000/** * File name: RkMain.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkMain.h" #include "RkLog.h" #include "RkWidget.h" #include "RkMainImpl.h" RkMain::RkMain() : o_ptr{std::make_unique(this)} { } RkMain::RkMain(int argc, char **argv) : o_ptr{std::make_unique(this, argc, argv)} { } RkMain::~RkMain() { } bool RkMain::setTopLevelWidget(RkWidget *widget) { return o_ptr->setTopLevelWidget(widget); } RkWidget* RkMain::topLevelWidget(void) const { return o_ptr->topLevelWidget(); } RkEventQueue* RkMain::eventQueue() const { return o_ptr->getEventQueue(); } int RkMain::exec(bool block) { return o_ptr->exec(block); } geonkick-2.10.2/src/redkite/src/RkMainImpl.cpp000066400000000000000000000051121451017372400211050ustar00rootroot00000000000000/** * File name: RkMainImpl.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkLog.h" #include "RkWidget.h" #include "RkMainImpl.h" #include "RkPlatform.h" #include "RkEventQueue.h" #include #include RkMain::RkMainImpl::RkMainImpl(RkMain *interfaceMain) : inf_ptr{interfaceMain} , topWidget{nullptr} , eventQueue{std::make_unique()} { RK_UNUSED(inf_ptr); RK_LOG_DEBUG("called"); } RkMain::RkMainImpl::RkMainImpl(RkMain *interfaceMain, int argc, char **argv) : inf_ptr{interfaceMain} , topWidget{nullptr} , eventQueue{std::make_unique()} { RK_UNUSED(inf_ptr); RK_UNUSED(argc); RK_UNUSED(argv); RK_LOG_DEBUG("called"); } RkMain::RkMainImpl::~RkMainImpl() { delete topWidget; RK_LOG_DEBUG("called"); } bool RkMain::RkMainImpl::setTopLevelWidget(RkWidget* widget) { if (topWidget || !widget) return false; RK_LOG_DEBUG("set top window: " << topWidget); topWidget = widget; eventQueue->addObject(topWidget); return true; } RkWidget* RkMain::RkMainImpl::topLevelWidget(void) { return topWidget; } RkEventQueue* RkMain::RkMainImpl::getEventQueue() const { return eventQueue.get(); } int RkMain::RkMainImpl::exec(bool block) { if (!topLevelWidget()) { RK_LOG_ERROR("top window not defined"); return 1; } if (!block) { eventQueue->processQueue(); } else { for (; block ;) { eventQueue->processQueue(); if (topWidget->isClose()) break; std::this_thread::sleep_for(std::chrono::milliseconds(1)); } } return 0; } geonkick-2.10.2/src/redkite/src/RkModel.cpp000066400000000000000000000031431451017372400204410ustar00rootroot00000000000000/** * File name: RkModel.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkModel.h" #include "RkModelImpl.h" RkVariant RkModelItem::data(int dataType) const { return itemModel->itemData(getIndex(), dataType); } RkModel::RkModel(RkObject *parent) : RkObject(parent, std::make_unique(this, parent)) , impl_ptr{static_cast(o_ptr.get())} { } bool RkModel::isItemSelected(size_t index) const { return impl_ptr->isItemSelected(index); } void RkModel::selectItem(size_t index) { if (isValidIndex(index)) { impl_ptr->selectItem(index); action modelChanged(); action itemSelected(RkModelItem(this, index)); } } bool RkModel::isValidIndex(size_t index) const { return index < itemsNumber(); } geonkick-2.10.2/src/redkite/src/RkModelImpl.cpp000066400000000000000000000024231451017372400212630ustar00rootroot00000000000000/** * File name: RkModelImpl.h * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkModelImpl.h" RkModel::RkModelImpl::RkModelImpl(RkModel* interface, RkObject* parent) : RkObjectImpl(interface, parent) , inf_ptr{interface} , selectedIndex(-1) { } bool RkModel::RkModelImpl::isItemSelected(size_t index) const { return selectedIndex < inf_ptr->itemsNumber() && index == selectedIndex; } void RkModel::RkModelImpl::selectItem(size_t index) { selectedIndex = index; } geonkick-2.10.2/src/redkite/src/RkObject.cpp000066400000000000000000000064571451017372400206220ustar00rootroot00000000000000/** * File name: RkObject.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkObjectImpl.h" #include "RkLog.h" #include "RkEventQueue.h" RkObject::RkObject(RkObject *parent) : o_ptr{std::make_unique(this, parent)} { RK_LOG_DEBUG("called: " << this); if (parent) parent->addChild(this); } RkObject::RkObject(RkObject *parent, std::unique_ptr impl) : o_ptr{std::move(impl)} { RK_LOG_DEBUG("called: " << this); if (parent) parent->addChild(this); } RkObject::~RkObject() { RK_LOG_DEBUG("called: " << this); if (eventQueue()) eventQueue()->removeObject(this); o_ptr->removeChildrens(); } Rk::ObjectType RkObject::type() const { return o_ptr->getObjectType(); } RkObject* RkObject::parent() const { return o_ptr->parent(); } const std::unordered_set& RkObject::children() const { return o_ptr->getChildren(); } void RkObject::setEventQueue(RkEventQueue* queue) { o_ptr->setEventQueue(queue); } RkEventQueue* RkObject::eventQueue() const { return o_ptr->getEventQueue(); } void RkObject::event(RkEvent *event) { RK_UNUSED(event); } void RkObject::addShortcut(Rk::Key key, Rk::KeyModifiers modifier) { if (eventQueue()) eventQueue()->addShortcut(this, key, modifier); } void RkObject::removeShortcut(Rk::Key key, Rk::KeyModifiers modifier) { if (eventQueue()) eventQueue()->removeShortcut(this, key, modifier); } void RkObject::rk__add_observer(std::unique_ptr observer) { o_ptr->addObserver(std::move(observer)); } const std::vector>& RkObject::rk__observers() const { return o_ptr->observers(); } void RkObject::rk__add_bound_object(RkObject* obj) { o_ptr->addBoundObject(obj); } void RkObject::addChild(RkObject* child) { o_ptr->addChild(child); } void RkObject::removeChild(RkObject *child) { o_ptr->removeChild(child); } void RkObject::removeObservers(RkObject *obj) { o_ptr->removeObservers(obj); } void RkObject::removeBoundObject(RkObject *obj) { o_ptr->removeBoundObject(obj); } RkObject* RkObject::findObject(const std::string &name) const { if (eventQueue()) return eventQueue()->findObjectByName(name); return nullptr; } void RkObject::setName(const std::string &name) { o_ptr->setName(name); } std::string RkObject::name() const { return o_ptr->name(); } geonkick-2.10.2/src/redkite/src/RkObjectImpl.cpp000066400000000000000000000126061451017372400214350ustar00rootroot00000000000000/** * File name: RkObjectImpl.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2020 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkObjectImpl.h" #include "RkEventQueue.h" #include "RkObserver.h" RkObject::RkObjectImpl::RkObjectImpl(RkObject* interface, RkObject* parent, Rk::ObjectType type) : inf_ptr{interface} , objectType{type} , parentObject{parent} , eventQueue{nullptr} { RK_LOG_DEBUG("["<< this << "][inf: " << inf_ptr << "]: parent " << parent); } RkObject::RkObjectImpl::~RkObjectImpl() { RK_LOG_DEBUG("[" << this << "]: interface: " << inf_ptr); // Remove myself from bound objects. for (auto &obj: boundObjects) obj->removeObservers(inf_ptr); boundObjects.clear(); // Remove myself from the observers objects. for (const auto &o: observersList) { if (o->object()) o->object()->removeBoundObject(inf_ptr); } observersList.clear(); // Remove myself from the paren object. if (inf_ptr->parent()) inf_ptr->parent()->removeChild(inf_ptr); } void RkObject::RkObjectImpl::removeChildrens() { RK_LOG_DEBUG("size: " << objectChildren.size()); auto tmpChidlren = std::move(objectChildren); for (auto child: tmpChidlren) delete child; } RkObject* RkObject::RkObjectImpl::parent() const { return parentObject; } const std::unordered_set& RkObject::RkObjectImpl::getChildren() const { return objectChildren; } void RkObject::RkObjectImpl::setEventQueue(RkEventQueue *queue) { if (!eventQueue && queue) { eventQueue = queue; for (auto child: objectChildren) child->setEventQueue(eventQueue); } } RkEventQueue* RkObject::RkObjectImpl::getEventQueue() const { return eventQueue; } void RkObject::RkObjectImpl::addObserver(std::unique_ptr ob) { auto res = std::find(observersList.begin(), observersList.end(), ob); if (res == std::end(observersList)) observersList.push_back(std::move(ob)); } void RkObject::RkObjectImpl::removeObservers(RkObject *obj) { observersList.erase(std::remove_if(observersList.begin(), observersList.end(), [obj](const std::unique_ptr &o) { return o->object() != nullptr && o->object() == obj; }) , observersList.end()); } const std::vector>& RkObject::RkObjectImpl::observers() const { return observersList; } void RkObject::RkObjectImpl::addBoundObject(RkObject *obj) { auto res = std::find(std::begin(boundObjects), std::end(boundObjects), obj); if (res == std::end(boundObjects)) boundObjects.push_back(obj); } void RkObject::RkObjectImpl::removeBoundObject(RkObject *obj) { boundObjects.erase(std::remove_if(boundObjects.begin(), boundObjects.end(), [obj](RkObject *o) { if (o == obj) return true; return false; }) , boundObjects.end()); } void RkObject::RkObjectImpl::addChild(RkObject* child) { RK_LOG_DEBUG("add child: " << child); objectChildren.insert(child); if (eventQueue) { RK_LOG_DEBUG("add child to queue: " << child); eventQueue->addObject(child); } } void RkObject::RkObjectImpl::removeChild(RkObject* child) { RK_LOG_DEBUG("size: " << objectChildren.size()); RK_LOG_DEBUG("remove child: " << child); if (!objectChildren.empty()) { auto res = objectChildren.find(child); if (res != objectChildren.end()) { RK_LOG_DEBUG("erase child" << child); objectChildren.erase(child); } } } Rk::ObjectType RkObject::RkObjectImpl::getObjectType() const { return objectType; } void RkObject::RkObjectImpl::setName(const std::string &name) { objectName = name; } std::string RkObject::RkObjectImpl::name() const { return objectName; } geonkick-2.10.2/src/redkite/src/RkPainter.cpp000066400000000000000000000073101451017372400210030ustar00rootroot00000000000000/** * File name: RkPainter.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkPainter.h" #include "RkPainterImpl.h" RkPainter::RkPainter(RkCanvas *canvas) : o_ptr{std::make_unique(this, canvas)} { } RkPainter::~RkPainter() { } void RkPainter::drawText(int x, int y, const std::string &text) { if (!text.empty()) o_ptr->drawText(text, x, y); } void RkPainter::drawText(const RkPoint &p, const std::string &text) { drawText(p.x(), p.y(), text); } void RkPainter::drawText(const RkRect &rect, const std::string &text, Rk::Alignment alignment) { if (!text.empty()) { RkFont f = font(); int x; if (alignment == Rk::Alignment::AlignLeft) x = rect.left(); else if (alignment == Rk::Alignment::AlignCenter) x = rect.left() + (rect.width() - o_ptr->getTextWidth(text)) / 2; else x = rect.right() - o_ptr->getTextWidth(text); int y = rect.top() + f.size() + (rect.height() - f.size()) / 2; drawText(x, y, text); } } void RkPainter::drawImage(const RkImage &image, int x, int y) { if (!image.isNull()) o_ptr->drawImage(image, x, y); } void RkPainter::drawCircle(int x, int y, int radius) { if (radius > 0) o_ptr->drawEllipse(RkPoint(x, y), 2 * radius, 2 * radius); } void RkPainter::drawCircle(const RkPoint &p, int radius) { if (radius > 0) o_ptr->drawEllipse(p, 2 * radius, 2 * radius); } void RkPainter::drawLine(int x1, int y1, int x2, int y2) { if (RkPoint(x1, y1) != RkPoint(x2, y2)) drawLine(RkPoint(x1, y1), RkPoint(x2, y2)); } void RkPainter::drawLine(const RkPoint &p1, const RkPoint &p2) { if (p1 != p2) o_ptr->drawLine(p1, p2); } void RkPainter::drawRect(const RkRect &rect) { o_ptr->drawRect(rect); } void RkPainter::drawPolyline(const std::vector &points) { if (points.size() > 1) o_ptr->drawPolyline(points); } void RkPainter::fillRect(const RkRect &rect, const RkColor &color) { if (rect.area() > 0) o_ptr->fillRect(rect, color); } void RkPainter::applyAlpha(int alpha) { o_ptr->applyAlpha(alpha); } const RkPen& RkPainter::pen() const { return o_ptr->pen(); } void RkPainter::setPen(const RkPen &pen) { return o_ptr->setPen(pen); } const RkFont& RkPainter::font() const { return o_ptr->font(); } void RkPainter::setFont(const RkFont &font) { o_ptr->setFont(font); } void RkPainter::translate(const RkPoint &offset) { o_ptr->translate(offset); } void RkPainter::rotate(rk_real angle) { o_ptr->rotate(angle); } int RkPainter::getTextWidth(const std::string &text) const { return o_ptr->getTextWidth(text); } geonkick-2.10.2/src/redkite/src/RkPainterImpl.cpp000066400000000000000000000063471451017372400216360ustar00rootroot00000000000000/** * File name: RkPainterImpl.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkPainterImpl.h" #include "RkCairoGraphicsBackend.h" #include "RkLog.h" RkPainter::RkPainterImpl::RkPainterImpl(RkPainter* interface, RkCanvas* canvas) : inf_ptr{interface} #ifdef RK_GRAPHICS_CAIRO_BACKEND , backendGraphics{std::make_unique(canvas)} #else #error No graphics backend defined #endif { RK_UNUSED(inf_ptr); backendGraphics->setPen(painterPen); backendGraphics->setFont(painterFont); } RkPainter::RkPainterImpl::~RkPainterImpl() { } void RkPainter::RkPainterImpl::drawText(const std::string &text, int x, int y) { backendGraphics->drawText(text, x, y); } void RkPainter::RkPainterImpl::drawImage(const std::string &file, int x, int y) { backendGraphics->drawImage(file, x, y); } void RkPainter::RkPainterImpl::drawImage(const RkImage &image, int x, int y) { backendGraphics->drawImage(image, x, y); } void RkPainter::RkPainterImpl::drawEllipse(const RkPoint& p, int width, int height) { backendGraphics->drawEllipse(p, width, height); } void RkPainter::RkPainterImpl::drawLine(const RkPoint &p1, const RkPoint &p2) { backendGraphics->drawLine(p1, p2); } void RkPainter::RkPainterImpl::drawRect(const RkRect &rect) { backendGraphics->drawRect(rect); } void RkPainter::RkPainterImpl::drawPolyline(const std::vector &points) { backendGraphics->drawPolyLine(points); } void RkPainter::RkPainterImpl::fillRect(const RkRect &rect, const RkColor &color) { backendGraphics->fillRect(rect, color); } const RkPen& RkPainter::RkPainterImpl::pen() const { return painterPen; } void RkPainter::RkPainterImpl::setPen(const RkPen &pen) { backendGraphics->setPen(pen); painterPen = pen; } const RkFont& RkPainter::RkPainterImpl::font() const { return painterFont; } void RkPainter::RkPainterImpl::setFont(const RkFont &font) { painterFont = font; backendGraphics->setFont(painterFont); } void RkPainter::RkPainterImpl::translate(const RkPoint &offset) { backendGraphics->translate(offset); } void RkPainter::RkPainterImpl::rotate(rk_real angle) { backendGraphics->rotate(angle); } int RkPainter::RkPainterImpl::getTextWidth(const std::string &text) const { return backendGraphics->getTextWidth(text); } void RkPainter::RkPainterImpl::applyAlpha(int alpha) { backendGraphics->applyAlpha(alpha); } geonkick-2.10.2/src/redkite/src/RkProgressBar.cpp000066400000000000000000000055151451017372400216370ustar00rootroot00000000000000/** * File name: RkProgressBar.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkProgressBar.h" #include "RkProgressBarImpl.h" #include "RkLog.h" RkProgressBar::RkProgressBar(RkWidget *parent) : RkWidget(parent, std::make_unique(this, parent)) , impl_ptr{static_cast(o_ptr.get())} { show(); } int RkProgressBar::beginValue() const { return impl_ptr->beginValue(); } void RkProgressBar::setBeginValue(int value) { if (beginValue() != value) { impl_ptr->setBeginValue(value); update(); } } int RkProgressBar::endValue() const { return impl_ptr->endValue(); } void RkProgressBar::setEndValue(int value) { if (endValue() != value) { impl_ptr->setEndValue(value); update(); } } void RkProgressBar::setRange(int begin, int end) { bool toUpdate = false; if (beginValue() != begin) { impl_ptr->setBeginValue(begin); toUpdate = true; } if (endValue() != end) { toUpdate = true; impl_ptr->setEndValue(end); } if (toUpdate) update(); } Rk::Orientation RkProgressBar::orientation() const { return impl_ptr->orientation(); } void RkProgressBar::setOrientation(Rk::Orientation orientation) { if (orientation != impl_ptr->orientation()) { impl_ptr->setOrientation(orientation); update(); } } int RkProgressBar::value() const { return impl_ptr->value(); } void RkProgressBar::setValue(int value) { if (value != impl_ptr->value()) { impl_ptr->setValue(value); update(); } } void RkProgressBar::reset() { setValue(beginValue()); } void RkProgressBar::setProgressColor(const RkColor &color) { impl_ptr->setProgressColor(color); update(); } void RkProgressBar::paintEvent(RkPaintEvent *event) { impl_ptr->drawProgressBar(); } geonkick-2.10.2/src/redkite/src/RkProgressBarImpl.cpp000066400000000000000000000064731451017372400224650ustar00rootroot00000000000000/** * File name: RkProgressBarImpl.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkProgressBarImpl.h" #include "RkPainter.h" #include "RkImage.h" #include "RkLog.h" RkProgressBar::RkProgressBarImpl::RkProgressBarImpl(RkProgressBar *interface, RkWidget *parent) : RkWidgetImpl(static_cast(interface), parent) , inf_ptr{interface} , beginVal{0} , endVal{100} , currentVal{beginVal} , progressColor{52, 116, 209} , progressOrientation{Rk::Orientation::Horizontal} { } RkProgressBar::RkProgressBarImpl::~RkProgressBarImpl() { } int RkProgressBar::RkProgressBarImpl::beginValue() const { return beginVal; } void RkProgressBar::RkProgressBarImpl::setBeginValue(int val) { if (currentVal < val) currentVal = val; beginVal = val; } int RkProgressBar::RkProgressBarImpl::endValue() const { return endVal; } void RkProgressBar::RkProgressBarImpl::setEndValue(int val) { if (currentVal > val) currentVal = val; endVal = val; } Rk::Orientation RkProgressBar::RkProgressBarImpl::orientation() const { return progressOrientation; } void RkProgressBar::RkProgressBarImpl::setOrientation(Rk::Orientation orientation) { progressOrientation = orientation; } int RkProgressBar::RkProgressBarImpl::value() const { return currentVal; } void RkProgressBar::RkProgressBarImpl::setValue(int val) { if (val > endVal) currentVal = endVal; else if (val < beginVal) currentVal = beginVal; else currentVal = val; } void RkProgressBar::RkProgressBarImpl::setProgressColor(const RkColor &color) { progressColor = color; } void RkProgressBar::RkProgressBarImpl::drawProgressBar() { RkSize thisSize = size(); RkImage img(thisSize); RkPainter painter(&img); painter.fillRect(rect(), background()); int range = std::abs(endVal - beginVal); if (range > 0 && thisSize.width() > 0 && thisSize.height() > 0) { if (progressOrientation == Rk::Orientation::Horizontal) painter.fillRect(RkRect(0, 0, currentVal * thisSize.width() / range, thisSize.height()), progressColor); else painter.fillRect(RkRect(0, 0, thisSize.width(), currentVal * thisSize.height() / range), progressColor); RkPainter paint(inf_ptr); paint.drawImage(img, 0, 0); } } geonkick-2.10.2/src/redkite/src/RkTimer.cpp000066400000000000000000000041061451017372400204610ustar00rootroot00000000000000/** * File name: RkTimer.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkTimer.h" #include "RkEventQueue.h" #include RkTimer::RkTimer(RkObject *parent, int interval) : RkObject(parent) , timerInterval{interval} , timerStarted{false} , lastTime{-1} { if (eventQueue()) eventQueue()->subscribeTimer(this); } RkTimer::~RkTimer() { if (eventQueue()) eventQueue()->unsubscribeTimer(this); } void RkTimer::start() { timerStarted = true; lastTime = getCurrentTime(); } bool RkTimer::started() const { return timerStarted; } void RkTimer::stop() { timerStarted = false; } long int RkTimer::interval() const { return timerInterval; } void RkTimer::setInterval(int val) { timerInterval = val; } bool RkTimer::isTimeout() const { if (lastTime < 0 || timerInterval < 0) return false; if (getCurrentTime() - lastTime > timerInterval) return true; return false; } long int RkTimer::getCurrentTime() const { return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); } void RkTimer::callTimeout() { timeout(); lastTime = getCurrentTime(); } geonkick-2.10.2/src/redkite/src/RkWidget.cpp000066400000000000000000000354461451017372400206370ustar00rootroot00000000000000/** * File name: RkWidget.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkLog.h" #include "RkWidget.h" #include "RkEvent.h" #include "RkWidgetImpl.h" #include "RkPlatform.h" #include "RkMain.h" RkWidget::RkWidget(RkMain *mainApp, Rk::WindowFlags flags) : RkObject(nullptr, std::make_unique(this, nullptr, flags, true)) , impl_ptr{static_cast(o_ptr.get())} { RK_LOG_DEBUG("called: " << this); mainApp->setTopLevelWidget(this); } RkWidget::RkWidget(RkMain *mainApp, const RkNativeWindowInfo &parent, Rk::WindowFlags flags) : RkObject(nullptr, std::make_unique(this, parent, flags, true)) , impl_ptr{static_cast(o_ptr.get())} { RK_LOG_DEBUG("called: " << this); mainApp->setTopLevelWidget(this); } RkWidget::RkWidget(RkWidget *parent, Rk::WindowFlags flags) : RkObject(parent, std::make_unique(this, parent, flags)) , impl_ptr{static_cast(o_ptr.get())} { RK_LOG_DEBUG("called: " << this); if (modality() == Rk::Modality::ModalTopWidget) { auto topWidget = getTopWidget(); if (topWidget) topWidget->disableInput(); else if (parentWidget() && modality() == Rk::Modality::ModalParent) parentWidget()->disableInput(); } } RkWidget::RkWidget(RkWidget *parent, std::unique_ptr impl) : RkObject(parent, std::move(impl)) , impl_ptr{static_cast(o_ptr.get())} { if (modality() == Rk::Modality::ModalTopWidget) { auto topWidget = getTopWidget(); if (topWidget) topWidget->disableInput(); else if (parentWidget() && modality() == Rk::Modality::ModalParent) parentWidget()->disableInput(); } } RkWidget::~RkWidget() { RK_LOG_DEBUG("called: " << this); if (parentWidget()) { if (modality() == Rk::Modality::ModalTopWidget) { if (!parentWidget()->isModal()) { auto topWidget = getTopWidget(); if (topWidget) topWidget->enableInput(); } else { // Enable inputs only for parent widget and its // childs since it is modal. parentWidget()->enableInput(); } } else if (modality() == Rk::Modality::ModalParent) { parentWidget()->enableInput(); } } } Rk::WindowFlags RkWidget::windowFlags() const { return impl_ptr->windowFlags(); } void RkWidget::setTitle(const std::string &title) { impl_ptr->setTitle(title); } const std::string& RkWidget::title() const { return impl_ptr->title(); } void RkWidget::show(bool b) { impl_ptr->show(b); } bool RkWidget::isShown() const { return impl_ptr->isShown(); } void RkWidget::hide() { impl_ptr->show(false); } const RkNativeWindowInfo* RkWidget::nativeWindowInfo() const { return impl_ptr->nativeWindowInfo(); } RkWindowId RkWidget::id() const { return impl_ptr->id(); } RkWidget* RkWidget::parentWidget() const { return dynamic_cast(parent()); } bool RkWidget::isClose() const { return impl_ptr->isClose(); } void RkWidget::setSize(int w, int h) { if (w > maximumWidth()) w = maximumWidth(); else if (w < minimumWidth()) w = minimumWidth(); if (h > maximumHeight()) h = maximumHeight(); else if (h < minimumHeight()) h = minimumHeight(); impl_ptr->setSize(RkSize(w, h)); } void RkWidget::setSize(const RkSize &size) { impl_ptr->setSize(size); } RkSize RkWidget::size() const { return impl_ptr->size(); } void RkWidget::setWidth(int w) { if (w > maximumWidth()) impl_ptr->setSize(RkSize(maximumWidth(), impl_ptr->size().height())); else if (w < minimumWidth()) impl_ptr->setSize(RkSize(minimumWidth(), impl_ptr->size().height())); else impl_ptr->setSize(RkSize(w, impl_ptr->size().height())); } int RkWidget::width() const { return impl_ptr->size().width(); } int RkWidget::minimumWidth() const { return impl_ptr->minimumWidth(); } int RkWidget::maximumWidth() const { return impl_ptr->maximumWidth(); } void RkWidget::setHeight(int h) { if (h > maximumHeight()) impl_ptr->setSize(RkSize(impl_ptr->size().width(), maximumHeight())); else if (h < minimumHeight()) impl_ptr->setSize(RkSize(impl_ptr->size().width(), minimumHeight())); else impl_ptr->setSize(RkSize(impl_ptr->size().width(), h)); } int RkWidget::height() const { return impl_ptr->size().height(); } int RkWidget::minimumHeight() const { return impl_ptr->minimumHeight(); } int RkWidget::maximumHeight() const { return impl_ptr->maximumHeight(); } void RkWidget::setMinimumWidth(int width) { impl_ptr->setMinimumWidth(width); } void RkWidget::setMinimumHeight(int height) { impl_ptr->setMinimumHeight(height); } void RkWidget::setMaximumWidth(int width) { impl_ptr->setMaximumWidth(width); } void RkWidget::setMaximumHeight(int height) { impl_ptr->setMaximumHeight(height); } void RkWidget::setFixedWidth(int width) { setMinimumWidth(width); setMaximumWidth(width); setWidth(width); } void RkWidget::setFixedHeight(int height) { setMinimumHeight(height); setMaximumHeight(height); setHeight(height); } void RkWidget::setFixedSize(const RkSize &size) { setMinimumWidth(size.width()); setMaximumWidth(size.width()); setMinimumHeight(size.height()); setMaximumHeight(size.height()); setSize(size); } void RkWidget::setFixedSize(int width, int height) { setFixedSize(RkSize(width, height)); } int RkWidget::x() const { return impl_ptr->position().x(); } void RkWidget::setX(int x) { impl_ptr->setPosition(RkPoint(x, impl_ptr->position().y())); } int RkWidget::y() const { return impl_ptr->position().y(); } void RkWidget::setY(int y) { return impl_ptr->setPosition(RkPoint(impl_ptr->position().x(), y)); } void RkWidget::setPosition(int x, int y) { impl_ptr->setPosition(RkPoint(x, y)); } void RkWidget::setPosition(const RkPoint &p) { impl_ptr->setPosition(p); } RkPoint RkWidget::position() const { return impl_ptr->position(); } void RkWidget::setBorderWidth(int width) { impl_ptr->setBorderWidth(width); } int RkWidget::borderWidth() const { return impl_ptr->borderWidth(); } void RkWidget::setBorderColor(int red, int green, int blue) { if (red > 255) red = 255; if (red < 0) red = 0; if (green > 255) green = 255; if (green < 0) green = 0; if (blue > 255) blue = 255; if (blue < 0) blue = 0; impl_ptr->setBorderColor(RkColor(red, green, blue)); } const RkColor& RkWidget::borderColor() const { return impl_ptr->borderColor(); } void RkWidget::setBackgroundColor(int red, int green, int blue) { if (red > 255) red = 255; if (red < 0) red = 0; if (green > 255) green = 255; if (green < 0) green = 0; if (blue > 255) blue = 255; if (blue < 0) blue = 0; impl_ptr->setBackgroundColor(RkColor(red, green, blue)); } void RkWidget::setBackgroundColor(const RkColor &color) { impl_ptr->setBackgroundColor(color); } const RkColor& RkWidget::background() const { return impl_ptr->background(); } void RkWidget::setTextColor(const RkColor &color) { return impl_ptr->setTextColor(color); } const RkColor& RkWidget::textColor() const { return impl_ptr->textColor(); } const RkColor& RkWidget::color() const { return impl_ptr->color(); } void RkWidget::setColor(const RkColor &color) { return impl_ptr->setColor(color); } const RkFont& RkWidget::font() const { return impl_ptr->font(); } void RkWidget::setFont(const RkFont &font) { impl_ptr->setFont(font); } void RkWidget::enableInput() { setWidgetAttribute(static_cast(static_cast(Rk::WidgetAttribute::KeyInputEnabled) | static_cast(Rk::WidgetAttribute::MouseInputEnabled) | static_cast(Rk::WidgetAttribute::CloseInputEnabled))); for (const auto &ch: children()) { auto widget = dynamic_cast(ch); if (widget) widget->enableInput(); } } void RkWidget::disableInput() { if (modality() == Rk::Modality::ModalParent || modality() == Rk::Modality::ModalTopWidget) return; clearWidgetAttribute(static_cast(static_cast(Rk::WidgetAttribute::KeyInputEnabled) | static_cast(Rk::WidgetAttribute::MouseInputEnabled) | static_cast(Rk::WidgetAttribute::CloseInputEnabled))); for (const auto &ch: children()) { auto widget = dynamic_cast(ch); if (widget) widget->disableInput(); } } bool RkWidget::isInputEnabled() const { return static_cast(widgetAttributes()) & static_cast(Rk::WidgetAttribute::KeyInputEnabled); } void RkWidget::enableGrabKey(bool b) { // For now only for top level window. if (!parentWidget()) impl_ptr->enableGrabKey(b); } bool RkWidget::grabKeyEnabled() const { return impl_ptr->grabKeyEnabled(); } void RkWidget::propagateGrabKey(bool b) { impl_ptr->propagateGrabKey(b); } bool RkWidget::propagateGrabKeyEnabled() const { return impl_ptr->propagateGrabKeyEnabled(); } void RkWidget::event(RkEvent *event) { impl_ptr->event(event); RkObject::event(event); } void RkWidget::closeEvent([[maybe_unused]] RkCloseEvent *event) { if (parentWidget()) { auto event = std::make_unique(parentWidget(), this); eventQueue()->postEvent(parentWidget(), std::move(event)); } } void RkWidget::keyPressEvent(RkKeyEvent *event) { RK_UNUSED(event); } void RkWidget::keyReleaseEvent(RkKeyEvent *event) { RK_UNUSED(event); } void RkWidget::shortcutEvent(RkKeyEvent *event) { RK_UNUSED(event); } void RkWidget::mouseMoveEvent(RkMouseEvent *event) { RK_UNUSED(event); } void RkWidget::mouseButtonPressEvent(RkMouseEvent *event) { RK_UNUSED(event); } void RkWidget::mouseButtonReleaseEvent(RkMouseEvent *event) { RK_UNUSED(event); } void RkWidget::mouseDoubleClickEvent(RkMouseEvent *event) { mouseButtonPressEvent(event); } void RkWidget::wheelEvent(RkWheelEvent *event) { RK_UNUSED(event); } void RkWidget::dropEvent(RkDropEvent *event) { if (parentWidget()) getTopWidget()->dropEvent(event); } void RkWidget::moveEvent(RkMoveEvent *event) { RK_UNUSED(event); } void RkWidget::resizeEvent(RkResizeEvent *event) { RK_UNUSED(event); } void RkWidget::paintEvent(RkPaintEvent *event) { RK_UNUSED(event); } void RkWidget::showEvent(RkShowEvent *event) { RK_UNUSED(event); } void RkWidget::hideEvent(RkHideEvent *event) { RK_UNUSED(event); } void RkWidget::focusEvent([[maybe_unused]] RkFocusEvent *event) { update(); } void RkWidget::hoverEvent(RkHoverEvent *event) { RK_UNUSED(event); } void RkWidget::update() { impl_ptr->update(); } const RkCanvasInfo* RkWidget::getCanvasInfo() const { return impl_ptr->getCanvasInfo(); } RkRect RkWidget::rect() const { return impl_ptr->rect(); } void RkWidget::close() { eventQueue()->postEvent(this, std::make_unique()); } bool RkWidget::isModal() const { return modality() != Rk::Modality::NonModal; } Rk::Modality RkWidget::modality() const { return impl_ptr->modality(); } void RkWidget::setWidgetAttribute(Rk::WidgetAttribute attribute) { impl_ptr->setWidgetAttribute(attribute); } void RkWidget::clearWidgetAttribute(Rk::WidgetAttribute attribute) { impl_ptr->clearWidgetAttribute(attribute); } Rk::WidgetAttribute RkWidget::widgetAttributes() const { return impl_ptr->getWidgetAttributes(); } RkWidget* RkWidget::getTopWidget() { if (!parentWidget()) return this; return parentWidget()->getTopWidget(); } bool RkWidget::isTopWindow() const { return !parentWidget(); } void RkWidget::setFocus(bool b) { impl_ptr->setFocus(b); } bool RkWidget::hasFocus() const { return impl_ptr->hasFocus(); } void RkWidget::setPointerShape(Rk::PointerShape shape) { impl_ptr->setPointerShape(shape); } Rk::PointerShape RkWidget::pointerShape() const { return impl_ptr->pointerShape(); } void RkWidget::setScaleFactor(double factor) { impl_ptr->setScaleFactor(factor); for (const auto &ch: children()) { auto widget = dynamic_cast(ch); if (widget) widget->setScaleFactor(factor); } if (this == getTopWidget()) eventQueue()->setScaleFactor(factor); update(); } double RkWidget::scaleFactor() const { return impl_ptr->scaleFactor(); } bool RkWidget::pointerIsOverWindow() const { return impl_ptr->pointerIsOverWindow(); } bool RkWidget::isChild(RkWidget *widget) { for (const auto &child: children()) { if (widget == dynamic_cast(child)) return true; } return false; } geonkick-2.10.2/src/redkite/src/RkWidgetImpl.cpp000066400000000000000000000344371451017372400214600ustar00rootroot00000000000000/** * File name: RkWidgetImpl.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkWidgetImpl.h" #include "RkEvent.h" #include "RkPainter.h" #ifdef RK_OS_WIN #include "RkWindowWin.h" #elif RK_OS_MAC #include "RkWindowMac.h" #else // X11 #include "RkWindowX.h" #undef KeyPress #undef KeyRelease #undef Paint #undef FocusIn #undef FocusOut #endif RkWidget::RkWidgetImpl::RkWidgetImpl(RkWidget* widgetInterface, RkWidget* parent, Rk::WindowFlags flags, bool isTopWindow) : RkObject::RkObjectImpl(widgetInterface, parent, Rk::ObjectType::Widget) , inf_ptr{widgetInterface} #ifdef RK_OS_WIN , platformWindow{!parent ? std::make_unique(nullptr, flags) : std::make_unique(parent->nativeWindowInfo(), flags, isTopWindow)} #elif RK_OS_MAC , platformWindow{!parent ? std::make_unique(nullptr, flags) : std::make_unique(parent->nativeWindowInfo(), flags, isTopWindow)} #else // X11 , platformWindow{!parent ? std::make_unique(nullptr, flags) : std::make_unique(parent->nativeWindowInfo(), flags, isTopWindow)} #endif , widgetClosed{false} , widgetMinimumSize{0, 0} , widgetMaximumSize{1000000, 1000000} , widgetSize{platformWindow->size()} , widgetBackground(platformWindow->background()) , widgetAttributes{defaultWidgetAttributes()} , widgetModality{(static_cast(flags) & static_cast(Rk::WindowFlags::Dialog)) ? Rk::Modality::ModalTopWidget : Rk::Modality::NonModal} , widgetTextColor{0, 0, 0} , widgetDrawingColor{0, 0, 0} , widgetPointerShape{Rk::PointerShape::Arrow} , isWidgetSown{false} , isGrabKeyEnabled{false} , isPropagateGrabKey{true} { RK_LOG_DEBUG("called"); platformWindow->init(); } RkWidget::RkWidgetImpl::RkWidgetImpl(RkWidget* widgetInterface, const RkNativeWindowInfo &parent, Rk::WindowFlags flags, bool isTopWindow) : RkObject::RkObjectImpl(widgetInterface, nullptr, Rk::ObjectType::Widget) , inf_ptr{widgetInterface} #ifdef RK_OS_WIN , platformWindow{std::make_unique(parent, flags, isTopWindow)} #elif RK_OS_MAC , platformWindow{std::make_unique(parent, flags, isTopWindow)} #else // X11 , platformWindow{std::make_unique(parent, flags, isTopWindow)} #endif , widgetClosed{false} , widgetMinimumSize{0, 0} , widgetMaximumSize{1000000, 1000000} , widgetSize{platformWindow->size()} , widgetAttributes{defaultWidgetAttributes()} , widgetModality{(static_cast(flags) & static_cast(Rk::WindowFlags::Dialog)) ? Rk::Modality::ModalTopWidget : Rk::Modality::NonModal} , widgetTextColor{0, 0, 0} , widgetDrawingColor{0, 0, 0} , widgetPointerShape{Rk::PointerShape::Arrow} , isGrabKeyEnabled{false} { RK_LOG_DEBUG("called"); platformWindow->init(); } RkWidget::RkWidgetImpl::~RkWidgetImpl() { RK_LOG_DEBUG("called"); } Rk::WindowFlags RkWidget::RkWidgetImpl::windowFlags() const { return platformWindow->flags(); } Rk::WidgetAttribute RkWidget::RkWidgetImpl::defaultWidgetAttributes() { return static_cast(static_cast(Rk::WidgetAttribute::KeyInputEnabled) | static_cast(Rk::WidgetAttribute::MouseInputEnabled) | static_cast(Rk::WidgetAttribute::CloseInputEnabled)); } void RkWidget::RkWidgetImpl::show(bool b) { isWidgetSown = b; platformWindow->show(isWidgetSown); } bool RkWidget::RkWidgetImpl::isShown() const { return isWidgetSown; } void RkWidget::RkWidgetImpl::setTitle(const std::string &title) { widgetTitle = title; platformWindow->setTitle(widgetTitle); } const std::string& RkWidget::RkWidgetImpl::title() const { return widgetTitle; } const RkNativeWindowInfo* RkWidget::RkWidgetImpl::nativeWindowInfo() const { return platformWindow->nativeWindowInfo(); } bool RkWidget::RkWidgetImpl::isClose() const { return widgetClosed; } RkWindowId RkWidget::RkWidgetImpl::id() const { return platformWindow->id(); } void RkWidget::RkWidgetImpl::event(RkEvent *event) { switch (event->type()) { case RkEvent::Type::Paint: inf_ptr->paintEvent(static_cast(event)); break; case RkEvent::Type::KeyPressed: RK_LOG_DEBUG("RkEvent::Type::KeyPressed: " << title()); if (static_cast(widgetAttributes) & static_cast(Rk::WidgetAttribute::KeyInputEnabled)) { auto keyEvent = static_cast(event); if (keyEvent->isShortcut()) inf_ptr->shortcutEvent(keyEvent); else inf_ptr->keyPressEvent(keyEvent); } break; case RkEvent::Type::KeyReleased: RK_LOG_DEBUG("RkEvent::Type::KeyReleased: " << title()); if (static_cast(widgetAttributes) & static_cast(Rk::WidgetAttribute::KeyInputEnabled)) { auto keyEvent = static_cast(event); if (keyEvent->isShortcut()) inf_ptr->shortcutEvent(keyEvent); else inf_ptr->keyReleaseEvent(keyEvent); } break; case RkEvent::Type::FocusedIn: RK_LOG_DEBUG("RkEvent::Type::FocsedIn:" << title()); inf_ptr->focusEvent(static_cast(event)); break; case RkEvent::Type::FocusedOut: RK_LOG_DEBUG("RkEvent::Type::FocsedOut: " << title()); inf_ptr->focusEvent(static_cast(event)); break; case RkEvent::Type::MouseButtonPress: RK_LOG_DEBUG("RkEvent::Type::MouseButtonPress: " << title()); if (static_cast(widgetAttributes) & static_cast(Rk::WidgetAttribute::MouseInputEnabled)) inf_ptr->mouseButtonPressEvent(static_cast(event)); break; case RkEvent::Type::MouseDoubleClick: RK_LOG_DEBUG("RkEvent::Type::MouseDoubleClick:" << title()); if (static_cast(widgetAttributes) & static_cast(Rk::WidgetAttribute::MouseInputEnabled)) inf_ptr->mouseDoubleClickEvent(static_cast(event)); break; case RkEvent::Type::MouseButtonRelease: RK_LOG_DEBUG("RkEvent::Type::MouseButtonRelease:" << title()); if (static_cast(widgetAttributes) & static_cast(Rk::WidgetAttribute::MouseInputEnabled)) inf_ptr->mouseButtonReleaseEvent(static_cast(event)); break; case RkEvent::Type::MouseMove: if (static_cast(widgetAttributes) & static_cast(Rk::WidgetAttribute::MouseInputEnabled)) inf_ptr->mouseMoveEvent(static_cast(event)); break; case RkEvent::Type::Drop: RK_LOG_DEBUG("RkEvent::Type::Drop:" << title()); if (static_cast(widgetAttributes) & static_cast(Rk::WidgetAttribute::MouseInputEnabled)) inf_ptr->dropEvent(static_cast(event)); break; case RkEvent::Type::Hover: RK_LOG_DEBUG("RkEvent::Type::Hover:" << title()); inf_ptr->hoverEvent(static_cast(event)); break; case RkEvent::Type::Resize: widgetSize = platformWindow->size(); platformWindow->resizeCanvas(); inf_ptr->resizeEvent(static_cast(event)); break; case RkEvent::Type::Show: isWidgetSown = true; inf_ptr->showEvent(static_cast(event)); break; case RkEvent::Type::Hide: isWidgetSown = false; inf_ptr->hideEvent(static_cast(event)); break; case RkEvent::Type::DeleteChild: RK_LOG_DEBUG("RkEvent::Type::DeleteChild:" << title()); delete static_cast(event)->child(); break; case RkEvent::Type::Close: RK_LOG_DEBUG("RkEvent::Type::Close"); if (static_cast(widgetAttributes) & static_cast(Rk::WidgetAttribute::CloseInputEnabled)) { widgetClosed = true; inf_ptr->closeEvent(static_cast(event)); } break; default: break; RK_LOG_DEBUG("RkEvent::Type::Unknown:" << title()); } } void RkWidget::RkWidgetImpl::setSize(const RkSize &size) { if (size.width() > 1 && size.height() > 1) platformWindow->setSize(size); widgetSize = size; } RkSize RkWidget::RkWidgetImpl::size() const { return widgetSize; } int RkWidget::RkWidgetImpl::minimumWidth() const { return widgetMinimumSize.width(); } int RkWidget::RkWidgetImpl::maximumWidth() const { return widgetMaximumSize.width(); } int RkWidget::RkWidgetImpl::minimumHeight() const { return widgetMinimumSize.height(); } int RkWidget::RkWidgetImpl::maximumHeight() const { return widgetMaximumSize.height(); } void RkWidget::RkWidgetImpl::setMinimumWidth(int width) { widgetMinimumSize.setWidth(width); } void RkWidget::RkWidgetImpl::setMaximumWidth(int width) { widgetMaximumSize.setWidth(width); } void RkWidget::RkWidgetImpl::setMinimumHeight(int height) { widgetMinimumSize.setHeight(height); } void RkWidget::RkWidgetImpl::setMaximumHeight(int height) { widgetMaximumSize.setHeight(height); } void RkWidget::RkWidgetImpl::setPosition(const RkPoint &position) { platformWindow->setPosition(position); } RkPoint RkWidget::RkWidgetImpl::position() const { return platformWindow->position(); } void RkWidget::RkWidgetImpl::setBorderWidth(int width) { platformWindow->setBorderWidth(width); } int RkWidget::RkWidgetImpl::borderWidth() const { return platformWindow->borderWidth(); } void RkWidget::RkWidgetImpl::setBorderColor(const RkColor &color) { platformWindow->setBorderColor(color); } const RkColor& RkWidget::RkWidgetImpl::borderColor() const { return platformWindow->borderColor(); } void RkWidget::RkWidgetImpl::setBackgroundColor(const RkColor &color) { platformWindow->setBackgroundColor(color); widgetBackground = color; } const RkColor& RkWidget::RkWidgetImpl::background() const { return widgetBackground; } RkRect RkWidget::RkWidgetImpl::rect() const { return RkRect(RkPoint(0, 0), size()); } const RkCanvasInfo* RkWidget::RkWidgetImpl::getCanvasInfo() const { return platformWindow->getCanvasInfo(); } void RkWidget::RkWidgetImpl::update() { platformWindow->update(); } Rk::Modality RkWidget::RkWidgetImpl::modality() const { return widgetModality; } void RkWidget::RkWidgetImpl::setWidgetAttribute(Rk::WidgetAttribute attribute) { widgetAttributes = static_cast(static_cast(widgetAttributes) | static_cast(attribute)); } void RkWidget::RkWidgetImpl::clearWidgetAttribute(Rk::WidgetAttribute attribute) { widgetAttributes = static_cast(static_cast(widgetAttributes) & (~static_cast(attribute))); } Rk::WidgetAttribute RkWidget::RkWidgetImpl::getWidgetAttributes() const { return widgetAttributes; } void RkWidget::RkWidgetImpl::setFocus(bool b) { platformWindow->setFocus(b); } bool RkWidget::RkWidgetImpl::hasFocus() const { return platformWindow->hasFocus(); } void RkWidget::RkWidgetImpl::setTextColor(const RkColor &color) { widgetTextColor = color; } const RkColor& RkWidget::RkWidgetImpl::textColor() const { return widgetTextColor; } const RkColor& RkWidget::RkWidgetImpl::color() const { return widgetDrawingColor; } void RkWidget::RkWidgetImpl::setColor(const RkColor &color) { widgetDrawingColor = color; } const RkFont& RkWidget::RkWidgetImpl::font() const { return widgetFont; } void RkWidget::RkWidgetImpl::setFont(const RkFont &font) { widgetFont = font; } void RkWidget::RkWidgetImpl::setPointerShape(Rk::PointerShape shape) { if (widgetPointerShape != shape) { widgetPointerShape = shape; platformWindow->setPointerShape(widgetPointerShape); } } Rk::PointerShape RkWidget::RkWidgetImpl::pointerShape() const { return widgetPointerShape; } void RkWidget::RkWidgetImpl::enableGrabKey(bool b) { isGrabKeyEnabled = b; } bool RkWidget::RkWidgetImpl::grabKeyEnabled() const { return isGrabKeyEnabled; } void RkWidget::RkWidgetImpl::propagateGrabKey(bool b) { isPropagateGrabKey = b; } bool RkWidget::RkWidgetImpl::propagateGrabKeyEnabled() const { return isPropagateGrabKey; } bool RkWidget::RkWidgetImpl::pointerIsOverWindow() const { return platformWindow->pointerIsOverWindow(); } void RkWidget::RkWidgetImpl::setScaleFactor(double factor) { platformWindow->setScaleFactor(factor); } double RkWidget::RkWidgetImpl::scaleFactor() const { return platformWindow->getScaleFactor(); } geonkick-2.10.2/src/redkite/src/platforms/000077500000000000000000000000001451017372400204065ustar00rootroot00000000000000geonkick-2.10.2/src/redkite/src/platforms/xwin/000077500000000000000000000000001451017372400213735ustar00rootroot00000000000000geonkick-2.10.2/src/redkite/src/platforms/xwin/RkEventQueueX.cpp000066400000000000000000000231461451017372400246200ustar00rootroot00000000000000/** * File name: RkQueueX.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkWidget.h" #include "RkEventQueueX.h" #include "RkLog.h" #include #include #include RkEventQueueX::RkEventQueueX() : xDisplay{nullptr} , keyModifiers{0} , scaleFactor{1} { RK_LOG_DEBUG("called"); } RkEventQueueX::~RkEventQueueX() { RK_LOG_DEBUG("called"); } bool RkEventQueueX::pending() const { if (xDisplay) return XPending(xDisplay) > 0; return false; } void RkEventQueueX::setDisplay(Display *display) { xDisplay = display; } Display* RkEventQueueX::display() const { return xDisplay; } std::vector>> RkEventQueueX::getEvents() { std::vector>> events; while (pending()) { XEvent e; XNextEvent(xDisplay, &e); std::unique_ptr event = nullptr; switch (e.type) { case Expose: if (reinterpret_cast(&e)->count == 0) event = std::make_unique(); break; case KeyPress: event = getKeyEvent(&e); break; case KeyRelease: event = getKeyEvent(&e); break; case FocusIn: case FocusOut: event = getFocusEvent(&e); break; case ButtonPress: event = getButtonPressEvent(&e); break; case ButtonRelease: event = std::make_unique(); event->setType(RkEvent::Type::MouseButtonRelease); break; case MotionNotify: event = getMouseMove(&e); break; case ConfigureNotify: event = std::make_unique(); break; case EnterNotify: case LeaveNotify: { auto hoveEvent = std::make_unique(); hoveEvent->setHover(e.type == EnterNotify); event = std::move(hoveEvent); break; } case ClientMessage: { auto atom = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True); if (static_cast(e.xclient.data.l[0]) == atom) event = std::make_unique(); break; } default: break; } if (event) { auto id = rk_id_from_x11(reinterpret_cast(&e)->window); std::pair> pair(id, std::move(event)); events.push_back(std::move(pair)); } } return events; } std::unique_ptr RkEventQueueX::getButtonPressEvent(XEvent *e) { auto buttonEvent = reinterpret_cast(e); auto mouseEvent = std::make_unique(); mouseEvent->setTime(std::chrono::system_clock::time_point(std::chrono::milliseconds(buttonEvent->time))); mouseEvent->setX(buttonEvent->x / scaleFactor); mouseEvent->setY(buttonEvent->y / scaleFactor); switch (buttonEvent->button) { case Button1: mouseEvent->setButton(RkMouseEvent::ButtonType::Left); break; case Button2: mouseEvent->setButton(RkMouseEvent::ButtonType::Middle); break; case Button3: mouseEvent->setButton(RkMouseEvent::ButtonType::Right); break; case Button4: mouseEvent->setButton(RkMouseEvent::ButtonType::WheelUp); break; case Button5: mouseEvent->setButton(RkMouseEvent::ButtonType::WheelDown); break; default: mouseEvent->setButton(RkMouseEvent::ButtonType::Unknown); } auto diff = mouseEvent->time().time_since_epoch() - lastTimePressed.time_since_epoch(); if (std::chrono::duration_cast(diff).count() < 300000) mouseEvent->setType(RkEvent::Type::MouseDoubleClick); lastTimePressed = mouseEvent->time(); return mouseEvent; } std::unique_ptr RkEventQueueX::getMouseMove(XEvent *e) { auto buttonEvent = reinterpret_cast(e); auto mouseEvent = std::make_unique(); mouseEvent->setTime(std::chrono::system_clock::time_point(std::chrono::milliseconds(buttonEvent->time))); mouseEvent->setType(RkEvent::Type::MouseMove); mouseEvent->setX(buttonEvent->x / scaleFactor); mouseEvent->setY(buttonEvent->y / scaleFactor); return mouseEvent; } std::unique_ptr RkEventQueueX::getKeyEvent(XEvent *e) { auto event = std::make_unique(); event->setType(e->type == KeyPress ? RkEvent::Type::KeyPressed : RkEvent::Type::KeyReleased); auto keyCode = XkbKeycodeToKeysym(xDisplay, reinterpret_cast(e)->keycode, 0, keyModifiers & static_cast(Rk::KeyModifiers::Shift) ? 1 : 0); event->setKey(fromKeysym(keyCode)); updateModifiers(event->key(), event->type()); if (keyModifiers != static_cast(Rk::KeyModifiers::NoModifier)) event->setModifiers(keyModifiers); return event; } void RkEventQueueX::updateModifiers(Rk::Key key, RkEvent::Type type) { if (key == Rk::Key::Key_Shift_Left || key == Rk::Key::Key_Shift_Right || key == Rk::Key::Key_Control_Left || key == Rk::Key::Key_Control_Right) { if (type == RkEvent::Type::KeyPressed) keyModifiers |= static_cast(key) >> 16; else keyModifiers &= ~(static_cast(key) >> 16); } } Rk::Key RkEventQueueX::fromKeysym(int keycode) const { // Latine1 if (0x00000020 <= keycode && keycode <= 0x000000ff) return static_cast(keycode); switch (keycode) { // Key modifiers case XK_Shift_L: return Rk::Key::Key_Shift_Left; case XK_Shift_R: return Rk::Key::Key_Shift_Right; case XK_Control_L: return Rk::Key::Key_Control_Left; case XK_Control_R: return Rk::Key::Key_Control_Right; case XK_Caps_Lock: return Rk::Key::Key_Caps_Lock; case XK_Shift_Lock: return Rk::Key::Key_Shift_Lock; case XK_Meta_L: return Rk::Key::Key_Meta_Left; case XK_Meta_R: return Rk::Key::Key_Meta_Right; case XK_Alt_L: return Rk::Key::Key_Alt_Left; case XK_Alt_R: return Rk::Key::Key_Alt_Right; case XK_Super_L: return Rk::Key::Key_Super_Left; case XK_Super_R: return Rk::Key::Key_Super_Right; case XK_Hyper_L: return Rk::Key::Key_Hyper_Left; case XK_Hyper_R: return Rk::Key::Key_Hyper_Right; case XK_space: return Rk::Key::Key_Space; // Pointer control keys case XK_Home: return Rk::Key::Key_Home; case XK_Left: return Rk::Key::Key_Left; case XK_Up: return Rk::Key::Key_Up; case XK_Right: return Rk::Key::Key_Right; case XK_Down: return Rk::Key::Key_Down; case XK_Page_Up: return Rk::Key::Key_Page_Up; case XK_Page_Down: return Rk::Key::Key_Page_Down; case XK_End: return Rk::Key::Key_End; case XK_Begin: return Rk::Key::Key_Begin; case XK_BackSpace: return Rk::Key::Key_BackSpace; case XK_Tab: return Rk::Key::Key_Tab; case XK_Linefeed: return Rk::Key::Key_Linefeed; case XK_Clear: return Rk::Key::Key_Clear; case XK_Return: return Rk::Key::Key_Return; case XK_Pause: return Rk::Key::Key_Pause; case XK_Scroll_Lock: return Rk::Key::Key_Scroll_Lock; case XK_Sys_Req: return Rk::Key::Key_Sys_Req; case XK_Escape: return Rk::Key::Key_Escape; case XK_Delete: return Rk::Key::Key_Delete; default: return Rk::Key::Key_None; } } std::unique_ptr RkEventQueueX::getFocusEvent(XEvent *e) { auto event = std::make_unique(); event->setType(e->type == FocusIn ? RkEvent::Type::FocusedIn : RkEvent::Type::FocusedOut); return event; } void RkEventQueueX::setScaleFactor(double factor) { scaleFactor = factor; } geonkick-2.10.2/src/redkite/src/platforms/xwin/RkLogX.cpp000066400000000000000000000030131451017372400232420ustar00rootroot00000000000000/** * File name: RkLogX.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkLog.h" #include "RkPlatform.h" void rkLogMessage(RkLogLevel level, const std::string &func_name, const std::string &msg) { std::string strLevel; switch (level) { case RkLogLevel::Debug: strLevel = "Debug"; break; case RkLogLevel::Info: strLevel = "Info"; break; case RkLogLevel::Warning: strLevel = "Warning"; break; case RkLogLevel::Error: strLevel = "Error"; break; default: strLevel = "Unknown"; } std::cout << "[" << strLevel << "] " << func_name << ": " << msg << std::endl; } geonkick-2.10.2/src/redkite/src/platforms/xwin/RkPlatformX.cpp000066400000000000000000000023451451017372400243140ustar00rootroot00000000000000/** * File name: RkPlatformX.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkPlatform.h" RkNativeWindowInfo rk_from_native_x11(Display* display, int screenNumber, Window window) { RkNativeWindowInfo info; info.display = display; info.screenNumber = screenNumber; info.window = window; return info; } RkWindowId rk_id_from_x11(Window window) { RkWindowId id; id.id = window; return id; } geonkick-2.10.2/src/redkite/src/platforms/xwin/RkWindowX.cpp000066400000000000000000000333411451017372400237770ustar00rootroot00000000000000/** * File name: RkWindowX.cpp * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "RkLog.h" #include "RkWindowX.h" #include "RkCanvasInfo.h" #include #include RkWindowX::RkWindowX(const RkNativeWindowInfo *parent, Rk::WindowFlags flags, bool isTop) : parentWindowInfo{parent ? *parent : RkNativeWindowInfo() } , windowFlags{flags} , xDisplay{parent ? parent->display : nullptr} , screenNumber{parent ? parent->screenNumber : 0} , xWindow{0} , winBorderWidth{0} , winBorderColor{255, 255, 255} , winBackgroundColor{255, 255, 255} , canvasInfo{nullptr} , windowInfo{nullptr} , scaleFactor{parent ? parent->scaleFactor : 1} , isTopWindow{isTop} { RK_LOG_DEBUG("called: d: " << xDisplay << ", s: " << screenNumber); } RkWindowX::RkWindowX(const RkNativeWindowInfo &parent, Rk::WindowFlags flags, bool isTop) : parentWindowInfo{parent} , windowFlags{flags} , xDisplay{parent.display} , screenNumber{parent.screenNumber} , xWindow{0} , winBorderWidth{0} , winBorderColor{255, 255, 255} , winBackgroundColor{255, 255, 255} , canvasInfo{nullptr} , windowInfo{nullptr} , scaleFactor{parent.scaleFactor} , isTopWindow{isTop} { RK_LOG_DEBUG("called: d: " << xDisplay << ", s: " << screenNumber); } RkWindowX::~RkWindowX() { RK_LOG_DEBUG("called"); if (xDisplay) { freeCanvasInfo(); XDestroyWindow(xDisplay, xWindow); if (isTopWindow) XCloseDisplay(xDisplay); } } bool RkWindowX::hasParent() const { return parentWindowInfo.display != nullptr; } bool RkWindowX::openDisplay() { xDisplay = XOpenDisplay(nullptr); screenNumber = DefaultScreen(xDisplay); return xDisplay != nullptr; } bool RkWindowX::init() { RK_LOG_DEBUG("called"); if (!hasParent()) { if (!openDisplay()) { RK_LOG_ERROR("can't open display"); return false; } } Window parent = 0; if (static_cast(windowFlags) & static_cast(Rk::WindowFlags::Dialog)) { RK_LOG_DEBUG("is or dialog, get root window"); parent = RootWindow(xDisplay, screenNumber); } else { parent = hasParent() ? parentWindowInfo.window : RootWindow(xDisplay, screenNumber); } auto res = XMatchVisualInfo(xDisplay, screenNumber, 32, TrueColor, &visualInfo); if (res == 0) { RK_LOG_ERROR("visual info was not found"); return false; } XSetWindowAttributes attr; unsigned long mask = CWColormap | CWBorderPixel | CWBackPixel | CWEventMask; attr.background_pixmap = None; attr.colormap = XCreateColormap(xDisplay, parent, visualInfo.visual, AllocNone); attr.border_pixel = winBorderColor.argb(); attr.background_pixel = winBackgroundColor.argb(); attr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask | KeymapStateMask | FocusChangeMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | StructureNotifyMask | PropertyChangeMask | PointerMotionMask; attr.override_redirect = False; auto pos = position(); auto winSize = size(); RK_LOG_DEBUG("create window: d: " << xDisplay << ", p: " << parent); xWindow = XCreateWindow(xDisplay, parent, pos.x(), pos.y(), winSize.width() * scaleFactor, winSize.height() * scaleFactor, winBorderWidth, visualInfo.depth, InputOutput, visualInfo.visual, mask, &attr); if (!xWindow) { RK_LOG_ERROR("can't create window"); return false; } if ((static_cast(windowFlags) & static_cast(Rk::WindowFlags::Dialog)) && hasParent()) { RK_LOG_DEBUG("set WM_TRANSIENT_FOR"); XSetTransientForHint(xDisplay, parentWindowInfo.window, xWindow); } deleteWindowAtom = XInternAtom(display(), "WM_DELETE_WINDOW", True); XSetWMProtocols(xDisplay, xWindow, &deleteWindowAtom, 1); createCanvasInfo(); windowInfo = std::make_unique(); windowInfo->display = xDisplay; windowInfo->screenNumber = screenNumber; windowInfo->window = xWindow; windowInfo->scaleFactor = scaleFactor; RK_LOG_DEBUG("window created"); return true; } void RkWindowX::show(bool b) { if (isWindowCreated()) { if (b) XMapRaised(display(), xWindow); else XUnmapWindow(display(), xWindow); } } Display* RkWindowX::display() const { return xDisplay; } const RkNativeWindowInfo* RkWindowX::nativeWindowInfo() const { return windowInfo ? windowInfo.get() : nullptr; } void RkWindowX::setTitle(const std::string &title) { if (isWindowCreated() && !title.empty()) XStoreName(xDisplay, xWindow, title.c_str()); } bool RkWindowX::isWindowCreated() const { return xDisplay != nullptr && xWindow; } RkSize RkWindowX::size() const { if (isWindowCreated()) { XWindowAttributes attributes; XGetWindowAttributes(xDisplay, xWindow, &attributes); return RkSize(attributes.width / scaleFactor, attributes.height / scaleFactor); } return {250, 250}; } void RkWindowX::setSize(const RkSize &size) { if (size.width() > 0 && size.height() > 0) { if (isWindowCreated()) XResizeWindow(display(), xWindow, size.width() * scaleFactor, size.height() * scaleFactor); } } RkPoint RkWindowX::position() const { if (isWindowCreated()) { XWindowAttributes attributes; XGetWindowAttributes(xDisplay, xWindow, &attributes); return RkPoint(attributes.x / scaleFactor, attributes.y / scaleFactor); } return {0, 0}; } void RkWindowX::setPosition(const RkPoint &position) { if (isWindowCreated()) { int x = position.x(); int y = position.y(); if (hasParent() && (static_cast(flags()) & static_cast(Rk::WindowFlags::Dialog))) { XWindowAttributes parentAttributes; XGetWindowAttributes(display(), parentWindowInfo.window, &parentAttributes); int parentRootX; int parentRootY; Window child; XTranslateCoordinates(display(), parentWindowInfo.window, RootWindow(display(), screenNumber), parentAttributes.x, parentAttributes.y, &parentRootX, &parentRootY, &child); RK_UNUSED(child); x += parentRootX - parentAttributes.x; y += parentRootY - parentAttributes.y; } XMoveWindow(display(), xWindow, x * scaleFactor, y * scaleFactor); } } void RkWindowX::setBorderWidth(int width) { winBorderWidth = width * scaleFactor; if (isWindowCreated()) XSetWindowBorderWidth(display(), xWindow, winBorderWidth); } int RkWindowX::borderWidth() const { return winBorderWidth / scaleFactor; } void RkWindowX::setBorderColor(const RkColor &color) { winBorderColor = color; if (isWindowCreated()) XSetWindowBorder(display(), xWindow, winBorderColor.argb()); } const RkColor& RkWindowX::borderColor() const { return winBorderColor; } void RkWindowX::setBackgroundColor(const RkColor &color) { winBackgroundColor = color; if (isWindowCreated()) XSetWindowBackground(display(), xWindow, winBackgroundColor.argb()); } const RkColor& RkWindowX::background() const { return winBackgroundColor; } RkWindowId RkWindowX::id() const { RkWindowId id; id.id = xWindow; return id; } void RkWindowX::update() { if (isWindowCreated()) { XExposeEvent event; event.type = Expose; event.send_event = false; event.display = display(); event.window = xWindow; event.x = 0; event.y = 0; event.width = size().width() * scaleFactor; event.height = size().height() * scaleFactor; event.count = 0; XSendEvent(display(), xWindow, True, ExposureMask, reinterpret_cast(&event)); } } #ifdef RK_GRAPHICS_CAIRO_BACKEND void RkWindowX::createCanvasInfo() { canvasInfo = std::make_unique(); canvasInfo->cairo_surface = cairo_xlib_surface_create(display(), xWindow, visualInfo.visual, size().width() * scaleFactor, size().height() * scaleFactor); cairo_surface_set_device_scale(canvasInfo->cairo_surface, scaleFactor, scaleFactor); } void RkWindowX::resizeCanvas() { cairo_xlib_surface_set_size(canvasInfo->cairo_surface, size().width() * scaleFactor, size().height() * scaleFactor); cairo_surface_set_device_scale(canvasInfo->cairo_surface, scaleFactor, scaleFactor); } const RkCanvasInfo* RkWindowX::getCanvasInfo() const { return canvasInfo ? canvasInfo.get() : nullptr; } void RkWindowX::freeCanvasInfo() { if (canvasInfo) cairo_surface_destroy(canvasInfo->cairo_surface); } #else #error No graphics backend defined #endif // RK_GRAPHICS_CAIRO_BACKEND void RkWindowX::setFocus(bool b) { if (b) XSetInputFocus(display(), xWindow, RevertToParent, CurrentTime); else XSetInputFocus(display(), None, RevertToParent, CurrentTime); } bool RkWindowX::hasFocus() const { Window focus_return; int revert_to; XGetInputFocus(display(), &focus_return, &revert_to); return focus_return == xWindow; } void RkWindowX::setPointerShape(Rk::PointerShape shape) { if (!isWindowCreated()) return; Cursor pointer; switch (shape) { case Rk::PointerShape::Arrow: pointer = XCreateFontCursor(display(), XC_arrow); break; case Rk::PointerShape::IBeam: pointer = XCreateFontCursor(display(), XC_xterm); break; default: return; }; XDefineCursor(display(), xWindow, pointer); } Rk::WindowFlags RkWindowX::flags() const { return windowFlags; } bool RkWindowX::pointerIsOverWindow() const { if (isWindowCreated()) { Window child; int proot_x; int proot_y; Window root_win; unsigned int mask; int win_x; int win_y; XQueryPointer(display(), xWindow, &root_win, &child, &proot_x, &proot_y, &win_x, &win_y, &mask); RK_UNUSED(root_win); RK_UNUSED(mask); RK_UNUSED(win_x); RK_UNUSED(win_y); return child == xWindow; } return false; } void RkWindowX::setScaleFactor(double factor) { auto p = position(); auto z = size(); scaleFactor = factor; windowInfo->scaleFactor = scaleFactor; setPosition(p); setSize(z); resizeCanvas(); } double RkWindowX::getScaleFactor() const { return scaleFactor; } geonkick-2.10.2/src/redkite/tools/000077500000000000000000000000001451017372400167505ustar00rootroot00000000000000geonkick-2.10.2/src/redkite/tools/CMakeLists.txt000066400000000000000000000007161451017372400215140ustar00rootroot00000000000000set(RK_TOOLS_SOURCES ${RK_TOOLS_PATH}/rkpng2c.c) if (RK_GRAPHICS_BACKEND MATCHES Cairo) set(RK_GRAPHICS_BACKEND_LINK_LIBS "-lcairo") endif() add_executable(rkpng2c ${RK_EXEC_OPTION} ${RK_TOOLS_SOURCES}) if (CMAKE_SYSTEM_NAME MATCHES Windows) target_link_libraries(rkpng2c "-mwindows -lstdc++ -lm -lmingw32") target_link_libraries(rkpng2c ${RK_GRAPHICS_BACKEND_LINK_LIBS}) else() target_link_libraries(rkpng2c ${RK_GRAPHICS_BACKEND_LINK_LIBS}) endif() geonkick-2.10.2/src/redkite/tools/rkpng2c.c000066400000000000000000000053311451017372400204640ustar00rootroot00000000000000/** * File name: rkpng2c.c * Project: Redkite (A small GUI toolkit) * * Copyright (C) 2019 Iurie Nistor * * This file is part of Redkite. * * Redkite 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * rkpng2c is a part of Redkite GUI toolkit. * It converts a PNG image to C array encoded in ARGB32 format. */ #define RK_VERSION_STR "1.0.0" #include #include #include int main(int argc , char **argv) { if (argc != 4) { printf("Redkite GUI toolkit\n"); printf("rkpng2c version %s\n", RK_VERSION_STR); printf("Converts a PNG to C array encoded in ARGB32\n"); printf("Copyright (C) 2019 Iurie Nistor\n"); printf("License GPLv2\n"); printf("Usage: rkpng2c \n"); return 0; } cairo_surface_t *image = cairo_image_surface_create_from_png(argv[1]); int w = cairo_image_surface_get_width(image); int h = cairo_image_surface_get_height(image); printf("image size: %dx%d\n", w, h); FILE *fptr; fptr = fopen(argv[2], "wb"); if (fptr == NULL) { printf("can't open file %s\n", argv[2]); return 1; } const unsigned char *buff = cairo_image_surface_get_data(image); fprintf(fptr, "/**\n" " * Generated with rkpng2c version %s, part of Redkite GUI toolkit.\n" " * File name: %s\n" " * Image size: %dx%d\n" " */\n" "\n" "const unsigned char %s[] = {\n", RK_VERSION_STR, basename(argv[1]), w, h, basename(argv[3])); for (int i = 0; i < w * h * 4; i++) { if ((i + 1) == 12 || (i + 1) % 12 == 0) fprintf(fptr, "0x%02x,\n", buff[i]); else fprintf(fptr, "0x%02x, ", buff[i]); } fprintf(fptr, "};\n"); fclose(fptr); cairo_surface_destroy(image); return 0; } geonkick-2.10.2/src/resources/000077500000000000000000000000001451017372400161735ustar00rootroot00000000000000geonkick-2.10.2/src/resources/README000066400000000000000000000002441451017372400170530ustar00rootroot00000000000000All images under this directory are released under CC0 1.0 Universal (CC0 1.0) Public Domain Dedication https://creativecommons.org/publicdomain/zero/1.0/deed.en . geonkick-2.10.2/src/resources/add_per_button.png000066400000000000000000000003641451017372400216750ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYsMstEXtSoftwarewww.inkscape.org<qIDAT8c@`'͍|3gb````&Āa`} Inh |Cnb.P$/[0@eoXf&|a"`c[wIENDB`geonkick-2.10.2/src/resources/app_icon.png000066400000000000000000000063231451017372400204750ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYsT٪btEXtSoftwarewww.inkscape.org< PIDAThYkl[y~CRhQH.dIe+e)ђZ-YcgH%AG Ce 6dE(U؊hqWm8HHQo<{C6ZXٟ!|}>|o]?ShR8@'3HDk >X]v}BJ_ڒ 24rcd~ dB|34P,+e(V[v|*, oA}/l.>7=80 =])= X,qR!DRAs,z_3і87Hu ~W:[E<CX{#? RT*@# ≶SsWkFǣX*}˥8ՙB9[KgMiIH{[RT'">޿]$2J%w !֒_5'H8|Pi~)=v  ЈsF*eRm/MOO[˭GhBͱǩqȧk)}~A˿rJu@ӄQxj5U XSzau~^g^L0D"@< V+a\@Ι L_5MCcc#v܉P_ʳ7d?VxJ04<XWʚD DJk:|t5U74XtR $qd2!z @|Q,}G}mmmrvx^^M!!4t137Wd2WV%pJs"Y ".rE5\t}}f C@"8Yq?^MoU^RqJGׇޚc>B}}=(~TJ#kI`||̒63YݑfT^/R)\|2^#ؕ5- N9Lv`,D5]|A&33g&]OoÓ̩dcޖhoGP9S8vvw#`;ϞHw3 h3)忝9Km۬ݍW'oJw{SnWo݃C_c[-o^̘B:X'ԉxwΩߛx`sG[~{ՠt-NNd$CDBE'Є˗/uCw'd+l)httT %&FhM"@4M+i1+g >zq"otqSX~:|d22}0tǣ1s Ya]NS$Ln%%J2r2:~qq"ٶ-C"uѨsĉD"s~nۚuRzK=$hz@HÀFY~Őu]C![D,`J㑺iر<>>nM8|699YW__p]kH)CLXH@D,lMSB;`'Nl8lp |]+ C:4QdY-|5M[u8!R.9Ooo jem{4MJEs^Ắ)E׻nDnqaY,"뺮 R'.' cccMzQn4u*J9r٩w,.|3B]ͯi69ۺmۖaXuuuiFRdwuu'OTXdioPIENDB`geonkick-2.10.2/src/resources/button_browse_sample.png000066400000000000000000000013721451017372400231410ustar00rootroot00000000000000PNG  IHDRCt sBIT|d pHYsӱ9tEXtSoftwarewww.inkscape.org<wIDATHWn@=wƉ]RJ$e e4B%B3*R~"V,Ȳ&!v,Mbp$+;}ǘ0^% \O Ө(gPxmm5vzfe3f+n6oˣ?3Zv_}iѿe;DD'4 J)~t~}0'k3޴e:XMD6rBN%7~;>k' !6;+RJCF:[BR8 F&iY!RS"Ɛ.(2y(8`qf(f2qR?s" !@BVolRYZ3R2QN($sŋ!D_Ј(2iL>T T)ueHDnT8M@YO8F Tˋ"% cfQ9q=ZHX屓Ip8l˥1'p3 8 #6[8Y0cE^i6Af%Sˇ% ݏ^^,̉FIENDB`geonkick-2.10.2/src/resources/cancel.png000066400000000000000000000043461451017372400201350ustar00rootroot00000000000000PNG  IHDRZa xsBIT|d pHYsSSⰘtEXtSoftwarewww.inkscape.org<cIDAThoǿ̙;u \ښKMB7ZE.I+E >RZVHP).(uB_6BU\jv1aMk;s~}ˎvFW3gsgw.s /,FPGGeY%ů1"f,Q^L/NMq)%א MN}JJIe2@`ᱮ.~9ҒyIs!H)&L&?|d648'dB)"dvPp6m*CЬ9s&NM]Su .՜P} ca>1+I(]y#떊be۶mN!=== 0eT:us>MTp]WEG}1*#eMuww;ldJRWT[W_EM,:jG]3[q=~-:vrl_4Ms4r._.R^1 l^7/iN:Aݎ=>>n"Ͳ'Or۶B} IDf}30#͛CUD*8K>R)ƿ5ZHSl|>,⚦ ->̓9gWǽ{>zv$"(PJ筜saYL@cc#,3@ rΫ3ҌbyPAQ`:ohh`  0"1(8' R"1@)?sY öL6o8 n;ŜJJwz5_8 DU:T@ Ma!cw^H&7ހy'V˯cG@) LÀrc` 8@2Ƃ;U X0tȳ/f1WKQ 8@.* lێIPDݱc{ ӄav .|R;~/< 4 OXr%sltF:?҆]m6E͋Wuu`AӴ`V%hhEPJAJ "B\Kc=)B`M-asT-aLF[I[8{=lnzHmIENDB`geonkick-2.10.2/src/resources/compressor_attack_label.png000066400000000000000000000016401451017372400235640ustar00rootroot00000000000000PNG  IHDR5[TsBIT|d pHYs&C&C(PDTtEXtSoftwarewww.inkscape.org<IDATHՕoG??NK 5iT46R WT JBSh┒x)x+*Dx؉=C+S6T=̛HR=B<U񍔍4!} YKtK}y.DatessaYV9)&&IϿ6@ ŋs^[J.CCA"1ܲ*?g41UUm !BS^!qL4p߱,H$p8 ̇,-thX ;ŵk߲K29ʼno8L~.\H.Q(-Lyp2yD&+8իClvg* Dަ/F<-$RJ >e~u]|>PmAA,BJɍ j5|V_W,I$FTUFGYm3:@J ػ yTU_u;Q5ضM__%ӧrU:'O:1/j{b4|&[0 2ܾavOؖ(Jw;lxUyʽ5`^ƗB(_ Q%tuwc7%AqݔeEAUUbTU ϣ:n!lV##(6T`(eVn;;1Yy]RJ`%xun޸A;E&&'9;18OX4M(|ߋ8jB;Cʃc* |A&%oeTb``#B$Byy|ЗtwwNRήRfW1 D)%`HG9?=MO,F kk6S,I$|>j)UEu4M{%š?z"dY[\i,..255+WBPV봴9˲8;9 <\%w˩)~{2eaY7_V#90([^& qLӤT,6a~gH)1MũSmsx!BF# Lp!@4E!iiU%N3嗄#[[[R6( PAXlvKIG4J\R q\<@JI{{K6t:)J<εZ ]q:|>_B)%M;w(t?o qG9+Vs9go{gF|~n(HvwHi) ]?wDtðvaWth8p8BmJ,W| IENDB`geonkick-2.10.2/src/resources/compressor_enable_active.png000066400000000000000000000034201451017372400237350ustar00rootroot00000000000000PNG  IHDRN)sBIT|d pHYs!q!qLztEXtSoftwarewww.inkscape.org<IDATXkLTg9ep*^0V-KkѺ5c[1i6nֵiI@knnkXuM- À\ s6H4\<{Z$KiEQ9fB E~%Œ8}~bη4M8ݧ%UU3mv!HO/U]$i6gڏ@iBzB~fd6璗gabb.^UBlY)Ng/>ߣ=q,u닔 eY9w .^#8$^Bk'8d4Δ{POe*>ϴrE//nw0x@ @jj*6[ zm$>3-zG0bPXX@JJ UUddd088(,[VJVJJ̛Fuu5EE Xf5 2ՔzQIII(..fjt:6[% 25i}}ڌm߾kɥK4n:;/())fϞX :N'lK.,@j@O /Bmm mm\柳tiHb*MM=yߐ^gݺz<"#)) nc߾8&'8YYYt2 uhtwٷo/eLNYb\rwSӣx?6mjʕ~ /`&Ξ }t7owKرwС䘢[GzFx\.;=:UU455Cll,v>;a ܾ=Z usQio3gR]mcMt:"{?u5.vPPOg;/2iiin֭k}( 166Q^/B!RRRM(==hs'O~Lczjkk8u?+16xxbjj 7$4!\p-[6344L{{;qq46ljoN'S^^NO3zjVY`0dbnee+hQa4NDZcxrTU%;; x?꫻X nݺٽ{hӉZ`QSc# 1<< Lk41q$&&Pφ S</--kN|ȲĉIMMy`D" ;vlGUUz=]:sN-HUU9qC~?%$&&dbr1<꫿cXxC<'O~L8޻kEzƅprr2&!GP(z...Ō8=D"Ș(0!$FFF$ 1{6#Gb6iѮ'If>@ @bb"y םIIItuu(LF</`!fzz<& G  >>}ϙrMql6Ó(\lSghkڵORCD B??%$!6.24!$Yqϙ7 4MdINZj,鈢,aÃt:ݿU_JIENDB`geonkick-2.10.2/src/resources/compressor_enable_hover.png000066400000000000000000000033331451017372400236100ustar00rootroot00000000000000PNG  IHDRN)sBIT|d pHYs!q!qLztEXtSoftwarewww.inkscape.org<XIDATXoSs8$c1!,`;yq"FA("VP'1m::Uc AK3'Y"H!!N_;[|>@ݢAH Us;{ukUu v (ʬ 55voJm+"NĢ EJޑ=TVZңH)wH)#%1lR}|TuBr5 Hү,TUرx,(*7ooᄇ$^'B;ʕ+zz9pŅ *݇pCv{-. ]׹wxł}X,ξ}{Y\\"ֆ(ΒLj;٪d444JPLNłh4j:<7HytvvPQQA @RLN!P^^NkEp/D"H.cjj\.\7ކ?v_/OgΜqS(lvq/33yD"&;Fϵ[Cr8qbÇq_\tG), fg? дr59Ϝ<9ZW!%288*`pA218O6oYʷ 㟱IWW'CCLMqtvvpP,s g@`Eb$\)=[^W4 .Rd`}}M{|DQ<J{ZMMnFG?x*u.^@?]]]\t[BU C4ϗ[V0>QG:&@2o 2PL&lf'kkkV_l.gii̴il!Gfs9PN*&PVVj%fY?oK뢧v>of/ׅ?ܛLyS"?>v)%B!Ԏm $NI!ĜAhx"^maËH-۲. IENDB`geonkick-2.10.2/src/resources/compressor_goupbox_bk.png000066400000000000000000000012251451017372400233140ustar00rootroot00000000000000PNG  IHDR?EbKGD pHYsktIME4 W"IDATxjQsL Z nC%Hе;."+ĵow)Pڔ.:viI3Ud[]8?|pUnhZ(z "Up+^5-"'~kv٣(zd|Y=ͲO""FDa^}T*L̏snYV8Ot8ދ*̟1&EѵglWW* bmLAhcKU%tI`T,+z?{, @ @ @ @  @ @ @ @  U3j yt:=c ` Y0} {}a9 _8,Fe_GW-cB+՗~Ј'I\Gι5&#Ͳoў{[Ku{IENDB`geonkick-2.10.2/src/resources/compressor_groupbox_label.png000066400000000000000000000030711451017372400241620ustar00rootroot00000000000000PNG  IHDRQ asBIT|d pHYs tEXtSoftwarewww.inkscape.org<IDATHݕlU?{n/R :Yn0 s&ٲ-dLޚ0Q5Tzo4S22p΅(1h0$@SsX^=?znKt&or\z,~Dƽ[Er9٠{{Nk8 3D'N~i-Xד5ZhF6 {I7-?QbwwKw%=5+0:35os{ۮ-J[@߮l0J"Lؑ71 C(?"w"ᑠuZT\ys׵AP{WkjOWUP3Ӝp. ʻ ^-(莛W}S]ڟK /Fi[=<ΠFeO~N)dnUuS:)EԊb{>b(ʨ8]%JAÈyԄFT31Ta /աk0z's7!+/5d(gJZ"inlOAjD| W`cugXL0̀<œ\9`V|Iz)@W{;xpkpj=*+bP7!F9rP gDޒWUeEu!zH2ѩ^3IENDB`geonkick-2.10.2/src/resources/compressor_knee_label.png000066400000000000000000000011711451017372400232360ustar00rootroot00000000000000PNG  IHDR?csBIT|d pHYs  <|tEXtSoftwarewww.inkscape.org<IDAT(Kq?_:t^)M#CS0CmNU QHtu#AE6 _߽n.$/;Wy1;@=}ё (79LljRxcv,v#.8p LӤGt| PB?O_dzɖR|k%rՁA8tL&Kv{Ǚ½f˼zlgqLr13}\@0@㹧tu&|^/˅wdlv)m~=KӸ4xtwLW'G8t?mKC lѲ,4Mò$`L]O:bX*SY]{˅~|>G(4urRl)+=~ۃf:J)YJd":;}GX)0*MEi:7 V6wA)~$+.HNIENDB`geonkick-2.10.2/src/resources/compressor_makeup_label.png000066400000000000000000000020421451017372400235740ustar00rootroot00000000000000PNG  IHDR5[TsBIT|d pHYs&C&C(PDTtEXtSoftwarewww.inkscape.org<IDATHՖMOW{ª`1P &"mŪ]t}Q4VJ 4N?(i?=Uh}uy;҈X,'eYs*RoMMXzCLͲ{ ,t,#j)%؇l .RNNa٭qTv0? ǹnM(ly\v(J\q P'OtJ)F8Bs??=Bffn6\폍E&\ѣz\ H( n}a,,,NgAUՎd==oࠗx|5._39yDI5MB!B~2,Pqi> "> NCCCh (nu=~'/5)l6G0 v 4gW@[[S*0M)%R#ljGY@>f{6P$rrYZ0>>Q:TJ%67ZH334# qt¥KP(۽먪 0Wh4^ct>^*pPT0M_P1<{~VE^ldx}LM];+:: E666&H`YrJBУG<|>O2WP(`6ZYFFX4}ΎD,v7ZH@Q*[BHw]qOQ' s$IENDB`geonkick-2.10.2/src/resources/compressor_release_label.png000066400000000000000000000016541451017372400237420ustar00rootroot00000000000000PNG  IHDR&sBIT|d pHYs  <|tEXtSoftwarewww.inkscape.org<)IDAT8OSw?-%Qڊ0^&d8؛%Jt&KGK,{4382\HAnvX{fn9yyN#i2IlM}^&&om?ºap._R.P00Ms!vRI{.I6BOQ , lA}C kkkryXdw9iT9_,Dwwbj #I>ۀ0Є8j0H C4=4MnՑªn7ϊ8;pHl6 @_ B Bnoepa`8no.~Kki|58 S}H4:W0>*4Mr0 Lvu fi)_j;bR**=aa!_XDt $1:q#N.GQ42GtVW|d*M7[41$I&} I ~B9vO0Tzd*K;vA>|6xh?W`=Cۍ,},~;wRnǏ!2a#H\&Mk `.u:: (lHQDyώIENDB`geonkick-2.10.2/src/resources/compressor_threshold_label.png000066400000000000000000000021211451017372400243040ustar00rootroot00000000000000PNG  IHDR5[TsBIT|d pHYs&C&C(PDTtEXtSoftwarewww.inkscape.org<IDATHݕOWwƞAfL YSᙸB0HU@(Lݷ$Y CR @SRE$ƞµ* 9;sH)M+Ew]#-!@?mVuy >tAy~Q*n<17KD&~/Ukj z}.՝Jz˲Nw0HRŋRT*i(@uEg=4M+؊zOUMBPTTtkx@a躎/O x<  lfz @۶"`kkJzhll L 7g|ܹ$Eu_`y9Vh@<'CQT r,+K8Exq*gYCJJJTxrrR#n~]\ngb|EQ"^f*y4, 5-U*Nnm ||ЀtjehSi(ׄfLO 95FdYnrIE*99wvfڵay+˲L&uyXGXȽUR¡Ç#'FnD7:;IMM<:o.^$`C B;{;wFeOF/F]8))I9 C$I~gsa&\RZ1M?pAi&ۻW,yG"2###f ijj6j%u*&5gxaI_uXuXuX5F}@\ed 4w S:IENDB`geonkick-2.10.2/src/resources/controls_noise_hover.png000066400000000000000000000016731451017372400231530ustar00rootroot00000000000000PNG  IHDRE#sBIT|d pHYs!q!qLztEXtSoftwarewww.inkscape.org<8IDATXkw_K(I\$ULM[Cҕ⪣" mEٓ -2ddZ឴G5$I:{{/{waMdn΍4-]Y9ODs(0˥’vԩ!R3{<(N*tV]8}F$& S,fӘPTIlldx|.^R2>>-tej:"ŋ)Y\.9eR `mw&&>geُ>nz3r3^7{{{<~a4ajj\nGVtK$tIxa.\3;xJ)^9f}=|MQgy.. M4zWV-T>̴EatX$ S.WX\͛ T?#"Dgp[*  p4ׯ/l6q:Q*}6$˫Wyjq;Dh Sp$zoP(HZ ^bV q]  p𝲽M/~]F`9N""lmj%6l)lcX!DD)>4G"4yj0JD\+ K?6mCkQ+-IENDB`geonkick-2.10.2/src/resources/controls_noise_off.png000066400000000000000000000016461451017372400226020ustar00rootroot00000000000000PNG  IHDRE#sBIT|d pHYs!q!qLztEXtSoftwarewww.inkscape.org<#IDATXkV8Qdh!^)昌&.p)4kf(#lu.$9좛 و=G? BbannZ}+HJEA_:R~r.۞; Pի.u`@q(r!.t)Xh4(`ЏH/R,.HUUYY]%rNT7rffN6>a6B20 Wax ]׽皦1{6ߺ_ uW$ ԪU( :ud+ys#R?`ye41i="{V(4?eY66eTH&^_^%R\׭-yV}iԪU޻ym܈%&'1MzvOϞLMMj6}r!cccG"೵5aXĬo.0 VO}t: dˠ!Cc'''4ɡ!BNZŶ=ׯiGGGDQǡhDBT*oTUel|u9Tp2H)i4Hg~zR {!SH*۶Bb@t:_:8+:R~'+3NcX$IENDB`geonkick-2.10.2/src/resources/controls_noise_on.png000066400000000000000000000017541451017372400224440ustar00rootroot00000000000000PNG  IHDRE#sBIT|d pHYs!q!qLztEXtSoftwarewww.inkscape.org<iIDATX_H[g$ajIN5QfKdđ័يZ&Xː^ w[aR((Ϋ 5-]qVq &18tJzη<#>w{?89Z-2z (]bU=EQ<-utH)E686\B8 ",y! @J)UdW6"i.++}nTU5# aZ_ԩq랻o`Pm3"=>RJ~4r9UUQYYaVtfhk#H<hr'8$)"0}npXl={^cddT*Mww'AMдE! s$n|~}+WA\.'/~izff&ZQUB$ycnn[&8~}"0׮}MpA kUUH4 G1 bO4rq */aq8oeSr*Px͛sZ< P[[C<ݏx<^|RI.\s>NV~[oT)?7w>r({V299œ'!}}bssX,F6exxg?C댍] 8tHvv-4lrI$#inD2NL&]daaM`ؿ )% {0Ew]۰+e!RRJR(|6)y1RJ2eiQy !Ɵ꺗RGk_28iIENDB`geonkick-2.10.2/src/resources/controls_osc1_hover.png000066400000000000000000000025401451017372400226750ustar00rootroot00000000000000PNG  IHDRE#sBIT|d pHYs!q!qLztEXtSoftwarewww.inkscape.org<IDATXOSg?9ԶbF)BIE qv fa3iHƼpA.f #c(R R(ОX$qŖ}4fQTfs3M8ammm4#EZ!H$."9Ϻ\.2 `v6̕LU.Q]](B&)_˘l@3ʋq{Db޾}|'F{{w~ʮ9\xJ=fllUU,--qhLOO`ZzQU7KR)yt:TbUp8,,,r9)--e}} 6668QU`0D27m9U044̫Wux<Ndx<hFGGkkkdYΟ?Ç-H QRRLk U677;hłlvQTt1~A Ѝs4|C'[xo]JI,NAA08D"*ׯ_ev6LOjj`Z.\#RJbtt $ ܾɋ b0hܺ5~-p!]]?HyҞg}}Tj޺A<ě7oyr&]ln R"dhhsmLMM#۩m1VTUK3B! z@(RJFFF9Cee`4ilncd+ess;w`(+;N4:E4TUٳ59,.)//CUxm1 666*B`28~2ئ0L\BssVy9JJqt5f`z:$mtv~U9g~ߢ:lv[L& Wp℗<#Dz?6 m^=sl.t]gffT*pv #q % B:)f(3;a N9jxX_  :N0 F˹2XM5 c}!|!"M&W<)%Bjn.Ģh~G GmvIENDB`geonkick-2.10.2/src/resources/controls_osc1_off.png000066400000000000000000000024611451017372400223260ustar00rootroot00000000000000PNG  IHDRE#sBIT|d pHYs!q!qLztEXtSoftwarewww.inkscape.org<IDATXSWg7\ȅ$  &V`I2 PZ ZѾe(hqD,@n9}ptj#wo~gWRUBtK)U)B)gM)˘fS>waP,1JZ[{؅Pk*)8oP=|l`LBpd p8!rL00L}syjjjNT*YfK8AJ(2333<_^>Pa6a~sӨ 24<̍wlOo/\-a*\4ʩp)//*===u4M#@("Je28NZZZP*S(B70H%4Bh 6(x^666hjjX,reR[_t*ĵLwb>DU)t R*H%T*.t:V+>>EJRo{p$ep(BK#1@T!ZişJRϡG R) FHIENDB`geonkick-2.10.2/src/resources/controls_osc1_on.png000066400000000000000000000026321451017372400221700ustar00rootroot00000000000000PNG  IHDRE#sBIT|d pHYs!q!qLztEXtSoftwarewww.inkscape.org<IDATXKTi?ϹIΌՌ3n–emY2[ڍXjB@7 B 7t "j mccy+G9Ͼbd>/;}>ٳg*JaY`"5Um0,TE2%=k!N+Ab{ B0a]9MRz!B ʧ9E{>p0y`0L__߈NZZl6ET BCr9 : d9䣏fffwkoo'zO9x0iTUU165KK˻W`?>A29; q_J$0Ҍ_*]~>b?80b]q]_ԩ\.'LGH099ͱcݥN*C4IF4M|>I=zDPرnΞ=a Q,xR eY|?XYY@<}a:q\x<fg,,4M\.> i~M*:|Pf$&qON1,b`^>| ^VڷP(x X45sTDI}}=cyΝ;O(% ֐N1M۷055MPh*---|,/bY@89fq0$T8|CD &onvb0~BN'N]g!ԙ38Nʥ}ܟc=7;埲|p,PÇQU$a*PrӧVlmmq7nH Sי} !7_F$$IjPy<ێI.`gmm[ %"@eifgQH4Jo,LOMNڿϝò,t]oVm~`(Gc&u7dl|/\hކ׮^}麺sQFFGe|w/͑ q[ÁGf_hQAWWټe$IE.c}}bYzM&0 JrEQ0 UzYQUjOUȲL˲PUr,jOQmoZFٳmBQ,;(Eϡ=w$#i%DC%JT IU^Z|QV ٟ*/"wIENDB`geonkick-2.10.2/src/resources/controls_osc2_on.png000066400000000000000000000027711451017372400221750ustar00rootroot00000000000000PNG  IHDRE#sBIT|d pHYs!q!qLztEXtSoftwarewww.inkscape.org<vIDATX[lSu? 9:]n]H F1n-DCB >H 1D$D`®ht`t6V٥lCˋ}s~_PTT%biܕ.KRU$uivG sȰKub4͘@AnwZ4\u}%Au]m#E=2 x<䓔ŜTGX,p`y6+.2w X(h$IsH=ߗ1+ٱ ^zͧg[Aii)v}_niݺg)++e||yf %+ƍմp) 򩭭al:N}j`0HWYt]GQrsW`Xr }<#,Z@oKS()YIcc۶iXVq۷1| k>ESS3'N|IyӱN]svEahhիWc48v8.2F#+Bl7|-|GAu^(HRJbb"@?%%ŨjsB!V*CUU}=0?K1 qZU,˱<'_~1(--Сزe3oqc5UU_y&v$gϞ|ɧqE688ŋ}INNB!<Ǔ֭IOwp8a~اOv# cywRR"/i=X, &f++{ oAMqttt ܀dB$B}pY({?~,Y-r@NNq~Q# 21qsֻ|r ׯijj]ϩXOII1hIho[WdĉZ[r;BC×LOG9}'zp8$ɱn돳e˫."(dzk$3P[[`g^ͷ*Ng*&_Ѣvg266fF k.9a#$&:ZF0xQE!##ys zujF/@e.^e||Eq:qc"O;vɛ:vvF \RgGk! Aw]JuAEI/ y03<[~?@EȲlR0ƾB!8rITja PW%s>ǰ?зS"@FmeOl_ :8l<긼*f@븅b٬YsGc=sAp] aXxm#\BZa8j6. Ca􇃭 */3i*=eB:PJ!%11N_R4>;\IENDB`geonkick-2.10.2/src/resources/copy_per_button_hover.png000066400000000000000000000006711451017372400233230ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs(ditEXtSoftwarewww.inkscape.org<6IDAT8NPO 68I* tߡ7 4j']&7@I!:6ܒTs8'!c(!!a|8;p9"{DO:N0 q~z᷼ 6pPFYL&>cSn\ UU( W|3LT-Kal6îiL̉Gܻlybq;fۨ<=@ Hr2 ־ܗ$rf*PyQ0IENDB`geonkick-2.10.2/src/resources/copy_per_button_on.png000066400000000000000000000007011451017372400226060ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs(ditEXtSoftwarewww.inkscape.org<>IDAT8NP{mtLP6Y$ ]'05A>A_w&("M;v^…["o|OrmA )`MsR)lr(z|t]!W'o+A R) nWԁ$.KS h4M89;^q0-d63xn^:F4Z {"TUE絃VKx<ʦ)?a=-&N~qws+ˋ2 0Dn@AŲP%h:OJ=B!oi W1tx~8SlpIENDB`geonkick-2.10.2/src/resources/distortion_drive_env.png000066400000000000000000000014301451017372400231360ustar00rootroot00000000000000PNG  IHDR5[TsBIT|d pHYs&C&C(PDTtEXtSoftwarewww.inkscape.org<IDATHݖMOQ۹ÝHʂ(6D/R[.$. h ,K1big\ c|=#& ZkShCJ9s,IG;q2_G{y]t4uj2^C!Z!)!qjGG^Zellr\Ir{IR8r[QZ4h۶1b>xD"A5GA@T:N]|g dX$Ruq jBвfyb"|&J%"{]˲TIAp$MSϴl${IENDB`geonkick-2.10.2/src/resources/distortion_drive_env_active.png000066400000000000000000000015031451017372400244720ustar00rootroot00000000000000PNG  IHDR5[TsBIT|d pHYs&C&C(PDTtEXtSoftwarewww.inkscape.org<IDATHݖKSasv6z1et̋J[&iA'袨K;O(*n›0"̦Ӌ9b;mg]DRDD@}SjooTeY:Ys\^PUollҜNuVMdS9y3eK+6XdBEYU/P٬$KK_{>b(qQ^o ꤯X [Pr޻Ȳanݺŋ(J|:W\f` 333:tMx|#Ax-'nX x^z4A$ ::v( ennpO>#NseN- @M,tze|hh[lFhN={vS__Ouu5~[wn7]]GH>#ݧ9uo޼X,R(HRD"ch v(EQy:K\.3<Ç+>|ٳg2-'C䪪*&,0r$ɕimmaj*I6%F,'ˡ:7|Md2  /~kc$ˏ?c'754U]$4Jċ$=vyIFp_q+8NIwIENDB`geonkick-2.10.2/src/resources/distortion_drive_env_hover.png000066400000000000000000000014441451017372400243460ustar00rootroot00000000000000PNG  IHDR5[TsBIT|d pHYs&C&C(PDTtEXtSoftwarewww.inkscape.org<IDATHݖMOSAi/KKʇ%(+U B\4aR[ E  "B[ ԶүqAB4. g;g޼gΙ#~(o$ K)m\QiBᕢ(2Ym1vnł-..kZ0lFVRZM&ӹD*+|>"L NwY^^!JWL&RJ~s8nL!$rX]]3d,Ddll!?`t(Ba, V}gⴷQUOx<f8880|}p\nx4h4EBr L[xff#w(’XP0?d2yf7XY ؀l6. ͆]mDž᪪tHP(Loo==XZF>'ˑH$¨J,#Jb8)I02rt=cK_hgaaiNg.鴱DWgm@U3o)%:hR@SS#hL&wiVjjjWOtՑNY[[?U%˱lG6 BNוHƏ2|vBB] }0IENDB`geonkick-2.10.2/src/resources/distortion_drive_label.png000066400000000000000000000011321451017372400234240ustar00rootroot00000000000000PNG  IHDR?csBIT|d pHYs 1 17tEXtSoftwarewww.inkscape.org<IDAT(oRQFϻ<<%jkerHk mJltehDĤƵZR2 e\Il<_}ɹSBo_R~_"B:}iH)ٍ?߫'%8^ @9rHJm(l^lm5 ߇j0==5:_\.'̝?r$R噙 %@#{bi|'@pqn‘(7,bߘRS.W`2r9P5Ǐ( v^D" @բ vk8[VlHh+^T?:lZIENDB`geonkick-2.10.2/src/resources/distortion_enable.png000066400000000000000000000023521451017372400224070ustar00rootroot00000000000000PNG  IHDRE#sBIT|d pHYs!H!HaEWtEXtSoftwarewww.inkscape.org<gIDATXMOW;cLlP:8QJEUBɢJhQߴ,"4 $ME"P)c.4"Uû͹>̝шcRUBRB^Kʯ|-놡ްz1h\PaMv 蚦xR8WB(@!}1&HDZ{{Bc!" $ 3 H$B`vwF;M@J:f|bON$_?ϻ; $١}}===OLַw=B__'GF:M@Rc7P)Hl2s"ODHR<~բZR.x&gmml.GȲh묮bAb:3B!bBuaVq0ABzJ$dYĢQLcJRHuE4~x;U2,Fb,--B2,E#deeϧYBQB̧gΐgT"i! 74o ds9>kql.G\>h+ RJ huUUYz\P(}fggq]+.Ӎܝ#74Ԟcaay~w5M#wq:gfp ^kרV*D"PĞ(J|g''mOiR,i6<EQvuP PXY< 掷jQ`VwAQUnb8cNjЎ#Bjb/W "}}t:뺯juLQ4Fvח{; &˲87= @XvO&I=(,>yyl4yfg׵&n8~~?<'Ld;htTvTU LNvO|0{{)Ji]]]J%NmFuC^j5zS,1 ʥa`Y8RJt]'Pc&qTΡ 3BG Rzo:E_{h'x֗nhnKʯb~>IENDB`geonkick-2.10.2/src/resources/distortion_enable_active.png000066400000000000000000000025201451017372400237370ustar00rootroot00000000000000PNG  IHDRE#sBIT|d pHYs!q!qLztEXtSoftwarewww.inkscape.org<IDATX]LWmA(b[ -jBPP*drbso71SuS% řӹM +h}.p5F)^lW{y~9$Gחk|2\V#)Zw82!/)s+.v B|T2EJ#!RJ?5(m(vj*)))AQLQ\l\\`_'t i5lܸ5OwZ gصk-]tv~DYY)7nrX-ŶyW*b|;t:xqHKhnn&++Ph-X,EDQzFn7B@8`Ӧ \TTSTH@`Hd ?ӹV pٿZ-F:;bhhooCAv~իkp+g``8u6fff8tSN a6z=WHoezzөٶM.^ǿψFiooc'8zN3^fCxj, r44gϞp8OyL&CqQ]h\( xH$x>fxpx?99ɒ%i (Z dwypϗ:)**D"Q\.S~ϏUlFASS#X=::ƣGhl\(L&r^?RH)?˅~ӱo_g|Ey㺚6W]7o\W_x^Cy!\h~IE(=)%ccR(qi8H&],;Znrt1IENDB`geonkick-2.10.2/src/resources/distortion_enable_hover.png000066400000000000000000000024161451017372400236130ustar00rootroot00000000000000PNG  IHDRE#sBIT|d pHYs!q!qLztEXtSoftwarewww.inkscape.org<IDATXOSW?^(-e"A RPb͇&,/ٿbbtf!J|AgfY",Ѩ*}>ڞ՘IŖ=|Iد1ST剄&E~K&ؽL" 3oB` )ePRz JR(v"E}t:>\.fN<ϖ>L kdX!l|Fݑr'8ysN>vw 1:p8<ێt7zo>jv ΅ YZZq38G~VfIVr1</ *jC@2#(--?~BX$O22&HW90388hDJI4ٯQUHd!`y9ʩS_o_]Ob4$"ea!#=)JC8fqq ͑#t:QUQ t!(h4RJfs}d2 /^ʕY^rܸ1G$2NTҥ+^{D"L&2?Ks\}oCܻ׹|;"z,WΒN޺@Z},ԹsGBAG4IJW!4 l6V EQFcT5bVZryiGPTU Gr$a{۹x2bo=XV X__1dV)˄B(j%PՐRvbr8LO@SݦZs<`狘L\.QVR27wwƮT*ܺǎ}F0GSSXHLD"Uo'F4 B>jAUX]]lރ$J`0`u)%BNFDR @G|@TՊL&=z [NrL<@Jdbl]-->5 g0([HBuRB'O$L3z2>.Gc ?MIENDB`geonkick-2.10.2/src/resources/distortion_goupbox_label.png000066400000000000000000000031721451017372400240040ustar00rootroot00000000000000PNG  IHDRQ asBIT|d pHYseevItEXtSoftwarewww.inkscape.org<IDATH[lTx\BJS3.7%%!MP@RH1 DH!%I4DCEhJSAA*36CM]sM\Og"Ad jF~D`mzuK`Mt}H֮sjx̍x+tr7F_ 60!_=}0UmOcS|!njlnVv8ߙ2…6L|K-(H0K`HVVi`G=%a5F}Z^HewO .ή93׸Um6ԟvnHaʿtVMY T/ ΅P%a8A6"0-Ӽ|flݜrbc nt^^Tw>i4EL Hc"+Yw@ykP9lG*K;5.'XCrhkd+N +~1p rc2cC[#p/08ˍyo . sđZRdY,["(iULGlRfYrZ݅V/ ,B$0K6SfN-B,E\ix)Jڍ4,!.9W nԫjݘE@U_t<7꭪V= `cfuFZTs^mM~_W(촘QokX>j"ټnBuu#qUu|ը(pݷof4k?Fk 7jC95=Q7Zp^A䜊ʗ8@kĦOo>fY :-Ӽ` nž$k "3*: YН3P)#Tu T&/Օ;l9|{\TêZ34gh|ըέ\wIؾk-B0z?1y5G7uRMWp # yf`SCmSeU->0Ν\VaL"n[wJRb_21ka8WIJf[G~sN \*|:չfz}ng|ć EX}[&g6 Ɵ ֞8PEA}# 7#4db]z!#L_Hpg ]3krk*Sl:;5SL~\diS?[K~CRTVǼvh%|#l#ܟ U}׆?75`Pg{[Ku{IENDB`geonkick-2.10.2/src/resources/distortion_in_limiter.png000066400000000000000000000012751451017372400233170ustar00rootroot00000000000000PNG  IHDR5[TsBIT|d pHYs&C&C(PDTtEXtSoftwarewww.inkscape.org<:IDATHՖOOAva[V=DNH"DB x/ջ<@ A'Rivƃۢ\~fww&]nDZǀ'xT!| !0Su~}Z/iR)'F,.> PD 0P o1Met¶m\7AX"sh4lme8>>fvB"!"rL&CTf~>J,RJ~oe  m,bmmf]B! Go X4M060LCgЏR(5d9C677,h/}>pp1p/eYLLN=f_'53Ø_WuyjYujX'''N6% C L0 IGu˲X*csDY1 # PڛJqN<` ?5B$>iȲ88ߚxhP,jaQC6nqRPo,Jyx;^enѕ+hB*HZudYf`wQMBURQ:;;erynU-199OfV+a :yd$Wg`P06vᆊܹcΜis ܹKK⟐{{9bD"P$hƅ !6|V}5M#0e\.zR~E%lW7ŋJ+˗$( HYI7IӇ G~\.M$2ӛ45eb{rEH$$ bUcMMȍ׹xv1==SV X,'dݫԼf<TLf ٌwttc0YzX]]d2#S*d2d8|EUJ >]r9^7d27IUURɍ}I& ! k+mN]!ė:_=ʪIENDB`geonkick-2.10.2/src/resources/distortion_volume_env_hover.png000066400000000000000000000017201451017372400245410ustar00rootroot00000000000000PNG  IHDR5[TsBIT|d pHYs&C&C(PDTtEXtSoftwarewww.inkscape.org<MIDATHݖMOW;o[xG 16*A1u!]?vMEU" oMrSWy9.k sRBZ~pc{`v;PK![[۔<~F۷LMMF1::BB3;;ニ]@L&f3lly3uW~$H999Xdmm5ML$!epp't:lnn1=vi9:MӰmj,0MRj!0MS:PV9<4(smw׏X_`~~è vLS*f3}loUşw?#eBWZ191!\.WW~ H4C "JR*H)Io*z!0餿?Ec6FFA 'JaYNݾ[bmR)x^UEL^3Nò/f7?[)WRՓ”IENDB`geonkick-2.10.2/src/resources/effects_groupbox_label.png000066400000000000000000000021371451017372400234070ustar00rootroot00000000000000PNG  IHDR- sBIT|d pHYs-otEXtSoftwarewww.inkscape.org<IDAT8OSw?'4;8(bBt"  L̗ŋdafvCYh,!2ė]` $ֶ=`6"Y7y.~>7Àá0?] $ITԐ/}},,,iTUUz{z- |^\̭]s\aeeeDŽ2$IZWhQimkchp+݈2T99L={FZj*Ʉj%Y0 XVdH$Db$Idi UA,#"23Y^Ya9&5- Պ%c<{=rInC(JjȲ~/KJ2v:NiZ,XI$F;;F**~a{_',//ŋ64ٚFm]׺9{3.ccDOw7`|z޾I֟<,ܺy]׷tSs3K>="nGӴhmkF.3zch0l1 vx>=MaQ##2624 H$r84F#(srx:9 N7v]AUU|s6d21 ˗r8%Wzޑ_uX,d" 'UCQjN5힙A`9aI1eEaQѶ4YAJˑ$ QV֦iUU9 UUl:trl6(( ---fm0Q[_O 6Nt]q4M[W"޹Gl}=ד & TVU~PGJId0e}jeY<zt]|' <4 zN$ضmke*EQ Ea39x:\.T*-)%iRՐRr<0"vu,h۴Z-fIRtz(~g:^szZ}FAT""3i.8_[BdD*eIENDB`geonkick-2.10.2/src/resources/env_button_hover.png000066400000000000000000000010051451017372400222630ustar00rootroot00000000000000PNG  IHDRH-sBIT|d pHYs||MAJtEXtSoftwarewww.inkscape.org<IDAT(`?ŭP0PvBP|m12`F&"PP6+jq9sDӑRʈoHQ[x;`^u( J~px@UU1M۶)J!X CfQtO|v2M^`@'.r!ijc2IJ,0q-łD"Au]N#:b0 m!O&0 4M{S^iшfCdY4RJ|߿YT~FQrhP(0h6m9 |>v,jqL&\!vc:\.uEQ8au:ϜW;(Q~ꋄ롈7IENDB`geonkick-2.10.2/src/resources/env_button_on.png000066400000000000000000000007671451017372400215720ustar00rootroot00000000000000PNG  IHDRH-sBIT|d pHYs||MAJtEXtSoftwarewww.inkscape.org<tIDAT(`q j*MXxS[F"(Q$̮~qq'dvaG 0 _|CD5 ܖJw򫆦iy#%6Iò,H)NH)~MĶmF#!۶V__4N~g2yRJRvu^Q0aYݎVٮ:bl6!H&Z׬V+Gܪy8af3tL&RYxjon7|ߧhP*0h6}a|RO}BnK^G)xz@a"u]t]G|t:EE2~VO[RQ.;F5nϥIENDB`geonkick-2.10.2/src/resources/export.png000066400000000000000000000013641451017372400202260ustar00rootroot00000000000000PNG  IHDR H@U,sBIT|d pHYs+tEXtSoftwarewww.inkscape.org<qIDAT8MK$DkBhD)Ez҈Dq4Bzػ?Cz R"C h7$`K"YY\P|.3ü3/cx7??lj7r8N'n( b{0L8NLF#5bL&` H\/&'r<>fppYy#Rr:h4Jgg'Jt&>\.VdY`bb#'Du&B8lmaֆX\ZZSf3X M*$GJ6EݽЀ)P(OjeUUPUhF\4tuu~aIx@Xdk{F.EwvvH_{mq*&MnTUd2]o#@ "(b6EUp8XVB!A ( RRH0HKK @ǃ$Iw>U ` >M$!ix^U˨J2$rj MNS(xwahxXs?%zfݎ,7V_cPsrrrkRӌߎ􅝣XIENDB`geonkick-2.10.2/src/resources/export_active.png000066400000000000000000000017501451017372400215600ustar00rootroot00000000000000PNG  IHDR&OsBIT|d pHYs՘tEXtSoftwarewww.inkscape.org<eIDATH=L[WY`dǐJ) l!vP! C!]J"l%M7@HHR؎qqg::<)jϹ!i8'vV4zzzXrQ%$,N컑*j'O(1(?|5d2@XD,(2h6Hv;{oߢijb2ЭX, j9f3N$ڸa&''imm7>NkRiEapp+Wp8/aX;5E:rq?D"f<8@?}}}޽~ @<gmm իl6988/fw'&ecc!fgg+͛aR^fwOܺ}Y__D"A0d5~}fJ+++:/Ir9N'nQx񂝝dY D"hFX\.K$Atb4$ IjFW(RVDVUUh0 ׼^~{L&- $p8looraJ;JiL:[]ǼO-UUj~?__aߏiȲL4X,*ccc(B21uɫ*lk^/^{j6#ݍ b}WO8{=H@_q'~XC -DE_kijx]{|"r(4d1"z{?T޳oZTIKR59>gZCxb4;HԈ&IAQyg,sqAhL2@M`g,tۮU< #0KKiOG{%:b1z;wڷv{~fv\E7z-8 YtC&H @.$F3;tEFN68]caFi"4@jG!j=̬[fQhRS͋WoUVzh%ZiZmE`р]v{HF>h#&OLO6cB*-]UW[k[nexR~F1;5\)'v+Hh>r%U>I"MOv~-7'r/ȥ& j3E.@gMlW Z>}wߔGuJU4+.FؑEP].Sɘ Bq%d *lشmRSZ1Jieb'EcuLAmIRWh7;I'L+KAEOi(xK'& d pe qmqk+]umˢ,cFl:ڔhāJsalD ":ZvD\<"{;r_( LU\BFxؿSiL#7nΝmyuʹs222d]ih؜*}}W2:ZJCCCFGKik۞<73<</y꩎lڴ)sssDĪUuٽ{wޞ5kڵ9slZ0v@PC-fpzΞ=#Gd߾}/3sssݯ6;wLc$\x6nZ.^'x<=5ˍ7e`` {m۞|:rɴ>?q}H).]?Xu:[>}{immN$̝ ^|?cy2cccqc(9MM ⭷ghh8##oG _|!^},R=RJJtty26֭[W3Lw8~e{뭷sN1tv\mqq1}}iiiŋݙoߞڵks]y]I[ZS__-۾fͪY6N}Q.sѼ++?47^OOo+nxޛLNN.׬JsssV^X׮OSSS.]ŋ+P'޿GUaʓ)~cêBfgIdii!55|/Tk]ynGHWw30pOYme=7%ggt>W|ٵ}ٶld]Ŵ,gyR(r޽{%ÚT00puťb1]]ן۷'399wf|沵P33w266Jdlt`&&&yTWW̙3R烵 V" HD @$" HDHD @$" HD  @$" HD @$@$" HD @$" H HD @$" HD D @$" HD @$" HD @$"@$" HD @$" HDHD @$" HD P2TP88IENDB`geonkick-2.10.2/src/resources/export_browse.png000066400000000000000000000045111451017372400216040ustar00rootroot00000000000000PNG  IHDRZa xsBIT|d pHYsSSⰘtEXtSoftwarewww.inkscape.org<IDATh[lƿs7޵| Ď1!!!ML0"MպDBJRJEAЧBHHCA$/$@lDL.mjo읙3߇ٝuS5B؟4gΙeåbA*bLsϒ樥R.1"KR,N >Hhճu]Eхx\%s9Nuvv*zvb ˊƤ8Bbu9D]olHMSsR)]gX]ٳGf]]]/L&c'4M\l8 ca11+ /qJ;67o\`3ᢷ7bF"l^eߛH&"sq/Rn7t4& P+Wq.(O$鳖evwwRS3M4͚SFt>o<(yh?ŲX,N]ODu!u 1l~oA l6kpE*bmG*s'圁1^ R1QJ!LOgk_2MRLd2fY4Mfe]vbtv$"(PJ+8²,d*fYg$9/]dzĨ`00Mu^YY@㌁m:X;2@Ġ@` (.8c "&,Tco9gu «֬Ï> ,GⓃ'7B,p8+:P< !c]ıc޽;@njϋ! G:\c` 8@n]v 'J P`So+~!/6+ hQU]7^~܂>lF">~ǟ} f>Xr%}5yj4^ 2q=@4zϘݭ[qG[+lۆnL.A5؄XWb:N-浭L$P][44Y//e`Bg(N b=K͍y~'~Bǖ-_&T+дjkk+PU]_<Ճ_| J%eԱ^5~Ow.:y DގcGR`w#h'Oᶵk5 T,[?z9b1Ek{;h?رv~h774r_p|9z]qrp'O|w9ehC q09>[o{ߏZ |G8~t~S㸐,wXI,\D<N ^pj uls3 FF>yw!jDe}86pă?ہGuM 6m—G1<4FMy_6l cW_A.Aؖy]NEo¬ KL/{ٯC %e~+˲,4Z'~z4a:ׯt^ `=xSSӬ!I)IPT#.R^mV *)vL?tLy߽ݠ҄IqR)GBؓg \PK5qeYXmN5!GM!#KӤip8Y4 Cx_XD"F"V6ƀվ,6 s#gLK^PRxbnhh Yi`ѱu OY&"d& ҕm-ٍА;;;Uooo0zi|櫑$I]DtMKg4㬫dq/N"TNU:._n߯ ]p1|5_ ȹw]7Ԁ8|GG^nPLci͵J haZZ@ͷ$,٩p%a\[Zx"#"IIENDB`geonkick-2.10.2/src/resources/export_button.png000066400000000000000000000042151451017372400216170ustar00rootroot00000000000000PNG  IHDRZa xsBIT|d pHYsSSⰘtEXtSoftwarewww.inkscape.org< IDAThZ]lTܹŻf1u(6P6ٔ4Vr"rChV>WK+4QE (M(?,Ʃw9}{^jݙ;̧3g0S*hhh樳Y( R*$Vc6T*\.15K䵵->Ą!f2V|>bGdɣnu)ёdRZkm3iWL&x>QmRy`ZTʮT*Ά FhVǏOriY06jAuJl@"ЛgaOV'''7ovB sߟHR\!ڔei*efkwt44,}\tXmRmrrԲ^T*DE^t*=В|=,A Bg!fYc1ǮOtz\|:4+>:)9>6 bUWcQ^Ν9{G}s9ZsTt\{*Μ81H& 0kX˜C ۸A ,r36=.7[9P<>`ĖD::;Ѷt)>~`17MZsTӱhK`܅`@Chk֭Î+Zx!Y6Z ?WxW\~<ׅ88t <'i]]? f8xaZ:11M"Hcsը i5FFF2mwZ/_%a((e)\ڽ-!,g0}6=kլڶd2YYzt*u7p 1}c;,ǶRZVHP).(uB_6BU\jv1aMk;s~}ˎvFW3gsgw.s /,FPGGeY%ů1"f,Q^L/NMq)%א MN}JJIe2@`ᱮ.~9ҒyIs!H)&L&?|d648'dB)"dvPp6m*CЬ9s&NM]Su .՜P} ca>1+I(]y#떊be۶mN!=== 0eT:us>MTp]WEG}1*#eMuww;ldJRWT[W_EM,:jG]3[q=~-:vrl_4Ms4r._.R^1 l^7/iN:Aݎ=>>n"Ͳ'Or۶B} IDf}30#͛CUD*8K>R)ƿ5ZHSl|>,⚦ ->̓9gWǽ{>zv$"(PJ筜saYL@cc#,3@ rΫ3ҌbyPAQ`:ohh`  0"1(8' R"1@)?sY öL6o8 n;ŜJJwz5_8 DU:T@ Ma!cw^H&7ހy'V˯cG@) LÀrc` 8@2Ƃ;U X0tȳ/f1WKQ 8@.* lێIPDݱc{ ӄav .|R;~/< 4 OXr%sltF:?҆]m6E͋Wuu`AӴ`V%hhEPJAJ "B\Kc=)B`M-asT-aLF[I[8{=lnzHmIENDB`geonkick-2.10.2/src/resources/export_format_pressed.png000066400000000000000000000006571451017372400233270ustar00rootroot00000000000000PNG  IHDR Vu\sBIT|d pHYsϋtEXtSoftwarewww.inkscape.org<,IDAT(}N[8 (CKvIWٕY|]i# QX:'rZPxs=Gȑiu4:@d RU.3 Hd XD 5MY4-Ʊ@MUQа,+N&zKR7eAs_ӏr֪T*7yIENDB`geonkick-2.10.2/src/resources/export_format_unpressed.png000066400000000000000000000005201451017372400236570ustar00rootroot00000000000000PNG  IHDR Vu\sBIT|d pHYsϋtEXtSoftwarewww.inkscape.org<IDAT(1RAE_7PxgC X R? :ST3Q4&pބ:g1Y.p]GF<IENDB`geonkick-2.10.2/src/resources/export_hover.png000066400000000000000000000014151451017372400214260ustar00rootroot00000000000000PNG  IHDR H@U,sBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDAT8MH֌RCbRKPa"X-Ma떐C0Je;<%9l"Oh}С?J|mM.54>\M]]AvrT R TUTt:M$@Q ,oab_~l6>om 0MUUSx^N'U6{{{~*n޺' #",Ç8>..rj5  rppϱX,L("aL :'y $ d2Ioo/pB@PWJ%Ltuu0 >bܸ~n(KKK*_QZl."T*uC*++('[p]uł4562==ͽwq8p:|>IӤix?/""looO=+IENDB`geonkick-2.10.2/src/resources/export_kit_button.png000066400000000000000000000006171451017372400224700ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs""Ȅ)btEXtSoftwarewww.inkscape.org< IDAT8NP?lӵDveE}E]Xx\p̸SN6(Nr䜌ilf9VXkԏay-^sɄe[XaK_-]rEQ(&mj@㬉1$Q;Q9е$I"*K'KN$rh֥=l&>\]=0y.:mZ%HE 9]_FIENDB`geonkick-2.10.2/src/resources/filter_disabled.png000066400000000000000000000013261451017372400220170ustar00rootroot00000000000000PNG  IHDR 'sBIT|d pHYs!H!HaEWtEXtSoftwarewww.inkscape.org<SIDATH]OAYt1@"&&oWҟ`^m"*hH Jٝ^m՜L2F/qB0I)S\*4-4OVA}B>eɿ@Z~VVW>lnzy#>󬬭GLN$(J@uujeYضJ0P]y(=KUn|' e3reif9LĨ ÌI8qx^a 0<<̷Nbȓ6'vvH)yȳI ضl$l 1:6F^ۛ`&d&],B2d< cLMOsN8;;c6! 976p;;žzÄk=@-eQn h4rhV8{+ܓis9qtm Ne:BfD*J[jSxiR)RT*<O_]Zk蝏5-? АR% "@sсl AY]]ETb0RὢR]'N0n갯<[hlj_χކb`C$IZh9++'L&v% =7D"zb{{u,>۩S"p^OWgEEE.N?jjkNga`Q8Uw1 H~i{F2<`>Ȭ{YU~uuVuִs_ x<3Fuh4<k~?iUj㔗0(N1=E$Na{+@wW7łVed5C/_FI ̥jۿߟKs%22(Bq_ݵ˲|-cB|J yIENDB`geonkick-2.10.2/src/resources/filter_enabled_hover.png000066400000000000000000000013601451017372400230430ustar00rootroot00000000000000PNG  IHDR 'sBIT|d pHYs!H!HaEWtEXtSoftwarewww.inkscape.org<mIDATHMOQ  Fkh & ªZWm ӍiRv݈#.L j]P@bfPTl޼癛+^EQK!hTUU 6CQf e_ B[ @ѥiyoz]<% 6OO&_ |#˝C!RZ$I"NS(V&͢=$;_P,K,VL&b(>\.Ͻ=6,/-Qx9zzz<ӳg(3<2BX$˅hp\8].NN6DVWU eOx~r̻)jH%಺rrj;wtxx%RL&scHE"fz=tަ|>O"TU"u  d2n^,8;K&͆lfeeEvvvMI _t,˙+~G9-UUQH'4\.ϨewBP{zДIENDB`geonkick-2.10.2/src/resources/filter_type_bp.png000066400000000000000000000007101451017372400217060ustar00rootroot00000000000000PNG  IHDR.usBIT|d pHYs--imwtEXtSoftwarewww.inkscape.org<EIDAT81k@;C"dk_bw?%Y nϻMmq'sn$}6sxl`"XIࡥ$-rt`0Zp`^ce6!|>W-Ѥ,K q5yN'ZFc1,8Nt:Wq5YF)U˨]@EDQsnW0D)ElZFx( $ap8DkM経F,Ic !>^|_~Uvh1MJzJ'ݙhKg=o{?6-U1+SIENDB`geonkick-2.10.2/src/resources/filter_type_bp_checked.png000066400000000000000000000007341451017372400233620ustar00rootroot00000000000000PNG  IHDR.usBIT|d pHYs--imwtEXtSoftwarewww.inkscape.org<YIDAT8JQ9*qHpL Øj%r%ND)/6#? 3-"EcVӻy>QL,5O(p8B`x˙!@4\.)zJH$Ħh=[Mgeд$fpxO*0[!ŒNz޻mvL6v Iɤ}m溞BUhZ.cjep]b2O;8ΜVǙS,Pn|2$0NX.W $i_mˏ@&!Cz* n}o'IENDB`geonkick-2.10.2/src/resources/filter_type_bp_hover.png000066400000000000000000000007641451017372400231220ustar00rootroot00000000000000PNG  IHDR.usBIT|d pHYsU 7tEXtSoftwarewww.inkscape.org<qIDAT8J@+m`iUKV7"">b/xYxIĔ.q!T7&gygb<\BPtV:Y'&h^"J dWV1MMkb1|~yԏ$`5nM_H&s{*\*K6~iSh6x<8.tftfQ $&eԮ*$V j K_st&#Z #4RIϨt.TJXk1&`83BNZ hVoREhd${j[(BV$rl0 ؙֆ ~AD^0 OdηvkO@<׮!G;$k9L L,_c!D`'mYIENDB`geonkick-2.10.2/src/resources/filter_type_hp_hover.png000066400000000000000000000006511451017372400231230ustar00rootroot00000000000000PNG  IHDR.usBIT|d pHYsU 7tEXtSoftwarewww.inkscape.org<&IDAT8JBQF-wcWj՟Gz{ A"a.?4f%iU$q)o;03G잪@]á9 ǠkTI3,U6kb, JruY& x8xG^l"MR+Frp8ly|hQFDD|6nbc5t:#^y@ht~BA|{;>R׾JNO &LjٺlOfSިJF T'_Ź]EIENDB`geonkick-2.10.2/src/resources/filter_type_lp.png000066400000000000000000000006141451017372400217230ustar00rootroot00000000000000PNG  IHDR.usBIT|d pHYs--imwtEXtSoftwarewww.inkscape.org< IDAT8n0Fϵ$WREb'/K !J*1ʀ۩R=??RUի~ ,x<U}Ks IY"&dhCYv;. ,cӶ$IuGD($}s:hloLS9yӶ-:j2Lrݨn>zeݎΏiJ$8B4Nb|>?^g ɿx ,C᪪"TvXRIENDB`geonkick-2.10.2/src/resources/filter_type_lp_checked.png000066400000000000000000000006231451017372400233710ustar00rootroot00000000000000PNG  IHDR.usBIT|d pHYs--imwtEXtSoftwarewww.inkscape.org<IDAT8ӱj@1NuQA  %ĥ/!"E &\:4 z?}N#( P;[ VAfO&QXPJ_:$ 7jg+[l6oV6I0?6ޙNRH~8Nk#Zk&/?^̲ jYz,- π}2M]**:IENDB`geonkick-2.10.2/src/resources/filter_type_lp_hover.png000066400000000000000000000006511451017372400231270ustar00rootroot00000000000000PNG  IHDR.usBIT|d pHYsU 7tEXtSoftwarewww.inkscape.org<&IDAT8=NQ6F0 şB˜\K7a? hl+,&4|4Gd9eL4lU iB1)Dc8>9!:A0~~|`2GB5q] Ag3W>|F7Y;xU'jY|ߧi/'j9qۺmlvs)TP*۠|H\_]& ׿xDK KEpRW YVuuIENDB`geonkick-2.10.2/src/resources/fl_cutoff_button_hover.png000066400000000000000000000017471451017372400234570ustar00rootroot00000000000000PNG  IHDR?h6sBIT|d pHYs!q!qLztEXtSoftwarewww.inkscape.org<dIDATXK[W?McMfYi]&50˰&Zhٞ!{P[]-݃mj8W5^Š9{|mIt}s\HRH)o\ xRŚS-R[= !۟]k-vv-GYb~GSB`u\8!@7mM.|dx8A,6{@ @__+)DR]7ZVDuBPVfgXXX|D"N"ƍ_h׿cbK"~]|7$HdCJ* gΌA(qv, C<>DFoo/'(`'XYY\&'=H\AT4& uǿer?Z.9* ΍#X,aY_ q!샃Uq>H9IMO V0Q_BLjp`r:0S?{Y|>C!k$IE#+FP`1CJ |S.#dflܽ{T;o+i3G<\E>EUUWVXZ^t0 س,t]g.a4>}&0;74wv'LJZ%JV*#z̙=w/]b٤Z u]ڭz۶ye`&`e.msmmH4L,V\&4 04<۶=3/  vҺ~OV` ~esc77iZΟ? Z /FQ<ϣaƱs8D0M)%RJZ&px,vciFΛ,Bt]p8C$ R2ɠ*B2,?uw0ϳ#`8\`GX,r 4]Fy{os]woDxႌc B8k[;gY|>~?NMH!0mAUU{x"eY _^oM~o粃((;w~e|:ZkJTDbbȉ!P__3?@OOaq'Ո]rD"ٳ'fp:H$e_e8J)N'ӾB@"`eFGbܻed+.]߈ŎLNp$\] i  }'UQy i,BkL v*]0 bΞz'bF|jy۩|>ֺ7"LNP[[K,v~J%Y^^ajǏM51Nq0eq7%@4ZQs]d2=`0HC9\.7\AMM l0hm݋tzbHuu5`\.eYOղNݘ4+ԍIv"beF-z;?)/Zk"SR`f;MSc65Z9IENDB`geonkick-2.10.2/src/resources/flac16_radio_button.png000066400000000000000000000022151451017372400225260ustar00rootroot00000000000000PNG  IHDR?<sBIT|d pHYso*tEXtSoftwarewww.inkscape.org< IDATH]UUk3#60exg[RQLe Ћ$V2EH%tiQ"n$TR&zrszeFU,ꁳe 43/,"&]1֞CIG__ߔxFzd߸ ɇIͮ.q֋]z3 U40xMUoL:.^XJɖg7_dx9^HoBs #{I><2d1YܚtΞݰ Lfn2u@;j7apXb3kD' m=QynEeWnR;?ine׳F@6H yc?eQf aT3|>߈26/ ]L-Zt1bN6>Llt '[=ePz#8L?$aOo7H܎ED0. .6652_dUۛ~!6F;眈l >0/U91W}wtV .&Ok ;0x"X D"4 OfMU} \^IRW(g.G8(%9P؂nctA0Z{F)XXkO4C^ GE䷿WQuRm{#n{G79s}o ]a0 `Fan~ys;"?G J[8="ԒF.uZO5tO5 D( Ui1^Pt z煮C݈Oרs@D>OŹF$U+ؕI5ģiեTKkƭ"RϦL~GߎY4n;/u @,j~] <TzK)73񿈫~l.VPщ/nUaIENDB`geonkick-2.10.2/src/resources/flac16_radio_button_active.png000066400000000000000000000025131451017372400240620ustar00rootroot00000000000000PNG  IHDR?<sBIT|d pHYso*tEXtSoftwarewww.inkscape.org<IDATH]lUQ-` >B%"\%C"~DP>P_JUc<AݩÈH"E6-3s|n,'7{93ށ0$נw"pLDՉ '+NDNn:@U# ""GrW|9[Ŝ=D7ai3s\׵]:@}>uT>-u&K>լˇyʔQUX)< C7vcΝ_IR{ޝ{"1&:P}T0gE}k֬'p55+}?fjku=H$(Nae̚9-ְ!IF;O~-֘ƆQ]]]%ȭEG}b`8[[p`ufbo`B""BDD-BuǍ[pd!7 12 PDzށ!60 J :2h>q1}}?\ ȫ\?yN!/_DDuYO`f;gnm7RذaȖevN5hԉgF$2'Y(dr<"#' lsܛPF \Ϧ*w(LNQ21D /67^v7770_-UdU`:oo 7m2!fw"ep[j:TuYg\ X :2yc[Uwb`nZTU=j*x+Edj23s^R 0^7MqZht n{ڸgE20fQ5~6Y$P!}Ҷ[?x^NivU5gTT*UW_N :;;냉Q`|7wEWi*X(;:`u-- fʼd29OM/ bŸ Ixq uo rrWi@VŶAJjcqUly뭷y惊I===~8 Ā%5L-hɥP*+²DWӌ.k(eiO-394F.֖hqz{o9/L Ԅx\S(1,ެX ,{ns"Ԡ?qc6h\5J`^_JIrwHLX>e:UaOYl $;ԁ7>N~|AK+FFd=ϛf~79xgr㴽?Y~54Y~=:vgW[ty kPyjG&~?7 {;# 7Ql|aluao)<e#+#3@"fΜgUs"̝;wOYj-m'Ưo&fWXC(i{KGpfGDB6") or^aP"Ednhh8mϺul49<|X}}ЍWiQWU6c/vvrMS;"83EIQMQ64Mw|`),z/Xgܟa@m6ȇS/L5:,IENDB`geonkick-2.10.2/src/resources/fm_radio.png000066400000000000000000000030641451017372400204640ustar00rootroot00000000000000PNG  IHDRLsBIT|d pHYs!q!qLztEXtSoftwarewww.inkscape.org<IDATXSwJ !  $dć'L'35tbRY@1$t}TBY.BH-nI˰["hlr! dY oNteS~sKTUt"$L:t\l6RM׽װA]ב$ѥ%t]fze{Ì!IzXׯokl|PsEƵޞ(F;|[<[bEa...HRV+@rUy,OOOAX#rrrrA9j&gψRV)r9l6@KшݍJ% 3Qvzz{qgZފ50K:biqڏ;;8NٌǙV*f7o}B쌑16YF>v&8<8 IovH4TS|cۍQTl̿z1,˔J%YFٌǙ{d2 $IHM kIӵlχIQPV69<@Я)ϙ{p @d~6~($ DSӨV+Z=y8:pja|GGǵX9(FGG f#ɠi{{ {~gfXY^fowަ=i 0LR)>b1*Fww7 8> urB81̌f]K.aFY&N7Qf3]UBrJ6wvS*+0ł IRlXx< oή.TL&C>G$ܝ q/OFu 6/\4%-nY[\G$늦LՅ+U*_i!D X{qIENDB`geonkick-2.10.2/src/resources/fm_radio_active.png000066400000000000000000000032511451017372400220150ustar00rootroot00000000000000PNG  IHDRLsBIT|d pHYs!q!qLztEXtSoftwarewww.inkscape.org<&IDATXSSg?(Q !"$L@,NiS/:j^8Ӌ^tv۱RD~ D5lhG(!$R 9{aMMsy=<}`2d  @HHrl((I2QlTFAb~EB$cgP(3VQQY I 3lkZ&+K\.g|܃}b:R??yhsYhZ\.SSTJE. Ek׮sE$IZ#Gi&iii[wRYz +MU]}+OB/8`Ĺ?el|ȑ7ٮ'<XZ 066FJJ %jP(gD&u'uw`2L02cDAK1'D]l׬g@`jfgx&jS))R;fÆ `4woB;MSS3w6oĎ/D]-EaȥKc7nm{,//J[;JBB~ UU/a+Q8?ٺp9C47l.XQHMM38C;vR,U\NkUDQDeQE5lF-NO{(,,@RV10p9**ω/uk&Ju,raauo_9{o&Sx<5VVV~1ccnd2Ԍ$Ilۦԩsϥ099Ν/r떃@ RLAn\pf=9C;w**ׯM||Kڂ(h]IZ>?M%&&b4 7MAFJJ kYa:Vr9.kMNFCnn>QoߞMRRn~(& Oa1~;3j [' ҳ\s%2Qaz3)Hd2l6q54 I.;C?|蟄}Č IENDB`geonkick-2.10.2/src/resources/fm_radio_hover.png000066400000000000000000000031371451017372400216700ustar00rootroot00000000000000PNG  IHDRLsBIT|d pHYs!q!qLztEXtSoftwarewww.inkscape.org<IDATXOs1617S6oq4M4Ti+)Rm/Uԭ!xIH `L @m4~ـ~h㎂Mxt=s~iBV@P4)ceR[FcUB: EQ^voRQYGN|*E)o @Q!B(vF~-لH$kll@V$LqWtL&D"vw?DZ/PQQAew ?@:Oyy9_|W2 o؈Z:f J &'z10`lDQ&'=X__?+bXh4h4VV >'gpp)%7ol6Kgguu(J?$ϷvOݰD"A}}Gad2I:oܸ\099` d|波\NO? d2f K$՝U[[T)K8{MuH$J6 {vW^ann3,10ࠬLM8F344‚\.Gkk n FB]],vn;[[/ ;Ͱx\.GooVk[[/GwogYasQ!+,--єc۰픗ard2t:ffpttTT/ikx(jGr9,h[\N͊Fʍ"ˢjFV3::#&Kwr #aZd2Y!Mz0M0)%PXNvvvm--t:"#|,PDFW +3|ɨ3R2H!&9obQ^dkPT*H))0ފE6J􋡨T5%O$~IENDB`geonkick-2.10.2/src/resources/general_groupbox_label.png000066400000000000000000000021521451017372400234020ustar00rootroot00000000000000PNG  IHDR-3gZsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDATH]Sg]0~I%HzJ~T;Nb3M3u5QǪȧ" .=Pғ$Lxy}y]azf'&c}P$/Ql6 NTVv.a8==eumC} J,NX?p0;;KggGFmu8@Q~_Y͛7۷~n7p嫇NY0I&~.//$ 2pI򒭭-NNNF4z{{yHWWQ4chߖl%7$I|>($:>IA@蠿P(DRt@EV+ zыEdYfjr @__@Պ(300Ef, ?+mH$~#0T/_x𭬮Nǃ(<_\DUUDQ仹9##Rϕ HwW`X~a\j4@~Z/e=ku8]4  2===D'&c$IjLRհF\__ BtAGGG eYf,\.S״ahiEC_fggg@keյ5fgfSLDEtxxz(RViB! (R*Z|9XJBT"NAX.'\]]Quύ jE]'޼ZJB&0 b0HRr~~ܔD&Xb1j{{{lP.r\_]*J$ZıNIENDB`geonkick-2.10.2/src/resources/hboxbk_ampl_env.png000066400000000000000000000111041451017372400220340ustar00rootroot00000000000000PNG  IHDR}o|zTXtRaw profile type exifxWY(sHr${}9첫U3ֳKD&ac#XhUqJ_=)q?~`@(j~w[=ׇr}r t} ̄h$O| T/[{4Yag9WHr~€LfN9rZϿnhPxxY@w+c| Sx~O~ #<|!?L;?(ksu Dn>hy>)._lUq豃{lz HVGSL uG]Gz<"5\[rF5mIǼGBSJlS++7Di I0m}G+ś߮XpU0( !7幅s I DMYhDp, a9e IR0*FKG[R&!Yd1 Ǹ\MfeU5"疍MLͬX5/pJ)xRZ-Vw c9;jܸIfڼ}:wڭ^y@&e3 `m8]1oe2XIo6maiSmM6+O@7^sս,En􏇪ֲS} (!:vdIq"șR!%9&<5ck@xH^bt6C!9C9ںF poٜ*ر-(flHz `הp'P R;sl>Mjp`n7vt O5<q6&`BO!kf O5!uP~>MtIZfYYhU 5ȳB¶A͚wi.J>'K#{Is~4D*nH恸c+CAm YA>琌Bo^#x}c_zti21<ڕ'ґʂ2JYǣͶ1sSa1TS!?fb;-{^$nY4i+iKT|ֈk\$ݖ7؎ 8ݿ^޽/K+ejwMB\$ug[W}|yAO2|*6ïpxA2OᏐ ߀lxw 8c\_kwr :5ZO!Ak>3NM]ळ\uiv!!ASz!|"# DLyёt %oFRK=CYAElB16K8Ut ꚡ1b{Zc%U򈅑uHњ #B1 Nyr>o֦O${놤,NeE60 gsM\aӨG ·R\F9҄ _Gxx.R"ِE^`q)>WG9063JNTr.r0:qXiy0Wz麒zN2J;GƩJ&zOiCCPICC profilex}=H@_SR*"vqP,'B*ZUKIC(X:8* NN.RB 4*L5Ut".fsbA#$fsT>Ey?G7gnXSy8JB|N3S۶ DJ1a5ƍO/~?dߝG: ЉBmS,Y]]M,+ׯ_.>WV8NoP(JSe}pFGGGzu^ qyOM۲FDd9L۶wGW;E&/݊9 P ҽȡÇ.ʹ/?AMNÇ+@(x(++cnnXov5 P^fW¶m\.աx;ohikX h_pU|~?{#fwWVAk[ =y1,ˢ@ @<gGc#NYFG, PM ~V ܾM"pq8 e}a M;w,--Dhim JXs)µ$q ?N}8xhl۲{PU]M[{;)IWw7=(H|@%B558yǣpz ;"_=_yqBGP@>gfzm۷r؈m<˶Y]]0 g_Gx[[s{M0 #G[FYZZXԇҾkCC: pcI\׿ր|T*UZ&ILtp|w׳X,F" e ngμv-[03=]zlzjn \tj63SS >V>[QJXdEXo_ٳg,//@ [J!|>b~N**8O}SJNK8\|>ϵW'$`*CG̀Wg8fp]0{evf*+=y(~vx<;K]$a;~q~?45,FFFimm$R_OMM .\TK6!RUUd2}WWI&`09I[[</\Nr70 $Xť ags3a0=5Pi[e1;;ˮݻ1 ɉ n`ll|@]74\Y`V>2Hl}Kcc#s [ H6f@YQ:7?j UTy;TD(@Q" PD(ED(@Q" PD(ED(@Q" PDQ"E(@(" PDQ"E(@(" PDQ"E(@(ED(@Q" PD(ED(@Q" PD(ED(@Q" PDQ"E(@(" PDQ"E(@(YGg'=] ;GOP'gD4@K P|>T G0daa׮Jhkomt=}Ju(i8x)vOXd#ݽP6/oE'N2IFimk  BX74ݻFrJVs9"eNKeff˲f֭ejkkr lŞ{yp>|{@P(T7ضݻ>si[ZVTpsxnp|*N:EX,e~сמs\ML:M04M^/33ض\&J"{d2>lJ+++\,ܲl|>mY׿Ͳ,ҩ՘eY{<?_Et `YenNS(|>"###?QzQxKKKD"L|RF$WV+/d2|tvuQS,n\^Zb1gAsy}l`gs3p m#Xf#˕f 1@@ykRYY(b1m`xh  ̀xcJp||C_ah4Mjjj(xH&,.,l5`beE3l eț 0 04 ":Q"E(@-'ݑ_NH5?7 T`rIENDB`geonkick-2.10.2/src/resources/hboxbk_compressor.png000066400000000000000000000014661451017372400224410ustar00rootroot00000000000000PNG  IHDR?EsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDATxٽNQgvgXvD@ -k0Zy6V4ބ1Vlh(\>DeD+ 7>jI͓L8|4qUβ,ێRuqy.nNNM]_֗J8LPg$IDx0 v-gԩKpxT*8Js w}MU[kp`,L4O+jz0T8nUj°rz0HpA6ދ:цsUm0D!  `C"@0D!  `C"@0D!  `C"@0D!  `C"@0D!  `C"@0D!  `Hp`˵S p<;e4M%MS{ٺFQ68o[I$8twwi'z=M@RF/vvv>z)FJKQ_ITN:ǫq""qiZ~Z4k˫9qY{?.\<?oO~:r/5s"2d/gYmw:o:pjD IENDB`geonkick-2.10.2/src/resources/hboxbk_filter.png000066400000000000000000000046061451017372400215310ustar00rootroot00000000000000PNG  IHDR}o|%zTXtRaw profile type exifxV[* g$W.~g3kc !9zgEs*)y\†lwqo8I Q@WC T?]>F8`X+3&!]N0T~VcQ{AT -vWۺ[ȧ;{侢5lFe:05w ێ@>M=QJYcAs\p&9)g^kcҦ‰iOHAM b 2 E$JvRRH1IJIJrA&UZr1KNYs%[%R22$kJMUkZؤ-Ҭsi=m#iȣ f2ԙgvc`k:t"30Ƒ.м8bFPcqc0bt$79{s=vZ_umQ0EX%œ`MRt!1?'\F` @_5WIҦx:J_VI^zlqEv;ll eƒTZz+*m~Lp}uzO^-HX!p5Oս}'cD#UU ;inFIKsK",$aHS.WD6CzYh 8_YO#(\ZȐ}_0%7%$w9{K<5d? ԛh=ޝؐz}3eqD<:J]^5t&DN#k 8pΩFY̢viCCPICC profilex}=H@_SR*"vqP,'B*ZUKIC(X:8* NN.RB 4*L5Ut".fsbA#$fsT>Ey?G7gnXSy8JB|NW pi$)찘PJ-̝Bi4.i>幫͹NYOOM8\ovv0pAAAAAAAAAAA @@ @@ @@ @ 0Vg&dks3nWp7d\tRJ_e~]Zޞ,..fj2t:\͛޽u-@x>9>v~{8>_ \v-/\f>pdLOO'I۵v4h}\ILͫw ޣ$gWO/_ssɓlnn&Ilo ΦydVVVjc#W^ׯ<{X,ק Ϝ[jRUUdbb"I2b}=ܿ,fg )ݻxb>>w.I/t299 o± 6 Fݺ jyuRJQJyZU!FJa0`8p83?G!FFi4k,//SU{~Op^|[^ƍFNNNh4RJ11xGu]ׯ?5.IENDB`geonkick-2.10.2/src/resources/hboxbk_noise_env.png000066400000000000000000000040621451017372400222250ustar00rootroot00000000000000PNG  IHDR}o| zTXtRaw profile type exifxW]v( }gKB9Y\m&mLclYL]A)ybH!l8|3.ii뼎~<&A/?îTv'Dǃ[B#3.ڹHy?֐K-p\?: (5@܅d[W /O;,~ŋery[mg. ߮gc >C>apcħ">H+c8F.DáЙ1p,\VX@3׶ZB[*(o[ ZD #O2 ꫯTQΆ,[U&O~6lIY?j5nZU\{ƨ"`z k1gxo$,H 9VE-Y< >h= ؊b {O[ Q 2*g\rC Gc#hri"TIA PVQAMӤ9HAC"M̛Z0hr-ƘbNk,ŔR2ȕa)\%++S}XS͍4,-4k;=t뱧6d# q?X;X~bjk:[ ˉN{6yrE's-1&2ɍk4̽ěo97 > '` 1Ln^a,zzn @9RleEj8jFEK)!LOr?C}fv/~NNgUjpsnwv^ !f:><c뺙śfnĶm/|e"}ub\~\4[' ~?OeY^N*K)Re `S @ @ @ @ @ @ @ @ @ @ @           OJ0 2Wǒs @ @ @ @ p}#{LpÙ}=r_||1VxuBZ IENDB`geonkick-2.10.2/src/resources/hboxbk_osc_env.png000066400000000000000000000066231451017372400217010ustar00rootroot00000000000000PNG  IHDR}o|zTXtRaw profile type exifxWQgw H  ι;x˿tpbl,W?f!Jnj<R OAєPQ7[ԟCp]/nq3D;/G튵v>q\(]Nx Y =&UaG(rUJ7U1Lm-([Zl֒W6. uɊ٬$a煖p5у-?5a(sZab5wke15DRdlFԠefk>z^z#|TVɋ[W6w(9brܹټ-KU^rD!S##Oz{ XI蝟$zsmZzig=cz9MKRYl)Q'K;x.O]OW|,>F14 9l].Yx=`? |J+uAeL" XcW=\[4+^)`ӟs{^gf2LK4 -vu%0sfbι0C{f !NT1/΄$PMA;xDTy v&Zq}R)t-AT Bzm;m"Öc֩5,H9u-5F GUJբa(Ip\8 mX Sm{܊`,,@˻:/A@  k<O`AWxx8:ILJdWC|3@[$$W7dq}DY-Gp} p =\_}Zʆ{G` ?Oų). sɠdA[O܆ V q=8ā5{ޟ8oQiCCPICC profilex}=H@_SR*"vqP,'B*ZUKIC(X:8* NN.RB 4*L5Ut".fsbA#$fsT>Ey?G7gnXSy8JB|N`!MNzB p{y|T-___ ֈD"D꘽s޽tЗLD..|3[p^mٽyFFGiiiP(n]0da~ZZZA?m~ p[T*14%A6`w@ }E |3?`nnv\`ddc( 5fn&(TU>`q8@ܛd*tC `ffRC_\/xsl,WRgI$𳯤PQ4%nT*ST P2@I($ PJ(%dJ2@%d P2@I($ P2@I($ PJ(%dJ2@%d P2@I($ P2@I($ PJ(%dJ2@%d P2@I($ P2@I($ PJ(%dJ2@%d P2@I($ PJ($ PJ(ha:үIs W9oA%瀒JzUds9GBz ^'+/IENDB`geonkick-2.10.2/src/resources/kit_button.png000066400000000000000000000006331451017372400210650ustar00rootroot00000000000000PNG  IHDRwܧbKGD pHYs oytIME  F88(IDATX?O@{ǎme]>|&;q#' MapApK뤱*E@z}{%);e8exwqu ᒔ8ES~:36E;};1s}0MR]& ,mW||yH~ej5\v:CM&IXBC8Vqj!k ]|b\hkپ;i4uc@LLLLLLLLLLLX(z-lc8IENDB`geonkick-2.10.2/src/resources/kit_midi_hover.png000066400000000000000000000002531451017372400216750ustar00rootroot00000000000000PNG  IHDRsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<(IDATHc/0i ,xQG-xQG-wYzIENDB`geonkick-2.10.2/src/resources/kit_midi_off.png000066400000000000000000000002531451017372400213240ustar00rootroot00000000000000PNG  IHDRsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<(IDATHctY0i ,xQG-xQG-[0tIENDB`geonkick-2.10.2/src/resources/kit_midi_on.png000066400000000000000000000002521451017372400211650ustar00rootroot00000000000000PNG  IHDRsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<'IDATH10 umeL}bX,w4IENDB`geonkick-2.10.2/src/resources/knob.png000066400000000000000000000142761451017372400176440ustar00rootroot00000000000000PNG  IHDRFFq.sBIT|d pHYsnnyDhtEXtSoftwarewww.inkscape.org<;IDATx|[\ו[UMͧ(5Eڒl 02â[^âl"/~=;u٢HH=u9}: *.\xPJ03$o !<0B3G"Z/^Ο?c.xT19̴3_yҥ]'%1{ɧz"k""f&"#I"uG0n0_y?PtgϪ ~[DPyo""1~NI,K$0 `6`f(.Xk̼1IGxq詧 *ϋ3lmeYPJB.cpjZ$HԄ1onng̜H$jLOJҝ5#YEi53~wCCDKK!R^HzmqqV0s}ߛsWZȲZ(&JYm/--륥,$'!Dcn7{ssDž/v#Jݐ<3~PZhzgYիW~мvFi@ܜd6HYmu ,//od0YP߶ޡCF}ؑOz})<3v٥Kvޏ~={s]cRǑFiW\՛ uRz̙K_:VU$(\|<AfSSS0EaTD$TbV;\liQ&''#F>}?!~ghmےF]zUlltZ뛓4ZV'NOOp4*\tKnWFQ$ $c̲4=^׺PEa$eYKKKwxOm"He)hEFsUqFٞ0EDӧ Ο?j]PF[H ĉc1dTЅNbԎdY.19&nN#{9˲yD$&&"ϳr^[hmm4L !gO:i ÏG> .ó}k}}Y3H~0effE7EE@SSe[$r`;spqq=<_#?d]_ZZ|}{{{zu$&jCgΜ >MBQWz}eeuB Cg'OzgȲ m@u:(9yRV;$ -t:{&/ݏ?p*10#6FN !7-x%1k99hZm#blooϲT~3tyqqqe,:4*eҢ(ҕul? uq8pvu^EQd _IY`/)eYf)ڭV˪׃br"|_7i4hF| ! ,//'R" jtgKĨ68hlV@v7n؀x= zGq뻀{~}v1ZF`b.MS2H$+\>t.3 ɓSSSi9H|D)u9mۘ{y!q4J)ZA55)q+ 3?bpdh92wzzf~D;;;bQiѣG8 iVժWѻ1aZT*~lgY_0/{k׼cf~k<: ,f툈˗,ǭرcwl>5)u)33aيRg02F_.,,x=b02R4*8VUw'&C4MW^Yy.Y xW1X^^Ə~#\x{PJkIe!ĠRff<$YEbZ1})F{&<Ӷm:R( + CAD!eN:)@ů~++X^^F$$F5\|ii8q'z) owo)7LN0 Hm!z"왙i133Mk|2?9hX^^)~_ h{~~l6m>R _>{dŹssRѨ f݉Ng+Vyns߻ÈؓaKju03VޡCxbq>I_}7-1cOT?B|XTV*akW:N%֎?~Y甇ȅRz')|m8(4F3qv,jjMFkqf0(rǁ8_ )gϞu !~R^LZ DZ&IR^?t$yXfޛ~yWDA-m4uA9ZӇ_L2 3ɋ1_|IwGpץn ŨOX\\w{+NWDZ\h]Ȣ(8֌b4JImYq'./ &˲q,aYYL(3M/’R̨4ʲY,s͔ x;˲?^XX >3gΨv5"C!x1II!mBDmrsc6)o2666o1TI>@>OYM"m|uXqsc(3ohgxVLem#-ܩnT3Tq1I'o{҉VD?eB1!X 5ΰGD3ƄD`sEk{-ܧ=n[Gb]IENDB`geonkick-2.10.2/src/resources/knob_50x50.png000066400000000000000000000075101451017372400204760ustar00rootroot00000000000000PNG  IHDR22?sBIT|d pHYs N N*ūtEXtSoftwarewww.inkscape.org<IDATh͚[l\y̜.].;)GT+ ?9AQQ!(CRHEHQ uӴEJ|e֍ڲbUJԒ\sfܕ%Yr,Y|3]N4|{IqK)9s%ƘֺODV+.a[o @ BRTFMӜ1ޓRJ(ꫯ ڶm)DLlfv.6v vLQɸ~+>3/>%RaΝ 1K@hqq1_*;ID\ \ER][(;4MsDEgttF$ T:yl'N'Z|!D\.um͌t9RJZt:zw{ dD\zJ=hlT$)Ȗ˥ܼo߸`!IH)!, a@Jtee9虦3>>933;55yݩ0;NsWg}qd\˗^vm=T* O=TX,0p)&!lzwN\lL7?A'A|ܹs;ws3/!lXzҥ+WzS#GF>DQC?˔RiNi~`z,I.H9z*w%H83g]ԸrD'3Lbȃ0)58AXdӓNAnRʍ}R&=5==}^_6G\"yq\Y|>_9ud Sܩ4 9l^A+ɉ ;DjFѽ;xDKB}\zSlzԉ|H|~4{=8c^T*~h0 (Vɓ'%"ɸB߿l7'Փ'OG 1`Y_l^ A =1l6Zt b4-8cQJI_(7GWVuY1r"&;/Ǐxv.m$)W*Ic* #LOOJe4Iqmmzg2.'"FD_>v4bi/dj#nw_p#cS8| ]X]mĎc5MD]!1^""fKf;H6VdqŽ$HUsIf77$IeU ɓ'1,ҭVk]XX0 6lefql ǏO00APdF阾d T*~׎G4MQV}?tL461SJ{ᤵlz-rZnז2mq·aFD DD C9::j$n$hY0 0Dy!QE`r#t;%B.8yG"0ô9Dq*4pu)I%:i*8N1`o i161!\l%I"r,wX#{B9SRJӃR8gJGֺˈ[,s. 4w ?q !eY *Ռ]ieeY)< < 5M3-+3\gZZk)SmYVζ@noqgZ곓ahڑiY9)dR3ƫHDDVwl@{>Q >.0 ^tbe2N$Ilw_^sssx199!h62MS?B>2w} U: #VT*B_YYmD'|1^\ڞ?6(CqMg^ZZ~ vLl{pٶ~<,--=T۶pjn=˲6fG~_j)oU+1e5}b~~vygg} …֗T, !hff1(+Wj=#c}X{~~ u @J) wP7gggg`A{h sf:FFo6C0>{nEr8pqqTJ̙3J,1;VIt]h6[F6[[[$e Y0~7znfXXN47nFZ d}}=AD_Z811'Iy]ƍ0mۦ۶$Iٳy^oq:4yI5~_/+ @fggkD ]ƺi*A+pur]ǨVGS`uFr[8[?x`-~?Rߞ9sopGnǶjg xt`MLN ֫qKƆn6[(F>#4D[\FۓϿmu\.?U*ݩI;p_|XNǦ}_ ѣc|یf &˲0ĦRljaqiBam;cl,JREwp9| 0 M5ĉۛj4q.HNsb0LhRKTCDRq ϶͓0tر)q^%mNq4r9R+ |Z[O&FFFNa<MiwhWW?z---5n|Љ7RIENDB`geonkick-2.10.2/src/resources/knob_bk_50x50.png000066400000000000000000000006321451017372400211500ustar00rootroot00000000000000PNG  IHDR<<:rbKGD pHYs[[(w]tIME L/G'IDAThKn0a1Hɹl`% tQ)DyXbO<lFQ1ݶڶm>* 8XY8n _+$\9pJhJNO:ga}[*KӖ|a 0`?ցJdWaK 0` p0.|QuDG/ja1WLwG[kϜzaSs<vΝ6)z Z"f6=+` 0`/L_ɦ?~IENDB`geonkick-2.10.2/src/resources/knob_bk_image.png000066400000000000000000000023441451017372400214530ustar00rootroot00000000000000PNG  IHDRPPbKGD pHYs΃tIME13 gqIDATxou7ȃ0Mv-1K:c"tMF&5G{LܲöH붸-V-;ۤPҹhay+~"D=H4gNB.cm~D;qRBQNC׭S(0t9slQ9zׄe.?lezdm <9Fs#kX(-l qahuuRݚw`xggş뎥hxuL|T*euӟNc!6/}QT`\fX("<Md!qd~-H˾L|ͦmh04⳯빫/>ǩj7-sǮZZлWr5HH!NEq$W_,Wʸu/DQzЊ4$@$@ `/, ) TRl;` z{+HM4Z L:/W 6oey\J. :/^dN RP6hPJ%ffg`YK}Cm}'jʏQ,)D5Zm4 ug~?.\t-Zx^/χ@$˿ /vr_oDh?cƀԒnqQF0OBR$d\f65`0h6铿{v`- RhHH$@$@ .bIENDB`geonkick-2.10.2/src/resources/knob_bk_image_freq.png000066400000000000000000000030251451017372400224650ustar00rootroot00000000000000PNG  IHDRPPsBIT|d pHYs΃tEXtSoftwarewww.inkscape.org<IDATxOluf˿"Rv=1"Q1.h= (jR%`p01Jj4b-3 Pwl?4Myݙooݝ ~4oEIJjulX]s ͮ;g +Çʳ+KHz焐REm^moStޫ<' 0a^9yo &ʁ8 J%f X9yo$"V_[=Ll: Xfy:2O{>Fac:ͪ^zf՚3=j>u[el .νM)DMq}ܲH$Ν[״K|(s``e\~tZ%t}?"%JnG'qߐ+i^2iS$xiώhJ%t#qzFnsD:J-Qٳ#ٜ7p̷}$c"@f1G 2>c} 4ÆŻSTL6+TwmT2 \c_ŠP \1NO|>Y 6֋E$I8˳[vZ/I^S%z7>]7+i@TJujq-y뉝;l]v]7d|( q-yz1D ]DB89$m `g,ҟ!i)|'DM'~ XPtً/{Je2E,FKLfپ mG qKׁ|#H[na@eq:]utPiT.Tw_|y>@E""*O?*$4;i'*\]xd{at<WBh:hFRɺ&aemٲTKBvvdy^!As.31ˡMH4U ٫<'f܍o§=[Ա_= V4v:D$rHHT3IENDB`geonkick-2.10.2/src/resources/layer1.png000066400000000000000000000005631451017372400201020ustar00rootroot00000000000000PNG  IHDRAsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDAT8呱J`Ϲ:$` v8d&| wp0]lH"9.*7%x{e]L@;Xy@j(`n]!Hz3 i?\ ^7ŷ`<v|m~ H1_zwʲ~ݤAF$IGEQKt$75 0˻[~ 9Ip̀x8X[`ManAbj?IENDB`geonkick-2.10.2/src/resources/layer1_disabled.png000066400000000000000000000005071451017372400217270ustar00rootroot00000000000000PNG  IHDRAsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDAT8ݑ= P}j)R)h7P/ x.5FT !O)e>vFD6J dU5q Ecت?38q@.o-( e6ҷ@-niZR];7@Q' ߯@XK7 ߾*Z ͺ]i}eA98@$8?$z!(&IENDB`geonkick-2.10.2/src/resources/layer1_env.png000066400000000000000000000005661451017372400207550ustar00rootroot00000000000000PNG  IHDRAsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDAT8命JQ^ݵ@ĸv>S@y.S?"/ldHlrݰÙs朑GXN=*BʜeA FA#ƗEߙÓQݞǭو׏yվ3"1?Z6BUQ9ζ< Amڧv&Wn FkQ;Z#FչrU2gu!F!epoJ_!uK'Ō7FCtcrIENDB`geonkick-2.10.2/src/resources/layer1_env_active.png000066400000000000000000000005631451017372400223050ustar00rootroot00000000000000PNG  IHDRAsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDAT8ݑjAEϛ A,۸;H K{;*`$EI,eB!I4ŪA)/3̛'JO@QF] rA(QoP%c|CVա8WV,#Ʊ|[(t>^f?= qo۩b 2|f>hm c1OEAϖ@H@GabS%7HIENDB`geonkick-2.10.2/src/resources/layer1_hover.png000066400000000000000000000005251451017372400213030ustar00rootroot00000000000000PNG  IHDRAsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDAT8ݑ;n1Eϵ(фahEb, Vu%@Pt~TT0ϑl6yϗDLcKI#gfi@O(ퟟ >4vrzo Ov[$W;xAHZVXP6x-b4w:]YIuC<`N'?~ -qw6-XtIENDB`geonkick-2.10.2/src/resources/layer2.png000066400000000000000000000010501451017372400200730ustar00rootroot00000000000000PNG  IHDRAsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDAT8S]KA=w2.JŤ%J ժ/W7O@EbIyc"M퓊 g9޹Cm턱ӆe-jDhοqin7ba 6 Ðabq9 ;;T9R߯a,|<ϋ"fރ PI E D$h걛GpjnWCIENDB`geonkick-2.10.2/src/resources/layer2_disabled.png000066400000000000000000000007531451017372400217330ustar00rootroot00000000000000PNG  IHDRAsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<hIDAT8O/CAwkEU!$m֢߄O7]WV 񤍔PFFjnr9;3u)F  8E* 9h/XV @T?,%{֜ ǹvKZkN =gK84rHtE4TPV2Y(k([R~zBJ6rzbέiOIctxbMZ >\835Q Ft覗ʮR<ףA"jD̑v%*#QjZ"A~(rcC--?UDWcq[^~?c#m?>Uˣdº68?`jZJy-ٵyhىK{8TdC3R'7$ ^& ,B3(Az\5'rIENDB`geonkick-2.10.2/src/resources/layer2_env_active.png000066400000000000000000000010611451017372400223000ustar00rootroot00000000000000PNG  IHDRAsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDAT8KKBQ9#R040AAOpҨMfA$RPja@EMX RMFtk}k! h#JX R$pQH6(H !%!2H5},:YOsjHn|pT(ظ\jvV'@,TziEJ('Ȭ zNE)Ic6MϰXHS#׻Pr-v:=gɽHP5yF+:WG<)!o4*n像S) ZbU":᮰k;xbOɧ%令D &lVa@rȮ+@Qq\eT4@$y묕ض=\wDmt:d 1&Ja"xb`p1^~Վ)\> "=055MFRLft' A""bA_/,pr|Du Zk./`W՝(˲XG "s\򥡴0ģ;Syg)IENDB`geonkick-2.10.2/src/resources/layers_mixer.png000066400000000000000000000040731451017372400214100ustar00rootroot00000000000000PNG  IHDRnA0sBIT|d pHYs*'*'t4tEXtSoftwarewww.inkscape.org<IDATxmSn%9&1 $hN+*qN_~:gӪV (Z" 7@as`ƶ y?׏kwkWtG>?FS"82jS4߻Wxogq:`syn߼ PY>;TK## I446rqdl lu~Z="3::;وQs9.@$--Ԭ-l,w'&30L\. PU'ssD"= ˆessY4SSo*ֲDߏ$I~B{{{e}05,\a}}/ppq?{*K/^F=XgrYV,LMΞ;Ǖ?'qMJ7/C!ʏwko/[lnl*R }5jiڈbx|隦\\tw7F$ L\.bnF񾲼b6cw8?0(QD#( b__'G_I$LO$Ix<_^R\.+Fo旧O p\h&Hd2y ./_'Lbfeye>MI&TmeeVy G&!¡?ݽ?w9ߏユ&Ea|lO1wMVl4L&qc1~]Y!H 2TI"X|>Oss3]'O2;3Cbk B`bf)ȒĢQ"p@2d)ȲL*bscݝ$I" SUdYfwgudY&Ecc#\ǏQUD@6% J& 4&'bH,b12ez_E:Jzύ}G撗, >x BB]OF4^vdz~Dp:%)NtJS"8N*?0<6 _wJ8xXe[„zS*pAtJSo jt:ijjW=B*W9ť˗qh{{s'Tv]'q:%)NtJS"8:8Yihhe-{(@lwO4LC!*(!GU=L?KKKklP#賳lo!#Ȳ5ۈǑe /f}# /]BQnߺEXu];T j>"Np8hxXp(TSB .H0LeF C +\$y#uJS"8NDp:%)N=?Յfb|٥iZ=EQ.TǏ8{n U\Y^'"[zib_*8M!Q /8lL&Cww7TfRT=k(2 4n؏?beN:$[SNp?WՉIENDB`geonkick-2.10.2/src/resources/load_preview_sample.png000066400000000000000000000013001451017372400227140ustar00rootroot00000000000000PNG  IHDR! lsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<=IDATHœ]OAݥ 7$vi1P_!1!5$hQ -]H)(v/%.9aucFe!~VhzO2L hxZGIljbr_]0HQbklF-y<ޭ' =AP>f$l,됙,/.:d3iV-QTx= XRXXbH.\TU͜ @)%y@Aq$A))$t9.!${_y"4IENDB`geonkick-2.10.2/src/resources/load_preview_sample_pressed.png000066400000000000000000000015431451017372400244520ustar00rootroot00000000000000PNG  IHDR! lsBIT|d pHYs  LtEXtSoftwarewww.inkscape.org<IDATHŒILSQ{мNBK-m 1!H(hF2nԨ;cb\hL\)a8-(,X` %FEִElQ5sKwJ(<8g PĀSlB @ AB "oިZ]A5StM;b۪v;hLi\~K3h(,]'y3?_r-+ Ӄfekʛ$04jͭ^mFAygBd%fZ.QYwIyn}hk.TkV\ v׍F&e/#x+ .S ٷ<ݑ!oknPhݠ/ƧR=ϏNN~z<B(l:mߛP(*1܋J2}9-M)I0!_} 0%I|ngܕO4 'Ɉw-T\[qMʎXF?|^_qjw nDjJ{]WKB:XllPivޚS)uZqk`PaTmAKm?5px0yU{M+rG1/wma^)ٟgB`I4)ǔa͙}\'^d(’,y-<R2@ ʀ(ل8%qx0x4$ IENDB`geonkick-2.10.2/src/resources/logo.png000066400000000000000000000027601451017372400176460ustar00rootroot00000000000000PNG  IHDR> *sBIT|d pHYs"jtEXtSoftwarewww.inkscape.org<mIDATHOSw?-85QD&:l67Vqx%nr~fY%sN73N˦Zbۡ:衔 lMݼY\>>oQl(..fCz &A 077e{ރFwGS0^nc5Doos,j()-d=JՊR\bPp4CoR^'N"I4ڿRVpePJR0aŊX,5!Nl"jmnrd6SXXH<glt ϗDh"*++ApqJf5~Vq(ظ,9gJsS3w&q-]{%k!?/ijͳe6;;KuzV ;;u06:F(n1HpwLLܣ|olۆfС]t^blºukp8<''_p(L{J? nCRsxz0gw.BU)iۉB--b( ?JeJSTUUKO,$6RZ}WQXTʲRz݃$I n eLPbգRhn9L._nDXGwO/[n\.\M yy;*'0 1.7xsʃj*+>M:4%7nH!hhJDMMh@JPR  $IJzjjZnV0}v(~ .z{`0o9:-}ξ}s745с`*'RȠX,?yS|mmdddظJ311A"R5 Vk-Vk-iiirl[$iݻ,@KX{cj òj&'98&wyQFI$_ ~իLdffrt^ݸy_^㕗_bgZNG"::.ys-!^(ͥJwҿpACn6r!Ҋ>xχ!!QDF ˖Nr PTdeg- ,IENDB`geonkick-2.10.2/src/resources/logo_about.png000066400000000000000000001155511451017372400210430ustar00rootroot00000000000000PNG  IHDR,dvtsBIT|d pHYs;;ratEXtSoftwarewww.inkscape.org< IDATxw$Wy}OuӓYͳ;D0\Illl.am0_%ajl^I]Vڝٝ9UUӡg{{>LwUW{)I66hG M*:LB<@!p |^d$X8??444 @Zj\&+>+-o-uDXDJ8 i"zs$bSgΜI-јLSVl]\ ] ട3l\_\ꁙ,`-ovs75/ Ds&Sۙ_nмCZ\c[Zك'Z!TS}-`q ] 1~z'-Lyb7(w^%Hm&e;|Rd'ygA[gD``   V8$Q% (ID!KDEw9e };8lᅡZk>D1Xnjfjf EO Q!I?"N#J# N#@$dŚ/N 3)Xu]70ǍoED 6.NNV6  c뒳ɷ$! H H"#"+X/'+u)XuF__MpGlqc ;N'nlyP,$\9-SPi<# L"O !#VY]NlݡLUm L^wK\8@]8bj ;*T*SE9$IizJ_E$I#LfL8΁/<#Ʌdݐ/@^ȱVAf+ T1 A6c:V[QQ|Y(B%p^>PMRb!B.T8pq._ݛXL)XKu]g 'Yȱl .'>/n7vzBEA`XzNu#3*b2% lbV[Idx:F,th02P<'kWLZLZxD6cp9hn fi!RV+, lVyȡ)RDrBN\繺 W_? =ǐ~D1l6L&l6[ @z/q|bDb'H'Bb2wLZD l$|z"fESáZ!jf BY$Ilr֍0_jc  {\֚YQǘ ͪ7\xSӘA2;@O, &c "088苋S Qrbѿ(clZ ;3jf jLQje29"]/3LO0:1x" i~ ,ƝG9I嘂Ucfp_PBksn;e/TĘpjn+R)dsLfũRXl6UkM%':S)d2;$I$B|8G>/6 Sj\5;Z1|^6r)"YvyEPلY+$ N#J!NW-f!pv{H#D:NCM N12:p46({Y{;< | @p.A[KsYvCc[P/L&D"d2 q#vf)y+raXrt:a4G*fE)R郑pG0 OSv _:~,TnP,V+9`l6;/uTmQZ)\DbtvjuQT_xf.^B8ԙщ}FB]L[w\·R-|+~vd9G<'R W缮ĩHOY9, 8;u,]S9`UȎ;ZEs%@Wg(QnvDT*p8l83j(SEfCSSbnFBDHxNLUExR.냬0L탃q?ā oYю@@~H1xnrDl6 RtSO9r:hjj1s=rݭdw!_PcxHUx^u';"0 [w^ ~`5Kv98TSI"(TSN~A{I*c1yfJ"N?:}t7`ˮ7ۗ-؄U=E@™rJ05=&b 2렧`*5WirHu޷4ED\/:wu>]O|0K$"0DG{;ީaZLĒÈD"V)TK[[Edp:rZP1@ ŊHUD+wu\>Rϴ\0K-/*~;֯+RJ055%<@47=s8t&h4QڬVH8^^ hlOF;G/jlL*b!^ V*@&|j4;HpXǪ24sX.T I!"+cjzw:h F]##jSزknUߋuWJ8C&ԔJ_ámmR)q8NPZwsZh6!H ,zEv WPf唈 `ͪU-;  49_à5/I2TG͊"x 6!N#{KGG͐ض{(cY#tı.KNMfaDeMqѲ8r@$ Քsc f3*;Ni,V`m9*Nt?4`DnoCG[kImu"QII U-kKkH$ j+lEQ,5D˝ ۻ<_ÏUh h?Ƙ*VTTd⫔"_ZcN!SDQ ZV Ȕ1-7uvwȊMY " ~mhok-i6ʈմd2iM`ZU)$I\ i!"|>Fh9׷t?62gV`d"h?[Vb3 A纖(Q }w x^@H"fQ*nlkҥK+Ê~+]FZ[QbY)%H%_kb1\K2"ʉL YqAre;wnEV`pn9rF#͕@SV0Uvp:ew)Rr>l&]x:U5^(?'~&Vy1&j&F,Vz74&Zd2 )/A,K#> #0.L3c##ǫ#Xwレ@CyG]Q`Bp333kbeRLyJ IR` ""H՝]!XW 4[8&Nz׮)h ȑ{~OmM2DE\%")GhfiDVM31<<禉Fٜe$|*6+֭^)V^,gqE"i%SL4 i BR=6μ5J/j $6V@Aeoam= wc[ Ss9(4W-!ތ2)L&!*T\ "niabj9vY{wrkWǷ``IVu9\nn133Sz)hbR-J$rpi.bBłc p pͲ'##5KβDb_s֖lۜtPr9 4bksQ$ryr^-rwrN߳gaY#l+M߃p>=]]j5ŢE%5&Eǜs\t L&W+q{Di1l*wXVڍ~iYS­;'¿ iY gQ'fʅ/ccrr)V&@< ("9rb|b\}=8b-ǿ,;e9~a`DX17[ܪE}噣2E$!NÓ~Wp:0=cL r~}jjlFbM h&-dRp[D"mn6&ՂTbR;vXr!5^ -"#OV{KͲ0b͊kVUl6zgK&+frp8ԕCժZD$5z16Q(bOkGO.,b0a=j'j&w}z4E2-+7::Ncrg&@ q\ذv5NVXF  }~?J걻\.5wP7cLj\cQ1:6&?ȕY {vttp`ۮwVsKɲرUbaPaaUC]fU$)0&D>ɀS 2z>2x06>^B_<8Xh~nTlk.ߡJ 8LF2Y$8Je&&&ftm8ۍkט,Ñe4|$AǍz׫ n[#Ҋ=7"brjg;H\JrQaW&ݎ5Wx \p1L:$.zv#=i+@,COWgXY,UpX-ߪ7^#ؤvqq"o-Fǖ͂ J0958NdySCA51s)nkKC[Xv`Ost`^.f4Z* al[o ;()k(xG|W@jn@vcY w:V{12f%ѰkNC;ۺCuzV+֮)rp SS%, a "7w]s+@v]/=<&&'>FAM X,Y5Ev06f' +Xr }hvoEzVxIZMN'>o,sXW^s/>.Wȅ|c6Vww6ʚ F4`17 vAٺRJߛHMtclZ=r8*ZW/%bͪ >F)`K/K5(I[uwwf1#Bt8Z4`11*Ekr\xwUxw]oM_hYYP{(ׁB[qp1gxwM[Bv츦ځ=/Ś[n~_ꏵv#b\b*h )^Gqp8l6+&0[2W7q:M;v\UWD{\.=r3!X Di ɕů`S|NuQI`@RokBCo3 `IJۺȖpw-mx   #!H L!ɔUEHSjC`@$uQxQV_N@ڬfev컊 \.+m˶9\yŕxKQuZ,5j8|;SsRCR~+QՅ'ͪ$"̹dx权[Owl1?_^8Io5|^0@QJHD붷Ttbe5P9G6-* ̆l6l6X,VX, /X TlH)]9DKS#c5k)!oF^[.*Nk/) kJPMzzz49G&A,40::P(D"dˌZ8gr"@2gļk8k8ܪPׂ;m6\8{.9QFȺJ(+T7,--d2%5$IH$1::qD"dt噽!~Qt>B!5ZҮ98}c@74ko0z`)IV.RuU(T57#`PSql5fFfJa9ˬ61? VE]-Tb[4.#HխZt) r+wd2YR-U>.ӉW>MR[lbbb62MMkkN'Em)˥\CČcxԭ`mNvl˥&hZWJ }TZd3%/(^`#Pk,0 9$œ&΅YC8[,[,i} IDATA8FBQV`Pt tJ$a8hBL&199p{MszbhZ6`sG\#ĈX==6K9,knє ҁ7f ~TJteΞLjfqĮ\8H$W-_{ݒCzKE6 䌃9L.sX % jl"\--- tmA^Z}ep-\,*΁DBnV{ XUW $ID"Fx{~,@jZQΟz խ.jJz$̄ лmƽLjO"@:F0,I* {9~D^ jAԻpf'uu+w[nJPbbrrU Z7dEQ֗CqƯtRڱXp9/&]WWYuf.I"_)0A(Hiz/&,tv9R,H`rreUvl^c-&lUOiy1LJE~,"6t j2yRonv\"1bA:Ѫض3>ٜ\ZϊE*טM NiR()Xz61-FUdZsnӺ^ja-sLMMrXQG*?an  kJ?j5fo J_jsێC9Vl6X܄@`@ mjjAjTk3A$å9֮7݌vU/'?xjQDKŬj9,pΑJp8剗<5t:Ht=vpE鏺̓mMo(Q3j8fFX,\}U]V((N֖VWeWz{p-[nC7 rQ0Y_8+f28ܴr:15[@8ujXZ9u3gY6n˫X|U{:xYey -w#NWaKv_w5dXLR)8G=m/ukq&v%u ֖>젪5e&~wwe:, ~?䂎T̔tOrp7['ZPkQ7CtQk ֯u͎ã?W^ʪe/y)>iD= ʮ.*žBg%"XAV1Xh{qѾa5%kla쁟،1l\Ձ]x[Q.P̲-^R jcQn?kJ\;q[MLOԿUr?J5kobSMW':gN߂'ࡃzMqe]W^~/bFFq~N9;Ki477Nme/Õ| zPZi"@w ZYjj'kMG{n}-+-?|.xK%/^R%졟?TBhin%S=ع}'؄mDKC$&'1crj NFO G@vщn_kWy̼K/fR]A^tZ-ڶ5_D.Ҳ^z|c GWܸc "ᦛk.|/Xv=s mAOwO#;i1/\%sՠvbH [79 2$;tOjhM&twUR4z PHW>-Xم{>%'?w݂l޴|+m˶$(^:)g-, s2>Bޚ?\q V_<" + /%֬^ìsג^fRr(n8KȦT)\B*U_[*׿TN$ɧv[ޱC)6 VleZRpV2qXXXuzM&zʖڵ`E=shR Μ>έnֵSnB~u#X4\¦+ _,#G`zzZ}MI31>UrR e4%bS7ŹőQ)f} БC=MiҠ~jEpAFY1T"QYY5,"هfKJ1oZf^_cǏnX,(¤ݜH}2+Rd-,`C$MmOpVh(2Wjzt.GOv71@ Pq?%Y/BLnNֺ,4n9m+m^55ANx]6i`>)a | J#'ɴ +γ*X[jXat߁}O|}tlfi2W$Z2-Zʔ8)XňN ?rXnsޯ/SO?U[D'ĤRn{cNs$Z3֑zԍ`l6C +uRĒp^R>y.tvt.B3"_<^^I`i̋njHCj{4a9|K g5ڻwoL&K[X=5+/!0nAebR@@nPlal8$B8 +]p u#Xݰݬ( P)Xa+5,5 d21YʔeX䊽@AHS)d2\WeU@g\ԋjﴰk^ u5fTM2YV`rM&@T2*|/ꌶ:ԕ`qҷDI2 4iD-K><^>Wdcza EzeZհ\&AG~Z[ގntww}/zbժUphnnF)hxfrxg;>'>ɩɒ-b r o\N16sc1ykQK + w/ \/]wݮbZK>◿G?Q|o=~p3:&&' hkmQ]lD(ߙʓ&Jqi1NYN>Dp8B5G>;a]E]w]͙) 5Lw\ߵ T O1<%3ưmv8N8NS'Obbp5&o׿_w=m݆Z_d^8SϞ±'+kT_Q:H6-.qD?Ç* u'X8fY1h4`SSJ"X䬅e78)]qq!| >Ey}k'mW\qy,V+mߎa~Yxp8GO~@[[ ȕ]L}I kj6p$L(@} 8')XU/ ^G?.:r;`\`%gO?_?ku]ر}G'kn_n:9҂ӧOcqf0R , |~?2VKϫH`~anh$HܰđTԥ`{]kg"h`a~EUU!%7tհM1J@+ ';U |}_狚, l:""SSSĂN'MMhnnxH4K]"4Y$w}LMO#k#}ԥ``q155VXJtD8j'yjq+Ͽn:={?qko7 [nǎF >NDקV D$p9G$4(xoE 8 -<߫H5WDr8CFpY%V/8?m $zT%uQW.hdW0=^/֭_gOo[l߂}-3 ʿŰ^}oo/Z[+?n9>E)#NkZE 6łTZ/7`t8>˅G]%?stg$) R^Q}"BSQRQ`9zxF2K~FOOD/~k7[V<Sϟŧԋ}sbeZn:Ye)1,Uve:% 0>1Qqɓ/[ 9nm瘘*|,cWccqknoz}UJ(cD.ԳpXX ۼe ߯^xw/^v\24aZz1>>a{\٩5c Ҳ&J4g,bJ4@?/sìu-X\AS`br K[@s1Ϟ:|=o7߆ɇo V!U}b&XZbp8}>t$/J{m޼ SxRQM\V g31?(GN 5"9L~*EH[+zׄa|㿾7ބ?y]JWŽ;:ETly^l޼Y7h>\i؀puCĴޣ5%qΔ?19d`1|mukE?.qRxN~/{׽ CVi#43OzQň{``1YN[nEggjiPp8Ԋ%LF*ϟ7Cus ~㓓Htө2W+f7n8쩊^3\?4͙^|`y<W–[q5,[*L^;"P[s7n*O$8j|C VyUءMN3,,jMY)7 ޏ;7=\o&CsƎU˝DN\gv[Z%"4ׇYB`TʹVUk[[q$wFN8PC"!s _EggXm6455:w ݎt:h4 ^lfo~ww)>O:rhbSx+*~\$I-.WI |%DD fYfa`ۗ񼔈wٺ*|`0hvr~ 6w: `aom G` qFkmvzQdcæ T?{\yj|N%rYVXP[Z}/Zx IDATUq1&@ɩ)L}:4`;xOmϊ".\NKZZZcN ]DG>\w!8|=C[y(OdjTFF -󩱃LF*sȊj= !X Vűq}^/6oقh։':gfc5s~ㅔ6Ũ!nW +If X F+Ic% #XbuT/]**6wz}vNsGyS?s;>[5dݲM )*jhYWn\)_D8g"vWQ֖\Csaɓ*"twwWe,@v¶mtS39hhJ\$ O<7צ&,e])紤TU>yG_\F4`K0K$16>r"Zb`p4@^\p\I{|=>1je]\.rI6r(Ο?H̰P& %XG; sq~xD3I*V?ngg'\}56\vYA@zV#72+.Fzjii7b /`ߡ#Vxݑ{`KDC ua[vy3B]/OR8~ĢB60rR1 ={̐*ԤWTbm͆l&Sa"dYNѡ;84+gv)HVrȅ:ˤ۷o_RwSj@-pf5gCx;Qwh@Wp8s^D2l ]] rsyRݰVҨL^XI TөZR!HgʉCH4`3tƒI{ᅒ1u5DNݻcN$ ۶oe0(7GϋVa5`Dr-FD8uTG;8usn0ё ϶wnpU,GkKZ%D "8R)ī#%]]];b׬A5 0Ei-\Z~+@vwvtC‹ep.9%Ёx0uHks#أY@Hg8YܾM}9V^P(dX^P_F155d"֨S|>ә4Z־fÚ5kȍ$oqq Fp*wѩPlݵ{ݩ1훮DWw^C]ojzOM7^+WP(oe ^q#$IB"(9ٳg2+c^#²\3s:Ipp]^|A3d, 7lokC \ mO$8s` a},Ћ`O,³gΔY~ki.,!VV~ɓ%-mA=`>G1>>^0A@ jhR_,䜑CJa0 bAUS}ׅ pLpL~ރ3` % O={Dqիk4JBRo`ڵp#L7X:8;Ѡ1gΜ1uh `Loh,yA-4Wh5VUCg{nOpرr9~pth߯ʏqXVB6!z9퓆[b&˛N FR3fqϞŹro1eYvuqN$ 'Oc W\~yrL7sw[X,ظq#AP?333c'*HO4 NneRHe28~)Hw0W`۱t›T9GeEOc|F8XX!G&2,0%²,̻ZtO۱Q"BS ^SL !{+"z!"RJ ]GThPƑ=4t..]wtw0 GJZn\rĤ"u kwttTA"wgΞʼngNKpȓC,β,9Go`j:`\Ng%>te2X,MM=jooǚ\phRi\?11' ҀrlaσFgN bOn 'H>vDB}.?>KmE0`MrwXnɞJtmVιH$'G|b1򱹎Xu̙[8`88r\ V7n4EˤA/z|z?4ܷlo޻wo܎β*]84wO&Lޮ{W$"djS5Xߏ+ҩlQ$}rG @o;>ty Xc#ç:hH,1Akkݑ܌H$lp\\~`!J31t0ξ` c7\b .g W0 ~J.1 #X^`07nySp؉x3+h~ܹs [x(Ύ_J 0Ypr:Պ@2C d!J,T|oiiQ/rr O:O=YJN^㏯/:Ws @w-s)mVʇ܌l6hԸ5ftىu׃b ٳ8ta]+߯-w . Ow< ]ta//"y(0&ө|gyLnrӦKZRj;3eJM(EȦ ;& "URhs-]BekS4۝9;7ۛ&]u_Y33Ϝ3C5r$ Ce?Aܹq xgNt _HV,tذ0[YXv-zt)+C޽nݺ$G^`5!00hР*tϝ@>By=QV3NK;Lv VXaׂSXv rS4Eyy9֯__5fϢO> Ht@a;'@f=݋/U+ `3֮kA@37^^/*{CF" 4MMMzee[~Cطo_rFHhko|T_ K0 1_ UwΒ}w `e5C /[˗1į =,4 hll$J Æ 1AǓHY1pugVȪ=d Ѱ~Z1ho:]D߿?zwI58#2s[[k>d2>Yғ Sudw[aՊ >| @c6n†1xkW>KKK1x`=DիqOY|8BVJ/[уX~a +U˗4xTv֮ŀrD©av4M 2ibƍzq7p衇biݸq#ޜ]Ukw,H%8;VXX|ًC֎+Wo̒,]SVbSc4}AUUF gKVŦ.pۂTv{Zauf>1؊+V兿+QE" :HDv# رcQfbgb3]`fbA*yN~A;*q9 ~=PDacǢ$>קwV|hhhi2kv>!.>ɏ"ڊԜ9X򞾐ڙwSC ;У@}zcac1 \d5uGE\ѫW/=C.`{{.64R67{G 5aDO5 9ctqRXj>䓂14; QQ]FɒUT*ޢEXn^&`'ǣ60~ၑ~=A27~7v,鳅By۱x,_f'!{}RT@&i]jVݖH) Zy_Flj6QQcTۺbkk+rJ32jԨoI˒T>ڷW S?C0o k*cp=# 0?ƌ9 ᇲYڰdЊkcQFu2oMQk;koKO??'v F+`lUQx=-1 7j$Fp@y*v,[˗/kvH$}(sӝJc{?F[#/WS߭KlvH 2D^81lpL( QeUX6RVV 1,+%B4nnVE3gΜ%VX; 1nBLtz8DC PGuq+ʬVʕ+baÆaa߿ښYv-.VY_Bډ^UUnc6x { -{(VZɦG?|0 :F҄zS<6l{/HoCF~ͯ[5]NfGr#/6F..*ȊߨQ.N)ߞNc͚5Xf 0+!SEi&h>^\jr kc_.9Sމh1n5 ď2#"GP1b8oNTwM}tׯuаǹ)))0h@ 0ˀ=UTX'Ѷ}C`sSXu q'쫫g V ő"=|Fi)DO.uz87oތ Xa6nܸ)b0p z2R٧jч+3>f\?Ι3gnsL30. nLb.EAʱߨ6thQ )-Y>ڊ 7bӦMhjjBSSfD$A޽Qֻ7~uyoJVXE|Ukne\m@32j +##Чw/7j$FBn{9q#ݼy3Z[[܌ֶ6teDPRRh4ݻ7zq rTQ555/^M=ys/nomC+1&L(MS?ut֭@D}*Ɛ!C`@Q*tN)t#ގt: ?I:@]IBEHHinUd,vJ)+Vŋ|*whԶ63gVX & L tʶ"*b0|P 00zb=Pouo\T`ڵXl-[~ `~PMs]#iv1b+6,:-!zF1| : *nM4֮[e˗+eG|Y0[Lh9alUվ`9ga;fB2x0 ;++*fljlڵkr*X 4)k ꦅs 5;VX39NW#[L@ 4_~ۧOfGQuv@Q ƍ~Y׀5k֠ig 4=Za}N8冯HAyקOfv ._OhjlDذq#74`Mz{6>^,\8SU9D+? Tmܧ !`(wR*-EiiiGl㝡N܌`sss6af*|>ãӧ&Ps k7 d L| dlbB0@qq1JQ)0!:i?QlQ>|mmhkoGɟ ~MvQ `M3T0:L?oս@o k7g/ݷ: 'e\AX"´"zhg!tI8jv WY .||aڃ0a°v! / 2}Fԃ0Aܹw@@n#6PƗzhD JhA+/0zGc CLt#tX-KA- zyC}q KӉ &1J1WK@T0xo0pV_۞uaր k V'>a>)bf8pIJ~X)"6eә $6DA ۤgj4Fh4Fh4Fh4Fh4Fh4Fh4Fh4Fh4Fh4Fh4Fh4Fh4Fh4Fh4Fh4Fh4Fh4Fh4Fh4Fh4Fh4f@Dba0X]"zȲYD.p]"m|7m_Ǹm۷]RR ":1{m٭cfRKD?$}y9=|WuuYȪjL`fyލEN`3RNm{Ӯ{8e"am=`f>Wo['J&/3őRKm]We}DtAn9fF| g!F+y~n R"`*0d"JJw Mp>ݿeQVDC!xO7[ _Bg3Dyab]y#B<R{f6s#Pa1`A8s]D5d/m^U 5D73s9_C "=jY֟`ƌ?,//?@H$r q|=۶/{S_U!a?*x"B @RgL2%(/HOH8ED/0yBޣzxʮ&GBy 0`rNwºVYYѶ_۶}83<; @*B;3Ͷ-z0/LA"dWi43&2F^N)uFlܣA8X&Mj ӞR?2f.4Mr#yp=\-?+ 82 M`|aQ.Tge] . @ &M< MlEEDT_KN˖+ĤI<H$p8`rMD33詉'~1r #?innkOi4&u)6]"yR4yR,5m4! u{DRgg T2w;|7{2W=(sSBZn!ă| 3ceDݰX,֩yטf.0/@kFh S[+i~@6 y4s0sR-bsK3漬\$o/d_!]wmC=^YCX5(5M$"Nԩwy)P ,;?D& 4/0dfm۶BN'Nh0+-<u^`o,+wSO!S]-haADf_8w]uWf6{sC8Uɋ?8@c:~27LfDi_?~\axO#L](_,{ 7 H$᾿/Yr'OnB 0!3555%r =+3^fޏ {[ӘyZ@3f:R?54վ83Jaz;2C_9ƲTna˲! N 9you3z|eYYRǹbE@DkžiORׄCOr3n̓sdi3_X__zvIRj 9BOj ctB柞@6i{1󴖖%v D4Ŷ3Ӥ+[ZZFv'Oٶ?&* ۶+1}pa xIZ=D"oV\}^mP۶=ag_WWw`۶m… #/X˲,CHDjjj̝q/eee%We%%%gQ/˲m]-Ϟ3obj9>A9w>,܄'sy}R!y<2u˖cfJ&g* B7b_kjjJO/Ap>2gض|X!Wl3ollleyީJQf: ~ܧOnjӾ-3m4c>P !;AqgZn^.eJ) φa|^`۶oefRLxc߈㼤| !$3?`|.\illaf93L˲LDS7'ɛ]]8"yޭJ8߻j700.<;B)5fyDt3_8ίD0rf38δD"BL 7 KJ9U)/"UXE۶f*++7fKGŗ?~C]]݁CD ?u/ g¨SqޖR~ynkkmվg D$codfT :O[F(Bl # /jNfpSyy7R+x"v?0b˾`)3NDl*/ FA0 ׅDZ,{*{fof 3`t)9,YfH$vi˲ z0<{  fa7j,x<~HTRffADxx 3+a07<;N)u+M3 wyލǝ;wn?!CDD!RǙ{B'V"_QJJs먩1ROQ(W 3JL.FfvRow8_ B|[=CJ9n#%\;k֬kll0|npW"<{OB0_j"8*t0 !8Fqh" ZKJJ4!=;91eەRwсQ◹ ø @Q#}!ă&)mJ׉h92gG !.!<̗&+if>:|B(|"|OpRF'J)OF߻wJ׳?G0ψhÄ33]LDw3&vqli7)~8]skkkasjjj!LD\3IR}}?+ɲdG0VJ_]]].cY֬faooyDy5"@D¶8L ƍ {}+5aɓjSݙN/p[x /vm@ye6M"#: mmGQr c"3^z}I)u$3OM#"n"ν<FD y3Rff(t!RB`f@:/HmD'/R@[nF!t<m02fyRj_ 3si~nK]@_ɓciK÷2t0s3򨪪$ROvff.6UUU<{@J9!D4$LBx,@ }r'JnBJy3e/P6+**Ra9#t 3APPYYpr2<'' WDLqyCt؈hi[&D3DڐnA;-LQJYRR2e'"J,\ s->(3fجz,әP㺮nR6SMMi_X,2LDB 7lؠQf2LVH$q^h ^RJ;&FʥhOTJ~TSSqCvR? ["33ft,zqǹͶNCFf~oC70?oOPJ@DUK.ɶg=CBMD'͜9s-'yReg-\0s̯Q"b"zuN G=,__pHA!(35w7f?[ZZ 6 cQ=y.]:@I}Ry/s<@<: D<|xR~[)jNB@KKMhy)埉efԅ>)ht뺏Q@D2-˚JQ^bKjR^iDqj]k4Rʇ4x}"#0'$cp\qI)\QQ1uGpccD-O<SJVEEś=ʙJcf(DR!īR?XFD`ׯu*9<{wx|yjll<)TH)HDR_CfP/---FOK)1y oPf |"]RD)LD֭[i"iʔ)A2<]}Z1p&3J{{7~|y%3- Y^fq]!;(**+礔<}߿P ˲ eJ˄x׏O"q[bm{7]}1,t]w <@GGqpdzP+fk;̼yҥL"di3@D#cd\]]jYW<`3 !8̜Jq rfm?h4"E^zmPJ-Ȟ$3ym؛ٚ5k8ma;˲NLomY֕f8ù~NDhT%E]EEE ɫ:!ĥ\ADC?v_VJKD1fY7r_Lf>ö^#۶;fRvFtz93ߞ'~`P]]DDg!/߳BlTJ.x$f lj߶퓉q":8q(.pRj433w,˺7GW}?21<=Mζ]y J,}l;AĕRpr3mJD0Xf~Բ۶5,/eYBt܃X,6D -[$Z f>!,C) !X8NɁ?VJݝ[13_/D4'c53Z1p!}D4L)5qKÈ.q3MsBD | 3 D7nIB|9=kpESSSFAJf M7xwyj9>kĮ@ f^=,"SSSSw,Fh4Fh4Fh4Fh4Fh4No9M0mo;9m=e۶gJ]3˶o)巈Ͳ\ Hd X ɲ?3=HEDB<FP ZtSL }_ t#8b}**}`"aƵ .lڴDt3%X,9=&MZ];Dܶn}hÖe='D,ydۻ~@8ؖklDpضo}}B~u2vRju L qjH)/p\=_'7Rٶo~MMMԔD"yDw())yѨ%8l}]0wrۖXUVa/))*^z˩onZXv12Q RVV_D[lƑBiB 0N7MX R}?!Fw9c-IDATfe]/VWQQ>1J{RJjkk0@Ex&1”RҒbIJ&"26H)ua17F5{mu1L~/++sl۾q|߷ι`/"m:0tBCϗ"3H);FeY߱,#(F*+"d-mnɒ%Gsyɶsl۾߲l8/q-F˲d&c:\ѡ=hl)SfWwm%%%]-ngf37ƴiӌ G@'"-e/n39e]iYmivvi8&1V""۶o` ˲ _$+.m' M"^UUU f/RQwR'&MVJe٫,z!D)))B ۶oۺ/ %,gVTT___ۼq\!}q)v#H3s ]/mۯQ2 [54C!Ru .(E{k„ l׾p2K&l;Dby3C '!r]KaXQA  !.b)/$UJm9Ѯ+C<u3{wA~T*U$|2 sDTVV/F_&Uv}}Z}å 2),[pa$Y<Dt`h*G@y"j# @bazz,qۄuaninn93ƙh[ ʶ_o[y,,d왘m&C)yz$)SJ?pV8DEESJ}ߟ mD"1z"zuݛ$cfɥ?0 x/"FOڷ)L,&CDG{/2k:y$"yOYTJ E)uZOddP02X0 vf.SJiY, c7jii*3LD+n^KKˉ9 %8Vg":@)5jvj6u/9Ժ3f )fBqRj=[!AoqbB)F>'j%KJ)8qGiNDs\_np9{QTTiWXGf<ѩ`YD<3ߢ---O>] ra{DtD"IB4@KK%n!%6DTm?L(qB# )իW+"z<b!< R|߿&4I)5Ӳ"GY/ԙ|IqaH 7.@ْRnW3R.\@Dq` vyCQ>]SSc Xi!@ap0*ܫee4JD}wĉg.D"Rl^SH&A0,έ!W !XleN;!]WWUUU- ":vaVg̙#1,UÀW4]IFEA4FŘ5 ưF8^H>c]ur0܄3gNw}tO33\ּUt}}RR9:bT*QevH3Ulvs*ݨһ1ƌ64cV$t9 q3+ͭ[n1bJEXe9\.8qAZ9)Y `}T*ZDњw1mֺ=9|}]]sk<ҾܹOơt˲~=ax1_04cAZe }9 õ( *i>lc^fg@{{)ZB,zO)D&Mk=Dk8 yD4MRjnݘ#xW !Z?Tg}p- RjeY4۶Z{-xM q2qJJsYEmRJH*R!bx\Ec0 JQdz ðRsօDRo!C3ƕ}qƭ^qX qD47S=ϛ^WWLD+.b3?&"Q*yI#)$)4Ta aBIDTRp/Ōb!}WJUJ3\oc[j33{ zy޻aDp/u+W۶BDO q/|)T)|%3ύeB\!uk%3JQ `RK|=d)mBL| ҇fOǍwr%v˲Kmkk;_6L37ByIRˍ1gXApcTzuҤI]J) biۘ,fz@Ds( Q$[뺫R2{},>OÙx(N@U〸a\ !2IMJF)]Ϲ5uMzs`tUuk3dȐ/v)^/Q̼F6_. :Ӄ RQt]C) Zcz9T?,Zx kjGWWDMs,LjTO- ұIQu!έ5HDk<.f8h-cZwkٲe_,kPWWW8%qf\DJc 0c *$ Լw.۶'^ ðJP۶Gckhh5I&HQJMV{{rqKK5qNϤe w5+BǍ1Zd2*&f>"pVT93PJ=nݺ:5B!m&"_)DHTkLDN^z5@kg1hft]k='81u\§!|׿S(,Ro!\_(z/г"RL>omm <׊i3_gYzҥY*J3|ffRJ]ߏ38V)I!ӫy+lhllIKDޏ:\()}&ܺE̼0ˇ3BϤOرcO3Ỹ*s0H)PJ8g-m.޳_:*\;(y1fni5Q MNO=R,HkrKD L&\̟Ӆ1A|>_~eNPT~ X!37}RjykT/ݻZ)'%-[.8jFc̓ƘS߶2h;}FDsRJ}HDP* `kbS1[qU.[ Ƙ:Dwu;hNEO+>TJmbyDuI>f)8B[.+1LVfQq6666n!.zeY0y~_)T*bQ$*"":qMZ?0׌1'LT*13oODe q.0 7*:DQtQ~slTǶmx;VwN .fu򚠡i!Ķ0 7j;9/۶?1sa.QJmm{)3SqSn)Ap%wƺ`Ȳ,_)aY\"u% g"jRda|U b[lykB{bO3"˲6Rp`c.0pϞ=JcQMIř6'y3&U*1G;v|9}W)qUJRi:!:9۶#---{1ShRjw'rkKIENDB`geonkick-2.10.2/src/resources/meter_scale.png000066400000000000000000000103751451017372400211720ustar00rootroot00000000000000PNG  IHDR$,$AsBIT|d pHYs66+tEXtSoftwarewww.inkscape.org<zIDATx{pŝǿYIX" f}@dYrW`@ R?qs Jڈ "Z*z} P:4;82yv4$d2ZOm҄իW׿ryKuJ{M]IPq3P<}":r3׳7Ńo;p|sV]H)%=ܹs](&&X B -:d׮]7nܯw]u!nbDd2yk.[PlPXYg v] _= A~/ɦi~lsP׆ I)H#MY)555Gmu=r5d>dYe}4M7!"<ϻWJ ! X 4ΥcYR 'TkMKR4fOJg(J-"< PoooA:dɒ[OD/R*Tk}}=!ѥ'tRbv)e8v. :CQ)5Rʇ}ˈh5$cvuu=:<<9,__y|fNӸ >ƒg•N__-0U^S3L&Bk\R,"5u>b:R`ooo-/#,Zxܠ|ݞRJ=DDK VJ-ګyRQ"$DXr)pMX܄+c0"RJ}@) YSL]BKDOHW'Z8dk׮ Q2>knn^Y]O]aBaBaD*Q#rww$۶gF*DD$ 9 Ee慶m?kH; øٲ%I3 cVnܮ!f&f.V~[Dzd`]ffݶ͛7u]RB/,]V:,MwyޣLel ikkӘ 84==RʊN3̞={8J&"!UJm"r|>?SO"c "N܂QBaBaBaBaL8HO\477h |7O#Rޮ:Rf"2 b̸V|3'mN{iw{%"P(lu۶'8Q/_>j9ܥ0 㶠 6ȉJyܧΝ;O>= VAo#;s=;::yXymۣ/)5M;{k w r9ZӴE%#^}ËY6y7!D8!nstGo FEd2;;;/N$_B,kjjz32!47ϝ;##O'\I              1ᄢ"5BF5Msv1"zO?ֶ µD+PJ@19H\ p_dBS( 邔r=#"Jӻd2*ODLDZ#+n"m8{jKgO9[ƲeeY/""Sq)"T[B|Z[:{JӴ!۶o)'xVq"3֭-{7V[ض={nfLf !854y+pyˤ[ X!x̩)S\5×˙p%u,F,F,F,F,F,F,F,F,F,F,F,F,F,Ƅ\mL&ofnȅR 'Z<^!DF)H6oUJ5444þP}QhHWJMD5{gz. ߘnoo'Osyw&l6̬3L*&{챓* em)SL* bzRK|b&ºujGA;Fg.;qDt>3p3l\.rH.j#ϳ,.H "뺋B_2s;3mdf_J9Rj .f&۶_TL۶}ǎa_+͛7O{?Vz8&"Zi{e]J~P- Շ$C)~'o'Q[FZx{azTHp Vw";Dk5UQzv^GxX[8Z p#;ugJ*99U*\okhh]Kfʈy?V֦[)PhldݺE_v6Tзw ҍ1*LfـaYb^f8Q[06t'ɠÎ~AJ dS5X))2U([T-:08-"X,@f@L>el8lrTdgg_=!fnz ^1YE"lvpC63|61JF"^&eU8ʍjuqdL֙ Ed-Y_^)?|pK&rS]۬J\p=o+k_ѨXWT[@5m4@̣.q [g%GվP՛M@.gEoDW[^fIENDB`geonkick-2.10.2/src/resources/mono_radio_button_active.png000066400000000000000000000023271451017372400237610ustar00rootroot00000000000000PNG  IHDR3GsBIT|d pHYso*tEXtSoftwarewww.inkscape.org<TIDATHMlTe[uD4Bt~rBBkФB!AߢB$WF!jTPZ:3$j`!;v`:m1%Fy{=wws?$TV^6𵈜у\5A`P8iZ-"rƎ :\G{ X'"_e&hlKfsh4Mf>k*"WGQUkO6. io0DdbfNUEjx(v7lX?`7o>{vo ޣ'73 f'{B~IxsdByy_{GYYY]"t@g[9yީږ;+b:#Kx %ڴzZ@UXUG~8@}G}a&U`̶t.kzuC,sT̲[3G&wo!qݓhiZVXkwD"1ho1ĉ9CD74yǏ5 7ѩw8Ih RTūPy 5ۯEJ E_U\OjUJ| P)Q^Ԧdq#N< .iah. ΫY=],mR\(ZL?ztw.rHS }tDhmF@ACCCg'MmHumaaᕡ:2*k8NaQQ($ϟ C޾@*עIq\֮}gYbeG0L59&"}cF Sh WFE3.\X}Ke,O z\`0.P[[3ԩyyg***߽ 522s᰷'߿8kLT>o}].FVQpثŚz>ReZ4T"rRU/$bߗO\ۯ5׆*Y7~N HԋxkfOڗ.H[zqF+两_ZgF1aVvATvQ`.._Hۍ]ޱx"IENDB`geonkick-2.10.2/src/resources/mute.png000066400000000000000000000006451451017372400176600ustar00rootroot00000000000000PNG  IHDRabKGD pHYs(ditIME 2>2IDAT8˥@EOA" 16id"f-Jl4FD E]XIjޅ9nj~U]<f4QV9NLӜiqd2 zJ)jLo6 CRضE^8C4u,햋Pf am95 c.BZRJ(*\b! "!p4MK"q\^t:uV v)%á=F$IJs0ܶ-Zk;br)hx x?¿y~TpygDIENDB`geonkick-2.10.2/src/resources/mute_hover.png000066400000000000000000000007431451017372400210620ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs(ditEXtSoftwarewww.inkscape.org<`IDAT8P?.B  ÄF'| FI-0&[bv͞r7܌~?Tvnu]G)uy1@Ӵ!"$c0|ztb6uLvAQ6I) A@~ )IA)Ulۦjq\~DÐ|eY3ш8, ǡ^c&^Z(t:ضM sÁteYH)L&J@e,XV}T*aY6Mu9m àX,Eb4Ms0LVkFeRoQ} J)VUd2v!3 mP.?!Bd<:2y>+j}vkIENDB`geonkick-2.10.2/src/resources/next_page.png000066400000000000000000000007151451017372400206560ustar00rootroot00000000000000PNG  IHDRw=sBIT|d pHYs-o-oo]QtEXtSoftwarewww.inkscape.org<JIDATHՕ@EA #\? &MM+٤S Vl-f~s˛b6뺮 B>Xe8@OnG I8n)Rl61E8p]N/衔b^ò,ahZna&! z$Im}rzAi<@廵30:N(B|qfh/ Iv;niEQp8 󇵣UU!q$ha1{z,HeY ~(^8Hໂ4Mq\{)OAUU//S8nA)7{!,f hIENDB`geonkick-2.10.2/src/resources/next_page_hover.png000066400000000000000000000007401451017372400220570ustar00rootroot00000000000000PNG  IHDRw=sBIT|d pHYs-o-oo]QtEXtSoftwarewww.inkscape.org<]IDATHՕKaǿ ]9pS){n7Xs*FBb -*t `7t5w}~}$Ni.i(^(>/75%Q.B ._"XD"JR8}~$'TH$`G&sbȁ JQ(\!=G(6" hj=bx~ѶZ`0,0+PwTuLoLBCq^a8HZtl>@u9 dLe zFk}fhu2RvBf*f „IENDB`geonkick-2.10.2/src/resources/next_page_on.png000066400000000000000000000007351451017372400213540ustar00rootroot00000000000000PNG  IHDRw=sBIT|d pHYs-o-oo]QtEXtSoftwarewww.inkscape.org<ZIDATHՕKPǿoR0!'!I{_<zL$0I?AB'!]vq 4nAN7=x?H&Lax=41~qlhkyACekx±;j5t]_jRlfpUUͬiqZh$/ HE+$>gh׀(k IB>['߷bkVIENDB`geonkick-2.10.2/src/resources/noise_groupbox_label.png000066400000000000000000000017061451017372400231060ustar00rootroot00000000000000PNG  IHDR% 8DEsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<CIDAT8QhUffЇR&RP;`E Ziŗ>" F,)Ft_EURٚR{|لam r/wυsNs!JDs JD2fFGGJV11JF$snZfffC!y5f_U J4M&9>Kj5KDƌ1;˲Z^oVHD&&&$O|E <-iz ە; ˲aϽs{ND^ڂk{$/a8s=,ˆI~ `hYkN4IɲlG T续{|v'K8I `GXk'stiiiI `/4}pJUHo4GIj1ʲ|t:]ׂ xJUi,ɽZڦh|ocՠZsPwp*PxdR՚fWD'9!;څ,I{97 EĆaxLU6TA3(qjNGQ9 n@@Q0l< `HVODd[Ǘ7Vks>f8P8>`QD&Izoax'!k79b) $ɟι,*U.b=6eM p;ww.mTI)hu۔RSJWJ\:u]syyybu:<%J-ȼ>~׷Z5J@{"dD4J)`xxxi\3 cܲrNkK$f{0,JQYG)umZ_x ZagӅBa6(L0CDR/XZZlp-زyu77$o;h; :Dm)f mwڶ$3oB5;EwK8(JTrܝD4=mx2pk<3w:s̼_JE&s=߭2(:$}?e\ KeVkyޭƅ/jJ xiGRՒ "4>TO!"z_Jys'E0;::&m~8cnW.>P_$l6j4aO or\[5_, BGqD4HD['''g۶/T."OdQݒNP^UG"x= i"tuuEB )zL<:!>8V7 j}J(&t]WAp? MX&Wyr,b:gg?}^p9sFKBfnB<efm>GDR_ L~`D+B͖eZ:ȝWIENDB`geonkick-2.10.2/src/resources/noise_radio_button_active.png000066400000000000000000000021601451017372400241210ustar00rootroot00000000000000PNG  IHDR0 9sBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDATHT]hUΝJ""J`iM$1TZ[M5iC2>T%AY"JAٹY 4VDk ***Y`=>em p;ww.MTA)hy۔RSJUJX"Z$ɸr -h(᧘^GũM 6U3 JSa|ۘ+IR9BI hO3SRl6 FGGkRC&!RJ@`M+ekd&ʐ'~h$0l/+Da^ u555mnm{+3AuN L stN"n۶EmQft0ܚLq9s̼[RI'n@\U>)L$I)B\,k\Dv9.x^k=UJ NMMcHk=*c*]kuB!w7q"Zpk% 3ruҶ퇘yݦi^^vVX;l 0] y"4o>KGElnnkt:\B,舾G<꠴Н$0  P )?Af0 {@k%8: A2w=D%JSӁW;sw 0 \(Tb0֤#$r( ;*BiID/1R}M(O C2,^4-Bv4e343:LSRb{{堔R!`aa;;;XXX@ "Hi`{{NUx4&&nT*Ci:hZ0@\4 PJ]9G#訆VY.=Cuj:j,FoW4ib.099 MF;w`cc#Ӧ8EC|,!LĽwgAQثT0<^| fipna3R`bb;;;Ԉ0(=mP/Oowvvgzss "(8;;%4ߟu:r ! ChPTqtt p]a !Y.coo7mAW^~j`_ `l.^Ӕ >SQ gIqGGV*(h5<|(/2UΆl5R soWDǜ,DܣP6#~4%,+czz*(Z+a ƭ16OqrzٹYm0{x }dFztQ\YVY=u$a &Ϯb~~BPɽ~ Ҵq}0z+@ZSh8/9\Kh60 {`r]zE~ ~F}@e^ rH|',qAkfhJ)03nGS4:!x*sQ4Ą^p.Qk N*vJ8tFエ9nBNɊ\‘"C%S. pN[*.OMMA...Wy!/|qWHzLq23 `Ze |X,s9-\);LMo;nNv%χ㸎EnIFׯ" g33P <ﯶm{333jcc#KKKzuuշ,%mqbdʜil^*jBoj+++zmmB0bfQ)DT̖EV>4 ;-?Jt~aAt=zdy\oy?],SlIt% jؖ% wu&\R͈IENDB`geonkick-2.10.2/src/resources/noise_type_brownian_active.png000066400000000000000000000046151451017372400243170ustar00rootroot00000000000000PNG  IHDRZa xsBIT|d pHYsSSⰘtEXtSoftwarewww.inkscape.org< IDATh_lǿ3;{KLۍ DH4@G"Lj}"*->`T nq&-8)!&CMl_aw식J)B_iowgggv>e+\d26<<̚Y,R)d2 _c6DdX6Hh+e.dnk&Ӥi]UXmmm*N+z vCCC#2QFc9BbBJh.Je\MSsr]WFrM!6ðr (Ь;v,L&c'4M" 7pz6 D(˯J uF:rK?) `3?bF%^W%D2us{s|KEodj%QBVDG]U jgg2t:Ll1Msë _]ٶ*|ρf5Dn¶*ɊUXfO?T$ C׍o Mܲ-$9-:€G.|v~v۶)2 ;p,+*߹<8~\5ȋ͚D?KdLN(;d2Yd2L j5M[-t9g5[bp#HDP*ϡ1];9Za&UU-H( s^nH3:Q͂=݈4]p]㌁sVk}]k@`D bP 0pNDg D\׭Tc9gA8rWǞz @:.ON^kMGΡCIzŬp8[u0S&,k BZ؊#߽lš.|k_`4 .\&B4{` -Gt ?0$C,9QO3~k]9;<y!i[4M`x?@qz>wŶBp] %%5ė-Cֻ}+V,Te_|xBBhqٹs|<1 j!>ܜӬ@u.\'g jDaf ՁW`V*aG^W0:&P.w iX~=[d9|cvLaףWckvCŋ:Dk+g0M_LeSϟE$g m݊D2{DuᙟgBh1#W5_r~8[a֤\ts<bpkرa=6Fnޔ±wc:"(N<7C:X n p=11S*fE(B ݍõ\Љ#T{6r#_c1tx|%]xRɬT0S<9V29`~O{X-xxS/ce{nڸ>R6*] i{)둆8Be[yM~&x(2iϹ{W~ah|y2UO6ӯj R):6fJEH)sOCCׇ={0z,Ԝ<Cö]A5-WWz5^<[00:+:^KH[О's9<M8{q]L\T{#Gp={<0c߫֞?r0=R ߼~|p("xwL0v]>u Qw~1gfa&\)n}yP8KH؎m.& ~{MSndj*q<׍_¯"Fn3ЭVK}˴Lߝ5(:|5>E ܲ˲[[[=i۶揥&=;?g !@ӕA箣 Hߘ7^a֤6bf!f3BD Ys!\Yq c8Vz|~q~&MAM$@RG,X*jݗѲ 3CR "72M&A Њ`KF@6`҂3݈hHH(@vgTec b3AA%?Af$I G TeV, hI(հH'T^XuԹ6,­۷"\;=˛BOc kObZǡKCkb|GZ-{KR´, 4BqSM qgp c!FUf"|"q1+>sj~5z=ض˗h6:nM`p u ?ppcz:,k 33 4)yF8;Ael*2eit-p'mcC]DQi!abooаm$qA#0+c4a:=0 ~k_>[M9J h΁~v{X\ "BVCu{h4 x[ô빨[8tӁbRYie z΁1rEHFӨ&7b8dי.0.\C5In4BR"3#K U s`}\_!I7'h6 = 7Tz\UVUCps/V`iWo3 \[>tC=(DTOQ!U ADqYn)%VW?& vww]qajxJP٬;a6g&0+(E CӾȂ% |HH^.=;szzѲwp9hř}ZjT2Bf=BDyys:\Y cB2J .T*Uf$s)4M="uЫ޽=; 23 ( "]!M\$tvvi-0+3QFt]!f&A%:QaHLP`B0EOq C)˅ !8 y;%7mKǎ5+;=(Ӄ08pЧzKA; Dzފi!YԽ.ӧb[iHM@Ww7 uõ)M0WJ%\z})%t)WG5?k4= (2i 7>8jxm1 v>\®} 8Џg yQ:~էFs1o'БD!la p33`AX5 R8CjDXy:@xN[+9dpmj 񷿼=膁dg ,_ Q,Om4 x] Bj&6mق1~_qд(~g*RSSx6(,,4۽u2֝t9,_нB!ؖo\,ܞ5Z:{|7:k8Vߏ<_lNU]ǁ#dsW's(,,y[y3B_E5QcAJkCQCtur"/_څai\w͍oL;u 7_+V\m{E{((:ӻ 3MtA.O>+W\0ᇘ{ ,+<6U,Y>*++ MB ?|e0 kP]qa xt@ڍ7mi?+u?ajbR JPJu]03 ,|`Zt]sβ)%;_  bP#a+mV *+vJ?t^JJ%@`.*HZ/=y 9|pmVi!3 ̨Z|۶L,x#:X&/*۶`uS)qd/,}}}d24Qٳ74h^p$oݘ 8'- }}}NZ5ꛒď'xK m|jVX/XˮJ$^wzzڋ###j||܊K2V_~P2ҥk8۠Rp,]..9;r~n)[cccjbbq 6]דBRD'Q%uި(uQr\9xn@t 4{l %>jKȈ=:69uMIENDB`geonkick-2.10.2/src/resources/ogg_radio_button.png000066400000000000000000000021101451017372400222200ustar00rootroot00000000000000PNG  IHDR3GsBIT|d pHYso*tEXtSoftwarewww.inkscape.org<IDATH_Te73nk,Rh 1"édgϙfc(KrxcQ RMEXeBq̊¢eCMuw{XΎvaP~;5$nNߘ+-I@'Jpo/WV`7]{6/F 0`2g?lm8-{umm y[O -48x̖lv3V`s2 QK;©jچRC>{O S9m4_޺ B ZeL$0s-"@7yxq Fjfo&zWðirZ--gHg0<ǥW WE!fm/N، мj͂0,m4df:B* _X1YB)&9\mDW6#nNr~``~C#--jΜvkK0; |RZ&Mp.ɢ\.i~E2gLWZg0ǕhJiڑa.`N::?M3l2ŃU$MteLf6l陵ti NApbllLmr (ӺhqkkPv?q +e0>rSZ3ܽ{͙ n LҪ\xqְܻ5-N[G@°NW ONכX.Hs󙧲'K*mB3X{'_QN0cZ/ ‘FE|gNާ*|d3g<xe k0ߒn5 巔IENDB`geonkick-2.10.2/src/resources/ogg_radio_button_active.png000066400000000000000000000023731451017372400235660ustar00rootroot00000000000000PNG  IHDR3GsBIT|d pHYso*tEXtSoftwarewww.inkscape.org<xIDATHohU?sudtw+tEdwscR,71-hETIXR {' C+Pl5?blSw_/kww/EE׭]ptZi9FQdG|U=!bPV)\׽ki ^m[uwweee#=Ovn7D8캑ǒQ^; M'b$VkD`/2Qq,^#VoE׍4X}]E5fHklr‘չs'H`,-}xZ*"LlFBD9Nl&W*2dx&3gNL6K8鲵LŶ*ƘrTN㱿fcB~  JӎS-Èjj\"ۃro6`tHBn\n8iWXii茎~[QQ14LW 6Vui~U% y?0/_FG&cykL @6ꁠϹ<8 "44l 2lذRqqd󤈌dnd2N y>nEeOK[[ۜ;8N ӨjU3j(UWWcG7=ٹDU_2H$Ew敌_5k9kó ܍ 7W٬b_N׍~2SwJ*+bfbyido$UP8z4(|Ԋ0_+4F"˹E|#cpLg[Y3p|("voU-*b`FD|ycN2 ToIENDB`geonkick-2.10.2/src/resources/open.png000066400000000000000000000013361451017372400176450ustar00rootroot00000000000000PNG  IHDR NUsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<[IDAT8O?9h H5jbBE]D75:hmjZWMj-5IfgL1)ф"p9`n æ(@V$ >P.+4644|~&b{{AXY]4ͻ!zߏ$I\Z"q"===v:^*q<T-"/9==Ee,o|>uLLߏ(ȲlMEXV"\wԪUf$ WK `ppFFRkrb ~{R!Z[HPU>N'{xsR4]]]8:Usè. j^Mv#I6O8 JW0 Yz{{ylnU!^/##i:B0 UU̵u]geu'LOc&ߒIEZp~~N((BkkDQBhhVZzDEt]'  xB4jگ ͔JBM&0MbOi] C 6IENDB`geonkick-2.10.2/src/resources/open_active.png000066400000000000000000000042361451017372400212020ustar00rootroot00000000000000PNG  IHDRZa xsBIT|d pHYsSSⰘtEXtSoftwarewww.inkscape.org<IDAThݏWǿgf0-!Md[ђlb%mvIzQ\iⅈ5]6].Ef\PB\Xv ʸtgH@Lh9Vt?w\[fpVYCk WtȐ D}/-eF$h,$J8!PA.sHv׭[Y.uW)=:e2iz}:r\.;,]]]BPdr2ɇ,50mcm'~``zz>::I lvl>!I<ϖ:֘qB[Y%33&^au߶,T^.KzQƾP(M1 C,o6izY9Rj顡gΜ1|i!礐;-DTH泌@]T089333~A;\Uw8@kHBG†zz4p$l޻C]v?I|IENDB`geonkick-2.10.2/src/resources/open_hover.png000066400000000000000000000013561451017372400210520ustar00rootroot00000000000000PNG  IHDR NUsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<kIDAT8OZHPɣQZhL :Xlpw]mx0y6hbP:9 M\"&bhDxR@P:46NvwN(lL&yIVd2b#===Vu7DO'&p8<%eo5Yi0"P(}}}RhF>|>2 0 &8N9Nz{&CDӸKEl6*l4 aN'h6`0z~CjV`0fYFQU׋k2Au$ 8D^/1jI$I'''46Zl6rih4TUF#X,F166_yBAin7ffHR|;8\.??63...( l JPY QPT*1n}j4JRAUUFGGnRa\ۈ(@\n'eyv;b? I(}NR5|>#juֺkMYIENDB`geonkick-2.10.2/src/resources/open_kit_button.png000066400000000000000000000006141451017372400221050ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYsMstEXtSoftwarewww.inkscape.org< IDAT8MN@II:l0 p9=M .aF7$&=y'D))`a/Dq|pw`Yɕ5(o t"@NW)(a*5hMUUh `'ݮ?y6NCV5]&mLUUQE=)۶fpm|8ib& |>BX e%s>AP_&CzĽxmIENDB`geonkick-2.10.2/src/resources/osc1_preview_sample.png000066400000000000000000000013601451017372400226500ustar00rootroot00000000000000PNG  IHDR! lsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<mIDATHŔNQ=B;R$"[Ѡ7 E, UB)Q , efh^1M[x3,[-gSx _ z !n "YUUE4T81Op+6yJfse]"9k2>:BP`#k9TpL6K:vxJaCFE!גP_05mo6`qy\hYg$뛛<~#squ|*[.S:BM᜞^H)98,5 "0F %!4xR:\D#&iGR ꞇ[.STH&;?8خnp%萄U F/k2>:Lɶj,P8'13p,MW/uMCQ>J-IT5GFƆy<_9})a0`)vݛA|fbt M)6(QV9)ZGG4M Mzh;KZx#*yf!ٮ'^JS;6*R Hz $oMK{IENDB`geonkick-2.10.2/src/resources/osc1_preview_sample_hover.png000066400000000000000000000014131451017372400240520ustar00rootroot00000000000000PNG  IHDR! lsBIT|d pHYs{tEXtSoftwarewww.inkscape.org<IDATHNSA()EAARVQC bQ(;neOmO[ԴsDҲcߏ3'#}BA4K-KSr&!#ĕ\~4)-t®i![m6jlyyyx]TTT H0;=I(<3F:VX;1 !7o.g0"tnts(WXXnOaY:˥3$ {ƦLOrn/.]'|}=8N,- @<c/̊ȘӥlomvݸtQFANjeY =(--zIH)Qm `&dvO+K$ JyrX 5X_[=$5UU%bDHR-mTVVM*cwBJ*;.Tdvf 4A6?m|)%nw f"&'BmE4M#‹!J*  " N(H[{.},̥1M+]b`.`m)aXLptqOOkύ"65ͷӰ3q=e]y;&m%)5_Qt]%<?xƎ?@pK(2f6KƠQԭr9}s!kMvgG,6[|/4+][kM(hx'It?MRm?z 6Y`)`/z~S׵u m2 P*?S),ǩ藊}'8&4le)IQmu̝N4uKѐ z ͩIDM+չsv]z{-(.;Yl#tɆvȧHPHYO[:O;>o78g*^z"ɢxXtT\UL)e""g O4K! "ӱI{WS 0]0п, P:`C*'ŵ=@IENDB`geonkick-2.10.2/src/resources/osc1_radio_button.png000066400000000000000000000024131451017372400223170ustar00rootroot00000000000000PNG  IHDR?h6sBIT|d pHYs!H!HaEWtEXtSoftwarewww.inkscape.org<IDATXoSU?ϭ-v[V7 dн0$%ƿPB#nP ЮZ vc$=Ǐ0 [ e\ 欨_Y nvO4ߋf;`w8D"V/IlWp}w뫂.NLӤ j'EP8LXr!"JB0 hwATU( p:F.>3H i0;3$(v9Nӕ+?s0yYޝ;8q$f.^l ?xd9=|i1X]Y 䶫p8bR.TU~B)4*pb ( `P8L^g4\..?>W'-/-!Itz׼ݟ'308HPӉ *C''j|61i4MSndljil6Sot:l~"tR%իhg_f[K"}}}8].$IǔJ%*GG( \a:|ӹd*Ev}_o4M>?{:?^L<(tttET,2;3C"S|^ι]_ozE QU,xq`pq&w;X,V+xn2 HK4^677D"H  %5P.b ===-srMӈFn:C!Jҁ_֮mlyP"P2 ivv;++T*N9ùuIyƾf_\i2GUULO32:J4tR(X\Xx%atd|g}+M0DdYV<P&77ZIj׺N'p]Ӑe@ @.k=^/`zNnx<8NdYޝeEy5۬mo!"F[h"[EQRm4ij_KNnSIENDB`geonkick-2.10.2/src/resources/osc1_radio_button_active.png000066400000000000000000000025541451017372400236600ustar00rootroot00000000000000PNG  IHDR?h6sBIT|d pHYs!q!qLztEXtSoftwarewww.inkscape.org<IDATX_LSw? 4Q4:2lAYLLCF_{ZT2Lls#a0",, mQ\ PJ{XFD=Ϲ7\r9DQ.k_(Tňx=33kmbU]UUa|v/dggSX>^G ˸p{&'8v`;t2nbT 9*++ t:6 12D3%%9w;LOOQׂ~cxǽqw3իWjio wq9Yٺ E Uӧn~DQMx@aa4 uu'y'oCqqg Ü9s%G}|~ߍl)$iTVϱZ-$%Yỹ,|oQV~E?bhsHDrr2SSSḲx;111hZvC[[;YYYh8}B96lp ..FCyy)%%5q: 8y5~P(D~~>f =ϽK>1TTSYUUillTEEE>Ao v{6sss Il6nwĞXdrr{EQHL4c4xF{AGZY:yA]n"AP5AJSEAp .!j4dY^ #}VC42IENDB`geonkick-2.10.2/src/resources/osc2_preview_sample.png000066400000000000000000000015141451017372400226520ustar00rootroot00000000000000PNG  IHDR! lsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDATHŔMOW{1 &gLjJ*d($d`CIplc `왹]PLRU9gsty,f]+$ Xx,|ւĐ!.+d"EkQQ <2re>tUb>kK(% ͊f-Z[;3֚Ö Ep\|3u9$r]ߧyu0JJ]b2butONPJR03lbGee!%1|.}HѶI%cum]Q "c(J)@l0'=a1ugD,Ƌz}w6yӤ84vR)<Ӿyg3Q{D~jMٮتmSmԩ7O|@m* ɰjm`|gbG&9;O'}:GG,|LOch0>":Nm5ǡZ}?~, FTB~,9hibjbn]z?y+K"s|_5873&^uڝe!oq]xO0 yڇ\XL`ѵ/O\RϐBKn+Mgo%!CVz  -IENDB`geonkick-2.10.2/src/resources/osc2_preview_sample_hover.png000066400000000000000000000015521451017372400240570ustar00rootroot00000000000000PNG  IHDR! lsBIT|d pHYs{tEXtSoftwarewww.inkscape.org<IDATHŔNTWawM}`CzMkЛ0.4kJ4"3 Q {Hq(}oPp\ɛB*A؇/Wp!rS)%eaJ;ikks%^A0 (ϒ.p!cIf~nA8z96*|{㡔w;ꎶcHO'N3)jCx,QgX[{0L'o޼fdWܛDHg#sx>@y˸^T_?ZC,tu%^32vAa~ /WW8;0Hvhy[!8;`S*.QT8:F\bC?~oM~x}{j$T?AP)f+`8s(M4wadsKMƍ ,h{C*c*?3c&J)VK^0 9fY\BК+]7looTؐr;^,t%V<~ҋErףV4otKO2EbqÙݗ|N!Oܹs{7 T/$&_qZj q/+-"ySrn* +^<IENDB`geonkick-2.10.2/src/resources/osc2_preview_sample_pressed.png000066400000000000000000000020321451017372400243730ustar00rootroot00000000000000PNG  IHDR! lsBIT|d pHYs  LtEXtSoftwarewww.inkscape.org<IDATH[lU9߲]^6vn-+ B$f:I|0^@' 5>=X$&1p)t1Fuk׮r|0hauhw/~GEX?Õ P-B @J ]PP[TU+heרt_9,~7+Cd+}u*^]>[{m} cLikY$AtMb6*e:85/^QSX׭4h澪?N$AqY([a[3>O O.2&cc̥ް6>gĴ|tT86}IQ=DΥOyx@A|Sޔ^cUn[rAc07 ¼ڻ<`lS>n8YAYXe[S70;v6O@@ ehb,wvO2Pñ.3_C'GtوXZCj̼ b1]P6HnJTm/{{GiA B钲;w=pew%?H$ J:sRuݺ3g~;EJY8<=Ͷ@<yO !R"Lf@d3ںRHJ<UDT*m3Jf2cbRt:֊BRʗ0<>Nk08k?Kkc&kxBZia^dx8 Ëe=;a3R,Z `^Y߲3?,})DGf>Z0W `^5o373/qSM:uW*>#3?溮t]^!DaC<{>Ƙ2la0 SJbsM{JiCDGRӔRmZz!Dx0 g8̇STmEUƘExjxE۶ݲ8o1cdy&]V\b9}>qA|ӣ1$I%I)6^@moo͛7#1}VގKDJʱD"1neYg u`&=nyPǨGչG1=QJu:N}V¶ñX`)/D8Dt:H$Jъ'DFD_v璘dY?1P;hy|A0$RjSƘSZzD4y"AFKєq|mg)Z?ED;1_b ɤ)#:ګ z/1539ED?ܰaP_`e,QJm0M k]]]2m}~aFpֺ;2OLH& vuuMfƘ <)öm[G`׮]߾}P).\RH,"`W/D"z"j7B]uKLRٳ'-[X>?ysׯ_?3cƌyD"3/F{&2R:cY֡t:Bp*K;0t;neY ֚w0\_p޶D<AƘay @a"mmm7KqS/#3,k'=]WWԲ/'c؇f)ea3_!MJğ"H8v1y)ewG.{RDzc~{c7gy"(e2桡mD UMM͏*1n̼Ds"L?G3>ȽIENDB`geonkick-2.10.2/src/resources/osc2_radio_button_active.png000066400000000000000000000035261451017372400236610ustar00rootroot00000000000000PNG  IHDRW  sBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDATHWmTg~{.݆]>hABMd#biRCҀμ b1jԁJ$Ϥj{;N8RɬH"JwWdvgߙ;0}9yι߄@3I\ecv9 J̼&uζEeR?VX#|f+#NcXLDyo.{YT&myIOHK( BQ B[y hM#7 c~OH)$lhnn~<:m,~!`ܪ^)lf̛y"/zFGR祔 Le˖}6lG+cmrMMM2N7ym_nmm}7FnYzbXD"1P pnT*u=ϫ1cƵɖeո{2ND8o G)L&Eu@df3r+,z~D"S*ؑJj3LMssbzT:ᶊB3e>:Ns֗f f*Bc߄{1o+=P(~>?pmO {X:̧,zIѥ pnY߶}}}/ضeY|0GZ+GM0`fXú{)S=C," p%WDf~MJwd.3o<ϵ,k1ieԩSG.]`>?/_>9}D<3/F'2R*ct:Bp<M08Zڳm{}Pاz|>"3d&! 1ƬZ @a";WmXb`)}m8_.@oo%mb@6u{{{Xjllmhhh2?,H$N1&oYVWG.{R۶E.c~{cWgy"(e2D .EMMͳƘk탙x~BD4WLRIENDB`geonkick-2.10.2/src/resources/osc_ampl_button_hover.png000066400000000000000000000025431451017372400233000ustar00rootroot00000000000000PNG  IHDR?h6sBIT|d pHYs!q!qLztEXtSoftwarewww.inkscape.org<IDATXOWgfv .Ȯ.piEBccLBh5C-*ihLcA%& 4. r~Y **19}9yόhjj$ aJa$I}n'tB߻i"Nfc=aUy8UiVKb{ @H:Ow2_ZZf.b|^vN|>x^7[kZ!mil: v9zM(L[1JKK6[G"t]f7d++nW ﯠ.B~~>vJrss  VcXۿ'(**TR4|uehZ!H$Lcc^×!jkjVoJIJ(TPOu.?Ç0;;Gaax*_qRv#G@OO_FCe[~!HDb 0NL|͉thGehiiLNFY+ U#2C<>N$Rf~pp <@ s:u̺@gg7kZ0 _aN58/weR)ΟG8p`\Y+Bp)EAeF$ $nHb& X3<##Rib)\$lHҫ.,kL$O`nF~~>gu!iyw'Vhl'͘7WO`PUU%CCP\<7r}giiӉŢ NxMg&cjhimq$Y~#:#w74lۦI&h4xA'h45 nd2CHR.~4mrNmm-(ЀDQg\,'\.tLWꋾPֶ6UՂ.V޹Cg&CG|zU\GRpw7##(‰'1 u|>]W8$q1~uds3 ޿|>O,g>|kk 9!IgUSSAQdcc^n\kUTK ,355Z:%hz,EUUåKXE<1ٙɠӎWI>S[W{Pe2^Ie <_T`0H>'L:\9]Ƕ,ưLhtK^om](L;pС^4MP*UΒHIS)Μ=Kqm v* [W!;z7#DXdyySFC_YP(PaxGQJ008j8ḭAHU<'H$I[x X }e0 #Ix̓'Ғp) OR(^k g.,Fgك'E݃"XN#@(Ho;!RE%&}<9st:$R\@݇*IM%Y%Kt*+kN4Sw?B m{I1$'DpA9,(jQթz,TU߶ =NFM&YYjEc0;7gB0y,,X`͚Oh4l ++<>!Y9Yee.E\FFyyv{ Oy ^g޼b \'..n8ĉx^22Eu.lyyy6, qqq.*++5+u_BB< .t3fcfcZ-G JeG̟>vRR~y UUسg:u>cpp$.`߾7 ˬ^1ǏDqq(vа +v .e$&&zG=&6oC$^ΡCusyFCsUnܸUonnq/^b cۣ J1<RRR| I-ȑ͵]Rzzz9{H>%>KLcr ˘1H?/_abEee۶},<67p8غK zp8 3Mnv#CC$%%qq5ENgع%-GWXBBV\:;;={6< d26f% 騫aHOOgdd cX0PTTȝ;ȲDZZZ'%Ō͖CQzȲLAA>zv222wP(D||LWFkѣx<ɔx-LAx9CTQxgUUTI$+Y]]y+50>>Imm-vH&S* :m/m[ duuuRB477aY6I\% xL((B{{zyt]ukssiز(JF)%>d^Zrg8ztEe>.^bn.كaq\LNN{G8u4B4To۷XlQDUd2\w"{2RJٳmGL `qqxGu63:z7ؽ{7=b8k-!`zz۶dGkUQxh~?BZ7giU_ZzHMM *HD"?o];4~T~`u+0Rr0ں u1"RJLLL46) hhhX4 Z322TO$Hur<heYtt 5222Bl6G'z{{4 E4Mt]ȑ)JFP4M]o@UU wΝ;tt38w9t(b,nR4BFg*~<2x<xjjjF#\ϧTςoM"TuXZZQ$l/($HE"aۖ@JmRbڕX>?6(hǎIENDB`geonkick-2.10.2/src/resources/osc_freq_button_off.png000066400000000000000000000026301451017372400227300ustar00rootroot00000000000000PNG  IHDR?h6sBIT|d pHYs!H!HaEWtEXtSoftwarewww.inkscape.org<IDATXOY?3vŖE"+`R$>^ f݇]7/5ohU*vz:g* nDL˙s;oD B?G BLȕʏ6˲~{uul)rW!pق2W`۪tT9]0] jR7--DLMNZPŖqm|JB\#zZ[eD";ȤT,UàD"ix^o |MM۷U`ii !ݎf 2PE&&t:$I"@ @*P(L;t҂iS)Ν?,˘I8&\Yc5㽽XD"U3 W/l6n7j5 YX9w<FQ_VgůPTu!Z6ܺu- ekBZ[[rkjBuJ2 s9<o]B{s2X>$ Mֆ(ՕxuqX d2 ^W~a|M0 c6uݎ$I_ JGEk0LV*C$vz8Dc6ZU&'/珚ߏs{7jU9} >0-Ν?n &_WslnO&S$_0*3 }}/ L{v4͏#H (e`~l}vE_±c }mѿ2;;{% bllkco:Df( @?ld$iqJ,K&9.BbBY> V&^n^([u Bȇk)@%Uh6}y3yϼ-$5}m-|0DQ|FHk!0 ai)LI\u]o˶qAp80Da{zO]ħp 2gSYll6ogܲ;}V $QUvjjjrϲ,cZe;\[@}} ! c29$̱inO}s~ݻCtuu:u6vtvvw[ښΥK?S]|5ܿR[drQill@$y|:bo^,hAgϞ݌=`|/zz'tsss$/^wBbShfCEwLo3i+++L&B͈@'ر#-466qyiOׯ0==@yy9C(q$<-^JKKz=SV栲YTyIşDPUFFn4vUFFnJZb12:z[noйuWAl6L˗BQQ>/311ʊ/LNFyɖ j{]׹{74MczzL&ABQTE`vvٹ6_xC(ܸ \I8GkWJ& NS\lFV )}S㏈FqD"$9sӹCy$qOvP<{#}J$2۷oq8<|g`Eaaa#GzuU~cÁ$97/42UUnWTr_ (X굵;X,LOϰ$IX,%z=,--$177%RIel6;ZT,Sڗvh|hn` DS)0 *(EaDoW_'AǏ CDizBIENDB`geonkick-2.10.2/src/resources/osc_pitch_button_off.png000066400000000000000000000023331451017372400231020ustar00rootroot00000000000000PNG  IHDR?h6sBIT|d pHYs!H!HaEWtEXtSoftwarewww.inkscape.org<XIDATXSg$lfl+!֠cRV3u:^t^^9DkgDAe$d iH=ys+ 5G`lC}Ѹr'$ub,~Lzh/FFxU*BX|rWeڕq"_Fy2y-(x 8ñi4<܊O<D:6J‘^B03,/-p(td*ŹI|B4D;; I,  B{zڗyQt2ΟR.sܚHo/aUe5nLM1s&i"mUqaSSܙ%xey/oc#|s:׮^V7:<*ywm%j9~JaJ%Jf|m u]/*nܚ& 1>1Au:fnUyqxP6α>dhooopmg~?^H"0 j&V.8W/E^ |=9IRAe~{d a:##8mr9@f2̀߿#+-j$eIXUY]Yi~t]gee7UqݨW;|IXZ\IJ,EAVHg'rfOQxU*F1 JB8^Fl'?V"8¼eYLrDxE;۲~lzû+6Hg?iIENDB`geonkick-2.10.2/src/resources/osc_pitch_button_on.png000066400000000000000000000025121451017372400227430ustar00rootroot00000000000000PNG  IHDR?h6sBIT|d pHYs!q!qLztEXtSoftwarewww.inkscape.org<IDATXOSW?&uP JAeZo]bFH6],XٻEcd1?`C],, 0A0@P p{ׄ!,KL{<{sr˵Aef& xJԫDIV[Vj,«]UUe{p'FuzaYY6:DTuy7TU_v//Np`6)VZTZ-Vhu-K&EAA>IIIȲR\6$&&b2IKK[@2gFtR]]EaaE8s'D(c$9  ٽ]` 8ܽFii)۶27??ߧv*Cn7e}QᏆR5z< g~fƍdee(iSz!Λk08ss zLNN+1?UƧ,Uxy{8qǬ^7w>GU($--l?SqmrtȲ~{;  X,xLH%IENDB`geonkick-2.10.2/src/resources/per_play_hover.png000066400000000000000000000006051451017372400217200ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs(ditEXtSoftwarewww.inkscape.org<IDAT81k@嶺ձ7TPJq?!C\ !mw]$h7;%AIr}x |(H)(Dk+: <@+kGCȼTJ2e6{!r (tnZW+-("wcL˭ztjQT0+00?oXk ",H)hi~IuIENDB`geonkick-2.10.2/src/resources/per_play_on.png000066400000000000000000000005731451017372400212150ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs(ditEXtSoftwarewww.inkscape.org<IDAT8c@`a```x=ÿH ((1 z&dAnKKsFFF]aaaae(..`PVV"fbHMMbk >Imm-55UC3ڵϟĹ28;;1TV2`A^````7^/5k m`xi.x)޽Gȁa 'N":eWOFFF/LAAA4#|K*IENDB`geonkick-2.10.2/src/resources/phase_label.png000066400000000000000000000013551451017372400211440ustar00rootroot00000000000000PNG  IHDRO$bKGD pHYsjjkGtIME 34ozIDAT(ύOUϙdC5A sqQFM17训QAW*6{0Ť?eJ;w{ۢ\=x^xXsMZ}}DjU|Q&'ƍxb&#=Vu:l>+?C̷B&{/ͱ 0w~Zwj3 !d9 xh8 4lz@JՓdF@F(_R+̘av[{f`pˤfl.`- qLk&10Ӫ:`(^N"C AvŇr}{oqU Eħuh;:r }(E}+RNX]vCx_buՇr풾> ^B꾗bx396`uCpٰ|Z`ޫay/iϯ\ݽF><˂e{V(fHb6[5g8ӾL!ܟN z2o?p.32cOJg+wG$ ̧Xu18 ʻ#kaBa60°3=@2Iu<쬰}2Vo]wrV;[o)y"Q/%qϮ!ipFj<4(lLmVЕ%{P0>3 w~(ҳ#ê|AoV 'R5݁[f]wj=3\@t+ Ta2uE^]-G .rIVO7c˜ /BX\?B L'~\ K*%3_1u]㗓Z,2xa= ,Q3[$9ڳ+ӑ˶ q ]wPٖBB)ov-HFMLD--&vg|(-nSd3|srfM21D$ᴪ\ePq"$)nк:ʘ&@ح=JLϋ45vƺǍzOC.,wQ}Z '2cH@uq0=v juCЉ%#z+ձ<>{A Wάђ[[wOBUqDm%إR8b2˕B[r\^ܟDrIENDB`geonkick-2.10.2/src/resources/play_pressed.png000066400000000000000000000015031451017372400213720ustar00rootroot00000000000000PNG  IHDR+('sBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDATH͖KHTQ8؝)#e Là!7B ZDP-٪]V-"Q+Ғ^$hmiFGfRzkN[u?=sxUd"f9b ](*O2D֢ UB+YHņb$L3aX-6*09i%q\.\\D6[(|тԶeՔ斅B/=O:Yi!%">Q yJM IT\PV)iqsd##H\BRtYFQ}h C!Y9(>-)0abφ t 9Lkb{̭U?mXJIiBHF$P_;B\@B J"Pu{/-qAhCh;9gfw)_}emk{w*Qלih7FBk͋y1E>ol0;3ilC\h;u=&(Tʿ===h Ak40 "hZJ\!N7J1>:Rbp4|Z]8̤G",./!AɑOc5燇L$oeY\YFiN<CDbt1G0۹R,fgS2_ c? 1DwX/MʕV/'56a#$کP.IC1&SMVa (Egcp=߿ўנXz6K2ɣ),<.VWe P\}o@"/QssoKс y`dY|\ZF0l$5)F n(۟!ܗ;8IENDB`geonkick-2.10.2/src/resources/play_preview_sample_hover.png000066400000000000000000000012771451017372400241620ustar00rootroot00000000000000PNG  IHDR! lsBIT|d pHYs{tEXtSoftwarewww.inkscape.org<j! }<Iٜ]+񠰯抾WK8TEaW\!+$qy$*00pH%JnrJ;>ĪNh8 cY S{ZIfsv| &F|߿[eL=_,9֦TFGkrdkɹ?v`Fg'P}YTsUYz GL9v", ~-l55'=p"yv㌣ " e/&Yء=̻:AE/mPd>g w0R6q$,%RrU< u\e{q**J$9θI}p` (#p;sILIENDB`geonkick-2.10.2/src/resources/presets_button.png000066400000000000000000000016061451017372400217640ustar00rootroot00000000000000PNG  IHDRI|;bKGD pHYs8!8!tIME %+K`IDATXIOSQZےGDWj&h$ѕ Zh R]B7&"BIp%~Gj꫺0T_tcܫX,^`I_SSW q83XVϯ7lHjj3h`Νp#6YYYH>wv8p`0ص{7uŚl0<|Ir>Q=H$;hqCvNeXbZꟕ7].NUU(|R~x415yYnG5A͆d2)a:۹{HVJʔM7t`@"lV߾yCJJLܜ2MUu5 騩Рa?""T=l6z"88Mg(L&TUvi/烶,4MfʽƂpbr AB0aYVyLSk(R;@$ض ]W AaXVEu8_E˲ aqDQԽxt:u]\.}$IHPy|>1֨h/9MSAnXV+弓pK/3~*ґi> !_z]_eIENDB`geonkick-2.10.2/src/resources/prev_page_hover.png000066400000000000000000000007361451017372400220620ustar00rootroot00000000000000PNG  IHDRw=sBIT|d pHYs-o-oo]QtEXtSoftwarewww.inkscape.org<[IDATHՕ1kP{"CSbpY+TB%P/)b A/vXbmF; t tY}{9Z, BxβaAa~(D1z XRGP ӭ}<R\~@)$($ HQdN\$I($x+ XDs~ :1F/4S+͠f3w(>1oP\t^`f0xBzFѳ-9`BUU[f74-ԥ"MP]C˝BR χxeݱ0oEq]!(N\__g%',IENDB`geonkick-2.10.2/src/resources/remove_per_button.png000066400000000000000000000010441451017372400224360ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs(ditEXtSoftwarewww.inkscape.org<IDAT8KNAtK>7^9i7B>FL l 9Jd1#k!ՓG0C(%ԯvf@aRJRd1DQij)0ƤqhԱblm?.=_DWo^H!up~cefqRrź3LWg+c aHTb^@) 6E<|>Z77y5* Zj$Ȯb  "~,Q98Nv˞Al:e4PXeG=:N*PNB`sk@)11|<9hDThĮ3z_{t̓o !Zg4DQuL&#Roܵ}Wg<IENDB`geonkick-2.10.2/src/resources/remove_per_button_hover.png000066400000000000000000000010331451017372400236370ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs(ditEXtSoftwarewww.inkscape.org<IDAT8ˎA*Þ= ; 8D/|2}5pq=>kIj4RIU*f3?Ja1柄\5Z gOQq!oԉhQ1''TU.;iJ+v]G9`  eqh8eu g3zo X.|yh8DJYA:m>lFbv>cZ["jQXJIPHw2Zj 2'gZHe=<0d2@u&Lv;3NӁŐ Th$IRͼr}y>} @J1?([" ZoM>}53 IENDB`geonkick-2.10.2/src/resources/remove_per_button_on.png000066400000000000000000000010201451017372400231240ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs(ditEXtSoftwarewww.inkscape.org<IDAT8NPNXvDhP[v(PWQ)DP; M$n; R1M=WjߐFUU1  TUe`} biww{)P*rP.sRУ ARi>It{=L^GM~W+lZFRJn-) AZd\ˋxrghWVA:&bvYQ|I~d*Ŧe1ˑ(K[ wl8%3O>,8yu16NP(3\岱h oC4gw" :m4Mn3~=Lh]7Eyf˾pwOIJIyF?,G@٨IENDB`geonkick-2.10.2/src/resources/reset.png000066400000000000000000000014571451017372400200320ustar00rootroot00000000000000PNG  IHDR! lsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDATHŔKOQ33R)p .X#t2 ,pAr3ʥig әbݝ;9sޓ󉧯?xߒ,IU*5&ߥ+1P.;4TUi+!C!ʦB#!vl )ڕ$v[\q\2s_U+Q z|󵦞54<)M>rH)gړR(Bٲd<%mŲ,MO#] p [[,zGON:O"f2I0h ItMLP8*r}h+{iON213K2F,Ƈ,,\/xd"Q*3] ôi-+)&wzzYڦh G8Vq\b{׃wIDclp\*ni!0- 82 2>5EG{;.쪍+]ײ8.^m~V儕5:; G45 475ddۮrzj(\>OCCFDR-ǶaW\a}saprHlX"~C ?ZD.6y5SjTkz0 RP?Vm?P( P¥` C!HMxwvUpK+IENDB`geonkick-2.10.2/src/resources/reset_button.png000066400000000000000000000020641451017372400214200ustar00rootroot00000000000000PNG  IHDR+}`6sBIT|d pHYsK=tEXtSoftwarewww.inkscape.org<IDATHՖMHw?_&F7)_+v !&otf΋tzX:;xXٛ0ˬ"R5]ԖMķm V-`~<#Ǹ ?h4ȅA|>/9<<@RRR .h++y<$ (TUU>wtww3??JB{W(({SSSIIIyIHKK;G:1ٷZZ^IGGYYYHJ%mmmAǸnZZZb9ֆJtSo/;;;\=S}+$(b4gjzq߹CRR,SxX[[l$'ǙXeoFv;~hJ+WD"LOOSӑI__/&'{jX^Zױ1DQdwwfr-JJJehrv;fa?z8677yUhjj˦c99LLL`@(|c4 Q= lXDmm-.9‘n VK}]_! !E= z&lPt2oc٨hA(p5>ގ$Itb2CM tV޼XV~|l6Քq V###R{FYXXQ^^N͛ EBdddBq&%Y;5/V+~p(Drr2hǃj%VIE^~MOO{{{x^ $'8"V11j5p '77&-\\{ 7vMB&IENDB`geonkick-2.10.2/src/resources/reset_button_hover.png000066400000000000000000000031451451017372400226240ustar00rootroot00000000000000PNG  IHDRZa xsBIT|d pHYs^^tEXtSoftwarewww.inkscape.org<IDATho?3^;$4pk޶Tq)i(+T"m%£U RcwNg<JuʻϜ=3̪\P J6[ p-`-9ޓj0T6kISK$|LaT?@&‹DZ%ϣB{[{WE Y_+u*2Zk⑂w)QE׷k )Bk/p[X[3+ԫ5|E $EkC6rlm;im?3$!-aӔqXkcw;݁QUkwcD2u(ccPm8ifSJEBvAjtٹ 9; nN?t! m*k"%2t~sU0lS<<+iYszGT_lڿJuo-no1j[joTo.pm`bSG'2TNT{tOQ<|ȣG865̹s\~=S(B+~Cȸj\?& (/?[ZɓLNNri$P(,"O,--qQNNO$ |_cqqEf_cyy/-.p!TWxU,)q̹?ᆪ^ҥKT*>f+ޟ`ddSNq5$Ak˗_}͹]sׂ~[ JJssE|ǎ O>R D8~5ǝbcc8}Zo+qc?ΝwjiOaffe{noo;*nhpsn _~RFGGi& kklln2==1<`|lϟ399I^gumr™ӧw]zx`(dҥ۵o/SJ~9s|_~aum>ŋܝhp9{,Qnߦ\P"ISnh gmW٨5(NrA r9tk-$IB$4M&iRVV qk TJpZy7mL]Tka$)ڬuyWbݱ1D>iPRm .I2I;i$I&A+P^L~?wCW E+,ޏDF3aK)fr 6o!tՋD+w:5-8|VV2Hapß!_ȓZakk U`КaPNkЂ1: MVh _옽ڑ`Ğ4]O 9; L*z|B#ҟ˹"H(IENDB`geonkick-2.10.2/src/resources/reset_button_on.png000066400000000000000000000032401451017372400221110ustar00rootroot00000000000000PNG  IHDRZa xsBIT|d pHYs^^tEXtSoftwarewww.inkscape.org<IDAThO\?3s[d[~bJDqT㦢c8Hw5/VCM[#EQl4jl^J 1˲s;w粻F_js?s9g Z]P J=6=[Zpj-`-Rޕ>޾WT7k"KNS{QT ~l$"P,#d2VZo־ Y_+lnnZ(&ݖ]c`ћdYBk6؊a֎:Arl6He-Zz~y cZ7C?wgꝣ(J?% CRPp6kZcN?wxCZc#$֮Al1 0i 89A $' V:.;W AP_t{UG5vKhH黌xsUצxJŬRDcgu1O>:K5ϦT}o_zW^~*lBgDz%>qB#m501&쳃*SG!2THNTmtORw211'8[\z5QR$sfFpU~fLA.q=~`hh>~rNL011Aoo/#/""|ܿǏ380@\oofjjwFF8r~oofzz'-.s&hTjVxu1 /p뫯}_ٳgr rc /_faak~1VVVv۱z,觥ΜeqQ12"o^|IΟ?Ͻ)ܹéS@lEN[\z]p!667#Ammm|n룸%νƎWl>C_- `-5J١tj+¿gf?&''7ifo7: nQZ_׮1<<\-pMKK,-/S CNhgmА82#STk-aHT** QQZ[cyqt6]E)UR/񼛶f\ˆ'"Il>b!cc ֋| dVI%]J|"*0 MTW&I 0d+\gg"Ize?L;о( WX Jbn=LTi͇."sXdL1Mꤔ"ݞ8E*FY~qXXEM-:_!YX,ks Bl:^Ze- 2$Ŷٻ6@-]ؕ>I$"gIG?^>H *װFIENDB`geonkick-2.10.2/src/resources/reset_hover.png000066400000000000000000000015061451017372400212300ustar00rootroot00000000000000PNG  IHDR! lsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDATHŒNSQ۞Hi 5JEhTgЗ0('01^8Y8 T)Q(ӒS^@ECgm+{DYBJ)yJV @kBsĎj.Bv^/ax$)%aT|6!eY = cp$ʡGtJ r) K2x<~A!`)WǻfI,Z2 ֤{h߲d)%E{{w`Y(mn\Dk#Qߍ1 T}G>8$Zb8[0u$S=<y+<7Gg6~i݅a\mo^0ƒhimm3}(J)pa PŲ,vwu31>F\)S.#.6M7N7~4MH)m`B~7 uv;;q5m/`ۋYi(mF_#I]]=33Ӹe~n P#OqAJTvB@DKke=rU{i.е!ibC|><ӧY&'?L)d&}T;;w>av#Zkgu1WzCƯb=UjQgo%&J)!xU%Q!"hIENDB`geonkick-2.10.2/src/resources/resources.rc000066400000000000000000000125521451017372400205400ustar00rootroot00000000000000set(gkick_rc_png_files # === Common === app_icon.png logo_about.png about.png # === Top Menu === logo.png separator.png open_active.png open.png open_hover.png play.png play_hover.png play_pressed.png layer1.png layer2.png layer3.png layer1_disabled.png layer2_disabled.png layer3_disabled.png layer1_hover.png layer2_hover.png layer3_hover.png reset.png reset_hover.png reset_active.png tune_checkbox_on.png tune_checkbox_off.png tune_checkbox_hover.png topmenu_midi_off.png topmenu_midi_active.png topmenu_midi_hover.png topmenu_controls_active.png topmenu_controls_hover.png topmenu_controls_off.png topmenu_kit_active.png topmenu_kit_hover.png topmenu_kit_off.png topmenu_presets_active.png topmenu_presets_hover.png topmenu_presets_off.png topmenu_samples_active.png topmenu_samples_hover.png topmenu_samples_off.png # === Envelopes Menu === show_ampl_env_active.png show_ampl_env.png show_ampl_env_hover.png show_freq_env_active.png show_freq_env.png show_freq_env_hover.png show_filter_env_active.png show_filter_env.png show_filter_env_hover.png layer1_env_active.png layer1_env.png layer2_env_active.png layer2_env.png layer3_env_active.png layer3_env.png show_general_envelopes_button_active.png show_general_envelopes_button.png show_noise_envelopes_button_active.png show_noise_envelopes_button.png show_osc1_envelopes_button_active.png show_osc1_envelopes_button.png show_osc2_envelopes_button_active.png show_osc2_envelopes_button.png # === Controls Area === # === Oscillator === controls_osc1_on.png controls_osc1_hover.png controls_osc1_off.png controls_osc2_on.png controls_osc2_hover.png controls_osc2_off.png controls_noise_on.png controls_noise_hover.png controls_noise_off.png fm_radio.png fm_radio_hover.png fm_radio_active.png compressor_enable.png compressor_enable_hover.png compressor_enable_active.png compressor_attack_label.png compressor_goupbox_bk.png compressor_groupbox_label.png compressor_knee_label.png compressor_makeup_label.png compressor_ratio_label.png compressor_release_label.png compressor_threshold_label.png distortion_enable.png distortion_enable_hover.png distortion_enable_active.png distortion_drive_env.png distortion_drive_env_hover.png distortion_drive_env_active.png distortion_goupbox_label.png distortion_groupbox_bk.png distortion_groupbox_label.png distortion_in_limiter.png distortion_volume_env.png distortion_volume_env_active.png distortion_volume_env_hover.png effects_groupbox_label.png export_button.png export_browse.png export_cancel.png export.png export_hover.png export_bk.png export_format_pressed.png export_format_unpressed.png flac16_radio_button.png flac16_radio_button_active.png flac24_radio_button.png flac24_radio_button_active.png wav16_radio_button.png wav16_radio_button_active.png wav24_radio_button.png wav24_radio_button_active.png wav32_radio_button.png wav32_radio_button_active.png ogg_radio_button.png ogg_radio_button_active.png mono_radio_button.png mono_radio_button_active.png stereo_radio_button.png stereo_radio_button_active.png meter_scale.png # ==== Filter ==== filter_enabled.png filter_disabled.png filter_enabled_hover.png filter_type_hp.png filter_type_lp.png filter_type_bp.png filter_type_hp_checked.png filter_type_lp_checked.png filter_type_bp_checked.png filter_type_hp_hover.png filter_type_lp_hover.png filter_type_bp_hover.png fl_cutoff_button_on.png fl_cutoff_button_hover.png fl_cutoff_button_off.png general_groupbox_label.png # ==== OSC section ==== hboxbk_ampl_env.png hboxbk_compressor.png hboxbk_filter.png hboxbk_noise_env.png hboxbk_osc_env.png knob_50x50.png knob_bk_50x50.png knob_bk_image_freq.png knob_bk_image.png knob.png osc_ampl_button_off.png osc_ampl_button_on.png osc_ampl_button_hover.png osc_freq_button_off.png osc_freq_button_on.png osc_freq_button_hover.png osc_pitch_button_off.png osc_pitch_button_on.png osc_pitch_button_hover.png phase_label.png noise_groupbox_label.png noise_type_brownian_active.png noise_type_brownian.png noise_type_white_active.png noise_type_white.png cancel.png save_active.png save.png save_hover.png wave_button_sawtooth_active.png wave_button_sawtooth.png wave_button_sine_active.png wave_button_sine.png wave_button_square_active.png wave_button_square.png wave_button_triangle_active.png wave_button_triangle.png wave_button_sample.png wave_button_sample_active.png button_browse_sample.png wf_bk_hbox.png layers_mixer.png controls_button.png #----- Kit ----- kit_midi_on.png kit_midi_hover.png kit_midi_off.png kit_button.png add_per_button.png open_kit_button.png save_kit_button.png export_kit_button.png mute.png mute_hover.png mute_on.png solo.png solo_hover.png solo_on.png per_play.png per_play_hover.png per_play_on.png remove_per_button.png remove_per_button_hover.png remove_per_button_on.png copy_per_button.png copy_per_button_hover.png copy_per_button_on.png presets_button.png next_page.png next_page_hover.png next_page_on.png prev_page.png prev_page_hover.png prev_page_on.png env_button.png env_button_hover.png env_button_on.png # === File Dialog === scrollbar_button_up.png scrollbar_button_down.png # === Samples Browser === play_preview_sample.png play_preview_sample_hover.png play_preview_sample_pressed.png load_preview_sample.png load_preview_sample_hover.png load_preview_sample_pressed.png osc1_preview_sample.png osc1_preview_sample_hover.png osc1_preview_sample_pressed.png osc2_preview_sample.png osc2_preview_sample_hover.png osc2_preview_sample_pressed.png ) geonkick-2.10.2/src/resources/save.png000066400000000000000000000013131451017372400176350ustar00rootroot00000000000000PNG  IHDR _bsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<HIDAT(QKZ545ZVuړRSCFߢoXo빉zI,re6Ѡ^. {y:8?vqͷC2 V~fz~><B @p8 QVQh$B$Aǭ-^'[]T*ba}}dQi6|) KFFX^^n#I>D"}677 Q#!Q^'1>>F6$j]T*<$"+ k4ggg(³ r8*(!fnnvdKш~hhYyO<Fnoo;fE199I\FUUAb6t:q:h48??Gfa%z\..$*^(vy53)#2br (tL<=`V^NM$L& vx,ƋpF"j ͢izQ`ZFP@5^fCQ4M4L TU뙻ikZne߬77XzxIENDB`geonkick-2.10.2/src/resources/save_active.png000066400000000000000000000042141451017372400211730ustar00rootroot00000000000000PNG  IHDRZa xsBIT|d pHYsSSⰘtEXtSoftwarewww.inkscape.org< IDAThZ]o~Ι3^صۄP0qCMVmVڤVi+p?`Z7JoڪR[T 7Y `bq ˇ1k];_gۋ/$bE#G::9ϼyÝ`A.c###lgрL&C\"h$r94/RڲJ{dk"@̐it3TrkkkSlVȎM[VUm 5M1IJŔva؅BVf}}}|pp0N4M\9ai"Bc ~W us#iu˥RuBlI~8r*ιB=}8YH\URQD'$þjtͲJ|٬ffiFpK2eyۊ mC4?hly4.L;v30k$/4oQ' RA9QJ!N'z-NWr###̲,i#Bx0Q^ DB9R "0Ơ:*ι,0̲,-4%s^?cAj@FΗ.]@㌁ '>^E߇ݳk1qF"c@P3"bR:B=s8 nJEoڄ}091^})vլp8{u(םo !c!9i8+H$XTL  #GQaWnGk|_Uز};@jq\ClőmY]]c eZ8zJn5;Hjsʇ9L&KRLJ$~\\!t8WƚK{В\ɉ ؖ-۶a݆ 6"s=e^Wyy c XXx1]~#lҗ6`(@؟DRJv:v畎_7 mJ|aG9|͛>3翸jm85==pO<4;wshI&VBTb50"nh] : )=l?Knz|NVEWq{v*[h 1y((X7D^S1㑕yg]ρsغcGǏO=n܈  9yk/QuO~뛰-)Z#1D(WQ z˿0M_}`[fExJ),Y8曰f41F#g"N㥗_qp y!_]icccIuۍqty0ƠiZ|XVRR<DZEq˱u}j͚5BRzu7j#zh֬Te3y琄1{)!b^B9O&~$[( * NA纮,VZdŋʶm uq2q#0hQ A`!׋`iҰ w]@RJ2ORXx!gV'G,4DظiIJóQG@gg4M2MLMu롡ť,cD &" 7ɳm+HثWFGG}fj``1 ,4_J*EL48; RΔ"o/<+ _ N:']Smp72RrB^=!f%k?>>j?x9IENDB`geonkick-2.10.2/src/resources/save_hover.png000066400000000000000000000013451451017372400210450ustar00rootroot00000000000000PNG  IHDR _bsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<bIDAT(OHssR 83fN *d`]Mc$AHL䟛„e%ds@vE>煇IkbxPNDC}=j]ݍ 9 q~~@G{;mmmt:Qmaz.~0nFGGAUUbXYY!mss1;3l& RUU555('77ݎhDyw76,..ӃjERRRBG{;aZÌP(Dmm-)0[Vnl>~ǹe2q'?̌ JKK4CC񰢂̌ RbXʯӉ%b4FQE{ iQe%Ŝl8T³T!Qmwgt,/mGGr`@"iniFJ2d2)[[[""ITCfgg%H)!--zED$HFSt!zֲ9ڨp@U<27`"Ϧ0`8tt2o'˲[*>fX@|4b38B")`%;l6CZ;sRVQ9Wq$EoK謩 Q]eq$I 7IENDB`geonkick-2.10.2/src/resources/separator.png000066400000000000000000000002321451017372400206760ustar00rootroot00000000000000PNG  IHDRle?sBIT|d pHYsmtEXtSoftwarewww.inkscape.org<IDATc466C-SIENDB`geonkick-2.10.2/src/resources/show_ampl_env.png000066400000000000000000000023401451017372400215410ustar00rootroot00000000000000PNG  IHDR3QsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<]IDATHݖOSg?眞ӛ踴EHPn5ә1..f&s~~Z̜.2c,lXp[R\Z"/9>^O,@|l']1 ۫ᐐeg_$%Aq\|honÇ9Ս^*+i矝(\K8t-i( †ǎ(x<7].$I`״<7n"NoI'Eeainffvp(DuU%syKvy`]8t ,LGMM =P˲##6|^T8y8F84Uҕ+?ϣ1 }JIR6$I$Ԅ 5=;TE004u(X^#La4ÑG\y`0@ӞF;JeqZ#FvtP]U!x10CCfdwm-m`L6k)vtDZ,@ @NUiGy%}Ϧ:&'y xd 78i&"i\L8L"dCRzBx%6דTX_ipHih@EvudM 4L*iXeA0JBilaY$=(}@IQd2#NX¥?(eϩnZ[l1]DԆ7?}VVW(/+c |;|\.fqF"253CC`jvzi#4Ll!cOp1*- BDezfŌ?`_[ΜAuRL@8{lA7 TUE^/t:Q5 ]בeRi3zLäONo#𳺲J4t"9$r9^/(TȲΊ 2KK//GQ^0n ıF*,˲muuL >|M4(ȗ]0x߯ڝ%lǫ٩(|Jm`[bZ>օjoɵOIENDB`geonkick-2.10.2/src/resources/show_ampl_env_active.png000066400000000000000000000024301451017372400230740ustar00rootroot00000000000000PNG  IHDR3QsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDATHݖ_LSw?޶Z!MFť@$/>@Bd/EØ{sQ%62LӇ9D& ZM'4Xޛ]Qbf;;{ݟڪ@|MȲ$IołjEl(fl6۪dnɓȗAGG>̚5k\.Ueߏ~̎ۑ$FASt8+$p8uh1yd2I?pxUI8***Zd؛&&&pݸ\.=zw~?hI*bhh={`Z >}M7LMMv4rqf|>tP8̆9y;͛7IKK m2EÎȃX! ԔÀi ڵkߏd۶mK}_ׯ_˕f"K "p%=~Y[[[^Ξ3g(_d4Jq^WǃLܹsa߿O$ჲ2t]sz"s([l Rdx63Fӧ^ ?/^b16oDK2í[1Y,$O g3v$:Lfi9KV:EAZ Mrfb |g& JK"E!#fp p@ j+A|k.*** Vj(**`2fJKK|4>1o/sQ&''15551==Mut*!Ν;c||Fw{n:rt:M"޽{z>|/H%FKK+= ,9r- PZRLB[yӐ(4n}Sc6(,(/ؽ]:;}X;Y)AKE@͊7orSvimo' Q[d2Gm⏎K|{*3Jactt\bΊ\bBhnk7#e׳ >MŲe|đY zA;X啕Ȳln~~\63<2¢lRM痳g G"u0t;;SV@ 3R+X4FIR/@UU*ˑ2$'S`UWU @\`$ Rn`#OR躎e޻drFeH?9QVr&:wLe/A8H?tԚ\oll ~8괞{Tx|_w z3ӚIENDB`geonkick-2.10.2/src/resources/show_filter_env.png000066400000000000000000000014471451017372400221040ustar00rootroot00000000000000PNG  IHDRsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDATHOSQ{B bڇB ?B]%/57> lzh^ޫWH&gLCװG䇤bEQh:>%dDž6I>% t:Q[7obn۷x63uw369ǃp$4>MӨuLsUU,~f8N0,N9uy%cXV~>6.. ۅARdHloͲP_χ߿g=bk\' )J< B|h8u !GJՊiyT7L*\ fwZ0_z2=jNPr1<40EE& Du5R܍ |Lc'kjp8d ).;@ FHtޝvR&ozdT\RRYQA.#H] vx#\zpHqq1j4Ldggk0}+I0TUUr*VUGFX__G~kXHǸnyB4JSfcXXXeڸt"恵tb(**BQr-r}>t]?HGӴ%I4I<555NKK KKKLOOH-vwwYT*E0S233ÛodEh"P(Duu5᧷ȲL&FcFu*B_ŗ^*7_Iee%f$ p\E-/sN'R__O0bgg's@ tttH$#155E{{;=b"㔖r[&+Ieeei0tdZ),,$cXH$H&YL<TUEc8fC4#0=J7 7y-IENDB`geonkick-2.10.2/src/resources/show_filter_env_hover.png000066400000000000000000000015001451017372400232750ustar00rootroot00000000000000PNG  IHDRsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDATHOa3P(-DUK%T11#ƿD]`L0IP.j-:Z\c5D~9{swZwY8ۆTE əQ$Iq ȲP^^-X, |ֻ Rh,wxo WzuɄi/)!77.WF7,[8v֯#2JeK{}lPU7M$\8wь"tR\Tġq}·+2EN,6bY,188DLBUQdYIꔐ QX,>1XV4Mcbr9sHwxy/2IENDB`geonkick-2.10.2/src/resources/show_freq_env.png000066400000000000000000000024171451017372400215520ustar00rootroot00000000000000PNG  IHDR4sBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDATHݖOW?wwvdkf*/[6[ԐJD1m5iڴICXB| .® FBm1̜;={]]&Ʀ#dwʲa&\5~8CduҰMLj`BXs\h|`!Avi配 !el6ܚDu@t:1 _+0c4ȧH&s>]VVa}0̝{D[k B쨫rqe>{CCwc٘*LΜi"oAF\N'TWmg5}7hkio}a|vcdcy(ll@'-~4{DMU>?Grņѫտ|W4Mce|O0m{Z$U.]z;jk8,fe%En7r;]kyrQҒJMM5L54@"EUUWQ:U۷p8 Njj}SS&'r9OR[]9]'\&tox@*9|>XJdqrAt\/i"IJ$Iֻ]xJJ,H"O!$a mM77S MrC $ ?èlpxRww6R\\L{[$-UnJc}% B0pkyz0Mg"jn«(T|>jDɶ2FVf}#BiGrc5=Mt.y8:ʾvLYNC?F4+#}>@4.^oC|q8#dYb1B0 uww٥4YX0 KVb&s=En$gOҕ+<)(D!b<Pe KJ.nSvd0M͆WQI5]N'O2T:]`>g=YtnnR)RP5:s6fXu0 Ecۘ&LBLzUmI(URt`$ɤ%۰ Ld>U۽RsMwt?&LIENDB`geonkick-2.10.2/src/resources/show_freq_env_active.png000066400000000000000000000025111451017372400231000ustar00rootroot00000000000000PNG  IHDR4sBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDATHݖOLTWox 2 QFĮ&&64QЍ hmR.,b]&2 +!qX Pajaߌo@$Fͻw9߹{ʪ*e]c1td2y{@H$wǏx"@OO!E1ȋ/&LJE, Ϟ={sjevv5f#JjEӴ `hxN !"޺0#??t:M[{;7o55֭l(+#;+Οl}=:;x|>絵:=x^jm&NsA,fܡPQQx<P(D0^/[(..rs~{ @ffմP__O"೽{ l9>d{M k.dY^6X,_Z _gel< HӸ\.)++ vQ _@8SK*--%#2,RU0tuwZ͔ܸq>~L$166z<7JDNNӤiCip8!466ߏjEeǑ$bSSXUzMӌuoȄw6L8eXlp`2D"l/4P]{#Dd  IENDB`geonkick-2.10.2/src/resources/show_freq_env_hover.png000066400000000000000000000025011451017372400227470ustar00rootroot00000000000000PNG  IHDR4sBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDATHݖoU?3۝CM_4M-P]AԈĈQQAc'L`xVJFnKin[C@Ӧ!9ry6ic9$Yeh]Md޻{\;J,ZCW^`lXQY9sW Hs Kj.tX]VRDAÇi5jq\XVvlJNv6:p֯YCq2{bw Y&/EzsJ OCCzؾm+GWRU]êrsrȠ|zv:9r0KҜ+cMi)1ж7|‘J:ذn- yS9NQu%-+v ,P||<qht,),`""?/p8pu}:xpn Bvv;JvXhu YAXҟ{vEa=rYF$NCp\F4èaFLj-DՇuDE4CVcm%$IfK6==M9_|EIDX_QcªiGQQ|~?cc~kxMg#[^ l&820rͷx?$7SSS3쩨 sL VMg# `6 ޽[0JDRb"GGMZ$Rt]g``G6D~??_N⟎dYn χ$J(b%'c$45*,  D"f:& ÎI0MoZOPWq*C`pZDTUe6Z_  ]& Gi|Mӌ b˗+ut׋y05.#Ȋ\Yij%EUU0m 8S3hmiadx#&)0x3~?y!JclY(_jme ]4qZ>@$66,HLGfy޹Zʫ*!0tw^Gh_`{g"]ubm8 wI$/.tjk|HrD`5n"IG ϖŐE^VIH|i^d5u]g(zIPMHgrʤ[BuKbn~{CDu켎623#0/3s8p^ 2y7݅],A"q0l #|Z;* :9yjjJqJќܿ%Ch4NOhdY潫WZR)|>"ȉH͛hu Qq8Q`6e$i4W _m|x| kkekk޿vBVVW$/,hE6QU簾Eezb|=>N4 v;`h4ʾ^Hכffii ǽB!|]zE^, ǻWinnrxEl8P.F#@uuuFkZ-w`N/wwp1 #Ϟ%ƫQTT,ӄa^$''󵵄A$p8LMM 8ND"ߏfe>LNN貲E6668#I@eee:i5@@2 ++ I$׫9;Xz(Jbvv2&nz{zIUUy%Z-OP UUU qoaqsAy"+++1H}njÁbOp:R__\.fb;;@\!]677p:i~FTU%~k&RDE6773- Vp(j n'HNCePN+HOϞL&9#Id9<<l6cR)4 Dr$wƩ'^e+\IENDB`geonkick-2.10.2/src/resources/show_noise_envelopes_button.png000066400000000000000000000013671451017372400245400ustar00rootroot00000000000000PNG  IHDREΣsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<tIDAT8OQ?K]C Am`JDʥhDL4Q/@ D\BvBd7bt79TD9EASUӭiTVV*346PWgGhlhOFq@Nj?dYv|([[!PU=$x# OuU5Fml~?]!Y?vv0M FekjdE "cUd9KKt 5UUt`>dY")W.0BsBcumg/^@ous8Kg%innfoYS)&cSx4 4;]jic!! ࿊(!N@|D2Igɬ 8<<8cN:";ܜBt:YY_DͭY7z<\E]+a"kinߢ/(B*T*3e t,]c+/ ;N83C`3V=7H,.5 Ƣ {h (LbT>$ 4.|R"۳ R((J\NaTeta`rTzoffIENDB`geonkick-2.10.2/src/resources/show_noise_envelopes_button_active.png000066400000000000000000000014221451017372400260630ustar00rootroot00000000000000PNG  IHDREΣsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDAT8픿K[Q?ϧ/{ H"Ŋ- ꤍ)Z,,.ڡƥA!jlpպT!KicgS$L|=ýWxR_D]Bl tkMU1.x3<1!n-o8BׇMM$InGlnn 2f6ʢQvS^VFFAmm---%Mgg'@GGC$ffgnE!yv+G)'Wn^1QCzz{)UWǙFZeVVWy;2B$fuPAEEAe%F(q:'CQQRaJe$Q[SC^^Fp8LSc͞t:mt:y=0@vv6YFV, 2"vY_pЀyk+D]󪿟QT Mb1&ed2|( 1χ댍VVVhooG4 $<#Naj EcDQjrrr9`SUv -$J,N%I˅H:cIENDB`geonkick-2.10.2/src/resources/show_osc1_envelopes_button.png000066400000000000000000000022721451017372400242640ustar00rootroot00000000000000PNG  IHDR5OsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<7IDATHSSW$9zH@ &oH\jk#hg}vZ/`qtP@L M"d'䐤S.3Y{%:NA_೨Bv;X M>fJA6 drKNш$++WjU\TDVv;pxɩM~l7ccZ._¥ޙM75l\C4Q \xd2qT':I7n 8vy'EQ0 6ۺ[h4bBP t:t:$v& ٜ_KD,)uiMzρz?]&Btwuus?B}4}E45|IC}=BT[w~Mѣm(F$ZY0{wQ[[r?0(?M|iO`sgY‘ț8%%#ἠsv"&eh1/^hgW{zH$9ю$mxjjz@4% 2crj);޺MI77y"ss_4m{, xCZRYN'㓓 G5.pl6ʊ |~?ϧ G" > NX^&Jb$|&&p\y,//oG?RʒqYHr<V+ds"$H3lh'd$;"TAf|gϧд4ݧطg7*+8;w;vs EXL&|?eؿN?wn{vp9476lh9T{rʨx[r~%zo-o᝙A$TUd{LwB<ؑoBL&w6Iyi)';:6GFP3]x084CAhv;,3;#!(rYIղhD٘5_! cXP dYW-,,D8\NlFM$6L$IX->|gQ tS۵{IENDB`geonkick-2.10.2/src/resources/show_osc1_envelopes_button_active.png000066400000000000000000000023411451017372400256140ustar00rootroot00000000000000PNG  IHDR5OsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<^IDATHQLWK-m@S,]b*J"+B]2d0S\L;cAgFfPmI0 Pbae#Y߫{?ߑʶlI?7oIPVK? 4 #IZp8h$LOOGȂB`0צrrrp( n޻?;~U*JKK=quu8N~3|NF#_;OȲɺ:N#GPմ؈磢UV%L&Z Ea6$)9w%=zrz$Ib,:- JFIt::.9BXh4,74(Ζ%%%h4'w ={ذahF ;v`m!Ez{{fkfT*{eڵՅbv~:v;ׯ_pPTXǏq:f; ?ܸAGGGR;/_@ 44FQ\\̵k,EorqYFGG:tJJ6mb\|gΐス|>ioon圿p&rss)//O \G$W^xC xܡSx@1?؋B! .^nj&unݺBQAnJЌ`ӧll,] JF*!MN&Ǔ؜Ti g1k|ݻDQjjjpz 8x+Wfk+;zO gdzcƍ(Ç9Q[;o VBVVv}VŶy3XA~~>e6^7L#44`߾}ԕݕ+tvv" sAbMikkbn$Ibtto-?C~^UUU/zLuu5Ozzpdff-S(ȲL__/_Ld) #d $5_I0L!B e/l6#IPD"ZF3<<vygmm:ҿ-kY`?faXeDnl6Yg6p}E!B,2޳:9zHENx<=+|t\D<>uէNRR\?^ S ԺDo^/e%Ǹ?JB\I( ('gᏩt#2qUe:$GԺ):z[=Յ(xO7fy-/}^^YכR)r`eDl؂9UUIrc B -/׷)O*}t:z)/+㔖Ʒ}};o̷'OTNNY6ÜD[XX'p8xtHvv6竫0 hZ={2EEQp҂ ӽ NGDDn^=Q$)4)"0 /^e2aZ5N'KKٲe 4.74p-eϞ=\ol$66/@E\OX,ۇ><׋n ZjEHJJLQS[{窪eoN`rra0088H[[>ZEAE t  | 0EE!2璘ӧwŘeSRl465QQQ| $ILtvuxkf&|w,` 4~g*+|ҧǏINN&..6$zFFFErKIIa||(uuu199cbbM6q]粒,A7ECp:ttv"Je_Pjj*\z:>rn7(}T\  (3L)/iUU ;:; 9<~rqQho'22_Mtt4T:(˂ 9Ǔl&//oɸuV6nHbb"6~.@yy9pZY~=fnG ";;UUbM ===ڵ AhyÅ (..f۶m.74e߿ϝ;w(,(!~֭x/]`0,~L k_M}6#XeE055(9dB cbb"M$bbbp̰ࡪ*>Yx`B^eujzȯ4Ma61X.A@V8<& )%áVEQ^z$I5~l)Zo˲>T,)H) :t]RJ)$loB@.s:ضmljwi-Jp8x>mJZkvB8p\X,,KlۦlYV@deY8t:!8I3htDE̶^y޹Ϻ "L7@ ݹ\y7@ڳpqe]]pa3(f!2'N4z/^|`_2sgpKoIr?IO$g,yz> tx!P(j;I$i#bbpw $ZW^5),h$ e)|hLRY+`jFI^vetw߬$E@$McfI5zL>ҦbBz}Ɣj R3q6L里{- b ?~mpm/Vrm9ƫKo߾i˜Bt?ϗj4- %;> Kw0[&,aP73IZ-HhHcNҢO98b ]6TZ6\:I͋8g9.˹u(9ЯGu364Ry$i)3$w^ mIG`~41r֎}LqaEf=|yG>_10wGy6F]G|y6kWu_>R#p%gI!*p2A[<&SZ=Sdz+ qpwR22|T:$ݣRLh$+#?-mglZDi^g÷Ҿ@/0 %"NNT`3p5mӽ SPU9ߠWR" d&׽~zgj:_jaZ =;wXO?~[*y[},;ba|c/35"E$)/-_krً0\4Yotx#$"AѻMDRxѪrݜ(W Jely/fu&gMqf~yGB)' ݂mY<~QDhT_6suxf]1r}*}e@Z5_gر;nfǞ={v\bv}{UST\"rSϜ9ZP*"* mʴQ6b|4LnEoZ]48 S_j#Gŋhei}EWt2 <[O1 CF4Ukaj*hK-*`m`ZzF30;x<#GQv1eY& Ty`00gaͺj}r1PXVYۿ=`z$dNfvUٌF#A+,,`"L&4J4F#yL&@l< 6r8t(-[Jx<)k۽3c:::.4_1~uuu5)BԵk;w~jص'^%I"^VT8LY,n!c`Yg2}e~7MӠV!BN>XitvvŇ5mڴyl**HNF=hywkl7=}L8Jɓ_nMUUUrȌ<::*VW{ tu]Ml1P߽{/58813ON>m6+URRq'hYrр(^^Stu]IFJOOo*)n4 OBa9 Kfb$$H 3f}SFg+W~J.6SRZ*Rw#Z  咒b^o@4̀iDU7'*Ia(B4d$( i )B!7ɲL.^ϱ'bmmĉ5K1 N啕v;}t·`0(qiiF;v|w:L 0LI^n=QCCy\R[X|nϜ#ܾk=t3 ߁~ >!xA4T8!c;_^ $9sog]tRX 7hM8$PA;Hokj^7FLN$ldjexDZXߘOC cN}Zd `'N6 lv655-)ʖyM##gcc# @ MJzf\.+ryUeCE02͘y,<ڪU`Z)Ac>^$E`IAUUΝ{ZF"r4 }5|08j#B<>'?y2U=X,UU#w+|^b,y}=}}(tf$]JrÍd2Uu8h( Ap"|w: ͽTbxn5J \^{79egV?*iF^+:E*[C=I>bΟBs6|9_WP(5N_}3Bff(5]cS yތn7g2p&Uv.3@GG;rB%*!t:饥 @{{0,N$n#d2N>lЂ `c岾Ұa ~+ oq64 7Jh73&IENDB`geonkick-2.10.2/src/resources/topmenu_controls_off.png000066400000000000000000000023471451017372400231530ustar00rootroot00000000000000PNG  IHDR6!sBIT|d pHYs+tEXtSoftwarewww.inkscape.org<dIDATXNHόC|8kSRhWݮ@fOC ڢ&PKIۉ$vl F٪iA*97sM ;dv , @ldXpdM'_$IB9z޿ 8KX4>_11 : UL~ᾺX>S)m>3jO8d1ժ"[ X, mF(y}N:-x|\ZU4Ø#L$Fa,A9f.PB`>՝B^,򛍆m۶dDJ)Z-4M' 0Oqn0&j6+Fh -_|' aLqAPBpgo`~K&(8qRJ a!KY!c4sS~A??jS,~z(vm0 X_Oi~.ek++oOfUNX?T*Q1ZZZYt]Hin4e|aJ XRLR޿?:4g.J3X_O^&Λz}=<<kf.۶,_e'OR!h};us3L7F1C&F @R"Z q3kh"YD\!}A,cz)iJbX`mmxTjBm%ш~H$zCrEAt`0q,KH&*٪b{'_Nu3¶mJX&s|0 Z-orJp8DDr°Gf4}_%JW^o~GVGTw䤌 UN9/5r/..J 4MJ#.UuFWטd1a# kHÇ|\sIENDB`geonkick-2.10.2/src/resources/topmenu_kit_hover.png000066400000000000000000000010501451017372400224360ustar00rootroot00000000000000PNG  IHDR6!sBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDATXJbQ6iLz]ξRV\ץnx@H 9>>"MSYdr1qGlQ8>Aktܱ0 K"bbAh4vZ9Ir+/&VV-c , *paa08a\#wl:A)% QfCEοDl6(,i:Gk1'&+ݽ>R[}ϊ{q;*\Z^hǃIENDB`geonkick-2.10.2/src/resources/topmenu_midi_active.png000066400000000000000000000003431451017372400227250ustar00rootroot00000000000000PNG  IHDR$z5sBIT|d pHYs+tEXtSoftwarewww.inkscape.org<`IDATH10 лؠ(BH)Zp},Ð೵ PIw nKte[+C y2ɐ'C y21@3:&$5HEOCIENDB`geonkick-2.10.2/src/resources/topmenu_midi_hover.png000066400000000000000000000003431451017372400225750ustar00rootroot00000000000000PNG  IHDR$z5sBIT|d pHYs+tEXtSoftwarewww.inkscape.org<`IDATHα E " 0%CfL.ංU%kI 2i id.E!O<D!O<I*lR뽗`k}jIENDB`geonkick-2.10.2/src/resources/topmenu_midi_off.png000066400000000000000000000003471451017372400222300ustar00rootroot00000000000000PNG  IHDR$z5sBIT|d pHYs+tEXtSoftwarewww.inkscape.org<dIDATHα 0 `(0J`<qi Z:LuIXJ̣Ţ_dȓ!O<dȓ!8/$5g)dK:(IENDB`geonkick-2.10.2/src/resources/topmenu_presets_active.png000066400000000000000000000021611451017372400234700ustar00rootroot00000000000000PNG  IHDR6!sBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDATXMLu3|3 l|m"D+|hc(E4Ƴ Ԙ@=R= Z Mꡦ&t)`5۝aK費 ;HQbRRBJ|ng'/\{r9TMEMȘ~ÖrxYfj'.+.S:;;j[T [P(D(BQ9umΝk"99DRRRn$&&}7 uuLwW|]>=*D"n7}}LOPUUIMM5׮bdv؏~ݻzzMM`ZA0MqSb*?ɓo""/21qh4ٳ915uχv?!!v￱ę3Vpq::>vkz<=cǬzܮY,fl6h׷{x<|~DQŌx.X\^Dٻ^4-&1s iÖIENDB`geonkick-2.10.2/src/resources/topmenu_presets_hover.png000066400000000000000000000021101451017372400233320ustar00rootroot00000000000000PNG  IHDR6!sBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDATXK[w?$:^XURzQV ^һA:J. Lv#lX(&Fbqv!IFeyy}_apMA;@L!Qx4S39~hSSX$fQWw|>j|>H95X6\"ankL&C0bssMѣ\PVp$I łnt0 x_tiHD&9롬rx&;o_RWwIFE~^rg6sD"*^l6xTUUa6Zi"T UUeHd"QpȎ=|=lv_XܵŞkjr\d2ӧ?I5z=:PmR{s՟dY.^ pW QU@Ƕ$|XlRk,. 0vL,ٳIssH$I&St:AcS^…6A`c# W|ߢi( _i s4/A '*#ܹsO;(ioA/--P[bw9fx\ EˋI}}=x]+(82Dx<dYfqm3VSV_ITv͜(QQ!N8!Z*r6bF.IENDB`geonkick-2.10.2/src/resources/topmenu_presets_off.png000066400000000000000000000020161451017372400227660ustar00rootroot00000000000000PNG  IHDR6!sBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDATX]Of/;I(` JVz!ThhBؤ6A0*QPBW*ADR T>Ǐ|-,,,{DArKDxqGk_Mߋl6)˴Z#QU+IN:϶mEz~ 0x?R:$H(AB!H$ߎGuP׻$a$uB! C' ( }ha|sTUJ%)dY&H0:' uHe'O!2ɬR՘K32ȍFW4-FG,aww~BELbu5+ljjEQQ4QUIؤlr,>~uHʲL.'ߡ(Ab(X/V`v6M&X,b[[I&j5^F$T)HId2A,q(x̏I\ahhFa$IDQ$ ( Z u,j$cŞR{lqB1 Ch4pJ$[d, Qo^ßYYPV/zm{4MZG:d$:iJ$ͱObbfTjH$L>I/Tƾ۶t:ݻ#>Cr,.eYǏ<A~~<;;czz$mwr_qDD ''-RMA\fձ\tJI 4M4-26 MS9<<¶-Alq:-2qҿc)IENDB`geonkick-2.10.2/src/resources/topmenu_samples_active.png000066400000000000000000000025051451017372400234510ustar00rootroot00000000000000PNG  IHDR6!sBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDATXݖOg#-!@s%Ƒi6$Fcf>,S\L%h8"1ƩQ01lN r>Sz2ZxC7αL4yם$TU{_C@+B, ,K5("Ԉ X^w) +X.#F`v +)bI"#Rr4R8|SF#6Ņ ߼)??dO\21?5a0虜BP` oMjBDEPFP`Zx܏d 7+#1q >Ϟ=XRbIbvvq7,Ư]kv355t?jij^СY&~R9GafV+:A`Г^a W|^CR!2oΝ׮r~z=44|(TU}d"P[cKpv)/MQf_o;cIvڹ(Nܹ:F#q=:uMCSMqAggEEl\ȣP(؃롮 (b0x\z IHKK]Gll,`v233z}SZ>#!D鴨ܻ=u(J֭KAXE XX ,H\x> :??#I!$IBիW`6b$a$177ӧ=[墴t'%%"E܊%%kdjja6q+lZ(JII!;M:;ز(naۃglťKh4288ƍv.^D\إ57? ''G;881Ce'?~ Y&FA߇ @Kˏ Wɓ_""in~REYY 'N|[#zG|:/l'VKVV&$==f3(?@NN6χF!--^>$&k$xZ1͆ l6cro`˥{?"&@uu[ut8^vW߽'>GhRVB3c@x4sԙ,4SX.d2QS $;|%9K`#lV֯_ի?-T]]bddl6+WRZZJuuY())`zzb(lBU5 F)$ItH$P6 T-h4ʫWs&q:$IaMӀl].'!/L&==l6s xزP(n2/\T*ͅ ?݃,<Ó' ܽ۽ . ywTUe``pXXBDy65uV*ndؼyI&Fd2*_ͼs2ށ^vHR+@jA\2MdTz&@]:QUU,xyuE*,,((!NH133ŋB46~-~;'O@<gbb vz[-{&{5k$m۶뙘_WD"t])ذܺuH$BAAAN= k9~Q'xÇԩhF,[?Nii9cll ׯߤyg~x|ǏEӱkjNǎYfٌWF*bppUUe!144allBn]9!a2(-u3:`Ϟ]Xn߾۽E6jRVED^QC(frr-߇jҥe[ 1_~wI,D@BaEgb|0 IENDB`geonkick-2.10.2/src/resources/topmenu_samples_off.png000066400000000000000000000022771451017372400227560ustar00rootroot00000000000000PNG  IHDR6!sBIT|d pHYs+tEXtSoftwarewww.inkscape.org</Cy<0 .^FQJdY&ivtz4) j5Νd*hqTUaa1,,x<<|OC$Rfs#x<~?kk?z"J>eIfI 81J8B4:djAEҥ (B,%Q,j,--8FZ"$I?y˪"B"H'߃i8gbooX,eqlooypcy\.pYm/`k6B j8$u63q}W]~ C0tEӧOa&9ujׯ_' `FjJ<>i\.]"0}ȲL"ql>ò,&i277O&j#>l0<<8KXEOOOWݻڵ$>[[U^\ʕܸ3 l…N@Ϟ=gj2kgOp} ^DkzvJy14PuyP);w[9=i `6fs+l{zuQT2RdSiCuZJiҸw0 "Q{kģG~'o~1 n/-ԆΣ) @h~ }dIENDB`geonkick-2.10.2/src/resources/tune_checkbox_on.png000066400000000000000000000013771451017372400222260ustar00rootroot00000000000000PNG  IHDR! lsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<|IDATHŔKHQsی3NhjNbRThXnjS;2ֶj jH#u?_Z'3ͻr,:jgO.1_\ؙA(H>!@D@"KtfL㺙6 ꬧o 6 "c09۽8:}{QPIpQLJ@@@dR#;5(Hg"Peݕ&)Ѝ҈yv FRV>:P}iTgԮ5ueHG`:ؗ-ou7=9bQ7˱t?u*pl\m}DdCLyoFZ߫3Ok%onGXmp(b'wlNY,{vma'ҹ$P%xg,0-"D T-jQ56fgrjA)oWP7YQ5\ F>+Tw/%g!t#8 e,cjfҍ8sH0~zL暙d$h';*MBk^|._D"7Fs~diFR`>"?O s\ĕ "IENDB`geonkick-2.10.2/src/resources/wav16_radio_button.png000066400000000000000000000023711451017372400224210ustar00rootroot00000000000000PNG  IHDR>NgsBIT|d pHYso*tEXtSoftwarewww.inkscape.org<vIDATHkTe{fo mk(Pl4` mEhEi%;d $KFw:sfVTJFX莺;>}X:.{;RBUGw@r]j W+ PRxMD~.)@UCJڿ=,D;5o>sy7;ž+An|ϥө"u>$ G㟕ӕ2nX̧R`gđwv'4܉D"gT^i*%U=N gV^,;{&?Wg .3kY&y3~IM4 ]J$}hniW JIkFNLfD4\o2Y!~VQ5cG#=/2yjTĉ-K4N'os itjE1q wR`k<dӈOEcWX뼥U&gԫH$g"d:à #T㊉2)LF@R@i =d =ph4Kt:yD)t:عsg\n F+4NSFFs&Ilذle PqϯVq#祖x^j ,& rGg laCy̘ٮux@A.9zD0XFe@Uձ4B4]Tmjj: 9>] Lf7]5Yw%YlEh ''h^O"rH$rI"lAUCdXyu>( uz3#"RLfQRaww{ ο9Q_(lJaVe5@6EPՋۈ#ceҝ@kUV8}a)KX /&?uYgO瘧'+"?v񑢪c{IHTD?/ḀHlgIENDB`geonkick-2.10.2/src/resources/wav16_radio_button_active.png000066400000000000000000000027001451017372400237500ustar00rootroot00000000000000PNG  IHDR>NgsBIT|d pHYso*tEXtSoftwarewww.inkscape.org<=IDATHilTU羡0jR@$hh0a2eE #!E1B$Mh]H%P*S$Ơ(.D!v7}u$F|89? 'Tup=P绰=@`1p+ȳX#"ߜQ/W`4+R {:-[>Lxq94pŧ>纉9"=5ߨbBj MD0`ˡP\}MMSVZh7o],k7knXU͛,ikks}Uap)<9uuuADyCFC*<R<=_|Ud/+A 0 c΂Xj՗_~ܹsڻwon~Z˩Ԏ#2 h4/,'o#tI|krd2L/(?aҮF q\7~ݙ'|S}ag/۷o_0ȑ#+Wd}Uվ64+ܐH$]E_(Lt8Q>Sх*kTX|a ½ثR~cgC$9)hU*U ι?B`h~֬YY}$;@T&$T;+˾ 23E@(4`yy'u(_T$+\-سgOtxY٠{D a‹XyuLw#iy^lj'lS֭["vXyy׈le 0_dby2XzT0=DTT7wwW}'oF4%dfpEsgVf ZwFJP:X0XBe@QQPԴ:QbԩC1:Mm=SR]KNXsj c?B4@ϢXCZ̘1uȐ!n[x$9` :qѣiY_"|8<ܟ@Q9'"}"Iιm1o&L8v횯^PP`KKK;*++.]%'ԫgA"}NTwfHaza|K`s6E7FY}#W&;ɾ93m̘qr$٬TvWhY yTջFD"]w pu_pƼ("=/RP#ucEP#ƻ9Ғ:X=M<VH4NZ n䏏{"/y&29UIENDB`geonkick-2.10.2/src/resources/wav24_radio_button.png000066400000000000000000000025171451017372400224220ustar00rootroot00000000000000PNG  IHDR>NgsBIT|d pHYso*tEXtSoftwarewww.inkscape.org<IDATH{Tew^ESKE2"Jgwf<2 ]*"J3 %M肑Aj*vaٮs9.bdTtͽ9oΎH!<~p;($P` 0-Rc;'oFD~.)@UK7q܀"/.to$ aYrٗRx=]8k[UeB$4J&w2:,w]wmi8|qtNNmgU51 PTQIġEs, dy׮]E\ \2c޺u ϛ 8ͥR+l+#fz Y$wf7tX#ۀC; 8jU=eAW#Hc8\f/ ;Pk'g@dMgjQ6qo{PV `2?7Hsv>jbX aG1ld wxa􏸨?c4^L&}ˮiTߴLej4ؖI$vDEpרΝ-x4Zu"ԉ俕+(8?Fсwg2G<@ 9N[&пw/gHmSSSµ!i#Ph70ɁBݞ.)ǀUUU^2*3qAG m:40ݲ gT(**h}(lD^Y@NgsBIT|d pHYso*tEXtSoftwarewww.inkscape.org<IDATHlu_\.si&H@ Z;]a1h&lIE!$R#,5NL 1c,skfl ca[vs\odwr<{QH%k%9 yBQԸ] [;_.`m)@U_+>D; < "* A0x;LLƿ גs;q`'0ݜZxSKIogeLfA%PuKaA5meϞݵ}}O,߱%ά_5 `||ٸ+?}Olٲoѣ g}wWn-5B- jSg3@vbɸ1⛚!pd򥥘5KA9om]D˯<w}T7A0Drˑ籕Ǐ_Ttttj͗rhxZS9}Gfhh(F*תȾr?kN3[Mst4pC^)nu #.O:6J5)\.'+U8stÇY4;;;CD@5&4i)V-k#tzRT*f"Oʁ5=j\]:DWR=I܋26Ȏ睈4Ԕ) ]o'߷ر*.{ ݧ5*&rdJ.0mA|>8zFlڿKE^t #HrFjkk fUVMQ#،+;zT-̢$2g9y4@:3σ=nCH493c%)Tk:ߥSr՝]v-էAԉhU`{3l9/M7<А}|SDVj[[GPj{ߍUҘw#`Cqрv rmOLVt|}@O|;|g/x[6H/"2?hlllll<9| (X5f&" Ydklpy#@cdk`ĹuBPďJ>Vșd֮/HuUbf:;E؅/Z&}IENDB`geonkick-2.10.2/src/resources/wav32_radio_button.png000066400000000000000000000025761451017372400224260ustar00rootroot00000000000000PNG  IHDR>NgsBIT|d pHYso*tEXtSoftwarewww.inkscape.org<IDATHmTUϙM $,(+"evκ&hF)"BQR!Qi!AfV!־a8 ?9r=RMME>n\cĸ^nUyDs PI@;0@Ձ$t]V¯\yO;[B7|qBp̎<ߟƘ޶굺s7@lz;+yk1}Zz\`Х("UNcxZUT0YNt[YcQFbt=SB.cutm6݆yLW]Jh֫ \ptZP٪FQ}<:4`iLG8q@Fs@ESވ_, vT0u>.uQ&^FQq Z} tn#LTV`y4}*p uwwOK+u>h/By ʘ1ҎJhY5nqڒd*TS擑XR!ixzUƺH@sFÖcYsif`uLy7|πdOOtU%ɣvX ]iy4n<GD5@Ydˊ~\Tjj@Db3We%hmA! a Eղ4E}s󑱚jllq&dgXLDb*w̛)0Jcûp'"/ MCz{;.Gvy6QRIl ڗNLOOu}DfkvZ60gi;9XMRsQLfΜ*_bMmnmr&cG^ )qu+iG]L1ȣm0y z\iuĨ'(ֺa).0qiz( Ua*c׉S< NIQK,?)^-;Z?u #IENDB`geonkick-2.10.2/src/resources/wav32_radio_button_active.png000066400000000000000000000030471451017372400237530ustar00rootroot00000000000000PNG  IHDR>NgsBIT|d pHYso*tEXtSoftwarewww.inkscape.org<IDATHlUg?{*2 C!kJ﹜VdΠSb,Y48 CL3IXfXrK{]1`b{]>q{m-Y?=}(P/?kKo_5zWSUE _+(]&N/E[ ^qBn 9N !78mT9=[X8X;!qQDL)2Twm [ETUE[k9{ٱcS'Jج}cU]]VT_lj>,"]Qj|2\$=ᆐ5LfЃL6r-ZhRݾg}rR}}źyہn=qG]sO^paѬEy&kbU]N/D^sA03䛛0Xhg6̼P ]e=.*)VyiAv>[FGG^};C?XS.,ꢨN*GOxW_}\ А"E<ȣ(2c=QnRxbǎAm)7LVrG*Z=Veyqo%TH6i9wy; \|%$DTO)q :g"~t_ 7ov$*(rZK`uKzlL#zRo!ce}ܨ[{Wecj@D9h^z&uNԲX<qF9Jm[###P622?)PHڟ5K+WT)L&Sх+v*Dޘ.Vd2^ju|hKƘyo[MMMa'}MD^iizM5[d6n8([Vx4=d24u댑4[8:&}A<\nTêrZH ų- ]*mײt( .(΋"ca˽;! 0(:5q| VV(] F?(@MytGkGDelQ L GDWU:!yhbc*g-;WՂѡR!|H*(nv0N\{~@k9 gIƊEP'\g*5jTL2`= #-Hh_Jp>fvwAOaEntl"[=;qߤ"Ј X1Ak:m l:ό2.?zg\bE棺W@^hso aYjn..PH]Gw53ꇥs1fA'aJF,\Mf577bM^L]܄{ڎ϶Mf`4 l>Eeͽ2JpD䜪do;+"'Ҕ$'19o` ߻F# IENDB`geonkick-2.10.2/src/resources/wave_button_sample_active.png000066400000000000000000000024241451017372400241340ustar00rootroot00000000000000PNG  IHDRCt sBIT|d pHYsӱ9tEXtSoftwarewww.inkscape.org<IDATHKlUUϥP q(GAl9ڴ_E cLLL"/9MPJA!J{m+A&H8V({KH8{?TUJDq`U̽ D  SfŪM [Z@ui /{kUM3"͚Ç19`'[zMj)#Ґin¨֝fXk֯_W9 ` 6?~|BUl'}Gkjh W#L"2 a\G {exӽX' 3uy“--ĔD\Xr ~SVVq4^jbG2}v/Vf;笪LΘg WcvtS!AqTc@Q 2&}W-UKd$~tƎ\tqqq_~{U3,Ll6>~zhi&L66Xj W֦X7;QQ+*#N-ylmZz{{}dQ(7y>ȤSf4+k~~4׉s3iH"8qB{)ihmW|=nOE-*K{z=]*-5TLHblFN`#0&|l٣N R/t@|)3Qv7{ 6|U/rIENDB`geonkick-2.10.2/src/resources/wave_button_sawtooth.png000066400000000000000000000023251451017372400231700ustar00rootroot00000000000000PNG  IHDRCt sBIT|d pHYsӱ9tEXtSoftwarewww.inkscape.org<RIDATH]e;g5֕+B0=3ggZJ㪐.[ #P, 4jמ3u I\]t帝l273ߙUSU]ɘXU6`>C"/'@U;k o("#|>_LCM\ZDR ljlO~#]R_Qx198*7C\܈ U:aFEoqv7ߣ5q\wuu9q_}cF$ɜs|~``'6hzȎʃjIbh9V8 bbݫl.ru÷rcIV >S~Z3Oَ3-RaU Vշu`^^pVqTcs`r7X"y[qܳX`D*/Ur|Nl٧&}RϽj(_dNN-;Q&MWw]b8U Vkef~p,I'bvD'F`$ƜH,BY&kz!6{7L â4VTtZ1"8Vc ʕ{+2nTytЈGqRkW][-"{2 i{PÞvX"M]790T*xa6`*־><< d+vID63IC "KuSloN0/\-<(уIT,XUB9 p(-i+Eam֕utt9]L\ rܗ*|zv|[W71^5+y*?]x x~ 7 OsYUZ 8ȥlJom~IENDB`geonkick-2.10.2/src/resources/wave_button_sawtooth_active.png000066400000000000000000000025661451017372400245320ustar00rootroot00000000000000PNG  IHDRCt sBIT|d pHYstEXtSoftwarewww.inkscape.org<IDATHklU֭ZYF$>"L;[j1 D!D!$ 7 WLZMXc4A0 v?tYh{{@UkTUqeLV)@; f`D'@UVL\HDL&DK״jF Ԯ[ 6gϞD)"j |#_ gƂ|?B! _rRP2b͚5qZeٲ'O]uش^P99%TΙnmQ Ȟ_ׯ_ApUQ^T0qtAu,k!K-59F7+7Et^CKw fحs]?m> ŷDY ]7=?ʷ=S^^U}M>MbKu]? cMUo q̧Zot3T$c#y5j,wl,/@o1}Q>o9wuy(&Dȸwbqgb1j$;AO6T*ydrYdҖ+\.7gkץ"wJ %cS :hQt5fH|Q];~l:,J-舵vXp5Jrɤ]/.pDEsv_boUNv'<ۛify.bHpkWECsle^5Y놟!4@.X U>{{3 "yetʕ_FQ$DB+xLD~y7a>h0ȍ|氐砏Hk'8~1vc);#"~6tu;,)"yk@=/dIϝ?N5䫚 I{J=. LvKFlfp'm)@AD;8?,GLIENDB`geonkick-2.10.2/src/resources/wave_button_sine.png000066400000000000000000000014651451017372400222620ustar00rootroot00000000000000PNG  IHDRCt sBIT|d pHYsӱ9tEXtSoftwarewww.inkscape.org<IDATHOUewfqDsL hcn3{gD\"$Ah궠,'|rI# ]A"?| vp;y}##*9]1|| 'زDoe}U.>βdgyfC1 KvL?av }rF:εO^|?~lpx>3gfǍ!Dws6mqg& g|%'ʹl<*@B? ]~)܉,KNGsިT:2h[ .ݧ<%v89𕙅$6r!'MIENDB`geonkick-2.10.2/src/resources/wave_button_sine_active.png000066400000000000000000000017401451017372400236110ustar00rootroot00000000000000PNG  IHDRCt sBIT|d pHYsӱ9tEXtSoftwarewww.inkscape.org<]IDATHAhe$MФ% 4'#(di*-AJ.b E#b=ijT mcf%T*bh6&l;FiY0o11n уi> fDjjjTyU=o>U޿˟^ulTWnE*rcpxUDNG=ϋ9!UN$2n¨jF=LG&~7B`޴WT)8o\7} O2 `=yWZ jĀB t\s@bQ4TEZ5T 2 |`"*0H>Tj嗺w~:Ξ T%n[D"3h{ |> [V$Gt~IZ h\FTnjLY.ƌV{J P 0??Z+ئhLd&z./ݝW4)/wE~`"{Q]gwD l߱cX,amkk[jii)Ƃۭ|3|tsVJʒBD DՏl(ޝa,@{{G.9`cccipp>MAx &+PU(߹n5cHKºZ#1۵kqjjjsCfL&IRټ3z2sgaI~v@DgzUJd2DZssZ S}ō0=c;0 ܳ\ۨªͭJ8:تTXmT?Z?rozSIENDB`geonkick-2.10.2/src/resources/wave_button_square.png000066400000000000000000000021041451017372400226130ustar00rootroot00000000000000PNG  IHDRCt sBIT|d pHYsӱ9tEXtSoftwarewww.inkscape.org<IDATH_U?3woAXD A[Kewzg |XS60D62%EPȥh;34rkŰ?Ea#v^03=3np k>H2`%$.'%H <:f'7imW.wh'p&`,f"Psfo{+Sm3nt~f8UCgՑ uzuzA3]2kԥBYWEQtɺ(Hdac-p= Z`>3x$ R˲h L_Jf&33eW$I-T.FQYtLMR.ٽjNxh,0xqi@<k+w{Z,:fb}&Y{ys,~vEXQOOwco&<^)didp?9 -ϕCC atS:.\ƻƺfyZەeI M}ffPcy8מ2~Kˠsz傉3(`Ա^Kҥ]W49,My)2o/YTIOLD ZZZ6탃բ eYͶ =65? 0t5/D0kLzhz3iNqv x!AfNRoӾߚTsJŭifv;Wa[0-u0|(vа x{*溉:p~}PH4%bb 5ORFdAEm=>tҐj%s7=s/TUB`u P `'18;yS-ݱXө[èjx̙fsx299Ro2"aOW|6ۈz ;:޾pp`ɒڡKUUU9EU=!"ߏg[Y_o~|ݺSc6[afEiHHykGVnHF׍nVL&tXʊ0H,! E&yʠSQqW[DC4#a_{(ka[p֍5m/R{o&Sy1 I Tf`lmz?MĮBj]A%٧: y8u(GӓN-Z5TgXk |LqB5,Psz$QLT*A8v}F~u% U76ڡ6OTe.FEE0By Qhmt[C*ߊjZ ģnOFE ZDc>FA 6 ZM4&*_>!d2ٚ_ϻ"w2A؛zZF~u_. {k\̣HsؚL&*[Gy5<ϫt<cF"W/Ωd TIs Ogb¸ `r Lž(lht[Y@@D@D$;Ird#TE M4t>wIENDB`geonkick-2.10.2/src/resources/wave_button_triangle.png000066400000000000000000000021071451017372400231230ustar00rootroot00000000000000PNG  IHDRCt sBIT|d pHYstEXtSoftwarewww.inkscape.org<IDATHOhUμX.,%Si6BhNm҅YX+"PQ(uSTDl Ԧf{5mPMmj"MEMk^lo3sf9? 偪n6gS@/>@U}@p uVJQZխ,+S|]d6xE[kr?7κfدrÎw"nV'SvU5 d-as;LgbxQD^`@UڨETSDN>P[;~nj1Ԥ1:L6:Xj88طMWoT=O{>$UugxrNTE󔪫7|NjaXlզ&V ݦhxy{9s9,hD^,roHWX:cރ~QmN_K1+//Zfɴ1XUPd8 wtuu[y}ol6{ >sv㼂4~6cmn~'b(Ju9^GDN ZDHxGD6@r#qJ912pq| c"Ýp+Xk D0k??)R(hIENDB`geonkick-2.10.2/src/resources/wave_button_triangle_active.png000066400000000000000000000023531451017372400244610ustar00rootroot00000000000000PNG  IHDRCt sBIT|d pHYstEXtSoftwarewww.inkscape.org<hIDATH}hUu?9y,ڢ([sJsD889{ EE$-|C? *4Qnޝi3m3RSr=?[[ s}=:Xv͕v!Zl^Lښ~v9+JE3-U~sv΂䵆nD&ۋۊ2L.>جrJĶdy)63}A5ٴUQG-())hhxtuuu;ZTb?hmMVnTUb7@ u}<IENDB`geonkick-2.10.2/src/resources/wf_bk_hbox.png000066400000000000000000000100041451017372400210040ustar00rootroot00000000000000PNG  IHDRUP*'zTXtRaw profile type exifxڭVm+' )r8*7ӌǻ{fLy!Dw#H+~楖rkW'|ԞTFۧ˾]?ܞa]wgf ηvʗzZco P,)cC64 sϧlo|ȑP x [BvY/X # } ?wD䵦յ\hu;:{ ; sXA1vG;(8:ʠJ WL-4b`p̃lspTy)BMLqp989o=yL0mI_uƆ6m[Yc3!.^.aA`PO lGJڒgm!J6/s+3!#eQGbYJF+hΌR,Mf@ @u&wm)>Zf QAĶCb5}/f !wicK OV)8xڐQ9't%dHgsD^g(Uk^a"+)ɰSm 3۾䄮7S@Fa'D-(fȾdR>ۑ-Oo鑒Is2qBځ{*0,Xb5PYښ, xc*9@ !{եG“f(LPYEϨR@}4v\kCӃpY> M_W?d}JŵNi{$g*Ci;u7M/j~թ! HA]Grl4ށ?Hq"ܷ CjZ//)CVz)rmRO@8pDg끇*PZi|aUtՖ=p?*2\i繯{Ԉأ iPF%䏠g ЉXC'ӊ 嶆Mp`GROGGB><GA+V*ᎁ웛m$F@ H=>p>e??R{?f:ԧ@N\"PH?pu簾]9=?{G_ێb #( `LiCCPICC profilex}=H@_SR*"vqP,'B*ZUKIC(X:8* NN.RB 4*L5Ut".fsbA#$fsT>Ey?G7gnXSy8JB|N+q3IDATxO[96 HHz^V$-ILNWlg쟘jRҦ5kn` / J$-E7˱ͣ9< -C~gn &"Pu #ղ?_|c;^LJ\.zahD~"벵0A  O䩩VgȧF<-Od"\1-iuu'?LE*M0~zSgY>07 me&FL, shCx}p/]"yawe:`lt3gmOɓ{|>}=FG͡!<#M|%*$#؈淋v"}\Ӊ碑t p_mmmJh h,eYDQ<tuws1 LL&CWw7WFFxW`fzgzbMM<ضMTb:`~7nP(#gynLN2HVuF;ZY! I\\.c}}=g5ի }}8C[[e1;; @_?GzzdY~ \^ZZZX^Z4MPVYZ\W_{rM\PL:i9rp864o$y)>Q:9zCI(؁`zB݇I 8/@>4ü94Dc$B6P(8zN>q#J;_kj" ruI$.dYfggf?38yqNW_~WWP,bIcK].pex,$,/-^˲,_.1>6O>,zjϻ~*cbLڂ~kkk P__O*!Dc1j+M.RԎM4n[g9>0@gW]]]VV(jh,xVO/^T,RVk?5ҷJRu^iV:Ts}>## b&tv_Xqjw_qI4S?b| <}h, DQ~jqo"...ׇg|||'|h4g/LVm{)(.P:ǿm5ǡE{{;n b۴J(=b6H$B0ݻΏF Z peyuIӸKT"׶lgΞobCirmN3rpŅ55նnKR!ra,¶TU]/^zsϳV(с=kx)ΟP(N\$Ԅ?|!1w|JBCC $L2nl$bd3x@ @2d\޹`B>dux<nhY~_c( *E(ED(@Q" PD(ED(@Q" PD(ED(@(" PDQ"E(@(" PDQ"E(@(" PDQ" PD(ED(@Q" PD~ 1(PDy0hQ"G{͙ahDD^sڂP|bny['hZE*qIENDB`geonkick-2.10.2/src/standalone/000077500000000000000000000000001451017372400163115ustar00rootroot00000000000000geonkick-2.10.2/src/standalone/CMakeLists.txt000066400000000000000000000011711451017372400210510ustar00rootroot00000000000000set(GKICK_STANDALONE_SOURCES ${GKICK_STANDALONE_DIR}/main.cpp) add_executable(geonkick ${GKICK_COMMON_HEADERS} ${GKICK_COMMON_RESOURCES} ${GKICK_STANDALONE_SOURCES}) add_dependencies(geonkick api_standalone) add_dependencies(geonkick redkite) target_link_libraries(geonkick geonkick_common api_standalone redkite) target_link_libraries(geonkick "-lstdc++fs") target_link_libraries(geonkick "-lX11 -ljack -lsndfile -lcrypto -lrt -lm -ldl -lpthread -lcairo") install(TARGETS geonkick DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) install(FILES ${GKICK_DOC_DIR}/man/geonkick.1 DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1) geonkick-2.10.2/src/standalone/main.cpp000066400000000000000000000045721451017372400177510ustar00rootroot00000000000000/** * File name: main.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2017 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "MainWindow.h" #include "geonkick_api.h" #include #include static geonkick* createDSP() { geonkick* geonkickApi = nullptr; if (geonkick_create(&geonkickApi, Geonkick::defaultSampleRate) != GEONKICK_OK) { GEONKICK_LOG_ERROR("can't create geonkick API"); return nullptr; } return geonkickApi; } int main(int argc, char *argv[]) { auto dsp = createDSP(); if (!dsp) { GEONKICK_LOG_ERROR("can't create DSP"); exit(EXIT_FAILURE); } if (mlockall(MCL_CURRENT) == -1) { GEONKICK_LOG_ERROR("can't lock memory"); exit(EXIT_FAILURE); } RkMain app(argc, argv); std::string preset; if (argc == 2) preset = argv[1]; auto api = new GeonkickApi(Geonkick::defaultSampleRate, GeonkickApi::InstanceType::Standalone, dsp); api->setEventQueue(app.eventQueue()); api->setStandalone(true); if (!api->init()) { GEONKICK_LOG_ERROR("can't init API"); delete api; exit(EXIT_FAILURE); } auto window = new MainWindow(&app, api, preset); if (!window->init()) { GEONKICK_LOG_ERROR("can't init main window"); exit(EXIT_FAILURE); } auto res = app.exec(); if (munlockall() == -1) { GEONKICK_LOG_ERROR("can't unlock memory"); exit(EXIT_FAILURE); } return res; } geonkick-2.10.2/src/top_bar.cpp000066400000000000000000000471261451017372400163250ustar00rootroot00000000000000/** * File name: top_bar.cpp * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "top_bar.h" #include "geonkick_button.h" #include "preset_browser_model.h" #include "preset_browser_view.h" #include "ViewState.h" #include "kit_model.h" #include "percussion_model.h" #include "MidiKeyWidget.h" #include #include #include #include RK_DECLARE_IMAGE_RC(separator); RK_DECLARE_IMAGE_RC(logo); RK_DECLARE_IMAGE_RC(open); RK_DECLARE_IMAGE_RC(open_hover); RK_DECLARE_IMAGE_RC(save); RK_DECLARE_IMAGE_RC(save_hover); RK_DECLARE_IMAGE_RC(export); RK_DECLARE_IMAGE_RC(export_hover); RK_DECLARE_IMAGE_RC(reset); RK_DECLARE_IMAGE_RC(reset_hover); RK_DECLARE_IMAGE_RC(reset_active); RK_DECLARE_IMAGE_RC(play); RK_DECLARE_IMAGE_RC(play_pressed); RK_DECLARE_IMAGE_RC(play_hover); RK_DECLARE_IMAGE_RC(layer1); RK_DECLARE_IMAGE_RC(layer2); RK_DECLARE_IMAGE_RC(layer3); RK_DECLARE_IMAGE_RC(layer1_disabled); RK_DECLARE_IMAGE_RC(layer2_disabled); RK_DECLARE_IMAGE_RC(layer3_disabled); RK_DECLARE_IMAGE_RC(layer1_hover); RK_DECLARE_IMAGE_RC(layer2_hover); RK_DECLARE_IMAGE_RC(layer3_hover); RK_DECLARE_IMAGE_RC(tune_checkbox_on); RK_DECLARE_IMAGE_RC(tune_checkbox_off); RK_DECLARE_IMAGE_RC(tune_checkbox_hover); RK_DECLARE_IMAGE_RC(topmenu_controls_active); RK_DECLARE_IMAGE_RC(topmenu_controls_hover); RK_DECLARE_IMAGE_RC(topmenu_controls_off); RK_DECLARE_IMAGE_RC(topmenu_midi_off); RK_DECLARE_IMAGE_RC(topmenu_midi_active); RK_DECLARE_IMAGE_RC(topmenu_midi_hover); #ifndef GEONKICK_SINGLE RK_DECLARE_IMAGE_RC(topmenu_kit_active); RK_DECLARE_IMAGE_RC(topmenu_kit_hover); RK_DECLARE_IMAGE_RC(topmenu_kit_off); #endif // GEONKICK_SINGLE RK_DECLARE_IMAGE_RC(topmenu_presets_active); RK_DECLARE_IMAGE_RC(topmenu_presets_hover); RK_DECLARE_IMAGE_RC(topmenu_presets_off); RK_DECLARE_IMAGE_RC(topmenu_samples_active); RK_DECLARE_IMAGE_RC(topmenu_samples_hover); RK_DECLARE_IMAGE_RC(topmenu_samples_off); TopBar::TopBar(GeonkickWidget *parent, KitModel *model) : GeonkickWidget(parent) , kitModel{model} , openFileButton{nullptr} , saveFileButton{nullptr} , exportFileButton{nullptr} , presetNameLabel{nullptr} , layer1Button{nullptr} , layer2Button{nullptr} , layer3Button{nullptr} , controlsButton{nullptr} , midiKeyButton{nullptr} #ifndef GEONKICK_SINGLE , kitButton{nullptr} #endif // GEONKICK_SINGLE , presetsButton{nullptr} , samplesButton{nullptr} { setFixedSize({parent->width(), 30}); auto mainLayout = new RkContainer(this); mainLayout->setSize(size()); auto logo = new RkLabel(this); RkImage image(62, 10, RK_IMAGE_RC(logo)); logo->setSize(image.width(), image.height()); logo->setBackgroundColor(68, 68, 70); logo->setImage(image); logo->show(); mainLayout->addWidget(logo); addSeparator(mainLayout, 10); openFileButton = new GeonkickButton(this); openFileButton->show(); openFileButton->setSize(26, 10); openFileButton->setType(RkButton::ButtonType::ButtonCheckable); openFileButton->setImage(RkImage(openFileButton->size(), RK_IMAGE_RC(open)), RkButton::State::Unpressed); openFileButton->setImage(RkImage(openFileButton->size(), RK_IMAGE_RC(open_hover)), RkButton::State::UnpressedHover); openFileButton->setImage(RkImage(openFileButton->size(), RK_IMAGE_RC(open_hover)), RkButton::State::Pressed); RK_ACT_BINDL(openFileButton, pressed, RK_ACT_ARGS(), [=](){openFileButton->setPressed(false); action openFile();}); mainLayout->addWidget(openFileButton); addSeparator(mainLayout, 10); saveFileButton = new GeonkickButton(this); saveFileButton->setSize(23, 10); saveFileButton->setType(RkButton::ButtonType::ButtonCheckable); saveFileButton->setImage(RkImage(saveFileButton->size(), RK_IMAGE_RC(save)), RkButton::State::Unpressed); saveFileButton->setImage(RkImage(saveFileButton->size(), RK_IMAGE_RC(save_hover)), RkButton::State::UnpressedHover); saveFileButton->setImage(RkImage(saveFileButton->size(), RK_IMAGE_RC(save_hover)), RkButton::State::Pressed); RK_ACT_BINDL(saveFileButton, pressed, RK_ACT_ARGS(), [=](){saveFileButton->setPressed(false); action saveFile();}); mainLayout->addWidget(saveFileButton); addSeparator(mainLayout, 10); exportFileButton = new GeonkickButton(this); exportFileButton->setSize(29, 10); saveFileButton->setType(RkButton::ButtonType::ButtonCheckable); exportFileButton->setImage(RkImage(exportFileButton->size(), RK_IMAGE_RC(export)), RkButton::State::Unpressed); exportFileButton->setImage(RkImage(exportFileButton->size(), RK_IMAGE_RC(export_hover)), RkButton::State::UnpressedHover); exportFileButton->setImage(RkImage(exportFileButton->size(), RK_IMAGE_RC(export_hover)), RkButton::State::Pressed); RK_ACT_BINDL(exportFileButton, pressed, RK_ACT_ARGS(), [=](){exportFileButton->setPressed(false); action openExport();}); mainLayout->addWidget(exportFileButton); addSeparator(mainLayout, 10); auto playButton = new RkButton(this); playButton->setType(RkButton::ButtonType::ButtonPush); playButton->setSize(43, 18); playButton->setImage(RkImage(playButton->size(), RK_IMAGE_RC(play)), RkButton::State::Unpressed); playButton->setImage(RkImage(playButton->size(), RK_IMAGE_RC(play_hover)), RkButton::State::UnpressedHover); playButton->setImage(RkImage(playButton->size(), RK_IMAGE_RC(play_pressed)), RkButton::State::Pressed); RK_ACT_BIND(playButton, pressed, RK_ACT_ARGS(), kitModel->api(), playKick()); playButton->show(); mainLayout->addWidget(playButton); addSeparator(mainLayout, 10); createLyersButtons(mainLayout); addSeparator(mainLayout, 10); auto resetButton = new RkButton(this); resetButton->setSize(33, 18); resetButton->setType(RkButton::ButtonType::ButtonPush); resetButton->setImage(RkImage(resetButton->size(), RK_IMAGE_RC(reset)), RkButton::State::Unpressed); resetButton->setImage(RkImage(resetButton->size(), RK_IMAGE_RC(reset_hover)), RkButton::State::UnpressedHover); resetButton->setImage(RkImage(resetButton->size(), RK_IMAGE_RC(reset_active)), RkButton::State::Pressed); resetButton->show(); RK_ACT_BIND(resetButton, pressed, RK_ACT_ARGS(), this, resetToDefault()); mainLayout->addWidget(resetButton); addSeparator(mainLayout, 10); tuneCheckbox = new GeonkickButton(this); tuneCheckbox->setCheckable(true); tuneCheckbox->setFixedSize(33, 18); tuneCheckbox->setImage(RkImage(tuneCheckbox->size(), RK_IMAGE_RC(tune_checkbox_off)), RkButton::State::Unpressed); tuneCheckbox->setImage(RkImage(tuneCheckbox->size(), RK_IMAGE_RC(tune_checkbox_on)), RkButton::State::Pressed); tuneCheckbox->setImage(RkImage(tuneCheckbox->size(), RK_IMAGE_RC(tune_checkbox_hover)), RkButton::State::PressedHover); tuneCheckbox->setImage(RkImage(tuneCheckbox->size(), RK_IMAGE_RC(tune_checkbox_hover)), RkButton::State::UnpressedHover); tuneCheckbox->show(); RK_ACT_BIND(tuneCheckbox, toggled, RK_ACT_ARGS(bool b), kitModel->api(), tuneAudioOutput(kitModel->api()->currentPercussion(), b)); mainLayout->addWidget(tuneCheckbox); addSeparator(mainLayout, 10); presetNameLabel = new RkLabel(this); presetNameLabel->setBackgroundColor(background()); presetNameLabel->setTextColor({210, 226, 226, 140}); auto font = presetNameLabel->font(); font.setSize(10); presetNameLabel->setFont(font); presetNameLabel->setSize(80, 30); presetNameLabel->show(); mainLayout->addWidget(presetNameLabel); addSeparator(mainLayout); midiKeyButton = new GeonkickButton(this); midiKeyButton->setTextColor({200, 200, 200}); midiKeyButton->setType(RkButton::ButtonType::ButtonUncheckable); midiKeyButton->setSize(36, 20); midiKeyButton->setImage(RkImage(midiKeyButton->size(), RK_IMAGE_RC(topmenu_midi_off)), RkButton::State::Unpressed); midiKeyButton->setImage(RkImage(midiKeyButton->size(), RK_IMAGE_RC(topmenu_midi_active)), RkButton::State::Pressed); midiKeyButton->setImage(RkImage(midiKeyButton->size(), RK_IMAGE_RC(topmenu_midi_hover)), RkButton::State::UnpressedHover); RK_ACT_BIND(midiKeyButton, toggled, RK_ACT_ARGS(bool pressed), this, showMidiPopup()); mainLayout->addWidget(midiKeyButton); // Controls button addSeparator(mainLayout); controlsButton = new GeonkickButton(this); controlsButton->setPressed(viewState()->getMainView() == ViewState::View::Controls); controlsButton->setFixedSize(54, 20); controlsButton->setImage(RkImage(controlsButton->size(), RK_IMAGE_RC(topmenu_controls_off)), RkButton::State::Unpressed); controlsButton->setImage(RkImage(controlsButton->size(), RK_IMAGE_RC(topmenu_controls_active)), RkButton::State::Pressed); controlsButton->setImage(RkImage(controlsButton->size(), RK_IMAGE_RC(topmenu_controls_hover)), RkButton::State::UnpressedHover); controlsButton->show(); mainLayout->addWidget(controlsButton); RK_ACT_BIND(controlsButton, pressed, RK_ACT_ARGS(), viewState(), setMainView(ViewState::View::Controls)); RK_ACT_BIND(viewState(), mainViewChanged, RK_ACT_ARGS(ViewState::View view), controlsButton, setPressed(view == ViewState::View::Controls)); #ifndef GEONKICK_SINGLE // Kit button addSeparator(mainLayout); kitButton = new GeonkickButton(this); kitButton->setPressed(viewState()->getMainView() == ViewState::View::Kit); kitButton->setFixedSize(54, 20); kitButton->setImage(RkImage(kitButton->size(), RK_IMAGE_RC(topmenu_kit_off)), RkButton::State::Unpressed); kitButton->setImage(RkImage(kitButton->size(), RK_IMAGE_RC(topmenu_kit_active)), RkButton::State::Pressed); kitButton->setImage(RkImage(kitButton->size(), RK_IMAGE_RC(topmenu_kit_hover)), RkButton::State::UnpressedHover); kitButton->show(); RK_ACT_BIND(kitButton, pressed, RK_ACT_ARGS(), viewState(), setMainView(ViewState::View::Kit)); RK_ACT_BIND(viewState(), mainViewChanged, RK_ACT_ARGS(ViewState::View view), kitButton, setPressed(view == ViewState::View::Kit)); mainLayout->addWidget(kitButton); #endif // GEONKICK_SINGLE // Presets button addSeparator(mainLayout); presetsButton = new GeonkickButton(this); presetsButton->setPressed(viewState()->getMainView() == ViewState::View::Presets); presetsButton->setFixedSize(54, 20); presetsButton->setImage(RkImage(presetsButton->size(), RK_IMAGE_RC(topmenu_presets_off)), RkButton::State::Unpressed); presetsButton->setImage(RkImage(presetsButton->size(), RK_IMAGE_RC(topmenu_presets_active)), RkButton::State::Pressed); presetsButton->setImage(RkImage(presetsButton->size(), RK_IMAGE_RC(topmenu_presets_hover)), RkButton::State::UnpressedHover); presetsButton->show(); RK_ACT_BIND(presetsButton, pressed, RK_ACT_ARGS(), viewState(), setMainView(ViewState::View::Presets)); RK_ACT_BIND(viewState(), mainViewChanged, RK_ACT_ARGS(ViewState::View view), presetsButton, setPressed(view == ViewState::View::Presets)); mainLayout->addWidget(presetsButton); // Samples button addSeparator(mainLayout); samplesButton = new GeonkickButton(this); samplesButton->setPressed(viewState()->getMainView() == ViewState::View::Samples); samplesButton->setFixedSize(54, 20); samplesButton->setImage(RkImage(samplesButton->size(), RK_IMAGE_RC(topmenu_samples_off)), RkButton::State::Unpressed); samplesButton->setImage(RkImage(samplesButton->size(), RK_IMAGE_RC(topmenu_samples_active)), RkButton::State::Pressed); samplesButton->setImage(RkImage(samplesButton->size(), RK_IMAGE_RC(topmenu_samples_hover)), RkButton::State::UnpressedHover); samplesButton->show(); RK_ACT_BIND(samplesButton, pressed, RK_ACT_ARGS(), viewState(), setMainView(ViewState::View::Samples)); RK_ACT_BIND(viewState(), mainViewChanged, RK_ACT_ARGS(ViewState::View view), samplesButton, setPressed(view == ViewState::View::Samples)); mainLayout->addWidget(samplesButton); RK_ACT_BIND(kitModel, modelUpdated, RK_ACT_ARGS(), this, updateGui()); RK_ACT_BINDL(kitModel, percussionUpdated, RK_ACT_ARGS(PercussionModel* model), [=](PercussionModel* model) { if (model->isSelected()) updateGui(); } ); updateGui(); } void TopBar::addSeparator(RkContainer *mainLayout, int width) { mainLayout->addSpace(width); auto separator = new RkLabel(this); separator->setSize(2, 21); separator->setBackgroundColor(68, 68, 70); separator->setImage(RkImage(separator->size(), RK_IMAGE_RC(separator))); separator->show(); mainLayout->addWidget(separator); mainLayout->addSpace(width); } void TopBar::createLyersButtons(RkContainer *mainLayout) { layer1Button = new GeonkickButton(this); layer1Button->setBackgroundColor(background()); layer1Button->setSize(24, 18); layer1Button->setImage(RkImage(layer1Button->size(), RK_IMAGE_RC(layer1_disabled)), RkButton::State::Unpressed); layer1Button->setImage(RkImage(layer1Button->size(), RK_IMAGE_RC(layer1)), RkButton::State::Pressed); layer1Button->setImage(RkImage(layer1Button->size(), RK_IMAGE_RC(layer1_hover)), RkButton::State::PressedHover); layer1Button->setImage(RkImage(layer1Button->size(), RK_IMAGE_RC(layer1_hover)), RkButton::State::UnpressedHover); layer1Button->setCheckable(true); mainLayout->addWidget(layer1Button); mainLayout->addSpace(2); layer2Button = new GeonkickButton(this); layer2Button->setBackgroundColor(background()); layer2Button->setSize(24, 18); layer2Button->setImage(RkImage(layer2Button->size(), RK_IMAGE_RC(layer2_disabled)), RkButton::State::Unpressed); layer2Button->setImage(RkImage(layer2Button->size(), RK_IMAGE_RC(layer2)), RkButton::State::Pressed); layer2Button->setImage(RkImage(layer2Button->size(), RK_IMAGE_RC(layer2_hover)), RkButton::State::PressedHover); layer2Button->setImage(RkImage(layer2Button->size(), RK_IMAGE_RC(layer2_hover)), RkButton::State::UnpressedHover); layer2Button->setCheckable(true); mainLayout->addWidget(layer2Button); mainLayout->addSpace(2); layer3Button = new GeonkickButton(this); layer3Button->setBackgroundColor(background()); layer3Button->setSize(24, 18); layer3Button->setImage(RkImage(layer3Button->size(), RK_IMAGE_RC(layer3_disabled)), RkButton::State::Unpressed); layer3Button->setImage(RkImage(layer3Button->size(), RK_IMAGE_RC(layer3)), RkButton::State::Pressed); layer3Button->setImage(RkImage(layer3Button->size(), RK_IMAGE_RC(layer3_hover)), RkButton::State::PressedHover); layer3Button->setImage(RkImage(layer3Button->size(), RK_IMAGE_RC(layer3_hover)), RkButton::State::UnpressedHover); layer3Button->setCheckable(true); mainLayout->addWidget(layer3Button); RK_ACT_BIND(layer1Button, toggled, RK_ACT_ARGS(bool b), kitModel->api(), enbaleLayer(GeonkickApi::Layer::Layer1, b)); RK_ACT_BIND(layer3Button, toggled, RK_ACT_ARGS(bool b), kitModel->api(), enbaleLayer(GeonkickApi::Layer::Layer3, b)); RK_ACT_BIND(layer2Button, toggled, RK_ACT_ARGS(bool b), kitModel->api(), enbaleLayer(GeonkickApi::Layer::Layer2, b)); } void TopBar::setPresetName(const std::string &name) { if (name.size() > 20) { std::string preset = name; preset.resize(15); preset += "..."; presetNameLabel->setText(preset); } else { presetNameLabel->setText(name); } } void TopBar::updateGui() { auto api = kitModel->api(); layer1Button->setPressed(api->isLayerEnabled(GeonkickApi::Layer::Layer1)); layer2Button->setPressed(api->isLayerEnabled(GeonkickApi::Layer::Layer2)); layer3Button->setPressed(api->isLayerEnabled(GeonkickApi::Layer::Layer3)); tuneCheckbox->setPressed(api->isAudioOutputTuned(api->currentPercussion())); setPresetName(kitModel->currentPercussion()->name()); midiKeyButton->setText(MidiKeyWidget::midiKeyToNote(kitModel->currentPercussion()->key())); } void TopBar::showMidiPopup() { auto midiPopup = new MidiKeyWidget(dynamic_cast(getTopWidget()), kitModel->currentPercussion()); midiPopup->setPosition(midiKeyButton->x() - 170, y() + 35); RK_ACT_BIND(midiPopup, isAboutToClose, RK_ACT_ARGS(), midiKeyButton, setPressed(false)); midiPopup->show(); } geonkick-2.10.2/src/top_bar.h000066400000000000000000000050731451017372400157650ustar00rootroot00000000000000/** * File name: top_bar.h * Project: Geonkick (A kick synthesizer) * * Copyright (C) 2018 Iurie Nistor * * This file is part of Geonkick. * * GeonKick 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef GEONKICK_TOP_BAR_H #define GEONKICK_TOP_BAR_H #include "geonkick_widget.h" #include "geonkick_api.h" class RkLabel; class GeonkickButton; class RkContainer; class ViewState; class KitModel; class TopBar : public GeonkickWidget { public: TopBar(GeonkickWidget *parent, KitModel *model); void setPresetName(const std::string &name); RK_DECL_ACT(saveFile, saveFile(), RK_ARG_TYPE(), RK_ARG_VAL()); RK_DECL_ACT(openFile, openFile(), RK_ARG_TYPE(), RK_ARG_VAL()); RK_DECL_ACT(openAbout, openAbout(), RK_ARG_TYPE(), RK_ARG_VAL()); RK_DECL_ACT(resetToDefault, resetToDefault(), RK_ARG_TYPE(), RK_ARG_VAL()); RK_DECL_ACT(openExport, openExport(), RK_ARG_TYPE(), RK_ARG_VAL()); RK_DECL_ACT(layerSelected, layerSelected(GeonkickApi::Layer layer, bool b), RK_ARG_TYPE(GeonkickApi::Layer, bool), RK_ARG_VAL(layer, b)); void updateGui(); protected: void showMidiPopup(); private: void createLyersButtons(RkContainer *mainLayout); void addSeparator(RkContainer *mainLayout, int width = 5); KitModel *kitModel; GeonkickButton *openFileButton; GeonkickButton *saveFileButton; GeonkickButton *exportFileButton; RkLabel* presetNameLabel; GeonkickButton *layer1Button; GeonkickButton *layer2Button; GeonkickButton *layer3Button; GeonkickButton *tuneCheckbox; GeonkickButton *controlsButton; GeonkickButton *midiKeyButton; #ifndef GEONKICK_SINGLE GeonkickButton *kitButton; #endif // GEONKICK_SINGLE GeonkickButton *presetsButton; GeonkickButton *samplesButton; }; #endif // GEONKICK_TOP_BAR_H